main.rsannotatedmain.rssource224 lines · 5.9 KB · raw
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 {

Directory containing JSON tests

101    #[arg(long, short = 'd')]
102    dir_path: String,

Emulate the NES 6502 instead of the standard 6502

105    #[arg(long, short = 'n', default_value_t)]
106    nes: bool,
107}
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}