diff --git a/src/chip8.rs b/src/chip8.rs index 099fbdf..192fa91 100644 --- a/src/chip8.rs +++ b/src/chip8.rs @@ -1,6 +1,8 @@ // chip8.rs // Implementation of chip8 machine, registers, and memory makeup +use std::fmt::Debug; + use crate::util; // Memory map constants @@ -54,38 +56,64 @@ struct EmulationState { // | Reserved for | // | interpreter | // +---------------+= 0x000 (0) Start of Chip-8 RAM - memory: [u8; 4096] + memory: [u8; 4096], + + // Misc members + debug_enabled: bool } -pub fn start_emulation(data: [u8; MAX_PRG_SIZE]) { - let mut state: EmulationState = EmulationState{ +/// # start_emulation +/// +/// Begin emulating the loaded CHIP8 program. +/// +/// * `data` - u8 array of program data. +/// +pub fn start_emulation(data: [u8; MAX_PRG_SIZE], dbg_mode: bool) +{ + let mut state: EmulationState = EmulationState { program_counter: 0, stack_ptr: 0, registers: [0; 16], - memory: [0; MAX_MEM_SIZE] + memory: [0; MAX_MEM_SIZE], + debug_enabled: dbg_mode }; load_program(data, &mut state); exec_program(&mut state); } -// Load the program into virtual memory -fn load_program(data: [u8; MAX_PRG_SIZE], state: &mut EmulationState) { +/// # load_program +/// +/// Load the program into virtual memory. +/// +/// * `data` - u8 array of program data. +/// * `state` - Global emulation state. +/// +fn load_program(data: [u8; MAX_PRG_SIZE], state: &mut EmulationState) +{ println!("Copying program data to memory"); - for i in 0..MAX_PRG_SIZE { + for i in 0..MAX_PRG_SIZE + { state.memory[i+PRGRM_START] = data[i]; } } -// Execute the program starting at the userspace address -fn exec_program(state: &mut EmulationState) { +/// # exec_program +/// +/// Execute the program starting at the userspace address. +/// +/// * `state` - Global emulation state. +/// +fn exec_program(state: &mut EmulationState) +{ println!("Starting emulation"); let mut head_byte: u8; let mut tail_byte: u8; let mut instruction: u16; - for _i in PRGRM_START+usize::from(state.program_counter)..MAX_MEM_SIZE { + for _i in PRGRM_START+usize::from(state.program_counter)..MAX_MEM_SIZE + { // Grab the first byte of instruction head_byte = state.memory[PRGRM_START + usize::from(state.program_counter)]; state.program_counter += 1; @@ -96,15 +124,14 @@ fn exec_program(state: &mut EmulationState) { instruction = util::u8_tuple_to_u16((head_byte, tail_byte)); - - println!("0x{:02X?}{:02X?}", head_byte, tail_byte); - - if instruction == INSTR_EXIT { + if instruction == INSTR_EXIT + { println!("Read exit instruction, stopping emulation"); return; } - if instruction == INSTR_NOOP { + if instruction == INSTR_NOOP + { println!("Read no-op instruction, stopping emulation"); return; } @@ -113,28 +140,55 @@ fn exec_program(state: &mut EmulationState) { } } +/// # parse_instruction +/// +/// Parse binary instructions passed from program execution. +/// +/// * `state` - Global emulation state. +/// * `instr` - Incoming instruction. +/// fn parse_instruction(state: &mut EmulationState, instr: u16) { - - if (instr == INSTR_CLS) { - println!("Clear screen"); + if instr == INSTR_CLS + { + debug_log_instr(state, instr, "CLS".to_string()); } - if (instr == INSTR_RET) { - println!("Return"); + if instr == INSTR_RET + { + debug_log_instr(state, instr, "RET".to_string()); } - if ((instr & INSTR_MASK_SYS) > 0) { - let addr: u16 = (instr & ADDR_MASK); - println!("Got SYS instruction {:04X?} with address {:04X?}", instr, addr); + if (instr & INSTR_MASK_SYS) > 0 + { + let addr: u16 = instr & ADDR_MASK; + debug_log_instr(state, instr, format!("SYS {:04X?}", addr)); } - if ((instr & INSTR_MASK_JP) > 0) { - let addr: u16 = (instr & ADDR_MASK); - println!("Got JP instruction {:04X?} with address {:04X?}", instr, addr); + if (instr & INSTR_MASK_JP) > 0 + { + let addr: u16 = instr & ADDR_MASK; + debug_log_instr(state, instr, format!("JP {:04X?}", addr)); } - if ((instr & INSTR_MASK_CALL) > 0) { - let addr: u16 = (instr & ADDR_MASK); - println!("Got CALL instruction {:04X?} with address {:04X?}", instr, addr); + if (instr & INSTR_MASK_CALL) > 0 + { + let addr: u16 = instr & ADDR_MASK; + debug_log_instr(state, instr, format!("CALL {:04X?}", addr)); + } +} + +/// # debug_log_instr +/// +/// Debug log the program counter, instruction, and a brief string message in one line. +/// +/// * `state` - Global emulation state. +/// * `instr` - Incoming instruction. +/// * `msg` - Message string to append +/// +fn debug_log_instr(state: &mut EmulationState, instr: u16, msg: String) +{ + if state.debug_enabled + { + println!("PC: {:04?}; INSTR {:04X?}; {}", state.program_counter, instr, msg); } } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 94a15fe..8ec75b6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +use core::num; use std::path; use std::env; use std::fs; @@ -7,22 +8,54 @@ use std::process::exit; mod util; mod chip8; +/// # main +/// +/// Main entry point of the emulator +/// fn main() { // Collect our execution args - let args: Vec = env::args().collect(); + let args: Vec = env::args().skip(1).collect(); - // Grab our filepath from our options - if &args.len() < &2 + // Check if we received enough arguments + if args.len() == 0 { - // No file given, terminate + // No arguments given, terminate util::print_help(); exit(0); } // Read last argument as path of chip8 executable let file_path: &String = &args[args.len()-1]; + + // Check if an actual file path was provided and not just options + if file_path.starts_with('-') + { + // Only options were given, terminate + println!("[Error] A filename must be provided when passing arguments"); + exit(0); + } + + // Runtime options + let mut debug_mode: bool = false; + + // Option parsing + let num_options: usize = args.len() - 1; + for i in 0..num_options + { + if args[i].starts_with('-') + { + let flags: &str = &args[i][1..args[i].len()]; + + for char in flags.chars() { + if char == 'd' { + debug_mode = true; + } + } + } + } + // Check if file_path exists in filesystem if path::Path::new(file_path).exists() { println!("'{}' exists, reading...", file_path); @@ -47,7 +80,7 @@ fn main() } println!("Read {} bytes of program data", file_data.len()); - chip8::start_emulation(program); + chip8::start_emulation(program, debug_mode); } } else