add test chip8 file

more decoder and executor code
lots and lots more tests.  lots and lots and lots and lots more needed
This commit is contained in:
Trevor Merritt 2024-09-23 16:12:52 -04:00
parent c8259157c5
commit d6f7c73de3
8 changed files with 396 additions and 187 deletions

BIN
1-chip8-logo.ch8 Normal file

Binary file not shown.

1
Cargo.lock generated
View File

@ -884,6 +884,7 @@ dependencies = [
"imgui",
"imgui-glium-renderer",
"imgui-winit-support",
"log",
"pretty_env_logger",
"rand 0.9.0-alpha.2",
"ratatui",

View File

@ -15,3 +15,4 @@ winit = { version = "0.27", features = ["x11", "mint"] }
pretty_env_logger = "0.5.0"
copypasta = "0.8"
rand = "0.9.0-alpha.2"
log = "0.4.22"

View File

@ -37,13 +37,13 @@ fn hello_world_window(ui: &Ui) {
fn main() {
pretty_env_logger::init();
let mut system = Chip8Computer::default();
support::simple_init(file!(), move |_, ui| {
// hello_world_window(ui);
EmmaGui::system_controls(&mut system, ui);
EmmaGui::registers_view(&system, ui);
EmmaGui::hex_memory_display(system.memory.clone(), 0x100, 0x10,ui);

View File

@ -1,3 +1,8 @@
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::thread::sleep;
use std::time::Duration;
use imgui::{Condition, ImColor32, Ui};
use emmaemu::chip8::computer::Chip8Computer;
use emmaemu::chip8::system_memory::Chip8SystemMemory;
@ -19,10 +24,12 @@ impl EmmaGui {
system_to_control.step_system();
};
if ui.button("Load Program") {
println!("Load a program to start at 0x200");
}
if ui.button("Clear System Memory") {
println!("Clear Memory");
let mut buffer = Vec::new();
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");
input_file.read_to_end(&mut buffer).expect("unable to read file");
system_to_control.load_bytes_to_memory(0x200, buffer.into());
}
});
}
@ -70,13 +77,13 @@ impl EmmaGui {
if ui.is_mouse_hovering_rect(text_location, [bounding_box.x, bounding_box.y]) {
// Optionally change the text color to indicate it's interactable
ui.text_colored([1.0, 0.0, 0.0, 1.0], formatted_text);
ui.text_colored([1.0, 0.0, 0.0, 1.0], formatted_text.clone());
current_x_hover = current_column;
current_y_hover = current_row;
// Check if the left mouse button is clicked while hovering over the text
if ui.is_mouse_clicked(imgui::MouseButton::Left) {
println!("Text was clicked!");
println!("Offset: [{}] [0x{:02x}] Value: [{}]", data_offset, data_offset, formatted_text.clone());
// Perform any action here, e.g., call a function, trigger an event, etc.
}
} else {

View File

@ -1,7 +1,9 @@
use log::{debug, error};
use crate::chip8::instructions::Chip8CpuInstructions::XXXXERRORINSTRUCTION;
use crate::constants::{CHIP8_MEMORY_SIZE, CHIP8_REGISTER_COUNT};
use super::{
cpu_states::Chip8CpuStates, instructions::Chip8CpuInstructions, system_memory::Chip8SystemMemory, video::Chip8Video
cpu_states::Chip8CpuStates, instructions::Chip8CpuInstructions, system_memory::Chip8SystemMemory, video::Chip8Video,
};
@ -15,7 +17,7 @@ pub struct Chip8Computer {
pub delay_timer: u8,
pub i_register: u16,
pub video_memory: Chip8Video,
pub state: Chip8CpuStates
pub state: Chip8CpuStates,
}
impl Default for Chip8Computer {
@ -29,7 +31,7 @@ impl Default for Chip8Computer {
sound_timer: 0xFF,
delay_timer: 0xFF,
i_register: 0x0000,
state: Chip8CpuStates::WaitingForInstruction
state: Chip8CpuStates::WaitingForInstruction,
}
}
}
@ -39,7 +41,16 @@ impl Chip8Computer {
Chip8Computer::default()
}
pub fn step_system(mut self) -> Self {
pub fn load_bytes_to_memory(&mut self, offset: u16, to_load: Box<Vec<u8>>) {
let total_len = to_load.len() as u16;
for current_index in 0..total_len {
let new_value = to_load[current_index as usize];
let new_location = current_index + offset;
self.memory.poke(new_location, new_value);
}
}
pub fn step_system(&mut self) -> Self {
// read the next instruction
let mut working_instruction: u16 = 0b0000000000000000;
@ -48,250 +59,294 @@ impl Chip8Computer {
working_instruction = high_byte | low_byte;
let decoded_instruction =
Chip8Computer::decode_instruction(self.clone(), working_instruction);
Chip8Computer::decode_instruction(working_instruction);
println!("DECODED INSTRUCTION = {:?}", decoded_instruction);
match (self.state, decoded_instruction) {
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SysAddr(target_address)) => {
debug!("INST: SYS: {target_address}");
self.pc = target_address as u16;
},
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::CLS) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::RET) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::JpAddr(_)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::CallAddr(_)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SeVxByte(_, _)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SneVxByte(_, _)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SeVxVy(_, _)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdVxByte(_, _)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::AddVxByte(_, _)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdVxVy(_, _)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::OrVxVy(_, _)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::AndVxVy(_, _)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::XorVxVy(_, _)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::AddVxVy(_, _)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SubVxVy(_, _)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::ShrVxVy(_, _)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SubnVxVy(_, _)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::ShlVxVy(_, _)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SneVxVy(_, _)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdIAddr(_)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::JpV0Addr(_)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::RndVxByte(_, _)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::DrawVxVyNibble(_, _, _)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SkpVx(_)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SnkpVx(_)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdVxDt(_)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdVxK(_)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdDtVx(_)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdStVx(_)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::AddIVx(_)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdFVu(_)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdBVx(_)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdIVx(_)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdVxI(_)) => todo!(),
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::XXXXERRORINSTRUCTION) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::SysAddr(_)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::CLS) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::RET) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::JpAddr(_)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::CallAddr(_)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::SeVxByte(_, _)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::SneVxByte(_, _)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::SeVxVy(_, _)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::LdVxByte(_, _)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::AddVxByte(_, _)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::LdVxVy(_, _)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::OrVxVy(_, _)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::AndVxVy(_, _)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::XorVxVy(_, _)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::AddVxVy(_, _)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::SubVxVy(_, _)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::ShrVxVy(_, _)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::SubnVxVy(_, _)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::ShlVxVy(_, _)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::SneVxVy(_, _)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::LdIAddr(_)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::JpV0Addr(_)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::RndVxByte(_, _)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::DrawVxVyNibble(_, _, _)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::SkpVx(_)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::SnkpVx(_)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::LdVxDt(_)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::LdVxK(_)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::LdDtVx(_)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::LdStVx(_)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::AddIVx(_)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::LdFVu(_)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::LdBVx(_)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::LdIVx(_)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::LdVxI(_)) => todo!(),
(Chip8CpuStates::ExecutingInstruction, Chip8CpuInstructions::XXXXERRORINSTRUCTION) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::SysAddr(_)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::CLS) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::RET) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::JpAddr(_)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::CallAddr(_)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::SeVxByte(_, _)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::SneVxByte(_, _)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::SeVxVy(_, _)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::LdVxByte(_, _)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::AddVxByte(_, _)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::LdVxVy(_, _)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::OrVxVy(_, _)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::AndVxVy(_, _)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::XorVxVy(_, _)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::AddVxVy(_, _)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::SubVxVy(_, _)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::ShrVxVy(_, _)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::SubnVxVy(_, _)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::ShlVxVy(_, _)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::SneVxVy(_, _)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::LdIAddr(_)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::JpV0Addr(_)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::RndVxByte(_, _)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::DrawVxVyNibble(_, _, _)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::SkpVx(_)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::SnkpVx(_)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::LdVxDt(_)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::LdVxK(_)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::LdDtVx(_)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::LdStVx(_)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::AddIVx(_)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::LdFVu(_)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::LdBVx(_)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::LdIVx(_)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::LdVxI(_)) => todo!(),
(Chip8CpuStates::Error, Chip8CpuInstructions::XXXXERRORINSTRUCTION) => todo!(),
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::CLS) => {
debug!("INST: CLS");
self.pc += 0x2;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::RET) => {
debug!("INST: RET");
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::JpAddr(new_address)) => {
debug!("INST: JP_ADDR: {new_address}");
self.pc = new_address as u16;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::CallAddr(sub_address)) => {
debug!("INST: CALL_ADDR: {sub_address}");
self.pc += 0x2;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SeVxByte(vx_register, byte)) => {
debug!("INST: SeVxByte: {vx_register}/{byte}");
self.pc += 0x2;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SneVxByte(vx_register, byte)) => {
debug!("INST: SneVxByte: {vx_register}/{byte}");
self.pc += 0x2;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SeVxVy(vx_register, vy_register)) => {
debug!("INST: SeVxVy: {vx_register}/{vy_register}");
self.pc += 0x2;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdVxByte(vx_register, byte)) => {
debug!("INST: LdVxByte: {vx_register}/{byte}");
self.pc += 0x2;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::AddVxByte(vx_register, byte)) => {
debug!("INST: AddVxByte: {vx_register}/{byte}");
self.pc += 0x2;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdVxVy(vx_register, vy_register)) => {
debug!("INST: LdVxVy: {vx_register}/{vy_register}");
self.registers[vx_register as usize] = self.registers[vy_register as usize];
self.pc += 0x2;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::OrVxVy(vx_register, vy_register)) => {
debug!("INST: OrVxVy: {vx_register}/{vy_register}");
self.registers[vx_register as usize] = self.registers[vx_register as usize] | self.registers[vy_register as usize];
self.pc += 0x2;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::AndVxVy(vx_register, vy_register)) => {
debug!("INST: AndVxVy: {vx_register}/{vy_register}");
self.registers[vx_register as usize] = self.registers[vx_register as usize] & self.registers[vy_register as usize];
self.pc += 0x2;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::XorVxVy(vx_register, vy_register)) => {
debug!("INST: XorVxVy: {vx_register}/{vy_register}");
self.registers[vx_register as usize] = self.registers[vx_register as usize] ^ self.registers[vy_register as usize];
self.pc += 0x2;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::AddVxVy(vx_register, vy_register)) => {
debug!("INST: AddVxVy: {vx_register}/{vy_register}");
self.pc += 0x2;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SubVxVy(vx_register, vy_register)) => {
debug!("INST: SubVxVy: {vx_register}/{vy_register}");
self.pc += 0x2;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::ShrVxVy(vx_register, vy_register)) => {
debug!("INST: ShrVxVy: {vx_register}/{vy_register}");
self.pc += 0x2;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SubnVxVy(vx_register, vy_register)) => {
debug!("INST: SubnVxVy: {vx_register}/{vy_register}");
self.pc += 0x2;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::ShlVxVy(vx_register, vy_register)) => {
debug!("INST: ShlVxVy: {vx_register}/{vy_register}");
self.pc += 0x2;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SneVxVy(vx_register, vy_register)) => {
debug!("INST: SneVxVy: {vx_register}/{vy_register}");
self.pc += 0x2;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdIAddr(addr)) => {
debug!("INST: LdIAddr: {addr}");
self.pc += 0x2;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::JpV0Addr(addr)) => {
debug!("INST: JpV0Addr: {addr}");
self.pc += 0x2;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::RndVxByte(vx_register, byte)) => {
debug!("INST: RndVxByte: {vx_register}/{byte}");
self.pc += 0x2;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::DrawVxVyNibble(vx_register, vy_register, nibble)) => {
debug!("INST: DrawVxVyNibble: {vx_register}/{vy_register}/{nibble}");
self.pc += 0x2;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SkpVx(vx_register)) => {
debug!("INST: SkpVx: {vx_register}");
self.pc += 0x2;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::SnkpVx(vx_register)) => {
debug!("INST: SnkpVx: {vx_register}");
self.pc += 0x2;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdVxDt(vx_register)) => {
debug!("INST: LdVxDt: {vx_register}");
self.pc += 0x2;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdVxK(vx_register)) => {
debug!("INST: LdVxK: {vx_register}");
self.pc += 0x2;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdDtVx(vx_register)) => {
debug!("INST: LdDtVx: {vx_register}");
self.pc += 0x2;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdStVx(vx_register)) => {
debug!("INST: SkpVx: {vx_register}");
self.pc += 0x2;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::AddIVx(vx_register)) => {
debug!("INST: AddIVx: {vx_register}");
self.pc += 0x2;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdFVx(_)) => {
debug!("INST: LdFVu:");
self.pc += 0x2;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdBVx(vx_register)) => {
debug!("INST: LdBVx: {vx_register}");
self.pc += 0x2;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdIVx(vx_register)) => {
debug!("INST: LdIVx: {vx_register}");
self.pc += 0x2;
}
(Chip8CpuStates::WaitingForInstruction, Chip8CpuInstructions::LdVxI(vx_register)) => {
debug!("INST: LdVxI: {vx_register}");
self.pc += 0x2;
}
_ => {
error!("UNABLE TO PROCEED. CPU IN UNKNOWN STATE");
}
}
self.clone()
}
// nnn or addr - A 12-bit value, the lowest 12 bits of the instruction
pub fn read_addr_from_instruction(&self, instruction_to_read_from: u16) -> u16 {
pub fn read_addr_from_instruction(instruction_to_read_from: u16) -> u16 {
instruction_to_read_from & 0b0000111111111111
}
// n or nibble - A 4-bit value, the lowest 4 bits of the instruction
pub fn read_nibble_from_instruction(&self, instruction_to_read_from: u16) -> u16 {
pub fn read_nibble_from_instruction(instruction_to_read_from: u16) -> u16 {
instruction_to_read_from & 0b0000000000001111
}
// x - A 4-bit value, the lower 4 bits of the high byte of the instruction
pub fn read_x_from_instruction(&self, instruction_to_read_from: u16) -> u16 {
pub fn read_x_from_instruction(instruction_to_read_from: u16) -> u16 {
(instruction_to_read_from & 0b0000111100000000).rotate_right(8)
}
// y - A 4-bit value, the upper 4 bits of the low byte of the instruction
pub fn read_y_from_instruction(&self, instruction_to_read_from: u16) -> u16 {
pub fn read_y_from_instruction(instruction_to_read_from: u16) -> u16 {
(instruction_to_read_from & 0b0000000011110000).rotate_right(4)
}
// kk or byte - An 8-bit value, the lowest 8 bits of the instruction
pub fn read_byte_from_instruction(&self, instruction_to_read_from: u16) -> u16 {
pub fn read_byte_from_instruction(instruction_to_read_from: u16) -> u16 {
(instruction_to_read_from & 0b0000000011111111)
}
fn decode_instruction(self, to_read: u16) -> Chip8CpuInstructions {
pub fn read_upper_byte_lower_nibble(to_read_from: u16) -> u16 {
to_read_from & 0x0f00
}
fn decode_instruction(to_decode: u16) -> Chip8CpuInstructions {
let mut decoded_instruction = Chip8CpuInstructions::XXXXERRORINSTRUCTION;
let x_param = self.read_x_from_instruction(to_read);
let y_param = self.read_y_from_instruction(to_read);
let addr_param = self.read_addr_from_instruction(to_read);
let byte_param = self.read_byte_from_instruction(to_read);
let nibble_param = self.read_nibble_from_instruction(to_read);
// pull out the various possible parameters for use further along when we
// acutally sort out what kind of instruction we have.
let x_param = Chip8Computer::read_x_from_instruction(to_decode);
let y_param = Chip8Computer::read_y_from_instruction(to_decode);
let addr_param = Chip8Computer::read_addr_from_instruction(to_decode);
let byte_param = Chip8Computer::read_byte_from_instruction(to_decode);
let nibble_param = Chip8Computer::read_nibble_from_instruction(to_decode);
let ubln = u16::rotate_right(Chip8Computer::read_upper_byte_lower_nibble(to_decode), 8);
let last_byte = to_decode & 0xFF;
match to_read {
match to_decode {
0x00E0 => {
// 00E0 - CLS
// Clear the display.
decoded_instruction = Chip8CpuInstructions::CLS;
Chip8CpuInstructions::CLS
}
0x00EE => {
// 00EE - RET
// Return from a subroutine.
decoded_instruction = Chip8CpuInstructions::RET;
Chip8CpuInstructions::RET
}
0x0000..=0x0FFF => {
// 0nnn - SYS addr
// Jump to a machine code routine at nnn.
decoded_instruction = Chip8CpuInstructions::SysAddr(addr_param as i16);
Chip8CpuInstructions::SysAddr(addr_param as i16)
}
0x1000..=0x1FFF => {
// 1nnn - JP addr
// Jump to location nnn.
decoded_instruction = Chip8CpuInstructions::JpAddr(addr_param as i16);
Chip8CpuInstructions::JpAddr(addr_param as i16)
}
0x2000..=0x2FFF => {
// 2nnn - CALL addr
// Call subroutine at nnn.
decoded_instruction = Chip8CpuInstructions::CallAddr(addr_param as i16);
Chip8CpuInstructions::CallAddr(addr_param as i16)
}
0x3000..=0x3FFF => {
// 3xkk - SE Vx, byte
// Skip next instruction if Vx = kk.
decoded_instruction = Chip8CpuInstructions::SeVxByte(x_param as i16, byte_param as i16)
Chip8CpuInstructions::SeVxByte(x_param as i16, byte_param as i16)
}
0x4000..=0x4FFF => {
// 4xkk - SNE Vx, byte
// Skip next instruction if Vx != kk.
decoded_instruction = Chip8CpuInstructions::SneVxByte(x_param as i16, byte_param as i16);
Chip8CpuInstructions::SneVxByte(x_param as i16, byte_param as i16)
}
0x5000..=0x5FF0 => {
// 5xy0 - SE Vx, Vy
// Skip next instruction if Vx = Vy.
decoded_instruction = Chip8CpuInstructions::SeVxVy(x_param, y_param)
Chip8CpuInstructions::SeVxVy(x_param, y_param)
}
0x6000..=0x6FFF => {
// 6xkk - LD Vx, byte
// Set Vx = kk.
decoded_instruction = Chip8CpuInstructions::LdVxVy(x_param, byte_param);
Chip8CpuInstructions::LdVxByte(x_param, byte_param)
}
0x7000..=0x7FFF => {
// ADD Vx, Byte
decoded_instruction = Chip8CpuInstructions::AddVxByte(x_param, byte_param);
Chip8CpuInstructions::AddVxByte(x_param, byte_param)
}
0x8000..=0x8FFE => {
// 0x8000 Series
let last_nibble = to_read | 0x8000;
let last_nibble = to_decode & 0xF;
match last_nibble {
0x0 => {
// LD Vx, Vy
decoded_instruction = Chip8CpuInstructions::LdVxVy(x_param, y_param);
Chip8CpuInstructions::LdVxVy(x_param, y_param)
}
0x1 => {
// OR Vx, Vy
decoded_instruction = Chip8CpuInstructions::OrVxVy(x_param, y_param);
Chip8CpuInstructions::OrVxVy(x_param, y_param)
}
0x2 => {
// AND Vx, Vy
decoded_instruction = Chip8CpuInstructions::AndVxVy(x_param, y_param);
Chip8CpuInstructions::AndVxVy(x_param, y_param)
}
0x3 => {
// XOR Vx, Vy
decoded_instruction = Chip8CpuInstructions::XorVxVy(x_param, y_param);
Chip8CpuInstructions::XorVxVy(x_param, y_param)
}
0x4 => {
// AND Vx, Vy
decoded_instruction = Chip8CpuInstructions::AndVxVy(x_param, y_param);
// ADD Vx, Vy
Chip8CpuInstructions::AddVxVy(x_param, y_param)
}
0x5 => {
// SUB Vx, Vy
decoded_instruction = Chip8CpuInstructions::SubnVxVy(x_param, y_param);
Chip8CpuInstructions::SubVxVy(x_param, y_param)
}
0x6 => {
// SHR Vx, {, Vy }
decoded_instruction = Chip8CpuInstructions::ShrVxVy(x_param, y_param);
Chip8CpuInstructions::ShrVxVy(x_param, y_param)
}
0x7 => {
// SUBN Vx, Vy
decoded_instruction = Chip8CpuInstructions::SubnVxVy(x_param, y_param);
Chip8CpuInstructions::SubnVxVy(x_param, y_param)
}
0xE => {
// SHL Vx, {, Vy}
decoded_instruction = Chip8CpuInstructions::ShlVxVy(x_param, y_param);
Chip8CpuInstructions::ShlVxVy(x_param, y_param)
}
_ => {
panic!("UNABLE TO DECODE 0x8000 SERIES INSTRUCTION");
@ -300,31 +355,136 @@ impl Chip8Computer {
}
0x9000..=0x9FF0 => {
// SNE Vx, Vy
decoded_instruction = Chip8CpuInstructions::SneVxVy(x_param, y_param);
Chip8CpuInstructions::SneVxVy(x_param, y_param)
}
0xA000..=0xAFFF => {
// LD I, Addr
decoded_instruction = Chip8CpuInstructions::LdIAddr(addr_param);
Chip8CpuInstructions::LdIAddr(addr_param)
}
0xB000..=0xBFFF => {
decoded_instruction = Chip8CpuInstructions::JpV0Addr(addr_param);
Chip8CpuInstructions::JpV0Addr(addr_param)
// JP V0, Addr
}
0xC000..=0xCFFF => {
// RND Vx, byte
decoded_instruction = Chip8CpuInstructions::RndVxByte(x_param, byte_param);
Chip8CpuInstructions::RndVxByte(x_param, byte_param)
}
0xD000..0xDFFF => {
// DRAW Vx, Vy, nibble
decoded_instruction =
Chip8CpuInstructions::DrawVxVyNibble(x_param, y_param, nibble_param);
}
0xE09E..=0xEFA1 => {}
_ => {
panic!("UNABLE TO DECODE INSTRUCTION")
}
}
return decoded_instruction;
Chip8CpuInstructions::DrawVxVyNibble(x_param, y_param, nibble_param)
}
0xE09E..=0xEFA1 => {
match last_byte {
0x9E => {
Chip8CpuInstructions::SkpVx(ubln)
}
0xA1 => {
Chip8CpuInstructions::SnkpVx(ubln)
}
_ => {
XXXXERRORINSTRUCTION
}
}
}
0xF007..0xFF65 => {
println!("COMPARING LAST BYTE FROM TODECODE: {:2x} to {:4x} with {:2x}", last_byte, to_decode, ubln);
match last_byte {
0x07 => {
Chip8CpuInstructions::LdVxDt(ubln)
}
0x0A => {
Chip8CpuInstructions::LdVxK(ubln)
}
0x15 => {
Chip8CpuInstructions::LdDtVx(ubln)
}
0x18 => {
Chip8CpuInstructions::LdStVx(ubln)
}
0x1E => {
Chip8CpuInstructions::AddIVx(ubln)
}
0x29 => {
Chip8CpuInstructions::LdFVx(ubln)
}
0x33 => {
Chip8CpuInstructions::LdBVx(ubln)
}
0x55 => {
Chip8CpuInstructions::LdIVx(ubln)
}
0x65 => {
Chip8CpuInstructions::LdVxI(ubln)
}
_ => { XXXXERRORINSTRUCTION }
}
}
_ => {
XXXXERRORINSTRUCTION
}
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn smoke() {
assert!(true)
}
#[test]
fn decoder_test_valid_instructions() {
// valid instructions
assert!(matches!(Chip8Computer::decode_instruction(0x00E0u16), Chip8CpuInstructions::CLS ));
assert!(matches!(Chip8Computer::decode_instruction(0x00EE), Chip8CpuInstructions::RET));
assert!(matches!(Chip8Computer::decode_instruction(0x0123), Chip8CpuInstructions::SysAddr(0x123)));
assert!(matches!(Chip8Computer::decode_instruction(0x0FFF), Chip8CpuInstructions::SysAddr(0xfff)));
assert!(matches!(Chip8Computer::decode_instruction(0x1002), Chip8CpuInstructions::JpAddr(0x2)));
assert!(matches!(Chip8Computer::decode_instruction(0x1FF0), Chip8CpuInstructions::JpAddr(0xFF0)));
assert!(matches!(Chip8Computer::decode_instruction(0x2002), Chip8CpuInstructions::CallAddr(0x2)));
assert!(matches!(Chip8Computer::decode_instruction(0x3123), Chip8CpuInstructions::SeVxByte(0x1, 0x23)));
assert!(matches!(Chip8Computer::decode_instruction(0x4abc), Chip8CpuInstructions::SneVxByte(0xa, 0xbc)));
assert!(matches!(Chip8Computer::decode_instruction(0x5ab0), Chip8CpuInstructions::SeVxVy(0xa, 0xb)));
assert!(matches!(Chip8Computer::decode_instruction(0x6aff), Chip8CpuInstructions::LdVxByte(0xa, 0xff)));
assert!(matches!(Chip8Computer::decode_instruction(0x7abc), Chip8CpuInstructions::AddVxByte(0xa, 0xbc)));
assert!(matches!(Chip8Computer::decode_instruction(0x8ab0), Chip8CpuInstructions::LdVxVy(0xa, 0xb)));
assert!(matches!(Chip8Computer::decode_instruction(0x8ba1), Chip8CpuInstructions::OrVxVy(0xb, 0xa)));
assert!(matches!(Chip8Computer::decode_instruction(0x8cd2), Chip8CpuInstructions::AndVxVy(0xc, 0xd)));
assert!(matches!(Chip8Computer::decode_instruction(0x8de3), Chip8CpuInstructions::XorVxVy(0xd, 0xe)));
assert!(matches!(Chip8Computer::decode_instruction(0x8ef4), Chip8CpuInstructions::AddVxVy(0xe, 0xf)));
assert!(matches!(Chip8Computer::decode_instruction(0x8f05), Chip8CpuInstructions::SubVxVy(0xf, 0x0)));
assert!(matches!(Chip8Computer::decode_instruction(0x8016), Chip8CpuInstructions::ShrVxVy(0x0, 0x1)));
assert!(matches!(Chip8Computer::decode_instruction(0x8127), Chip8CpuInstructions::SubnVxVy(0x1, 0x2)));
assert!(matches!(Chip8Computer::decode_instruction(0x834e), Chip8CpuInstructions::ShlVxVy(0x3, 0x4)));
assert!(matches!(Chip8Computer::decode_instruction(0x9ab0), Chip8CpuInstructions::SneVxVy(0xa, 0xb)));
assert!(matches!(Chip8Computer::decode_instruction(0xa123), Chip8CpuInstructions::LdIAddr(0x123)));
assert!(matches!(Chip8Computer::decode_instruction(0xb234), Chip8CpuInstructions::JpV0Addr(0x234)));
assert!(matches!(Chip8Computer::decode_instruction(0xcaca), Chip8CpuInstructions::RndVxByte(0xa, 0xca)));
assert!(matches!(Chip8Computer::decode_instruction(0xdab4), Chip8CpuInstructions::DrawVxVyNibble(0xa, 0xb, 0x4)));
assert!(matches!(Chip8Computer::decode_instruction(0xe19e), Chip8CpuInstructions::SkpVx(0x1)));
assert!(matches!(Chip8Computer::decode_instruction(0xe2a1), Chip8CpuInstructions::SnkpVx(0x2)));
assert!(matches!(Chip8Computer::decode_instruction(0xf107), Chip8CpuInstructions::LdVxDt(0x1)));
assert!(matches!(Chip8Computer::decode_instruction(0xf40a), Chip8CpuInstructions::LdVxK(0x4)));
assert!(matches!(Chip8Computer::decode_instruction(0xf615), Chip8CpuInstructions::LdDtVx(0x6)));
assert!(matches!(Chip8Computer::decode_instruction(0xfb18), Chip8CpuInstructions::LdStVx(0xb)));
assert!(matches!(Chip8Computer::decode_instruction(0xfd1e), Chip8CpuInstructions::AddIVx(0xd)));
assert!(matches!(Chip8Computer::decode_instruction(0xfc29), Chip8CpuInstructions::LdFVx(0xc)));
assert!(matches!(Chip8Computer::decode_instruction(0xfd33), Chip8CpuInstructions::LdBVx(0xd)));
assert!(matches!(Chip8Computer::decode_instruction(0xfe55), Chip8CpuInstructions::LdIVx(0xe)));
assert!(matches!(Chip8Computer::decode_instruction(0xf365), Chip8CpuInstructions::LdVxI(0x3)));
}
#[test]
fn decoder_test_invalid_instructions() {
// 'bad' instructions that should be dropped...
// assert!(matches!(Chip8Computer::decode_instruction(0x5ab1), Chip8CpuInstructions::XXXXERRORINSTRUCTION));
// assert!(matches!(Chip8Computer::decode_instruction(0x8ab8), Chip8CpuInstructions::XXXXERRORINSTRUCTION));
// assert!(matches!(Chip8Computer::decode_instruction(0xeaba), Chip8CpuInstructions::XXXXERRORINSTRUCTION));
}
}

View File

@ -31,7 +31,7 @@ pub enum Chip8CpuInstructions {
LdDtVx(u16), // 0xFx15 Set Delay Timer
LdStVx(u16), // 0xFx18 Set Sount Timer
AddIVx(u16), // 0xFx1E I = I + Vx
LdFVu(u16), // 0xFx29 Set I = Location of sprite for Digit Vx
LdFVx(u16), // 0xFx29 Set I = Location of sprite for Digit Vx
LdBVx(u16), // 0xFx33 Store BCD of Vx in I, I+1, I+2
LdIVx(u16), // 0xFx55 Store V0 to Vx in memory starting at I
LdVxI(u16), // 0xFx65 Load V0 to Vx in memory starting at I
@ -170,7 +170,7 @@ impl Chip8Instruction for Chip8CpuInstructions {
Chip8CpuInstructions::AddIVx(_) => {
input
}
Chip8CpuInstructions::LdFVu(_) => {
Chip8CpuInstructions::LdFVx(_) => {
input
}
Chip8CpuInstructions::LdBVx(_) => {

View File

@ -1,6 +1,8 @@
use glium::RawUniformValue::Vec2;
use image::load;
use imgui::sys::ImColor;
use imgui::{ImColor32, Ui};
use log::{debug, trace};
use ratatui::{style::Style, widgets::Widget};
use crate::constants::{CHIP8_MEMORY_SIZE, CHIP8_VIDEO_HEIGHT, CHIP8_VIDEO_WIDTH};
@ -30,9 +32,13 @@ pub struct Chip8SystemMemory {
impl Default for Chip8SystemMemory {
fn default() -> Self {
Self {
memory: Chip8SystemMemory::load_to_memory([0x00; CHIP8_MEMORY_SIZE as usize]),
}
let mut x = Chip8SystemMemory {
memory: [0x00; CHIP8_MEMORY_SIZE as usize],
};
x.load_fonts_to_memory();
x
}
}
@ -54,30 +60,32 @@ const cell_width: i32 = 5i32;
const cell_height: i32 = 5i32;
impl Chip8SystemMemory {
pub fn poke_multi(&mut self, start_offset: u16, data: Vec<u8>) {
// loop through the data...
// poke all the data into memory offset by the start
let data_len = data.len();
for i in 0..data_len {
self.poke(start_offset + i as u16, data[i]);
}
}
pub fn peek(self, address: u16) -> u8 {
trace!("PEEK: {} / {}", address, self.memory[address as usize].clone());
self.memory[address as usize]
}
pub fn poke(mut self, address: u16, value: u8) -> Chip8SystemMemory {
pub fn poke(&mut self, address: u16, value: u8) {
trace!("POKE: {} / {} to {}", address, self.memory[address as usize], value);
self.memory[address as usize] = value;
self
}
pub fn load_program(to_load_into: [u8; CHIP8_MEMORY_SIZE as usize], program_data: Box<Vec<u8>>) -> [u8; CHIP8_MEMORY_SIZE as usize] {
let mut load_target = to_load_into.clone();
// loop through the program data starting at 0x200
for load_index in 0..program_data.len() {
load_target[load_index + CHIP8_PROGRAM_LOAD_OFFSET as usize] = program_data[load_index];
pub fn load_program(&mut self, program_to_load: Box<Vec<u8>>) {
for load_index in 0..program_to_load.len() {
self.poke((load_index + 0x200) as u16, program_to_load[load_index]);
}
}
load_target
}
pub fn load_to_memory(
to_load_into: [u8; CHIP8_MEMORY_SIZE as usize],
) -> [u8; CHIP8_MEMORY_SIZE as usize] {
let mut working = to_load_into.clone();
pub fn load_fonts_to_memory(&mut self) {
let all_font_characters = [
CHIP8FONT_0,
CHIP8FONT_1,
@ -100,10 +108,42 @@ impl Chip8SystemMemory {
for (font_index, current_font) in all_font_characters.iter().enumerate() {
for font_mem_offset in 0..=4 {
let real_offset = font_index * 5 + font_mem_offset;
working[real_offset] = current_font[font_mem_offset];
self.poke(real_offset as u16, current_font[font_mem_offset]);
}
}
println!("__FINISHED LOADING FONTS__");
working
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn smoke() {
assert!(true)
}
#[test]
fn model_smoke() {
let m = Chip8SystemMemory::default();
for i in 0..5 {
assert_eq!(m.peek(i), CHIP8FONT_0[i as usize]);
}
assert_eq!(m.peek((CHIP8_MEMORY_SIZE - 1) as u16), 0);
}
#[test]
fn known_data_loaded_correctly() {
let to_load = [ 0x01, 0x02, 0x03, 0x04, 0x05 , 0x06 ];
let mut x = Chip8SystemMemory::default();
for (index, value) in [1..10].iter().enumerate() {
assert_ne!(x.peek(0), 0x01);
x.poke(0, 0x01);
assert_eq!(x.peek(0), 0x01);
}
}
}