1
 8080 MACRO ASSEMBLER, VER 3.0        ERRORS = 0      
+                                                      19:22  02/29/2017
+                                                                                      PAGE 1
      


                   ; MBL - MULTI BOOT LOADER FOR THE ALTAIR 8800
                   ;
                   ; DISAASEMBLED AND COMMENTED BY GEOFF HARRISON (GHOV SOLIVANT COM)
                   ;
                   ; THIS IS A BOOT LOADER THAT CAN BE LOACATED IN ROM AND WHICH
                   ; CAN READ ANY OF THE PUNCHED TAPE FORMATS THAT MITS DESIGNED.
                   ; IT RUNS OUT OF ROM BY CREATING A READBYTE ROUTINE IN RAM WHICH
                   ; IS CUSTOMIZED FOR WHATEVER I/O BOARD THE USER SPECIFIES AT
                   ; RUN TIME ON THE FRONT PANEL SWITCHES. IT THEN SKIPS OVER THE
                   ; LEADIN BYTES AND THE SECOND STAGE LOADER WHICH ALWAYS PRECEED
                   ; THE PAYLOAD ON A MITS TAPE.  SINCE IT DOESN'T USE THE
                   ; SECOND STAGE LOADER, IT DOESN'T CARE WHICH OF SEVERAL POSSIBLE
                   ; VERSIONS OF TAPE IT IS LOADING.  NORMALLY YOU HAVE TO BE SURE
                   ; TO USE THE CORRECT 1ST STAGE LOADER FOR THE TAPE YOU ARE LOADING,
                   ; WITH THE MBL IT DOESN'T MATTER.  AFTER SKIPPING THE 2ND STAGE
                   ; LOADER ON THE TAPE, IT STARTS READING AND STORING PAYLOAD
                   ; BYTES AND CHECKING THAT THE CHECKSUM IS CORRECT FOR EACH PACKET
                   ; OF BYTES READ.  WHEN THE ENTIRE PAYLOAD HAS BEEN READ INTO MEMORY,
                   ; IT READS THE ENTRY POINT FOR THE PROGRAM FROM THE TAPE AND JUMPS
                   ; TO THAT LOCATION.
                   ;
                   ; THE FORMAT OF A MITS PAPER TAPE IS:
                   ;       - SOME NUMBER OF IDENTICAL BYTES, EACH CONTAINING
                   ;               THE LENGTH (N) OF THE STAGE2 LOADER
                   ;       - N BYTES OF STAGE 2 LOADER
                   ;       - OPTIONALLY SOME NUMBER OF NULLS
                   ;       - ONE OR MORE LOAD RECORDS (SEE BELOW)
                   ;       - AN EOF RECORD (SEE BELOW)
                   ;
                   ; A LOAD RECORD CONSISTS OF:
                   ;       074     - SYNC BYTE (HEX 03CH)
                   ;       NNN     - # BYTES IN RECORD
                   ;       LLL     - LOW BYTE OF LOAD ADDRESS
                   ;       HHH     - HIGH BYTE OF LOAD ADDRESS
                   ;   <NNN BYTES> - NNN BYTES OF PROGRAM DATA (THE PAYLOAD)
                   ;       CCC     - CHECKSUM BYTE
                   ;                (CHECKSUM INCLUDES LLL & HHH)
                   ; THERE WILL BE 1 OR MORE PROG LOAD RECORDS,
                   ; EACH ONE WITH UP TO 256 BYTES OF PAYLOAD
                   ; BYTES.
                   ;
                   ; THE EOF RECORD CONSISTS OF:
                   ;       0170    - SYNC BYTE (HEX 078H)
                   ;       LLL     - LOW BYTE OF START ADDRESS
                   ;       HHH     - HIGH BYTE OF START ADDRESS
                   ;
                   ; BEFORE EXECUTING THIS PROGRAM, THE FRONT PANEL SENSE SWITCHES
                   ; MUST BE SET TO INDICATE WHAT DEVICE TO READ THE TAPE FROM AND
                   ; WHAT DEVICE THE TERMINAL IS ATTACHED TO.  THIS PROGRAM USES
                   ; A11..A8 TO DETERMINE THE DEVICE ATTACHED TO THE TAPE READER.
                   ; IF THE PAYLOAD IS NOT EARLIER THAN BASIC 4.0, IT USES A15..A12
