
;*****************************************************************************
;
; TI9900 Self-Test
;
; Modified from the TI990 boot loader ROM; TI990 Handbook, 6-11 (page 218).
; by Scott L Baker, June 2002
;
; Assembled using Macro Assembler AS V1.42 (Alfred Arnold et. al.)
;
;
;*****************************************************************************

          RELAXED ON
          SUPMODE ON

          ORG  0x0000

          WORD 0xF800         ; initial WP
          WORD START          ; initial PC

; cpu test routine entry point

START:    SBO  0x0B           ; switch "Fault" light on
          LI   R8,0x7FFF
          INCT R8
          JNO  ERROR1         ; stop if no overflow
          MOV  R0,R1
          MOV  R8,R0
          SRA  R8,5
          SRL  R8,1
          SRA  R8,0
          MOV  R1,R0          ; restore R0
          CI   R8,0x3F00
          JNE  ERROR1         ; stop if different from the value expected
          SETO R4
          ANDI R4,0x5555
          BL   @ERR1          ; this should clear R4, or stop if it fails
          ORI  R4,0x5555
          BL   @ERR1
          LIMI 0x0001         ; effectively sets least significant bit in ST
          BLWP @INDIR1        ; jump indirect to CONT1

; should be called with R4 == 0x5555

ERR1:     ORI  R4,0xAAAA
          INC  R4
          JNE  ERROR1         ; stop if R4 != 0
          B    *R11

; continue on...

CONT1:    STST R11
          SRA  R11,1          ; test LSBit in ST
          JNC  ERROR1         ; stop if not set
          CLR  R15            ; -> clear ST
          LI   R14,CONT2      ; actual jump address
          RTWP

INDIR1:   WORD 0xF7EC         ; BLWP vector
          WORD CONT1

; continue on...  This gets tricky :-) .

CONT2:    STST R7
          MOV  R7,R1
          JNE  ERROR1         ; stop if ST not clear

LOOP1:    MOV  R1,R2
          SRC  R2,5
          SWPB R2             ; effectively rotate R2 3 bits left
          MOV  R2,R11
          ABS  R11

          MOV  R2,R3
          SLA  R3,1           ; test R2 sign bit
          JNC  CONT3          ; start if R2 is positive

          INV  R11            ; R11 = ~R11
          C    R11,R2         ; so R11 = R2-1
          JH   ERROR1         ; stop the machine if R11>R2
          JLT  OK1            ; huh ??? what if R2 == 0x8000 ? This must be a bug !

ERROR1:   JMP  ERROR1         ; stop everything

OK1:      C    R2,R11
          JL   ERROR2         ; we still have R11 = R2-1...
          INCT R11
          DEC  R11            ; now R11 == R2

; we jump here when R2 >= 0

CONT3:    C    R11,R2
          JNE  ERROR2

          MOV  R1,R3
          A    R2,R3
          MOV  R1,R4
          NEG  R4
          S    R2,R4
          NEG  R4
          CB   R3,R4           ; we should have R3==R4
          JH   ERROR2          ; stop if logically higher
          JLE  CONT4           ; jump there if OK
          JMP  ERROR2          ; stop if arithmetically greater

CONT4:    CB   @0xF807,@0xF809 ; compare R3 MSB and R4 MSB
          JNE  ERROR2          ; stop if different
          COC  R4,R3           ; (R3 & R4) == R3 ?
          JNE  ERROR2
          INV  R4
          CZC  R4,R3           ; (R3 & ~R4) == R3 ?
          JNE  ERROR2
          INV  R4
          SB   R3,R4           ; result 0 in R4 MSB
          JLT  ERROR2
          JNE  ERROR2
          SB   @0xF807,@0xF809 ; subtract LSB
          JNE  ERROR2
          MOVB R2,R4           ; move R2 MSB
          MOVB @0xF805,@0xF809 ; move R2 LSB
          AB   @0xF803,@0xF809 ; add R1 LSB
          JNC  CONT5
          AI   R4,0x0100       ; adjust carry if needed
CONT5:    AB   R1,R4           ; add R1 MSB
          C    R3,R4           ; we should still have R3 = R1+R2, too...
          JNE  ERROR2
          INC  R3
          INC  R3
          C    R4,R3
          JEQ  ERROR2
          JGT  ERROR2
          DECT R3
          C    R3,R4
          JNE  ERROR2
          MOV  R3,R5
          MOV  R5,R6
          MOV  R5,R7
          SWPB R7             ; swap bytes (8-bit rotate)
          SRL  R5,8
          SLA  R6,8
          SOC  R5,R6          ; swap bytes with another method
          C    R6,R7
          JNE  ERROR2
          SRC  R6,8           ; swap bytes again
          C    R6,R3          ; should equate the original value
          JNE  ERROR2

; now, multiply R1 and R2 with custom code, then check MPY gives the same result

          LI   R10,0x0010     ; 16 bits to test
          CLR  R11
          CLR  R8
          CLR  R7
          MOV  R2,R6
          MOV  R1,R5

LOOP2:    SRA  R5,1           ; if R5 LSB is set
          JNC  CONT6

          A    R6,R11         ; add R7:R6 to R8:R11
          JNC  AR7R8
          INC  R8
