WDC 65C816 CPU Reference

Overview

The 65C816 is a 16-bit extension of the 6502 processor, designed by Western Design Center. It maintains backward compatibility with 6502 code while adding 16-bit operations, a 24-bit address space, and new addressing modes. This implementation provides a reusable, generic 65C816 CPU core that can be used by any system (SNES, Apple IIgs, etc.) by implementing the Memory65c816 trait.

Implementation: crates/core/src/cpu_65c816.rs

Architecture

Registers

The 65C816 has several operating modes that affect register sizes:

In Native Mode (16-bit)

  • A (Accumulator): 16-bit (or 8-bit when M flag is set)
  • X (Index Register X): 16-bit (or 8-bit when X flag is set)
  • Y (Index Register Y): 16-bit (or 8-bit when X flag is set)
  • SP (Stack Pointer): 16-bit
  • PC (Program Counter): 16-bit
  • PBR (Program Bank Register): 8-bit (forms 24-bit address with PC)
  • DBR (Data Bank Register): 8-bit (default bank for data access)
  • D (Direct Page Register): 16-bit (base for direct page addressing)
  • Status: 8-bit processor status register

Status Register Flags

NVMXDIZC (Native Mode)
││││││││
││││││││└─ Carry flag
│││││││└── Zero flag
││││││└─── IRQ Disable flag
│││││└──── Decimal mode flag
││││└───── Index register size (0=16-bit, 1=8-bit)
│││└────── Memory/Accumulator size (0=16-bit, 1=8-bit)
││└─────── Overflow flag
│└──────── Negative flag

E flag (separate): Emulation mode (0=Native, 1=6502 Emulation)

Memory Map

The 65C816 has a 24-bit address space (16MB):

  • $00:0000-$00:00FF: Direct Page (relocatable via D register)
  • $00:0100-$00:01FF: Stack in emulation mode
  • $00:0000-$00:FFFF: Bank 0
  • $01:0000-$FF:FFFF: Banks 1-255

Interrupt Vectors

Native Mode Vectors

  • $00:FFE4: COP (Co-processor)
  • $00:FFE6: BRK
  • $00:FFE8: ABORT
  • $00:FFEA: NMI
  • $00:FFEE: IRQ

Emulation Mode Vectors (6502 compatibility)

  • $00:FFFA-$00:FFFB: NMI
  • $00:FFFC-$00:FFFD: RESET
  • $00:FFFE-$00:FFFF: IRQ/BRK

Operating Modes

Emulation Mode (E=1)

  • Acts like a 6502 with some enhancements
  • 8-bit accumulator and index registers
  • 8-bit stack pointer (in page 1)
  • Compatible with 6502 code

Native Mode (E=0)

  • Full 16-bit capabilities
  • Configurable register sizes via M and X flags
  • 16-bit stack pointer
  • 24-bit addressing

Usage

Systems using the 65C816 must implement the Memory65c816 trait:

pub trait Memory65c816 {
    fn read(&self, bank: u8, addr: u16) -> u8;
    fn write(&mut self, bank: u8, addr: u16, val: u8);
}

Example

use emu_core::cpu_65c816::{Cpu65c816, Memory65c816};

struct MySystem {
    ram: [u8; 0x1000000], // 16MB
}

impl Memory65c816 for MySystem {
    fn read(&self, bank: u8, addr: u16) -> u8 {
        let full_addr = ((bank as usize) << 16) | (addr as usize);
        self.ram[full_addr]
    }
    
    fn write(&mut self, bank: u8, addr: u16, val: u8) {
        let full_addr = ((bank as usize) << 16) | (addr as usize);
        self.ram[full_addr] = val;
    }
}

let system = MySystem { ram: [0; 0x1000000] };
let mut cpu = Cpu65c816::new(system);
cpu.reset();

Instruction Set

The 65C816 includes all 6502 instructions plus:

New 16-bit Instructions

  • 16-bit versions of most 6502 arithmetic and logical operations
  • Conditional execution based on M and X flags

Block Move Instructions

  • MVN: Move Negative (block copy, decrementing)
  • MVP: Move Positive (block copy, incrementing)

Stack Operations

  • PHB: Push Data Bank Register
  • PLB: Pull Data Bank Register
  • PHD: Push Direct Page Register
  • PLD: Pull Direct Page Register
  • PHK: Push Program Bank Register
  • PHX: Push X Register
  • PHY: Push Y Register
  • PLX: Pull X Register
  • PLY: Pull Y Register

Transfer Instructions

  • TCD: Transfer 16-bit A to Direct Page Register
  • TDC: Transfer Direct Page Register to 16-bit A
  • TCS: Transfer 16-bit A to Stack Pointer
  • TSC: Transfer Stack Pointer to 16-bit A
  • TXY: Transfer X to Y
  • TYX: Transfer Y to X

Status/Mode Instructions

  • XCE: Exchange Carry and Emulation flags (mode switch)
  • REP: Reset Processor Status Bits
  • SEP: Set Processor Status Bits

New Addressing Modes

  • Stack Relative: (SP),Y
  • Direct Page Indirect Long: [D]
  • Direct Page Indirect Long Indexed: [D],Y
  • Absolute Long: 24-bit absolute addressing
  • Absolute Long Indexed: 24-bit absolute,X

Other New Instructions

  • COP: Co-processor
  • WDM: Reserved for future expansion
  • PEA: Push Effective Absolute Address
  • PEI: Push Effective Indirect Address
  • PER: Push Effective PC Relative Address
  • WAI: Wait for Interrupt
  • STP: Stop the processor

Addressing Modes

The 65C816 supports all 6502 addressing modes plus:

  1. Stack Relative: LDA $03,S
  2. Stack Relative Indirect Indexed: LDA ($03,S),Y
  3. Direct Page Indirect Long: LDA [$42]
  4. Direct Page Indirect Long Indexed: LDA [$42],Y
  5. Absolute Long: LDA $123456 (24-bit)
  6. Absolute Long Indexed: LDA $123456,X (24-bit)
  7. Program Counter Relative Long: BRL label (16-bit offset)

Systems Using 65C816

This CPU core is used by the following systems in Hemulator:

  • SNES (Super Nintendo Entertainment System) - crates/systems/snes/

Implementation Notes

Mode Switching

Switching between emulation and native mode is done via the XCE instruction:

CLC      ; Clear carry
XCE      ; Switch to native mode (E=0)

SEC      ; Set carry
XCE      ; Switch to emulation mode (E=1)

Register Size Management

In native mode, the M and X flags control register sizes:

  • M=0: 16-bit accumulator/memory operations
  • M=1: 8-bit accumulator/memory operations
  • X=0: 16-bit index registers
  • X=1: 8-bit index registers

Use REP and SEP to change these flags:

REP #$30  ; Clear M and X flags (16-bit mode)
SEP #$30  ; Set M and X flags (8-bit mode)

Bank Wrapping

Address calculations wrap within banks:

  • Incrementing $00:FFFF goes to $00:0000, not $01:0000
  • The bank register (PBR or DBR) determines which bank

Direct Page

The Direct Page register allows relocating zero page anywhere in bank 0:

LDA #$2000
TCD        ; Move direct page to $2000
LDA $42    ; Accesses $00:2042, not $00:0042

Performance Considerations

Variable Timing

Instruction timing depends on:

  • Register size (8-bit vs 16-bit operations)
  • Direct page alignment (D register low byte = $00 saves 1 cycle)
  • Memory access patterns
  • Emulation vs native mode

References