updates tests to cover most code.

-> Draw Sprite
-> Ret
-> Call
This commit is contained in:
Trevor Merritt 2024-10-06 10:33:02 -04:00
parent d1e39307a7
commit 8c3cc5085c
6 changed files with 395 additions and 75 deletions

View File

@ -1,5 +1,5 @@
[alias] [alias]
coverage = "tarpaulin --out Html --skip-clean" coverage = "tarpaulin --out Html --skip-clean --output-dir coverage"
[build] [build]
# rustc-wrapper = "sccache" rustc-wrapper = "sccache"

0
coverage.sh Normal file
View File

View File

@ -22,7 +22,6 @@ pub struct Chip8Computer {
pub state: Chip8CpuStates, pub state: Chip8CpuStates,
pub keypad: Keypad pub keypad: Keypad
} }
impl Default for Chip8Computer { impl Default for Chip8Computer {
fn default() -> Self { fn default() -> Self {
Self { Self {
@ -77,8 +76,17 @@ impl Chip8Computer {
// todo: THIS IS BAD AND IS A SIDE EFFECT // todo: THIS IS BAD AND IS A SIDE EFFECT
decoded_instruction.execute(self); decoded_instruction.execute(self);
self.sound_timer.tick(); match self.state {
self.delay_timer.tick(); Chip8CpuStates::WaitingForInstruction => {
self.sound_timer.tick();
self.delay_timer.tick();
}
Chip8CpuStates::WaitingForKey => {
println!("waiting for a key press...");
}
_ => {}
}
self self
} }

View File

@ -2,6 +2,7 @@
pub enum Chip8CpuStates { pub enum Chip8CpuStates {
#[default] #[default]
WaitingForInstruction, WaitingForInstruction,
WaitingForKey,
ExecutingInstruction, ExecutingInstruction,
Error, Error,
} }

View File

@ -3,6 +3,7 @@ use imgui::ColorPicker3;
use log::debug; use log::debug;
use rand::random; use rand::random;
use crate::chip8::computer::{Chip8Computer}; use crate::chip8::computer::{Chip8Computer};
use crate::chip8::cpu_states::Chip8CpuStates::WaitingForKey;
use crate::chip8::instructions::Chip8CpuInstructions::XXXXERRORINSTRUCTION; use crate::chip8::instructions::Chip8CpuInstructions::XXXXERRORINSTRUCTION;
use crate::chip8::util::InstructionUtil; use crate::chip8::util::InstructionUtil;
use crate::chip8::video::Chip8Video; use crate::chip8::video::Chip8Video;
@ -424,7 +425,7 @@ impl Chip8CpuInstructions {
// 0x0nnn Exit to System Call // 0x0nnn Exit to System Call
Chip8CpuInstructions::SysAddr(new_address) => { Chip8CpuInstructions::SysAddr(new_address) => {
debug!("SysAddr [0x{new_address:3x}]"); debug!("SysAddr [0x{new_address:3x}]");
// println!("SYS TO [{new_address}]");
input.registers.poke_pc(*new_address as u16); input.registers.poke_pc(*new_address as u16);
} }
// * 0x00E0 Clear Screen // * 0x00E0 Clear Screen
@ -464,7 +465,7 @@ impl Chip8CpuInstructions {
Chip8CpuInstructions::SeVxVy(x, y) => { Chip8CpuInstructions::SeVxVy(x, y) => {
let lhs = input.registers.peek(*x as u8); let lhs = input.registers.peek(*x as u8);
let rhs = input.registers.peek(*y as u8); let rhs = input.registers.peek(*y as u8);
// println!("COMPARING [{lhs}] to [{rhs}]");
if lhs == rhs { if lhs == rhs {
input.registers.advance_pc(); 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. // 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 y_register = input.registers.peek(*y as u8);
let x_register = input.registers.peek(*x 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(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, _) => { Chip8CpuInstructions::ShlVxVy(x, _) => {
@ -591,31 +597,42 @@ impl Chip8CpuInstructions {
} }
Chip8CpuInstructions::DrawVxVyNibble(x, y, n) => { Chip8CpuInstructions::DrawVxVyNibble(x, y, n) => {
// Display n-byte sprite starting at memory location I at (Vx, Vy), set VF = collision. // 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. // 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). // These bytes are then displayed as sprites on screen at coordinates (Vx, Vy).
// Sprites are XORed onto the existing screen. // Sprites are XORed onto the existing screen.
// If this causes any pixels to be erased, VF is set to 1, // If this causes any pixels to be erased, VF is set to 1,
// otherwise it is set to 0. // otherwise it is set to 0.
// If the sprite is positioned so part of it is outside the coordinates of the display, // 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. // 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; let num_bytes_to_read = *n;
for i in start_position..start_position + num_bytes_to_read { println!("CHIP8CPUINSTRUCTION:DRAWVXNIBBLE -> PREPARING TO READ {num_bytes_to_read} BYTES FROM MEMORY TO VIDEO");
// let current_byte = input.memory[i as usize]; for byte_index in 0..num_bytes_to_read {
println!("READ BYTE [0x{i:2x}]"); 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; 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 { if did_change {
input.registers.poke(0xf, 1u8); input.registers.poke(0xf, 1u8);
} else { } else {
@ -657,6 +674,7 @@ impl Chip8CpuInstructions {
// Wait for a key press, store the value of the key in Vx. // 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. // 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) => { Chip8CpuInstructions::LdDtVx(source_register) => {
// Fx15 - LD DT, Vx // Fx15 - LD DT, Vx
@ -683,13 +701,39 @@ impl Chip8CpuInstructions {
// Fx29 - LD F, Vx // Fx29 - LD F, Vx
// Set I = location of sprite for digit 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) => { Chip8CpuInstructions::LdBVx(x) => {
// Fx33 - LD B, Vx // Fx33 - LD B, Vx
// Store BCD representation of Vx in memory locations I, I+1, and I+2. // 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) => { Chip8CpuInstructions::LdIVx(x) => {
// Store registers V0 through Vx in memory starting at location I. // 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. // The interpreter reads values from memory starting at location I into registers V0 through Vx.
let offset = input.registers.peek_i(); let offset = input.registers.peek_i();
debug!("STARTING TO READ AT {offset:03x}");
let num_loops = input.registers.peek(*x as u8); let num_loops = input.registers.peek(*x as u8);
debug!("WILL READ {num_loops:x} BYTES");
for index in 0..num_loops { 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 => {} Chip8CpuInstructions::XXXXERRORINSTRUCTION => {}
@ -719,6 +768,9 @@ impl Chip8CpuInstructions {
#[cfg(test)] #[cfg(test)]
mod 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::*; use super::*;
#[test] #[test]
@ -1019,6 +1071,7 @@ mod test {
assert_eq!(x.registers.peek(0xf), 1); assert_eq!(x.registers.peek(0xf), 1);
assert_eq!(x.registers.peek_pc(), 0x206); assert_eq!(x.registers.peek_pc(), 0x206);
} }
#[test] #[test]
fn ldi_addr_test() { fn ldi_addr_test() {
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
@ -1035,9 +1088,10 @@ mod test {
Chip8CpuInstructions::JpV0Addr(0x100).execute(&mut x); Chip8CpuInstructions::JpV0Addr(0x100).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0x1FF); assert_eq!(x.registers.peek_pc(), 0x1FF);
} }
#[test] #[test]
fn cls_test() { fn cls_test() {
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
Chip8CpuInstructions::CLS.execute(&mut x); Chip8CpuInstructions::CLS.execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0x202); assert_eq!(x.registers.peek_pc(), 0x202);
} }
@ -1065,39 +1119,39 @@ mod test {
assert_eq!(x.registers.peek_i(), 0xacc); assert_eq!(x.registers.peek_i(), 0xacc);
} }
#[test] #[test]
fn ldstvt_test() { fn ldstvt_test() {
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0x01, 0xf0).execute(&mut x); Chip8CpuInstructions::LdVxByte(0x01, 0xf0).execute(&mut x);
Chip8CpuInstructions::LdStVx(0x01).execute(&mut x); Chip8CpuInstructions::LdStVx(0x01).execute(&mut x);
assert_eq!(x.sound_timer.current(), 0xf0); assert_eq!(x.sound_timer.current(), 0xf0);
x.sound_timer.tick(); x.sound_timer.tick();
x.sound_timer.tick(); x.sound_timer.tick();
x.sound_timer.tick(); x.sound_timer.tick();
assert_eq!(x.sound_timer.current(), 0xed); assert_eq!(x.sound_timer.current(), 0xed);
} }
#[test] #[test]
fn rnd_vx_byte_text() { fn rnd_vx_byte_text() {
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
Chip8CpuInstructions::RndVxByte(0x1, 0x0f).execute(&mut x); Chip8CpuInstructions::RndVxByte(0x1, 0x0f).execute(&mut x);
let new_value = x.registers.peek(0x1); let new_value = x.registers.peek(0x1);
assert!(new_value < 0x10); assert!(new_value < 0x10);
} }
/* /*
#[test] #[test]
fn skp_vx_test() { fn skp_vx_test() {
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
x.keypad.push_key(0x1); x.keypad.push_key(0x1);
Chip8CpuInstructions::LdVxByte(0x1, 0x1).execute(&mut x); Chip8CpuInstructions::LdVxByte(0x1, 0x1).execute(&mut x);
Chip8CpuInstructions::SkpVx(0x1).execute(&mut x); Chip8CpuInstructions::SkpVx(0x1).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0x208); assert_eq!(x.registers.peek_pc(), 0x208);
x.keypad.release_key(0x1); x.keypad.release_key(0x1);
Chip8CpuInstructions::SkpVx(0x1).execute(&mut x); Chip8CpuInstructions::SkpVx(0x1).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0x20A); assert_eq!(x.registers.peek_pc(), 0x20A);
} }
*/ */
#[test] #[test]
fn add_vx_byte_test() { fn add_vx_byte_test() {
@ -1139,7 +1193,7 @@ mod test {
Chip8CpuInstructions::LdVxByte(0x1, 0x10).execute(&mut x); Chip8CpuInstructions::LdVxByte(0x1, 0x10).execute(&mut x);
Chip8CpuInstructions::LdDtVx(0x1).execute(&mut x); Chip8CpuInstructions::LdDtVx(0x1).execute(&mut x);
assert_eq!(x.delay_timer.current(), 0x10); assert_eq!(x.delay_timer.current(), 0x10);
for i in 0..0x20 { for i in 0..0x20 {
x.delay_timer.tick(); x.delay_timer.tick();
} }
assert_eq!(x.delay_timer.current(), 0); assert_eq!(x.delay_timer.current(), 0);
@ -1157,4 +1211,234 @@ mod test {
assert_eq!(x.registers.peek(0x1), 0xed); 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, );
}
}
}
} }

View File

@ -28,24 +28,32 @@ impl Chip8Video {
} }
pub fn poke(&mut self, address: u16, new_value: bool) -> Self { 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.memory[address as usize] = new_value;
self.to_owned() self.to_owned()
} }
pub fn poke_byte(&mut self, first_address: u16, to_write: u8) -> Self { 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() { for i in (0..8).rev() {
let shifted = ((1 << i) & to_write) >> i; let shifted = ((1 << i) & to_write) >> i;
// //
let target_address = first_address + (7 - i); let target_address = first_address + (7 - i);
let is_set = shifted == 1; let is_set = shifted == 1;
println!("POKE {} with {} / {shifted:8b}", target_address, is_set);
self.poke(target_address, is_set); self.poke(target_address, is_set);
} }
self.to_owned() self.to_owned()
} }
pub fn poke_sprite(&mut self, first_address: u16, to_write: Vec<u8>) -> 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 { pub fn format_as_string(self) -> String {
let mut output = String::new(); let mut output = String::new();
for row in 0..32 { for row in 0..32 {
@ -65,9 +73,7 @@ impl Chip8Video {
output output
} }
pub fn write_sprite(&mut self, sprite_data: Vec<u8>, origin: (u8, u8)) {
debug!("Writing [{:?}] at [{}]x[{}]", sprite_data, origin.0, origin.1);
}
} }
impl Default for Chip8Video { impl Default for Chip8Video {
@ -219,15 +225,15 @@ mod test {
assert!(v.peek(0x46)); assert!(v.peek(0x46));
assert!(v.peek(0x47)); assert!(v.peek(0x47));
// row 3 column 1 // row 3 column 1
assert!(!v.peek(0xC0)); assert!(!v.peek(0xC0));
assert!(v.peek(0xC1)); assert!(v.peek(0xC1));
assert!(!v.peek(0xC2)); assert!(!v.peek(0xC2));
assert!(v.peek(0xC3)); assert!(v.peek(0xC3));
assert!(!v.peek(0xC4)); assert!(!v.peek(0xC4));
assert!(v.peek(0xC5)); assert!(v.peek(0xC5));
assert!(!v.peek(0xC6)); assert!(!v.peek(0xC6));
assert!(v.peek(0xC7)); assert!(v.peek(0xC7));
} }
} }
@ -251,7 +257,6 @@ mod test {
} }
let test_offset = (x_offset * 64 + y_offset) as u16; 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));
assert!(!v.peek(test_offset + 1)); assert!(!v.peek(test_offset + 1));
assert!(!v.peek(test_offset + 2)); assert!(!v.peek(test_offset + 2));
@ -270,16 +275,38 @@ mod test {
assert!(v.peek(test_offset + 5)); assert!(v.peek(test_offset + 5));
assert!(v.peek(test_offset + 6)); assert!(v.peek(test_offset + 6));
assert!(v.peek(test_offset + 7)); assert!(v.peek(test_offset + 7));
} }
#[test] #[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
}