Big code restructure
This commit is contained in:
parent
185796456b
commit
3dc79b8975
300
src/chip8.rs
300
src/chip8.rs
|
@ -1,300 +0,0 @@
|
|||
use sdl2::keyboard::Keycode;
|
||||
|
||||
const MEMORY_SIZE: usize = 4096;
|
||||
const WIDTH: u32 = 64;
|
||||
const HEIGHT: u32 = 32;
|
||||
const DATA_REGISTERS: usize = 16;
|
||||
const STACK_DEPTH: usize = 16;
|
||||
const KEYS: usize = 16;
|
||||
|
||||
pub const SCALE_FACTOR: u32 = 10;
|
||||
pub const WINDOW_WIDTH: u32 = WIDTH * SCALE_FACTOR;
|
||||
pub const WINDOW_HEIGHT: u32 = HEIGHT * SCALE_FACTOR;
|
||||
|
||||
type Memory = [u8; MEMORY_SIZE];
|
||||
|
||||
pub enum Register {
|
||||
V0,
|
||||
V1,
|
||||
V2,
|
||||
V3,
|
||||
V4,
|
||||
V5,
|
||||
V6,
|
||||
V7,
|
||||
V8,
|
||||
V9,
|
||||
VA,
|
||||
VB,
|
||||
VC,
|
||||
VD,
|
||||
VE,
|
||||
VF,
|
||||
I,
|
||||
DT,
|
||||
ST,
|
||||
PC,
|
||||
SP,
|
||||
}
|
||||
|
||||
struct Registers {
|
||||
data: [u8; DATA_REGISTERS],
|
||||
i: u16,
|
||||
delay_timer: u8,
|
||||
sound_timer: u8,
|
||||
pc: u16,
|
||||
sp: u8,
|
||||
}
|
||||
|
||||
type Stack = [u16; STACK_DEPTH];
|
||||
|
||||
type Keyboard = [bool; KEYS];
|
||||
|
||||
pub struct Chip8 {
|
||||
memory: Memory,
|
||||
registers: Registers,
|
||||
stack: Stack,
|
||||
keyboard: Keyboard,
|
||||
}
|
||||
|
||||
fn data_register_to_index(register: Register) -> usize {
|
||||
match register {
|
||||
Register::V0 => 0,
|
||||
Register::V1 => 1,
|
||||
Register::V2 => 2,
|
||||
Register::V3 => 3,
|
||||
Register::V4 => 4,
|
||||
Register::V5 => 5,
|
||||
Register::V6 => 6,
|
||||
Register::V7 => 7,
|
||||
Register::V8 => 8,
|
||||
Register::V9 => 9,
|
||||
Register::VA => 10,
|
||||
Register::VB => 11,
|
||||
Register::VC => 12,
|
||||
Register::VD => 13,
|
||||
Register::VE => 14,
|
||||
Register::VF => 15,
|
||||
_ => panic!("invalid data register"),
|
||||
}
|
||||
}
|
||||
|
||||
const DEFAULT_CHARACTER_SET: [u8; 80] = [
|
||||
0xf0, 0x90, 0x90, 0x90, 0xf0, // 0
|
||||
0x20, 0x60, 0x20, 0x20, 0x70, // 1
|
||||
0xf0, 0x10, 0xf0, 0x80, 0xf0, // 2
|
||||
0xf0, 0x10, 0xf0, 0x10, 0xf0, // 3
|
||||
0x90, 0x90, 0xf0, 0x10, 0x10, // 4
|
||||
0xf0, 0x80, 0xf0, 0x10, 0xf0, // 5
|
||||
0xf0, 0x80, 0xf0, 0x90, 0xf0, // 6
|
||||
0xf0, 0x10, 0x20, 0x40, 0x40, // 7
|
||||
0xf0, 0x90, 0xf0, 0x90, 0xf0, // 8
|
||||
0xf0, 0x90, 0xf0, 0x10, 0xf0, // 9
|
||||
0xf0, 0x90, 0xf0, 0x90, 0x90, // A
|
||||
0xe0, 0x90, 0xe0, 0x90, 0xe0, // B
|
||||
0xf0, 0x80, 0x80, 0x80, 0xf0, // C
|
||||
0xe0, 0x90, 0x90, 0x90, 0xe0, // D
|
||||
0xf0, 0x80, 0xf0, 0x80, 0xf0, // E
|
||||
0xf0, 0x80, 0xf0, 0x80, 0x80, // F
|
||||
];
|
||||
|
||||
const DEFAULT_CHARACTER_SET_START: usize = 0;
|
||||
|
||||
fn copy_default_char_set(destination: &mut Chip8) {
|
||||
let mut index = DEFAULT_CHARACTER_SET_START;
|
||||
for byte in DEFAULT_CHARACTER_SET.iter() {
|
||||
destination.memory_set(index, *byte);
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
impl Chip8 {
|
||||
pub fn new() -> Chip8 {
|
||||
let mut chip8 = Chip8 {
|
||||
memory: [0; MEMORY_SIZE],
|
||||
registers: Registers {
|
||||
data: [0; DATA_REGISTERS],
|
||||
i: 0,
|
||||
delay_timer: 0,
|
||||
sound_timer: 0,
|
||||
pc: 0,
|
||||
sp: 0,
|
||||
},
|
||||
stack: [0; STACK_DEPTH],
|
||||
keyboard: [false; KEYS],
|
||||
};
|
||||
copy_default_char_set(&mut chip8);
|
||||
chip8
|
||||
}
|
||||
|
||||
pub fn memory_set(&mut self, index: usize, value: u8) -> () {
|
||||
self.memory[index] = value;
|
||||
}
|
||||
|
||||
pub fn memory_get(&self, index: usize) -> u8 {
|
||||
self.memory[index]
|
||||
}
|
||||
|
||||
pub fn register_set(&mut self, register: Register, value: u16) -> () {
|
||||
match register {
|
||||
Register::I => self.registers.i = value,
|
||||
Register::DT => self.registers.delay_timer = value as u8,
|
||||
Register::ST => self.registers.sound_timer = value as u8,
|
||||
Register::PC => self.registers.pc = value,
|
||||
Register::SP => self.registers.sp = value as u8,
|
||||
_ => self.registers.data[data_register_to_index(register)] = value as u8,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_get(&self, register: Register) -> u16 {
|
||||
match register {
|
||||
Register::I => self.registers.i,
|
||||
Register::DT => self.registers.delay_timer as u16,
|
||||
Register::ST => self.registers.sound_timer as u16,
|
||||
Register::PC => self.registers.pc,
|
||||
Register::SP => self.registers.sp as u16,
|
||||
_ => self.registers.data[data_register_to_index(register)] as u16,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stack_push(&mut self, value: u16) -> () {
|
||||
assert!((self.registers.sp as usize) < STACK_DEPTH, "stack overflow");
|
||||
self.stack[self.registers.sp as usize] = value;
|
||||
self.registers.sp += 1;
|
||||
}
|
||||
|
||||
pub fn stack_pop(&mut self) -> u16 {
|
||||
assert!(self.registers.sp > 0, "stack underflow");
|
||||
self.registers.sp -= 1;
|
||||
assert!((self.registers.sp as usize) < STACK_DEPTH, "stack overflow");
|
||||
self.stack[self.registers.sp as usize]
|
||||
}
|
||||
|
||||
pub fn keyboard_map(&self, key: Keycode) -> Option<usize> {
|
||||
match key {
|
||||
Keycode::Num1 => Some(1),
|
||||
Keycode::Num2 => Some(2),
|
||||
Keycode::Num3 => Some(3),
|
||||
Keycode::Num4 => Some(12),
|
||||
Keycode::Q => Some(4),
|
||||
Keycode::W => Some(5),
|
||||
Keycode::E => Some(6),
|
||||
Keycode::R => Some(13),
|
||||
Keycode::A => Some(7),
|
||||
Keycode::S => Some(8),
|
||||
Keycode::D => Some(9),
|
||||
Keycode::F => Some(14),
|
||||
Keycode::Z => Some(10),
|
||||
Keycode::X => Some(0),
|
||||
Keycode::C => Some(11),
|
||||
Keycode::V => Some(15),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn key_down(&mut self, key: usize) -> () {
|
||||
self.keyboard[key] = true
|
||||
}
|
||||
|
||||
pub fn key_up(&mut self, key: usize) -> () {
|
||||
self.keyboard[key] = false
|
||||
}
|
||||
|
||||
pub fn is_key_down(&self, key: usize) -> bool {
|
||||
self.keyboard[key]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_has_the_correct_memory_size() {
|
||||
assert_eq!(Chip8::new().memory.len(), MEMORY_SIZE);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_can_write_the_memory() {
|
||||
let mut chip8 = Chip8::new();
|
||||
chip8.memory_set(200, 42);
|
||||
assert_eq!(chip8.memory[200..=202], [42, 0, 0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_can_read_the_memory() {
|
||||
let mut chip8 = Chip8::new();
|
||||
chip8.memory_set(2, 42);
|
||||
assert_eq!(chip8.memory_get(2), 42);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_has_the_correct_number_of_data_registers() {
|
||||
assert_eq!(Chip8::new().registers.data.len(), DATA_REGISTERS);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_can_write_data_registers() {
|
||||
let mut chip8 = Chip8::new();
|
||||
chip8.register_set(Register::VA, 42);
|
||||
assert_eq!(chip8.registers.data[10], 42);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_can_read_data_registers() {
|
||||
let mut chip8 = Chip8::new();
|
||||
chip8.register_set(Register::VA, 42);
|
||||
assert_eq!(chip8.register_get(Register::VA), 42);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_can_write_special_registers() {
|
||||
let mut chip8 = Chip8::new();
|
||||
chip8.register_set(Register::PC, 42);
|
||||
assert_eq!(chip8.registers.pc, 42);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_can_read_special_registers() {
|
||||
let mut chip8 = Chip8::new();
|
||||
chip8.register_set(Register::PC, 42);
|
||||
assert_eq!(chip8.register_get(Register::PC), 42);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_can_push_to_and_pop_from_the_stack() {
|
||||
let mut chip8 = Chip8::new();
|
||||
assert_eq!(chip8.register_get(Register::SP), 0);
|
||||
chip8.stack_push(0xff);
|
||||
assert_eq!(chip8.register_get(Register::SP), 1);
|
||||
chip8.stack_push(0xaa);
|
||||
assert_eq!(chip8.register_get(Register::SP), 2);
|
||||
assert_eq!(chip8.stack_pop(), 170);
|
||||
assert_eq!(chip8.register_get(Register::SP), 1);
|
||||
assert_eq!(chip8.stack_pop(), 255);
|
||||
assert_eq!(chip8.register_get(Register::SP), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_maps_physical_keys_to_virtual_ones() {
|
||||
let chip8 = Chip8::new();
|
||||
assert_eq!(chip8.keyboard_map(Keycode::A), Some(7));
|
||||
assert_eq!(chip8.keyboard_map(Keycode::X), Some(0));
|
||||
assert_eq!(chip8.keyboard_map(Keycode::M), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_can_press_and_release_keys() {
|
||||
let mut chip8 = Chip8::new();
|
||||
chip8.key_down(1);
|
||||
assert!(chip8.is_key_down(1));
|
||||
chip8.key_up(1);
|
||||
assert!(!chip8.is_key_down(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_has_a_default_character_set() {
|
||||
let chip8 = Chip8::new();
|
||||
assert_eq!(chip8.memory[0..5], [0xf0, 0x90, 0x90, 0x90, 0xf0])
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
const WIDTH: u32 = 64;
|
||||
const HEIGHT: u32 = 32;
|
||||
|
||||
pub const SCALE_FACTOR: u32 = 10;
|
||||
pub const WINDOW_WIDTH: u32 = WIDTH * SCALE_FACTOR;
|
||||
pub const WINDOW_HEIGHT: u32 = HEIGHT * SCALE_FACTOR;
|
|
@ -0,0 +1,23 @@
|
|||
use crate::chip8::keyboard::Keyboard;
|
||||
use crate::chip8::memory::Memory;
|
||||
use crate::chip8::registers::Registers;
|
||||
use crate::chip8::stack::Stack;
|
||||
|
||||
pub struct Chip8 {
|
||||
pub memory: Memory,
|
||||
pub registers: Registers,
|
||||
pub stack: Stack,
|
||||
pub keyboard: Keyboard,
|
||||
}
|
||||
|
||||
impl Chip8 {
|
||||
pub fn new() -> Chip8 {
|
||||
let chip8 = Chip8 {
|
||||
memory: Memory::new(),
|
||||
registers: Registers::new(),
|
||||
stack: Stack::new(),
|
||||
keyboard: Keyboard::new(),
|
||||
};
|
||||
chip8
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
use sdl2::keyboard::Keycode;
|
||||
|
||||
const KEYS: usize = 16;
|
||||
|
||||
pub struct Keyboard {
|
||||
keyboard: [bool; KEYS],
|
||||
}
|
||||
|
||||
impl Keyboard {
|
||||
pub fn new() -> Keyboard {
|
||||
Keyboard {
|
||||
keyboard: [false; KEYS],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map(&self, key: Keycode) -> Option<usize> {
|
||||
match key {
|
||||
Keycode::Num1 => Some(1),
|
||||
Keycode::Num2 => Some(2),
|
||||
Keycode::Num3 => Some(3),
|
||||
Keycode::Num4 => Some(12),
|
||||
Keycode::Q => Some(4),
|
||||
Keycode::W => Some(5),
|
||||
Keycode::E => Some(6),
|
||||
Keycode::R => Some(13),
|
||||
Keycode::A => Some(7),
|
||||
Keycode::S => Some(8),
|
||||
Keycode::D => Some(9),
|
||||
Keycode::F => Some(14),
|
||||
Keycode::Z => Some(10),
|
||||
Keycode::X => Some(0),
|
||||
Keycode::C => Some(11),
|
||||
Keycode::V => Some(15),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn key_down(&mut self, key: usize) -> () {
|
||||
self.keyboard[key] = true
|
||||
}
|
||||
|
||||
pub fn key_up(&mut self, key: usize) -> () {
|
||||
self.keyboard[key] = false
|
||||
}
|
||||
|
||||
pub fn is_key_down(&self, key: usize) -> bool {
|
||||
self.keyboard[key]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_maps_physical_keys_to_virtual_ones() {
|
||||
let mut keyboard = Keyboard::new();
|
||||
assert_eq!(keyboard.map(Keycode::A), Some(7));
|
||||
assert_eq!(keyboard.map(Keycode::X), Some(0));
|
||||
assert_eq!(keyboard.map(Keycode::M), None);
|
||||
}
|
||||
#[test]
|
||||
fn it_can_press_and_release_keys() {
|
||||
let mut keyboard = Keyboard::new();
|
||||
keyboard.key_down(1);
|
||||
assert!(keyboard.is_key_down(1));
|
||||
keyboard.key_up(1);
|
||||
assert!(!keyboard.is_key_down(1));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
const MEMORY_SIZE: usize = 4096;
|
||||
|
||||
const DEFAULT_CHARACTER_SET_START: usize = 0;
|
||||
|
||||
const DEFAULT_CHARACTER_SET: [u8; 80] = [
|
||||
0xf0, 0x90, 0x90, 0x90, 0xf0, // 0
|
||||
0x20, 0x60, 0x20, 0x20, 0x70, // 1
|
||||
0xf0, 0x10, 0xf0, 0x80, 0xf0, // 2
|
||||
0xf0, 0x10, 0xf0, 0x10, 0xf0, // 3
|
||||
0x90, 0x90, 0xf0, 0x10, 0x10, // 4
|
||||
0xf0, 0x80, 0xf0, 0x10, 0xf0, // 5
|
||||
0xf0, 0x80, 0xf0, 0x90, 0xf0, // 6
|
||||
0xf0, 0x10, 0x20, 0x40, 0x40, // 7
|
||||
0xf0, 0x90, 0xf0, 0x90, 0xf0, // 8
|
||||
0xf0, 0x90, 0xf0, 0x10, 0xf0, // 9
|
||||
0xf0, 0x90, 0xf0, 0x90, 0x90, // A
|
||||
0xe0, 0x90, 0xe0, 0x90, 0xe0, // B
|
||||
0xf0, 0x80, 0x80, 0x80, 0xf0, // C
|
||||
0xe0, 0x90, 0x90, 0x90, 0xe0, // D
|
||||
0xf0, 0x80, 0xf0, 0x80, 0xf0, // E
|
||||
0xf0, 0x80, 0xf0, 0x80, 0x80, // F
|
||||
];
|
||||
|
||||
pub struct Memory {
|
||||
memory: [u8; MEMORY_SIZE],
|
||||
}
|
||||
|
||||
fn copy_default_char_set(memory: &mut Memory) {
|
||||
let mut index = DEFAULT_CHARACTER_SET_START;
|
||||
for byte in DEFAULT_CHARACTER_SET.iter() {
|
||||
memory.set(index, *byte);
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
impl Memory {
|
||||
pub fn new() -> Memory {
|
||||
let mut memory = Memory {
|
||||
memory: [0; MEMORY_SIZE],
|
||||
};
|
||||
copy_default_char_set(&mut memory);
|
||||
memory
|
||||
}
|
||||
|
||||
pub fn set(&mut self, index: usize, value: u8) -> () {
|
||||
self.memory[index] = value;
|
||||
}
|
||||
|
||||
pub fn get(&self, index: usize) -> u8 {
|
||||
self.memory[index]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_has_the_correct_memory_size() {
|
||||
assert_eq!(Memory::new().memory.len(), MEMORY_SIZE);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_can_write_the_memory() {
|
||||
let mut memory = Memory::new();
|
||||
memory.set(200, 42);
|
||||
assert_eq!(memory.memory[200..=202], [42, 0, 0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_can_read_the_memory() {
|
||||
let mut memory = Memory::new();
|
||||
memory.set(2, 42);
|
||||
assert_eq!(memory.get(2), 42);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_contains_the_default_character_set() {
|
||||
let memory = Memory::new();
|
||||
assert_eq!(memory.memory[0..5], [0xf0, 0x90, 0x90, 0x90, 0xf0])
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
pub mod display;
|
||||
pub mod emulator;
|
||||
pub mod keyboard;
|
||||
pub mod memory;
|
||||
pub mod registers;
|
||||
pub mod stack;
|
|
@ -0,0 +1,129 @@
|
|||
const DATA_REGISTERS: usize = 16;
|
||||
|
||||
pub enum Register {
|
||||
V0,
|
||||
V1,
|
||||
V2,
|
||||
V3,
|
||||
V4,
|
||||
V5,
|
||||
V6,
|
||||
V7,
|
||||
V8,
|
||||
V9,
|
||||
VA,
|
||||
VB,
|
||||
VC,
|
||||
VD,
|
||||
VE,
|
||||
VF,
|
||||
I,
|
||||
DT,
|
||||
ST,
|
||||
PC,
|
||||
SP,
|
||||
}
|
||||
|
||||
fn data_register_to_index(register: Register) -> usize {
|
||||
match register {
|
||||
Register::V0 => 0,
|
||||
Register::V1 => 1,
|
||||
Register::V2 => 2,
|
||||
Register::V3 => 3,
|
||||
Register::V4 => 4,
|
||||
Register::V5 => 5,
|
||||
Register::V6 => 6,
|
||||
Register::V7 => 7,
|
||||
Register::V8 => 8,
|
||||
Register::V9 => 9,
|
||||
Register::VA => 10,
|
||||
Register::VB => 11,
|
||||
Register::VC => 12,
|
||||
Register::VD => 13,
|
||||
Register::VE => 14,
|
||||
Register::VF => 15,
|
||||
_ => panic!("invalid data register"),
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Registers {
|
||||
data: [u8; DATA_REGISTERS],
|
||||
i: u16,
|
||||
delay_timer: u8,
|
||||
sound_timer: u8,
|
||||
pc: u16,
|
||||
pub sp: u8,
|
||||
}
|
||||
|
||||
impl Registers {
|
||||
pub fn new() -> Registers {
|
||||
Registers {
|
||||
data: [0; DATA_REGISTERS],
|
||||
i: 0,
|
||||
delay_timer: 0,
|
||||
sound_timer: 0,
|
||||
pc: 0,
|
||||
sp: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set(&mut self, register: Register, value: u16) -> () {
|
||||
match register {
|
||||
Register::I => self.i = value,
|
||||
Register::DT => self.delay_timer = value as u8,
|
||||
Register::ST => self.sound_timer = value as u8,
|
||||
Register::PC => self.pc = value,
|
||||
Register::SP => self.sp = value as u8,
|
||||
_ => self.data[data_register_to_index(register)] = value as u8,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, register: Register) -> u16 {
|
||||
match register {
|
||||
Register::I => self.i,
|
||||
Register::DT => self.delay_timer as u16,
|
||||
Register::ST => self.sound_timer as u16,
|
||||
Register::PC => self.pc,
|
||||
Register::SP => self.sp as u16,
|
||||
_ => self.data[data_register_to_index(register)] as u16,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_has_the_correct_number_of_data_registers() {
|
||||
assert_eq!(Registers::new().data.len(), DATA_REGISTERS);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_can_write_data_registers() {
|
||||
let mut registers = Registers::new();
|
||||
registers.set(Register::VA, 42);
|
||||
assert_eq!(registers.data[10], 42);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_can_read_data_registers() {
|
||||
let mut registers = Registers::new();
|
||||
registers.set(Register::VA, 42);
|
||||
assert_eq!(registers.get(Register::VA), 42);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_can_write_special_registers() {
|
||||
let mut registers = Registers::new();
|
||||
registers.set(Register::PC, 42);
|
||||
assert_eq!(registers.pc, 42);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_can_read_special_registers() {
|
||||
let mut registers = Registers::new();
|
||||
registers.set(Register::PC, 42);
|
||||
assert_eq!(registers.get(Register::PC), 42);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
pub const STACK_DEPTH: usize = 16;
|
||||
|
||||
pub struct Stack {
|
||||
stack: [u16; STACK_DEPTH],
|
||||
}
|
||||
|
||||
impl Stack {
|
||||
pub fn new() -> Stack {
|
||||
Stack {
|
||||
stack: [0; STACK_DEPTH],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push(&mut self, stack_pointer: &mut u8, value: u16) -> () {
|
||||
assert!((*stack_pointer as usize) < STACK_DEPTH, "stack overflow");
|
||||
self.stack[*stack_pointer as usize] = value;
|
||||
*stack_pointer += 1;
|
||||
}
|
||||
|
||||
pub fn pop(&mut self, stack_pointer: &mut u8) -> u16 {
|
||||
assert!((*stack_pointer as usize) > 0, "stack underflow");
|
||||
*stack_pointer -= 1;
|
||||
assert!((*stack_pointer as usize) < STACK_DEPTH, "stack overflow");
|
||||
self.stack[*stack_pointer as usize]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::chip8::registers::{Register, Registers};
|
||||
|
||||
#[test]
|
||||
fn it_can_push_to_and_pop_from_the_stack() {
|
||||
let mut registers = Registers::new();
|
||||
assert_eq!(registers.get(Register::SP), 0);
|
||||
|
||||
let mut stack = Stack::new();
|
||||
stack.push(&mut registers.sp, 0xff);
|
||||
assert_eq!(registers.get(Register::SP), 1);
|
||||
|
||||
stack.push(&mut registers.sp, 0xaa);
|
||||
assert_eq!(registers.get(Register::SP), 2);
|
||||
assert_eq!(stack.pop(&mut registers.sp), 170);
|
||||
assert_eq!(registers.get(Register::SP), 1);
|
||||
assert_eq!(stack.pop(&mut registers.sp), 255);
|
||||
assert_eq!(registers.get(Register::SP), 0);
|
||||
}
|
||||
}
|
19
src/main.rs
19
src/main.rs
|
@ -1,14 +1,15 @@
|
|||
mod chip8;
|
||||
use chip8::emulator::Chip8;
|
||||
|
||||
use sdl2::event::Event;
|
||||
use sdl2::keyboard::Keycode;
|
||||
use sdl2::pixels::Color;
|
||||
use sdl2::rect::Rect;
|
||||
|
||||
mod chip8;
|
||||
|
||||
const EMULATOR_WINDOW_TITLE: &str = "Rust CHIP-8";
|
||||
|
||||
fn main() -> Result<(), String> {
|
||||
let mut chip8 = chip8::Chip8::new();
|
||||
let mut chip8 = Chip8::new();
|
||||
|
||||
let sdl_context = sdl2::init()?;
|
||||
let video_subsystem = sdl_context.video()?;
|
||||
|
@ -16,8 +17,8 @@ fn main() -> Result<(), String> {
|
|||
let window = video_subsystem
|
||||
.window(
|
||||
EMULATOR_WINDOW_TITLE,
|
||||
chip8::WINDOW_WIDTH,
|
||||
chip8::WINDOW_HEIGHT,
|
||||
chip8::display::WINDOW_WIDTH,
|
||||
chip8::display::WINDOW_HEIGHT,
|
||||
)
|
||||
.position_centered()
|
||||
.build()
|
||||
|
@ -51,16 +52,16 @@ fn main() -> Result<(), String> {
|
|||
Event::KeyDown {
|
||||
keycode: Some(key), ..
|
||||
} => {
|
||||
if let Some(vkey) = chip8.keyboard_map(key) {
|
||||
chip8.key_down(vkey);
|
||||
if let Some(vkey) = chip8.keyboard.map(key) {
|
||||
chip8.keyboard.key_down(vkey);
|
||||
println!("key down: {}", vkey);
|
||||
}
|
||||
}
|
||||
Event::KeyUp {
|
||||
keycode: Some(key), ..
|
||||
} => {
|
||||
if let Some(vkey) = chip8.keyboard_map(key) {
|
||||
chip8.key_up(vkey);
|
||||
if let Some(vkey) = chip8.keyboard.map(key) {
|
||||
chip8.keyboard.key_up(vkey);
|
||||
println!("key up: {}", vkey);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue