The photograph below is a highly magnified image of the 8085's silicon, showing the relevant parts of the chip. In the upper-left, the arithmetic logic unit (ALU) performs 8-bit arithmetic operations. The status flag circuitry is below the ALU and the flags are connected to the data bus (indicated in blue). To the right of the ALU, the control PLA decodes the instructions into control lines that control the operations of the ALU and flag circuits.
- Bit 7 is the sign flag, indicating a negative two's-complement value, which is simply a byte with the top bit set.
- Bit 6 is the zero flag, indicating a value that is all zeros.
- Bit 5 is the undocumented K (or X5) flag, indicating either a carry from the 16-bit incrementer/decrementer or the result of a signed comparison. See my article on the undocumented K and V flags.
- Bit 4 is the auxiliary carry, indicating a carry out of the 4 low-order bits. This is typically used for BCD (binary-coded decimal) arithmetic.
- Bit 3 is unused and set to 0. Interestingly, a fairly large transistor drives the data bus line to 0 when reading the flags, so this unused flag bit doesn't come for free.
- Bit 2 is the parity flag, which is set if the result has an even number of 1 bits.
- Bit 1 is the undocumented signed overflow flag V (details).
- Bit 0 is the carry flag.
The image below zooms in on the flag silicon, showing individual transistors. The large transistors labeled with the flag name drive the flag value onto the data bus. From the data bus, the flag values control the results of conditional jumps, calls, and returns. The complex circuits above these transistors compute and store the flag values.
Each flag bit has a latch and control lines to write a value to the latch. Most flags are updated by the same arithmetic instructions and controlled by the
arith_to_flags control line. The carry flag is affected by additional instructions and has its own control line. The undocumented K and V flags are updated in different circumstances and have their own control lines.
bus_to_flags control loads the flags from the data bus for the
POP PSW instruction, while the
flags_to_bus control sends the flag values over the data bus for the
PUSH PSW instruction or for conditional branches.
The circuitry to compute most flag values is straightforward. The sign flag is set based on bit 7 of the result. The auxiliary carry flag is set on the carry out of bit 3. The K and V flags are set based on the top two bits (details).
The zero flag is normally set from the
alu_zero signal that indicates all bits are zero.
The zero flag has support for multi-byte zero: at each step it can
AND the existing zero flag with the current ALU zero value, so the zero flag will be set if both bytes are zero. This is only used for the (undocumented)
DSUB 16-bit subtract instruction. Strangely, this circuit is also activated for the 16-bit
DAD instructions, but the result is not stored in the flag.
If you look at the chip photograph at the top of the article, the flags are arranged in apparently-random order, not in their bit order as you might expect. Presumably the layout used is more efficient. Also notice that the carry flag C is off to the right of the ALU. Because of the complexity of the carry logic, which will be discussed next, the circuitry wouldn't fit under the ALU with the rest of the flag logic.
The carry logicThe schematic below shows the circuit for the carry flag. The logic for carry is more complex than for the other flags because carry is used in a variety of ways.
The value stored in the carry flagThe top part of the circuit computes
carry_result, the value stored in the carry flag. This value has several different meanings depending on the instruction:
- For arithmetic operations, the carry flag is loaded with the value generated by the ALU. That is,
alu_carry_7(the high-order carry from bit 7 of the ALU) is used. (See Inside the ALU of the 8085 microprocessor for details on how this is computed.)
DAA(decimal adjust accumulator), the carry flag is set if the high-order digit is >= 10. This value is
alu_hi_ge_10, which is selected by the
CMC(complement carry), the carry flag value is complemented. To compute this, the previous carry flag value
c_flagis selected by
use_carry_flagand complemented by the
RRC(rotate right operations), bit 0 of the rotated value goes into the carry. In the circuit,
reg_act_0(the low-order bit in the undocumented ACT (accumulator temp) register) is selected by the
xor_carry_resultcontrol inverts the carry value in a few cases. For subtraction and comparison, it flips the carry bit to be the borrow bit. For
STC(set carry), the
xor_carry_resultcontrol forces the carry to 1. For
ANDoperations, it forces the carry to 0.
Generating the carry input signalThe middle part of the circuit selects the appropriate
carry_invalue that is supplied to the ALU.
xor_carry_in. This is used for most instructions.
xor_carry_incontrol does this.
LDSIinstructions. These instructions add a constant to a 16-bit register pair, so they need to add the carry from of the low-order sum to the high-order byte. The carry latch temporarily holds the carry, and this value is selected by the
use_latched_carrycontrol line. You might wonder why not just use the normal carry flag; the
LDSIinstructions are designed to leave the carry flag unchanged, so they need somewhere else to temporarily store the carry. The surprising conclusion that Intel deliberately included circuitry in the 8085 specifically to support these undocumented instructions, and then decided not to support these instructions. (In contrast, the 6502's unsupported instructions are just random consequences of unsupported opcodes.)
Generating the shift_right input signalEach bit of the ALU has a shift right input. For most of the bits, the input comes from the bit to the left, but the high-order bit uses different inputs depending on the instruction. The bottom circuit in the schematic below generates the shift right input for the ALU. This circuit has two simple options.
- Normally the carry flag is fed into
shift_right_in. For the
RARinstructions, this causes the carry flag to go into the high-order bit.
- For the
RLCinstructions (rotate A left/right), the
rotate_carrycontrol selects bit 0 as the shift right input.
ConclusionsBy reverse-engineering the 8085, we can see how the flag circuits in the 8085 actually works at the gate and silicon level. One interesting feature is the circuitry to implement undocumented instructions and flags. Another interesting feature is the complexity of the carry flag compared to the other flags.
This information is based on the 8085 reverse-engineering done by the visual 6502 team. This team dissolves chips in acid to remove the packaging and then takes many close-up photographs of the die inside. Pavel Zima converted these photographs into mask layer images, generated a transistor net from the layers, and wrote a transistor-level 8085 simulator.
Footnotes on rotateI recommend you skip this section, but there are few confusing things about the rotate logic that I wanted to write down.
For some reason the rotate operations are named very strangely in the 8080 and 8085.
RRC is the "rotate accumulator right" instruction and
RAR is the "rotate accumulator right through carry" instruction. Based on the abbreviations, the names seem reversed. The left rotates
RAL are similar. The Z-80 processor has a similar
RRC instruction, but calls it "rotate right circular", making the abbreviation slightly less nonsensical.
Bit 0 of ACT is fed into
shift_right_in for both
However, this input is just ignored for
RLC since the rotation is the other direction, so I assume this is just a result of the control logic treating
RLC the same.)
To reduce the control circuitry, the
use_latched_carry control lines are actually the same control line since the instructions that use them don't conflict. In other words, there is just one control line, but it has two distinct functions.