Skip to content

Undocumented 6809 Behaviours

David Banks edited this page May 21, 2023 · 35 revisions

Contents

Introduction

This page attempts to describe the behaviour of all undefined 6809 opcodes.

In addition, it describes the behaviour of flags where the existing documentation is either unclear, or is inconsistent. For example, when the overflow (V) or half-carry flag (H) become undefined after an instruction, what actually happens?

There is a similar page covering the 6309 here.

These behaviours have been validated against original devices, by writing test cases that carefully (sometimes exhaustively) exercised the instruction, then exposed the resultant state by pushing it onto the stack. The data seen in each bus cycle is then verified against the emulation model in the 6809 Decoder.

References

The following 6809 references were used in the development of the 6809/6309 Bus Protocol decoder:

More general references:

The best existing description of undocumented 6809 opcodes is Atkinson page 153, but even this was not completely accurate. Several of the findings disclosed on this page are new discoveries.

Undocumented Base-Page Opcodes

XHCF

Halt-and-Catch-Fire!

Opcodes:

  0x14 - XHCF (runs until RESET)
  0x15 - XHCF (runs until RESET)
  0xCD - XHCF (runs until RESET)

After executing this instruction, the processor enters a test mode where the address is incremented by one each cycle, starting at the current program counter value. The mode cannot be interrupted, except by a RESET.

All three varient of HCF appear identical, and any prefixes are ignored.

X18

Opcodes:

  0x18 - X18 (3 cycles)

This instruction updates the flags.

Flags:

  • E' = (F & B6)
  • F' = (H & B5)
  • H' = (I & B4)
  • I' = (N & B3)
  • N' = (Z & B2)
  • Z' = (V & B1)
  • V' = (C & B0) | (Z & B2)
  • C' = 0

where Bn is bit n of the byte at PC + 1 (the next opcode)

Note: the extact way in which this instruction depends on the following byte (Bn) was discovered by David Flamand, see his document in the References section.

NOP

Opcodes:

  0x1B - NOP (2 cycles)

This instruction behaves the same as NOP (0x12)

XANDCC

Opcodes:

  0x38 - XANDCC #immediate8 (4-cycles)

This instruction behaves like a 4-cycle version of ANDCC

XRES

Opcodes:

  0x3E - XRES (19 cycles)

This instruction is similar to SWI (0x3F), except the RESET vector (0xFFFE/F) is used to determine the next PC value, and it does not correctly set the E flag in the saved machine state.

Flags:

  • all flags are unchanged

Note: unlike a hardware RESET, the F and I flags are not set.

Bus cycles:

  cycle  1 - read opcode (0x3E)
  cycle  2 - read PC + 1
  cycle  3 - dead cycle
  cycle  4 - write PCL to system stack
  cycle  5 - write PCH to system stack
  cycle  6 - write UL to system stack
  cycle  7 - write UH to system stack
  cycle  8 - write YL to system stack
  cycle  9 - write YH to system stack
  cycle 10 - write XL to system stack
  cycle 11 - write XH to system stack
  cycle 12 - write DP to system stack
  cycle 13 - write ACCA to system stack
  cycle 14 - write ACCB to system stack
  cycle 15 - write CC to system stack (note: E is not explicitely)
  cycle 16 - dead cycle
  cycle 17 - read 0xFFFE
  cycle 18 - read 0xFFFF
  cycle 19 - dead cycle

NEG

Opcodes:

  0x01 - NEG direct
  0x41 - NEGA
  0x51 - NEGB
  0x61 - NEG indexed
  0x71 - NEG extended

These are exact aliases of the documented NEG instruction, i.e. bit 0 of the opcode is ignored when decoding NEG.

XNC

Opcodes:

  0x02 - XNC direct
  0x42 - XNCA
  0x52 - XNCB
  0x62 - XNC indexed
  0x72 - XNC extended

This instruction behaves like NEG if C=0 or COM if C=1;

LSR

Opcodes:

  0x05 - LSR direct
  0x45 - LSRA
  0x55 - LSRB
  0x65 - LSR indexed
  0x75 - LSR extended

