Consider a program which sets the interrupt routine to "ini:ei:ret" and
then waits for one interrupt with c set to 249 and hl set to a storage
area. This will tell us approximately how long the interrupt lasts for by
storing one byte per loop round, and it will also tell us whether the status
register kept its value for the whole interrupt.
Also, by inserting more "ini" instructions we can vary the length of time
taken by the interrupt and so narrow it down further. Or by changing the
"ini" to "inc l" we can shorten the interrupt routine and perhaps get a more
precise answer.
By my estimation it takes 16 cycles to enter the interrupt routine.
SimCoupe agrees with this but doesn't count it towards the length of time
the interrupt is active. The ini takes 20, ei takes 4 and ret takes 12.
(If the screen is active then these times are 24, 32, 8 and 24 respectively
(the first 24 is again my estimate of the interrupt latency) but this is
unimportant as everything seems to take place off the screen even if a line
interrupt is being tested).
An interrupt will not occur between "ei" and "ret" so the interrupt routine
will execute a whole number of times. Timings for various interrupt
routines and the number of times round it goes (in the format "52x3" where
the routine takes 52 cycles and goes round 3 times) based on the above are
as follows.
Code My sam SimCoupe
1. ini:ini:ini:ei:ret 92x2 76x4
2. ini:ini:ei:ret 72x2 56x5
3. ini:ei:ret 52x3 36x7
4. inc l:nop:nop:nop:ei:ret 48x3 32x8
5. inc l:nop:nop:ei:ret 44x3 28x9
6. inc l:nop:ei:ret 40x4 24x11
7. inc l:ei:ret 36x4 20x13
>From this we can gather that SimCoupe keeps the interrupt active for somewhere
between 240 and 252 cycles. But then we know that as it tells us each time on
startup. The real Sam is keeping the interrupt active for somewhere between
120 and 132 cycles.
The values stored in the data area from the ini instruction for the real Sam
are as follows (SimCoupe is uninteresting because the interrupt line is equal
to "status<255").
1. ini:ini:ini:ei:ret 247 247 247 255 255 255
2. ini:ini:ei:ret 247 247 247 255
3. ini:ei:ret 247 247 255
Line 2 is interesting because the two consecutive "ini" instructions gave
different answers on the second time round. This means that if you execute
"ini:ini" exactly 88 cycles after the interrupt occurs then the first
one gives 247 and the second one gives 255. So (based on my approximate
interpretation of the "ini" instruction) the status register keeps its value
for somewhere between 100 and 116 cycles.
The z80 code and a short binary follow. You can test the binary by loading
it at the start of a page (avoiding 49152 because it must start in section
C) - let's say &10000 - and printing its usr (or letting a variable equal
its usr). This value will be the number of bytes saved. You can then print
out that many bytes starting at &10100 (unless you used "inc l" instead of
"ini"). Initially it has "ini:ini:ini:ei:ret" as the interrupt routine -
you can poke any shorter routine in at &10038.
To test the line interrupts you can say "pause 1:out 249,191" just before
calling the routine.
;start on page boundary, execute in section C
di
ld (spstore+&c000),sp
ld sp,&c000
in a,(251)
and 31
or 32
out (250),a
jp lmem
hmem:
ld a,31
out (250),a
ld sp,(spstore+&c000)
ei
ret
spstore:
defw 0
defs 56-p
int:
; getting here takes 16/24 cycles (?)
ini ; 20/32
ei ; 4 /8
ret ; 12/24
; total to service interrupt = 52/88 cycles
lmem:
ld hl,data
push hl
ld bc,249
im1
ei
halt
di
ld (hl),0
pop de
and a
sbc hl,de
ld c,l
ld b,h
jp hmem+&8000
defs 256-p
data:
begin 644 ints.bin
M\^US'<`Q`,#;^^8?]B#3^L-``#X?T_KM>QW`^\D``"@`"?%O/38P,#`P#3(`
J"5MO<'1X*S0-/`#MHNVB[:+[R2$``>4!^0#M1OMV\S8`T:?M4DU$PQ.`
`
end
imc