Would providing full 8080 compatibility have interfered with or made more complicated implementing new features the Z80 designers wanted?
It would have made it impossible. Any extension adding functionality will naturally add incompatibility as it needs to use existing resources in a new way. The task to solve is which change will have the least impact and/or result at least in a positive impact when weighting lost vs. gained functionality.
Lets look at three examples of such changes:
New Operations/Opcodes
One of the most basic tasks was to find opcodes. After all, new operations need new opcodes. Except, opcodes are a finite resource and the 8080 instruction space was already fully assigned. Thus, to add a new instruction some existing operations had to be dropped/changed.
The 8080 opcode CBh was assigned as unconditional jump. It was a legal operation, but as such redundant with C3h. All assemblers following Intel's manuals would translate JMP
to C3h, the 'official' JMP. Only a rather small number of programs would use the 'other' jump.
Zilog assumed it safe to use CBh as opcode for one (or more) new functions, in this case as prefix for all new bit manipulation opcodes. The very same was done with DDh/EDh/FDh, which were all absolute CALL
s for the 8080, and turned into prefix bytes for the Z80.
In similar way other duplicate operations were used for new, non prefixed instructions. Every 8080 program using one of these instructions will fail on a Z80 - seems there weren't many, as it worked out rather fine.
Changed Operation Behaviour
Beside adding instructions, improvements can also be made to existing instructions, adding/extending functionality to them. Here the resource would be flags within the flag register. Again, the whole 8080 flag register was not only well defined, but also for all 8 bits. Any added/changed behaviour needed for new functions thus has a potential of breaking existing code.
DAA - let Opcodes Communicate
One such example is DAA
- Decimal Adjust after Addition. DAA
is used on the 8080 to correct the result after adding two BCD numbers, as the adder only does a binary add. This works by checking for half carry and illegal values (>9) in either nibble. But it is restricted to additions. Subtractions can not be corrected by DAA
on the 8080.
Zilog thought it would be handy if BCD data could be subtracted as easily. Of course they could have added a DAA
version for subtraction, as Intel later did with the 8086's DAS
- Decimal Adjust after Subtraction. Except, as already seen, free opcodes were at an extreme premium. An alternative way would be to make DAA
aware of whether the last instruction was an ADD
or SUB
type. Luckily the 8080 had three unused flags (2¹, 2³ and 2⁵) so 2¹ was used, now called N, to record the type of the last instruction, and DAA
could act accordingly.
In total, this gave a solution much like the Half-Carry flag which is already part of the communication between arithmetic operations and DAA
.
Of course this brought two incompatibilities:
DAA
after a subtraction would now work differently than on a 8080
- Bit 2¹ of the flag register is always 1 on a 8080, but may be zero on a Z80, if the last arithmetic operation was an addition.
Both are again fringe cases in programming. DAA
after a subtraction on an 8080 might not be illegal, but could be considered odd at least. Such programs may break on a Z80. Changing flag word behaviour in contrast would only influence programs that create a synthetic flag byte from scratch with 2¹ set to a default value. Such would fail either way (*1) when using DAA
afterwards.
Adding Functionality to Existing Flags
A much more severe shortcoming of the 8080 involved signed operations. The 8080 had no overflow signalling. Signed operations were rather cumbersome without that. While enhancing DAA
could be done 'hidden', creating an overflow flag is all about making it visible to programmers and more so to make it easily testable, which needs at least one, but better two related jump instructions (Jump when Overflow/Jump when No-Overflow) - plus calls as well.
Giving out 4 opcodes would be unhealthily expensive (*2). On the other hand, reusing an existing flag, one fitted with conditional instructions to test it, would require zero new instructions - only add meaning to an existing set of test instructions.
Of the existing (8080) flags Half-Carry can not be tested, leaving Sign, Zero, Parity, Carry. Of these only Parity is not essential for arithmetic operations (*3), making it the only candidate fulfilling all restrictions.
All arithmetic operations handle 2² now as Overflow, while logical operations produce Parity. Quite handy.
And like before, it adds incompatibility. But as before, chances are good that not many programs would fail - after all, parity evaluation is a rare operation, even less parity checking.
Conclusion
No, it is not possible to extend the ISA without any incompatible changes - unless that extension is already planned for.
Yes, there were incompatibilities added, but they were all fringe cases or issues that would have been quite rare cases.
*1 - In fact, by selecting N=0 for addition, Zilog would provoke such programs to fail with a high probability, as any program setting 2¹ as appropriate for a 8080 would make a following DAA
of a strict 8080 program misbehave.
*2 - Using prefixed versions would have worked, but adding another byte and 4 cycles to an instruction that might be common seems a bad idea - also breaking symmetry (i.e. patchability) with other conditional instructions.
*3 - Parity works mostly automatically, undisturbed from most operations.