diff --git a/coverage/tarpaulin-report.html b/coverage/tarpaulin-report.html
deleted file mode 100644
index 83440b3..0000000
--- a/coverage/tarpaulin-report.html
+++ /dev/null
@@ -1,671 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/gemma/src/chip8/computer.rs b/gemma/src/chip8/computer.rs
index efbf636..c34db82 100644
--- a/gemma/src/chip8/computer.rs
+++ b/gemma/src/chip8/computer.rs
@@ -12,7 +12,7 @@ use super::{
system_memory::Chip8SystemMemory, video::Chip8Video,
};
-#[derive(Clone, Serialize, Deserialize)]
+#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct Chip8Computer {
pub num_cycles: i32,
pub memory: Chip8SystemMemory,
@@ -43,15 +43,15 @@ impl Default for Chip8Computer {
}
impl Chip8Computer {
- pub fn reset(&mut self) {
+ pub fn reset(&mut self, quirk_mode: QuirkMode) {
self.video_memory.reset();
self.num_cycles = 0;
self.registers.reset();
self.delay_timer.reset();
self.sound_timer.reset();
self.stack.reset();
- self.memory.reset();
- self.quirk_mode = QuirkMode::Chip8;
+ self.memory.reset(quirk_mode);
+ self.quirk_mode = quirk_mode;
}
pub fn dump_keypad_to_string(&self) -> String {
@@ -108,7 +108,7 @@ impl Chip8Computer {
match self.state {
Chip8CpuStates::WaitingForInstruction => {
- println!("Ticking sound, delay, video");
+ // println!("Ticking sound, delay, video");
self.sound_timer.tick();
self.delay_timer.tick();
self.video_memory.tick();
diff --git a/gemma/src/chip8/computer_manager.rs b/gemma/src/chip8/computer_manager.rs
index 14fdfbf..9cea84f 100644
--- a/gemma/src/chip8/computer_manager.rs
+++ b/gemma/src/chip8/computer_manager.rs
@@ -1,21 +1,22 @@
-use std::thread;
-use std::thread::sleep;
-use std::time::{Duration, Instant};
use crate::chip8::computer::Chip8Computer;
use crate::chip8::cpu_states::Chip8CpuStates;
use crate::chip8::cpu_states::Chip8CpuStates::WaitingForInstruction;
+use crate::chip8::quirk_modes::QuirkMode;
+use std::thread;
+use std::thread::sleep;
+use std::time::{Duration, Instant};
+
pub enum ManagerDumpables {
Video,
Registers,
- Keyboard
+ Keyboard,
}
pub struct Chip8ComputerManager {
core_should_run: bool,
one_step: bool,
- core_cycle_timer: bool,
core_last_cycle_start: Instant,
- computer: Chip8Computer
+ computer: Chip8Computer,
}
impl Default for Chip8ComputerManager {
@@ -23,26 +24,27 @@ impl Default for Chip8ComputerManager {
Chip8ComputerManager {
core_should_run: false,
one_step: false,
- core_cycle_timer: false,
- core_last_cycle_start: Instant::now() ,
- computer: Chip8Computer::new()
+ core_last_cycle_start: Instant::now(),
+ computer: Chip8Computer::new(),
}
}
}
impl Chip8ComputerManager {
- pub fn reset(&mut self) {
+ pub fn quirks_mode(&self) -> QuirkMode {
+ self.computer.quirk_mode.clone()
+ }
+
+ pub fn reset(&mut self, mode: QuirkMode) {
self.one_step = false;
self.core_should_run = false;
- self.computer.reset();
+ self.computer.reset(mode);
}
pub fn new() -> Chip8ComputerManager {
- let core_handle = thread::spawn(move || {
- loop {
- let start_time = Instant::now();
- let sleep_time = Instant::now().duration_since(start_time).as_millis();
- sleep(Duration::from_millis((16 - sleep_time) as u64));
- }
+ let _ = thread::spawn(move || loop {
+ let start_time = Instant::now();
+ let sleep_time = Instant::now().duration_since(start_time).as_millis();
+ sleep(Duration::from_millis((16 - sleep_time) as u64));
});
Chip8ComputerManager::default()
@@ -64,15 +66,15 @@ impl Chip8ComputerManager {
&self.computer
}
- pub fn tick( &mut self) -> bool {
- // println!("STARTING TICK");
+ pub fn tick(&mut self) -> bool {
+ // println!("STARTING TICK");
let mut did_tick: bool = false;
- if self.one_step | self.core_should_run {
+ if self.one_step | self.core_should_run {
if let WaitingForInstruction = self.computer.state {
self.core_last_cycle_start = Instant::now();
self.computer.step_system();
did_tick = true
- // println!("SYSTEM STEP");
+ // println!("SYSTEM STEP");
}
};
if self.one_step {
@@ -112,21 +114,15 @@ impl Chip8ComputerManager {
}
pub fn load_new_program_to_system_memory(&mut self, bytes_to_load: Vec) {
- self.reset();
+ self.reset(self.computer.quirk_mode.clone());
self.computer.load_bytes_to_memory(0x200, &bytes_to_load);
}
pub fn dump_to_string(&self, dump_type: ManagerDumpables) -> String {
match dump_type {
- ManagerDumpables::Video => {
- self.computer.video_memory.format_as_string()
- }
- ManagerDumpables::Registers => {
- self.computer.registers.format_as_string()
- }
- ManagerDumpables::Keyboard => {
- self.computer.keypad.format_as_string()
- }
+ ManagerDumpables::Video => self.computer.video_memory.format_as_string(),
+ ManagerDumpables::Registers => self.computer.registers.format_as_string(),
+ ManagerDumpables::Keyboard => self.computer.keypad.format_as_string(),
}
}
-}
\ No newline at end of file
+}
diff --git a/gemma/src/chip8/cpu_states.rs b/gemma/src/chip8/cpu_states.rs
index f080126..b0fa9d2 100644
--- a/gemma/src/chip8/cpu_states.rs
+++ b/gemma/src/chip8/cpu_states.rs
@@ -1,6 +1,6 @@
use serde::{Deserialize, Serialize};
-#[derive(Clone, Copy, Default, Serialize, Deserialize)]
+#[derive(Clone, Copy, Default, Serialize, Deserialize, Debug)]
pub enum Chip8CpuStates {
#[default]
WaitingForInstruction,
diff --git a/gemma/src/chip8/delay_timer.rs b/gemma/src/chip8/delay_timer.rs
index a36e195..cee823a 100644
--- a/gemma/src/chip8/delay_timer.rs
+++ b/gemma/src/chip8/delay_timer.rs
@@ -1,6 +1,6 @@
use serde::{Deserialize, Serialize};
-#[derive(Clone, Copy, Serialize, Deserialize)]
+#[derive(Clone, Copy, Serialize, Deserialize, Debug)]
pub struct DelayTimer {
counter: u8,
}
diff --git a/gemma/src/chip8/instructions.rs b/gemma/src/chip8/instructions.rs
index ab624ea..08fd579 100644
--- a/gemma/src/chip8/instructions.rs
+++ b/gemma/src/chip8/instructions.rs
@@ -7,6 +7,7 @@ use crate::constants::*;
use log::debug;
use rand::Rng;
use serde::{Deserialize, Serialize};
+use std::ascii::AsciiExt;
use std::fmt::{Debug, Display, Formatter};
use std::ops::BitAnd;
use std::time::Instant;
@@ -270,6 +271,11 @@ pub enum Chip8CpuInstructions {
/// scroll screen content down N pixel, in XO-CHIP only selected bit
/// planes are scrolled (Quirks are HP48 specific)
SCU(u8),
+ /// 0xNNNN
+ ///
+ /// data word
+ /// used for specifying data to be used in system memory
+ DW(u16),
}
impl Chip8CpuInstructions {
@@ -322,6 +328,7 @@ impl Chip8CpuInstructions {
JPX(_, _) => INST_JPX,
XXXXERRORINSTRUCTION => "XX ERROR XX",
SCU(_) => INST_SCU,
+ DW(_) => INST_DW,
}
}
@@ -331,7 +338,8 @@ impl Chip8CpuInstructions {
let addr_for_display = (*x as u16) << 8 | *addr;
format!("0x{x:02x}, 0x{addr_for_display:04x}")
}
- Chip8CpuInstructions::SYS(addr)
+ Chip8CpuInstructions::DW(addr)
+ | Chip8CpuInstructions::SYS(addr)
| Chip8CpuInstructions::JPI(addr)
| Chip8CpuInstructions::JPA(addr)
| Chip8CpuInstructions::LDIA(addr)
@@ -406,7 +414,7 @@ impl Chip8CpuInstructions {
pub fn from_str(input: &str) -> Chip8CpuInstructions {
let mut parts = input.split(" ");
// print!("THERE ARE {} PARTS", parts.clone().count());
- let first_part = parts.next().unwrap_or("");
+ let first_part = parts.next().unwrap_or("").to_ascii_uppercase();
// take the next value...
// ...strip off the extra...
// ...convert it to an integer from base 16
@@ -414,6 +422,7 @@ impl Chip8CpuInstructions {
parts
.next()
.unwrap_or("0")
+ .to_ascii_lowercase()
.trim_start_matches("0x")
.trim_end_matches(","),
16,
@@ -423,6 +432,7 @@ impl Chip8CpuInstructions {
parts
.next()
.unwrap_or("0")
+ .to_ascii_lowercase()
.trim_start_matches("0x")
.trim_end_matches(","),
16,
@@ -432,13 +442,17 @@ impl Chip8CpuInstructions {
parts
.next()
.unwrap_or("0")
+ .to_ascii_lowercase()
.trim_start_matches("0x")
.trim_end_matches(","),
16,
)
.unwrap_or(0);
- // println!("\tFirst part is {:?} / {:?} / {:?} / {:?}", first_part, param1 ,param2 ,param3);
- match first_part {
+ println!(
+ "\tFirst part is {:?} / {:?} / {:?} / {:?}",
+ first_part, param1, param2, param3
+ );
+ match first_part.as_str() {
INST_ADDI => ADDI(param1 as u8),
INST_ADD => ADD(param1 as u8, param2 as u8),
INST_CLS => CLS,
@@ -483,6 +497,7 @@ impl Chip8CpuInstructions {
INST_LDIS => LDIS(param1 as u8),
INST_LDD => LDD(param1 as u8),
INST_JPX => JPX(param1 as u8, param2),
+ INST_DW => DW(param1 as u16),
_ => XXXXERRORINSTRUCTION,
}
}
@@ -571,8 +586,9 @@ impl Chip8CpuInstructions {
Chip8CpuInstructions::LDF2(x_register) => 0xF030 | ((*x_register as u16) << 8),
Chip8CpuInstructions::STR(x_register) => 0xF075 | ((*x_register as u16) << 8),
Chip8CpuInstructions::LIDR(x_register) => 0xF085 | ((*x_register as u16) << 8),
- XXXXERRORINSTRUCTION => 0xFFFF,
SCU(x_register) => 0x00D0 | (*x_register as u16),
+ DW(address) => *address as u16,
+ XXXXERRORINSTRUCTION => 0x0000,
}
}
pub fn decode(input: u16, quirk_mode: &QuirkMode) -> Chip8CpuInstructions {
@@ -661,7 +677,7 @@ impl Chip8CpuInstructions {
0x85 => Chip8CpuInstructions::LIDR(ubln),
_ => XXXXERRORINSTRUCTION,
},
- _ => XXXXERRORINSTRUCTION,
+ _ => DW(addr_param),
}
}
pub fn execute(&self, input: &mut Chip8Computer) -> Chip8Computer {
@@ -1084,6 +1100,7 @@ impl Chip8CpuInstructions {
input.registers.poke_i(offset + 1);
}
Chip8CpuInstructions::XXXXERRORINSTRUCTION => {}
+ // SCHIP1.1
Chip8CpuInstructions::SCD(x) => match input.quirk_mode {
QuirkMode::Chip8 => {
debug!("Attempt to execute SCD in Chip8 Mode");
@@ -1092,6 +1109,7 @@ impl Chip8CpuInstructions {
input.video_memory.scroll_down(*x as i32);
}
},
+ // SCHIP 1.1
Chip8CpuInstructions::SCR => match input.quirk_mode {
QuirkMode::Chip8 => {
debug!("Attempt to execute SCR in Chip8 Mode");
@@ -1100,6 +1118,7 @@ impl Chip8CpuInstructions {
input.video_memory.scroll_right();
}
},
+ // SCHIP 1.1
Chip8CpuInstructions::SCL => match input.quirk_mode {
QuirkMode::Chip8 => {
debug!("Attempt to execute SCL in Chip8 Mode");
@@ -1108,6 +1127,7 @@ impl Chip8CpuInstructions {
input.video_memory.scroll_left();
}
},
+ // SCHIP 1.0
Chip8CpuInstructions::LOW => match input.quirk_mode {
QuirkMode::Chip8 => {
debug!("ATTEMPT TO SET LOWRES IN CHIP8MODE");
@@ -1116,6 +1136,7 @@ impl Chip8CpuInstructions {
input.video_memory.set_lowres();
}
},
+ // SCHIP 1.0
Chip8CpuInstructions::HIGH => match input.quirk_mode {
QuirkMode::Chip8 => {
debug!("ATTEMPT TO SET HIGHRES IN CHIP8MODE");
@@ -1171,6 +1192,9 @@ impl Chip8CpuInstructions {
}
}
}
+ DW(addr) => {
+ println!("DATA WORD FOUND...");
+ }
};
let cycle_time = Instant::now().duration_since(start_time).as_nanos();
// println!("\t\tTook {cycle_time}ms");
diff --git a/gemma/src/chip8/keypad.rs b/gemma/src/chip8/keypad.rs
index 4379043..1a032a6 100644
--- a/gemma/src/chip8/keypad.rs
+++ b/gemma/src/chip8/keypad.rs
@@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};
use crate::constants::CHIP8_KEYBOARD;
-#[derive(Clone, Copy, Default, Serialize, Deserialize)]
+#[derive(Clone, Copy, Default, Serialize, Deserialize, Debug)]
pub struct Keypad {
keys: [bool; 0x10],
}
diff --git a/gemma/src/chip8/quirk_modes.rs b/gemma/src/chip8/quirk_modes.rs
index d00faec..26d3caa 100644
--- a/gemma/src/chip8/quirk_modes.rs
+++ b/gemma/src/chip8/quirk_modes.rs
@@ -1,9 +1,21 @@
use serde::{Deserialize, Serialize};
+use std::fmt::Display;
-#[derive(Default, Clone, Serialize, Deserialize)]
+#[derive(Default, Clone, Serialize, Deserialize, Copy, Debug)]
pub enum QuirkMode {
- #[default]
Chip8,
XOChip,
+ #[default]
SChipModern,
}
+
+impl Display for QuirkMode {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let variant = match &self {
+ QuirkMode::Chip8 => "Chip8".to_string(),
+ QuirkMode::XOChip => "XO Chip".to_string(),
+ QuirkMode::SChipModern => "SChip-Modern".to_string(),
+ };
+ write!(f, "{}", variant)
+ }
+}
diff --git a/gemma/src/chip8/registers.rs b/gemma/src/chip8/registers.rs
index 1795728..4fbdc3e 100644
--- a/gemma/src/chip8/registers.rs
+++ b/gemma/src/chip8/registers.rs
@@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};
/// Registers. numbered 1-16 publicly.
/// Privately using zero base array so -1 to shift from pub to priv.
-#[derive(Clone, Copy, Serialize, Deserialize)]
+#[derive(Clone, Copy, Serialize, Deserialize, Debug)]
pub struct Chip8Registers {
pub registers: [u8; 16],
pub i_register: u16,
diff --git a/gemma/src/chip8/sound_timer.rs b/gemma/src/chip8/sound_timer.rs
index 2dd1fda..8cf1f31 100644
--- a/gemma/src/chip8/sound_timer.rs
+++ b/gemma/src/chip8/sound_timer.rs
@@ -1,7 +1,7 @@
use log::trace;
use serde::{Deserialize, Serialize};
-#[derive(Clone, Copy, Serialize, Deserialize)]
+#[derive(Clone, Copy, Serialize, Deserialize, Debug)]
pub struct SoundTimer {
counter: i32,
}
diff --git a/gemma/src/chip8/stack.rs b/gemma/src/chip8/stack.rs
index 16adcb5..eb58ab3 100644
--- a/gemma/src/chip8/stack.rs
+++ b/gemma/src/chip8/stack.rs
@@ -1,6 +1,6 @@
use serde::{Deserialize, Serialize};
-#[derive(Clone, Default, Serialize, Deserialize)]
+#[derive(Clone, Default, Serialize, Deserialize, Debug)]
pub struct Chip8Stack {
items: Vec,
}
diff --git a/gemma/src/chip8/system_memory.rs b/gemma/src/chip8/system_memory.rs
index 59a9a3e..5a68a44 100644
--- a/gemma/src/chip8/system_memory.rs
+++ b/gemma/src/chip8/system_memory.rs
@@ -4,7 +4,10 @@ use crate::constants::*;
use serde::Deserialize;
use serde::Serialize;
-#[derive(Clone, Serialize, Deserialize)]
+use super::quirk_modes;
+use super::quirk_modes::QuirkMode;
+
+#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct Chip8SystemMemory {
memory: Vec,
}
@@ -18,6 +21,7 @@ impl Default for Chip8SystemMemory {
x
}
}
+
impl Chip8SystemMemory {
fn empty_memory() -> Vec {
let mut working_memory = vec![];
@@ -28,10 +32,19 @@ impl Chip8SystemMemory {
working_memory
}
- pub fn reset(&mut self) {
+ pub fn reset(&mut self, quirk_modes: QuirkMode) {
self.memory = Chip8SystemMemory::empty_memory();
- self.load_fonts_to_memory();
- self.load_schip_fonts_to_memory();
+ match quirk_modes {
+ QuirkMode::Chip8 => {
+ self.load_fonts_to_memory();
+ }
+ QuirkMode::XOChip => {
+ println!("NO XO FONT LOADING DONE YET");
+ }
+ QuirkMode::SChipModern => {
+ self.load_schip_fonts_to_memory();
+ }
+ }
}
pub fn new() -> Self {
@@ -114,10 +127,10 @@ impl Chip8SystemMemory {
SCHIPFONT_F,
];
for (font_index, current_font) in all_font_characters.iter().enumerate() {
- let base_offset = 0x100;
- for font_mem_offset in 0..=4 {
- let real_offset = base_offset + font_index * 0x10 + font_mem_offset;
- self.poke(real_offset as u16, current_font[font_mem_offset]);
+ let base_offset = SCHIPFONT_OFFSET;
+ for font_mem_offset in 0..=9 {
+ let real_offset = base_offset + (font_index * 0x0a) as u32 + font_mem_offset;
+ self.poke(real_offset as u16, current_font[font_mem_offset as usize]);
}
}
}
diff --git a/gemma/src/chip8/video.rs b/gemma/src/chip8/video.rs
index 371daa9..146d735 100644
--- a/gemma/src/chip8/video.rs
+++ b/gemma/src/chip8/video.rs
@@ -6,13 +6,13 @@ use crate::constants::{
use log::debug;
use serde::{Deserialize, Serialize};
-#[derive(Clone, Copy, Serialize, Deserialize)]
+#[derive(Clone, Copy, Serialize, Deserialize, Debug)]
pub enum Chip8VideoModes {
LowRes,
HighRes,
}
-#[derive(Clone, Serialize, Deserialize)]
+#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct Chip8Video {
memory: Vec,
pub has_frame_changed: bool,
@@ -172,14 +172,16 @@ impl Chip8Video {
}
pub fn scroll_left(&mut self) {
+ println!("SCROLLLEFTPRE:::[{}]", self.format_as_string());
let (width, height) = self.get_resolution();
for current_row in 0..height {
let row_offset = current_row * width;
- for current_column in 0..width - 4 {
- let source: usize = (row_offset + current_column) as usize;
- let target: usize = source + 4;
+ for current_column in (0..width - 4) {
+ let target: usize = (row_offset + current_column) as usize;
+ let source: usize = target + 4;
self.memory[target] = self.memory[source];
+ println!("Moving from {source} to {target}");
}
let clear_start: usize = (row_offset + width - 4) as usize;
@@ -187,6 +189,7 @@ impl Chip8Video {
self.memory[clear_start..clear_end].fill(false);
}
+ println!("SCROLLLEFTPOST:::[{}]", self.format_as_string());
}
pub fn scroll_up(&mut self, how_far: &u8) {
diff --git a/gemma/src/constants.rs b/gemma/src/constants.rs
index ac4e9eb..bc46ba7 100644
--- a/gemma/src/constants.rs
+++ b/gemma/src/constants.rs
@@ -12,7 +12,7 @@ pub const CHIP8_KEYBOARD: [[u8; 4]; 4] = [
[0x01, 0x02, 0x03, 0x0C],
[0x04, 0x05, 0x06, 0x0D],
[0x07, 0x08, 0x09, 0x0E],
- [0x0A, 0x00, 0x0B, 0x0F]
+ [0x0A, 0x00, 0x0B, 0x0F],
];
pub const SCHIP_VIDEO_HEIGHT: i32 = 64;
@@ -65,6 +65,10 @@ pub const INST_LOW: &str = "LOW";
pub const INST_HIGH: &str = "HIGH";
pub const INST_ORY: &str = "ORY";
pub const INST_SCU: &str = "SCU";
+/// Data Word
+/// Data to be loaded to memory for application use
+pub const INST_DW: &str = "DW";
+
pub const CHIP8_PROGRAM_LOAD_OFFSET: i32 = 0x200;
pub const CHIP8FONT_0: [u8; 5] = [0xF0, 0x90, 0x90, 0x90, 0xF0];
pub const CHIP8FONT_1: [u8; 5] = [0x20, 0x60, 0x20, 0x20, 0x70];
@@ -83,90 +87,20 @@ pub const CHIP8FONT_D: [u8; 5] = [0xE0, 0x90, 0x90, 0x90, 0xE0];
pub const CHIP8FONT_E: [u8; 5] = [0xF0, 0x80, 0xF0, 0x80, 0xF0];
pub const CHIP8FONT_F: [u8; 5] = [0xF0, 0x80, 0xF0, 0x80, 0x80];
-pub const SCHIPFONT_0: [u8; 0x20] = [
- 0x00, 0x00,
- 0x01, 0x80,
- 0x03, 0xc0,
- 0x06, 0x60,
- 0x0c, 0x30,
- 0x0c, 0x30,
- 0x18, 0x18,
- 0x18, 0x18,
- 0x18, 0x18,
- 0x18, 0x18,
- 0x0c, 0x30,
- 0x0c, 0x30,
- 0x06, 0x60,
- 0x03, 0xc0, // 0b0000001111000000
- 0x01, 0x80, // 0b0000000110000000
- 0x00, 0x00 // 0b0000000000000000
-];
-pub const SCHIPFONT_1: [u8; 0x20] = [
- 0x00, 0x00,
- 0x03, 0xc0,
- 0x02, 0xc0,
- 0x00, 0xc0,
- 0x00, 0xc0,
- 0x00, 0xc0,
- 0x00, 0xc0,
- 0x00, 0xc0,
- 0x00, 0xc0,
- 0x00, 0xc0,
- 0x00, 0xc0,
- 0x00, 0xc0,
- 0x00, 0xc0,
- 0x00, 0xc0,
- 0x03, 0xf0,
- 0x00, 0x00
-];
-pub const SCHIPFONT_2: [u8; 0x20] = [0x7C, 0xFE, 0xFF, 0xFF, 0xC7, 0xC3, 0xC0, 0xE0,
- 0xF0, 0x78, 0x3C, 0x1E, 0x0F, 0x07, 0xFF, 0xFF,0x78, 0xFE, 0xFF, 0xFF, 0x83, 0x01, 0x01, 0xFF,
- 0xFF, 0xFF, 0xC3, 0xE3, 0xFF, 0xFE, 0xFC, 0x78];
-pub const SCHIPFONT_3: [u8; 0x20] = [0x7C, 0xFE, 0xFF, 0xFF, 0xC7, 0xC3, 0xC0, 0xE0,
- 0xF0, 0x78, 0x3C, 0x1E, 0x0F, 0x07, 0xFF, 0xFF,0x78, 0xFE, 0xFF, 0xFF, 0x83, 0x01, 0x01, 0xFF,
- 0xFF, 0xFF, 0xC3, 0xE3, 0xFF, 0xFE, 0xFC, 0x78];
-pub const SCHIPFONT_4: [u8; 0x20] = [0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xF7, 0xF3, 0xF1,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0xC0, 0xC0, 0xC0,0x78, 0xFE, 0xFF, 0xFF, 0x83, 0x01, 0x01, 0xFF,
- 0xFF, 0xFF, 0xC3, 0xE3, 0xFF, 0xFE, 0xFC, 0x78];
-pub const SCHIPFONT_5: [u8; 0x20] = [0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x3F, 0x7F, 0x7F,
- 0x01, 0x01, 0xC1, 0xE3, 0xFF, 0xFE, 0xFC, 0x78,0x78, 0xFE, 0xFF, 0xFF, 0x83, 0x01, 0x01, 0xFF,
- 0xFF, 0xFF, 0xC3, 0xE3, 0xFF, 0xFE, 0xFC, 0x78];
-pub const SCHIPFONT_6: [u8; 0x20] = [0x78, 0xFE, 0xFF, 0xFF, 0x83, 0x01, 0x01, 0xFF,
- 0xFF, 0xFF, 0xC3, 0xE3, 0xFF, 0xFE, 0xFC, 0x78,0x78, 0xFE, 0xFF, 0xFF, 0x83, 0x01, 0x01, 0xFF,
- 0xFF, 0xFF, 0xC3, 0xE3, 0xFF, 0xFE, 0xFC, 0x78
-];
-pub const SCHIPFONT_7: [u8; 0x20] = [0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0xF0, 0x78, 0x3C,
- 0x1E, 0x0F, 0x07, 0x03, 0x01, 0x01, 0x01, 0x01,0x78, 0xFE, 0xFF, 0xFF, 0x83, 0x01, 0x01, 0xFF,
- 0xFF, 0xFF, 0xC3, 0xE3, 0xFF, 0xFE, 0xFC, 0x78
-];
-pub const SCHIPFONT_8: [u8; 0x20] = [0x7C, 0xFE, 0xFF, 0xFF, 0xC3, 0xC3, 0xC3, 0xFF,
- 0x7E, 0xFE, 0xC3, 0xC3, 0xFF, 0xFF, 0xFE, 0x7C,0x78, 0xFE, 0xFF, 0xFF, 0x83, 0x01, 0x01, 0xFF,
- 0xFF, 0xFF, 0xC3, 0xE3, 0xFF, 0xFE, 0xFC, 0x78
-];
-pub const SCHIPFONT_9: [u8; 0x20] = [0x7C, 0xFE, 0xFF, 0xFF, 0xC3, 0xC3, 0xC3, 0xFF,
- 0xFF, 0x7F, 0x03, 0x03, 0xC7, 0xFF, 0xFE, 0x7C,0x78, 0xFE, 0xFF, 0xFF, 0x83, 0x01, 0x01, 0xFF,
- 0xFF, 0xFF, 0xC3, 0xE3, 0xFF, 0xFE, 0xFC, 0x78
-];
-pub const SCHIPFONT_A: [u8; 0x20] = [0x7C, 0xFE, 0xFF, 0xFF, 0xC3, 0xC3, 0xC3, 0xFF,
- 0xFF, 0xFF, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3,0x78, 0xFE, 0xFF, 0xFF, 0x83, 0x01, 0x01, 0xFF,
- 0xFF, 0xFF, 0xC3, 0xE3, 0xFF, 0xFE, 0xFC, 0x78
-];
-pub const SCHIPFONT_B: [u8; 0x20] = [0xFE, 0xFF, 0xFF, 0xFF, 0xC3, 0xC3, 0xFE, 0xFF,
- 0xFF, 0xFF, 0xC3, 0xC3, 0xC3, 0xFF, 0xFF, 0xFE,0x78, 0xFE, 0xFF, 0xFF, 0x83, 0x01, 0x01, 0xFF,
- 0xFF, 0xFF, 0xC3, 0xE3, 0xFF, 0xFE, 0xFC, 0x78
-];
-pub const SCHIPFONT_C: [u8; 0x20] = [0x7C, 0xFE, 0xFF, 0xFF, 0xC3, 0xC3, 0x01, 0x01,
- 0x01, 0x01, 0xC3, 0xC3, 0xFF, 0xFE, 0xFC, 0x78,0x78, 0xFE, 0xFF, 0xFF, 0x83, 0x01, 0x01, 0xFF,
- 0xFF, 0xFF, 0xC3, 0xE3, 0xFF, 0xFE, 0xFC, 0x78
-];
-pub const SCHIPFONT_D: [u8; 0x20] = [0xFE, 0xFF, 0xFF, 0xFF, 0xC3, 0xC3, 0xC3, 0xC3,
- 0xC3, 0xC3, 0xC3, 0xC3, 0xFF, 0xFF, 0xFE, 0x7C, 0x78, 0xFE, 0xFF, 0xFF, 0x83, 0x01, 0x01, 0xFF,
- 0xFF, 0xFF, 0xC3, 0xE3, 0xFF, 0xFE, 0xFC, 0x78
-];
-pub const SCHIPFONT_E: [u8; 0x20] = [0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x03, 0x03, 0xFF,
- 0xFF, 0xFF, 0x03, 0x03, 0xFF, 0xFF, 0xFF, 0xFF,0x78, 0xFE, 0xFF, 0xFF, 0x83, 0x01, 0x01, 0xFF,
- 0xFF, 0xFF, 0xC3, 0xE3, 0xFF, 0xFE, 0xFC, 0x78
-];
-pub const SCHIPFONT_F: [u8; 0x20] = [0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x03, 0x03, 0xFF,
- 0xFF, 0xFF, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,0x78, 0xFE, 0xFF, 0xFF, 0x83, 0x01, 0x01, 0xFF,
- 0xFF, 0xFF, 0xC3, 0xE3, 0xFF, 0xFE, 0xFC, 0x78];
+pub const SCHIPFONT_OFFSET: u32 = 0x81;
+pub const SCHIPFONT_0: [u8; 0x0A] = [0xF0, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0xF0];
+pub const SCHIPFONT_1: [u8; 0x0A] = [0x20, 0x60, 0xA0, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70];
+pub const SCHIPFONT_2: [u8; 0x0A] = [0xF0, 0x10, 0x10, 0x10, 0xF0, 0x80, 0x80, 0x80, 0x80, 0xF0];
+pub const SCHIPFONT_3: [u8; 0x0A] = [0xF0, 0x10, 0x10, 0x10, 0xF0, 0x10, 0x10, 0x10, 0x10, 0xF0];
+pub const SCHIPFONT_4: [u8; 0x0A] = [0x90, 0x90, 0x90, 0x90, 0xF0, 0x10, 0x10, 0x10, 0x10, 0x10];
+pub const SCHIPFONT_5: [u8; 0x0A] = [0xF0, 0x80, 0x80, 0x80, 0xF0, 0x10, 0x10, 0x10, 0x10, 0xF0];
+pub const SCHIPFONT_6: [u8; 0x0A] = [0xF0, 0x80, 0x80, 0x80, 0xF0, 0x90, 0x90, 0x90, 0x90, 0xF0];
+pub const SCHIPFONT_7: [u8; 0x0A] = [0xF0, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10];
+pub const SCHIPFONT_8: [u8; 0x0A] = [0xF0, 0x90, 0x90, 0x90, 0xF0, 0x90, 0x90, 0x90, 0x90, 0xF0];
+pub const SCHIPFONT_9: [u8; 0x0A] = [0xF0, 0x90, 0x90, 0x90, 0xF0, 0x10, 0x10, 0x10, 0x10, 0xF0];
+pub const SCHIPFONT_A: [u8; 0x0A] = [0xF0, 0x90, 0x90, 0x90, 0xF0, 0x90, 0x90, 0x90, 0x90, 0x90];
+pub const SCHIPFONT_B: [u8; 0x0A] = [0xE0, 0x90, 0x90, 0x90, 0xE0, 0x90, 0x90, 0x90, 0x90, 0xE0];
+pub const SCHIPFONT_C: [u8; 0x0A] = [0xF0, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xF0];
+pub const SCHIPFONT_D: [u8; 0x0A] = [0xE0, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0xE0];
+pub const SCHIPFONT_E: [u8; 0x0A] = [0xF0, 0x80, 0x80, 0x80, 0xF0, 0x80, 0x80, 0x80, 0x80, 0xF0];
+pub const SCHIPFONT_F: [u8; 0x0A] = [0xF0, 0x80, 0x80, 0x80, 0xF0, 0x80, 0x80, 0x80, 0x80, 0x80];
diff --git a/gemma/tests/computer_tests.rs b/gemma/tests/computer_tests.rs
index 9716434..1385de8 100644
--- a/gemma/tests/computer_tests.rs
+++ b/gemma/tests/computer_tests.rs
@@ -2,7 +2,9 @@ use gemma::chip8::computer::Chip8Computer;
use gemma::constants::CHIP8_VIDEO_MEMORY;
#[test]
-fn smoke() { assert!(true) }
+fn smoke() {
+ assert!(true)
+}
fn load_result(to_load: &str) -> String {
std::fs::read_to_string(format!("../resources/test/{}", to_load)).unwrap()
@@ -20,7 +22,7 @@ fn reset_clears_video() {
x.video_memory.poke(i as u16, i % 2 == 0);
}
- x.reset();
+ x.reset(gemma::chip8::quirk_modes::QuirkMode::Chip8);
x.video_memory.reset();
assert_eq!(x.dump_video_to_string(), x.video_memory.format_as_string());
@@ -40,7 +42,10 @@ fn level1_test() {
while x.num_cycles < 0x40 {
x.step_system();
}
- assert_eq!(x.dump_video_to_string(), load_result("gemma_integration_level_1_test.asc"));
+ assert_eq!(
+ x.dump_video_to_string(),
+ load_result("gemma_integration_level_1_test.asc")
+ );
}
#[test]
@@ -56,21 +61,25 @@ fn level2_test() {
// ...then verify that the current video memory of the chip-8
// simulator matches what we expect it to be
- assert_eq!(x.dump_video_to_string(), load_result("gemma_integration_ibm_rom_output.asc"));
+ assert_eq!(
+ x.dump_video_to_string(),
+ load_result("gemma_integration_ibm_rom_output.asc")
+ );
}
#[test]
fn level3_test() {
let mut x = Chip8Computer::new();
- x.load_bytes_to_memory(
- 0x200, (&load_rom("3-corax+.ch8"))
- );
+ x.load_bytes_to_memory(0x200, (&load_rom("3-corax+.ch8")));
for i in 0..0x180 {
x.step_system();
}
- assert_eq!(x.dump_video_to_string(), load_result("gemma_integration_corax_plus.asc"));
+ assert_eq!(
+ x.dump_video_to_string(),
+ load_result("gemma_integration_corax_plus.asc")
+ );
}
#[test]
@@ -80,12 +89,18 @@ fn rps_test() {
for _ in 0..0xF0 {
x.step_system();
}
- assert_eq!(x.dump_video_to_string(), load_result("gemma_integration_rps_stage1.asc"));
+ assert_eq!(
+ x.dump_video_to_string(),
+ load_result("gemma_integration_rps_stage1.asc")
+ );
x.keypad.push_key(0x01);
for _ in 0..0x200 {
x.step_system();
}
- assert_eq!(x.dump_video_to_string(), load_result("gemma_integration_rps_stage2.asc"));
+ assert_eq!(
+ x.dump_video_to_string(),
+ load_result("gemma_integration_rps_stage2.asc")
+ );
}
#[test]
@@ -97,5 +112,8 @@ fn level4_test() {
x.step_system();
}
- assert_eq!(x.dump_video_to_string(), load_result("gemma_integration_flags.asc"));
+ assert_eq!(
+ x.dump_video_to_string(),
+ load_result("gemma_integration_flags.asc")
+ );
}
diff --git a/gemma/tests/state_tests.rs b/gemma/tests/state_tests.rs
index 3a3e868..e0f51bf 100644
--- a/gemma/tests/state_tests.rs
+++ b/gemma/tests/state_tests.rs
@@ -1,34 +1,55 @@
-use flate2::write::{GzEncoder, GzDecoder};
+use flate2::write::{GzDecoder, GzEncoder};
use flate2::Compression;
-use std::io::prelude::*;
use gemma::chip8::computer::Chip8Computer;
+use std::io::prelude::*;
fn load_result(to_load: &str) -> String {
- std::fs::read_to_string(format!("resources/test/state/{}", to_load)).unwrap()
+ let full_path = format!("resources/test/state/{}", to_load);
+ println!("Loading state => (([{}]))", full_path);
+ std::fs::read_to_string(full_path).unwrap()
}
-fn load_compressed_result(to_load: &str) -> String {
- // load the file...
- // ...then uncompress the string.
- let mut decoder = GzDecoder::new(Vec::new());
- decoder.write_all(load_result(to_load).as_ref()).expect("Decompression failed");
- let decompressed_data = decoder.finish().expect("Failed to finish decompression");
- String::from_utf8(decompressed_data).expect("Invalid UTF-8")
+fn load_compressed_result(file_path: &str) -> io::Result {
+ // Load the compressed file contents
+ let compressed_data = fs::read(file_path)?;
+
+ // Create a GzDecoder to uncompress the data
+ let mut decoder = GzDecoder::new(&compressed_data[..]);
+ let mut decompressed_data = String::new();
+
+ // Read the decompressed data directly into a String
+ decoder.read_to_string(&mut decompressed_data)?;
+
+ Ok(decompressed_data)
}
fn load_rom(to_load: &str) -> Vec {
- std::fs::read(format!("resources/roms/{}", to_load)).unwrap()
+ std::fs::read(format!("resources/test/roms/{}", to_load)).unwrap()
}
+
#[test]
-fn smoke_round_trip_serialize_deserialize() {
- let x = Chip8Computer::new();
- let expected_string = load_result("smoke_001_round_trip_serialize_deserialize.json");
- let serialized = serde_json::to_string(&x).unwrap();
- let deserialized: Chip8Computer = serde_json::from_str(&expected_string).unwrap();
- println!("SERIALIZED [{}]", serialized);
- // TODO: using trim here is a hack to handle editors adding a newline at the end of a file...even a 1 line file
- assert_eq!(serialized.trim(), expected_string.trim());
- assert_eq!(deserialized, x);
+fn test_serialization_round_trip() {
+ let original_computer = Chip8Computer::new();
+ let expected_json = load_result("smoke_001_round_trip_serialize_deserialize.json");
+
+ // Serialize the Chip8Computer instance
+ let serialized = serde_json::to_string(&original_computer).expect("Serialization failed");
+
+ // Compare the serialized output to the expected JSON
+ println!("Serialized Output: [{}]", serialized);
+ assert_eq!(
+ serialized.trim(),
+ expected_json.trim(),
+ "Serialized output does not match expected JSON"
+ );
+
+ // Deserialize back to Chip8Computer and assert equality
+ let deserialized_computer: Chip8Computer =
+ serde_json::from_str(&serialized).expect("Deserialization failed");
+ assert_eq!(
+ deserialized_computer, original_computer,
+ "Deserialized instance does not match the original"
+ );
}
#[test]
@@ -36,5 +57,7 @@ fn computer_001_system_zero_state() {
let x = Chip8Computer::new();
let expected_string = load_compressed_result("smoke_002_round_trip_serialize_deserialize.tflt");
let serialized = serde_json::to_string(&x).unwrap();
+ assert_eq!(serialized, expected_string);
+}
}
diff --git a/gemma/tests/unit_tests.rs b/gemma/tests/unit_tests.rs
index 5ba7d99..971c64e 100644
--- a/gemma/tests/unit_tests.rs
+++ b/gemma/tests/unit_tests.rs
@@ -4,7 +4,7 @@ use gemma::chip8::delay_timer::DelayTimer;
use gemma::chip8::instructions::Chip8CpuInstructions;
use gemma::chip8::instructions::Chip8CpuInstructions::JPX;
use gemma::chip8::keypad::Keypad;
-use gemma::chip8::quirk_modes::QuirkMode::{Chip8, SChipModern, XOChip};
+use gemma::chip8::quirk_modes::QuirkMode::{self, Chip8, SChipModern, XOChip};
use gemma::chip8::registers::Chip8Registers;
use gemma::chip8::sound_timer::SoundTimer;
use gemma::chip8::stack::Chip8Stack;
@@ -14,6 +14,7 @@ use gemma::chip8::video::{Chip8Video, Chip8VideoModes};
use gemma::constants::*;
use log::debug;
use rand::random;
+use serde::Serialize;
const TEST_OUTPUT_SAMPLE_DIR: &str = "../resources/test/";
@@ -1497,3 +1498,26 @@ fn system_memory_new() {
let x = Chip8SystemMemory::new();
assert!(true);
}
+
+#[test]
+fn video_lowres_schip_draw_chip8_sprite() {
+ let mut x = Chip8Computer::new();
+ x.quirk_mode = QuirkMode::SChipModern;
+ x.video_memory.set_lowres();
+ // point at the 1 from chip8
+ x.registers.poke_i(0x0005);
+ // point to 1,2 for the drawing
+ x.registers.poke(0x01, 0x01);
+ x.registers.poke(0x02, 0x02);
+ Chip8CpuInstructions::DRW(0x01, 0x01, 0x08).execute(&mut x);
+ let expected_state = read_test_result("state/video_lowres_schip_draw_chip8_sprite_result.json");
+ let actual_state = serde_json::to_string(&x).unwrap();
+ assert_eq!(expected_state, actual_state);
+}
+
+#[test]
+fn video_lowres_schip_draw_schip_sprite() {}
+#[test]
+fn video_highres_schip_draw_chip8_sprite() {}
+#[test]
+fn video_highres_schip_draw_schip_sprite() {}
diff --git a/gemmaegui/src/bin/gemmaegui.rs b/gemmaegui/src/bin/gemmaegui.rs
index 5ad6758..373cb6c 100644
--- a/gemmaegui/src/bin/gemmaegui.rs
+++ b/gemmaegui/src/bin/gemmaegui.rs
@@ -1,18 +1,38 @@
-use std::path::PathBuf;
-use std::time::Instant;
use crate::support::gemma_egui_support::{EGuiFileList, GemmaEguiSupport};
use eframe::egui;
use egui::Key;
use gemma::chip8::computer::Chip8Computer;
use gemma::chip8::computer_manager::Chip8ComputerManager;
+use std::path::PathBuf;
+use std::time::Instant;
mod support;
const LIN_KEYS: [[(Key, u8); 4]; 4] = [
- [(Key::Num1, 0x01), (Key::Num2, 0x02), (Key::Num3, 0x03), (Key::Num4, 0x0c)],
- [(Key::Q, 0x04), (Key::W, 0x05), (Key::E, 0x06), (Key::R, 0x0d)],
- [(Key::A, 0x07), (Key::S, 0x08), (Key::D, 0x09), (Key::F, 0x0e)],
- [(Key::Z, 0x0a), (Key::X, 0x00), (Key::C, 0x0b), (Key::V, 0x0F)]
+ [
+ (Key::Num1, 0x01),
+ (Key::Num2, 0x02),
+ (Key::Num3, 0x03),
+ (Key::Num4, 0x0c),
+ ],
+ [
+ (Key::Q, 0x04),
+ (Key::W, 0x05),
+ (Key::E, 0x06),
+ (Key::R, 0x0d),
+ ],
+ [
+ (Key::A, 0x07),
+ (Key::S, 0x08),
+ (Key::D, 0x09),
+ (Key::F, 0x0e),
+ ],
+ [
+ (Key::Z, 0x0a),
+ (Key::X, 0x00),
+ (Key::C, 0x0b),
+ (Key::V, 0x0F),
+ ],
];
#[derive(Default)]
@@ -88,29 +108,40 @@ fn main() -> eframe::Result {
// state.is_running = false;
}
if ui.button("Reset").clicked() {
- computer.reset();
+ computer.reset(computer.quirks_mode());
// state.is_running = false;
}
});
- if ui.button(format!("Load {}", state.selected_filename)).clicked() {
- // println!("Should load the bin now");
- let read_bin = std::fs::read(PathBuf::from(format!("resources/roms/{}", state.selected_filename))).unwrap();
+ if ui
+ .button(format!("Load {}", state.selected_filename))
+ .clicked()
+ {
+ // println!("Should load the bin now");
+ let read_bin = std::fs::read(PathBuf::from(format!(
+ "resources/roms/{}",
+ state.selected_filename
+ )))
+ .unwrap();
computer.load_new_program_to_system_memory(read_bin);
};
- EGuiFileList::display_path(PathBuf::from("resources/roms"), &mut state.selected_filename, ui);
+ EGuiFileList::display_path(
+ PathBuf::from("resources/roms"),
+ &mut state.selected_filename,
+ ui,
+ );
ctx.input(|input| {
// loop through the keys we are checking...
for row in LIN_KEYS {
for (keyboard_key, keypad_key) in row {
if input.key_pressed(keyboard_key) {
computer.press_key(keypad_key);
- // println!("KEY {keypad_key:02x} DOWN");
+ // println!("KEY {keypad_key:02x} DOWN");
} else {
// release it if the user just did
if computer.is_key_pressed(keypad_key) {
computer.release_key(keypad_key);
- // println!("KEY {keypad_key:02x} up");
+ // println!("KEY {keypad_key:02x} up");
}
}
}
diff --git a/gemmaegui/src/bin/support/gemma_egui_support.rs b/gemmaegui/src/bin/support/gemma_egui_support.rs
index b504d43..eccf7a2 100644
--- a/gemmaegui/src/bin/support/gemma_egui_support.rs
+++ b/gemmaegui/src/bin/support/gemma_egui_support.rs
@@ -1,10 +1,10 @@
+use crate::Chip8Computer;
+use egui::Rect;
+use egui::Ui;
+use egui::Vec2;
+use egui::{Align, Color32, ComboBox, Pos2};
use std::fs::read_dir;
use std::path::PathBuf;
-use egui::{Align, Color32, ComboBox, Pos2, TextBuffer};
-use egui::Rect;
-use egui::Vec2;
-use egui::Ui;
-use crate::Chip8Computer;
const CELL_WIDTH: f32 = 5.0;
const CELL_HEIGHT: f32 = 5.0;
@@ -14,21 +14,26 @@ impl EGuiFileList {
pub fn display_path(root: PathBuf, selected_filename: &mut String, ui: &mut Ui) {
let working_filename = selected_filename.clone();
ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| {
- // ui.label(format!("Displaying {}", root.to_str().unwrap_or("Unable to Load Path")));
+ // ui.label(format!("Displaying {}", root.to_str().unwrap_or("Unable to Load Path")));
ComboBox::from_label("Select ROM")
.selected_text(selected_filename.clone())
.show_ui(ui, |ui| {
-
let mut sorted_options = vec![];
for option in read_dir(root.as_path()).unwrap() {
- let to_push = option.unwrap().file_name().into_string().unwrap_or( String::new());
+ let to_push = option
+ .unwrap()
+ .file_name()
+ .into_string()
+ .unwrap_or(String::new());
sorted_options.push(to_push);
}
sorted_options.sort();
for item in sorted_options {
- // Add each option to the ComboBox
- if ui.selectable_label(selected_filename.eq(&item.as_str()), item.clone()).clicked()
+ // Add each option to the ComboBox
+ if ui
+ .selectable_label(selected_filename.eq(&item.as_str()), item.clone())
+ .clicked()
{
*selected_filename = item;
}
@@ -43,36 +48,41 @@ pub struct GemmaEguiSupport {}
impl GemmaEguiSupport {
pub fn controls_view(system: &mut Chip8Computer, ui: &mut Ui) {
-
ui.with_layout(egui::Layout::left_to_right(Align::TOP), |ui| {
- // ui.checkbox(&mut state.display_memory, "Display Memory");
- // ui.checkbox(&mut state.display_video, "Display Video");
- // ui.checkbox(&mut state.display_registers, "Display Registers");
+ // ui.checkbox(&mut state.display_memory, "Display Memory");
+ // ui.checkbox(&mut state.display_video, "Display Video");
+ // ui.checkbox(&mut state.display_registers, "Display Registers");
});
}
pub fn registers_view(system: &Chip8Computer, ui: &mut Ui) {
- ui.label(format!("V0-7: {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} ",
- system.registers.peek(0x00),
- system.registers.peek(0x01),
- system.registers.peek(0x02),
- system.registers.peek(0x03),
- system.registers.peek(0x04),
- system.registers.peek(0x05),
- system.registers.peek(0x06),
- system.registers.peek(0x07)
+ ui.label(format!(
+ "V0-7: {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} ",
+ system.registers.peek(0x00),
+ system.registers.peek(0x01),
+ system.registers.peek(0x02),
+ system.registers.peek(0x03),
+ system.registers.peek(0x04),
+ system.registers.peek(0x05),
+ system.registers.peek(0x06),
+ system.registers.peek(0x07)
));
- ui.label(format!("V8-F: {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} ",
- system.registers.peek(0x08),
- system.registers.peek(0x09),
- system.registers.peek(0x0A),
- system.registers.peek(0x0B),
- system.registers.peek(0x0C),
- system.registers.peek(0x0D),
- system.registers.peek(0x0E),
- system.registers.peek(0x0F)
+ ui.label(format!(
+ "V8-F: {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} ",
+ system.registers.peek(0x08),
+ system.registers.peek(0x09),
+ system.registers.peek(0x0A),
+ system.registers.peek(0x0B),
+ system.registers.peek(0x0C),
+ system.registers.peek(0x0D),
+ system.registers.peek(0x0E),
+ system.registers.peek(0x0F)
+ ));
+ ui.label(format!(
+ "PC: {:04x}\tI: {:04x}",
+ system.registers.peek_pc(),
+ system.registers.peek_i()
));
- ui.label(format!("PC: {:04x}\tI: {:04x}", system.registers.peek_pc(), system.registers.peek_i()));
}
pub fn video_view(system: &Chip8Computer, ui: &mut Ui) {
@@ -95,7 +105,7 @@ impl GemmaEguiSupport {
// system.video_memory.peek(data_offset));
}
}
- // thread::sleep(Duration::from_secs(1));
+ // thread::sleep(Duration::from_secs(1));
}
pub fn memory_view(system: &Chip8Computer, ui: &mut Ui) {
diff --git a/gemmaimgui/src/bin/gemmaimgui.rs b/gemmaimgui/src/bin/gemmaimgui.rs
index 948b83d..2440ac9 100644
--- a/gemmaimgui/src/bin/gemmaimgui.rs
+++ b/gemmaimgui/src/bin/gemmaimgui.rs
@@ -3,6 +3,7 @@ use gemma::chip8::computer_manager::Chip8ComputerManager;
use std::path::Path;
use std::time::Instant;
use std::{default::Default, path::PathBuf};
+use support::ui_state::ROMPATH_DEFAULT;
use support::{emmagui_support::GemmaImguiSupport, ui_state::ImGuiUiState};
mod support;
@@ -45,7 +46,12 @@ fn main() {
let mut system = Chip8ComputerManager::default();
println!("GOT A ROMS_DIRECTORY => [{:?}]", cli_options.roms_directory);
- let mut ui_state = ImGuiUiState::new(cli_options.roms_directory);
+ println!("{:?}", std::env::var("PWD"));
+ let mut ui_state = if cli_options.roms_directory.is_none() {
+ ImGuiUiState::new(Some(PathBuf::from(ROMPATH_DEFAULT)))
+ } else {
+ ImGuiUiState::new(cli_options.roms_directory)
+ };
support::simple_init(file!(), move |_, ui| {
let current_time = Instant::now();
diff --git a/gemmaimgui/src/bin/support/emmagui_support.rs b/gemmaimgui/src/bin/support/emmagui_support.rs
index 0fdbb40..3dad500 100644
--- a/gemmaimgui/src/bin/support/emmagui_support.rs
+++ b/gemmaimgui/src/bin/support/emmagui_support.rs
@@ -1,8 +1,10 @@
use crate::support::gui_file_list::GuiFileList;
use crate::ImGuiUiState;
+use dimensioned::ucum::f32consts::CUP_M;
use gemma::chip8::computer::Chip8Computer;
use gemma::chip8::computer_manager::Chip8ComputerManager;
use gemma::chip8::computer_manager::ManagerDumpables::{Keyboard, Registers, Video};
+use gemma::chip8::quirk_modes::QuirkMode::{self, *};
use gemma::chip8::system_memory::Chip8SystemMemory;
use gemma::constants::CHIP8_KEYBOARD;
use imgui::{CollapsingHeader, Condition, ImColor32, Ui};
@@ -116,7 +118,7 @@ impl GemmaImguiSupport {
.build(|| {
/* System Step Counter */
ui.text(format!("Step {:04x}", system_to_control.num_cycles()).as_str());
-
+ ui.text(format!("Mode {}", system_to_control.quirks_mode()));
/* ROM Lister */
if CollapsingHeader::new("Roms").build(ui) {
let new_filename = GuiFileList::display_path(
@@ -158,7 +160,7 @@ impl GemmaImguiSupport {
system_to_control.state().memory.peek(0x200),
system_to_control.state().memory.peek(0x201),
];
- let mut show_buttons = bytes[0] != 0 || bytes[1] == 0xe0;
+ let show_buttons = bytes[0] != 0 || bytes[1] == 0xe0;
if show_buttons {
if ui.button("Step") {
@@ -175,7 +177,7 @@ impl GemmaImguiSupport {
}
ui.same_line();
if ui.button("Reset") {
- system_to_control.reset();
+ system_to_control.reset(system_to_control.quirks_mode());
}
if ui.button("Dump Video Memory") {
println!("{}", system_to_control.dump_to_string(Video));
@@ -201,6 +203,27 @@ impl GemmaImguiSupport {
ui.input_int("Target IPS", &mut gui_state.target_ips)
.build();
};
+
+ let selectors = [Chip8, SChipModern, XOChip];
+ for current_selector in selectors {
+ let mut working_selector =
+ ui.selectable_config(current_selector.clone().to_string());
+ match system_to_control.quirks_mode() {
+ Chip8 => {
+ working_selector = working_selector.selected(true);
+ }
+ SChipModern => {
+ working_selector = working_selector.selected(true);
+ }
+ XOChip => {
+ working_selector = working_selector.selected(true);
+ }
+ }
+ if working_selector.build() {
+ system_to_control.reset(current_selector);
+ println!("CLICK ON {}", ¤t_selector);
+ }
+ }
});
}
diff --git a/gemmaimgui/src/bin/support/ui_state.rs b/gemmaimgui/src/bin/support/ui_state.rs
index 89fa4c2..e36219d 100644
--- a/gemmaimgui/src/bin/support/ui_state.rs
+++ b/gemmaimgui/src/bin/support/ui_state.rs
@@ -16,7 +16,7 @@ pub struct ImGuiUiState {
pub frame_time: u32,
pub last_frame_instant: Instant,
pub target_ips: i32,
- pub roms_root_path: PathBuf,
+ pub roms_root_path: PathBuf
}
impl Clone for ImGuiUiState {
@@ -33,8 +33,7 @@ impl Clone for ImGuiUiState {
frame_time: self.frame_time,
last_frame_instant: self.last_frame_instant,
target_ips: self.target_ips,
-
- roms_root_path: self.roms_root_path.to_owned(),
+ roms_root_path: self.roms_root_path.to_owned()
}
}
}
@@ -53,7 +52,7 @@ impl Default for ImGuiUiState {
frame_time: 16,
last_frame_instant: Instant::now(),
target_ips: 200000,
- roms_root_path: PathBuf::from(ROMPATH_DEFAULT),
+ roms_root_path: PathBuf::from(ROMPATH_DEFAULT)
}
}
}
diff --git a/gemmasdl2/src/bin/gemmasdl2.rs b/gemmasdl2/src/bin/gemmasdl2.rs
index 35b3613..acff6dc 100644
--- a/gemmasdl2/src/bin/gemmasdl2.rs
+++ b/gemmasdl2/src/bin/gemmasdl2.rs
@@ -1,13 +1,13 @@
mod support;
-use std::{sync::Arc, time::Instant};
+use crate::support::gemma_egui_support::GemmaEguiSupport;
use anyhow::Context;
use egui::TextBuffer;
use egui_glow::glow::{HasContext, COLOR_BUFFER_BIT};
use egui_sdl2_platform::sdl2;
-use sdl2::event::{Event, WindowEvent};
use gemma::chip8::computer::Chip8Computer;
+use sdl2::event::{Event, WindowEvent};
+use std::{sync::Arc, time::Instant};
use support::timestep::TimeStep;
-use crate::support::gemma_egui_support::GemmaEguiSupport;
const SCREEN_WIDTH: u32 = 800;
const SCREEN_HEIGHT: u32 = 480;
@@ -15,14 +15,19 @@ const SCREEN_HEIGHT: u32 = 480;
async fn run() -> anyhow::Result {
// Initialize SDL2 and video subsystem
let sdl = sdl2::init().map_err(|e| anyhow::anyhow!("Failed to create SDL context: {}", e))?;
- let mut video = sdl.video().map_err(|e| anyhow::anyhow!("Failed to initialize SDL video subsystem: {}", e))?;
+ let mut video = sdl
+ .video()
+ .map_err(|e| anyhow::anyhow!("Failed to initialize SDL video subsystem: {}", e))?;
// Create SDL2 window and OpenGL context
- let window = video.window("Window", SCREEN_WIDTH, SCREEN_HEIGHT)
+ let window = video
+ .window("Window", SCREEN_WIDTH, SCREEN_HEIGHT)
.opengl()
.position_centered()
.build()?;
- let _gl_context = window.gl_create_context().expect("Failed to create GL context");
+ let _gl_context = window
+ .gl_create_context()
+ .expect("Failed to create GL context");
// Load OpenGL functions
let gl = unsafe {
@@ -34,7 +39,9 @@ async fn run() -> anyhow::Result {
// Setup Egui and SDL2 platform
let mut platform = egui_sdl2_platform::Platform::new(window.size())?;
- let mut event_pump = sdl.event_pump().map_err(|e| anyhow::anyhow!("Failed to get SDL event pump: {}", e))?;
+ let mut event_pump = sdl
+ .event_pump()
+ .map_err(|e| anyhow::anyhow!("Failed to get SDL event pump: {}", e))?;
// Initial settings
let color = [0.0, 0.0, 0.0, 1.0]; // Background color
@@ -75,7 +82,12 @@ async fn run() -> anyhow::Result {
// Paint Egui outputs and update textures
let size = window.size();
- painter.paint_and_update_textures([size.0, size.1], 1.0, paint_jobs.as_slice(), &full_output.textures_delta);
+ painter.paint_and_update_textures(
+ [size.0, size.1],
+ 1.0,
+ paint_jobs.as_slice(),
+ &full_output.textures_delta,
+ );
window.gl_swap_window();
// Run the timestep logic
@@ -85,38 +97,56 @@ async fn run() -> anyhow::Result {
for event in event_pump.poll_iter() {
match event {
Event::Quit { .. }
- | Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::Escape), .. } => break 'main,
- Event::Window { window_id, win_event, .. } if window_id == window.id() => {
+ | Event::KeyDown {
+ keycode: Some(sdl2::keyboard::Keycode::Escape),
+ ..
+ } => break 'main,
+ Event::Window {
+ window_id,
+ win_event,
+ ..
+ } if window_id == window.id() => {
if let WindowEvent::Close = win_event {
break 'main;
}
}
- Event::KeyUp { keycode: Some(sdl2::keyboard::Keycode::F3), .. } => {
+ Event::KeyUp {
+ keycode: Some(sdl2::keyboard::Keycode::F3),
+ ..
+ } => {
println!("USER PRESSED F3 -> running");
is_running = true;
}
- Event::KeyUp { keycode: Some(sdl2::keyboard::Keycode::F4), .. } => {
+ Event::KeyUp {
+ keycode: Some(sdl2::keyboard::Keycode::F4),
+ ..
+ } => {
println!("USER PRESSED F4 -> stopping");
is_running = false;
}
- Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::F5), .. } => {
+ Event::KeyDown {
+ keycode: Some(sdl2::keyboard::Keycode::F5),
+ ..
+ } => {
println!("USER PRESSED F5 -> Step");
computer.step_system();
}
- Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::F6), .. } => {
+ Event::KeyDown {
+ keycode: Some(sdl2::keyboard::Keycode::F6),
+ ..
+ } => {
println!("USER PRESSED F6 -> RESET");
- computer.reset();
+ computer.reset(computer.quirk_mode.clone());
}
Event::ControllerButtonDown { which, .. } => {
println!("PLAYER {which} DOWN");
}
- Event::ControllerButtonDown { button, .. } => {
- println!("BUTTON {:?}", button);
- }
- Event::JoyButtonDown {button_idx, .. } => {
+ Event::JoyButtonDown { button_idx, .. } => {
println!("JoyButtonDown {}", button_idx);
}
- Event::JoyAxisMotion { which, axis_idx, .. } => {
+ Event::JoyAxisMotion {
+ which, axis_idx, ..
+ } => {
println!("JoyAxismotion {which} {axis_idx}");
}
_ => platform.handle_event(&event, &sdl, &video),
diff --git a/gemmasdl2/src/bin/support/gemma_egui_support.rs b/gemmasdl2/src/bin/support/gemma_egui_support.rs
index 4205938..3f4eda3 100644
--- a/gemmasdl2/src/bin/support/gemma_egui_support.rs
+++ b/gemmasdl2/src/bin/support/gemma_egui_support.rs
@@ -1,10 +1,10 @@
+use crate::Chip8Computer;
+use egui::Rect;
+use egui::Ui;
+use egui::Vec2;
+use egui::{Align, Color32, ComboBox, Pos2};
use std::fs::read_dir;
use std::path::PathBuf;
-use egui::{Align, Color32, ComboBox, Pos2, TextBuffer};
-use egui::Rect;
-use egui::Vec2;
-use egui::Ui;
-use crate::Chip8Computer;
const CELL_WIDTH: f32 = 5.0;
const CELL_HEIGHT: f32 = 5.0;
@@ -14,21 +14,26 @@ impl EGuiFileList {
pub fn display_path(root: PathBuf, selected_filename: &mut String, ui: &mut Ui) {
let working_filename = selected_filename.clone();
ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| {
- // ui.label(format!("Displaying {}", root.to_str().unwrap_or("Unable to Load Path")));
+ // ui.label(format!("Displaying {}", root.to_str().unwrap_or("Unable to Load Path")));
ComboBox::from_label("Select ROM")
.selected_text(selected_filename.clone())
.show_ui(ui, |ui| {
-
let mut sorted_options = vec![];
for option in read_dir(root.as_path()).unwrap() {
- let to_push = option.unwrap().file_name().into_string().unwrap_or( String::new());
+ let to_push = option
+ .unwrap()
+ .file_name()
+ .into_string()
+ .unwrap_or(String::new());
sorted_options.push(to_push);
}
sorted_options.sort();
for item in sorted_options {
- // Add each option to the ComboBox
- if ui.selectable_label(selected_filename.eq(&item.as_str()), item.clone()).clicked()
+ // Add each option to the ComboBox
+ if ui
+ .selectable_label(selected_filename.eq(&item.as_str()), item.clone())
+ .clicked()
{
*selected_filename = item;
}
@@ -43,65 +48,69 @@ pub struct GemmaEguiSupport {}
impl GemmaEguiSupport {
pub fn controls_view(system: &mut Chip8Computer, ui: &mut Ui) {
- ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| {
- if ui.button("Start").clicked() {
- println!("Start");
- // state.is_running = true;
- }
- if ui.button("Step").clicked() {
- system.step_system();
- }
-
- if ui.button("Stop").clicked() {
- println!("STOP");
- // state.is_running = false;
- }
- if ui.button("Reset").clicked() {
- system.reset();
- // state.is_running = false;
- }
- });
-
- // if ui.button(format!("Load {}", state.selected_rom_filename)).clicked() {
- // load the bin...
- // let read_bin = std::fs::read(PathBuf::from(format!("resources/roms/{}", state.selected_rom_filename))).unwrap();
- // ...then feed the system.
- // system.load_bytes_to_memory(0x200, &read_bin);
- // println!("Loaded {}", state.selected_rom_filename);
- // }
- // EGuiFileList::display_path(PathBuf::from("resources/roms"), &mut state.selected_rom_filename, ui);
+ ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| {
+ if ui.button("Start").clicked() {
+ println!("Start");
+ // state.is_running = true;
+ }
+ if ui.button("Step").clicked() {
+ system.step_system();
+ }
+ if ui.button("Stop").clicked() {
+ println!("STOP");
+ // state.is_running = false;
+ }
+ if ui.button("Reset").clicked() {
+ system.reset(system.quirk_mode.clone());
+ // state.is_running = false;
+ }
+ });
+ // if ui.button(format!("Load {}", state.selected_rom_filename)).clicked() {
+ // load the bin...
+ // let read_bin = std::fs::read(PathBuf::from(format!("resources/roms/{}", state.selected_rom_filename))).unwrap();
+ // ...then feed the system.
+ // system.load_bytes_to_memory(0x200, &read_bin);
+ // println!("Loaded {}", state.selected_rom_filename);
+ // }
+ // EGuiFileList::display_path(PathBuf::from("resources/roms"), &mut state.selected_rom_filename, ui);
ui.with_layout(egui::Layout::left_to_right(Align::TOP), |ui| {
- // ui.checkbox(&mut state.display_memory, "Display Memory");
- // ui.checkbox(&mut state.display_video, "Display Video");
- // ui.checkbox(&mut state.display_registers, "Display Registers");
+ // ui.checkbox(&mut state.display_memory, "Display Memory");
+ // ui.checkbox(&mut state.display_video, "Display Video");
+ // ui.checkbox(&mut state.display_registers, "Display Registers");
});
}
pub fn registers_view(system: &Chip8Computer, ui: &mut Ui) {
- ui.label(format!("V0-7: {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} ",
- system.registers.peek(0x00),
- system.registers.peek(0x01),
- system.registers.peek(0x02),
- system.registers.peek(0x03),
- system.registers.peek(0x04),
- system.registers.peek(0x05),
- system.registers.peek(0x06),
- system.registers.peek(0x07)
+ ui.label(format!(
+ "V0-7: {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} ",
+ system.registers.peek(0x00),
+ system.registers.peek(0x01),
+ system.registers.peek(0x02),
+ system.registers.peek(0x03),
+ system.registers.peek(0x04),
+ system.registers.peek(0x05),
+ system.registers.peek(0x06),
+ system.registers.peek(0x07)
));
- ui.label(format!("V8-F: {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} ",
- system.registers.peek(0x08),
- system.registers.peek(0x09),
- system.registers.peek(0x0A),
- system.registers.peek(0x0B),
- system.registers.peek(0x0C),
- system.registers.peek(0x0D),
- system.registers.peek(0x0E),
- system.registers.peek(0x0F)
+ ui.label(format!(
+ "V8-F: {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} ",
+ system.registers.peek(0x08),
+ system.registers.peek(0x09),
+ system.registers.peek(0x0A),
+ system.registers.peek(0x0B),
+ system.registers.peek(0x0C),
+ system.registers.peek(0x0D),
+ system.registers.peek(0x0E),
+ system.registers.peek(0x0F)
+ ));
+ ui.label(format!(
+ "PC: {:04x}\tI: {:04x}",
+ system.registers.peek_pc(),
+ system.registers.peek_i()
));
- ui.label(format!("PC: {:04x}\tI: {:04x}", system.registers.peek_pc(), system.registers.peek_i()));
}
pub fn video_view(system: &Chip8Computer, ui: &mut Ui) {
@@ -124,7 +133,7 @@ impl GemmaEguiSupport {
// system.video_memory.peek(data_offset));
}
}
- // thread::sleep(Duration::from_secs(1));
+ // thread::sleep(Duration::from_secs(1));
}
pub fn memory_view(system: &Chip8Computer, ui: &mut Ui) {
diff --git a/gemmautil/src/bin/ch8asm.rs b/gemmautil/src/bin/ch8asm.rs
index 39e4d4f..914ecd4 100644
--- a/gemmautil/src/bin/ch8asm.rs
+++ b/gemmautil/src/bin/ch8asm.rs
@@ -1,9 +1,14 @@
-use std::fs;
+use gemma::chip8::util::InstructionUtil;
+use gemma::chip8::{instructions::Chip8CpuInstructions, quirk_modes::QuirkMode};
+use std::fs::{self, File};
+use std::io::Write;
// Ch8Asm
// Converts well formed CH8ASM.
// no variables.
// no labels.
// nothing fun.
+// VALID FORMAT
+// <0x|0X>[, ][<0x|OX>[, ][<0x|OX>]][; comment]
use pest::Parser;
use pest_derive::Parser;
@@ -14,62 +19,30 @@ pub struct Chip8AsmParser;
fn main() {
println!("Taxation is Theft");
- let unparsed = fs::read_to_string("resources/test/gemma_disassembler_1_chip_logo_ch8_asm.asc").expect("Unable to read input");
+ let unparsed = fs::read_to_string("resources/test/gemma_disassembler_manual_document.asc")
+ .expect("Unable to read input");
- let file = Chip8AsmParser::parse(Rule::file, &unparsed).expect("Unable to parse. Try again.")
- .next().unwrap();
+ let file = Chip8AsmParser::parse(Rule::file, &unparsed)
+ .expect("Unable to parse. Try again.")
+ .next()
+ .unwrap();
+
+ println!("PREPARING TO WRITE THE TARGET FILE...");
+ let mut target_file = File::create("thisismytarget.ch8").expect("Unable to create target");
for record in file.into_inner() {
+ // let mut working_instruction = Chip8CpuInstructions::XXXXERRORINSTRUCTION;
match record.as_rule() {
- Rule::instruction => {
- println!("INSTRUCTION = {:?}", record.into_inner().flatten())
- }
- _ => {
- println!("UNHANDLED PART.");
+ Rule::record => {
+ print!("record = {:?}\t", record.as_str());
+ let x = Chip8CpuInstructions::from_str(record.as_str());
+ println!("DECODED TO {:?} {:04x}", x, x.encode());
+ let (high, low) = InstructionUtil::split_bytes(x.encode());
+ target_file
+ .write_all(&[high, low])
+ .expect("Unable to write to the file.");
}
+ _ => {}
}
}
}
-
-#[cfg(test)]
-mod test {
- use pest::Parser;
- use crate::{Chip8AsmParser, Rule};
-
- #[test]
- fn bits_all_parse() {
- println!("PARSED: {:?}",
- Chip8AsmParser::parse(Rule::instruction, "CLS")
- );
- println!("PARSED: {:?}",
- Chip8AsmParser::parse(Rule::parameter, "0x01")
- );
- let parsed = Chip8AsmParser::parse(Rule::comment, "; comment").unwrap();
- for i in parsed {
- println!("PARSED COMMENT -> {:?}", i);
- }
-
- let parsed =
- Chip8AsmParser::parse(Rule::record, "CLS ; comment").unwrap();
- for i in parsed {
- println!("RULE PAIR THING: {:?}", i);
- }
-
- let parsed = Chip8AsmParser::parse(Rule::record, "ADDI 0x01 ; comment");
- for i in parsed {
- println!("RULE PAIR THING: {:?}", i);
- }
-
- println!("PARSED: {:?}",
- Chip8AsmParser::parse(Rule::record, "ADD ADD ADD")
- );
-
- println!("PARSED: {:?}",
- Chip8AsmParser::parse(Rule::record, "ADD 0x01 0x02 ; Comment")
- );
-
- println!("PARSED: {:?}",
- Chip8AsmParser::parse(Rule::record, "ADD 0x01 0x02 ; Comment")
- );
- }
-}
\ No newline at end of file
diff --git a/gemmautil/src/chip8_asm.pest b/gemmautil/src/chip8_asm.pest
index 054c309..d305ba4 100644
--- a/gemmautil/src/chip8_asm.pest
+++ b/gemmautil/src/chip8_asm.pest
@@ -1,7 +1,7 @@
-WHITESPACE = _{ " " }
-instruction = { ASCII_ALPHA+ }
-parameter = { "0X" ~ ASCII_HEX_DIGIT+ }
-comment = { ";" ~ WHITESPACE* ~ ASCII* }
-parameters = { parameter ~ (WHITESPACE* ~ "," ~ WHITESPACE* ~ parameter)* }
-record = { instruction ~ WHITESPACE* ~ parameters? ~ WHITESPACE* ~ comment? }
-file = { SOI ~ (record ~ ("\r\n" | "\n"))* ~ EOI }
+WHITESPACE = _{ " " | "\t" } // Allow tabs and multiple spaces
+instruction = { ASCII_ALPHA+ } // Instruction, e.g., "CLS" or "LDX"
+parameter = { ("0x" | "0X") ~ ASCII_HEX_DIGIT+ } // Parameter format, e.g., "0X250"
+comment = { ";" ~ (!"\n" ~ ANY)* } // Capture everything after ";", up to end of line
+parameters = { (parameter ~ (WHITESPACE* ~ ","* ~ WHITESPACE* ~ parameter)*)?} // Zero or more parameters
+record = { instruction ~ WHITESPACE* ~ parameters ~ WHITESPACE* ~ comment? } // Record structure
+file = { SOI ~ (record ~ (("\r\n" | "\n") | WHITESPACE*))* ~ EOI } // Allow for trailing newlines and empty lines
diff --git a/resources/custom/001-schip-display-sprite.ch8 b/resources/custom/001-schip-display-sprite.ch8
new file mode 100644
index 0000000..c0ff896
Binary files /dev/null and b/resources/custom/001-schip-display-sprite.ch8 differ
diff --git a/resources/custom/002-schip-scroll-tests.ch8 b/resources/custom/002-schip-scroll-tests.ch8
new file mode 100644
index 0000000..a9f978f
Binary files /dev/null and b/resources/custom/002-schip-scroll-tests.ch8 differ
diff --git a/resources/roms/000test1.ch8 b/resources/roms/000test1.ch8
new file mode 100644
index 0000000..1eff4db
Binary files /dev/null and b/resources/roms/000test1.ch8 differ
diff --git a/resources/test/gemma_disassembler_1_chip_logo_ch8_asm.asc b/resources/test/gemma_disassembler_1_chip_logo_ch8_asm.asc
index bea2542..39d4fbc 100644
--- a/resources/test/gemma_disassembler_1_chip_logo_ch8_asm.asc
+++ b/resources/test/gemma_disassembler_1_chip_logo_ch8_asm.asc
@@ -29,7 +29,7 @@ LDX 0X00, 0X18 ; 6018
LDI 0X2C8 ; A2C8
DRW 0X00, 0X01, 0X0F ; D01F
LDX 0X00, 0X20 ; 6020
-LDI 2D7 ; A2D7
+LDI 0X2D7 ; A2D7
DRW 0X00, 0X01, 0X0F ; D01F
LDX 0X00, 0X28 ; 6028
LDI 0X2E6 ; A2E6
diff --git a/resources/test/gemma_disassembler_manual_document.asc b/resources/test/gemma_disassembler_manual_document.asc
index a2f9a6e..7cd68ee 100644
--- a/resources/test/gemma_disassembler_manual_document.asc
+++ b/resources/test/gemma_disassembler_manual_document.asc
@@ -23,11 +23,11 @@ JPI 0x0123 ; Bytes [0xb123] offset [0x002a]
RND 0x01, 0x23 ; Bytes [0xc123] offset [0x002c]
DRW 0x01, 0x02, 0x03 ; Bytes [0xd123] offset [0x002e]
SKP 0x01 ; Bytes [0xe19e] offset [0x0030]
-SNKP ; Bytes [0xe1a1] offset [0x0032]
+SKNP 0x01 ; Bytes [0xe1a1] offset [0x0032]
LDRD 0x01 ; Bytes [0xf107] offset [0x0034]
LDRK 0x01 ; Bytes [0xf10a] offset [0x0036]
LDD 0x01 ; Bytes [0xf115] offset [0x0038]
-LIDS 0x01 ; Bytes [0xf118] offset [0x003a]
+LDIS 0x01 ; Bytes [0xf118] offset [0x003a]
ADDI 0x01 ; Bytes [0xf11e] offset [0x003c]
LDF 0x01 ; Bytes [0xf129] offset [0x003e]
BCD 0x01 ; Bytes [0xf133] offset [0x0040]
diff --git a/resources/test/roms/1-chip8-logo.ch8 b/resources/test/roms/1-chip8-logo.ch8
new file mode 100644
index 0000000..19c5cf3
Binary files /dev/null and b/resources/test/roms/1-chip8-logo.ch8 differ
diff --git a/resources/test/roms/2-ibm-logo.ch8 b/resources/test/roms/2-ibm-logo.ch8
new file mode 100644
index 0000000..677f227
Binary files /dev/null and b/resources/test/roms/2-ibm-logo.ch8 differ
diff --git a/resources/test/roms/3-corax+.ch8 b/resources/test/roms/3-corax+.ch8
new file mode 100644
index 0000000..9fc874c
Binary files /dev/null and b/resources/test/roms/3-corax+.ch8 differ
diff --git a/resources/test/roms/3a-random_number_test.ch8 b/resources/test/roms/3a-random_number_test.ch8
new file mode 100644
index 0000000..a6225e5
Binary files /dev/null and b/resources/test/roms/3a-random_number_test.ch8 differ
diff --git a/resources/test/roms/4-flags.ch8 b/resources/test/roms/4-flags.ch8
new file mode 100644
index 0000000..0698a10
Binary files /dev/null and b/resources/test/roms/4-flags.ch8 differ
diff --git a/resources/test/roms/5-quirks.ch8 b/resources/test/roms/5-quirks.ch8
new file mode 100644
index 0000000..1c87f6c
Binary files /dev/null and b/resources/test/roms/5-quirks.ch8 differ
diff --git a/resources/test/roms/RPS.ch8 b/resources/test/roms/RPS.ch8
new file mode 100644
index 0000000..3ea42d4
Binary files /dev/null and b/resources/test/roms/RPS.ch8 differ
diff --git a/resources/test/state/video_lowres_schip_draw_chip8_sprite_result.json b/resources/test/state/video_lowres_schip_draw_chip8_sprite_result.json
new file mode 100644
index 0000000..e69de29