return to Choose a Color Scheme: Dark
Print Mode

Learn Multi platform 6502 Assembly Programming... For Monsters!

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

Welcome To the Dark Side!... I grew up with the Amstrad CPC, and I started learning Assembly with the Z80, however as my experience with Z80 assembly grew, I wanted to start learning about other architctures, and see how they compared!

The 6502, and it's varients powered many of the biggest systems from the 80's and 90'... From the ubiquitous C64... to the Nintendo Entertainment System, as well as the BBC Micro, PC-Engine and Atari Lynx... even the Super Nintendo used a 16 bit varient of the 6502 known as the 65816

The 6502's origins are somewhat odd, a cost reduced version of the 8-bit '6800'  (which was the predecessor to the venerable16-bit 68000)... the 6502 sacrificed some functions for a cheaper unit price, which allowed it such wide support... the 6510 which powered the C64 had a few added features...
A later version, the 65C02 added more commands (Used in systems like the Apple IIc and the Atari Lynx) ... and HudsonSoft made a custom version of the 65C02 with even more features, called the HuC6280 and exclusively used in the PC Engine

All these CPU variants are 8 bit, and the basic 6502 command set works in the same way on all these sysems, and it's that instruction set we'll be learning in these tutorials...

These tutorials will be written from the perspective of a Z80 programmer learning 6502, but they will not assume any prior knowledge of Z80, so if you're starting out in assembly, these tutorials will also be fine for you!

the 6502

The 65C02 die

If you want to learn 6502 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:
Apple IIe
Atari 800 and 5200
Atari Lynx
Commodore 64
Super Nintendo (SNES)
Nintendo Entertainment System / Famicom
PC Engine
Vic 20

Recommended PDF resources:
6502 CPU Manual
6502 Getting started
6502 Tricks

What is the 6502 and what are 8 '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 6502 is an 8-Bit processor with a 16 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)

And what is 65535? well that's 64 kilobytes ... in computers Kilo is 1024, because four bytes is 1024 bytes
64 kilobytes is the amount of memory a basic 8-bit system can access

6502 is 8 bit so it's best at numbers less than 256... it can do numbers up to 65535 too more slowly... and really big numbers will be much harder to do! - we can design our game round small numbers so these limits aren't a problem.

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!!!
6502 code is small, fast, and super efficient - with ASM you can do things in 1k that will amaze you!

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.

In this guide, Binary will shown with a % symbol... eg %11001100 ... hexadecimal will be shown with & eg.. &FF.

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

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 6502 Registers
Compared to the Z80, the 6502 has a more limited register set...  

The Z80 has Accumulator, 3 pairs of 8 bit regsiters (BC,DE,HL), usable for 16 bit maths and 2 16-bit  indirect registers (IX,IY), it also has a 16 bit Stack pointer, and there are 'Shadow Regsiters' for special purposes

The 6502 is very different, it has an 8 bit Accumulator, two 8 bit  indirect registers (X,Y)  and an 8 bit stack pointer... it also has a 16 bit Program Counter... it has no Shadow Registers

8 Bit 16 Bit Use cases
Accumulator A
Flags F
Indirect X X Preindex register , stack pointer manipulation
Indirect Y Y Postindex register
Stack Pointer SP
Program Counter PC

    Flags: NV-BDIZC
Name Meaning
N Negative 1=Negative
V Overflow 1=True
- unused
B BRK command
D Decimal mode 1=True
I IRQ disable 1=Disable
Z Zero 1=Result Zero
C Carry 1=Carry

At a glance this may make the 6502 seem significantly inferior to the Z80, however the 6502 has some tricks up it's sleeve!... Where as the fastest command on the Z80 takes 4 ticks, on the 6502 it takes only 1... and the 6502 makes up for it's lack of registers with superior addressing modes!

Special Memory addresses on the 6502
Compared to the Z80, two things are apparent about the 6502... firstly the stack pointer is only 8 bit... and secondly we have very few registers!

The way the Stack pointer works is simple... the stack is always positioned beween $0100 and $01FF...   Where xx is the SP register, the stack pointer will point to $01xx

The 'solution' to the lack of registers is special addressing options... the first 256 bytes between &0000 and &00FF are called the 'Zero Page', and the 6502 has many special functions which allow data in this memory range to be quickly used with the accumulator and other functions as if they were 'registers'!
From To Meaning
$0000 $00FF Zero Page
$0100 $01FF Stack Pointer
$0200 $FFFF Normal memory (and mapped registers)

