The 6809 was pretty good... and
Hitachi decided they wanted to make their own version - the 6309! However, they decided they could do better, and added a ton of extra commands and registers to the chip!... the 6309 has an extra 2 eight bit registers, for a total of 32 bits! Rather strangely they forgot to tell anyone, and it was much later that anyone found out about all the extra functionality! |
![]() |
If
you
want to learn 6309 get the 6309
cheatsheet It contains all the 6809 and 6309 opcodes. |
![]() |
![]() |
We'll be using
Macroassembler AS for our assembly in these tutorials... VASM is
an assembler which supports rarer CPU's like 6309 and 65816 and
many more, and also supports multiple syntax schemes... You can get the source and documentation for AS from the official website HERE |
![]() |
You probably
think 64 kilobytes doesn't sound much when a small game now
takes 8 gigabytes, but that's 'cos modern games are sloppy,
inefficient, fat and lazy - like the basement dwelling
losers who wrote them!!! 6309 code is small, fast, and super efficient - with ASM you can do things in 1k that will amaze you! |
Assemblers will
use a symbol to denote a hexadecimal number, some use $FF or #FF
or even 0x, but this guide uses & - as this is how
hexadecimal is represented in CPC basic All the code in this tutorial is designed for compiling with WinApe's assembler - if you're using something else you may need to change a few things! But remember, whatever compiler you use, while the text based source code may need to be slightly different, the compiled "BYTES' will be the same! |
![]() |
Decimal | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | ... | 255 |
Binary | 0000 | 0001 | 0010 | 0011 | 0100 | 0101 | 0110 | 0111 | 1000 | 1001 | 1010 | 1011 | 1100 | 1101 | 1110 | 1111 | 11111111 | |
Hexadecimal | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | FF |
Bit position | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
Digit Value (D) | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
Our number (N) | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 0 |
D x N | 128 | 64 | 0 | 0 | 8 | 4 | 0 | 0 |
128+64+8+4= 204 So %11001100 = 204 ! |
If you ever get confused, look at Windows
Calculator, Switch to 'Programmer Mode' and it has binary
and Hexadecimal view, so you can change numbers from one form to
another! If you're an Excel fan, Look up the functions DEC2BIN and DEC2HEX... Excel has all the commands to you need to convert one thing to the other! |
![]() |
Negative number | -1 | -2 | -3 | -5 | -10 | -20 | -50 | -254 | -255 |
Equivalent Byte value | 255 | 254 | 253 | 251 | 246 | 236 | 206 | 2 | 1 |
Equivalent Hex Byte Value | FF | FE | FD | FB | F6 | EC | CE | 2 | 1 |
![]() |
All these number types can be confusing,
but don't worry! Your Assembler will do the work for you! You can type %11111111 , &FF , 255 or -1 ... but the assembler knows these are all the same thing! Type whatever you prefer in your ode and the assembler will work out what that means and put the right data in the compiled code! |
|
MD: 0I------FN
CC: EFHINZVC
|
The Direct page is like the 6502 Zero Page, however
it does not need to be at zero! We can load A with a value, then TFR A,DP to set the direct page... we need to tell the assembler where the direct page is, otherwise some commands may malfunction, we do this with ASSUME dpr:$xx - this is called SETDP on some assemblers |
![]() |
Address | Vector (Address) | Registers Auto-pushed onto stack |
$FFF2 | SWi 3
Vector |
D,X,Y,U,DP,CC |
$FFF4 | SWI 2
Vector |
D,X,Y,U,DP,CC |
$FFF6 | FIRQ Vector | CC (E
flag cleared) |
$FFF8 | IRQ Vector | D,X,Y,U,DP,CC |
$FFFA | SWI 1 Vector | D,X,Y,U,DP,CC |
$FFFC | NMI Vector | D,X,Y,U,DP,CC |
$FFFE | RESET Vector | NA |
Inherent Addressing | Commands that don't take a parameter | ABX |
Register Addressing | Commands that only use register | TFR A,DP |
Immediate Addressing | Direct Address of command | ADDA #$10 ADDD #$1000 |
Direct Page addressing | Read from DP (zero page) | ADDA $10 |
Extended Direct addressing | Read from an address | ADDA $1234 |
Extended Indirect Addressing | Read from the address specified... then get the value from that address | ADDA [$1234] |
Indexed Addressing | Uses a 2nd setting byte - allows for Autoinc | ,R offset,R label,pcr ,R+ ,-R [] |
Indexed Addressing: Zero Offset | Just use the address in the register | LDA ,Y LDA 0,Y LDA Y |
Indexed Addressing: 5 bit offset | -16 to +15 offset | LDA -1,Y |
Indexed Addressing: Constant offset from base register | 8 / 16 bit offset from X,Y,U,S ... Can be negative or positive | LDA 1000,Y |
Indexed Addressing: Constant Offset From PC | 8 / 16 bit offset from PC | LDA $10,PC |
Program counter relative | PCR is like PC, but is calculated by the assembler | ADDA label,PCR |
Indirect with constant offset from base register | Load from the address in the register + offset | LDA [1,X] |
Accumulator offset from Base register | Add accumulator (A/B/D) to a X,Y,U,S (not PC) | LDA B,Y |
Indirect Accumulator offset from Base register | Load from the address made up of a X,Y,U,S Plus the accumulator | LD [B,Y] |
AutoIncrement | Add 1 or 2 to the register | ADDA ,X+ ADDA ,X++ |
AutoDecrement | Subtract 1 or 2 from the register | ADDA ,-X ADDA ,--X |
Indirect AutoIncrement | Load from the address in Register, then add 1 or 2 | ADDA [,X+] ADDA [,X++] |
Indirect AutoDecrement | Subtract 1 or 2 then Load from the address in Register | ADDA [,-X] ADDA [,--X] |
Program relative | Offset to PC | BRA label |
Prefix | Example | Z80 equivalent | Meaning |
# | #16384 | 16384 | Decimal Number |
#% | #%00001111 | %00001111 | Binary Number |
#$ | #$4000 | &4000 | Hexadecimal number |
#' | #'a | 'a' | ascii value |
12345 | (16384) | decimal memory address | |
$ | $4000 | (&4000) | Hexadecimal memory address |
DEX/DEY/INX/INY | Tfr X,D ;replace X with Y if required DecB ;or IncB as required Tfr D,X ;replace X with Y if required |
|
CLC | AndCC #%11111110 | |
SEC | OrCC #%00000001 | |
DeX |
LEAX -1,X |
|
Lesson
1 - Getting started with 6309 Lets learn the basics of using 6309... We'll take a look at the new registers, and some of the new commands. |
![]() The 6809 devtools support 6309 |
6309_Lesson_1.asm
|
![]() |
![]() |
6309 supports
everything 6809 does... so we'll only cover the new stuff here! If you don't know 6809 yet, please see these tutorials! |
The 6809's two 8 bit registers have been doubled! As well as the old A and B, we now have E and F! |
![]() |
Of course, The 16 bit D register is still made up of A+B, but we
now have another 16 bit pair known as W W is made up of E+F |
![]() |
All 4 8 bit registers are combined in some cases to make one 32
bit quad, known as Q Q is made up of A+B+E+F |
![]() |
There are a few more weird and wonderful registers! 0 (or z in asw) is a hardwired zero, we can use it as a substitute for a regsiter, to get a zero value, or 'discard' the result of a command. V is a 16 bit register referred to as the 'Transfer Value'. We can't set it directly like E and F, but we can move values into it. Also, strangely, it keeps its value after a reboot! The final register is MD - the MoDe register. We can turn on bit 0 to enable Native 6309 mode, though we can use all the new commands without doing so!. Turning on native mode alters the way the interrupts work. |
![]() |
Here are the results. |
![]() |
Commands using the
new registers like E and F often take an extra byte than the
ones using A and B. You should still prioritize the original registers where you can, and just use the new ones as an alternative to the zero page, or to reduce push pops. |
![]() |
Many of the previous maths commands can now be used with the new
registers. We can use Clear, Inc and Dec... and these now even work with the D register. The new registers also work with ADD and SUB |
![]() |
Here are the results. |
![]() |
We have new ADDR and SUBR commands, which will add and subtract
one register from/to another. The destination is on the right. ADCR and SBCR provide similar functions, but add and subtract the carry as well Unfortunately we dont have a 32 bit add or subtract command which works with Q, but we can use the 16 bit commands to effect a 32 bit add or subtract via the carry. |
![]() |
Here are the results |
![]() |
We now have 16 bit rotate
and shift commands, which work with the D and W registers. ROR and ROL shift between the 16 bits of the register, plus the carry as a 17th bit We do not, however, have any that work with the 32 bit Q |
![]() |
Here are the results |
![]() |
We have new logical shift commands for unsigned 16 bit numbers. We now have LSRD and LSRW Strangely, while we have a LSLD, we do not have a LSLW, however "ADDR W,W" will add W to itself giving the same effect. |
![]() |
Here are the results |
![]() |
We have new Arithmetic shift commands for signed 16 bit numbers. We now have ASRD and ASRW Strangely, while we have a ASLD, we do not have a ASLW, however "ADDR W,W" will add W to itself giving the same effect. |
![]() |
Here are the results |
![]() |
We have TST and CMP commands for the new 8 and 16 bit registers.
These commands work the same as usual. TST will set the Zero and Negative flags based on the contents of a register. CMP will set the flags like a subtraction, but will not change the tested register. Unfortunately these commands cannot work with the 32 bit Q register. |
![]() |
Here are the results | ![]() |
We have some new logical operations! EOR, AND and OR now can work with the 16 bit D register (but not the W one) We now also have commands which work with register to register values. The first specified register will be processed with the second, and the result stored in the second register. |
![]() |
Here are the results |
![]() |
We have some new commands which perform an operation with an
Immediate value and the address in a register + an offset,
changing the byte at the effective address. EIM will Eor an Immediate with Memory AIM will And an Immediate with Memory OIM will Or an Immediate with Memory TIM will Test an Immediate with Memory - setting the flags like logical operation AND |
![]() |
Here are the results. |
![]() |
We have a few other useful commands which perform Bit operations SEXW will Sign EXtend 16 bit W into a 32 bit value Q (effectively filling D with the top bit of W) NEGD will negate the 16 bit D register - however there is no command to negate the W register. We also have a COMplement command, which will flip the bits of a register, this works on all the 8 and 16 bit registers, but the 32 bit Q cannot be flipped in a single command |
![]() |
Here are the results |
![]() |
Lesson
3 - More new commands! Lets take a look at the last of the new 6309 commands. |
![]() The 6809 devtools support 6309 |
6309_Lesson_3.asm
|
![]() |
The PSH and PUL commands do
not work with the new E and F registers (and by implication W) The only way to backup and restore these registers via the stack are using the 4 new commands. PSHSW will Push W onto the S stack. PULSW will Pull W off the S stack. PSHUW will Push W onto the U stack. PULUW will Pull W off the U stack. |
![]() |
Here are the results |
![]() |
The 6309 adds some impressive new bulk transfer commands...
these give functions like LDIR on the Z80, or MVN on the 65816. The TFM command allows us to transfer from one register to another, with a variety of Increment and Decrement options (4 in total) TFM r1+,r2+ will copy W bytes from the address in r1 to the address in r2, incrementing both after each byte. TFM r1-,r2- will copy W bytes from the address in r1 to the address in r2, decrementing both after each byte. |
![]() |
Here are the results! We copied 4 bytes to the right, then 4 bytes to the left. |
![]() |
We have two more slightly strange options TFM r1+,r2 will copy W bytes from the address in r1 to the address in r2, incrementing only the source after each byte TFM r1,r2+ will copy W bytes from the address in r1 to the address in r2, incrementing only the destination after each byte These could be usedful for transferring a block of data to, or from, a memory mapped hardware port. |
![]() |
Here are the results, first we transferred 89,AB,CD,EF to $7104
(leaving only EF visible) Then we transferred 89 four times. |
![]() |
We have a new 16/32 bit
Multiply command! MULD will multiply D by either an immediate or memory value - alas we can't multiply by the w register. DIVQ is the opposite, this divides the 32 bit value in Q by the specified value, putting the whole number result in W, and the remainder in D DIVD is the opposite of the original MUL command, this divides the 16 bit value in D, putting the whole number result in B and the remainder in A |
![]() |
Here is the results! | ![]() |
ASW seems to not assembly the DIVD command
correctly, it seems to store the immediate value as a 16 bit
value, causing the command to end up as 4 bytes, rather than the
apparently correct 3. |
![]() |
There are a set of special new commands for bit manipulation on
the 6309: LDBT Load Memory Bit into Register Bit STBT Store value of a Register Bit into Memory BAND Logically AND Register Bit with Memory Bit BOR Logically OR Memory Bit with Register Bit BEOR Exclusive-OR Register Bit with Memory Bit BITD Bit Test Accumulator D with Memory Word Value BIEOR Exclusively-OR Register Bit with Inverted Memory Bit BIOR Logically OR Register Bit with Inverted Memory Bit These have an odd format: CMD Reg.Bit, Address.Bit Where Reg and Address are the source/destination (Depending on the command), and Bit is the source and destination bit numbers (0-7) Unfortunately, it seems they are not emulated properly - the 3 register options A,B,CC are mixed up (I think the assembler is doing them right), so we won't be covering them in detail here. |
![]() |