; TI990/4 LOAD ROMs disassembly ; ; This ROM could be the item listed as 945121-0002 in 990 Handbook, 6-11 (page 218). ; ;General background : ; These ROMs contain the handler for the LOAD/RESTART interrupt. Provided the board configuration is ; correct, this interrupt is called at start-up (instead of RESET), and we execute the boot loader. ; ; This interrupt can also be triggered by the programmer panel, and there is code to handle the panel ; functions. ; ;Description : ; Include a CPU test ROM set at >FC00, and a LOAD/RESTART ROM with programmer panel handling and ; boot strap code at >FE00. Note that the LOAD ROM calls the test routine in the CPU test ROM. ; ; The first ROM should be interesting for people who want to test an emulator. ; The second ROM is a source of documentation on the boot process, and the programmer panel. ; ; If you want a starting point, you can have a look at the LOAD vector located at >FFFC. ; The LOAD routine starts at >FE00. ; ;Notes : ; * The code assumes 32 (?) bytes of RAM are available at >F800. ; * The code can ignore the programmer panel provided the proper CRU bit is set, which allows the use ; of programmer-panel-less TI990/4. ; * The boot strap loader boots from a 733 ASR terminal at CRU address >000. Though, if you write a ; non-zero value to @>F800 (using the programmer panel), the ROM should boot from a Card Reader ; located at CRU address >020. The loader seems to support booting from a MDU (Maintenance ; Diagnostics Unit) when such a MDU is connected in the programmer panel slot. ; (Standard CRU map reference : 990 computer family systems handbook, H-2, page 346) ; * A description of the 733 ASR terminal is given in the 990 handbook. Among other things, there is ; an optionnal tape unit, which could be actually used by the boot process. Unfortunately, I have no ; documentation on the ASR CRU interface. ; ;Conventions used : ; TMS9900 assembly conventions should have been respected. Particularily : ; * bit are numbered from MSB to LSB (unlike almost every other CPU) ; ; ; Raphael Nabet, 2000/04/24 ; ; revision history : ; 2000/04/24 : clarified a few things with MDU support ; 2000/02/20 : fixed a few comments, documented object code and Hollerith code better ; 2000/02/16 : fixed a few comments, replaced DW and DB with standard DATA and BYTE directives ; 2000/01/31 : fixed some ambiguous comments, documented programmer panel better, found out actual ; identity of boot devices ; 2000/01/28 : initial release ; Here we have a cpu test ROM ; test routine entry point ; preserves R0. ; jumps to the boot routine on exit FC00: 1D0B SBO >000B ; switch "Fault" light on FC02: 0208 7FFF LI R8,>7FFF FC06: 05C8 INCT R8 FC08: 1933 JNO >FC70 ; stop if no overflow FC0A: C040 MOV R0,R1 FC0C: C008 MOV R8,R0 FC0E: 0858 SRA R8,5 FC10: 0918 SRL R8,1 FC12: 0808 SRA R8,0 FC14: C001 MOV R1,R0 ; restore R0 FC16: 0288 3F00 CI R8,>3F00 FC1A: 162A JNE >FC70 ; stop if different from the value expected FC1C: 0704 SETO R4 FC1E: 0244 5555 ANDI R4,>5555 FC22: 06A0 FC36 BL @>FC36 ; this should clear R4, or stop if it fails FC26: 0264 5555 ORI R4,>5555 FC2A: 06A0 FC36 BL @>FC36 FC2E: 0300 0001 LIMI >0001 ; effectively sets least significant bit in ST FC32: 0420 FC4E BLWP @>FC4E ; jump to >FC40 ; should be called with R4 == >5555 FC36: 0264 AAAA ORI R4,>AAAA FC3A: 0584 INC R4 FC3C: 1619 JNE >FC70 ; stop if R4 != 0 FC3E: 045B B *R11 ; continue on... FC40: 02CB STST R11 FC42: 081B SRA R11,1 ; test LSBit in ST FC44: 1715 JNC >FC70 ; stop if not set FC46: 04CF CLR R15 ; -> clear ST FC48: 020E FC52 LI R14,>FC52 ; actual jump address FC4C: 0380 RTWP FC4E: F7EC FC40 DATA >F7EC,>FC40 ; BLWP vector ; continue on... This gets tricky :-) . FC52: 02C7 STST R7 FC54: C047 MOV R7,R1 FC56: 160C JNE >FC70 ; stop if ST not clear FC58: C081 MOV R1,R2 FC5A: 0B52 SRC R2,5 ; FC5C: 06C2 SWPB R2 ; effectively rotate R2 3 bits left FC5E: C2C2 MOV R2,R11 FC60: 074B ABS R11 FC62: C0C2 MOV R2,R3 FC64: 0A13 SLA R3,1 ; test R2 sign bit FC66: 1709 JNC >FC7A ; start if R2 is positive FC68: 054B INV R11 ; R11 = ~R11 FC6A: 808B C R11,R2 ; so R11 = R2-1 FC6C: 1B7A JH >FD62 ; stop the machine if R11>R2 FC6E: 1101 JLT >FC72 ; huh ??? what if R2 == 0x8000 ? This must be a bug ! FC70: 10FF JMP >FC70 ; stop everything FC72: 82C2 C R2,R11 FC74: 1A76 JL >FD62 ; we still have R11 = R2-1... FC76: 05CB INCT R11 FC78: 060B DEC R11 ; now R11 == R2 ; we jump here when R2 >= 0 FC7A: 808B C R11,R2 FC7C: 1672 JNE >FD62 FC7E: C0C1 MOV R1,R3 FC80: A0C2 A R2,R3 FC82: C101 MOV R1,R4 FC84: 0504 NEG R4 FC86: 6102 S R2,R4 FC88: 0504 NEG R4 FC8A: 9103 CB R3,R4 ; we should have R3==R4 FC8C: 1B6A JH >FD62 ; stop if logically higher FC8E: 1201 JLE >FC92 ; jump there if OK FC90: 1068 JMP >FD62 ; stop if arithmetically greater FC92: 9820 F807 F809 CB @>F807,@>F809 ; compare R3 MSB and R4 MSB FC98: 1664 JNE >FD62 ; stop if different FC9A: 20C4 COC R4,R3 ; (R3 & R4) == R3 ? FC9C: 1662 JNE >FD62 FC9E: 0544 INV R4 FCA0: 24C4 CZC R4,R3 ; (R3 & ~R4) == R3 ? FCA2: 165F JNE >FD62 FCA4: 0544 INV R4 FCA6: 7103 SB R3,R4 ; result 0 in R4 MSB FCA8: 115C JLT >FD62 FCAA: 165B JNE >FD62 FCAC: 7820 F807 F809 SB @>F807,@>F809 ; substract LSB FCB2: 1657 JNE >FD62 FCB4: D102 MOVB R2,R4 ; move R2 MSB FCB6: D820 F805 F809 MOVB @>F805,@>F809 ; move R2 LSB FCBC: B820 F803 F809 AB @>F803,@>F809 ; add R1 LSB FCC2: 1702 JNC >FCC8 FCC4: 0224 0100 AI R4,>0100 ; adjust carry if needed FCC8: B101 AB R1,R4 ; add R1 MSB FCCA: 8103 C R3,R4 ; we should still have R3 = R1+R2, too... FCCC: 164A JNE >FD62 FCCE: 0583 INC R3 FCD0: 0583 INC R3 FCD2: 80C4 C R4,R3 FCD4: 1346 JEQ >FD62 FCD6: 1545 JGT >FD62 FCD8: 0643 DECT R3 FCDA: 8103 C R3,R4 FCDC: 1642 JNE >FD62 FCDE: C143 MOV R3,R5 FCE0: C185 MOV R5,R6 FCE2: C1C5 MOV R5,R7 FCE4: 06C7 SWPB R7 ; swap bytes (8-bit rotate) FCE6: 0985 SRL R5,8 FCE8: 0A86 SLA R6,8 FCEA: E185 SOC R5,R6 ; swap bytes with another method FCEC: 81C6 C R6,R7 FCEE: 1639 JNE >FD62 FCF0: 0B86 SRC R6,8 ; swap bytes again FCF2: 80C6 C R6,R3 ; should equate the original value FCF4: 1636 JNE >FD62 ; now, multiply R1 and R2 with custom code, then check MPY gives the same result FCF6: 020A 0010 LI R10,>0010 ; 16 bits to test FCFA: 04CB CLR R11 FCFC: 04C8 CLR R8 FCFE: 04C7 CLR R7 FD00: C182 MOV R2,R6 FD02: C141 MOV R1,R5 FD04: 0815 SRA R5,1 ; if R5 LSB is set FD06: 1704 JNC >FD10 FD08: A2C6 A R6,R11 ; add R7:R6 to R8:R11 FD0A: 1701 JNC >FD0E FD0C: 0588 INC R8 FD0E: A207 A R7,R8 FD10: 0A17 SLA R7,1 ; shift R7:R6 FD12: 0A16 SLA R6,1 FD14: 1801 JOC >FD18 FD16: 1001 JMP >FD1A FD18: 0587 INC R7 FD1A: 060A DEC R10 ; loop FD1C: 16F3 JNE >FD04 FD1E: C0C1 MOV R1,R3 FD20: 38E0 F804 MPY @>F804,R3 ; = MPY R2,R3 FD24: 8203 C R3,R8 ; compare MSWord FD26: 161D JNE >FD62 FD28: 82C4 C R4,R11 ; compare LSWord FD2A: 161B JNE >FD62 ; now, test DIVide FD2C: 8081 C R1,R2 FD2E: 1408 JHE >FD40 FD30: A101 A R1,R4 ; if R1 < R2, add R1 to MSW (R4) FD32: 1702 JNC >FD38 FD34: 6101 S R1,R4 ; if carry, revert to normal method FD36: 1004 JMP >FD40 ; (otherwise, there might be an overflow when executing DIV) FD38: 3CC2 DIV R2,R3 FD3A: 04A0 FD34 X @>FD34 ; = S R1,R4 (remainder should be R1, and is fixed to be 0) FD3E: 1003 JMP >FD46 FD40: 3CE0 F804 DIV @>F804,R3 ; compute R3:R4 / R2 FD44: C104 MOV R4,R4 ; test the remainder FD46: 160D JNE >FD62 ; should be 0 ; now test parity ST bit FD48: D181 MOVB R1,R6 FD4A: 0207 0008 LI R7,>0008 ; 8 bits to test FD4E: 04C8 CLR R8 FD50: 0A16 SLA R6,1 FD52: 1701 JNC >FD56 FD54: 0588 INC R8 ; count bits FD56: 0607 DEC R7 FD58: 15FB JGT >FD50 ; loop if some bits left FD5A: 0B18 SRC R8,1 ; test parity FD5C: 1703 JNC >FD64 FD5E: D041 MOVB R1,R1 ; should be odd parity FD60: 1C03 JOP >FD68 FD62: 10FF JMP >FD62 FD64: B1C1 AB R1,R7 ; R7 == 0 from previous loop (remember ?) FD66: 1CFD JOP >FD62 ; should be even parity FD68: C142 MOV R2,R5 FD6A: 4142 SZC R2,R5 ; R5 = R5 & ~R2 FD6C: 16FA JNE >FD62 ; result should be 0 FD6E: A142 A R2,R5 ; hence R5 = R2 FD70: 5142 SZCB R2,R5 ; R2 MSB & R5 MSB FD72: 16F7 JNE >FD62 FD74: 5820 F805 F80B SZCB @>F805,@>F80B ; R2 LSB & R5 LSB FD7A: 16F3 JNE >FD62 FD7C: 070B SETO R11 ; R11 = >FFFF FD7E: 42C2 SZC R2,R11 ; R11 = R11 & ~R2 FD80: 13F0 JEQ >FD62 ; obviously, R2 is assumed to be non-zero FD82: F141 SOCB R1,R5 ; R5 should be 0 from previous computation. FD84: 06C5 SWPB R5 FD86: F160 F803 SOCB @>F803,R5 ; bit-wise OR with R1 LSB FD8A: 0B85 SRC R5,8 ; so R5 = R1 FD8C: 6141 S R1,R5 FD8E: 16E9 JNE >FD62 FD90: 0581 INC R1 ; increment R1 FD92: 0281 FFC0 CI R1,>FFC0 FD96: 1B0A JH >FDAC ; if R1 > >FFC0, bail out FD98: C181 MOV R1,R6 FD9A: 0204 0013 LI R4,>0013 FD9E: 3D44 DIV R4,R5 ; effectively computes R6 / 19 (R5 is still 0, remember ?) FDA0: C186 MOV R6,R6 ; test the remainder FDA2: 1602 JNE >FDA8 FDA4: 0221 03E8 AI R1,>03E8 ; add 1000 to R1 if R1 is multiple of 19 FDA8: 0460 FC58 B @>FC58 ; and loop ; this effectively results into testing a sequence : ; 0,1,...,18,1019,1020,1021,1022,1023,1024,1025,2026,2027,2028,...,2032,3033,...,65467,...,65472. ; The next value is 65473, which is greater than >FFC0 (65472), so we exit the routine at this point. ; Looks like a decent way to shuffle values. ; memory test ; we use some nasty self-compiling code FDAC: 070A SETO R10 FDAE: 0202 02E0 LI R2,>02E0 ; LWPI FDB2: 04C3 CLR R3 ; >0000 FDB4: 0204 0460 LI R4,>0460 ; B @ FDB8: 0205 FDD0 LI R5,>FDD0 ; >FDD0 ; loop start FDBC: 0283 F800 CI R3,>F800 FDC0: 131A JEQ >FDF6 ; don't test system area >F800->FFFF FDC2: 02A6 STWP R6 FDC4: 8183 C R3,R6 ; do not overwrite our workspace. FDC6: 1602 JNE >FDCC ; geez, the previous test already ensured this ! FDC8: 0223 0020 AI R3,>0020 FDCC: C053 MOV *R3,R1 ; save future R0 register FDCE: 0442 B R2 ; this emulates missing "LWP R3" instruction FDD0: 2820 F814 XOR @>F814,R0 ; XOR old R10 with new R0 FDD4: 02E0 F800 LWPI >F800 ; restore old WP FDD8: 0541 INV R1 ; R1 = ~R1 FDDA: C1C1 MOV R1,R7 FDDC: 84C1 C R1,*R3 ; should give the same result as R0 = R0 ^ >FFFF FDDE: 1606 JNE >FDEC FDE0: 0553 INV *R3 ; Restore memory location FDE2: 2873 XOR *R3+,R1 ; result should be >FFFF FDE4: 0581 INC R1 FDE6: 13EA JEQ >FDBC ; try next word if OK FDE8: 0643 DECT R3 ; else restore R3 FDEA: 0547 INV R7 ; restore value read in RAM FDEC: C183 MOV R3,R6 FDEE: 0A36 SLA R6,3 ; if the address in on a 8kb-boundary, we must only have left FDF0: 1302 JEQ >FDF6 ; the RAM area, so we continue on (?) FDF2: C087 MOV R7,R2 FDF4: 0340 IDLE ; else stop the machine FDF6: C060 FFF8 MOV @>FFF8,R1 ; read pointer to boot routine FDFA: 0451 B *R1 ; and jump there ; entry point for self-test routine FDFC: 0460 FC00 B @>FC00 ; Here, we have programmer panel handling and boot strap code. ; LOAD vector entry point : FE00: 020C 1FE0 LI R12,>1FE0 ; load programmer panel CRU base address FE04: 0221 6700 AI R1,>6700 ; test for magical value (>9900) FE08: 1602 JNE >FE0E ; jump if not found FE0A: 1D0A SBO >000A ; switch "Run" light on FE0C: 0450 B *R0 ; jump to routine pointed by R0 FE0E: 0209 00A0 LI R9,>00A0 FE12: 04C0 CLR R0 FE14: 1F0B TB >000B ; test if programmer panel present and active FE16: 13F2 JEQ >FDFC ; if panel inactive, silently jump to CPU self-test FE18: C08E MOV R14,R2 ; else copy old PC to display register ; programmer panel scan : we have 4 rows of 8 switches ; 16 switches allow to toggle the display in R2. ; the 16 other switches allow to access programmer panel functions. ; ; register conventions here : ; R2 = data register ; R7 = memory address register ; R13 = WP ; R14 = PC ; R15 = ST ; keyscan loop start FE1A: 3202 LDCR R2,8 ; display data register MSB FE1C: 3220 F805 LDCR @>F805,8 ; display data register LSB FE20: 0205 FE7C LI R5,>FE7C FE24: 0695 BL *R5 ; read current 8 switches from programmer panel to R1 MSB FE26: 16FE JNE >FE24 ; wait for all switches to be released FE28: 0695 BL *R5 FE2A: 16FC JNE >FE24 ; test again to fix switch bounce problems FE2C: 04C1 CLR R1 FE2E: 1D08 SBO >0008 ; "Increment scan" bit -> select another row of switches FE30: 0695 BL *R5 FE32: 13FD JEQ >FE2E ; if no switch pressed, scan next row FE34: 0695 BL *R5 FE36: 13FB JEQ >FE2E ; fixes switch bounce problems FE38: 1F09 TB >0009 ; scan count bit 0 -> is current switch row number odd or even ? FE3A: 1601 JNE >FE3E FE3C: 06C1 SWPB R1 ; if odd, we read last 8 switches in 16 switches, so we rotate 8 bits. FE3E: 1F08 TB >0008 ; scan count bit 1 -> is this command switches or data switches ? FE40: 1302 JEQ >FE46 ; command switches -> jump to interpret command FE42: 2881 XOR R1,R2 ; data switches -> toggle the data displayed as requested FE44: 10EA JMP >FE1A ; update display and go back to scan routine ; command interpreter for programmer panel FE46: 04C5 CLR R5 FE48: 05C5 INCT R5 FE4A: 0A11 SLA R1,1 FE4C: 17FD JNC >FE48 ; count first bit set to 1 in R1, i.e. first switch pressed FE4E: 04A5 FE52 X @>FE52(R5) ; execute command as needed. FE52: 10E3 JMP >FE1A ; update display and go back to scan routine if we have not jumped away ; The jump offsets in the following table must be >FE52-relative, since this is the value of PC when X ; executes the instruction. FE54: 1013 JMP *+>FE78->FE52 ; "Halt/SIE" switch FE56: 1011 JMP *+>FE74->FE52 ; "Run" switch FE58: 0360 RSET ; "Reset" switch FE5A: 10D5 JMP *+>FDFC->FE52 ; "Load" switch FE5C: C08D MOV R13,R2 ; "Display WP" switch FE5E: C08E MOV R14,R2 ; "Display PC" switch FE60: C08F MOV R15,R2 ; "Display ST" switch FE62: C087 MOV R7,R2 ; "Display MA" switch FE64: C342 MOV R2,R13 ; "Enter WP" switch FE66: C382 MOV R2,R14 ; "Enter PC" switch FE68: C3C2 MOV R2,R15 ; "Enter ST" switch FE6A: C1C2 MOV R2,R7 ; "Enter MA" switch FE6C: C097 MOV *R7,R2 ; "MDD" switch ("Memory Data Display") FE6E: 05C7 INCT R7 ; "MAI" switch ("Memory Address Increment") FE70: C5C2 MOV R2,*R7 ; "MDE" switch ("Memory Data Enter") FE72: 04C2 CLR R2 ; "CLR" switch ; Code for run switch FE74: 1D0A SBO >000A ; switch "Run" light on FE76: 0380 RTWP ; code for "Halt/SIE" switch FE78: 1D0E SBO >000E ; set "Single Instruction Execute" FE7A: 0380 RTWP ; Restart trap will occur after 2 instructions ; The second instruction executed will be an instruction from the program we interrupted. ; Pressing the HALT/SIE switch repetitively, you will trace the current program. ; this routine read a row of 8 switches from the programmer panel FE7C: 1D0D SBO >000D ; start panel timer FE7E: 1F0A TB >000A ; wait for timer active bit to be set FE80: 16FE JNE >FE7E FE82: 3601 STCR R1,8 ; retrieve value FE84: 045B B *R11 ; Real boot routine, called after the CPU self-test ; ; We boot from a 733 ASR terminal located at CRU address >000. ; ; Note that when R0 is non-zero, we boot from a card reader located at CRU address >020 instead, but ; this possibility is never used since R0 is cleared by the LOAD vector. ; This must be a debug mode, which should be enabled with the programmer panel. ; ; The boot code is encoded as object code, as described in the Model 990 Computer Programming Card. ; ; We receive characters from the boot device. These are interpreted as commands or as hexadecimal ; immediate operands. Note that we actually only use hexadecimal characters, and 'G','H',':'. ; ; Commands with 16-bit immediate (encoded in hexadecimal) : ; * '1' : Absolute Entry Address : boot routine address = IMM ; * '2' : Relocatable Entry Address : boot routine address = offset + IMM ; * '7' : Checksum : stops the machine if (sum of all data received since start of record) + IMM != 0 ; * '9' : Absolute Load Address : memory address pointer = IMM ; * 'A' : Relocatable Load Address : memory address pointer = offset + IMM ; * 'B' : Absolute Data : * (memory address pointer ++) = IMM ; * 'C' : Relocatable Data : * (memory address pointer ++) = offset + IMM ; * 'D' : Load Bias Or Offset : offset = IMM ; * 'E' : (no reference) : stop the machine ; ; Control commands (parameter-less) : ; * ':' : (no reference) : branch to boot routine address ; * 'F' : End Of Record : wait for next card to be inserted ; ; Unsupported commands with 16-bit immediate (encoded in hexadecimal) : ; * '8' : Ignore Checksum Value : does nothing ; * 'J' : (no reference) ; Unsupported commands with 16-bit immediate (encoded in hexadecimal) and 6-char symbol name : ; * '3', '4', '5', '6', 'G', 'H' (symbol-related commands) ; Unsupported commands with 16-bit immediate (encoded in hexadecimal) and 8-char program name : ; * '0' : Program Start ; * 'I' : (no reference) ; FE86: 0360 RSET ; RESET external peripherals FE88: 0203 FFCA LI R3,>FFCA ; address of the start-up routine we jump to - defaults to error routine FE8C: 020B FFC8 LI R11,>FFC8 ; points routine which fetches data from the boot device FE90: 0205 F7FE LI R5,>F7FE ; last word before our small system memory area FE94: C555 MOV *R5,*R5 ; try to read and write current word FE96: 0645 DECT R5 ; previous word FE98: 18FD JOC >FE94 ; loop until we wrap around to >FFFE FE9A: 1D0A SBO >000A ; switch "Run" light on FE9C: 1F0E TB >000E ; this bit must tell a MDU is present FE9E: 1613 JNE >FEC6 ; boot from the MDU ? FEA0: 020C 0040 LI R12,>0040 FEA4: C000 MOV R0,R0 ; R0 was cleared on line >FE12 (!) - can be set with the programmer pannel to use the card reader FEA6: 1605 JNE >FEB2 ; skip if card reader FEA8: 04CC CLR R12 ; device address >000 : 733 ASR FEAA: 1D09 SBO >0009 ; set-up device FEAC: 1D0A SBO >000A FEAE: 3220 FFC0 LDCR @>FFC0,8 ; effectively writes >11 FEB2: C000 MOV R0,R0 FEB4: 1308 JEQ >FEC6 ; skip if ASR FEB6: 1F07 TB >0007 ; set up card reader at CRU address >020 FEB8: 13FE JEQ >FEB6 FEBA: 1F01 TB >0001 FEBC: 13FE JEQ >FEBA FEBE: 1E0F SBZ >000F FEC0: 1F07 TB >0007 FEC2: 16FE JNE >FEC0 FEC4: 1D0F SBO >000F FEC6: 04C7 CLR R7 ; clear checksum FEC8: 069B BL *R11 ; read command number in R10 from boot device (range 0-19) FECA: D0AA FFE4 MOVB @>FFE4(R10),R2 ; read routine offset in table FECE: 1327 JEQ >FF1E ; handle 'F' command separately because it is parameter-less FED0: 0882 SRA R2,8 ; convert to signed 16-bit offset FED2: C107 MOV R7,R4 ; save checksum FED4: 0201 0004 LI R1,>0004 ; 4*4 = 16 bits to read FED8: 069B BL *R11 ; read hex digit (4 bits) FEDA: 0A46 SLA R6,4 ; shift digits we have already read FEDC: A18A A R10,R6 ; and insert new digit in R6 FEDE: 0601 DEC R1 FEE0: 16FB JNE >FED8 ; loop until we have read a 16-bit hex value FEE2: 0462 FEE2 B @>FEE2(R2) ; jump to routine ; entry point for '3', '4', '5', '6','G','H' ; read 6 additional characters and ignore them FEE6: 0201 0006 LI R1,>0006 ; read 6 chars FEEA: 1015 JMP >FF16 ; entry point for 'A' ; add offset to R6, set address pointer FEEC: A189 A R9,R6 ; add offset to R6 (R9 set by 'D' command, defaults to >00A0) ; entry point for '9' ; set address pointer FEEE: C146 MOV R6,R5 ; set address pointer FEF0: 10EB JMP >FEC8 ; continue on ? ; entry point for 'C' ; add offset to R6, write two bytes FEF2: A189 A R9,R6 ; entry point for 'B' ; write two bytes FEF4: DD46 MOVB R6,*R5+ FEF6: 06C6 SWPB R6 FEF8: DD46 MOVB R6,*R5+ FEFA: 10E6 JMP >FEC8 ; entry point for '7' ; check checksum FEFC: A106 A R6,R4 FEFE: 13E4 JEQ >FEC8 ; continue on if value matches checksum ; entry point for 'E' ; boot failure FF00: 046B 0002 B @>0002(R11) ; jump to >FFCA (IDLE) ; entry point for 'D' ; set bias/offset FF04: C246 MOV R6,R9 ; set bias/offset value FF06: 0249 FFFE ANDI R9,>FFFE ; convert to word address FF0A: 10DE JMP >FEC8 ; entry point for '2' ; add offset to R6, set boot routine address FF0C: A189 A R9,R6 ; entry point for '1' ; set boot routine address FF0E: C0C6 MOV R6,R3 ; set boot routine address FF10: 10DB JMP >FEC8 ; entry point for '0','I' ; read 8 additionnal characters and ignore them FF12: 0201 0008 LI R1,>0008 ; read 8 chars FF16: 069B BL *R11 FF18: 0601 DEC R1 FF1A: 16FD JNE >FF16 FF1C: 10D5 JMP >FEC8 ; entry point for 'F' command (parameter-less) ; wait for next card to be inserted FF1E: 069B BL *R11 FF20: 16FE JNE >FF1E ; wait for next card FF22: 10C7 JMP >FEB2 ; the following routine seems to read a hex digit from either boot device FF24: C000 MOV R0,R0 ; test device type FF26: 1338 JEQ >FF98 ; card reader located at >020 ; ; Read Hollerith code, and translate it to ASCII code, then hexadecimal number. ; Seems to set EQ flag if the card is not inserted and R10 contains 0. ; The routine is obviously incomplete, but we do translate alphanumeric characters and ':'... FF28: 1F07 TB >0007 ; test whether card is inserted ? FF2A: 1634 JNE >FF94 FF2C: 1F0F TB >000F ; wait for next row of punch holes ? FF2E: 16FC JNE >FF28 FF30: 340A STCR R10,16 ; read code ; ; Encoding : ; ; We use Hollerith code. It uses 12 lines (0-9 and 11-12), plus one synchro line. ; ; Line map : ; MSB LSB ; CRU bit : 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 ; register bit : 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ; card line : ? 1 2 3 4 5 6 7 ? 12 11 0 9 8 ? synchro ; ; Hollerith character codes (source : Model 990 Computer Programming Card) : ; ; Space : Blank ; ! : 12-7-8 ; " : 7-8 ; # : 3-8 ; $ : 11-3-8 ; % : 0-4-8 ; & : 12 ; ' : 5-8 ; ( : 12-5-8 ; ) : 11-5-8 ; * : 11-4-8 ; + : 12-6-8 ; , : 0-3-8 ; - : 11 ; . : 12-3-8 ; / : 0-1 ; 0 : 0 ; 1 : 1 ; 2 : 2 ; 3 : 3 ; 4 : 4 ; 5 : 5 ; 6 : 6 ; 7 : 7 ; 8 : 8 ; 9 : 9 ; : : 2-8 ; ; : 11-6-8 ; < : 12-4-8 ; = : 6-8 ; > : 0-6-8 ; ? : 0-7-8 ; @ : 4-8 ; A/a : 12-1 ; B/b : 12-2 ; C/c : 12-3 ; D/d : 12-4 ; E/e : 12-5 ; F/f : 12-6 ; G/g : 12-7 ; H/h : 12-8 ; I/i : 12-9 ; J/j : 11-1 ; K/k : 11-2 ; L/l : 11-3 ; M/m : 11-4 ; N/n : 11-5 ; O/o : 11-6 ; P/p : 11-7 ; Q/q : 11-8 ; R/r : 11-9 ; S/s : 0-2 ; T/t : 0-3 ; U/u : 0-4 ; V/v : 0-5 ; W/w : 0-6 ; X/x : 0-7 ; Y/y : 0-8 ; Z/z : 0-9 ; [ : 12-2-8 ; ] : 11-2-8 ; ^ : 11-7-8 ; _ : 0-5-8 ; ; missing : ; \ : 0-2-8 FF32: 04C8 CLR R8 FF34: D20A MOVB R10,R8 ; copy MSB FF36: 092A SRL R10,2 FF38: 0ABA SLA R10,11 ; keep 5 bits (bits 9-13) in R10 FF3A: 058A INC R10 FF3C: 0A18 SLA R8,1 ; find last bit set to 1 in R8 and increment R10 accordingly FF3E: 16FD JNE >FF3A FF40: 060A DEC R10 ; fix R10 FF42: C20A MOV R10,R8 FF44: 06C8 SWPB R8 FF46: E288 SOC R8,R10 FF48: C20A MOV R10,R8 ; Current result : ; R10 MSB = R8 MSB = R10 LSB = R8 LSB ; bits 0-4 (most significant) : bits 9-13 from 16-bit code ; bits 5-7 (least significant) : number of the last bit set to 1 in the MSByte of the 16-bit code ; (0 if none) FF4A: 024A 000F ANDI R10,>000F ; so line 8 -> >8, and ':' (2-8) -> >A (!) FF4E: 022A 0030 AI R10,>0030 FF52: 0A18 SLA R8,1 FF54: 1702 JNC >FF5A FF56: 022A 0010 AI R10,>0010 ; line 12 : A -> ... FF5A: 0A18 SLA R8,1 FF5C: 1702 JNC >FF62 FF5E: 022A 0019 AI R10,>0019 ; line 11 : J -> ... FF62: 0A18 SLA R8,1 FF64: 1702 JNC >FF6A FF66: 022A 0023 AI R10,>0023 ; line 0 : 0 (number), and S -> ... FF6A: 0A18 SLA R8,1 FF6C: 1702 JNC >FF72 FF6E: 022A 0009 AI R10,>0009 ; line 9 : 9, etc. FF72: 028A 0030 CI R10,>0030 ; no hole -> space FF76: 1602 JNE >FF7C FF78: 020A 0020 LI R10,>0020 FF7C: 028A 0053 CI R10,>0053 ; 0 FF80: 1602 JNE >FF86 FF82: 020A 0030 LI R10,>0030 FF86: 028A 0055 CI R10,>0055 FF8A: 1101 JLT >FF8E FF8C: 064A DECT R10 FF8E: 1F0F TB >000F FF90: 13FE JEQ >FF8E ; wait for device ready ? FF92: 1010 JMP >FFB4 ; goto character decode routine ; we jump there if no card is inserted (?) when reading from the card reader located at >020 FF94: 828A C R10,R10 FF96: 1017 JMP >FFC6 ; read routine ; Read ASCII character from 733 ASR located at >000 or MDU unit located at >FF0, and translate it to ; one hexadecimal number. FF98: 1F0C TB >000C ; wait for incoming character ? FF9A: 16FE JNE >FF98 FF9C: 1E0C SBZ >000C ; clear FF9E: 35CA STCR R10,7 ; read ASCII character FFA0: 098A SRL R10,8 FFA2: 028A 000D CI R10,>000D FFA6: 130F JEQ >FFC6 ; exit if CR FFA8: 028A 005A CI R10,>005A ; if higher than 'Z' FFAC: 15F5 JGT >FF98 FFAE: 028A 0020 CI R10,>0020 ; or lower than ' ' FFB2: 11F2 JLT >FF98 ; try again FFB4: A1CA A R10,R7 ; add R10 to checksum in R7 (?) FFB6: 022A FFD0 AI R10,>FFD0 ; substract '0' FFBA: 028A 000A CI R10,>000A ; if character is ':' FFBE: 1306 JEQ >FFCC ; jump to boot routine FFC0: 1102 JLT >FFC6 FFC2: 022A FFF9 AI R10,>FFF9 ; substract 7 for 'A'-'F' to give hexadecimal digit FFC6: 069B BL *R11 ; return and restore R11 to point to >FFC8 (!) ; entry point for the routine which reads data from the boot device FFC8: 10AD JMP >FF24 FFCA: 0340 IDLE ; stop the machine ; entry point for the ':' command (parameter-less command) ; jump to external boot routine FFCC: C000 MOV R0,R0 FFCE: 1608 JNE >FFE0 FFD0: 1E0B SBZ >000B ; clean up the ASR status ? FFD2: 1E0C SBZ >000C FFD4: 0581 INC R1 FFD6: 16FE JNE >FFD4 FFD8: 1F0C TB >000C FFDA: 13F8 JEQ >FFCC FFDC: 1E09 SBZ >0009 FFDE: 1E0D SBZ >000D FFE0: 0360 RSET ; reset peripherals FFE2: 0453 B *R3 ; jumps to boot routine specified by >1 or >2 command ; (jumps to >FFCA if no routine was specified) ; offset table for jumps in object code interpreter, used on >FEE2 (offsets relative to >FEE2) FFE4: 302C 2A04 BYTE >FF12->FEE2, >FF0E->FEE2, >FF0C->FEE2, >FEE6->FEE2 FFE8: 0404 041A BYTE >FEE6->FEE2, >FEE6->FEE2, >FEE6->FEE2, >FEFC->FEE2 FFEC: E60C 0A12 BYTE >FEC8->FEE2, >FEEE->FEE2, >FEEC->FEE2, >FEF4->FEE2 FFF0: 1022 1E00 BYTE >FEF2->FEE2, >FF04->FEE2, >FF00->FEE2, >00 FFF4: 0404 30E6 BYTE >FEE6->FEE2, >FEE6->FEE2, >FF12->FEE2, >FEC8->FEE2 ; pointers to routines FFF8: FE86 DATA >FE86 ; boot loader FFFA: FEC6 DATA >FEC6 ; boot loader for MDU (?) ; LOAD vector FFFC: F800 FE00 DATA >F800,>FE00