1use clap::Parser; 2use env_logger::Env; 3use mos6502_emu::bus::BusInterface; 4use mos6502_emu::{CpuRegisters, Mos6502, StatusFlags, StatusReadContext}; 5use serde::Deserialize; 6use std::fs::File; 7use std::io::BufReader; 8use std::mem; 9use std::path::Path; 10 11#[derive(Debug, Clone, Copy, PartialEq, Eq)] 12enum BusCycle { 13 Read(u16, u8), 14 Write(u16, u8), 15} 16 17struct Bus { 18 ram: Vec<u8>, 19 addresses_written: Vec<u16>, 20 cycles: Vec<BusCycle>, 21} 22 23impl Bus { 24 fn new() -> Self { 25 Self { ram: vec![0; 64 * 1024], addresses_written: Vec::new(), cycles: Vec::new() } 26 } 27 28 fn clear(&mut self) { 29 for address in mem::take(&mut self.addresses_written) { 30 self.ram[address as usize] = 0; 31 } 32 self.cycles.clear(); 33 } 34} 35 36impl BusInterface for Bus { 37 #[inline] 38 fn read(&mut self, address: u16) -> u8 { 39 let value = self.ram[address as usize]; 40 self.cycles.push(BusCycle::Read(address, value)); 41 value 42 } 43 44 #[inline] 45 fn write(&mut self, address: u16, value: u8) { 46 self.ram[address as usize] = value; 47 self.addresses_written.push(address); 48 self.cycles.push(BusCycle::Write(address, value)); 49 } 50 51 #[inline] 52 fn nmi(&self) -> bool { 53 false 54 } 55 56 #[inline] 57 fn acknowledge_nmi(&mut self) {} 58 59 #[inline] 60 fn irq(&self) -> bool { 61 false 62 } 63} 64 65#[derive(Debug, Clone, Deserialize)] 66struct SystemState { 67 pc: u16, 68 s: u8, 69 a: u8, 70 x: u8, 71 y: u8, 72 p: u8, 73 ram: Vec<(u16, u8)>, 74} 75 76#[derive(Debug, Clone, Deserialize)] 77struct Cycle(u16, u8, String); 78 79impl Cycle { 80 fn to_bus_cycle(&self) -> BusCycle { 81 match self.2.as_str() { 82 "read" => BusCycle::Read(self.0, self.1), 83 "write" => BusCycle::Write(self.0, self.1), 84 _ => panic!("Invalid bus cycle type, expected read/write: {}", self.2), 85 } 86 } 87} 88 89#[derive(Debug, Clone, Deserialize)] 90struct TestDescription { 91 name: String, 92 initial: SystemState, 93 #[serde(rename = "final")] 94 final_: SystemState, 95 cycles: Vec<Cycle>, 96} 97 98#[derive(Debug, Parser)] 99struct Args {
Emulate the NES 6502 instead of the standard 6502
109fn main() -> anyhow::Result<()> { 110 env_logger::Builder::from_env(Env::default().default_filter_or("info")).init(); 111 112 let args = Args::parse(); 113 let mut bus = Bus::new(); 114 115 for opcode in 0x00..=0xFF { 116 let file_path = Path::new(&args.dir_path).join(format!("{opcode:02x}.json")); 117 let tests: Vec<TestDescription> = 118 serde_json::from_reader(BufReader::new(File::open(&file_path)?))?; 119 120 let mut failures = 0; 121 let test_count = tests.len(); 122 for test in tests { 123 bus.clear(); 124 for &(address, value) in &test.initial.ram { 125 bus.write(address, value); 126 } 127 128 let mut cpu = 129 if args.nes { Mos6502::new_nes(&mut bus) } else { Mos6502::new_standard(&mut bus) }; 130 131 cpu.set_registers(CpuRegisters { 132 accumulator: test.initial.a, 133 x: test.initial.x, 134 y: test.initial.y, 135 status: StatusFlags::from_byte(test.initial.p), 136 pc: test.initial.pc, 137 sp: test.initial.s, 138 ..cpu.registers().clone() 139 }); 140 141 bus.cycles.clear(); 142 cpu.tick(&mut bus); 143 while cpu.is_mid_instruction() && !cpu.frozen() { 144 cpu.tick(&mut bus); 145 } 146 147 if cpu.frozen() { 148 // Don't bother testing KIL opcodes 149 continue; 150 } 151 152 if check_state(&cpu, &bus, &test.final_, &test.cycles) { 153 failures += 1; 154 log::debug!("Above failures in '{}'", test.name); 155 } 156 } 157 158 if failures != 0 { 159 log::error!("Failed {failures} out of {test_count} tests for opcode {opcode:02X}"); 160 } 161 } 162 163 Ok(()) 164} 165 166fn check_state(cpu: &Mos6502, bus: &Bus, final_state: &SystemState, cycles: &[Cycle]) -> bool { 167 let mut errors = false; 168 169 for &(address, expected_value) in &final_state.ram { 170 let actual_value = bus.ram[address as usize]; 171 if expected_value != actual_value { 172 errors = true; 173 log::debug!( 174 "RAM[{address:04X}]: expected={expected_value:02X}, actual={actual_value:02X}" 175 ); 176 } 177 } 178 179 let registers = cpu.registers(); 180 errors |= check_register("A", final_state.a, registers.accumulator); 181 errors |= check_register("X", final_state.x, registers.x); 182 errors |= check_register("Y", final_state.y, registers.y); 183 errors |= check_register("S", final_state.s, registers.sp); 184 errors |= check_register( 185 "P", 186 final_state.p | 0x10, 187 registers.status.to_byte(StatusReadContext::Brk) | 0x10, 188 ); 189 190 if final_state.pc != registers.pc { 191 log::debug!("PC: expected={:04X} actual={:04X}", final_state.pc, registers.pc); 192 errors = true; 193 } 194 195 if cycles.len() != bus.cycles.len() { 196 log::debug!( 197 "Cycle count does not match: expected={}, actual={}", 198 cycles.len(), 199 bus.cycles.len() 200 ); 201 log::debug!(" Expected: {cycles:?}"); 202 log::debug!(" Actual: {:?}", bus.cycles); 203 errors = true; 204 } else { 205 for (i, (expected, &actual)) in cycles.iter().zip(&bus.cycles).enumerate() { 206 let expected = expected.to_bus_cycle(); 207 if expected != actual { 208 log::debug!("Cycle {i} mismatch: expected={expected:?}, actual={actual:?}"); 209 errors = true; 210 } 211 } 212 } 213 214 errors 215} 216 217fn check_register(name: &str, expected: u8, actual: u8) -> bool { 218 if expected != actual { 219 log::debug!("{name}: expected={expected:02X}, actual={actual:02X}"); 220 true 221 } else { 222 false 223 } 224}