1
 8080 MACRO ASSEMBLER, VER 3.0        ERRORS = 0      
+                                                      19:22  02/29/2017
+                                                                                      PAGE 2
      


                   ; TO DETERMINE WHERE THE TERMINAL DEVICE IS.
                   ; POSSIBLE SWITCH VALUES ARE
                   ;
                   ; DEVICE                SWITCH VALUE
                   ; 2SIO (2 STOP BITS)      0000B
                   ; 2SIO (1 STOP BIT)       0001B
                   ; SIO                     0010B
                   ; ACR                     0011B
                   ; 4PIO                    0100B
                   ; PIO                     0101B
                   ; HSR                     0110B
                   ;
                   ; IF A VALUE LARGER THAN 7 IS ENTERED THIS PROGRAM WILL RETURN
                   ; AN ERROR.
                   ;
                   ; PRIOR TO BASIC 4.0, MITS USED DIFFERENT SENSE SWITCH SETTINGS
                   ; TO SPECIFY THE TERMINAL DEVICE.  YOU SHOULD STILL BE ABLE TO
                   ; LOAD AN OLDER TAPE WITH THIS LOADER BY SETTING THE SWITCHES AS
                   ; ABOVE, STOPPING THE PROGRAM AFTER IT LOADS AND STARTS, CHANGING
                   ; THE SENSE SWITCHES (SEE THE APPROPRIATE MITS MANUAL FOR SWITCH
                   ; SETTINGS) AND RESTARTING THE PROGRAM AT ITS ENTRY POINT (E.G.
                   ; 00000H FOR BASIC 3.2).
                   ;
                   ; THIS LISTING WAS GENERATED FROM A HEX DUMP OF MBL PROVIDED BY
                   ; GRANT STOCKLY.  THE ASSEMBLY CODE AND COMMENTS WERE REVERSE
                   ; ENGINEERED FROM THAT DUMP BY GEOFF HARRISON.
                    
                   ; DEFAULT LOCATION IS AT 0FE00H.  MAY BE ASSEMBLED TO RUN
                   ; AT ANY LOCATION.
                   ;
                   ; ASSEMBLER SYNTAX IS FOR THE SPASM ASSEMBLER.  TO ASSEMBLE USE:
                   ;      SPASM MBL /F
                   ;      SLINK MBL=MBL.OBJ /C:0FE00
                   ;
                   ; FOR OTHER ASSEMBLERS, UNCOMMENT THE FOLLOWING LINE AND
                   ; (POSSIBLY) CHANGE THE SYNTAX OF ORG, DB, ETC.
                   ;
   FE00                            ORG 0FE00H
                    
                   ;----------------------------------------------------------
                   ; WE DON'T KNOW WHAT I/O CARDS ARE IN THE SYSTEM, SO
                   ; TRY TO INITIALIZE JUST ABOUT ANYTHING MITS HAD AVAILABLE
                   ; AT THE TIME.
                   ;
   FE00   F3                       DI
   FE01   AF                       XRA A
   FE02   D320                     OUT 020H
   FE04   D321                     OUT 021H
   FE06   D324                     OUT 024H
   FE08   D325                     OUT 025H
   FE0A   D326                     OUT 026H
1
 8080 MACRO ASSEMBLER, VER 3.0        ERRORS = 0      
