forked from MrKWatkins/ZXSpectrumNextTests
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathint_skip.asm
411 lines (377 loc) · 14 KB
/
int_skip.asm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
; (C): copyright 2022 Peter Ped Helcmanovsky, license: MIT
; name: test to see if block of bytes XX (0xDD/0xFD/0x00/0xDD+0xFD) does inhibit processing of /INT signal
; public git repo: https://github.com/MrKWatkins/ZXSpectrumNextTests/
;
; to assemble (with z00m's sjasmplus https://github.com/z00m128/sjasmplus/ v1.19.0+)
; run: sjasmplus int_skip.asm
;
; history: 2022-05-16: v3.0 - added "ISR entries per /INT signal" check (should be 2+ if emulating /INT as 32T signal)
; - set resulting BORDER color also into sysvar, so will retain in BASIC
; 2022-02-19: v2.1 - adding EI and DI test blocks, removing the sync-halt induced +1 from counter
; 2022-02-19: v2.0 - complete rewrite of test logic:
; * result is displayed as text OK/ERR and returns to BASIC
; * test works also on faster machines (up to 30MHz) with slightly unstable IRQ period
; 2022-02-17: v1.0 - initial version
;
; purpose: to check if Z80 skips interrupt when it is inside long block of XX prefixes,
; where XX is one of the DD/FD values and miscellaneous others (nop,ei,di,...)
;
; ISR entries number depends on CPU frequency, but with 3.5MHz and 32T /INT it should count at least two entries.
; (if "1" is displayed, the emulator/machine does end /INT upon interrupt ACK, triggering it only once per frame)
;
CLEAR_ADR EQU $8FFF ; 36863 - have BASIC stack high in uncontended memory (just in case)
XX_BLOCK_SZ EQU 40*256 ; 10240 bytes of XX prefix = ~41k T per one run
ROM_ATTR_P: EQU $5C8D
ROM_BORDCR: EQU $5C48
ROM_CLS: EQU $0DAF
ROM_PRINT: EQU $203C
OPT --syntax=abf
DEVICE ZXSPECTRUM48,CLEAR_ADR
TEST_FLAG_BENCHMARK EQU 1
TEST_FLAG_ALLOWS EQU 2
TEST_FLAG_INHIBITS EQU 3
STRUCT S_TEST_DATA
prefix1 BYTE
prefix2 BYTE
counter BYTE
name TEXT 8, { ' ' }
flag BYTE TEST_FLAG_BENCHMARK
ENDS
ORG $9000
code_start:
ASSERT CLEAR_ADR < $
; CLS + print info text
ld a,7<<3
ld (ROM_ATTR_P),a ; ATTR-P = PAPER 7 : INK 0 : BRIGHT 0 : FLASH 0
call ROM_CLS
ld de,head_txt ; text at top of screen
ld bc,head_txt.sz
call ROM_PRINT
di
; setup IM2 - create 257 byte table
ld hl,im2_ivt
.set_ivt:
ld (hl),low im2_isr
dec l
jr nz,.set_ivt
inc h
ld (hl),low im2_isr
; setup IM2 - rest of the setup
ld a,high im2_ivt
ld i,a
im 2
; IM2 re-entry test (roughly measuring /INT signal lenth or whether it's "trigger" once-only)
ld hl,im2_isr_entries_test
ld de,im2_isr
ld bc,im2_isr_entries_test.sz
ldir ; setup IM2 handler fore re-entry test
ld b,8 ; try 8 times to measure the max, here C = 0 (max)
ei
re_entry_test:
halt
djnz re_entry_test
; print result of re-entrance test
di
ld a,c
call printDecimalA
ld de,head_txt2 ; remaining header text
ld bc,head_txt2.sz
call ROM_PRINT
; setup IM2 handler for block-test
ld hl,im2_isr_block_test
ld de,im2_isr
ld bc,im2_isr_block_test.sz
ldir
; start testing of different blocks
ld a,low txt_verdict_s_ok
ld (global_err_flag),a ; reset global error flag
ld ix,test_data
mainloop:
; run the test + output results for current S_TEST_DATA at IX
call run_test
; repeat test through all defined blocks
ld bc,S_TEST_DATA
add ix,bc
ld a,low test_data.end
cp ixl
jr nz,mainloop
; change border color depending on the global result
ld a,(global_err_flag)
cp low txt_verdict_s_ok
ld a,4
jr z,.all_ok
ld a,2
.all_ok:
out ($FE),a ; border green/red
.3 add a,a
ld (ROM_BORDCR),a ; set also BASIC sysvar for border color
; return back to basic
im 1
ld a,$3F
ld i,a
ei
ret
txt_verdict_benchmark:
DB " | |benchmark\r"
.sz EQU $-txt_verdict_benchmark
txt_verdict_s_ok:
DB " |OK |"
txt_verdict_s_err:
DB " |ERR|"
txt_verdict.small_sz EQU $-txt_verdict_s_err
; In: IX = S_TEST_DATA pointer
set_block_and_run_test:
ld hl,xx_block
; fill the stack Nx with xx_block address to Nx execute the block as part of test
ld b,3500000/40000 ; run the block for 1 second at 3.5MHz (~6 frames at 28MHz, ~50 frames at 3.5MHz)
.fill_stack:
push hl
djnz .fill_stack ; all of this will be executed after `ret` is reached in each xx_block
; setup the block itself (fill memory with prefix data)
ld b,high (XX_BLOCK_SZ) ; set about XX_BLOCK_SZ bytes
ld e,(ix+S_TEST_DATA.prefix1)
ld d,(ix+S_TEST_DATA.prefix2)
.set_loop:
ld (hl),e
inc l
ld (hl),d
inc l
jr nz,.set_loop
inc h
djnz .set_loop
; append `nop : ret` after the block
ld (hl),b ; nop
inc l
ld (hl),$C9 ; ret
; sync with halt, reset counter, and run the prefix blocks N times
ei
halt
ld (ix+S_TEST_DATA.counter),b ; reset counter to 0 (after halt did already modify it once)
ret
; IM2 interrupt handler 1 - testing block instruction inhibition
im2_isr_block_test:
DISP im2_isr
push af
inc (ix+S_TEST_DATA.counter) ; increment the test-counter
pop af
ei
ret
ENT
.sz: EQU $-im2_isr_block_test
; IM2 interrupt handler 2 - checking re-entrance with long-enough /INT signal
im2_isr_entries_test:
DISP im2_isr
ei
nop ; 8T until ready for re-entry of handler (ACK is 11T, so 19T total)
; reaching here when INT goes back up, count entries by examining stack content
ASSERT 0 == (0x200 & re_entry_test) && 0x200 == (0x200 & im2_isr)
; B = test-loop counter, C = 0 (max)
xor a ; current counter
.count_entries:
inc a
pop hl
bit 1,h ; 0x92xx return address => count as re-entry
jr nz,.count_entries
cp c
jr c,.keep_old_max
ld c,a ; new max re-entry
.keep_old_max:
jp (hl) ; return back into test-loop
ENT
.sz: EQU $-im2_isr_entries_test
; In: IX = S_TEST_DATA pointer
; Out: (IX + S_TEST_DATA.counter) = count of Interrupts during running test sequence
run_test:
; set the block to designed prefix and run it N-times, before returning here
call set_block_and_run_test
di
; print result - name
ld de,ix ; fake ; ld e,ixl : ld d,ixh
ld hl,S_TEST_DATA.name
add hl,de
ex de,hl ; DE = test.name
ld bc,S_TEST_DATA.flag-S_TEST_DATA.name
call ROM_PRINT
ld de,txt_verdict_benchmark+1 ; use part of this for framing
ld bc,2
call ROM_PRINT
; print result - count
ld a,(ix+S_TEST_DATA.counter)
ld c,a
call printDecimalA
ld b,(ix+S_TEST_DATA.flag)
djnz .not_benchmark
; TEST_FLAG_BENCHMARK - print fixed string
ld de,txt_verdict_benchmark
ld bc,txt_verdict_benchmark.sz
jp ROM_PRINT
.not_benchmark:
; check if block did inhibit ISR or not
ld a,(test_data.counter) ; NOP-block counter
srl a
cp c ; (NOP.counter/2) - test.counter
sbc a,a ; A = 00 inhibits / FF allows
ld c,a
; check if the result is as expected by test: OK/ERR verdict
djnz .allows_expected ; TEST_FLAG_INHIBITS
cpl ; TEST_FLAG_ALLOWS
.allows_expected:
; A = 00 ok / FF error
push bc
ld de,txt_verdict_s_ok
ld bc,txt_verdict.small_sz
ASSERT (high txt_verdict_s_ok == high txt_verdict_s_err) && (txt_verdict_s_err == txt_verdict_s_ok + txt_verdict.small_sz)
and c
add a,e
ld e,a ; DE = txt_verdict_s_ok / txt_verdict_s_err
; tamper global error flag in case there was error in any test
ld a,(global_err_flag)
ASSERT low txt_verdict_s_ok != low txt_verdict_s_ok | low txt_verdict_s_err ; if this fails, use `and e` instead
or e
ld (global_err_flag),a
; print OK/ERR verdict
call ROM_PRINT
; print long description verdict
pop af ; CF=0 inhibits / 1 allows
ASSERT txt_verdict_allows == txt_verdict_inhibits + txt_verdict.long_sz
ld hl,txt_verdict_inhibits
ld bc,txt_verdict.long_sz
jr nc,.did_inhibit
add hl,bc ; HL = txt_verdict_allows
.did_inhibit:
ex de,hl
jp ROM_PRINT
printDecimalA:
push bc
ld e,' ' ; align with space
ld bc,$FF00 | 100 ; b = -1, c = 100
call .FindAndOutDigitOrSpace
ld a,c
ld bc,$FF00 | 10 ; b = -1, c = 10
call .FindAndOutDigitOrSpace
; output final digit (even zero)
ld a,c
pop bc
jr .OutDecDigit
.FindAndOutDigitOrSpace:
inc b
sub c ; if A is less than current 10th power, CF will be set
jr nc,.FindAndOutDigitOrSpace
add a,c ; fix A back above zero (B is OK, as it started at -1)
ld c,a
ld a,b
add a,e ; test also against previously displayed digits, to catch any non-zero
ld e,a ; remember the new mix
cp ' '
jp z,$10 ; if still no non-zero digit was printed, print space
ld a,b
.OutDecDigit:
add a,'0'
rst $10
ret
txt_verdict_inhibits:
DB "inhibits ISR\r"
txt_verdict_allows:
DB "allows ISR \r"
txt_verdict.long_sz EQU $-txt_verdict_allows
head_txt:
DB "v3.0 2022-05-16 Ped7g, count\r"
DB "interrupts while executing\r"
DB "long block of DD/FD prefixes\r\r"
DB "ISR entries per /INT signal:"
.sz EQU $-head_txt
head_txt2:
DB "\r\rblock of|count|verdict\r"
DB "--------+-----+----------------\r"
.sz EQU $-head_txt2
test_data S_TEST_DATA { $00, $00, $00, {"NOP"}, TEST_FLAG_BENCHMARK } ; nop chain
S_TEST_DATA { $DD, $DD, $00, {"DD"}, TEST_FLAG_INHIBITS } ; DD chain
S_TEST_DATA { $FD, $FD, $00, {"FD"}, TEST_FLAG_INHIBITS } ; FD chain
S_TEST_DATA { $DD, $FD, $00, {"DDFD"}, TEST_FLAG_INHIBITS } ; DDFD chain
S_TEST_DATA { $37, $3F, $00, {"SCF+CCF"}, TEST_FLAG_ALLOWS } ; scf, ccf chain (verify NOP-like)
S_TEST_DATA { $FB, $FB, $00, {"EI"}, TEST_FLAG_INHIBITS } ; ei chain
S_TEST_DATA { $F3, $F3, $00, {"DI"}, TEST_FLAG_INHIBITS } ; di chain (your emulator +1 here = LOL)
.end
code_end: ; this is enough to store into TAP file, rest is initialised by code
global_err_flag:
ds 1
; IM2 interrupt handler (must start at specific $xyxy address)
IF low $ <= high $
DS high $ - low $, 0 ; pad to $xyxy address for im2_isr
ELSE
DS (high $ - low $) + 257, 0 ; pad to $xyxy address for im2_isr
ENDIF
im2_isr:
ASSERT low im2_isr == high im2_isr
DS im2_isr_block_test.sz >? im2_isr_entries_test.sz
ALIGN 256
im2_ivt:
ds 257
ALIGN 256
xx_block:
ds XX_BLOCK_SZ+16
;; produce SNA file with the test code
SAVESNA "int_skip.sna", code_start
tkCODE EQU $AF
tkUSR EQU $C0
tkLOAD EQU $EF
tkCLEAR EQU $FD
tkRANDOMIZE EQU $F9
tkREM EQU $EA
;; produce TAP file with the test code
DEFINE tape_file "int_skip.tap"
DEFINE prog_name "int_skip"
;; 10 CLEAR 36863:LOAD "int_skip"CODE
;; 20 RANDOMIZE USR 36864
ORG $5C00
tap_bas:
DB 0,10 ;; Line number 10
DW .l10ln ;; Line length
ASSERT 36863 == CLEAR_ADR
.l10: DB tkCLEAR,"36863",$0E,0,0,low (CLEAR_ADR),high (CLEAR_ADR),0,':'
DB tkLOAD,'"'
.fname: DB prog_name
ASSERT ($ - .fname) <= 10
DB '"',tkCODE,"\r"
.l10ln: EQU $-.l10
DB 0,20 ;; Line number 20
DW .l20ln
.l20: DB tkRANDOMIZE,tkUSR,"36864",$0E,0,0,low code_start,high code_start,0,"\r"
.l20ln: EQU $-.l20
DB 0,99 ;; Line number 99
DW .l99ln
.l99: DB tkREM,"https://github.com/MrKWatkins/ZXSpectrumNextTests/\r"
.l99ln: EQU $-.l99
.l: EQU $-tap_bas
EMPTYTAP tape_file
SAVETAP tape_file,BASIC,prog_name,tap_bas,tap_bas.l,1
SAVETAP tape_file,CODE,prog_name,code_start,code_end-code_start,code_start
;; produce TRD file with the test code
DEFINE trd_file "int_skip.trd"
;; 10 CLEAR 36863:RANDOMIZE USR 15619:REM:LOAD "int_skip"CODE
;; 20 RANDOMIZE USR 36864
ORG $5C00
trd_bas:
DB 0,10 ;; Line number 10
DW .l10ln ;; Line length
ASSERT 36863 == CLEAR_ADR
.l10: DB tkCLEAR,"36863",$0E,0,0,low (CLEAR_ADR),high (CLEAR_ADR),0,':'
DB tkRANDOMIZE,tkUSR,"15619",$0E,0,0,low 15619,high 15619,0,':'
DB tkREM,':',tkLOAD,'"'
.fname: DB "int_skip"
ASSERT ($ - .fname) <= 8
DB '"',tkCODE,$0D
.l10ln: EQU $-.l10
DB 0,20 ;; Line number 20
DW .l20ln
ASSERT 36864 == code_start
.l20: DB tkRANDOMIZE,tkUSR,"36864",$0E,0,0,low code_start,high code_start,0,"\r"
.l20ln: EQU $-.l20
DB 0,99 ;; Line number 99
DW .l99ln
.l99: DB tkREM,"https://github.com/MrKWatkins/ZXSpectrumNextTests/\r"
.l99ln: EQU $-.l99
.l: EQU $-trd_bas
EMPTYTRD trd_file
SAVETRD trd_file,"boot.B",trd_bas,trd_bas.l,10
SAVETRD trd_file,"int_skip.C",code_start,code_end-code_start