adds more test roms

adds list of test roms and ability to load as desired
passes the Corax+ opcode test now
This commit is contained in:
Trevor Merritt 2024-10-09 09:44:51 -04:00
parent 6fe644ef46
commit 067164c657
12 changed files with 273 additions and 237 deletions

View File

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="EMPTY_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/chip8_core/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/chip8_toy/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/emma/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

File diff suppressed because one or more lines are too long

View File

@ -1,3 +1,6 @@
use std::default::Default;
use std::fs::DirEntry;
use std::time::Instant;
use emmaemu::{ use emmaemu::{
chip8::computer::Chip8Computer, chip8::computer::Chip8Computer,
constants::{CHIP8_MEMORY_SIZE, CHIP8_VIDEO_HEIGHT, CHIP8_VIDEO_WIDTH}, constants::{CHIP8_MEMORY_SIZE, CHIP8_VIDEO_HEIGHT, CHIP8_VIDEO_WIDTH},
@ -11,53 +14,61 @@ use support::emmagui_support::EmmaGui;
mod support; mod support;
fn hello_world_window(ui: &Ui) {
let mut value = 1;
let choices = ["test test this is 1", "test test this is 2"];
ui.window("EmmaGui")
.size([300.0, 110.0], Condition::FirstUseEver)
.build(|| {
ui.text_wrapped("Hello world!");
ui.text_wrapped("こんにちは世界!");
if ui.button(choices[value]) {
value += 1;
value %= 2;
}
ui.button("This...is...imgui-rs!");
ui.separator();
let mouse_pos = ui.io().mouse_pos;
ui.text(format!(
"Mouse Position: ({:.1},{:.1})",
mouse_pos[0], mouse_pos[1]
));
});
}
#[derive(Copy, Clone)]
struct UiState { struct UiState {
pub show_registers: bool, pub show_registers: bool,
pub show_memory: bool, pub show_memory: bool,
pub show_video: bool pub show_video: bool,
pub filename_to_load: String,
pub on_colour: ImColor32,
pub off_colour: ImColor32,
pub is_running: bool,
pub frame_time: f32
}
impl Clone for UiState {
fn clone(&self) -> Self {
UiState {
show_registers: self.show_registers,
show_memory: self.show_memory,
show_video: self.show_video,
filename_to_load: self.filename_to_load.to_string(),
on_colour: self.on_colour,
off_colour: self.off_colour,
is_running: self.is_running,
frame_time: self.frame_time
}
}
} }
impl Default for UiState { impl Default for UiState {
fn default() -> Self { fn default() -> Self {
UiState { UiState {
show_registers: true, show_registers: false,
show_memory: true, show_memory: false,
show_video: true show_video: true,
filename_to_load: String::new(),
on_colour: ImColor32::from_rgb(0xff, 0xff, 0x00),
off_colour: ImColor32::from_rgb(0x00, 0xff, 0xff),
is_running: false,
frame_time: 32.0
} }
} }
} }
fn main() { fn main() {
pretty_env_logger::init(); pretty_env_logger::init();
let mut system = Chip8Computer::default(); let mut system = Chip8Computer::default();
let mut ui_state = UiState::default(); let mut ui_state = UiState::default();
let mut last_tick_time = Instant::now();
support::simple_init(file!(), move |_, ui| { support::simple_init(file!(), move |_, ui| {
let current_time = Instant::now();
let time_since_last_tick = current_time.duration_since(last_tick_time).as_millis();
if ui_state.is_running && time_since_last_tick > ui_state.frame_time as u128 {
system.step_system();
last_tick_time = current_time;
}
EmmaGui::system_controls(&mut system, &mut ui_state, ui); EmmaGui::system_controls(&mut system, &mut ui_state, ui);
if ui_state.show_registers { if ui_state.show_registers {
@ -65,16 +76,12 @@ fn main() {
} }
if ui_state.show_video { if ui_state.show_video {
EmmaGui::video_display(&system, ui); EmmaGui::video_display(&system, &ui_state, ui);
} }
if ui_state.show_memory { if ui_state.show_memory {
let active_instruction = system.registers.peek_pc(); let active_instruction = system.registers.peek_pc();
EmmaGui::hex_memory_display(system.memory.clone(), (0x100, 0x10), active_instruction as i16, ui); EmmaGui::hex_memory_display(system.memory.clone(), (0x100, 0x10), active_instruction as i16, ui);
} }
// EmmaGui::system_memory_render(system.memory, ui);
// system.memory.gui_render(ui);
}); });
} }

View File

@ -1,29 +1,51 @@
use std::fs::File; use std::fs::File;
use std::io::Read; use std::io::Read;
use std::path::Path; use std::path::{Path, PathBuf};
use std::thread::sleep; use std::thread::sleep;
use std::time::Duration; use std::time::Duration;
use imgui::{Condition, ImColor32, Ui}; use imgui::{Condition, ImColor32, Ui};
use log::debug;
use emmaemu::chip8::computer::Chip8Computer; use emmaemu::chip8::computer::Chip8Computer;
use emmaemu::chip8::system_memory::Chip8SystemMemory; use emmaemu::chip8::system_memory::Chip8SystemMemory;
use emmaemu::constants::{CHIP8_VIDEO_HEIGHT, CHIP8_VIDEO_WIDTH}; use emmaemu::constants::{CHIP8_VIDEO_HEIGHT, CHIP8_VIDEO_WIDTH};
use crate::UiState; use crate::UiState;
pub struct EmmaGui { pub struct EmmaGui {}
}
const CELL_WIDTH: i32 = 5i32; const CELL_WIDTH: i32 = 5i32;
const CELL_HEIGHT: i32 = 5i32; const CELL_HEIGHT: i32 = 5i32;
struct GuiFileList {}
impl GuiFileList {
pub fn display_path(root: PathBuf, selected_filename: &String, ui: &Ui) -> String {
let mut working_filename = selected_filename.clone();
ui.text(format!("Displaying {}", root.to_str().unwrap_or("Unable to parse path")));
for (index, entry) in std::fs::read_dir(root.as_path()).unwrap().enumerate() {
let filename = entry.unwrap().file_name();
let filename = filename.to_str().unwrap();
let mut working_select = ui.selectable_config(format!("{}", filename));
if filename == selected_filename {
working_select = working_select.selected(true);
}
if working_select.build() {
debug!("SELECTED {index} / {filename}");
working_filename = filename.to_string();
};
}
working_filename
}
}
impl EmmaGui { impl EmmaGui {
pub fn video_display(system_to_control: &Chip8Computer, ui: &Ui) { pub fn video_display(system_to_control: &Chip8Computer, gui_state: &UiState, ui: &Ui) {
// draw area size // draw area size
let draw_area_size = ui.io().display_size; let draw_area_size = ui.io().display_size;
let cell_width = ((draw_area_size[0] as i32 / 64) * 6) / 10; let cell_width = ((draw_area_size[0] as i32 / 64) * 6) / 10;
let cell_height = ((draw_area_size[1] as i32 / 32) * 6) / 10; let cell_height = ((draw_area_size[1] as i32 / 32) * 6) / 10;
ui.window(format!("Display {cell_width}x{cell_height}")) ui.window(format!("Display {cell_width}x{cell_height}"))
.size([300.0, 300.0], Condition::FirstUseEver) .size([300.0, 300.0], Condition::FirstUseEver)
.build(|| { .build(|| {
@ -32,58 +54,77 @@ impl EmmaGui {
ui.text("This is the video display."); ui.text("This is the video display.");
for current_row in 0..=31 { for current_row in 0..=31 {
for current_column in 0..=63 { for current_column in 0..=63 {
let x_offset = origin[0] as i32 + (current_column * cell_width); let x_offset = origin[0] as i32 + (current_column * cell_width);
let y_offset = origin[1] as i32 + (current_row * cell_height); let y_offset = origin[1] as i32 + (current_row * cell_height);
let current_origin = [x_offset as f32, y_offset as f32]; let current_origin = [x_offset as f32, y_offset as f32];
let current_limit = [(x_offset + cell_width) as f32, (y_offset + cell_height) as f32]; let current_limit = [(x_offset + cell_width) as f32, (y_offset + cell_height) as f32];
let memory_offset = (current_row * 64 + current_column) as u16; let memory_offset = (current_row * 64 + current_column) as u16;
let to_render = system_to_control.video_memory.peek(memory_offset); let to_render = system_to_control.video_memory.peek(memory_offset);
let color: ImColor32 = if to_render { let color: ImColor32 = if to_render {
ImColor32::from_rgb(0xff, 0x00, 0x00) gui_state.on_colour
} else { } else {
ImColor32::from_rgb(0x00, 0x00, 0xff) gui_state.off_colour
}; };
fg.add_rect_filled_multicolor(current_origin, current_limit, color, color, color, color); fg.add_rect_filled_multicolor(current_origin, current_limit, color, color, color, color);
} }
} }
}); });
} }
pub fn system_controls(system_to_control: &mut Chip8Computer, gui_state: &mut UiState, ui: &Ui) { pub fn system_controls(system_to_control: &mut Chip8Computer, gui_state: &mut UiState, ui: &Ui) {
ui.window("!!!! CONTROLS !!!!") ui.window("!!!! CONTROLS !!!!")
.size([200.0, 200.0], Condition::FirstUseEver) .size([345.0, 200.0], Condition::FirstUseEver)
.build(|| { .build(|| {
ui.text("Computer Controls"); /* ROM Lister */
let new_filename = GuiFileList::display_path(PathBuf::from("resources/roms"), &gui_state.filename_to_load, ui);
if !new_filename.is_empty() {
if new_filename != gui_state.filename_to_load {
println!("NEW FILENAME SELECTED -> {new_filename}");
gui_state.filename_to_load = new_filename;
}
if ui.button("Load Program") {
let mut buffer = Vec::new();
println!("PREPARING TO LOAD {}", gui_state.filename_to_load);
// let mut input_file = File::open(Path::new("./1-chip8-logo.ch8")).expect("put 1-chip8-logo.ch8 in this directory");
let mut input_file = File::open(Path::new(&("resources/roms/".to_string() + &gui_state.filename_to_load))).expect("put 1-chip8-logo.ch8 in this directory");
input_file.read_to_end(&mut buffer).expect("unable to read file");
system_to_control.load_bytes_to_memory(0x200, buffer.into());
}
}
ui.separator();
if ui.button("Step") { if ui.button("Step") {
system_to_control.step_system(); system_to_control.step_system();
}; };
if ui.button("Load Program") { if ui.button("Run") {
let mut buffer = Vec::new(); gui_state.is_running = true;
println!("STARTING THE SYSTEM");
println!("PREPARING TO LOAD 1-chip8-logo.ch8");
// let mut input_file = File::open(Path::new("./1-chip8-logo.ch8")).expect("put 1-chip8-logo.ch8 in this directory");
let mut input_file = File::open(Path::new("./coraxhard.ch8")).expect("put 1-chip8-logo.ch8 in this directory");
input_file.read_to_end(&mut buffer).expect("unable to read file");
system_to_control.load_bytes_to_memory(0x200, buffer.into());
} }
if ui.button("Stop") {
gui_state.is_running = false;
println!("STOPPING THE SYSTEM");
}
ui.same_line();
if ui.button("Reset") { if ui.button("Reset") {
*system_to_control = Chip8Computer::new(); *system_to_control = Chip8Computer::new();
} }
if ui.button("Dump Video Memory") { if ui.button("Dump Video Memory") {
println!("{}", system_to_control.dump_video_to_string()); println!("{}", system_to_control.dump_video_to_string());
} }
ui.same_line();
if ui.button("Dump Keypad State") { if ui.button("Dump Keypad State") {
println!("{}", system_to_control.dump_keypad_to_string()); println!("{}", system_to_control.dump_keypad_to_string());
} }
ui.same_line();
if ui.button("Dump Registers") { if ui.button("Dump Registers") {
println!("{}", system_to_control.dump_registers_to_string()); println!("{}", system_to_control.dump_registers_to_string());
} }
ui.separator();
ui.checkbox("Show Memory", &mut gui_state.show_memory); ui.checkbox("Show Memory", &mut gui_state.show_memory);
ui.same_line();
ui.checkbox("Show Video", &mut gui_state.show_video); ui.checkbox("Show Video", &mut gui_state.show_video);
ui.same_line();
ui.checkbox("Show Registers", &mut gui_state.show_registers); ui.checkbox("Show Registers", &mut gui_state.show_registers);
}); });
} }
@ -114,6 +155,7 @@ impl EmmaGui {
let cols = position.1; let cols = position.1;
ui.window("System Memory") ui.window("System Memory")
.size([400.0, 300.0], Condition::FirstUseEver) .size([400.0, 300.0], Condition::FirstUseEver)
.position([500.0, 300.0], Condition::Always)
.build(|| { .build(|| {
let mut current_x_hover: i32 = 0; let mut current_x_hover: i32 = 0;
let mut current_y_hover: i32 = 0; let mut current_y_hover: i32 = 0;

View File

@ -496,24 +496,38 @@ impl Chip8CpuInstructions {
} }
// 0x8xy1 Set Vx = Vx OR Vy // 0x8xy1 Set Vx = Vx OR Vy
Chip8CpuInstructions::OrVxVy(x, y) => { Chip8CpuInstructions::OrVxVy(x, y) => {
input.registers.poke(*x as u8, input.registers.peek(*x as u8) | input.registers.peek(*y as u8)); // shift them to 16 bit
let working_16_x = input.registers.peek(*x as u8) as u16;
let working_16_y = input.registers.peek(*y as u8) as u16;
// OR them
let working_16_or = working_16_x | working_16_y;
// shift them back to 8 bit.
let to_set = working_16_or as u8;
input.registers.poke(*x as u8, to_set);
debug!("OrVxVy [0x{x:1x}] [0x{y:1x}]") debug!("OrVxVy [0x{x:1x}] [0x{y:1x}]")
} }
// 0x8xy2 Set Vx = Vx AND Vy // 0x8xy2 Set Vx = Vx AND Vy
Chip8CpuInstructions::AndVxVy(x, y) => { Chip8CpuInstructions::AndVxVy(x, y) => {
input.registers.poke(*x as u8, input.registers.peek(*x as u8) & input.registers.peek(*y as u8)); let lhs_16 = input.registers.peek(*x as u8) as u16;
debug!("AndVxVy [0x{x:1x}] [0x{y:1x}]"); let rhs_16 = input.registers.peek(*y as u8) as u16;
let and_16 = lhs_16 & rhs_16;
input.registers.poke(*x as u8, and_16 as u8);
debug!("AndVxVy [0x{x:02x}] [0x{y:02x}] [0b{and_16:08b}");
} }
// 0x8xy3 Set Vx = Vx XOR Vy // 0x8xy3 Set Vx = Vx XOR Vy
Chip8CpuInstructions::XorVxVy(x, y) => { Chip8CpuInstructions::XorVxVy(x, y) => {
input.registers.poke(*x as u8, input.registers.peek(*x as u8) ^ input.registers.peek(*y as u8)); let lhs_16 = input.registers.peek(*x as u8) as u16;
let rhs_16 = input.registers.peek(*y as u8) as u16;
let xor_16 = lhs_16 ^ rhs_16;
input.registers.poke(*x as u8, xor_16 as u8);
} }
// 0x8xy4 Set Vx = Vx + Vy (SET VF on Carry) // 0x8xy4 Set Vx = Vx + Vy (SET VF on Carry)
Chip8CpuInstructions::AddVxVy(x, y) => { Chip8CpuInstructions::AddVxVy(x, y) => {
let lhs = input.registers.peek(*x as u8); let lhs = input.registers.peek(*x as u8) as i16;
let rhs = input.registers.peek(*y as u8); let rhs = input.registers.peek(*y as u8) as i16;
let working = (lhs as i16 + rhs as i16) as i16; let working = lhs + rhs;
if working > 255 { if working > 0xff {
input.registers.poke(0xf, 0x01); input.registers.poke(0xf, 0x01);
} }
input.registers.poke(*x as u8, working as u8); input.registers.poke(*x as u8, working as u8);
@ -759,11 +773,14 @@ impl Chip8CpuInstructions {
Chip8CpuInstructions::LdIVx(x) => { Chip8CpuInstructions::LdIVx(x) => {
// Store registers V0 through Vx in memory starting at location I. // Store registers V0 through Vx in memory starting at location I.
// //
// The interpreter copies the values of registers V0 through Vx into memory, starting at the address in I. // The interpreter copies the values of registers V0 through Vx into memory,
// starting at the address in I.
let num_loops = x;
let offset = input.registers.peek_i(); let offset = input.registers.peek_i();
for i in 0..*x { for i in 0..=*x {
input.memory.poke(offset + i, input.registers.peek(i as u8)); input.memory.poke(offset + i, input.registers.peek(i as u8));
} }
input.registers.poke_i(offset + 1);
} }
Chip8CpuInstructions::LdVxI(x) => { Chip8CpuInstructions::LdVxI(x) => {
// Read registers V0 through Vx from memory starting at location I. // Read registers V0 through Vx from memory starting at location I.
@ -771,14 +788,14 @@ impl Chip8CpuInstructions {
// The interpreter reads values from memory starting at location I into registers V0 through Vx. // The interpreter reads values from memory starting at location I into registers V0 through Vx.
let offset = input.registers.peek_i(); let offset = input.registers.peek_i();
debug!("STARTING TO READ AT {offset:03x}"); debug!("STARTING TO READ AT {offset:03x}");
let num_loops = input.registers.peek(*x as u8); let num_loops = x + 1;
debug!("WILL READ {num_loops:x} BYTES"); println!("WILL READ {num_loops:x} BYTES");
for index in 0..num_loops { for index in 0..num_loops {
let src_value = input.memory.peek(index as u16 + offset); let src_value = input.memory.peek(index as u16 + offset);
input.registers.poke(index as u8, src_value);
input.registers.poke(index, src_value); println!("POKING Register 0x{index:02x} with 0x{src_value:04x} using offset 0x{offset:04x}");
debug!("POKING {index} with {src_value}");
} }
input.registers.poke_i(offset + 1);
} }
Chip8CpuInstructions::XXXXERRORINSTRUCTION => {} Chip8CpuInstructions::XXXXERRORINSTRUCTION => {}
}; };
@ -896,8 +913,12 @@ mod test {
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
Chip8CpuInstructions::SysAddr(0).execute(&mut x); Chip8CpuInstructions::SysAddr(0).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0); assert_eq!(x.registers.peek_pc(), 0);
let mut x = Chip8Computer::new();
Chip8CpuInstructions::SysAddr(0xFA0).execute(&mut x); Chip8CpuInstructions::SysAddr(0xFA0).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0xFA0); assert_eq!(x.registers.peek_pc(), 0xFA0);
let mut x = Chip8Computer::new();
Chip8CpuInstructions::SysAddr(0x0AF).execute(&mut x); Chip8CpuInstructions::SysAddr(0x0AF).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0x0AF); assert_eq!(x.registers.peek_pc(), 0x0AF);
} }
@ -908,6 +929,8 @@ mod test {
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
Chip8CpuInstructions::JpAddr(0).execute(&mut x); Chip8CpuInstructions::JpAddr(0).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0); assert_eq!(x.registers.peek_pc(), 0);
let mut x = Chip8Computer::new();
Chip8CpuInstructions::JpAddr(0xABC).execute(&mut x); Chip8CpuInstructions::JpAddr(0xABC).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0xABC); assert_eq!(x.registers.peek_pc(), 0xABC);
} }
@ -919,10 +942,10 @@ mod test {
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(1, 0x12).execute(&mut x); Chip8CpuInstructions::LdVxByte(1, 0x12).execute(&mut x);
assert_eq!(x.registers.peek(1), 0x12); assert_eq!(x.registers.peek(1), 0x12);
assert_eq!(x.registers.peek_pc(), 0x202);
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(2, 0x21).execute(&mut x); Chip8CpuInstructions::LdVxByte(2, 0x21).execute(&mut x);
assert_eq!(x.registers.peek(2), 0x21); assert_eq!(x.registers.peek(2), 0x21);
assert_eq!(x.registers.peek_pc(), 0x204);
} }
#[test] #[test]
@ -933,69 +956,41 @@ mod test {
// test setup: Load value 0x84 into V1 // test setup: Load value 0x84 into V1
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(1, 0x84).execute(&mut x); x.registers.poke(0x1, 0x84);
Chip8CpuInstructions::SeVxByte(1, 0x84).execute(&mut x); Chip8CpuInstructions::SeVxByte(1, 0x48).execute(&mut x);
// we should be 6 instructions past.
// 2 for the LDVXBYTE
// 2 for the SEVXBYTE
// 2 for skipping.
assert_eq!(x.registers.peek_pc(), 0x206);
}
#[test]
fn sevxbyte_nomatch_test() {
// 0x3xkk Skip next instruction if Vx = kk.
// The interpreter compares register Vx to kk,
// and if they are equal, increments the program counter by 2.
// test setup: Load value 0x84 into V1
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0x01, 0x84).execute(&mut x);
// PC will be 2 bytes past as we executed an instruction...
assert_eq!(x.registers.peek_pc(), 0x202); assert_eq!(x.registers.peek_pc(), 0x202);
Chip8CpuInstructions::SeVxByte(1, 0x48).execute(&mut x); let mut x = Chip8Computer::new();
// we should be 2 instructions past. x.registers.poke(0x1, 0x84);
// 2 for what we executed
assert_eq!(x.registers.peek_pc(), 0x204);
Chip8CpuInstructions::SeVxByte(1, 0x84).execute(&mut x); Chip8CpuInstructions::SeVxByte(1, 0x84).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0x208); assert_eq!(x.registers.peek_pc(), 0x204);
} }
#[test] #[test]
fn se_vx_vy_test() { fn se_vx_vy_test() {
// 0x4xkk Skip next instruction if Vx != kk // 0x4xkk Skip next instruction if Vx != kk
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0x01, 0x84).execute(&mut x); x.registers.poke(0x01, 0x84);
Chip8CpuInstructions::LdVxByte(0x02, 0x84).execute(&mut x); x.registers.poke(0x2, 0x84);
assert_eq!(x.registers.peek_pc(), 0x204);
// skip, compare 0x84 to 0x84 // skip, compare 0x84 to 0x84
Chip8CpuInstructions::SeVxVy(0x1, 0x2).execute(&mut x); Chip8CpuInstructions::SeVxVy(0x1, 0x2).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0x208); assert_eq!(x.registers.peek_pc(), 0x204);
// load 0x48 (not matching) into V2 let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0x2, 0x48).execute(&mut x); x.registers.poke(0x01, 0x84);
// verify its there. x.registers.poke(0x2, 0x48);
assert_eq!(x.registers.peek(2), 0x48);
// no skip, compare 0x84 and 0x48
Chip8CpuInstructions::SeVxVy(0x01, 0x02).execute(&mut x); Chip8CpuInstructions::SeVxVy(0x01, 0x02).execute(&mut x);
assert_eq!(x.registers.peek(2), 0x48); assert_eq!(x.registers.peek_pc(), 0x202);
assert_eq!(x.registers.peek_pc(), 0x20C);
} }
#[test] #[test]
fn ld_vx_vy_test() { fn ld_vx_vy_test() {
// 0x8xy0 Set value of Vy in Vx // 0x8xy0 Set value of Vy in Vx
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0x01, 0x01).execute(&mut x); x.registers.poke(0x01, 0x01);
Chip8CpuInstructions::LdVxByte(0x02, 0x02).execute(&mut x); x.registers.poke(0x02, 0x02);
assert_eq!(x.registers.peek_pc(), 0x204);
assert_eq!(x.registers.peek(1), 0x01);
Chip8CpuInstructions::LdVxVy(0x01, 0x02).execute(&mut x); Chip8CpuInstructions::LdVxVy(0x01, 0x02).execute(&mut x);
assert_eq!(x.registers.peek(1), 0x02); assert_eq!(x.registers.peek(1), 0x02);
assert_eq!(x.registers.peek_pc(), 0x206);
} }
#[test] #[test]
@ -1005,11 +1000,10 @@ mod test {
// | 0b0000 1010 (0x0A) // | 0b0000 1010 (0x0A)
// 0b0101 1010 (0x5A) // 0b0101 1010 (0x5A)
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(1, 0x50).execute(&mut x); x.registers.poke(0x01, 0b01010000);
Chip8CpuInstructions::LdVxByte(2, 0x0A).execute(&mut x); x.registers.poke(0x02, 0b00001010);
Chip8CpuInstructions::OrVxVy(1, 2).execute(&mut x); Chip8CpuInstructions::OrVxVy(1, 2).execute(&mut x);
assert_eq!(x.registers.peek(1), 0x5A); assert_eq!(x.registers.peek(1), 0b01011010);
assert_eq!(x.registers.peek_pc(), 0x206);
} }
#[test] #[test]
@ -1019,11 +1013,10 @@ mod test {
// & 0b1100 1010 (0xCA) // & 0b1100 1010 (0xCA)
// 0b1100 1000 (0xC8) // 0b1100 1000 (0xC8)
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(1, 0xFC).execute(&mut x); x.registers.poke(0x01, 0xFC);
Chip8CpuInstructions::LdVxByte(2, 0xCA).execute(&mut x); x.registers.poke(0x02, 0xCA);
Chip8CpuInstructions::AndVxVy(1, 2).execute(&mut x); Chip8CpuInstructions::AndVxVy(1, 2).execute(&mut x);
assert_eq!(x.registers.peek(1), 0xC8); assert_eq!(x.registers.peek(1), 0xC8);
assert_eq!(x.registers.peek_pc(), 0x206);
} }
#[test] #[test]
@ -1033,11 +1026,10 @@ mod test {
// ^ 0b1100 1010 (0xCA) // ^ 0b1100 1010 (0xCA)
// 0b0011 0110 (0x36) // 0b0011 0110 (0x36)
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(1, 0xFC).execute(&mut x); x.registers.poke(0x01, 0b11111100);
Chip8CpuInstructions::LdVxByte(2, 0xCA).execute(&mut x); x.registers.poke(0x02, 0b11001010);
Chip8CpuInstructions::XorVxVy(1, 2).execute(&mut x); Chip8CpuInstructions::XorVxVy(1, 2).execute(&mut x);
assert_eq!(x.registers.peek(1), 0x36); assert_eq!(x.registers.peek(1), 0b00110110);
assert_eq!(x.registers.peek_pc(), 0x206);
} }
#[test] #[test]
@ -1048,24 +1040,20 @@ mod test {
// + 0x01 0x01 // + 0x01 0x01
// 0x02 F0 0x00 F1 // 0x02 F0 0x00 F1
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0xf, 0x00).execute(&mut x); x.registers.poke(0x0f, 00);
Chip8CpuInstructions::LdVxByte(1, 0x01).execute(&mut x); x.registers.poke(0x01, 0x01);
Chip8CpuInstructions::LdVxByte(2, 0x01).execute(&mut x); x.registers.poke(0x02, 0x01);
Chip8CpuInstructions::AddVxVy(1, 2).execute(&mut x); Chip8CpuInstructions::AddVxVy(0x01, 0x02).execute(&mut x);
// T1 assert_eq!(x.registers.peek(0xf), 0x00);
assert_eq!(x.registers.peek(0xf), 0); assert_eq!(x.registers.peek(0x01), 0x02);
assert_eq!(x.registers.peek(1), 2);
assert_eq!(x.registers.peek_pc(), 0x208);
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0xf, 0x00).execute(&mut x); x.registers.poke(0x0f, 0x00);
Chip8CpuInstructions::LdVxByte(0x1, 0xff).execute(&mut x); x.registers.poke(0x01, 0xff);
Chip8CpuInstructions::LdVxByte(0x2, 0x01).execute(&mut x); x.registers.poke(0x02, 0x01);
Chip8CpuInstructions::AddVxVy(1, 2).execute(&mut x); Chip8CpuInstructions::AddVxVy(1, 2).execute(&mut x);
// T2
assert_eq!(x.registers.peek(0xf), 1); assert_eq!(x.registers.peek(0xf), 1);
assert_eq!(x.registers.peek(1), 0); assert_eq!(x.registers.peek(1), 0);
assert_eq!(x.registers.peek_pc(), 0x208)
} }
#[test] #[test]
@ -1076,21 +1064,19 @@ mod test {
If the least-significant bit of Vx is 1, then VF is set to 1, otherwise 0. Then Vx is divided by 2. If the least-significant bit of Vx is 1, then VF is set to 1, otherwise 0. Then Vx is divided by 2.
*/ */
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0xf, 0x00).execute(&mut x); x.registers.poke(0x0f, 0x00);
Chip8CpuInstructions::LdVxByte(0x1, 0x08).execute(&mut x); // 0b0000 1000 (0x08) x.registers.poke(0x01, 0b00001000);
Chip8CpuInstructions::LdVxByte(0x2, 0x2).execute(&mut x); x.registers.poke(0x02, 0b00000000);
Chip8CpuInstructions::ShrVxVy(0x1, 0x2).execute(&mut x); // 0b0000 0010 (0x02) (Not Set) Chip8CpuInstructions::ShrVxVy(0x1, 0x2).execute(&mut x); // 0b0000 0010 (0x02) (Not Set)
assert_eq!(x.registers.peek(1), 0x04); assert_eq!(x.registers.peek(1), 0b00000100);
assert_eq!(x.registers.peek(0xf), 0); assert_eq!(x.registers.peek(0xf), 0);
assert_eq!(x.registers.peek_pc(), 0x208);
x = Chip8Computer::new(); x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0xf, 0x00).execute(&mut x); x.registers.poke(0x0f, 0x00);
Chip8CpuInstructions::LdVxByte(0x1, 0b00001001).execute(&mut x); // 0b0000 1001 (0x09) x.registers.poke(0x01, 0b00001001);
Chip8CpuInstructions::ShrVxVy(0x1, 0x2).execute(&mut x); // 0b0000 0010 (0x02) (Set) Chip8CpuInstructions::ShrVxVy(0x1, 0x2).execute(&mut x);
assert_eq!(x.registers.peek(1), 0x04); assert_eq!(x.registers.peek(1), 0b00000100);
assert_eq!(x.registers.peek(0xf), 1); assert_eq!(x.registers.peek(0xf), 1);
assert_eq!(x.registers.peek_pc(), 0x206);
} }
#[test] #[test]
@ -1105,7 +1091,7 @@ mod test {
fn jp_v0addr_test() { fn jp_v0addr_test() {
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
/// jump to I + nnn /// jump to I + nnn
Chip8CpuInstructions::LdVxByte(0x0, 0xFF).execute(&mut x); x.registers.poke(0x0, 0xff);
Chip8CpuInstructions::JpV0Addr(0x100).execute(&mut x); Chip8CpuInstructions::JpV0Addr(0x100).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0x1FF); assert_eq!(x.registers.peek_pc(), 0x1FF);
} }
@ -1120,22 +1106,21 @@ mod test {
#[test] #[test]
fn skip_next_instruction_ne_text() { fn skip_next_instruction_ne_text() {
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0x1, 0xf0).execute(&mut x); x.registers.poke(0x1, 0xf0);
// 202
Chip8CpuInstructions::SneVxByte(0x1, 0x0f).execute(&mut x); Chip8CpuInstructions::SneVxByte(0x1, 0x0f).execute(&mut x);
// 204+2 assert_eq!(x.registers.peek_pc(), 0x204);
assert_eq!(x.registers.peek_pc(), 0x206);
Chip8CpuInstructions::SneVxByte(0x1, 0xf0).execute(&mut x);
// 208
assert_eq!(x.registers.peek_pc(), 0x208);
}
let mut x = Chip8Computer::new();
x.registers.poke(0x1, 0xf0);
Chip8CpuInstructions::SneVxByte(0x1, 0xf0).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0x202);
}
#[test] #[test]
fn addivx_test() { fn addivx_test() {
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdIAddr(0xabc).execute(&mut x); x.registers.poke_i(0xabc);
Chip8CpuInstructions::LdVxByte(0x0, 0x10).execute(&mut x); x.registers.poke(0x0, 0x10);
Chip8CpuInstructions::AddIVx(0x0).execute(&mut x); Chip8CpuInstructions::AddIVx(0x0).execute(&mut x);
assert_eq!(x.registers.peek_i(), 0xacc); assert_eq!(x.registers.peek_i(), 0xacc);
} }
@ -1143,7 +1128,7 @@ mod test {
#[test] #[test]
fn ldstvt_test() { fn ldstvt_test() {
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0x01, 0xf0).execute(&mut x); x.registers.poke(0x1, 0xf0);
Chip8CpuInstructions::LdStVx(0x01).execute(&mut x); Chip8CpuInstructions::LdStVx(0x01).execute(&mut x);
assert_eq!(x.sound_timer.current(), 0xf0); assert_eq!(x.sound_timer.current(), 0xf0);
x.sound_timer.tick(); x.sound_timer.tick();
@ -1159,26 +1144,12 @@ mod test {
let new_value = x.registers.peek(0x1); let new_value = x.registers.peek(0x1);
assert!(new_value < 0x10); assert!(new_value < 0x10);
} }
/*
#[test]
fn skp_vx_test() {
let mut x = Chip8Computer::new();
x.keypad.push_key(0x1);
Chip8CpuInstructions::LdVxByte(0x1, 0x1).execute(&mut x);
Chip8CpuInstructions::SkpVx(0x1).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0x208);
x.keypad.release_key(0x1);
Chip8CpuInstructions::SkpVx(0x1).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0x20A);
}
*/
#[test] #[test]
fn add_vx_byte_test() { fn add_vx_byte_test() {
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
// set a value in the register // set a value in the register
Chip8CpuInstructions::LdVxByte(0x1, 0xab).execute(&mut x); x.registers.poke(0x01, 0xab);
// add 0x10 to register // add 0x10 to register
Chip8CpuInstructions::AddVxByte(0x1, 0x10).execute(&mut x); Chip8CpuInstructions::AddVxByte(0x1, 0x10).execute(&mut x);
assert_eq!(x.registers.peek(1), 0xbb); assert_eq!(x.registers.peek(1), 0xbb);
@ -1188,30 +1159,33 @@ mod test {
fn sub_vx_vy_test() { fn sub_vx_vy_test() {
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
// load values in 2 registers // load values in 2 registers
Chip8CpuInstructions::LdVxByte(0x01, 0x10).execute(&mut x); x.registers.poke(0x1, 0x10);
Chip8CpuInstructions::LdVxByte(0x02, 0x08).execute(&mut x); x.registers.poke(0x2, 0x08);
Chip8CpuInstructions::SubVxVy(0x1, 0x02).execute(&mut x); Chip8CpuInstructions::SubVxVy(0x1, 0x02).execute(&mut x);
assert_eq!(x.registers.peek(0xf), 0); assert_eq!(x.registers.peek(0xf), 0);
assert_eq!(x.registers.peek(0x1), 0x8); assert_eq!(x.registers.peek(0x1), 0x8);
assert_eq!(x.registers.peek_pc(), 0x206); assert_eq!(x.registers.peek_pc(), 0x202);
} }
#[test] #[test]
fn sne_vx_vy_test() { fn sne_vx_vy_test() {
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0x1, 0x10).execute(&mut x); x.registers.poke(0x1, 0x10);
Chip8CpuInstructions::LdVxByte(0x2, 0x10).execute(&mut x); x.registers.poke(0x2, 0x10);
Chip8CpuInstructions::SneVxVy(0x1, 0x2).execute(&mut x); Chip8CpuInstructions::SneVxVy(0x1, 0x2).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0x206); assert_eq!(x.registers.peek_pc(), 0x202);
Chip8CpuInstructions::LdVxByte(0x2, 0x00).execute(&mut x);
let mut x = Chip8Computer::new();
x.registers.poke(0x1, 0x10);
x.registers.poke(0x2, 0x00);
Chip8CpuInstructions::SneVxVy(0x01, 0x02).execute(&mut x); Chip8CpuInstructions::SneVxVy(0x01, 0x02).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0x20C) assert_eq!(x.registers.peek_pc(), 0x204)
} }
#[test] #[test]
fn ld_dt_vx_test() { fn ld_dt_vx_test() {
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0x1, 0x10).execute(&mut x); x.registers.poke(0x1, 0x10);
Chip8CpuInstructions::LdDtVx(0x1).execute(&mut x); Chip8CpuInstructions::LdDtVx(0x1).execute(&mut x);
assert_eq!(x.delay_timer.current(), 0x10); assert_eq!(x.delay_timer.current(), 0x10);
for i in 0..0x20 { for i in 0..0x20 {
@ -1223,13 +1197,12 @@ mod test {
#[test] #[test]
fn ld_vx_dt_test() { fn ld_vx_dt_test() {
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0x1, 0xf0).execute(&mut x); x.registers.poke(0x1, 0xf0);
Chip8CpuInstructions::LdDtVx(0x1).execute(&mut x); Chip8CpuInstructions::LdDtVx(0x1).execute(&mut x);
x.delay_timer.tick(); x.delay_timer.tick();
x.delay_timer.tick(); x.delay_timer.tick();
x.delay_timer.tick(); x.delay_timer.tick();
Chip8CpuInstructions::LdVxDt(0x1).execute(&mut x); assert_eq!(x.delay_timer.current(), 0xed);
assert_eq!(x.registers.peek(0x1), 0xed);
} }
#[test] #[test]
@ -1241,20 +1214,18 @@ mod test {
// The carry flag (VF) is set to 1 if there is no borrow (i.e., Vy is greater // The carry flag (VF) is set to 1 if there is no borrow (i.e., Vy is greater
// than or equal to Vx), and it is set to 0 if there is a borrow. // than or equal to Vx), and it is set to 0 if there is a borrow.
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0x1, 0xa0).execute(&mut x); x.registers.poke(0x1, 0xa0);
Chip8CpuInstructions::LdVxByte(0x2, 0xab).execute(&mut x); x.registers.poke(0x2, 0xab);
Chip8CpuInstructions::SubnVxVy(0x1, 0x2).execute(&mut x); Chip8CpuInstructions::SubnVxVy(0x1, 0x2).execute(&mut x);
// expect the result to be 0x0b // expect the result to be 0x0b
assert_eq!(x.registers.peek(0x1), 0x0b); assert_eq!(x.registers.peek(0x1), 0x0b);
// expect the vf register to be set to 1 as there was overflow // expect the vf register to be set to 1 as there was overflow
assert_eq!(x.registers.peek(0xf), 0x0); assert_eq!(x.registers.peek(0xf), 0x0);
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0x1, 0xab).execute(&mut x); x.registers.poke(0x01, 0xab);
Chip8CpuInstructions::LdVxByte(0x2, 0xa0).execute(&mut x); x.registers.poke(0x02, 0xa0);
Chip8CpuInstructions::SubnVxVy(0x1, 0x2).execute(&mut x); Chip8CpuInstructions::SubnVxVy(0x1, 0x2).execute(&mut x);
// expect the result to be 11110101, -0xB, -11, 245, 0xF5 // expect the result to be 11110101, -0xB, -11, 245, 0xF5
@ -1263,21 +1234,20 @@ mod test {
} }
#[test] #[test]
fn shl_vx_vy_test() { fn shl_vx_vy_test_1() {
// 8xyE - SHL Vx {, Vy} // 8xyE - SHL Vx {, Vy}
// Set Vx = Vx SHL 1. // Set Vx = Vx SHL 1.
// //
// If the most-significant bit of Vx is 1, then VF is set to 1, otherwise to 0. Then Vx is multiplied by 2. // If the most-significant bit of Vx is 1, then VF is set to 1, otherwise to 0. Then Vx is multiplied by 2.
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0x1, 0b00100000).execute(&mut x); x.registers.poke(0x1, 0b00100000);
Chip8CpuInstructions::ShlVxVy(0x1, 0x1).execute(&mut x); Chip8CpuInstructions::ShlVxVy(0x1, 0x1).execute(&mut x);
assert_eq!(x.registers.peek(0x1), 0b01000000); assert_eq!(x.registers.peek(0x1), 0b01000000);
assert_eq!(x.registers.peek(0xf), 0x0); assert_eq!(x.registers.peek(0xf), 0x0);
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0x1, 0b10101010).execute(&mut x); x.registers.poke(0x1, 0b10101010);
Chip8CpuInstructions::ShlVxVy(0x1, 0x1).execute(&mut x); Chip8CpuInstructions::ShlVxVy(0x1, 0x1).execute(&mut x);
assert_eq!(x.registers.peek(0x1), 0b01010100); assert_eq!(x.registers.peek(0x1), 0b01010100);
assert_eq!(x.registers.peek(0xf), 0x1); assert_eq!(x.registers.peek(0xf), 0x1);
@ -1292,12 +1262,13 @@ mod test {
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
// target_sprite = 2 // target_sprite = 2
// target_offset = 0x5 // target_offset = 0x5
Chip8CpuInstructions::LdVxByte(0x1, 0x2).execute(&mut x); x.registers.poke(0x1, 0x2);
Chip8CpuInstructions::LdFVx(0x1).execute(&mut x); Chip8CpuInstructions::LdFVx(0x1).execute(&mut x);
assert_eq!(x.registers.peek_i(), 0x5); assert_eq!(x.registers.peek_i(), 0x5);
Chip8CpuInstructions::LdVxByte(0x1, 0x6).execute(&mut x); let mut x = Chip8Computer::new();
x.registers.poke(0x01, 0x06);
Chip8CpuInstructions::LdFVx(0x1).execute(&mut x); Chip8CpuInstructions::LdFVx(0x1).execute(&mut x);
assert_eq!(x.registers.peek_i(), 25); assert_eq!(x.registers.peek_i(), 25);
} }
@ -1313,10 +1284,8 @@ mod test {
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
// load the value 123 (0x7b) // load the value 123 (0x7b)
Chip8CpuInstructions::LdVxByte(0x1, 0x7b).execute(&mut x); x.registers.poke(0x1, 0x7b);
// set I to 0x500 x.registers.poke_i(0x500);
Chip8CpuInstructions::LdIAddr(0x500).execute(&mut x);
;
Chip8CpuInstructions::LdBVx(0x1).execute(&mut x); Chip8CpuInstructions::LdBVx(0x1).execute(&mut x);
assert_eq!(x.memory.peek(0x500), 0x1); assert_eq!(x.memory.peek(0x500), 0x1);
assert_eq!(x.memory.peek(0x501), 0x2); assert_eq!(x.memory.peek(0x501), 0x2);
@ -1366,16 +1335,17 @@ mod test {
// where to load from // where to load from
x.registers.poke_i(0x500); x.registers.poke_i(0x500);
// how much to load // how much to load
x.registers.poke(0x0, to_load.len() as u8); x.registers.poke(0x6, to_load.len() as u8);
// then copying them values memory to registers // then copying them values memory to registers
Chip8CpuInstructions::LdVxI(0x0).execute(&mut x); Chip8CpuInstructions::LdVxI(0x6).execute(&mut x);
// now check that we have the right values in our registers // now check that we have the right values in our registers
for (idx, value) in to_load.iter().enumerate() { for (idx, value) in to_load.iter().enumerate() {
assert_eq!(x.registers.peek(idx as u8), *value); assert_eq!(x.registers.peek(idx as u8), *value);
} }
}
}
#[test] #[test]
fn Sknkpvx_test() { fn Sknkpvx_test() {
@ -1390,7 +1360,6 @@ mod test {
x.registers.poke(0x1, 0x5); x.registers.poke(0x1, 0x5);
Chip8CpuInstructions::SnkpVx(0x1).execute(&mut x); Chip8CpuInstructions::SnkpVx(0x1).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0x202); assert_eq!(x.registers.peek_pc(), 0x202);
x.keypad.release_key(0x5); x.keypad.release_key(0x5);
Chip8CpuInstructions::SnkpVx(0x1).execute(&mut x); Chip8CpuInstructions::SnkpVx(0x1).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0x206); assert_eq!(x.registers.peek_pc(), 0x206);
@ -1435,20 +1404,18 @@ mod test {
// 3,2->3,9 // 3,2->3,9
// 4,2->4,9 // 4,2->4,9
// 5,2->5,9 // 5,2->5,9
// let byte_to_check = CHIP8FONT_0[0]; // let byte_to_check = CHIP8FONT_0[0];
for row_in_sprite in 0..5 { for row_in_sprite in 0..5 {
let row_data = CHIP8FONT_2[row_in_sprite]; let row_data = CHIP8FONT_2[row_in_sprite];
for bit_in_byte in 0..8 { for bit_in_byte in 0..8 {
let data_offset = (x_offset let data_offset = (x_offset
as u16 + row_in_sprite as u16) * 64 + (bit_in_byte + y_offset) as u16; as u16 + row_in_sprite as u16) * 64 + (bit_in_byte + y_offset) as u16;
let real_bit_in_byte = 7-bit_in_byte; let real_bit_in_byte = 7 - bit_in_byte;
let shifted_one = 0x01 << real_bit_in_byte; let shifted_one = 0x01 << real_bit_in_byte;
let one_shift_set = (shifted_one & row_data )> 0; let one_shift_set = (shifted_one & row_data) > 0;
println!("ROWDATA = \t\t[{row_data:08b}]\tBIT IN BYTE = \t[{bit_in_byte}]\tONE_SHIFT_SET = [{one_shift_set}]\tSHIFTED ONE = [{shifted_one:08b}]"); println!("ROWDATA = \t\t[{row_data:08b}]\tBIT IN BYTE = \t[{bit_in_byte}]\tONE_SHIFT_SET = [{one_shift_set}]\tSHIFTED ONE = [{shifted_one:08b}]");
println!("DATA_OFFSET FOR SOURCE DATA {}x{} is {} / offset by {}x{} and should be {} working with byte {:08b}", println!("DATA_OFFSET FOR SOURCE DATA {}x{} is {} / offset by {}x{} and should be {} working with byte {:08b}",
bit_in_byte, row_in_sprite, data_offset, x_offset, y_offset, one_shift_set, row_data); bit_in_byte, row_in_sprite, data_offset, x_offset, y_offset, one_shift_set, row_data);
// assert_eq!(should_be_set, );
assert_eq!(one_shift_set, x.video_memory.peek(data_offset as u16));
} }
} }
} }
@ -1468,14 +1435,19 @@ mod test {
#[test] #[test]
fn ret_test() { fn ret_test() {
// SETUP
// Return from a subroutine. // Return from a subroutine.
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
x.stack.push(&0x132); x.stack.push(&0x132);
x.stack.push(&0xabc); x.stack.push(&0xabc);
// EXECUTE
Chip8CpuInstructions::RET.execute(&mut x); Chip8CpuInstructions::RET.execute(&mut x);
// VERIFY
assert_eq!(x.registers.peek_pc(), 0xabc); assert_eq!(x.registers.peek_pc(), 0xabc);
assert_eq!(x.stack.depth(), 1); assert_eq!(x.stack.depth(), 1);
// EXECUTE
Chip8CpuInstructions::RET.execute(&mut x); Chip8CpuInstructions::RET.execute(&mut x);
// VERIFY
assert_eq!(x.registers.peek_pc(), 0x132); assert_eq!(x.registers.peek_pc(), 0x132);
assert_eq!(x.stack.depth(), 0); assert_eq!(x.stack.depth(), 0);
} }
@ -1483,15 +1455,10 @@ mod test {
#[test] #[test]
fn ldvxk_test() { fn ldvxk_test() {
// SETUP
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0x1, 0x1).execute(&mut x); x.registers.poke(0x01, 0x01);
Chip8CpuInstructions::LdVxK(0x1).execute(&mut x); Chip8CpuInstructions::LdVxK(0x1).execute(&mut x);
assert!(matches!(x.state, WaitingForKey)); assert!(matches!(x.state, WaitingForKey));
} }
#[test]
fn f6_fail_hard_test() {
let mut x = Chip8Computer::new();
}
} }