+                                                      19:22  02/29/2017
+                                                                                      PAGE 3
      


   FE0C   D322                     OUT 022H
   FE0E   2F                       CMA
   FE0F   D323                     OUT 023H
   FE11   D327                     OUT 027H
   FE13   3E0C                     MVI A,00CH
   FE15   D324                     OUT 024H
   FE17   3E2C                     MVI A,02CH
   FE19   D320                     OUT 020H
   FE1B   D322                     OUT 022H
   FE1D   D326                     OUT 026H
   FE1F   3E03                     MVI A,003H
   FE21   D327                     OUT 027H
   FE23   D310                     OUT 010H
   FE25   3E11                     MVI A,011H
   FE27   D310                     OUT 010H
                    
                   ;----------------------------------------------------------
                   ; FIND TOP OF RAM.  ASSUMES THAT THERE IS NOT 64K OF RAM
                   ; AVAILABLE, A REASONABLE ASSUMPTION GIVEN THAT THIS ROUTINE
                   ; RESIDES IN ROM SOMEWHERE AT THE TOP OF MEMORY.
                   ;
   FE29   21FFFF                   LXI H, 0FFFFH
   FE2C   23       SCANRAM:        INX H           ; POINT @ NEXT BYTE
   FE2D   7E                       MOV A,M         ; GET IT
   FE2E   47                       MOV B,A         ; SAVE IT
   FE2F   2F                       CMA             ; INVERT IT
   FE30   77                       MOV M,A         ; TRY TO WRITE IT BACK OUT
   FE31   BE                       CMP M           ; SEE IF IT WROTE OUT CORRECTLY
   FE32   70                       MOV M,B         ; RESTORE THE BYTE TO ITS ORIG VALUE
   FE33   CA2CFE                   JZ SCANRAM      ; LOOP IF THE BYTE WROTE CORRECTLY
                    
                                   ; SOMETHING'S WRONG IF THE FAILURE TO WRITE TO RAM
                                   ; HAPPENED ON OTHER THAN A PAGE BOUNDARY
   FE36   AF                       XRA A           ; A = 0
   FE37   BD                       CMP L
   FE38   C2C1FE                   JNZ MERROR      ; IF L != 0 GO TO 'M' ERROR HANDLER
                    
                                   ; HL NOW POINTS TO THE LAST WRITABLE BYTE IN RAM PLUS 1.
                                   ; SUBTRACT 14 TO MAKE ROOM FOR A STACK & INITIALIZE SP.
   FE3B   01F2FF                   LXI B,0FFF2H
   FE3E   09                       DAD B           ; HL += BC
   FE3F   F9                       SPHL            ; SP = HL
                    
   FE40   09                       DAD B           ; SET HL TO POINT TO WHAT WILL BE
                                                   ; THE START OF THE READBYTE ROUTINE.
   FE41   E5                       PUSH H          ; SAVE THE START ADDRESS ON THE STACK.
                    
   FE42   DBFF                     IN 0FFH         ; READ FRONT PANEL SWITCHES
   FE44   E60F                     ANI 00FH        ; MASK LOWER 4 BITS
   FE46   FE07                     CPI 007H        ; SWITCHES SET TO >= 7?
   FE48   F2D7FE                   JP IERROR       ; Y - GO TO 'I' ERROR HANDLER
1
 8080 MACRO ASSEMBLER, VER 3.0        ERRORS = 0      
