diff --git a/emma/src/chip8/instructions.rs b/emma/src/chip8/instructions.rs index 641ada7..b002f14 100644 --- a/emma/src/chip8/instructions.rs +++ b/emma/src/chip8/instructions.rs @@ -1,3 +1,4 @@ +use std::ops::{Shl, Shr}; use imgui::ColorPicker3; use log::debug; use rand::random; @@ -53,6 +54,7 @@ pub enum Chip8CpuInstructions { LdVxI(u16), // 0xFx65 Load V0 to Vx in memory starting at I XXXXERRORINSTRUCTION, } + impl Chip8CpuInstructions { pub fn encode(&self) -> u16 { match self { @@ -172,7 +174,7 @@ impl Chip8CpuInstructions { let addr_param = InstructionUtil::read_addr_from_instruction(input); let byte_param = InstructionUtil::read_byte_from_instruction(input); let nibble_param = InstructionUtil::read_nibble_from_instruction(input); - let ubln = u16::rotate_right(InstructionUtil::read_upper_byte_lower_nibble(input), 8); + let ubln = InstructionUtil::read_upper_byte_lower_nibble(input); let last_byte = input & 0xFF; match input { @@ -303,6 +305,8 @@ impl Chip8CpuInstructions { 0xE09E..=0xEFA1 => { match last_byte { 0x9E => { + println!("DECODING {:4x}", input); + println!("UBLN: {:4x}", ubln); Chip8CpuInstructions::SkpVx(ubln) } 0xA1 => { @@ -314,7 +318,7 @@ impl Chip8CpuInstructions { } } 0xF007..=0xFF65 => { - // println!("COMPARING LAST BYTE FROM TODECODE: {:2x} to {:4x} with {:2x}", last_byte, input, ubln); + // println!("COMPARING LAST BYTE FROM TODECODE: {:2x} to {:4x} with {:2x}", last_byte, input, ubln); match last_byte { 0x07 => { Chip8CpuInstructions::LdVxDt(ubln) @@ -360,7 +364,7 @@ impl Chip8CpuInstructions { let _ = match self { // 0x0nnn Exit to System Call Chip8CpuInstructions::SysAddr(new_address) => { - // println!("SYS TO [{new_address}]"); + // println!("SYS TO [{new_address}]"); input.registers.poke_pc(*new_address as u16); } // * 0x00E0 Clear Screen @@ -396,10 +400,10 @@ impl Chip8CpuInstructions { } } // 0x5xy0 Skip next instruction if Vx == Vy - Chip8CpuInstructions::SeVxVy(x,y) => { + 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}]"); + // println!("COMPARING [{lhs}] to [{rhs}]"); if lhs == rhs { input.registers.advance_pc(); } @@ -408,7 +412,7 @@ impl Chip8CpuInstructions { Chip8CpuInstructions::LdVxByte(register, byte) => { let start_value = input.registers.peek(*register as u8); let byte_value = *byte as u8; - // println!("SETTING REGISTER [{register}] FROM [{start_value}] to [{byte_value}] by loading."); + // 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 @@ -452,7 +456,7 @@ impl Chip8CpuInstructions { } input.registers.poke(*x as u8, working as u8); } - Chip8CpuInstructions::SubVxVy(x,y) => { + Chip8CpuInstructions::SubVxVy(x, y) => { // 8xy5 - SUB Vx, Vy // Set Vx = Vx - Vy, set VF = NOT borrow. // @@ -470,9 +474,9 @@ impl Chip8CpuInstructions { if 0xb1 & initial_value == 1 { input.registers.poke(0xf, 1); } - input.registers.poke(*x as u8, initial_value.rotate_left(1)); + input.registers.poke(*x as u8, initial_value.shr(1)); } - Chip8CpuInstructions::SubnVxVy(x,y) => { + Chip8CpuInstructions::SubnVxVy(x, y) => { // 8xy7 - SUBN Vx, Vy // Set Vx = Vy - Vx, set VF = NOT borrow. // @@ -494,7 +498,7 @@ impl Chip8CpuInstructions { if 0x80 & initial_value == 0x80 { input.registers.poke(0xf, 1); } - input.registers.poke(*x as u8, initial_value.rotate_left(1)); + input.registers.poke(*x as u8, initial_value.shl(1)); } Chip8CpuInstructions::SneVxVy(vx_register, vy_register) => { // 9xy0 - SNE Vx, Vy @@ -528,9 +532,10 @@ impl Chip8CpuInstructions { // Cxkk - RND Vx, byte // Set Vx = random byte AND kk. // - // The interpreter generates a random number from 0 to 255, which is then ANDed with the value kk. The results are stored in Vx. See instruction 8xy2 for more information on AND. - - let new_value: u8 = random() ; + // The interpreter generates a random number from 0 to 255, + // which is then ANDed with the value kk. + // The results are stored in Vx. + let new_value: u8 = random(); input.registers.poke(*x as u8, (new_value & *byte as u8)) } Chip8CpuInstructions::DrawVxVyNibble(x, y, n) => { @@ -548,8 +553,8 @@ impl Chip8CpuInstructions { let start_position = input.registers.peek_i(); 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}"); + // let current_byte = input.memory[i as usize]; + // println!("READ BYTE [{current_byte:8b}"); } let mut did_change: bool = false; @@ -572,8 +577,6 @@ impl Chip8CpuInstructions { // // 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 key_to_check = input.registers.peek(*x as u8); - - } Chip8CpuInstructions::SnkpVx(x) => { @@ -581,8 +584,6 @@ impl Chip8CpuInstructions { // 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. - - } Chip8CpuInstructions::LdVxDt(x) => { // Fx07 - LD Vx, DT @@ -598,15 +599,17 @@ impl Chip8CpuInstructions { // // All execution stops until a key is pressed, then the value of that key is stored in Vx. } - Chip8CpuInstructions::LdDtVx(new_time) => { + Chip8CpuInstructions::LdDtVx(source_register) => { // Fx15 - LD DT, Vx // Set delay timer = Vx. // // DT is set equal to the value of Vx. - println!("SETTING DELAY TIMER TO [{}]", *new_time); - input.delay_timer.set_timer(*new_time as i32); + 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); } Chip8CpuInstructions::LdStVx(new_time) => { + println!("SETTING SOUND TIMER TO [0x{:2x}]", *new_time); input.sound_timer.set_timer(*new_time as i32); } Chip8CpuInstructions::AddIVx(x) => { @@ -623,14 +626,12 @@ impl Chip8CpuInstructions { // 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. - } 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. - } Chip8CpuInstructions::LdIVx(x) => { // Store registers V0 through Vx in memory starting at location I. @@ -704,8 +705,8 @@ mod test { assert_eq!(Chip8CpuInstructions::LdBVx(0xd).encode(), 0xfd33); assert_eq!(Chip8CpuInstructions::LdIVx(0xe).encode(), 0xfe55); assert_eq!(Chip8CpuInstructions::LdVxI(0x3).encode(), 0xf365); - assert!(matches!( Chip8CpuInstructions::decode(0x00E0u16), Chip8CpuInstructions::CLS)); - assert!(matches!( Chip8CpuInstructions::decode(0x00EEu16), Chip8CpuInstructions::RET)); + assert!(matches!(Chip8CpuInstructions::decode(0x00E0u16), Chip8CpuInstructions::CLS)); + assert!(matches!(Chip8CpuInstructions::decode(0x00EEu16), Chip8CpuInstructions::RET)); assert!(matches!(Chip8CpuInstructions::decode(0x0123), Chip8CpuInstructions::SysAddr(0x123))); assert!(matches!(Chip8CpuInstructions::decode(0x0FFF), Chip8CpuInstructions::SysAddr(0xfff))); assert!(matches!(Chip8CpuInstructions::decode(0x1002), Chip8CpuInstructions::JpAddr(0x2))); @@ -741,6 +742,7 @@ mod test { assert!(matches!(Chip8CpuInstructions::decode(0xfd33), Chip8CpuInstructions::LdBVx(0xd))); assert!(matches!(Chip8CpuInstructions::decode(0xfe55), Chip8CpuInstructions::LdIVx(0xe))); assert!(matches!(Chip8CpuInstructions::decode(0xf365), Chip8CpuInstructions::LdVxI(0x3))); + } /// START OF THE EXECUTION TESTS @@ -755,9 +757,13 @@ mod test { Chip8CpuInstructions::SysAddr(0x0AF).execute(&mut x); assert_eq!(x.registers.peek_pc(), 0x0AF); } + fn cls_test() { // * 0x00E0 Clear Screen // todo: Need to write this + let mut x = Chip8Computer::new(); + + } fn ret_test() { @@ -875,7 +881,6 @@ mod test { Chip8CpuInstructions::LdVxVy(0x01, 0x02).execute(&mut x); assert_eq!(x.registers.peek(1), 0x02); assert_eq!(x.registers.peek_pc(), 0x206); - } #[test] @@ -887,7 +892,7 @@ mod test { let mut x = Chip8Computer::new(); Chip8CpuInstructions::LdVxByte(1, 0x50).execute(&mut x); Chip8CpuInstructions::LdVxByte(2, 0x0A).execute(&mut x); - Chip8CpuInstructions::OrVxVy(1,2).execute(&mut x); + Chip8CpuInstructions::OrVxVy(1, 2).execute(&mut x); assert_eq!(x.registers.peek(1), 0x5A); assert_eq!(x.registers.peek_pc(), 0x206); } @@ -901,7 +906,7 @@ mod test { let mut x = Chip8Computer::new(); Chip8CpuInstructions::LdVxByte(1, 0xFC).execute(&mut x); Chip8CpuInstructions::LdVxByte(2, 0xCA).execute(&mut x); - Chip8CpuInstructions::AndVxVy(1,2).execute(&mut x); + Chip8CpuInstructions::AndVxVy(1, 2).execute(&mut x); assert_eq!(x.registers.peek(1), 0xC8); assert_eq!(x.registers.peek_pc(), 0x206); } @@ -915,7 +920,7 @@ mod test { let mut x = Chip8Computer::new(); Chip8CpuInstructions::LdVxByte(1, 0xFC).execute(&mut x); Chip8CpuInstructions::LdVxByte(2, 0xCA).execute(&mut x); - Chip8CpuInstructions::XorVxVy(1,2).execute(&mut x); + Chip8CpuInstructions::XorVxVy(1, 2).execute(&mut x); assert_eq!(x.registers.peek(1), 0x36); assert_eq!(x.registers.peek_pc(), 0x206); } @@ -941,69 +946,90 @@ mod test { Chip8CpuInstructions::LdVxByte(0xf, 0x00).execute(&mut x); Chip8CpuInstructions::LdVxByte(0x1, 0xff).execute(&mut x); Chip8CpuInstructions::LdVxByte(0x2, 0x01).execute(&mut x); - Chip8CpuInstructions::AddVxVy(1,2).execute(&mut x); + Chip8CpuInstructions::AddVxVy(1, 2).execute(&mut x); // T2 assert_eq!(x.registers.peek(0xf), 1); assert_eq!(x.registers.peek(1), 0); assert_eq!(x.registers.peek_pc(), 0x208) } -/* #[test] - fn SubVxVy_test() { - todo: this test sucks. dont have the borrow concept in here. - 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. - let mut x = Chip8Computer::new(); - Chip8CpuInstructions::LdVxByte(1, 0x10).execute(&mut x); - Chip8CpuInstructions::LdVxByte(2, 0x01).execute(&mut x); - Chip8CpuInstructions::LdVxByte(0xf, 0x00).execute(&mut x); - Chip8CpuInstructions::SubVxVy(0x1, 0x2).execute(&mut x); + /* #[test] + fn SubVxVy_test() { + todo: this test sucks. dont have the borrow concept in here. + 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. + let mut x = Chip8Computer::new(); + Chip8CpuInstructions::LdVxByte(1, 0x10).execute(&mut x); + Chip8CpuInstructions::LdVxByte(2, 0x01).execute(&mut x); + Chip8CpuInstructions::LdVxByte(0xf, 0x00).execute(&mut x); + Chip8CpuInstructions::SubVxVy(0x1, 0x2).execute(&mut x); - assert_eq!(x.registers.peek_pc(), 0x208); - assert_eq!(x.registers.peek(1), 0xF); - assert_eq!(x.registers.peek(0x10), 0); + assert_eq!(x.registers.peek_pc(), 0x208); + assert_eq!(x.registers.peek(1), 0xF); + assert_eq!(x.registers.peek(0x10), 0); - } -*/ + } + */ + #[test] fn ShrVxVy_test() { /* Set Vx = Vx SHR 1. If the least-significant bit of Vx is 1, then VF is set to 1, otherwise 0. Then Vx is divided by 2. */ - let x = Chip8Computer::new(); - Chip8CpuInstructions::LdVxByte(0xf, 0x00); - Chip8CpuInstructions::LdVxByte(0x1, 0x08); // 0b0000 1000 (0x08) - Chip8CpuInstructions::LdVxByte(0x2, 0x2); - Chip8CpuInstructions::ShrVxVy(0x1, 0x2); // 0b0000 0010 (0x02) (Not Set) - assert_eq!(x.registers.peek(1), 0x02); + let mut x = Chip8Computer::new(); + Chip8CpuInstructions::LdVxByte(0xf, 0x00).execute(&mut x); + Chip8CpuInstructions::LdVxByte(0x1, 0x08).execute(&mut x); // 0b0000 1000 (0x08) + Chip8CpuInstructions::LdVxByte(0x2, 0x2).execute(&mut x); + Chip8CpuInstructions::ShrVxVy(0x1, 0x2).execute(&mut x); // 0b0000 0010 (0x02) (Not Set) + assert_eq!(x.registers.peek(1), 0x04); assert_eq!(x.registers.peek(0xf), 0); - assert_eq!(x.registers.peek_pc(), 0x206); + assert_eq!(x.registers.peek_pc(), 0x208); - let x = Chip8Computer::new(); - Chip8CpuInstructions::LdVxByte(0xf, 0x00); - Chip8CpuInstructions::LdVxByte(0x1, 0x09); // 0b0000 1001 (0x09) - Chip8CpuInstructions::LdVxByte(0x2, 0x2); - Chip8CpuInstructions::ShrVxVy(0x1, 0x2); // 0b0000 0010 (0x02) (Set) - assert_eq!(x.registers.peek(1), 0x02); - assert_eq!(x.registers.peek(0xf), 1); - assert_eq!(x.registers.peek_pc(), 0x206); + let mut x = Chip8Computer::new(); + Chip8CpuInstructions::LdVxByte(0xf, 0x00).execute(&mut x); + Chip8CpuInstructions::LdVxByte(0x1, 0b00001001).execute(&mut x); // 0b0000 1001 (0x09) + Chip8CpuInstructions::ShrVxVy(0x1, 0x2).execute(&mut x); // 0b0000 0100 (0x02) (Set) + Chip8CpuInstructions::ShrVxVy(0x1, 0x1).execute(&mut x); + assert_eq!(x.registers.peek(0x1), 0b00000010); + assert_eq!(x.registers.peek(0xf), 0x1); + assert_eq!(x.registers.peek_pc(), 0x208); } + fn SneVxVy_test() {} + fn LdiAddr_test() {} + fn JpV0Addr_test() {} - fn RndVxByte_test() {} + + #[test] + fn RndVxByte_test() { + let mut x = Chip8Computer::new(); + + // generate random number masked by 0xF0; + let mask = 0xF0u8; + Chip8CpuInstructions::RndVxByte(0x0, mask as u16).execute(&mut x); + let register_value = x.registers.peek(0x0); + assert!(register_value < mask); + + // generate random number masked by 0x0F; + let mask2 = 0x0Fu8; + Chip8CpuInstructions::RndVxByte(0x1, mask2 as u16).execute(&mut x); + let register_value = x.registers.peek(0x1); + assert!(register_value < mask); + } + fn DrawVxVyNibble_test() {} + fn SkpVx_test() { // skip if key pressed - - } + fn SnKpVx_test() { // skip key not pressed - } + #[test] fn LdVxDt_test() { // delay timer reading @@ -1028,33 +1054,54 @@ mod test { fn LdVxK_test() { // 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. - } + + #[test] fn LdStVx_test() { // sound timer setting - } - fn LdIVx_test() {} - fn LdVxI_test() {} -/* - #[test] - fn LdDtVx_test() { - - // delay timer setting let mut x = Chip8Computer::new(); + Chip8CpuInstructions::LdVxByte(0x1, 0x10).execute(&mut x); + Chip8CpuInstructions::LdStVx(0x10).execute(&mut x); - // lets set our delay timer... - Chip8CpuInstructions::LdVxByte(0x0, 0x80).execute(&mut x); - Chip8CpuInstructions::LdDtVx(0x0).execute(&mut x); + // tick from 0x8 to 0x1 + for i in 0..6 { x.sound_timer.tick(); } - // now that we have our timer set to 0x80 we should tick it 0x10 times - // so we are then down to 0x70 - for i in 0..0x10 { - x.delay_timer.tick(); - } - // Then tell the CPU to copy that timer over into our V0 - Chip8CpuInstructions::LdVxK(0x0).execute(&mut x); - let register_value = x.registers.peek(0); - // assert_eq!(register_value, 0x70); + assert_eq!(x.sound_timer.current(), 0xA); } - */ + + fn LdIVx_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. + + } + + fn LdVxI_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. + + /* + #[test] + fn LdDtVx_test() { + + // delay timer setting + let mut x = Chip8Computer::new(); + + // lets set our delay timer... + Chip8CpuInstructions::LdVxByte(0x0, 0x80).execute(&mut x); + Chip8CpuInstructions::LdDtVx(0x0).execute(&mut x); + + // now that we have our timer set to 0x80 we should tick it 0x10 times + // so we are then down to 0x70 + for i in 0..0x10 { + x.delay_timer.tick(); + } + // Then tell the CPU to copy that timer over into our V0 + Chip8CpuInstructions::LdVxK(0x0).execute(&mut x); + let register_value = x.registers.peek(0); + // assert_eq!(register_value, 0x70); + } + */ } diff --git a/emma/src/chip8/registers.rs b/emma/src/chip8/registers.rs index a2e3c53..9f7e9af 100644 --- a/emma/src/chip8/registers.rs +++ b/emma/src/chip8/registers.rs @@ -56,4 +56,7 @@ impl Chip8Registers { #[cfg(test)] mod test { + #[test] + fn smoke() { assert!(true) } + } \ No newline at end of file diff --git a/emma/src/chip8/sound_timer.rs b/emma/src/chip8/sound_timer.rs index 878053f..d1970fb 100644 --- a/emma/src/chip8/sound_timer.rs +++ b/emma/src/chip8/sound_timer.rs @@ -19,6 +19,7 @@ impl SoundTimer { } pub fn tick(&mut self) { + println!("TICKING SOUND FROM {} to {}", self.counter, self.counter - 1); if self.counter > 0 { self.counter -= 1; /* diff --git a/emma/src/chip8/util.rs b/emma/src/chip8/util.rs index cd9a840..c912309 100644 --- a/emma/src/chip8/util.rs +++ b/emma/src/chip8/util.rs @@ -1,6 +1,25 @@ - pub struct InstructionUtil {} + impl InstructionUtil { + pub fn byte_to_bools(to_convert: u8) -> [bool; 8] { + let mut return_values = [false; 8]; + for i in 0..8 { + let new_value = to_convert >> i & 0x1 == 1; + return_values[i as usize] = new_value; + } + return_values + } + + pub fn bools_to_byte(to_convert: [bool; 8]) -> u8 { + let mut return_value = 0u8; + for i in 0..to_convert.len() { + let new_bit = 0x1 << i; + if to_convert[i] { + return_value = return_value | new_bit + } + } + return_value + } pub fn split_bytes(to_split: u16) -> (u8, u8) { let high = to_split.rotate_left(8) as u8; @@ -10,7 +29,7 @@ impl InstructionUtil { } pub fn join_bytes(high: u8, low: u8) -> u16 { - let result = (high as u16 )<< 8 | low as u16; + let result = (high as u16) << 8 | low as u16; result } @@ -40,11 +59,10 @@ impl InstructionUtil { } pub fn read_upper_byte_lower_nibble(to_read_from: u16) -> u16 { - to_read_from & 0x0f00 + (to_read_from & 0x0f00) >> 8 } } - #[cfg(test)] mod test { use super::*; @@ -65,7 +83,7 @@ mod test { #[test] fn join_bytes() { // from 0xAB low and 0xCD high we get 0xABCD - let merged = InstructionUtil::join_bytes( 0xcd, 0xab); + let merged = InstructionUtil::join_bytes(0xcd, 0xab); assert_eq!(merged, 0xcdab); } @@ -83,6 +101,20 @@ mod test { #[test] fn ubln() { // from 0xABCD we should see B - assert_eq!(InstructionUtil::read_upper_byte_lower_nibble(0xABCD), 0xB << 8); + assert_eq!(InstructionUtil::read_upper_byte_lower_nibble(0xABCD), 0xB); + assert_eq!(InstructionUtil::read_upper_byte_lower_nibble(0x0123), 0x1); + assert_eq!(InstructionUtil::read_upper_byte_lower_nibble(0x0000), 0x0); } -} \ No newline at end of file + + #[test] + fn byte_to_bool_changes() { + assert_eq!(InstructionUtil::byte_to_bools(0b00000000), [false, false, false, false, false, false, false, false]); + assert_eq!(InstructionUtil::byte_to_bools(0b11111111), [true, true, true, true, true, true, true, true]); + assert_eq!(InstructionUtil::byte_to_bools(0b11001100), [false, false, true, true, false, false, true, true]); + assert_eq!(InstructionUtil::byte_to_bools(0b11110000), [false, false, false, false, true, true, true, true]); + assert_eq!(InstructionUtil::bools_to_byte([false, false, false, false, false, false, false, false]), 0b00000000); + assert_eq!(InstructionUtil::bools_to_byte([true, true, true, true, true, true, true, true]), 0b11111111); + assert_eq!(InstructionUtil::bools_to_byte([false, false, true, true, false, false, true, true]), 0b11001100); + assert_eq!(InstructionUtil::bools_to_byte([false, false, false, false, true, true, true, true]), 0b11110000); + } +}