Negative numbers: Two’s complement
A group of bits has no sign by itself. The program chooses whether to read it as unsigned or signed.
Number basics
A byte can mean an unsigned number from 0 to 255. The same byte can mean a signed number from −128 to +127. Two’s complement is the signed format used by Z80 arithmetic.
One byte: unsigned and signed ranges
Unsigned 8-bit
0 to 255All eight bits contribute positive place values.
Signed 8-bit
−128 to +127The same 256 bit patterns are assigned to signed values.
A byte always has a value from 0 to 255 when read as unsigned. It represents a negative number only when the program treats it as signed two’s complement.
How an 8-bit negative value is made
To encode a negative value, start with its positive value in 8 bits. Invert every bit. Then add 1.
Example: make −5
0000 0101 = 05h1111 1010 = FAh1111 1011 = FBhSo −5 is 1111 1011 in binary and FBh in hexadecimal.
Read the same byte both ways
If bit 7 is 0, the signed value is 0 to 127. If bit 7 is 1, subtract 256 from the unsigned value to get the signed value.
signed value = unsigned value − 256 when bit 7 is 1
| Signed value | 8-bit binary | Hex | Unsigned value | Check |
|---|---|---|---|---|
| +127 | 0111 1111 | 7Fh | 127 | 127 |
| −1 | 1111 1111 | FFh | 255 | 255 − 256 = −1 |
| −5 | 1111 1011 | FBh | 251 | 251 − 256 = −5 |
| −127 | 1000 0001 | 81h | 129 | 129 − 256 = −127 |
| −128 | 1000 0000 | 80h | 128 | 128 − 256 = −128 |
Why does the range start at −128? Eight bits make 256 patterns. Two’s complement uses one pattern for zero, 127 patterns for positive values, and 128 patterns for negative values. That gives −128 to +127. There is no positive +128 value in signed 8-bit form.
The CPU adds bits; the program reads the result
The Z80 does not attach a “signed” label to a register. ADD A,n adds the bit patterns in the same way either way. We decide whether to read the result as signed or unsigned. The flags help a program detect different kinds of results.
Example: −5 + 1
LD A,0FBh ; A = 11111011b ADD A,01h ; A = A + 1 ; result: FCh = 11111100b
FBh is 251 unsigned, or −5 signed. After adding one, FCh is 252 unsigned, or −4 signed. The processor performed one addition; only our interpretation changed.
| Flag | Value | What it says here |
|---|---|---|
| S | 1 | Result bit 7 is set |
| Z | 0 | Result is not zero |
| H | 0 | No carry from bit 3 to bit 4 |
| P/V | 0 | No signed overflow |
| N | 0 | This was addition |
| C | 0 | No carry out of bit 7 |
Example: signed overflow without unsigned carry
LD A,07Fh ; +127 ADD A,01h ; result: 80h = −128 signed
In signed 8-bit arithmetic, +127 + 1 is outside the range −128…+127. The eight result bits wrap to 80h, which reads as −128. P/V=1 reports signed overflow, while C=0 says there was no unsigned carry beyond the byte.
This is why carry and overflow are different. C is useful for unsigned arithmetic; P/V after ADD or SUB detects signed overflow. The S flag only copies the top result bit; it does not by itself prove overflow.
The same rule with 16 bits
A 16-bit value has 65,536 patterns. Unsigned, it ranges from 0 to 65,535. Signed, it ranges from −32,768 to +32,767. When bit 15 is 1, subtract 65,536 from the unsigned value.
signed value = unsigned value − 65,536 when bit 15 is 1
| Signed value | 16-bit binary | Hex | Unsigned value |
|---|---|---|---|
| +32,767 | 0111 1111 1111 1111 | 7FFFh | 32,767 |
| −1 | 1111 1111 1111 1111 | FFFFh | 65,535 |
| −5 | 1111 1111 1111 1011 | FFFBh | 65,531 |
| −32,768 | 1000 0000 0000 0000 | 8000h | 32,768 |
Hexadecimal does not change the value or its sign. It is a shorter way to write the same bits. For example, FBh and 1111 1011 are the same byte; the signed or unsigned meaning comes from how the program interprets it.