This article is supplemental to my BlueSky thread on Z80 ISAs. Please read that first.
The Z80 opcode encoding is organised in four parts: opcodes 00-3F, 40-7F, 80-BF, and C0-FF. I'll talk about each part separately. I've layed out the unprefixed opcodes, the ED-prefixed opcodes and the CB-prefixed opcodes side-by-side.
Legend:
White cells - R800 opcodes
Green cells - Z280 opcodes
Yellow cells - unused opcodes
Black cells - prefix opcodes
As you can see the in the right column, the CB-range doesn't see much action. Where the Z80 has undocumented opcodes, Z280 has a Test and SET instruction. This is a somewhat weird instruction that affects the sign flag (only!) according to the destination, and then sets that to all 1's. I'm not sure what you'd use it for, except maybe allocations and locks. Sadly the CB-range won't be getting any more interesting than this!
In the middle column we have the ED-range. This range is mostly empty on Z80, so we see a lot of new things here. The first instruction is LDA, which means LoaD Address, a very useful instruction that looks like a indirect memory load, but it just calculates the address of the memory and loads that in the destination register. Not only can we use this for more complicated pointer math, we can also abuse it to do other calculations for which we don't need the regular arithmetic flag results.
Speaking of addressing modes, we see a lot of new ones here: stack-relative, program-relative, 8-bit and 16-bit unsigned indexed, and indexed by index register. I suppose the term "index register" can be interpreted two ways, right! For the rest we see 16-bit loads and some Exchange instructions with the Accumulator. I must say these addressing modes are nice, but they are also very non-orthogonal.
Looking at the left column, we see the DD and FD prefixes provide 16-bit memory versions of increments, decrements, and 8-bit and 16-bit immediate loads. Even immediate loads to absolute memory addresses, which is a very CISCy thing to have!
Two other instructions that stand our here are JAR and JAF. These are relative jumps with the condition of whether, respectively, EXX or EX AF,AF' was active. Zilog likes to advertise the shadow registers as useful for fast interrupt context switching. Using these instructions you can support nested interrupts by saving the registers on the stack if the shadow registers were already in use.
The middle column shows that ADD/ADC/SUB/SBC HL is now orthogonal with regards to IX and IY. While nice and consistent (in as far as the IX/IY mechanism itself is consistent), this is ultimately a dead end for 16-bit arithmetic. You won't find any 16-bit operations from the boolean group.
We also see some pretty creative I/O to and from memory using the new addressing modes. Honestly these instructions also look messy. There are also instructions to add A signed-extended to 16-bit pointers, and separate sign extension instructions for A into HL and HL into DEHL. We also see interrupt mode 3 support, a stray Cache Purge instruction, and a System Call instruction that are particular to the internal functioning of the Z280. Finally, the LDCTL (load control register) instructions control various aspects of the internal functioning of the Z280. It also includes access to some internal peripherals which in other Zilog processors are handled by separate "internal I/O" instructions.
The left column just has LD A "from memory" instructions that stand out. The encoding is different compared to LD A "to memory" in the previous part (middle column). It seems messy to have a different encoding now while there was room in the other place near where the rest is at. On the other hand this encoding is the same as for the arithmetic operations you'll see in the next part.
The third part is pretty straight-forward. The middle column has instructions for I/O to 16-bit databus, some user/system mode stuff, and a mechanism for External Processing Units. I won't go into these here.
The left column just fills out the 8-bit arithmetic instructions with versions that make use of the new addressing modes. Seeing the gap in the FD-prefix just makes it feel unbalanced. Like, wasn't there one more addressing mode that could fit in there? There were such unbalanced gaps in the first part as well.
Now, you'd better hold on to something, because the last part is pretty surprising!
Woop woop! Now that's an interesting looking section. The middle column is almost completely filled with a full set of signed, unsigned, 8-bit and 16-bit multiply and divide instructions. You can see that the R800 already added a few of these (it was inspired by the Z280's predecessor the Z800) but there are so much more in Z280, right?! In addition we have 16-bit arithmetic ADD, SUB and compare instructions which were missing in Z80 (it only has the ADC HL and SBC HL ones. The Z80's ADD HL is for address calculations, not arithmetic!), but sadly the IX/IY rule is different here so it feels messy.
The weirdest thing in the middle column is a lonely EX H,L instruction, which makes no sense to exist by itself. Maybe it's specifically for byte order changes, it's useful for that at least.
In the left column what stands out most is the complete set of indirect jump and call instructions for the new addressing modes, as well as (HL) which I'm assuming is just the value of the HL register for the new conditional JP and all the CALL instructions as well. I have to assume this, because that's how JP (HL) works and that too is described throughout the entire Zilog manual as "indirect register" addressing mode, which is definitely wrong. So either JP NZ,(HL) is the same as JP (HL) or it actually reads an address from (HL).
We also have some PUSH and POP instructions that read/write directly to memory, as well as a PUSH immediate. Very useful! These things require slow EX (SP),HL tricks on Z80. Another thing that breaks the IX/IY rule is the existence of EX HL,IX and EX HL,IY. These are very welcome though, because there's no way to load between these registers otherwise.
Conclusion
The Z280 has some good ideas, but also it's messy in places. It solves some problems of the Z80's instruction set, but then gets itself into more trouble. The fact that the CB-range wasn't utilised for new instructions seems like a miss. Although, due to mistakes made in the original Z80, it's pretty hard to use it for instructons that do something different than 8-bit signed indexing. Still, it's a lot of opcode real-estate to leave unused.
That's something the Z380 did better, which I'll be analysing next! That article will be available for supporters and members only.
