From 77ef72d2ee7bbe14b0485f9a7cf2e1c7b0502884 Mon Sep 17 00:00:00 2001 From: Garrett Dickinson Date: Sat, 10 Aug 2024 05:41:28 -0500 Subject: [PATCH] Basic instruction handling, more formatting, step-through mode --- programs/test.ch8 | Bin 12 -> 20 bytes src/chip8.rs | 157 +++++++++++++++++++++++++++++++++++++--------- src/main.rs | 21 +++++-- src/util.rs | 7 ++- 4 files changed, 151 insertions(+), 34 deletions(-) diff --git a/programs/test.ch8 b/programs/test.ch8 index 0bdc1c1303a1c5edd6ffa1f3b1fd2756e0bc00d9..99a4756c195220eaba4935bac25cc9575145e6f6 100644 GIT binary patch literal 20 acmZR0!0 0 - { - let addr: u16 = instr & ADDR_MASK; - debug_log_instr(state, instr, format!("SYS {:04X?}", addr)); - } - + // JP nnn; Jump to location nnn. + // Sets the program counter to nnn. if (instr & INSTR_MASK_JP) > 0 { let addr: u16 = instr & ADDR_MASK; debug_log_instr(state, instr, format!("JP {:04X?}", addr)); + + state.program_counter = addr; + return; } + // CALL nnn; Call subroutine at nnn + // Puts the current PC in the stack, then increments the stack pointer. The program counter + // is then set to nnn. if (instr & INSTR_MASK_CALL) > 0 { let addr: u16 = instr & ADDR_MASK; debug_log_instr(state, instr, format!("CALL {:04X?}", addr)); + + // Halt emulation if stack_ptr is increments above stack limit, as this will cause a + // stack overflow + if (state.stack_ptr + 1) > MAX_STACK_SIZE + { + halt(state, instr, format!("Cannot increment stack pointer above stack limit {}", MAX_STACK_SIZE)); + } + + state.stack[state.stack_ptr] = state.program_counter; + state.stack_ptr += 1; + state.program_counter = addr; + return; + } + + // SE Vx, byte; Skip next instruction if Vx = kk. + // Compares register Vx to kk, and if they are equal, increments the + // program counter by 2. + if (instr & INSTR_MASK_SE_VX) > 0 + { + let bytes:[u8; 2] = instr.to_be_bytes(); + let register_num: u8 = bytes[0] & 0x0F; + let value: u8 = bytes[1] & 0xFF; + debug_log_instr(state, instr, format!("SE V{:01X?} {:02X?}", register_num, value)); + + if value == state.registers[register_num as usize] + { + state.program_counter += 2; + } + return; } } @@ -187,8 +263,33 @@ fn parse_instruction(state: &mut EmulationState, instr: u16) { /// fn debug_log_instr(state: &mut EmulationState, instr: u16, msg: String) { - if state.debug_enabled + if state.options.debug_mode { println!("PC: {:04?}; INSTR {:04X?}; {}", state.program_counter, instr, msg); } -} \ No newline at end of file +} + +/// # halt +/// +/// Halt the emulator and throw an error +/// +/// * `msg` - Message string describing the error +/// +fn halt(state: &mut EmulationState, instr: u16, msg: String) +{ + println!("ERROR: EMULATION HALTED\n"); + println!("\t{}\n", msg); + println!("\tPC: {:04?}\t\tINSTR {:04X?}", state.program_counter, instr); + exit(0); +} + +/// # Pause +/// +/// Pause the emulation and poll for keyboard input; +/// Used for stepping over program data 1 instruction at a time +/// +fn pause() { + // Read a single byte and discard + let mut stdin = io::stdin(); + let _ = stdin.read(&mut [0u8]).unwrap(); +} diff --git a/src/main.rs b/src/main.rs index 8ec75b6..fb33cb2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use core::num; +use std::option; use std::path; use std::env; use std::fs; @@ -8,6 +8,14 @@ use std::process::exit; mod util; mod chip8; + +#[derive(Default)] +struct EmulationOpts { + debug_mode: bool, + step_mode: bool +} + + /// # main /// /// Main entry point of the emulator @@ -37,7 +45,7 @@ fn main() } // Runtime options - let mut debug_mode: bool = false; + let mut emu_options: EmulationOpts = EmulationOpts{..Default::default()}; // Option parsing let num_options: usize = args.len() - 1; @@ -49,7 +57,12 @@ fn main() for char in flags.chars() { if char == 'd' { - debug_mode = true; + println!("Enabling debug mode..."); + emu_options.debug_mode = true; + } + if char == 's' { + println!("Enabling step-through mode..."); + emu_options.step_mode = true; } } } @@ -80,7 +93,7 @@ fn main() } println!("Read {} bytes of program data", file_data.len()); - chip8::start_emulation(program, debug_mode); + chip8::start_emulation(program, emu_options); } } else diff --git a/src/util.rs b/src/util.rs index c6c6ac5..7280cd9 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,9 +1,12 @@ pub fn print_help() { - print!("Usage: saltnvinegar [CHIP8_FILE]\n"); + println!("Usage: saltnvinegar [-OPTIONS] [CHIP8_FILE]"); + println!("Options:"); + println!("\t-d\tEnable debug output to terminal"); + println!("\t-s\tEnable step-through-instruction mode"); } pub fn u8_tuple_to_u16(u8_in: (u8, u8)) -> u16 { let head_byte: u16 = u16::from(u8_in.0) << 8; let tail_byte: u16 = u16::from(u8_in.1); return head_byte + tail_byte; -} \ No newline at end of file +}