$PAGINATE
$title(Arnold 5 test)
$subtitle(Main file which sets segments and includes main test routine)
$copyright(Copyright (c) 1989, 1990, Amstrad plc.)
$pagewidth=131


; This file should be the first one linked as it contains the RSTs that go
; at the start of the ROM.


        EXTERN  ScreenSetMode           ;defined in SUPPORT
        EXTERN  ScreenResetPalette      ;defined in SUPPORT
        EXTERN  ProcessorTest           ;defined in PROCESSR
        EXTERN  ErrorHandler            ;defined in ERROR
        EXTERN  SetCursorPos            ;defined in SUPPORT
        EXTERN  ScreenPrintChar         ;defined in SUPPORT
        EXTERN  PrinterPrintChar        ;defined in SUPPORT
        EXTERN  PrintStringHL           ;defined in SUPPORT
        EXTERN  PrintAHex               ;defined in SUPPORT
        EXTERN  LprintAHex              ;defined in SUPPORT
        EXTERN  DelayASeconds           ;defined in SUPPORT
        EXTERN  KeyboardRead            ;defined in SUPPORT
        EXTERN  UnlockArn5              ;defined in SUPPORT
        EXTERN  RelockArn5              ;defined in SUPPORT
        EXTERN  DefaultTests            ;defined in DEFAULTS
        EXTERN  PrintLoopCount          ;defined in COUNTER
        EXTERN  PrintLoopLimit          ;defined in COUNTER
        EXTERN  .InitProcessorMessPt1   ;defined in MESSAGES
        EXTERN  .InitProcessorMessPt2   ;defined in MESSAGES
        EXTERN  .CrLfMess               ;defined in MESSAGES
        EXTERN  .Arnold5ErrorMess       ;defined in MESSAGES
        EXTERN  .CheckSumMess           ;defined in MESSAGES
        EXTERN  Im1Interrupt            ;defined in INTTEST
        EXTERN  .MainMenuTable          ;defined if MESSAGES
        EXTERN  ProcessAMenu            ;defined in MENU
        EXTERN  ?RAMstack               ;defined in TESTVARS
        EXTERN  ?ArnoldType             ;defined in TESTVARS
        EXTERN  ?TestLastHighlight      ;defined in TESTVARS
        EXTERN  ?TestLoopLimit          ;defined in TESTVARS
        EXTERN  ?TestLoopCounter        ;defined in TESTVARS


        include "equates.inc"


RAMLessCall     %MACRO  TargetRoutine
                %GENSYM ReturnLabel
                ld      sp,ReturnLabel
                jp      TargetRoutine
ReturnLabel     defw    ReturnLabel + 2
                %ENDM


        DEFSEG  TestCode, CLASS=CODE, START=0       ;ensure power up entry is positioned

        SEG     TestCode

PowerUpEntry:
        jp      _TestStart

        defb    "Arnold 5 Test ROM by C J Lawson, (C) Amstrad plc 1990"

; The above is exactly the right length to pad out so that the following
; jump is located at RST 7 (038h).
;
; I'm not exactly crazy about this way of doing it but to use ORG statements
; involves the use of ABSOLUTE segments and if I do that then there is a
; problem that the following relocatable modules (INITIAL....) restart the
; counter from 0 and hence two pieces of code try to locate at 0.
;
        jp      Im1Interrupt            ;when in IM1 interrupts come here and
                                        ;are then passed to the timer
                                        ;interrupt test code.

.ROMDateAndTime
        defb    "Date=00-00-1900 "      ;these entries will be filled in by my
        defb    "Time=00:00:00$"        ;DATEROM utility that reads the system
                                        ;date/time and puts it into the .HEX
                                        ;file at this fixed point.

