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:
parent
6fe644ef46
commit
067164c657
13
.idea/trevors_chip8_toy.iml
generated
13
.idea/trevors_chip8_toy.iml
generated
@ -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
@ -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);
|
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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(|| {
|
||||||
@ -39,9 +61,9 @@ impl EmmaGui {
|
|||||||
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);
|
||||||
}
|
}
|
||||||
@ -50,40 +72,59 @@ impl EmmaGui {
|
|||||||
}
|
}
|
||||||
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 ui.button("Step") {
|
if !new_filename.is_empty() {
|
||||||
system_to_control.step_system();
|
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") {
|
if ui.button("Load Program") {
|
||||||
let mut buffer = Vec::new();
|
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("./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");
|
input_file.read_to_end(&mut buffer).expect("unable to read file");
|
||||||
system_to_control.load_bytes_to_memory(0x200, buffer.into());
|
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") {
|
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;
|
||||||
|
|||||||
@ -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,15 +1335,16 @@ 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]
|
||||||
@ -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);
|
||||||
@ -1447,8 +1416,6 @@ mod test {
|
|||||||
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();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
11
emma/tests/computer_tests.rs
Normal file
11
emma/tests/computer_tests.rs
Normal 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();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
BIN
resources/roms/1-chip8-logo.ch8
Normal file
BIN
resources/roms/1-chip8-logo.ch8
Normal file
Binary file not shown.
BIN
resources/roms/2-ibm-logo.ch8
Normal file
BIN
resources/roms/2-ibm-logo.ch8
Normal file
Binary file not shown.
BIN
resources/roms/3-corax+.ch8
Normal file
BIN
resources/roms/3-corax+.ch8
Normal file
Binary file not shown.
BIN
resources/roms/4-flags.ch8
Normal file
BIN
resources/roms/4-flags.ch8
Normal file
Binary file not shown.
BIN
resources/roms/5-quirks.ch8
Normal file
BIN
resources/roms/5-quirks.ch8
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user