These are exact aliases of the documented LSR instruction, i.e. bit 0 of the opcode is ignored when decoding LSR.

XDEC

Opcodes:

  0x0B - XDEC direct
  0x4B - XDECA
  0x5B - XDECB
  0x6B - XDEC indexed
  0x7B - XDEC extended

This instruction is similar to DEC instruction, except that the carry flag is modified.

Flags:

  • Z - set if the result is zero, otherwise cleared (the same as DEC)
  • N - set if the result is negative, otherwise cleared (the same as DEC)
  • V - set if the operand is is 0x80, otherwise cleared (the same as DEC)
  • C - cleared if the operand is zero, otherwise set (different to DEC)
  • all other flags unchanged

XCLRA/B

Opcodes:

  0x4E - XCLRA
  0x5E - XCLRB

This instruction is similar to CLRA/B, except the C flag is unchanged.

Flags:

  • Z = 1
  • N = 0
  • V = 0
  • all other flags unchanged.

Store Immediate

Opcodes:

  0x87 - XSTA #immediate8
  0x8F - XSTX #immediate16
  0xC7 - XSTB #immediate8
  0xCF - XSTU #immediate16

These instructions are only partly functional.

Bus cycles (XSTA):

  cycle 1 = read opcode (0x87)
  cycle 2 = read the 8-bit immediate operand (and discard the value)

Bus cycles (XSTB):

  cycle 1 = read opcode (0xC7)
  cycle 2 = read the 8-bit immediate operand (and discard the value)

Bus cycles (XSTX):

  cycle 1 = read opcode (0x8F)
  cycle 2 = read MSB of the 16-bit immediate operand (and discard the value)
  cycle 3 = write LSB of the 16-bit immediate operand with the LSB of the X register

Bus cycles (XSTU):

  cycle 1 = read opcode (0xCF)
  cycle 2 = read MSB of the 16-bit immediate operand (and discard the value)
  cycle 3 = write LSB of the 16-bit immediate operand with the LSB of the U register

Flags for unprefixed Store Immediate

For the forms of Store Immediate that are unprefixed, the N, Z, V flag behaviour is complex, and depends on the group of the previous instruction.

Flags for 0x87 (STA) and 0xC7 (STB):

The previous instuction can be categorized into one of 8 groups:

  • GRP_N0_Z1 (BITA, CMPA)

    • Z = 1
    • N = 0
    • V = 0
  • GRP_A_00 (LDA, LDD)

    • Z set if A is zero, otherwise cleared
    • N set if A is negative, otherwise cleared
    • V = 0
  • GRP_A_FF (ADCA, ADDA, ANDA, EORA, ORA, SBCA, SUBA)

    • Z set if A = 0xFF, otherwise cleared
    • N set if A is positive, otherwise cleared
    • V = 0
  • GRP_A_01 (ANDD, ASLA, ASRA, COMA, DAA, DECA, INCA, LSRA, NEGA, ROLA, RORA, SUBD, TSTA, XDECA, XNCA)

    • Z set if A = 0x01, otherwise cleared
    • N set if (A - 1) is negative, otherwise cleared
    • V set if A = 0x80, otherwise cleared
  • GRP_B_01 (SEX)

    • Z set if B = 0x01, otherwise cleared
    • N set if (B - 1) is negative, otherwise cleared
    • V set if B = 0x80, otherwise cleared
  • GRP_R16_01 (CMPD, CMPS, CMPU, CMPX, CMPY, XADDD, XADDU)

    • let R = result[15:8]
    • Z set if R = 0x01, otherwise cleared
    • N set if (R - 1) is negative, otherwise cleared
    • V set if R = 0x80, otherwise cleared
  • GRP_LEA (LEAS, LEAU, LEAX, LEAY)

    • let R = (index[15:8] | index[7:0])
    • Z set if R = 0x01, otherwise cleared
    • N set if (R - 1) is negative, otherwise cleared
    • V set if R = 0x80, otherwise cleared
  • Default (all other instructions)

    • N = 1
    • Z = 0
    • V = 0

