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