;$PAGINATE
;$title(Arnold 5 test)
;$subtitle(Test of 1/300s timer interrupt)
;$copyright(Copyright (c) 1989, 1990, Amstrad plc.)
;$pagewidth=131
;

;
; To test the interrupts the machine is run in interrupt mode 1 so that any
; interrupts that occur will go to RST 7 (00038h). This then jumps to this
; routine.
;

;        PUBLIC  Im1Interrupt            ;jumped to from RST 38h in TESTPACK
;        PUBLIC  InterruptTest           ;called indirectly from TESTPACK
;                                        ; out of MainMenuTable
;
;        EXTERN  PrintStringHL
;        EXTERN  SetCursorPos
;        EXTERN  DelayASeconds
;        EXTERN  OfferRetest
;
;        EXTERN  .InterruptMess
;
;        EXTERN  QUESTMARK_IM1InterruptCounter    ;in (TESTVARS)
;
;        DEFSEG  TestCode, CLASS=CODE
;
;        SEG     TestCode

;============
Im1Interrupt:
;============
;
; While in IM1 an interrupt will transfer control to 00038h. That just has a
; jump to this routine.
;
; This routine just increments a RAM based variable (RAM will have been
; validated by the time this routine can be run). The following test code
; sits in a loop watching for the variable increasing at the appropriate
; moment.
;
        ld      hl,QUESTMARK_IM1InterruptCounter         ;(TESTVARS)
        inc     (hl)                            ;short and sweet !
        ei
        ret

;=============
InterruptTest:
;=============
;
; At all times while the test software is executing, interrupts are DIsabled
; In order to test that they are OK, this routine will ensure we are in
; interrupt mode 1 so that any interrupts go to 0038h. The QUESTMARK_InterruptCounter
; will be reset, then interrupts will be enabled.
; We sort of hope that one might occur within 1/300 S so we enter a loop of
; this length waiting to see QUESTMARK_InterruptCounter increase to 1. If it doesn't
; then we assume that there is no timer interrupt.
;
; As soon as we see the variable increase to 1 we must have got the leading
; edge of an interrupt. We then start a counter and wait for the
; to InterruptCounter increase to 11 (i.e. another 10 interrupts). At the end
; of this time we would hope that the counter holds a value roughly equal
; to 1/30 S. We should be able to flag if the error is either that the period
; is far too low or too high.
;
        ld      de,00A02h
        call    SetCursorPos

        ld      hl,.InterruptMess
        ld      b,1
        call    PrintStringHL

;
; We sync with frame flyback so that the INT test always starts at a known
; point in the interrupt cycle.
;
        ld      bc,0F500h               ;8255 port B (bit 0 is f/f)
_IntWaitFFLow:
        in      a,(c)
        bit     0,a                     ;this will set Z if bit 0 = 0
        jr      nz,_IntWaitFFLow        ;need to see it low
;
; out of FF
;
_IntWaitFFHigh:
        in      a,(c)
        bit     0,a
        jr      z,_IntWaitFFHigh        ;then wait for the low to high edge
;
; we've now seen the leading edge of FF - it's just started
;


        im      1
        ld      hl,QUESTMARK_IM1InterruptCounter
        ld      (hl),0                          ;reset the counter

        ld      de,1000h                        ;the time allowed for it
        ei                                      ;let them rip
_IntWaitCount1:
        ld      a,4
        cp      (hl)                            ;has counter gone to 4 yet QUESTMARK_
        jr      z,_IntSeenFirstInterrupt
        dec     de
        ld      a,d
        or      e
        jr      nz,_IntWaitCount1

        ld      b,1                             ;ERROR B=1, no INT happened
_IntFailure
        ld      a,5                             ;an Interrupt type of error
        scf
_IntEndTest:
        di
        jr      c,_IntSkipReTest
        push    af
        ld      a,2
        call    DelayASeconds
        call    OfferRetest
        pop     bc
        jp      c,InterruptTest
        push    bc
        pop     af
_IntSkipReTest:
        di
        ret

_IntSeenFirstInterrupt
;
; We just saw count go 3 -> 4 so an INT leading edge just happened
;
        ld      de,0                            ;gonna count time for 10
_IntWaitForNext10:
        ld      a,104
        cp      (hl)                            ;have we reached 11 yet QUESTMARK_
        jr      z,_IntNowHad11
        inc     de
        jr      _IntWaitForNext10

_IntNowHad11:
;
; At this point DE should have counted the elapsed time for 10 interrupts
;
        di
        ld      hl,7592h                        ;the time we predict
        sbc     hl,de                           ;compare the two
        ld      a,h
        cp      0
        jr      z,_IntHighPeriodOK
        cp      0FFh                            ;may be just -ve
        jr      z,_IntHighPeriodOK
        ld      b,2                             ;ERROR B=2, INT period incorrect
        jr      _IntFailure

_IntHighPeriodOK:
        ld      a,l
        add     a,3                             ;allow +/- 3
        cp      6
        jr      nc,_IntFailure

        xor     a                               ;clear Carry cos its OK
        jr      _IntEndTest

        ld      b,2                             ;ERROR B=3, INT period incorrect
        jr      _IntFailure

