Looking through the source code of the 6502 MS-BASIC, certain parts of it seem more reminiscent of how things would be done on the 8080, then on how they should be done on the 6502. Code to find a line with a specified number, for example, is a relatively performance-critical, but Bill Gates' code seems less than optimal:
FNDLIN: LDWX TXTTAB ;LOAD [X,A] WITH [TXTTAB] FNDLNC: LDYI 1 STWX LOWTR ;STORE [X,A] INTO LOWTR LDADY LOWTR ;SEE IF LINK IS 0 BEQ FLINRT INY INY LDA LINNUM+1 ;COMP HIGH ORDERS OF LINE NUMBERS. CMPDY LOWTR BCC FLNRTS ;NO SUCH LINE NUMBER. BEQ FNDLO1 DEY BNE AFFRTS ;ALWAYS BRANCH. FNDLO1: LDA LINNUM DEY CMPDY LOWTR ;COMPARE LOW ORDERS. BCC FLNRTS ;NO SUCH NUMBER. BEQ FLNRTS ;GO TIT. AFFRTS: DEY LDADY LOWTR ;FETCH LINK. TAX DEY LDADY LOWTR BCS FNDLNC ;ALWAYS BRANCHES. FLINRT: CLC ;C MAY BE HIGH. FLNRTS: RTS ;RETURN TO CALLER.
The code uses a lot of INY and DEY instructions, even though the value of Y at any given spot in the code will always be the same; using an
LDY #1 rather than
AFFRTS would have allowed
BEQ FNDL01 / DEY / BNE AFFRTS to be replaced with
BNE AFFRTS. Further,
INY INY could be cheaply replaced with
LDY #3 and eliminate the need for the preceding
LDY #1. Further, the use of X:A to hold a temporary high/low address foregoes the possibility of using (ind,x) mode [with x set to zero] which could help eliminate some of the gymnastics with Y.
On the other hand, if the code was based on a reworking of a similar algorithm on the 8080, the use of INY/DEY could make sense since the effects would be analogous to incrementing or decrementing HL on the 8080. On the 8080, code could rather efficiently do something like:
; Using Z80 mnemonics for 8080 opcodes loop: ex de,hl start: ld e,(HL) ; Fetch link LSB inc HL ld d,(HL) ; Fetch link MSB inc HL ld a,c ; Line number LSB sub a,(HL) inc hl ld a,b ; Line number MSB sbc a,(HL) jc lp
For the 6502, a more efficient approach would be to have each line preceded by a length byte, allowing something like:
start: lda linNum+1 sta lnTemp ldy #2 ldx #0 loop: lda (ptr),y cmp lnTemp ; Carry clear if looking for < what's there bcs oddBall lda ptr adc (ptr,x) sta ptr bcc loop inc ptr+1 bcs lp ; Carry still set from before oddBall: bne notFound lda lnNum ; Do other byte of line # if we haven't yet sta lnTemp dey bne loop : If not equal, we need to do LSB of line # clc notFound: rts
Only 25 cycles per line in the common case, with an extra 7-8 cycles on each page crossing (depending upon whether the
lda(ind),y crossed a page). So about twice as fast as the original code. This code would rely upon the 6502's ability to directly access two addresses relative to (ind) without having to manipulate any index registers, which is something the 8080 couldn't do, but exploiting that ability could have made things much faster. Further, it benefits from the fact that adding an 8-bit value to a 16-bit pointer is much faster than loading a 16-bit value.
Does the 8080 BASIC use routines which largely mirror the logic of the 6502 versions, thus suggesting that the 6502 was strongly derived from it? Or was Bill Gates simply following the coding style of a processor other than the 6502 (perhaps an 8080, or maybe PDP-10 or something else)?
ldax d/stax h/inx d/inx h/inc c/jnz lp/inc b/jnz lp. Sure RST vectors are cute, but they're not exactly speedy.