_TestStart:
;
; The idea here is to set the various I/O devices into "safe" conditions and get
; the machine to a point where, if during the following tests and error occurs,
; it is woken up enuff to at least have a stab at printing it to the Centronics
; port and perhaps even printing it on the screen.
;
        di                              ;interrupts will only ever be enabled
                                        ;during the INTTEST and also after the
                                        ;switch to IM2 when testing the various
                                        ;vectored interrupt mechanisms.

        im      1                       ;for the time being the machine is put
                                        ;into interrupt mode 1 so that when
                                        ;interrupts are first enabled they
                                        ;will vector via RST 038h

        exx                             ;switch to alternate register set cos
                                        ;B' will always hold 07Fh (the I/O
                                        ;addr of the ULA) and C' will hold the
                                        ;last value that set the mode and rom
                                        ;bits.

        ld      b,07Fh                  ;I/O addr of the ULA
        ld      c,0C0h
        out     (c),c                   ;make sure default RAM page selected

        ld      c,089h                  ;this magic value has the top 3 bits
                                        ;=100 (i.e. set ROM+MODE). The clear
                                        ;52 divider bit is 0, the
                                        ;lower ROM will be enabled and the
                                        ;screen mode is set to 1 (40x25).
        out     (c),c                   ;set that state into the hardware.

        exx                             ;back to the "normal" register set

        RAMLessCall ProcessorTest       ;(in PROCESSR)
        jp      c,_ProcessorFail        ;if processor failed then warn

        exx                             ;reset alternate registers
        ld      bc,07F89h               ;C' has mode bits etc.
        ld      l,1                     ;L':0 set means we can print
        exx

        ld      bc,0DF00h               ;DFxx=ROM switching port
        out     (c),c                   ;select ROM 0 as upper ROM

        ld      bc,0F782h               ;F7xx=8255 control register
        out     (c),c                   ;set port A=out, B=In and C=Out

        ld      bc,0F600h
        out     (c),c                   ;make sure BDIR/BC1=0 and cassette off

        ld      a,0
        ld      bc,0FA7Eh
        out     (c),a                   ;switch off disk motors

;
; it is not necessary to set the AY port A (keyboard columns) to input as
; this is the default state when the AY chip is reset.
;

        ld      bc,0EF7Fh               ;EFxx=Centronics latch
        out     (c),c                   ;set all bits high except not-STROBE

        ld      hl,8000h                ;RAM variable area
        ld      de,8001h
        ld      bc,1000h
        ld      (hl),0AAh
        ldir                            ;fill RAM variable area with AA test

        ld      hl,8000h
        ld      bc,1000h
_RAMVarTestLoop1:
        ld      a,0AAh
        cpi
        jp      nz,_RAMVarFail
        ld      a,b
        or      c
        jr      nz,_RAMVarTestLoop1

        ld      hl,8000h                ;RAM variable area
        ld      de,8001h
        ld      bc,1000h
        ld      (hl),055h
        ldir                            ;fill RAM variable area with 55 test

        ld      hl,8000h
        ld      bc,1000h
_RAMVarTestLoop2:
        ld      a,055h
        cpi
        jp      nz,_RAMVarFail
        ld      a,b
        or      c
        jr      nz,_RAMVarTestLoop2
        jr      _RAMVarPass

_RAMVarPass:
        ld      hl,8000h
        ld      de,8001h
        ld      bc,0FFFh                ;that's all memory from 8000 to 9000
        ld      (hl),0
        ldir                            ;clear all vars to 0 (Floppy stuff
                                        ;needs this !)

        ld      sp,?RAMstack            ;(in TESTVARS)

        call    ScreenResetPalette      ;set a known collection of colors

        ld      a,1
        call    ScreenSetMode

        exx
        ld      l,1                     ;assume printer is there
        exx

        ld      hl,.CrLfMess
        ld      b,2
        call    PrintStringHL           ;get printer ready for message

        ld      hl,.InitProcessorMessPt1 ;"Arnold 5 diagnostics Version "
        ld      b,2                     ;printer
        call    PrintStringHL

        ld      a,Version               ;an EQU in EQUATES.INC
        call    PrinterPrintChar        ;print the version digit to printer

        ld      a,'.'                   ;the intervening full stop
        call    PrinterPrintChar        ;and to the printer

        ld      a,Mark                  ;an EQU in EQUATES.INC
        call    PrinterPrintChar        ;print the mark digit to printer

        ld      hl,.CrLfMess
        ld      b,3                     ;both screen and printer
        call    PrintStringHL

        ld      hl,.CrLfMess
        ld      b,3
        call    PrintStringHL

        ld      hl,.ROMDateAndTime      ;in this module (@ about 40h)
        ld      b,3
        call    PrintStringHL           ;print Rom Date and time

        ld      bc,07F81h               ;mode 1 and both ROMs on
        out     (c),c

        ld      de,0                    ;the 16 bit sum
        ld      hl,0                    ;the pointer
        ld      bc,4000h                ;count of bytes in lower ROM
