Implement opcodes, restructure
This commit is contained in:
parent
3b23c8c5b1
commit
e374d5f4ec
|
@ -51,6 +51,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||
name = "chip-8"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"rand",
|
||||
"sdl2",
|
||||
]
|
||||
|
||||
|
@ -105,6 +106,17 @@ dependencies = [
|
|||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
|
@ -133,6 +145,52 @@ dependencies = [
|
|||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"rand_hc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.5"
|
||||
|
@ -214,6 +272,12 @@ version = "0.0.10"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d63556a25bae6ea31b52e640d7c41d1ab27faba4ccb600013837a3d0b3994ca1"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.2+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
|
|
|
@ -7,4 +7,5 @@ edition = "2018"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
rand = "0.8.3"
|
||||
sdl2 = { version = "0.34.2", features = ["bundled"] }
|
||||
|
|
|
@ -1 +1,30 @@
|
|||
pub const INSTRUCTION_LENGTH: u16 = 2;
|
||||
|
||||
pub struct Instruction {
|
||||
pub nibbles: (u8, u8, u8, u8),
|
||||
pub addr: u16,
|
||||
pub byte: u8,
|
||||
pub x: usize,
|
||||
pub y: usize,
|
||||
pub nibble: u8,
|
||||
}
|
||||
|
||||
impl Instruction {
|
||||
pub fn parse(instruction: u16) -> Self {
|
||||
let nibbles = (
|
||||
((instruction & 0xF000) >> 12) as u8,
|
||||
((instruction & 0x0F00) >> 8) as u8,
|
||||
((instruction & 0x00F0) >> 4) as u8,
|
||||
(instruction & 0x000F) as u8,
|
||||
);
|
||||
|
||||
Instruction {
|
||||
nibbles,
|
||||
addr: (instruction & 0x0FFF),
|
||||
nibble: nibbles.3,
|
||||
byte: (instruction & 0x00FF) as u8,
|
||||
x: nibbles.1 as usize,
|
||||
y: nibbles.2 as usize,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,9 +32,7 @@ impl Screen {
|
|||
if b & 0b10000000 >> lx > 0 {
|
||||
let dx = (x + lx) % WIDTH;
|
||||
let dy = (y + ly) % HEIGHT;
|
||||
if !pixel_collission && self.is_pixel_set(dx, dy) {
|
||||
pixel_collission = true;
|
||||
}
|
||||
pixel_collission = pixel_collission || self.is_pixel_set(dx, dy);
|
||||
|
||||
self.pixel_set(dx, dy);
|
||||
}
|
||||
|
@ -43,6 +41,10 @@ impl Screen {
|
|||
|
||||
pixel_collission
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.pixels = [[false; WIDTH]; HEIGHT];
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
use crate::chip8::audio::Speaker;
|
||||
use crate::chip8::cpu::Instruction;
|
||||
use crate::chip8::display::Screen;
|
||||
use crate::chip8::keyboard::Keyboard;
|
||||
use crate::chip8::memory::{self, Memory};
|
||||
use crate::chip8::registers::{Register::*, Registers};
|
||||
use crate::chip8::registers::Registers;
|
||||
use crate::chip8::stack::Stack;
|
||||
|
||||
use rand;
|
||||
use sdl2::AudioSubsystem;
|
||||
|
||||
use std::{fs, thread, time::Duration};
|
||||
|
@ -31,20 +33,18 @@ impl Chip8 {
|
|||
}
|
||||
|
||||
pub fn handle_delay_timer(&mut self) -> () {
|
||||
let delay_timer = self.registers.get(DT);
|
||||
if delay_timer > 0 {
|
||||
if self.registers.get_dt() > 0 {
|
||||
thread::sleep(Duration::new(0, 1_000_000_000u32 / 60));
|
||||
self.registers.set(DT, delay_timer - 1);
|
||||
self.registers.dec_dt();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_sound_timer(&mut self) {
|
||||
let sound_timer = self.registers.get(ST);
|
||||
let status = sound_timer > 0;
|
||||
let status = self.registers.get_st() > 0;
|
||||
self.speaker.beep(status);
|
||||
if status {
|
||||
thread::sleep(Duration::new(0, 1_000_000_000u32 / 60));
|
||||
self.registers.set(ST, sound_timer - 1);
|
||||
self.registers.dec_st();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,9 +64,268 @@ impl Chip8 {
|
|||
}
|
||||
|
||||
pub fn exec(&mut self) {
|
||||
let pc = self.registers.get(PC);
|
||||
let pc = self.registers.get_pc();
|
||||
let opcode = self.memory.read_opcode(pc as usize);
|
||||
println!("{}", opcode);
|
||||
self.registers.set(PC, pc + 2);
|
||||
let instruction = Instruction::parse(opcode);
|
||||
self.registers.advance_pc();
|
||||
|
||||
match instruction.nibbles {
|
||||
// CLS: clear the display
|
||||
(0x00, 0x00, 0x0E, 0x00) => self.screen.clear(),
|
||||
|
||||
// RET: return from subroutine
|
||||
(0x00, 0x00, 0x0E, 0x0E) => {
|
||||
let new_pc = self.stack.pop(&mut self.registers);
|
||||
self.registers.set_pc(new_pc);
|
||||
}
|
||||
|
||||
// JP addr: jump to location addr
|
||||
(0x01, _, _, _) => self.registers.set_pc(instruction.addr),
|
||||
|
||||
// CALL addr: call subroutine at addr
|
||||
(0x02, _, _, _) => {
|
||||
let pc = self.registers.get_pc();
|
||||
self.stack.push(&mut self.registers, pc);
|
||||
self.registers.set_pc(instruction.addr);
|
||||
}
|
||||
|
||||
// SE Vx, byte: skip next instruction if Vx = byte
|
||||
(0x03, _, _, _) => {
|
||||
let x = self.registers.get_v(instruction.x);
|
||||
if x == instruction.byte {
|
||||
self.registers.advance_pc();
|
||||
}
|
||||
}
|
||||
|
||||
// SNE Vx, byte: skip next instruction if Vx != byte
|
||||
(0x04, _, _, _) => {
|
||||
let x = self.registers.get_v(instruction.x);
|
||||
if x != instruction.byte {
|
||||
self.registers.advance_pc();
|
||||
}
|
||||
}
|
||||
|
||||
// SE Vx, Vy: skip next instruction if Vx = Vy
|
||||
(0x05, _, _, 0x00) => {
|
||||
let x = self.registers.get_v(instruction.x);
|
||||
let y = self.registers.get_v(instruction.y);
|
||||
|
||||
if x == y {
|
||||
self.registers.advance_pc();
|
||||
}
|
||||
}
|
||||
|
||||
// LD Vx, byte: set Vx = byte
|
||||
(0x06, _, _, _) => {
|
||||
let register = instruction.x;
|
||||
let value = instruction.byte;
|
||||
self.registers.set_v(register, value);
|
||||
}
|
||||
|
||||
// ADD Vx, byte: set Vx = Vx + byte
|
||||
(0x07, _, _, _) => {
|
||||
let register = instruction.x;
|
||||
let value = self.registers.get_v(register) as u16;
|
||||
let new_value = value + instruction.byte as u16;
|
||||
self.registers.set_v(register, new_value as u8);
|
||||
}
|
||||
|
||||
// LD Vx, Vy: set Vx = Vy
|
||||
(0x08, _, _, 0x00) => {
|
||||
let y = self.registers.get_v(instruction.y);
|
||||
self.registers.set_v(instruction.x, y);
|
||||
}
|
||||
|
||||
// OR Vx, Vy: set Vx = Vx OR Vy
|
||||
(0x08, _, _, 0x01) => {
|
||||
let x = self.registers.get_v(instruction.x);
|
||||
let y = self.registers.get_v(instruction.y);
|
||||
self.registers.set_v(instruction.x, x | y);
|
||||
}
|
||||
|
||||
// AND Vx, Vy: set Vx = V AND Vy
|
||||
(0x08, _, _, 0x02) => {
|
||||
let x = self.registers.get_v(instruction.x);
|
||||
let y = self.registers.get_v(instruction.y);
|
||||
self.registers.set_v(instruction.x, x & y);
|
||||
}
|
||||
|
||||
// XOR Vx, Vy: set Vx = Vx XOR Vy
|
||||
(0x08, _, _, 0x03) => {
|
||||
let x = self.registers.get_v(instruction.x);
|
||||
let y = self.registers.get_v(instruction.y);
|
||||
self.registers.set_v(instruction.x, x ^ y);
|
||||
}
|
||||
|
||||
// ADD Vx, Vy: set Vx = Vx + Vy, set VF = carry
|
||||
(0x08, _, _, 0x04) => {
|
||||
let x = self.registers.get_v(instruction.x) as u16;
|
||||
let y = self.registers.get_v(instruction.y) as u16;
|
||||
let result = x + y;
|
||||
|
||||
self.registers.set_carry_if(result > 255);
|
||||
self.registers.set_v(instruction.x, result as u8);
|
||||
}
|
||||
|
||||
// SUB Vx, Vy: set Vx = Vx - Vy, set VF = NOT borrow
|
||||
(0x08, _, _, 0x05) => {
|
||||
let x = self.registers.get_v(instruction.x);
|
||||
let y = self.registers.get_v(instruction.y);
|
||||
|
||||
self.registers.set_carry_if(x > y);
|
||||
self.registers.set_v(instruction.x, x.wrapping_sub(y));
|
||||
}
|
||||
|
||||
// SHR Vx {, Vy}: set Vx = Vx SHR 1o
|
||||
(0x08, _, _, 0x06) => {
|
||||
let x = self.registers.get_v(instruction.x);
|
||||
self.registers.set_carry_if(x & 1 == 1);
|
||||
self.registers.set_v(instruction.x, x >> 1);
|
||||
}
|
||||
|
||||
// SUBN Vx, Vy: set Vx = Vy - Vx, set VF = NOT borrow
|
||||
(0x08, _, _, 0x07) => {
|
||||
let x = self.registers.get_v(instruction.x);
|
||||
let y = self.registers.get_v(instruction.y);
|
||||
|
||||
self.registers.set_carry_if(y > x);
|
||||
self.registers.set_v(instruction.x, y.wrapping_sub(x));
|
||||
}
|
||||
|
||||
// SHL Vx {, Vy}: set Vx = Vx SHL 1
|
||||
(0x08, _, _, 0x0E) => {
|
||||
let x = self.registers.get_v(instruction.x);
|
||||
let msb = 1 << 7;
|
||||
|
||||
self.registers.set_carry_if(x & msb == 1);
|
||||
self.registers.set_v(instruction.x, x << 1);
|
||||
}
|
||||
|
||||
// SNE Vx, Vy: skip next instruction if Vx != Vy
|
||||
(0x09, _, _, 0x00) => {
|
||||
let x = self.registers.get_v(instruction.x);
|
||||
let y = self.registers.get_v(instruction.y);
|
||||
|
||||
if x != y {
|
||||
self.registers.advance_pc();
|
||||
}
|
||||
}
|
||||
|
||||
// LD I, addr: set I = addr
|
||||
(0x0A, _, _, _) => self.registers.set_i(instruction.addr),
|
||||
|
||||
// JP V0, addr: jump to location nnn + V0
|
||||
(0x0B, _, _, _) => {
|
||||
self.registers
|
||||
.set_pc(self.registers.get_v(0) as u16 + instruction.addr);
|
||||
}
|
||||
|
||||
// RND Vx, byte: et Vx = random byte AND kk.
|
||||
(0x0C, _, _, _) => {
|
||||
let n: u8 = rand::random();
|
||||
self.registers.set_v(instruction.x, n & instruction.byte);
|
||||
}
|
||||
|
||||
//DRW Vx, Vy, nibble: display n-byte sprite starting at memory
|
||||
// location I at (Vx, Vy), set VF = collision.
|
||||
(0x0D, _, _, _) => {
|
||||
let x = self.registers.get_v(instruction.x) as usize;
|
||||
let y = self.registers.get_v(instruction.y) as usize;
|
||||
let start = self.registers.get_i() as usize;
|
||||
let sprite = &self.memory.read(start, instruction.nibble);
|
||||
|
||||
let collission = self.screen.draw_sprite(x, y, sprite);
|
||||
self.registers.set_carry_if(collission);
|
||||
}
|
||||
|
||||
// SKP Vx: skip next instruction if key with the value of Vx is
|
||||
// pressed
|
||||
(0x0E, _, 0x09, 0x0E) => {
|
||||
let x = self.registers.get_v(instruction.x) as usize;
|
||||
if self.keyboard.is_key_down(x) {
|
||||
self.registers.advance_pc();
|
||||
}
|
||||
}
|
||||
|
||||
// SKNP Vx: skip next instruction if key with the value of Vx is
|
||||
// not pressed
|
||||
(0x0E, _, 0x0A, 0x01) => {
|
||||
let x = self.registers.get_v(instruction.x) as usize;
|
||||
if !self.keyboard.is_key_down(x) {
|
||||
self.registers.advance_pc();
|
||||
}
|
||||
}
|
||||
|
||||
// LD Vx, DT: set Vx = delay timer value
|
||||
(0x0F, _, 0x00, 0x07) => {
|
||||
self.registers.set_v(instruction.x, self.registers.get_dt());
|
||||
}
|
||||
|
||||
// LD Vx, K: wait for a key press, store the value of the key in V
|
||||
(0x0F, _, 0x00, 0x0A) => {
|
||||
// TODO
|
||||
}
|
||||
|
||||
// LD DT, Vx
|
||||
(0x0F, _, 0x01, 0x05) => {
|
||||
self.registers.set_dt(self.registers.get_v(instruction.x));
|
||||
}
|
||||
|
||||
// LD ST, Vx: set sound timer = Vx
|
||||
(0x0F, _, 0x01, 0x08) => {
|
||||
self.registers.set_st(self.registers.get_v(instruction.x));
|
||||
}
|
||||
|
||||
// ADD I, Vx: set I = I + Vx
|
||||
(0x0F, _, 0x01, 0x0E) => {
|
||||
let i = self.registers.get_i();
|
||||
let x = self.registers.get_v(instruction.x) as u16;
|
||||
let result = (i + x) as u16;
|
||||
self.registers.set_i(result);
|
||||
self.registers.set_carry_if(result > (1 << 15));
|
||||
}
|
||||
|
||||
// LD F, Vx: set I = location of sprite for digit Vx
|
||||
(0x0F, _, 0x02, 0x09) => {
|
||||
let x = self.registers.get_v(instruction.x) as u16;
|
||||
self.registers.set_i(x * 5);
|
||||
}
|
||||
|
||||
// LD B, Vx: store BCD representation of Vx in memory locations I,
|
||||
// I+1, and I+2.
|
||||
(0x0F, _, 0x03, 0x03) => {
|
||||
let x = self.registers.get_v(instruction.x) as u16;
|
||||
let i = self.registers.get_i() as usize;
|
||||
|
||||
self.memory.set(i, (x / 100) as u8);
|
||||
self.memory.set(i + 1, ((x % 100) / 10) as u8);
|
||||
self.memory.set(i + 2, (x % 10) as u8)
|
||||
}
|
||||
|
||||
// LD [I], Vx: store registers V0 through Vx in memory starting at
|
||||
// location I.
|
||||
(0x0F, _, 0x05, 0x05) => {
|
||||
let x = self.registers.get_v(instruction.x) as usize;
|
||||
let i = self.registers.get_i() as usize;
|
||||
|
||||
for n in 0..=x {
|
||||
self.memory.set(i + n, self.registers.get_v(n));
|
||||
}
|
||||
}
|
||||
|
||||
// LD Vx, [I]: read registers V0 through Vx from memory starting at
|
||||
// location I
|
||||
(0x0F, _, 0x06, 0x05) => {
|
||||
let x = self.registers.get_v(instruction.x) as usize;
|
||||
let i = self.registers.get_i() as usize;
|
||||
|
||||
for n in 0..x {
|
||||
let value = self.memory.get(i + n);
|
||||
self.registers.set_v(n, value);
|
||||
}
|
||||
}
|
||||
|
||||
_ => println!("Invalid instruction"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn it_maps_physical_keys_to_virtual_ones() {
|
||||
let mut keyboard = Keyboard::new();
|
||||
let 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);
|
||||
|
|
|
@ -1,68 +1,21 @@
|
|||
use super::cpu;
|
||||
use super::memory;
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
use Register::*;
|
||||
|
||||
fn data_register_to_index(register: Register) -> usize {
|
||||
match register {
|
||||
V0 => 0,
|
||||
V1 => 1,
|
||||
V2 => 2,
|
||||
V3 => 3,
|
||||
V4 => 4,
|
||||
V5 => 5,
|
||||
V6 => 6,
|
||||
V7 => 7,
|
||||
V8 => 8,
|
||||
V9 => 9,
|
||||
VA => 10,
|
||||
VB => 11,
|
||||
VC => 12,
|
||||
VD => 13,
|
||||
VE => 14,
|
||||
VF => 15,
|
||||
_ => panic!("invalid data register"),
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Registers {
|
||||
data: [u8; DATA_REGISTERS],
|
||||
v: [u8; DATA_REGISTERS],
|
||||
i: u16,
|
||||
delay_timer: u8,
|
||||
sound_timer: u8,
|
||||
pc: u16,
|
||||
pub sp: u8,
|
||||
sp: u8,
|
||||
}
|
||||
|
||||
impl Registers {
|
||||
pub fn new() -> Self {
|
||||
Registers {
|
||||
data: [0; DATA_REGISTERS],
|
||||
v: [0; DATA_REGISTERS],
|
||||
i: 0,
|
||||
delay_timer: 0,
|
||||
sound_timer: 0,
|
||||
|
@ -71,26 +24,72 @@ impl Registers {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn set(&mut self, register: Register, value: u16) -> () {
|
||||
match register {
|
||||
I => self.i = value,
|
||||
DT => self.delay_timer = value as u8,
|
||||
ST => self.sound_timer = value as u8,
|
||||
PC => self.pc = value,
|
||||
SP => self.sp = value as u8,
|
||||
_ => self.data[data_register_to_index(register)] = value as u8,
|
||||
}
|
||||
pub fn get_v(&self, n: usize) -> u8 {
|
||||
self.v[n]
|
||||
}
|
||||
|
||||
pub fn get(&self, register: Register) -> u16 {
|
||||
match register {
|
||||
I => self.i,
|
||||
DT => self.delay_timer as u16,
|
||||
ST => self.sound_timer as u16,
|
||||
PC => self.pc,
|
||||
SP => self.sp as u16,
|
||||
_ => self.data[data_register_to_index(register)] as u16,
|
||||
}
|
||||
pub fn set_v(&mut self, n: usize, value: u8) {
|
||||
self.v[n] = value;
|
||||
}
|
||||
|
||||
pub fn get_i(&self) -> u16 {
|
||||
self.i
|
||||
}
|
||||
|
||||
pub fn set_i(&mut self, addr: u16) {
|
||||
self.i = addr;
|
||||
}
|
||||
|
||||
pub fn get_dt(&self) -> u8 {
|
||||
self.delay_timer
|
||||
}
|
||||
|
||||
pub fn set_dt(&mut self, value: u8) {
|
||||
self.delay_timer = value;
|
||||
}
|
||||
|
||||
pub fn dec_dt(&mut self) {
|
||||
self.delay_timer -= 1;
|
||||
}
|
||||
|
||||
pub fn get_st(&self) -> u8 {
|
||||
self.sound_timer
|
||||
}
|
||||
|
||||
pub fn set_st(&mut self, value: u8) {
|
||||
self.sound_timer = value;
|
||||
}
|
||||
|
||||
pub fn dec_st(&mut self) {
|
||||
self.sound_timer -= 1;
|
||||
}
|
||||
|
||||
pub fn get_pc(&self) -> u16 {
|
||||
self.pc
|
||||
}
|
||||
|
||||
pub fn set_pc(&mut self, value: u16) {
|
||||
self.pc = value;
|
||||
}
|
||||
|
||||
pub fn advance_pc(&mut self) {
|
||||
self.pc += cpu::INSTRUCTION_LENGTH;
|
||||
}
|
||||
|
||||
pub fn get_sp(&self) -> u8 {
|
||||
self.sp
|
||||
}
|
||||
|
||||
pub fn inc_sp(&mut self) {
|
||||
self.sp += 1;
|
||||
}
|
||||
|
||||
pub fn dec_sp(&mut self) {
|
||||
self.sp -= 1;
|
||||
}
|
||||
|
||||
pub fn set_carry_if(&mut self, condition: bool) {
|
||||
self.v[0xf] = if condition { 1 } else { 0 };
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -100,34 +99,34 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn it_has_the_correct_number_of_data_registers() {
|
||||
assert_eq!(Registers::new().data.len(), DATA_REGISTERS);
|
||||
assert_eq!(Registers::new().v.len(), DATA_REGISTERS);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_can_write_data_registers() {
|
||||
let mut registers = Registers::new();
|
||||
registers.set(VA, 42);
|
||||
assert_eq!(registers.data[10], 42);
|
||||
registers.set_v(0xA, 42);
|
||||
assert_eq!(registers.v[0xA], 42);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_can_read_data_registers() {
|
||||
let mut registers = Registers::new();
|
||||
registers.set(VA, 42);
|
||||
assert_eq!(registers.get(VA), 42);
|
||||
registers.set_v(0xA, 42);
|
||||
assert_eq!(registers.get_v(0xA), 42);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_can_write_special_registers() {
|
||||
let mut registers = Registers::new();
|
||||
registers.set(PC, 42);
|
||||
registers.set_pc(42);
|
||||
assert_eq!(registers.pc, 42);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_can_read_special_registers() {
|
||||
let mut registers = Registers::new();
|
||||
registers.set(PC, 42);
|
||||
assert_eq!(registers.get(PC), 42);
|
||||
registers.set_pc(42);
|
||||
assert_eq!(registers.get_pc(), 42);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use crate::chip8::registers::Registers;
|
||||
|
||||
pub const STACK_DEPTH: usize = 16;
|
||||
|
||||
pub struct Stack {
|
||||
|
@ -11,39 +13,42 @@ impl Stack {
|
|||
}
|
||||
}
|
||||
|
||||
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 push(&mut self, registers: &mut Registers, value: u16) {
|
||||
let stack_pointer = registers.get_sp() as usize;
|
||||
assert!(stack_pointer < STACK_DEPTH, "stack overflow");
|
||||
self.stack[stack_pointer] = value;
|
||||
registers.inc_sp();
|
||||
}
|
||||
|
||||
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]
|
||||
pub fn pop(&mut self, registers: &mut Registers) -> u16 {
|
||||
let stack_pointer = registers.get_sp() as usize;
|
||||
assert!(stack_pointer > 0, "stack underflow");
|
||||
registers.dec_sp();
|
||||
assert!(stack_pointer < STACK_DEPTH, "stack overflow");
|
||||
self.stack[registers.get_sp() 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(SP), 0);
|
||||
assert_eq!(registers.get_sp(), 0);
|
||||
|
||||
let mut stack = Stack::new();
|
||||
stack.push(&mut registers.sp, 0xff);
|
||||
assert_eq!(registers.get(SP), 1);
|
||||
stack.push(&mut registers, 0xff);
|
||||
assert_eq!(registers.get_sp(), 1);
|
||||
assert_eq!(stack.stack[0], 0xff);
|
||||
|
||||
stack.push(&mut registers.sp, 0xaa);
|
||||
assert_eq!(registers.get(SP), 2);
|
||||
assert_eq!(stack.pop(&mut registers.sp), 170);
|
||||
assert_eq!(registers.get(SP), 1);
|
||||
assert_eq!(stack.pop(&mut registers.sp), 255);
|
||||
assert_eq!(registers.get(SP), 0);
|
||||
stack.push(&mut registers, 0xaa);
|
||||
assert_eq!(registers.get_sp(), 2);
|
||||
assert_eq!(stack.stack[1], 0xaa);
|
||||
assert_eq!(stack.pop(&mut registers), 170);
|
||||
assert_eq!(registers.get_sp(), 1);
|
||||
assert_eq!(stack.pop(&mut registers), 255);
|
||||
assert_eq!(registers.get_sp(), 0);
|
||||
}
|
||||
}
|
||||
|
|
27
src/main.rs
27
src/main.rs
|
@ -63,19 +63,6 @@ fn run(rom: &str) -> Result<(), String> {
|
|||
'mainloop: loop {
|
||||
canvas.set_draw_color(Color::RGB(255, 255, 255));
|
||||
|
||||
for y in 0..display::HEIGHT {
|
||||
for x in 0..display::WIDTH {
|
||||
if chip8.screen.is_pixel_set(x, y) {
|
||||
canvas.fill_rect(Rect::new(
|
||||
(x as u32 * display::SCALE_FACTOR) as i32,
|
||||
(y as u32 * display::SCALE_FACTOR) as i32,
|
||||
display::SCALE_FACTOR,
|
||||
display::SCALE_FACTOR,
|
||||
))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for event in event_pump.poll_iter() {
|
||||
match event {
|
||||
Event::Quit { .. }
|
||||
|
@ -108,8 +95,20 @@ fn run(rom: &str) -> Result<(), String> {
|
|||
canvas.present();
|
||||
chip8.handle_delay_timer();
|
||||
chip8.handle_sound_timer();
|
||||
|
||||
chip8.exec();
|
||||
|
||||
for y in 0..display::HEIGHT {
|
||||
for x in 0..display::WIDTH {
|
||||
if chip8.screen.is_pixel_set(x, y) {
|
||||
canvas.fill_rect(Rect::new(
|
||||
(x as u32 * display::SCALE_FACTOR) as i32,
|
||||
(y as u32 * display::SCALE_FACTOR) as i32,
|
||||
display::SCALE_FACTOR,
|
||||
display::SCALE_FACTOR,
|
||||
))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
Loading…
Reference in New Issue