basic instructions

This commit is contained in:
nora 2021-05-15 21:19:59 +02:00
parent 392bdaf015
commit 31cb1bed9e
4 changed files with 346 additions and 1 deletions

78
Cargo.lock generated
View file

@ -1,5 +1,83 @@
# This file is automatically @generated by Cargo.
# 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]]
name = "chip-8"
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"

View file

@ -7,3 +7,4 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rand = "0.8.3"

261
src/interpreter.rs Normal file
View 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
}

View file

@ -1,3 +1,8 @@
use crate::interpreter::{run};
mod interpreter;
fn main() {
println!("Hello, world!");
run(&[])
}