WIP
richer test suite Updates imgui to have target ips and limits correctly. Initial IPS = 1000, plan to expose to user More tests for encode/decode of instructions Adding text to instruction parsing
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
use log::{debug};
|
||||
use crate::chip8::delay_timer::DelayTimer;
|
||||
use crate::chip8::keypad::Keypad;
|
||||
use crate::chip8::quirk_modes::QuirkMode;
|
||||
use crate::chip8::registers::Chip8Registers;
|
||||
use crate::chip8::sound_timer::SoundTimer;
|
||||
use crate::chip8::stack::Chip8Stack;
|
||||
@@ -19,7 +20,8 @@ pub struct Chip8Computer {
|
||||
pub video_memory: Chip8Video,
|
||||
pub state: Chip8CpuStates,
|
||||
pub keypad: Keypad,
|
||||
pub stack: Chip8Stack
|
||||
pub stack: Chip8Stack,
|
||||
pub quirk_mode: QuirkMode
|
||||
}
|
||||
impl Default for Chip8Computer {
|
||||
fn default() -> Self {
|
||||
@@ -32,7 +34,8 @@ impl Default for Chip8Computer {
|
||||
delay_timer: DelayTimer::new(),
|
||||
state: Chip8CpuStates::default(),
|
||||
keypad: Keypad::default(),
|
||||
stack: Chip8Stack::default()
|
||||
stack: Chip8Stack::default(),
|
||||
quirk_mode: QuirkMode::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,6 +48,8 @@ impl Chip8Computer {
|
||||
self.delay_timer.reset();
|
||||
self.sound_timer.reset();
|
||||
self.stack.reset();
|
||||
self.memory.reset();
|
||||
self.quirk_mode = QuirkMode::Chip8;
|
||||
}
|
||||
|
||||
pub fn dump_keypad_to_string(&self) -> String {
|
||||
|
||||
@@ -23,7 +23,7 @@ impl Default for Chip8ComputerManager {
|
||||
fn default() -> Self {
|
||||
Chip8ComputerManager {
|
||||
core_should_run: false,
|
||||
one_step: true,
|
||||
one_step: false,
|
||||
core_cycle_timer: false,
|
||||
core_last_cycle_start: Instant::now() ,
|
||||
computer: Chip8Computer::new()
|
||||
@@ -32,18 +32,16 @@ impl Default for Chip8ComputerManager {
|
||||
}
|
||||
|
||||
impl Chip8ComputerManager {
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.one_step = false;
|
||||
self.core_should_run = false;
|
||||
self.computer.reset();
|
||||
}
|
||||
pub fn new() -> Chip8ComputerManager {
|
||||
let core_handle = thread::spawn(move || {
|
||||
loop {
|
||||
let start_time = Instant::now();
|
||||
// println!("Core Thread starting at {start_time:?}");
|
||||
|
||||
let sleep_time = Instant::now().duration_since(start_time).as_millis();
|
||||
// println!("Core Thread sleeping for {sleep_time}ms");
|
||||
sleep(Duration::from_millis((16 - sleep_time) as u64));
|
||||
}
|
||||
});
|
||||
@@ -67,13 +65,15 @@ impl Chip8ComputerManager {
|
||||
&self.computer
|
||||
}
|
||||
|
||||
pub fn tick( &mut self) {
|
||||
pub fn tick( &mut self) -> bool {
|
||||
// println!("STARTING TICK");
|
||||
let mut did_tick: bool = false;
|
||||
if self.one_step | self.core_should_run {
|
||||
match self.computer.state {
|
||||
WaitingForInstruction => {
|
||||
self.core_last_cycle_start = Instant::now();
|
||||
self.computer.step_system();
|
||||
did_tick = true
|
||||
// println!("SYSTEM STEP");
|
||||
}
|
||||
_ => {}
|
||||
@@ -84,7 +84,9 @@ impl Chip8ComputerManager {
|
||||
// stop the CPU for the next cycle, we are only
|
||||
// wanting one step.
|
||||
self.one_step = false;
|
||||
did_tick = true;
|
||||
}
|
||||
did_tick
|
||||
}
|
||||
|
||||
pub fn press_key(&mut self, key_index: u8) {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct DelayTimer {
|
||||
counter: i32
|
||||
counter: u8
|
||||
}
|
||||
|
||||
impl DelayTimer {
|
||||
pub fn current(&self) -> i32 {
|
||||
pub fn current(&self) -> u8 {
|
||||
self.counter
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@ impl DelayTimer {
|
||||
self.counter = 0xff;
|
||||
}
|
||||
|
||||
pub fn set_timer(&mut self, new_value: i32) {
|
||||
self.counter = new_value
|
||||
pub fn set_timer(&mut self, new_value: u8) {
|
||||
self.counter = new_value as u8
|
||||
}
|
||||
|
||||
pub fn tick(&mut self) {
|
||||
|
||||
+136
-22
@@ -2,11 +2,12 @@ use std::arch::x86_64::_mm_xor_pd;
|
||||
use std::fmt::{Debug, Display, Formatter};
|
||||
use std::ops::{BitAnd, Deref, Shr};
|
||||
use std::time::Instant;
|
||||
use chrono::ParseMonthError;
|
||||
use log::debug;
|
||||
use rand::{random, Rng};
|
||||
use crate::chip8::computer::{Chip8Computer};
|
||||
use crate::chip8::cpu_states::Chip8CpuStates::WaitingForKey;
|
||||
use crate::chip8::instructions::Chip8CpuInstructions::XXXXERRORINSTRUCTION;
|
||||
use crate::chip8::instructions::Chip8CpuInstructions::*;
|
||||
use crate::chip8::util::InstructionUtil;
|
||||
use crate::constants::{*};
|
||||
|
||||
@@ -188,7 +189,7 @@ pub enum Chip8CpuInstructions {
|
||||
/// Set sound timer = Vx.
|
||||
///
|
||||
/// ST is set equal to the value of Vx.
|
||||
LDI_S(u8),
|
||||
LDIS(u8),
|
||||
/// Fx1E - ADD I, Vx
|
||||
/// Set I = I + Vx.
|
||||
///
|
||||
@@ -280,7 +281,7 @@ impl Chip8CpuInstructions {
|
||||
Chip8CpuInstructions::LDIA(_) => INST_LDIA,
|
||||
Chip8CpuInstructions::LDIX(_) => INST_LDIX,
|
||||
Chip8CpuInstructions::LIDR(_) => INST_LIDR,
|
||||
Chip8CpuInstructions::LDI_S(_) => INST_LIDS,
|
||||
Chip8CpuInstructions::LDIS(_) => INST_LDIS,
|
||||
Chip8CpuInstructions::LDR(_, _) => INST_LDR,
|
||||
Chip8CpuInstructions::LDRD(_) => INST_LDRD,
|
||||
Chip8CpuInstructions::LDRI(_) => INST_LDRI,
|
||||
@@ -327,6 +328,7 @@ impl Chip8CpuInstructions {
|
||||
Chip8CpuInstructions::ADD(x, byte) => {
|
||||
format!("0x{x:02x}, 0x{byte:02x}")
|
||||
}
|
||||
// Reg, Reg
|
||||
Chip8CpuInstructions::SEY(x, y) |
|
||||
Chip8CpuInstructions::LDR_Y(x, y) |
|
||||
Chip8CpuInstructions::OR(x, y) |
|
||||
@@ -338,50 +340,162 @@ impl Chip8CpuInstructions {
|
||||
Chip8CpuInstructions::SUBC(x, y) |
|
||||
Chip8CpuInstructions::SHL(x, y) |
|
||||
Chip8CpuInstructions::SNEY(x, y) => {
|
||||
format!("0x{x:02x}, 0x{y:02x}")
|
||||
format!("0x{x:01x}, 0x{y:01x}")
|
||||
}
|
||||
// Reg, Reg, Nibble
|
||||
Chip8CpuInstructions::DRW(x, y, nibble) => {
|
||||
format!("0x{x:02x}, 0x{y:02x}, 0x{nibble:02x}")
|
||||
}
|
||||
// Registers. 0-F
|
||||
Chip8CpuInstructions::LDD(x) |
|
||||
Chip8CpuInstructions::LDI_S(x) |
|
||||
Chip8CpuInstructions::LDIS(x) |
|
||||
Chip8CpuInstructions::ADDI(x) |
|
||||
Chip8CpuInstructions::LDFX(x) |
|
||||
Chip8CpuInstructions::BCD(x) |
|
||||
Chip8CpuInstructions::LDIX(x) |
|
||||
Chip8CpuInstructions::SKP(x) |
|
||||
Chip8CpuInstructions::LDRD(x) |
|
||||
Chip8CpuInstructions::LDRK(x) |
|
||||
Chip8CpuInstructions::LDRI(x) |
|
||||
Chip8CpuInstructions::LDF2(x) |
|
||||
Chip8CpuInstructions::STR(x) |
|
||||
Chip8CpuInstructions::LIDR(x) |
|
||||
Chip8CpuInstructions::SDN(x) |
|
||||
Chip8CpuInstructions::SKNP(x) |
|
||||
Chip8CpuInstructions::SKP(x) => {
|
||||
format!("0x{x:02x}")
|
||||
}
|
||||
_ => { String::new() }
|
||||
Chip8CpuInstructions::EXIT |
|
||||
Chip8CpuInstructions::ENA |
|
||||
Chip8CpuInstructions::DIS |
|
||||
Chip8CpuInstructions::SLF |
|
||||
Chip8CpuInstructions::XXXXERRORINSTRUCTION |
|
||||
Chip8CpuInstructions::SRT |
|
||||
Chip8CpuInstructions::CLS |
|
||||
Chip8CpuInstructions::RET => {
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Chip8CpuInstructions {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{} {}", self.name(), self.operands())
|
||||
let ops = self.operands();
|
||||
let space = if ops.is_empty() { "" } else { " " };
|
||||
write!(f, "{}{}{}", self.name(), space, ops)
|
||||
}
|
||||
}
|
||||
|
||||
impl Chip8CpuInstructions {
|
||||
pub fn from_str(input: &str) -> Chip8CpuInstructions {
|
||||
let parts = input.split(" ");
|
||||
println!("THERE ARE {} PARTS", parts.count());
|
||||
XXXXERRORINSTRUCTION
|
||||
//
|
||||
// match input.to_uppercase().as_str() {
|
||||
// INST_ADDI => Chip8CpuInstructions::ADDI(parts.nth(1)),
|
||||
// INST_ADD => Chip8CpuInstructions::ADD(parts[1], parts[2]),
|
||||
// INST_ADDR => Chip8CpuInstructions::ADDR(parts[1], parts[2]),
|
||||
// _ => XXXXERRORINSTRUCTION
|
||||
// }
|
||||
let mut parts = input.split(" ");
|
||||
// print!("THERE ARE {} PARTS", parts.clone().count());
|
||||
let first_part = parts.next().unwrap_or("");
|
||||
// take the next value...
|
||||
// ...strip off the extra...
|
||||
// ...convert it to an integer from base 16
|
||||
let param1 = u16::from_str_radix(parts.next().unwrap_or("0").trim_start_matches("0x").trim_end_matches(","), 16).unwrap_or(0);
|
||||
let param2 = u16::from_str_radix(parts.next().unwrap_or("0").trim_start_matches("0x").trim_end_matches(","), 16).unwrap_or(0);
|
||||
let param3 = u16::from_str_radix(parts.next().unwrap_or("0").trim_start_matches("0x").trim_end_matches(","), 16).unwrap_or(0);
|
||||
// println!("\tFirst part is {:?} / {:?} / {:?} / {:?}", first_part, param1 ,param2 ,param3);
|
||||
match first_part {
|
||||
INST_CLS => {
|
||||
CLS
|
||||
}
|
||||
INST_DRW => {
|
||||
DRW(param1 as u8, param2 as u8, param3 as u8)
|
||||
}
|
||||
INST_ADD => {
|
||||
ADD(param1 as u8, param2 as u8)
|
||||
}
|
||||
INST_CALL => {
|
||||
CALL(param1)
|
||||
}
|
||||
INST_SYS => {
|
||||
SYS(param1)
|
||||
}
|
||||
INST_RET => {
|
||||
RET
|
||||
}
|
||||
INST_JPA => {
|
||||
JPA(param1)
|
||||
}
|
||||
INST_JPI => {
|
||||
JPI(param1)
|
||||
}
|
||||
INST_SEX => {
|
||||
SEX(param1 as u8, param2 as u8)
|
||||
}
|
||||
INST_SNEB => {
|
||||
SNEB(param1 as u8, param2 as u8)
|
||||
}
|
||||
INST_SDN => {
|
||||
SDN(param1 as u8)
|
||||
}
|
||||
INST_SRT => {
|
||||
SRT
|
||||
}
|
||||
INST_SLF => {
|
||||
SLF
|
||||
}
|
||||
INST_EXIT => {
|
||||
EXIT
|
||||
}
|
||||
INST_DIS => {
|
||||
DIS
|
||||
}
|
||||
INST_ENA => {
|
||||
ENA
|
||||
}
|
||||
INST_SEY => {
|
||||
SEY(param1 as u8, param2 as u8)
|
||||
}
|
||||
INST_LDRY => {
|
||||
LDR_Y(param1 as u8, param2 as u8)
|
||||
}
|
||||
INST_LDR => {
|
||||
LDR(param1 as u8, param2 as u8)
|
||||
}
|
||||
INST_OR => {
|
||||
OR(param1 as u8, param2 as u8)
|
||||
}
|
||||
INST_AND => {
|
||||
AND(param1 as u8, param2 as u8)
|
||||
}
|
||||
INST_ORY => {
|
||||
ORY(param1 as u8, param2 as u8)
|
||||
}
|
||||
INST_ADDR => {
|
||||
ADDR(param1 as u8, param2 as u8)
|
||||
}
|
||||
INST_SUB => {
|
||||
SUB(param1 as u8, param2 as u8)
|
||||
}
|
||||
INST_SHR => {
|
||||
SHR(param1 as u8, param2 as u8)
|
||||
}
|
||||
INST_SHL => {
|
||||
SHL(param1 as u8, param2 as u8)
|
||||
}
|
||||
INST_SUBC => {
|
||||
SUBC(param1 as u8, param2 as u8)
|
||||
}
|
||||
INST_SNEY => {
|
||||
SNEY(param1 as u8, param2 as u8)
|
||||
}
|
||||
INST_LDIA => {
|
||||
LDIA(param1)
|
||||
}
|
||||
INST_RND => {
|
||||
RND(param1 as u8, param2 as u8)
|
||||
}
|
||||
INST_DRW => {
|
||||
DRW(param1 as u8, param2 as u8, param3 as u8)
|
||||
}
|
||||
_ => {
|
||||
XXXXERRORINSTRUCTION
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encode(&self) -> u16 {
|
||||
@@ -417,7 +531,7 @@ impl Chip8CpuInstructions {
|
||||
Chip8CpuInstructions::LDRD(x_register) => 0xF007 | ((*x_register as u16) << 8),
|
||||
Chip8CpuInstructions::LDRK(x_register) => 0xF00A | ((*x_register as u16) << 8),
|
||||
Chip8CpuInstructions::LDD(x_register) => 0xF015 | ((*x_register as u16) << 8),
|
||||
Chip8CpuInstructions::LDI_S(x_register) => 0xF018 | ((*x_register as u16) << 8),
|
||||
Chip8CpuInstructions::LDIS(x_register) => 0xF018 | ((*x_register as u16) << 8),
|
||||
Chip8CpuInstructions::ADDI(x_register) => 0xF01E | ((*x_register as u16) << 8),
|
||||
Chip8CpuInstructions::LDFX(x_register) => 0xF029 | ((*x_register as u16) << 8),
|
||||
Chip8CpuInstructions::BCD(x_register) => 0xF033 | ((*x_register as u16) << 8),
|
||||
@@ -488,7 +602,7 @@ impl Chip8CpuInstructions {
|
||||
0x07 => Chip8CpuInstructions::LDRD(ubln),
|
||||
0x0A => Chip8CpuInstructions::LDRK(ubln),
|
||||
0x15 => Chip8CpuInstructions::LDD(ubln),
|
||||
0x18 => Chip8CpuInstructions::LDI_S(ubln),
|
||||
0x18 => Chip8CpuInstructions::LDIS(ubln),
|
||||
0x1E => Chip8CpuInstructions::ADDI(ubln),
|
||||
0x29 => Chip8CpuInstructions::LDFX(ubln),
|
||||
0x30 => Chip8CpuInstructions::LDF2(ubln),
|
||||
@@ -816,9 +930,9 @@ impl Chip8CpuInstructions {
|
||||
//
|
||||
// DT is set equal to the value of Vx.
|
||||
let new_time = input.registers.peek(*source_register as u8);
|
||||
input.delay_timer.set_timer(new_time as i32);
|
||||
input.delay_timer.set_timer(new_time);
|
||||
}
|
||||
Chip8CpuInstructions::LDI_S(new_time) => {
|
||||
Chip8CpuInstructions::LDIS(new_time) => {
|
||||
let new_value = input.registers.peek(*new_time as u8);
|
||||
input.sound_timer.set_timer(new_value as i32);
|
||||
}
|
||||
|
||||
@@ -11,15 +11,17 @@ impl Keypad {
|
||||
// draw a 4x4 grid showing the keys with * filling the cells that are depressed
|
||||
for row in CHIP8_KEYBOARD.iter() {
|
||||
for (index, key) in row.iter().enumerate() {
|
||||
let is_lit = if self.keys[*key as usize] { "*".to_string() } else { char::from_digit(*key as u32, 16).unwrap_or(' ').to_string() };
|
||||
match index {
|
||||
3 => {
|
||||
// last in col
|
||||
return_value += format!("|{}|\n", is_lit).as_str();
|
||||
}
|
||||
_=> {
|
||||
return_value += format!("|{}", is_lit).as_str();
|
||||
}
|
||||
let is_lit = if self.keys[*key as usize] {
|
||||
"*".to_string()
|
||||
} else {
|
||||
char::from_digit(*key as u32, 16).unwrap_or(' ').to_string()
|
||||
};
|
||||
|
||||
if index == 3 {
|
||||
return_value += format!("|{}|\n", is_lit).as_str();
|
||||
|
||||
} else {
|
||||
return_value += format!("|{}", is_lit).as_str();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
#[derive(Default, Clone)]
|
||||
pub enum QuirkMode {
|
||||
#[default]
|
||||
Chip8,
|
||||
SChipLegacy,
|
||||
XOChip,
|
||||
SChipModern
|
||||
}
|
||||
@@ -20,6 +20,12 @@ impl Default for Chip8SystemMemory {
|
||||
}
|
||||
}
|
||||
impl Chip8SystemMemory {
|
||||
|
||||
pub fn reset(&mut self){
|
||||
self.memory = [0x00; CHIP8_MEMORY_SIZE as usize];
|
||||
self.load_fonts_to_memory();
|
||||
self.load_schip_fonts_to_memory();
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
Chip8SystemMemory {
|
||||
|
||||
@@ -136,10 +136,9 @@ impl Chip8Video {
|
||||
(CHIP8_VIDEO_WIDTH, CHIP8_VIDEO_HEIGHT)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_memory_size(&self) -> i32 {
|
||||
let w = self.get_resolution();
|
||||
w.1 * w.0
|
||||
let (width, height) = self.get_resolution();
|
||||
width * height
|
||||
}
|
||||
|
||||
pub fn tick(&mut self) {
|
||||
|
||||
@@ -36,7 +36,7 @@ pub const INST_LDF2: &str = "LDF2";
|
||||
pub const INST_LDIA: &str = "LDIA";
|
||||
pub const INST_LDIX: &str = "LDIX";
|
||||
pub const INST_LIDR: &str = "LIDR";
|
||||
pub const INST_LIDS: &str = "LIDS";
|
||||
pub const INST_LDIS: &str = "LIDS";
|
||||
pub const INST_LDR: &str = "LDR";
|
||||
pub const INST_LDRD: &str = "LDRD";
|
||||
pub const INST_LDRI: &str = "LDRI";
|
||||
|
||||
+1
-1
@@ -11,8 +11,8 @@ pub mod chip8 {
|
||||
pub mod registers;
|
||||
|
||||
pub mod stack;
|
||||
|
||||
pub mod computer_manager;
|
||||
pub mod quirk_modes;
|
||||
}
|
||||
|
||||
pub mod constants;
|
||||
Reference in New Issue
Block a user