more test etc.

video needs stuff bad
This commit is contained in:
Trevor Merritt 2024-09-28 22:20:51 -04:00
parent 731f20d894
commit 24ea413848
10 changed files with 176 additions and 1315 deletions

1243
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,3 @@
[workspace] [workspace]
members = ["chip8_toy","chip8_core", "emma"] members = ["emma"]
resolver = "2" resolver = "2"

View File

@ -1,5 +1,5 @@
use std::io::{stdout, Result}; use std::io::{stdout, Result};
/*
use emmaemu::{chip8::{computer::Chip8Computer, video::Chip8Video}, constants::{CHIP8_MEMORY_SIZE, CHIP8_REGISTER_COUNT, CHIP8_ROM_SIZE, CHIP8_VIDEO_MEMORY}}; use emmaemu::{chip8::{computer::Chip8Computer, video::Chip8Video}, constants::{CHIP8_MEMORY_SIZE, CHIP8_REGISTER_COUNT, CHIP8_ROM_SIZE, CHIP8_VIDEO_MEMORY}};
use ratatui::{ use ratatui::{
backend::CrosstermBackend, backend::CrosstermBackend,
@ -162,3 +162,7 @@ mod test {
assert_eq!(Chip8Video::new(grid_data).format_as_string(), expected_data); assert_eq!(Chip8Video::new(grid_data).format_as_string(), expected_data);
} }
} }
*/
fn main() {
println!("Taxation is theft");
}

View File

@ -53,8 +53,8 @@ impl EmmaGui {
let mut buffer = Vec::new(); let mut buffer = Vec::new();
println!("PREPARING TO LOAD 1-chip8-logo.ch8"); println!("PREPARING TO LOAD 1-chip8-logo.ch8");
// let mut input_file = File::open(Path::new("./1-chip8-logo.ch8")).expect("put 1-chip8-logo.ch8 in this directory"); let mut input_file = File::open(Path::new("./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("./coraxhard.ch8")).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());
} }

View File

