The condition code register

The condition code register

Alongside its data and address registers, the M68K keeps five one-bit summaries of a recent result. Together they form the condition code register, or CCR. The simulator displays them in the flags panel above the registers, in the order X N Z V C.

A one-bit summary is called a flag. A flag is set when its value is 1 and clear when its value is 0.

flagnamewhat it summarizes for the selected size
Xextendcarry or borrow that can be passed between pieces of a calculation
Nnegativethe highest bit of the result is 1
Zzerothe result is zero
Voverflowthe signed result is outside the selected size's range
Ccarryan addition carried out, or a subtraction borrowed

The instruction size matters. For a byte instruction, these summaries describe the 8-bit byte result. For a word instruction they describe the 16-bit word result, and for a long instruction they describe the 32-bit long result. Preserved upper bits in a data register do not take part in a smaller operation's flags.

Zero and negative

Z is set when the result within the selected size is zero. This can happen even when the full data register still contains nonzero upper bits.

N copies the highest bit within the selected size. For a byte, that is bit 7; for a word, bit 15; for a long, bit 31. In two's-complement signed reading, a 1 in that position marks a negative value.

Each playground on this page begins with its registers at zero and all five displayed flags clear. The following instructions then give both registers explicit values before the byte operations.

    move.l #$A5A50001, d0
    sub.b #1, d0
    move.l #$11223344, d1
    move.b #$80, d1
after this instructionregister valueXNZVC
move.l #$A5A50001, d0d0=$A5A5000101000
sub.b #1, d0d0=$A5A5000000100
move.l #$11223344, d1d1=$1122334400000
move.b #$80, d1d1=$1122338001000

After the subtraction, the selected byte result is $00, so Z is set. The upper three bytes of d0 remain $A5A500, but they do not affect a byte instruction's flags. The last instruction moves the byte $80. Its highest bit is 1, so N is set even though the full register begins with the positive-looking long-sized pattern $11.

Carry and overflow

C and V describe the same fixed-width calculation from two different numeric readings:

  • C records an unsigned carry out of an addition or borrow from a subtraction.
  • V records a result outside the signed range for the selected size.

An unsigned byte ranges from 0 through 255. A signed byte ranges from -128 through 127. These two ranges make the difference visible with small values.

    move.l #$000000FF, d0
    add.b #1, d0
    move.l #$0000007F, d1
    add.b #1, d1
    move.l #3, d2
    sub.b #5, d2
after this instructionregister valueXNZVC
move.l #$FF, d0d0=$000000FF00000
add.b #1, d0d0=$0000000010101
move.l #$7F, d1d1=$0000007F10000
add.b #1, d1d1=$0000008001010
move.l #3, d2d2=$0000000300000
sub.b #5, d2d2=$000000FE11001

For $FF + 1, the unsigned answer 256 does not fit in a byte. The stored byte wraps to $00, so C and Z are set. Signed $FF means -1, and -1 + 1 gives the valid signed result 0, so V is clear.

For $7F + 1, the signed answer 128 is outside the signed-byte range. The stored byte is $80, so V and N are set. The unsigned answer 128 fits in a byte, so C is clear.

For 3 - 5, an unsigned subtraction needs a borrow. The byte result wraps to $FE, setting C. The signed answer -2 fits in a byte, so V remains clear. Its top bit is 1, so N is set.

X, the extend flag, follows carry or borrow for the add and sub forms used here. Arithmetic on a value split into several pieces can use that saved bit when working on the next piece. In these examples, the flags panel lets you observe X alongside C.

Compare with cmp

cmp performs a subtraction for its effect on the flags while preserving both operands. Its form is:

cmp.size source, destination

It calculates destination - source at the selected size and sets N, Z, V and C as sub would. It preserves X because there is no stored arithmetic result to extend.

    move.l #3, d0
    cmp.b #5, d0
    move.l #5, d1
    cmp.b #5, d1
