From 0a1d1207e0ba4fd8b5217435605f42df1c31c047 Mon Sep 17 00:00:00 2001 From: Franco Colmenarez Date: Tue, 23 Jan 2024 23:16:00 -0500 Subject: [PATCH] WIP script to run tests --- Cargo.toml | 2 +- snes-core/src/cpu/bus.rs | 1 + snes-core/src/cpu/cycles.rs | 1 + snes-core/src/rom/mod.rs | 1 + snes-core/src/rom/special_ram_cart.rs | 36 +++++++ snes-cpu-test-runner/Cargo.toml | 12 +++ snes-cpu-test-runner/src/main.rs | 131 ++++++++++++++++++++++++++ 7 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 snes-core/src/rom/special_ram_cart.rs create mode 100644 snes-cpu-test-runner/Cargo.toml create mode 100644 snes-cpu-test-runner/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index 0fae707..91663e6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,3 @@ [workspace] -members = ["snes-core", "snes-frontend"] +members = ["snes-core", "snes-cpu-test-runner", "snes-frontend"] resolver = "2" diff --git a/snes-core/src/cpu/bus.rs b/snes-core/src/cpu/bus.rs index a960f51..7f66ca0 100644 --- a/snes-core/src/cpu/bus.rs +++ b/snes-core/src/cpu/bus.rs @@ -42,6 +42,7 @@ impl Bus { } fn map_address(address: u32) -> MemoryMap { + return MemoryMap::Cartridge; let (bank, sub_address) = { let bank = (address >> 16) as u8; let sub_address = address as u16; diff --git a/snes-core/src/cpu/cycles.rs b/snes-core/src/cpu/cycles.rs index b7f336a..2e41b86 100644 --- a/snes-core/src/cpu/cycles.rs +++ b/snes-core/src/cpu/cycles.rs @@ -108,6 +108,7 @@ fn common_conditions(cpu_registers: &Registers, addressing_mode: AddressingMode, fn common_bytes_cycles_arithmetic(addressing_mode: AddressingMode) -> (u16, usize) { match addressing_mode { + A::Accumulator => (1, 2), A::Immediate => (2, 2), A::Absolute => (3, 4), A::AbsoluteLong => (4, 5), diff --git a/snes-core/src/rom/mod.rs b/snes-core/src/rom/mod.rs index 7853840..4539b5b 100644 --- a/snes-core/src/rom/mod.rs +++ b/snes-core/src/rom/mod.rs @@ -1,4 +1,5 @@ pub mod lo_rom; +pub mod special_ram_cart; use std::fs::File; use std::io::Read; diff --git a/snes-core/src/rom/special_ram_cart.rs b/snes-core/src/rom/special_ram_cart.rs new file mode 100644 index 0000000..8b42ed9 --- /dev/null +++ b/snes-core/src/rom/special_ram_cart.rs @@ -0,0 +1,36 @@ +use super::ROM; + +pub struct SpecialRAMCart { + data: Vec, +} + +impl SpecialRAMCart { + pub fn new() -> Self { + Self { + data: vec![0x00; 0x1000000], + } + } +} + +impl ROM for SpecialRAMCart { + fn load(&mut self, _filename: &str) -> std::io::Result { + Ok(true) + } + + fn read(&self, address: u32) -> u8 { + match self.data.get(address as usize) { + Some(byte) => *byte, + None => 0x00, + } + } + + fn write(&mut self, address: u32, value: u8) { + self.data[address as usize] = value; + } +} + +impl Default for SpecialRAMCart { + fn default() -> Self { + Self::new() + } +} diff --git a/snes-cpu-test-runner/Cargo.toml b/snes-cpu-test-runner/Cargo.toml new file mode 100644 index 0000000..ea44668 --- /dev/null +++ b/snes-cpu-test-runner/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "snes-cpu-test-runner" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +snes-core = { path = "../snes-core" } + +serde = { version = "1.0.195", features = ["derive"] } +serde_json = "1.0.111" diff --git a/snes-cpu-test-runner/src/main.rs b/snes-cpu-test-runner/src/main.rs new file mode 100644 index 0000000..5a14462 --- /dev/null +++ b/snes-cpu-test-runner/src/main.rs @@ -0,0 +1,131 @@ +use snes_core::cpu::bus::Bus; +use snes_core::cpu::registers::Registers; +/// https://github.com/TomHarte/ProcessorTests/tree/main/65816 + +use snes_core::emulator::Emulator; +use serde::{Deserialize, Serialize}; +use serde_json::Result; +use snes_core::rom::special_ram_cart::SpecialRAMCart; + +use std::fs::File; +use std::io::Read; + +#[derive(Serialize, Deserialize, Debug)] +pub struct TestState { + pc: usize, + s: usize, + p: usize, + a: usize, + x: usize, + y: usize, + dbr: usize, + d: usize, + pbr: usize, + e: usize, + ram: Vec<(usize, usize)>, +} + +#[derive(Serialize, Deserialize)] +pub struct TestSuite { + name: String, + initial: TestState, + r#final: TestState, + cycles: Vec<(usize, Option, String)>, +} + +#[derive(Serialize, Deserialize)] +pub struct TestSuiteList(Vec); + +fn print_failed(expected_state: &TestState, bus: &Bus, cpu_registers: &Registers) { + eprintln!("FAILED!"); + eprintln!("----------"); + eprintln!("Expected:"); + eprintln!("{:?}", expected_state); + eprintln!("Result:"); + let mut result_ram = vec![]; + for (address, _) in &expected_state.ram { + result_ram.push((*address, bus.read_external(*address as u32) as usize)) + } + let result_state = TestState { + pc: cpu_registers.pc as usize, + s: cpu_registers.sp as usize, + p: cpu_registers.p as usize, + a: cpu_registers.a as usize, + x: cpu_registers.x as usize, + y: cpu_registers.y as usize, + dbr: cpu_registers.dbr as usize, + d: cpu_registers.d as usize, + pbr: cpu_registers.pbr as usize, + e: if cpu_registers.emulation_mode {1} else {0}, + ram: result_ram, + }; + eprintln!("{:?}", result_state); + eprintln!("----------"); + std::process::exit(1); +} + +fn main() -> Result<()> { + let args: Vec = std::env::args().collect(); + if args.len() < 2 { + eprintln!("A test file must be provided"); + std::process::exit(1); + } + let filename = args.get(1).unwrap(); + if filename.is_empty() { + eprintln!("A test file must be provided"); + std::process::exit(1); + } + let mut file = File::open(filename).unwrap(); + let mut buff = String::new(); + file.read_to_string(&mut buff).unwrap(); + + let tests: TestSuiteList = serde_json::from_str(&buff)?; + + let mut emulator = Emulator::new(); + emulator.bus.rom = Box::new(SpecialRAMCart::new()); + + for test in tests.0 { + println!("running test case {}", test.name); + + emulator.cpu.registers.pc = test.initial.pc as u16; + emulator.cpu.registers.sp = test.initial.s as u16; + emulator.cpu.registers.p = test.initial.p as u8; + emulator.cpu.registers.a = test.initial.p as u16; + emulator.cpu.registers.x = test.initial.x as u16; + emulator.cpu.registers.y = test.initial.y as u16; + emulator.cpu.registers.dbr = test.initial.dbr as u8; + emulator.cpu.registers.d = test.initial.d as u16; + emulator.cpu.registers.pbr = test.initial.pbr as u8; + emulator.cpu.registers.emulation_mode = test.initial.e == 1; + + for (address, value) in test.initial.ram { + emulator.bus.write(address as u32, value as u8); + } + + emulator.tick(); + + // compare the results + if + emulator.cpu.registers.pc as usize != test.r#final.pc || + emulator.cpu.registers.sp as usize != test.r#final.s || + emulator.cpu.registers.p as usize != test.r#final.p || + emulator.cpu.registers.a as usize != test.r#final.p || + emulator.cpu.registers.x as usize != test.r#final.x || + emulator.cpu.registers.y as usize != test.r#final.y || + emulator.cpu.registers.dbr as usize != test.r#final.dbr || + emulator.cpu.registers.d as usize != test.r#final.d || + emulator.cpu.registers.pbr as usize != test.r#final.pbr || + emulator.cpu.registers.emulation_mode != (test.r#final.e == 1) + { + print_failed(&test.r#final, &emulator.bus, &emulator.cpu.registers); + } + for (address, value) in &test.r#final.ram { + if *value != emulator.bus.read_external(*address as u32) as usize { + print_failed(&test.r#final, &emulator.bus, &emulator.cpu.registers); + } + } + } + + println!("PASSED!"); + Ok(()) +}