Function docs, better debug logging, basic option flag parsing
This commit is contained in:
parent
e12bbe91a0
commit
fec824610c
112
src/chip8.rs
112
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);
|
||||
}
|
||||
}
|
||||
43
src/main.rs
43
src/main.rs
|
|
@ -1,3 +1,4 @@
|
|||
use core::num;
|
||||
use std::path;
|
||||
use std::env;
|
||||
use std::fs;
|
||||
|
|
@ -7,15 +8,19 @@ use std::process::exit;
|
|||
mod util;
|
||||
mod chip8;
|
||||
|
||||
/// # main
|
||||
///
|
||||
/// Main entry point of the emulator
|
||||
///
|
||||
fn main()
|
||||
{
|
||||
// Collect our execution args
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let args: Vec<String> = 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);
|
||||
}
|
||||
|
|
@ -23,6 +28,34 @@ fn main()
|
|||
// 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
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user