123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- ; First battery of tests: Check how long it takes for the VDP to ack the interrupt,
- ; and to drive the interrupt line high again.
- ;
- ; Test at different cycles relative to the interrupt.
- TestAckTiming proc
- local AckTestLoop, AckFailed
- local LoopFindOnes
- local IntSCF, intINCE
- ; We have to fight with Pasmo's operator precedence, where
- ; unary minus is lower precedence that addition/subtraction,
- ; so -a-b is taken as -(a-b), and in some contexts (-a)-b can't
- ; be used, e.g. ld hl,(-a)-b gives error. We had two options:
- ; (a) work around it, (b) switch to --brackets mode and use
- ; parentheses. We chose (a): using +(-a)-b works and 0-a-b works.
- ; Synchronize with the interrupt, start counting on return
- call SyncVInt
- ; SyncVInt returns with di, 9T after interrupt, IntVec trashed, ack pending
- ; *** First test ***
- ; Check how far away from the interrupt we can acknowledge
- ; 9T ; from SyncVInt
- in a,(99h) ; 12T ; Acknowledge the interrupt that SyncVInt left pending
- ld hl,IntSCF ; 11T
- ld (IntVec),hl ; 17T
- ld ix,AckTiming ; 16T
- ld (ix+0),22 ; 21T ; Start 22 cycles after interrupt (13 for INT in IM1
- ; + 9 for minimal IN, is the fastest it can be
- ; acknowledged in an ISR)
- ld hl,0-124+22 ; 11T ; Cycles used at input time, and starting value of [AckTiming+0]
- AckTestLoop: call WaitFrmPlusHL ; 18T
- in a,(99h) ; 9T ; pre input
- ; > input signalled here (IORQ and RD go low)
- ; first time: 9+12+11+17+16+21+11+18+9 = 124
- ; subsequent times: 3+20+20+5+5+5+ 0+5+13+(-5)+11+25+13 + 18+9 = 147 (no int)
- ; 3T ; post input
- ex (sp),hl ; 20T
- ex (sp),hl ; 20T
- xor a ; 5T
- ei ; 5T
- nop ; 5T ; Interrupt should be detected immediately after this
- ; 0T/46T ; 19(IM2)+11(JP)+5(SCF)+11(RET)=46T from interrupt, if it happens
- di ; 5T
- jr c,AckFailed ; 13T ; If an interrupt happens, it means the ack fails
- ; -5T ; for taking the false branch
- ; The ack succeeded and stopped the interrupt.
- ld hl,0-147-1 ; 11T ; Total cycles during repeat + move the ack 1T earlier
- dec (ix+0) ; 25T
- jr AckTestLoop ; 13T
- ; *** Second test ***
- ; Check where, in relation to the interrupt, bit 7 of the status register turns on
- ; 122T ; So far: 3+20+20+5+5+5+46+5+13 - (ix+0)
- AckFailed:
- in a,(99h) ; 12T ; Ack the interrupt for which the previous ack failed
- ld a,(ix+0) ; 21T ; This is the number of cycles with respect to the reference position
- inc (ix+0) ; 25T ; Correct the value so it shows the first ACK, not the last non-ACK
- neg ; 10T
- ld e,a ; 5T
- rlca ; 5T
- sbc a,a ; 5T
- ld d,a ; 5T
- ld (ix+1),-20 ; 21T ; Start 20 frames before the interrupt
- ld hl,0-310-20 ; 11T ; 122+12+21+25+10+5+5+5+5+21+11+12+21+8+18+9 = 310
- add hl,de ; 12T
- ld (ix+2),0 ; 21T ; Flag to detect falling edges, as they shouldn't happen
- ; Used as temporary (can be overwritten in the next routine)
- ld b,24 ; 8T ; How many cycles into it to run
- ; Alternative version that wastes up to 5 frames during syncing, used to validate the above:
- ; inc (ix+0) ; Correct the value so it shows the first ACK, not the last non-ACK
- ; ld (ix+1),-16 ; 21T
- ; ld (ix+2),0 ; 21T
- ; in a,(99h) ; Clear pending int
- ; ld b,40 ; 8T
- ; call SyncVInt
- ; ; 9T ; after interrupt
- ; in a,(99h) ; 12T ; Clear int
- ; ld hl,0-59-16 ; 11T ; 9+12+11+18+9 = 59
- LoopFindOnes: call WaitFrmPlusHL ; 18T
- in a,(99h) ; 9T ; pre input
- ; 3T ; post input
- rlca ; 5T
- sbc a,a ; 5T
- ld e,a ; 5T
- cpl ; 5T
- and (ix+2) ; 21T ; nonzero if it was set and now reset
- ld (ix+2),e ; 21T ; Store current value
- ld a,3 ; 8T ; Error code 3: "Falling edge while testing bit 7 of status reg"
- jp nz,Finish ; 11T
- ld a,(ix+1) ; 21T
- add a,e ; 5T ; Increment if e=0, part 1/2 (note e is either 0 or -1)
- inc a ; 5T ; Increment if e=0, part 2/2
- ld (ix+1),a ; 21T
- in a,(99h) ; 12T ; Ack any pending interrupt
- ld hl,0-200+1 ; 11T ; 3+5+5+5+5+21+21+8+11+21+5+5+21+12+11+14 + 18+9 = 200, +1 to shift phase forwards
- djnz LoopFindOnes ; 14T
- ; -5T ; for false condition
- ; *** Third test
- ; Check how many cycles pass until the ack is effective and the INT is no longer retriggered.
- ; We can't test this as effectively as we wish; the time seems very short but at least 3 cycles
- ; (at least in the machines we've tried), and we don't have enough control for the cycle count.
- ;
- ; Approach:
- ; 1) test whether it's up to 2 cycles by using EI / IN A,(99h). No int means the ack takes 0 to 2 cycles.
- ; 2) test whether it's up to 5 cycles by using EI / INI.
- ; 3) test whether it's up to 10 cycles by using EI / INIR with a repeat > 1.
- ; 4) test whether it's up to 12 cycles by using IN A,(99h) / EI / NOP.
- ; 5) test whether it's up to 13 cycles by using SCF / IN A,(99h) / EI / RET NC.
- ; 6) test whether it's up to 14 cycles by using IN A,(99h) / EI / INC HL.
- ; 7) test whether it's up to 15 cycles by using IN A,(99h) / EI / OR 0.
- ; Else it's 16 or longer (dubious).
- ; 1) test 0 to 2 cycles
- ; Last int was acked, so we need another one
- ld hl,IntSCF + 1 ; point to a RET
- ld (IntVec),hl
- ei
- halt
- ld (ix+2),0
- ld (ix+3),2
- ld hl,IntINCE
- ld (IntVec),hl
- ld e,0 ; Flag to detect interrupt
- ei
- in a,(99h)
- di
- dec e
- ret nz ; If int not triggered, it's 0 to 2 cycles
- ; 2) Test 3 to 5 cycles
- ld hl,IntSCF + 1 ; point to a RET
- ld (IntVec),hl
- ei
- halt
- ld hl,IntINCE
- ld (IntVec),hl
- ld (ix+2),3
- ld (ix+3),5
- ld d,8
- ld hl,AckTiming+4 ; use as scratch area
- ld bc,99h
- ei
- ini
- di
- dec e
- ret nz ; Int not triggered, it's 3 to 5 cycles.
- ; 3) Test 6 to 10 cycles
- ld hl,IntSCF + 1 ; point to a RET
- ld (IntVec),hl
- ei
- halt
- ld hl,IntINCE
- ld (IntVec),hl
- ld (ix+2),6
- ld (ix+3),10
- ld hl,AckTiming+4
- ld bc,299h
- ei
- inir ; If int happens, must be in 1st one, so min 11T, else 6-10.
- di
- dec e
- ret nz
- ; 4) Test 11 to 12 cycles
- ld hl,IntSCF + 1 ; point to a RET
- ld (IntVec),hl
- ei
- halt
- ld hl,IntINCE
- ld (IntVec),hl
- ld (ix+2),11
- ld (ix+3),12
- in a,(99h)
- ei
- nop
- di
- dec e
- ret nz
- ; 5) Test 13 cycles
- ld hl,IntSCF + 1 ; point to a RET
- ld (IntVec),hl
- ei
- halt
- ld hl,IntINCE
- ld (IntVec),hl
- ld (ix+2),13
- ld (ix+3),13
- scf
- in a,(99h)
- ei
- ret nc ; always false
- di
- dec e
- ret nz
- ; 6) Test 14 cycles
- ld hl,IntSCF + 1 ; point to a RET
- ld (IntVec),hl
- ei
- halt
- ld hl,IntINCE
- ld (IntVec),hl
- ld (ix+2),14
- ld (ix+3),14
- scf
- in a,(99h)
- ei
- inc hl
- di
- dec e
- ret nz
- ; 7) Test 15 cycles
- ld hl,IntSCF + 1 ; point to a RET
- ld (IntVec),hl
- ei
- halt
- ld hl,IntINCE
- ld (IntVec),hl
- ld (ix+2),15
- ld (ix+3),15
- scf
- in a,(99h)
- ei
- or 0
- di
- dec e
- ret nz
- ; Must be 16 or more
- ld (ix+2),16
- ld (ix+3),-1
- ret
- IntSCF: scf ; 5T
- ret ; 11T
- IntINCE: inc e ; 5T
- ret
- endp
|