32

Although the Z80 is nearly fully backward compatible with the Intel 8080, there are minor differences such as the Z80 handling the parity flag differently with certain operations.

Why? Would providing full 8080 compatibility have interfered with or made more complicated implementing new features the Z80 designers wanted?

4
  • 7
    Was backward compatibility even seen as a design goal? Or was it more pretty-much-compatible so that the programmers did not have to completely change syntax?
    – Jon Custer
    Nov 18, 2021 at 23:26
  • 19
    @JonCuster it was the #1 sales argument for the Z80. Use our CPU, run all your existing code and gain by using our enhanced peripherals designed for even shorter reaction time.
    – Raffzahn
    Nov 19, 2021 at 1:27
  • 4
    @Raffzahn - yes, but really the concept of 'compatibility' was nothing like it has been for the last 30 years or so. Very different ethos...
    – Jon Custer
    Nov 19, 2021 at 1:44
  • 9
    @JonCuster Not sure where you see a difference. 8080 compatibility was the most important issue for the Z80. It was the way Faggin saw to get a successful product out, able to keep the company afloat until the Z8 was done. Not to mention that the 8080 itself was already the third iteration of a compatible series.
    – Raffzahn
    Nov 19, 2021 at 1:50

2 Answers 2

38

The designers of the Z80 thought it would be useful for code to quickly determine whether signed arithmetic operations overflowed. There were a few ways they could have accommodated this:

  1. Add a new flag, and use up a couple of opcodes to branch based upon whether the new flag is set or clear. This would require giving up two precious opcodes that could probably be employed for some more useful purpose.

  2. Add some new add/subtract instructions which set/clear the existing P flag to indicate whether an overflow occurred. To avoid having to give up a lot of opcodes, these would probably need to be preceded by a certain prefix. This might not have been too difficult, but any code that cares about overflow would need use an extra byte and take an extra four cycles to perform the check.

  3. Simply make add and subtract set/clear the P flag according to whether an overflow occurred, while other instructions affect it in the usual fashion. Since relatively little software for the 8080 would care about the state of the P flag after an add or subtract operation, this approach was deemed most advantageous.

  4. Have a "compatibility mode" flag which switches some instructions between perfect 8080 emulation and enhanced Z80 operation, along with instructions to turn it on and off. This would add some complexity, and there isn't enough code that cares about the P flag after an add or subtract to justify using it just for that, but it might have been a good design feature if it allowed new behaviors to be assigned to the seven otherwise-useless opcodes 0x40, 0x49, 0x52, 0x5B, 0x64, 0x6D, and 0x7F (which are LD B,B; LD C,C; etc.).

They opted for approach #3, because they didn't think the slight behavioral difference in ADD/ADC/SUB/SBB would affect any code that wasn't expecting the new behavior.

12
  • 6
    So you're saying that it's "technically breaking compatibility", but this fact was deemed not a problem in practise
    – OmarL
    Nov 18, 2021 at 21:12
  • 1
    Are LD B,B and the like useless? I thought that they set some flags based on the value in the register.
    – cjs
    Nov 18, 2021 at 21:19
  • How is it that "any code that cares about overflow would need use an extra byte and take an extra four cycles to perform the check" only applies to #2 and not #3? Nov 18, 2021 at 21:32
  • 2
    @snips-n-snails - #2 is "keep old add and subtract ops; add new add and subtract ops using a prefix byte", #3 is "keep old add and subtract ops but change their behaviour". no extra byte to read from memory. I assume 4 cycles is the cost of reading memory. Nov 18, 2021 at 21:59
  • 3
    @cjs: The 8-bit LD operations do not affect any flags on the 8080 or Z80, even though similar instructions would set flags on other processors like the 68000 or 6502.
    – supercat
    Nov 18, 2021 at 22:09
30

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 CALLs 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:

  1. DAA after a subtraction would now work differently than on a 8080
  2. 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.

4
  • 2
    "The 8080 opcode CBh was assigned as unconditional jump. It was a legal operation, b but as such redundant with C3h." Why did Intel assign multiple opcodes to the same operation, rather than having just one legal opcode for each operation and explicitly reserving the rest of the opcode space for future extensions to the instruction set?
    – Vikki
    Nov 19, 2021 at 7:22
  • 7
    @Vikki: CBh = C3h+08h. These chips were made when every gate mattered. The unconditional jump instruction simply didn't decode the top bit. Yes, the CBh instruction could have been documented as "reserved" but it's always been common to document the implemented behavior.
    – MSalters
    Nov 19, 2021 at 9:36
  • For *1 only if there is no add between setting the flag and the DAA.
    – Brian
    Nov 19, 2021 at 11:58
  • @Brian Any arithmetic inbetween would set it as intended.
    – Raffzahn
    Nov 19, 2021 at 13:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.