Implement DRW and reorganize project
This commit is contained in:
parent
b320398394
commit
be4be99c92
|
@ -0,0 +1,456 @@
|
||||||
|
use super::periph::{Display, Keypad, Timer};
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{self, Read};
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct Chip8 {
|
||||||
|
opcode: u16,
|
||||||
|
i: u16,
|
||||||
|
pc: u16,
|
||||||
|
sp: u8,
|
||||||
|
key: Keypad,
|
||||||
|
v: [u8; 16],
|
||||||
|
stack: [u16; 16],
|
||||||
|
memory: [u8; 4096],
|
||||||
|
delay: Timer,
|
||||||
|
sound: Timer,
|
||||||
|
display: Display,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Chip8 {
|
||||||
|
fn default() -> Self {
|
||||||
|
Chip8 {
|
||||||
|
opcode: 0,
|
||||||
|
i: 0,
|
||||||
|
pc: 0x200, // Progrm counter starts at 0x200
|
||||||
|
sp: 0,
|
||||||
|
key: Default::default(),
|
||||||
|
v: [0; 16],
|
||||||
|
stack: [0; 16],
|
||||||
|
memory: [0; 4096],
|
||||||
|
delay: Default::default(),
|
||||||
|
sound: Default::default(),
|
||||||
|
display: Display::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Chip8 {
|
||||||
|
pub fn execute_cycle(&mut self) {
|
||||||
|
self.opcode = self.get_opcode();
|
||||||
|
println!("{:#x}", self.opcode);
|
||||||
|
self.handle_opcode();
|
||||||
|
|
||||||
|
self.delay.tick();
|
||||||
|
self.sound.tick();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_rom<P: AsRef<Path>>(&mut self, path: P) -> Result<(), io::Error> {
|
||||||
|
let mut file = File::open(path.as_ref())?;
|
||||||
|
let mut rom_buf: Vec<u8> = vec![];
|
||||||
|
file.read_to_end(&mut rom_buf)?;
|
||||||
|
|
||||||
|
for (i, byte) in rom_buf.iter().enumerate() {
|
||||||
|
self.memory[i + 0x200] = *byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_opcode(&self) -> u16 {
|
||||||
|
let pc = self.pc as usize;
|
||||||
|
|
||||||
|
((self.memory[pc] as u16) << 8) | self.memory[pc + 1] as u16
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_opcode(&mut self) {
|
||||||
|
// Given: 0xA2F0
|
||||||
|
let nib_1 = (self.opcode & 0xF000) >> 12; //0xA
|
||||||
|
let nib_2 = (self.opcode & 0x0F00) >> 8; // 0x2
|
||||||
|
let nib_3 = (self.opcode & 0x00F0) >> 4; // 0xF
|
||||||
|
let nib_4 = self.opcode & 0x000F; // 0x0
|
||||||
|
|
||||||
|
self.pc += 2;
|
||||||
|
|
||||||
|
// nib_ns are u16s so we waste 4 bytes here.
|
||||||
|
|
||||||
|
match (nib_1, nib_2, nib_3, nib_4) {
|
||||||
|
// CLS
|
||||||
|
(0x0, 0x0, 0xE, 0x0) => self.cls(),
|
||||||
|
// 00EE
|
||||||
|
(0x0, 0x0, 0xE, 0xE) => self.ret(),
|
||||||
|
// 1NNN
|
||||||
|
(0x1, _, _, _) => self.jmp_addr(nib_2, nib_3, nib_4),
|
||||||
|
// 2NNN
|
||||||
|
(0x2, _, _, _) => self.call_addr(nib_2, nib_3, nib_4),
|
||||||
|
// 3XKK
|
||||||
|
(0x3, _, _, _) => self.se_vx_byte(nib_2, nib_3, nib_4),
|
||||||
|
// 4xkk
|
||||||
|
(0x4, _, _, _) => self.sne_vx_byte(nib_2, nib_3, nib_4),
|
||||||
|
// 5xy0
|
||||||
|
(0x5, _, _, 0x0) => self.se_vx_vy(nib_2, nib_3),
|
||||||
|
// 6xkk
|
||||||
|
(0x6, _, _, _) => self.ld_vx_byte(nib_2, nib_3, nib_4),
|
||||||
|
// 7xkk
|
||||||
|
(0x7, _, _, _) => self.add_vx_byte(nib_2, nib_3, nib_4),
|
||||||
|
// 8xy0
|
||||||
|
(0x8, _, _, 0x0) => self.ld_vx_vy(nib_2, nib_3),
|
||||||
|
// 8xy1
|
||||||
|
(0x8, _, _, 0x1) => self.or_vx_vy(nib_2, nib_3),
|
||||||
|
// 8xy2
|
||||||
|
(0x8, _, _, 0x2) => self.and_vx_vy(nib_2, nib_3),
|
||||||
|
// 8xy3
|
||||||
|
(0x8, _, _, 0x3) => self.xor_vx_vy(nib_2, nib_3),
|
||||||
|
// 8xy4
|
||||||
|
(0x8, _, _, 0x4) => self.add_vx_vy(nib_2, nib_3),
|
||||||
|
// 8xy5
|
||||||
|
(0x8, _, _, 0x5) => self.sub_vx_vy(nib_2, nib_3),
|
||||||
|
// 8xy6
|
||||||
|
(0x8, _, _, 0x6) => self.shr_vx(nib_2),
|
||||||
|
// 8xy7
|
||||||
|
(0x8, _, _, 0x7) => self.subn_vx_vy(nib_2, nib_3),
|
||||||
|
// 8xyE
|
||||||
|
(0x8, _, _, 0xE) => self.shl_vx(nib_2),
|
||||||
|
// 9xy0
|
||||||
|
(0x9, _, _, 0x0) => self.sne_vx_vy(nib_2, nib_3),
|
||||||
|
// Annn
|
||||||
|
(0xA, _, _, _) => self.ld_i_addr(nib_2, nib_3, nib_4),
|
||||||
|
// Bnnn
|
||||||
|
(0xB, _, _, _) => self.jmp_v0_addr(nib_2, nib_3, nib_4),
|
||||||
|
// Cxkk
|
||||||
|
(0xC, _, _, _) => self.rnd_vx_byte(nib_2, nib_3, nib_4),
|
||||||
|
//Dxyn
|
||||||
|
(0xD, _, _, _) => self.drw_vx_vy_nib(nib_2, nib_3, nib_4),
|
||||||
|
// Ex9E
|
||||||
|
(0xE, _, 0x9, 0xE) => self.skp_vx(nib_2),
|
||||||
|
// ExA1
|
||||||
|
(0xE, _, 0xA, 0x1) => self.sknp_vx(nib_2),
|
||||||
|
// Fx07
|
||||||
|
(0xF, _, 0x0, 0x7) => self.ld_vx_dt(nib_2),
|
||||||
|
// Fx0A
|
||||||
|
(0xF, _, 0x0, 0xA) => self.ld_vx_k(nib_2),
|
||||||
|
// Fx15
|
||||||
|
(0xF, _, 0x1, 0x15) => self.ld_dt_vx(nib_2),
|
||||||
|
// Fx18
|
||||||
|
(0xF, _, 0x1, 0x8) => self.ld_st_vx(nib_2),
|
||||||
|
// Fx1E
|
||||||
|
(0xF, _, 0x1, 0xE) => self.add_i_vx(nib_2),
|
||||||
|
// Fx29
|
||||||
|
(0xF, _, 0x2, 0x9) => self.ld_f_vx(nib_2),
|
||||||
|
// Fx33
|
||||||
|
(0xF, _, 0x3, 0x3) => self.ld_b_vx(nib_2),
|
||||||
|
// Fx55
|
||||||
|
(0xF, _, 0x5, 0x5) => self.ld_i_vx(nib_2),
|
||||||
|
// Fx65
|
||||||
|
(0xF, _, 0x6, 0x5) => self.ld_vx_i(nib_2),
|
||||||
|
// General Case
|
||||||
|
_ => println!("UNIMPLEMENTED OPCODE: {:#x}", self.opcode),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cls(&mut self) {
|
||||||
|
// Clear the display
|
||||||
|
self.display.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn jmp_addr(&mut self, n_1: u16, n_2: u16, n_3: u16) {
|
||||||
|
// sets the program counter to addr (nnn)
|
||||||
|
self.pc = Self::convert_to_addr(n_1, n_2, n_3);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ret(&mut self) {
|
||||||
|
// sets the program counter to the addres at the top of the stack,
|
||||||
|
// then subtracts one (1) from the stack pointer
|
||||||
|
self.pc = self.stack[self.sp as usize];
|
||||||
|
self.sp -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call_addr(&mut self, n_1: u16, n_2: u16, n_3: u16) {
|
||||||
|
// increments the stack pointer, then puts current pc on top of stack
|
||||||
|
// pc is then set to addr
|
||||||
|
self.sp += 1;
|
||||||
|
self.stack[self.sp as usize] = self.pc;
|
||||||
|
self.pc = Self::convert_to_addr(n_1, n_2, n_3);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn se_vx_byte(&mut self, x: u16, k_1: u16, k_2: u16) {
|
||||||
|
// compares Vx to kk. If they are equal, pc is incremented by 2
|
||||||
|
if self.v[x as usize] == Self::convert_to_byte(k_1, k_2) {
|
||||||
|
self.pc += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sne_vx_byte(&mut self, x: u16, k_1: u16, k_2: u16) {
|
||||||
|
// compares Vx to kk. If they are **not** equal, pc is incremented by 2
|
||||||
|
if self.v[x as usize] != Self::convert_to_byte(k_1, k_2) {
|
||||||
|
self.pc += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn se_vx_vy(&mut self, x: u16, y: u16) {
|
||||||
|
// compares Vx to Vy. If they are equal, pc is incremented by 2
|
||||||
|
if self.v[x as usize] == self.v[y as usize] {
|
||||||
|
self.pc += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ld_vx_byte(&mut self, x: u16, k_1: u16, k_2: u16) {
|
||||||
|
// put value kk into Vx
|
||||||
|
self.v[x as usize] = Self::convert_to_byte(k_1, k_2);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_vx_byte(&mut self, x: u16, k_1: u16, k_2: u16) {
|
||||||
|
// calculate Vx + kk, then store it in Vx
|
||||||
|
let x = x as usize;
|
||||||
|
self.v[x] = self.v[x] + Self::convert_to_byte(k_1, k_2);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ld_vx_vy(&mut self, x: u16, y: u16) {
|
||||||
|
// store Vy in Vx
|
||||||
|
self.v[x as usize] = self.v[y as usize];
|
||||||
|
}
|
||||||
|
|
||||||
|
fn or_vx_vy(&mut self, x: u16, y: u16) {
|
||||||
|
// calc bitwise OR on Vx and Vy, then store in Vx
|
||||||
|
let x = x as usize;
|
||||||
|
self.v[x] = self.v[x] | self.v[y as usize];
|
||||||
|
}
|
||||||
|
|
||||||
|
fn and_vx_vy(&mut self, x: u16, y: u16) {
|
||||||
|
// calc bitwise AND on Vx and Vy, then store in Vx
|
||||||
|
let x = x as usize;
|
||||||
|
self.v[x] = self.v[x] & self.v[y as usize];
|
||||||
|
}
|
||||||
|
|
||||||
|
fn xor_vx_vy(&mut self, x: u16, y: u16) {
|
||||||
|
// calc bitwise XOR on Vx and Vy, then store in Vx
|
||||||
|
let x = x as usize;
|
||||||
|
self.v[x] = self.v[x] ^ self.v[y as usize];
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_vx_vy(&mut self, x: u16, y: u16) {
|
||||||
|
// add Vx and Vy, if result is greater than 8 bits
|
||||||
|
// set VF to 1, otherwise 0
|
||||||
|
// only the lowest 8 bits of result are stored in Vx
|
||||||
|
let x = x as usize;
|
||||||
|
|
||||||
|
let (res, did_overflow) = self.v[x].overflowing_add(self.v[y as usize]);
|
||||||
|
self.v[0xF as usize] = if did_overflow { 1 } else { 0 };
|
||||||
|
self.v[x] = res;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sub_vx_vy(&mut self, x: u16, y: u16) {
|
||||||
|
// subtract Vx and Vy, if Vx > Vy VF is set to 1, otherwise 0
|
||||||
|
// then set Vx to Vx - Vy
|
||||||
|
let vx = self.v[x as usize];
|
||||||
|
let vy = self.v[y as usize];
|
||||||
|
|
||||||
|
self.v[0xF as usize] = if vx > vy { 1 } else { 0 };
|
||||||
|
self.v[x as usize] = vx - vy;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn shr_vx(&mut self, x: u16) {
|
||||||
|
// if LSB is set to 1, set VF to 1, otherwise set VF to 0
|
||||||
|
// then shift Vx one to the right
|
||||||
|
let x = x as usize;
|
||||||
|
|
||||||
|
self.v[0xF as usize] = if (self.v[x] & 1) == 1 { 1 } else { 0 };
|
||||||
|
self.v[x] = self.v[x] >> 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn subn_vx_vy(&mut self, x: u16, y: u16) {
|
||||||
|
// subtract Vy and Vx, if Vy > Vx VF is set to 1, otherwise 0
|
||||||
|
// then set Vx = Vy - Vx
|
||||||
|
let vx = self.v[x as usize];
|
||||||
|
let vy = self.v[y as usize];
|
||||||
|
|
||||||
|
self.v[0xF as usize] = if vy > vx { 1 } else { 0 };
|
||||||
|
self.v[x as usize] = vy - vx;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn shl_vx(&mut self, x: u16) {
|
||||||
|
// if MSB is set to 1, set VF to 1, otherwise set VF to 0
|
||||||
|
// then shift Vx one to the left;
|
||||||
|
let x = x as usize;
|
||||||
|
|
||||||
|
self.v[0xF as usize] = if (self.v[x] & 0x80) == 1 { 1 } else { 0 };
|
||||||
|
self.v[x] = self.v[x] << 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sne_vx_vy(&mut self, x: u16, y: u16) {
|
||||||
|
// if Vx != vy program counter is increased by 2
|
||||||
|
if self.v[x as usize] != self.v[y as usize] {
|
||||||
|
self.pc += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ld_i_addr(&mut self, n_1: u16, n_2: u16, n_3: u16) {
|
||||||
|
// set i to addr
|
||||||
|
self.i = Self::convert_to_addr(n_1, n_2, n_3);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn jmp_v0_addr(&mut self, n_1: u16, n_2: u16, n_3: u16) {
|
||||||
|
// set program counter to addr + V0
|
||||||
|
self.pc = Self::convert_to_addr(n_1, n_2, n_3) + self.v[0 as usize] as u16;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rnd_vx_byte(&mut self, x: u16, k_1: u16, k_2: u16) {
|
||||||
|
// generate a random number from 0 to 255
|
||||||
|
// AND with the value of kk, then store in Vx
|
||||||
|
|
||||||
|
self.v[x as usize] = rand::random::<u8>() & Self::convert_to_byte(k_1, k_2);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drw_vx_vy_nib(&mut self, x: u16, y: u16, nib: u16) {
|
||||||
|
// read n bytes from memory starting from self.i
|
||||||
|
// then display them starting at (vx, vy)
|
||||||
|
let i = self.i as usize;
|
||||||
|
let disp_x = self.v[x as usize];
|
||||||
|
let disp_y = self.v[y as usize];
|
||||||
|
|
||||||
|
let mut display_buf = self.display.buf;
|
||||||
|
|
||||||
|
let mut pixel: u8;
|
||||||
|
self.v[0xF as usize] = 0;
|
||||||
|
|
||||||
|
// TODO: Rewrite Your Solution to better fit
|
||||||
|
// the rest of your code base.
|
||||||
|
// http://www.multigesture.net/articles/how-to-write-an-emulator-chip-8-interpreter/
|
||||||
|
for i in 0..(nib as usize) {
|
||||||
|
// height of sprite (y-axis)
|
||||||
|
pixel = self.memory[self.i as usize + i];
|
||||||
|
for j in 0..8 {
|
||||||
|
// Loop over the 8 bits (x-axis)
|
||||||
|
if (pixel & (0x80 >> j)) != 0 {
|
||||||
|
let index = (disp_x as usize) + j + (((disp_y as usize) + i) * 64);
|
||||||
|
|
||||||
|
if display_buf[index] == 1 {
|
||||||
|
self.v[0xF as usize] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
display_buf[index] ^= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn skp_vx(&mut self, x: u16) {
|
||||||
|
// if current key is the same as the on in Vx
|
||||||
|
// program counter is increased by 2
|
||||||
|
|
||||||
|
if let Some(key) = self.key.get_pressed() {
|
||||||
|
if self.v[x as usize] == key {
|
||||||
|
self.pc += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sknp_vx(&mut self, x: u16) {
|
||||||
|
// if current key is not the sameas the one in Vx
|
||||||
|
// increment the program counter by 2
|
||||||
|
|
||||||
|
match self.key.get_pressed() {
|
||||||
|
Some(key) => {
|
||||||
|
if self.v[x as usize] != key {
|
||||||
|
self.pc += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => self.pc += 2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ld_vx_dt(&mut self, x: u16) {
|
||||||
|
// set Vx to be the value of the delay timer
|
||||||
|
self.v[x as usize] = self.delay.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ld_vx_k(&mut self, x: u16) {
|
||||||
|
// wait (blocking) until a key is pressed
|
||||||
|
// once pressed, store in Vx
|
||||||
|
|
||||||
|
match self.key.get_pressed() {
|
||||||
|
Some(key) => self.v[x as usize] = key,
|
||||||
|
None => self.pc -= 2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ld_dt_vx(&mut self, x: u16) {
|
||||||
|
// set delay timer to be value of Vx
|
||||||
|
self.delay.set(self.v[x as usize]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ld_st_vx(&mut self, x: u16) {
|
||||||
|
// set sound timer to be value of Vx
|
||||||
|
self.delay.set(self.v[x as usize]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_i_vx(&mut self, x: u16) {
|
||||||
|
// set I to be I + Vx
|
||||||
|
self.i = self.i + self.v[x as usize] as u16;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ld_f_vx(&mut self, _x: u16) {
|
||||||
|
todo!("Implement 0xF_29");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ld_b_vx(&mut self, x: u16) {
|
||||||
|
// take hundreds digit and place it at I
|
||||||
|
// take tens digit and place it at I + 1
|
||||||
|
// take ones digit and place it at I + 2
|
||||||
|
let i = self.i as usize;
|
||||||
|
let mut iter = Self::digits(self.v[x as usize]);
|
||||||
|
|
||||||
|
let ones = iter.next().unwrap(); // Ther has to at least be a ones lol
|
||||||
|
let tens = iter.next().unwrap_or(0);
|
||||||
|
let hundreds = iter.next().unwrap_or(0);
|
||||||
|
|
||||||
|
self.memory[i] = hundreds;
|
||||||
|
self.memory[i + 1] = tens;
|
||||||
|
self.memory[i + 2] = ones;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ld_i_vx(&mut self, x: u16) {
|
||||||
|
// copy values v0 -> Vx to memory starting at i
|
||||||
|
|
||||||
|
for n in 0..=(x as usize) {
|
||||||
|
self.memory[self.i as usize + n] = self.v[n];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ld_vx_i(&mut self, x: u16) {
|
||||||
|
// read what will be values of v0 -> vx from memory starting at i
|
||||||
|
let x = x as usize;
|
||||||
|
|
||||||
|
for n in 0..=x {
|
||||||
|
self.v[n] = self.memory[self.i as usize + n];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_to_byte(k_1: u16, k_2: u16) -> u8 {
|
||||||
|
((k_1 as u8) << 4) | (k_2 as u8)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_to_addr(n_1: u16, n_2: u16, n_3: u16) -> u16 {
|
||||||
|
(n_1 << 8) | (n_2 << 4) | n_3
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://stackoverflow.com/questions/41536479/how-do-i-split-an-integer-into-individual-digits
|
||||||
|
fn digits(mut num: u8) -> impl Iterator<Item = u8> {
|
||||||
|
let mut divisor = 1;
|
||||||
|
while num >= divisor * 10 {
|
||||||
|
divisor *= 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::iter::from_fn(move || {
|
||||||
|
if divisor == 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let v = num / divisor;
|
||||||
|
num %= divisor;
|
||||||
|
divisor /= 10;
|
||||||
|
Some(v)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod emu;
|
||||||
|
pub mod periph;
|
529
src/main.rs
529
src/main.rs
|
@ -1,534 +1,9 @@
|
||||||
// I used: http://devernay.free.fr/hacks/chip8/C8TECH10.HTM#00E0
|
// I used: http://devernay.free.fr/hacks/chip8/C8TECH10.HTM#00E0
|
||||||
// and https://en.wikipedia.org/wiki/CHIP-8#Opcode_table
|
// and https://en.wikipedia.org/wiki/CHIP-8#Opcode_table
|
||||||
// for information about how to implement a CHIP-8 Emulator
|
// for information about how to implement a CHIP-8 Emulator
|
||||||
use std::fs::File;
|
use chip8::emu::Chip8;
|
||||||
use std::io::{self, Read};
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
const SCREEN_SIZE: usize = 64 * 32;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
struct Chip8 {
|
|
||||||
opcode: u16,
|
|
||||||
i: u16,
|
|
||||||
pc: u16,
|
|
||||||
sp: u8,
|
|
||||||
key: Option<u8>,
|
|
||||||
v: [u8; 16],
|
|
||||||
stack: [u16; 16],
|
|
||||||
memory: [u8; 4096],
|
|
||||||
delay: Timer,
|
|
||||||
sound: Timer,
|
|
||||||
display: Display,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Chip8 {
|
|
||||||
fn default() -> Self {
|
|
||||||
Chip8 {
|
|
||||||
opcode: 0,
|
|
||||||
i: 0,
|
|
||||||
pc: 0x200, // Progrm counter starts at 0x200
|
|
||||||
sp: 0,
|
|
||||||
key: None,
|
|
||||||
v: [0; 16],
|
|
||||||
stack: [0; 16],
|
|
||||||
memory: [0; 4096],
|
|
||||||
delay: Default::default(),
|
|
||||||
sound: Default::default(),
|
|
||||||
display: Display::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Chip8 {
|
|
||||||
pub fn execute_cycle(&mut self) {
|
|
||||||
self.opcode = self.get_opcode();
|
|
||||||
println!("{:#x}", self.opcode);
|
|
||||||
self.handle_opcode();
|
|
||||||
|
|
||||||
self.delay.tick();
|
|
||||||
self.sound.tick();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_rom<P: AsRef<Path>>(&mut self, path: P) -> Result<(), io::Error> {
|
|
||||||
let mut file = File::open(path.as_ref())?;
|
|
||||||
let mut rom_buf: Vec<u8> = vec![];
|
|
||||||
file.read_to_end(&mut rom_buf)?;
|
|
||||||
|
|
||||||
for (i, byte) in rom_buf.iter().enumerate() {
|
|
||||||
self.memory[i + 0x200] = *byte;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_opcode(&self) -> u16 {
|
|
||||||
let pc = self.pc as usize;
|
|
||||||
|
|
||||||
((self.memory[pc] as u16) << 8) | self.memory[pc + 1] as u16
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_opcode(&mut self) {
|
|
||||||
// Given: 0xA2F0
|
|
||||||
let nib_1 = (self.opcode & 0xF000) >> 12; //0xA
|
|
||||||
let nib_2 = (self.opcode & 0x0F00) >> 8; // 0x2
|
|
||||||
let nib_3 = (self.opcode & 0x00F0) >> 4; // 0xF
|
|
||||||
let nib_4 = self.opcode & 0x000F; // 0x0
|
|
||||||
|
|
||||||
self.pc += 2;
|
|
||||||
|
|
||||||
// nib_ns are u16s so we waste 4 bytes here.
|
|
||||||
|
|
||||||
match (nib_1, nib_2, nib_3, nib_4) {
|
|
||||||
// CLS
|
|
||||||
(0x0, 0x0, 0xE, 0x0) => self.cls(),
|
|
||||||
// 00EE
|
|
||||||
(0x0, 0x0, 0xE, 0xE) => self.ret(),
|
|
||||||
// 1NNN
|
|
||||||
(0x1, _, _, _) => self.jmp_addr(nib_2, nib_3, nib_4),
|
|
||||||
// 2NNN
|
|
||||||
(0x2, _, _, _) => self.call_addr(nib_2, nib_3, nib_4),
|
|
||||||
// 3XKK
|
|
||||||
(0x3, _, _, _) => self.se_vx_byte(nib_2, nib_3, nib_4),
|
|
||||||
// 4xkk
|
|
||||||
(0x4, _, _, _) => self.sne_vx_byte(nib_2, nib_3, nib_4),
|
|
||||||
// 5xy0
|
|
||||||
(0x5, _, _, 0x0) => self.se_vx_vy(nib_2, nib_3),
|
|
||||||
// 6xkk
|
|
||||||
(0x6, _, _, _) => self.ld_vx_byte(nib_2, nib_3, nib_4),
|
|
||||||
// 7xkk
|
|
||||||
(0x7, _, _, _) => self.add_vx_byte(nib_2, nib_3, nib_4),
|
|
||||||
// 8xy0
|
|
||||||
(0x8, _, _, 0x0) => self.ld_vx_vy(nib_2, nib_3),
|
|
||||||
// 8xy1
|
|
||||||
(0x8, _, _, 0x1) => self.or_vx_vy(nib_2, nib_3),
|
|
||||||
// 8xy2
|
|
||||||
(0x8, _, _, 0x2) => self.and_vx_vy(nib_2, nib_3),
|
|
||||||
// 8xy3
|
|
||||||
(0x8, _, _, 0x3) => self.xor_vx_vy(nib_2, nib_3),
|
|
||||||
// 8xy4
|
|
||||||
(0x8, _, _, 0x4) => self.add_vx_vy(nib_2, nib_3),
|
|
||||||
// 8xy5
|
|
||||||
(0x8, _, _, 0x5) => self.sub_vx_vy(nib_2, nib_3),
|
|
||||||
// 8xy6
|
|
||||||
(0x8, _, _, 0x6) => self.shr_vx(nib_2),
|
|
||||||
// 8xy7
|
|
||||||
(0x8, _, _, 0x7) => self.subn_vx_vy(nib_2, nib_3),
|
|
||||||
// 8xyE
|
|
||||||
(0x8, _, _, 0xE) => self.shl_vx(nib_2),
|
|
||||||
// 9xy0
|
|
||||||
(0x9, _, _, 0x0) => self.sne_vx_vy(nib_2, nib_3),
|
|
||||||
// Annn
|
|
||||||
(0xA, _, _, _) => self.ld_i_addr(nib_2, nib_3, nib_4),
|
|
||||||
// Bnnn
|
|
||||||
(0xB, _, _, _) => self.jmp_v0_addr(nib_2, nib_3, nib_4),
|
|
||||||
// Cxkk
|
|
||||||
(0xC, _, _, _) => self.rnd_vx_byte(nib_2, nib_3, nib_4),
|
|
||||||
//Dxyn
|
|
||||||
(0xD, _, _, _) => self.drw_vx_vy_nib(nib_2, nib_3, nib_4),
|
|
||||||
// Ex9E
|
|
||||||
(0xE, _, 0x9, 0xE) => self.skp_vx(nib_2),
|
|
||||||
// ExA1
|
|
||||||
(0xE, _, 0xA, 0x1) => self.sknp_vx(nib_2),
|
|
||||||
// Fx07
|
|
||||||
(0xF, _, 0x0, 0x7) => self.ld_vx_dt(nib_2),
|
|
||||||
// Fx0A
|
|
||||||
(0xF, _, 0x0, 0xA) => self.ld_vx_k(nib_2),
|
|
||||||
// Fx15
|
|
||||||
(0xF, _, 0x1, 0x15) => self.ld_dt_vx(nib_2),
|
|
||||||
// Fx18
|
|
||||||
(0xF, _, 0x1, 0x8) => self.ld_st_vx(nib_2),
|
|
||||||
// Fx1E
|
|
||||||
(0xF, _, 0x1, 0xE) => self.add_i_vx(nib_2),
|
|
||||||
// Fx29
|
|
||||||
(0xF, _, 0x2, 0x9) => self.ld_f_vx(nib_2),
|
|
||||||
// Fx33
|
|
||||||
(0xF, _, 0x3, 0x3) => self.ld_b_vx(nib_2),
|
|
||||||
// Fx55
|
|
||||||
(0xF, _, 0x5, 0x5) => self.ld_i_vx(nib_2),
|
|
||||||
// Fx65
|
|
||||||
(0xF, _, 0x6, 0x5) => self.ld_vx_i(nib_2),
|
|
||||||
// General Case
|
|
||||||
_ => println!("UNIMPLEMENTED OPCODE: {:#x}", self.opcode),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cls(&mut self) {
|
|
||||||
// Clear the display
|
|
||||||
self.display.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn jmp_addr(&mut self, n_1: u16, n_2: u16, n_3: u16) {
|
|
||||||
// sets the program counter to addr (nnn)
|
|
||||||
self.pc = Self::convert_to_addr(n_1, n_2, n_3);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ret(&mut self) {
|
|
||||||
// sets the program counter to the addres at the top of the stack,
|
|
||||||
// then subtracts one (1) from the stack pointer
|
|
||||||
self.pc = self.stack[self.sp as usize];
|
|
||||||
self.sp -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn call_addr(&mut self, n_1: u16, n_2: u16, n_3: u16) {
|
|
||||||
// increments the stack pointer, then puts current pc on top of stack
|
|
||||||
// pc is then set to addr
|
|
||||||
self.sp += 1;
|
|
||||||
self.stack[self.sp as usize] = self.pc;
|
|
||||||
self.pc = Self::convert_to_addr(n_1, n_2, n_3);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn se_vx_byte(&mut self, x: u16, k_1: u16, k_2: u16) {
|
|
||||||
// compares Vx to kk. If they are equal, pc is incremented by 2
|
|
||||||
if self.v[x as usize] == Self::convert_to_byte(k_1, k_2) {
|
|
||||||
self.pc += 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sne_vx_byte(&mut self, x: u16, k_1: u16, k_2: u16) {
|
|
||||||
// compares Vx to kk. If they are **not** equal, pc is incremented by 2
|
|
||||||
if self.v[x as usize] != Self::convert_to_byte(k_1, k_2) {
|
|
||||||
self.pc += 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn se_vx_vy(&mut self, x: u16, y: u16) {
|
|
||||||
// compares Vx to Vy. If they are equal, pc is incremented by 2
|
|
||||||
if self.v[x as usize] == self.v[y as usize] {
|
|
||||||
self.pc += 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ld_vx_byte(&mut self, x: u16, k_1: u16, k_2: u16) {
|
|
||||||
// put value kk into Vx
|
|
||||||
self.v[x as usize] = Self::convert_to_byte(k_1, k_2);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_vx_byte(&mut self, x: u16, k_1: u16, k_2: u16) {
|
|
||||||
// calculate Vx + kk, then store it in Vx
|
|
||||||
let x = x as usize;
|
|
||||||
self.v[x] = self.v[x] + Self::convert_to_byte(k_1, k_2);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ld_vx_vy(&mut self, x: u16, y: u16) {
|
|
||||||
// store Vy in Vx
|
|
||||||
self.v[x as usize] = self.v[y as usize];
|
|
||||||
}
|
|
||||||
|
|
||||||
fn or_vx_vy(&mut self, x: u16, y: u16) {
|
|
||||||
// calc bitwise OR on Vx and Vy, then store in Vx
|
|
||||||
let x = x as usize;
|
|
||||||
self.v[x] = self.v[x] | self.v[y as usize];
|
|
||||||
}
|
|
||||||
|
|
||||||
fn and_vx_vy(&mut self, x: u16, y: u16) {
|
|
||||||
// calc bitwise AND on Vx and Vy, then store in Vx
|
|
||||||
let x = x as usize;
|
|
||||||
self.v[x] = self.v[x] & self.v[y as usize];
|
|
||||||
}
|
|
||||||
|
|
||||||
fn xor_vx_vy(&mut self, x: u16, y: u16) {
|
|
||||||
// calc bitwise XOR on Vx and Vy, then store in Vx
|
|
||||||
let x = x as usize;
|
|
||||||
self.v[x] = self.v[x] ^ self.v[y as usize];
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_vx_vy(&mut self, x: u16, y: u16) {
|
|
||||||
// add Vx and Vy, if result is greater than 8 bits
|
|
||||||
// set VF to 1, otherwise 0
|
|
||||||
// only the lowest 8 bits of result are stored in Vx
|
|
||||||
let x = x as usize;
|
|
||||||
|
|
||||||
let (res, did_overflow) = self.v[x].overflowing_add(self.v[y as usize]);
|
|
||||||
self.v[0xF as usize] = if did_overflow { 1 } else { 0 };
|
|
||||||
self.v[x] = res;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sub_vx_vy(&mut self, x: u16, y: u16) {
|
|
||||||
// subtract Vx and Vy, if Vx > Vy VF is set to 1, otherwise 0
|
|
||||||
// then set Vx to Vx - Vy
|
|
||||||
let vx = self.v[x as usize];
|
|
||||||
let vy = self.v[y as usize];
|
|
||||||
|
|
||||||
self.v[0xF as usize] = if vx > vy { 1 } else { 0 };
|
|
||||||
self.v[x as usize] = vx - vy;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn shr_vx(&mut self, x: u16) {
|
|
||||||
// if LSB is set to 1, set VF to 1, otherwise set VF to 0
|
|
||||||
// then shift Vx one to the right
|
|
||||||
let x = x as usize;
|
|
||||||
|
|
||||||
self.v[0xF as usize] = if (self.v[x] & 1) == 1 { 1 } else { 0 };
|
|
||||||
self.v[x] = self.v[x] >> 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn subn_vx_vy(&mut self, x: u16, y: u16) {
|
|
||||||
// subtract Vy and Vx, if Vy > Vx VF is set to 1, otherwise 0
|
|
||||||
// then set Vx = Vy - Vx
|
|
||||||
let vx = self.v[x as usize];
|
|
||||||
let vy = self.v[y as usize];
|
|
||||||
|
|
||||||
self.v[0xF as usize] = if vy > vx { 1 } else { 0 };
|
|
||||||
self.v[x as usize] = vy - vx;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn shl_vx(&mut self, x: u16) {
|
|
||||||
// if MSB is set to 1, set VF to 1, otherwise set VF to 0
|
|
||||||
// then shift Vx one to the left;
|
|
||||||
let x = x as usize;
|
|
||||||
|
|
||||||
self.v[0xF as usize] = if (self.v[x] & 0x80) == 1 { 1 } else { 0 };
|
|
||||||
self.v[x] = self.v[x] << 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sne_vx_vy(&mut self, x: u16, y: u16) {
|
|
||||||
// if Vx != vy program counter is increased by 2
|
|
||||||
if self.v[x as usize] != self.v[y as usize] {
|
|
||||||
self.pc += 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ld_i_addr(&mut self, n_1: u16, n_2: u16, n_3: u16) {
|
|
||||||
// set i to addr
|
|
||||||
self.i = Self::convert_to_addr(n_1, n_2, n_3);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn jmp_v0_addr(&mut self, n_1: u16, n_2: u16, n_3: u16) {
|
|
||||||
// set program counter to addr + V0
|
|
||||||
self.pc = Self::convert_to_addr(n_1, n_2, n_3) + self.v[0 as usize] as u16;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rnd_vx_byte(&mut self, x: u16, k_1: u16, k_2: u16) {
|
|
||||||
// generate a random number from 0 to 255
|
|
||||||
// AND with the value of kk, then store in Vx
|
|
||||||
|
|
||||||
self.v[x as usize] = rand::random::<u8>() & Self::convert_to_byte(k_1, k_2);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn drw_vx_vy_nib(&mut self, x: u16, y: u16, nib: u16) {
|
|
||||||
// read n bytes from memory starting from self.i
|
|
||||||
// then display them starting at (vx, vy)
|
|
||||||
let i = self.i as usize;
|
|
||||||
let _disp_x = self.v[x as usize];
|
|
||||||
let _disp_y = self.v[y as usize];
|
|
||||||
|
|
||||||
// There's Probably an off-by-one error here.
|
|
||||||
let _sprite_buf = &self.memory[i..=(i + nib as usize)];
|
|
||||||
let _display_buf = self.display.buf;
|
|
||||||
|
|
||||||
todo!("Finish implementing DRW");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn skp_vx(&mut self, x: u16) {
|
|
||||||
// if current key is the same as the on in Vx
|
|
||||||
// program counter is increased by 2
|
|
||||||
if let Some(key) = self.key {
|
|
||||||
if self.v[x as usize] == key {
|
|
||||||
self.pc += 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sknp_vx(&mut self, x: u16) {
|
|
||||||
// if current key is not the sameas the one in Vx
|
|
||||||
// increment the program counter by 2
|
|
||||||
match self.key {
|
|
||||||
Some(key) => {
|
|
||||||
if key != self.v[x as usize] {
|
|
||||||
self.pc += 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => self.pc += 2,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ld_vx_dt(&mut self, x: u16) {
|
|
||||||
// set Vx to be the value of the delay timer
|
|
||||||
self.v[x as usize] = self.delay.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ld_vx_k(&mut self, x: u16) {
|
|
||||||
// wait (blocking) until a key is pressed
|
|
||||||
// once pressed, store in Vx
|
|
||||||
|
|
||||||
match self.key {
|
|
||||||
Some(key) => self.v[x as usize] = key,
|
|
||||||
None => self.pc -= 2,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ld_dt_vx(&mut self, x: u16) {
|
|
||||||
// set delay timer to be value of Vx
|
|
||||||
self.delay.set(self.v[x as usize]);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ld_st_vx(&mut self, x: u16) {
|
|
||||||
// set sound timer to be value of Vx
|
|
||||||
self.delay.set(self.v[x as usize]);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_i_vx(&mut self, x: u16) {
|
|
||||||
// set I to be I + Vx
|
|
||||||
self.i = self.i + self.v[x as usize] as u16;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ld_f_vx(&mut self, _x: u16) {
|
|
||||||
todo!("Implement 0xF_29");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ld_b_vx(&mut self, x: u16) {
|
|
||||||
// take hundreds digit and place it at I
|
|
||||||
// take tens digit and place it at I + 1
|
|
||||||
// take ones digit and place it at I + 2
|
|
||||||
let i = self.i as usize;
|
|
||||||
let mut iter = Self::digits(self.v[x as usize]);
|
|
||||||
|
|
||||||
let ones = iter.next().unwrap(); // Ther has to at least be a ones lol
|
|
||||||
let tens = iter.next().unwrap_or(0);
|
|
||||||
let hundreds = iter.next().unwrap_or(0);
|
|
||||||
|
|
||||||
self.memory[i] = hundreds;
|
|
||||||
self.memory[i + 1] = tens;
|
|
||||||
self.memory[i + 2] = ones;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ld_i_vx(&mut self, x: u16) {
|
|
||||||
// copy values v0 -> Vx to memory starting at i
|
|
||||||
|
|
||||||
for n in 0..=(x as usize) {
|
|
||||||
self.memory[self.i as usize + n] = self.v[n];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ld_vx_i(&mut self, x: u16) {
|
|
||||||
// read what will be values of v0 -> vx from memory starting at i
|
|
||||||
let x = x as usize;
|
|
||||||
|
|
||||||
for n in 0..=x {
|
|
||||||
self.v[n] = self.memory[self.i as usize + n];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn convert_to_byte(k_1: u16, k_2: u16) -> u8 {
|
|
||||||
((k_1 as u8) << 4) | (k_2 as u8)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn convert_to_addr(n_1: u16, n_2: u16, n_3: u16) -> u16 {
|
|
||||||
(n_1 << 8) | (n_2 << 4) | n_3
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://stackoverflow.com/questions/41536479/how-do-i-split-an-integer-into-individual-digits
|
|
||||||
fn digits(mut num: u8) -> impl Iterator<Item = u8> {
|
|
||||||
let mut divisor = 1;
|
|
||||||
while num >= divisor * 10 {
|
|
||||||
divisor *= 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::iter::from_fn(move || {
|
|
||||||
if divisor == 0 {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
let v = num / divisor;
|
|
||||||
num %= divisor;
|
|
||||||
divisor /= 10;
|
|
||||||
Some(v)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
struct Display {
|
|
||||||
buf: [u8; SCREEN_SIZE],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display {
|
|
||||||
pub fn clear(&mut self) {
|
|
||||||
self.buf = [0; SCREEN_SIZE]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Display {
|
|
||||||
fn default() -> Self {
|
|
||||||
Display {
|
|
||||||
buf: [0; SCREEN_SIZE],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
struct Timer {
|
|
||||||
remaining: u8,
|
|
||||||
enabled: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Timer {
|
|
||||||
fn default() -> Self {
|
|
||||||
Timer {
|
|
||||||
remaining: 0,
|
|
||||||
enabled: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Timer {
|
|
||||||
pub fn set(&mut self, secs: u8) {
|
|
||||||
self.remaining = secs;
|
|
||||||
self.enabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(&self) -> u8 {
|
|
||||||
self.remaining
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tick(&mut self) {
|
|
||||||
if self.enabled {
|
|
||||||
self.remaining -= 1;
|
|
||||||
|
|
||||||
if self.remaining == 0 {
|
|
||||||
println!("Beep!");
|
|
||||||
self.enabled = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remember that the digits are yielded in the oposite order
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub struct Digits {
|
|
||||||
num: u8,
|
|
||||||
flag: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<u8> for Digits {
|
|
||||||
fn from(num: u8) -> Self {
|
|
||||||
Digits { num, flag: true }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Iterator for Digits {
|
|
||||||
type Item = u8;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<u8> {
|
|
||||||
if self.num >= 10 {
|
|
||||||
// There is something next
|
|
||||||
let tmp = self.num;
|
|
||||||
self.num = self.num / 10;
|
|
||||||
|
|
||||||
Some(tmp % 10)
|
|
||||||
} else {
|
|
||||||
if self.flag {
|
|
||||||
self.flag = false;
|
|
||||||
Some(self.num % 10)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut chip8: Chip8 = Default::default();
|
let mut chip8: Chip8 = Default::default();
|
||||||
|
|
||||||
|
@ -539,6 +14,6 @@ fn main() {
|
||||||
loop {
|
loop {
|
||||||
chip8.execute_cycle();
|
chip8.execute_cycle();
|
||||||
|
|
||||||
std::thread::sleep(Duration::from_millis(1000));
|
std::thread::sleep(Duration::from_millis(300));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct Display {
|
||||||
|
pub buf: [u8; Self::SCREEN_SIZE],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display {
|
||||||
|
const SCREEN_SIZE: usize = 64 * 32;
|
||||||
|
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.buf = [0; Self::SCREEN_SIZE]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Display {
|
||||||
|
fn default() -> Self {
|
||||||
|
Display {
|
||||||
|
buf: [0; Self::SCREEN_SIZE],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct Timer {
|
||||||
|
remaining: u8,
|
||||||
|
enabled: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Timer {
|
||||||
|
fn default() -> Self {
|
||||||
|
Timer {
|
||||||
|
remaining: 0,
|
||||||
|
enabled: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Timer {
|
||||||
|
pub fn set(&mut self, secs: u8) {
|
||||||
|
self.remaining = secs;
|
||||||
|
self.enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self) -> u8 {
|
||||||
|
self.remaining
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tick(&mut self) {
|
||||||
|
if self.enabled {
|
||||||
|
self.remaining -= 1;
|
||||||
|
|
||||||
|
if self.remaining == 0 {
|
||||||
|
println!("Beep!");
|
||||||
|
self.enabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct Keypad {
|
||||||
|
keys: [bool; 16],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Keypad {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self { keys: [false; 16] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Keypad {
|
||||||
|
pub fn get_pressed(&self) -> Option<u8> {
|
||||||
|
let mut found: Option<u8> = None;
|
||||||
|
|
||||||
|
for (index, key) in self.keys.iter().enumerate() {
|
||||||
|
if *key == true {
|
||||||
|
found = Some(index as u8);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
found
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue