Skip to main content
Commonmark migration
Source Link
Source Link
cjs
  • 28k
  • 2
  • 83
  • 200

Yes, Microsoft 6502 BASIC was clearly a port of their 8080 BASIC.

Unfortunately the original 8080 source code for Microsoft BASIC isn't easily available, so we can't compare it directly to that without going to the Harvard University library to read the paper copy.

But the Microsoft source code for a later version of their 8080 BASIC, BASIC-80 5.2 (actually marked 5.11 in the source) is available online. This is copyright 1982 (from the copyright notice in INIT.MAC), but contains material obviously from the original 8080 source code, such as the following comment in BINTRP.MAC:

;--------- ---- -- ---- ----- --- ---- -----
;COPYRIGHT 1975 BY BILL GATES AND PAUL ALLEN
;--------- ---- -- ---- ----- --- ---- -----
;
;ORIGINALLY WRITTEN ON THE PDP-10 FROM
;FEBRUARY 9 TO  APRIL 9 1975

Comparing this with the 6502 source code we see substantial similarities in the overall organization of the code and almost exact correspondence of many routines within it.

START and INIT

For example, the entry point in both versions is at address 0 and contains JMP INIT; in both cases the INIT routine overwrites the target of this JMP with the address of a routine called READY that does a "warm start". After that there are a couple of words storing the addresses of routines to convert a floating point number to an integer and vice versa.

Here's the 8080 version from 1982:

        PUBLIC  START
START:
        PUBLIC  JMPINI
JMPINI: JMP     INIT                    ;INIT IS THE INTIALIZATION ROUTINE
                                        ;IT SETS UP CERTAIN
                                        ;LOCATIONS DELETES FUNCTIONS IF
                                        ;DESIRED AND
                                        ;CHANGES THIS TO JMP READY
                                        ;WARM START FOR ISIS

                                        ;OF THE ROUTINE TO CONVERT [A,B]
                                        ;TO A FLOATING POINT NUMBER IN THE FAC
        DW      FRCINT                  ;TURN FAC INTO AN INTEGER IN [H,L]
        DW      MAKINT                  ;TURN [H,L] INTO A VALUE IN THE FAC
                                        ;SET VALTYP FOR INTEGER

And the 6502 version from 1978:

START:  JMP     INIT            ;INITIALIZE - SETUP CERTAIN LOCATIONS
                                ;AND DELETE FUNCTIONS IF NOT NEEDED,
                                ;AND CHANGE THIS TO "JMP READY"
                                ;IN CASE USER RESTARTS AT LOC ZERO.
RDYJSR: JMP     INIT            ;CHANGED TO "JMP STROUT" BY "INIT"
                                ;TO HANDLE ERRORS.
ADRAYI: ADR(AYINT)              ;STORE HERE THE ADDR OF THE
                                ;ROUTINE TO TURN THE FAC INTO A 
                                ;TWO BYTE SIGNED INTEGER IN [Y,A]
ADRGAY: ADR(GIVAYF)>            ;STORE HERE THE ADDR OF THE
                                ;ROUTINE TO CONVERT [Y,A] TO A FLOATING
                                ;POINT NUMBER IN THE FAC.

STROUT

Another example is the STROUT routine, which clearly works in the same way, modulo the differences in processor architecture, and even has some comments that are nearly identical. Here's the 8080 version:

;
; PRINT THE STRING POINTED TO BY [H,L] WHICH ENDS WITH A ZERO
; IF THE STRING IS BELOW DSCTMP IT WILL BE COPIED INTO STRING SPACE
;
STROUI: INX     H                       ;POINT AT NEXT CHARACTER
STROUT: CALL    STRLIT                  ;GET A STRING LITERAL
;
; PRINT THE STRING WHOSE DESCRIPTOR IS POINTED TO BY FACLO.
;
STRPRT: CALL    FREFAC                  ;RETURN TEMP POINTER BY FACLO
        CALL    GETBCD                  ;[D]=LENGTH [B,C]=POINTER AT DATA
        INR     D                       ;INCREMENT AND DECREMENT EARLY
                                        ;TO CHECK FOR NULL STRING
STRPR2: DCR     D                       ;DECREMENT THE LENGTH
        RZ                              ;ALL DONE
        LDAX    B                       ;GET CHARACTER TO PRINT
        CALL    OUTDO
        CPI     13
        CZ      CRFIN
        INX     B                       ;POINT TO THE NEXT CHARACTER
        JMP     STRPR2                  ;AND PRINT IT...
        PAGE

And here's the 6502 version:

;
; PRINT THE STRING POINTED TO BY [Y,A] WHICH ENDS WITH A ZERO.
; IF THE STRING IS BELOW DSCTMP IT WILL BE COPIED INTO STRING SPACE.
;
STROUT: JSR     STRLIT          ;GET A STRING LITERAL.
;
; PRINT THE STRING WHOSE DESCRIPTOR IS POINTED TO BY FACMO.
;
STRPRT: JSR     FREFAC          ;RETURN TEMP POINTER.
        TAX                     ;PUT COUNT INTO COUNTER.
        LDYI    0
        INX                     ;MOVE ONE AHEAD.
STRPR2: DEX
        BEQ     PRTRTS          ;ALL DONE.
        LDADY   INDEX           ;PNTR TO ACT STRNG SET BY FREFAC.
        JSR     OUTDO
        INY
        CMPI    13
        BNE     STRPR2
        JSR     CRFIN           ;TYPE REST OF CARRIAGE RETURN.
        JMP     STRPR2          ;AND ON AND ON.

PTRGET

Even routines that are different due to feature changes are pretty clearly derived from common original source. For example, the PTRGET routine that that reads a variable name and fetches its value uses a pair of zero-page memory locations in the 6502 version to store the variable name, which is limited to two characters in that older version of BASIC. The newer 8080 code allows up to 40 characters in a variable name, but still stores the first two characters of the name in the C and B registers (memory was used in the 6502 because it has far fewer registers) and compares those first, only moving on to a further comparison with the rest of the name in a memory buffer if that fails.

And of course there's the similarity in the comments and routine name itself. The 8080 version:

; ROUTINE TO READ THE VARIABLE NAME AT THE CURRENT TEXT POSITION
; AND PUT A POINTER TO ITS VALUE IN [D,E]. [H,L] IS UPDATED
; TO POINT TO THE CHARACTER AFTER THE VARIABLE NAME.
; VALTYP IS SETUP. NOTE THAT EVALUATING SUBSCRIPTS IN
; A VARIABLE NAME CAN CAUSE RECURSIVE CALLS TO PTRGET SO AT
; THAT POINT ALL VALUES MUST BE STORED ON THE STACK.
; ON RETURN, [A] DOES NOT REFLECT THE VALUE OF THE TERMINATING CHARACTER
;
PTRGET: XRA     A                       ;MAKE [A]=0

And the 6502 version:

; ROUTINE TO READ THE VARIABLE NAME AT THE CURRENT TEXT POSITION
; AND  PUT A POINTER TO ITS VALUE IN VARPNT. [TXTPTR]
; POINTS TO THE TERMINATING CHARCTER.. NOT THAT EVALUATING SUBSCRIPTS
; IN A VARIABLE NAME CAN CAUSE RECURSIVE CALLS TO "PTRGET" SO AT
; THAT POINT ALL VALUES MUST BE STORED ON THE STACK.
;
PTRGET: LDXI    0               ;MAKE [ACCX]=0.

Comparison with Other Object Code

Though we don't have original source for an 8080 version of Microsoft BASIC from the same era as the 6502 source, we do have binaries that can be disassembled. Comparing this disassembly of Altair BASIC 3.2 we can also find substantial similarities in the code (including all the routines mentioned above), though of course there are no comments to compare.

I myself have been working on a disassembly of the 6800 BASIC that Microsoft wrote for the Altair 680B after they'd written the 8080 basic for Altair's 8800 but probably before they started the 6502 version. Both ports were done by Ric Weiland, and again the 6800 version shows the same substantial similarities in code organization and the details of how the particular routines work.