791 lines
21 KiB
Rust
791 lines
21 KiB
Rust
use crate::test_utils::load_compressed_result;
|
|
use gemma::chip8::computer::Chip8Computer;
|
|
use gemma::chip8::computer_manager::Chip8ComputerManager;
|
|
use gemma::chip8::cpu_states::Chip8CpuStates::WaitingForInstruction;
|
|
use gemma::chip8::delay_timer::DelayTimer;
|
|
use gemma::chip8::instructions::Chip8CpuInstructions;
|
|
use gemma::chip8::instructions::Chip8CpuInstructions::JPX;
|
|
use gemma::chip8::keypad::Keypad;
|
|
use gemma::chip8::quirk_modes::QuirkMode::{self, Chip8, SChipModern, XOChip};
|
|
use gemma::chip8::registers::Chip8Registers;
|
|
use gemma::chip8::sound_timer::SoundTimer;
|
|
use gemma::chip8::stack::Chip8Stack;
|
|
use gemma::chip8::system_memory::Chip8SystemMemory;
|
|
use gemma::chip8::util::InstructionUtil;
|
|
use gemma::chip8::video::{Chip8Video, Chip8VideoModes};
|
|
use gemma::constants::*;
|
|
use log::debug;
|
|
use rand::random;
|
|
use serde::Serialize;
|
|
use std::fs::File;
|
|
use std::io::Read;
|
|
use test_utils::read_compressed_test_result;
|
|
|
|
mod test_utils;
|
|
|
|
#[test]
|
|
fn smoke() {
|
|
assert!(true)
|
|
}
|
|
|
|
#[test]
|
|
#[ignore]
|
|
fn decoder_test_invalid_instructions() {
|
|
let invalid_to_encode = [
|
|
0x5ab1, 0x5abf, 0x8ab8, 0x8abd, 0x8abf, 0x9ab1, 0x9abf, 0xea9d, 0xea9f, 0xeaa0, 0xeaa2,
|
|
0xf006, 0xf008,
|
|
];
|
|
|
|
for i in invalid_to_encode {
|
|
assert_eq!(
|
|
Chip8CpuInstructions::decode(i, &Chip8).encode(),
|
|
XXXXERRORINSTRUCTION_ENCODED
|
|
);
|
|
assert!(matches!(
|
|
Chip8CpuInstructions::decode(i, &Chip8),
|
|
Chip8CpuInstructions::XXXXERRORINSTRUCTION
|
|
));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn format_as_string_looks_right() {
|
|
let mut x = Chip8Registers::default();
|
|
for i in 0..0x10 {
|
|
x.registers[i] = i as u8;
|
|
}
|
|
x.pc = 0xabc;
|
|
x.i_register = 0xcab;
|
|
let result_string = x.format_as_string();
|
|
assert_eq!(result_string, String::from("Vx: 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07\n 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f\nI: 0x0cab\tPC: 0x0abc"));
|
|
}
|
|
|
|
|
|
#[test]
|
|
fn stack_push_pop_test() {
|
|
let mut x = Chip8Stack::new();
|
|
|
|
// lets see if we can push and pop a bunch
|
|
x.push(&0xabcu16);
|
|
x.push(&0xcdeu16);
|
|
x.pop();
|
|
assert_eq!(x.depth(), 1);
|
|
}
|
|
|
|
#[test]
|
|
fn video_split_bytes() {
|
|
// from 0xABCD we should have AB high, CD low
|
|
let (low, high) = InstructionUtil::split_bytes(0xabcd);
|
|
assert_eq!(low, 0xAB);
|
|
assert_eq!(high, 0xCD);
|
|
}
|
|
|
|
#[test]
|
|
fn video_join_bytes() {
|
|
// from 0xAB low and 0xCD high we get 0xABCD
|
|
let merged = InstructionUtil::join_bytes(0xcd, 0xab);
|
|
assert_eq!(merged, 0xcdab);
|
|
}
|
|
|
|
#[test]
|
|
fn instruction_read_from_instruction() {
|
|
// from 0xABCD
|
|
let source = 0xABCD;
|
|
assert_eq!(InstructionUtil::read_addr_from_instruction(source), 0xBCD);
|
|
assert_eq!(InstructionUtil::read_nibble_from_instruction(source), 0xD);
|
|
assert_eq!(InstructionUtil::read_x_from_instruction(source), 0xB);
|
|
assert_eq!(InstructionUtil::read_y_from_instruction(source), 0xC);
|
|
assert_eq!(InstructionUtil::read_byte_from_instruction(source), 0xCD);
|
|
}
|
|
|
|
#[test]
|
|
fn instruction_ubln() {
|
|
// from 0xABCD we should see B
|
|
assert_eq!(InstructionUtil::read_upper_byte_lower_nibble(0xABCD), 0xB);
|
|
assert_eq!(InstructionUtil::read_upper_byte_lower_nibble(0x0123), 0x1);
|
|
assert_eq!(InstructionUtil::read_upper_byte_lower_nibble(0x0000), 0x0);
|
|
}
|
|
|
|
#[test]
|
|
fn instruction_byte_to_bool_changes() {
|
|
assert_eq!(
|
|
InstructionUtil::byte_to_bools(0b00000000),
|
|
[false, false, false, false, false, false, false, false]
|
|
);
|
|
assert_eq!(
|
|
InstructionUtil::byte_to_bools(0b11111111),
|
|
[true, true, true, true, true, true, true, true]
|
|
);
|
|
assert_eq!(
|
|
InstructionUtil::byte_to_bools(0b11001100),
|
|
[false, false, true, true, false, false, true, true]
|
|
);
|
|
assert_eq!(
|
|
InstructionUtil::byte_to_bools(0b11110000),
|
|
[false, false, false, false, true, true, true, true]
|
|
);
|
|
assert_eq!(
|
|
InstructionUtil::bools_to_byte([false, false, false, false, false, false, false, false]),
|
|
0b00000000
|
|
);
|
|
assert_eq!(
|
|
InstructionUtil::bools_to_byte([true, true, true, true, true, true, true, true]),
|
|
0b11111111
|
|
);
|
|
assert_eq!(
|
|
InstructionUtil::bools_to_byte([false, false, true, true, false, false, true, true]),
|
|
0b11001100
|
|
);
|
|
assert_eq!(
|
|
InstructionUtil::bools_to_byte([false, false, false, false, true, true, true, true]),
|
|
0b11110000
|
|
);
|
|
}
|
|
|
|
fn real_build_checkboard(in_hd: bool) -> Chip8Video {
|
|
let mut r = Chip8Video::default();
|
|
let (width, height) = if in_hd {
|
|
r.set_highres();
|
|
(SCHIP_VIDEO_WIDTH, SCHIP_VIDEO_HEIGHT)
|
|
} else {
|
|
(CHIP8_VIDEO_WIDTH, CHIP8_VIDEO_HEIGHT)
|
|
};
|
|
|
|
println!("BUILDING BOARD WITH SIZE OF {width}x{height}");
|
|
|
|
for row in 0..height {
|
|
let data_offset = row * width;
|
|
|
|
for col in 0..width {
|
|
// XOR row and column indices to alternate in a checkerboard pattern
|
|
let to_poke = (row % 2) ^ (col % 2) == 1;
|
|
let local_offset: u16 = (data_offset + col) as u16;
|
|
|
|
r.poke(local_offset, to_poke);
|
|
}
|
|
}
|
|
r
|
|
}
|
|
|
|
fn build_checkboard_hd() -> Chip8Video {
|
|
real_build_checkboard(true)
|
|
}
|
|
|
|
fn build_checkerboard() -> Chip8Video {
|
|
real_build_checkboard(false)
|
|
}
|
|
|
|
#[test]
|
|
fn video_default_test() {
|
|
let mut x = Chip8Video::default();
|
|
|
|
for i in 0..CHIP8_VIDEO_MEMORY {
|
|
assert!(!x.clone().peek(i as u16));
|
|
// then flip the value and test again.
|
|
x.poke(i as u16, true);
|
|
assert!(x.clone().peek(i as u16));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn video_set_initial_memory_sd() {
|
|
let mut x = Chip8Video::default();
|
|
// let mut initial_memory = [false; CHIP8_VIDEO_MEMORY];
|
|
let mut ws = String::new();
|
|
// set our checkerboard
|
|
for cbr in 0..32 {
|
|
for cbc in 0..64 {
|
|
let dof = cbr * 64 + cbc;
|
|
if (dof as i32 % 2) == 0 {
|
|
x.poke(dof, true);
|
|
ws += "*";
|
|
} else {
|
|
ws += " ";
|
|
}
|
|
}
|
|
ws += "\n";
|
|
}
|
|
assert_eq!(x.format_as_string(), ws);
|
|
}
|
|
|
|
#[test]
|
|
fn video_poke_byte_test() {
|
|
let to_poke = 0b11001111;
|
|
let mut x = Chip8Video::default();
|
|
x.poke_byte(0x05, to_poke);
|
|
let mut expected = String::new();
|
|
expected = " ** **** \n".to_string();
|
|
for i in 0..31 {
|
|
expected += &*(" ".repeat(64) + "\n");
|
|
}
|
|
assert_eq!(x.format_as_string(), expected);
|
|
}
|
|
|
|
#[test]
|
|
fn video_poke_2byte_test() {
|
|
let to_poke: [u8; 2] = [0b11001111, 0b00111100];
|
|
|
|
let mut x = Chip8Video::default();
|
|
x.poke_2byte(0x00, to_poke);
|
|
|
|
let mut expected = String::new();
|
|
expected = "** **** **** ".to_string() + &*" ".repeat(64 - 16).to_string() + "\n";
|
|
for i in 0..31 {
|
|
expected += &*((*" ".repeat(64)).to_string() + "\n");
|
|
}
|
|
|
|
assert_eq!(expected, x.format_as_string());
|
|
}
|
|
|
|
#[test]
|
|
fn video_poke_multirow_2_byte_sprite() {
|
|
// take 2 rows of 16bits and write them to memory
|
|
}
|
|
|
|
#[test]
|
|
fn video_cls_stddef() {
|
|
let width = 64;
|
|
let height = 32;
|
|
let initial_memory = vec![];
|
|
let mut ws = String::new();
|
|
let mut set_x = Chip8Video::new(initial_memory.into());
|
|
for cbr in 0..32 {
|
|
ws += &*" ".repeat(width);
|
|
ws += "\n";
|
|
}
|
|
set_x.cls();
|
|
|
|
assert_eq!(set_x.format_as_string(), ws);
|
|
}
|
|
|
|
#[test]
|
|
fn video_poke_byte_test_2() {
|
|
let to_poke = 0b10101010;
|
|
let mut v = Chip8Video::default();
|
|
v.poke_byte(0x00, to_poke);
|
|
assert!(v.clone().peek(0x00));
|
|
assert!(!v.clone().peek(0x01));
|
|
assert!(v.clone().peek(0x02));
|
|
assert!(!v.clone().peek(0x03));
|
|
assert!(v.clone().peek(0x04));
|
|
assert!(!v.clone().peek(0x05));
|
|
assert!(v.clone().peek(0x06));
|
|
assert!(!v.clone().peek(0x07));
|
|
for i in 0x8..CHIP8_VIDEO_MEMORY {
|
|
assert!(!v.clone().peek(i as u16));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn video_poke_multi_line_test() {
|
|
let mut v = Chip8Video::default();
|
|
let to_poke = [0b00000000, 0b11111111, 0b10101010, 0b01010101];
|
|
|
|
for (byte_in_set, byte_to_poke) in to_poke.iter().enumerate() {
|
|
let base_offset = byte_in_set * 64;
|
|
v.poke_byte(base_offset as u16, *byte_to_poke);
|
|
}
|
|
|
|
// row 2 column 1
|
|
{
|
|
assert!(v.clone().peek(0x40));
|
|
assert!(v.clone().peek(0x41));
|
|
assert!(v.clone().peek(0x42));
|
|
assert!(v.clone().peek(0x43));
|
|
assert!(v.clone().peek(0x44));
|
|
assert!(v.clone().peek(0x45));
|
|
assert!(v.clone().peek(0x46));
|
|
assert!(v.clone().peek(0x47));
|
|
|
|
// row 3 column 1
|
|
assert!(!v.clone().peek(0xC0));
|
|
assert!(v.clone().peek(0xC1));
|
|
assert!(!v.clone().peek(0xC2));
|
|
assert!(v.clone().peek(0xC3));
|
|
assert!(!v.clone().peek(0xC4));
|
|
assert!(v.clone().peek(0xC5));
|
|
assert!(!v.clone().peek(0xC6));
|
|
assert!(v.clone().peek(0xC7));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn video_moved_poke_test() {
|
|
let mut v = Chip8Video::default();
|
|
let to_poke = [0b00000000, 0b11111111, 0b10101010, 0b01010101];
|
|
|
|
let x_offset = 20;
|
|
let y_offset = 5;
|
|
|
|
for (byte_in_set, byte_to_poke) in to_poke.iter().enumerate() {
|
|
let base_offset = (x_offset + byte_in_set) * 64 + y_offset;
|
|
v.poke_byte(base_offset as u16, *byte_to_poke);
|
|
}
|
|
|
|
let test_offset = (x_offset * 64 + y_offset) as u16;
|
|
assert!(!v.clone().peek(test_offset));
|
|
assert!(!v.clone().peek(test_offset + 1));
|
|
assert!(!v.clone().peek(test_offset + 2));
|
|
assert!(!v.clone().peek(test_offset + 3));
|
|
assert!(!v.clone().peek(test_offset + 4));
|
|
assert!(!v.clone().peek(test_offset + 5));
|
|
assert!(!v.clone().peek(test_offset + 6));
|
|
assert!(!v.clone().peek(test_offset + 7));
|
|
|
|
let test_offset = test_offset + 0x40;
|
|
assert!(v.clone().peek(test_offset));
|
|
assert!(v.clone().peek(test_offset + 1));
|
|
assert!(v.clone().peek(test_offset + 2));
|
|
assert!(v.clone().peek(test_offset + 3));
|
|
assert!(v.clone().peek(test_offset + 4));
|
|
assert!(v.clone().peek(test_offset + 5));
|
|
assert!(v.clone().peek(test_offset + 6));
|
|
assert!(v.clone().peek(test_offset + 7));
|
|
}
|
|
|
|
#[test]
|
|
fn video_verify_change_registered() {
|
|
let mut v = Chip8Video::default();
|
|
v.poke(0x01, true);
|
|
v.poke(0x01, true);
|
|
assert!(v.has_frame_changed);
|
|
|
|
v.start_frame();
|
|
assert!(!v.has_frame_changed);
|
|
}
|
|
|
|
#[test]
|
|
fn video_write_checkboard() {
|
|
let v = build_checkerboard();
|
|
assert_eq!(
|
|
v.clone().format_as_string(),
|
|
load_compressed_result("test_video_write_checkerboard")
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
#[ignore]
|
|
fn video_zero_test() {
|
|
let mut x = Chip8Video::default();
|
|
|
|
for (byte_index, data_offset) in (0..=0x100).step_by(0x40).enumerate() {
|
|
x.poke_byte(data_offset as u16, CHIP8FONT_0[byte_index]);
|
|
}
|
|
|
|
assert_eq!(
|
|
load_compressed_result("test_video_zero"),
|
|
x.format_as_string()
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn video_multi_sprite_test() {
|
|
let mut x = Chip8Video::default();
|
|
// draw a row of digits 01234567
|
|
let to_draw = [
|
|
CHIP8FONT_0,
|
|
CHIP8FONT_1,
|
|
CHIP8FONT_2,
|
|
CHIP8FONT_3,
|
|
CHIP8FONT_4,
|
|
CHIP8FONT_5,
|
|
CHIP8FONT_6,
|
|
CHIP8FONT_7,
|
|
];
|
|
for (index, sprite) in to_draw.iter().enumerate() {
|
|
let data_base_offset = index * 0x8;
|
|
for (index, offset) in (0..=0x100).step_by(0x40).enumerate() {
|
|
x.poke_byte((data_base_offset + offset) as u16, sprite[index]);
|
|
}
|
|
}
|
|
|
|
assert_eq!(
|
|
load_compressed_result("test_multi_sprite"),
|
|
x.format_as_string()
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn video_reset_test() {
|
|
let mut x = build_checkerboard();
|
|
x.reset();
|
|
assert_eq!(
|
|
x.format_as_string(),
|
|
load_compressed_result("test_reset_clears_video")
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn video_collision_test() {
|
|
// Setup: Set 0xFF to 0x00 with a new frame ready
|
|
// Action: Run Poke to the same area
|
|
// Test: Verify the 'changed' flag is tripped
|
|
let mut x = Chip8Video::default();
|
|
x.poke_byte(0x00, 0xff);
|
|
x.tick();
|
|
// set the cell thats already set...
|
|
x.poke(0x00, true);
|
|
// it becomes unset and theres a frame changed
|
|
assert!(!x.peek(0x00));
|
|
|
|
assert!(x.clone().has_frame_changed);
|
|
}
|
|
|
|
#[test]
|
|
fn video_collision_test2() {
|
|
let mut x = Chip8Video::default();
|
|
x.poke_byte(0x00, 0b11110000);
|
|
assert!(x.has_frame_changed);
|
|
x.tick();
|
|
assert!(!x.has_frame_changed);
|
|
// clear the 'has changed' flag
|
|
|
|
// now set a no-collision value
|
|
x.poke_byte(0x00, 0b00001111);
|
|
assert!(x.has_frame_changed);
|
|
}
|
|
|
|
#[test]
|
|
fn video_collision_test3() {
|
|
// draw a couple sprites that do not overlap.
|
|
// goal being drawing without triggering the collision
|
|
// detection.
|
|
let mut x = Chip8Video::default();
|
|
x.poke_byte(0x00, 0b11110000);
|
|
}
|
|
|
|
#[test]
|
|
fn video_peek_out_of_bounds_doesnt_panic() {
|
|
let x = Chip8Video::default();
|
|
|
|
let y = x.clone().peek(2049);
|
|
let y = x.clone().peek(0);
|
|
|
|
// if we got here we didn't panic
|
|
assert!(true);
|
|
}
|
|
|
|
#[test]
|
|
fn video_scroll_down_1_row_test() {
|
|
let mut x = build_checkerboard();
|
|
x.scroll_down(1);
|
|
assert_eq!(
|
|
load_compressed_result("test_video_scroll_down_1"),
|
|
x.format_as_string()
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn video_scroll_down_10_row_test() {
|
|
let mut x = build_checkerboard();
|
|
x.scroll_down(10);
|
|
assert_eq!(
|
|
load_compressed_result("test_video_scroll_down_10"),
|
|
x.format_as_string()
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn video_high_res_has_right_resolution() {
|
|
let x = build_checkboard_hd();
|
|
println!("[{}]", x.format_as_string());
|
|
assert_eq!(
|
|
load_compressed_result("test_video_highdef"),
|
|
x.format_as_string()
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn video_scroll_down_1_row_test_schip() {
|
|
let mut x = build_checkboard_hd();
|
|
x.scroll_down(1);
|
|
|
|
assert_eq!(
|
|
load_compressed_result("test_scroll_down_1_hd"),
|
|
x.format_as_string()
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn video_scroll_down_10_row_test_schip() {
|
|
let mut x = build_checkboard_hd();
|
|
x.scroll_down(10);
|
|
assert_eq!(
|
|
load_compressed_result("test_scroll_down_10_hd"),
|
|
x.format_as_string()
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn video_scroll_left_4_row_test_std_def() {
|
|
let mut x = build_checkerboard();
|
|
x.scroll_left();
|
|
assert_eq!(
|
|
load_compressed_result("test_scroll_left_4"),
|
|
x.format_as_string()
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn video_scroll_left_4_row_test_high_def() {
|
|
let mut x = build_checkboard_hd();
|
|
x.scroll_left();
|
|
assert_eq!(
|
|
load_compressed_result("test_scroll_left_4_hd"),
|
|
x.format_as_string()
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn video_scroll_right_4_row_test_std_def() {
|
|
let mut x = build_checkerboard();
|
|
x.scroll_right();
|
|
assert_eq!(
|
|
load_compressed_result("test_scroll_right_4"),
|
|
x.format_as_string()
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn video_scroll_right_4_row_test_high_def() {
|
|
let mut x = build_checkboard_hd();
|
|
x.scroll_right();
|
|
assert_eq!(
|
|
load_compressed_result("test_scroll_right_4_hd"),
|
|
x.format_as_string()
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn instructions_operands_tests() {
|
|
assert_eq!(Chip8CpuInstructions::SYS(0x000).operands(), "0x0000");
|
|
assert_eq!(Chip8CpuInstructions::JPI(0x123).operands(), "0x0123");
|
|
assert_eq!(Chip8CpuInstructions::JPA(0x234).operands(), "0x0234");
|
|
assert_eq!(Chip8CpuInstructions::LDIA(0x345).operands(), "0x0345");
|
|
assert_eq!(Chip8CpuInstructions::CALL(0x456).operands(), "0x0456");
|
|
}
|
|
|
|
#[test]
|
|
fn instruction_ena_dis_tests() {
|
|
let mut x = Chip8Computer::new();
|
|
for quirk in [SChipModern, XOChip] {
|
|
x.quirk_mode = quirk;
|
|
assert!(!x.video_memory.is_highres());
|
|
Chip8CpuInstructions::HIGH.execute(&mut x);
|
|
assert!(x.video_memory.is_highres());
|
|
Chip8CpuInstructions::HIGH.execute(&mut x);
|
|
assert!(x.video_memory.is_highres());
|
|
Chip8CpuInstructions::LOW.execute(&mut x);
|
|
assert!(!x.video_memory.is_highres());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn instruction_test_scrolling_lowres() {
|
|
for quirk in [SChipModern, XOChip] {
|
|
let mut x = Chip8Computer::new();
|
|
x.video_memory = build_checkerboard();
|
|
x.quirk_mode = quirk.clone();
|
|
Chip8CpuInstructions::SCR.execute(&mut x);
|
|
assert_eq!(
|
|
load_compressed_result("test_scroll_right_4"),
|
|
x.dump_video_to_string()
|
|
);
|
|
|
|
x = Chip8Computer::new();
|
|
x.video_memory = build_checkerboard();
|
|
x.quirk_mode = quirk.clone();
|
|
Chip8CpuInstructions::SCL.execute(&mut x);
|
|
|
|
assert_eq!(
|
|
load_compressed_result("test_scroll_left_4"),
|
|
x.dump_video_to_string()
|
|
);
|
|
|
|
x = Chip8Computer::new();
|
|
x.video_memory = build_checkerboard();
|
|
x.quirk_mode = quirk.clone();
|
|
Chip8CpuInstructions::SCD(0x01).execute(&mut x);
|
|
assert_eq!(
|
|
load_compressed_result("test_video_scroll_down_1"),
|
|
x.dump_video_to_string()
|
|
);
|
|
|
|
x = Chip8Computer::new();
|
|
x.video_memory = build_checkerboard();
|
|
x.quirk_mode = quirk.clone();
|
|
Chip8CpuInstructions::SCD(0xA).execute(&mut x);
|
|
assert_eq!(
|
|
load_compressed_result("test_video_scroll_down_10"),
|
|
x.dump_video_to_string()
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn computer_dump_keypad_to_string() {
|
|
let mut x = Chip8Computer::new();
|
|
x.keypad.push_key(0x1);
|
|
x.keypad.push_key(0x2);
|
|
assert_eq!(
|
|
load_compressed_result("test_keypad_to_string"),
|
|
x.dump_keypad_to_string()
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn computer_dump_registers_to_string() {
|
|
let mut x = Chip8Computer::new();
|
|
let values_to_set = [
|
|
0x0b, 0xad, 0xbe, 0xef, 0xca, 0xb0, 0x7a, 0xc0, 0xca, 0x70, 0xba, 0xdb, 0xed, 0x00, 0x00,
|
|
0x00,
|
|
];
|
|
let expected_value = "Vx: 0x0b 0xad 0xbe 0xef 0xca 0xb0 0x7a 0xc0\n 0xca 0x70 0xba 0xdb 0xed 0x00 0x00 0x00\nI: 0x0000\tPC: 0x0200";
|
|
|
|
for i in 0..16 {
|
|
x.registers.poke(i, values_to_set[i as usize]);
|
|
}
|
|
|
|
// now verify.
|
|
assert_eq!(expected_value, x.dump_registers_to_string());
|
|
}
|
|
|
|
#[test]
|
|
fn video_scroll_up_tests_sd() {
|
|
let mut x = build_checkerboard();
|
|
let distance = 1u8;
|
|
x.scroll_up(&distance);
|
|
assert_eq!(
|
|
load_compressed_result("test_video_scroll_up_test_sd"),
|
|
x.format_as_string()
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn video_scroll_up_tests_hd() {
|
|
let mut x = build_checkboard_hd();
|
|
let distance = 1u8;
|
|
x.scroll_up(&distance);
|
|
assert_eq!(
|
|
load_compressed_result("test_video_scroll_up_test_hd"),
|
|
x.format_as_string()
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn video_resolution_changing() {
|
|
let mut x = Chip8Video::default();
|
|
|
|
x.set_highres();
|
|
assert_eq!(x.get_resolution(), (SCHIP_VIDEO_WIDTH, SCHIP_VIDEO_HEIGHT));
|
|
assert!(matches!(
|
|
x.get_screen_resolution(),
|
|
Chip8VideoModes::HighRes
|
|
));
|
|
x.set_lowres();
|
|
assert_eq!(x.get_resolution(), (CHIP8_VIDEO_WIDTH, CHIP8_VIDEO_HEIGHT));
|
|
assert!(matches!(x.get_screen_resolution(), Chip8VideoModes::LowRes));
|
|
}
|
|
|
|
#[test]
|
|
fn delay_timer_default() {
|
|
let t = DelayTimer::default();
|
|
assert_eq!(t.current(), 0xff);
|
|
}
|
|
|
|
#[test]
|
|
fn instruction_jpx() {
|
|
// JPX -> 0xBXnn
|
|
// Jump to Xnn+Vx
|
|
let mut x = Chip8Computer::new();
|
|
// set X1 to 4...
|
|
x.registers.poke(0x01, 0x04);
|
|
// ...use (x1)+0x134
|
|
let to_execute = JPX(0x01, 0x34);
|
|
// expect to set PC to 0x834
|
|
to_execute.execute(&mut x);
|
|
assert_eq!(x.registers.peek_i(), 0x834);
|
|
}
|
|
|
|
#[test]
|
|
fn instruction_ldrd() {
|
|
let mut x = Chip8Computer::new();
|
|
x.state = WaitingForInstruction;
|
|
x.delay_timer.set_timer(0x01);
|
|
Chip8CpuInstructions::LDRD(0x01).execute(&mut x);
|
|
assert_eq!(x.registers.peek(0x01), 0x01);
|
|
x.delay_timer.set_timer(0xff);
|
|
Chip8CpuInstructions::LDRD(0x0).execute(&mut x);
|
|
assert_eq!(x.registers.peek(0x00), 0xff);
|
|
x.step_system();
|
|
x.step_system();
|
|
x.step_system();
|
|
x.step_system();
|
|
Chip8CpuInstructions::LDRD(0x0).execute(&mut x);
|
|
assert_eq!(x.registers.peek(0x0), 0xfb);
|
|
}
|
|
|
|
#[test]
|
|
fn video_hires_loop_check() {
|
|
let max_address = SCHIP_VIDE_MEMORY;
|
|
let x = build_checkboard_hd();
|
|
x.peek((max_address + 1) as u16);
|
|
// if we got here we didn't explode!
|
|
assert!(true);
|
|
}
|
|
|
|
#[test]
|
|
fn system_memory_new() {
|
|
let x = Chip8SystemMemory::new();
|
|
|
|
// check its empty.
|
|
for i in 0..CHIP8_MEMORY_SIZE {
|
|
assert_eq!(x.peek(i as u16), 0x00);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
#[ignore]
|
|
fn video_lowres_schip_draw_chip8_sprite() {
|
|
let mut x = Chip8Computer::new();
|
|
x.quirk_mode = QuirkMode::SChipModern;
|
|
x.video_memory.set_lowres();
|
|
// point at the 1 from chip8
|
|
x.registers.poke_i(0x0005);
|
|
// point to 1,2 for the drawing
|
|
x.registers.poke(0x01, 0x01);
|
|
x.registers.poke(0x02, 0x02);
|
|
Chip8CpuInstructions::DRW(0x01, 0x01, 0x08).execute(&mut x);
|
|
let expected_state = read_compressed_test_result("state/video_lowres_schip_draw_chip8_sprite_result");
|
|
let actual_state = x.dump_state_to_json();
|
|
assert_eq!(expected_state, actual_state);
|
|
}
|
|
|
|
#[test]
|
|
#[ignore]
|
|
fn video_lowres_schip_draw_schip_sprite() {
|
|
let mut x = Chip8Computer::new();
|
|
x.quirk_mode = SChipModern;
|
|
x.video_memory.set_lowres();
|
|
x.registers.poke_i(0x0005);
|
|
x.registers.poke(0x01, 0x01);
|
|
x.registers.poke(0x02, 0x02);
|
|
Chip8CpuInstructions::DRW(0x01, 0x01, 0x08).execute(&mut x);
|
|
let expected_state = read_compressed_test_result("state/video_lowres_schip_draw_schip_sprite");
|
|
let actual_state = x.dump_state_to_json();
|
|
assert_eq!(expected_state, actual_state);
|
|
}
|
|
|
|
#[test]
|
|
fn video_highres_schip_draw_chip8_sprite() {}
|
|
#[test]
|
|
fn video_highres_schip_draw_schip_sprite() {}
|
|
|
|
|
|
#[test]
|
|
fn quirk_mode_labels() {
|
|
assert_eq!(format!("{}", Chip8), LABEL_QUIRK_CHIP8);
|
|
assert_eq!(format!("{}", XOChip), LABEL_QUIRK_XOCHIP);
|
|
assert_eq!(format!("{}", SChipModern), LABEL_QUIRK_SCHIP);
|
|
}
|