_TestCheckSum1:
        ld      a,(hl)                  ;get byte from ROM
        add     e                       ;add lower half of DE
        ld      e,a                     ;and save result
        ld      a,0
        adc     d                       ;add any carry into d
        ld      d,a                     ;and save that result
        inc     hl                      ;step on memory pointer
        dec     bc                      ;and reduce counter
        ld      a,b
        or      c
        jr      nz,_TestCheckSum1
;
; DE has result for lower ROM at this point
;
        ld      hl,0C000h               ;now point at upper ROM
        ld      bc,4000h                ;which is also 16K long
_TestCheckSum2:
        ld      a,(hl)                  ;get byte from ROM
        add     e                       ;add lower half of DE
        ld      e,a                     ;and save result
        ld      a,0
        adc     d                       ;add any carry into d
        ld      d,a                     ;and save that result
        inc     hl                      ;step on memory pointer
        dec     bc                      ;and reduce counter
        ld      a,b
        or      c
        jr      nz,_TestCheckSum2

        exx
        out     (c),c                   ;set mode bits back to normal
        exx

        push    de                      ;save checksum
        ld      hl,.CheckSumMess
        ld      b,3
        call    PrintStringHL
        pop     de
        push    de
        ld      a,d
        call    PrintAHex               ;print hi byte on screen
        pop     de
        push    de
        ld      a,d
        call    LprintAHex              ;and printer
        pop     de
        push    de
        ld      a,e
        call    PrintAHex               ;print low byte on screen
        pop     de
        ld      a,e
        call    LprintAHex              ;and printer

        ld      hl,.CrLfMess
        ld      b,2
        call    PrintStringHL           ;move printer down a line

_TestWaitNoKey:
        call    KeyboardRead            ;if space held down then wait
        cp      47
        jr      z,_TestWaitNoKey

;
; Once the initialisation has been performed we arrive at this entry point.
; The main loop is entered to allow other tests to be chosen from a menu.
;
_TestMainCode:
        ld      de,00806h               ;(8,6)
        call    SetCursorPos

        exx
        ld      l,1                     ;wind printer up after L' corrupted
        exx

        ld      hl,.CrLfMess
        ld      b,2                     ;printer only
        call    PrintStringHL           ;allow printer to timeout if not there

        ld      hl,.InitProcessorMessPt2 ;"Processor test PASSED"
        ld      b,3                     ;screen and printer
        call    PrintStringHL

        ld      hl,.CrLfMess
        ld      b,2                     ;only CR LF on printer
        call    PrintStringHL

        call    UnLockArn5              ;rets C if it couldn't - assume Arn 1-4
        jr      c,_TestGotArnold1234
        call    ReLockArn5              ;assume this will work (NC)
        jr      c,_TestArn5Error
        ld      a,5                     ;Arnold type
        jr      _TestSetArnoldType

_TestArn5Error:
        ld      hl,.Arnold5ErrorMess
        ld      b,3
        call    PrintStringHL           ;warn user that we couldn' disable Arn5
        jp      _TheVeryEND             ;and die


_TestGotArnold1234:
        ld      a,4                     ;means 1..4

_TestSetArnoldType:
        ld      (?ArnoldType),a

        ld      a,1
        call    DelayASeconds           ;long enough to read the messages


        ld      hl,?TestLoopLimit       ;(in TESTVARS)
        ld      (hl),1                  ;set loop counter to 1

_TestMainLoop:
        ld      a,0                     ;do all menu including re-draw
_TestProcessMenu:
        ld      hl,.MainMenuTable       ;(in MESSAGES)
        call    ProcessAMenu            ;(in MENU)
        ld      (?TestLastHighlight),a
        jr      nc,_TestMenuSelected    ;if carry set then menu timed out
        call    DefaultTests            ;so run default tests
        jr      _TestMainLoop           ;then start again

