Newer
Older
use std::io::{Cursor, Read};
use crate::{chip8::Chip8, util::random};
#[cfg(feature = "wasm")]
use wasm_bindgen::prelude::*;
pub const DISPLAY_WIDTH: usize = 64;
pub const DISPLAY_HEIGHT: usize = 32;
const RAM_SIZE: usize = 4096;
const STACK_SIZE: usize = 16;
const REGISTERS_SIZE: usize = 16;
const KEYS_SIZE: usize = 16;
/// The starting address for the ROM loading, should be
const ROM_START: usize = 0x200;
static FONT_SET: [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
];
ram: [u8; RAM_SIZE],
vram: [u8; DISPLAY_WIDTH * DISPLAY_HEIGHT],
pc: u16,
i: u16,
sp: u8,
dt: u8,
st: u8,
impl Chip8 for Chip8Neo {
fn name(&self) -> &str {
"neo"
self.vram = [0u8; DISPLAY_WIDTH * DISPLAY_HEIGHT];
self.stack = [0u16; STACK_SIZE];
self.pc = ROM_START as u16;
self.i = 0x0;
self.sp = 0x0;
self.dt = 0x0;
self.st = 0x0;
self.load_default_font();
}
self.ram = [0u8; RAM_SIZE];
self.reset();
}
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
fn beep(&self) -> bool {
self.st > 0
}
fn pc(&self) -> u16 {
self.pc
}
fn sp(&self) -> u8 {
self.sp
}
fn ram(&self) -> Vec<u8> {
self.ram.to_vec()
}
fn vram(&self) -> Vec<u8> {
self.vram.to_vec()
}
fn get_state(&self) -> Vec<u8> {
let mut buffer: Vec<u8> = Vec::new();
buffer.extend(self.ram.iter());
buffer.extend(self.vram.iter());
buffer.extend(self.stack.map(|v| v.to_le_bytes()).iter().flatten());
buffer.extend(self.regs.iter());
buffer.extend(self.pc.to_le_bytes().iter());
buffer.extend(self.i.to_le_bytes().iter());
buffer.extend(self.sp.to_le_bytes().iter());
buffer.extend(self.dt.to_le_bytes().iter());
buffer.extend(self.st.to_le_bytes().iter());
buffer.extend(self.keys.map(|v| v as u8).iter());
buffer.extend(self.last_key.to_le_bytes().iter());
buffer
}
fn set_state(&mut self, state: &[u8]) {
let mut u8_buffer = [0u8; 1];
let mut u16_buffer = [0u8; 2];
let mut regs_buffer = [0u8; REGISTERS_SIZE * 2];
let mut keys_buffer = [0u8; KEYS_SIZE];
let mut cursor = Cursor::new(state.to_vec());
cursor.read_exact(&mut self.ram).unwrap();
cursor.read_exact(&mut self.vram).unwrap();
cursor.read_exact(&mut regs_buffer).unwrap();
self.stack.clone_from_slice(
regs_buffer
.chunks(2)
.map(|v| {
u16_buffer.clone_from_slice(&v[0..2]);
u16::from_le_bytes(u16_buffer)
})
.collect::<Vec<u16>>()
.as_slice(),
);
cursor.read_exact(&mut self.regs).unwrap();
cursor.read_exact(&mut u16_buffer).unwrap();
self.pc = u16::from_le_bytes(u16_buffer);
cursor.read_exact(&mut u16_buffer).unwrap();
self.i = u16::from_le_bytes(u16_buffer);
cursor.read_exact(&mut u8_buffer).unwrap();
self.sp = u8::from_le_bytes(u8_buffer);
cursor.read_exact(&mut u8_buffer).unwrap();
self.dt = u8::from_le_bytes(u8_buffer);
cursor.read_exact(&mut u8_buffer).unwrap();
self.st = u8::from_le_bytes(u8_buffer);
cursor.read_exact(&mut keys_buffer).unwrap();
self.keys.clone_from_slice(
keys_buffer
.map(|v| if v == 1 { true } else { false })
.iter()
.as_slice(),
);
cursor.read_exact(&mut u8_buffer).unwrap();
self.last_key = u8::from_le_bytes(u8_buffer);
}
fn load_rom(&mut self, rom: &[u8]) {
self.ram[ROM_START..ROM_START + rom.len()].clone_from_slice(&rom);
}
// fetches the current instruction and increments
// the PC (program counter) accordingly
let instruction =
(self.ram[self.pc as usize] as u16) << 8 | self.ram[self.pc as usize + 1] as u16;
let opcode = instruction & 0xf000;
let address = instruction & 0x0fff;
let x = ((instruction & 0x0f00) >> 8) as usize;
let y = ((instruction & 0x00f0) >> 4) as usize;
let nibble = (instruction & 0x000f) as u8;
let byte = (instruction & 0x00ff) as u8;
match opcode {
0x0000 => match byte {
0xe0 => self.clear_screen(),
0xee => {
self.sp -= 1;
self.pc = self.stack[self.sp as usize];
}
_ => panic!(
"unimplemented instruction 0x0000, instruction 0x{:04x}",
instruction
),
},
0x1000 => self.pc = address,
0x2000 => {
self.stack[self.sp as usize] = self.pc;
self.sp += 1;
self.pc = address;
}
0x3000 => self.pc += if self.regs[x] == byte { 2 } else { 0 },
0x4000 => self.pc += if self.regs[x] != byte { 2 } else { 0 },
0x5000 => self.pc += if self.regs[x] == self.regs[y] { 2 } else { 0 },
0x6000 => self.regs[x] = byte,
0x7000 => self.regs[x] = self.regs[x].wrapping_add(byte),
0x0 => self.regs[x] = self.regs[y],
0x1 => self.regs[x] |= self.regs[y],
0x2 => self.regs[x] &= self.regs[y],
0x3 => self.regs[x] ^= self.regs[y],
0x4 => {
let (result, overflow) = self.regs[x].overflowing_add(self.regs[y]);
self.regs[x] = result;
self.regs[0xf] = overflow as u8;
}
0x5 => {
self.regs[0xf] = (self.regs[x] > self.regs[y]) as u8;
self.regs[x] = self.regs[x].wrapping_sub(self.regs[y]);
}
0x6 => {
self.regs[0xf] = self.regs[x] & 0x01;
self.regs[x] >>= 1;
}
0x7 => {
self.regs[0xf] = (self.regs[y] > self.regs[x]) as u8;
self.regs[x] = self.regs[y].wrapping_sub(self.regs[x]);
}
0xe => {
self.regs[0xf] = (self.regs[x] & 0x80) >> 7;
self.regs[x] <<= 1;
}
_ => panic!(
"unimplemented instruction 0x8000, instruction 0x{:04x}",
instruction
),
0x9000 => self.pc += if self.regs[x] != self.regs[y] { 2 } else { 0 },
0xb000 => self.pc = address + self.regs[0x0] as u16,
self.regs[x] as usize,
self.regs[y] as usize,
0x9e => {
let key = self.regs[x] as usize;
self.pc += if self.keys[key] { 2 } else { 0 }
}
0xa1 => {
let key = self.regs[x] as usize;
self.pc += if !self.keys[key] { 2 } else { 0 }
}
"unimplemented instruction 0xe000, instruction 0x{:04x}",
instruction
),
},
0xf000 => match byte {
0x07 => self.regs[x] = self.dt,
0x0a => {
if self.keys[self.last_key as usize] {
self.regs[x] = self.last_key;
} else {
self.pc -= 2
}
}
0x15 => self.dt = self.regs[x],
0x18 => self.st = self.regs[x],
0x1e => self.i = self.i.saturating_add(self.regs[x] as u16),
0x33 => {
self.ram[self.i as usize] = self.regs[x] / 100;
self.ram[self.i as usize + 1] = (self.regs[x] / 10) % 10;
self.ram[self.i as usize + 2] = self.regs[x] % 10;
}
0x55 => self.ram[self.i as usize..self.i as usize + x + 1]
.clone_from_slice(&self.regs[0..x + 1]),
0x65 => self.regs[0..x + 1]
.clone_from_slice(&self.ram[self.i as usize..self.i as usize + x + 1]),
"unimplemented instruction 0xf000, instruction 0x{:04x}",
instruction
),
},
"unimplemented opcode 0x{:04x}, instruction 0x{:04x}",
self.dt = self.dt.saturating_sub(1)
}
self.st = self.st.saturating_sub(1)
}
fn key_press(&mut self, key: u8) {
fn key_lift(&mut self, key: u8) {
#[cfg_attr(feature = "wasm", wasm_bindgen(constructor))]
pub fn new() -> Chip8Neo {
let mut chip8 = Chip8Neo {
ram: [0u8; RAM_SIZE],
vram: [0u8; DISPLAY_WIDTH * DISPLAY_HEIGHT],
stack: [0u16; STACK_SIZE],
regs: [0u8; REGISTERS_SIZE],
pc: ROM_START as u16,
i: 0x0,
sp: 0x0,
dt: 0x0,
st: 0x0,
keys: [false; KEYS_SIZE],
last_key: 0x0,
};
chip8.load_default_font();
chip8
}
fn load_font(&mut self, position: usize, font_set: &[u8]) {
self.ram[position..position + font_set.len()].clone_from_slice(&font_set);
}
fn load_default_font(&mut self) {
self.load_font(0, &FONT_SET);
}
fn clear_screen(&mut self) {
self.vram = [0u8; DISPLAY_WIDTH * DISPLAY_HEIGHT];
}
fn draw_sprite(&mut self, x0: usize, y0: usize, height: usize) {
for y in 0..height {
let line_byte = self.ram[(self.i as usize + y)];
for x in 0..8 {
let yf = (y0 + y) % DISPLAY_HEIGHT;
let xf = (x0 + x) % DISPLAY_WIDTH;
if line_byte & (0x80 >> x) == 0 {
continue;
}
let addr = yf * DISPLAY_WIDTH + xf;
if self.vram[addr] == 1 {
}
self.vram[addr] ^= 1
}
}
}
#[cfg_attr(feature = "wasm", wasm_bindgen)]
impl Chip8Neo {
pub fn load_rom_ws(&mut self, rom: &[u8]) {
self.load_rom(rom)
}
pub fn reset_ws(&mut self) {
self.reset()
}
pub fn reset_hard_ws(&mut self) {
self.reset_hard()
}
pub fn beep_ws(&self) -> bool {
self.beep()
}
pub fn vram_ws(&self) -> Vec<u8> {
self.vram()
}
pub fn clock_ws(&mut self) {
self.clock()
}
pub fn clock_dt_ws(&mut self) {
self.clock_dt()
}
pub fn clock_st_ws(&mut self) {
self.clock_st()
}
pub fn key_press_ws(&mut self, key: u8) {
self.key_press(key)
}
pub fn key_lift_ws(&mut self, key: u8) {
self.key_lift(key)
}
impl Default for Chip8Neo {
fn default() -> Chip8Neo {
Chip8Neo::new()
}
}