Initial Commit
This commit adds most of the logic for the processor Things that are missing: + Most sdl2 interactions + Cartridge loading + Main loop for the game
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
138
Cargo.lock
generated
Normal file
138
Cargo.lock
generated
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "c_vec"
|
||||||
|
version = "1.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8a318911dce53b5f1ca6539c44f5342c632269f0fa7ea3e35f32458c27a7c30"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chip8-rs"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"rand",
|
||||||
|
"sdl2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[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"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.88"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "03b07a082330a35e43f63177cc01689da34fbffa0105e1246cf0311472cac73a"
|
||||||
|
|
||||||
|
[[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 = "sdl2"
|
||||||
|
version = "0.34.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fcbb85f4211627a7291c83434d6bbfa723e28dcaa53c7606087e3c61929e4b9c"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"c_vec",
|
||||||
|
"lazy_static",
|
||||||
|
"libc",
|
||||||
|
"sdl2-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sdl2-sys"
|
||||||
|
version = "0.34.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "28d81feded049b9c14eceb4a4f6d596a98cebbd59abdba949c5552a015466d33"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 0.1.10",
|
||||||
|
"libc",
|
||||||
|
"version-compare",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version-compare"
|
||||||
|
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"
|
||||||
15
Cargo.toml
Normal file
15
Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
[package]
|
||||||
|
name = "chip8-rs"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Marco Thomas <mail@marco-thomas.net>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
rand = "0.8.3"
|
||||||
|
|
||||||
|
[dependencies.sdl2]
|
||||||
|
version = "0.34"
|
||||||
|
default-features = false
|
||||||
|
features = ["ttf","image","gfx","mixer"]
|
||||||
21
README.md
Normal file
21
README.md
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
chip8-rs
|
||||||
|
========
|
||||||
|
|
||||||
|
Yet another chip8 emulator written in rust \o/
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
Expand on this README
|
||||||
|
|
||||||
|
## System Dependencies
|
||||||
|
|
||||||
|
`sdl2` together with its dependencies are required to be installed on your hostmachine.
|
||||||
|
On arch-based distros you can use
|
||||||
|
`sudo pacman -Syu sdl2 sdl2_gfx sdl2_image sdl2_mixer sdl2_ttf`
|
||||||
|
to install them.
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
|
For this project I used the help from the following sites:
|
||||||
|
+ [Chip-8 Technical Reference](http://devernay.free.fr/hacks/chip8/C8TECH10.HTM#3.0)
|
||||||
|
+ [How to write an emulator (CHIP-8 interpreter)](http://www.multigesture.net/articles/how-to-write-an-emulator-chip-8-interpreter/)
|
||||||
18
src/fontset.rs
Normal file
18
src/fontset.rs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
pub (crate) const FONT: [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
|
||||||
|
];
|
||||||
0
src/handle_input.rs
Normal file
0
src/handle_input.rs
Normal file
27
src/main.rs
Normal file
27
src/main.rs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
extern crate rand;
|
||||||
|
extern crate sdl2;
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
mod processor;
|
||||||
|
mod fontset;
|
||||||
|
|
||||||
|
use crate::processor::Processor;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let cartridge = env::args().nth(1);
|
||||||
|
match cartridge {
|
||||||
|
Some(_) => println!("Found a cartridge file! Trying to load..."),
|
||||||
|
None => {
|
||||||
|
println!("No cartridge file found! Exiting!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut processor = Processor::new();
|
||||||
|
|
||||||
|
// load cartridge file
|
||||||
|
|
||||||
|
processor.start();
|
||||||
|
|
||||||
|
}
|
||||||
451
src/processor.rs
Normal file
451
src/processor.rs
Normal file
@@ -0,0 +1,451 @@
|
|||||||
|
extern crate sdl2;
|
||||||
|
|
||||||
|
use rand::Rng;
|
||||||
|
|
||||||
|
use crate::fontset::FONT;
|
||||||
|
|
||||||
|
const MEMORY_SIZE: usize = 4096;
|
||||||
|
const GAME_ENTRY: usize = 0x200; // most games load into 0x200
|
||||||
|
const SCREEN_WIDTH: usize = 64;
|
||||||
|
const SCREEN_HEIGHT: usize = 32;
|
||||||
|
const OPCODE_SIZE: usize = 2;
|
||||||
|
|
||||||
|
pub struct Processor {
|
||||||
|
memory: [u8; MEMORY_SIZE],
|
||||||
|
register: [u8; 16],
|
||||||
|
index: usize, // used to store memory addresses
|
||||||
|
pc: usize, // programm counter
|
||||||
|
screen: [[u8; SCREEN_WIDTH]; SCREEN_HEIGHT],
|
||||||
|
draw_flag: bool, // redraw screen if true
|
||||||
|
delay_timer: usize,
|
||||||
|
sound_timer: usize,
|
||||||
|
stack: [usize; 16],
|
||||||
|
sp: usize, // stack pointer
|
||||||
|
key: [bool; 16],
|
||||||
|
waiting_for_key: bool,
|
||||||
|
waiting_key_location: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Processor {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let mut mem = [0; MEMORY_SIZE];
|
||||||
|
// load font
|
||||||
|
for (pos, &val) in FONT.iter().enumerate() {
|
||||||
|
mem[pos] = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
Processor {
|
||||||
|
memory: mem,
|
||||||
|
register: [0; 16],
|
||||||
|
index: 0,
|
||||||
|
pc: GAME_ENTRY,
|
||||||
|
screen: [[0; SCREEN_WIDTH]; SCREEN_HEIGHT],
|
||||||
|
draw_flag: false,
|
||||||
|
delay_timer: 0,
|
||||||
|
sound_timer: 0,
|
||||||
|
stack: [0; 16],
|
||||||
|
sp: 0,
|
||||||
|
key: [false; 16],
|
||||||
|
waiting_for_key: false,
|
||||||
|
waiting_key_location: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: cartridge needed
|
||||||
|
pub fn start(&mut self) {
|
||||||
|
let sdl_ctx = sdl2::init().unwrap();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
// get keyinput using sdl2
|
||||||
|
|
||||||
|
// emulate one cycle
|
||||||
|
self.cycle();
|
||||||
|
|
||||||
|
// draw to screen using sdl2
|
||||||
|
if self.draw_flag {
|
||||||
|
}
|
||||||
|
|
||||||
|
// play sound using sdl2
|
||||||
|
|
||||||
|
// add delay
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cycle(&mut self) {
|
||||||
|
|
||||||
|
// reset
|
||||||
|
self.draw_flag = false;
|
||||||
|
|
||||||
|
// opcode FX0A holds the program, until a key is pressed
|
||||||
|
if self.waiting_for_key {
|
||||||
|
for (pos, &val) in self.key.iter().enumerate() {
|
||||||
|
if self.key[pos] {
|
||||||
|
self.waiting_for_key = false;
|
||||||
|
self.register[self.waiting_key_location] = val as u8;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// decr both timers every cycle
|
||||||
|
if self.delay_timer > 0 {
|
||||||
|
self.delay_timer -= 1;
|
||||||
|
}
|
||||||
|
if self.sound_timer > 0 {
|
||||||
|
self.sound_timer -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// execute current opcode
|
||||||
|
let opcode = self.fetch_opcode();
|
||||||
|
self.decode_opcode(opcode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_game(&mut self, game: &[u8]) {
|
||||||
|
// load game
|
||||||
|
for (pos, &val) in game.iter().enumerate() {
|
||||||
|
let position = GAME_ENTRY + pos;
|
||||||
|
if position < MEMORY_SIZE { // don't go above mem limit
|
||||||
|
self.memory[position] = val;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fetch_opcode(&self) -> u16 {
|
||||||
|
// final opcode consists of 2 bytes
|
||||||
|
(self.memory[self.pc] as u16) << 8 | (self.memory[self.pc + 1] as u16)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decode_opcode(&mut self, opcode: u16) {
|
||||||
|
// values from http://devernay.free.fr/hacks/chip8/C8TECH10.HTM#3.0
|
||||||
|
|
||||||
|
// n or nibble - A 4-bit value, the lowest 4 bits of the instruction
|
||||||
|
let nibbles = (
|
||||||
|
(opcode & 0xF000 >> 12) as usize,
|
||||||
|
(opcode & 0x0F00 >> 8) as usize,
|
||||||
|
(opcode & 0x00F0 >> 4) as usize,
|
||||||
|
(opcode & 0x000F) as usize
|
||||||
|
);
|
||||||
|
|
||||||
|
// nnn or addr - A 12-bit value, the lowest 12 bits of the instruction
|
||||||
|
let nnn = (opcode & 0x0FFF) as usize;
|
||||||
|
|
||||||
|
// n or nibble - A 4-bit value, the lowest 4 bits of the instruction
|
||||||
|
let n = nibbles.3 as usize;
|
||||||
|
|
||||||
|
// x - A 4-bit value, the lower 4 bits of the high byte of the instruction
|
||||||
|
let x = nibbles.1 as usize;
|
||||||
|
|
||||||
|
// y - A 4-bit value, the upper 4 bits of the low byte of the instruction
|
||||||
|
let y = nibbles.2 as usize;
|
||||||
|
|
||||||
|
//kk or byte - An 8-bit value, the lowest 8 bits of the instruction
|
||||||
|
let kk = (opcode & 0x00FF) as u8;
|
||||||
|
|
||||||
|
// match nibbles to opcodes => run funtion
|
||||||
|
match nibbles {
|
||||||
|
(0x00, 0x00, 0x0e, 0x00) => self.code_00e0(),
|
||||||
|
(0x00, 0x00, 0x0e, 0x0e) => self.code_00ee(),
|
||||||
|
(0x01, _, _, _) => self.code_1nnn(nnn),
|
||||||
|
(0x02, _, _, _) => self.code_2nnn(nnn),
|
||||||
|
(0x03, _, _, _) => self.code_3xkk(x, kk),
|
||||||
|
(0x04, _, _, _) => self.code_4xkk(x, kk),
|
||||||
|
(0x05, _, _, 0x00) => self.code_5xy0(x, y),
|
||||||
|
(0x06, _, _, _) => self.code_6xkk(x, kk),
|
||||||
|
(0x07, _, _, _) => self.code_7xkk(x, kk),
|
||||||
|
(0x08, _, _, 0x00) => self.code_8xy0(x, y),
|
||||||
|
(0x08, _, _, 0x01) => self.code_8xy1(x, y),
|
||||||
|
(0x08, _, _, 0x02) => self.code_8xy2(x, y),
|
||||||
|
(0x08, _, _, 0x03) => self.code_8xy3(x, y),
|
||||||
|
(0x08, _, _, 0x04) => self.code_8xy4(x, y),
|
||||||
|
(0x08, _, _, 0x05) => self.code_8xy5(x, y),
|
||||||
|
(0x08, _, _, 0x06) => self.code_8xy6(x, y),
|
||||||
|
(0x08, _, _, 0x07) => self.code_8xy7(x, y),
|
||||||
|
(0x08, _, _, 0x0e) => self.code_8xye(x, y),
|
||||||
|
(0x09, _, _, 0x00) => self.code_9xy0(x, y),
|
||||||
|
(0x0a, _, _, _) => self.code_annn(nnn),
|
||||||
|
(0x0b, _, _, _) => self.code_bnnn(nnn),
|
||||||
|
(0x0c, _, _, _) => self.code_cxkk(x, kk),
|
||||||
|
(0x0d, _, _, _) => self.code_dxyn(x, y, n),
|
||||||
|
(0x0e, _, 0x09, 0x0e) => self.code_ex9e(x),
|
||||||
|
(0x0e, _, 0x0a, 0x01) => self.code_exa1(x),
|
||||||
|
(0x0f, _, 0x00, 0x07) => self.code_fx07(x),
|
||||||
|
(0x0f, _, 0x00, 0x0a) => self.code_fx0a(x),
|
||||||
|
(0x0f, _, 0x01, 0x05) => self.code_fx15(x),
|
||||||
|
(0x0f, _, 0x01, 0x08) => self.code_fx18(x),
|
||||||
|
(0x0f, _, 0x01, 0x0e) => self.code_fx1e(x),
|
||||||
|
(0x0f, _, 0x02, 0x09) => self.code_fx29(x),
|
||||||
|
(0x0f, _, 0x03, 0x03) => self.code_fx33(x),
|
||||||
|
(0x0f, _, 0x05, 0x05) => self.code_fx55(x),
|
||||||
|
(0x0f, _, 0x06, 0x05) => self.code_fx65(x),
|
||||||
|
_ => self.pc += OPCODE_SIZE
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear screen
|
||||||
|
fn code_00e0(&mut self) {
|
||||||
|
self.screen = [[0; SCREEN_WIDTH]; SCREEN_HEIGHT];
|
||||||
|
self.pc += OPCODE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return from subroutine
|
||||||
|
fn code_00ee(&mut self) {
|
||||||
|
self.sp -= 1;
|
||||||
|
self.pc = self.stack[self.sp];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Jump to location nnn
|
||||||
|
fn code_1nnn(&mut self, nnn: usize) {
|
||||||
|
self.pc = nnn;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call subroutine at nnn
|
||||||
|
fn code_2nnn(&mut self, nnn: usize) {
|
||||||
|
self.sp += 1;
|
||||||
|
self.stack[self.sp] = self.pc;
|
||||||
|
self.pc = nnn;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip next instruction if Vx = kk
|
||||||
|
fn code_3xkk(&mut self, x: usize, kk: u8) {
|
||||||
|
if self.register[x] == kk {
|
||||||
|
self.pc += 2 * OPCODE_SIZE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.pc += OPCODE_SIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip next instruction if Vx != kk
|
||||||
|
fn code_4xkk(&mut self, x: usize, kk: u8) {
|
||||||
|
if self.register[x] != kk {
|
||||||
|
self.pc += 2 * OPCODE_SIZE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.pc += OPCODE_SIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip next instruction if Vx = Vy
|
||||||
|
fn code_5xy0(&mut self, x: usize, y: usize) {
|
||||||
|
if self.register[x] == self.register[y] {
|
||||||
|
self.pc += 2 * OPCODE_SIZE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.pc += OPCODE_SIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Vx = kk
|
||||||
|
fn code_6xkk(&mut self, x: usize, kk: u8) {
|
||||||
|
self.register[x] = kk;
|
||||||
|
self.pc += OPCODE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Vx = Vx + kk
|
||||||
|
fn code_7xkk(&mut self, x: usize, kk: u8) {
|
||||||
|
self.register[x] = self.register[x] + kk;
|
||||||
|
self.pc += OPCODE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Vx = Vy
|
||||||
|
fn code_8xy0(&mut self, x: usize, y: usize) {
|
||||||
|
self.register[x] = self.register[y];
|
||||||
|
self.pc += OPCODE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Vx = Vx OR Vy
|
||||||
|
fn code_8xy1(&mut self, x: usize, y: usize) {
|
||||||
|
self.register[x] = self.register[x] | self.register[y];
|
||||||
|
self.pc += OPCODE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Vx = Vx AND Vy
|
||||||
|
fn code_8xy2(&mut self, x: usize, y: usize) {
|
||||||
|
self.register[x] = self.register[x] & self.register[y];
|
||||||
|
self.pc += OPCODE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Vx = Vx XOR Vy
|
||||||
|
fn code_8xy3(&mut self, x: usize, y: usize) {
|
||||||
|
self.register[x] = self.register[x] ^ self.register[y];
|
||||||
|
self.pc += OPCODE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Vx = Vx + Vy, set VF = carry
|
||||||
|
fn code_8xy4(&mut self, x: usize, y: usize) {
|
||||||
|
let result = (self.register[x]) as usize + (self.register[y]) as usize;
|
||||||
|
|
||||||
|
self.register[x] = result as u8; // write back lowest 8bit
|
||||||
|
self.register[0x0f] = (result > 255) as u8; // set carry flag
|
||||||
|
self.pc += OPCODE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Vx = Vx - Vy, set VF = NOT borrow
|
||||||
|
fn code_8xy5(&mut self, x: usize, y: usize) {
|
||||||
|
let x_val = self.register[x];
|
||||||
|
let y_val = self.register[y];
|
||||||
|
|
||||||
|
self.register[0x0f] = (x_val > y_val) as u8;
|
||||||
|
self.register[x] = (x_val - y_val) as u8;
|
||||||
|
self.pc += OPCODE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Vx = Vx SHR 1
|
||||||
|
fn code_8xy6(&mut self, x: usize, _y: usize) {
|
||||||
|
self.register[0x0f] = self.register[x] & 1; // set if least significant bit == 1
|
||||||
|
self.register[x] = self.register[x] / 2;
|
||||||
|
self.pc += OPCODE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Vx = Vy - Vx, set VF = NOT borrow
|
||||||
|
fn code_8xy7(&mut self, x: usize, y: usize) {
|
||||||
|
self.register[0x0f] = (self.register[y] > self.register[x]) as u8;
|
||||||
|
self.register[x] = self.register[y] - self.register[x];
|
||||||
|
self.pc += OPCODE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Vx = Vx SHL 1
|
||||||
|
fn code_8xye(&mut self, x: usize, _y: usize) {
|
||||||
|
self.register[0x0f] = (self.register[x] & 0b10000000) >> 7;
|
||||||
|
self.register[x] = self.register[x] * 2;
|
||||||
|
self.pc += OPCODE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip next instruction if Vx != Vy
|
||||||
|
fn code_9xy0(&mut self, x: usize, y: usize) {
|
||||||
|
if self.register[x] != self.register[y] {
|
||||||
|
self.pc += 2 * OPCODE_SIZE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.pc += OPCODE_SIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set I = nnn
|
||||||
|
fn code_annn(&mut self, nnn: usize) {
|
||||||
|
self.index = nnn;
|
||||||
|
self.pc += OPCODE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Jump to location nnn + V0
|
||||||
|
fn code_bnnn(&mut self, nnn: usize) {
|
||||||
|
self.pc = nnn + self.register[0x00] as usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Vx = random byte AND kk
|
||||||
|
fn code_cxkk(&mut self, x: usize, kk: u8) {
|
||||||
|
let rng = rand::thread_rng().gen_range(0..255);
|
||||||
|
self.register[x] = kk & rng;
|
||||||
|
self.pc += OPCODE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display n-byte sprite starting at memory location I at (Vx, Vy), set VF = collision.
|
||||||
|
fn code_dxyn(&mut self, x: usize, y: usize, n: usize) {
|
||||||
|
// TODO
|
||||||
|
self.register[0x0f] = 0;
|
||||||
|
for byte in 0..n {
|
||||||
|
// get y coord, which we want to draw -> use modulo, so we don't overlap
|
||||||
|
let y = (self.register[y] as usize + byte) % SCREEN_HEIGHT;
|
||||||
|
for bit in 0..8 {
|
||||||
|
// get x coord, just as above
|
||||||
|
let x = (self.register[x] as usize + bit) % SCREEN_WIDTH;
|
||||||
|
// bit hack to get every bit in a row
|
||||||
|
let pixel_to_draw = (self.memory[self.index + byte] >> (7 - bit)) & 1;
|
||||||
|
// check if we will overwrite an existing pixel
|
||||||
|
self.register[0x0f] = self.register[0x0f] | (pixel_to_draw & self.screen[x][y]);
|
||||||
|
self.screen[x][y] = self.screen[x][y] ^ pixel_to_draw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.draw_flag = true;
|
||||||
|
self.pc += OPCODE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip next instruction if key with the value of Vx is pressed
|
||||||
|
fn code_ex9e(&mut self, x: usize) {
|
||||||
|
if self.key[self.register[x] as usize] {
|
||||||
|
self.pc += 2 * OPCODE_SIZE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.pc += OPCODE_SIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip next instruction if key with the value of Vx is not pressed
|
||||||
|
fn code_exa1(&mut self, x: usize) {
|
||||||
|
if !(self.key[self.register[x] as usize]) {
|
||||||
|
self.pc += 2 * OPCODE_SIZE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.pc += OPCODE_SIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Vx = delay timer value
|
||||||
|
fn code_fx07(&mut self, x: usize) {
|
||||||
|
self.register[x] = self.delay_timer as u8;
|
||||||
|
self.pc = OPCODE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for a key press, store the value of the key in Vx
|
||||||
|
fn code_fx0a(&mut self, x: usize) {
|
||||||
|
self.waiting_for_key = true;
|
||||||
|
self.waiting_key_location = x; // safe for later
|
||||||
|
self.pc += OPCODE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set delay timer = Vx
|
||||||
|
fn code_fx15(&mut self, x: usize) {
|
||||||
|
self.delay_timer = self.register[x] as usize;
|
||||||
|
self.pc += OPCODE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sound timer = Vx
|
||||||
|
fn code_fx18(&mut self, x: usize) {
|
||||||
|
self.sound_timer = self.register[x] as usize;
|
||||||
|
self.pc += OPCODE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set I = I + Vx
|
||||||
|
fn code_fx1e(&mut self, x: usize) {
|
||||||
|
self.index += self.register[x] as usize;
|
||||||
|
self.pc += OPCODE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set I = location of sprite for digit Vx
|
||||||
|
fn code_fx29(&mut self, x: usize) {
|
||||||
|
let sprite_name = self.register[x] as usize;
|
||||||
|
let mem_position = sprite_name * 5; // single sprite is 5byte
|
||||||
|
self.index = mem_position;
|
||||||
|
self.pc += OPCODE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store BCD representation of Vx in memory locations I, I+1, and I+2
|
||||||
|
fn code_fx33(&mut self, x: usize) {
|
||||||
|
let val = self.register[x];
|
||||||
|
self.memory[self.index] = (val / 100) as u8;
|
||||||
|
self.memory[self.index + 1] = ((val % 100) / 10) as u8;
|
||||||
|
self.memory[self.index + 2] = (val % 10) as u8;
|
||||||
|
self.pc += OPCODE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store registers V0 through Vx in memory starting at location I
|
||||||
|
fn code_fx55(&mut self, x: usize) {
|
||||||
|
// TODO offset correct?
|
||||||
|
for reg_i in 0..x {
|
||||||
|
self.memory[self.index + reg_i] = self.register[reg_i];
|
||||||
|
}
|
||||||
|
self.pc += OPCODE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read registers V0 through Vx from memory starting at location I
|
||||||
|
fn code_fx65(&mut self, x: usize) {
|
||||||
|
for reg_i in 0..x {
|
||||||
|
self.register[reg_i] = self.memory[self.index + reg_i];
|
||||||
|
}
|
||||||
|
self.pc += OPCODE_SIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user