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::{
chip8::computer::Chip8Computer,
constants::{CHIP8_MEMORY_SIZE, CHIP8_VIDEO_HEIGHT, CHIP8_VIDEO_WIDTH},
@ -11,53 +14,61 @@ use support::emmagui_support::EmmaGui;
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 {
pub show_registers: 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 {
fn default() -> Self {
UiState {
show_registers: true,
show_memory: true,
show_video: true
show_registers: false,
show_memory: false,
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() {
pretty_env_logger::init();
let mut system = Chip8Computer::default();
let mut ui_state = UiState::default();
let mut last_tick_time = Instant::now();
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);
if ui_state.show_registers {
@ -65,16 +76,12 @@ fn main() {
}
if ui_state.show_video {
EmmaGui::video_display(&system, ui);
EmmaGui::video_display(&system, &ui_state, ui);
}
if ui_state.show_memory {
let active_instruction = system.registers.peek_pc();
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::io::Read;
use std::path::Path;
use std::path::{Path, PathBuf};
use std::thread::sleep;
use std::time::Duration;
use imgui::{Condition, ImColor32, Ui};
use log::debug;
use emmaemu::chip8::computer::Chip8Computer;
use emmaemu::chip8::system_memory::Chip8SystemMemory;
use emmaemu::constants::{CHIP8_VIDEO_HEIGHT, CHIP8_VIDEO_WIDTH};
use crate::UiState;
pub struct EmmaGui {
}
pub struct EmmaGui {}
const CELL_WIDTH: 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 {
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
let draw_area_size = ui.io().display_size;
let cell_width = ((draw_area_size[0] as i32 / 64) * 6) / 10;
let cell_height = ((draw_area_size[1] as i32 / 32) * 6) / 10;
ui.window(format!("Display {cell_width}x{cell_height}"))
.size([300.0, 300.0], Condition::FirstUseEver)
.build(|| {
@ -39,9 +61,9 @@ impl EmmaGui {
let memory_offset = (current_row * 64 + current_column) as u16;
let to_render = system_to_control.video_memory.peek(memory_offset);
let color: ImColor32 = if to_render {
ImColor32::from_rgb(0xff, 0x00, 0x00)
gui_state.on_colour
} else {
ImColor32::from_rgb(0x00, 0x00, 0xff)
gui_state.off_colour
};
fg.add_rect_filled_multicolor(current_origin, current_limit, color, color, color, color);
}
@ -50,40 +72,59 @@ impl EmmaGui {
}
pub fn system_controls(system_to_control: &mut Chip8Computer, gui_state: &mut UiState, ui: &Ui) {
ui.window("!!!! CONTROLS !!!!")
.size([200.0, 200.0], Condition::FirstUseEver)
.size([345.0, 200.0], Condition::FirstUseEver)
.build(|| {
ui.text("Computer Controls");
if ui.button("Step") {
system_to_control.step_system();
};
/* 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 1-chip8-logo.ch8");
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("./coraxhard.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") {
system_to_control.step_system();
};
if ui.button("Run") {
gui_state.is_running = true;
println!("STARTING THE SYSTEM");
}
if ui.button("Stop") {
gui_state.is_running = false;
println!("STOPPING THE SYSTEM");
}
ui.same_line();
if ui.button("Reset") {
*system_to_control = Chip8Computer::new();
}
if ui.button("Dump Video Memory") {
println!("{}", system_to_control.dump_video_to_string());
}
ui.same_line();
if ui.button("Dump Keypad State") {
println!("{}", system_to_control.dump_keypad_to_string());
}
ui.same_line();
if ui.button("Dump Registers") {
println!("{}", system_to_control.dump_registers_to_string());
}
ui.separator();
ui.checkbox("Show Memory", &mut gui_state.show_memory);
ui.same_line();
ui.checkbox("Show Video", &mut gui_state.show_video);
ui.same_line();
ui.checkbox("Show Registers", &mut gui_state.show_registers);
});
}
@ -114,6 +155,7 @@ impl EmmaGui {
let cols = position.1;
ui.window("System Memory")
.size([400.0, 300.0], Condition::FirstUseEver)
.position([500.0, 300.0], Condition::Always)
.build(|| {
let mut current_x_hover: i32 = 0;
let mut current_y_hover: i32 = 0;

View File

@ -496,24 +496,38 @@ impl Chip8CpuInstructions {
}
// 0x8xy1 Set Vx = Vx OR Vy
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}]")
}
// 0x8xy2 Set Vx = Vx AND Vy
Chip8CpuInstructions::AndVxVy(x, y) => {
input.registers.poke(*x as u8, input.registers.peek(*x as u8) & input.registers.peek(*y as u8));
debug!("AndVxVy [0x{x:1x}] [0x{y:1x}]");
let lhs_16 = input.registers.peek(*x as u8) as u16;
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
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)
Chip8CpuInstructions::AddVxVy(x, y) => {
let lhs = input.registers.peek(*x as u8);
let rhs = input.registers.peek(*y as u8);
let working = (lhs as i16 + rhs as i16) as i16;
if working > 255 {
let lhs = input.registers.peek(*x as u8) as i16;
let rhs = input.registers.peek(*y as u8) as i16;
let working = lhs + rhs;
if working > 0xff {
input.registers.poke(0xf, 0x01);
}
input.registers.poke(*x as u8, working as u8);
@ -759,11 +773,14 @@ impl Chip8CpuInstructions {
Chip8CpuInstructions::LdIVx(x) => {
// 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();
for i in 0..*x {
for i in 0..=*x {
input.memory.poke(offset + i, input.registers.peek(i as u8));
}
input.registers.poke_i(offset + 1);
}
Chip8CpuInstructions::LdVxI(x) => {
// 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.
let offset = input.registers.peek_i();
debug!("STARTING TO READ AT {offset:03x}");
let num_loops = input.registers.peek(*x as u8);
debug!("WILL READ {num_loops:x} BYTES");
let num_loops = x + 1;
println!("WILL READ {num_loops:x} BYTES");
for index in 0..num_loops {
let src_value = input.memory.peek(index as u16 + offset);
input.registers.poke(index, src_value);
debug!("POKING {index} with {src_value}");
input.registers.poke(index as u8, src_value);
println!("POKING Register 0x{index:02x} with 0x{src_value:04x} using offset 0x{offset:04x}");
}
input.registers.poke_i(offset + 1);
}
Chip8CpuInstructions::XXXXERRORINSTRUCTION => {}
};
@ -896,8 +913,12 @@ mod test {
let mut x = Chip8Computer::new();
Chip8CpuInstructions::SysAddr(0).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0);
let mut x = Chip8Computer::new();
Chip8CpuInstructions::SysAddr(0xFA0).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0xFA0);
let mut x = Chip8Computer::new();
Chip8CpuInstructions::SysAddr(0x0AF).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0x0AF);
}
@ -908,6 +929,8 @@ mod test {
let mut x = Chip8Computer::new();
Chip8CpuInstructions::JpAddr(0).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0);
let mut x = Chip8Computer::new();
Chip8CpuInstructions::JpAddr(0xABC).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0xABC);
}
@ -919,10 +942,10 @@ mod test {
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(1, 0x12).execute(&mut x);
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);
assert_eq!(x.registers.peek(2), 0x21);
assert_eq!(x.registers.peek_pc(), 0x204);
}
#[test]
@ -933,69 +956,41 @@ mod test {
// test setup: Load value 0x84 into V1
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(1, 0x84).execute(&mut x);
Chip8CpuInstructions::SeVxByte(1, 0x84).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...
x.registers.poke(0x1, 0x84);
Chip8CpuInstructions::SeVxByte(1, 0x48).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0x202);
Chip8CpuInstructions::SeVxByte(1, 0x48).execute(&mut x);
// we should be 2 instructions past.
// 2 for what we executed
assert_eq!(x.registers.peek_pc(), 0x204);
let mut x = Chip8Computer::new();
x.registers.poke(0x1, 0x84);
Chip8CpuInstructions::SeVxByte(1, 0x84).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0x208);
assert_eq!(x.registers.peek_pc(), 0x204);
}
#[test]
fn se_vx_vy_test() {
// 0x4xkk Skip next instruction if Vx != kk
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0x01, 0x84).execute(&mut x);
Chip8CpuInstructions::LdVxByte(0x02, 0x84).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0x204);
x.registers.poke(0x01, 0x84);
x.registers.poke(0x2, 0x84);
// skip, compare 0x84 to 0x84
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
Chip8CpuInstructions::LdVxByte(0x2, 0x48).execute(&mut x);
// verify its there.
assert_eq!(x.registers.peek(2), 0x48);
// no skip, compare 0x84 and 0x48
let mut x = Chip8Computer::new();
x.registers.poke(0x01, 0x84);
x.registers.poke(0x2, 0x48);
Chip8CpuInstructions::SeVxVy(0x01, 0x02).execute(&mut x);
assert_eq!(x.registers.peek(2), 0x48);
assert_eq!(x.registers.peek_pc(), 0x20C);
assert_eq!(x.registers.peek_pc(), 0x202);
}
#[test]
fn ld_vx_vy_test() {
// 0x8xy0 Set value of Vy in Vx
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0x01, 0x01).execute(&mut x);
Chip8CpuInstructions::LdVxByte(0x02, 0x02).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0x204);
assert_eq!(x.registers.peek(1), 0x01);
x.registers.poke(0x01, 0x01);
x.registers.poke(0x02, 0x02);
Chip8CpuInstructions::LdVxVy(0x01, 0x02).execute(&mut x);
assert_eq!(x.registers.peek(1), 0x02);
assert_eq!(x.registers.peek_pc(), 0x206);
}
#[test]
@ -1005,11 +1000,10 @@ mod test {
// | 0b0000 1010 (0x0A)
// 0b0101 1010 (0x5A)
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(1, 0x50).execute(&mut x);
Chip8CpuInstructions::LdVxByte(2, 0x0A).execute(&mut x);
x.registers.poke(0x01, 0b01010000);
x.registers.poke(0x02, 0b00001010);
Chip8CpuInstructions::OrVxVy(1, 2).execute(&mut x);
assert_eq!(x.registers.peek(1), 0x5A);
assert_eq!(x.registers.peek_pc(), 0x206);
assert_eq!(x.registers.peek(1), 0b01011010);
}
#[test]
@ -1019,11 +1013,10 @@ mod test {
// & 0b1100 1010 (0xCA)
// 0b1100 1000 (0xC8)
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(1, 0xFC).execute(&mut x);
Chip8CpuInstructions::LdVxByte(2, 0xCA).execute(&mut x);
x.registers.poke(0x01, 0xFC);
x.registers.poke(0x02, 0xCA);
Chip8CpuInstructions::AndVxVy(1, 2).execute(&mut x);
assert_eq!(x.registers.peek(1), 0xC8);
assert_eq!(x.registers.peek_pc(), 0x206);
}
#[test]
@ -1033,11 +1026,10 @@ mod test {
// ^ 0b1100 1010 (0xCA)
// 0b0011 0110 (0x36)
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(1, 0xFC).execute(&mut x);
Chip8CpuInstructions::LdVxByte(2, 0xCA).execute(&mut x);
x.registers.poke(0x01, 0b11111100);
x.registers.poke(0x02, 0b11001010);
Chip8CpuInstructions::XorVxVy(1, 2).execute(&mut x);
assert_eq!(x.registers.peek(1), 0x36);
assert_eq!(x.registers.peek_pc(), 0x206);
assert_eq!(x.registers.peek(1), 0b00110110);
}
#[test]
@ -1048,24 +1040,20 @@ mod test {
// + 0x01 0x01
// 0x02 F0 0x00 F1
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0xf, 0x00).execute(&mut x);
Chip8CpuInstructions::LdVxByte(1, 0x01).execute(&mut x);
Chip8CpuInstructions::LdVxByte(2, 0x01).execute(&mut x);
Chip8CpuInstructions::AddVxVy(1, 2).execute(&mut x);
// T1
assert_eq!(x.registers.peek(0xf), 0);
assert_eq!(x.registers.peek(1), 2);
assert_eq!(x.registers.peek_pc(), 0x208);
x.registers.poke(0x0f, 00);
x.registers.poke(0x01, 0x01);
x.registers.poke(0x02, 0x01);
Chip8CpuInstructions::AddVxVy(0x01, 0x02).execute(&mut x);
assert_eq!(x.registers.peek(0xf), 0x00);
assert_eq!(x.registers.peek(0x01), 0x02);
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0xf, 0x00).execute(&mut x);
Chip8CpuInstructions::LdVxByte(0x1, 0xff).execute(&mut x);
Chip8CpuInstructions::LdVxByte(0x2, 0x01).execute(&mut x);
x.registers.poke(0x0f, 0x00);
x.registers.poke(0x01, 0xff);
x.registers.poke(0x02, 0x01);
Chip8CpuInstructions::AddVxVy(1, 2).execute(&mut x);
// T2
assert_eq!(x.registers.peek(0xf), 1);
assert_eq!(x.registers.peek(1), 0);
assert_eq!(x.registers.peek_pc(), 0x208)
}
#[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.
*/
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0xf, 0x00).execute(&mut x);
Chip8CpuInstructions::LdVxByte(0x1, 0x08).execute(&mut x); // 0b0000 1000 (0x08)
Chip8CpuInstructions::LdVxByte(0x2, 0x2).execute(&mut x);
x.registers.poke(0x0f, 0x00);
x.registers.poke(0x01, 0b00001000);
x.registers.poke(0x02, 0b00000000);
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_pc(), 0x208);
x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0xf, 0x00).execute(&mut x);
Chip8CpuInstructions::LdVxByte(0x1, 0b00001001).execute(&mut x); // 0b0000 1001 (0x09)
Chip8CpuInstructions::ShrVxVy(0x1, 0x2).execute(&mut x); // 0b0000 0010 (0x02) (Set)
assert_eq!(x.registers.peek(1), 0x04);
x.registers.poke(0x0f, 0x00);
x.registers.poke(0x01, 0b00001001);
Chip8CpuInstructions::ShrVxVy(0x1, 0x2).execute(&mut x);
assert_eq!(x.registers.peek(1), 0b00000100);
assert_eq!(x.registers.peek(0xf), 1);
assert_eq!(x.registers.peek_pc(), 0x206);
}
#[test]
@ -1105,7 +1091,7 @@ mod test {
fn jp_v0addr_test() {
let mut x = Chip8Computer::new();
/// jump to I + nnn
Chip8CpuInstructions::LdVxByte(0x0, 0xFF).execute(&mut x);
x.registers.poke(0x0, 0xff);
Chip8CpuInstructions::JpV0Addr(0x100).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0x1FF);
}
@ -1120,22 +1106,21 @@ mod test {
#[test]
fn skip_next_instruction_ne_text() {
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0x1, 0xf0).execute(&mut x);
// 202
x.registers.poke(0x1, 0xf0);
Chip8CpuInstructions::SneVxByte(0x1, 0x0f).execute(&mut x);
// 204+2
assert_eq!(x.registers.peek_pc(), 0x206);
Chip8CpuInstructions::SneVxByte(0x1, 0xf0).execute(&mut x);
// 208
assert_eq!(x.registers.peek_pc(), 0x208);
}
assert_eq!(x.registers.peek_pc(), 0x204);
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]
fn addivx_test() {
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdIAddr(0xabc).execute(&mut x);
Chip8CpuInstructions::LdVxByte(0x0, 0x10).execute(&mut x);
x.registers.poke_i(0xabc);
x.registers.poke(0x0, 0x10);
Chip8CpuInstructions::AddIVx(0x0).execute(&mut x);
assert_eq!(x.registers.peek_i(), 0xacc);
}
@ -1143,7 +1128,7 @@ mod test {
#[test]
fn ldstvt_test() {
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0x01, 0xf0).execute(&mut x);
x.registers.poke(0x1, 0xf0);
Chip8CpuInstructions::LdStVx(0x01).execute(&mut x);
assert_eq!(x.sound_timer.current(), 0xf0);
x.sound_timer.tick();
@ -1159,26 +1144,12 @@ mod test {
let new_value = x.registers.peek(0x1);
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]
fn add_vx_byte_test() {
let mut x = Chip8Computer::new();
// set a value in the register
Chip8CpuInstructions::LdVxByte(0x1, 0xab).execute(&mut x);
x.registers.poke(0x01, 0xab);
// add 0x10 to register
Chip8CpuInstructions::AddVxByte(0x1, 0x10).execute(&mut x);
assert_eq!(x.registers.peek(1), 0xbb);
@ -1188,30 +1159,33 @@ mod test {
fn sub_vx_vy_test() {
let mut x = Chip8Computer::new();
// load values in 2 registers
Chip8CpuInstructions::LdVxByte(0x01, 0x10).execute(&mut x);
Chip8CpuInstructions::LdVxByte(0x02, 0x08).execute(&mut x);
x.registers.poke(0x1, 0x10);
x.registers.poke(0x2, 0x08);
Chip8CpuInstructions::SubVxVy(0x1, 0x02).execute(&mut x);
assert_eq!(x.registers.peek(0xf), 0);
assert_eq!(x.registers.peek(0x1), 0x8);
assert_eq!(x.registers.peek_pc(), 0x206);
assert_eq!(x.registers.peek_pc(), 0x202);
}
#[test]
fn sne_vx_vy_test() {
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0x1, 0x10).execute(&mut x);
Chip8CpuInstructions::LdVxByte(0x2, 0x10).execute(&mut x);
x.registers.poke(0x1, 0x10);
x.registers.poke(0x2, 0x10);
Chip8CpuInstructions::SneVxVy(0x1, 0x2).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0x206);
Chip8CpuInstructions::LdVxByte(0x2, 0x00).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0x202);
let mut x = Chip8Computer::new();
x.registers.poke(0x1, 0x10);
x.registers.poke(0x2, 0x00);
Chip8CpuInstructions::SneVxVy(0x01, 0x02).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0x20C)
assert_eq!(x.registers.peek_pc(), 0x204)
}
#[test]
fn ld_dt_vx_test() {
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0x1, 0x10).execute(&mut x);
x.registers.poke(0x1, 0x10);
Chip8CpuInstructions::LdDtVx(0x1).execute(&mut x);
assert_eq!(x.delay_timer.current(), 0x10);
for i in 0..0x20 {
@ -1223,13 +1197,12 @@ mod test {
#[test]
fn ld_vx_dt_test() {
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0x1, 0xf0).execute(&mut x);
x.registers.poke(0x1, 0xf0);
Chip8CpuInstructions::LdDtVx(0x1).execute(&mut x);
x.delay_timer.tick();
x.delay_timer.tick();
x.delay_timer.tick();
Chip8CpuInstructions::LdVxDt(0x1).execute(&mut x);
assert_eq!(x.registers.peek(0x1), 0xed);
assert_eq!(x.delay_timer.current(), 0xed);
}
#[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
// than or equal to Vx), and it is set to 0 if there is a borrow.
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0x1, 0xa0).execute(&mut x);
Chip8CpuInstructions::LdVxByte(0x2, 0xab).execute(&mut x);
x.registers.poke(0x1, 0xa0);
x.registers.poke(0x2, 0xab);
Chip8CpuInstructions::SubnVxVy(0x1, 0x2).execute(&mut x);
// expect the result to be 0x0b
assert_eq!(x.registers.peek(0x1), 0x0b);
// expect the vf register to be set to 1 as there was overflow
assert_eq!(x.registers.peek(0xf), 0x0);
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0x1, 0xab).execute(&mut x);
Chip8CpuInstructions::LdVxByte(0x2, 0xa0).execute(&mut x);
x.registers.poke(0x01, 0xab);
x.registers.poke(0x02, 0xa0);
Chip8CpuInstructions::SubnVxVy(0x1, 0x2).execute(&mut x);
// expect the result to be 11110101, -0xB, -11, 245, 0xF5
@ -1263,21 +1234,20 @@ mod test {
}
#[test]
fn shl_vx_vy_test() {
fn shl_vx_vy_test_1() {
// 8xyE - SHL Vx {, Vy}
// 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.
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0x1, 0b00100000).execute(&mut x);
x.registers.poke(0x1, 0b00100000);
Chip8CpuInstructions::ShlVxVy(0x1, 0x1).execute(&mut x);
assert_eq!(x.registers.peek(0x1), 0b01000000);
assert_eq!(x.registers.peek(0xf), 0x0);
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0x1, 0b10101010).execute(&mut x);
x.registers.poke(0x1, 0b10101010);
Chip8CpuInstructions::ShlVxVy(0x1, 0x1).execute(&mut x);
assert_eq!(x.registers.peek(0x1), 0b01010100);
assert_eq!(x.registers.peek(0xf), 0x1);
@ -1292,12 +1262,13 @@ mod test {
let mut x = Chip8Computer::new();
// target_sprite = 2
// target_offset = 0x5
Chip8CpuInstructions::LdVxByte(0x1, 0x2).execute(&mut x);
x.registers.poke(0x1, 0x2);
Chip8CpuInstructions::LdFVx(0x1).execute(&mut x);
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);
assert_eq!(x.registers.peek_i(), 25);
}
@ -1313,10 +1284,8 @@ mod test {
let mut x = Chip8Computer::new();
// load the value 123 (0x7b)
Chip8CpuInstructions::LdVxByte(0x1, 0x7b).execute(&mut x);
// set I to 0x500
Chip8CpuInstructions::LdIAddr(0x500).execute(&mut x);
;
x.registers.poke(0x1, 0x7b);
x.registers.poke_i(0x500);
Chip8CpuInstructions::LdBVx(0x1).execute(&mut x);
assert_eq!(x.memory.peek(0x500), 0x1);
assert_eq!(x.memory.peek(0x501), 0x2);
@ -1366,15 +1335,16 @@ mod test {
// where to load from
x.registers.poke_i(0x500);
// 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
Chip8CpuInstructions::LdVxI(0x0).execute(&mut x);
Chip8CpuInstructions::LdVxI(0x6).execute(&mut x);
// now check that we have the right values in our registers
for (idx, value) in to_load.iter().enumerate() {
assert_eq!(x.registers.peek(idx as u8), *value);
}
}
#[test]
@ -1390,7 +1360,6 @@ mod test {
x.registers.poke(0x1, 0x5);
Chip8CpuInstructions::SnkpVx(0x1).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0x202);
x.keypad.release_key(0x5);
Chip8CpuInstructions::SnkpVx(0x1).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0x206);
@ -1441,14 +1410,12 @@ mod test {
for bit_in_byte in 0..8 {
let data_offset = (x_offset
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 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!("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);
// 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]
fn ret_test() {
// SETUP
// Return from a subroutine.
let mut x = Chip8Computer::new();
x.stack.push(&0x132);
x.stack.push(&0xabc);
// EXECUTE
Chip8CpuInstructions::RET.execute(&mut x);
// VERIFY
assert_eq!(x.registers.peek_pc(), 0xabc);
assert_eq!(x.stack.depth(), 1);
// EXECUTE
Chip8CpuInstructions::RET.execute(&mut x);
// VERIFY
assert_eq!(x.registers.peek_pc(), 0x132);
assert_eq!(x.stack.depth(), 0);
}
@ -1483,15 +1455,10 @@ mod test {
#[test]
fn ldvxk_test() {
// SETUP
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0x1, 0x1).execute(&mut x);
x.registers.poke(0x01, 0x01);
Chip8CpuInstructions::LdVxK(0x1).execute(&mut x);
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) {
if register_number > 0xf {
panic!("INVALID REGISTER");
}
self.registers[(register_number) as usize] = value;
}
@ -100,4 +103,23 @@ mod test {
x.set_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.