SNES Development
Learning 65816 Assembly

I trust that you have learned some other kind of assembly language. If you have not, I’m afraid that learning 65816 as your first assembly language may be extremely hard. However, I won’t stop you from trying. You will need other 65816 documents, because I do not teach much in this tutorial. These tutorials are for teaching you how to use 65816 with the SNES, so I’m trying to write the main material first. Maybe later I’ll really expand this part and make it full. But really, the 65816 documents I linked you to earlier teach everything, so it would be really redundant for me to write a 65816 tutorial. However, if I receive feedback asking for one, I will make an effort. Ok, let’s begin.

Modes of Operation

  • The 65816 can run in 2 modes of operation, Native 65816 mode and 6502 Emulation mode.
  • The 65816 boots up in 6502 Emulation mode. (I found that odd, but whatever)
  • We’ll be programming in Native mode, otherwise it would be like coding for NES (somewhat).



  • This register may be either 16 or 8 bits (depending on bit 5 of the status register. more on this later).
  • This register is used for most general-purposes.
  • When in 8-bit mode, the low byte is accessible, and the high byte is not. However, you can exchange both the high and low byte.
  • When in 16-bit mode, all 2 bytes are accessible and the accumulator is designated as C.

X,Y Index Registers

  • The X and Y index registers can also be designated as 8 or 16 bits in size.
  • I usually see these kept set as 16 bits in size.
  • You will come to see their usefulness. One example is using them as counters.

Direct Page Register (D), Stack Pointer (S), Program Bank Register (PBR), Data Bank Register (DBR)

  • Please see section 3.13-3.16 of the 65816 Primer for information on these registers. I would only be quoting, and I don’t feel like it, so just read those sections. I will at least provide some notes.
  • The stack grows downwards.
  • I usually see the program’s init routine set the stack to $1FFF
  • I usually see a program’s init routine set the DBR = PBR
  • I also see the direct page set to $0000
  • So far, I have not really had to do any heavy messing around with these registers, so if your a little confused by the descriptions have no fear.

Processor Status Register (P)

  • See Section 3.01 of the 65816 Primer for a GREAT graphic description of the processor status register.

Number Format

It’s the same as NES if you have coded NES before. In previous ASM languages you may have written in, you may have used $10 or 10h to say that the number is in hex, and [$10] to say that it’s a hex address. Well it’s a little different for the SNES. To use an immediate value (not an address, just a number), you place a # before the number. #16 = 16 decimal number. To use an immediate hex value, use #$. #$10 = 10 hex, or 16 decimal. To use an immediate binary value, use #%. #%00000011 = 3 decimal. To say the number is an address, use $. $2000 for example… We basically took out the #, which meant it was an immediate value. See? That’s that!

Changing bits in the P Register

To alter bits in the Processor Status Register, simply use the opcodes SEP #xx or REP #xx. SEP sets bits (makes the bits 1) and REP resets bits (makes the bits 0). Assuming you read section 3.01 of the 65816 Primer, let’s go and make the accumulator 8 bits and the XY registers 16 bits in size.

rep #$10
sep #$20

I could also write (for visual simplicity)
rep #%00010000
sep #%00100000

nvMXdizc - these are simply helpful abbreviations of bits 0-7 of the Processor Status Register. See how we REP’d and SEP’d the P register to set A to 8 bits, and X/Y to 16 bits? Resetting the M or X bits (of nvmxdizc) makes the register(s) 16 bits, setting the bit makes it 8 bits.

Basic Opcodes

This is another crappy listing, simply because the docs already out there are already enough to get you going. Nonetheless, here is a brief and crappy intro. There are many ways to use most opcodes, so I will only list a couple basic methods. I encourage you to look in Appendix A of the 65816 Primer for more addressing modes of each opcode. Appendix A tells you everything for almost every opcode, so be sure to check it out.

Notes: I’ve taken the syntax conventions from 65816 Primer for simplicity:

addr          2 byte address
const         1 or 2 byte constant (immediate number)
label         label of code in same 64K bank as instruction
nearlabel     label of code close enough to instruction to be reachable by a one-byte signed offset (-127/+127).
long          3-byte address (includes bank byte)

LDA : Load Accumulator with memory Usage:

LDA #const
LDA addr
LDA long
LDA addr,X
...- You will be using this a LOT to load values and values at addresses.

LDX, LDY - Same as above, but for X/Y.

Also, addressing modes are more limited when using X/Y. See the primer.

STA : Store A into memory Usage:

STA addr
STA long
STA addr,X
STA addr,Y
...- You will be using this a LOT to store values in A into addresses.

STY, STX - Same as Above, but for X/Y.

Also, addressing modes are more limited when using X/Y. See the primer.

STZ : Store zero byte to memory Usage:

STZ addr
STZ addr,X
- A good replacement for:
lda #$00
sta $xxxx

CLC : Clear Carry Flag

clc before adc

SEC : Set Carry Flag

sec before sbc>

ADC : Add with carry Usage:

ADC #const
ADC addr
ADC long
ADC addr,X
...- You should CLC before ADC'ing. 
- Carry flag set if overflow.

SBC : Subtract with carry Usage:

SBC #const
SBC addr
SBC long
SBC addr,X
...- You should SEC before SBC-ing.
- Set if unsigned borrow not required.

Well that is enough for now don’t you agree?? Again please look at the primer and other CPU docs for some real opcode info.

On to Writing the Header!

Tutorial by bazz