diff --git a/gemma/src/bin/speedtest.rs b/gemma/src/bin/speedtest.rs index 55a8666..ea6ea0b 100644 --- a/gemma/src/bin/speedtest.rs +++ b/gemma/src/bin/speedtest.rs @@ -1,4 +1,3 @@ -use gemma::chip8::computer::Chip8Computer; use gemma::chip8::computer_manager::Chip8ComputerManager; use std::fs::File; use std::io::Read; @@ -24,7 +23,7 @@ fn main() { let mut num_cycles = 0; // how many instructions in 10s. let end_time = start_time.add(Duration::from_secs(10)); - while (Instant::now().le(&end_time)) { + while Instant::now().le(&end_time) { num_cycles += 1; x.step(); } diff --git a/gemma/src/bin/stringcomp.rs b/gemma/src/bin/stringcomp.rs index 0b235bf..4b202dc 100644 --- a/gemma/src/bin/stringcomp.rs +++ b/gemma/src/bin/stringcomp.rs @@ -1,7 +1,6 @@ use clap::{Parser, Subcommand}; use flate2::{write::{GzEncoder, GzDecoder}, Compression}; -use std::io::prelude::*; -use std::io::{self, Write}; +use std::io::Write; #[derive(Parser)] #[command(author, version, about = "Compress or decompress a string", long_about = None)] diff --git a/gemma/src/chip8/computer.rs b/gemma/src/chip8/computer.rs index ddee91d..0453322 100644 --- a/gemma/src/chip8/computer.rs +++ b/gemma/src/chip8/computer.rs @@ -92,10 +92,6 @@ impl Chip8Computer { self.registers.set_pc(offset); } - pub fn execute_instruction(&mut self, instruction_to_execute: Chip8CpuInstructions) { - // - } - pub fn step_system(&mut self) -> &mut Chip8Computer { println!("Stepping System 1 Step"); diff --git a/gemma/src/chip8/instructions.rs b/gemma/src/chip8/instructions.rs index 6dba5fd..695f417 100644 --- a/gemma/src/chip8/instructions.rs +++ b/gemma/src/chip8/instructions.rs @@ -9,7 +9,6 @@ use rand::Rng; use serde::{Deserialize, Serialize}; use std::fmt::{Debug, Display, Formatter}; use std::ops::BitAnd; -use std::time::Instant; /* nnn or addr - A 12-bit value, the lowest 12 bits of the instruction @@ -681,7 +680,7 @@ impl Chip8CpuInstructions { } pub fn execute(&self, input: &mut Chip8Computer) -> Chip8Computer { // print!("INSTRUCTION {}", self); - let start_time = Instant::now(); + // let start_time = Instant::now(); let start_pc = input.registers.peek_pc(); input.registers.poke_pc(start_pc + 2); match self { @@ -796,7 +795,7 @@ impl Chip8CpuInstructions { let lhs = input.registers.peek(*x); let rhs = input.registers.peek(*y); - let mut result = 0; + let result; let borrow_flag: u8 = if rhs > lhs { result = (lhs as u16 + 0x100) - rhs as u16; @@ -1003,7 +1002,7 @@ impl Chip8CpuInstructions { let value_to_set = input.delay_timer.current(); input.registers.poke(*x, value_to_set); } - Chip8CpuInstructions::LDRK(x) => { + Chip8CpuInstructions::LDRK(_) => { // Fx0A - LD Vx, K // Wait for a key press, store the value of the key in Vx. // @@ -1061,9 +1060,6 @@ impl Chip8CpuInstructions { // 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 as u16) << 4) | units as u16; // 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); @@ -1191,11 +1187,11 @@ impl Chip8CpuInstructions { } } } - DW(addr) => { + DW(_) => { println!("DATA WORD FOUND..."); } }; - let cycle_time = Instant::now().duration_since(start_time).as_nanos(); + // let cycle_time = Instant::now().duration_since(start_time).as_nanos(); // println!("\t\tTook {cycle_time}ms"); input.to_owned() } diff --git a/gemma/src/chip8/util.rs b/gemma/src/chip8/util.rs index c243951..6779588 100644 --- a/gemma/src/chip8/util.rs +++ b/gemma/src/chip8/util.rs @@ -1,6 +1,8 @@ pub struct InstructionUtil {} impl InstructionUtil { + /// Byte To Bools + /// Convert a u8 byte to an array of bool pub fn byte_to_bools(to_convert: u8) -> [bool; 8] { let mut return_values = [false; 8]; for i in 0..8 { @@ -10,6 +12,8 @@ impl InstructionUtil { return_values } + /// Byte To Bools + /// Convert an array of bool to a u8 pub fn bools_to_byte(to_convert: [bool; 8]) -> u8 { let mut return_value = 0u8; for i in 0..to_convert.len() { @@ -21,6 +25,8 @@ impl InstructionUtil { return_value } + /// Split Bytes + /// Split a u16 into a pair of u8 values for high and low byte pub fn split_bytes(to_split: u16) -> (u8, u8) { let high = to_split.rotate_left(8) as u8; let low = (to_split & 0xff) as u8; @@ -28,37 +34,88 @@ impl InstructionUtil { (high, low) } + /// Join Bytes + /// Joins 2 u8 into a single u16 with high and low + /// bytes provided pub fn join_bytes(high: u8, low: u8) -> u16 { - (high as u16) << 8 | low as u16 } - // nnn or addr - A 12-bit value, the lowest 12 bits of the instruction + /// Swap Endian + /// Swaps the endianess of a value + /// 0xabcd -> 0xcdab + pub fn swap_endian(to_swap:u16) -> u16 { + let low = to_swap & 0xff; + let high = to_swap & 0xff00; + low << 8 | high >> 8 + } + + /// Split Bytes Swap Endian + /// Swaps endianness and return both bytes separately + pub fn split_bytes_swap_endian(to_split: u16) -> (u8, u8) { + InstructionUtil::split_bytes(InstructionUtil::swap_endian(to_split)) + } + + /// Join Bytes Swap Endian + /// Swaps endianness and returns the combined bytes + pub fn join_bytes_swap_endian(high :u8, low: u8) -> u16 { + InstructionUtil::swap_endian(InstructionUtil::join_bytes(high, low)) + } + + /// Read Address from Instruction + /// Reads the 'address' portion of an instruction + /// Returns the 12 LSB + /// 0xabcd -> 0x0bcd pub fn read_addr_from_instruction(instruction_to_read_from: u16) -> u16 { instruction_to_read_from & 0x0FFF } - // n or nibble - A 4-bit value, the lowest 4 bits of the instruction + /// Read Nibble from Instruction + /// Returns the lowest 4 bits of the instruction + /// 0xabcd -> 0x0d pub fn read_nibble_from_instruction(instruction_to_read_from: u16) -> u8 { ( instruction_to_read_from & 0x000F )as u8 } - // x - A 4-bit value, the lower 4 bits of the high byte of the instruction + /// Read X From Instruction + /// Returns the 4 LSB of the high byte of the instruction + /// Returns bits 9-13 + /// 0xabcd -> 0x0b pub fn read_x_from_instruction(instruction_to_read_from: u16) -> u8 { (instruction_to_read_from & 0x0F00).rotate_right(8) as u8 } - // y - A 4-bit value, the upper 4 bits of the low byte of the instruction + /// Read Y from Instruction + /// Returns the 4 MSB from the low byte of the instruction + /// Returns bits 5-8 + /// 0xabcd -> 0x0c pub fn read_y_from_instruction(instruction_to_read_from: u16) -> u8 { (instruction_to_read_from & 0x00F0).rotate_right(4) as u8 } - // kk or byte - An 8-bit value, the lowest 8 bits of the instruction + /// Read Byte from Instruction + /// Returns the 8 LSB from the instruction + /// Returns bits 0-7 + /// 0xabcd -> 0xcd pub fn read_byte_from_instruction(instruction_to_read_from: u16) -> u8 { (instruction_to_read_from & 0x00FF) as u8 } + /// Alias for read_x_from_instruction pub fn read_upper_byte_lower_nibble(to_read_from: u16) -> u8 { - ((to_read_from & 0x0f00) >> 8) as u8 + InstructionUtil::read_x_from_instruction(to_read_from) + } + + pub fn read_bits_from_instruction(instruction_bytes: u16, first_bit: u8, last_bit: u8) -> u16 { + // shift the value over by the 'first bit' bits + let working = instruction_bytes.rotate_right(first_bit as u32); + let mut working_mask: i32 = 0x00; + // build the mask + for _ in first_bit..=last_bit { + working_mask = working_mask.rotate_left(1) | 0x01; + } + + // apply the mask to the working value + working & working_mask as u16 } } diff --git a/gemma/src/chip8/video.rs b/gemma/src/chip8/video.rs index 80d295b..85c7f73 100644 --- a/gemma/src/chip8/video.rs +++ b/gemma/src/chip8/video.rs @@ -152,7 +152,7 @@ impl Chip8Video { } pub fn scroll_right(&mut self) { - println!("SCROLLRIGHTPRE:::[{}]", self.format_as_string()); + // println!("SCROLLRIGHTPRE:::[{}]", self.format_as_string()); let (width, height) = self.get_resolution(); for current_row in 0..height { @@ -168,11 +168,11 @@ impl Chip8Video { // Clear the first 4 pixels in the current row self.memory[row_offset..row_offset + 4].fill(false); } - println!("SCROLLRIGHTPOST:::[{}]", self.format_as_string()); + // println!("SCROLLRIGHTPOST:::[{}]", self.format_as_string()); } pub fn scroll_left(&mut self) { - println!("SCROLLLEFTPRE:::[{}]", self.format_as_string()); + // println!("SCROLLLEFTPRE:::[{}]", self.format_as_string()); let (width, height) = self.get_resolution(); for current_row in 0..height { @@ -181,7 +181,7 @@ impl Chip8Video { let target: usize = (row_offset + current_column) as usize; let source: usize = target + 4; self.memory[target] = self.memory[source]; - println!("Moving from {source} to {target}"); + // println!("Moving from {source} to {target}"); } let clear_start: usize = (row_offset + width - 4) as usize; @@ -189,7 +189,7 @@ impl Chip8Video { self.memory[clear_start..clear_end].fill(false); } - println!("SCROLLLEFTPOST:::[{}]", self.format_as_string()); + // println!("SCROLLLEFTPOST:::[{}]", self.format_as_string()); } pub fn scroll_up(&mut self, how_far: &u8) { @@ -240,7 +240,7 @@ impl Default for Chip8Video { Chip8Video { memory: mem, has_frame_changed: false, - current_res: Chip8VideoModes::LowRes, + current_res: LowRes, } } } diff --git a/gemma/src/chip8_2/instructions/add.rs b/gemma/src/chip8_2/instructions/add.rs index ac86548..5c4eeb7 100644 --- a/gemma/src/chip8_2/instructions/add.rs +++ b/gemma/src/chip8_2/instructions/add.rs @@ -18,6 +18,7 @@ impl InstructionTrait for Add { } fn decode(from: Vec) -> &dyn InstructionTrait { + todo!() } } \ No newline at end of file diff --git a/gemma/tests/state_tests.rs b/gemma/tests/state_tests.rs index e0f51bf..7ba1ff9 100644 --- a/gemma/tests/state_tests.rs +++ b/gemma/tests/state_tests.rs @@ -1,3 +1,4 @@ +use std::{fs, io}; use flate2::write::{GzDecoder, GzEncoder}; use flate2::Compression; use gemma::chip8::computer::Chip8Computer; @@ -14,7 +15,7 @@ fn load_compressed_result(file_path: &str) -> io::Result { let compressed_data = fs::read(file_path)?; // Create a GzDecoder to uncompress the data - let mut decoder = GzDecoder::new(&compressed_data[..]); + let mut decoder = GzDecoder::new(&mut compressed_data[..]); let mut decompressed_data = String::new(); // Read the decompressed data directly into a String @@ -60,4 +61,3 @@ fn computer_001_system_zero_state() { assert_eq!(serialized, expected_string); } -} diff --git a/gemma/tests/unit_tests.rs b/gemma/tests/unit_tests.rs index 971c64e..d34a23c 100644 --- a/gemma/tests/unit_tests.rs +++ b/gemma/tests/unit_tests.rs @@ -304,7 +304,7 @@ fn ld_dt_vx_test() { x.registers.poke(0x1, 0x10); Chip8CpuInstructions::LDD(0x1).execute(&mut x); assert_eq!(x.delay_timer.current(), 0x10); - for i in 0..0x20 { + for _ in 0..0x20 { x.delay_timer.tick(); } assert_eq!(x.delay_timer.current(), 0); diff --git a/gemma/tests/util_tests.rs b/gemma/tests/util_tests.rs new file mode 100644 index 0000000..dab1089 --- /dev/null +++ b/gemma/tests/util_tests.rs @@ -0,0 +1,219 @@ +use std::collections::BTreeMap; +use gemma::chip8::util::InstructionUtil; + +#[test] +fn byte_to_bools() { + let data_set: BTreeMap = BTreeMap::from( + [ + (0x00, [false, false, false, false, false, false, false, false]), + (0x0f, [true, true, true, true, false, false, false, false]), + (0xaa, [false, true, false, true, false, true, false, true]), + (0xf0, [false, false, false, false, true, true, true, true]), + (0xff, [true, true, true, true, true, true, true, true]), + ] + ); + + for (key, value) in data_set.iter() { + let result_array = InstructionUtil::byte_to_bools(*key); + for i in 0..8 { + assert_eq!(result_array[i], value[i]); + } + } +} + + +#[test] +fn bools_to_byte() { + let data_set: BTreeMap = BTreeMap::from( + [ + (0x00, [false, false, false, false, false, false, false, false]), + (0x0f, [true, true, true, true, false, false, false, false]), + (0xaa, [false, true, false, true, false, true, false, true]), + (0xf0, [false, false, false, false, true, true, true, true]), + (0xff, [true, true, true, true, true, true, true, true]), + ] + ); + + for (key, value) in data_set.iter() { + let result = InstructionUtil::bools_to_byte(*value); + assert_eq!(result, *key); + } +} + +#[test] +fn split_bytes() { + let data_set: BTreeMap = BTreeMap::from( + [ + (0xff00, (0xff, 0x00)), + (0x00ff, (0x00, 0xff)), + (0xabcd, (0xab, 0xcd)), + (0xcdab, (0xcd, 0xab)) + ] + ); + + for (key, value) in data_set.iter() { + assert_eq!(InstructionUtil::split_bytes(*key), *value); + } +} + +#[test] +fn join_bytes() { + let data_set: BTreeMap = BTreeMap::from( + [ + (0xff00, (0xff, 0x00)), + (0x00ff, (0x00, 0xff)), + (0xabcd, (0xab, 0xcd)), + (0xcdab, (0xcd, 0xab)) + ] + ); + + for (key, (low, high )) in data_set.iter() { + assert_eq!(InstructionUtil::join_bytes(*low, *high), *key); + } +} + +#[test] +fn swap_endian() { + let data_set: BTreeMap = BTreeMap::from( + [ + (0xabcd, 0xcdab), + (0x00ff, 0xff00), + (0xff00, 0x00ff), + (0xffff, 0xffff), + (0x0000, 0x0000) + ] + ); + + for (key, value) in data_set.iter() { + assert_eq!(InstructionUtil::swap_endian(*key), *value); + } +} + +#[test] +fn split_bytes_swap_endian() { + let data_set: BTreeMap = BTreeMap::from([ + (0x0000, (0x00, 0x00)), + (0xffff, (0xff, 0xff)), + (0xff00, (0x00, 0xff)), + (0x00ff, (0xff, 0x00)), + (0xabcd, (0xcd, 0xab)) + ]); + + for (key, result) in data_set.iter() { + assert_eq!(InstructionUtil::split_bytes_swap_endian(*key), *result) + } +} + +#[test] +fn join_bytes_swap_endian() { + let data_set: BTreeMap = BTreeMap::from([ + (0xffff, (0xff, 0xff)), + (0x0000, (0x00, 0x00)), + (0xff00, (0x00, 0xff)), + (0x00ff, (0xff, 0x00)), + (0xabcd, (0xcd, 0xab)) + ]); + + for (key, (low, high)) in data_set.iter() { + assert_eq!(InstructionUtil::join_bytes_swap_endian(*low, *high), *key); + } + +} + +#[test] +fn read_address_from_instruction() { + let data_set: BTreeMap = BTreeMap::from([ + (0xabcd, 0x0bcd), + (0xffff, 0x0fff), + (0x0000, 0x0000), + (0x8123, 0x0123) + ]) ; + + for (key, value) in data_set.iter() { + assert_eq!(InstructionUtil::read_addr_from_instruction(*key), *value); + } +} + +#[test] +fn read_nibble_from_instruction() { + let data_set: BTreeMap = BTreeMap::from([ + (0xffff, 0x0f), + (0x0000, 0x00), + (0xabcd, 0x0d), + ]); + + for(key, value) in data_set.iter() { + assert_eq!(InstructionUtil::read_nibble_from_instruction(*key), *value); + } +} + +#[test] +fn read_x_from_instruction() { + let data_set: BTreeMap = BTreeMap::from([ + (0xffff, 0x0f), + (0x0000, 0x00), + (0x0a00, 0x0a), + (0x0b00, 0x0b) + ]); + + for (key, value) in data_set.iter() { + assert_eq!(InstructionUtil::read_x_from_instruction(*key), *value); + } +} + +#[test] +fn read_y_from_instruction() { + let data_set: BTreeMap = BTreeMap::from([ + (0xffff, 0x0f), + (0x0000, 0x00), + (0x00a0, 0x0a), + (0x00b0, 0x0b) + ]); + + for (key, value) in data_set.iter() { + assert_eq!(InstructionUtil::read_y_from_instruction(*key), *value); + } +} + +#[test] +fn read_byte_from_instruction() { + let data_set: BTreeMap = BTreeMap::from([ + (0xffff, 0xff), + (0x0000, 0x00), + (0xabcd, 0xcd), + (0xcdab, 0xab) + ]); + + for (key, value) in data_set.iter() { + assert_eq!(InstructionUtil::read_byte_from_instruction(*key), *value); + } +} + +#[test] +fn read_upper_byte_lower_nibble() { + let data_set: BTreeMap = BTreeMap::from([ + (0x0000, 0x00), + (0xabcd, 0x0b), + (0xffff, 0x0f), + (0xcdab, 0x0d) + ]); + + for (key, value) in data_set.iter() { + assert_eq!(InstructionUtil::read_upper_byte_lower_nibble(*key), *value); + } +} + +#[test] +fn read_bits_from_instruction() { + let data_set: BTreeMap = BTreeMap::from([ + (0x0000, (0, 7, 0x00)), + (0x0000, (8, 15, 0x00)), + (0xffff, (0, 7, 0xff)), + (0xffff, (8, 15, 0xff)), + ]); + + for (key, (start, end, value)) in data_set.iter() { + let result = InstructionUtil::read_bits_from_instruction(*key, *start, *end); + assert_eq!(result, *value); + } +} \ No newline at end of file