diff --git a/.idea/workspace.xml b/.idea/workspace.xml index c1eaa0a..231b130 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -9,12 +9,13 @@ - - - + + + + + - - { - "keyToString": { - "Cargo.Build `Run emmagui`.executor": "Run", - "Cargo.Build `Run gemmaegui_viewer`.executor": "Run", - "Cargo.Build `Run gemmaegui`.executor": "Run", - "Cargo.Build `Run gemmaimgui`.executor": "Run", - "Cargo.Build `Run trevors_chip8_toy`.executor": "Run", - "Cargo.Build `Test chip8::computer::test::cls_test`.executor": "Run", - "Cargo.Build `Test chip8::instructions::test::LdStVx_test`.executor": "Run", - "Cargo.Build `Test chip8::instructions::test::random_produces_different_numbers`.executor": "Run", - "Cargo.Build `Test chip8::instructions::test::series8xy6_corex_tests`.executor": "Run", - "Cargo.Build `Test chip8::instructions::test::shl_vx_vy_test`.executor": "Run", - "Cargo.Build `Test chip8::util::test::byte_to_bool_changes`.executor": "Run", - "Cargo.Build `Test chip8::util::test::ubln`.executor": "Run", - "Cargo.Build `Test chip8::video::test::poke_byte_test`.executor": "Run", - "Cargo.Build `Test chip8::video::test::poke_byte`.executor": "Run", - "Cargo.Build `Test computer::test`.executor": "Run", - "Cargo.Build `Test instructions::test (1)`.executor": "Run", - "Cargo.Build `Test instructions::test`.executor": "Run", - "Cargo.Build gemma.executor": "Run", - "Cargo.Run emmagui.executor": "Run", - "Cargo.Run gemmaegui.executor": "Run", - "Cargo.Run gemmaegui_viewer.executor": "Debug", - "Cargo.Run gemmaimgui.executor": "Run", - "Cargo.Run trevors_chip8_toy.executor": "Debug", - "Cargo.Test chip8::computer::test::cls_test.executor": "Run", - "Cargo.Test chip8::computer::test::decoder_test_valid_instructions.executor": "Run", - "Cargo.Test chip8::instructions::test::LdStVx_test.executor": "Run", - "Cargo.Test chip8::instructions::test::LdVxDt_test.executor": "Run", - "Cargo.Test chip8::instructions::test::LdiAddr_test.executor": "Run", - "Cargo.Test chip8::instructions::test::RndVxByte_test.executor": "Run", - "Cargo.Test chip8::instructions::test::ShrVxVy_test.executor": "Run", - "Cargo.Test chip8::instructions::test::SneVxVy_test.executor": "Run", - "Cargo.Test chip8::instructions::test::decoder_test_invalid_instructions.executor": "Run", - "Cargo.Test chip8::instructions::test::draw_nibble_vx_vy_n_test.executor": "Debug", - "Cargo.Test chip8::instructions::test::encode_decode_test.executor": "Run", - "Cargo.Test chip8::instructions::test::random_produces_different_numbers.executor": "Run", - "Cargo.Test chip8::instructions::test::series4000_corex_tests.executor": "Run", - "Cargo.Test chip8::instructions::test::series8xy4_corex_tests.executor": "Run", - "Cargo.Test chip8::instructions::test::series8xy6_corex_tests.executor": "Run", - "Cargo.Test chip8::instructions::test::shl_vx_vy_test.executor": "Run", - "Cargo.Test chip8::instructions::test::subn_vx_vy_test.executor": "Run", - "Cargo.Test chip8::util::test::bool_to_byte_changes.executor": "Run", - "Cargo.Test chip8::util::test::byte_to_bool_changes.executor": "Run", - "Cargo.Test chip8::util::test::ubln.executor": "Run", - "Cargo.Test chip8::video::test::poke_byte.executor": "Run", - "Cargo.Test chip8::video::test::poke_byte_test.executor": "Run", - "Cargo.Test chip8::video::test::poke_sprite_test.executor": "Debug", - "Cargo.Test computer::test.executor": "Debug", - "Cargo.Test instructions::test (1).executor": "Debug", - "Cargo.Test instructions::test.executor": "Debug", - "Cargo.Test sound_timer::test.executor": "Run", - "Cargo.Test util::test.executor": "Run", - "Cargo.Test video::test.executor": "Coverage", - "RunOnceActivity.ShowReadmeOnStart": "true", - "RunOnceActivity.rust.reset.selective.auto.import": "true", - "git-widget-placeholder": "rename__instructions", - "last_opened_file_path": "/home/tmerritt/Projects/chip8_toy/gemmaimgui", - "node.js.detected.package.eslint": "true", - "node.js.selected.package.eslint": "(autodetect)", - "nodejs_package_manager_path": "npm", - "org.rust.cargo.project.model.PROJECT_DISCOVERY": "true", - "org.rust.cargo.project.model.impl.CargoExternalSystemProjectAware.subscribe.first.balloon": "", - "org.rust.first.attach.projects": "true", - "settings.editor.selected.configurable": "language.rust.build.tool.cargo" + +}]]> + - + - + diff --git a/Cargo.lock b/Cargo.lock index c65940c..2a6a087 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1835,6 +1835,8 @@ version = "0.1.0" dependencies = [ "clap", "gemma", + "pest", + "pest_derive", ] [[package]] @@ -3216,6 +3218,51 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "pest" +version = "2.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d214365f632b123a47fd913301e14c946c61d1c183ee245fa76eb752e59a02dd" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb55586734301717aea2ac313f50b2eb8f60d2fc3dc01d190eefa2e625f60c4e" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.79", +] + +[[package]] +name = "pest_meta" +version = "2.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b75da2a70cf4d9cb76833c990ac9cd3923c9a8905a8929789ce347c84564d03d" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + [[package]] name = "pin-project" version = "1.1.6" @@ -3748,6 +3795,17 @@ dependencies = [ "digest", ] +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "shlex" version = "1.3.0" @@ -4151,6 +4209,12 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + [[package]] name = "uds_windows" version = "1.1.0" diff --git a/gemma/src/chip8/computer_manager.rs b/gemma/src/chip8/computer_manager.rs index 120bd47..3eb7e49 100644 --- a/gemma/src/chip8/computer_manager.rs +++ b/gemma/src/chip8/computer_manager.rs @@ -3,6 +3,7 @@ use std::thread; use std::thread::{sleep, JoinHandle, Thread}; use std::time::{Duration, Instant}; use crate::chip8::computer::Chip8Computer; +use crate::chip8::cpu_states::Chip8CpuStates; use crate::chip8::cpu_states::Chip8CpuStates::WaitingForInstruction; pub enum ManagerDumpables { @@ -13,10 +14,10 @@ pub enum ManagerDumpables { pub struct Chip8ComputerManager { - pub core_should_run: bool, - pub one_step: bool, - pub core_cycle_timer: bool, - pub core_last_cycle_start: Instant, + core_should_run: bool, + one_step: bool, + core_cycle_timer: bool, + core_last_cycle_start: Instant, computer: Chip8Computer } @@ -41,11 +42,10 @@ impl Chip8ComputerManager { let core_handle = thread::spawn(move || { loop { let start_time = Instant::now(); - println!("Core Thread starting at {start_time:?}"); - + // 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"); + // println!("Core Thread sleeping for {sleep_time}ms"); sleep(Duration::from_millis((16 - sleep_time) as u64)); } }); @@ -53,16 +53,16 @@ impl Chip8ComputerManager { Chip8ComputerManager::default() } - pub fn start(managed: &mut Chip8ComputerManager) { - managed.core_should_run = true; + pub fn start(&mut self) { + self.core_should_run = true; } - pub fn stop(managed: &mut Chip8ComputerManager) { - managed.core_should_run = false + pub fn stop(&mut self) { + self.core_should_run = false } - pub fn step(managed: &mut Chip8ComputerManager) { - managed.one_step = true; + pub fn step(&mut self) { + self.one_step = true; } pub fn state(&mut self) -> &Chip8Computer { @@ -70,11 +70,16 @@ impl Chip8ComputerManager { } pub fn tick( &mut self) { - println!("STARTING TICK"); - if self.one_step | self.core_should_run { - self.core_last_cycle_start = Instant::now(); - self.computer.step_system(); - println!("SYSTEM STEP"); + // println!("STARTING TICK"); + if self.one_step | self.core_should_run { + match self.computer.state { + WaitingForInstruction => { + self.core_last_cycle_start = Instant::now(); + self.computer.step_system(); + println!("SYSTEM STEP"); + } + _ => {} + } }; if self.one_step { println!("SYSTEM HALTED AFTER 1 STEP"); @@ -86,6 +91,9 @@ impl Chip8ComputerManager { pub fn press_key(&mut self, key_index: u8) { self.computer.keypad.push_key(key_index); + if matches!(self.computer.state, Chip8CpuStates::WaitingForKey) { + self.computer.state = WaitingForInstruction + } } pub fn release_key(&mut self, key_index: u8) { self.computer.keypad.release_key(key_index); diff --git a/gemmaegui/src/bin/gemmaegui.rs b/gemmaegui/src/bin/gemmaegui.rs index 17e4340..3241260 100644 --- a/gemmaegui/src/bin/gemmaegui.rs +++ b/gemmaegui/src/bin/gemmaegui.rs @@ -1,23 +1,31 @@ +use std::path::PathBuf; use std::time::Instant; use crate::support::gemma_egui_support::{EGuiFileList, GemmaEguiSupport}; use crate::support::gemma_egui_state::GemmaEGuiState; use eframe::egui; -use egui::Ui; +use egui::{Key, Ui}; use gemma::chip8::computer::Chip8Computer; use gemma::chip8::computer_manager::Chip8ComputerManager; 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)] +]; + #[derive(Default)] struct DisplayOptions { pub video: bool, pub registers: bool, - pub memory: bool + pub memory: bool, } pub struct GemmaViewerState { pub selected_file_index: i32, pub selected_filename: String, - pub display_options: DisplayOptions + pub display_options: DisplayOptions, } impl Default for GemmaViewerState { @@ -25,7 +33,7 @@ impl Default for GemmaViewerState { GemmaViewerState { selected_file_index: -1, selected_filename: String::new(), - display_options: Default::default() + display_options: Default::default(), } } } @@ -54,28 +62,28 @@ fn main() -> eframe::Result { computer.tick(); let local_computer = computer.state(); //if state.display_video { - GemmaEguiSupport::video_view(local_computer, ui); - // } + GemmaEguiSupport::video_view(local_computer, ui); + // } ui.heading("EGUI Gemma"); - // if state.display_memory { - GemmaEguiSupport::memory_view(&local_computer, ui); - // } + // if state.display_memory { + GemmaEguiSupport::memory_view(&local_computer, ui); + // } - // if state.display_registers { - GemmaEguiSupport::registers_view(&local_computer, ui); - // } + // if state.display_registers { + GemmaEguiSupport::registers_view(&local_computer, ui); + // } ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| { if ui.button("Start").clicked() { - computer.core_should_run = true; + computer.start(); } if ui.button("Step").clicked() { - computer.one_step = true; + computer.step(); } if ui.button("Stop").clicked() { - computer.core_should_run = false; + computer.stop(); // state.is_running = false; } if ui.button("Reset").clicked() { @@ -84,17 +92,31 @@ fn main() -> eframe::Result { } }); - if ui.button(format!("Load {}", state.selected_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); + 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_bytes_to_system_memory(read_bin); + }; + EGuiFileList::display_path(PathBuf::from("resources/roms"), &mut state.selected_filename, ui); + let input = 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"); + } 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"); + } + } + } + } + }); + ctx.request_repaint(); }); }) } diff --git a/gemmaimgui/src/bin/gemmaimgui.rs b/gemmaimgui/src/bin/gemmaimgui.rs index dfa4d7a..fa918bf 100644 --- a/gemmaimgui/src/bin/gemmaimgui.rs +++ b/gemmaimgui/src/bin/gemmaimgui.rs @@ -45,10 +45,8 @@ fn main() { } // END DEBUG CODE - for (key_code, key_reg) in LIN_KEYS { if down_keys[key_code as usize] { - system.press_key(key_reg); system.press_key(key_reg); system.wait_for_instruction(); } else { diff --git a/gemmautil/Cargo.toml b/gemmautil/Cargo.toml index 54a63ff..4257587 100644 --- a/gemmautil/Cargo.toml +++ b/gemmautil/Cargo.toml @@ -6,3 +6,5 @@ edition = "2021" [dependencies] gemma = { path = "../gemma" } clap = { version = "4.5.20", features = ["derive"] } +pest = "2.7.14" +pest_derive = "2.7.14" \ No newline at end of file diff --git a/gemmautil/src/bin/ch8asm.rs b/gemmautil/src/bin/ch8asm.rs index 709c232..937e2b1 100644 --- a/gemmautil/src/bin/ch8asm.rs +++ b/gemmautil/src/bin/ch8asm.rs @@ -1,59 +1,87 @@ -use std::fs::File; -use std::io; -use std::io::BufRead; -use std::path::Path; -use clap::Parser; -use gemma::chip8::instructions::Chip8CpuInstructions; - -/// Ch8Asm -/// Converts well formed CH8ASM. -/// no variables. -/// no labels. -/// nothing fun. - -pub struct Assembler {} - -impl Assembler { - -} +use std::fs; +// Ch8Asm +// Converts well formed CH8ASM. +// no variables. +// no labels. +// nothing fun. +use pest::Parser; +use pest_derive::Parser; #[derive(Parser)] -#[command(version, about, long_about = None)] -struct AssemblerApp { - #[arg(short)] - input_file: Box, -} +#[grammar = "chip8_asm.pest"] +pub struct Chip8AsmParser; fn main() { println!("Taxation is Theft"); - let result = AssemblerApp::parse(); - let mut assembled_code: Vec = vec![]; - // println!("Preparing to assemble {}", result.input_file.file_name()); + let unparsed = fs::read_to_string("resources/test/gemma_disassembler_1_chip_logo_ch8_asm.asc").expect("Unable to read input"); - let path = Path::new(result.input_file); + let file = Chip8AsmParser::parse(Rule::file, &unparsed).expect("Unable to parse. Try again.") + .next().unwrap(); - // Open the file in read-only mode - let file = File::open(&path)?; - - // Use a buffered reader for efficient reading - let reader = io::BufReader::new(file); - - // Read the file line by line - for line in reader.lines() { - // Get the line, handling possible errors - let line = line?; - - // Split the line by ';' and collect parts into a vector - let parts: Vec<&str> = line.split(';').collect(); - - // Print each part or process it as needed - for part in &parts { - println!("{}", part); + for record in file.into_inner() { + match record.as_rule() { + Rule::instruction => { + println!("INSTRUCTION = {:?}", record.into_inner().flatten()) + } + _ => { + println!("UNHANDLED PART."); + } } } - // read the line split by a semicolon +} +mod test { + use super::*; + + #[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); + } + + + let parsed = Chip8AsmParser::parse(Rule::record, "ADDI ; comment"); + for i in parsed { + println!("RULE PAIR THING: {:?}", i); + } + + + println!("PARSED: {:?}", + Chip8AsmParser::parse(Rule::record, "ADD 0x01 0x02 ; Comment") + ); + + 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/bin/ch8disasm.rs b/gemmautil/src/bin/ch8disasm.rs index b002e3c..f6ac8eb 100644 --- a/gemmautil/src/bin/ch8disasm.rs +++ b/gemmautil/src/bin/ch8disasm.rs @@ -18,7 +18,41 @@ struct DisassemblerApp { struct Disassembler {} impl Disassembler { pub fn disassemble(from_data: Vec) -> String { - String::new() + let mut working_instruction: u16 = 0x0000; + + // read the input data and loop through it byte by byte. + let mut output_string = String::new(); + + for (offset, byte) in from_data.iter().enumerate() { + working_instruction = (working_instruction << 8) | (*byte as u16); + if offset % 2 != 0 { + let decoded = Chip8CpuInstructions::decode(working_instruction); + let decoded_string = decoded.to_string(); + let mut current_parts = String::new(); + + match decoded { + XXXXERRORINSTRUCTION => { + current_parts = format!("DW 0x{:04x}", working_instruction); + } + _ => { + current_parts = format!("{}", decoded); + } + }; + + let target_length: i32 = 25; + let spacing_length = target_length.saturating_sub(current_parts.len() as i32); + + // now add the rest after the string + let x = spacing_length as usize; + current_parts = format!("{}{: { - current_parts = format!("DW 0x{:04x}", working_instruction); - } - _ => { - current_parts = format!("{}", decoded); - } - }; - - let target_length: i32 = 25; - let spacing_length = target_length.saturating_sub(current_parts.len() as i32); - - // now add the rest after the string - let x = spacing_length as usize; - current_parts = format!("{}{: { println!("Output to console."); - println!("OS: \n\n{}", output_string); + println!("OS: \n\n{}", decompiled_program); } Some(target) => { println!("Output to {:?}", target); - std::fs::write(target, output_string).unwrap(); + std::fs::write(target, decompiled_program).unwrap(); } } } diff --git a/gemmautil/src/chip8_asm.pest b/gemmautil/src/chip8_asm.pest new file mode 100644 index 0000000..727d8de --- /dev/null +++ b/gemmautil/src/chip8_asm.pest @@ -0,0 +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 }