+                                                      19:22  02/29/2017
+                                                                                      PAGE 4
      


                    
                                   ; POINT HL AT THE NTH ENTRY IN THE MOD TABLE,
                                   ; WHERE N = THE FRONT PANEL SWITCH SETTING
   FE4B   21EBFE                   LXI H, TABLE1   ; POINT HL TO START OF THE MOD TABLE
   FE4E   04                       INR B           ; B CURRENTLY CONTAINS 0FFH, INC IT TO 0
   FE4F   4F                       MOV C,A         ;  
   FE50   87                       ADD A           ;    C = A * 3
   FE51   81                       ADD C           ;  /
   FE52   4F                       MOV C,A         ; /
   FE53   09                       DAD B           ; HL += BC
                    
                   ;----------------------------------------------------------
                   ; CONSTRUCT AN INPUT FUNCTION ON THE STACK.
                   ; SINCE THIS IS A PUSH DOWN STACK, THE ROUTINE IS BUILT IN REVERSE.
                   ; WHEN FINISHED, THE ROUTINE WILL LOOK LIKE THIS. (XX VALUES ARE
                   ; CALCULATED ON THE FLY OR READ FROM THE MOD TABLE.  THE ROUTINE
                   ; HAS TO BE BUILT DYNAMICALLY LIKE THIS BECAUSE DIFFERENT I/O CARDS
                   ; REQUIRE DIFFERENT CODE TO READ THEIR DATA AND STATUS.  THE USER
                   ; SPECIFIED WHICH CARD IS IN USE ON THE FRONT PANEL SWITCHES.)
                   ; READBYTE:
                   ;       DB XX           IN XX           ; CHECK PORT STATUS
                   ;       E6 XX           ANI XX
                   ;       XX LO HI        JZ/JNZ READBYTE ; LOOP 'TILL BYTE ARRIVES
                   ;       DB XX           IN XX           ; GET INPUT BYTE
                   ;       F5              PUSH PSW        ; SAVE IT
                   ;       80              ADD B           ; UPDATE CHECKSUM IN B
                   ;       47              MOV B,A
                   ;       F1              POP PSW         ; RETRIEVE INPUT BYTE
                   ;       C9              RET             ; RETURN A=BYTE, B=CHECKSUM
                   ;
   FE54   D1                       POP D           ; DE = THE START ADRESS OF THIS ROUTINE
                    
                                   ; CONSTRUCT RET; POP PSW
   FE55   01F1C9                   LXI B,0C9F1H
   FE58   C5                       PUSH B
                    
                                   ; CONSTRUCT MOV B,A; ADD B
   FE59   018047                   LXI B,04780H
   FE5C   C5                       PUSH B
                    
                                   ; CONSTRUCT  PUSH PSW;  INPUT PORT RETRIEVED FROM MOD TABLE 
   FE5D   06F5                     MVI B,0F5H
   FE5F   4E                       MOV C,M
   FE60   79                       MOV A,C
   FE61   C5                       PUSH B
                    
                                   ; CONSTRUCT IN (USED WITH PORT CONSTRUCTED ABOVE);  HIGH BYTE OF
   FE62   06DB                     MVI B,0DBH
   FE64   4A                       MOV C,D
   FE65   C5                       PUSH B
                    
1
 8080 MACRO ASSEMBLER, VER 3.0        ERRORS = 0      
+                                                      19:22  02/29/2017
+                                                                                      PAGE 5
      


                                   ; CONSTRUCT  LOW BYTE OF JMP ; JZ/JNZ (FROM MOD TABLE)
   FE66   43                       MOV B,E
   FE67   23                       INX H
   FE68   4E                       MOV C,M
   FE69   C5                       PUSH B
                    
                                   ; CONSTRUCT ANI  MASK FROM MOD TABLE 
   FE6A   23                       INX H
   FE6B   46                       MOV B,M
   FE6C   0EE6                     MVI C,0E6H
   FE6E   C5                       PUSH B
                    
                                   ; CONSTRUCT IN  PORT STATUS ADDRESS (PORT NUMBER - 1) 
   FE6F   3D                       DCR A
   FE70   47                       MOV B,A
   FE71   0EDB                     MVI C,0DBH
   FE73   C5                       PUSH B
                   ;----------------------------------------------------------
                   ; READ A STREAM OF INPUT BYTES.
                   ;
   FE74   EB                       XCHG            ; HL = START OF READBYTE ROUTINE, DE = JUNK
                    
                                   ; ???????????????????????????????????????????????????
   FE75   3E04                     MVI A,004H      ; WHY ARE WE SENDING 04H TO DEVICE 027H?
   FE77   D327                     OUT 027H        ; IN FACT, WHAT IS DEVICE 027H? ANYONE KNOW? IS
                                                   ; 88-HSR?  PERHAPS WE'RE SWITCHING ON THE DRIVE
                                   ; ???????????????????????????????????????????????????
                    
                                   ; SKIP OVER LEADIN BYTES.
   FE79   CDA5FE                   CALL LINK       ; FLUSH INPUT BUFFER
   FE7C   CDA5FE                   CALL LINK       ; GET A BYTE
   FE7F   4F                       MOV C,A         ; REMEMBER IT
   FE80   CDA5FE   LEADINSKIP:     CALL LINK       ; GET ANOTHER BYTE
   FE83   B9                       CMP C
   FE84   CA80FE                   JZ LEADINSKIP   ; LOOP UNTIL WE RECEIVE A DIFFERENT BYTE VALUE
                    
                                   ; AT THIS POINT, C CONTAINS THE FIRST LEADIN BYTE
                                   ; THAT WAS ON THE TAPE, WHICH SHOULD REPRESENT THE
                                   ; LENGTH OF THE STAGE 2 LOADER.
                                   ; SKIP OVER STAGE 2 LOADER.
   FE87   0D                       DCR C
   FE88   CDA5FE   STAGE2SKIP:     CALL LINK       ; GET A BYTE
   FE8B   0D                       DCR C
   FE8C   C288FE                   JNZ STAGE2SKIP  ; LOOP WHILE C > 0
                    
                                   ; NOW WE'VE SKIPPED OVER ALL THE UNNEEDED STUFF, START
                                   ; LOOKING FOR THE FIRST LOAD RECORD.
   FE8F   CDA5FE   FINDTOKEN:      CALL LINK       ; GET A BYTE
   FE92   FE3C                     CPI 03CH        ; IS IT A LOAD RECORD TOKEN?
   FE94   CAA6FE                   JZ LOADRECORD   ; Y - GO AND PROCESS A LOAD RECORD
   FE97   FE78                     CPI 078H        ; IS IT AN EOF TOKEN?
