a whole lotta quirk fixes

This commit is contained in:
2026-01-31 01:04:36 -06:00
parent 235c7759fc
commit 3693caa458
4 changed files with 107 additions and 49 deletions

View File

@@ -38,7 +38,16 @@ struct Chip8State {
input: [bool; 16], input: [bool; 16],
} }
pub fn run<S: AsRef<str>>(chip8_executable_filepath: S, debug_mode: bool) { pub struct Chip8Quirks {
pub vf_reset: bool,
pub memory: bool,
pub display_wait: bool, // TODO: Looks to be working, but not in quirks test file
pub clipping: bool, // TODO: Looks to be working, but not in quirks test file
pub shifting: bool,
pub jumping: bool,
}
pub fn run<S: AsRef<str>>(chip8_executable_filepath: S, quirks: &Chip8Quirks, debug_mode: bool) {
let mut state = Chip8State { let mut state = Chip8State {
eti_600_flag: false, eti_600_flag: false,
mem: [0; 4096], mem: [0; 4096],
@@ -63,10 +72,10 @@ pub fn run<S: AsRef<str>>(chip8_executable_filepath: S, debug_mode: bool) {
let _ = memory::load_file_to_memory(&mut state, chip8_executable_filepath.as_ref()); let _ = memory::load_file_to_memory(&mut state, chip8_executable_filepath.as_ref());
// Run Program // Run Program
start(&mut state, debug_mode); start(&mut state, &quirks, debug_mode);
} }
fn start(state: &mut Chip8State, debug_mode: bool) { fn start(state: &mut Chip8State, quirks: &Chip8Quirks, debug_mode: bool) {
// TODO rip out as much RL stuff from here and put into renderer // TODO rip out as much RL stuff from here and put into renderer
// Init Rendering Pipeline // Init Rendering Pipeline
let (mut rl, thread) = raylib::init() let (mut rl, thread) = raylib::init()
@@ -124,7 +133,7 @@ fn start(state: &mut Chip8State, debug_mode: bool) {
debug::print_debug(state, instruction); debug::print_debug(state, instruction);
} }
cpu::execute_instruction(state, instruction); cpu::execute_instruction(state, instruction, &quirks);
} }
// move to timers.rs // move to timers.rs

View File

@@ -1,10 +1,10 @@
use crate::chip8::gpu; use crate::chip8::{Chip8Quirks, gpu};
use super::Chip8State; use super::Chip8State;
use super::memory::read_n_bytes; use super::memory::read_n_bytes;
// Need to come back and add good comments for each of the match patterns // Need to come back and add good comments for each of the match patterns
pub fn execute_instruction(state: &mut Chip8State, instruction: u16) { pub fn execute_instruction(state: &mut Chip8State, instruction: u16, quirks: &Chip8Quirks) {
let c = ((instruction & 0xF000) >> 12) as u8; let c = ((instruction & 0xF000) >> 12) as u8;
let x = ((instruction & 0x0F00) >> 8) as u8; let x = ((instruction & 0x0F00) >> 8) as u8;
let y = ((instruction & 0x00F0) >> 4) as u8; let y = ((instruction & 0x00F0) >> 4) as u8;
@@ -49,9 +49,24 @@ pub fn execute_instruction(state: &mut Chip8State, instruction: u16) {
(0x6, _, _, _) => state.r_v[x as usize] = kk, (0x6, _, _, _) => state.r_v[x as usize] = kk,
(0x7, _, _, _) => state.r_v[x as usize] = state.r_v[x as usize].wrapping_add(kk), (0x7, _, _, _) => state.r_v[x as usize] = state.r_v[x as usize].wrapping_add(kk),
(0x8, _, _, 0x0) => state.r_v[x as usize] = state.r_v[y as usize], (0x8, _, _, 0x0) => state.r_v[x as usize] = state.r_v[y as usize],
(0x8, _, _, 0x1) => state.r_v[x as usize] |= state.r_v[y as usize], (0x8, _, _, 0x1) => {
(0x8, _, _, 0x2) => state.r_v[x as usize] &= state.r_v[y as usize], state.r_v[x as usize] |= state.r_v[y as usize];
(0x8, _, _, 0x3) => state.r_v[x as usize] ^= state.r_v[y as usize], if quirks.vf_reset {
state.r_v[0xF] = 0;
}
}
(0x8, _, _, 0x2) => {
state.r_v[x as usize] &= state.r_v[y as usize];
if quirks.vf_reset {
state.r_v[0xF] = 0;
}
}
(0x8, _, _, 0x3) => {
state.r_v[x as usize] ^= state.r_v[y as usize];
if quirks.vf_reset {
state.r_v[0xF] = 0;
}
}
(0x8, _, _, 0x4) => { (0x8, _, _, 0x4) => {
let val: u16 = state.r_v[x as usize] as u16 + state.r_v[y as usize] as u16; let val: u16 = state.r_v[x as usize] as u16 + state.r_v[y as usize] as u16;
if val > u8::MAX as u16 { if val > u8::MAX as u16 {
@@ -68,6 +83,9 @@ pub fn execute_instruction(state: &mut Chip8State, instruction: u16) {
state.r_v[0xF] = if flag { 1 } else { 0 }; state.r_v[0xF] = if flag { 1 } else { 0 };
} }
(0x8, _, _, 0x6) => { (0x8, _, _, 0x6) => {
if !quirks.shifting {
state.r_v[x as usize] = state.r_v[y as usize];
}
let flag = (state.r_v[x as usize] & 0b00000001) == 1; let flag = (state.r_v[x as usize] & 0b00000001) == 1;
state.r_v[x as usize] = state.r_v[x as usize] / 2; state.r_v[x as usize] = state.r_v[x as usize] / 2;
state.r_v[0xF] = if flag { 1 } else { 0 }; state.r_v[0xF] = if flag { 1 } else { 0 };
@@ -78,6 +96,9 @@ pub fn execute_instruction(state: &mut Chip8State, instruction: u16) {
state.r_v[0xF] = if flag { 1 } else { 0 }; state.r_v[0xF] = if flag { 1 } else { 0 };
} }
(0x8, _, _, 0xE) => { (0x8, _, _, 0xE) => {
if !quirks.shifting {
state.r_v[x as usize] = state.r_v[y as usize];
}
let flag = ((state.r_v[x as usize] & 0b10000000) >> 7) == 1; let flag = ((state.r_v[x as usize] & 0b10000000) >> 7) == 1;
state.r_v[x as usize] = state.r_v[x as usize].wrapping_mul(2); state.r_v[x as usize] = state.r_v[x as usize].wrapping_mul(2);
state.r_v[0xF] = if flag { 1 } else { 0 }; state.r_v[0xF] = if flag { 1 } else { 0 };
@@ -88,16 +109,25 @@ pub fn execute_instruction(state: &mut Chip8State, instruction: u16) {
} }
} }
(0xA, _, _, _) => state.r_i = nnn, (0xA, _, _, _) => state.r_i = nnn,
(0xB, _, _, _) => state.r_pc = nnn + state.r_v[0] as u16, (0xB, _, _, _) => {
if quirks.jumping {
state.r_pc = nnn + state.r_v[x as usize] as u16;
} else {
state.r_pc = nnn + state.r_v[0] as u16;
}
}
(0xC, _, _, _) => { (0xC, _, _, _) => {
let rng = rand::random_range(0..256) as u8; let rng = rand::random_range(0..256) as u8;
let result = rng & kk; let result = rng & kk;
state.r_v[x as usize] = result; state.r_v[x as usize] = result;
} }
(0xD, _, _, _) => { (0xD, _, _, _) => {
state.r_v[0xF] = 0;
let bytes = read_n_bytes(&state.mem, state.mem.len(), state.r_i as usize, n as usize); let bytes = read_n_bytes(&state.mem, state.mem.len(), state.r_i as usize, n as usize);
gpu::wrapping_draw(state, x, y, &bytes, n); if quirks.clipping {
gpu::clipping_draw(state, x, y, &bytes, n);
} else {
gpu::draw(state, x, y, &bytes, n);
}
} }
(0xE, _, _, 0xE) => { (0xE, _, _, 0xE) => {
let key_index = state.r_v[x as usize]; let key_index = state.r_v[x as usize];
@@ -153,6 +183,9 @@ pub fn execute_instruction(state: &mut Chip8State, instruction: u16) {
i = i + 1; i = i + 1;
} }
if quirks.memory {
state.r_i += i as u16;
}
} }
_ => {} _ => {}
} }

View File

@@ -9,68 +9,72 @@ pub static SPRITE_WIDTH: u8 = 8;
// I probably don't need state here // I probably don't need state here
// refactor to just receive a mutatable buffer // refactor to just receive a mutatable buffer
pub fn draw(state: &mut Chip8State, vx: u8, vy: u8, bytes_to_draw: &[u8], bytes_to_draw_len: u8) { pub fn draw(state: &mut Chip8State, vx: u8, vy: u8, bytes_to_draw: &[u8], bytes_to_draw_len: u8) {
state.r_v[0xF] = 0;
let start_x = state.r_v[vx as usize] % CHIP8_DISPLAY_WIDTH as u8;
let start_y = state.r_v[vy as usize] % CHIP8_DISPLAY_HEIGHT as u8;
let mut bytes_idx = 0; let mut bytes_idx = 0;
let start_x = state.r_v[vx as usize]; while bytes_idx < bytes_to_draw_len {
let start_y = state.r_v[vy as usize]; // TODO: this should be a u8 for safety
for y in start_y..start_y + bytes_to_draw_len { let y: u8 = start_y.wrapping_add(bytes_idx) % CHIP8_DISPLAY_HEIGHT as u8;
println!("{}", bytes_idx);
if (y as i32) < CHIP8_DISPLAY_HEIGHT {
// 8 is the hardcoded sprite width, has to atleast have 1 8 bit value to display
let mut bit_idx = 0;
for x in start_x..start_x + SPRITE_WIDTH {
println!("(X,Y): ({},{})", x, y);
if (x as i32) < CHIP8_DISPLAY_WIDTH {
let sprite_pixel = ((bytes_to_draw[bytes_idx] >> (7 - bit_idx)) & 1) == 1;
let current_pixel = state.display[y as usize][x as usize];
let new_pixel = current_pixel ^ sprite_pixel;
state.display[y as usize][x as usize] = new_pixel; let mut bit_idx = 0;
while bit_idx < SPRITE_WIDTH {
let x: u8 = start_x.wrapping_add(bit_idx) % CHIP8_DISPLAY_WIDTH as u8;
if new_pixel != current_pixel { let sprite_pixel = ((bytes_to_draw[bytes_idx as usize] >> (7 - bit_idx)) & 1) == 1;
state.r_v[0xF] = 1; if sprite_pixel {
} let current_pixel = state.display[y as usize][x as usize];
bit_idx += 1; if current_pixel {
state.r_v[0xF] = 1;
} }
state.display[y as usize][x as usize] ^= true;
} }
bit_idx += 1;
} }
bytes_idx += 1; bytes_idx += 1;
} }
} }
pub fn wrapping_draw( pub fn clipping_draw(
state: &mut Chip8State, state: &mut Chip8State,
vx: u8, vx: u8,
vy: u8, vy: u8,
bytes_to_draw: &[u8], bytes_to_draw: &[u8],
bytes_to_draw_len: u8, bytes_to_draw_len: u8,
) { ) {
let mut bytes_idx = 0; state.r_v[0xF] = 0;
let start_x = state.r_v[vx as usize]; let start_x = state.r_v[vx as usize] % CHIP8_DISPLAY_WIDTH as u8;
let start_y = state.r_v[vy as usize]; let start_y = state.r_v[vy as usize] % CHIP8_DISPLAY_HEIGHT as u8;
let mut bytes_idx = 0;
while bytes_idx < bytes_to_draw_len { while bytes_idx < bytes_to_draw_len {
let mut y = start_y.wrapping_add(bytes_idx); let y: u16 = (start_y + bytes_idx) as u16;
if y as i32 >= CHIP8_DISPLAY_HEIGHT { // TODO: general reminder to cleanup type casts to be consistent
y = (y - CHIP8_DISPLAY_HEIGHT as u8) % CHIP8_DISPLAY_HEIGHT as u8; if y >= CHIP8_DISPLAY_HEIGHT as u16 {
break;
} }
let mut bit_idx = 0; let mut bit_idx = 0;
while bit_idx < SPRITE_WIDTH { while bit_idx < SPRITE_WIDTH {
let mut x = start_x.wrapping_add(bit_idx); let x = (start_x + bit_idx) as u16;
if x as i32 >= CHIP8_DISPLAY_WIDTH { if x >= CHIP8_DISPLAY_WIDTH as u16 {
x = (x - CHIP8_DISPLAY_WIDTH as u8) % CHIP8_DISPLAY_WIDTH as u8; break;
} }
let sprite_pixel = ((bytes_to_draw[bytes_idx as usize] >> (7 - bit_idx)) & 1) == 1; let sprite_pixel = ((bytes_to_draw[bytes_idx as usize] >> (7 - bit_idx)) & 1) == 1;
let current_pixel = state.display[y as usize][x as usize]; if sprite_pixel {
let new_pixel = current_pixel ^ sprite_pixel; let current_pixel = state.display[y as usize][x as usize];
state.display[y as usize][x as usize] = new_pixel; if current_pixel {
state.r_v[0xF] = 1;
}
if new_pixel != current_pixel { state.display[y as usize][x as usize] ^= true;
state.r_v[0xF] = 1;
} }
bit_idx += 1; bit_idx += 1;

View File

@@ -11,17 +11,29 @@ struct Args {
/// Set path for chip8 binary file to run /// Set path for chip8 binary file to run
#[arg(short, long)] #[arg(short, long)]
file: Option<String>, file: Option<String>,
#[arg(short = 'q', long = "quirks", value_delimiter = ',')]
quirks: Vec<String>,
} }
fn main() { fn main() {
let args = Args::parse(); let args = Args::parse();
// TODO: should define my raylib/sound/etc handles here eventually and pass them down let quirks = chip8::Chip8Quirks {
vf_reset: args.quirks.contains(&String::from("vfreset"))
|| args.quirks.contains(&String::from("chip8")),
memory: args.quirks.contains(&String::from("memory"))
|| args.quirks.contains(&String::from("chip8")),
display_wait: args.quirks.contains(&String::from("displaywait")),
clipping: args.quirks.contains(&String::from("clipping"))
|| args.quirks.contains(&String::from("chip8")),
shifting: args.quirks.contains(&String::from("shifting")),
jumping: args.quirks.contains(&String::from("jumping")),
};
if let Some(filepath) = args.file { if let Some(filepath) = args.file {
if args.debug { if args.debug {
chip8::run(filepath, true); chip8::run(filepath, &quirks, true);
} else { } else {
chip8::run(filepath, false); chip8::run(filepath, &quirks, false);
} }
} }
} }