From 8c3cc5085cdf0734e6114fcb7b11f267b33ec595 Mon Sep 17 00:00:00 2001 From: Trevor Merritt Date: Sun, 6 Oct 2024 10:33:02 -0400 Subject: [PATCH] updates tests to cover most code. -> Draw Sprite -> Ret -> Call --- .cargo/config.toml | 4 +- coverage.sh | 0 emma/src/chip8/computer.rs | 14 +- emma/src/chip8/cpu_states.rs | 1 + emma/src/chip8/instructions.rs | 376 +++++++++++++++++++++++++++++---- emma/src/chip8/video.rs | 75 ++++--- 6 files changed, 395 insertions(+), 75 deletions(-) create mode 100644 coverage.sh diff --git a/.cargo/config.toml b/.cargo/config.toml index 6736d47..9bb778c 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,5 +1,5 @@ [alias] -coverage = "tarpaulin --out Html --skip-clean" +coverage = "tarpaulin --out Html --skip-clean --output-dir coverage" [build] -# rustc-wrapper = "sccache" +rustc-wrapper = "sccache" diff --git a/coverage.sh b/coverage.sh new file mode 100644 index 0000000..e69de29 diff --git a/emma/src/chip8/computer.rs b/emma/src/chip8/computer.rs index efc369d..bba629c 100644 --- a/emma/src/chip8/computer.rs +++ b/emma/src/chip8/computer.rs @@ -22,7 +22,6 @@ pub struct Chip8Computer { pub state: Chip8CpuStates, pub keypad: Keypad } - impl Default for Chip8Computer { fn default() -> Self { Self { @@ -77,8 +76,17 @@ impl Chip8Computer { // todo: THIS IS BAD AND IS A SIDE EFFECT decoded_instruction.execute(self); - self.sound_timer.tick(); - self.delay_timer.tick(); + match self.state { + Chip8CpuStates::WaitingForInstruction => { + self.sound_timer.tick(); + self.delay_timer.tick(); + } + Chip8CpuStates::WaitingForKey => { + println!("waiting for a key press..."); + } + _ => {} + } + self } diff --git a/emma/src/chip8/cpu_states.rs b/emma/src/chip8/cpu_states.rs index 382f62b..ad0b11e 100644 --- a/emma/src/chip8/cpu_states.rs +++ b/emma/src/chip8/cpu_states.rs @@ -2,6 +2,7 @@ pub enum Chip8CpuStates { #[default] WaitingForInstruction, + WaitingForKey, ExecutingInstruction, Error, } diff --git a/emma/src/chip8/instructions.rs b/emma/src/chip8/instructions.rs index 299262a..67a0c3e 100644 --- a/emma/src/chip8/instructions.rs +++ b/emma/src/chip8/instructions.rs @@ -3,6 +3,7 @@ use imgui::ColorPicker3; use log::debug; use rand::random; use crate::chip8::computer::{Chip8Computer}; +use crate::chip8::cpu_states::Chip8CpuStates::WaitingForKey; use crate::chip8::instructions::Chip8CpuInstructions::XXXXERRORINSTRUCTION; use crate::chip8::util::InstructionUtil; use crate::chip8::video::Chip8Video; @@ -424,7 +425,7 @@ impl Chip8CpuInstructions { // 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); } // * 0x00E0 Clear Screen @@ -464,7 +465,7 @@ impl Chip8CpuInstructions { Chip8CpuInstructions::SeVxVy(x, y) => { let lhs = input.registers.peek(*x as u8); let rhs = input.registers.peek(*y as u8); - // println!("COMPARING [{lhs}] to [{rhs}]"); + if lhs == rhs { input.registers.advance_pc(); } @@ -535,9 +536,14 @@ impl Chip8CpuInstructions { // If Vy > Vx, then VF is set to 1, otherwise 0. Then Vx is subtracted from Vy, and the results stored in Vx. let y_register = input.registers.peek(*y as u8); let x_register = input.registers.peek(*x as u8); - let new_value = if y_register > x_register { 1 } else { 0 }; + let new_value = if y_register <= x_register { 1 } else { 0 }; + let value_to_poke = if y_register <= x_register { + ((y_register as u16 + 256) - x_register as u16) as u8 + } else { + y_register - x_register + }; input.registers.poke(0xf, new_value); - input.registers.poke(*x as u8, x_register - y_register); + input.registers.poke(*x as u8, value_to_poke); } Chip8CpuInstructions::ShlVxVy(x, _) => { @@ -591,31 +597,42 @@ impl Chip8CpuInstructions { } Chip8CpuInstructions::DrawVxVyNibble(x, y, n) => { // Display n-byte sprite starting at memory location I at (Vx, Vy), set VF = collision. + + // The interpreter reads n bytes from memory, starting at the address stored in I. + + // These bytes are then displayed as sprites on screen at coordinates (Vx, Vy). // Sprites are XORed onto the existing screen. // If this causes any pixels to be erased, VF is set to 1, // otherwise it is set to 0. // If the sprite is positioned so part of it is outside the coordinates of the display, // it wraps around to the opposite side of the screen. - // - // read nibble bytes from memory starting at I - let start_position = input.registers.peek_i(); + let source_memory_offset = input.registers.peek_i(); + + let x_offset = input.registers.peek(*x as u8); + let y_offset = input.registers.peek(*y as u8); + + let target_memory_offset = x_offset * 64 + y_offset; + + println!("CHIP8CPUINSTRUCTION:DRAWVXNIBBLE -> STARTING AT {source_memory_offset} WRITING TO {target_memory_offset}"); 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 [0x{i:2x}]"); + println!("CHIP8CPUINSTRUCTION:DRAWVXNIBBLE -> PREPARING TO READ {num_bytes_to_read} BYTES FROM MEMORY TO VIDEO"); + for byte_index in 0..num_bytes_to_read { + let current_byte = input.memory.peek(byte_index + source_memory_offset); + println!("CHIP8CPUINSTRUCTION:DRAWVXNIBBLE -> READ BYTE [0x{byte_index:2x}]\t{current_byte:02x}\t{current_byte:08b}"); + for bit_index in 0..8 { + let data_offset = ((x_offset as u16 + byte_index) * 64 ) + (y_offset + bit_index) as u16; + let current_bit = (current_byte.shr(7 - bit_index) & 0x1u8) == 0x1u8; + input.video_memory.poke(data_offset, current_bit); + } } + println!("PPOOSSTT -> CHIP8CPUINSTRUCTION:DRAWVXNIBBLE -> {}", input.video_memory.format_as_string()); + let mut did_change: bool = false; - for draw_x in 0..*n { - // let mut new_value = input.memory[(input.i_register + draw_x) as usize]; - //for draw_y in 0..8 { - // } - } - if did_change { input.registers.poke(0xf, 1u8); } else { @@ -657,6 +674,7 @@ impl Chip8CpuInstructions { // Wait for a key press, store the value of the key in Vx. // // All execution stops until a key is pressed, then the value of that key is stored in Vx. + input.state = WaitingForKey; } Chip8CpuInstructions::LdDtVx(source_register) => { // Fx15 - LD DT, Vx @@ -683,13 +701,39 @@ impl Chip8CpuInstructions { // Fx29 - LD F, Vx // Set I = location of sprite for digit Vx. // - // The value of I is set to the location for the hexadecimal sprite corresponding to the value of Vx. See section 2.4, Display, for more information on the Chip-8 hexadecimal font. + // The value of I is set to the location for the hexadecimal sprite corresponding + // to the value of Vx. See section 2.4, Display, for more information on + // the Chip-8 hexadecimal font. + + let to_offset = input.registers.peek(*x as u8) - 1; + let real_offset = to_offset as u16 * 5; + input.registers.poke_i(real_offset as u16); } Chip8CpuInstructions::LdBVx(x) => { // Fx33 - LD B, Vx // Store BCD representation of Vx in memory locations I, I+1, and I+2. // - // The interpreter takes the decimal value of Vx, and places the hundreds digit in memory at location in I, the tens digit at location I+1, and the ones digit at location I+2. + // The interpreter takes the decimal value of Vx, and places the hundreds + // digit in memory at location in I, the tens digit at location I+1, + // and the ones digit at location I+2. + + let to_convert = input.registers.peek(*x as u8); + + // how many hundreds + let hundreds = to_convert / 100; + // how many tens, minus the hundreds + let tens = (to_convert / 10) - (hundreds * 10); + // whats the leftover when dividing by 10 + let units = to_convert % 10; + + // Convert to BCD + let result = ((hundreds as u16) << 8) | units as u16; + (tens << 4) | units; + // write them to the memory pointed to by I, I+1, and I+2 + let target_start_offset = input.registers.peek_i(); + input.memory.poke(target_start_offset, hundreds); + input.memory.poke(target_start_offset + 1, tens); + input.memory.poke(target_start_offset + 2, units); } Chip8CpuInstructions::LdIVx(x) => { // Store registers V0 through Vx in memory starting at location I. @@ -705,9 +749,14 @@ impl Chip8CpuInstructions { // // The interpreter reads values from memory starting at location I into registers V0 through Vx. let offset = input.registers.peek_i(); + debug!("STARTING TO READ AT {offset:03x}"); let num_loops = input.registers.peek(*x as u8); + debug!("WILL READ {num_loops:x} BYTES"); for index in 0..num_loops { - input.registers.poke(index, input.memory.peek(index as u16 + offset)); + let src_value = input.memory.peek(index as u16 + offset); + + input.registers.poke(index, src_value); + debug!("POKING {index} with {src_value}"); } } Chip8CpuInstructions::XXXXERRORINSTRUCTION => {} @@ -719,6 +768,9 @@ impl Chip8CpuInstructions { #[cfg(test)] mod test { + use dimensioned::typenum::assert_type; + use ratatui::crossterm::execute; + use crate::chip8::system_memory::{CHIP8FONT_0, CHIP8FONT_1, CHIP8FONT_9}; use super::*; #[test] @@ -1019,6 +1071,7 @@ mod test { assert_eq!(x.registers.peek(0xf), 1); assert_eq!(x.registers.peek_pc(), 0x206); } + #[test] fn ldi_addr_test() { let mut x = Chip8Computer::new(); @@ -1035,9 +1088,10 @@ mod test { Chip8CpuInstructions::JpV0Addr(0x100).execute(&mut x); assert_eq!(x.registers.peek_pc(), 0x1FF); } + #[test] fn cls_test() { - let mut x = Chip8Computer::new(); + let mut x = Chip8Computer::new(); Chip8CpuInstructions::CLS.execute(&mut x); assert_eq!(x.registers.peek_pc(), 0x202); } @@ -1065,39 +1119,39 @@ mod test { assert_eq!(x.registers.peek_i(), 0xacc); } - #[test] - fn ldstvt_test() { - let mut x = Chip8Computer::new(); - Chip8CpuInstructions::LdVxByte(0x01, 0xf0).execute(&mut x); - Chip8CpuInstructions::LdStVx(0x01).execute(&mut x); - assert_eq!(x.sound_timer.current(), 0xf0); - x.sound_timer.tick(); - x.sound_timer.tick(); - x.sound_timer.tick(); - assert_eq!(x.sound_timer.current(), 0xed); - } + #[test] + fn ldstvt_test() { + let mut x = Chip8Computer::new(); + Chip8CpuInstructions::LdVxByte(0x01, 0xf0).execute(&mut x); + Chip8CpuInstructions::LdStVx(0x01).execute(&mut x); + assert_eq!(x.sound_timer.current(), 0xf0); + x.sound_timer.tick(); + x.sound_timer.tick(); + x.sound_timer.tick(); + assert_eq!(x.sound_timer.current(), 0xed); + } #[test] fn rnd_vx_byte_text() { - let mut x = Chip8Computer::new(); + let mut x = Chip8Computer::new(); Chip8CpuInstructions::RndVxByte(0x1, 0x0f).execute(&mut x); let new_value = x.registers.peek(0x1); 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] + 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] fn add_vx_byte_test() { @@ -1139,7 +1193,7 @@ mod test { 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 { + for i in 0..0x20 { x.delay_timer.tick(); } assert_eq!(x.delay_timer.current(), 0); @@ -1157,4 +1211,234 @@ mod test { assert_eq!(x.registers.peek(0x1), 0xed); } + #[test] + fn subn_vx_vy_test() { + // This instruction subtracts the value in + // register Vx from the value in register Vy and stores the result in register Vx. + // The subtraction is performed as follows: Vx = Vy - Vx. If Vy is less than Vx, + // the result will wrap around (due to the 8-bit nature of the registers). + // 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. + + + let mut x = Chip8Computer::new(); + Chip8CpuInstructions::LdVxByte(0x1, 0xa0).execute(&mut x); + Chip8CpuInstructions::LdVxByte(0x2, 0xab).execute(&mut x); + Chip8CpuInstructions::SubnVxVy(0x1, 0x2).execute(&mut x); + + // expect the result to be 0x0b + assert_eq!(x.registers.peek(0x1), 0x0b); + // expect the vf register to be set to 1 as there was overflow + assert_eq!(x.registers.peek(0xf), 0x0); + + let mut x = Chip8Computer::new(); + Chip8CpuInstructions::LdVxByte(0x1, 0xab).execute(&mut x); + Chip8CpuInstructions::LdVxByte(0x2, 0xa0).execute(&mut x); + Chip8CpuInstructions::SubnVxVy(0x1, 0x2).execute(&mut x); + + // expect the result to be 11110101, -0xB, -11, 245, 0xF5 + assert_eq!(x.registers.peek(0x1), 0xf5); + assert_eq!(x.registers.peek(0xf), 0x1); + } + + #[test] + fn shl_vx_vy_test() { + // 8xyE - SHL Vx {, Vy} + // 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. + + let mut x = Chip8Computer::new(); + Chip8CpuInstructions::LdVxByte(0x1, 0b00100000).execute(&mut x); + Chip8CpuInstructions::ShlVxVy(0x1, 0x1).execute(&mut x); + assert_eq!(x.registers.peek(0x1), 0b01000000); + assert_eq!(x.registers.peek(0xf), 0x0); + + + let mut x = Chip8Computer::new(); + Chip8CpuInstructions::LdVxByte(0x1, 0b10101010).execute(&mut x); + Chip8CpuInstructions::ShlVxVy(0x1, 0x1).execute(&mut x); + assert_eq!(x.registers.peek(0x1), 0b01010100); + assert_eq!(x.registers.peek(0xf), 0x1); + } + + #[test] + fn ld_f_vx_test() { + // Fx29 - LD F, Vx + // Set I = location of sprite for digit Vx. + // + // The value of I is set to the location for the hexadecimal sprite corresponding to the value of Vx. See section 2.4, Display, for more information on the Chip-8 hexadecimal font. + let mut x = Chip8Computer::new(); + // target_sprite = 2 + // target_offset = 0x5 + Chip8CpuInstructions::LdVxByte(0x1, 0x2).execute(&mut x); + Chip8CpuInstructions::LdFVx(0x1).execute(&mut x); + + assert_eq!(x.registers.peek_i(), 0x5); + + Chip8CpuInstructions::LdVxByte(0x1, 0x6).execute(&mut x); + Chip8CpuInstructions::LdFVx(0x1).execute(&mut x); + assert_eq!(x.registers.peek_i(), 25); + } + + #[test] + fn ld_b_vx_test() { + // Fx33 - LD B, Vx + // Store BCD representation of Vx in memory locations I, I+1, and I+2. + // + // The interpreter takes the decimal value of Vx, and places the hundreds digit + // in memory at location in I, the tens digit at location I+1, + // and the ones digit at location I+2. + let mut x = Chip8Computer::new(); + + // load the value 123 (0x7b) + Chip8CpuInstructions::LdVxByte(0x1, 0x7b).execute(&mut x); + // set I to 0x500 + Chip8CpuInstructions::LdIAddr(0x500).execute(&mut x); + ; + Chip8CpuInstructions::LdBVx(0x1).execute(&mut x); + assert_eq!(x.memory.peek(0x500), 0x1); + assert_eq!(x.memory.peek(0x501), 0x2); + assert_eq!(x.memory.peek(0x502), 0x3); + } + + #[test] + fn ld_i_vx_test() { + // 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. + let mut x = Chip8Computer::new(); + + // Load Registers. + let to_load = [0xab, 0xba, 0xca, 0xca, 0xbe, 0xef]; + for (idx, val) in to_load.iter().enumerate() { + x.registers.poke(idx as u8, *val); + } + x.registers.poke_i(0x500); + + Chip8CpuInstructions::LdIVx(to_load.len() as u16).execute(&mut x); + + // Verify the values are in memory from 0x500 to 0x507 + for (idx, value) in to_load.iter().enumerate() { + assert_eq!(x.memory.peek(0x500 + idx as u16), *value); + } + } + + #[test] + fn ld_vx_i_test() { + // Read registers V0 through Vx from memory starting at location I. + // + // The interpreter reads values from memory starting at location I into registers V0 through Vx. + + let mut x = Chip8Computer::new(); + + let base_offset = 0x500; + let to_load = [0xab, 0xba, 0xca, 0xca, 0xbe, 0xef]; + + // start by setting values in memory + for (idx, memory) in to_load.iter().enumerate() { + let target_address = base_offset + idx; + let target_value = *memory; + x.memory.poke(target_address as u16, target_value); + } + // where to load from + x.registers.poke_i(0x500); + // how much to load + x.registers.poke(0x0, to_load.len() as u8); + + // then copying them values memory to registers + Chip8CpuInstructions::LdVxI(0x0).execute(&mut x); + + // now check that we have the right values in our registers + for (idx, value) in to_load.iter().enumerate() { + assert_eq!(x.registers.peek(idx as u8), *value); + } + } + + #[test] + fn Sknkpvx_test() { + // ExA1 - SKNP Vx + // 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. + let mut x = Chip8Computer::new(); + x.keypad.push_key(0x5); + x.registers.poke(0x1, 0x5); + Chip8CpuInstructions::SnkpVx(0x1).execute(&mut x); + assert_eq!(x.registers.peek_pc(), 0x204); + + x.keypad.release_key(0x5); + Chip8CpuInstructions::SnkpVx(0x1).execute(&mut x); + assert_eq!(x.registers.peek_pc(), 0x206); + } + + #[test] + fn skpvx_test() { + // Ex9E - SKP Vx + // Skip next instruction if key with the value of Vx is pressed. + // + // Checks the keyboard, and if the key corresponding to the value of Vx is currently in the down position, PC is increased by 2. + let mut x = Chip8Computer::new(); + x.keypad.push_key(0x5); + x.registers.poke(0x1, 0x5); + Chip8CpuInstructions::SkpVx(0x1).execute(&mut x); + assert_eq!(x.registers.peek_pc(), 0x202); + + x.keypad.release_key(0x5); + Chip8CpuInstructions::SkpVx(0x1).execute(&mut x); + assert_eq!(x.registers.peek_pc(), 0x204); + } + + #[test] + fn ldvxk_test() { + println!("THIS TEST DOES NOT REALLY DO ANYTHING AS IT DEPENDS ON THE LOCAL IMPLEMENTATION"); + println!("OF HOW TO DELAY AND GET KEYBOARD INPUTS. THIS EXPECTS THAT THE SYSTEM GOES INTO"); + println!("ITS HOLDING STATE AND ONCE THE KEY IS PRESSED THE KEYBOARD 'PRESS_KEY' IS COMPLETED"); + println!("THE PC IS ALREADY ADVANCED AT THIS POINT SO THE SKPVX OR WHATEVER CAN HANDLE IT"); + let mut x = Chip8Computer::new(); + } + + #[test] + fn draw_nibble_vx_vy_n_test() { + let mut x = Chip8Computer::new(); + let x_register = 0x1; + let y_register = 0x2; + let x_offset = 1; + let y_offset = 2; + + // use the font characters to draw to video memory +// assert_eq!(x.memory.peek(0x0), CHIP8FONT_0[0]); +// assert_eq!(x.memory.peek(0x1), CHIP8FONT_0[1]); +// assert_eq!(x.memory.peek(0x2), CHIP8FONT_0[2]); +// assert_eq!(x.memory.peek(0x3), CHIP8FONT_0[3]); +// assert_eq!(x.memory.peek(0x4), CHIP8FONT_0[4]); + + // now lets set the X and Y to 1,2 + x.registers.poke(x_register, x_offset); + x.registers.poke(y_register, y_offset); + x.registers.poke_i(0x5); + // we are using 5 rows. + Chip8CpuInstructions::DrawVxVyNibble(x_register as u16, y_register as u16, 5).execute(&mut x); + + // now check that video memory has the values at + // 1,2->1,9 + // 2,2->2,9 + // 3,2->3,9 + // 4,2->4,9 + // 5,2->5,9 + // let byte_to_check = CHIP8FONT_0[0]; + for row_in_sprite in 0..5 { + let row_data = CHIP8FONT_1[row_in_sprite]; + for bit_in_byte in 0..8 { + let data_offset = (x_offset + row_in_sprite as u8) * 64 + (bit_in_byte + y_offset); + let should_be_set = (row_data.shr(bit_in_byte) & 0x1) == 1; + println!("DATA_OFFSET FOR {}x{} is {} when offsetting by {}x{} and should be {} working with byte {:08b}", + bit_in_byte, row_in_sprite, data_offset, x_offset, y_offset, should_be_set, row_data); + // assert_eq!(should_be_set, ); + } + } + } } diff --git a/emma/src/chip8/video.rs b/emma/src/chip8/video.rs index 216d48a..7ee1a77 100644 --- a/emma/src/chip8/video.rs +++ b/emma/src/chip8/video.rs @@ -28,24 +28,32 @@ impl Chip8Video { } pub fn poke(&mut self, address: u16, new_value: bool) -> Self { - println!("POKING {new_value} AT {address}"); + println!("**VIDEO** POKING {new_value} TO {address}"); self.memory[address as usize] = new_value; 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 poke_sprite(&mut self, first_address: u16, to_write: Vec) -> Self { + let sprite_length = to_write.len(); + + for (index, byte) in to_write.iter().enumerate() { + let real_address = index * 64; + self.poke_byte(real_address as u16, *byte); + } + self.to_owned() + } + pub fn format_as_string(self) -> String { let mut output = String::new(); for row in 0..32 { @@ -62,12 +70,10 @@ impl Chip8Video { output += "\n"; } // println!("{}", output); - output + output } - pub fn write_sprite(&mut self, sprite_data: Vec, origin: (u8, u8)) { - debug!("Writing [{:?}] at [{}]x[{}]", sprite_data, origin.0, origin.1); - } + } impl Default for Chip8Video { @@ -219,15 +225,15 @@ mod test { assert!(v.peek(0x46)); assert!(v.peek(0x47)); - // row 3 column 1 - assert!(!v.peek(0xC0)); - assert!(v.peek(0xC1)); - assert!(!v.peek(0xC2)); - assert!(v.peek(0xC3)); - assert!(!v.peek(0xC4)); - assert!(v.peek(0xC5)); - assert!(!v.peek(0xC6)); - assert!(v.peek(0xC7)); + // row 3 column 1 + assert!(!v.peek(0xC0)); + assert!(v.peek(0xC1)); + assert!(!v.peek(0xC2)); + assert!(v.peek(0xC3)); + assert!(!v.peek(0xC4)); + assert!(v.peek(0xC5)); + assert!(!v.peek(0xC6)); + assert!(v.peek(0xC7)); } } @@ -251,7 +257,6 @@ mod test { } let test_offset = (x_offset * 64 + y_offset) as u16; - println!("TEST OFFSET = {test_offset}"); assert!(!v.peek(test_offset)); assert!(!v.peek(test_offset + 1)); assert!(!v.peek(test_offset + 2)); @@ -270,16 +275,38 @@ mod test { assert!(v.peek(test_offset + 5)); assert!(v.peek(test_offset + 6)); assert!(v.peek(test_offset + 7)); - - } #[test] - fn write_sprite_test() { + fn poke_sprite_test() { + let mut v = Chip8Video::default(); + let to_poke = [ + 0b00000000, + 0b11111111, + 0b10101010, + 0b01010101 + ]; + + v.poke_sprite(0x00, to_poke.into()); + + assert!(v.peek(0x40)); + assert!(v.peek(0x41)); + assert!(v.peek(0x42)); + assert!(v.peek(0x43)); + assert!(v.peek(0x44)); + assert!(v.peek(0x45)); + assert!(v.peek(0x46)); + assert!(v.peek(0x47)); + + // row 3 column 1 + assert!(!v.peek(0xC0)); + assert!(v.peek(0xC1)); + assert!(!v.peek(0xC2)); + assert!(v.peek(0xC3)); + assert!(!v.peek(0xC4)); + assert!(v.peek(0xC5)); + assert!(!v.peek(0xC6)); + assert!(v.peek(0xC7)); } } - -fn is_bit_set(to_check: u8, bit_index: u8) -> bool { - (to_check >> bit_index )& 0x1 == 0x1 -} \ No newline at end of file