Hi again,
Interrupts are REEAALLY interesting stuff, aren't they?
> > The interrupt routine should end with an instruction that re-enables the
> > interrupts. I think RETI does that. Anyway, the MSX BIOS uses EI followed
> > by RET instead.
>
> Yup. That the BIOS uses EI is very weird. But play it safe, and use EI; RET
> like they do :)
Does RETI re-enable interrupts or not? Is this so difficult to
determine? I think most Z80 programmers will know there are two flags
that determine wheter (maskable!) interrupts are accepted, or not.
These are called IFF1 and IFF2. I think everyone will agree that EI
sets both, and DI resets both. At a reset of the Z80 CPU, these are
both cleared (0 = DI). Note that for instance the MSX BIOS ROM
starts at address 0 with a DI (I think it was at address 0 itself).
Why? Well simple, because when you get to address 0, this is not
always as a result of power on, or reset, but might also be the
result of some jump to address 0 ("warm boot"). You would like
things to work the same after that, right? So that's what this DI
is for...
During a NON-maskable interrupt, these 2 IFF1 & 2 CAN differ, but not
in the case of MASKABLE interrupts (the ones you have in the MSX). At
least that simplifies things a bit.
At the acceptance of a (maskable!) interrupt, IFF1 and IFF2 are both
cleared (=DI), and must be set again after that by the
programmer/interrupt-processing routine.
But does RETI do this, or is a seperate EI needed for this?
Well, a RETI cannot restore the interrupt status from one of these
IFF1 or 2, because in that case they might be different during
maskable interrupts, and they're not. ANY data on the Z80 is quite
clear these 2 can only differ in the case of NON-maskable interrupts.
So, a RETI then either works as a RET, OR as an EI/RET combination.
Which will it be? Let's find out by just using a RETI in place of a
RET, using this for a mini-subroutine, and check whether it
re-enabled interrupts, or not. Let's try this piece of code, for
instance:
sub:
RETI
test:
DI ;clear both IFF's
CALL sub ;and return, and EI?
NOP
NOP ;just to avoid any complications
HALT ;that should make things clear
RET ;back to BASIC/DOS whatever?
If RETI does re-enable interrupts, the HALT would just give a
1-interrupt delay, and continue after that.
If RETI does not re-enable interrupts, this HALT will bring the
machine really to a HALT (hangs)
Now just POKE this stuff somewhere, CALL it at the "test:" address,
and either of these 2 things will happen:
-'Ok': RETI must have re-enabled the interrupts
-hangs: RETI seems to have left the interrupts disabled
Ofcourse, don't try this on your emulator, but on a real Z80...
(will somebody try it on a R800 as well? Just for the fun of it?
Ofcourse, use some R800 mode, and the result will be the same, no
doubt)
I found this interesting enough to try it myself, I used a Sony
HB-F9P for the purpose (there is a real Z80 in there somewhere).
The result: the above piece of code made the machine HANG.
So that leaves no doubt on this tiny issue: RETI does NOT, I repeat
NOT re-enable interrupts, it works just like a normal RET, and does
NOT, I repeat NOT include an EI-function. I said this earlier, the
above proves I was right, at least on this one.
To make sure I didn't make a mistake, I tried this again, replacing
the 1st NOP in the above example with an EI. As expected: "Ok"
Then, out of curiosity, I took the same piece of code, and replaced
only the second NOP with an EI (the first one back to "NOP"), giving
an EI-HALT directly after each other. With interrupts disabled before
that, theory would suggest: EI -> 1 instruction to be executed first
(the HALT here), with interrupts still disabled, then re-enabled
again. This should hang the machine. To my surprise, it did not.
(removing the CALL to this RETI gave the same result). What's going
on in this case?
I found the answer to this in my Z80 databook again, I quote: "While
in the HALT state, the processor will execute NOP's to maintain
memory refresh logic."
In other words: In this case, when entering this HALT, interrupts ARE
still disabled for one more instruction, but the HALT works as a
sequence of NOP's (until an interrupt occurs). After the first
simulated NOP, interrupts ARE re-enabled, allowing a next interrupt
to complete the HALT instruction. Ofcourse, at that point, first the
interrupt routine is processed, and only then the instructions after
the HALT get executed.
Why does a MSX BIOS interrupt routine end with a EI-RET combination
then?
a) I guess the "EI" serves as a 'just in case', in case interrupts
are still disabled at that point, and
b) The RET after that is simply shorter, and faster than a RETI
The 'signal' function of a RETI mentioned earlier by someone, is
simply not used in any MSX hardware, so ending the interrupt routine
with a normal RET just makes it a tiny bit faster.
The rest of interrupt-related stuff is just a matter of how
peripherals, like the VDP, generate them, and how you can control
these peripherals doing this. Damn, these VDP's are complicated
enough, though....
I hope this helps things a bit further,
Alwin Henseler ([EMAIL PROTECTED])
http://www.twente.nl/~cce/index.htm (computerclub Enschede)
http://huizen.dds.nl/~alwinh/msx (MSX Tech Doc page)
****
MSX Mailinglist. To unsubscribe, send an email to [EMAIL PROTECTED] and put
in the body (not subject) "unsubscribe msx [EMAIL PROTECTED]" (without the
quotes :-) Problems? contact [EMAIL PROTECTED] (www.stack.nl/~wiebe/mailinglist/)
****