after this instructionregister valuesXNZVC
move.l #3, d0d0=$00000003, d1=$0000000000000
cmp.b #5, d0d0=$00000003, d1=$0000000001001
move.l #5, d1d0=$00000003, d1=$0000000500000
cmp.b #5, d1d0=$00000003, d1=$0000000500100

The first comparison calculates 3 - 5. That byte result would be $FE, so N is set and the unsigned subtraction records a borrow in C. The second calculates 5 - 5, so Z is set. The register values stay at 3 and 5 throughout their comparisons.

Test a value with tst

tst summarizes one existing value without changing it:

tst.size operand

It sets N and Z from the selected byte, word or long, clears V and C, and preserves X.

    move.l #$00000080, d0
    tst.b d0
    move.l #$AA550000, d1
    tst.w d1
after this instructionregister valuesXNZVC
move.l #$80, d0d0=$00000080, d1=$0000000000000
tst.b d0d0=$00000080, d1=$0000000001000
move.l #$AA550000, d1d0=$00000080, d1=$AA55000001000
tst.w d1d0=$00000080, d1=$AA55000000100

The byte $80 has its highest bit set, so the byte test sets N. The low word of d1 is $0000, so the word test sets Z. In both cases, tst leaves the register itself unchanged.

Which familiar instructions update the CCR

A flag describes the most recent instruction that updates it. Different instructions update different parts of the CCR.

instruction from this working seteffect on Xeffect on N Z V C
add, sub with a data resultset from carry/borrowset from the sized arithmetic result
move to a data register or memorypreservedset N/Z from value; clear V/C
extpreservedset N/Z from result; clear V/C
move to an address registerpreservedpreserved
cmppreservedset from a subtraction
tstpreservedset N/Z from value; clear V/C

The move spelling used with an address-register destination performs an address-register copy and preserves the CCR. For a memory operand such as 4(a0), 0(a0,d1.w), (a0)+ or -(a0), the effective-address calculation also leaves the CCR alone. The instruction using that address can still update the flags according to its own row in the table.

Here the zero byte written to memory sets Z. The address setup preserves that set flag. The final move.b sets Z from the zero byte it reads, while the postincrement changes a0 from $2000 to $2001 as part of the memory access.

    move.l #1, d0
    move.b #0, $2000
    move.l #$2000, a0
    move.b (a0)+, d0
after this instructionrelevant valuesXNZVC
move.l #1, d0d0=$0000000100000
move.b #0, $2000byte at $2000 is $0000100
move.l #$2000, a0a0=$00002000; flags preserved00100
move.b (a0)+, d0d0=$00000000, a0=$0000200100100

Stepping one instruction at a time keeps the current row visible in the flags panel.

Check your understanding

For each independent row, predict the destination afterward and all five flags. The starting CCR is given as X N Z V C.

starting valuestarting CCRinstruction
d0=$123456001 1 0 1 1move.b #0, d0
d0=$000000FF0 0 0 0 0add.b #1, d0
d0=$0000007F1 0 0 0 1add.b #1, d0
d0=$000000031 0 0 0 0cmp.b #5, d0
d0=$A5A500801 0 0 1 1ext.w d0
d0=$123456801 0 0 1 1tst.b d0
Show answers
instructiondestination afterwardending CCRreason
move.b #0, d0d0=$123456001 0 1 0 0the moved byte is zero; move preserves X
add.b #1, d0d0=$000000001 0 1 0 1$FF + 1 carries out and leaves byte zero
add.b #1, d0d0=$000000800 1 0 1 0signed $7F + 1 overflows; unsigned 128 fits
cmp.b #5, d0d0=$000000031 1 0 0 13 - 5 is negative and needs an unsigned borrow; X is preserved
ext.w d0d0=$A5A5FF801 1 0 0 0the sign-extended word is negative and nonzero; X is preserved
tst.b d0d0=$123456801 1 0 0 0byte $80 has its highest bit set; tst preserves X