Appendix E: X65 Quick Reference Cheat Sheet¶
65816 CPU Registers¶
Register |
Size |
Description |
Notes |
|---|---|---|---|
A (,B=C) |
8/16-bit |
Accumulator |
Width controlled by m flag (0=16-bit) |
X |
8/16-bit |
X Index Register |
Width controlled by x flag (0=16-bit) |
Y |
8/16-bit |
Y Index Register |
Width controlled by x flag (0=16-bit) |
S |
16-bit |
Stack Pointer |
Points to next free location on stack |
D |
16-bit |
Direct Page Register |
Base address for direct page addressing |
PBR |
8-bit |
Program Bank Register |
Current code bank (upper 8 bits of PC) |
DBR |
8-bit |
Data Bank Register |
Default data bank for absolute addressing |
P |
8-bit |
Processor Status |
Contains status flags |
Status Register (P) Flags¶
Bit |
Flag |
Description |
When Set (1) |
When Clear (0) |
|---|---|---|---|---|
7 |
N |
Negative |
Result is negative |
Result is positive |
6 |
V |
Overflow |
Overflow occurred |
No overflow |
5 |
M |
Memory/Accumulator Select |
8-bit accumulator |
16-bit accumulator |
4 |
X |
Index Register Select |
8-bit index registers |
16-bit index registers |
3 |
D |
Decimal Mode |
BCD arithmetic mode |
Binary arithmetic mode |
2 |
I |
IRQ Disable |
IRQ interrupts disabled |
IRQ interrupts enabled |
1 |
Z |
Zero |
Result is zero |
Result is non-zero |
0 |
C |
Carry |
Carry occurred |
No carry occurred |
Common 65816 Instructions¶
Register Instructions¶
REP #$30 ; Reset bits in P (set 16-bit A,X,Y mode)
SEP #$30 ; Set bits in P (set 8-bit A,X,Y mode)
XBA ; Exchange B and A (swap high/low bytes)
TCS ; Transfer C (16-bit A) to S
TCD ; Transfer C (16-bit A) to D
PHB/PLB ; Push/Pull Data Bank Register
PHD/PLD ; Push/Pull Direct Page Register
PHK ; Push Program Bank Register
Mode Switching¶
CLC ; Clear carry
XCE ; Exchange Carry with Emulation flag (enter native mode)
SEC ; Set carry
XCE ; Exchange Carry with Emulation flag (enter emulation mode)
Bank Register Management¶
LDA #$01 ; Load bank number
PHA ; Push to stack
PLB ; Pull to Data Bank Register
Memory Block Operations¶
MVN src,dst ; Move memory block (ascending)
MVP src,dst ; Move memory block (descending)
16-bit Operations¶
REP #$20 ; 16-bit accumulator mode
LDA #$1234 ; Load 16-bit immediate value
ADC #$4321 ; 16-bit addition
SBC #$0001 ; 16-bit subtraction
Long Addressing¶
JML $123456 ; Jump Long (sets Program Bank Register)
JSL $123456 ; Jump to Subroutine Long
RTL ; Return from Subroutine Long
LDA $123456 ; Load from long address
STA $123456 ; Store to long address
Common Assembly Code Snippets¶
System Initialization¶
CLC ; Clear carry flag
XCE ; Switch to native mode
REP #$30 ; 16-bit A,X,Y
LDA #$0000 ; Set direct page to 0
TCD
LDX #$01FF ; Set up stack at $01FF
TXS
SEP #$20 ; 8-bit A for I/O operations
Playing a Note on the SGU-1¶
The SGU-1 register window lives at $FEC0–$FEFF. The bottom of the window holds the currently-selected channel’s per-channel registers (frequency, volume, gate, etc.); a channel-select register picks which of the nine channels is mapped in. See Chapter 12 for the channel-select sequence.
; With channel 0 selected in the SGU window:
LDA #<7256 ; phase increment for ~A4 (≈440 Hz at 48 kHz)
STA $FEC0 ; SGU CHN_FREQ_L
LDA #>7256
STA $FEC1 ; SGU CHN_FREQ_H
LDA #64 ; volume
STA $FEC2 ; SGU CHN_VOL
LDA #$01 ; FLAGS0 bit 0 = GATE (key-on)
STA $FEC4 ; SGU CHN_FLAGS0
CGIA Register Reference¶
The CGIA register window lives at $FF00–$FF7F. Frequently-touched registers:
Address |
Register |
Notes |
|---|---|---|
|
|
Bit 0 = HIRES (768 px horizontal), bit 1 = INTERLACE (480 vert). |
|
|
High 8 bits of address for background-plane fetches. |
|
|
High 8 bits of address for sprite fetches. |
|
|
Current raster line (read-only). |
|
|
Line at which to fire a raster interrupt. |
|
|
|
|
|
Same layout; write to acknowledge. |
|
|
High nibble = plane type (0 background, 1 sprite); low nibble = enable bits per plane. |
|
|
Plane Z-order permutation (one byte selects one of 24 SJT-encoded orderings). |
|
|
Border / fill color. |
|
|
16-bit per-plane pointers to the display list or sprite descriptor table. |
|
|
Four blocks of 16 plane registers each (interpretation depends on plane type and active mode). |
USB HID Quick Reference¶
The HID window at $FFB0–$FFBF is rebindable: writing to $FFB0 selects which device (and, for keyboards, which page or, for gamepads, which player) appears in $FFB1–$FFBF.
Polling a Gamepad¶
; Select merged gamepad (OR of all connected pads). Use $12/$22/$32/$42
; to select specific player 1..4 instead.
LDA #$02
STA $FFB0
LDA $FFB1 ; D-pad (bits 0..3) + features (bits 6..7)
LDA $FFB3 ; buttons 0..7
LDA $FFB4 ; buttons 8..15 (Home = bit 4)
LDA $FFB5 ; left-stick X (signed 8-bit)
LDA $FFB6 ; left-stick Y
LDA $FFB9 ; left trigger (unsigned 8-bit)
Polling a USB Keyboard¶
; Full keyboard state is 32 bytes, exposed in two 16-byte pages.
; Page 0 (high-nibble 0): bytes 0..15 of the state bitmap.
; Page 1 (high-nibble 1): bytes 16..31.
LDA #$00 ; keyboard, page 0
STA $FFB0
; "any key pressed?" — BIT on the "valid device" flag at $FFB0
BIT $FFB0 ; N/Z reflect bits 7/6 of the first byte
; "is key with HID keycode $K pressed?"
; Each state bit lives at bit (K & 7) of byte (K >> 3).
; Example: HID keycode $04 (letter A) → bit 4 of byte 0 of page 0.
LDA $FFB1 ; byte 0 of state
AND #%00010000 ; mask bit 4 (A key)
BNE a_key_down
RGB LED Quick Reference¶
The LED window at $FFA0–$FFA7 has two parallel interfaces: four direct RGB332 bytes for the four on-board LEDs, and a four-byte chain protocol for addressing any LED in a WS2812 chain.
Direct RGB332 (LEDs 0–3)¶
; RGB332 byte: [7:5]R, [4:2]G, [1:0]B
LDA #%11100000 ; pure red
STA $FFA0 ; LED 0 → red (single STA commits)
LDA #%00011100 ; pure green
STA $FFA1 ; LED 1 → green
Chain Protocol (any LED 0–255, 24-bit RGB)¶
The write to $FFA4 is the commit — latch the G, B, and index bytes first, then store the R byte last:
LDA #5 ; target LED index
STA $FFA7 ; latched
LDA #$80 ; green
STA $FFA5 ; latched
LDA #$00 ; blue
STA $FFA6 ; latched
LDA #$FF ; red → this write commits the chain update
STA $FFA4
System Buzzer Quick Reference¶
The buzzer window lives at $FFA8–$FFAB. Frequency is encoded logarithmically as FREQ_16bit / 65535 × 10 octaves starting from 20 Hz.
; ~1 kHz beep at 50% duty
LDA #$FF ; FREQ_L
STA $FFA8
LDA #$8C ; FREQ_H
STA $FFA9
LDA #$80 ; 50% duty
STA $FFAA
; Silence
LDA #0
STA $FFAA ; duty = 0 → no output
System Timers Quick Reference¶
Two CIA-compatible 16-bit timers at $FF98–$FF9F, each counting down in 1 µs ticks.
; Set Timer A to 1 ms (1000 µs), continuous, enable its interrupt
LDA #<1000
STA $FF98 ; TAL — latches low byte
LDA #>1000
STA $FF99 ; TAH — loads latched value into counter
LDA #%10000001 ; [7]=1 set bits, [0]=1 enable Timer A IRQ
STA $FF9D ; ICR
LDA #%00000001 ; [0]=1 start Timer A (continuous mode)
STA $FF9E ; CRA
; Read counter (µs remaining)
LDA $FF98 ; low byte
LDX $FF99 ; high byte
Reading ICR ($FF9D) clears the pending interrupt flags; latch it into a register before dispatching.