; 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
