Skip to content

Instantly share code, notes, and snippets.

@luckasRanarison
Last active May 30, 2023 07:04
Show Gist options
  • Save luckasRanarison/79d0a602401b5d58a02bb03bc30bc2fb to your computer and use it in GitHub Desktop.
Save luckasRanarison/79d0a602401b5d58a02bb03bc30bc2fb to your computer and use it in GitHub Desktop.
6502-rs (base 2A03)
use crate::opcodes::CPU_OPCODES;
use self::{AddressMode::*, AsmInstr::*, StatusFlag::*};
#[rustfmt::skip]
#[derive(Debug, Clone, Copy)]
pub enum AsmInstr {
LDA, LDX, LDY,
STA, STX, STY,
TAX, TAY, TSX, TXA, TXS, TYA,
PHA, PHP, PLA, PLP,
DEC, DEX, DEY,
INC, INX, INY,
ADC, SBC,
AND, EOR, ORA,
ASL, LSR,
ROL, ROR,
CLC, CLD, CLI, CLV,
SEC, SED, SEI,
CMP, CPX, CPY,
BCC, BCS, BEQ, BMI, BNE, BPL, BVC, BVS,
JMP, JSR, RTS,
BRK, RTI,
BIT, NOP
}
#[derive(Debug, Clone, Copy)]
pub enum AddressMode {
Implied(Register),
Immediate,
Absolute,
AbsoluteX,
AbsoluteY,
ZeroPage,
ZeroPageX,
ZeroPageY,
Indirect,
IndirectX,
IndirectY,
Relative,
}
#[rustfmt::skip]
#[derive(Debug, Clone, Copy)]
pub enum Register { A, X, Y, SP, SR, PC }
#[rustfmt::skip]
#[derive(Debug)]
enum StatusFlag { C, Z, I, D, B, __, V, N }
#[derive(Debug)]
enum Address {
MemoryAddress(u16),
Register(Register),
}
#[derive(Debug)]
pub struct Cpu {
register_a: u8,
register_x: u8,
register_y: u8,
stack_pointer: u8,
status_register: u8,
program_counter: u16,
memory_map: [u8; 0xFFFF],
}
impl Cpu {
pub fn new() -> Self {
Self {
register_a: 0x00,
register_x: 0x00,
register_y: 0x00,
stack_pointer: 0xFF,
program_counter: 0x8000,
status_register: 0b0011_0000,
memory_map: [0x00; 0xFFFF],
}
}
pub fn load_program(&mut self, program: Vec<u8>) {
let program_rom = &mut self.memory_map[0x8000..0x8000 + &program.len()];
program_rom.copy_from_slice(&program);
}
pub fn run(&mut self) {
loop {
let opcode_index = self.read_u8(self.program_counter);
let opcode = CPU_OPCODES
.get(&opcode_index)
.unwrap_or_else(|| panic!("Invalid opcode {:x}", opcode_index));
let adr_mode = opcode.adr_mode;
self.program_counter += 1;
let prev_counter = self.program_counter;
match opcode.asm {
LDA => self.lda(adr_mode),
LDX => self.ldx(adr_mode),
LDY => self.ldy(adr_mode),
STA => self.sta(adr_mode),
STX => self.stx(adr_mode),
STY => self.sty(adr_mode),
TAX => self.tax(),
TAY => self.tay(),
TSX => self.tsx(),
TXA => self.txa(),
TXS => self.txs(),
TYA => self.tya(),
PHA => self.pha(),
PHP => self.php(),
PLA => self.pla(),
PLP => self.plp(),
DEC => self.dec(adr_mode),
DEX => self.dex(adr_mode),
DEY => self.dey(adr_mode),
INC => self.inc(adr_mode),
INX => self.inx(adr_mode),
INY => self.iny(adr_mode),
ADC => self.adc(adr_mode),
SBC => self.sbc(adr_mode),
AND => self.and(adr_mode),
EOR => self.eor(adr_mode),
ORA => self.ora(adr_mode),
ASL => self.asl(adr_mode),
LSR => self.lsr(adr_mode),
ROL => self.rol(adr_mode),
ROR => self.ror(adr_mode),
CLC => self.clc(),
CLD => self.cld(),
CLI => self.cli(),
CLV => self.clv(),
SEC => self.sec(),
SED => self.sed(),
SEI => self.sei(),
CMP => self.cmp(adr_mode),
CPX => self.cpx(adr_mode),
CPY => self.cpy(adr_mode),
BCC => self.bcc(),
BCS => self.bcs(),
BEQ => self.beq(),
BMI => self.bmi(),
BNE => self.bne(),
BPL => self.bpl(),
BVC => self.bvc(),
BVS => self.bvs(),
JMP => self.jmp(adr_mode),
JSR => self.jsr(),
RTS => self.rts(),
RTI => self.rti(),
BIT => self.bit(adr_mode),
NOP => self.nop(),
BRK => {
self.brk();
break;
}
};
if prev_counter == self.program_counter {
let counter = self.program_counter.wrapping_add(opcode.len() as u16 - 1);
self.program_counter = counter;
}
}
}
fn read_u8(&self, address: u16) -> u8 {
self.memory_map[address as usize]
}
fn read_u16(&self, address: u16) -> u16 {
let right = self.read_u8(address) as u16;
let left = self.read_u8(address + 1) as u16;
(left << 8) | (right)
}
fn write_u8(&mut self, data: u8, address: u16) {
self.memory_map[address as usize] = data;
}
fn write_u16(&mut self, data: u16, address: u16) {
let left = (data >> 8) as u8;
let right = (data & 0xFF) as u8;
self.write_u8(right, address);
self.write_u8(left, address + 1);
}
fn get_register(&mut self, register: Register) -> u8 {
match register {
Register::A => self.register_a,
Register::X => self.register_x,
Register::Y => self.register_y,
Register::SP => self.stack_pointer,
Register::SR => self.status_register,
Register::PC => unimplemented!(),
}
}
fn get_register_mut(&mut self, register: Register) -> &mut u8 {
match register {
Register::A => &mut self.register_a,
Register::X => &mut self.register_x,
Register::Y => &mut self.register_y,
Register::SP => &mut self.stack_pointer,
Register::SR => &mut self.status_register,
Register::PC => unimplemented!(),
}
}
fn set_flag(&mut self, flag: StatusFlag) {
self.status_register |= 1 << flag as u8;
}
fn clear_flag(&mut self, flag: StatusFlag) {
self.status_register &= !(1 << flag as u8);
}
fn update_negative_flag(&mut self, result: u8) {
match (result >> 7 & 1) == 1 {
true => self.set_flag(N),
false => self.clear_flag(N),
}
}
fn update_zero_flag(&mut self, result: u8) {
match result == 0 {
true => self.set_flag(Z),
false => self.clear_flag(Z),
}
}
fn update_negative_zero_flag(&mut self, result: u8) {
self.update_negative_flag(result);
self.update_zero_flag(result);
}
fn update_carry_flag(&mut self, predicate: bool) {
match predicate {
true => self.set_flag(C),
false => self.clear_flag(C),
}
}
fn update_overflow_flag(&mut self, predicate: bool) {
match predicate {
true => self.set_flag(V),
false => self.clear_flag(V),
}
}
fn get_flag(&self, flag: StatusFlag) -> u8 {
self.status_register >> flag as u8 & 1
}
fn get_address(&self, mode: AddressMode) -> Address {
match mode {
Immediate => Address::MemoryAddress(self.program_counter),
Absolute => Address::MemoryAddress(self.read_u16(self.program_counter)),
AbsoluteX => {
let address = self.read_u16(self.program_counter);
Address::MemoryAddress(address.wrapping_add(self.register_x as u16))
}
AbsoluteY => {
let address = self.read_u16(self.program_counter);
Address::MemoryAddress(address.wrapping_add(self.register_y as u16))
}
ZeroPage => Address::MemoryAddress(self.read_u8(self.program_counter) as u16),
ZeroPageX => {
let address = self.read_u8(self.program_counter);
Address::MemoryAddress(address.wrapping_add(self.register_x) as u16)
}
ZeroPageY => {
let address = self.read_u8(self.program_counter);
Address::MemoryAddress(address.wrapping_add(self.register_y) as u16)
}
Indirect => {
let ptr_address = self.read_u16(self.program_counter);
let address = match ptr_address & 0x00FF == 0x00FF {
true => {
let right = self.read_u8(ptr_address) as u16;
let left = self.read_u8(ptr_address & 0xFF00) as u16;
(left << 8) | right
}
false => self.read_u16(ptr_address),
};
Address::MemoryAddress(address)
}
IndirectX => {
let ptr = self.read_u8(self.program_counter);
let ptr = ptr.wrapping_add(self.register_x);
Address::MemoryAddress(self.read_u16(ptr as u16))
}
IndirectY => {
let ptr = self.read_u8(self.program_counter);
let address = self.read_u16(ptr as u16);
Address::MemoryAddress(address.wrapping_add(self.register_y as u16))
}
Relative => {
let offset = self.read_u8(self.program_counter);
let new_address = match offset > 0x7F {
true => self.program_counter.wrapping_sub(!offset as u16),
false => self
.program_counter
.wrapping_add(offset as u16)
.wrapping_add(1),
};
Address::MemoryAddress(new_address)
}
Implied(register) => Address::Register(register),
}
}
fn get_memory_address(&self, adr_mode: AddressMode) -> Option<u16> {
match self.get_address(adr_mode) {
Address::MemoryAddress(address) => Some(address),
_ => None,
}
}
fn read_address(&mut self, address: &Address) -> u8 {
match &address {
Address::MemoryAddress(address) => self.memory_map[*address as usize],
Address::Register(register) => self.get_register(*register),
}
}
fn write_address(&mut self, data: u8, address: Address) {
let ptr = match address {
Address::MemoryAddress(address) => &mut self.memory_map[address as usize],
Address::Register(register) => self.get_register_mut(register),
};
*ptr = data;
}
/*
MOS6502 CPU legal instructions
*/
fn lda(&mut self, adr_mode: AddressMode) {
self.load(Register::A, adr_mode);
}
fn ldx(&mut self, adr_mode: AddressMode) {
self.load(Register::X, adr_mode);
}
fn ldy(&mut self, adr_mode: AddressMode) {
self.load(Register::Y, adr_mode);
}
fn sta(&mut self, adr_mode: AddressMode) {
self.store(Register::A, adr_mode);
}
fn stx(&mut self, adr_mode: AddressMode) {
self.store(Register::X, adr_mode);
}
fn sty(&mut self, adr_mode: AddressMode) {
self.store(Register::Y, adr_mode);
}
fn tax(&mut self) {
self.transfert(Register::A, Register::X);
}
fn tay(&mut self) {
self.transfert(Register::A, Register::Y);
}
fn tsx(&mut self) {
self.transfert(Register::SP, Register::X);
}
fn txa(&mut self) {
self.transfert(Register::X, Register::A);
}
fn txs(&mut self) {
self.transfert(Register::X, Register::SP);
}
fn tya(&mut self) {
self.transfert(Register::Y, Register::A);
}
fn pha(&mut self) {
self.push_stack_u8(self.register_a);
}
fn php(&mut self) {
self.set_flag(B);
self.push_stack_u8(self.status_register);
}
fn pla(&mut self) {
self.register_a = self.pull_stack_u8();
}
fn plp(&mut self) {
let status = self.pull_stack_u8();
let mask = 0b1110_1111;
self.status_register = (status & mask) | (self.get_flag(B) << 4);
}
fn dec(&mut self, adr_mode: AddressMode) {
self.decrement(adr_mode);
}
fn dex(&mut self, adr_mode: AddressMode) {
self.decrement(adr_mode);
}
fn dey(&mut self, adr_mode: AddressMode) {
self.decrement(adr_mode);
}
fn inc(&mut self, adr_mode: AddressMode) {
self.increment(adr_mode);
}
fn inx(&mut self, adr_mode: AddressMode) {
self.increment(adr_mode);
}
fn iny(&mut self, adr_mode: AddressMode) {
self.increment(adr_mode);
}
fn adc(&mut self, adr_mode: AddressMode) {
let address = self.get_address(adr_mode);
let value = self.read_address(&address);
self.add_to_register_a(value);
}
fn sbc(&mut self, adr_mode: AddressMode) {
let address = self.get_address(adr_mode);
let value = self.read_address(&address);
self.add_to_register_a(!value);
}
fn and(&mut self, adr_mode: AddressMode) {
let address = self.get_address(adr_mode);
let value = self.read_address(&address);
let result = self.register_a & value;
self.update_negative_zero_flag(result);
}
fn eor(&mut self, adr_mode: AddressMode) {
let address = self.get_address(adr_mode);
let value = self.read_address(&address);
let result = self.register_a ^ value;
self.update_negative_zero_flag(result);
}
fn ora(&mut self, adr_mode: AddressMode) {
let address = self.get_address(adr_mode);
let value = self.read_address(&address);
let result = self.register_a | value;
self.update_negative_zero_flag(result);
}
fn asl(&mut self, adr_mode: AddressMode) {
let address = self.get_address(adr_mode);
let value = self.read_address(&address);
let result = value.wrapping_shl(1);
self.update_carry_flag(value >> 7 == 1);
self.update_negative_zero_flag(result);
self.write_address(result, address);
}
fn lsr(&mut self, adr_mode: AddressMode) {
let address = self.get_address(adr_mode);
let value = self.read_address(&address);
let result = value.wrapping_shr(1);
self.update_carry_flag(value << 7 == 128);
self.update_negative_zero_flag(result);
self.write_address(result, address);
}
fn rol(&mut self, adr_mode: AddressMode) {
let address = self.get_address(adr_mode);
let value = self.read_address(&address);
let result = value.rotate_left(1);
self.update_carry_flag(value >> 7 == 1);
self.update_negative_zero_flag(result);
self.write_address(result, address);
}
fn ror(&mut self, adr_mode: AddressMode) {
let address = self.get_address(adr_mode);
let value = self.read_address(&address);
let result = value.rotate_right(1);
self.update_carry_flag(value << 7 == 128);
self.update_negative_zero_flag(result);
self.write_address(result, address);
}
fn clc(&mut self) {
self.clear_flag(C);
}
fn cld(&mut self) {
self.clear_flag(D);
}
fn cli(&mut self) {
self.clear_flag(I);
}
fn clv(&mut self) {
self.clear_flag(V);
}
fn sec(&mut self) {
self.set_flag(C);
}
fn sed(&mut self) {
self.set_flag(D);
}
fn sei(&mut self) {
self.set_flag(I);
}
fn cmp(&mut self, adr_mode: AddressMode) {
self.compare(Register::A, adr_mode);
}
fn cpx(&mut self, adr_mode: AddressMode) {
self.compare(Register::X, adr_mode);
}
fn cpy(&mut self, adr_mode: AddressMode) {
self.compare(Register::Y, adr_mode);
}
fn bcc(&mut self) {
self.branch(self.get_flag(C) == 0);
}
fn bcs(&mut self) {
self.branch(self.get_flag(C) == 1);
}
fn beq(&mut self) {
self.branch(self.get_flag(Z) == 1);
}
fn bmi(&mut self) {
self.branch(self.get_flag(N) == 1);
}
fn bne(&mut self) {
self.branch(self.get_flag(Z) == 0);
}
fn bpl(&mut self) {
self.branch(self.get_flag(N) == 0);
}
fn bvc(&mut self) {
self.branch(self.get_flag(V) == 0);
}
fn bvs(&mut self) {
self.branch(self.get_flag(V) == 1);
}
fn jmp(&mut self, adr_mode: AddressMode) {
self.program_counter = self.get_memory_address(adr_mode).unwrap();
}
fn jsr(&mut self) {
self.push_stack_u16(self.program_counter + 1);
self.program_counter = self.get_memory_address(AddressMode::Absolute).unwrap();
}
fn rts(&mut self) {
self.program_counter = self.pull_stack_u16() + 1;
}
fn brk(&mut self) {
self.set_flag(I);
self.set_flag(B);
self.push_stack_u16(self.program_counter + 1);
self.push_stack_u8(self.status_register);
}
fn rti(&mut self) {
self.plp();
self.program_counter = self.pull_stack_u16();
}
fn bit(&mut self, adr_mode: AddressMode) {
let address = self.get_address(adr_mode);
let value = self.read_address(&address);
let result = self.register_a & value;
self.update_zero_flag(result);
self.update_negative_flag(value);
self.update_overflow_flag((value >> 6 & 1) == 1);
}
fn nop(&self) {}
/*
Helper functions
*/
fn load(&mut self, register: Register, adr_mode: AddressMode) {
let address = self.get_address(adr_mode);
let value = self.read_address(&address);
self.write_address(value, Address::Register(register));
self.update_negative_zero_flag(value);
}
fn store(&mut self, register: Register, adr_mode: AddressMode) {
let value = self.get_register(register);
let address = self.get_address(adr_mode);
self.write_address(value, address);
}
fn transfert(&mut self, register_a: Register, register_b: Register) {
let value = self.get_register(register_a);
let ptr = self.get_register_mut(register_b);
*ptr = value;
self.update_negative_zero_flag(value);
}
fn decrement(&mut self, adr_mode: AddressMode) {
let address = self.get_address(adr_mode);
let value = self.read_address(&address);
let value = value.wrapping_sub(1);
self.write_address(value, address);
self.update_negative_zero_flag(value);
}
fn increment(&mut self, adr_mode: AddressMode) {
let address = self.get_address(adr_mode);
let value = self.read_address(&address);
let value = value.wrapping_add(1);
self.write_address(value, address);
self.update_negative_zero_flag(value);
}
fn add_to_register_a(&mut self, value: u8) {
let carry = self.get_flag(C);
let (sum, c1) = self.register_a.overflowing_add(value);
let (sum, c2) = sum.overflowing_add(carry);
let overflow = (self.register_a ^ sum) & (value ^ sum) >> 7 != 0;
self.update_carry_flag(c1 || c2);
self.update_overflow_flag(overflow);
self.update_negative_zero_flag(sum);
self.register_a = sum;
}
fn push_stack_u8(&mut self, data: u8) {
self.write_u8(data, self.stack_pointer as u16 + 0x100);
self.stack_pointer = self.stack_pointer.wrapping_sub(1);
}
fn push_stack_u16(&mut self, data: u16) {
let left = (data >> 8) as u8;
let right = (data & 0xFF) as u8;
self.push_stack_u8(left);
self.push_stack_u8(right);
}
fn pull_stack_u8(&mut self) -> u8 {
self.stack_pointer = self.stack_pointer.wrapping_add(1);
self.read_u8(self.stack_pointer as u16 + 0x100)
}
fn pull_stack_u16(&mut self) -> u16 {
let right = self.pull_stack_u8() as u16;
let left = self.pull_stack_u8() as u16;
(left << 8) | (right)
}
fn compare(&mut self, register: Register, adr_mode: AddressMode) {
let value_a = self.get_register(register);
let address_b = self.get_address(adr_mode);
let value_b = self.read_address(&address_b);
let (sum, c1) = value_a.overflowing_add(!value_b);
let (sum, c2) = sum.overflowing_add(1);
self.update_carry_flag(c1 || c2);
self.update_negative_zero_flag(sum);
}
fn branch(&mut self, predicate: bool) {
if predicate {
self.program_counter = self.get_memory_address(AddressMode::Relative).unwrap();
}
}
}
#[cfg(test)]
mod test {
use crate::cpu::StatusFlag::*;
use super::Cpu;
#[test]
fn test_status_flag() {
let mut cpu = Cpu::new();
cpu.set_flag(C);
cpu.set_flag(N);
cpu.set_flag(V);
assert_eq!(cpu.status_register, 0b1111_0001);
cpu.clear_flag(V);
assert_eq!(cpu.status_register, 0b1011_0001);
}
#[test]
fn test_immediate_address() {
let mut cpu = Cpu::new();
cpu.load_program(vec![0xA9, 0x0F]);
cpu.run();
assert_eq!(cpu.register_a, 0x0F);
}
#[test]
fn test_absolute_address() {
let mut cpu = Cpu::new();
cpu.write_u16(0x0F, 0x8030);
cpu.load_program(vec![0xAD, 0x30, 0x80]);
cpu.run();
assert_eq!(cpu.register_a, 0x0F);
}
#[test]
fn test_zero_page_address() {
let mut cpu = Cpu::new();
cpu.write_u8(0x0F, 0x30);
cpu.load_program(vec![0xA5, 0x30]);
cpu.run();
assert_eq!(cpu.register_a, 0x0F);
}
#[test]
fn test_indirect_address_x() {
let mut cpu = Cpu::new();
cpu.register_x = 0x05;
cpu.write_u16(0x23, 0x0075);
cpu.write_u16(0x30, 0x0076);
cpu.write_u16(0xA5, 0x3023);
cpu.load_program(vec![0xA1, 0x70]);
cpu.run();
assert_eq!(cpu.register_a, 0xA5);
}
#[test]
fn test_indirect_address_y() {
let mut cpu = Cpu::new();
cpu.register_y = 0x10;
cpu.write_u16(0x43, 0x0070);
cpu.write_u16(0x35, 0x0071);
cpu.write_u16(0x23, 0x3553);
cpu.load_program(vec![0xB1, 0x70]);
cpu.run();
assert_eq!(cpu.register_a, 0x23);
}
#[test]
fn test_relative_address_pos() {
let mut cpu = Cpu::new();
cpu.load_program(vec![0xA9, 0xFF, 0x69, 0x01, 0xB0, 0x02, 0xA9, 0x01]);
cpu.run();
assert_eq!(cpu.program_counter, 0x8008 + 1); // BRK increments PC
}
#[test]
fn test_relative_address_neg() {
let mut cpu = Cpu::new();
cpu.load_program(vec![0xA9, 0xFF, 0x69, 0x01, 0xB0, 0xFC, 0xA9, 0x01]);
cpu.run();
assert_eq!(cpu.get_flag(C), 0);
}
#[test]
fn test_indirect_address() {
let mut cpu = Cpu::new();
cpu.load_program(vec![
0xA9, 0xC4, 0x8D, 0x82, 0xFF, 0xA9, 0x80, 0x8D, 0x83, 0xFF, 0x6C, 0x82, 0xFF,
]);
cpu.run();
assert_eq!(cpu.program_counter, 0x80C4 + 1);
}
#[test]
fn test_indirect_address_boundary_page() {
let mut cpu = Cpu::new();
cpu.load_program(vec![
0xA9, 0x50, 0x8D, 0x00, 0x30, 0xA9, 0x80, 0x8D, 0xFF, 0x30, 0xA9, 0x40, 0x8D, 0x00,
0x31, 0x6C, 0xFF, 0x30,
]);
cpu.run();
assert_eq!(cpu.program_counter, 0x5080 + 1);
}
#[test]
fn test_inx() {
let mut cpu = Cpu::new();
cpu.load_program(vec![0xA2, 0x01, 0xE8]);
cpu.run();
assert_eq!(cpu.register_x, 2);
}
#[test]
fn test_adc() {
let mut cpu = Cpu::new();
cpu.load_program(vec![0xA9, 0xFF, 0x69, 0x01, 0x69, 0x00, 0x69, 0x7F]);
cpu.run();
assert_eq!(cpu.register_a, 0x80);
assert_eq!(cpu.get_flag(N), 1);
assert_eq!(cpu.get_flag(C), 0);
assert_eq!(cpu.get_flag(V), 1);
}
#[test]
fn test_sbc() {
let mut cpu = Cpu::new();
cpu.load_program(vec![0xA9, 0x05, 0xE9, 0x03]);
cpu.run();
assert_eq!(cpu.register_a, 0x01);
}
#[test]
fn test_cmp() {
let mut cpu = Cpu::new();
cpu.load_program(vec![0xA9, 0x05, 0xC9, 0x05]);
cpu.run();
assert_eq!(cpu.get_flag(N), 0);
assert_eq!(cpu.get_flag(Z), 1);
assert_eq!(cpu.get_flag(C), 1);
}
#[test]
fn test_jsr_rts() {
let mut cpu = Cpu::new();
cpu.load_program(vec![
0xA9, 0x01, 0x20, 0x08, 0x80, 0x69, 0x01, 0x00, 0x69, 0x01, 0x60,
]);
cpu.run();
assert_eq!(cpu.register_a, 3);
}
}
use lazy_static::lazy_static;
use std::collections::HashMap;
use crate::cpu::{
AddressMode::{self, *},
AsmInstr::{self, *},
Register::*,
};
#[derive(Debug, Clone, Copy)]
pub struct Opcode {
pub asm: AsmInstr,
pub adr_mode: AddressMode,
pub cycle: u8,
}
impl Opcode {
pub fn len(&self) -> u8 {
match self.adr_mode {
Implied(_) => 1,
Immediate | ZeroPage | ZeroPageX | ZeroPageY | IndirectX | IndirectY | Relative => 2,
Absolute | AbsoluteX | AbsoluteY | Indirect => 3,
}
}
fn new(asm: AsmInstr, adr_mode: AddressMode, cycle: u8) -> Self {
Self {
asm,
adr_mode,
cycle,
}
}
}
lazy_static! {
#[rustfmt::skip]
pub static ref CPU_OPCODES: HashMap<u8, Opcode> = HashMap::from([
(0xA9, Opcode::new(LDA, Immediate, 2)),
(0xA5, Opcode::new(LDA, ZeroPage, 3)),
(0xB5, Opcode::new(LDA, ZeroPageX, 4)),
(0xAD, Opcode::new(LDA, Absolute, 4)),
(0xBD, Opcode::new(LDA, AbsoluteX, 4)),
(0xB9, Opcode::new(LDA, AbsoluteY, 4)),
(0xA1, Opcode::new(LDA, IndirectX, 6)),
(0xB1, Opcode::new(LDA, IndirectY, 5)),
(0xA2, Opcode::new(LDX, Immediate, 2)),
(0xA6, Opcode::new(LDX, ZeroPage, 3)),
(0xB6, Opcode::new(LDX, ZeroPageY, 4)),
(0xAE, Opcode::new(LDX, Absolute, 4)),
(0xBE, Opcode::new(LDX, AbsoluteY, 4)),
(0xA0, Opcode::new(LDY, Immediate, 2)),
(0xA4, Opcode::new(LDY, ZeroPage, 3)),
(0xB4, Opcode::new(LDY, ZeroPageX, 4)),
(0xAC, Opcode::new(LDY, Absolute, 4)),
(0xBC, Opcode::new(LDY, AbsoluteX, 4)),
(0x85, Opcode::new(STA, ZeroPage, 3)),
(0x95, Opcode::new(STA, ZeroPageX, 4)),
(0x8D, Opcode::new(STA, Absolute, 4)),
(0x9D, Opcode::new(STA, AbsoluteX, 5)),
(0x99, Opcode::new(STA, AbsoluteY, 5)),
(0x81, Opcode::new(STA, IndirectX, 6)),
(0x91, Opcode::new(STA, IndirectY, 6)),
(0x86, Opcode::new(STX, ZeroPage, 3)),
(0x96, Opcode::new(STX, ZeroPageY, 4)),
(0x8E, Opcode::new(STX, Absolute, 4)),
(0x84, Opcode::new(STY, ZeroPage, 3)),
(0x94, Opcode::new(STY, ZeroPageX, 4)),
(0x8C, Opcode::new(STY, Absolute, 4)),
(0xAA, Opcode::new(TAX, Implied(X), 2)),
(0xA8, Opcode::new(TAY, Implied(Y), 2)),
(0xBA, Opcode::new(TSX, Implied(X), 2)),
(0x8A, Opcode::new(TXA, Implied(A), 2)),
(0x9A, Opcode::new(TXS, Implied(SP), 2)),
(0x98, Opcode::new(TYA, Implied(A), 2)),
(0xC6, Opcode::new(DEC, ZeroPage, 5)),
(0xD6, Opcode::new(DEC, ZeroPageX, 6)),
(0xCE, Opcode::new(DEC, Absolute, 6)),
(0xDE, Opcode::new(DEC, AbsoluteX, 7)),
(0xCA, Opcode::new(DEX, Implied(X), 2)),
(0x88, Opcode::new(DEY, Implied(Y), 2)),
(0xE6, Opcode::new(INC, ZeroPage, 5)),
(0xF6, Opcode::new(INC, ZeroPageX, 6)),
(0xEE, Opcode::new(INC, Absolute, 6)),
(0xFE, Opcode::new(INC, AbsoluteX, 7)),
(0xE8, Opcode::new(INX, Implied(X), 2)),
(0xC8, Opcode::new(INY, Implied(Y), 2)),
(0x48, Opcode::new(PHA, Implied(A), 3)),
(0x08, Opcode::new(PHP, Implied(SR), 3)),
(0x68, Opcode::new(PLA, Implied(A), 4)),
(0x28, Opcode::new(PLP, Implied(SR), 4)),
(0x69, Opcode::new(ADC, Immediate, 2)),
(0x65, Opcode::new(ADC, ZeroPage, 3)),
(0x75, Opcode::new(ADC, ZeroPageX, 4)),
(0x6D, Opcode::new(ADC, Absolute, 4)),
(0x7D, Opcode::new(ADC, AbsoluteX, 4)),
(0x79, Opcode::new(ADC, AbsoluteY, 4)),
(0x61, Opcode::new(ADC, IndirectX, 6)),
(0x71, Opcode::new(ADC, IndirectY, 5)),
(0xE9, Opcode::new(SBC, Immediate, 2)),
(0xE5, Opcode::new(SBC, ZeroPage, 3)),
(0xF5, Opcode::new(SBC, ZeroPageX, 4)),
(0xED, Opcode::new(SBC, Absolute, 4)),
(0xFD, Opcode::new(SBC, AbsoluteX, 4)),
(0xF9, Opcode::new(SBC, AbsoluteY, 4)),
(0xE1, Opcode::new(SBC, IndirectX, 6)),
(0xF1, Opcode::new(SBC, IndirectY, 5)),
(0x29, Opcode::new(AND, Immediate, 2)),
(0x25, Opcode::new(AND, ZeroPage, 3)),
(0x35, Opcode::new(AND, ZeroPageX, 4)),
(0x2D, Opcode::new(AND, Absolute, 4)),
(0x3D, Opcode::new(AND, AbsoluteX, 4)),
(0x39, Opcode::new(AND, AbsoluteY, 4)),
(0x21, Opcode::new(AND, IndirectX, 6)),
(0x31, Opcode::new(AND, IndirectY, 5)),
(0x49, Opcode::new(EOR, Immediate, 2)),
(0x45, Opcode::new(EOR, ZeroPage, 3)),
(0x55, Opcode::new(EOR, ZeroPageX, 4)),
(0x4D, Opcode::new(EOR, Absolute, 4)),
(0x5D, Opcode::new(EOR, AbsoluteX, 4)),
(0x59, Opcode::new(EOR, AbsoluteY, 4)),
(0x41, Opcode::new(EOR, IndirectX, 6)),
(0x51, Opcode::new(EOR, IndirectY, 5)),
(0x09, Opcode::new(ORA, Immediate, 2)),
(0x05, Opcode::new(ORA, ZeroPage, 3)),
(0x15, Opcode::new(ORA, ZeroPageX, 4)),
(0x0D, Opcode::new(ORA, Absolute, 4)),
(0x1D, Opcode::new(ORA, AbsoluteX, 4)),
(0x19, Opcode::new(ORA, AbsoluteY, 4)),
(0x01, Opcode::new(ORA, IndirectX, 6)),
(0x11, Opcode::new(ORA, IndirectY, 5)),
(0x0A, Opcode::new(ASL, Implied(A), 2)),
(0x06, Opcode::new(ASL, ZeroPage, 5)),
(0x16, Opcode::new(ASL, ZeroPageX, 6)),
(0x0E, Opcode::new(ASL, Absolute, 6)),
(0x1E, Opcode::new(ASL, AbsoluteX, 7)),
(0x4A, Opcode::new(LSR, Implied(A), 2)),
(0x46, Opcode::new(LSR, ZeroPage, 5)),
(0x56, Opcode::new(LSR, ZeroPageX, 6)),
(0x4E, Opcode::new(LSR, Absolute, 6)),
(0x5E, Opcode::new(LSR, AbsoluteX, 7)),
(0x2A, Opcode::new(ROL, Implied(A), 2)),
(0x26, Opcode::new(ROL, ZeroPage, 5)),
(0x36, Opcode::new(ROL, ZeroPageX, 6)),
(0x2E, Opcode::new(ROL, Absolute, 6)),
(0x3E, Opcode::new(ROL, AbsoluteX, 7)),
(0x6A, Opcode::new(ROR, Implied(A), 2)),
(0x66, Opcode::new(ROR, ZeroPage, 5)),
(0x76, Opcode::new(ROR, ZeroPageX, 6)),
(0x6E, Opcode::new(ROR, Absolute, 6)),
(0x7E, Opcode::new(ROR, AbsoluteX, 7)),
(0x18, Opcode::new(CLC, Implied(SR), 2)),
(0xD8, Opcode::new(CLD, Implied(SR), 2)),
(0x58, Opcode::new(CLI, Implied(SR), 2)),
(0xB8, Opcode::new(CLV, Implied(SR), 2)),
(0x38, Opcode::new(SEC, Implied(SR), 2)),
(0xF8, Opcode::new(SED, Implied(SR), 2)),
(0x78, Opcode::new(SEI, Implied(SR), 2)),
(0xC9, Opcode::new(CMP, Immediate, 2)),
(0xC5, Opcode::new(CMP, ZeroPage, 3)),
(0xD5, Opcode::new(CMP, ZeroPageX, 4)),
(0xCD, Opcode::new(CMP, Absolute, 4)),
(0xDD, Opcode::new(CMP, AbsoluteX, 4)),
(0xD9, Opcode::new(CMP, AbsoluteY, 4)),
(0xC1, Opcode::new(CMP, IndirectX, 6)),
(0xD1, Opcode::new(CMP, IndirectY, 5)),
(0xE0, Opcode::new(CPX, Immediate, 2)),
(0xE4, Opcode::new(CPX, ZeroPage, 3)),
(0xEC, Opcode::new(CPX, Absolute, 4)),
(0xC0, Opcode::new(CPY, Immediate, 2)),
(0xC4, Opcode::new(CPY, ZeroPage, 3)),
(0xCC, Opcode::new(CPY, Absolute, 4)),
(0x90, Opcode::new(BCC, Relative, 2)),
(0xB0, Opcode::new(BCS, Relative, 2)),
(0xF0, Opcode::new(BEQ, Relative, 2)),
(0x30, Opcode::new(BMI, Relative, 2)),
(0xD0, Opcode::new(BNE, Relative, 2)),
(0x10, Opcode::new(BPL, Relative, 2)),
(0x50, Opcode::new(BVC, Relative, 2)),
(0x70, Opcode::new(BVS, Relative, 2)),
(0x4C, Opcode::new(JMP, Absolute, 3)),
(0x6C, Opcode::new(JMP, Indirect, 5)),
(0x20, Opcode::new(JSR, Absolute, 6)),
(0x60, Opcode::new(RTS, Implied(PC), 6)),
(0x00, Opcode::new(BRK, Implied(PC), 7)),
(0x40, Opcode::new(RTI, Implied(PC), 6)),
(0x24, Opcode::new(BIT, ZeroPage, 3)),
(0x2C, Opcode::new(BIT, Absolute, 4)),
(0xEA, Opcode::new(NOP, Implied(PC), 2)),
]);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment