go to lunch

This commit is contained in:
Trevor Merritt 2024-10-02 12:02:50 -04:00
parent 9d3fabe0c3
commit ef1b6e70f7
6 changed files with 145 additions and 63 deletions

View File

@ -72,12 +72,8 @@ impl Chip8Computer {
let high_byte = (self.memory.clone().peek(start_pc) as u16).rotate_left(8); let high_byte = (self.memory.clone().peek(start_pc) as u16).rotate_left(8);
let low_byte = self.memory.clone().peek(start_pc + 1) as u16; let low_byte = self.memory.clone().peek(start_pc + 1) as u16;
let result = high_byte | low_byte; let result = high_byte | low_byte;
debug!("JOINED BYTES [{high_byte:2x}] and [{low_byte:2x}] to build [{result:4x}]");
let decoded_instruction = let decoded_instruction =
Chip8CpuInstructions::decode(result); Chip8CpuInstructions::decode(result);
debug!("PREPARING TO EXECUTE {:4x} as {:?}", result, decoded_instruction);
// println!("DECODED INSTRUCTION = {:?}", decoded_instruction);
// start by moving to the next instruction
// todo: THIS IS BAD AND IS A SIDE EFFECT // todo: THIS IS BAD AND IS A SIDE EFFECT
decoded_instruction.execute(self); decoded_instruction.execute(self);

View File

@ -367,8 +367,6 @@ impl Chip8CpuInstructions {
0xE09E..=0xEFA1 => { 0xE09E..=0xEFA1 => {
match last_byte { match last_byte {
0x9E => { 0x9E => {
println!("DECODING {:4x}", input);
println!("UBLN: {:4x}", ubln);
Chip8CpuInstructions::SkpVx(ubln) Chip8CpuInstructions::SkpVx(ubln)
} }
0xA1 => { 0xA1 => {
@ -380,7 +378,6 @@ impl Chip8CpuInstructions {
} }
} }
0xF007..=0xFF65 => { 0xF007..=0xFF65 => {
// println!("COMPARING LAST BYTE FROM TODECODE: {:2x} to {:4x} with {:2x}", last_byte, input, ubln);
match last_byte { match last_byte {
0x07 => { 0x07 => {
Chip8CpuInstructions::LdVxDt(ubln) Chip8CpuInstructions::LdVxDt(ubln)
@ -426,6 +423,7 @@ impl Chip8CpuInstructions {
let _ = match self { let _ = match self {
// 0x0nnn Exit to System Call // 0x0nnn Exit to System Call
Chip8CpuInstructions::SysAddr(new_address) => { Chip8CpuInstructions::SysAddr(new_address) => {
debug!("SysAddr [0x{new_address:3x}]");
// println!("SYS TO [{new_address}]"); // println!("SYS TO [{new_address}]");
input.registers.poke_pc(*new_address as u16); input.registers.poke_pc(*new_address as u16);
} }
@ -441,23 +439,26 @@ impl Chip8CpuInstructions {
} }
// 0x1nnn Jump to Address // 0x1nnn Jump to Address
Chip8CpuInstructions::JpAddr(new_address) => { Chip8CpuInstructions::JpAddr(new_address) => {
debug!("JpAddr [0x{new_address:3x}]");
input.registers.poke_pc(*new_address as u16); input.registers.poke_pc(*new_address as u16);
} }
// 0x2nnn Call Subroutine // 0x2nnn Call Subroutine
Chip8CpuInstructions::CallAddr(new_address) => { Chip8CpuInstructions::CallAddr(new_address) => {
debug!("CALL ADDR {new_address}"); debug!("CALL ADDR 0x{new_address:3x}");
} }
// 0x3xkk Skip next instruction if Vx = kk. // 0x3xkk Skip next instruction if Vx = kk.
Chip8CpuInstructions::SeVxByte(vx_register, byte) => { Chip8CpuInstructions::SeVxByte(vx_register, byte) => {
if input.registers.peek(*vx_register as u8) == *byte as u8 { if input.registers.peek(*vx_register as u8) == *byte as u8 {
input.registers.advance_pc(); input.registers.advance_pc();
} }
debug!("SeVxByte [0x{vx_register:1x}] [0x{byte:2x}]");
} }
// 0x4xkk Skip next instruction if Vx != kk // 0x4xkk Skip next instruction if Vx != kk
Chip8CpuInstructions::SneVxByte(x, byte) => { Chip8CpuInstructions::SneVxByte(x, byte) => {
if input.registers.peek(*x as u8) != *byte as u8 { if input.registers.peek(*x as u8) != *byte as u8 {
input.registers.advance_pc(); input.registers.advance_pc();
} }
debug!("SneVxByte [0x{x:1x}] [0x{byte:2x}");
} }
// 0x5xy0 Skip next instruction if Vx == Vy // 0x5xy0 Skip next instruction if Vx == Vy
Chip8CpuInstructions::SeVxVy(x, y) => { Chip8CpuInstructions::SeVxVy(x, y) => {
@ -467,27 +468,33 @@ impl Chip8CpuInstructions {
if lhs == rhs { if lhs == rhs {
input.registers.advance_pc(); input.registers.advance_pc();
} }
debug!("SeVxVy [0x{x:1x}] [0x{y:1x}]");
} }
// 0x6xkk Set Vx = kk // 0x6xkk Set Vx = kk
Chip8CpuInstructions::LdVxByte(register, byte) => { Chip8CpuInstructions::LdVxByte(register, byte) => {
let byte_value = *byte as u8; let byte_value = *byte as u8;
input.registers.poke(*register as u8, byte_value); input.registers.poke(*register as u8, byte_value);
debug!("LdVxByte [0x{register:1x}] [0x{byte:2.0x}]");
} }
// 0x7xkk Set Vx = Vx + kk // 0x7xkk Set Vx = Vx + kk
Chip8CpuInstructions::AddVxByte(vx_register, byte) => { Chip8CpuInstructions::AddVxByte(vx_register, byte) => {
input.registers.poke(*vx_register as u8, (input.registers.peek(*vx_register as u8) + *byte as u8)); input.registers.poke(*vx_register as u8, (input.registers.peek(*vx_register as u8) + *byte as u8));
debug!("AddVxByte [0x{vx_register:1x}] [0x{byte:2x}]");
} }
// 0x8xy0 Set value of Vy in Vx // 0x8xy0 Set value of Vy in Vx
Chip8CpuInstructions::LdVxVy(x, y) => { Chip8CpuInstructions::LdVxVy(x, y) => {
input.registers.poke(*x as u8, input.registers.peek(*y as u8)); input.registers.poke(*x as u8, input.registers.peek(*y as u8));
debug!("LdVxVy [0x{x:1x}] [0x{y:1x}]");
} }
// 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)); input.registers.poke(*x as u8, input.registers.peek(*x as u8) | input.registers.peek(*y as u8));
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)); input.registers.poke(*x as u8, input.registers.peek(*x as u8) & input.registers.peek(*y as u8));
debug!("AndVxVy [0x{x:1x}] [0x{y:1x}]");
} }
// 0x8xy3 Set Vx = Vx XOR Vy // 0x8xy3 Set Vx = Vx XOR Vy
Chip8CpuInstructions::XorVxVy(x, y) => { Chip8CpuInstructions::XorVxVy(x, y) => {
@ -510,7 +517,7 @@ impl Chip8CpuInstructions {
// 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.
input.registers.poke(*x as u8, input.registers.peek(*x as u8) - input.registers.peek(*y as u8)); input.registers.poke(*x as u8, input.registers.peek(*x as u8) - input.registers.peek(*y as u8));
} }
Chip8CpuInstructions::ShrVxVy(x, y) => { Chip8CpuInstructions::ShrVxVy(x, _) => {
// 8xy6 - SHR Vx {, Vy} // 8xy6 - SHR Vx {, Vy}
// Set Vx = Vx SHR 1. // Set Vx = Vx SHR 1.
// //
@ -533,7 +540,7 @@ impl Chip8CpuInstructions {
input.registers.poke(*x as u8, x_register - y_register); input.registers.poke(*x as u8, x_register - y_register);
} }
Chip8CpuInstructions::ShlVxVy(x, y) => { Chip8CpuInstructions::ShlVxVy(x, _) => {
// 8xyE - SHL Vx {, Vy} // 8xyE - SHL Vx {, Vy}
// Set Vx = Vx SHL 1. // Set Vx = Vx SHL 1.
// //
@ -561,7 +568,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}"); debug!("LdiAddr [0x{new_index:3x}]");
input.registers.poke_i(*new_index); input.registers.poke_i(*new_index);
} }
// 0xBnnn Jump to nnn+V0 // 0xBnnn Jump to nnn+V0
@ -598,7 +605,7 @@ impl Chip8CpuInstructions {
let num_bytes_to_read = *n; let num_bytes_to_read = *n;
for i in start_position..start_position + num_bytes_to_read { for i in start_position..start_position + num_bytes_to_read {
// let current_byte = input.memory[i as usize]; // let current_byte = input.memory[i as usize];
// println!("READ BYTE [{current_byte:8b}"); println!("READ BYTE [0x{i:2x}]");
} }
let mut did_change: bool = false; let mut did_change: bool = false;
@ -631,9 +638,8 @@ impl Chip8CpuInstructions {
// and if the key corresponding to the value of Vx is currently in the up position, // and if the key corresponding to the value of Vx is currently in the up position,
// PC is increased by 2. // PC is increased by 2.
let target_key = input.registers.peek(*x as u8); let target_key = input.registers.peek(*x as u8);
println!("TESTING REGISTER {x} -> READ {target_key}");
let is_pressed = input.keypad.pressed(target_key); let is_pressed = input.keypad.pressed(target_key);
println!("KEY STATE = {is_pressed}"); debug!("SnKpVx [{x:1x}]");
if is_pressed { if is_pressed {
input.registers.advance_pc(); input.registers.advance_pc();
} }
@ -713,7 +719,6 @@ impl Chip8CpuInstructions {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use ratatui::crossterm::execute;
use super::*; use super::*;
#[test] #[test]
@ -836,7 +841,7 @@ mod test {
// ** test moved up so it can be used later // ** test moved up so it can be used later
#[test] #[test]
fn LdVxByte_test() { fn ld_vx_byte_test() {
// 0x6xkk Set Vx = kk // 0x6xkk Set Vx = kk
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(1, 0x12).execute(&mut x); Chip8CpuInstructions::LdVxByte(1, 0x12).execute(&mut x);
@ -886,7 +891,7 @@ mod test {
} }
#[test] #[test]
fn SeVxVy_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); Chip8CpuInstructions::LdVxByte(0x01, 0x84).execute(&mut x);
@ -908,7 +913,7 @@ mod test {
} }
#[test] #[test]
fn LdVxVy_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); Chip8CpuInstructions::LdVxByte(0x01, 0x01).execute(&mut x);
@ -921,7 +926,7 @@ mod test {
} }
#[test] #[test]
fn OrVxVy_test() { fn or_vx_vy_test() {
// 0x8xy1 Set Vx = Vx OR Vy // 0x8xy1 Set Vx = Vx OR Vy
// 0b0101 0000 (0x50) // 0b0101 0000 (0x50)
// | 0b0000 1010 (0x0A) // | 0b0000 1010 (0x0A)
@ -935,7 +940,7 @@ mod test {
} }
#[test] #[test]
fn AndVxVy_test() { fn and_vx_vy_test() {
// 0x8xy2 Set Vx = Vx AND Vy // 0x8xy2 Set Vx = Vx AND Vy
// 0b1111 1100 (0xFC) // 0b1111 1100 (0xFC)
// & 0b1100 1010 (0xCA) // & 0b1100 1010 (0xCA)
@ -949,7 +954,7 @@ mod test {
} }
#[test] #[test]
fn XorVxVy_test() { fn xor_vx_vy_test() {
// 0x8xy3 Set Vx = Vx XOR Vy // 0x8xy3 Set Vx = Vx XOR Vy
// 0b1111 1100 (0xFC) // 0b1111 1100 (0xFC)
// ^ 0b1100 1010 (0xCA) // ^ 0b1100 1010 (0xCA)
@ -963,7 +968,7 @@ mod test {
} }
#[test] #[test]
fn AddVxVy_test() { fn add_vx_vy_test() {
// 0x8xy4 Set Vx = Vx + Vy (SET VF on Carry) // 0x8xy4 Set Vx = Vx + Vy (SET VF on Carry)
// T1 T2: Judgement Test // T1 T2: Judgement Test
// 0x01 0xFF // 0x01 0xFF
@ -991,7 +996,7 @@ mod test {
} }
#[test] #[test]
fn ShrVxVy_test() { fn shr_vx_vy_test() {
/* /*
Set Vx = Vx SHR 1. Set Vx = Vx SHR 1.
@ -1015,7 +1020,7 @@ mod test {
assert_eq!(x.registers.peek_pc(), 0x206); assert_eq!(x.registers.peek_pc(), 0x206);
} }
#[test] #[test]
fn LdiAddr_test() { fn ldi_addr_test() {
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdIAddr(0x123).execute(&mut x); Chip8CpuInstructions::LdIAddr(0x123).execute(&mut x);
assert_eq!(x.registers.peek_i(), 0x123); assert_eq!(x.registers.peek_i(), 0x123);
@ -1023,37 +1028,13 @@ mod test {
} }
#[test] #[test]
fn JpV0Addr_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); Chip8CpuInstructions::LdVxByte(0x0, 0xFF).execute(&mut x);
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);
} }
// #[test]
fn SnKpVx_test() {
// skip key not pressed
let mut x = Chip8Computer::new();
x.keypad.push_key(2);
Chip8CpuInstructions::LdVxByte(0x1, 0x02);
Chip8CpuInstructions::SnkpVx(2).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0x204);
}
#[test]
fn LdVxDt_test() {
// delay timer reading
let mut x = Chip8Computer::new();
// set the value we want in the timer to V0...
Chip8CpuInstructions::LdVxByte(0x1, 0x10).execute(&mut x);
Chip8CpuInstructions::LdDtVx(0x1).execute(&mut x);
// ...then tell the CPU to use that value for the timer.
Chip8CpuInstructions::LdVxDt(0x1).execute(&mut x);
let new_reg_value = x.registers.peek(0x1);
assert_eq!(new_reg_value, 0x1);
}
#[test] #[test]
fn cls_test() { fn cls_test() {
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
@ -1074,15 +1055,6 @@ mod test {
assert_eq!(x.registers.peek_pc(), 0x208); assert_eq!(x.registers.peek_pc(), 0x208);
} }
#[test]
fn lddtvx_test() {
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdDtVx(0x10).execute(&mut x);
assert_eq!(x.delay_timer.current(), 0x10);
x.delay_timer.tick();
x.delay_timer.tick();
assert_eq!(x.delay_timer.current(), 0x0E);
}
#[test] #[test]
fn addivx_test() { fn addivx_test() {
@ -1126,4 +1098,63 @@ mod test {
} }
*/ */
#[test]
fn add_vx_byte_test() {
let mut x = Chip8Computer::new();
// set a value in the register
Chip8CpuInstructions::LdVxByte(0x1, 0xab).execute(&mut x);
// add 0x10 to register
Chip8CpuInstructions::AddVxByte(0x1, 0x10).execute(&mut x);
assert_eq!(x.registers.peek(1), 0xbb);
}
#[test]
fn sub_vx_vy_test() {
let mut x = Chip8Computer::new();
// load values in 2 registers
Chip8CpuInstructions::LdVxByte(0x01, 0x10).execute(&mut x);
Chip8CpuInstructions::LdVxByte(0x02, 0x08).execute(&mut x);
Chip8CpuInstructions::SubVxVy(0x1, 0x02).execute(&mut x);
assert_eq!(x.registers.peek(0xf), 0);
assert_eq!(x.registers.peek(0x1), 0x8);
assert_eq!(x.registers.peek_pc(), 0x206);
}
#[test]
fn sne_vx_vy_test() {
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0x1, 0x10).execute(&mut x);
Chip8CpuInstructions::LdVxByte(0x2, 0x10).execute(&mut x);
Chip8CpuInstructions::SneVxVy(0x1, 0x2).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0x206);
Chip8CpuInstructions::LdVxByte(0x2, 0x00).execute(&mut x);
Chip8CpuInstructions::SneVxVy(0x01, 0x02).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0x20C)
}
#[test]
fn ld_dt_vx_test() {
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0x1, 0x10).execute(&mut x);
Chip8CpuInstructions::LdDtVx(0x1).execute(&mut x);
assert_eq!(x.delay_timer.current(), 0x10);
for i in 0..0x20 {
x.delay_timer.tick();
}
assert_eq!(x.delay_timer.current(), 0);
}
#[test]
fn ld_vx_dt_test() {
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0x1, 0xf0).execute(&mut x);
Chip8CpuInstructions::LdDtVx(0x1).execute(&mut x);
x.delay_timer.tick();
x.delay_timer.tick();
x.delay_timer.tick();
Chip8CpuInstructions::LdVxDt(0x1).execute(&mut x);
assert_eq!(x.registers.peek(0x1), 0xed);
}
} }

View File

@ -34,7 +34,6 @@ 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 {
@ -57,8 +56,24 @@ impl Chip8Registers {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::chip8::registers::Chip8Registers;
#[test] #[test]
fn smoke() { assert!(true) } fn smoke() { assert!(true) }
#[test]
fn register_rw_test() {
let mut x = Chip8Registers::default();
x.poke(0x0, 0xff);
x.poke(0x1, 0xab);
assert_eq!(x.peek(0x0), 0xff);
assert_eq!(x.peek(0x1), 0xab);
}
#[test]
fn i_register_test() {
}
} }

View File

@ -1,5 +1,6 @@
use std::{thread, time}; use std::{thread, time};
use beep::beep; use beep::beep;
use log::trace;
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct SoundTimer { pub struct SoundTimer {
@ -16,12 +17,12 @@ impl SoundTimer {
} }
} }
pub fn set_timer(&mut self, new_value: i32) { pub fn set_timer(&mut self, new_value: i32) {
println!("SETTING SOUND TIMER TO {new_value}"); trace!("SETTING SOUND TIMER TO {new_value}");
self.counter = new_value self.counter = new_value
} }
pub fn tick(&mut self) { pub fn tick(&mut self) {
println!("TICKING SOUND FROM {} to {}", self.counter, self.counter - 1); trace!("TICKING SOUND FROM {} to {}", self.counter, self.counter - 1);
if self.counter > 0 { if self.counter > 0 {
self.counter -= 1; self.counter -= 1;
/* /*

View File

@ -122,6 +122,4 @@ mod test {
assert_eq!(x.peek(0), 0x01); assert_eq!(x.peek(0), 0x01);
} }
} }
} }

View File

@ -160,6 +160,8 @@ mod test {
} }
assert_eq!(x.format_as_string(), expected); assert_eq!(x.format_as_string(), expected);
} }
#[test]
fn cls() { fn cls() {
let mut initial_memory = [false; CHIP8_VIDEO_MEMORY]; let mut initial_memory = [false; CHIP8_VIDEO_MEMORY];
let mut ws = String::new(); let mut ws = String::new();
@ -178,4 +180,43 @@ mod test {
set_x.cls(); set_x.cls();
assert_eq!(set_x.format_as_string(), ws); assert_eq!(set_x.format_as_string(), ws);
} }
#[test]
fn poke_byte_test() {
let to_poke = 0b10101010;
let mut v = Chip8Video::default();
v.poke_byte(0x00, to_poke);
assert!(v.peek(0x00));
assert!(v.peek(0x02));
assert!(v.peek(0x04));
assert!(v.peek(0x06));
assert!(!v.peek(0x01));
assert!(!v.peek(0x03));
assert!(!v.peek(0x05));
assert!(!v.peek(0x07));
}
#[test]
fn poke_multi_line_test() {
let mut v = Chip8Video::default();
let to_poke = [
0b00000000,
0b11111111,
0b10101010,
0b01010101
];
for to_poke_index in to_poke.iter() {
for current_to_poke_bit_index in 0..8 {
let right_shifted_bit = to_poke[*to_poke_index as usize] >> current_to_poke_bit_index;
let offset = to_poke_index * 8 + current_to_poke_bit_index;
v.poke(offset, is_bit_set(to_poke[*to_poke_index as usize] as u8, current_to_poke_bit_index as u8));
}
}
assert!(false);
}
}
fn is_bit_set(to_check: u8, bit_index: u8) -> bool {
(to_check >> bit_index )& 0x1 == 0x1
} }