updates tests to cover most code.
-> Draw Sprite -> Ret -> Call
This commit is contained in:
parent
d1e39307a7
commit
8c3cc5085c
@ -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"
|
||||
|
||||
0
coverage.sh
Normal file
0
coverage.sh
Normal file
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
pub enum Chip8CpuStates {
|
||||
#[default]
|
||||
WaitingForInstruction,
|
||||
WaitingForKey,
|
||||
ExecutingInstruction,
|
||||
Error,
|
||||
}
|
||||
|
||||
@ -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, );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<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 {
|
||||
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<u8>, 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
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user