1
 8080 MACRO ASSEMBLER, VER 3.0        ERRORS = 0      
+                                                      19:22  02/29/2017
+                                                                                      PAGE 6
      


   FE99   C28FFE                   JNZ FINDTOKEN   ; N - KEEP LOOKING
                    
                                   ; GOT AN EOF TOKEN.  READ NEXT TWO BYTES AS PROGRAM
                                   ; START ADDRESS AND JUMP TO THAT ADDRESS.
   FE9C   CDA5FE   EOF:            CALL LINK       ; GET PROG START LOW
   FE9F   4F                       MOV C,A
   FEA0   CDA5FE                   CALL LINK       ; GET PROG START HIGH
   FEA3   69                       MOV L,C
   FEA4   67                       MOV H,A
                    
                                   ; THIS IS MOSTLY USED TO CALL THE BYTE INPUT ROUTINE
                                   ; ON THE STACK, BUT IS ALSO USED TO JUMP TO THE ENTRY
                                   ; POINT OF THE DOWNLOADED PROGRAM.
   FEA5   E9       LINK:           PCHL            ; JMP TO (HL)
                    
                                   ; PROCESS A LOAD RECORD
   FEA6   CDA5FE   LOADRECORD:     CALL LINK       ; GET BYTE COUNT IN THIS RECORD
   FEA9   4F                       MOV C,A         ; SAVE IT
   FEAA   0600                     MVI B,000H      ; INITIALIZE CHECKSUM
   FEAC   CDA5FE                   CALL LINK       ; GET LOAD ADDRESS LOW
   FEAF   5F                       MOV E,A
   FEB0   CDA5FE                   CALL LINK       ; GET LOAD ADDRESS HIGH
   FEB3   57                       MOV D,A
                    
   FEB4   7A       LOREC2:         MOV A,D
   FEB5   BC                       CMP H           ; ARE THE INCOMING BYTES ABOUT TO
                                                   ;    OVERWRITE THE READBYTE ROUTINE?
   FEB6   3E4F                     MVI A,04FH      ; PREPARE TO SEND 'O', IF NECESSARY
   FEB8   CAD9FE                   JZ ERREXIT      ; Y - ERROR EXIT
   FEBB   CDA5FE                   CALL LINK       ; N - GET ANOTHER BYTE
   FEBE   EB                       XCHG            ; HL = DEST POINTER, DE = START OF READBYTE
   FEBF   77                       MOV M,A         ; STORE RECEIVED BYTE AT DESTINATION
   FEC0   BE                       CMP M           ; DID IT STORE CORRECTLY?
   FEC1   3E4D     MERROR:         MVI A,04DH      ; PREPARE TO SEND 'M', IF NECESSARY
   FEC3   C2D9FE                   JNZ ERREXIT     ; N - ERROR EXIT
   FEC6   23                       INX H           ; Y - INCREMENT DESTINATION POINTER
   FEC7   EB                       XCHG            ; HL = START OF READBYTE, DE = DEST POINTER
   FEC8   0D                       DCR C           ; DECREMENT BYTE COUNTER
   FEC9   C2B4FE                   JNZ LOREC2      ; IF NOT 0 THEN LOOP TO GET MORE BYTES
                    
   FECC   48                       MOV C,B         ; SAVE CALCULATED CHECKSUM VALUE BEFORE WE UPDAT
   FECD   CDA5FE                   CALL LINK       ; GET EXPECTED CHECKSUM FROM INPUT STREAM
   FED0   B9                       CMP C           ; DOES IT MATCH OUR CALCULATED SUM?
   FED1   CA8FFE                   JZ FINDTOKEN    ; Y - LOOK FOR MORE RECORDS ON THE TAPE
   FED4   3E43                     MVI A,043H      ; N - PREPARE TO SEND 'C' ERROR
   FED6   01                       DB 001H         ; LXI - HIDES NEXT TWO BYTES
   FED7   3E       IERROR:         DB 03EH         ; MVI A, 049H   ; PREPARE TO SEND 'I'
   FED8   49                       DB 049H
                    
                   ; STORE AN ERROR CODE AND THE ADDRESS @ WHICH IT HAPPENED IN
                   ; THE FIRST 3 BYTES OF RAM, THEN LOOP FOREVER SENDING THE
