; TI990/10 LOAD ROMs disassembly ; ;General background : ; These ROMs contain the handler for the LOAD/RESTART interrupt. This interrupt is called at ; start-up (instead of RESET, which is used for "warm reset", I think), so that the boot loader in ; ROMs can be executed. ; ; This interrupt can also be triggered by the programmer panel, and there is code to handle the ; panel functions. ; ;Description : ; We have 1kb of ROMs (4 256*8 ROMs). ; ; If you want a starting point, you can have a look at the LOAD vector located at >FFFC. ; The LOAD routine starts at >FC00. ; ; The ROMs are obviously an instance of the "universal ROM loader" as described in the ; Model 990 Universal ROM Loader User's Guide. As such, the boot loader is identical to the TI990/12 ; loader. You should read the Universal ROM Loader User's Guide before reading the disassembly. ; ; The ROM basically do the following : ; * handle programmer panel functions if applicable ; * perform minimal self-test (compute ROM checksum) ; * look for a MDU, and boot from it if present ; * if programmer panel disabled, boot from the first tape/disk unit it finds (using a MT3200/WD900 ; controller or equivalent) ; * if programmer panel enabled, boot from the unit designated in RO/R1/R2 (either ASR tape, ; FD800 single-sided disk, or tape/disk based on a MT3200/WD900 controller or equivalent) ; ;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 : initial release ; LOAD vector entry point FC00: 020C 1FE0 LI R12,>1FE0 ; programmer panel CRU base FC04: 0221 6700 AI R1,>6700 ; look for magical value (>9900) FC08: 1602 JNE >FC0E ; continue with normal vector if not found FC0A: 1D0A SBO >000A ; switch "Run" light on FC0C: 0450 B *R0 ; branch to routine if found FC0E: 0209 00A0 LI R9,>00A0 ; R9 points to free area in RAM ? FC12: 0700 SETO R0 ; R0 < 0 : use TILINE unit (tape, FD1000, hard disk) by default FC14: 0201 F800 LI R1,>F800 ; TILINE address for default disk unit FC18: 0202 0800 LI R2,>0800 ; if programmer panel enabled, boot from drive unit 0 by default FC1C: 1F0B TB >000B FC1E: 133B JEQ >FC96 ; if programmer panel disabled, jump to self-test FC20: C10E MOV R14,R4 ; load old PC to display it ; programmer panel scan : we have 4 rows of 8 switches ; 16 switches allow to toggle the display in R4. ; the 16 other switches allow to access programmer panel functions. ; ; register conventions here : ; R4 = data register ; R7 = memory address register ; R13 = WP ; R14 = PC ; R15 = ST ; keyscan loop start FC22: 3204 LDCR R4,8 ; display data register MSB FC24: 3220 0089 LDCR @>0089,8 ; display data register LSB FC28: 0205 FC8C LI R5,>FC8C ; read current 8 switches from programmer panel to R3 MSB FC2C: 0695 BL *R5 ; wait for all switches to be released FC2E: 16FE JNE >FC2C FC30: 0695 BL *R5 ; test again to fix switch bounce problems FC32: 16FC JNE >FC2C FC34: 0695 BL *R5 ; again FC36: 16FA JNE >FC2C FC38: 04C3 CLR R3 FC3A: 1D08 SBO >0008 ; "Increment scan" bit -> select another row of switches FC3C: 0695 BL *R5 FC3E: 13FD JEQ >FC3A ; if no switch pressed, scan next row FC40: 0695 BL *R5 FC42: 13FB JEQ >FC3A ; fixes switch bounce problems FC44: 0695 BL *R5 FC46: 13F9 JEQ >FC3A ; again FC48: 1F09 TB >0009 ; scan count bit 0 -> is current switch row number odd or even ? FC4A: 1601 JNE >FC4E FC4C: 06C3 SWPB R3 ; if odd, we read last 8 switches in 16 switches, so we rotate 8 bits FC4E: 1F08 TB >0008 ; scan count bit 1 -> is this command switches or data switches ? FC50: 1302 JEQ >FC56 ; command switches -> jump to interpret command FC52: 2903 XOR R3,R4 ; data switches -> toggle the data on display as requested FC54: 10E6 JMP >FC22 ; update display and go back to scan routine FC56: 04C5 CLR R5 FC58: 05C5 INCT R5 FC5A: 0A13 SLA R3,1 FC5C: 17FD JNC >FC58 ; count first bit set to 1 in R3, i.e. first switch pressed FC5E: 04A5 FC62 X @>FC62(R5) ; execute command as needed FC62: 10DF JMP >FC22 ; jump to display value if the X instruction did not jump ; Programmer panel instruction table. ; All jump offsets must be relative to FC62 since this is the value of PC when the instruction ; is executed. FC64: 1013 JMP *+>FC88->FC62 ; "Halt/SIE" switch FC66: 1011 JMP *+>FC84->FC62 ; "Run" switch FC68: 0360 RSET ; "Reset" switch FC6A: 101A JMP *+>FC96->FC62 ; "Load" switch FC6C: C10D MOV R13,R4 ; "Display WP" switch FC6E: C10E MOV R14,R4 ; "Display PC" switch FC70: C10F MOV R15,R4 ; "Display ST" switch FC72: C107 MOV R7,R4 ; "Display MA" switch FC74: C344 MOV R4,R13 ; "Enter WP" switch FC76: C384 MOV R4,R14 ; "Enter PC" switch FC78: C3C4 MOV R4,R15 ; "Enter ST" switch FC7A: C1C4 MOV R4,R7 ; "Enter MA" switch FC7C: C117 MOV *R7,R4 ; "MDD" switch ("Memory Data Display") FC7E: 05C7 INCT R7 ; "MAI" switch ("Memory Address Increment") FC80: C5C4 MOV R4,*R7 ; "MDE" switch ("Memory Data Enter") FC82: 04C4 CLR R4 ; "CLR" switch ; Code for run switch FC84: 1D0A SBO >000A ; switch "Run" light on FC86: 0380 RTWP ; return to interrupt point ; code for "Halt/SIE" switch FC88: 1D0E SBO >000E ; set "Single Instruction Execute" FC8A: 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 FC8C: 1D0D SBO >000D ; start panel timer FC8E: 1F0A TB >000A ; wait for timer active bit to be set FC90: 16FE JNE >FC8E FC92: 3603 STCR R3,8 ; retrieve value FC94: 045B B *R11 ; Self-test routine ; compute ROM checksum for address range >FC00->FFF3, and compare it with value in >FFF4. FC96: 020B AAAA LI R11,>AAAA FC9A: 0205 FC00 LI R5,>FC00 FC9E: 2AF5 XOR *R5+,R11 FCA0: 0B1B SRC R11,1 FCA2: 0285 FFF4 CI R5,>FFF4 FCA6: 16FB JNE >FC9E FCA8: 854B C R11,*R5 ; compare with checksum at >FFF4 FCAA: 130E JEQ >FCC8 ; jump to boot if OK FCAC: 1D0A SBO >000A FCAE: 0703 SETO R3 FCB0: 0460 FFA4 B @>FFA4 ; stop in error ; Real boot routine, called after the CPU self-test ; ; We can load from several devices : MDU (Maintenance Diagnostics Unit, with a tape reader), ; ASR (a data terminal with optionnal tape reader), a single-sided disk (dumb controller on CRU bus), ; a tape unit with MT3200 controller, a double-sided floppy disk or a hard disk with WD900 controller ; (intelligent controllers on TI-LINE memory bus). ; ; tape units (MDU, ASR and MT3200) use some object code encoded with alphanumeric and hexadecimal ; characters. ; Single-sided floppy uses a simple boot routine to fetch a binary start-up program. ; The routine for double-sided floppies and hard-disk is somewhat more complex. ; offset table for jumps in object code interpreter, used on >FD42 (offsets relative to >FD42) FCB4: 302C 2A04 BYTE >FD72->FD42,>FD6E->FD42,>FD6C->FD42,>FD46->FD42 FCB8: 0404 041A BYTE >FD46->FD42,>FD46->FD42,>FD46->FD42,>FD5C->FD42 FCBC: E60C 0A12 BYTE >FD28->FD42,>FD4E->FD42,>FD4C->FD42,>FD54->FD42 FCC0: 1022 1E00 BYTE >FD52->FD42,>FD64->FD42,>FD60->FD42,>00 FCC4: 0404 30E6 BYTE >FD46->FD42,>FD46->FD42,>FD72->FD42,>FD28->FD42 ; actual boot routine FCC8: 0360 RSET ; RESET external peripherals FCCA: 0203 FE94 LI R3,>FE94 ; address of the start-up routine we jump to - defaults to error routine FCCE: 0205 F7FE LI R5,>F7FE ; last word before system memory space FCD2: C381 MOV R1,R14 ; if programmer panel enabled, TILINE peripheral to boot from FCD4: C3C2 MOV R2,R15 ; if programmer panel enabled, unit to load from ; simple memory test >F7FE -> >0000 FCD6: C555 MOV *R5,*R5 ; try to read and write current word FCD8: 0645 DECT R5 FCDA: 18FD JOC >FCD6 ; loop until we wrap around to >FFFE ; now real boot FCDC: 1D0A SBO >000A ; switch "Run" light on FCDE: 1F0E TB >000E ; this bit must tell a MDU is present FCE0: 1302 JEQ >FCE6 ; jump if not present FCE2: 04C0 CLR R0 FCE4: 101D JMP >FD20 ; boot from the MDU ? FCE6: C000 MOV R0,R0 ; read boot device type (defaults to -1) FCE8: 114E JLT >FD86 ; boot from TILINE tape or disk device (FD1000, hard disk...) FCEA: 154C JGT >FD84 ; boot from FD800 single-sided floppy disk ; if (R0 == 0), boot from ASR terminal FCEC: 04CC CLR R12 ; device address >000 : 733 ASR FCEE: 1D09 SBO >0009 ; set-up device FCF0: 1D0A SBO >000A FCF2: 3220 FE8A LDCR @>FE8A,8 ; effectively writes >11 ; entry point to boot from TILINE tape unit (MT3200 controller or equivalent) FCF6: 04CD CLR R13 FCF8: C000 MOV R0,R0 FCFA: 1312 JEQ >FD20 ; skip if booting from ASR FCFC: C183 MOV R3,R6 ; save R3, R5 FCFE: C285 MOV R5,R10 FD00: 04C0 CLR R0 ; meaningless FD02: 04C1 CLR R1 ; meaningless FD04: 04C2 CLR R2 ; read offset = 0 (read record from the beginning) FD06: 0203 0050 LI R3,>0050 ; char count = >0050 (max record lenght = 80 chars) ; It seems that we cannot have more than 80 bytes of code ! FD0A: 04C4 CLR R4 ; load at address 0 FD0C: 0205 0400 LI R5,>0400 ; >4 : read binary forward command FD10: E14F SOC R15,R5 ; set unit ID FD12: 06A0 FF54 BL @>FF54 ; execute command on tape unit FD16: D920 FE6F 0050 MOVB @>FE6F,@>0050(R4) ; append carriage return after the data we read (@>FE6F = >0D) FD1C: C0C6 MOV R6,R3 ; restore R3, R5 FD1E: C14A MOV R10,R5 ; entry point to boot from MDU FD20: 020B FE92 LI R11,>FE92 ; pointer to read routine FD24: 04C7 CLR R7 ; clear checksum FD26: 058D INC R13 ; object code interpreter ; ; ASR, MDU and MT3200 tapes use 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 (does not really work) ; ; 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) ; FD28: 069B BL *R11 ; read command number in R10 from tape (range 0-19) FD2A: D0AA FCB4 MOVB @>FCB4(R10),R2 ; read offset to routine in table FD2E: 1327 JEQ >FD7E ; handle 'F' command separately because it is parameter-less FD30: 0882 SRA R2,8 ; convert to signed 16-bit offset FD32: C207 MOV R7,R8 ; save checksum FD34: 0201 0004 LI R1,>0004 ; 4*4 = 16 bits to read FD38: 069B BL *R11 ; read hex digit (4 bits) FD3A: 0A46 SLA R6,4 ; shift digits we have already read FD3C: A18A A R10,R6 ; and insert new digit in R6 FD3E: 0601 DEC R1 FD40: 16FB JNE >FD38 ; loop until we have read a 16-bit hex value FD42: 0462 FD42 B @>FD42(R2) ; jump to routine ; entry point for '3', '4', '5', '6','G','H' ; read 6 additional characters and ignore them FD46: 0201 0006 LI R1,>0006 ; read 6 chars FD4A: 1015 JMP >FD76 ; entry point for 'A' ; add offset to R6, set address pointer FD4C: A189 A R9,R6 ; add offset to R6 (R9 set by 'D' command, defaults to >00A0) ; entry point for '9' ; set address pointer FD4E: C146 MOV R6,R5 FD50: 10EB JMP >FD28 ; next command ; entry point for 'C' ; add offset to R6, write two bytes FD52: A189 A R9,R6 FD54: DD46 MOVB R6,*R5+ ; entry point for 'B' ; write two bytes FD56: 06C6 SWPB R6 FD58: DD46 MOVB R6,*R5+ FD5A: 10E6 JMP >FD28 ; entry point for '7' ; check checksum FD5C: A206 A R6,R8 FD5E: 13E4 JEQ >FD28 ; continue on if value matches checksum ; entry point for 'E' ; boot failure FD60: 046B 0002 B @>0002(R11) ; jump to >FE94 (stop machine and blink fail LED) ; entry point for 'D' ; set bias/offset FD64: C246 MOV R6,R9 ; set bias/offset value FD66: 0249 FFFE ANDI R9,>FFFE ; convert to word address FD6A: 10DE JMP >FD28 ; entry point for '2' ; add offset to R6, set boot routine address FD6C: A189 A R9,R6 ; entry point for '1' ; set boot routine address FD6E: C0C6 MOV R6,R3 ; set boot routine address FD70: 10DB JMP >FD28 ; entry point for '0','I' ; read 8 additionnal characters and ignore them FD72: 0201 0008 LI R1,>0008 ; read 8 chars FD76: 069B BL *R11 FD78: 0601 DEC R1 FD7A: 16FD JNE >FD76 FD7C: 10D5 JMP >FD28 ; entry point for 'F' command (parameter-less) ; wait for next record FD7E: 069B BL *R11 FD80: 16FE JNE >FD7E ; wait for next record FD82: 10BA JMP >FCF8 ; branch point to routine at >FDEE FD84: 1034 JMP >FDEE ; Boot from a tape/disk unit using MD900/MT3200 controller or equivalent on the TILINE bus ; ; When programmer panel is available, the routine expect the unit mask to be in R15. ; ; When programmer panel is unavailable, the routine scans tape units 0-3, then disk units 0-3. ; It generally boots on the first unit which is ready ; however, when possible, it does not boot from ; a writable disk. The resulting priority order is : tape #0, ..., tape #3, ; disk #0 (write-protected), ..., disk #3 (write-protected), ; disk #0 (writable), ..., disk #3 (writable). When no unit is ready, it keeps scanning ; all units continuously, so that the user can insert a boot disk/tape after start-up. ; ; R1 = >F800 (?) ; R2 = >0800 (?) FD86: 04CD CLR R13 FD88: 1F0B TB >000B ; programmer panel... FD8A: 1306 JEQ >FD98 ; if disabled, try each unit until we find a bootable one FD8C: C10F MOV R15,R4 ; current unit mask : 1 (and only 1) bit must be set FD8E: 0A44 SLA R4,4 FD90: 13B2 JEQ >FCF6 ; routine for tape unit FD92: 102C JMP >FDEC ; routine for disk unit ; we end here when no tape or writable disk could be found. ; R13 may contain the ID of some write-protected disk. FD94: C3CD MOV R13,R15 ; restore unit ID from R13 FD96: 162A JNE >FDEC ; jump to boot routine if non-zero ; else, we start over, until someone insert a disk/tape ; Find a bootable tape/disk on the TILINE bus FD98: 020C 1FC0 LI R12,>1FC0 ; Error interrupt register CRU base FD9C: 020F 0001 LI R15,>0001 FDA0: 020E F880 LI R14,>F880 ; TILINE base address for MT3200 tape controller FDA4: 1E0F SBZ >000F ; clear TIMEOUT (i.e. unimplemented memory) FDA6: 0B1F SRC R15,1 ; try next unit (try >8000 first, then >4000, etc) FDA8: 808F C R15,R2 ; compare with >0800 (support for up to 4 tape units) FDAA: 1B03 JH >FDB2 ; jump if greater than >0800 FDAC: C381 MOV R1,R14 ; else use WD900 disk unit at base address >F800 FDAE: D3CF MOVB R15,R15 ; if R15 MSByte is NULL, we are done FDB0: 13F1 JEQ >FD94 ; jump to boot from writable disk, or retry FDB2: C21E MOV *R14,R8 ; load disc status FDB4: 1F0F TB >000F ; test RAM TIMEOUT FDB6: 13F6 JEQ >FDA4 ; try next unit if no controller found on bus FDB8: C22E 000E MOV @>000E(R14),R8 ; load controller status FDBC: 1101 JLT >FDC0 ; wait for IDLE status bit to be cleared FDBE: 10FC JMP >FDB8 FDC0: 04DE CLR *R14 ; clear disc status FDC2: CB8F 000C MOV R15,@>000C(R14) ; select unit FDC6: 808F C R15,R2 FDC8: 1204 JLE >FDD2 ; skip 2 instructions if using disk unit FDCA: EB82 000C SOC R2,@>000C(R14) ; bits 4-7 = >8 : read transport status command FDCE: 04EE 000E CLR @>000E(R14) ; clear controller status FDD2: 0206 5E00 LI R6,>5E00 FDD6: 0606 DEC R6 FDD8: 16FE JNE >FDD6 ; wait for about 10 or 20ms FDDA: C21E MOV *R14,R8 ; read disk or tape transport status FDDC: 11E3 JLT >FDA4 ; try next unit if off-line status bit set (no unit or no tape/disk) FDDE: 808F C R15,R2 FDE0: 1B8A JH >FCF6 ; if this is a tape unit, jump to object code interpreter FDE2: C34D MOV R13,R13 FDE4: 1601 JNE >FDE8 FDE6: C34F MOV R15,R13 ; save R15 in R13 if it is the first bootable unit found FDE8: 0A38 SLA R8,3 FDEA: 17DC JNC >FDA4 ; try next unit if the disk is not write-protected (?) FDEC: 1061 JMP >FEB0 ; jump to disk boot routine if the disk is write-protected (?) ; Complete boot routine to boot from a single-sided diskette ; Boots from FD800 unit at CRU address >80->A0 (i.e. >40->50) FDEE: 020C 00A0 LI R12,>00A0 ; CRU base address for command register ? FDF2: 0702 SETO R2 ; R2 = >FFFF FDF4: 04C3 CLR R3 FDF6: 3408 STCR R8,16 ; read 16 bits FDF8: 1502 JGT >FDFE FDFA: 0A68 SLA R8,6 ; if negative, test CRU bit >A5 FDFC: 1814 JOC >FE26 ; if bit set, start over FDFE: 1F0A TB >000A FE00: 13FE JEQ >FDFE ; wait for something FE02: 3002 LDCR R2,16 ; write all 1s FE04: 0208 E000 LI R8,>E000 FE08: E203 SOC R3,R8 FE0A: 1F0A TB >000A FE0C: 13FE JEQ >FE0A ; wait again FE0E: 3008 LDCR R8,16 ; write track number ??? FE10: 1F0F TB >000F FE12: 16FE JNE >FE10 FE14: 3408 STCR R8,16 ; read 16 bits FE16: 0248 1BFC ANDI R8,>1BFC FE1A: 1306 JEQ >FE28 ; start boot FE1C: 0223 0400 AI R3,>0400 FE20: 0283 1000 CI R3,>1000 FE24: 1AEE JL >FE02 FE26: 10E3 JMP >FDEE ; start over FE28: 020C 0080 LI R12,>0080 ; CRU base address for data register ? FE2C: C289 MOV R9,R10 ; R9 points to free area in RAM ? FE2E: 0201 0080 LI R1,>0080 ; 256 bytes (= 128 words) in a data cluster ? FE32: 1F11 TB >0011 ; data present bit (?) FE34: 1306 JEQ >FE42 FE36: 1F1A TB >001A ; device error bit (?) FE38: 13FC JEQ >FE32 FE3A: 020C 00A0 LI R12,>00A0 FE3E: 3403 STCR R3,16 FE40: 102A JMP >FE96 ; stop the machine in error FE42: 343A STCR *R10+,16 ; read 16 bits FE44: 1E0F SBZ >000F FE46: 0641 DECT R1 FE48: 15F4 JGT >FE32 ; loop until read 256 bytes FE4A: 064A DECT R10 ; point to last word FE4C: 809A C *R10,R2 ; compare last word with >FFFF mark FE4E: 16EF JNE >FE2E ; if different, keep 254 previous bytes and append another sector (?) FE50: 0360 RSET ; else, jump to boot routine FE52: 0200 FEC8 LI R0,>FEC8 FE56: CE43 MOV R3,*R9+ ; write device number FE58: 0459 B *R9 ; execute boot routine at offset 2 ; read routine ; Read ASCII character from 733 ASR located at >000, a MDU unit located at >FF0, or a MT3200 tape unit ; on TILINE bus, and translate it to one hexadecimal number. FE5A: C000 MOV R0,R0 ; test device type flag FE5C: 1105 JLT >FE68 ; jump if MT3200 FE5E: 1F0C TB >000C FE60: 16FE JNE >FE5E ; wait... FE62: 1E0C SBZ >000C FE64: 35CA STCR R10,7 ; read 7 bits FE66: 1001 JMP >FE6A FE68: D2B4 MOVB *R4+,R10 ; read byte from memory (MT3200 is DMA driven) FE6A: 098A SRL R10,8 ; move MSB to LSB FE6C: 028A 000D CI R10,>000D ; carriage return ? (appended at end of MT3200 record) FE70: 130F JEQ >FE90 ; if so return FE72: 028A 005A CI R10,>005A ; greater than 'Z' ? FE76: 15F1 JGT >FE5A ; if so, ignore current char and read next char FE78: 028A 0020 CI R10,>0020 ; control character (lower than ' ') ? FE7C: 11EE JLT >FE5A ; if so, ignore current char and read next char FE7E: A1CA A R10,R7 ; update checksum FE80: 022A FFD0 AI R10,>FFD0 ; substract >30 (numeric character -> numeric value) FE84: 028A 000A CI R10,>000A FE88: 1307 JEQ >FE98 ; jump to handler if it is ':' FE8A: 1102 JLT >FE90 ; if greater, it must be 'A'-'F' FE8C: 022A FFF9 AI R10,>FFF9 ; convert to hex value FE90: 069B BL *R11 ; trick : return to the caller AND restore R11 to be >FE92 ; read routine entry point FE92: 10E3 JMP >FE5A ; entry point for bad ':' (called when no '1' command has been executed before ':') FE94: C0CD MOV R13,R3 FE96: 1057 JMP >FF46 ; handler for ':' command FE98: C000 MOV R0,R0 ; test boot device type flag FE9A: 1608 JNE >FEAC FE9C: 1E0B SBZ >000B ; clean-up ? FE9E: 1E0C SBZ >000C FEA0: 0581 INC R1 FEA2: 16FE JNE >FEA0 FEA4: 1F0C TB >000C FEA6: 13F8 JEQ >FE98 FEA8: 1E09 SBZ >0009 FEAA: 1E0D SBZ >000D FEAC: 0360 RSET ; reset all peripherals FEAE: 0453 B *R3 ; jump to boot routine ; boot from a disk unit attached to a WD900 disk controller (or equivalent) whose address is in R14 FEB0: C14F MOV R15,R5 ; unit ID FEB2: 020C FF54 LI R12,>FF54 FEB6: 020D 0005 LI R13,>0005 ; retry count ? FEBA: 0200 0700 LI R0,>0700 ; restore command FEBE: 04C1 CLR R1 FEC0: 069C BL *R12 FEC2: 04C0 CLR R0 ; store register command FEC4: 0203 0006 LI R3,>0006 ; command returns 6 bytes FEC8: 0204 0200 LI R4,>0200 ; free area in RAM FECC: 069C BL *R12 FECE: C2A4 0004 MOV @>0004(R4),R10 FED2: 09BA SRL R10,11 ; keep number of track per cylinder (i.e. number of heads) FED4: 0200 0400 LI R0,>0400 ; read unformatted FED8: 04C2 CLR R2 ; cylinder 0 FEDA: 069C BL *R12 ; read current track/cylinder address, # of sectors per record, and sector lenght FEDC: C004 MOV R4,R0 ; >0200 : read data FEDE: D064 0002 MOVB @>0002(R4),R1 ; set the "sectors per records" field FEE2: C0E4 0004 MOV @>0004(R4),R3 ; count = sector lenght FEE6: 0A13 SLA R3,1 ; word count -> byte count FEE8: 069C BL *R12 ; read record #0 FEEA: C3E4 000E MOV @>000E(R4),R15 ; alternate boot program track address FEEE: 890F 0024 C R15,@>0024(R4) ; compare with normal program track address FEF2: 130C JEQ >FF0C FEF4: C924 0024 000E MOV @>0024(R4),@>000E(R4) ; replace alternate boot program, with normal one ; so alternate program will be loaded only once FEFA: 0200 0300 LI R0,>0300 ; write data FEFE: 069C BL *R12 ; write record #0 FF00: C924 001C 0018 MOV @>001C(R4),@>0018(R4) ; use alternate load point FF06: C924 001E 001A MOV @>001E(R4),@>001A(R4) ; and alternate length FF0C: 0200 0400 LI R0,>0400 ; read unformatted FF10: 0203 0006 LI R3,>0006 ; command returns 6 bytes FF14: 06A0 FF48 BL @>FF48 ; compute cylinder/head address from track address in R15 FF18: 069C BL *R12 FF1A: C064 0002 MOV @>0002(R4),R1 ; set format & sector according to value read from controller FF1E: C004 MOV R4,R0 ; >0200 : read data FF20: 06A0 FF48 BL @>FF48 FF24: C3E4 0018 MOV @>0018(R4),R15 ; program load point FF28: C0E4 001A MOV @>001A(R4),R3 ; program length FF2C: 130A JEQ >FF42 ; fail if null FF2E: 0204 00A0 LI R4,>00A0 ; free area in RAM ? FF32: 069C BL *R12 ; read boot program FF34: 0200 ABC0 LI R0,>ABC0 ; magical value ? FF38: C04E MOV R14,R1 ; boot device address FF3A: 04C2 CLR R2 FF3C: D085 MOVB R5,R2 ; unit ID FF3E: 0360 RSET ; Reset for things to be cleaner FF40: 045F B *R15 ; jump to boot program ; exit point when boot program lenght is NULL FF42: 0203 D001 LI R3,>D001 ; load error code FF46: 102E JMP >FFA4 ; stop machine and blink the Fail LED ; compute cylinder & head address from "flat" track address ; input : ; R10 = number of heads ; R15 = track address ; returns : ; R2 = cylinder address ; R0 |= head address FF48: C24F MOV R15,R9 FF4A: 04C8 CLR R8 FF4C: 3E0A DIV R10,R8 ; compute R15/R10 FF4E: C088 MOV R8,R2 ; R2 = quotient : cylinder address FF50: E009 SOC R9,R0 ; R0 LSB = remainder : head address FF52: 045B B *R11 ; execute some command on a WD900/WT3200 controller ; R0 -> W1 : command and surface register (WD900) ; R1 -> W2 : format and sector (MSByte : sectors per record ; LSByte : sector number) (WD900) ; R2 -> W3 : cylinder address (cylinder number) (WD900) / read offset (WT3200) ; R3 -> W4 : count (length to read/write) ; R4 -> W5 : address (destination/source address to read/write from, 16 LSBits) ; R5 -> W6 : WD900 : select and address (unit selection, 1 bit per unit, in LOW nibble of MSByte, and ; 4 MSBits of address in low nibble of LSByte) ; WT3200 : command and transport select (unit selection, 1 bit per unit, in HIGH nibble ; of MSByte, command in high nibble of LSByte, and 4 MSBits of address in low nibble of ; LSByte) ; ; R14 = controller address ; ; cf 990 Handbook, 5-23, page 188, and WD900/MT3200 general description, chapter 3 ; FF54: C1CE MOV R14,R7 ; TILINE device address FF56: C227 000E MOV @>000E(R7),R8 FF5A: 1101 JLT >FF5E FF5C: 10FB JMP >FF54 ; wait for IDLE controller status bit to be cleared ; write device registers FF5E: 04F7 CLR *R7+ ; disc status FF60: CDC0 MOV R0,*R7+ FF62: CDC1 MOV R1,*R7+ FF64: CDC2 MOV R2,*R7+ FF66: CDC3 MOV R3,*R7+ FF68: CDC4 MOV R4,*R7+ FF6A: CDC5 MOV R5,*R7+ FF6C: 04D7 CLR *R7 FF6E: C217 MOV *R7,R8 FF70: 1101 JLT >FF74 FF72: 10FD JMP >FF6E ; wait for IDLE controller status bit to be cleared FF74: C1C5 MOV R5,R7 FF76: 09C7 SRL R7,12 ; 4 MSBits select unit on WT3200, and are unused on WD900 FF78: 1603 JNE >FF80 ; skip if we are using a tape unit FF7A: C1DE MOV *R14,R7 ; disk status FF7C: 0A27 SLA R7,2 FF7E: 18FD JOC >FF7A ; wait for NR (Not Ready) status bit to be cleared FF80: 0248 01FF ANDI R8,>01FF ; test all individual error bits in controller status FF84: 1602 JNE >FF8A ; jump on error FF86: 0700 SETO R0 FF88: 045B B *R11 ; normal return ; error handler : FF8A: C1DE MOV *R14,R7 ; read disk status FF8C: 11E3 JLT >FF54 ; wait for OL (off-line) status bit to be set FF8E: C1C5 MOV R5,R7 FF90: 09C7 SRL R7,12 FF92: 1602 JNE >FF98 ; skip if we are using a tape unit FF94: 060D DEC R13 FF96: 1691 JNE >FEBA ; do a few retries before fail ? FF98: 0288 0001 CI R8,>0001 ; check unit error / tape error in controller status FF9C: 1602 JNE >FFA2 ; means an error condition is set in disk status / tape transport status FF9E: 04C8 CLR R8 FFA0: D21E MOVB *R14,R8 ; read error code in disk status / tape transport status register FFA2: C0C8 MOV R8,R3 ; display either controller status or disk status / tape transport status ; error stop ; we jump to this routine when boot fails ; display R3 for diagnostics purpose, and blink the fail LED. FFA4: 020C 1FE0 LI R12,>1FE0 FFA8: 3203 LDCR R3,8 FFAA: 3220 0087 LDCR @>0087,8 FFAE: 1D0B SBO >000B FFB0: 05C1 INCT R1 FFB2: 15FD JGT >FFAE FFB4: 1E0B SBZ >000B FFB6: 10FC JMP >FFB0 ; spare room FFB8: 0000 DATA >0000 FFBA: 0000 DATA >0000 FFBC: 0000 DATA >0000 FFBE: 0000 DATA >0000 FFC0: 0000 DATA >0000 FFC2: 0000 DATA >0000 FFC4: 0000 DATA >0000 FFC6: 0000 DATA >0000 FFC8: 0000 DATA >0000 FFCA: 0000 DATA >0000 FFCC: 0000 DATA >0000 FFCE: 0000 DATA >0000 FFD0: 0000 DATA >0000 FFD2: 0000 DATA >0000 FFD4: 0000 DATA >0000 FFD6: 0000 DATA >0000 FFD8: 0000 DATA >0000 FFDA: 0000 DATA >0000 FFDC: 0000 DATA >0000 FFDE: 0000 DATA >0000 FFE0: 0000 DATA >0000 FFE2: 0000 DATA >0000 FFE4: 0000 DATA >0000 FFE6: 0000 DATA >0000 FFE8: 0000 DATA >0000 FFEA: 0000 DATA >0000 FFEC: 0000 DATA >0000 FFEE: 0000 DATA >0000 FFF0: 0000 DATA >0000 FFF2: 0000 DATA >0000 ; checksum FFF4: DDF1 DATA >DDF1 ; pointers to routines FFF6: FCCE DATA >FCCE ; boot loader FFF8: FCC8 DATA >FCC8 ; ditto FFFA: FD24 DATA >FD24 ; boot loader for MDU (?) ; LOAD vector FFFC: 0080 FC00 DATA >0080,>FC00