_TestMenuSelected:
;
; This returns with the address of the routine to call in HL. If the top bit
; of H is set then it is either increment or decrement loop counter so we
; definitely don't want to do it "TestLoopCounter" times or clear the screen.
;
; If the bit 6 in H is set then it is the address of a routine that sets its
; own screen mode so there is no point in clearing the screen here.
;
; This returns with A holding the currently highlighted line which can be stored
; and passed into ProcessAMenu next time in B with A=2 (which means reuse old
; highlight value).
;
        bit     7,h
        jr      z,_TestLoopy
        res     7,h                     ;make a sensible address
        call    _TestIndirectCallHL     ;run the inc/dec loop
        ld      a,1                     ;parm to ProcessAMenu means no re-draw
        jr      _TestProcessMenu

_TestLoopy:
        ld      a,(?TestLoopLimit)      ;get current setting of limit
        ld      (?TestLoopCounter),a    ;initialise counter before tests
_TestCounterLoop:
        bit     6,h                     ;if 6 set then no CLS
        jr      nz,_TestNoTouchScreen

        push    hl                      ;save routine addr in case we loop
        ld      a,(?TestLoopCounter)
        ld      l,a
        ld      a,(?TestLoopLimit)      ;only want CLS if 1st loop
        cp      l
        jr      nz,_TestNoClearScreen
        ld      a,1
        call    ScreenSetMode           ;all routines start on blank MODE 1
        call    PrintLoopLimit          ;with loop limit at bottom left
_TestNoClearScreen:
        call    PrintLoopCount          ;and loop count at bottom right
        ld      de,00200h               ;tests may start to print at (0,2)
        call    SetCursorPos
        pop     hl

_TestNoTouchScreen:
        res     6,h                     ;clear possible flag bit (no harm)
        push    hl
        call    _TestIndirectCallHL     ;go do the selected test routine
;
; routines return with C set if there was an error and A contains error code
;
        jp      c,ErrorHandler
        pop     hl                      ;retrieve that address
        ld      a,(?TestLoopCounter)
        dec     a
        ld      (?TestLoopCounter),a
        jr      nz,_TestCounterLoop
        ld      a,(?TestLastHighlight)
        ld      b,a
        ld      a,2                     ;parm for ProcessAMenu to re-use old
                                        ;highlight

        jp      _TestProcessMenu        ;This is the top command loop !

_TestIndirectCallHL:
;
; The worlds fave routine...
;
        jp      (hl)


_ProcessorFail:
;
; Serious shit man ! We'll have a crack at printing an error message but what
; hope is there really ?
;
        exx
        ld      l,1                     ;enable printer
        exx

        ld      a,'C'
        RAMLessCall PrinterPrintChar

        ld      a,'P'
        RAMLessCall PrinterPrintChar

        ld      a,'U'
        RAMLessCall PrinterPrintChar

        jr      _SeriousFailure


_RAMVarFail:
;
; If the unthinkable should happen and the 8000h..9000h block of RAM can't
; even hold AA or 55 then we are in dead shtuck. The best I can hope to do
; without using any RAM is to print a fail message on the printer using
; RAMLessCALLs to PrinterPrintChar. I can't even use PrintStringHL so each
; character must be sent individually.
;
        exx
        ld      l,1                     ;enable printer
        exx

        ld      a,'R'
        RAMLessCall PrinterPrintChar

        ld      a,'A'
        RAMLessCall PrinterPrintChar

        ld      a,'M'
        RAMLessCall PrinterPrintChar


_SeriousFailure:
        ld      a,' '
        RAMLessCall PrinterPrintChar

        ld      a,'E'
        RAMLessCall PrinterPrintChar

        ld      a,'R'
        RAMLessCall PrinterPrintChar

        ld      a,'R'
        RAMLessCall PrinterPrintChar

        ld      a,'O'
        RAMLessCall PrinterPrintChar

        ld      a,'R'
        RAMLessCall PrinterPrintChar

        ld      a,'!'
        RAMLessCall PrinterPrintChar

        ld      a,0Dh
        RAMLessCall PrinterPrintChar

        ld      a,0Ah
        RAMLessCall PrinterPrintChar


_TheVeryEND:
        jr      _TheVeryEND             ;If things are that bad what else can
                                        ;we do ?

        END
