Looking at the Z80's architecture diagram, there is the obvious register file in pink near the middle of the diagram. But four of those registers are not usable by programmers. Those are W
, W'
, Z
and Z'
. So what operations did these registers participate in if they were not part of any program that could run on the Z80?
-
16I was going to post this, until I realized this was the wrong site ;-) en.wikipedia.org/wiki/W_and_Z_bosons– uhohCommented Jun 7, 2018 at 10:25
3 Answers
But four of those registers are not usable by programmers. Those are
W
,W'
,Z
andZ'
.
First of all there is no W'/Z'
. There is only a single W/Z
pair (*1).
Second, of course they are usable by programmers. They get used with every 16 bit value, much like TEMP
and ACU
or the Instruction Register
(all the way on the left side) is used with every instruction.
So what operations did these registers participate in if they were not part of any program that could run on the Z80?
The W/Z
register pair is used for temporary storage. Without it, no program using a 16 bit address could be used.
The primary use of W/Z
is to hold an operand from a two or three byte instruction until it can be used. The mechanics around W/Z
are inherited from 8080 times as they feature the same design (*2) and work exactly alike (*3).
Instructions with an absolute (*4) address will have to read this address during M2/M3, but don't need it before M4. So they have to save them somewhere. Lets take the memory cycles of a LD A,(1234h)
(load A
from address) as example:
M1 -> Output 'PC'; Fetch Opcode (3Ah) into Instruction Register
M2 -> Output 'PC'; Fetch Lower Address Byte (34h) into `Z` Register
M3 -> Output 'PC'; Fetch Upper Address Byte (12h) into `W` Register
M4 -> Output 'WZ'; Fetch Data Byte at 1234h into `A` Register
There are a few special cases:
The jump (JP
) instruction does not load WZ
into the PC
, but fetches the target instruction in M4 much like a data byte and then loads PC
with WZ+1
(through the PC incrementer). Call works similarly. (*5)
16 bit loads (e.g. LD HL,(1234h)
) do use WZ+1
to load the second (higher) byte in M5.
All exchange with (SP) (e.g. EX (SP), HL
) do load W/Z
with (SP+1)/(SP)
during the exchange.
(there might be more; I just don't remember).
Further Reading: Programming the Z80 by Rodney Zaks describes this in great detail on page 87-91 (Get the PDF here).
*1 - This picture holds a number of errors, not just the doubled W/Z
, but also mispositioned incrementers and alike.
*2 - Intel's 8085 inherited them as well.
*3 - Differences are only due to Z80-specific extensions like additional 16-bit instructions, but also Interrupt Mode 2 where W
is loaded with I
and Z
with the external supplied vector.
*4 - Explicit in Z80 terms.
*5 - And both (JP
/CALL
) show a nice dance around the WZ
-pole when an interrupt happens. Now, instead of loading the PC with WZ+1
the increment will be suppressed and WZ
goes directly into PC
. Next WZ
is loaded with the interrupt vector to be used (depending on the interrupt mode active) - before continuing with the execution exactly like a call i.e. by pushing PC
, fetching the first instruction via WZ
and transferring WZ+1
into PC (IFF1/2 handling happened during interrupt acknowledge) - what a beautiful move, isn't it?
-
Fetching a byte in M4 and using it as an opcode byte seems like a useful technique, but I would think it would complicate interrupt handling. What happens if an interrupt arrives during the execution of a
JP
orCALL
? Are its effects delayed until after the execution of the following instruction? What if that instruction is also aJP
?– supercatCommented Jun 11, 2018 at 16:48 -
@supercat Hmm. Not sure. I do remember that on the 8080 the PC does not get incremented when an Interrupt is detected. Since the WZ mechanic is directly taken from the 8080, it might be the same on the Z80, so the loading goes without incrementing. So PC now holds the address of the jump target, freeing WZ to take the interrupt address, which happenes before PC is pushed (that's important as the address may be from an external supplied vector in mode 2, that needs to be stored before PC is available again). I may have to look it up again.– RaffzahnCommented Jun 15, 2018 at 12:15
-
Interesting how the Z80 handles call/return and interrupts. The 6502 burns a cycle when returning from subroutines because the subroutine call pushes the old PC before it has fetched the last byte of the target address. If I'm understanding you correctly, if I do a CALL $1234 instruction that starts at address $5678, the Z80 will read $34 into Z and then loads W with $12. At that point, PC will be $567B. It will then store PC to the stack. If there is no interrupt, the Z80 would read from the address in WZ (i.e. $567B) and load PC with WZ+1 ($567C). If there is an interrupt, ...– supercatCommented Jun 15, 2018 at 15:50
-
...then the value of PC that gets stored won't be incremented, but if I'm understanding you right, that's because the PC already has been incremented after fetching the last byte of the instruction. When the RETI is performed, the Z80 will end up in the same state as it would have been just before interrupt hadn't occurred, with the Z80 holding the return address in WZ, just about to fetch (WZ) into the instruction register as it loads WZ+1 into PC?– supercatCommented Jun 15, 2018 at 15:56
-
1Yeah, I guess this is a bit of a tangent. I see where you're going, though. Two PC values end up getting stacked during the execution of a single instruction. The first one (the address of the instruction to execute following the
RET
) would have been incremented, but the second (the one to use afterRETI
) wouldn't. How did the designers of the Z80 possibly manage to make all this stuff work!?– supercatCommented Jun 15, 2018 at 16:57
Found here, Down to the silicon: how the Z80's registers are implemented:
The WZ temporary registers The Z80 (like the 8080 and 8085) has a WZ register pair that is used for temporary storage but is invisible to the programmer. The primary use of WZ is to hold an operand from a two or three byte instruction until it can be used.
Edit: also worth noticing:
The diagram shows a separate incrementer for the refresh register (IR), an adder for IX and IY offsets, and a W'Z' register but those don't exist on the real chip
-
1In that case, why is there
W
andZ
in both register sets? Commented Jun 6, 2018 at 13:33 -
1
-
1The linked write-up is excellent! I think it might answer almost every "How does the Z80..." question on RCSE.– Brian HCommented Jun 6, 2018 at 13:52
-
2So it sounds
WZ
is likely to be the real name of the thing that hobbyists have documented as MEMPTR — gist.github.com/drhelius/8497817 ? If so then the question isn't quite correct; bits 3 and 5 of MEMPTR leak into the flags register duringBIT n, (HL)
, so sinceCPI
is known to increment MEMPTR, its state is fully inspectable. Albeit at great length and for no substantial benefit.– TommyCommented Jun 6, 2018 at 15:04 -
4@Tommy You are correct. Also I think
WZ
alters the behaviour of some of the undocumented instructions. It was a nightmare reverse engineering the effects for my Spectrum emulator.– JeremyPCommented Jun 7, 2018 at 9:10
And what is called "WZ register" here, is even programmatically visible, partially of course. In other places, that register is called "MEMPTR" and in some commands, some bits of that register may leak into otherwise unused bits of the flag register.
For example, BIT n,(HL)
command is known to leak bits 11 and 13 of "WZ" or "MEMPTR" into (unused) bits 3 and 5 of the flag register, what gives a way to know which values are left into MEMPTR by the previously executed command.
This topic is more or less thoroughfully studied and some results are for example here: https://gist.github.com/drhelius/8497817
Another interesting fact: there are subtle differences in MEMPTR setup for some commands in some Z80 clones. Thus it is a way to detect Z80 clones programmatically.