; 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 basically do the following :
; * handle programmer panel functions if applicable
; * look for a MDU, and boot from it if present
; * if programmer panel enabled, boot from the unit designated in RO/R1/R2
(either ASR tape,
;  Card Reader, or tape/disk based on a MT3200/WD900 controller or equivalent)
;
; by default, it boots from disk unit 0, using controller base address >F800.
;
;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/30
;
; revision history :
; 2000/04/30 : 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 controller
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 boot routine

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
FC2C: 0695           BL   *R5			; read current 8 switches
from programmer panel to R3 MSB
FC2E: 16FE           JNE  >FC2C			; wait for all switches to
be released
FC30: 0695           BL   *R5
FC32: 16FC           JNE  >FC2C			; test again to fix switch
bounce problems
FC34: 0695           BL   *R5
FC36: 16FA           JNE  >FC2C			; again

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 displayed 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 X
; executes the instruction in the table.
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 reads 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


; Real boot routine
;
; We can load from several devices : MDU (Maintenance Diagnostics Unit,
with a tape reader),
; ASR (a data terminal with optionnal tape reader), card reader, 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) and card reader use some object code
encoded with alphanumeric and
; hexadecimal characters.
; disks (double-sided floppies and hard-disk) use binary code.
;
FC96: 0360           RSET               ; RESET external peripherals
FC98: 0203 FDFE      LI   R3,>FDFE      ; address of the start-up routine
we jump to - defaults to error routine
FC9C: 0205 F7FE      LI   R5,>F7FE      ; last word before system memory space
FCA0: 020B FDFC      LI   R11,>FDFC     ; pointer to the read routine used
by the object code interpreter
FCA4: C381           MOV  R1,R14        ; if programmer panel enabled,
TILINE peripheral to boot from
FCA6: C3C2           MOV  R2,R15        ; if programmer panel enabled, unit
to load from

; simple memory test >F7FE -> >0000
FCA8: C555           MOV  *R5,*R5       ; try to read and write current word
FCAA: 0645           DECT R5
FCAC: 18FD           JOC  >FCA8         ; loop until we wrap around to >FFFE

; now real boot
FCAE: 1D0A           SBO  >000A         ; switch "Run" light on
FCB0: 1F0E           TB   >000E         ; this bit must tell a MDU is present
FCB2: 1302           JEQ  >FCB8         ; jump if not present
FCB4: 04C0           CLR  R0
FCB6: 102B           JMP  >FD0E         ; boot from the MDU ?

FCB8: C000           MOV  R0,R0         ; read boot device type (defaults
to -1)
FCBA: 1139           JLT  >FD2E         ; boot from TILINE tape or disk
device (FD1000, hard disk...)
FCBC: 020C 0040      LI   R12,>0040     ; device address >020 : card reader
FCC0: C000           MOV  R0,R0
FCC2: 1605           JNE  >FCCE         ; skip if card reader
FCC4: 04CC           CLR  R12           ; device address >000 : 733 ASR
FCC6: 1D09           SBO  >0009
FCC8: 1D0A           SBO  >000A
FCCA: 3220 FDF4      LDCR @>FDF4,8      ; effectively writes >11


FCCE: C000           MOV  R0,R0
FCD0: 131E           JEQ  >FD0E         ; skip if ASR
FCD2: 1109           JLT  >FCE6         ; skip if TILINE tape unit
FCD4: 1F07           TB   >0007         ; set up card reader at CRU address
>020
FCD6: 13FE           JEQ  >FCD4
FCD8: 1F01           TB   >0001
FCDA: 13FE           JEQ  >FCD8
FCDC: 1E0F           SBZ  >000F
FCDE: 1F07           TB   >0007
FCE0: 16FE           JNE  >FCDE
FCE2: 1D0F           SBO  >000F
FCE4: 1014           JMP  >FD0E

; entry point to boot from TILINE tape unit (MT3200 controller or equivalent)
FCE6: C183           MOV  R3,R6         ; save R3, R5
FCE8: C285           MOV  R5,R10
FCEA: 04C0           CLR  R0            ; meaningless
FCEC: 04C1           CLR  R1            ; meaningless
FCEE: 04C2           CLR  R2            ; read offset = 0 (read record from
the beginning)
FCF0: 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 !
FCF4: 04C4           CLR  R4            ; load at address 0
FCF6: 0205 0400      LI   R5,>0400      ; >4 : read binary forward command
FCFA: E14F           SOC  R15,R5        ; set unit ID
FCFC: 06A0 FF32      BL   @>FF32        ; execute command on tape unit
FD00: D920 FDD9 0050 MOVB @>FDD9,@>0050(R4) ; append carriage return after
the data we read (@>FDD9 = >0D)
FD06: C0C6           MOV  R6,R3         ; restore R3, R5
FD08: C14A           MOV  R10,R5
FD0A: 020B FDFC      LI   R11,>FDFC     ; pointer to the read routine used
by the object code interpreter

; entry point to boot from MDU
FD0E: 04C7           CLR  R7            ; clear checksum

; object code interpreter
;
; ASR, MDU, MT3200 tapes and cards 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)
;
FD10: 069B           BL   *R11          ; read command number in R10 from
tape (range 0-19)
FD12: D0AA FE18      MOVB @>FE18(R10),R2    ; read offset to routine in table
FD16: 132C           JEQ  >FD70         ; handle 'F' command separately
because it is parameter-less
FD18: 0882           SRA  R2,8          ; convert to signed 16-bit offset
FD1A: C207           MOV  R7,R8         ; save checksum
FD1C: 0201 0004      LI   R1,>0004      ; 4*4 = 16 bits to read
FD20: 069B           BL   *R11          ; read hex digit (4 bits)
FD22: 0A46           SLA  R6,4          ; shift digits we have already read
FD24: A18A           A    R10,R6        ; and insert new digit in R6
FD26: 0601           DEC  R1
FD28: 16FB           JNE  >FD20         ; loop until we have read a 16-bit
hex value
FD2A: 0462 FD2A      B    @>FD2A(R2)    ; jump to routine


; Boot from a tape/disk unit using MD900/MT3200 controller or equivalent on
the TILINE bus
;
; The routine expects the unit mask to be in R15.

FD2E: C10F           MOV  R15,R4        ; current unit mask : 1 (and only
1) bit must be set
FD30: 0A44           SLA  R4,4
FD32: 13D9           JEQ  >FCE6         ; routine for tape unit
FD34: 0460 FE86      B    @>FE86        ; routine for disk unit


; entry point for '3', '4', '5', '6','G','H'
; read 6 additional characters and ignore them
FD38: 0201 0006      LI   R1,>0006      ; read 6 chars
FD3C: 1015           JMP  >FD68

; entry point for 'A'
; add offset to R6, set address pointer
FD3E: A189           A    R9,R6         ; add offset to R6 (R9 set by 'D'
command, defaults to >00A0)
; entry point for '9'
; set address pointer
FD40: C146           MOV  R6,R5
FD42: 10E6           JMP  >FD10         ; next command

; entry point for 'C'
; add offset to R6, write two bytes
FD44: A189           A    R9,R6
; entry point for 'B'
; write two bytes
FD46: DD46           MOVB R6,*R5+
FD48: 06C6           SWPB R6
FD4A: DD46           MOVB R6,*R5+
FD4C: 10E1           JMP  >FD10

; entry point for '7'
; check checksum
FD4E: A206           A    R6,R8
FD50: 13DF           JEQ  >FD10         ; continue on if value matches checksum
; entry point for 'E'
; boot failure
FD52: 046B 0002      B    @>0002(R11)   ; jump to >FDFE (IDLE)

; entry point for 'D'
; set bias/offset
FD56: C246           MOV  R6,R9         ; set bias/offset value
FD58: 0249 FFFE      ANDI R9,>FFFE      ; convert to word address
FD5C: 10D9           JMP  >FD10

; entry point for '2'
; add offset to R6, set boot routine address
FD5E: A189           A    R9,R6
; entry point for '1'
; set boot routine address
FD60: C0C6           MOV  R6,R3         ; set boot routine address
FD62: 10D6           JMP  >FD10

; entry point for '0','I'
; read 8 additionnal characters and ignore them
FD64: 0201 0008      LI   R1,>0008      ; read 8 chars
FD68: 069B           BL   *R11
FD6A: 0601           DEC  R1
FD6C: 16FD           JNE  >FD68
FD6E: 10D0           JMP  >FD10

; entry point for 'F' command (parameter-less)
; wait for next card to be inserted
FD70: 069B           BL   *R11
FD72: 16FE           JNE  >FD70         ; wait for next card
FD74: 10AC           JMP  >FCCE

; read routine
; Read ASCII character from 733 ASR located at >000, a MDU unit located at
>FF0, a MT3200 tape unit
; on TILINE bus, or a card reader located at >020, and translate it to one
hexadecimal number.
; The routine for the card reader includes Hollerith->ASCII code conversion.
FD76: C000           MOV  R0,R0			; test device type flag
FD78: 1327           JEQ  >FDC8			; jump if ASR
FD7A: 112B           JLT  >FDD2			; jump if MT3200

; 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.
FD7C: 1F07           TB   >0007         ; test whether card is inserted ?
FD7E: 1622           JNE  >FDC4
FD80: 1F0F           TB   >000F         ; wait for next row of punch holes ?
FD82: 16FC           JNE  >FD7C
FD84: 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
;
; line | may be a card sensor.
;
; Hollerith character codes (source : Model 990 Computer Programming Card) :
;
; Space : Blank & : 12          - : 11          0 : 0
; 1 : 1         A/a : 12-1      J/j : 11-1      / : 0-1
; 2 : 2         B/b : 12-2      K/k : 11-2      S/s : 0-2
; 3 : 3         C/c : 12-3      L/l : 11-3      T/t : 0-3
; 4 : 4         D/d : 12-4      M/m : 11-4      U/u : 0-4
; 5 : 5         E/e : 12-5      N/n : 11-5      V/v : 0-5
; 6 : 6         F/f : 12-6      O/o : 11-6      W/w : 0-6
; 7 : 7         G/g : 12-7      P/p : 11-7      X/x : 0-7
; 8 : 8         H/h : 12-8      Q/q : 11-8      Y/y : 0-8
; 9 : 9         I/i : 12-9      R/r : 11-9      Z/z : 0-9
; : : 2-8       [ : 12-2-8      ] : 11-2-8      \ : 0-2-8
; # : 3-8       . : 12-3-8      $ : 11-3-8      , : 0-3-8
; @ : 4-8       < : 12-4-8      * : 11-4-8      % : 0-4-8
; ' : 5-8       ( : 12-5-8      ) : 11-5-8      _ : 0-5-8
; = : 6-8       + : 12-6-8      ; : 11-6-8      > : 0-6-8
; " : 7-8       ! : 12-7-8      ^ : 11-7-8      ? : 0-7-8

FD86: 04C0           CLR  R0
FD88: 04CC           CLR  R12
FD8A: 092A           SRL  R10,2

FD8C: 04CD           CLR  R13
FD8E: 0204 000D      LI   R4,>000D		; 13 lines to test
FD92: 091A           SRL  R10,1			; test line
FD94: 170B           JNC  >FDAC			; skip if line not punched

FD96: D024 FE6B      MOVB @>FE6B(R4),R0	; read compatibility mask
FD9A: 2700           CZC  R0,R12		; test ((R0 & ~R12) == R0)
- this checks that the current line
FD9C: 1304           JEQ  >FDA6			; is not incompatible with
the lines we have interpreted so far
FD9E: 0203 C001      LI   R3,>C001		; error code
FDA2: 0460 FF9E      B    @>FF9E		; abort boot in error

FDA6: B364 FE78      AB   @>FE78(R4),R13	; else, add offset
associated with line
FDAA: E300           SOC  R0,R12		; and update mask in R12

FDAC: 0604           DEC  R4
FDAE: 15F1           JGT  >FD92			; test next line

FDB0: 098D           SRL  R13,8			; 8-bit -> 16-bit offset
FDB2: D2AD FE2C      MOVB @>FE2C(R13),R10	; read ascii code in table
FDB6: 098A           SRL  R10,8			; convert to 16-bit value
FDB8: C00A           MOV  R10,R0

FDBA: 020C 0040      LI   R12,>0040
FDBE: 1F0F           TB   >000F
FDC0: 13FE           JEQ  >FDBE         ; wait for device ready ?
FDC2: 1012           JMP  >FDE8         ; goto character decode routine

; we jump there if no card is inserted (?) when reading from the card
reader located at >020
FDC4: 828A           C    R10,R10
FDC6: 1019           JMP  >FDFA

; read routine
; Read ASCII character from 733 ASR located at >000 or MDU unit located at
>FF0, and translate it to
; one hexadecimal number.
FDC8: 1F0C           TB   >000C         ; wait for incoming character ?
FDCA: 16FE           JNE  >FDC8
FDCC: 1E0C           SBZ  >000C
FDCE: 35CA           STCR R10,7         ; read 7-bit ASCII character
FDD0: 1001           JMP  >FDD4

FDD2: D2B4           MOVB *R4+,R10      ; read byte from memory (MT3200 is
DMA driven)

FDD4: 098A           SRL  R10,8         ; move MSB to LSB
FDD6: 028A 000D      CI   R10,>000D     ; carriage return ? (appended at
end of MT3200 record)
FDDA: 130F           JEQ  >FDFA         ; if so return
FDDC: 028A 005A      CI   R10,>005A     ; greater than 'Z' ?
FDE0: 15CA           JGT  >FD76         ; if so, ignore current char and
read next char
FDE2: 028A 0020      CI   R10,>0020     ; control character (lower than ' ') ?
FDE6: 11C7           JLT  >FD76         ; if so, ignore current char and
read next char
FDE8: A1CA           A    R10,R7        ; update checksum
FDEA: 022A FFD0      AI   R10,>FFD0     ; substract >30 (numeric character
-> numeric value)
FDEE: 028A 000A      CI   R10,>000A
FDF2: 1306           JEQ  >FE00         ; jump to handler if it is ':'
FDF4: 1102           JLT  >FDFA         ; if greater, it must be 'A'-'F'
(or 'G', 'H'...)
FDF6: 022A FFF9      AI   R10,>FFF9     ; convert to hex value
FDFA: 069B           BL   *R11          ; trick : return to the caller AND
restore R11 to be >FDFC

; entry point for the routine which reads data from the boot device
FDFC: 10BC           JMP  >FD76

FDFE: 0340           IDLE               ; stop the machine

; entry point for the ':' command (parameter-less command)
; jump to external boot routine
FE00: C000           MOV  R0,R0
FE02: 1608           JNE  >FE14
FE04: 1E0B           SBZ  >000B         ; clean up the ASR status ?
FE06: 1E0C           SBZ  >000C
FE08: 0581           INC  R1
FE0A: 16FE           JNE  >FE08
FE0C: 1F0C           TB   >000C
FE0E: 13F8           JEQ  >FE00
FE10: 1E09           SBZ  >0009
FE12: 1E0D           SBZ  >000D

FE14: 0360           RSET               ; reset peripherals
FE16: 0453           B    *R3           ; jumps to boot routine specified
by >1 or >2 command
                                        ; (jumps to >FDFE if no routine was
specified)


