Adds more util methods for swapping endianness and extracting data from instructions
Adds tests for new util methods
This commit is contained in:
parent
74bf2e71e4
commit
d27d2e8e45
@ -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();
|
||||
}
|
||||
|
||||
@ -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)]
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,6 +18,7 @@ impl InstructionTrait for Add {
|
||||
}
|
||||
|
||||
fn decode(from: Vec<u8>) -> &dyn InstructionTrait {
|
||||
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
@ -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<String> {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
219
gemma/tests/util_tests.rs
Normal file
219
gemma/tests/util_tests.rs
Normal file
@ -0,0 +1,219 @@
|
||||
use std::collections::BTreeMap;
|
||||
use gemma::chip8::util::InstructionUtil;
|
||||
|
||||
#[test]
|
||||
fn byte_to_bools() {
|
||||
let data_set: BTreeMap<u8, [bool; 8]> = 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<u8, [bool; 8]> = 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<u16, (u8, u8)> = 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<u16, (u8, u8)> = 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<u16, u16> = 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<u16, (u8, u8)> = 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<u16, (u8, u8)> = 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<u16, u16> = 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<u16, u8> = 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<u16, u8> = 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<u16, u8> = 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<u16, u8> = 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<u16, u8> = 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<u16, (u8, u8, u16)> = 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);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user