[cpu] Implement remaining instructions
This commit is contained in:
@@ -71,14 +71,13 @@ impl Device {
|
|||||||
self.registers.pc += 2;
|
self.registers.pc += 2;
|
||||||
|
|
||||||
let instruction = Instruction::decode_instruction(instr_slice);
|
let instruction = Instruction::decode_instruction(instr_slice);
|
||||||
self.execute_instruction(instruction);
|
self.execute_instruction(instruction)
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
/// convert the 2 indices into one
|
/// convert the 2 indices into one
|
||||||
fn get_framebuffer_index(x: usize, y: usize) -> usize {
|
fn get_framebuffer_index(x: usize, y: usize) -> usize {
|
||||||
y * Self::FRAME_BUFFER_WIDTH + x
|
y * Self::FRAME_BUFFER_WIDTH + x
|
||||||
}
|
}
|
||||||
pub fn execute_instruction(&mut self, instruction: Instruction) {
|
pub fn execute_instruction(&mut self, instruction: Instruction) -> EmulatorResult<()> {
|
||||||
// thread::sleep(Duration::from_millis(250));
|
// thread::sleep(Duration::from_millis(250));
|
||||||
log::trace!("Executing {:?}, {:?}",&instruction,&self.registers);
|
log::trace!("Executing {:?}, {:?}",&instruction,&self.registers);
|
||||||
match instruction {
|
match instruction {
|
||||||
@@ -142,7 +141,7 @@ impl Device {
|
|||||||
}
|
}
|
||||||
Instruction::JumpWithOffset(x, num) => {
|
Instruction::JumpWithOffset(x, num) => {
|
||||||
let regnum = if self.super_chip8_mode { x } else { 0 };
|
let regnum = if self.super_chip8_mode { x } else { 0 };
|
||||||
let new_pc = self.registers.v[regnum] as u16 + num ;
|
let new_pc = self.registers.v[regnum] as u16 + num;
|
||||||
self.registers.pc = new_pc;
|
self.registers.pc = new_pc;
|
||||||
}
|
}
|
||||||
Instruction::RandomAnd(dest, n) => {
|
Instruction::RandomAnd(dest, n) => {
|
||||||
@@ -191,25 +190,92 @@ impl Device {
|
|||||||
self.set_flag_register(is_overflow);
|
self.set_flag_register(is_overflow);
|
||||||
}
|
}
|
||||||
Instruction::RShift(x, y) => {
|
Instruction::RShift(x, y) => {
|
||||||
if(self.super_chip8_mode){
|
if (self.super_chip8_mode) {
|
||||||
self.registers.v[x] = self.registers.v[y];
|
self.registers.v[x] = self.registers.v[y];
|
||||||
}
|
}
|
||||||
let (shift_res,did_overflow) = self.registers.v[x].overflowing_shr(1);
|
let (shift_res, did_overflow) = self.registers.v[x].overflowing_shr(1);
|
||||||
self.registers.v[x] = shift_res;
|
self.registers.v[x] = shift_res;
|
||||||
self.set_flag_register(did_overflow);
|
self.set_flag_register(did_overflow);
|
||||||
}
|
}
|
||||||
Instruction::LShift(x, y) => {
|
Instruction::LShift(x, y) => {
|
||||||
if(self.super_chip8_mode){
|
if (self.super_chip8_mode) {
|
||||||
self.registers.v[x] = self.registers.v[y];
|
self.registers.v[x] = self.registers.v[y];
|
||||||
}
|
}
|
||||||
let (shift_res,did_overflow) = self.registers.v[x].overflowing_shl(1);
|
let (shift_res, did_overflow) = self.registers.v[x].overflowing_shl(1);
|
||||||
self.registers.v[x] = shift_res;
|
self.registers.v[x] = shift_res;
|
||||||
self.set_flag_register(did_overflow);
|
self.set_flag_register(did_overflow);
|
||||||
},
|
}
|
||||||
_=>{
|
|
||||||
todo!()
|
Instruction::FetchDelayTimer(x) => {
|
||||||
|
let timer_left = self.timer.poll_value()?;
|
||||||
|
self.registers.v[x] = timer_left
|
||||||
|
}
|
||||||
|
Instruction::SetDelayTimer(x) => {
|
||||||
|
let delay_timer_val = self.registers.v[x];
|
||||||
|
self.timer.try_set_timer(delay_timer_val)?;
|
||||||
|
}
|
||||||
|
Instruction::SetSoundTimer(_) => {
|
||||||
|
log::warn!("Sound unimplemented, instruction {:?}",instruction);
|
||||||
|
}
|
||||||
|
Instruction::AddToIndex(x) => {
|
||||||
|
let reg_value = self.registers.v[x];
|
||||||
|
let index_original = self.registers.i;
|
||||||
|
// newer instruction set requires wrapping on 12 bit overflow, and setting vf
|
||||||
|
let addn_res = if (self.super_chip8_mode) {
|
||||||
|
let overflowing = (reg_value as u16 + index_original) >= 0x1000;
|
||||||
|
self.set_flag_register(overflowing);
|
||||||
|
(reg_value as u16 + index_original) % 0x1000
|
||||||
|
} else {
|
||||||
|
reg_value as u16 + index_original
|
||||||
|
};
|
||||||
|
self.registers.i = addn_res;
|
||||||
|
}
|
||||||
|
Instruction::GetKey(x) => {
|
||||||
|
let key_expected = self.registers.v[x];
|
||||||
|
if !self.device_keyboard.query_key_down(key_expected) {
|
||||||
|
self.registers.pc -= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Instruction::SetIndexToFontCharacter(x) => {
|
||||||
|
let requested_char = self.registers.v[x];
|
||||||
|
// TODO extract 5 to constant
|
||||||
|
let font_address = Self::FONT_DEFAULT_MEM_LOCATION_START as u16 + 5 * requested_char as u16;
|
||||||
|
self.registers.i = font_address;
|
||||||
|
}
|
||||||
|
Instruction::DoBCDConversion(x) => {
|
||||||
|
let mut binary_value_to_decode_temp = self.registers.v[x];
|
||||||
|
let unit_digit = binary_value_to_decode_temp % 10;
|
||||||
|
binary_value_to_decode_temp /= 10;
|
||||||
|
let tens_digit = binary_value_to_decode_temp % 10;
|
||||||
|
binary_value_to_decode_temp /= 10;
|
||||||
|
let hundreds_digit = binary_value_to_decode_temp % 10;
|
||||||
|
binary_value_to_decode_temp /= 10;
|
||||||
|
// If this fails, something has gone truly wrong
|
||||||
|
assert_eq!(0, binary_value_to_decode_temp);
|
||||||
|
let val = [unit_digit, tens_digit, hundreds_digit];
|
||||||
|
let index = self.registers.i as usize;
|
||||||
|
self.memory[index..(index + 3)].copy_from_slice(&val);
|
||||||
|
}
|
||||||
|
Instruction::StoreRegistersToMemory(last_reg_to_store) => {
|
||||||
|
let reg_slice = &self.registers.v[0..=last_reg_to_store];
|
||||||
|
let index = self.registers.i as usize;
|
||||||
|
self.memory[index..=(index+last_reg_to_store)].copy_from_slice(reg_slice);
|
||||||
|
// Old Chip8 used to use i as a incrementing index
|
||||||
|
if !self.super_chip8_mode {
|
||||||
|
self.registers.i += last_reg_to_store as u16+ 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Instruction::LoadRegistersFromMemory(last_reg_to_load) => {
|
||||||
|
let index = self.registers.i as usize;
|
||||||
|
let mem_slice = &self.memory[index..=(index+last_reg_to_load)];
|
||||||
|
self.registers.v[0..=last_reg_to_load].copy_from_slice(mem_slice);
|
||||||
|
// Old Chip8 used to use i as a incrementing index
|
||||||
|
if !self.super_chip8_mode {
|
||||||
|
self.registers.i += last_reg_to_load as u16 + 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
///
|
///
|
||||||
/// Draw a sprite at location at (x,y) for n pixels long and 8 pixels wide.
|
/// Draw a sprite at location at (x,y) for n pixels long and 8 pixels wide.
|
||||||
|
@@ -5,12 +5,12 @@ use std::time::Duration;
|
|||||||
use crate::util::EmulatorResult;
|
use crate::util::EmulatorResult;
|
||||||
|
|
||||||
pub struct Timer {
|
pub struct Timer {
|
||||||
timer_left: Arc<Mutex<u16>>,
|
timer_left: Arc<Mutex<u8>>,
|
||||||
join_handle: Option<(JoinHandle<()>, std::sync::mpsc::Sender<()>)>,
|
join_handle: Option<(JoinHandle<()>, std::sync::mpsc::Sender<()>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Timer {
|
impl Timer {
|
||||||
pub const TIMER_THREAD_NAME: &str = "Timer";
|
pub const TIMER_THREAD_NAME: &'static str = "Timer";
|
||||||
pub fn new() -> Timer {
|
pub fn new() -> Timer {
|
||||||
Timer { timer_left: Arc::new(Mutex::default()), join_handle: None }
|
Timer { timer_left: Arc::new(Mutex::default()), join_handle: None }
|
||||||
}
|
}
|
||||||
@@ -37,19 +37,19 @@ impl Timer {
|
|||||||
self.join_handle = Some((res, sender));
|
self.join_handle = Some((res, sender));
|
||||||
}
|
}
|
||||||
/// Set a timer down tick from `val`
|
/// Set a timer down tick from `val`
|
||||||
pub fn try_set_timer(&self, val: u16) -> EmulatorResult<()> {
|
pub fn try_set_timer(&self, val: u8) -> EmulatorResult<()> {
|
||||||
let mut timer_val = self.timer_left.lock()?;
|
let mut timer_val = self.timer_left.lock()?;
|
||||||
*timer_val = val;
|
*timer_val = val;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn poll_value(&self) -> EmulatorResult<u16> {
|
pub fn poll_value(&self) -> EmulatorResult<u8> {
|
||||||
let res = self.timer_left.lock()?;
|
let res = self.timer_left.lock()?;
|
||||||
Ok(res.clone())
|
Ok(res.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stop(self) {
|
pub fn stop(self) {
|
||||||
if let Some((u, x)) = self.join_handle {
|
if let Some((u, _)) = self.join_handle {
|
||||||
u.join().expect("Failed to close thread");
|
u.join().expect("Failed to close thread");
|
||||||
} else {
|
} else {
|
||||||
log::warn!("Nothing present!");
|
log::warn!("Nothing present!");
|
||||||
|
Reference in New Issue
Block a user