2024-03-02 13:00:57 +05:30
|
|
|
use std::ops::SubAssign;
|
|
|
|
use std::sync::atomic::{AtomicU16, Ordering};
|
|
|
|
use std::sync::{Arc, Mutex, RwLock};
|
2024-03-03 11:48:07 +05:30
|
|
|
use std::thread;
|
2024-03-02 13:00:57 +05:30
|
|
|
use std::thread::{JoinHandle, sleep};
|
|
|
|
use std::time::Duration;
|
2024-03-03 11:48:07 +05:30
|
|
|
use crate::device::instruction::Instruction;
|
2024-03-02 13:00:57 +05:30
|
|
|
use crate::device::timer::Timer;
|
|
|
|
|
|
|
|
|
|
|
|
pub struct Device {
|
|
|
|
pub registers: RegisterFile,
|
|
|
|
pub memory: Box<[u8; Self::DEVICE_MEMORY_SIZE]>,
|
|
|
|
pub timer: Timer,
|
2024-03-03 11:48:07 +05:30
|
|
|
pub stack: Vec<u16>,
|
2024-03-04 09:40:33 +05:30
|
|
|
pub frame_buffer: Arc<Mutex<Box<[bool;64*32]>>>
|
2024-03-02 13:00:57 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
impl Device {
|
2024-03-03 11:48:07 +05:30
|
|
|
pub const DEVICE_MEMORY_SIZE: usize = 1 << 12;
|
|
|
|
pub const FRAME_BUFFER_WIDTH: usize = 64;
|
|
|
|
pub const FRAME_BUFFER_HEIGHT: usize = 32;
|
|
|
|
pub const FRAME_BUFFER_SIZE: usize = Self::FRAME_BUFFER_WIDTH*Self::FRAME_BUFFER_HEIGHT;
|
2024-03-04 09:40:33 +05:30
|
|
|
pub fn new(timer: Timer, fb: Arc<Mutex<Box<[bool;Device::FRAME_BUFFER_SIZE]>>>) -> Device {
|
2024-03-02 13:00:57 +05:30
|
|
|
let memory = vec![0u8; Self::DEVICE_MEMORY_SIZE].into_boxed_slice().try_into().unwrap();
|
|
|
|
log::trace!("Successfully initiated device memory");
|
|
|
|
Device {
|
|
|
|
registers: RegisterFile::new(),
|
|
|
|
memory,
|
2024-03-03 11:48:07 +05:30
|
|
|
frame_buffer: fb,
|
|
|
|
stack: Vec::with_capacity(16),
|
2024-03-02 13:00:57 +05:30
|
|
|
timer,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-03-03 11:48:07 +05:30
|
|
|
|
|
|
|
impl Device {
|
|
|
|
const DEFAULT_FONT: [u8; 5 * 16] = [
|
2024-03-02 13:00:57 +05:30
|
|
|
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
|
|
|
|
];
|
2024-03-03 11:48:07 +05:30
|
|
|
const FONT_DEFAULT_MEM_LOCATION_START: usize = 0x50;
|
|
|
|
const FONT_DEFAULT_MEM_LOCATION_END: usize = 0x9F;
|
|
|
|
const ROM_START: usize = 0x200;
|
|
|
|
pub fn cycle(&mut self) {
|
|
|
|
let pc = self.registers.pc as usize;
|
|
|
|
let instr_slice = self.memory.get(pc..pc + 2).expect("Failed to get memory");
|
|
|
|
self.registers.pc += 2;
|
2024-03-03 12:53:33 +05:30
|
|
|
|
2024-03-03 11:48:07 +05:30
|
|
|
let instruction = Instruction::decode_instruction(instr_slice);
|
|
|
|
self.execute_instruction(instruction);
|
2024-03-03 12:53:33 +05:30
|
|
|
|
2024-03-03 14:12:54 +05:30
|
|
|
|
2024-03-03 12:53:33 +05:30
|
|
|
}
|
2024-03-04 09:40:33 +05:30
|
|
|
/// convert the 2 indices into one
|
|
|
|
fn get_framebuffer_index(x:usize,y:usize)->usize{
|
2024-03-03 12:53:33 +05:30
|
|
|
y*Self::FRAME_BUFFER_WIDTH + x
|
2024-03-03 11:48:07 +05:30
|
|
|
}
|
|
|
|
pub fn execute_instruction(&mut self, instruction: Instruction) {
|
|
|
|
// thread::sleep(Duration::from_millis(250));
|
|
|
|
log::trace!("Executing {:?}, {:?}",&instruction,&self.registers);
|
|
|
|
match instruction{
|
|
|
|
Instruction::PassThrough => {
|
|
|
|
log::info!("Executing passthrough");
|
|
|
|
}
|
|
|
|
Instruction::ClearScreen => {
|
2024-03-03 12:53:33 +05:30
|
|
|
let mut frame_buffer = self.frame_buffer.lock().expect("Failed to grab framebuffer for drawing");
|
|
|
|
for pixel in frame_buffer.iter_mut(){
|
2024-03-04 09:40:33 +05:30
|
|
|
*pixel = false;
|
2024-03-03 12:53:33 +05:30
|
|
|
}
|
2024-03-03 14:12:54 +05:30
|
|
|
log::trace!("ClearScreen")
|
2024-03-03 11:48:07 +05:30
|
|
|
}
|
|
|
|
Instruction::JumpTo(new_pc) => {
|
2024-03-03 14:12:54 +05:30
|
|
|
// hint that we're jumping back to self
|
2024-03-03 11:48:07 +05:30
|
|
|
self.registers.pc = new_pc;
|
|
|
|
}
|
|
|
|
Instruction::SetRegister(reg_location, value) => {
|
|
|
|
self.registers.v[reg_location] = value;
|
|
|
|
}
|
|
|
|
Instruction::AddValueToRegister(reg_location, value) => {
|
|
|
|
self.registers.v[reg_location] += value;
|
|
|
|
}
|
|
|
|
Instruction::SetIndex(value) => {
|
|
|
|
self.registers.i = value;
|
|
|
|
}
|
2024-03-03 12:53:33 +05:30
|
|
|
Instruction::Draw(regx,regy, n) => {
|
|
|
|
let x = self.registers.v[regx] as usize;
|
|
|
|
let y = self.registers.v[regy] as usize;
|
2024-03-04 09:40:33 +05:30
|
|
|
let toggle_state = self.draw_sprite_at_location(x, y, n);
|
|
|
|
self.set_flag_register(toggle_state);
|
2024-03-04 18:03:56 +05:30
|
|
|
},
|
2024-03-04 19:46:15 +05:30
|
|
|
Instruction::JumpAndLink(jump_location) => {
|
|
|
|
self.stack.push(self.registers.pc);
|
|
|
|
self.registers.pc = jump_location;
|
2024-03-04 09:40:33 +05:30
|
|
|
}
|
2024-03-04 19:46:15 +05:30
|
|
|
Instruction::ReturnFromProcedure =>{
|
|
|
|
let old_pc = self.stack.pop().expect("Expected value on stack pop");
|
|
|
|
self.registers.pc = old_pc;
|
|
|
|
}
|
|
|
|
|
|
|
|
Instruction::ConditionalEqSkipNext(_, _) => {}
|
|
|
|
Instruction::ConditionalInEqSkipNext(_, _) => {}
|
|
|
|
Instruction::ConditionalEqRegisterSkipNext(_, _) => {}
|
|
|
|
Instruction::ConditionalInEqRegisterSkipNext(_, _) => {}
|
|
|
|
Instruction::JumpWithOffset(_, _) => {}
|
|
|
|
Instruction::RandomOr(_, _) => {}
|
|
|
|
Instruction::SkipIfKeyPressed(_) => {}
|
|
|
|
Instruction::SkipIfKeyNotPressed(_) => {}
|
2024-03-04 20:14:43 +05:30
|
|
|
Instruction::Set(x, y) => {
|
|
|
|
self.registers.v[x] = self.registers.v[y];
|
|
|
|
}
|
|
|
|
Instruction::Or(x, y) => {
|
|
|
|
self.registers.v[x] |= self.registers.v[y];
|
|
|
|
}
|
|
|
|
Instruction::And(x,y) => {
|
|
|
|
self.registers.v[x] &= self.registers.v[y];
|
|
|
|
}
|
|
|
|
Instruction::Xor(x,y) => {
|
|
|
|
self.registers.v[x] ^= self.registers.v[y];
|
|
|
|
}
|
|
|
|
Instruction::Add(x,y) => {
|
|
|
|
let left = self.registers.v[x];
|
|
|
|
let (wrapped_addition_result, is_overflow) = left.overflowing_add(self.registers.v[y]);
|
|
|
|
self.registers.v[x] = wrapped_addition_result;
|
|
|
|
self.set_flag_register(is_overflow);
|
|
|
|
}
|
|
|
|
Instruction::Sub(x,y) => {
|
|
|
|
let left = self.registers.v[x];
|
|
|
|
let (wrapped_subtraction_result, is_overflow) = left.overflowing_sub(self.registers.v[y]);
|
|
|
|
self.registers.v[x] = wrapped_subtraction_result;
|
|
|
|
self.set_flag_register(is_overflow);
|
|
|
|
}
|
2024-03-04 19:46:15 +05:30
|
|
|
Instruction::RShift(_, _) => {}
|
2024-03-04 20:14:43 +05:30
|
|
|
Instruction::RSub(x, y) => {
|
|
|
|
let left = self.registers.v[y];
|
|
|
|
let (wrapped_subtraction_result, is_overflow) = left.overflowing_sub(self.registers.v[x]);
|
|
|
|
self.registers.v[x] = wrapped_subtraction_result;
|
|
|
|
self.set_flag_register(is_overflow);
|
|
|
|
}
|
2024-03-04 19:46:15 +05:30
|
|
|
Instruction::LShift(_, _) => {}
|
2024-03-04 09:40:33 +05:30
|
|
|
};
|
|
|
|
}
|
|
|
|
///
|
|
|
|
/// Draw a sprite at location at (x,y) for n pixels long and 8 pixels wide.
|
|
|
|
/// Returns whether any pixel was toggled
|
|
|
|
fn draw_sprite_at_location(&mut self, x: usize, y: usize, n: u8)-> bool {
|
|
|
|
let mut frame_buffer = self.frame_buffer.lock().expect("Failed to grab framebuffer for drawing");
|
2024-03-03 12:53:33 +05:30
|
|
|
|
2024-03-04 09:40:33 +05:30
|
|
|
let mut is_pixel_toggled_off = false;
|
|
|
|
for i in 0..n as usize {
|
|
|
|
let index = Self::get_framebuffer_index(x, y + i);
|
|
|
|
let slice_from_memory = self.memory[self.registers.i as usize + i];
|
2024-03-03 12:53:33 +05:30
|
|
|
|
2024-03-04 09:40:33 +05:30
|
|
|
for bit_index in (0..8).rev() {
|
|
|
|
// if i'm going to the next line, stop
|
|
|
|
if Self::get_framebuffer_index(0, y + 1) == index {
|
|
|
|
break;
|
2024-03-03 12:53:33 +05:30
|
|
|
}
|
2024-03-04 09:40:33 +05:30
|
|
|
let bit_is_true = (slice_from_memory & (1 << bit_index)) == (1<<bit_index) ;
|
|
|
|
|
|
|
|
// if the pixel is going to be toggled false, set this flag bit to true
|
2024-03-04 18:03:56 +05:30
|
|
|
if frame_buffer[index + (7 - bit_index)] && (bit_is_true) { is_pixel_toggled_off = true;}
|
2024-03-04 09:40:33 +05:30
|
|
|
frame_buffer[index + (7 - bit_index)] = frame_buffer[index + (7 - bit_index)] ^ (bit_is_true);
|
2024-03-03 11:48:07 +05:30
|
|
|
}
|
2024-03-04 09:40:33 +05:30
|
|
|
}
|
|
|
|
is_pixel_toggled_off
|
|
|
|
}
|
|
|
|
fn set_flag_register(&mut self, x:bool){
|
|
|
|
self.registers.v[0xf] = if x {1} else { 0 }
|
2024-03-02 13:00:57 +05:30
|
|
|
}
|
|
|
|
|
2024-03-03 11:48:07 +05:30
|
|
|
pub fn set_default_font(&mut self) {
|
2024-03-02 15:55:27 +05:30
|
|
|
log::info!("Loaded default font from memory");
|
2024-03-02 13:00:57 +05:30
|
|
|
self.memory[Self::FONT_DEFAULT_MEM_LOCATION_START..=Self::FONT_DEFAULT_MEM_LOCATION_END].copy_from_slice(&Self::DEFAULT_FONT);
|
|
|
|
}
|
2024-03-02 15:55:27 +05:30
|
|
|
/// load a rom from bytes
|
2024-03-03 11:48:07 +05:30
|
|
|
pub fn load_rom(&mut self, rom: &[u8]) {
|
2024-03-02 15:55:27 +05:30
|
|
|
log::info!("Loaded ROM from memory");
|
|
|
|
self.memory[Self::ROM_START..].copy_from_slice(rom);
|
|
|
|
}
|
2024-03-02 13:00:57 +05:30
|
|
|
}
|
2024-03-03 11:48:07 +05:30
|
|
|
|
|
|
|
impl Drop for Device {
|
2024-03-02 13:00:57 +05:30
|
|
|
fn drop(&mut self) {
|
|
|
|
self.timer.send_stop_signal()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-03 11:48:07 +05:30
|
|
|
#[derive(Debug)]
|
2024-03-02 13:00:57 +05:30
|
|
|
pub struct RegisterFile {
|
|
|
|
pub v: [u8; 0x10],
|
2024-03-02 15:55:27 +05:30
|
|
|
/// program counter - only u12 technically.
|
2024-03-02 13:00:57 +05:30
|
|
|
pub pc: u16,
|
|
|
|
/// stack pointer
|
|
|
|
pub i: u16,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl RegisterFile {
|
2024-03-03 11:48:07 +05:30
|
|
|
pub const DEFAULT_PC_VALUE: u16 = Device::ROM_START as u16;
|
2024-03-02 13:00:57 +05:30
|
|
|
pub fn new() -> RegisterFile {
|
|
|
|
RegisterFile {
|
|
|
|
v: [0; 0x10],
|
2024-03-02 15:55:27 +05:30
|
|
|
pc: Self::DEFAULT_PC_VALUE,
|
2024-03-02 13:00:57 +05:30
|
|
|
i: 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|