diff --git a/src/chip8.rs b/src/chip8.rs index 45fb4f1..5390acb 100644 --- a/src/chip8.rs +++ b/src/chip8.rs @@ -38,7 +38,16 @@ struct Chip8State { input: [bool; 16], } -pub fn run>(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>(chip8_executable_filepath: S, quirks: &Chip8Quirks, debug_mode: bool) { let mut state = Chip8State { eti_600_flag: false, mem: [0; 4096], @@ -63,10 +72,10 @@ pub fn run>(chip8_executable_filepath: S, debug_mode: bool) { let _ = memory::load_file_to_memory(&mut state, chip8_executable_filepath.as_ref()); // 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 // Init Rendering Pipeline let (mut rl, thread) = raylib::init() @@ -124,7 +133,7 @@ fn start(state: &mut Chip8State, debug_mode: bool) { debug::print_debug(state, instruction); } - cpu::execute_instruction(state, instruction); + cpu::execute_instruction(state, instruction, &quirks); } // move to timers.rs diff --git a/src/chip8/cpu.rs b/src/chip8/cpu.rs index 914717a..25c7671 100644 --- a/src/chip8/cpu.rs +++ b/src/chip8/cpu.rs @@ -1,10 +1,10 @@ -use crate::chip8::gpu; +use crate::chip8::{Chip8Quirks, gpu}; use super::Chip8State; use super::memory::read_n_bytes; // 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 x = ((instruction & 0x0F00) >> 8) 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, (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, _, _, 0x1) => state.r_v[x as usize] |= state.r_v[y as usize], - (0x8, _, _, 0x2) => 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], + (0x8, _, _, 0x1) => { + 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) => { 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 { @@ -68,6 +83,9 @@ pub fn execute_instruction(state: &mut Chip8State, instruction: u16) { state.r_v[0xF] = if flag { 1 } else { 0 }; } (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; state.r_v[x as usize] = state.r_v[x as usize] / 2; 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 }; } (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; state.r_v[x as usize] = state.r_v[x as usize].wrapping_mul(2); 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, - (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, _, _, _) => { let rng = rand::random_range(0..256) as u8; let result = rng & kk; state.r_v[x as usize] = result; } (0xD, _, _, _) => { - state.r_v[0xF] = 0; 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) => { 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; } + if quirks.memory { + state.r_i += i as u16; + } } _ => {} } diff --git a/src/chip8/gpu.rs b/src/chip8/gpu.rs index 9e82f42..5390368 100644 --- a/src/chip8/gpu.rs +++ b/src/chip8/gpu.rs @@ -9,68 +9,72 @@ pub static SPRITE_WIDTH: u8 = 8; // I probably don't need state here // 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) { + 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 start_x = state.r_v[vx as usize]; - let start_y = state.r_v[vy as usize]; - for y in start_y..start_y + bytes_to_draw_len { - 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; + while bytes_idx < bytes_to_draw_len { + // TODO: this should be a u8 for safety + let y: u8 = start_y.wrapping_add(bytes_idx) % CHIP8_DISPLAY_HEIGHT as u8; - 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 { - state.r_v[0xF] = 1; - } + let sprite_pixel = ((bytes_to_draw[bytes_idx as usize] >> (7 - bit_idx)) & 1) == 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; } } -pub fn wrapping_draw( +pub fn clipping_draw( state: &mut Chip8State, vx: u8, vy: u8, bytes_to_draw: &[u8], bytes_to_draw_len: u8, ) { - let mut bytes_idx = 0; - let start_x = state.r_v[vx as usize]; - let start_y = state.r_v[vy as usize]; + 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; while bytes_idx < bytes_to_draw_len { - let mut y = start_y.wrapping_add(bytes_idx); - if y as i32 >= CHIP8_DISPLAY_HEIGHT { - y = (y - CHIP8_DISPLAY_HEIGHT as u8) % CHIP8_DISPLAY_HEIGHT as u8; + let y: u16 = (start_y + bytes_idx) as u16; + // TODO: general reminder to cleanup type casts to be consistent + if y >= CHIP8_DISPLAY_HEIGHT as u16 { + break; } let mut bit_idx = 0; - while bit_idx < SPRITE_WIDTH { - let mut x = start_x.wrapping_add(bit_idx); - if x as i32 >= CHIP8_DISPLAY_WIDTH { - x = (x - CHIP8_DISPLAY_WIDTH as u8) % CHIP8_DISPLAY_WIDTH as u8; + let x = (start_x + bit_idx) as u16; + if x >= CHIP8_DISPLAY_WIDTH as u16 { + break; } 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]; - let new_pixel = current_pixel ^ sprite_pixel; + if 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.r_v[0xF] = 1; + state.display[y as usize][x as usize] ^= true; } bit_idx += 1; diff --git a/src/main.rs b/src/main.rs index 7d397c1..e7967b7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,17 +11,29 @@ struct Args { /// Set path for chip8 binary file to run #[arg(short, long)] file: Option, + #[arg(short = 'q', long = "quirks", value_delimiter = ',')] + quirks: Vec, } fn main() { 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 args.debug { - chip8::run(filepath, true); + chip8::run(filepath, &quirks, true); } else { - chip8::run(filepath, false); + chip8::run(filepath, &quirks, false); } } }