Flags for 0x8F (STX):

  • N updated as above
  • if (X[7:0] != 0) then Z is cleared, otherwise updated as above
  • V = 0

Flags for 0xCF (STU):

  • N updated as above
  • if (U[7:0] != 0) then Z is cleared, otherwise updated as above
  • V = 0

Undocumented Page-2 Opcodes

The undocumented Page-2 opcodes generally behave as the unprefixed opcode, with the additional cycle for processing and discarding the 0x10 prefix. There are a few exceptions where the prefix does trigger different behaviour:

XLBRA Long Branch Always

Opcodes:

  0x10 0x20 - XLBRA relative16 (5 cycles)

This instruction is the same as LBRA (0x16), except it take 5 cycles rather than 4 cycles.

XSWI2

Opcodes:

  0x10 0x3E - XSWI2 (20 cycles)

This instruction is similar to SWI2 (0x10 0x3F), except it does not correctly set the E flag in the saved machine state.

Bus cycles:

  cycle  1 - read prefix (0x10)
  cycle  2 - read opcode (0x3E)
  cycle  3 - read PC + 1
  cycle  4 - dead cycle
  cycle  5 - write PCL to system stack
  cycle  6 - write PCH to system stack
  cycle  7 - write UL to system stack
  cycle  8 - write UH to system stack
  cycle  9 - write YL to system stack
  cycle 10 - write YH to system stack
  cycle 11 - write XL to system stack
  cycle 12 - write XH to system stack
  cycle 13 - write DP to system stack
  cycle 14 - write ACCA to system stack
  cycle 15 - write ACCB to system stack
  cycle 16 - write CC to system stack (note: E is not set explicitely)
  cycle 17 - dead cycle
  cycle 18 - read 0xFFF4
  cycle 19 - read 0xFFF5
  cycle 20 - dead cycle

XADDD

Opcodes:

  0x10 0xC3 - XADDD #immediate16.
  0x10 0xD3 - XADDD direct.
  0x10 0xE3 - XADDD indirect.
  0x10 0xF3 - XADDD extended.

XADDD performs a 16-bit addition of the operand with D, and sets the Z,N,C,V flags in an identical manner to ADDD. The result is, however, not written back to D. So it's not idendical to ADDD. It could also be thought of as a CMPD where the ALU is set to ADD16 rather than SUB16.

Flags:

  • Z - set if the result is zero, otherwise cleared
  • N - set if the result is negative, otherwise cleared
  • C - set if there is carry out of the 16-bit addition, otherwise cleared
  • V - set if there is two-complement signed overflow in the 16-bit addition, otherwise cleared

Page-2 Store Immediate

Opcodes:

  0x10 0x87 - XSTA #immediate8
  0x10 0x8F - XSTY #immediate16
  0x10 0xC7 - XSTB #immediate8
  0x10 0xCF - XSTS #immediate16

These instructions are only partly functional; the lower 8 bits of the register is written to the lower 8 bits of the immediate operand.

Flags:

  • N = 1
  • Z = 0
  • V = 0
  • all other flags unchanged

Bus cycles (XSTY):

  cycle 1 = read prefix (0x10)
  cycle 2 = read opcode (0x8F)
  cycle 3 = read MSB of the 16-bit immediate operand (and discard the value)
  cycle 4 = write LSB of the 16-bit immediate operand with the LSB of the Y register

Bus cycles: (XSTS):

  cycle 1 = read prefix (0x10)
  cycle 2 = read opcode (0xCF)
  cycle 3 = read MSB of the 16-bit immediate operand (and discard the value)
  cycle 4 = write LSB of the 16-bit immediate operand with the LSB of the S register

These instructions are similar to the unprefixed versions, except the flag behaviour is simpler.

Flags:

  • N = 1
  • Z = 0
  • V = 0
  • all other flags unchanged

Undocumented Page-3 Opcodes

The undocumented Page-3 opcodes generally behave as the unprefixed opcode, with the additional cycle for processing and discarding the 0x11 prefix. There are a few exceptions where the prefix does trigger different behaviour:

XFIRQ

Opcodes:

  0x11 0x3E - XFIRQ (20 cycles)

This instruction is simular to the unprefixed XRES (0x3E), except it take an additional cycle and uses the FIRQ vector.

Flags:

  • all flags are unchanged

Note: unlike a hardware FIRQ, the F and I flags are not set, and the full machine state is pushed.

Bus cycles:

  cycle  1 - read prefix (0x11)
  cycle  2 - read opcode (0x3E)
  cycle  3 - read PC + 1
  cycle  4 - dead cycle
  cycle  5 - write PCL to system stack
  cycle  6 - write PCH to system stack
  cycle  7 - write UL to system stack
  cycle  8 - write UH to system stack
  cycle  9 - write YL to system stack
  cycle 10 - write YH to system stack
  cycle 11 - write XL to system stack
  cycle 12 - write XH to system stack
  cycle 13 - write DP to system stack
  cycle 14 - write ACCA to system stack
  cycle 15 - write ACCB to system stack
  cycle 16 - write CC to system stack (note: E is not set explicitely)
  cycle 17 - dead cycle
  cycle 18 - read 0xFFF6
  cycle 19 - read 0xFFF7
  cycle 20 - dead cycle

XADDU

Opcodes:

  0x11 0xC3 - XADDU #immediate16
  0x11 0xD3 - XADDU direct
  0x11 0xE3 - XADDU indirect
  0x11 0xF3 - XADDU extended

XADDU performs a 16-bit addition of the operand with (U | 0xFF00), and sets the Z,N,C,V flags in an identical manner to ADDD. The result is, however, not written back to U.

Flags:

  • Z - set if the result is zero, otherwise cleared
  • N - set if the result is negative, otherwise cleared
  • C - set if there is carry out of the 16-bit addition, otherwise cleared
  • V - set if there is two-complement signed overflow in the 16-bit addition, otherwise cleared

Page-3 Store Immediate

Opcodes:

  0x11 0x87 - XSTA #immediate8
  0x11 0x8F - XSTX #immediate16
  0x11 0xC7 - XSTB #immediate8
  0x11 0xCF - XSTU #immediate16

These instructions are similar to the unprefixed versions, except the flag behaviour is simpler.

Flags:

  • N = 1
  • Z = 0
  • V = 0
  • all other flags unchanged

Undefined values of the Half-Carry Flag

The Motorola datasheet states the half-carry flag (H) becomes undefined after the following operations:

  • ASLA/ASLB/ASL
  • ASRA/ASRB/ASR
  • CMPA/CMPB
  • NEGA/NEGB/NEG
  • SBCA/SBCB
  • SUBA/SUBB

(Footnote 8 says: Value of half-carry flag is undefined)

In practice, in all these cases the half-carry flag is unchanged by the operation.

Undefined values of the Overflow Flag

The Motorola datasheet states the SEX instruction clears the overflow flag. That is incorrect, the overflow flag is unchanged.

The DAA operation perform the decimal adjust by adding a correction factor:

ACCA = ACCA + correction

The correction factor is calcuated as expected in all cases, see Atkinson page 56 for details:

int correction = 0x00;
if (H == 1 || (ACCA & 0x0f) > 0x09) {
   correction |= 0x06;
}
if (C == 1 || (ACCA & 0xf0) > 0x90 || ((ACCA & 0xf0) > 0x80 && (ACCA & 0x0f) > 0x09)) {
   correction |= 0x60;
}

The Motorola datasheet states the overflow flag (V) is cleared after DAA.

In practice the overflow flag is updated as follows:

V = (the C new value) XOR (the new bit 7 of ACCA)

Multiple Prefixes

It's possible to chain multiple prefixes together. Only the first prefix is significant, all subsequent prefixes are discarded. Each discarded prefix adds one cycle to the instruction. This allows for arbitrarily long instructions that are not interruptable.