MOS 6502 CPU Reference
Overview
The MOS 6502 is an 8-bit microprocessor that was widely used in home computers and gaming consoles during the 1970s and 1980s. This implementation provides a reusable, generic 6502 CPU core that can be used by any system (NES, Atari 2600, Apple II, etc.) by implementing the Memory6502 trait.
Implementation: crates/core/src/cpu_6502.rs
Architecture
Registers
- A (Accumulator): 8-bit general-purpose register for arithmetic and logic operations
- X (Index Register X): 8-bit index register for indexed addressing modes
- Y (Index Register Y): 8-bit index register for indexed addressing modes
- SP (Stack Pointer): 8-bit pointer to stack (points to 0x0100 + SP)
- PC (Program Counter): 16-bit pointer to current instruction
- Status: 8-bit processor status register (flags: NV-BDIZC)
Status Register Flags
NV-BDIZC
││ ││││└─ Carry flag
││ │││└── Zero flag
││ ││└─── IRQ Disable flag
││ │└──── Decimal mode flag (not used on NES)
││ └───── Break flag
│└────── (unused, always 1)
└─────── Overflow flag
N────── Negative flag
Memory Map
- $0000-$00FF: Zero Page (fast access)
- $0100-$01FF: Stack (grows downward from $01FF)
- $0200-$FFFF: General memory (system-specific)
Interrupt Vectors
- $FFFA-$FFFB: NMI vector
- $FFFC-$FFFD: RESET vector
- $FFFE-$FFFF: IRQ/BRK vector
Usage
Systems using the 6502 must implement the Memory6502 trait:
pub trait Memory6502 {
fn read(&self, addr: u16) -> u8;
fn write(&mut self, addr: u16, val: u8);
}
Example
use emu_core::cpu_6502::{Cpu6502, Memory6502};
// Implement Memory6502 for your system
struct MySystem {
ram: [u8; 65536],
}
impl Memory6502 for MySystem {
fn read(&self, addr: u16) -> u8 {
self.ram[addr as usize]
}
fn write(&mut self, addr: u16, val: u8) {
self.ram[addr as usize] = val;
}
}
// Create and use CPU
let system = MySystem { ram: [0; 65536] };
let mut cpu = Cpu6502::new(system);
cpu.reset();
cpu.step();
Instruction Set
The 6502 has 56 instructions organized into the following categories:
Data Transfer
- LDA: Load Accumulator
- LDX: Load X Register
- LDY: Load Y Register
- STA: Store Accumulator
- STX: Store X Register
- STY: Store Y Register
- TAX: Transfer A to X
- TAY: Transfer A to Y
- TXA: Transfer X to A
- TYA: Transfer Y to A
- TSX: Transfer SP to X
- TXS: Transfer X to SP
Arithmetic
- ADC: Add with Carry
- SBC: Subtract with Carry
- INC: Increment Memory
- INX: Increment X
- INY: Increment Y
- DEC: Decrement Memory
- DEX: Decrement X
- DEY: Decrement Y
Logical
- AND: Logical AND
- ORA: Logical OR
- EOR: Exclusive OR
- BIT: Bit Test
Shift/Rotate
- ASL: Arithmetic Shift Left
- LSR: Logical Shift Right
- ROL: Rotate Left
- ROR: Rotate Right
Comparison
- CMP: Compare Accumulator
- CPX: Compare X Register
- CPY: Compare Y Register
Branches
- BCC: Branch if Carry Clear
- BCS: Branch if Carry Set
- BEQ: Branch if Equal (Zero Set)
- BNE: Branch if Not Equal (Zero Clear)
- BMI: Branch if Minus (Negative Set)
- BPL: Branch if Plus (Negative Clear)
- BVC: Branch if Overflow Clear
- BVS: Branch if Overflow Set
Jumps/Subroutines
- JMP: Jump
- JSR: Jump to Subroutine
- RTS: Return from Subroutine
Stack
- PHA: Push Accumulator
- PHP: Push Processor Status
- PLA: Pull Accumulator
- PLP: Pull Processor Status
Status Flags
- CLC: Clear Carry
- CLD: Clear Decimal
- CLI: Clear IRQ Disable
- CLV: Clear Overflow
- SEC: Set Carry
- SED: Set Decimal
- SEI: Set IRQ Disable
Interrupts
- BRK: Force Break
- RTI: Return from Interrupt
Other
- NOP: No Operation
Addressing Modes
The 6502 supports 13 addressing modes:
- Implied: Instruction operates on register (e.g.,
TAX) - Accumulator: Operates on accumulator (e.g.,
ASL A) - Immediate: Operand is next byte (e.g.,
LDA #$42) - Zero Page: Address in zero page (e.g.,
LDA $42) - Zero Page,X: Zero page indexed by X (e.g.,
LDA $42,X) - Zero Page,Y: Zero page indexed by Y (e.g.,
LDX $42,Y) - Absolute: 16-bit address (e.g.,
LDA $4200) - Absolute,X: Absolute indexed by X (e.g.,
LDA $4200,X) - Absolute,Y: Absolute indexed by Y (e.g.,
LDA $4200,Y) - Indirect: Only for JMP (e.g.,
JMP ($4200)) - Indexed Indirect: (Zero Page,X) (e.g.,
LDA ($42,X)) - Indirect Indexed: (Zero Page),Y (e.g.,
LDA ($42),Y) - Relative: For branch instructions (signed 8-bit offset)
Systems Using 6502
This CPU core is used by the following systems in Hemulator:
- NES (Nintendo Entertainment System) -
crates/systems/nes/ - Atari 2600 (6507 variant) -
crates/systems/atari2600/
Implementation Notes
Cycle Accuracy
The implementation tracks cycle counts for all instructions. Some instructions take additional cycles based on:
- Page boundary crossings (certain addressing modes)
- Branch taken vs. not taken
- Specific timing requirements
Undocumented Instructions
The current implementation focuses on official/documented 6502 instructions. Undocumented opcodes may be added as needed for specific system compatibility.
Decimal Mode
The Decimal (D) flag is implemented but may not be used by all systems. For example, the NES's 6502 variant (RP2A03) has the decimal mode circuitry disabled.
References
- 6502.org - Official 6502 documentation and resources
- Visual 6502 - Transistor-level 6502 simulation
- 6502 Instruction Reference - Complete opcode listing