Introduction
The Motorola MC6809 (1978) is the 8-bit processor of the Tandy Color Computer and the Dragon 32 and 64, the Vectrex, the Thomson MO5 and TO7, the Fujitsu FM-7 and many arcade boards (Williams, Konami, Namco). The Hitachi HD6309 is a pin-compatible CMOS version with a second register file, a 32-bit register Q, block moves, bit instructions and hardware multiply and divide that Hitachi never documented in its data sheets (see HD6309 additions). The 6809 is big-endian: a 16-bit operand or address is stored high byte first. The older 6800, 6801 and 6303 have their own page: the 6809 is source-compatible with them, not binary-compatible.
The explorer’s disassembler for these processors is explorer/cpu/m6809/ (our own code). Where it meets this code (a
Color Computer machine-language file) every instruction carries one of these statuses, drawn in its own colour and also written as text:
- documented: in the MC6809 data sheet (or, with the HD6309 chosen, an HD6309 instruction that Hitachi’s documents list);
- illegal: an opcode, or an indexed postbyte, that the chip does not define. The MC6809 data sheet says nothing about what the chip does; the HD6309 traps through the
vector at
$FFF0. Shown as.byte, one byte long (or up to the postbyte for an undefined indexed form); - reserved: a TFR or EXG with a register code the data sheet does not define, or with registers of different sizes.
Several opcodes that the 6809 leaves undefined do something on real chips (they were used by copy-protection and a few demos). They are shown as illegal here because none of them was found in a source we could read; this is one of the reasons the reference is rated partial.
Registers
| Register | Size | Use |
|---|---|---|
A, B | 8 | Accumulators; together D (A is the high byte) |
X, Y | 16 | Index registers |
U, S | 16 | User stack pointer and system stack pointer (S is used by interrupts, BSR, JSR and RTS); both also serve as index registers |
PC | 16 | Program counter |
DP | 8 | Direct page register: the high byte of an address in direct addressing. It is $00 after a reset and changes only through TFR, EXG, PULS, PULU and RTI |
CC | 8 | Condition codes E F H I N Z V C (below) |
E, F, W, Q, V, MD, zero register | 8, 8, 16, 32, 16, 8 | HD6309 only: E and F are the halves of W; Q is D and W together (D high); V is a free 16-bit register; MD is the mode register |
Condition codes
| Bit | Flag | Meaning |
|---|---|---|
| 7 | E | Entire: set when an interrupt pushed the whole register set; RTI pulls all registers when it is set, only CC and PC when it is clear |
| 6 | F | FIRQ mask: set to ignore the fast interrupt |
| 5 | H | Half carry: carry out of bit 3, set by ADDA, ADDB and ADCA, ADCB, used by DAA |
| 4 | I | IRQ mask |
| 3 | N | Negative: the top bit of the result |
| 2 | Z | Zero |
| 1 | V | Signed overflow |
| 0 | C | Carry, or the inverse of borrow |
The viewer lists the flags an instruction changes and the ones it reads. A flag the data sheet calls undefined after an instruction
(H after NEG, ASL, SUB, SBC, CMP in 8 bits, V after DAA) is not listed as changed.
Addressing modes
| Mode | Written | Bytes | Notes |
|---|---|---|---|
| Inherent | nop | 1 | No operand; the register is in the mnemonic (inca) |
| Immediate | lda #$12, ldd #$1234 | 2 or 3 | 8 or 16 bits, big-endian; HD6309 ldq #$12345678 is 5 bytes |
| Direct | lda $12 | 2 | The address is DP (high byte) and the operand (low byte). The explorer does not follow DP through the program: a direct operand is shown as the byte, and the address it names is that byte plus the “DP register assumed” choice (default $00) |
| Extended | lda $1234 | 3 | A 16-bit address |
| Indexed | lda 5,x | 2 to 5 | A postbyte, then 0, 1 or 2 offset bytes: the table below |
| Relative | bra $2010 | 2 or 3 | A signed 8-bit (BRA…) or 16-bit (LBRA…) displacement from the address after the instruction; the viewer shows the target |
| Register pair | tfr a,b | 2 | One postbyte: source in the high nibble, destination in the low nibble |
| Stack mask | pshs a,b,x | 2 | One postbyte: a bit for each register |
Syntax choices in the explorer: Motorola writes a direct operand as $12 and an extended one as $1234; lwasm
(LWTOOLS) writes <$12 and >$1234, which cannot be misread. PC-relative operands are written $2010,pcr in both.
Flex and OS-9 assemblers use the Motorola mnemonics: they were not separately sourced. asl and lsl are the same opcode; the viewer writes asl.
The indexed postbyte
The byte after an indexed opcode. RR picks the register: 00 X, 01 Y, 10 U, 11 S.
Bit 7 clear is a 5-bit signed offset; bit 7 set selects one of the forms below, and bit 4 set makes the form indirect
(the computed address holds the 16-bit address to use). “Extra cycles” are added to the instruction’s base count; the second figure is for the indirect form.
| Postbyte | Form | Offset bytes | Extra cycles | Indirect |
|---|---|---|---|---|
0RRnnnnn | n,R (−16 to +15) | 0 | 1 | no |
1RR00000 | ,R+ | 0 | 2 | no |
1RR0 0001 | ,R++ | 0 | 3 | yes 1RR1 0001: +6 |
1RR00010 | ,-R | 0 | 2 | no |
1RR0 0011 | ,--R | 0 | 3 | yes 1RR1 0011: +6 |
1RR0 0100 | ,R | 0 | 0 | yes 1RR1 0100: +3 |
1RR0 0101 | B,R | 0 | 1 | yes: +4 |
1RR0 0110 | A,R | 0 | 1 | yes: +4 |
1RR0 1000 | n,R (8-bit) | 1 | 1 | yes: +4 |
1RR0 1001 | n,R (16-bit) | 2 | 4 | yes: +7 |
1RR0 1011 | D,R | 0 | 4 | yes: +7 |
1xx0 1100 | n,PCR (8-bit) | 1 | 1 | yes: +4 |
1xx0 1101 | n,PCR (16-bit) | 2 | 5 | yes: +8 |
1xx1 1111 | [n] extended indirect | 2 | 5 | always indirect |
xx means “any”: the register field is ignored by the PC-relative forms and, on the MC6809, by the extended indirect form ($9F $BF $DF $FF all mean the same).
The explorer shows the PC-relative operand as the address it names, followed by ,pcr; the offset is measured from the end of the whole instruction.
Every other postbyte of the MC6809 is undefined: the unused low nibbles 0111, 1010 and 1110, an indirect single increment or decrement
([,R+], [,-R]) and the extended form with bit 4 clear. They are shown as illegal, as many bytes long as the opcode and the postbyte.
On the HD6309 the unused low nibbles define extra forms, and the single increment and decrement indirect forms and the extended form with a register field become the W forms:
| Postbyte | Form |
|---|---|
1RR0 0111 · 1RR1 0111 | E,R · [E,R] |
1RR0 1010 · 1RR1 1010 | F,R · [F,R] |
1RR0 1110 · 1RR1 1110 | W,R · [W,R] |
$8F · $90 | ,W · [,W] |
$AF · $B0 | n16,W · [n16,W] (two offset bytes) |
$CF · $D0 | ,W++ · [,W++] |
$EF · $F0 | ,--W · [,--W] |
On the HD6309 the postbytes $92 $B2 $BF $D2 $DF $F2 $FF are undefined and trap. The cycle counts of the E, F, W and W-based forms are not given here (no source).
TFR and EXG register codes
| Code | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| MC6809 | D | X | Y | U | S | PC | – | – | A | B | CC | DP | – | – | – | – |
| HD6309 | D | X | Y | U | S | PC | W | V | A | B | CC | DP | 0 | 0 | E | F |
The source is the high nibble, the destination the low nibble: tfr a,b is $1F $89. The 8-bit registers are A B CC DP (and E F); the others are 16-bit. The data sheet does not define
a transfer between registers of different sizes, nor a code it leaves out: both are shown as reserved. On the HD6309 the codes C and D are a zero register: it reads as 0 and a write is discarded.
EXG with PC as one side jumps; so does TFR to PC.
PSHS, PULS, PSHU and PULU masks
| Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|---|---|---|---|---|---|---|---|---|
| Register | PC | U (S for PSHU and PULU) | Y | X | DP | B | A | CC |
| Bytes | 2 | 2 | 2 | 2 | 1 | 1 | 1 | 1 |
Registers are pushed from bit 7 down to bit 0 (PC first, at the highest address) and pulled in the opposite order. Cycles: 5 plus one for each byte moved. A PULS with bit 7 set returns, so the viewer marks it as a return.
The three opcode pages
An opcode is one byte. Two of the bytes, $10 and $11, are prefixes: the byte after them is an opcode of page 2 or page 3,
which hold the long branches (LBHI…), CMPD, CMPY, LDY, STY, LDS, STS, CMPU, CMPS, SWI2,
SWI3, and all the HD6309 additions. A prefix makes the length depend on the byte before it, so the explorer decodes an instruction at every byte and lays the results in columns
(the way it does for x86 and 6502). A prefix not followed by a defined opcode is shown as one illegal byte, so the next byte is decoded as an instruction of its own.
See the opcode maps.
Interrupt vectors
| Address | Vector |
|---|---|
$FFF0 | Reserved (MC6809); illegal-instruction and division-by-zero trap (HD6309) |
$FFF2 | SWI3 |
$FFF4 | SWI2 |
$FFF6 | FIRQ |
$FFF8 | IRQ |
$FFFA | SWI |
$FFFC | NMI |
$FFFE | RESET |
Timing
Cycle counts are the MC6809 data sheet’s (the number of E-clock cycles): the base count of the opcode plus the extra cycles of the indexed form, shown for the exact postbyte decoded.
SYNC and CWAI wait for an interrupt (4+ and 20+). RTI takes 6, or 15 when the pulled E flag is set. A long conditional branch takes 5, or 6 when taken. The HD6309 in native mode is faster
for many instructions; those counts are not tabulated here, and the HD6309’s own instructions have no cycle count in the viewer.
How this was checked
- Two published opcode tables written out separately and compared byte by byte with the decoder: the list of sbprojects.net’s 6309 opcode page (every MC6809 and HD6309 opcode, 436 opcodes, with the addressing mode and, by the rule of the data sheet, the length) and the Motorola opcode grid of the three pages. The decoder also has exactly 330 undefined opcodes on the HD6309, the figure given on the 6809Decoder wiki, with the seven undefined indexed postbytes it lists.
- Executing cores: every MC6809 opcode (pages 1 to 3) and every indexed postbyte it defines is run on two different cores (a core by Martin Maly and the JSVecX core) from random register and memory states, and the instruction length, the registers and flags that changed, whether data memory was read and written, the branch target and the cycle count are compared. The details and the places where a core is wrong are in the test notes.
- The debugger’s own decoder (
emulators.org/debugger/src/cpus/m6809.js) gives the same length and mnemonic for every opcode and postbyte both define; it accepts a few postbytes the data sheet leaves undefined.
Sources
- MC6809-MC6809E Microprocessor Programming Manual, Appendix F (opcode map) – Motorola
- Hitachi 6309 opcodes and mnemonics – sbprojects.net
- Undocumented 6309 Behaviours – David Banks’ 6809Decoder
Not described
- What the MC6809 does with its undefined opcodes and postbytes (no source was read); the HD6309’s illegal-opcode trap sequence and the
MDregister’s bits. - Which register receives the results of the HD6309
DIVD,DIVQandMULDbeyond the one-line summary, and the flags of the HD6309 register instructions, bit instructions and multiply and divide. - The HD6309’s native-mode cycle counts, and the cycle counts of the HD6309 additions.
- Bus timing, interrupt sequencing, and the MC6809E’s differences (
LIC,AVMA,BUSYpins).