diff --git a/emma/src/chip8/computer.rs b/emma/src/chip8/computer.rs index 3cdcc0a..efc369d 100644 --- a/emma/src/chip8/computer.rs +++ b/emma/src/chip8/computer.rs @@ -72,12 +72,8 @@ impl Chip8Computer { 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 result = high_byte | low_byte; - debug!("JOINED BYTES [{high_byte:2x}] and [{low_byte:2x}] to build [{result:4x}]"); let decoded_instruction = 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 decoded_instruction.execute(self); diff --git a/emma/src/chip8/instructions.rs b/emma/src/chip8/instructions.rs index 7e56b23..299262a 100644 --- a/emma/src/chip8/instructions.rs +++ b/emma/src/chip8/instructions.rs @@ -367,8 +367,6 @@ impl Chip8CpuInstructions { 0xE09E..=0xEFA1 => { match last_byte { 0x9E => { - println!("DECODING {:4x}", input); - println!("UBLN: {:4x}", ubln); Chip8CpuInstructions::SkpVx(ubln) } 0xA1 => { @@ -380,7 +378,6 @@ impl Chip8CpuInstructions { } } 0xF007..=0xFF65 => { - // println!("COMPARING LAST BYTE FROM TODECODE: {:2x} to {:4x} with {:2x}", last_byte, input, ubln); match last_byte { 0x07 => { Chip8CpuInstructions::LdVxDt(ubln) @@ -426,6 +423,7 @@ impl Chip8CpuInstructions { let _ = match self { // 0x0nnn Exit to System Call Chip8CpuInstructions::SysAddr(new_address) => { + debug!("SysAddr [0x{new_address:3x}]"); // println!("SYS TO [{new_address}]"); input.registers.poke_pc(*new_address as u16); } @@ -441,23 +439,26 @@ impl Chip8CpuInstructions { } // 0x1nnn Jump to Address Chip8CpuInstructions::JpAddr(new_address) => { + debug!("JpAddr [0x{new_address:3x}]"); input.registers.poke_pc(*new_address as u16); } // 0x2nnn Call Subroutine Chip8CpuInstructions::CallAddr(new_address) => { - debug!("CALL ADDR {new_address}"); + debug!("CALL ADDR 0x{new_address:3x}"); } // 0x3xkk Skip next instruction if Vx = kk. Chip8CpuInstructions::SeVxByte(vx_register, byte) => { if input.registers.peek(*vx_register as u8) == *byte as u8 { input.registers.advance_pc(); } + debug!("SeVxByte [0x{vx_register:1x}] [0x{byte:2x}]"); } // 0x4xkk Skip next instruction if Vx != kk Chip8CpuInstructions::SneVxByte(x, byte) => { if input.registers.peek(*x as u8) != *byte as u8 { input.registers.advance_pc(); } + debug!("SneVxByte [0x{x:1x}] [0x{byte:2x}"); } // 0x5xy0 Skip next instruction if Vx == Vy Chip8CpuInstructions::SeVxVy(x, y) => { @@ -467,27 +468,33 @@ impl Chip8CpuInstructions { if lhs == rhs { input.registers.advance_pc(); } + debug!("SeVxVy [0x{x:1x}] [0x{y:1x}]"); } // 0x6xkk Set Vx = kk Chip8CpuInstructions::LdVxByte(register, byte) => { let byte_value = *byte as u8; input.registers.poke(*register as u8, byte_value); + debug!("LdVxByte [0x{register:1x}] [0x{byte:2.0x}]"); } // 0x7xkk Set Vx = Vx + kk Chip8CpuInstructions::AddVxByte(vx_register, byte) => { 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 Chip8CpuInstructions::LdVxVy(x, y) => { 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 Chip8CpuInstructions::OrVxVy(x, y) => { 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 Chip8CpuInstructions::AndVxVy(x, y) => { input.registers.poke(*x as u8, input.registers.peek(*x as u8) & input.registers.peek(*y as u8)); + debug!("AndVxVy [0x{x:1x}] [0x{y:1x}]"); } // 0x8xy3 Set Vx = Vx XOR Vy 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. 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} // Set Vx = Vx SHR 1. // @@ -533,7 +540,7 @@ impl Chip8CpuInstructions { input.registers.poke(*x as u8, x_register - y_register); } - Chip8CpuInstructions::ShlVxVy(x, y) => { + Chip8CpuInstructions::ShlVxVy(x, _) => { // 8xyE - SHL Vx {, Vy} // Set Vx = Vx SHL 1. // @@ -561,7 +568,7 @@ impl Chip8CpuInstructions { // Set I = 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); } // 0xBnnn Jump to nnn+V0 @@ -598,7 +605,7 @@ impl Chip8CpuInstructions { let num_bytes_to_read = *n; for i in start_position..start_position + num_bytes_to_read { // 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; @@ -631,9 +638,8 @@ impl Chip8CpuInstructions { // and if the key corresponding to the value of Vx is currently in the up position, // PC is increased by 2. let target_key = input.registers.peek(*x as u8); - println!("TESTING REGISTER {x} -> READ {target_key}"); let is_pressed = input.keypad.pressed(target_key); - println!("KEY STATE = {is_pressed}"); + debug!("SnKpVx [{x:1x}]"); if is_pressed { input.registers.advance_pc(); } @@ -713,7 +719,6 @@ impl Chip8CpuInstructions { #[cfg(test)] mod test { - use ratatui::crossterm::execute; use super::*; #[test] @@ -836,7 +841,7 @@ mod test { // ** test moved up so it can be used later #[test] - fn LdVxByte_test() { + fn ld_vx_byte_test() { // 0x6xkk Set Vx = kk let mut x = Chip8Computer::new(); Chip8CpuInstructions::LdVxByte(1, 0x12).execute(&mut x); @@ -886,7 +891,7 @@ mod test { } #[test] - fn SeVxVy_test() { + fn se_vx_vy_test() { // 0x4xkk Skip next instruction if Vx != kk let mut x = Chip8Computer::new(); Chip8CpuInstructions::LdVxByte(0x01, 0x84).execute(&mut x); @@ -908,7 +913,7 @@ mod test { } #[test] - fn LdVxVy_test() { + fn ld_vx_vy_test() { // 0x8xy0 Set value of Vy in Vx let mut x = Chip8Computer::new(); Chip8CpuInstructions::LdVxByte(0x01, 0x01).execute(&mut x); @@ -921,7 +926,7 @@ mod test { } #[test] - fn OrVxVy_test() { + fn or_vx_vy_test() { // 0x8xy1 Set Vx = Vx OR Vy // 0b0101 0000 (0x50) // | 0b0000 1010 (0x0A) @@ -935,7 +940,7 @@ mod test { } #[test] - fn AndVxVy_test() { + fn and_vx_vy_test() { // 0x8xy2 Set Vx = Vx AND Vy // 0b1111 1100 (0xFC) // & 0b1100 1010 (0xCA) @@ -949,7 +954,7 @@ mod test { } #[test] - fn XorVxVy_test() { + fn xor_vx_vy_test() { // 0x8xy3 Set Vx = Vx XOR Vy // 0b1111 1100 (0xFC) // ^ 0b1100 1010 (0xCA) @@ -963,7 +968,7 @@ mod test { } #[test] - fn AddVxVy_test() { + fn add_vx_vy_test() { // 0x8xy4 Set Vx = Vx + Vy (SET VF on Carry) // T1 T2: Judgement Test // 0x01 0xFF @@ -991,7 +996,7 @@ mod test { } #[test] - fn ShrVxVy_test() { + fn shr_vx_vy_test() { /* Set Vx = Vx SHR 1. @@ -1015,7 +1020,7 @@ mod test { assert_eq!(x.registers.peek_pc(), 0x206); } #[test] - fn LdiAddr_test() { + fn ldi_addr_test() { let mut x = Chip8Computer::new(); Chip8CpuInstructions::LdIAddr(0x123).execute(&mut x); assert_eq!(x.registers.peek_i(), 0x123); @@ -1023,37 +1028,13 @@ mod test { } #[test] - fn JpV0Addr_test() { + fn jp_v0addr_test() { let mut x = Chip8Computer::new(); /// jump to I + nnn Chip8CpuInstructions::LdVxByte(0x0, 0xFF).execute(&mut x); Chip8CpuInstructions::JpV0Addr(0x100).execute(&mut x); 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] fn cls_test() { let mut x = Chip8Computer::new(); @@ -1074,15 +1055,6 @@ mod test { 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] 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); + } + } diff --git a/emma/src/chip8/registers.rs b/emma/src/chip8/registers.rs index 0743dbc..78bd459 100644 --- a/emma/src/chip8/registers.rs +++ b/emma/src/chip8/registers.rs @@ -34,7 +34,6 @@ impl Chip8Registers { } pub fn poke_i(&mut self, new_value: u16) { - println!("REGISTER: Setting I to {new_value}"); self.i_register = new_value; } pub fn peek(&self, register_number: u8) -> u8 { @@ -57,8 +56,24 @@ impl Chip8Registers { #[cfg(test)] mod test { + use crate::chip8::registers::Chip8Registers; #[test] 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() { + + } + } \ No newline at end of file diff --git a/emma/src/chip8/sound_timer.rs b/emma/src/chip8/sound_timer.rs index c8c62c8..659e72f 100644 --- a/emma/src/chip8/sound_timer.rs +++ b/emma/src/chip8/sound_timer.rs @@ -1,5 +1,6 @@ use std::{thread, time}; use beep::beep; +use log::trace; #[derive(Clone, Copy)] pub struct SoundTimer { @@ -16,12 +17,12 @@ impl SoundTimer { } } 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 } 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 { self.counter -= 1; /* diff --git a/emma/src/chip8/system_memory.rs b/emma/src/chip8/system_memory.rs index 46faa01..a4599d7 100644 --- a/emma/src/chip8/system_memory.rs +++ b/emma/src/chip8/system_memory.rs @@ -122,6 +122,4 @@ mod test { assert_eq!(x.peek(0), 0x01); } } - - } diff --git a/emma/src/chip8/video.rs b/emma/src/chip8/video.rs index 6cd50dc..96844b3 100644 --- a/emma/src/chip8/video.rs +++ b/emma/src/chip8/video.rs @@ -160,6 +160,8 @@ mod test { } assert_eq!(x.format_as_string(), expected); } + + #[test] fn cls() { let mut initial_memory = [false; CHIP8_VIDEO_MEMORY]; let mut ws = String::new(); @@ -178,4 +180,43 @@ mod test { set_x.cls(); 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 } \ No newline at end of file