Microsoft Basic listing from 1978
Microsoft Basic listing from 1978
Sorry if this has been posted already (didn't see it anywhere else, though), Pagetable has posted the source of Microsoft Basic 1978, with comments and their analysis. They claim this is the oldest publicly known software that Bill Gates worked on.
http://www.pagetable.com/?p=774
Funny easter egg included:
As has been discussed before, this code writes the string “MICROSOFT!” into the PET’s screen RAM if the argument to WAIT is “6502″. The encoded string is hidden as two extra 40 bit floating point numbers appended to the coefficients used by the SIN function:
http://www.pagetable.com/?p=774
Funny easter egg included:
As has been discussed before, this code writes the string “MICROSOFT!” into the PET’s screen RAM if the argument to WAIT is “6502″. The encoded string is hidden as two extra 40 bit floating point numbers appended to the coefficients used by the SIN function:
- barrym95838
- Posts: 2056
- Joined: 30 Jun 2013
- Location: Sacramento, CA, USA
Re: Microsoft Basic listing from 1978
I always thought that the CHRGET routine:
... could have been written better, but I never tried to improve it until now (about 40 years too late, it seems). It appears that a non-space character retrieved should arrive unmolested, but the C flag should be clear for the ASCII range [1..47] and set otherwise. Additionally, the Z flag should be set for NUL and ":" and clear otherwise.
Didn't I just save two bytes (and four cycles for chars < ":")? CHRGET is heavily used during run-time, and I'm willing to speculate that these improvements would be noticeable. In my personal experience, 65xx MS BASIC with 40-bit floats was already significantly snappier on the 1 MHz Apple ][ than it was with 32-bit floats on the 1.774 MHz TRS-80 Model 1 ... could Mr. Gates have been secretly sand-bagging, or was he just in such a hurry to deliver that he failed to notice?
Thoughts and/or corrections?
Mike B.
Code: Select all
CHRGET: INC CHRGET+7 ;INCREMENT THE WHOLE TXTPTR.
BNE CHRGOT
INC CHRGET+8
CHRGOT: LDA 60000 ;A LOAD WITH AN EXT ADDR.
TXTPTR= CHRGOT+1
CMPI " " ;SKIP SPACES.
BEQ CHRGET
QNUM: CMPI ":" ;IS IT A ":"?
BCS CHRRTS ;IT IS .GE. ":"
SEC
SBCI "0" ;ALL CHARS .GT. "9" HAVE RET'D SO
SEC
SBCI 256-"0" ;SEE IF NUMERIC.
;TURN CARRY ON IF NUMERIC.
;ALSO, SETZ IF NULL.
CHRRTS: RTS ;RETURN TO CALLER.
Code: Select all
CHRGET: INC CHRGET+7 ;INCREMENT THE WHOLE TXTPTR.
BNE CHRGOT
INC CHRGET+8
CHRGOT: LDA 60000 ;A LOAD WITH AN EXT ADDR.
TXTPTR= CHRGOT+1
CMPI " " ;SKIP SPACES.
BEQ CHRGET
QNUM: CMPI ":" ;IS IT A ":"?
BCS CHRRTS ;IT IS .GE. ":"
CMPI "0" ;ALL CHARS .GT. "9" HAVE RET'D SO
;TURN CARRY ON IF NUMERIC.
ANDI 255 ;ALSO, SETZ IF NULL.
CHRRTS: RTS ;RETURN TO CALLER.
Thoughts and/or corrections?
Mike B.
Re: Microsoft Basic listing from 1978
(Bear in mind the Z80's memory bus is such that it performs 2x, or 2.3x, slower than a 6502 at the same clock speed. So the TRS-80 at 1.774MHz is handicapped relative to a 1MHz 6502, on average. Of course the factor is a broad approximation, and the design of each system and software will fuzz the comparison.)
- barrym95838
- Posts: 2056
- Joined: 30 Jun 2013
- Location: Sacramento, CA, USA
Re: Microsoft Basic listing from 1978
In addition, the Applesoft tokenizer removed spaces from everything that wasn't a literal string, REMark or DATA statement (adding its own spaces in rather clumsily while LISTing). So the CMP " " and BEQ CHRGET could have been removed as well, saving four more bytes and four more cycles for every fetch, giving the Apple ][ an even greater speed advantage.
Mike B.
Mike B.
Re: Microsoft Basic listing from 1978
EhBASIC has a similar CHRGET routine:The last comment correctly states that the carry on RTS is clear for a numeric character. This comment is wrong in the original MS-BASIC source. The detour through the 2 SBCs is necesssary (and quite clever) to clear the carry instead of setting it!
Code: Select all
; character get subroutine for zero page
; For a 1.8432MHz 6502 including the JSR and RTS
; fastest (>=":") = 29 cycles = 15.7uS
; slowest (<":") = 40 cycles = 21.7uS
; space skip = +21 cycles = +11.4uS
; inc across page = +4 cycles = +2.2uS
; the target address for the LDA at LAB_2CF4 becomes the BASIC execute pointer once the
; block is copied to it's destination, any non zero page address will do at assembly
; time, to assemble a three byte instruction.
; ...
LAB_IGBY = $BC ; get next BASIC byte subroutine
LAB_GBYT = $C2 ; get current BASIC byte subroutine
Bpntrl = $C3 ; BASIC execute (get byte) pointer low byte
Bpntrh = Bpntrl+1 ; BASIC execute (get byte) pointer high byte
; ...
; page 0 initialisation table from $BC
; increment and scan memory
LAB_2CEE
INC Bpntrl ; increment BASIC execute pointer low byte
BNE LAB_2CF4 ; branch if no carry
; else
INC Bpntrh ; increment BASIC execute pointer high byte
; page 0 initialisation table from $C2
; scan memory
LAB_2CF4
LDA $FFFF ; get byte to scan (addr set by call routine)
CMP #TK_ELSE ; compare with the token for ELSE
BEQ LAB_2D05 ; exit if ELSE, not numeric, carry set
CMP #':' ; compare with ":"
BCS LAB_2D05 ; exit if >= ":", not numeric, carry set
CMP #' ' ; compare with " "
BEQ LAB_2CEE ; if " " go do next
SEC ; set carry for SBC
SBC #'0' ; subtract "0"
SEC ; set carry for SBC
SBC #$D0 ; subtract -"0"
; clear carry if byte = "0"-"9"
LAB_2D05
RTS
6502 sources on GitHub: https://github.com/Klaus2m5
Re: Microsoft Basic listing from 1978
Quote:
Paul Allen’s additional macros for 6502 development made the MACRO-10 assembler output one 36 bit PDP-10 instruction word for every 6502 byte. When targeting a real 6502 machine, the 6502 binary could be created by simply extracting one byte from every PDP-10 word.
In the case of targeting the simulator, the code created by the assembler could just be run without modification, since every emitted PDP-10 instruction was constructed so that it would trap – the linked-in simulator would then extract the 6502 opcode from the instruction and emulate the 6502 behavior.
In the case of targeting the simulator, the code created by the assembler could just be run without modification, since every emitted PDP-10 instruction was constructed so that it would trap – the linked-in simulator would then extract the 6502 opcode from the instruction and emulate the 6502 behavior.
- barrym95838
- Posts: 2056
- Joined: 30 Jun 2013
- Location: Sacramento, CA, USA
Re: Microsoft Basic listing from 1978
barrym95838 wrote:
In addition, the Applesoft tokenizer removed spaces from everything that wasn't a literal string, REMark or DATA statement (adding its own spaces in rather clumsily while LISTing). So the CMP " " and BEQ CHRGET could have been removed as well, saving four more bytes and four more cycles for every fetch, giving the Apple ][ an even greater speed advantage.
Mike B.
Mike B.
Mike B.
Re: Microsoft Basic listing from 1978
Funny thing..., the only Microsoft software to ever run on Apple computers were MS Basic and MS Office.. Or am I wrong?
Re: Microsoft Basic listing from 1978
Do you count PowerPoint, shipped in '87 for Mac and the company acquired later the same year by MS?
Quote:
The Microsoft Corporation announced its first significant software acquisition today, paying $14 million for Forethought Inc. of Sunnyvale, Calif. Forethought makes a program called PowerPoint that allows users of Apple Macintosh computers to make overhead transparencies or flip charts.
-
Uncle Warthog
- Posts: 14
- Joined: 24 Oct 2017
Re: Microsoft Basic listing from 1978
kakemoms wrote:
Funny thing..., the only Microsoft software to ever run on Apple computers were MS Basic and MS Office.. Or am I wrong?
Re: Microsoft Basic listing from 1978
(Welcome to the forums, Uncle Warthog!)
- barrym95838
- Posts: 2056
- Joined: 30 Jun 2013
- Location: Sacramento, CA, USA
Re: Microsoft Basic listing from 1978
Klaus2m5 wrote:
EhBASIC has a similar CHRGET routine:The last comment correctly states that the carry on RTS is clear for a numeric character. This comment is wrong in the original MS-BASIC source. The detour through the 2 SBCs is necesssary (and quite clever) to clear the carry instead of setting it!
Code: Select all
; character get subroutine for zero page
; For a 1.8432MHz 6502 including the JSR and RTS
; fastest (>=":") = 29 cycles = 15.7uS
; slowest (<":") = 40 cycles = 21.7uS
; space skip = +21 cycles = +11.4uS
; inc across page = +4 cycles = +2.2uS
; the target address for the LDA at LAB_2CF4 becomes the BASIC execute pointer once the
; block is copied to it's destination, any non zero page address will do at assembly
; time, to assemble a three byte instruction.
; ...
LAB_IGBY = $BC ; get next BASIC byte subroutine
LAB_GBYT = $C2 ; get current BASIC byte subroutine
Bpntrl = $C3 ; BASIC execute (get byte) pointer low byte
Bpntrh = Bpntrl+1 ; BASIC execute (get byte) pointer high byte
; ...
; page 0 initialisation table from $BC
; increment and scan memory
LAB_2CEE
INC Bpntrl ; increment BASIC execute pointer low byte
BNE LAB_2CF4 ; branch if no carry
; else
INC Bpntrh ; increment BASIC execute pointer high byte
; page 0 initialisation table from $C2
; scan memory
LAB_2CF4
LDA $FFFF ; get byte to scan (addr set by call routine)
CMP #TK_ELSE ; compare with the token for ELSE
BEQ LAB_2D05 ; exit if ELSE, not numeric, carry set
CMP #':' ; compare with ":"
BCS LAB_2D05 ; exit if >= ":", not numeric, carry set
CMP #' ' ; compare with " "
BEQ LAB_2CEE ; if " " go do next
SEC ; set carry for SBC
SBC #'0' ; subtract "0"
SEC ; set carry for SBC
SBC #$D0 ; subtract -"0"
; clear carry if byte = "0"-"9"
LAB_2D05
RTS
Code: Select all
...
SEC ; set carry for SBC
SBC #'0' ; subtract "0"
SEC ; set carry for SBC
SBC #$D0 ; subtract -"0"
; clear carry if byte = "0"-"9"
...Code: Select all
...
EOR #'0' ; convert to binary
CMP #10 ; clear carry if decimal digit
EOR #'0' ; convert back to ASCII
...Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!
Mike B. (about me) (learning how to github)
Mike B. (about me) (learning how to github)
Re: Microsoft Basic listing from 1978
And it has the virtue of working even if characters above '9' haven't already been eliminated.
- floobydust
- Posts: 1394
- Joined: 05 Mar 2013
Re: Microsoft Basic listing from 1978
I did a CMOS only version of EhBasic a while back... which does seem to run a tad faster. I also moved the CHRGET (equivalent) routine into ROM and used a CMOS addressing mode to get the current byte. Granted, that instruction takes one additional clock cycle but it saves a lot of page zero space. Here's the code snippet:
I'm guessing that moving the CMP #' ' ahead of the CMP#: might be a bit quicker on code execution, but depends on how the source code is formatted. In any case, I can make Mike's changes at the bottom of the code and give it a go on some long running programs for testing. One would think that optimizing this routine would make BASIC run faster, but it depends on how long the program takes to run and also, to some degree, how many spaces are in the program.
Code: Select all
; character get subroutine - ROM based
; the target address for the LDA at LAB_GBYT is the BASIC execute pointer.
; block is no longer copied to page zero and uses a CMOS instruction/address mode.
; 16-bit pointer is located in page zero.
; increment and scan memory
LAB_IGBY
INC Bpntrl ; increment BASIC execute pointer low byte
BNE LAB_GBYT ; branch if no carry, else
INC Bpntrh ; increment BASIC execute pointer high byte
; scan memory
LAB_GBYT
LDA (Bpntrl) ; get byte to scan (addr set by call routine)
CMP #TK_ELSE ; compare with the token for ELSE
BEQ LAB_2D05 ; exit if ELSE, not numeric, carry set
CMP #':' ; compare with ":"
BCS LAB_2D05 ; exit if >= ":", not numeric, carry set
CMP #' ' ; compare with " "
BEQ LAB_IGBY ; if " " go do next
SEC ; set carry for SBC
SBC #'0' ; subtract "0"
SEC ; set carry for SBC
SBC #$D0 ; subtract -"0"
; clear carry if byte = "0"-"9"
LAB_2D05
RTS
Regards, KM
https://github.com/floobydust
https://github.com/floobydust
- floobydust
- Posts: 1394
- Joined: 05 Mar 2013
Re: Microsoft Basic listing from 1978
I made a few changes to the CHRGET routine in EhBasic and found some interesting timing changes. I'm using Bill O's Mandelbrot program from an earlier post. I've added a couple CALL statements which reset and display my benchmark counter (part of my Monitor/BIOS). The stock CHRGET routine results in a timing of 113.74 seconds to compete one run of the Mandelbrot. BASIC program:
First, I moved the CMP #' ' space to the top after the LDA, like this:
The timing changed to 114.90 seconds, over a second longer per run. Next, I changed to:
The timing actually got worse, at 115.06 seconds! So I put it back to the original code. I then tried Mike's change to the lower section of code:
This resulted in a timing of 113.54 seconds, a savings of 200 milliseconds per run. Nice!
Code: Select all
5 CALL 65328
10 MX=20
20 LET C$=" .,'~!^:;[/<&?oxOX# "
30 FOR Y=-19 TO 19
40 FOR X=-49 TO 30
50 CR=X/30
70 CI=Y/15
80 ZR = CR
90 ZI = CI
95 CO = 1
100 ZM = ZR*ZR
105 ZN = ZI*ZI
107 ZL = ZM+ZN
110 IF ZL>4 THEN GOTO 170
120 ZJ=ZM-ZN+CR
130 ZI=ZR*ZI*2+CI
140 ZR=ZJ
150 CO=CO+1
160 IF CO<MX THEN GOTO 100
170 PRINT MID$(C$,1+COUNT,1);
180 NEXT X
185 PRINT ""
190 NEXT Y
195 CALL 57371
200 GOTO 5
Code: Select all
LAB_GBYT
LDA (Bpntrl) ; get byte to scan (addr set by call routine)
CMP #' ' ; compare with " "
BEQ LAB_IGBY ; if " " go do next
CMP #TK_ELSE ; compare with the token for ELSE
BEQ LAB_2D05 ; exit if ELSE, not numeric, carry set
CMP #':' ; compare with ":"
BCS LAB_2D05 ; exit if >= ":", not numeric, carry set
Code: Select all
LAB_GBYT
LDA (Bpntrl) ; get byte to scan (addr set by call routine)
CMP #TK_ELSE ; compare with the token for ELSE
BEQ LAB_2D05 ; exit if ELSE, not numeric, carry set
CMP #' ' ; compare with " "
BEQ LAB_IGBY ; if " " go do next
CMP #':' ; compare with ":"
BCS LAB_2D05 ; exit if >= ":", not numeric, carry set
Quote:
LAB_GBYT
LDA (Bpntrl) ; get byte to scan (addr set by call routine)
CMP #TK_ELSE ; compare with the token for ELSE
BEQ LAB_2D05 ; exit if ELSE, not numeric, carry set
CMP #':' ; compare with ":"
BCS LAB_2D05 ; exit if >= ":", not numeric, carry set
CMP #' ' ; compare with " "
BEQ LAB_IGBY ; if " " go do next
; SEC ; set carry for SBC
; SBC #'0' ; subtract "0"
; SEC ; set carry for SBC
; SBC #$D0 ; subtract -"0"
; clear carry if byte = "0"-"9"
; Change suggested by Mike Barry to replace above
EOR #'0' ; convert to binary
CMP #10 ; clear carry if decimal digit
EOR #'0' ; convert back to ASCII
LDA (Bpntrl) ; get byte to scan (addr set by call routine)
CMP #TK_ELSE ; compare with the token for ELSE
BEQ LAB_2D05 ; exit if ELSE, not numeric, carry set
CMP #':' ; compare with ":"
BCS LAB_2D05 ; exit if >= ":", not numeric, carry set
CMP #' ' ; compare with " "
BEQ LAB_IGBY ; if " " go do next
; SEC ; set carry for SBC
; SBC #'0' ; subtract "0"
; SEC ; set carry for SBC
; SBC #$D0 ; subtract -"0"
; clear carry if byte = "0"-"9"
; Change suggested by Mike Barry to replace above
EOR #'0' ; convert to binary
CMP #10 ; clear carry if decimal digit
EOR #'0' ; convert back to ASCII
Regards, KM
https://github.com/floobydust
https://github.com/floobydust