View File

@ -43,6 +43,9 @@ impl Chip8Registers {
} }
pub fn poke(&mut self, register_number: u8, value: u8) { pub fn poke(&mut self, register_number: u8, value: u8) {
if register_number > 0xf {
panic!("INVALID REGISTER");
}
self.registers[(register_number) as usize] = value; self.registers[(register_number) as usize] = value;
} }
@ -100,4 +103,23 @@ mod test {
x.set_pc(0x300); x.set_pc(0x300);
assert_eq!(x.peek_pc(), 0x300); assert_eq!(x.peek_pc(), 0x300);
} }
#[test]
#[should_panic]
fn invalid_register() {
let mut x = Chip8Registers::default();
x.poke(0x10, 0xff);
}
#[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"));
}
} }

View File

@ -0,0 +1,11 @@
use emmaemu::chip8::computer::Chip8Computer;
#[test]
fn smoke() { assert!(true) }
#[test]
fn test_rom_1_works() {
let mut x = Chip8Computer::new();
}

Binary file not shown.

Binary file not shown.

BIN
resources/roms/3-corax+.ch8 Normal file

Binary file not shown.

BIN
resources/roms/4-flags.ch8 Normal file

Binary file not shown.

BIN
resources/roms/5-quirks.ch8 Normal file

Binary file not shown.