test-ack-timing.code.asm 6.7 KB

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