Add basic program loading, memory map, etc
This commit is contained in:
parent
808d51918f
commit
0b0353c923
BIN
programs/ibm_logo.ch8
Normal file
BIN
programs/ibm_logo.ch8
Normal file
Binary file not shown.
BIN
programs/test.ch8
Normal file
BIN
programs/test.ch8
Normal file
Binary file not shown.
106
src/chip8.rs
106
src/chip8.rs
|
|
@ -1,7 +1,103 @@
|
||||||
// Miscellanious registers
|
// chip8.rs
|
||||||
static mut program_counter: u16 = 0;
|
// Implementation of chip8 machine, registers, and memory makeup
|
||||||
static mut stack_ptr: u8 = 0;
|
|
||||||
|
|
||||||
// General purpose registers
|
use crate::util;
|
||||||
static mut registers: [u8; 16] = [0; 16];
|
|
||||||
|
|
||||||
|
// 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
|
||||||
|
program_counter: u16,
|
||||||
|
stack_ptr: u8,
|
||||||
|
|
||||||
|
// General purpose registers
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
21
src/main.rs
21
src/main.rs
|
|
@ -5,6 +5,7 @@ use std::process::exit;
|
||||||
|
|
||||||
// Module imports
|
// Module imports
|
||||||
mod util;
|
mod util;
|
||||||
|
mod chip8;
|
||||||
|
|
||||||
fn main()
|
fn main()
|
||||||
{
|
{
|
||||||
|
|
@ -31,8 +32,24 @@ fn main()
|
||||||
|
|
||||||
if contents.is_ok()
|
if contents.is_ok()
|
||||||
{
|
{
|
||||||
let _bytes: &Vec<u8> = &contents.expect("");
|
let file_data: &Vec<u8> = &contents.expect("");
|
||||||
// let magic_num: &[u8] = &bytes[0..4];
|
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
|
} else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,9 @@
|
||||||
pub fn print_help() {
|
pub fn print_help() {
|
||||||
print!("Usage: saltnvinegar [CHIP8_FILE]\n");
|
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;
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user