mirror of
https://github.com/Noratrieb/chip-8.git
synced 2026-01-14 16:35:02 +01:00
basic instructions
This commit is contained in:
parent
392bdaf015
commit
31cb1bed9e
4 changed files with 346 additions and 1 deletions
78
Cargo.lock
generated
78
Cargo.lock
generated
|
|
@ -1,5 +1,83 @@
|
||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chip-8"
|
name = "chip-8"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"rand",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.94"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e"
|
||||||
|
|
||||||
|
[[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 = "wasi"
|
||||||
|
version = "0.10.2+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||||
|
|
|
||||||
|
|
@ -7,3 +7,4 @@ edition = "2018"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
rand = "0.8.3"
|
||||||
261
src/interpreter.rs
Normal file
261
src/interpreter.rs
Normal file
|
|
@ -0,0 +1,261 @@
|
||||||
|
// this should be read before reading the code
|
||||||
|
// http://devernay.free.fr/hacks/chip8/C8TECH10.HTM
|
||||||
|
|
||||||
|
use rand::Rng;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
struct Emulator {
|
||||||
|
memory: [u8; 4096],
|
||||||
|
reg: [u8; 16],
|
||||||
|
stack: [u16; 16],
|
||||||
|
pc: u16,
|
||||||
|
sp: u8,
|
||||||
|
i: u16,
|
||||||
|
delay_t: u8,
|
||||||
|
sound_t: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Emulator {
|
||||||
|
fn push(&mut self, n: u16) {
|
||||||
|
self.sp += 1;
|
||||||
|
self.stack[self.sp as usize] = n;
|
||||||
|
}
|
||||||
|
fn pop(&mut self) -> u16 {
|
||||||
|
let n = self.stack[self.sp as usize];
|
||||||
|
self.sp -= 1;
|
||||||
|
n
|
||||||
|
}
|
||||||
|
fn jmp(&mut self, location: u16) {
|
||||||
|
self.pc = location;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next(&mut self) {
|
||||||
|
self.pc += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_flag(&mut self, cond: bool) {
|
||||||
|
self.reg[0xF] = if cond {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn run(program: &[u16]) {
|
||||||
|
let mut em = Emulator {
|
||||||
|
memory: [0; 4096],
|
||||||
|
reg: [0; 16],
|
||||||
|
stack: [0; 16],
|
||||||
|
pc: 0,
|
||||||
|
sp: 0,
|
||||||
|
i: 0,
|
||||||
|
delay_t: 0,
|
||||||
|
sound_t: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
while em.pc < program.len() as u16 {
|
||||||
|
let instruction = program[em.pc as usize];
|
||||||
|
execute(instruction, &mut em);
|
||||||
|
em.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn execute(instruction: u16, em: &mut Emulator) {
|
||||||
|
match instruction {
|
||||||
|
0x00E0 => unimplemented!(), // clear display
|
||||||
|
0x00EE => { // return subroutine
|
||||||
|
let location = em.pop();
|
||||||
|
em.jmp(location);
|
||||||
|
}
|
||||||
|
0x1000..=0x1FFF => { // jmp
|
||||||
|
let location = instruction & 0x0FFF;
|
||||||
|
em.jmp(location - 1);
|
||||||
|
}
|
||||||
|
0x2000..=0x2FFF => { // 0nnn, call nnn
|
||||||
|
let location = instruction & 0x0FFF;
|
||||||
|
em.push(em.pc);
|
||||||
|
em.jmp(location - 1);
|
||||||
|
}
|
||||||
|
0x3000..=0x3FFF => { // SE - Skip equal | 0xkk, skip instruction if Vx == kk
|
||||||
|
let (x, kk) = extract_0xkk(instruction);
|
||||||
|
if em.reg[x] == kk {
|
||||||
|
em.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0x4000..=0x4FFF => { // SNE - Skip not equal | 0xkk, skip instruction if Vx != kk
|
||||||
|
let (x, kk) = extract_0xkk(instruction);
|
||||||
|
if em.reg[x] != kk {
|
||||||
|
em.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0x5000..=0x5FF0 => { // SE - Skip equal | 0xy0, skip if Vx == Vy
|
||||||
|
let (x, y) = extract_0xy0(instruction);
|
||||||
|
if em.reg[x] == em.reg[y] {
|
||||||
|
em.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0x6000..=0x6FFF => { // LD - Load | 0xkk, load kk into Vx
|
||||||
|
let (x, kk) = extract_0xkk(instruction);
|
||||||
|
em.reg[x] = kk;
|
||||||
|
}
|
||||||
|
0x7000..=0x7FFF => { // ADD - Add | 0xkk, add kk to Vx
|
||||||
|
let (x, kk) = extract_0xkk(instruction);
|
||||||
|
em.reg[x] = em.reg[x].wrapping_add(kk);
|
||||||
|
}
|
||||||
|
0x8000..=0x8FF0 => { // 0xyz, do z on Vy, Vx
|
||||||
|
let (x, y, z) = extract_0xyz(instruction);
|
||||||
|
match z {
|
||||||
|
0 => { // LD - Load | Stores Vy in Vx
|
||||||
|
em.reg[x] = em.reg[y];
|
||||||
|
}
|
||||||
|
1 => { // OR | Stores a bitwise OR of Vx and Vy in Vx
|
||||||
|
em.reg[x] |= em.reg[y];
|
||||||
|
}
|
||||||
|
2 => { // AND | Stores a bitwise AND of Vx and Vy in Vx
|
||||||
|
em.reg[x] &= em.reg[y];
|
||||||
|
}
|
||||||
|
3 => { // XOR | Stores a bitwise XOR of Vx and Vy in Vx
|
||||||
|
em.reg[x] ^= em.reg[y];
|
||||||
|
}
|
||||||
|
4 => { // ADD | Adds Vx to Vy. If it overflows, Vf = 1 else Vf = 0
|
||||||
|
let (val, f) = em.reg[x].overflowing_add(em.reg[y]);
|
||||||
|
em.reg[x] = val;
|
||||||
|
em.set_flag(f);
|
||||||
|
}
|
||||||
|
5 => { // SUB | If Vx > Vy, Vf = 1, else Vf = 0, then subtract Vy from Vx and store it into Vx
|
||||||
|
em.set_flag(em.reg[x] > em.reg[y]);
|
||||||
|
em.reg[x] = em.reg[x].wrapping_sub(em.reg[y])
|
||||||
|
}
|
||||||
|
6 => { // SHR - shift right | shift Vx right by one bit, if the rightmost bit is 1 set flag
|
||||||
|
em.set_flag((em.reg[x] & 0x0001) > 0);
|
||||||
|
em.reg[x] >>= 1;
|
||||||
|
}
|
||||||
|
7 => { // SUBN - Sub not borrow | subtract Vx from Vy and store it into Vx
|
||||||
|
em.set_flag(em.reg[y] > em.reg[x]);
|
||||||
|
em.reg[x] = em.reg[y].wrapping_sub(em.reg[x]);
|
||||||
|
}
|
||||||
|
0xE => { // SHL - Shift left | shift Vx left, if the leftmost bit is 1 set flag
|
||||||
|
em.set_flag((em.reg[x] & 0x80) > 0);
|
||||||
|
em.reg[x] <<= 1;
|
||||||
|
}
|
||||||
|
_ => unreachable!("invalid instruction")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0x9000..=0x9FFF => { // SNE - skip not equal | skip next if Vx != Vy
|
||||||
|
let (x, y) = extract_0xy0(instruction);
|
||||||
|
if em.reg[x] != em.reg[y] {
|
||||||
|
em.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0xA000..=0xAFFF => { // LDI - load i | load nnn into i
|
||||||
|
let nnn = extract_0nnn(instruction);
|
||||||
|
em.i = nnn;
|
||||||
|
}
|
||||||
|
0xB000..=0xBFFF => { // JMP | jump to nnn + V0
|
||||||
|
let nnn = extract_0nnn(instruction);
|
||||||
|
em.jmp(em.reg[0] as u16 + nnn - 1);
|
||||||
|
}
|
||||||
|
0xC000..=0xCFFF => { // RAN | set Vx to a random byte AND kk
|
||||||
|
let (x, kk) = extract_0xii(instruction);
|
||||||
|
let ran = rand::thread_rng().gen_range(0..=255);
|
||||||
|
em.reg[x] = ran & kk;
|
||||||
|
}
|
||||||
|
0xD000..=0xDFFF => { // 0xyn display n-byte sprite starting at location I at (Vx, Vy), set Vf if there's a collision
|
||||||
|
// read n bytes from memory at loc I and display it at coords (Vx, Vy)
|
||||||
|
// XOR them onto the screen, Vf = 1 if a pixel was erased
|
||||||
|
// wrap sprites around
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
0xE000..=0xEFFF => { // skip/not skip instruction if key Vx is pressed
|
||||||
|
let (x, sub_inst) = extract_0xii(instruction);
|
||||||
|
fn key_pressed(key: u8) -> bool { unimplemented!("{}", key) }
|
||||||
|
|
||||||
|
match sub_inst {
|
||||||
|
0x9E => { // skip
|
||||||
|
if key_pressed(em.reg[x]) {
|
||||||
|
em.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0xA1 => { // not skip
|
||||||
|
if !key_pressed(em.reg[x]) {
|
||||||
|
em.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unreachable!("invalid instruction")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0xF000..=0xFFFF => { // misc
|
||||||
|
let (x, sub_inst) = extract_0xii(instruction);
|
||||||
|
match sub_inst {
|
||||||
|
0x0F => { // set Vx = delay timer value
|
||||||
|
em.reg[x] = em.delay_t;
|
||||||
|
}
|
||||||
|
0x0A => { // wait for key and store it in Vx
|
||||||
|
let _key = unimplemented!();
|
||||||
|
em.reg[x] = _key;
|
||||||
|
}
|
||||||
|
0x15 => { // set delay timer = Vx
|
||||||
|
em.delay_t = em.reg[x];
|
||||||
|
}
|
||||||
|
0x18 => { // set sound timer = Vx
|
||||||
|
em.sound_t = em.reg[x];
|
||||||
|
}
|
||||||
|
0x1E => { // Add Vx to I and store it in I
|
||||||
|
em.i += em.reg[x] as u16;
|
||||||
|
}
|
||||||
|
0x29 => { // set I to location for the sprite with value Vx
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
0x33 => { // store a decimal representation of Vx at I, I+1, I+2
|
||||||
|
// convert Vx to decimal and store the digits, 100 -> I
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
0x55 => { // store registers V0 to Vx in memory I
|
||||||
|
for i in 0..x as u16 {
|
||||||
|
em.memory[(em.i + i) as usize] = em.reg[i as usize];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0x65 => { // load V0 to Vx from memory at I
|
||||||
|
for i in 0..x as u16 {
|
||||||
|
em.reg[i as usize] = em.memory[(em.i + i) as usize]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unreachable!("invalid instruction")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn extract_0xkk(value: u16) -> (usize, u8) {
|
||||||
|
let x = value & 0x0F00;
|
||||||
|
let kk = (value & 0x00FF) as u8;
|
||||||
|
(x as usize, kk)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract_0xy0(value: u16) -> (usize, usize) {
|
||||||
|
let x = value & 0x0F00;
|
||||||
|
let y = value & 0x00F0;
|
||||||
|
(x as usize, y as usize)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract_0xyz(value: u16) -> (usize, usize, u16) {
|
||||||
|
let x = value & 0x0F00;
|
||||||
|
let y = value & 0x00F0;
|
||||||
|
let z = value & 0x000F;
|
||||||
|
(x as usize, y as usize, z)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract_0xii(value: u16) -> (usize, u8) {
|
||||||
|
let x = value & 0x0F00;
|
||||||
|
let ii = (value & 0x00FF) as u8;
|
||||||
|
(x as usize, ii)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract_0nnn(value: u16) -> u16 {
|
||||||
|
value & 0xFFF
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
use crate::interpreter::{run};
|
||||||
|
|
||||||
|
mod interpreter;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("Hello, world!");
|
println!("Hello, world!");
|
||||||
|
run(&[])
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue