; 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
