The condition code register

The five bits X, N, Z, V and C, which instructions write which of them, and the fourteen conditions the branches read them through. It ends on the signed and unsigned families and what happens when you pick the wrong one.

The M68K keeps five bits about how the last instruction came out, and they live together in the low byte of the status register, which is called the condition code register, or CCR. The flags panel above the registers shows them in the M68K's own order:

  • X, extend. A second copy of the carry, kept for multi precision arithmetic.
  • N, negative. The top bit of the result, 1 when the result read as a signed number is negative.
  • Z, zero. 1 when the result was zero.
  • V, overflow. 1 when the result did not fit in the signed range of its size.
  • C, carry. 1 when the operation carried or borrowed out of the top bit, which is the unsigned answer to the same question.

Which instructions write which flags

An instruction does not touch all five. Four things can happen to a flag: it is set from the result, it is forced to 0 or to 1, or it is left exactly as it was.

instructionXNZVC
add, sub, addq, subq, addi, subi, neg, asl, asr
cmp, cmpi, cmpa, cmpm-
move, moveq, and, or, eor, not, tst, ext, swap, muls, mulu-00
divs, divu-0
lsl, lsr0
rol, ror-0
btst, bset, bclr, bchg----
clr-0100
lea, pea, movea, adda, suba, movem, exg, link, unlk, the branches-----

✓ means set from the result, 0 and 1 mean forced to that value, and - means left exactly as it was.

Two rows of that table decide how a program is written. The third one says that a plain move sets the flags, so a move between your comparison and your branch destroys the comparison. The last one says that lea, movea and adda do not, so you can work out an address in the middle of a comparison and the branch still sees what cmp left.

Step through it with the flags panel open. Z goes to 1 on the second line and stays 1 through three instructions that write two address registers, then the btst moves it and leaves N, V and C where they were.

cmp subtracts and keeps only the flags

cmp source, destination computes destination - source, throws the answer away, and keeps what it did to N, Z, V and C. It leaves X alone, which is the one difference from writing the sub out.

after this lineNZVC
cmp.l #5, d00100
cmp.l #5, d11001
cmp.l #5, d20000

The second one is the interesting row. 3 minus 5 is -2, which is negative, so N is 1; and as an unsigned subtraction it had to borrow, which is what C reports. The same two bits, read as the answers to two different questions, are what the two families of conditions below are built out of.

The fourteen conditions

b<cc>, db<cc> and s<cc> all read the flags through the same fourteen conditions. Six of them ask about one flag, four ask the signed question and four the unsigned one.

writtenmeansthe flags it reads
eqequalZ is 1
nenot equalZ is 0
miminusN is 1
plplusN is 0
vsoverflow setV is 1
vcoverflow clearV is 0
gtgreater than, signedZ is 0 and N equals V
gegreater or equal, signedN equals V
ltless than, signedN differs from V
leless or equal, signedZ is 1, or N differs from V
hihigher, unsignedC is 0 and Z is 0
hs, cchigher or same, unsignedC is 0
lo, cslower, unsignedC is 1
lslower or same, unsignedC is 1 or Z is 1

hs and cc are two spellings of one condition, and so are lo and cs: bcc is the same instruction as bhs, and you write whichever one says what you mean, "carry clear" when you are thinking about a carry and "higher or same" when you are comparing two unsigned numbers.

The signed conditions read N and V together, because a signed comparison that overflowed has a negative flag that lies: subtract a large negative number from a large positive one and the answer wraps round to negative while the true answer is positive. V is the bit that says so, and "N equals V" is the corrected answer.

Picking the wrong family

$FFFFFFFF is 4294967295 unsigned and -1 signed. Compared against 1, one of those is bigger and the other is smaller, so hi and gt disagree about the same two registers.

d2 comes out at 000000FF and d3 at 00000000. The cmp left N at 1, Z at 0, V at 0 and C at 0. hi wants C and Z both 0, which they are, and gt wants N to equal V, which it does not. One comparison, two right answers, and picking the family that matches what your numbers mean is on you. Sizes and addresses are unsigned, counts and differences are usually signed.

X, the flag nothing here reads

X is a copy of the carry that survives instructions which write C. On a real 68000 it exists so that addx, subx, negx, roxl and roxr can carry from one register into the next when a number is wider than 32 bits. This editor's assembler has none of those five, so X is written and never read: it is in the panel and no program you write here will branch on it.

Step through it. After the lsl.l both C and X are 1. The move on the next line puts C back to 0 and X stays 1, which is the whole point of having two of them. The rol.l sets C again and leaves X alone, because a rotate loses nothing.

Your turn

The test starts d0 at $FFFFFFFF and d1 at 1. Without branching, leave $FF in d2 if d0 is the higher of the two read as unsigned numbers, and $00 in d3 if d0 is not the greater read as signed numbers.

Show solution

The second one starts d0 at 1 and d1 at $FFFFFFFF, and wants the larger of the two read as unsigned numbers left in d0. Compare them and branch over the copy when d0 already holds it.

Show solution