Add basic program loading, memory map, etc

This commit is contained in:
Garrett Dickinson 2024-03-31 19:27:07 -05:00
parent 808d51918f
commit 0b0353c923
5 changed files with 126 additions and 7 deletions

BIN
programs/ibm_logo.ch8 Normal file

Binary file not shown.

BIN
programs/test.ch8 Normal file

Binary file not shown.

View File

@ -1,7 +1,103 @@
// chip8.rs
// Implementation of chip8 machine, registers, and memory makeup
use crate::util;
// Memory map constants
pub const PRGRM_START: usize = 512;
pub const MAX_MEM_SIZE: usize = 4096;
pub const MAX_PRG_SIZE: usize = 3584;
// Instruction constants
const INSTR_NOOP: u16 = 0x0000;
const INSTR_EXIT: u16 = 0x00FD;
#[derive(Debug)]
struct EmulationState {
// Miscellanious registers
static mut program_counter: u16 = 0;
static mut stack_ptr: u8 = 0;
program_counter: u16,
stack_ptr: u8,
// General purpose registers
static mut registers: [u8; 16] = [0; 16];
registers: [u8; 16],
// Memory Map:
// +---------------+= 0xFFF (4095) End of Chip-8 RAM
// | |
// | |
// | |
// | |
// | |
// | 0x200 to 0xFFF|
// | Chip-8 |
// | Program / Data|
// | Space |
// | |
// | |
// | |
// +- - - - - - - -+= 0x600 (1536) Start of ETI 660 Chip-8 programs
// | |
// | |
// | |
// +---------------+= 0x200 (512) Start of most Chip-8 programs
// | 0x000 to 0x1FF|
// | Reserved for |
// | interpreter |
// +---------------+= 0x000 (0) Start of Chip-8 RAM
memory: [u8; 4096]
}
pub fn start_emulation(data: [u8; MAX_PRG_SIZE]) {
let mut state: EmulationState = EmulationState{
program_counter: 0,
stack_ptr: 0,
registers: [0; 16],
memory: [0; MAX_MEM_SIZE]
};
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) {
println!("Copying program data to memory");
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) {
println!("Starting emulation");
let mut head_byte: u8;
let mut tail_byte: u8;
let mut instruction_tuple: (u8, u8);
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;
// Grab the second byte of instruction
tail_byte = state.memory[PRGRM_START + usize::from(state.program_counter)];
state.program_counter += 1;
instruction_tuple = (head_byte, tail_byte);
println!("0x{:02X?}{:02X?}", head_byte, tail_byte);
if util::u8_tuple_to_u16(instruction_tuple) == INSTR_EXIT {
println!("Read exit instruction, stopping emulation");
return;
}
if util::u8_tuple_to_u16(instruction_tuple) == INSTR_NOOP {
println!("Read no-op instruction, stopping emulation");
return;
}
}
}

View File

@ -5,6 +5,7 @@ use std::process::exit;
// Module imports
mod util;
mod chip8;
fn main()
{
@ -31,8 +32,24 @@ fn main()
if contents.is_ok()
{
let _bytes: &Vec<u8> = &contents.expect("");
// let magic_num: &[u8] = &bytes[0..4];
let file_data: &Vec<u8> = &contents.expect("");
if file_data.len() > chip8::MAX_PRG_SIZE.into() {
eprintln!("Error loading file, program size too large!");
exit(1);
}
else if file_data.len() == 0 {
eprintln!("Error loading file, no program data found!");
exit(1);
}
let mut program: [u8; chip8::MAX_PRG_SIZE] = [0; chip8::MAX_PRG_SIZE];
for i in 0..file_data.len() {
program[i] = file_data[i];
}
println!("Read {} bytes of program data", file_data.len());
chip8::start_emulation(program);
}
} else
{

View File

@ -1,3 +1,9 @@
pub fn print_help() {
print!("Usage: saltnvinegar [CHIP8_FILE]\n");
}
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;
}