1; MBL - MULTI BOOT LOADER FOR THE ALTAIR 8800 2; 3; DISAASEMBLED AND COMMENTED BY GEOFF HARRISON (GHOV SOLIVANT COM) 4; 5; THIS IS A BOOT LOADER THAT CAN BE LOACATED IN ROM AND WHICH 6; CAN READ ANY OF THE PUNCHED TAPE FORMATS THAT MITS DESIGNED. 7; IT RUNS OUT OF ROM BY CREATING A READBYTE ROUTINE IN RAM WHICH 8; IS CUSTOMIZED FOR WHATEVER I/O BOARD THE USER SPECIFIES AT 9; RUN TIME ON THE FRONT PANEL SWITCHES. IT THEN SKIPS OVER THE 10; LEADIN BYTES AND THE SECOND STAGE LOADER WHICH ALWAYS PRECEED 11; THE PAYLOAD ON A MITS TAPE. SINCE IT DOESN'T USE THE 12; SECOND STAGE LOADER, IT DOESN'T CARE WHICH OF SEVERAL POSSIBLE 13; VERSIONS OF TAPE IT IS LOADING. NORMALLY YOU HAVE TO BE SURE 14; TO USE THE CORRECT 1ST STAGE LOADER FOR THE TAPE YOU ARE LOADING, 15; WITH THE MBL IT DOESN'T MATTER. AFTER SKIPPING THE 2ND STAGE 16; LOADER ON THE TAPE, IT STARTS READING AND STORING PAYLOAD 17; BYTES AND CHECKING THAT THE CHECKSUM IS CORRECT FOR EACH PACKET 18; OF BYTES READ. WHEN THE ENTIRE PAYLOAD HAS BEEN READ INTO MEMORY, 19; IT READS THE ENTRY POINT FOR THE PROGRAM FROM THE TAPE AND JUMPS 20; TO THAT LOCATION. 21; 22; THE FORMAT OF A MITS PAPER TAPE IS: 23; - SOME NUMBER OF IDENTICAL BYTES, EACH CONTAINING 24; THE LENGTH (N) OF THE STAGE2 LOADER 25; - N BYTES OF STAGE 2 LOADER 26; - OPTIONALLY SOME NUMBER OF NULLS 27; - ONE OR MORE LOAD RECORDS (SEE BELOW) 28; - AN EOF RECORD (SEE BELOW) 29; 30; A LOAD RECORD CONSISTS OF: 31; 074 - SYNC BYTE (HEX 03CH) 32; NNN - # BYTES IN RECORD 33; LLL - LOW BYTE OF LOAD ADDRESS 34; HHH - HIGH BYTE OF LOAD ADDRESS 35; <NNN BYTES> - NNN BYTES OF PROGRAM DATA (THE PAYLOAD) 36; CCC - CHECKSUM BYTE 37; (CHECKSUM INCLUDES LLL & HHH) 38; THERE WILL BE 1 OR MORE PROG LOAD RECORDS, 39; EACH ONE WITH UP TO 256 BYTES OF PAYLOAD 40; BYTES. 41; 42; THE EOF RECORD CONSISTS OF: 43; 0170 - SYNC BYTE (HEX 078H) 44; LLL - LOW BYTE OF START ADDRESS 45; HHH - HIGH BYTE OF START ADDRESS 46; 47; BEFORE EXECUTING THIS PROGRAM, THE FRONT PANEL SENSE SWITCHES 48; MUST BE SET TO INDICATE WHAT DEVICE TO READ THE TAPE FROM AND 49; WHAT DEVICE THE TERMINAL IS ATTACHED TO. THIS PROGRAM USES 50; A11..A8 TO DETERMINE THE DEVICE ATTACHED TO THE TAPE READER. 51; IF THE PAYLOAD IS NOT EARLIER THAN BASIC 4.0, IT USES A15..A12 52; TO DETERMINE WHERE THE TERMINAL DEVICE IS. 53; POSSIBLE SWITCH VALUES ARE 54; 55; DEVICE SWITCH VALUE 56; 2SIO (2 STOP BITS) 0000B 57; 2SIO (1 STOP BIT) 0001B 58; SIO 0010B 59; ACR 0011B 60; 4PIO 0100B 61; PIO 0101B 62; HSR 0110B 63; 64; IF A VALUE LARGER THAN 7 IS ENTERED THIS PROGRAM WILL RETURN 65; AN ERROR. 66; 67; PRIOR TO BASIC 4.0, MITS USED DIFFERENT SENSE SWITCH SETTINGS 68; TO SPECIFY THE TERMINAL DEVICE. YOU SHOULD STILL BE ABLE TO 69; LOAD AN OLDER TAPE WITH THIS LOADER BY SETTING THE SWITCHES AS 70; ABOVE, STOPPING THE PROGRAM AFTER IT LOADS AND STARTS, CHANGING 71; THE SENSE SWITCHES (SEE THE APPROPRIATE MITS MANUAL FOR SWITCH 72; SETTINGS) AND RESTARTING THE PROGRAM AT ITS ENTRY POINT (E.G. 73; 00000H FOR BASIC 3.2). 74; 75; THIS LISTING WAS GENERATED FROM A HEX DUMP OF MBL PROVIDED BY 76; GRANT STOCKLY. THE ASSEMBLY CODE AND COMMENTS WERE REVERSE 77; ENGINEERED FROM THAT DUMP BY GEOFF HARRISON. 78 79; DEFAULT LOCATION IS AT 0FE00H. MAY BE ASSEMBLED TO RUN 80; AT ANY LOCATION. 81; 82; ASSEMBLER SYNTAX IS FOR THE SPASM ASSEMBLER. TO ASSEMBLE USE: 83; SPASM MBL /F 84; SLINK MBL=MBL.OBJ /C:0FE00 85; 86; FOR OTHER ASSEMBLERS, UNCOMMENT THE FOLLOWING LINE AND 87; (POSSIBLY) CHANGE THE SYNTAX OF ORG, DB, ETC. 88; 89 ORG 0FE00H 90 91;---------------------------------------------------------- 92; WE DON'T KNOW WHAT I/O CARDS ARE IN THE SYSTEM, SO 93; TRY TO INITIALIZE JUST ABOUT ANYTHING MITS HAD AVAILABLE 94; AT THE TIME. 95; 96 DI 97 XRA A 98 OUT 020H 99 OUT 021H 100 OUT 024H 101 OUT 025H 102 OUT 026H 103 OUT 022H 104 CMA 105 OUT 023H 106 OUT 027H 107 MVI A,00CH 108 OUT 024H 109 MVI A,02CH 110 OUT 020H 111 OUT 022H 112 OUT 026H 113 MVI A,003H 114 OUT 027H 115 OUT 010H 116 MVI A,011H 117 OUT 010H 118 119;---------------------------------------------------------- 120; FIND TOP OF RAM. ASSUMES THAT THERE IS NOT 64K OF RAM 121; AVAILABLE, A REASONABLE ASSUMPTION GIVEN THAT THIS ROUTINE 122; RESIDES IN ROM SOMEWHERE AT THE TOP OF MEMORY. 123; 124 LXI H, 0FFFFH 125SCANRAM: INX H ; POINT @ NEXT BYTE 126 MOV A,M ; GET IT 127 MOV B,A ; SAVE IT 128 CMA ; INVERT IT 129 MOV M,A ; TRY TO WRITE IT BACK OUT 130 CMP M ; SEE IF IT WROTE OUT CORRECTLY 131 MOV M,B ; RESTORE THE BYTE TO ITS ORIG VALUE 132 JZ SCANRAM ; LOOP IF THE BYTE WROTE CORRECTLY 133 134 ; SOMETHING'S WRONG IF THE FAILURE TO WRITE TO RAM 135 ; HAPPENED ON OTHER THAN A PAGE BOUNDARY 136 XRA A ; A = 0 137 CMP L 138 JNZ MERROR ; IF L != 0 GO TO 'M' ERROR HANDLER 139 140 ; HL NOW POINTS TO THE LAST WRITABLE BYTE IN RAM PLUS 1. 141 ; SUBTRACT 14 TO MAKE ROOM FOR A STACK & INITIALIZE SP. 142 LXI B,0FFF2H 143 DAD B ; HL += BC 144 SPHL ; SP = HL 145 146 DAD B ; SET HL TO POINT TO WHAT WILL BE 147 ; THE START OF THE READBYTE ROUTINE. 148 PUSH H ; SAVE THE START ADDRESS ON THE STACK. 149 150 IN 0FFH ; READ FRONT PANEL SWITCHES 151 ANI 00FH ; MASK LOWER 4 BITS 152 CPI 007H ; SWITCHES SET TO >= 7? 153 JP IERROR ; Y - GO TO 'I' ERROR HANDLER 154 155 ; POINT HL AT THE NTH ENTRY IN THE MOD TABLE, 156 ; WHERE N = THE FRONT PANEL SWITCH SETTING 157 LXI H, TABLE1 ; POINT HL TO START OF THE MOD TABLE 158 INR B ; B CURRENTLY CONTAINS 0FFH, INC IT TO 0 159 MOV C,A ; \ 160 ADD A ; \ C = A * 3 161 ADD C ; / 162 MOV C,A ; / 163 DAD B ; HL += BC 164 165;---------------------------------------------------------- 166; CONSTRUCT AN INPUT FUNCTION ON THE STACK. 167; SINCE THIS IS A PUSH DOWN STACK, THE ROUTINE IS BUILT IN REVERSE. 168; WHEN FINISHED, THE ROUTINE WILL LOOK LIKE THIS. (XX VALUES ARE 169; CALCULATED ON THE FLY OR READ FROM THE MOD TABLE. THE ROUTINE 170; HAS TO BE BUILT DYNAMICALLY LIKE THIS BECAUSE DIFFERENT I/O CARDS 171; REQUIRE DIFFERENT CODE TO READ THEIR DATA AND STATUS. THE USER 172; SPECIFIED WHICH CARD IS IN USE ON THE FRONT PANEL SWITCHES.) 173; READBYTE: 174; DB XX IN XX ; CHECK PORT STATUS 175; E6 XX ANI XX 176; XX LO HI JZ/JNZ READBYTE ; LOOP 'TILL BYTE ARRIVES 177; DB XX IN XX ; GET INPUT BYTE 178; F5 PUSH PSW ; SAVE IT 179; 80 ADD B ; UPDATE CHECKSUM IN B 180; 47 MOV B,A 181; F1 POP PSW ; RETRIEVE INPUT BYTE 182; C9 RET ; RETURN A=BYTE, B=CHECKSUM 183; 184 POP D ; DE = THE START ADRESS OF THIS ROUTINE 185 186 ; CONSTRUCT RET; POP PSW 187 LXI B,0C9F1H 188 PUSH B 189 190 ; CONSTRUCT MOV B,A; ADD B 191 LXI B,04780H 192 PUSH B 193 194 ; CONSTRUCT PUSH PSW; {INPUT PORT RETRIEVED FROM MOD TABLE} 195 MVI B,0F5H 196 MOV C,M 197 MOV A,C 198 PUSH B 199 200 ; CONSTRUCT IN (USED WITH PORT CONSTRUCTED ABOVE); {HIGH BYTE OF JMP INSTR} 201 MVI B,0DBH 202 MOV C,D 203 PUSH B 204 205 ; CONSTRUCT {LOW BYTE OF JMP}; JZ/JNZ (FROM MOD TABLE) 206 MOV B,E 207 INX H 208 MOV C,M 209 PUSH B 210 211 ; CONSTRUCT ANI {MASK FROM MOD TABLE} 212 INX H 213 MOV B,M 214 MVI C,0E6H 215 PUSH B 216 217 ; CONSTRUCT IN {PORT STATUS ADDRESS (PORT NUMBER - 1)} 218 DCR A 219 MOV B,A 220 MVI C,0DBH 221 PUSH B 222;---------------------------------------------------------- 223; READ A STREAM OF INPUT BYTES. 224; 225 XCHG ; HL = START OF READBYTE ROUTINE, DE = JUNK 226 227 ; ??????????????????????????????????????????????????? 228 MVI A,004H ; WHY ARE WE SENDING 04H TO DEVICE 027H? 229 OUT 027H ; IN FACT, WHAT IS DEVICE 027H? ANYONE KNOW? IS IT THE 230 ; 88-HSR? PERHAPS WE'RE SWITCHING ON THE DRIVE MOTOR. 231 ; ??????????????????????????????????????????????????? 232 233 ; SKIP OVER LEADIN BYTES. 234 CALL LINK ; FLUSH INPUT BUFFER 235 CALL LINK ; GET A BYTE 236 MOV C,A ; REMEMBER IT 237LEADINSKIP: CALL LINK ; GET ANOTHER BYTE 238 CMP C 239 JZ LEADINSKIP ; LOOP UNTIL WE RECEIVE A DIFFERENT BYTE VALUE 240 241 ; AT THIS POINT, C CONTAINS THE FIRST LEADIN BYTE 242 ; THAT WAS ON THE TAPE, WHICH SHOULD REPRESENT THE 243 ; LENGTH OF THE STAGE 2 LOADER. 244 ; SKIP OVER STAGE 2 LOADER. 245 DCR C 246STAGE2SKIP: CALL LINK ; GET A BYTE 247 DCR C 248 JNZ STAGE2SKIP ; LOOP WHILE C > 0 249 250 ; NOW WE'VE SKIPPED OVER ALL THE UNNEEDED STUFF, START 251 ; LOOKING FOR THE FIRST LOAD RECORD. 252FINDTOKEN: CALL LINK ; GET A BYTE 253 CPI 03CH ; IS IT A LOAD RECORD TOKEN? 254 JZ LOADRECORD ; Y - GO AND PROCESS A LOAD RECORD 255 CPI 078H ; IS IT AN EOF TOKEN? 256 JNZ FINDTOKEN ; N - KEEP LOOKING 257 258 ; GOT AN EOF TOKEN. READ NEXT TWO BYTES AS PROGRAM 259 ; START ADDRESS AND JUMP TO THAT ADDRESS. 260EOF: CALL LINK ; GET PROG START LOW 261 MOV C,A 262 CALL LINK ; GET PROG START HIGH 263 MOV L,C 264 MOV H,A 265 266 ; THIS IS MOSTLY USED TO CALL THE BYTE INPUT ROUTINE 267 ; ON THE STACK, BUT IS ALSO USED TO JUMP TO THE ENTRY 268 ; POINT OF THE DOWNLOADED PROGRAM. 269LINK: PCHL ; JMP TO (HL) 270 271 ; PROCESS A LOAD RECORD 272LOADRECORD: CALL LINK ; GET BYTE COUNT IN THIS RECORD 273 MOV C,A ; SAVE IT 274 MVI B,000H ; INITIALIZE CHECKSUM 275 CALL LINK ; GET LOAD ADDRESS LOW 276 MOV E,A 277 CALL LINK ; GET LOAD ADDRESS HIGH 278 MOV D,A 279 280LOREC2: MOV A,D 281 CMP H ; ARE THE INCOMING BYTES ABOUT TO 282 ; OVERWRITE THE READBYTE ROUTINE? 283 MVI A,04FH ; PREPARE TO SEND 'O', IF NECESSARY 284 JZ ERREXIT ; Y - ERROR EXIT 285 CALL LINK ; N - GET ANOTHER BYTE 286 XCHG ; HL = DEST POINTER, DE = START OF READBYTE 287 MOV M,A ; STORE RECEIVED BYTE AT DESTINATION 288 CMP M ; DID IT STORE CORRECTLY? 289MERROR: MVI A,04DH ; PREPARE TO SEND 'M', IF NECESSARY 290 JNZ ERREXIT ; N - ERROR EXIT 291 INX H ; Y - INCREMENT DESTINATION POINTER 292 XCHG ; HL = START OF READBYTE, DE = DEST POINTER 293 DCR C ; DECREMENT BYTE COUNTER 294 JNZ LOREC2 ; IF NOT 0 THEN LOOP TO GET MORE BYTES 295 296 MOV C,B ; SAVE CALCULATED CHECKSUM VALUE BEFORE WE UPDATE IT AGAIN 297 CALL LINK ; GET EXPECTED CHECKSUM FROM INPUT STREAM 298 CMP C ; DOES IT MATCH OUR CALCULATED SUM? 299 JZ FINDTOKEN ; Y - LOOK FOR MORE RECORDS ON THE TAPE 300 MVI A,043H ; N - PREPARE TO SEND 'C' ERROR 301 DB 001H ; LXI - HIDES NEXT TWO BYTES 302IERROR: DB 03EH ; MVI A, 049H ; PREPARE TO SEND 'I' 303 DB 049H 304 305; STORE AN ERROR CODE AND THE ADDRESS @ WHICH IT HAPPENED IN 306; THE FIRST 3 BYTES OF RAM, THEN LOOP FOREVER SENDING THE 307; ERROR CODE TO ALL POSSIBLE TERMINAL DEVICES. 308; 309ERREXIT: STA 00000H 310 SHLD 00001H 311 EI 312FOREVER: OUT 001H 313 OUT 011H 314 OUT 005H 315 OUT 023H 316 JMP FOREVER 317 318;---------------------------------------------------------- 319; MOD TABLE. 320; THIS IS A TABLE OF PORT ADDRESSES, COMMANDS FOR CHECKING 321; THE PORT STATUS (JZ/JNZ), AND STATUS MASKS FOR SEVERAL 322; I/O BOARDS. THE VALUES ARE STUFFED INTO THE READBYTE 323; ROUTINE AT RUNTIME TO CUSTOMIZE IT FOR THE HARDWARE 324; BEING USED TO LOAD THE TAPE. 325; 326TABLE1: DB 011H, 0CAH, 001H ; 2SIO (2 STOP BITS) 327 DB 011H, 0CAH, 001H ; 2SIO (1 STOP BIT) 328 DB 001H, 0C2H, 001H ; SIO 329 DB 007H, 0C2H, 001H ; ACR 330 DB 021H, 0CAH, 080H ; 4PIO 331 DB 005H, 0CAH, 002H ; PIO 332 DB 025H, 0CAH, 040H ; HSR 333 334 END 335