return to Choose a Color Scheme: Dark
Print Mode

Learn Multi platform 68000 Assembly Programming... By Magic!

You're viewing a preview of this site - My 68000 tutorials are planned to start in 2019!

In the late 80's as the 8 bit machines died out, and the world evelved into 16 bit... computing monsters were battling out for supremecy!

Some people favoured the Atari ST, others the Amiga... while some prefered consoles like the Genesis or Neo Geo... while in Japan, home computer users gazed in awe at the X68K with it's arcade perfect gameplay...

In fact, there was no battle between the CPU's... as all these were based on the 68000!... with the exception of the IBM PC's 8086 and the Super nintendo (which was based on a custom 16-bit 6502)... pretty much all the 16 bit machines of the 80's and early 90's were based on the 68000!

With it's 24 bit Address bus, it could access up to 16MB of memory... it was it's 16 bit Databus that defined it as a 16 bit processor... in fact all of it's registers are 32 bit!

The sucessor to the 8 bit 6800... the 68000 kept the powerful indirect addressing... added loads of new regsiters... and a huge command set!...

Adding a Supervisor mode,

Later models (the 68020,68030 and more) would add floating point, true 32 bit support and more... but it's the first generation 68000 that these tutorials will be looking at!

If you want to learn 68000 get the Cheatsheet! it has all the Z80 commands, and useful info on the CPC, Spectrum and MSX!
It will give you a quick reference when you're stuck or confused - and it's what the author used to develop ChibiAkumas!
Print it in color at high resolution on 2 sides of A4 for maximum performance!

Platforms covered in these tutorials
Amiga 500
Atari ST
Neo Geo
Sega Genesis (Sega Mega Drive)
X68000 (Sharp x68k)

PDF resources
Pocket 68000 guide
Motorola 68000 manual

What is the 68000 and what are 16 'bits' You can skip this if you know about binary and Hex (This is a copy of the same section in the Z80 tutorial)
The 68000 is an 16-Bit processor with a 24 bit Address bus!
What's 8 bit... well, one 'Bit' can be 1 or 0
four bits make a Nibble (0-15)
two nibbles (8 bits) make a byte (0-255)
two bytes (16 bits) make a word (0-65535)
three bytes (24 bits) represent one address on the 68000  (0-16777215)
four bytes (32 bits) is the limit of what one register can store on the 68000  (0-4294967295)

Numbers in Assembly can be represented in different ways.
A 'Nibble' (half a byte) can be represented as Binary (0000-1111) , Decimal (0-15) or  Hexadecimal (0-F)... unfortunately, you'll need to learn all three for programming!

Also a letter can be a number... Capital 'A'  is stored in the computer as number 65!

Think of Hexadecimal as being the number system invented by someone wit h 15 fingers, ABCDEF are just numbers above 9!
Decimal is just the same, it only has 1 and 0.
Assemblers will use a symbol to denote a hexadecimal number, in 68000 programming $ is typically used to denote hex, and # is used to tell the assembler to tell the assembler something is a number (rather than an address), so $# is used to tell the assembler a value is a Hex number
In this tutorial VASM will be used for all assembly, if you use something else, your syntax may be different! 
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

Another way to think of binary is think what each digit is 'Worth' ... each digit in a number has it's own value... lets take a look at %11001100 in detail and add up it's total

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 a binary number is small, it may be shown as %11 ... this is the same as %00000011
Also notice in the chart above, each bit has a number, the bit on the far right is no 0, and the far left is 7... don't worry about it now, but you will need it one day!

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!

But wait! I said a Byte could go from 0-255 before, well what happens if you add 1 to 255? Well it overflows, and goes back to 0!...  The same happens if we add 2 to 254... if we add 2 to 255, we will end up with 1
this is actually usefull, as if we want to subtract a number, we can use this to work out what number to add to get the effect we want

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!

The 68000 Registers

The 68000 has a far more advanced register set than the 8 bit machines we are used has 8 Data registers for general use (like BC) , 8 Address registers for memory functions and even stack pointers! (Can be used as HL,DE and even
 SP!)... Calling the 68000 16 bit is rather undrestating the system, as all it's registers are actually 32 bit!