1
 8080 MACRO ASSEMBLER, VER 3.0        ERRORS = 0      
+                                                      19:22  02/29/2017
+                                                                                      PAGE 7
      


                   ; ERROR CODE TO ALL POSSIBLE TERMINAL DEVICES.
                   ;
   FED9   320000   ERREXIT:        STA 00000H
   FEDC   220100                   SHLD 00001H
   FEDF   FB                       EI
   FEE0   D301     FOREVER:        OUT 001H
   FEE2   D311                     OUT 011H
   FEE4   D305                     OUT 005H
   FEE6   D323                     OUT 023H
   FEE8   C3E0FE                   JMP FOREVER
                    
                   ;----------------------------------------------------------
                   ; MOD TABLE.
                   ; THIS IS A TABLE OF PORT ADDRESSES, COMMANDS FOR CHECKING
                   ; THE PORT STATUS (JZ/JNZ), AND STATUS MASKS FOR SEVERAL
                   ; I/O BOARDS.  THE VALUES ARE STUFFED INTO THE READBYTE
                   ; ROUTINE AT RUNTIME TO CUSTOMIZE IT FOR THE HARDWARE
                   ; BEING USED TO LOAD THE TAPE.
                   ;
   FEEB   11CA01   TABLE1:         DB      011H, 0CAH, 001H        ; 2SIO (2 STOP BITS)
   FEEE   11CA01                   DB      011H, 0CAH, 001H        ; 2SIO (1 STOP BIT)
   FEF1   01C201                   DB      001H, 0C2H, 001H        ; SIO
   FEF4   07C201                   DB      007H, 0C2H, 001H        ; ACR
   FEF7   21CA80                   DB      021H, 0CAH, 080H        ; 4PIO
   FEFA   05CA02                   DB      005H, 0CAH, 002H        ; PIO
   FEFD   25CA40                   DB      025H, 0CAH, 040H        ; HSR
                    
                                   END
 NO PROGRAM ERRORS
1
 8080 MACRO ASSEMBLER, VER 3.0        ERRORS = 0      
+                                                      19:22  02/29/2017
+                                                                                      PAGE 8
      


                        SYMBOL TABLE

  * 01

  A      0007      B      0000      C      0001      D      0002      
  E      0003      EOF    FE9C *    ERREX  FED9      FINDT  FE8F      
  FOREV  FEE0      H      0004      IERRO  FED7      L      0005      
  LEADI  FE80      LINK   FEA5      LOADR  FEA6      LOREC  FEB4      
  M      0006      MERRO  FEC1      PSW    0006      SCANR  FE2C      
  SP     0006      STAGE  FE88      TABLE  FEEB      

