test-ack-timing.code.asm 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. ; First battery of tests: Check how long it takes for the VDP to ack the interrupt,
  2. ; and to drive the interrupt line high again.
  3. ;
  4. ; Test at different cycles relative to the interrupt.
  5. TestAckTiming proc
  6. ; We have to fight with Pasmo's operator precedence, where
  7. ; unary minus has lower precedence than addition/subtraction,
  8. ; so -a-b is taken as -(a-b), and in some contexts (-a)-b can't
  9. ; be used, e.g. ld hl,(-a)-b gives error. We had two options:
  10. ; (a) work around it, (b) switch to --brackets mode and use
  11. ; parentheses. We chose (a): using +(-a)-b works and 0-a-b works.
  12. ; Synchronize with the interrupt, start counting on return
  13. call SyncVInt
  14. ; SyncVInt returns with di, 9T after interrupt, IntVec trashed, ack pending
  15. ; *** First test ***
  16. ; Check how far away from the interrupt we can acknowledge
  17. ; 9T ; from SyncVInt
  18. in a,(99h) ; 12T ; Acknowledge the interrupt that SyncVInt left pending
  19. ld hl,_IntSCF ; 11T
  20. ld (IntVec),hl ; 17T
  21. ld ix,AckAfterInt ; 16T
  22. ld (ix+0),22 ; 21T ; Start 22 cycles after interrupt (13 for INT in IM1
  23. ; + 9 for minimal IN, is the fastest it can be
  24. ; acknowledged in an ISR)
  25. ld hl,0-124+22 ; 11T ; Cycles used at input time, and starting value of [AckBeforeInt]
  26. _AckTestLoop: call WaitFrmPlusHL ; 18T
  27. in a,(99h) ; 9T ; pre input
  28. ; > input signalled here (IORQ and RD go low)
  29. ; first time: 9+12+11+17+16+21+11+18+9 = 124
  30. ; subsequent times: 3+20+20+5+5+5+ 0+5+13+(-5)+11+25+13 + 18+9 = 147 (no int)
  31. ; 3T ; post input
  32. ex (sp),hl ; 20T
  33. ex (sp),hl ; 20T
  34. xor a ; 5T
  35. ei ; 5T
  36. nop ; 5T ; Interrupt should be detected immediately after this
  37. ; 0T/46T ; 19(IM2)+11(JP)+5(SCF)+11(RET)=46T from interrupt, if it happens
  38. di ; 5T
  39. jr c,_AckFailed ; 13T ; If an interrupt happens, it means the ack fails
  40. ; -5T ; for taking the false branch
  41. ; The ack succeeded and stopped the interrupt.
  42. ld hl,0-147-1 ; 11T ; Total cycles during repeat + move the ack 1T earlier
  43. dec (ix+0) ; 25T
  44. jr _AckTestLoop ; 13T
  45. ; *** Second test ***
  46. ; Check where, in relation to the interrupt, bit 7 of the status register turns on
  47. ; 122T ; So far: 3+20+20+5+5+5+46+5+13 - (ix+0)
  48. _AckFailed:
  49. in a,(99h) ; 12T ; Ack the interrupt for which the previous ack failed
  50. ld a,(ix+0) ; 21T ; This is the number of cycles with respect to the reference position
  51. inc (ix+0) ; 25T ; Correct the value so it shows the first ACK, not the last non-ACK
  52. neg ; 10T
  53. ld e,a ; 5T
  54. rlca ; 5T ; Read sign bit
  55. sbc a,a ; 5T
  56. ld d,a ; 5T ; Sign-extend E to DE
  57. ld (ix+1),-20 ; 21T ; Start 20 frames before the interrupt
  58. ld hl,0-310-20 ; 11T ; 122+12+21+25+10+5+5+5+5+21+11+12+21+8+18+9 = 310
  59. add hl,de ; 12T
  60. ld (ix+2),0 ; 21T ; Flag to detect falling edges, as they shouldn't happen
  61. ; Used as temporary (can be overwritten in the next routine)
  62. ld b,24 ; 8T ; How many cycles into it to run
  63. ; Alternative version that wastes up to 5 frames during syncing, used to validate the above:
  64. ; inc (ix+0) ; Correct the value so it shows the first ACK, not the last non-ACK
  65. ; ld (ix+1),-16 ; 21T
  66. ; ld (ix+2),0 ; 21T
  67. ; in a,(99h) ; Clear pending int
  68. ; ld b,40 ; 8T
  69. ; call SyncVInt
  70. ; ; 9T ; after interrupt
  71. ; in a,(99h) ; 12T ; Clear int
  72. ; ld hl,0-59-16 ; 11T ; 9+12+11+18+9 = 59
  73. _LoopFindOnes: call WaitFrmPlusHL ; 18T
  74. in a,(99h) ; 9T ; pre input
  75. ; 3T ; post input
  76. rlca ; 5T
  77. sbc a,a ; 5T
  78. ld e,a ; 5T
  79. cpl ; 5T
  80. and (ix+2) ; 21T ; nonzero if it was set and now reset
  81. ld (ix+2),e ; 21T ; Store current value
  82. ld a,3 ; 8T ; Error code 3: "Falling edge while testing bit 7 of status reg"
  83. jp nz,Finish ; 11T
  84. ld a,(ix+1) ; 21T
  85. add a,e ; 5T ; Increment if e=0, part 1/2 (note e is either 0 or -1)
  86. inc a ; 5T ; Increment if e=0, part 2/2
  87. ld (ix+1),a ; 21T
  88. in a,(99h) ; 12T ; Ack any pending interrupt
  89. 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
  90. djnz _LoopFindOnes ; 14T
  91. ; -5T ; for false condition
  92. ; *** Third test
  93. ; Check how many cycles pass until the ack is effective and the INT is no longer retriggered.
  94. ; We can't test this as effectively as we wish; the time seems very short but at least 3 cycles
  95. ; (at least in the machines we've tried), and we don't have enough control for the cycle count.
  96. ;
  97. ; Approach:
  98. ; 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.
  99. ; 2) test whether it's up to 5 cycles by using EI / INI.
  100. ; 3) test whether it's up to 10 cycles by using EI / INIR with a repeat > 1.
  101. ; 4) test whether it's up to 12 cycles by using IN A,(99h) / EI / NOP.
  102. ; 5) test whether it's up to 13 cycles by using SCF / IN A,(99h) / EI / RET NC.
  103. ; 6) test whether it's up to 14 cycles by using IN A,(99h) / EI / INC HL.
  104. ; 7) test whether it's up to 15 cycles by using IN A,(99h) / EI / OR 0.
  105. ; Else it's 16 or longer (dubious).
  106. ; 1) test 0 to 2 cycles
  107. ; Last int was acked, so we need another one
  108. ld hl,_IntSCF + 1 ; point to a RET
  109. ld (IntVec),hl
  110. ei
  111. halt
  112. ld (ix+2),0
  113. ld (ix+3),2
  114. ld hl,_IntINCE
  115. ld (IntVec),hl
  116. ld e,0 ; Flag to detect interrupt
  117. ei
  118. in a,(99h)
  119. di
  120. dec e
  121. ret nz ; If int not triggered, it's 0 to 2 cycles
  122. ; 2) Test 3 to 5 cycles
  123. ld hl,_IntSCF + 1 ; point to a RET
  124. ld (IntVec),hl
  125. ei
  126. halt
  127. ld hl,_IntINCE
  128. ld (IntVec),hl
  129. ld (ix+2),3
  130. ld (ix+3),5
  131. ld d,8
  132. ld hl,ScratchAck ; use as scratch area
  133. ld bc,99h
  134. ei
  135. ini
  136. di
  137. dec e
  138. ret nz ; Int not triggered, it's 3 to 5 cycles.
  139. ; 3) Test 6 to 10 cycles
  140. ld hl,_IntSCF + 1 ; point to a RET
  141. ld (IntVec),hl
  142. ei
  143. halt
  144. ld hl,_IntINCE
  145. ld (IntVec),hl
  146. ld (ix+2),6
  147. ld (ix+3),10
  148. ld hl,ScratchAck
  149. ld bc,299h
  150. ei
  151. inir ; If int happens, must be in 1st one, so min 11T, else 6-10.
  152. di
  153. dec e
  154. ret nz
  155. ; 4) Test 11 to 12 cycles
  156. ld hl,_IntSCF + 1 ; point to a RET
  157. ld (IntVec),hl
  158. ei
  159. halt
  160. ld hl,_IntINCE
  161. ld (IntVec),hl
  162. ld (ix+2),11
  163. ld (ix+3),12
  164. in a,(99h)
  165. ei
  166. nop
  167. di
  168. dec e
  169. ret nz
  170. ; 5) Test 13 cycles
  171. ld hl,_IntSCF + 1 ; point to a RET
  172. ld (IntVec),hl
  173. ei
  174. halt
  175. ld hl,_IntINCE
  176. ld (IntVec),hl
  177. ld (ix+2),13
  178. ld (ix+3),13
  179. scf
  180. in a,(99h)
  181. ei
  182. ret nc ; always false
  183. di
  184. dec e
  185. ret nz
  186. ; 6) Test 14 cycles
  187. ld hl,_IntSCF + 1 ; point to a RET
  188. ld (IntVec),hl
  189. ei
  190. halt
  191. ld hl,_IntINCE
  192. ld (IntVec),hl
  193. ld (ix+2),14
  194. ld (ix+3),14
  195. scf
  196. in a,(99h)
  197. ei
  198. inc hl
  199. di
  200. dec e
  201. ret nz
  202. ; 7) Test 15 cycles
  203. ld hl,_IntSCF + 1 ; point to a RET
  204. ld (IntVec),hl
  205. ei
  206. halt
  207. ld hl,_IntINCE
  208. ld (IntVec),hl
  209. ld (ix+2),15
  210. ld (ix+3),15
  211. scf
  212. in a,(99h)
  213. ei
  214. or 0
  215. di
  216. dec e
  217. ret nz
  218. ; Must be 16 or more
  219. ld (ix+2),16
  220. ld (ix+3),-1
  221. ret
  222. _IntSCF: scf ; 5T
  223. ret ; 11T
  224. _IntINCE: inc e ; 5T
  225. ret
  226. endp