Break io into chirp-minifb, and refactor to use Results in more places

This commit is contained in:
2023-04-01 00:14:15 -05:00
parent 3cc3aa534c
commit 7173b9e39b
12 changed files with 486 additions and 434 deletions

View File

@@ -43,47 +43,49 @@ fn setup_environment() -> (CPU, Bus) {
}
fn print_screen(bytes: &[u8]) {
bus! {Screen [0..0x100] = bytes}.print_screen().unwrap()
bus! {Screen [0..0x100] = bytes}
.print_screen()
.expect("Printing screen should not fail if Screen exists.")
}
/// Unused instructions
mod unimplemented {
use super::*;
#[test]
#[should_panic]
fn ins_5xyn() {
let (mut cpu, mut bus) = setup_environment();
bus.write(0x200u16, 0x500fu16); // 0x500f is not an instruction
cpu.tick(&mut bus).unwrap();
bus.write(0x200u16, 0x500fu16);
cpu.tick(&mut bus)
.expect_err("0x500f is not an instruction");
}
#[test]
#[should_panic]
fn ins_8xyn() {
let (mut cpu, mut bus) = setup_environment();
bus.write(0x200u16, 0x800fu16); // 0x800f is not an instruction
cpu.tick(&mut bus).unwrap();
bus.write(0x200u16, 0x800fu16);
cpu.tick(&mut bus)
.expect_err("0x800f is not an instruction");
}
#[test]
#[should_panic]
fn ins_9xyn() {
let (mut cpu, mut bus) = setup_environment();
bus.write(0x200u16, 0x900fu16); // 0x800f is not an instruction
cpu.tick(&mut bus).unwrap();
bus.write(0x200u16, 0x900fu16);
cpu.tick(&mut bus)
.expect_err("0x900f is not an instruction");
}
#[test]
#[should_panic]
fn ins_exbb() {
let (mut cpu, mut bus) = setup_environment();
bus.write(0x200u16, 0xe00fu16); // 0xe00f is not an instruction
cpu.tick(&mut bus).unwrap();
bus.write(0x200u16, 0xe00fu16);
cpu.tick(&mut bus)
.expect_err("0xe00f is not an instruction");
}
// Fxbb
#[test]
#[should_panic]
fn ins_fxbb() {
let (mut cpu, mut bus) = setup_environment();
bus.write(0x200u16, 0xf00fu16); // 0xf00f is not an instruction
cpu.tick(&mut bus).unwrap();
bus.write(0x200u16, 0xf00fu16);
cpu.tick(&mut bus)
.expect_err("0xf00f is not an instruction");
}
}
@@ -122,6 +124,7 @@ mod sys {
///
/// Basically anything that touches the program counter
mod cf {
use super::*;
/// 1aaa: Sets the program counter to an absolute address
#[test]
@@ -253,6 +256,24 @@ mod cf {
}
}
}
/// Tests `stupid_jumps` Quirk behavior
#[test]
fn jump_stupid() {
let (mut cpu, _) = setup_environment();
cpu.flags.quirks.stupid_jumps = true;
//set v[0..F] to 0123456789abcdef
for i in 0..0x10 {
cpu.v[i] = i as u8;
}
// just WHY
for reg in 0..0x10 {
// attempts to jump to 0x`reg`00 + 0
cpu.jump_indexed(reg * 0x100);
// jumps to 0x`reg`00 + v`reg` instead
assert_eq!(cpu.pc, reg * 0x101);
}
}
}
mod math {
@@ -635,12 +656,18 @@ mod io {
bus = bus.load_region(Program, test.program);
// Run the test program for the specified number of steps
while cpu.cycle() < test.steps {
cpu.multistep(&mut bus, test.steps - cpu.cycle()).unwrap();
cpu.multistep(&mut bus, test.steps - cpu.cycle())
.expect("Draw tests should not contain undefined instructions");
}
// Compare the screen to the reference screen buffer
bus.print_screen().unwrap();
bus.print_screen()
.expect("Printing screen should not fail if screen exists");
print_screen(test.screen);
assert_eq!(bus.get_region(Screen).unwrap(), test.screen);
assert_eq!(
bus.get_region(Screen)
.expect("Getting screen should not fail if screen exists"),
test.screen
);
}
}
}
@@ -719,7 +746,8 @@ mod io {
assert_eq!(0xff, cpu.v[x]);
// There are three parts to a button press
// When the button is pressed
cpu.press(key);
assert!(cpu.press(key).expect("Key should be pressed"));
assert!(!cpu.press(key).expect("Key shouldn't be pressed again"));
assert!(cpu.flags.keypause);
assert_eq!(0xff, cpu.v[x]);
// When the button is held
@@ -727,7 +755,8 @@ mod io {
assert!(cpu.flags.keypause);
assert_eq!(0xff, cpu.v[x]);
// And when the button is released!
cpu.release(key);
assert!(cpu.release(key).expect("Key should be released"));
assert!(!cpu.release(key).expect("Key shouldn't be released again"));
assert!(!cpu.flags.keypause);
assert_eq!(Some(key), cpu.flags.lastkey);
cpu.wait_for_key(x);
@@ -825,7 +854,11 @@ mod io {
cpu.load_sprite(reg);
let addr = cpu.i as usize;
assert_eq!(bus.get(addr..addr.wrapping_add(5)).unwrap(), test.output,);
assert_eq!(
bus.get(addr..addr.wrapping_add(5))
.expect("Region at addr should exist!"),
test.output,
);
}
}
}
@@ -880,17 +913,22 @@ mod io {
const DATA: &[u8] = b"ABCDEFGHIJKLMNOP";
// Load some test data into memory
let addr = 0x456;
cpu.v.as_mut_slice().write_all(DATA).unwrap();
cpu.v
.as_mut_slice()
.write_all(DATA)
.expect("Loading test data should succeed");
for len in 0..16 {
// Perform DMA store
cpu.i = addr as u16;
cpu.store_dma(len, &mut bus);
// Check that bus grabbed the correct data
let mut bus = bus.get_mut(addr..addr + DATA.len()).unwrap();
let bus = bus
.get_mut(addr..addr + DATA.len())
.expect("Getting a mutable slice at addr 0x0456 should not fail");
assert_eq!(bus[0..=len], DATA[0..=len]);
assert_eq!(bus[len + 1..], [0; 16][len + 1..]);
// clear
bus.write_all(&[0; 16]).unwrap();
bus.fill(0);
}
}
@@ -903,7 +941,7 @@ mod io {
// Load some test data into memory
let addr = 0x456;
bus.get_mut(addr..addr + DATA.len())
.unwrap()
.expect("Getting a mutable slice at addr 0x0456..0x0466 should not fail")
.write_all(DATA)
.unwrap();
for len in 0..16 {
@@ -914,13 +952,14 @@ mod io {
assert_eq!(cpu.v[0..=len], DATA[0..=len]);
assert_eq!(cpu.v[len + 1..], [0; 16][len + 1..]);
// clear
cpu.v = [0; 16];
cpu.v.fill(0);
}
}
}
mod behavior {
use super::*;
mod realtime {
use super::*;
use std::time::Duration;
@@ -930,7 +969,8 @@ mod behavior {
cpu.flags.monotonic = None;
cpu.delay = 10.0;
for _ in 0..2 {
cpu.multistep(&mut bus, 8).unwrap();
cpu.multistep(&mut bus, 8)
.expect("Running valid instructions should always succeed");
std::thread::sleep(Duration::from_secs_f64(1.0 / 60.0));
}
// time is within 1 frame deviance over a theoretical 2 frame pause
@@ -942,7 +982,8 @@ mod behavior {
cpu.flags.monotonic = None; // disable monotonic timing
cpu.sound = 10.0;
for _ in 0..2 {
cpu.multistep(&mut bus, 8).unwrap();
cpu.multistep(&mut bus, 8)
.expect("Running valid instructions should always succeed");
std::thread::sleep(Duration::from_secs_f64(1.0 / 60.0));
}
// time is within 1 frame deviance over a theoretical 2 frame pause
@@ -952,13 +993,14 @@ mod behavior {
fn vbi_wait() {
let (mut cpu, mut bus) = setup_environment();
cpu.flags.monotonic = None; // disable monotonic timing
cpu.flags.vbi_wait = true;
cpu.flags.draw_wait = true;
for _ in 0..2 {
cpu.multistep(&mut bus, 8).unwrap();
cpu.multistep(&mut bus, 8)
.expect("Running valid instructions should always succeed");
std::thread::sleep(Duration::from_secs_f64(1.0 / 60.0));
}
// Display wait is disabled after a 1 frame pause
assert!(!cpu.flags.vbi_wait);
assert!(!cpu.flags.draw_wait);
}
}
mod breakpoint {
@@ -967,7 +1009,8 @@ mod behavior {
fn hit_break() {
let (mut cpu, mut bus) = setup_environment();
cpu.set_break(0x202);
cpu.multistep(&mut bus, 10).unwrap();
cpu.multistep(&mut bus, 10)
.expect("Running valid instructions should always succeed");
assert!(cpu.flags.pause);
assert_eq!(0x202, cpu.pc);
}