; offset table for jumps in object code interpreter, used on >FD2A (offsets
relative to >FD2A)
FE18: 3A36 340E      BYTE >FD64->FD2A,>FD60->FD2A,>FD5E->FD2A,>FD38->FD2A
FE1C: 0E0E 0E22      BYTE >FD38->FD2A,>FD38->FD2A,>FD38->FD2A,>FD4E->FD2A
FE20: E616 141C      BYTE >FD10->FD2A,>FD40->FD2A,>FD3E->FD2A,>FD46->FD2A
FE24: 1A2C 2E00      BYTE >FD44->FD2A,>FD56->FD2A,>FD52->FD2A,>00
FE28: 0E0E 3AE6      BYTE >FD38->FD2A,>FD38->FD2A,>FD64->FD2A,>FD10->FD2A


; table used for the 2nd step of the hollerith->ascii code conversion
; blank, 1, 2, 3, ..., 7
FE2C: 2031 3233      BYTE ' ','1','2','3'
FE30: 3435 3637      BYTE '4','5','6','7'
; 0, 0-1, 0-2, ..., 0-7
FE34: 302F 5354      BYTE '0','/','S','T'
FE38: 5556 5758      BYTE 'U','V','W','X'
; 11, 11-1, ..., 11-7
FE3C: 2D4A 4B4C      BYTE '-','J','K','L'
FE40: 4D4E 4F50      BYTE 'M','N','O','P'
; 12, 12-1, ..., 12-7
FE44: 2641 4243      BYTE '&','A','B','C'
FE48: 4445 4647      BYTE 'D','E','F','G'
; 8, 9, 2-8, 3-8, ..., 7-8
FE4C: 3839 3A23      BYTE '8','9',':','#'
FE50: 4027 3D22      BYTE '@',''','=','"'
; 0-8, 0-9, 0-2-8, 0-3-8, ..., 0-7-8
FE54: 595A 5C2C      BYTE 'Y','Z','\',','
FE58: 255F 3E3F      BYTE '%','_','>','?'
; 11-8, 11-9, 11-2-8, 11-3-8, ..., 11-7-8
FE5C: 5152 5D24      BYTE 'Q','R',']','$'
FE60: 2A29 3B5E      BYTE '*',')',';','^'
; 12-8, 12-9, 12-2-8, 12-3-8, ..., 12-7-8
FE64: 4849 5B2E      BYTE 'H','I','[','.'
FE68: 3C28 2B21      BYTE '<','(','+','!'

; table used for hollerith->ascii code conversion :
; it allows to detect impossible superposition.
; line order : 1  2  3  4  5  6  7  | 12 11  0  9  8
;
; rules :
; * at most one punch in lines 1 2 3 4 5 6 7 and 9 (>01 mask)
; * lines 8 and 1 cannot be punched simulteanously (line 9 must be used
instead) (>02 mask)
; * at most one punch in lines 0 11 12 (>04 mask)
;
; Note that the >08 mask is needed to prevent the program from rejecting
character when the | line is
; punched.
;
FE6C: 0301 0101      BYTE >03,>01,>01,>01
FE70: 0101 0108      BYTE >01,>01,>01,>08
FE74: 0404 0403      BYTE >04,>04,>04,>03
FE78: 02             BYTE >02

; table used for the 1st step of the hollerith->ascii code conversion :
; it provides an offset associated with each line.
; line order : 1  2  3  4  5  6  7  | 12 11  0  9  8
FE79:   01 0203      BYTE >01,>02,>03
FE7C: 0405 0607      BYTE >04,>05,>06,>07
FE80: 0018 1008      BYTE >00,>18,>10,>08
FE84: 2120           BYTE >21,>20


; boot from a disk unit attached to a WD900 disk controller (or equivalent)
whose address is in R14
FE86: C14F           MOV  R15,R5        ; unit ID
FE88: 020C FF32      LI   R12,>FF32

FE8C: 0200 0700      LI   R0,>0700		; restore command
FE90: 04C1           CLR  R1
FE92: 069C           BL   *R12			; execute command

FE94: 04C0           CLR  R0			; store register command
FE96: 04C1           CLR  R1
FE98: 0203 0006      LI   R3,>0006		; command returns 6 bytes
FE9C: 0204 0200      LI   R4,>0200		; free area in RAM
FEA0: 069C           BL   *R12
FEA2: C2A4 0004      MOV  @>0004(R4),R10
FEA6: 09BA           SRL  R10,11		; keep number of track per
cylinder (i.e. number of heads)

FEA8: 0200 0400      LI   R0,>0400		; read unformatted
FEAC: 04C1           CLR  R1			; cylinder 0
FEAE: 04C2           CLR  R2
FEB0: 069C           BL   *R12			; read current
track/cylinder address, # of sectors per record, and sector length

FEB2: C004           MOV  R4,R0			; >0200 : read data
FEB4: D064 0002      MOVB @>0002(R4),R1	; set the "sectors per records" field
FEB8: C0E4 0004      MOV  @>0004(R4),R3	; count = sector lenght
FEBC: 0A13           SLA  R3,1			; word count -> byte count
FEBE: 069C           BL   *R12			; (reads record #0)
FEC0: C3E4 000E      MOV  @>000E(R4),R15	; alternate boot program
track address
FEC4: 890F 0024      C    R15,@>0024(R4)	; compare with normal
program track address
FEC8: 130D           JEQ  >FEE4

FECA: C924 0024 000E MOV  @>0024(R4),@>000E(R4)	; replace alternate boot
program, with normal one
                                        ; so alternate program will be
loaded only once
FED0: 0200 0300      LI   R0,>0300		; write data
FED4: 069C           BL   *R12			; write record back
FED6: C824 001C 00A0 MOV  @>001C(R4),@>00A0	; use alternate load point
FEDC: C824 001E 00A2 MOV  @>001E(R4),@>00A2	; and alternate length
FEE2: 1006           JMP  >FEF0

FEE4: C824 0018 00A0 MOV  @>0018(R4),@>00A0	; use normal load point
FEEA: C824 001A 00A2 MOV  @>001A(R4),@>00A2	; use normal length

FEF0: 0200 0400      LI   R0,>0400		; read unformatted
FEF4: 0203 0006      LI   R3,>0006		; command returns 6 bytes
FEF8: 06A0 FF26      BL   @>FF26		; compute cylinder/head
address from track address in R15
FEFC: 069C           BL   *R12

FEFE: C064 0002      MOV  @>0002(R4),R1	; set format & sector according to
value returned by controller
FF02: 0200 0200      LI   R0,>0200		; >0200 : read data
FF06: C0E0 00A2      MOV  @>00A2,R3		; program length
FF0A: 0204 00A0      LI   R4,>00A0		; free area in RAM ?
FF0E: 06A0 FF26      BL   @>FF26
FF12: C3E0 00A0      MOV  @>00A0,R15	; program load point
FF16: 069C           BL   *R12			; read boot program

FF18: 0200 ABC0      LI   R0,>ABC0		; magical value ?
FF1C: C04E           MOV  R14,R1		; boot device address
FF1E: 04C2           CLR  R2
FF20: D085           MOVB R5,R2			; unit ID
FF22: 0360           RSET				; Reset for things
to be cleaner
FF24: 045F           B    *R15			; jump to boot program

; compute cylinder & head address from "flat" track address
; input :
; R10 = number of heads
; R15 = track address
; returns :
; R2 = cylinder address
; R0 |= head address
FF26: C24F           MOV  R15,R9
FF28: 04C8           CLR  R8
FF2A: 3E0A           DIV  R10,R8		; compute R15/R10
FF2C: C088           MOV  R8,R2			; R2 = quotient : cylinder
address
FF2E: E009           SOC  R9,R0			; R0 LSB = remainder : head
address
FF30: 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
;
FF32: 020D 0005      LI   R13,>0005		; retry count
FF36: C1CE           MOV  R14,R7		; TILINE device address
FF38: C227 000E      MOV  @>000E(R7),R8
FF3C: 1101           JLT  >FF40
FF3E: 10FB           JMP  >FF36			; wait for IDLE controller
status bit to be cleared

FF40: C9C5 000C      MOV  R5,@>000C(R7)
FF44: C5D7           MOV  *R7,*R7
FF46: 11FC           JLT  >FF40

FF48: 04F7           CLR  *R7+			; disc status
FF4A: CDC0           MOV  R0,*R7+
FF4C: CDC1           MOV  R1,*R7+
FF4E: CDC2           MOV  R2,*R7+
FF50: CDC3           MOV  R3,*R7+
FF52: CDC4           MOV  R4,*R7+
FF54: CDC5           MOV  R5,*R7+
FF56: 04D7           CLR  *R7

FF58: C217           MOV  *R7,R8
FF5A: 1101           JLT  >FF5E
FF5C: 10FD           JMP  >FF58			; wait for IDLE controller
status bit to be cleared

FF5E: C1C5           MOV  R5,R7
FF60: 0247 F000      ANDI R7,>F000		; 4 MSBits select unit on WT3200,
and are unused on WD900
FF64: 1604           JNE  >FF6E			; skip if we are using a
tape unit

FF66: C1DE           MOV  *R14,R7		; disk status
FF68: 0247 4000      ANDI R7,>4000
FF6C: 16FC           JNE  >FF66			; wait for NR (Not Ready)
status bit to be cleared

FF6E: 0248 01FF      ANDI R8,>01FF		; test all individual error
bits in controller status
FF72: 1602           JNE  >FF78			; jump on error

FF74: 0700           SETO R0
FF76: 045B           B    *R11			; normal return

; error handler :
FF78: C1C5           MOV  R5,R7
FF7A: 0247 F000      ANDI R7,>F000
FF7E: 1303           JEQ  >FF86			; jump if disk unit

FF80: 0203 D001      LI   R3,>D001		; error code
FF84: 100C           JMP  >FF9E			; stop in error

FF86: 0288 0001      CI   R8,>0001		; check unit error in
controller status
FF8A: 1604           JNE  >FF94			; if set, means an error
condition is set in disk status

FF8C: C21E           MOV  *R14,R8		; read disk status
FF8E: 0248 E000      ANDI R8,>E000		; test bits OL, NR, WP
(Off-line, not-ready, write-protect)
FF92: 16CF           JNE  >FF32			; restarts everything !
(must be a bug...)

FF94: 060D           DEC  R13			; decrement retry count
FF96: 15CF           JGT  >FF36			; try again

FF98: 0203 D002      LI   R3,>D002		; error code
FF9C: 1000           JMP  >FF9E			; stop in error

; error stop
; we jump to this routine when boot fails
; display R3 for diagnostics purpose, light the fail LED, and stop.
FF9E: 020C 1FE0      LI   R12,>1FE0
FFA2: 1D0B           SBO  >000B
FFA4: 3203           LDCR R3,8
FFA6: 3220 0087      LDCR @>0087,8
FFAA: 0340           IDLE


; spare room
FFAC: 0000           DATA >0000
FFAE: 0000           DATA >0000
FFB0: 0000           DATA >0000
FFB2: 0000           DATA >0000
FFB4: 0000           DATA >0000
FFB6: 0000           DATA >0000
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
FFF4: 0000           DATA >0000


; pointers to routines
FFF6: FC9C           DATA >FC9C			; boot loader
FFF8: FC96           DATA >FC96			; ditto
FFFA: FD0E           DATA >FD0E			; boot loader for MDU (?)

; LOAD vector
FFFC: 0080 FC00      DATA >0080,>FC00