AR7R8:    A    R7,R8

CONT6:    SLA  R7,1           ; shift R7:R6
          SLA  R6,1
          JOC  INCR7
          JMP  DECR10
INCR7:    INC  R7

DECR10:   DEC  R10            ; loop
          JNE  LOOP2

          MOV  R1,R3
          MPY  @0xF804,R3     ; = MPY R2,R3
          C    R3,R8          ; compare MSWord
          JNE  ERROR2
          C    R4,R11         ; compare LSWord
          JNE  ERROR2

; now, test DIVide

          C    R1,R2
          JHE  DIV2
          A    R1,R4          ; if R1 < R2, add R1 to MSW (R4)
          JNC  DIV1
SUBR4:    S    R1,R4          ; if carry, revert to normal method
          JMP  DIV2           ; (otherwise, there might be an overflow when executing DIV)
DIV1:     DIV  R2,R3
          X    @SUBR4         ; = S R1,R4 (remainder should be R1, and is fixed to be 0)
          JMP  CHKREM

DIV2:     DIV  @0xF804,R3     ; compute R3:R4 / R2
          MOV  R4,R4          ; test the remainder
CHKREM:   JNE  ERROR2         ; should be 0

; now test parity ST bit

          MOVB R1,R6
          LI   R7,0x0008      ; 8 bits to test
          CLR  R8
SLAR6:    SLA  R6,1
          JNC  DECR7
          INC  R8             ; count bits
DECR7:    DEC  R7
          JGT  SLAR6          ; loop if some bits left

          SRC  R8,1           ; test parity
          JNC  ABR1R7

          MOVB R1,R1          ; should be odd parity
          JOP  MOVR2R5
ERROR2:   JMP  ERROR2

ABR1R7:   AB   R1,R7          ; R7 == 0 from previous loop (remember ?)
          JOP  ERROR2         ; should be even parity

MOVR2R5:  MOV  R2,R5
          SZC  R2,R5          ; R5 = R5 & ~R2
          JNE  ERROR2         ; result should be 0
          A    R2,R5          ; hence R5 = R2
          SZCB R2,R5          ; R2 MSB & R5 MSB
          JNE  ERROR2
          SZCB @0xF805,@0xF80B ; R2 LSB & R5 LSB
          JNE  ERROR2
          SETO R11            ; R11 = 0xFFFF
          SZC  R2,R11         ; R11 = R11 & ~R2
          JEQ  ERROR2         ; obviously, R2 is assumed to be non-zero
          SOCB R1,R5          ; R5 should be 0 from previous computation.
          SWPB R5
          SOCB @0xF803,R5     ; bit-wise OR with R1 LSB
          SRC  R5,8           ; so R5 = R1
          S    R1,R5
          JNE  ERROR2

          INC  R1             ; increment R1
          CI   R1,0xFFC0
          JH   MEMTEST        ; if R1 > 0xFFC0, bail out

          MOV  R1,R6
          LI   R4,0x0013
          DIV  R4,R5          ; effectively computes R6 / 19  (R5 is still 0, remember ?)
          MOV  R6,R6          ; test the remainder
          JNE  EOLOOP1
          AI   R1,0x03E8      ; add 1000 to R1 if R1 is multiple of 19
EOLOOP1:  B    @LOOP1         ; and loop

; this effectively results into testing a sequence :
; 0,1,...,18,1019,1020,1021,1022,...,2032,3033,...,65467,...,65472.
; The next value is 65473, which is greater than 0xFFC0 (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

MEMTEST:  SETO R10
          LI   R2,0x02E0      ; LWPI
          CLR  R3             ; 0x0000
          LI   R4,0x0460      ; B @
          LI   R5,0xFDD0      ; 0xFDD0

; memtest loop start

MEMLOOP:  CI   R3,0xF800
          JEQ  EOMLOOP        ; don't test system area 0xF800-0xFFFF
          STWP R6
          C    R3,R6          ; do not overwrite our workspace.
          JNE  MSKP1          ; geez, the previous test already ensured this !
          AI   R3,0x0020
MSKP1:    MOV  *R3,R1         ; save future R0 register

          B    R2             ; this emulates missing "LWP R3" instruction
          XOR  @0xF814,R0     ; XOR old R10 with new R0
          LWPI 0xF800         ; restore old WP
          INV  R1             ; R1 = ~R1
          MOV  R1,R7
          C    R1,*R3         ; should give the same result as R0 = R0 ^ 0xFFFF
          JNE  MOVR3
          INV  *R3            ; Restore memory location
          XOR  *R3+,R1        ; result should be 0xFFFF
          INC  R1
          JEQ  MEMLOOP        ; try next word if OK

          DECT R3             ; else restore R3
          INV  R7             ; restore value read in RAM
MOVR3:    MOV  R3,R6
          SLA  R6,3           ; if the address in on a 8kb-boundary, we must only have left
          JEQ  EOMLOOP        ; the RAM area, so we continue on (?)
          MOV  R7,R2
          IDLE                ; else stop the machine
          MOV  @0xFFF8,R1     ; read pointer to boot routine
EOMLOOP:  B    *R1            ; and jump here on PASS !!


INDADDR:  WORD EOMLOOP

