mod apu; mod controllers; pub mod header_menu; pub mod hex_view; mod mem; mod ppu; #[cfg(test)] mod test_roms; pub use ppu::{Color, PPU, RenderBuffer}; use std::{fs::File, io::Read, path::Path}; use thiserror::Error; use tracing::{debug, info}; use crate::{ apu::APU, controllers::Controllers, hex_view::Memory, mem::{MemoryMap, Segment}, }; #[derive(Error, Debug)] pub enum NESError { #[error(transparent)] Io(#[from] std::io::Error), #[error("File loaded was invalid")] InvalidFile, } type Result = std::result::Result; pub struct NES { clock_count: usize, cpu: Cpu, dma: DmaState, memory: MemoryMap, ppu: PPU, apu: APU, controller: Controllers, cycle: usize, last_instruction: String, dbg_int: bool, halted: bool, } impl std::fmt::Debug for NES { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { writeln!(f, "NES Emulator")?; writeln!( f, "CPU: {{ a: {:02X}, x: {:02X}, y: {:02X}, flags: {:?}, sp: {:02X}, pc: {:04X} }}", self.cpu.a, self.cpu.x, self.cpu.y, self.cpu.status, self.cpu.sp, self.cpu.pc )?; writeln!(f, " Decode {:X?}", self.cpu.clock_state)?; writeln!(f, " Last instruction: {}", self.last_instruction)?; writeln!(f, " Cycle: CPU={}, PPU={}", self.cycle, self.ppu.cycle)?; writeln!( f, "MEM: {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X}", self.peek(self.cpu.pc), self.peek(self.cpu.pc + 1), self.peek(self.cpu.pc + 2), self.peek(self.cpu.pc + 3), self.peek(self.cpu.pc + 4), self.peek(self.cpu.pc + 5), self.peek(self.cpu.pc + 6), self.peek(self.cpu.pc + 7), self.peek(self.cpu.pc + 8), self.peek(self.cpu.pc + 9), )?; writeln!( f, "0x1F0: {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X}", self.peek(0x1F0), self.peek(0x1F1), self.peek(0x1F2), self.peek(0x1F3), self.peek(0x1F4), self.peek(0x1F5), self.peek(0x1F6), self.peek(0x1F7), self.peek(0x1F8), self.peek(0x1F9), self.peek(0x1FA), self.peek(0x1FB), self.peek(0x1FC), self.peek(0x1FD), self.peek(0x1FE), self.peek(0x1FF), )?; writeln!( f, "STACK: {:02X} {:02X} | {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X}", self.peek(self.cpu.sp as u16 + 0xFF), self.peek(self.cpu.sp as u16 + 0x100), self.peek(self.cpu.sp as u16 + 0x101), self.peek(self.cpu.sp as u16 + 0x102), self.peek(self.cpu.sp as u16 + 0x103), self.peek(self.cpu.sp as u16 + 0x104), self.peek(self.cpu.sp as u16 + 0x105), self.peek(self.cpu.sp as u16 + 0x106), self.peek(self.cpu.sp as u16 + 0x107), self.peek(self.cpu.sp as u16 + 0x108), )?; write!(f, "PPU: {:?}", self.ppu)?; Ok(()) } } bitfield::bitfield! { pub struct CpuStatus(u8); carry, set_carry: 0; zero, set_zero: 1; interrupt_disable, set_interrupt_disable: 2; decimal, set_decimal: 3; brk, set_brk: 4; php, set_php: 5; overflow, set_overflow: 6; negative, set_negative: 7; } impl std::fmt::Debug for CpuStatus { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if self.negative() { write!(f, "N")?; } if self.overflow() { write!(f, "V")?; } if self.decimal() { write!(f, "D")?; } if self.interrupt_disable() { write!(f, "I")?; } if self.zero() { write!(f, "Z")?; } if self.carry() { write!(f, "C")?; } Ok(()) } } #[derive(Debug)] pub struct Cpu { a: u8, x: u8, y: u8, pc: u16, sp: u8, status: CpuStatus, clock_state: ClockState, } enum ClockState { ReadInstruction, ReadOperands { instruction: u8, ops: [u8; 5], count: u8, }, Hold { cycles: u8, instruction: u8, ops: [u8; 5], count: u8, }, HoldNmi { cycles: u8, }, HoldIrq { cycles: u8, }, } impl std::fmt::Debug for ClockState { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::ReadInstruction => write!(f, "ReadInstruction"), Self::ReadOperands { instruction, ops, count, } => f .debug_struct("ReadOperands") .field("instruction", instruction) .field("ops", &&ops[..*count as usize]) .finish(), Self::Hold { cycles, instruction, ops, count, } => f .debug_struct("Hold") .field("cycles", cycles) .field("instruction", instruction) .field("ops", &&ops[..*count as usize]) .finish(), ClockState::HoldNmi { cycles } => { f.debug_struct("HoldNmi").field("cycles", cycles).finish() } ClockState::HoldIrq { cycles } => { f.debug_struct("HoldIrq").field("cycles", cycles).finish() } } } } enum ExecState { Done, MoreParams, Hold(u8), } pub enum CPUMMRegisters { PPU, APU, } impl Cpu { fn init() -> Self { Self { a: 0, x: 0, y: 0, pc: 0, sp: 0, status: CpuStatus(0), clock_state: ClockState::Hold { cycles: 0, instruction: 0xEA, ops: [0; 5], count: 0 }, } } } pub enum DmaState { Passive, Running { cpu_addr: u16, rem: u8, read: Option, }, } pub struct CycleResult { pub cpu_exec: bool, pub ppu_frame: bool, pub dma: bool, pub dbg_int: bool, } impl NES { pub fn load_nes_file(file: impl AsRef) -> Result { let mut raw = Vec::new(); File::open(file)?.read_to_end(&mut raw)?; Self::load_nes_file_mem(&raw) } pub fn load_nes_file_mem(raw: &[u8]) -> Result { if &raw[0..4] != &[0x4E, 0x45, 0x53, 0x1A] { return Err(NESError::InvalidFile); } let prg_rom_size = raw[4]; let chr_rom_size = raw[5]; let mapper_flags = raw[6]; // let nes_20 = raw[7]; info!("PRG: {prg_rom_size}"); info!("CHR: {chr_rom_size}"); info!("FLAGS: {mapper_flags:b}"); if mapper_flags & 0b11111110 != 0 { todo!("Support other mapper flags"); } Ok(Self::from_rom( &raw[16..][..16384 * prg_rom_size as usize], &raw[16 + 16384 * prg_rom_size as usize..][..8192 * chr_rom_size as usize], )) } fn from_rom(prg_rom: &[u8], chr_rom: &[u8]) -> Self { let mut segments = vec![ Segment::ram("Internal RAM", 0x0000, 0x0800), Segment::mirror("Mirror of iRAM", 0x0800, 0x1800, 0), Segment::reg("PPU registers", 0x2000, 0x0008, CPUMMRegisters::PPU), Segment::mirror("Mirror of PPU", 0x2008, 0x1FF8, 2), Segment::reg("APU & IO registers", 0x4000, 0x0018, CPUMMRegisters::APU), Segment::mirror("Mirror of APU & IO", 0x4018, 0x0008, 2), ]; // let mut cur = 0x4020; segments.push(Segment::rom("PROG ROM", 0x8000, prg_rom)); Self { cycle: 7, dbg_int: false, halted: false, clock_count: 0, memory: MemoryMap::new(segments), cpu: Cpu::init(), ppu: PPU::with_chr_rom(chr_rom), apu: APU::init(), controller: Controllers::init(), dma: DmaState::Passive, last_instruction: "".into(), } } pub fn read(&mut self, addr: u16) -> u8 { self.memory.read(addr).reg_map(|reg, offset| match reg { CPUMMRegisters::PPU => self.ppu.read_reg(offset), CPUMMRegisters::APU => match offset { 0x16 => self.controller.read_joy1(), 0x17 => self.controller.read_joy2(), _ => self.apu.read_reg(offset), }, }) } pub fn peek(&self, addr: u16) -> u8 { self.memory.read(addr).reg_map(|_, _| todo!()) } pub fn write(&mut self, addr: u16, val: u8) { self.memory.write(addr, val, |reg, offset, val| match reg { CPUMMRegisters::PPU => self.ppu.write_reg(offset, val), CPUMMRegisters::APU => match offset { 0x14 => { self.dma = DmaState::Running { cpu_addr: u16::from_le_bytes([0, val]), rem: 0xFF, read: None, } } 0x16 => self.controller.write_joy_strobe(val), 0x17 => (), // TODO: frame counter control _ => self.apu.write_reg(offset, val), }, }); } pub fn read_abs(&mut self, low: u8, high: u8) -> u8 { self.read(u16::from_le_bytes([low, high])) } pub fn read_abs_x(&mut self, low: u8, high: u8) -> u8 { self.read(u16::from_le_bytes([low, high]) + self.cpu.x as u16) } pub fn read_abs_y(&mut self, low: u8, high: u8) -> u8 { self.read(u16::from_le_bytes([low, high]) + self.cpu.y as u16) } pub fn read_zp_x(&mut self, low: u8) -> u8 { self.read(u16::from_le_bytes([low.wrapping_add(self.cpu.x), 0])) } pub fn read_zp_y(&mut self, low: u8) -> u8 { self.read(u16::from_le_bytes([low.wrapping_add(self.cpu.y), 0])) } pub fn write_abs(&mut self, low: u8, high: u8, val: u8) { self.write(u16::from_le_bytes([low, high]), val) } pub fn write_abs_x(&mut self, low: u8, high: u8, val: u8) { self.write(u16::from_le_bytes([low, high]) + self.cpu.x as u16, val) } pub fn write_abs_y(&mut self, low: u8, high: u8, val: u8) { self.write(u16::from_le_bytes([low, high]) + self.cpu.y as u16, val) } pub fn write_zp_x(&mut self, low: u8, val: u8) { self.write(u16::from_le_bytes([low.wrapping_add(self.cpu.x), 0]), val) } pub fn write_zp_y(&mut self, low: u8, val: u8) { self.write(u16::from_le_bytes([low.wrapping_add(self.cpu.y), 0]), val) } pub fn push(&mut self, val: u8) { self.write(self.cpu.sp as u16 + 0x100, val); self.cpu.sp = self.cpu.sp.wrapping_sub(1); } pub fn pop(&mut self) -> u8 { self.cpu.sp = self.cpu.sp.wrapping_add(1); self.read(self.cpu.sp as u16 + 0x100) } pub fn push_16(&mut self, val: u16) { debug!("Writing rc = {:04X} at {:02X}", val, self.cpu.sp); let [low, high] = val.to_le_bytes(); self.push(high); self.push(low); } pub fn pop_16(&mut self) -> u16 { let low = self.pop(); let high = self.pop(); u16::from_le_bytes([low, high]) } fn jmp_rel(&mut self, off: u8) { if off < 128 { self.cpu.pc = self.cpu.pc.wrapping_add(off as u16); } else { self.cpu.pc = self.cpu.pc.wrapping_sub(0x100 - off as u16); } } fn halt(&mut self) { self.halted = true; } /// Returns true if more bytes are needed fn exec_instruction(&mut self, ins: u8, mut params: &[u8], held: bool) -> ExecState { macro_rules! inst { ($val:expr, $hold:expr, |$($name:pat_param),*| $eval:expr) => {{ let hold_time: u8 = ($hold).into(); if params.len() < ([$(inst!(@EMPTY $name)),*] as [(); _]).len() { ExecState::MoreParams } else if !held && hold_time != 0 { ExecState::Hold(hold_time - 1) } else { debug!("Running 0x{:04X} {} :{:X} {:X?}", self.cpu.pc - (1 + params.len() as u16), $val, ins, params); // debug!("Running 0x{:04X} {} :{:X} {:X?}", self.cpu.pc - (1 + params.len() as u16), $val, ins, params); self.last_instruction = format!("0x{:04X} {} :{:X} {:X?}", self.cpu.pc - (1 + params.len() as u16), $val, ins, params); $( let $name = params[0]; #[allow(unused_assignments)] { params = ¶ms[1..]; } )* {$eval} // self.cpu.status.set_interrupt_disable(true); ExecState::Done } }}; ($name:expr, $hold:expr, || $eval:expr) => { inst!($name, $hold, | | $eval) }; (@EMPTY $n:pat_param) => { () }; } match ins { 0x02 => inst!("HLT", 0, || self.halt()), 0x38 => inst!("SEC", 1, || self.cpu.status.set_carry(true)), 0x18 => inst!("CLC", 1, || self.cpu.status.set_carry(false)), 0x78 => inst!("SEI", 1, || self.cpu.status.set_interrupt_disable(true)), 0x58 => inst!("CLI", 1, || self.cpu.status.set_interrupt_disable(false)), 0xF8 => inst!("SED", 1, || self.cpu.status.set_decimal(true)), 0xD8 => inst!("CLD", 1, || self.cpu.status.set_decimal(false)), 0xB8 => inst!("CLV", 1, || self.cpu.status.set_overflow(false)), 0x00 => inst!("BRK", 7, |_ignored| { self.push_16(self.cpu.pc); self.push(self.cpu.status.0 | 0b00110000); self.cpu.status.set_interrupt_disable(true); self.cpu.pc = u16::from_le_bytes([self.read(0xFFFE), self.read(0xFFFF)]); }), 0x48 => inst!("PHA", 2, || { self.push(self.cpu.a); }), 0x08 => inst!("PHP", 2, || { self.push(self.cpu.status.0 | 0b0011_0000); }), 0x68 => inst!("PLA", 3, || { self.cpu.a = self.pop(); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0x28 => inst!("PLP", 3, || { self.cpu.status.0 = self.pop() & 0b1100_1111; }), // Loads 0xA9 => inst!("LDA imm", 0, |a| { self.cpu.a = a; self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0xA5 => inst!("LDA zp", 1, |off| { self.cpu.a = self.read(off as u16); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0xB5 => inst!("LDA zp,x", 2, |off| { self.cpu.a = self.read_zp_x(off); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0xAD => inst!("LDA abs", 1, |low, high| { self.cpu.a = self.read_abs(low, high); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0xBD => inst!("LDA abs,x", 1, |low, high| { self.cpu.a = self.read_abs_x(low, high); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0xB9 => inst!("LDA abs,y", 1, |low, high| { self.cpu.a = self.read_abs_y(low, high); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0xA1 => inst!("LDA (ind,x)", 4, |off| { let low = self.read_zp_x(off); let high = self.read_zp_x(off.wrapping_add(1)); self.cpu.a = self.read_abs(low, high); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0xB1 => inst!("LDA (ind),y", 3, |off| { let low = self.read_abs(off, 0); let high = self.read_abs(off.wrapping_add(1), 0); self.cpu.a = self.read(u16::from_le_bytes([low, high]) + self.cpu.y as u16); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0xA2 => inst!("LDX imm", 0, |x| { self.cpu.x = x; self.cpu.status.set_zero(self.cpu.x == 0); self.cpu.status.set_negative(self.cpu.x & 0x80 == 0x80); }), 0xA6 => inst!("LDX zp", 1, |off| { self.cpu.x = self.read(off as u16); self.cpu.status.set_zero(self.cpu.x == 0); self.cpu.status.set_negative(self.cpu.x & 0x80 == 0x80); }), 0xB6 => inst!("LDX zp,y", 1, |off| { self.cpu.x = self.read_zp_y(off); self.cpu.status.set_zero(self.cpu.x == 0); self.cpu.status.set_negative(self.cpu.x & 0x80 == 0x80); }), 0xAE => inst!("LDX abs", 1, |low, high| { self.cpu.x = self.read_abs(low, high); self.cpu.status.set_zero(self.cpu.x == 0); self.cpu.status.set_negative(self.cpu.x & 0x80 == 0x80); }), 0xBE => inst!("LDX abs,y", 1, |low, high| { self.cpu.x = self.read_abs_y(low, high); self.cpu.status.set_zero(self.cpu.x == 0); self.cpu.status.set_negative(self.cpu.x & 0x80 == 0x80); }), 0xA0 => inst!("LDy imm", 0, |y| { self.cpu.y = y; self.cpu.status.set_zero(self.cpu.y == 0); self.cpu.status.set_negative(self.cpu.y & 0x80 == 0x80); }), 0xA4 => inst!("LDY zp", 1, |off| { self.cpu.y = self.read(off as u16); self.cpu.status.set_zero(self.cpu.y == 0); self.cpu.status.set_negative(self.cpu.y & 0x80 == 0x80); }), 0xB4 => inst!("LDX zp,x", 1, |off| { self.cpu.y = self.read_zp_x(off); self.cpu.status.set_zero(self.cpu.y == 0); self.cpu.status.set_negative(self.cpu.y & 0x80 == 0x80); }), 0xAC => inst!("LDX abs", 1, |low, high| { self.cpu.y = self.read_abs(low, high); self.cpu.status.set_zero(self.cpu.y == 0); self.cpu.status.set_negative(self.cpu.y & 0x80 == 0x80); }), 0xBC => inst!("LDX abs,x", 1, |low, high| { self.cpu.y = self.read_abs_x(low, high); self.cpu.status.set_zero(self.cpu.y == 0); self.cpu.status.set_negative(self.cpu.y & 0x80 == 0x80); }), // Stores 0x85 => inst!("STA zp", 1, |off| { self.write(off as u16, self.cpu.a); }), 0x95 => inst!("STA zp,x", 2, |off| { self.write_zp_x(off, self.cpu.a); }), 0x8D => inst!("STA abs", 1, |low, high| { self.write_abs(low, high, self.cpu.a); }), 0x9D => inst!("STA abs,x", 1, |low, high| { self.write_abs_x(low, high, self.cpu.a); }), 0x99 => inst!("STA abs,y", 1, |low, high| { self.write_abs_y(low, high, self.cpu.a); }), 0x81 => inst!("STA (ind,x)", 4, |off| { let low = self.read_zp_x(off); let high = self.read_zp_x(off.wrapping_add(1)); self.write_abs(low, high, self.cpu.a); }), 0x91 => inst!("STA (ind),y", 4, |off| { let low = self.read_abs(off, 0); let high = self.read_abs(off.wrapping_add(1), 0); self.write( u16::from_le_bytes([low, high]) + self.cpu.y as u16, self.cpu.a, ); }), 0x86 => inst!("STX zp", 1, |off| { self.write(off as u16, self.cpu.x); }), 0x96 => inst!("STX zp,y", 2, |off| { self.write_zp_y(off, self.cpu.x); }), 0x8E => inst!("STX abs", 1, |low, high| { self.write_abs(low, high, self.cpu.x); }), 0x84 => inst!("STY zp", 1, |off| { self.write(off as u16, self.cpu.y); }), 0x94 => inst!("STY zp,x", 2, |off| { self.write_zp_x(off, self.cpu.y); }), 0x8C => inst!("STY abs", 1, |low, high| { self.write_abs(low, high, self.cpu.y); }), // Transfers 0xAA => inst!("TAX", 1, || { self.cpu.x = self.cpu.a; self.cpu.status.set_zero(self.cpu.x == 0); self.cpu.status.set_negative(self.cpu.x & 0x80 == 0x80); }), 0xA8 => inst!("TAY", 1, || { self.cpu.y = self.cpu.a; self.cpu.status.set_zero(self.cpu.y == 0); self.cpu.status.set_negative(self.cpu.y & 0x80 == 0x80); }), 0xBA => inst!("TSX", 1, || { self.cpu.x = self.cpu.sp; self.cpu.status.set_zero(self.cpu.x == 0); self.cpu.status.set_negative(self.cpu.x & 0x80 == 0x80); }), 0x8A => inst!("TXA", 1, || { self.cpu.a = self.cpu.x; self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0x9A => inst!("TXS", 1, || { self.cpu.sp = self.cpu.x; }), 0x98 => inst!("TYA", 1, || { self.cpu.a = self.cpu.y; self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), // Branch 0x90 => inst!("BCC", !self.cpu.status.carry(), |off| { if !self.cpu.status.carry() { self.jmp_rel(off); } }), 0xB0 => inst!("BCS", self.cpu.status.carry(), |off| { if self.cpu.status.carry() { self.jmp_rel(off); } }), 0xF0 => inst!("BEQ", self.cpu.status.zero(), |off| { if self.cpu.status.zero() { self.jmp_rel(off); } }), 0x30 => inst!("BMI", self.cpu.status.negative(), |off| { if self.cpu.status.negative() { self.jmp_rel(off); } }), 0xD0 => inst!("BNE", !self.cpu.status.zero(), |off| { if !self.cpu.status.zero() { self.jmp_rel(off); } }), 0x10 => inst!("BPL", !self.cpu.status.negative(), |off| { if !self.cpu.status.negative() { self.jmp_rel(off); } }), 0x4C => inst!("JMP abs", 3, |low, high| { self.cpu.pc = u16::from_le_bytes([low, high]); }), 0x6C => inst!("JMP abs", 3, |low, high| { self.cpu.pc = u16::from_le_bytes([ self.read_abs(low, high), self.read_abs(low.wrapping_add(1), high), // Known CPU bug ]); }), 0x20 => inst!("JSR", 3, |low, high| { self.push_16(self.cpu.pc - 1); self.cpu.pc = u16::from_le_bytes([low, high]); }), 0x60 => inst!("RTS", 5, || { self.cpu.pc = self.pop_16() + 1; }), 0x40 => inst!("RTI", 5, || { self.cpu.status.0 = self.pop() & 0b1100_1111; self.cpu.pc = self.pop_16(); }), // CMP 0xC9 => inst!("CMP imm", 0, |val| { let v = self.cpu.a.wrapping_sub(val); self.cpu.status.set_zero(v == 0); self.cpu.status.set_negative(v & 0x80 == 0x80); self.cpu.status.set_carry(self.cpu.a >= val); }), 0xC5 => inst!("CMP zp", 1, |off| { let val = self.read_abs(off, 0); let v = self.cpu.a.wrapping_sub(val); self.cpu.status.set_zero(v == 0); self.cpu.status.set_negative(v & 0x80 == 0x80); self.cpu.status.set_carry(self.cpu.a >= val); }), 0xD5 => inst!("CMP zp,x", 2, |off| { let val = self.read_zp_x(off); let v = self.cpu.a.wrapping_sub(val); self.cpu.status.set_zero(v == 0); self.cpu.status.set_negative(v & 0x80 == 0x80); self.cpu.status.set_carry(self.cpu.a >= val); }), 0xCD => inst!("CMP abs", 1, |low, high| { let val = self.read_abs(low, high); let v = self.cpu.a.wrapping_sub(val); self.cpu.status.set_zero(v == 0); self.cpu.status.set_negative(v & 0x80 == 0x80); self.cpu.status.set_carry(self.cpu.a >= val); }), 0xDD => inst!("CMP abs,x", 1, |low, high| { let val = self.read_abs_x(low, high); let v = self.cpu.a.wrapping_sub(val); self.cpu.status.set_zero(v == 0); self.cpu.status.set_negative(v & 0x80 == 0x80); self.cpu.status.set_carry(self.cpu.a >= val); }), 0xD9 => inst!("CMP abs,y", 1, |low, high| { let val = self.read_abs_y(low, high); let v = self.cpu.a.wrapping_sub(val); self.cpu.status.set_zero(v == 0); self.cpu.status.set_negative(v & 0x80 == 0x80); self.cpu.status.set_carry(self.cpu.a >= val); }), 0xE0 => inst!("CPX imm", 0, |val| { let v = self.cpu.x.wrapping_sub(val); self.cpu.status.set_zero(v == 0); self.cpu.status.set_negative(v & 0x80 == 0x80); self.cpu.status.set_carry(self.cpu.x >= val); }), 0xE4 => inst!("CPX zp", 1, |off| { let val = self.read_abs(off, 0); let v = self.cpu.x.wrapping_sub(val); self.cpu.status.set_zero(v == 0); self.cpu.status.set_negative(v & 0x80 == 0x80); self.cpu.status.set_carry(self.cpu.x >= val); }), 0xEC => inst!("CPX zp", 1, |low, high| { let val = self.read_abs(low, high); let v = self.cpu.x.wrapping_sub(val); self.cpu.status.set_zero(v == 0); self.cpu.status.set_negative(v & 0x80 == 0x80); self.cpu.status.set_carry(self.cpu.x >= val); }), 0xC0 => inst!("CPY imm", 0, |val| { let v = self.cpu.y.wrapping_sub(val); self.cpu.status.set_zero(v == 0); self.cpu.status.set_negative(v & 0x80 == 0x80); self.cpu.status.set_carry(self.cpu.y >= val); }), 0xC4 => inst!("CPY zp", 1, |off| { let val = self.read_abs(off, 0); let v = self.cpu.y.wrapping_sub(val); self.cpu.status.set_zero(v == 0); self.cpu.status.set_negative(v & 0x80 == 0x80); self.cpu.status.set_carry(self.cpu.y >= val); }), 0xCC => inst!("CPY zp", 1, |low, high| { let val = self.read_abs(low, high); let v = self.cpu.y.wrapping_sub(val); self.cpu.status.set_zero(v == 0); self.cpu.status.set_negative(v & 0x80 == 0x80); self.cpu.status.set_carry(self.cpu.y >= val); }), // Arithmetic 0x69 => inst!("ADC imm", 0, |val| { let (a, carry_1) = self.cpu.a.overflowing_add(val); let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into()); self.cpu .status .set_overflow((self.cpu.a ^ a) & (val ^ a) & 0x80 != 0); self.cpu.a = a; self.cpu.status.set_carry(carry_1 | carry_2); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0x65 => inst!("ADC zp", 0, |off| { let val = self.read_abs(off, 0); let (a, carry_1) = self.cpu.a.overflowing_add(val); let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into()); self.cpu .status .set_overflow((self.cpu.a ^ a) & (val ^ a) & 0x80 != 0); self.cpu.a = a; self.cpu.status.set_carry(carry_1 | carry_2); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0x75 => inst!("ADC zp,x", 0, |off| { let val = self.read_zp_x(off); let (a, carry_1) = self.cpu.a.overflowing_add(val); let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into()); self.cpu .status .set_overflow((self.cpu.a ^ a) & (val ^ a) & 0x80 != 0); self.cpu.a = a; self.cpu.status.set_carry(carry_1 | carry_2); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0x6D => inst!("ADC abs", 0, |low, high| { let val = self.read_abs(low, high); let (a, carry_1) = self.cpu.a.overflowing_add(val); let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into()); self.cpu .status .set_overflow((self.cpu.a ^ a) & (val ^ a) & 0x80 != 0); self.cpu.a = a; self.cpu.status.set_carry(carry_1 | carry_2); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0x7D => inst!("ADC abs,x", 0, |low, high| { let val = self.read_abs_x(low, high); let (a, carry_1) = self.cpu.a.overflowing_add(val); let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into()); self.cpu .status .set_overflow((self.cpu.a ^ a) & (val ^ a) & 0x80 != 0); self.cpu.a = a; self.cpu.status.set_carry(carry_1 | carry_2); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0x79 => inst!("ADC abs,y", 0, |low, high| { let val = self.read_abs_y(low, high); let (a, carry_1) = self.cpu.a.overflowing_add(val); let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into()); self.cpu .status .set_overflow((self.cpu.a ^ a) & (val ^ a) & 0x80 != 0); self.cpu.a = a; self.cpu.status.set_carry(carry_1 | carry_2); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0x61 => inst!("ADC (ind,x)", 0, |off| { let low = self.read_abs(off, 0); let high = self.read_abs(off.wrapping_add(1), 0); let val = self.read(u16::from_le_bytes([low, high]) + self.cpu.y as u16); let (a, carry_1) = self.cpu.a.overflowing_add(val); let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into()); self.cpu .status .set_overflow((self.cpu.a ^ a) & (val ^ a) & 0x80 != 0); self.cpu.a = a; self.cpu.status.set_carry(carry_1 | carry_2); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0x71 => inst!("ADC (ind),y", 0, |off| { let low = self.read_abs(off, 0); let high = self.read_abs(off.wrapping_add(1), 0); let val = self.read(u16::from_le_bytes([low, high]) + self.cpu.y as u16); let (a, carry_1) = self.cpu.a.overflowing_add(val); let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into()); self.cpu .status .set_overflow((self.cpu.a ^ a) & (val ^ a) & 0x80 != 0); self.cpu.a = a; self.cpu.status.set_carry(carry_1 | carry_2); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0xE9 => inst!("SBC imm", 0, |val| { let (a, carry_1) = self.cpu.a.overflowing_add(!val); let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into()); self.cpu .status .set_overflow((self.cpu.a ^ a) & (!val ^ a) & 0x80 != 0); self.cpu.a = a; // TODO: I'm pretty sure the carry logic is wrong here self.cpu.status.set_carry(carry_1 | carry_2); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0xE5 => inst!("SBC zp", 0, |off| { let val = self.read_abs(off, 0); let (a, carry_1) = self.cpu.a.overflowing_add(!val); let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into()); self.cpu .status .set_overflow((self.cpu.a ^ a) & (!val ^ a) & 0x80 != 0); self.cpu.a = a; self.cpu.status.set_carry(carry_1 | carry_2); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0xF5 => inst!("SBC zp,x", 0, |off| { let val = self.read_zp_x(off); let (a, carry_1) = self.cpu.a.overflowing_add(!val); let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into()); self.cpu .status .set_overflow((self.cpu.a ^ a) & (!val ^ a) & 0x80 != 0); self.cpu.a = a; self.cpu.status.set_carry(carry_1 | carry_2); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0xED => inst!("SBC abs", 0, |low, high| { let val = self.read_abs(low, high); let (a, carry_1) = self.cpu.a.overflowing_add(!val); let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into()); self.cpu .status .set_overflow((self.cpu.a ^ a) & (!val ^ a) & 0x80 != 0); self.cpu.a = a; self.cpu.status.set_carry(carry_1 | carry_2); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0xFD => inst!("SBC abs,x", 0, |low, high| { let val = self.read_abs_x(low, high); let (a, carry_1) = self.cpu.a.overflowing_add(!val); let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into()); self.cpu .status .set_overflow((self.cpu.a ^ a) & (!val ^ a) & 0x80 != 0); self.cpu.a = a; self.cpu.status.set_carry(carry_1 | carry_2); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0xF9 => inst!("SBC abs,y", 0, |low, high| { let val = self.read_abs_y(low, high); let (a, carry_1) = self.cpu.a.overflowing_add(!val); let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into()); self.cpu .status .set_overflow((self.cpu.a ^ a) & (!val ^ a) & 0x80 != 0); self.cpu.a = a; self.cpu.status.set_carry(carry_1 | carry_2); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0xE1 => inst!("SBC (ind,x)", 0, |off| { let low = self.read_abs(off, 0); let high = self.read_abs(off.wrapping_add(1), 0); let val = self.read(u16::from_le_bytes([low, high]) + self.cpu.y as u16); let (a, carry_1) = self.cpu.a.overflowing_add(!val); let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into()); self.cpu .status .set_overflow((self.cpu.a ^ a) & (!val ^ a) & 0x80 != 0); self.cpu.a = a; self.cpu.status.set_carry(carry_1 | carry_2); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0xF1 => inst!("SBC (ind),y", 0, |off| { let low = self.read_abs(off, 0); let high = self.read_abs(off.wrapping_add(1), 0); let val = self.read(u16::from_le_bytes([low, high]) + self.cpu.y as u16); let (a, carry_1) = self.cpu.a.overflowing_add(!val); let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into()); self.cpu .status .set_overflow((self.cpu.a ^ a) & (!val ^ a) & 0x80 != 0); self.cpu.a = a; self.cpu.status.set_carry(carry_1 | carry_2); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0xC6 => inst!("DEC zp", 3, |off| { let val = self.read_abs(off, 0); self.write_abs(off, 0, val.wrapping_sub(1)); self.cpu.status.set_zero(val == 0); self.cpu.status.set_negative(val & 0x80 == 0x80); }), 0xD6 => inst!("DEC zp,x", 4, |off| { let val = self.read_zp_x(off); self.write_zp_x(off, val.wrapping_sub(1)); self.cpu.status.set_zero(val == 0); self.cpu.status.set_negative(val & 0x80 == 0x80); }), 0xCE => inst!("DEC abs", 3, |low, high| { let val = self.read_abs(low, high); self.write_abs(low, high, val.wrapping_sub(1)); self.cpu.status.set_zero(val == 0); self.cpu.status.set_negative(val & 0x80 == 0x80); }), 0xDE => inst!("DEC abs,x", 3, |low, high| { let val = self.read_abs_x(low, high); self.write_abs_x(low, high, val.wrapping_sub(1)); self.cpu.status.set_zero(val == 0); self.cpu.status.set_negative(val & 0x80 == 0x80); }), 0xCA => inst!("DEX", 1, || { self.cpu.x = self.cpu.x.wrapping_sub(1); self.cpu.status.set_zero(self.cpu.x == 0); self.cpu.status.set_negative(self.cpu.x & 0x80 == 0x80); }), 0x88 => inst!("DEY", 1, || { self.cpu.y = self.cpu.y.wrapping_sub(1); self.cpu.status.set_zero(self.cpu.y == 0); self.cpu.status.set_negative(self.cpu.y & 0x80 == 0x80); }), 0xE6 => inst!("INC zp", 3, |off| { let val = self.read_abs(off, 0); self.write_abs(off, 0, val.wrapping_add(1)); self.cpu.status.set_zero(val == 0); self.cpu.status.set_negative(val & 0x80 == 0x80); }), 0xF6 => inst!("INC zp,x", 4, |off| { let val = self.read_zp_x(off); self.write_zp_x(off, val.wrapping_add(1)); self.cpu.status.set_zero(val == 0); self.cpu.status.set_negative(val & 0x80 == 0x80); }), 0xEE => inst!("INC abs", 3, |low, high| { let val = self.read_abs(low, high); self.write_abs(low, high, val.wrapping_add(1)); self.cpu.status.set_zero(val == 0); self.cpu.status.set_negative(val & 0x80 == 0x80); }), 0xFE => inst!("INC abs,x", 3, |low, high| { let val = self.read_abs_x(low, high); self.write_abs_x(low, high, val.wrapping_add(1)); self.cpu.status.set_zero(val == 0); self.cpu.status.set_negative(val & 0x80 == 0x80); }), 0xE8 => inst!("INX", 1, || { self.cpu.x = self.cpu.x.wrapping_add(1); self.cpu.status.set_zero(self.cpu.x == 0); self.cpu.status.set_negative(self.cpu.x & 0x80 == 0x80); }), 0xC8 => inst!("INY", 1, || { self.cpu.y = self.cpu.y.wrapping_add(1); self.cpu.status.set_zero(self.cpu.y == 0); self.cpu.status.set_negative(self.cpu.y & 0x80 == 0x80); }), // Logical 0x09 => inst!("ORA imm", 0, |val| { self.cpu.a |= val; self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0x05 => inst!("ORA zp", 1, |val| { self.cpu.a |= self.read_abs(val, 0); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0x15 => inst!("ORA zp,x", 2, |val| { self.cpu.a |= self.read_zp_x(val); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0x0D => inst!("ORA abs", 2, |low, high| { self.cpu.a |= self.read_abs(low, high); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0x1D => inst!("ORA abs,x", 2, |low, high| { self.cpu.a |= self.read_abs_x(low, high); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0x19 => inst!("ORA abs,y", 1, |low, high| { self.cpu.a |= self.read_abs_y(low, high); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0x01 => inst!("ORA (ind,x)", 4, |off| { let low = self.read_zp_x(off); let high = self.read_zp_x(off.wrapping_add(1)); self.cpu.a |= self.read_abs(low, high); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0x11 => inst!("ORA (ind),y", 4, |off| { let low = self.read_abs(off, 0); let high = self.read_abs(off.wrapping_add(1), 0); self.cpu.a |= self.read(u16::from_le_bytes([low, high]) + self.cpu.y as u16); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0x29 => inst!("AND imm", 0, |val| { self.cpu.a &= val; self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0x25 => inst!("AND zp", 1, |val| { self.cpu.a &= self.read_abs(val, 0); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0x35 => inst!("AND zp,x", 2, |val| { self.cpu.a &= self.read_zp_x(val); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0x2D => inst!("AND abs", 2, |low, high| { self.cpu.a &= self.read_abs(low, high); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0x3D => inst!("AND abs,x", 2, |low, high| { self.cpu.a &= self.read_abs_x(low, high); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0x39 => inst!("AND abs,y", 1, |low, high| { self.cpu.a &= self.read_abs_y(low, high); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0x21 => inst!("AND (ind,x)", 4, |off| { let low = self.read_zp_x(off); let high = self.read_zp_x(off.wrapping_add(1)); self.cpu.a &= self.read_abs(low, high); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0x31 => inst!("AND (ind),y", 4, |off| { let low = self.read_abs(off, 0); let high = self.read_abs(off.wrapping_add(1), 0); self.cpu.a &= self.read(u16::from_le_bytes([low, high]) + self.cpu.y as u16); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0x49 => inst!("EOR imm", 0, |val| { self.cpu.a ^= val; self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0x45 => inst!("EOR zp", 1, |val| { self.cpu.a ^= self.read_abs(val, 0); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0x55 => inst!("EOR zp,x", 2, |val| { self.cpu.a ^= self.read_zp_x(val); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0x4D => inst!("EOR abs", 1, |low, high| { self.cpu.a ^= self.read_abs(low, high); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0x5D => inst!("EOR abs,x", 1, |low, high| { self.cpu.a ^= self.read_abs_x(low, high); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0x59 => inst!("EOR abs,y", 1, |low, high| { self.cpu.a ^= self.read_abs_y(low, high); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0x41 => inst!("EOR (ind,x)", 4, |off| { let low = self.read_zp_x(off); let high = self.read_zp_x(off.wrapping_add(1)); self.cpu.a ^= self.read_abs(low, high); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0x51 => inst!("EOR (ind),y", 3, |off| { let low = self.read_abs(off, 0); let high = self.read_abs(off.wrapping_add(1), 0); self.cpu.a ^= self.read(u16::from_le_bytes([low, high]) + self.cpu.y as u16); self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); }), 0x24 => inst!("BIT zp", 1, |val| { let val = self.cpu.a & self.read_abs(val, 0); self.cpu.status.set_zero(val == 0); self.cpu.status.set_overflow(val & 0x40 == 0x40); self.cpu.status.set_negative(val & 0x80 == 0x80); }), 0x2C => inst!("BIT abs", 1, |low, high| { let val = self.cpu.a & self.read_abs(low, high); self.cpu.status.set_zero(val == 0); self.cpu.status.set_overflow(val & 0x40 == 0x40); self.cpu.status.set_negative(val & 0x80 == 0x80); }), // Shifts 0x4A => inst!("LSR A", 1, || { self.cpu.status.set_carry(self.cpu.a & 0b1 == 0b1); self.cpu.a = self.cpu.a >> 1; self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(false); }), 0x46 => inst!("LSR zp", 3, |off| { let val = self.read_abs(off, 0); self.cpu.status.set_carry(val & 0b1 == 0b1); let val = val >> 1; self.cpu.status.set_zero(val == 0); self.cpu.status.set_negative(false); self.write_abs(off, 0, val); }), 0x56 => inst!("LSR zp,x", 4, |off| { let val = self.read_zp_x(off); self.cpu.status.set_carry(val & 0b1 == 0b1); let val = val >> 1; self.cpu.status.set_zero(val == 0); self.cpu.status.set_negative(false); self.write_zp_x(off, val); }), 0x4E => inst!("LSR abs", 3, |low, high| { let val = self.read_abs(low, high); self.cpu.status.set_carry(val & 0b1 == 0b1); let val = val >> 1; self.cpu.status.set_zero(val == 0); self.cpu.status.set_negative(false); self.write_abs(low, high, val); }), 0x5E => inst!("LSR zp,x", 4, |low, high| { let val = self.read_abs_x(low, high); self.cpu.status.set_carry(val & 0b1 == 0b1); let val = val >> 1; self.cpu.status.set_zero(val == 0); self.cpu.status.set_negative(false); self.write_abs_x(low, high, val); }), 0x0A => inst!("ASL A", 1, || { self.cpu.status.set_carry(self.cpu.a & 0x80 == 0x80); self.cpu.a = self.cpu.a << 1; self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(false); }), 0x06 => inst!("ASL zp", 3, |off| { let val = self.read_abs(off, 0); self.cpu.status.set_carry(val & 0x80 == 0x80); let val = val << 1; self.cpu.status.set_zero(val == 0); self.cpu.status.set_negative(false); self.write_abs(off, 0, val); }), 0x16 => inst!("ASL zp,x", 4, |off| { let val = self.read_zp_x(off); self.cpu.status.set_carry(val & 0x80 == 0x80); let val = val << 1; self.cpu.status.set_zero(val == 0); self.cpu.status.set_negative(false); self.write_zp_x(off, val); }), 0x0E => inst!("ASL abs", 3, |low, high| { let val = self.read_abs(low, high); self.cpu.status.set_carry(val & 0x80 == 0x80); let val = val << 1; self.cpu.status.set_zero(val == 0); self.cpu.status.set_negative(false); self.write_abs(low, high, val); }), 0x1E => inst!("ASL zp,x", 4, |low, high| { let val = self.read_abs_x(low, high); self.cpu.status.set_carry(val & 0x80 == 0x80); let val = val << 1; self.cpu.status.set_zero(val == 0); self.cpu.status.set_negative(false); self.write_abs_x(low, high, val); }), 0x6A => inst!("ROR A", 1, || { let old_carry = if self.cpu.status.carry() { 0x80 } else { 0x00 }; self.cpu.status.set_carry(self.cpu.a & 0b1 == 0b1); self.cpu.a = self.cpu.a >> 1 | old_carry; self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(false); }), 0x66 => inst!("ROR zp", 3, |off| { let old_carry = if self.cpu.status.carry() { 0x80 } else { 0x00 }; let val = self.read_abs(off, 0); self.cpu.status.set_carry(val & 0b1 == 0b1); let val = val >> 1 | old_carry; self.cpu.status.set_zero(val == 0); self.cpu.status.set_negative(false); self.write_abs(off, 0, val); }), 0x76 => inst!("ROR zp,x", 4, |off| { let old_carry = if self.cpu.status.carry() { 0x80 } else { 0x00 }; let val = self.read_zp_x(off); self.cpu.status.set_carry(val & 0b1 == 0b1); let val = val >> 1 | old_carry; self.cpu.status.set_zero(val == 0); self.cpu.status.set_negative(false); self.write_zp_x(off, val); }), 0x6E => inst!("ROR abs", 3, |low, high| { let old_carry = if self.cpu.status.carry() { 0x80 } else { 0x00 }; let val = self.read_abs(low, high); self.cpu.status.set_carry(val & 0b1 == 0b1); let val = val >> 1 | old_carry; self.cpu.status.set_zero(val == 0); self.cpu.status.set_negative(false); self.write_abs(low, high, val); }), 0x7E => inst!("ROR zp,x", 4, |low, high| { let old_carry = if self.cpu.status.carry() { 0x80 } else { 0x00 }; let val = self.read_abs_x(low, high); self.cpu.status.set_carry(val & 0b1 == 0b1); let val = val >> 1 | old_carry; self.cpu.status.set_zero(val == 0); self.cpu.status.set_negative(false); self.write_abs_x(low, high, val); }), 0x2A => inst!("ROL A", 1, || { let old_carry = if self.cpu.status.carry() { 0x01 } else { 0x00 }; self.cpu.status.set_carry(self.cpu.a & 0x80 == 0x80); self.cpu.a = self.cpu.a << 1 | old_carry; self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_negative(false); }), 0x26 => inst!("ROL zp", 3, |off| { let old_carry = if self.cpu.status.carry() { 0x01 } else { 0x00 }; let val = self.read_abs(off, 0); self.cpu.status.set_carry(val & 0x80 == 0x80); let val = val << 1 | old_carry; self.cpu.status.set_zero(val == 0); self.cpu.status.set_negative(false); self.write_abs(off, 0, val); }), 0x36 => inst!("ROL zp,x", 4, |off| { let old_carry = if self.cpu.status.carry() { 0x01 } else { 0x00 }; let val = self.read_zp_x(off); self.cpu.status.set_carry(val & 0x80 == 0x80); let val = val << 1 | old_carry; self.cpu.status.set_zero(val == 0); self.cpu.status.set_negative(false); self.write_zp_x(off, val); }), 0x2E => inst!("ROL abs", 3, |low, high| { let old_carry = if self.cpu.status.carry() { 0x01 } else { 0x00 }; let val = self.read_abs(low, high); self.cpu.status.set_carry(val & 0x80 == 0x80); let val = val << 1 | old_carry; self.cpu.status.set_zero(val == 0); self.cpu.status.set_negative(false); self.write_abs(low, high, val); }), 0x3E => inst!("ROL zp,x", 4, |low, high| { let old_carry = if self.cpu.status.carry() { 0x01 } else { 0x00 }; let val = self.read_abs_x(low, high); self.cpu.status.set_carry(val & 0x80 == 0x80); let val = val << 1 | old_carry; self.cpu.status.set_zero(val == 0); self.cpu.status.set_negative(false); self.write_abs_x(low, high, val); }), 0xEA => inst!("NOP", 1, || {}), _ => todo!("ins: 0x{:04X}: 0x{ins:X}, {params:X?}", self.cpu.pc - 1), } } pub fn peek_opcode(&self) -> u8 { self.peek(self.cpu.pc) } fn clock_cpu(&mut self) { self.cpu.clock_state = match self.cpu.clock_state { ClockState::HoldNmi { cycles } => { if cycles == 0 { self.push_16(self.cpu.pc); self.push(self.cpu.status.0); self.cpu.pc = u16::from_le_bytes([self.read(0xFFFA), self.read(0xFFFB)]); ClockState::ReadInstruction } else { ClockState::HoldNmi { cycles: cycles - 1 } } } ClockState::HoldIrq { cycles } => { if cycles == 0 { todo!("Run NMI"); } else { ClockState::HoldIrq { cycles: cycles - 1 } } } ClockState::ReadInstruction => { if self.ppu.nmi_waiting() || self.apu.nmi_waiting() { ClockState::HoldNmi { cycles: 6 } } else if self.ppu.irq_waiting() || self.apu.irq_waiting() { ClockState::HoldIrq { cycles: 6 } } else { let instruction = self.read(self.cpu.pc); self.cpu.pc = self.cpu.pc.wrapping_add(1); match self.exec_instruction(instruction, &[], false) { ExecState::Done => ClockState::ReadInstruction, ExecState::MoreParams => ClockState::ReadOperands { instruction, ops: [0u8; 5], count: 0, }, ExecState::Hold(cycles) => ClockState::Hold { cycles, instruction, ops: [0u8; 5], count: 0, }, } } } ClockState::ReadOperands { instruction, mut ops, count, } => { if count == 5 { todo!() } ops[count as usize] = self.read(self.cpu.pc); self.cpu.pc = self.cpu.pc.wrapping_add(1); match self.exec_instruction(instruction, &ops[..count as usize + 1], false) { ExecState::Done => ClockState::ReadInstruction, ExecState::MoreParams => ClockState::ReadOperands { instruction, ops, count: count + 1, }, ExecState::Hold(cycles) => ClockState::Hold { cycles, instruction, ops, count: count + 1, }, } } ClockState::Hold { cycles, instruction, ops, count, } => { if cycles == 0 { match self.exec_instruction(instruction, &ops[..count as usize], true) { ExecState::Done => ClockState::ReadInstruction, ExecState::MoreParams => ClockState::ReadOperands { instruction, ops, count: count + 1, }, ExecState::Hold(_) => panic!("Should never return Hold after holding"), } } else { ClockState::Hold { cycles: cycles - 1, instruction, ops, count, } } } }; } fn cpu_cycle(&mut self) { self.cycle += 1; match self.dma { DmaState::Passive => self.clock_cpu(), // TODO: Validate that this takes the correct number of cycles (513 or 514 cycles) DmaState::Running { cpu_addr, rem, read: Some(read), } => { self.ppu.write_reg(4, read); debug!("OAM DMA write {:02X}", read); if rem == 0 { self.dma = DmaState::Passive; } else { self.dma = DmaState::Running { cpu_addr: cpu_addr + 1, rem: rem - 1, read: None, }; } } DmaState::Running { cpu_addr, rem, read: None, } => { let read = self.read(cpu_addr); debug!("OAM DMA read {:04X} {:02X}", cpu_addr, read); self.dma = DmaState::Running { cpu_addr, rem, read: Some(read), }; } } if [0x8031, 0x8014].contains(&self.cpu.pc) { self.dbg_int = true; } } pub fn run_one_clock_cycle(&mut self) -> CycleResult { if self.halted { return CycleResult { cpu_exec: false, ppu_frame: false, dma: false, dbg_int: true, }; } self.clock_count += 1; let cpu_exec = if self.clock_count % 3 == 1 { self.cpu_cycle(); matches!(self.cpu.clock_state, ClockState::ReadInstruction) } else { false }; // 3 PPU clock cycles for each CPU clock cycle let ppu_frame = self.ppu.run_one_clock_cycle(); let dbg_int = self.dbg_int | self.ppu.dbg_int; self.dbg_int = false; self.ppu.dbg_int = false; CycleResult { cpu_exec, ppu_frame, dma: matches!(self.dma, DmaState::Running { .. }), dbg_int, } } pub fn reset(&mut self) { self.cpu.pc = u16::from_le_bytes([self.read(0xFFFC), self.read(0xFFFD)]); self.cpu.sp = 0xFD; self.cpu.status.set_interrupt_disable(true); self.ppu.reset(); } pub fn power_cycle(&mut self) { // self.memory.clear(); // self.ppu.reset(); // self.ppu.memory.clear(); *self = Self::from_rom(self.memory.rom(6).expect("PRG ROM"), self.ppu.memory.rom(0).expect("CHR ROM")); self.reset(); } pub fn reset_and_run(&mut self) { self.reset(); while !self.halted { // info!("Running clock cycle: {}", self.clock_count); self.run_one_clock_cycle(); } } pub fn reset_and_run_with_timeout(&mut self, max_clock_cycles: usize) { self.reset(); let mut cur = 0; while !self.halted && cur < max_clock_cycles { // info!("Running clock cycle: {}", self.clock_count); self.run_one_clock_cycle(); cur += 1; } } pub fn image(&self) -> &RenderBuffer<256, 240> { &self.ppu.render_buffer } pub fn ppu(&self) -> &PPU { &self.ppu } pub fn cpu_mem(&self) -> &impl Memory { &self.memory } }