Back to showcase
6502

MOS 6502

Complete 8-bit processor with all 56 instructions, 13 addressing modes, BCD arithmetic, and a 26-state control unit FSM. The heart of the Apple II, Commodore 64, and NES.

8-bit 56 Instructions 151 Opcodes 13 Addr Modes BCD 26-State FSM

Processor Architecture

Control Unit

26-state FSM, instruction decoder

ALU

ADD, SUB, AND, OR, XOR, shift, BCD

Registers

A, X, Y, SP, PC, status

16-bit address bus
8-bit data bus
control lines (R/W, IRQ, NMI, RDY)

Specifications

ParameterValue
Data width8 bits
Address space64KB (16-bit)
RegistersA (accumulator), X, Y (index), SP, PC, P (status)
Instructions56 unique, 151 opcodes total
Addressing modes13 (immediate, zero page, absolute, indexed, indirect, etc.)
ALU operationsADC, SBC (with BCD), AND, ORA, EOR, ASL, LSR, ROL, ROR
InterruptsIRQ (maskable), NMI (non-maskable), RESET
Control FSM26 states with microcode-style control signals

RHDL Implementation

The control unit FSM decodes each instruction into a sequence of micro-operations, managing the fetch-decode-execute pipeline.

control_unit.rb
class ControlUnit < RHDL::Sim::SequentialComponent
  input  :clk, :rst
  input  :opcode,  width: 8
  input  :flags,   width: 8
  output :alu_op,  width: 4
  output :src_sel, width: 3
  output :dst_sel, width: 3
  output :mem_rw,  :addr_sel

  state_machine :phase,
    clock: :clk, reset: :rst,
    states: [
      :fetch_opcode,
      :fetch_low,
      :fetch_high,
      :execute,
      :write_back,
      # ... 21 more states
    ] do

    transition :fetch_opcode => :fetch_low
    transition :fetch_low => :fetch_high,
      when: needs_high_byte?(opcode)
    transition :fetch_low => :execute,
      unless: needs_high_byte?(opcode)
  end
end

The ALU handles all arithmetic and logic operations, including BCD mode for decimal arithmetic used in financial calculations.

alu_6502.rb
class ALU6502 < RHDL::Sim::Component
  input  :a,        width: 8
  input  :b,        width: 8
  input  :carry_in
  input  :bcd_mode
  input  :op,       width: 4
  output :result,   width: 8
  output :carry, :zero,
         :negative, :overflow

  behavior do
    raw = case_select(op, {
      0 => a + b + carry_in,  # ADC
      1 => a - b - ~carry_in, # SBC
      2 => a & b,            # AND
      3 => a | b,            # ORA
      4 => a ^ b,            # EOR
      5 => a << 1,           # ASL
      6 => a >> 1,           # LSR
    })

    result <= mux(bcd_mode,
      bcd_adjust(raw), raw)

    zero     <= result.eq?(0)
    negative <= result[7]
  end
end

The instruction decoder maps all 151 opcodes to their instruction type, addressing mode, and cycle count using a lookup table generated from the 6502 opcode matrix.

decoder.rb
class InstructionDecoder < RHDL::Sim::Component
  input  :opcode,    width: 8
  output :instr,     width: 6   # 56 instructions
  output :addr_mode, width: 4   # 13 addressing modes
  output :cycles,    width: 3   # base cycle count

  # Decode using opcode matrix pattern:
  # bits [1:0] = group (cc), [4:2] = operation (aaa), [7:5] = mode (bbb)
  behavior do
    cc  = opcode[1..0]
    aaa = opcode[4..2]
    bbb = opcode[7..5]

    instr     <= decode_instruction(cc, aaa)
    addr_mode <= decode_addr_mode(cc, bbb)
    cycles    <= base_cycles(addr_mode)
  end
end