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

The same bits, two meanings

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 255

All eight bits contribute positive place values.

Signed 8-bit

−128 to +127

The 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

Write +5 in 8 bits0000 0101 = 05h
Invert every bit1111 1010 = FAh
Add 11111 1011 = FBh

So −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 value8-bit binaryHexUnsigned valueCheck
+1270111 11117Fh127127
−11111 1111FFh255255 − 256 = −1
−51111 1011FBh251251 − 256 = −5
−1271000 000181h129129 − 256 = −127
−1281000 000080h128128 − 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.

FlagValueWhat it says here
S1Result bit 7 is set
Z0Result is not zero
H0No carry from bit 3 to bit 4
P/V0No signed overflow
N0This was addition
C0No 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 value16-bit binaryHexUnsigned value
+32,7670111 1111 1111 11117FFFh32,767
−11111 1111 1111 1111FFFFh65,535
−51111 1111 1111 1011FFFBh65,531
−32,7681000 0000 0000 00008000h32,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.