Register Bits Function
D0 31-24 23-16 15-8 7-0 Data Register
D1 31-24 23-16 15-8 7-0 Data Register
D2 31-24 23-16 15-8 7-0 Data Register
D3 31-24 23-16 15-8 7-0 Data Register
D4 31-24 23-16 15-8 7-0 Data Register
D5 31-24 23-16 15-8 7-0 Data Register
D6 31-24 23-16 15-8 7-0 Data Register
D7 31-24 23-16 15-8 7-0 Data Register
A0 31-24 23-16 15-8 7-0 Address Register
A1 31-24 23-16 15-8 7-0 Address Register
A2 31-24 23-16 15-8 7-0 Address Register
A3 31-24 23-16 15-8 7-0 Address Register
A4 31-24 23-16 15-8 7-0 Address Register
A5 31-24 23-16 15-8 7-0 Address Register
A6 31-24 23-16 15-8 7-0 Address Register
A7/SP (USP) 31-24 23-16 15-8 7-0 Address Register / User Stack Pointer
ISP 31-24 23-16 15-8 7-0 Interrupt Stack pointer (A7/SP in Supervisor mode)
PC 31-24 23-16 15-8 7-0 Program Counter

15-8 7-0 Condition Code Register (Flags)

Each register can be used as an 8,16 or 32 bit register depending on your requirements...smaller is faster, but it is not possible to use a 24 bit register - so for memory pointers you'll have to work at 32 bit.

A7 is generally the stack pointer, and can also be referred to as SP, however actually any address register can function as a stack pointer!

There are two 'Stack pointers' available, generally A7/SP will point to the 'User Stack Pointer' (USP) ... however when the procesor is in supervisor mode, the Interrupt stack pointer (ISP) shadow stack pointer will be used. Supervisor mode is only used by the system firmware and operating system, and we won't really use it in programming our software.

The Program Counter is 32 bit, but the x68000 uses 24 bits of addressable memory.

The Condition Code Regsiter is only 16 bit, and only 8 bit can be accessed outside of Supervisor Mode.

The Condition Control register's flags (CCR)
While the CCR is 16 bits, we can't use all of it in normal user mode, - the top 8 bits are protected, but we can access the first 8 bits anytime...
 F  E  D  C  B  A  9  8    7  6  5  4  3  2  1  0
T - S - - I I I - - - X N Z V C


Flag  Name Meaning
T  Trace bit 1 if tracing
S  Supervisor 1 if in supervisor mode
I  Interrupt interrupt level (0-7)
X  eXtend 1 if value of the C-bit  is 1
N  Negative 1 if topmost bit of result is 1
Z  Zero 1 if result equals zero
V  Overflow 1 if arithmatic overflow occured
C  Carry 1 if the last operation resulted in a Carry/borrow

We have no SCF/CCF type commands, if we want to set a flag, we do it with ANDI or ORI...
For example, let's set the X flag (bit 4):
    ORI #%00010000,CCR
Now, let's clear the X flag:
    ANDI #%11101111,CCR

Addresses, Numbers and Hex... 68000 notification
We'll be using VASM for our assembler, and MOT syntax, but most other 68000 assemblers use the same formats... however coming from Z80, they can be a little confusing, so lets make it clear which is which!
Prefix Example Z80 equivalent Meaning
# #16384 16384 Decimal Number
#% #%00001111 %00001111 Binary Number
#$ #$4000 &4000 Hexadecimal number
#' #'a' 'a' Ascii 'a'
12345 (16384) decimal memory address
$ $4000 (&4000) Hexadecimal memory address

A I Q and X!
Some Registers can support special versions, for example ADD... there are special versions for different cases!
Letter Command Meaning Why you need it
Q ADDQ #1,d0 Add a small value to a register (Quick) Faster
A ADDA addr,A0 Add a value to an address register Automatic
I ADDI #999,d0 Add an immediate value Automatic
X ADDX D0,D1 Add with carry (extend) Limited use