The 6502 Addressing Modes
The 6502 has 11 different addrssing modes... many have no comparable equivalent on the Z80
Mode Description Sample Command Z80 Equivalent effective result
Implied / Inherant A command that needs no paprameters SEC SEC  (set carry) SCF
Relative A command which uses the program counter PC with and offset nn (-128 to +127) BEQ #$nn BEQ [label] (branch if equal) JR Z,[label]
Accumulator A command which uses the Accumulator as the parameter ROL ROL (ROtate bits Left) RLCA
Immediate A command which takes a byte nn as a parameter ADC #$nn ADC #1 ADC 1 &nn
Absolute Take a parameter from a two byte memory address $nnnn LDA $nnnn LDA $2000 LD a,(&2000) (&nnnn)
Absolute Indexed Take a parameter from a two byte memory address $nnnn+X (or Y) LDA $nnnn,X LDA $2000,X (&nnnn+X)
Zero Page Take a parameter from the zero page address $00nn ADC $nn ADC $32 (&00nn)
Zero Page Indexed Takes a parameter from memory address $00nn+X ADC $nn,X ADC $32,X (&00nn+X)
Indirect Take a parameter from pointer at address $nnnn...
if $nnnn contains $1234 the parameter would come from the address at $1234
JMP ($1000) LD HL,(&1000)
Pre Indexed (Indirect,X) Take a paramenter from pointer at address $nnnn+X
if $nnnn contains $1234, and X contained 4  the parameter would come from the address at $1238
ADC ($nn,X) ADC ($32,X) ((&00nn+X))
Postindexed  (Indirect),Y Take pointer from address $nnnn, add Y... get the parameter from the result
if $nnnn contains $1234, and Y contained 4, the address would be read from $1234... then 4 would be added... and the parameter would be read from ther resulting address
ADC ($nn),Y ADC ($32),Y ((&00nn)+Y)

Addresses, Numbers and Hex... 6502 notification
We'll be using VASM for our assembler, but most other 6502 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
12345 (16384) decimal memory address
$ $4000 (&4000) Hexadecimal memory address

Low and High Byte
Because the 6502 has no 16 bit registers, it's often nesassary to split an address into its High and Low byte parts, by prefixing a label with < or > it's low or high bytes will be extracted and used in the compiled code, lets take a look!
Symbol Meaning Example Result
< Low Byte <#$1234 #$34
> High Byte >#$1234 #$12

BIT command
When any BIT command is applied, some of the other bits are stored in flags...
Bit 7 will be stored in the Negative flag (test with BPL / BMI)
Bit 6 will be stored in the Overflow flag (test with BVS / BVC)

Important commands that don't exist!
The 6502 lacks some surprisingly common commands that the 6800 had, but we can 'fake' them with the commands we do have!
6800 command Meaning 6502 alternative
ADD #5 ADD a number without carry CLC (Clear carry for add)
ADC #5 (Clear carry)
SUB #5 Subtract a number without carry SEC (Clear carry for sub)
SBC #5 (Clear carry)
NEG convert positive value in Accumulator  to negative value in Accumulator EOR #255 (XOR/Flip bits)
SEC (Clear carry)
ADD #1  (add 1)
SWAP A Swap two Nibbles in A ASL (shift left - bottom bit zero)
ADC #$80 (pop top bit off)
ROL (shift carry in)
ASL (shift left - bottom bit zero)
ADC #$80 (pop top bit off)
ROL (shift carry in)
If you're used to the Z80, don't go looking for INC A or DEC A on the 6502 ... they don't exist either, so you'll have to CLC, ADD #1 instead!... however they DO exist on the 65C02 and HU6280 as DEA and INA

Pretending we have 16 bit!
We can use Zero page pointers to fake the Z80's 16 bit operations!
INC (inc de) DEC (dec de) ADD (add bc to hl) SUB
        INC z_E
        BNE    IncDE_Done
        INC    z_D
        INC z_E
        BNE    IncDE_Done
        INC    z_D
        lda z_c
        adc z_l
        sta z_l
        lda z_b
        adc z_h
        sta z_h
        lda z_l
        sbc z_c
        sta z_l
        lda z_h
        sbc z_b
        sta z_h
Fast 16 bit loop
    lda (z_hl),y
    sta $2119
    sta $2118 
    lda #0   
    jsr blankblock
    bne fontchar_loop
    inc z_hl+1
    bne fontchar_loop

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