@ -99,34 +99,6 @@ mod test {
assert!(true) assert!(true)
} }
#[test]
fn decoder_test_invalid_instructions() {
// 'bad' instructions that should be dropped...
// 5xy0 is the only valid 5 series.
assert!(matches!(Chip8CpuInstructions::decode(0x5ab1), Chip8CpuInstructions::XXXXERRORINSTRUCTION));
assert!(matches!(Chip8CpuInstructions::decode(0x5abf), Chip8CpuInstructions::XXXXERRORINSTRUCTION));
// 8__8 -> 8__D and 8__F are invalid
assert!(matches!(Chip8CpuInstructions::decode(0x8ab8), Chip8CpuInstructions::XXXXERRORINSTRUCTION));
assert!(matches!(Chip8CpuInstructions::decode(0x8abd), Chip8CpuInstructions::XXXXERRORINSTRUCTION));
assert!(matches!(Chip8CpuInstructions::decode(0x8abf), Chip8CpuInstructions::XXXXERRORINSTRUCTION));
// 9__1 -> 9__F are invalid
assert!(matches!(Chip8CpuInstructions::decode(0x9ab1), Chip8CpuInstructions::XXXXERRORINSTRUCTION));
assert!(matches!(Chip8CpuInstructions::decode(0x9abf), Chip8CpuInstructions::XXXXERRORINSTRUCTION));
// Only valid E suffixes are 9E and A1
assert!(matches!(Chip8CpuInstructions::decode(0xea9d), Chip8CpuInstructions::XXXXERRORINSTRUCTION));
assert!(matches!(Chip8CpuInstructions::decode(0xea9f), Chip8CpuInstructions::XXXXERRORINSTRUCTION));
assert!(matches!(Chip8CpuInstructions::decode(0xeaa0), Chip8CpuInstructions::XXXXERRORINSTRUCTION));
assert!(matches!(Chip8CpuInstructions::decode(0xeaa2), Chip8CpuInstructions::XXXXERRORINSTRUCTION));
// oh god f is a mess.
assert!(matches!(Chip8CpuInstructions::decode(0xf006), Chip8CpuInstructions::XXXXERRORINSTRUCTION));
assert!(matches!(Chip8CpuInstructions::decode(0xf008), Chip8CpuInstructions::XXXXERRORINSTRUCTION));
}
#[test] #[test]
fn new_with_program() { fn new_with_program() {

View File

@ -410,17 +410,11 @@ impl Chip8CpuInstructions {
} }
// 0x6xkk Set Vx = kk // 0x6xkk Set Vx = kk
Chip8CpuInstructions::LdVxByte(register, byte) => { Chip8CpuInstructions::LdVxByte(register, byte) => {
let start_value = input.registers.peek(*register as u8); input.registers.poke(*register as u8, *byte as u8);
let byte_value = *byte as u8;
// println!("SETTING REGISTER [{register}] FROM [{start_value}] to [{byte_value}] by loading.");
input.registers.poke(*register as u8, byte_value);
} }
// 0x7xkk Set Vx = Vx + kk // 0x7xkk Set Vx = Vx + kk
Chip8CpuInstructions::AddVxByte(vx_register, byte) => { Chip8CpuInstructions::AddVxByte(vx_register, byte) => {
let to_add = *byte as u8; input.registers.poke(*vx_register as u8, (input.registers.peek(*vx_register as u8) + *byte as u8));
let old_value = input.registers.peek(*vx_register as u8);
println!("Adding [{old_value}] from register [{vx_register}] to [{to_add}] ");
input.registers.poke(*vx_register as u8, (old_value + to_add));
} }
// 0x8xy0 Set value of Vy in Vx // 0x8xy0 Set value of Vy in Vx
Chip8CpuInstructions::LdVxVy(x, y) => { Chip8CpuInstructions::LdVxVy(x, y) => {
@ -428,28 +422,20 @@ impl Chip8CpuInstructions {
} }
// 0x8xy1 Set Vx = Vx OR Vy // 0x8xy1 Set Vx = Vx OR Vy
Chip8CpuInstructions::OrVxVy(x, y) => { Chip8CpuInstructions::OrVxVy(x, y) => {
let lhs = input.registers.peek(*x as u8); input.registers.poke(*x as u8, input.registers.peek(*x as u8) | input.registers.peek(*y as u8));
let rhs = input.registers.peek(*y as u8);
input.registers.poke(*x as u8, lhs | rhs);
} }
// 0x8xy2 Set Vx = Vx AND Vy // 0x8xy2 Set Vx = Vx AND Vy
Chip8CpuInstructions::AndVxVy(x, y) => { Chip8CpuInstructions::AndVxVy(x, y) => {
let lhs = input.registers.peek(*x as u8); input.registers.poke(*x as u8, input.registers.peek(*x as u8) & input.registers.peek(*y as u8));
let rhs = input.registers.peek(*y as u8);
input.registers.poke(*x as u8, lhs & rhs);
} }
// 0x8xy3 Set Vx = Vx XOR Vy // 0x8xy3 Set Vx = Vx XOR Vy
Chip8CpuInstructions::XorVxVy(x, y) => { Chip8CpuInstructions::XorVxVy(x, y) => {
let lhs = input.registers.peek(*x as u8); input.registers.poke(*x as u8, input.registers.peek(*x as u8) ^ input.registers.peek(*y as u8));
let rhs = input.registers.peek(*y as u8);
input.registers.poke(*x as u8, lhs ^ rhs);
} }
// 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);
let rhs = input.registers.peek(*y as u8); let rhs = input.registers.peek(*y as u8);
let working = (lhs as i16 + rhs as i16) as i16; let working = (lhs as i16 + rhs as i16) as i16;
if working > 255 { if working > 255 {
input.registers.poke(0xf, 0x01); input.registers.poke(0xf, 0x01);
@ -461,9 +447,7 @@ impl Chip8CpuInstructions {
// Set Vx = Vx - Vy, set VF = NOT borrow. // Set Vx = Vx - Vy, set VF = NOT borrow.
// //
// If Vx > Vy, then VF is set to 1, otherwise 0. Then Vy is subtracted from Vx, and the results stored in Vx. // If Vx > Vy, then VF is set to 1, otherwise 0. Then Vy is subtracted from Vx, and the results stored in Vx.
let lhs = input.registers.peek(*x as u8); input.registers.poke(*x as u8, input.registers.peek(*x as u8) - input.registers.peek(*y as u8));
let rhs = input.registers.peek(*y as u8);
input.registers.poke(*x as u8, lhs - rhs);
} }
Chip8CpuInstructions::ShrVxVy(x, y) => { Chip8CpuInstructions::ShrVxVy(x, y) => {
// 8xy6 - SHR Vx {, Vy} // 8xy6 - SHR Vx {, Vy}
@ -493,7 +477,6 @@ impl Chip8CpuInstructions {
// 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 initial_value = input.registers.peek(*x as u8); let initial_value = input.registers.peek(*x as u8);
if 0x80 & initial_value == 0x80 { if 0x80 & initial_value == 0x80 {
input.registers.poke(0xf, 1); input.registers.poke(0xf, 1);
@ -517,6 +500,7 @@ impl Chip8CpuInstructions {
// Set I = nnn. // Set I = nnn.
// //
// The value of register I is set to nnn. // The value of register I is set to nnn.
println!("SETTING I to {new_index}");
input.registers.poke_i(*new_index); input.registers.poke_i(*new_index);
} }
// 0xBnnn Jump to nnn+V0 // 0xBnnn Jump to nnn+V0
@ -525,8 +509,7 @@ impl Chip8CpuInstructions {
// Jump to location nnn + V0. // Jump to location nnn + V0.
// //
// The program counter is set to nnn plus the value of V0. // The program counter is set to nnn plus the value of V0.
let x_reg = input.registers.peek(0); input.registers.poke_pc(input.registers.peek(0) as u16 + addr);
input.registers.poke_pc(x_reg as u16 + addr);
} }
Chip8CpuInstructions::RndVxByte(x, byte) => { Chip8CpuInstructions::RndVxByte(x, byte) => {
// Cxkk - RND Vx, byte // Cxkk - RND Vx, byte
@ -579,11 +562,15 @@ impl Chip8CpuInstructions {
let key_to_check = input.registers.peek(*x as u8); let key_to_check = input.registers.peek(*x as u8);
} }
Chip8CpuInstructions::SnkpVx(x) => { Chip8CpuInstructions::SnkpVx(x) => {
// ExA1 - SKNP Vx // ExA1 - SKNP Vx
// Skip next instruction if key with the value of Vx is not pressed. // Skip next instruction if key with the value of Vx is not pressed.
// //
// Checks the keyboard, and if the key corresponding to the value of Vx is currently in the up position, PC is increased by 2. // Checks the keyboard, and if the key corresponding to the value of Vx is currently in the up position, PC is increased by 2.
let key_to_check = input.registers.peek(*x as u8);
let is_pressed = input.keypad.pressed(*x as u8);
if is_pressed {
input.registers.advance_pc();
}
} }
Chip8CpuInstructions::LdVxDt(x) => { Chip8CpuInstructions::LdVxDt(x) => {
// Fx07 - LD Vx, DT // Fx07 - LD Vx, DT
@ -605,11 +592,9 @@ impl Chip8CpuInstructions {
// //
// DT is set equal to the value of Vx. // DT is set equal to the value of Vx.
let new_time = input.registers.peek(*source_register as u8); let new_time = input.registers.peek(*source_register as u8);
println!("SETTING DELAY TIMER TO [{}]", new_time);
input.delay_timer.set_timer(new_time as i32); input.delay_timer.set_timer(new_time as i32);
} }
Chip8CpuInstructions::LdStVx(new_time) => { Chip8CpuInstructions::LdStVx(new_time) => {
println!("SETTING SOUND TIMER TO [0x{:2x}]", *new_time);
input.sound_timer.set_timer(*new_time as i32); input.sound_timer.set_timer(*new_time as i32);
} }
Chip8CpuInstructions::AddIVx(x) => { Chip8CpuInstructions::AddIVx(x) => {
@ -742,7 +727,20 @@ mod test {
assert!(matches!(Chip8CpuInstructions::decode(0xfd33), Chip8CpuInstructions::LdBVx(0xd))); assert!(matches!(Chip8CpuInstructions::decode(0xfd33), Chip8CpuInstructions::LdBVx(0xd)));
assert!(matches!(Chip8CpuInstructions::decode(0xfe55), Chip8CpuInstructions::LdIVx(0xe))); assert!(matches!(Chip8CpuInstructions::decode(0xfe55), Chip8CpuInstructions::LdIVx(0xe)));
assert!(matches!(Chip8CpuInstructions::decode(0xf365), Chip8CpuInstructions::LdVxI(0x3))); assert!(matches!(Chip8CpuInstructions::decode(0xf365), Chip8CpuInstructions::LdVxI(0x3)));
}
#[test]
fn decoder_test_invalid_instructions() {
let invalid_to_encode = [
0x5ab1, 0x5abf, 0x8ab8, 0x8abd, 0x8abf,
0x9ab1, 0x9abf, 0xea9d, 0xea9f, 0xeaa0,
0xeaa2, 0xf006, 0xf008
];
for i in invalid_to_encode {
assert_eq!(Chip8CpuInstructions::decode(i).encode(), 0xffff);
assert!(matches!(Chip8CpuInstructions::decode(i), Chip8CpuInstructions::XXXXERRORINSTRUCTION));
}
} }
/// START OF THE EXECUTION TESTS /// START OF THE EXECUTION TESTS
@ -997,11 +995,46 @@ mod test {
assert_eq!(x.registers.peek_pc(), 0x208); assert_eq!(x.registers.peek_pc(), 0x208);
} }
fn SneVxVy_test() {} #[test]
fn SneVxVy_test() {
// 9xy0 - SNE Vx, Vy
// Skip next instruction if Vx != Vy.
//
// The values of Vx and Vy are compared, and if they are not equal, the program counter is increased by 2.
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0x01, 0xab).execute(&mut x);
Chip8CpuInstructions::LdVxByte(0x02, 0xba).execute(&mut x);
// they are not the same. we should skip.
assert_eq!(x.registers.peek_pc(), 0x204);
Chip8CpuInstructions::SneVxVy(0x01, 0x02).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0x208);
fn LdiAddr_test() {} Chip8CpuInstructions::LdVxByte(0x02, 0xab).execute(&mut x);
Chip8CpuInstructions::SneVxVy(0x01, 0x02).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0x20C);
}
fn JpV0Addr_test() {} #[test]
fn LdiAddr_test() {
// Annn - LD I, addr
// Set I = nnn.
//
// The value of register I is set to nnn.
let mut x = Chip8Computer::new();
let value_for_memory = 0xbe;
// load the value into V0
Chip8CpuInstructions::LdIAddr(0xfab).execute(&mut x);
assert_eq!(x.registers.peek_i(), 0xfab);
}
fn JpV0Addr_test() {
// Bnnn - JP V0, addr
// Jump to location nnn + V0.
//
// The program counter is set to nnn plus the value of V0.
}
#[test] #[test]
fn RndVxByte_test() { fn RndVxByte_test() {

View File

@ -1,3 +1,4 @@
use log::debug;
/// Registers. numbered 1-16 publicly. /// Registers. numbered 1-16 publicly.
/// Privately using zero base array so -1 to shift from pub to priv. /// Privately using zero base array so -1 to shift from pub to priv.
@ -33,6 +34,7 @@ impl Chip8Registers {
} }
pub fn poke_i(&mut self, new_value: u16) { pub fn poke_i(&mut self, new_value: u16) {
println!("REGISTER: Setting I to {new_value}");
self.i_register = new_value; self.i_register = new_value;
} }
pub fn peek(&self, register_number: u8) -> u8 { pub fn peek(&self, register_number: u8) -> u8 {

View File

@ -5,6 +5,7 @@ use beep::beep;
pub struct SoundTimer { pub struct SoundTimer {
counter: i32 counter: i32
} }
impl SoundTimer { impl SoundTimer {
pub fn current(&self) -> i32 { pub fn current(&self) -> i32 {
self.counter self.counter
@ -33,7 +34,6 @@ impl SoundTimer {
} }
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;

View File

@ -42,20 +42,6 @@ impl Default for Chip8SystemMemory {
} }
} }
impl Widget for Chip8SystemMemory {
fn render(self, area: ratatui::prelude::Rect, buf: &mut ratatui::prelude::Buffer)
where
Self: Sized,
{
// build the text version of the system memory...
// ...and stuff it in the display.
let style = Style::new();
let string = String::new();
buf.set_string(0, 0, string, style)
}
// Display the system memory as a widget
}
const cell_width: i32 = 5i32; const cell_width: i32 = 5i32;
const cell_height: i32 = 5i32; const cell_height: i32 = 5i32;
@ -71,8 +57,6 @@ impl Chip8SystemMemory {
self.memory[address as usize] = value; self.memory[address as usize] = value;
} }
pub fn load_program(&mut self, program_to_load: Box<Vec<u8>>) { pub fn load_program(&mut self, program_to_load: Box<Vec<u8>>) {
for load_index in 0..program_to_load.len() { for load_index in 0..program_to_load.len() {
self.poke((load_index + 0x200) as u16, program_to_load[load_index]); self.poke((load_index + 0x200) as u16, program_to_load[load_index]);

View File

@ -4,13 +4,31 @@ use ratatui::{layout::Rect, style::Style, widgets::Widget};
use crate::chip8::computer::Chip8Computer; use crate::chip8::computer::Chip8Computer;
use crate::constants::CHIP8_VIDEO_MEMORY; use crate::constants::CHIP8_VIDEO_MEMORY;
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct Chip8Video { pub struct Chip8Video {
memory: [bool; CHIP8_VIDEO_MEMORY] memory: [bool; CHIP8_VIDEO_MEMORY]
} }
impl Chip8Video { impl Chip8Video {
pub fn as_64bit(&self) -> Vec<u64> {
let mut to_return = vec![];
for row_in_video in 0..32 {
let mut working_row = 0u64;
for bit_in_video in 0..64 {
let data_offset = row_in_video * 64 + bit_in_video;
let to_convert = self.memory[data_offset];
let shifted_bit = if to_convert {
1 << bit_in_video
} else { 0 };
working_row = working_row | shifted_bit;
}
to_return.push(working_row);
}
to_return
}
pub fn new(initial_configuration: [bool; CHIP8_VIDEO_MEMORY]) -> Self { pub fn new(initial_configuration: [bool; CHIP8_VIDEO_MEMORY]) -> Self {
Self { Self {
memory: initial_configuration memory: initial_configuration
@ -26,6 +44,19 @@ impl Chip8Video {
self.to_owned() self.to_owned()
} }
pub fn poke_byte(&mut self, first_address: u16, to_write: u8) -> Self {
println!("PREPARING TO POKE {to_write:b} to {first_address:4x}");
for i in (0..8).rev() {
let shifted = ((1 << i) & to_write) >> i;
//
let target_address = first_address + (7 - i);
let is_set = shifted == 1;
println!("POKE {} with {} / {shifted:8b}", target_address, is_set);
self.poke(target_address, is_set);
}
self.to_owned()
}
pub fn format_as_string(self) -> String { pub fn format_as_string(self) -> String {
let mut output = String::new(); let mut output = String::new();
for row in 0..32 { for row in 0..32 {
@ -110,14 +141,15 @@ mod test {
#[test] #[test]
fn set_initial_memory() { fn set_initial_memory() {
let mut initial_memory = [false; CHIP8_VIDEO_MEMORY]; let mut x = Chip8Video::default();
// let mut initial_memory = [false; CHIP8_VIDEO_MEMORY];
let mut ws = String::new(); let mut ws = String::new();
// set our checkerboard // set our checkerboard
for cbr in 0..32 { for cbr in 0..32 {
for cbc in 0..64 { for cbc in 0..64 {
let dof = cbr * 64 + cbc; let dof = cbr * 64 + cbc;
if (dof as i32 % 2) == 0 { if (dof as i32 % 2) == 0 {
initial_memory[dof] = true; x.poke(dof, true);
ws += "*"; ws += "*";
} else { } else {
ws += " "; ws += " ";
@ -125,36 +157,37 @@ mod test {
} }
ws += "\n"; ws += "\n";
} }
let set_x = Chip8Video::new(initial_memory); assert_eq!(x.format_as_string(), ws);
assert_eq!(set_x.format_as_string(), ws);
} }
/*
#[test] #[test]
fn set_sprite_test() { fn poke_byte() {
let to_poke = 0b11001111;
let mut x = Chip8Video::default(); let mut x = Chip8Video::default();
let sprite = vec![0b00110011, x.poke_byte(0x05, to_poke);
0b11001100, let mut expected = String::new();
0b01010101, expected = " ** **** \n".to_string();
0b10101010]; for i in 0..31 {
expected += &*(" ".repeat(64) + "\n");
x.write_sprite(sprite.clone(), (0,0));
for sprite_row in 0..sprite.len() {
for bit_in_row in 0..8 {
let data_offset = sprite_row * 8 + bit_in_row;
let test_bit = 1 << bit_in_row;
// now we have a 1 in the 'right' place
let test_value = test_bit & sprite[sprite_row];
// if we found a bit where we looked
if test_bit == test_value {
assert!(x.peek(data_offset as u16));
} else {
// no bit. expect false.
assert!(!x.peek(data_offset as u16));
}
}
} }
assert_eq!(x.format_as_string(), expected);
} }
*/
#[test]
fn poke_sprite() {
let mut expected = String::new();
let to_poke = [
0b11001100,
0b00110011,
0b11001100,
0b00110011
];
// Position at 4,10
// 5,10
// 6,10
// 7,10
let start_address = (4 * 64) + 10;
}
} }