diff --git a/Cargo.lock b/Cargo.lock index 0681ff4..54ce5de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -170,27 +170,26 @@ dependencies = [ [[package]] name = "clap" -version = "3.0.0-beta.5" +version = "3.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feff3878564edb93745d58cf63e17b63f24142506e7a20c87a5521ed7bfb1d63" +checksum = "23b71c3ce99b7611011217b366d923f1d0a7e07a92bb2dbf1e84508c673ca3bd" dependencies = [ "atty", "bitflags", "clap_derive", + "clap_lex", "indexmap", - "lazy_static", - "os_str_bytes", + "once_cell", "strsim 0.10.0", "termcolor", - "textwrap 0.14.2", - "unicase", + "textwrap 0.15.0", ] [[package]] name = "clap_derive" -version = "3.0.0-beta.5" +version = "3.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b15c6b4f786ffb6192ffe65a36855bc1fc2444bcd0945ae16748dcd6ed7d0d3" +checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" dependencies = [ "heck", "proc-macro-error", @@ -199,6 +198,15 @@ dependencies = [ "syn", ] +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "cmake" version = "0.1.46" @@ -398,6 +406,18 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" +[[package]] +name = "harte_tests" +version = "0.1.0" +dependencies = [ + "clap 3.2.20", + "flate2", + "moa", + "serde", + "serde_derive", + "serde_json", +] + [[package]] name = "hashbrown" version = "0.11.2" @@ -406,12 +426,9 @@ checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" [[package]] name = "heck" -version = "0.3.3" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" [[package]] name = "hermit-abi" @@ -453,6 +470,12 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "itoa" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" + [[package]] name = "jni" version = "0.19.0" @@ -621,7 +644,7 @@ dependencies = [ name = "moa-minifb" version = "0.1.0" dependencies = [ - "clap 3.0.0-beta.5", + "clap 3.2.20", "minifb", "moa", "moa-common", @@ -812,9 +835,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.8.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" +checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0" [[package]] name = "orbclient" @@ -833,12 +856,9 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "4.2.0" +version = "6.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addaa943333a514159c80c97ff4a93306530d965d27e139188283cd13e06a799" -dependencies = [ - "memchr", -] +checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" [[package]] name = "parking_lot" @@ -1034,6 +1054,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "ryu" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" + [[package]] name = "same-file" version = "1.0.6" @@ -1083,6 +1109,28 @@ version = "1.0.130" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" +[[package]] +name = "serde_derive" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" +dependencies = [ + "itoa", + "ryu", + "serde", +] + [[package]] name = "shlex" version = "0.1.1" @@ -1175,12 +1223,9 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.14.2" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80" -dependencies = [ - "unicode-width", -] +checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" [[package]] name = "thiserror" @@ -1211,21 +1256,6 @@ dependencies = [ "serde", ] -[[package]] -name = "unicase" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicode-segmentation" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" - [[package]] name = "unicode-width" version = "0.1.9" diff --git a/src/cpus/m68k/debugger.rs b/src/cpus/m68k/debugger.rs index a0a7d5b..81749a6 100644 --- a/src/cpus/m68k/debugger.rs +++ b/src/cpus/m68k/debugger.rs @@ -6,6 +6,7 @@ use crate::devices::{Address, Addressable, Debuggable}; use super::state::M68k; use super::decode::M68kDecoder; +#[derive(Clone)] pub struct StackTracer { pub calls: Vec, } @@ -27,6 +28,7 @@ impl StackTracer { } +#[derive(Clone)] pub struct M68kDebugger { pub enabled: bool, pub breakpoints: Vec, @@ -68,10 +70,10 @@ impl Debuggable for M68k { } } - fn print_current_step(&mut self, system: &System) -> Result<(), Error> { + fn print_current_step(&mut self, _system: &System) -> Result<(), Error> { self.decoder.decode_at(&mut self.port, self.state.pc)?; self.decoder.dump_decoded(&mut self.port); - self.dump_state(system); + self.dump_state(); Ok(()) } diff --git a/src/cpus/m68k/decode.rs b/src/cpus/m68k/decode.rs index afee24e..c98a6c9 100644 --- a/src/cpus/m68k/decode.rs +++ b/src/cpus/m68k/decode.rs @@ -38,6 +38,7 @@ const OPCG_SHIFT: u8 = 0xE; const OPCG_FLINE: u8 = 0xF; +#[derive(Clone)] pub struct M68kDecoder { pub cputype: M68kType, pub start: u32, diff --git a/src/cpus/m68k/execute.rs b/src/cpus/m68k/execute.rs index b755f6d..2f1d414 100644 --- a/src/cpus/m68k/execute.rs +++ b/src/cpus/m68k/execute.rs @@ -31,8 +31,8 @@ impl Steppable for M68k { self.step_internal(system) } - fn on_error(&mut self, system: &System) { - self.dump_state(system); + fn on_error(&mut self, _system: &System) { + self.dump_state(); } } diff --git a/src/cpus/m68k/state.rs b/src/cpus/m68k/state.rs index 8237df7..9973876 100644 --- a/src/cpus/m68k/state.rs +++ b/src/cpus/m68k/state.rs @@ -1,5 +1,4 @@ -use crate::system::System; use crate::devices::Address; use crate::timers::CpuTimer; use crate::memory::BusPort; @@ -121,6 +120,7 @@ impl M68kState { } } +#[derive(Clone)] pub struct M68k { pub cputype: M68kType, pub frequency: u32, @@ -154,7 +154,7 @@ impl M68k { self.debugger = M68kDebugger::new(); } - pub fn dump_state(&mut self, _system: &System) { + pub fn dump_state(&mut self) { println!("Status: {:?}", self.state.status); println!("PC: {:#010x}", self.state.pc); println!("SR: {:#06x}", self.state.sr); diff --git a/src/memory.rs b/src/memory.rs index 3604b3d..b67d0ae 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -111,12 +111,14 @@ impl Transmutable for AddressAdapter { } +#[derive(Clone)] pub struct Block { pub base: Address, pub length: usize, pub dev: TransmutableBox, } +#[derive(Clone)] pub struct Bus { blocks: Vec, ignore_unmapped: bool, @@ -238,6 +240,7 @@ impl Addressable for Bus { } } +#[derive(Clone)] pub struct BusPort { offset: Address, address_mask: Address, diff --git a/src/timers.rs b/src/timers.rs index a8fb825..090d8a3 100644 --- a/src/timers.rs +++ b/src/timers.rs @@ -47,6 +47,7 @@ impl fmt::Display for AverageTimer { } +#[derive(Clone)] pub struct CpuTimer { pub decode: AverageTimer, pub execute: AverageTimer, diff --git a/tests/harte_tests/run_all.sh b/tests/harte_tests/run_all.sh new file mode 100755 index 0000000..2ed8df4 --- /dev/null +++ b/tests/harte_tests/run_all.sh @@ -0,0 +1,10 @@ +#!/bin/bash +COMMIT=$(git rev-parse HEAD) +DATE=$(date --iso) +LOCATION=$(dirname ${BASH_SOURCE[0]}) +{ + cd $LOCATION + echo "Last run on $DATE at commit $COMMIT" | tee latest.txt + echo "" | tee -a latest.txt + cargo run -- -q --testsuite "../ProcessorTests/680x0/68000/uncompressed/" | tee -a latest.txt +} diff --git a/tests/harte_tests/src/main.rs b/tests/harte_tests/src/main.rs index eeb5516..a9e5fb8 100644 --- a/tests/harte_tests/src/main.rs +++ b/tests/harte_tests/src/main.rs @@ -1,5 +1,5 @@ -const HART_TESTS: &str = "tests/ProcessorTests/680x0/68000/v1/"; +const DEFAULT_HART_TESTS: &str = "tests/ProcessorTests/680x0/68000/v1/"; use std::io::prelude::*; use std::fmt::Debug; @@ -23,6 +23,9 @@ use moa::cpus::m68k::state::Status; struct Args { /// Filter the tests by gzip file name filter: Option, + /// Only run the one test with the given number + #[clap(short, long)] + only: Option, /// Dump the CPU state when a test fails #[clap(short, long)] debug: bool, @@ -32,27 +35,14 @@ struct Args { /// Also test instruction timing #[clap(short, long)] timing: bool, -} - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -enum InfoLevel { - Quiet, - Normal, - Debug, + /// Directory to the test suite to run + #[clap(long, default_value = DEFAULT_HART_TESTS)] + testsuite: String, } fn main() { let args = Args::parse(); - - let level = if args.debug { - InfoLevel::Debug - } else if args.quiet { - InfoLevel::Quiet - } else { - InfoLevel::Normal - }; - - run_all_tests(args, level); + run_all_tests(&args); } @@ -91,6 +81,44 @@ struct TestCase { length: usize } +impl TestState { + pub fn dump(&self) { + println!("d0: {:08x} a0: {:08x}", self.d0, self.a0); + println!("d1: {:08x} a1: {:08x}", self.d1, self.a1); + println!("d2: {:08x} a2: {:08x}", self.d2, self.a2); + println!("d3: {:08x} a3: {:08x}", self.d3, self.a3); + println!("d4: {:08x} a4: {:08x}", self.d4, self.a4); + println!("d5: {:08x} a5: {:08x}", self.d5, self.a5); + println!("d6: {:08x} a6: {:08x}", self.d6, self.a6); + println!("d7: {:08x} usp: {:08x}", self.d7, self.usp); + println!("pc: {:08x} ssp: {:08x}", self.pc, self.ssp); + println!("sr: {:04x}", self.sr); + + print!("prefetch: "); + for word in self.prefetch.iter() { + print!("{:04x} ", *word); + } + println!(""); + + println!("ram: "); + for (addr, byte) in self.ram.iter() { + println!("{:08x} {:02x} ", *addr, *byte); + } + } +} + +impl TestCase { + pub fn dump(&self) { + println!("{}", self.name); + println!("initial:"); + self.initial_state.dump(); + println!("final:"); + self.final_state.dump(); + println!("cycles: {}", self.length); + } +} + + fn init_execute_test(cputype: M68kType, state: &TestState) -> Result<(M68k, System), Error> { let mut system = System::new(); @@ -205,17 +233,21 @@ fn step_cpu_and_assert(cpu: &mut M68k, system: &System, case: &TestCase, test_ti Ok(()) } -fn run_test(case: &TestCase, level: InfoLevel, test_timing: bool) -> Result<(), Error> { - let (mut cpu, system) = init_execute_test(M68kType::MC68010, &case.initial_state).unwrap(); +fn run_test(case: &TestCase, args: &Args) -> Result<(), Error> { + let (mut cpu, system) = init_execute_test(M68kType::MC68000, &case.initial_state).unwrap(); + let mut initial_cpu = cpu.clone(); - let result = step_cpu_and_assert(&mut cpu, &system, case, test_timing); + let result = step_cpu_and_assert(&mut cpu, &system, case, args.timing); match result { Ok(()) => Ok(()), Err(err) => { - if level > InfoLevel::Quiet { - if level == InfoLevel::Debug { - cpu.dump_state(&system); + if !args.quiet { + if args.debug { + case.dump(); + println!(""); + initial_cpu.dump_state(); + cpu.dump_state(); } println!("FAILED: {}", err.msg); } @@ -224,24 +256,37 @@ fn run_test(case: &TestCase, level: InfoLevel, test_timing: bool) -> Result<(), } } -fn test_json_file(path: PathBuf, level: InfoLevel, test_timing: bool) -> (usize, usize, String) { - let file = File::open(&path).unwrap(); - let mut decoder = GzDecoder::new(file); - let mut data = String::new(); - decoder.read_to_string(&mut data).unwrap(); - let cases: Vec = serde_json::from_str(&data).unwrap(); +fn test_json_file(path: PathBuf, args: &Args) -> (usize, usize, String) { + let extension = path.extension().unwrap(); + + let cases: Vec = if extension == "gz" { + let file = File::open(&path).unwrap(); + let mut decoder = GzDecoder::new(file); + let mut data = String::new(); + decoder.read_to_string(&mut data).unwrap(); + serde_json::from_str(&data).unwrap() + } else { + let data = fs::read(&path).unwrap(); + serde_json::from_slice(&data).unwrap() + }; let mut passed = 0; let mut failed = 0; for case in cases { - if level > InfoLevel::Quiet { + if let Some(only) = args.only.as_ref() { + if !case.name.ends_with(only) { + continue; + } + } + + if !args.quiet { println!("Running test {}", case.name); } - let result = run_test(&case, level, test_timing); + let result = run_test(&case, args); if let Err(err) = result { failed += 1; - if level > InfoLevel::Quiet { + if !args.quiet { println!("FAILED: {:?}", err); } } else { @@ -260,13 +305,13 @@ fn test_json_file(path: PathBuf, level: InfoLevel, test_timing: bool) -> (usize, } -fn run_all_tests(args: Args, level: InfoLevel) { +fn run_all_tests(args: &Args) { let mut passed = 0; let mut failed = 0; let mut messages = vec![]; - let mut tests: Vec = fs::read_dir(HART_TESTS) + let mut tests: Vec = fs::read_dir(&args.testsuite) .unwrap() .map(|dirent| dirent.unwrap().path()) .collect(); @@ -275,7 +320,8 @@ fn run_all_tests(args: Args, level: InfoLevel) { let start = SystemTime::now(); for path in tests { // Only test gzip files (the repo has .md files as well) - if path.extension().unwrap() != "gz" { + let extension = path.extension().unwrap(); + if extension != "json" && extension != "gz" { continue; } @@ -287,10 +333,10 @@ fn run_all_tests(args: Args, level: InfoLevel) { } // Run every test in the file - let (test_passed, test_failed, message) = test_json_file(path, level, args.timing); + let (test_passed, test_failed, message) = test_json_file(path, args); // In quiet mode, print each summary as it's received to give a progress update - if level == InfoLevel::Quiet { + if args.quiet { println!("{}", message); } @@ -301,7 +347,7 @@ fn run_all_tests(args: Args, level: InfoLevel) { let elapsed_secs = start.elapsed().unwrap().as_secs(); // Print the stored summary if not in quite mode - if level > InfoLevel::Quiet { + if !args.quiet { for message in messages { println!("{}", message); }