if we use Q we can speed up our code, and X will be needed to use the carry (though with 32 bit registers we probably won't need it!)
Technically we should use A and I when using Address registers, or immediate values - but actually our assembler will do it for us!

Commands and data sizes
Many (but not all) of the 68000 commands can work on different data sizes, and working at smaller sizes will save us processing time... With commands like Move we have up to 3 different options

Command Bits Meaning
Move.B 8 Move Byte
Move.W 16 Move Word
Move.L 32 Move Longword

We don't have a 24 bit option! which is strange considering our addresses are 24 bits, but that's the way it is... we use L for 32 bits with addresses...
in fact commands where the destination is an address register always affect all 32 bits of the destination - and the source register is extended if it's smaller.

When using B or W with data registers, the other 24 or 16 bits of the will be left untouched... THEY PROBABLY ARE NOT ZERO - so beware if you use more bits later!

There are also some special commanbds like INC and DEC called ADDQ and DECQ... these can add or subtract a value upto (and including) 8... and do so as a 'single command' with no parameter when compiled to bytes

The 68000 has much more flexibility than the 6502 - though some of it's commands are the same!...
When we're doing comparisons, the commands we use depend on whether our data should be treated as signed or unsigned.

CMP D0,D1 Signed Unsigned
D1 <= D0 BLE BLS
D1 <> D0 BNE BNE
D1 >= D0 BGE BCC

Addressing modes, and their format in the source code
tThe 68000 has multiple addressing modes, to make things a bit strange, modern notification has changed compared to the older one, lets take a look!
Mode New Example Old Notes
Direct Register Dn D0 Dn Use Value in register
Address register Indirect   (An) (A0) (An) Use value in address pointed to by register
Address register with Postincrement (An)+ (A0)+ (An)+ Use value in address pointed to by register, Then increase An... this can be used like a POP command!
Address register with Predecrement -(An) -(A0) -(An) Decrease An, then read the value pointed to by the register... this can be used like a PUSH command!
Address register indirect, with displacement (d,An) (5,A0) d(An) Read a value from the address in A0, shifted by fixed value d
Address register indirect with indexing and displacement (d,An,Xi) (5,A0,D0) d(A0,D0) Read a value from the address in A0, shifted by  d+Dn (d can be 0 if you only want to shift by Xi)
Program counter relative addressing with a 16-bit offset (d,PC) (5,PC) d(PC) Read from the current address plus a fixed value
Program counter relative addressing with an 8-bit offset plus an index
(d,PC,Xi) (5,PC,D0) d(PC,Xi) Read from the current address plus a fixed value plus a register (d can be zero if you only want to add Xi)
Stack Pointers
technically, the 68000 has no Stack Pointer... and no Push Pop commands... but don't panic! A7 works as the stack pointer... and in fact we can use ANY address register as a stack pointer... and we can use the Postincrement, and Predecrement commands to do our stack commands... even better we can back up and restore MANY or ALL registers in one go!... lets take a look!

Action 68000 command
PUSH D0 move.l a6,-(sp)
POP DO move.l (sp)+,d0
PUSH EVERYTHING   moveM.L d0-d7/a0-a7,-(sp)
PULL EVERYTHING moveM.L (sp)+,d0-d7/a0-a7

SP and A7 are the SAME REGISTER.... when you specify SP the assembler will treat it as A7.

GCC wants to make our life hard with special formatting!
If you're working with code online, it may be in GCC forrmat, which is really annoying, here's the MOT equivalent.
move.l %d1,-(%sp) MOVE.L D1,-(sp) All registers are preceeded by %
move.l %a1, %sp@- MOVE.L A1,-(SP) @ symbol denotes memory address like brackets in MOT
move.l %a0@(0x34), %d0 MOVE.L (#$34,A0),D0 displacement specified in brackets
clr.l    %a5@+ CLR.L (A5)+ Postincrement specified after address

Code alignment
Because the 68000 is a 16 bit processor, commands need to be 16 bit aligned... you won't need to worry about this if you're only using command code, but if you define byte data (like strings or bytes) you need to ensure the next commands are word aligned, do this by putting an even command in before your next code line.

Effective Addresses
Unlike the 8 bit systems, on our computer systems like the (AtariST and Amiga), we can't be sure where our code will actually end up running
lets take an example where  we have a label (eg mystring):
    mystring: dc.b "Hello World"

Rather than loading it's address with MOVE.L mystring,A0... we should use LEA mystring (load effective address) or PEA mystring (push effective address... to stack) to get the address...
On some systems, our MOVE.L mystring,A0 command may actually work - due to address relocation (where the OS will alter the addresses in the code!)... but on others it will not, and you'll be left wondering why your code is malfunctioning! therefore, you should always use PEA and LEA to be sure!

The need for SPEED!
Compared to 8 bit processor's, the 68000 is so fast you may not need to worry about speed, but there are some things worth knowing,

Firstly, you'll notice there are no INC or DEC commands, but we don't need to keep using the slow MOVE commands...
This job is done by the 'QUICK commands'... why are they Quick?... because the 'parameter' is included in the command... so the whole line compiles to a single word... just like INC and DEC on the 8 bits!
We also have a CLEAR command... not only can it wipe registers... it can wipe memory addresses!
Action 68000 command RANGE
INC / DEC ADDQ.L #-1 , d0 -8 to +8
LOAD MOVEQ.L # , d0 -128 to 127
CLEAR (set to 0) CLR.L ($1000) does not work on address registers

Learn Assembly for the Greatest Classic Processors:  Z80 - 6502 - 68000
Visit to get my games and their source code! | Support me on patreon