There's a lot of information available about the Z80. Unfortunately, a lot of it has conflicting information, for example:
Sean Young's
The Undocumented Z80 Documented contains the following:
QuoteThe INI/INIR/IND/INDR instructions use BC after decrementing B, and the OUTI/OTIR/OUTD/OTDR
instructions before.
Can someone please confirm this is NOT the case, it's the other way around.
Another error is in http://www.z80.info/z80ins.txt (http://www.z80.info/z80ins.txt) where it states:
QuoteMODE 0 - INTA(6) ODL(3) ODH(4) SWH(3) SWL(3) 6
(CALL INSERTED) SP-1 ---> SP-1 --->
- INTA(6) SWH(3) SWL(3) 4
(RST INSERTED)
SP-1 ---> SP-1 --->
MODE 1 INTA(7) SWH(3) SWL(3) 4
(RST 38H
INTERNAL)
SP-1 ---> SP-1 --->
MODE 2 - INTA(7) SWH(3) SWL(3) MRL(3) MRH(3) 6
(VECTOR
SUPPLIED)
SP-1 ---> SP-1 --->
This shows the timing for interrupt mode 0 as being different from the other types (ie. INTA(6) instead of INTA(7)) which also appears to be incorrect from my testing. The first cycle is always 2 + normal instruction timing (ie. 7 for RST, but 6 for call).
I'm trying to do a completely new Z80 core based on T-State timing and then apply the internal gate-array logic used for the WAIT signal in order to improve the Z80 core in JEMU, and it's all a bit hard with incorrect and conflicting information. I think I have it working correctly and PlusTest shows all timings as correct for the CPC. I've also gone through all instructions on the VZ-200 to test their timing and they are correct, but I'd hate to get a fundamental thing like interrupt timing wrong.
Regarding the IO instructions, BC is decremented after the in/out (attached extract from Z80 microprocessor family databook 1st edition)
I can at least confirm this for INI
EDIT : looks like this doc is wrong regarding OUTI/OUTD.
Quote from: gerald on 09:35, 04 October 12
Regarding the IO instructions, BC is decremented after the in/out (attached extract from Z80 microprocessor family databook 1st edition)
I can at least confirm this for INI
um0080.pdf from zilog's website states for outi:
"The contents of the HL register pair are placed on the address bus to select a
location in memory. The byte contained in this memory location is
temporarily stored in the CPU. Then, after the byte counter (B) is
decremented, the contents of register C are placed on the bottom half (A0
through A7) of the address bus to select the I/O device at one of 256
possible ports. Register B may be used as a byte counter, and its
decremented value is placed on the top half (A8 through A15) of the
address bus. The byte to be output is placed on the data bus and written to a
selected peripheral device. Finally, the register pair HL is incremented."
B is decremented *before* ;)
INI:
"The contents of register C are placed on the bottom half (A0 through A7) of
the address bus to select the I/O device at one of 256 possible ports.
Register B may be used as a byte counter, and its contents are placed on the
top half (A8 through A15) of the address bus at this time. Then one byte
from the selected port is placed on the data bus and written to the CPU. The
contents of the HL register pair are then placed on the address bus and the
input byte is written to the corresponding location of memory. Finally, the
byte counter is decremented and register pair HL is incremented."
So even Zilog can't agree with itself in it's doc but it seems to say B decremented before with OUTI and B decremented after with INI.
but then is documentation.. what really happens? ;)
Some test on real HW, checked with logic analyser :
INST IO MEM
ADDR ADDR
ld BC, &FF0F
ld HL, &4000
INI FF0F 4000
INI FE0F 4001
INI FD0F 4002
INI FC0F 4003
ld BC, &FF1F
ld HL, &4000
OUTI FE1F 4000
OUTI FD1F 4001
OUTI FC1F 4002
OUTI FB1F 4003
ld BC, &FF2F
ld HL, &4000
IND FF2F 4000
IND FE2F 3FFF
IND FD2F 3FFE
IND FC2F 3FFD
ld BC, &FF3F
ld HL, &4000
OUTD FE1F 4000
OUTD FD1F 3FFF
OUTD FC1F 3FFE
OUTD FB1F 3FFD
OUTI/OUTD decrement B, then does the IO access
INI/IND does the IO access, then decrement B
I did not test the R versions as these does not look friendly to the CPC, but B behaviour should be identical.
There is no error in regards to the doco for INI/OUTI and the INIR/OUTIR.
Both use the B register and the HL register.
At the end of the INI/OUTI instruction execution, the B register is decremented and HL incremented. The INIR/OUTIR simply repeat until B = 0.
As for access I/O devices, the system design of the hardware has to become aware of what I/O allocations are taken/free. The CPC itself has
allocated the upper I/O BUS for devices (VGA, CRT, ROM select, etc) so using B as a counter for multi-I/O requests is more or less pointless. However,
bits 8 to 10 are usable, so a small series of I/O can be used.
rpalmer
Arnoldemu and Gerald are right, INI/IND/INIR/INDR decrease B after storing the byte from the hardware port into memory, and reversely, OUTI/OUTD/OTIR/OTDR decrease B before sending the memory byte to the hardware port. This is why Executioner's optimized keyboard reading routine is based on LD B,$F4: IND rather than LD B,$F5: IND; similarly, demos like "Camembert 3 Meeting" tweak the CRTC with LD B,$BC... OUT (C),x: INC B: INC B: OUTI: DEC B: OUT (C),y rather than LD B,$BC... OUT (C),x: INC B: OUTI: OUT (C),y.
So it looks as though Sean Young got it the wrong way around... He does have an excuse since he never had a CPC to test it on so couldn't tell what was on the top half of the address bus. I knew it was an error, but just wanted to confirm. Yes, I has written a keyboard scan routine in the past using IND ::) but had forgotten about it. And I've seen many a demo using OUTI/OUTD with B set +1.
As for the other question... I'm assuming there's a typo in z80ins.txt since the RST instruction uses one more T-State in the first M cycle than CALL. The INTA(6) is correct for CALL, but should be INTA(7) for the RST version.
With the /WAIT signal held low for all but one of the 4 T-States my new Z80 core appears to work perfectly with Plustest for both the instruction timing and interrupt delay tests.