From 277f210b3e439bf4dcf6e8b39c08f3327c6b80e1 Mon Sep 17 00:00:00 2001 From: zxq5 Date: Sun, 15 Jun 2025 21:21:02 +0100 Subject: [PATCH] editor works --- Cargo.lock | 325 ++++++++++++++++++++++---- Cargo.toml | 1 + src/emulator/system/emulator.rs | 47 ++-- src/emulator/system/memory.rs | 8 +- src/emulator/system/processor/mod.rs | 1 - src/emulator/ui/editor.rs | 326 +++++++++++++++++++++++++++ src/emulator/ui/mod.rs | 1 + src/main.rs | 12 +- 8 files changed, 641 insertions(+), 80 deletions(-) create mode 100644 src/emulator/ui/editor.rs diff --git a/Cargo.lock b/Cargo.lock index c6f6ea8..18a039f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -35,7 +35,7 @@ dependencies = [ "atspi-common", "serde", "thiserror 1.0.69", - "zvariant", + "zvariant 4.2.0", ] [[package]] @@ -78,7 +78,7 @@ dependencies = [ "futures-lite", "futures-util", "serde", - "zbus", + "zbus 4.4.0", ] [[package]] @@ -212,6 +212,25 @@ dependencies = [ "libloading", ] +[[package]] +name = "ashpd" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cbdf310d77fd3aaee6ea2093db7011dc2d35d2eb3481e5607f1f8d942ed99df" +dependencies = [ + "async-fs", + "async-net", + "enumflags2", + "futures-channel", + "futures-util", + "rand 0.9.1", + "raw-window-handle", + "serde", + "serde_repr", + "url", + "zbus 5.7.1", +] + [[package]] name = "async-broadcast" version = "0.7.2" @@ -291,6 +310,17 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "async-net" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" +dependencies = [ + "async-io", + "blocking", + "futures-lite", +] + [[package]] name = "async-process" version = "2.3.1" @@ -382,11 +412,11 @@ dependencies = [ "enumflags2", "serde", "static_assertions", - "zbus", + "zbus 4.4.0", "zbus-lockstep", "zbus-lockstep-macros", - "zbus_names", - "zvariant", + "zbus_names 3.0.0", + "zvariant 4.2.0", ] [[package]] @@ -398,7 +428,7 @@ dependencies = [ "atspi-common", "atspi-proxies", "futures-lite", - "zbus", + "zbus 4.4.0", ] [[package]] @@ -409,8 +439,8 @@ checksum = "a5e6c5de3e524cf967569722446bcd458d5032348554d9a17d7d72b041ab7496" dependencies = [ "atspi-common", "serde", - "zbus", - "zvariant", + "zbus 4.4.0", + "zvariant 4.2.0", ] [[package]] @@ -473,6 +503,15 @@ dependencies = [ "objc2 0.5.2", ] +[[package]] +name = "block2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "340d2f0bdb2a43c1d3cd40513185b2bd7def0aa1052f956455114bc98f82dcf2" +dependencies = [ + "objc2 0.6.1", +] + [[package]] name = "blocking" version = "1.6.1" @@ -722,6 +761,7 @@ version = "0.1.0" dependencies = [ "eframe", "egui", + "rfd", ] [[package]] @@ -740,6 +780,18 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" +[[package]] +name = "dispatch2" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a0d569e003ff27784e0e14e4a594048698e0c0f0b66cabcb51511be55a7caa0" +dependencies = [ + "bitflags 2.9.1", + "block2 0.6.1", + "libc", + "objc2 0.6.1", +] + [[package]] name = "dispatch2" version = "0.3.0" @@ -1081,6 +1133,15 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", +] + [[package]] name = "futures-core" version = "0.3.31" @@ -1221,7 +1282,7 @@ dependencies = [ "bitflags 2.9.1", "cfg_aliases", "cgl", - "dispatch2", + "dispatch2 0.3.0", "glutin_egl_sys", "glutin_glx_sys", "glutin_wgl_sys", @@ -1753,6 +1814,19 @@ dependencies = [ "memoffset", ] +[[package]] +name = "nix" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" +dependencies = [ + "bitflags 2.9.1", + "cfg-if", + "cfg_aliases", + "libc", + "memoffset", +] + [[package]] name = "nohash-hasher" version = "0.2.0" @@ -1830,7 +1904,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ "bitflags 2.9.1", - "block2", + "block2 0.5.1", "libc", "objc2 0.5.2", "objc2-core-data", @@ -1846,6 +1920,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc" dependencies = [ "bitflags 2.9.1", + "block2 0.6.1", "objc2 0.6.1", "objc2-core-foundation", "objc2-core-graphics", @@ -1859,7 +1934,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ "bitflags 2.9.1", - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-core-location", "objc2-foundation 0.2.2", @@ -1871,7 +1946,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", ] @@ -1883,7 +1958,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ "bitflags 2.9.1", - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", ] @@ -1895,7 +1970,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" dependencies = [ "bitflags 2.9.1", - "dispatch2", + "dispatch2 0.3.0", "objc2 0.6.1", ] @@ -1906,7 +1981,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "989c6c68c13021b5c2d6b71456ebb0f9dc78d752e86a98da7c716f4f9470f5a4" dependencies = [ "bitflags 2.9.1", - "dispatch2", + "dispatch2 0.3.0", "objc2 0.6.1", "objc2-core-foundation", "objc2-io-surface", @@ -1918,7 +1993,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", "objc2-metal", @@ -1930,7 +2005,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-contacts", "objc2-foundation 0.2.2", @@ -1949,7 +2024,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ "bitflags 2.9.1", - "block2", + "block2 0.5.1", "dispatch", "libc", "objc2 0.5.2", @@ -1983,7 +2058,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-app-kit 0.2.2", "objc2-foundation 0.2.2", @@ -1996,7 +2071,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ "bitflags 2.9.1", - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", ] @@ -2008,7 +2083,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ "bitflags 2.9.1", - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", "objc2-metal", @@ -2031,7 +2106,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ "bitflags 2.9.1", - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-cloud-kit", "objc2-core-data", @@ -2051,7 +2126,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", ] @@ -2063,7 +2138,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ "bitflags 2.9.1", - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-core-location", "objc2-foundation 0.2.2", @@ -2230,6 +2305,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "pollster" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" + [[package]] name = "potential_utf" version = "0.1.2" @@ -2313,8 +2394,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", ] [[package]] @@ -2324,7 +2415,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", ] [[package]] @@ -2336,6 +2437,15 @@ dependencies = [ "getrandom 0.2.16", ] +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", +] + [[package]] name = "raw-window-handle" version = "0.6.2" @@ -2366,6 +2476,30 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" +[[package]] +name = "rfd" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80c844748fdc82aae252ee4594a89b6e7ebef1063de7951545564cbc4e57075d" +dependencies = [ + "ashpd", + "block2 0.6.1", + "dispatch2 0.2.0", + "js-sys", + "log", + "objc2 0.6.1", + "objc2-app-kit 0.3.1", + "objc2-core-foundation", + "objc2-foundation 0.3.1", + "pollster", + "raw-window-handle", + "urlencoding", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.59.0", +] + [[package]] name = "rustc-hash" version = "1.1.0" @@ -2868,8 +3002,15 @@ dependencies = [ "form_urlencoded", "idna", "percent-encoding", + "serde", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + [[package]] name = "utf8_iter" version = "1.0.4" @@ -3606,7 +3747,7 @@ dependencies = [ "android-activity", "atomic-waker", "bitflags 2.9.1", - "block2", + "block2 0.5.1", "bytemuck", "calloop", "cfg_aliases", @@ -3791,9 +3932,9 @@ dependencies = [ "futures-sink", "futures-util", "hex", - "nix", + "nix 0.29.0", "ordered-stream", - "rand", + "rand 0.8.5", "serde", "serde_repr", "sha1", @@ -3802,9 +3943,42 @@ dependencies = [ "uds_windows", "windows-sys 0.52.0", "xdg-home", - "zbus_macros", - "zbus_names", - "zvariant", + "zbus_macros 4.4.0", + "zbus_names 3.0.0", + "zvariant 4.2.0", +] + +[[package]] +name = "zbus" +version = "5.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3a7c7cee313d044fca3f48fa782cb750c79e4ca76ba7bc7718cd4024cdf6f68" +dependencies = [ + "async-broadcast", + "async-executor", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener", + "futures-core", + "futures-lite", + "hex", + "nix 0.30.1", + "ordered-stream", + "serde", + "serde_repr", + "tracing", + "uds_windows", + "windows-sys 0.59.0", + "winnow", + "zbus_macros 5.7.1", + "zbus_names 4.2.0", + "zvariant 5.5.3", ] [[package]] @@ -3814,7 +3988,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ca2c5dceb099bddaade154055c926bb8ae507a18756ba1d8963fd7b51d8ed1d" dependencies = [ "zbus_xml", - "zvariant", + "zvariant 4.2.0", ] [[package]] @@ -3828,7 +4002,7 @@ dependencies = [ "syn", "zbus-lockstep", "zbus_xml", - "zvariant", + "zvariant 4.2.0", ] [[package]] @@ -3841,7 +4015,22 @@ dependencies = [ "proc-macro2", "quote", "syn", - "zvariant_utils", + "zvariant_utils 2.1.0", +] + +[[package]] +name = "zbus_macros" +version = "5.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17e7e5eec1550f747e71a058df81a9a83813ba0f6a95f39c4e218bdc7ba366a" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", + "zbus_names 4.2.0", + "zvariant 5.5.3", + "zvariant_utils 3.2.0", ] [[package]] @@ -3852,7 +4041,19 @@ checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" dependencies = [ "serde", "static_assertions", - "zvariant", + "zvariant 4.2.0", +] + +[[package]] +name = "zbus_names" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97" +dependencies = [ + "serde", + "static_assertions", + "winnow", + "zvariant 5.5.3", ] [[package]] @@ -3864,8 +4065,8 @@ dependencies = [ "quick-xml 0.30.0", "serde", "static_assertions", - "zbus_names", - "zvariant", + "zbus_names 3.0.0", + "zvariant 4.2.0", ] [[package]] @@ -3952,7 +4153,22 @@ dependencies = [ "enumflags2", "serde", "static_assertions", - "zvariant_derive", + "zvariant_derive 4.2.0", +] + +[[package]] +name = "zvariant" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d30786f75e393ee63a21de4f9074d4c038d52c5b1bb4471f955db249f9dffb1" +dependencies = [ + "endi", + "enumflags2", + "serde", + "url", + "winnow", + "zvariant_derive 5.5.3", + "zvariant_utils 3.2.0", ] [[package]] @@ -3965,7 +4181,20 @@ dependencies = [ "proc-macro2", "quote", "syn", - "zvariant_utils", + "zvariant_utils 2.1.0", +] + +[[package]] +name = "zvariant_derive" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75fda702cd42d735ccd48117b1630432219c0e9616bf6cb0f8350844ee4d9580" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", + "zvariant_utils 3.2.0", ] [[package]] @@ -3978,3 +4207,17 @@ dependencies = [ "quote", "syn", ] + +[[package]] +name = "zvariant_utils" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16edfee43e5d7b553b77872d99bc36afdda75c223ca7ad5e3fbecd82ca5fc34" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "static_assertions", + "syn", + "winnow", +] diff --git a/Cargo.toml b/Cargo.toml index cdb5ede..3967a9d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,3 +10,4 @@ path = "src/lib.rs" [dependencies] eframe = "0.31.1" egui = "0.31.1" +rfd = "0.15.3" diff --git a/src/emulator/system/emulator.rs b/src/emulator/system/emulator.rs index 7e06394..7fa283e 100644 --- a/src/emulator/system/emulator.rs +++ b/src/emulator/system/emulator.rs @@ -15,33 +15,20 @@ use crate::{ }, }; -pub fn run_emulator(cmd_rx: &Receiver, state_tx: &Sender, cpu: Processor) { +pub fn run_emulator( + cmd_rx: &Receiver, + state_tx: &Sender, + mut processor: Processor, +) { println!("starting"); let mut running = Running::Paused; let mut addr = 0u32; let size = 256; - // Send initial state. - // let Ok(mut cpu_lock) = cpu.lock() else { - // panic!("Failed to lock CPU.") - // }; - - let mut cpu_lock = cpu; - - cpu_lock.get_stack(32); - println!("got stack"); - - cpu_lock.display(); - println!("got display"); - - let memory_view = cpu_lock.memory.read_range(addr, size); - println!("got view"); - let initial_state = state(&mut cpu_lock, running, 0, memory_view); - println!("got state"); - + let memory_view = processor.memory.read_range(addr, size); + let initial_state = state(&mut processor, running, 0, memory_view); let _ = state_tx.send(initial_state); - println!("sent state"); let mut instruction_count = 0; @@ -75,7 +62,7 @@ pub fn run_emulator(cmd_rx: &Receiver, state_tx: &Sender, cpu: P } Command::Reset => { running = Running::Paused; - cpu_lock.reset(); + processor.reset(); instruction_count = 0; println!("Emulator rebooted"); } @@ -83,10 +70,10 @@ pub fn run_emulator(cmd_rx: &Receiver, state_tx: &Sender, cpu: P running = Running::Paused; // Execute one cycle. - match cpu_lock.cycle() { + match processor.cycle() { Ok(_) => {} Err(why) => { - let pcx = cpu_lock.get(Register::Pcx); + let pcx = processor.get(Register::Pcx); eprintln!("Could not decode instruction at {pcx:x}. Reason: {why}"); continue; } @@ -100,7 +87,7 @@ pub fn run_emulator(cmd_rx: &Receiver, state_tx: &Sender, cpu: P println!("Memory view for address 0x{addr:08x}"); } Command::Write(offset, data) => { - cpu_lock.memory.write_range(offset, data); + processor.memory.write_range(offset, data); println!("Program loaded"); } Command::Interrupt(_interrupt) => { @@ -108,8 +95,8 @@ pub fn run_emulator(cmd_rx: &Receiver, state_tx: &Sender, cpu: P } } - let memory_view = cpu_lock.memory.read_range(addr, size); - let state = state(&mut cpu_lock, running, instruction_count, memory_view); + let memory_view = processor.memory.read_range(addr, size); + let state = state(&mut processor, running, instruction_count, memory_view); let _ = state_tx.send(state); } @@ -118,10 +105,10 @@ pub fn run_emulator(cmd_rx: &Receiver, state_tx: &Sender, cpu: P let mut update = false; // Execute one cycle. - let instruction = match cpu_lock.cycle() { + let instruction = match processor.cycle() { Ok(instruction) => instruction, Err(why) => { - let pcx = cpu_lock.get(Register::Pcx); + let pcx = processor.get(Register::Pcx); eprintln!("Could not decode instruction at {pcx:x}. Reason: {why}"); continue; } @@ -142,8 +129,8 @@ pub fn run_emulator(cmd_rx: &Receiver, state_tx: &Sender, cpu: P } if update { - let memory_view = cpu_lock.memory.read_range(addr, size); - let state = state(&mut cpu_lock, running, instruction_count, memory_view); + let memory_view = processor.memory.read_range(addr, size); + let state = state(&mut processor, running, instruction_count, memory_view); let _ = state_tx.send(state); } } else { diff --git a/src/emulator/system/memory.rs b/src/emulator/system/memory.rs index 87c3772..ff620d0 100644 --- a/src/emulator/system/memory.rs +++ b/src/emulator/system/memory.rs @@ -104,7 +104,11 @@ impl MemoryUnit for MainStore { block.data[(offset + 3) as usize] = value as u8; } - fn write_range(&mut self, _addr: u32, _value: Vec) { - todo!("We might need this for DMA."); + fn write_range(&mut self, addr: u32, value: Vec) { + for byte in value { + let (block_addr, offset) = Self::segment_addr(addr); + let block = self.mut_block(block_addr); + block.data[offset as usize] = byte; + } } } diff --git a/src/emulator/system/processor/mod.rs b/src/emulator/system/processor/mod.rs index cfec952..6cdc983 100644 --- a/src/emulator/system/processor/mod.rs +++ b/src/emulator/system/processor/mod.rs @@ -23,7 +23,6 @@ impl Processor { #[must_use] pub fn new(memory: Box, io_devices: Vec>) -> Self { Self { - // io_devices, memory, registers: RegFile::default(), halted: false, diff --git a/src/emulator/ui/editor.rs b/src/emulator/ui/editor.rs new file mode 100644 index 0000000..0f7a93d --- /dev/null +++ b/src/emulator/ui/editor.rs @@ -0,0 +1,326 @@ +use std::sync::mpsc::Sender; + +use egui::{Align, Context, Layout, Ui}; +use rfd::FileDialog; + +use crate::emulator::{ + system::model::{Command, State}, + ui::interface::Component, +}; + +pub struct Editor { + filename: String, + text: String, + output: Vec, + sender: Sender, + cursor_col: usize, + cursor_line: usize, + visible: bool, + load_offset: u32, + offset_str: String, + error: Option, +} + +impl Component for Editor { + fn name(&self) -> &'static str { + "Editor" + } + + fn visible(&mut self) -> &mut bool { + &mut self.visible + } + + fn category(&self) -> super::interface::Category { + super::interface::Category::Programming + } + + fn render(&mut self, state: &mut State, ui: &mut Ui, ctx: &Context) { + ui.vertical(|ui| { + // Top bar + self.render_toolbar(state, ui, ctx); + + ui.add_space(4.0); // Add some spacing instead of just a separator + ui.separator(); + + let remaining_height = f32::max(ui.available_height() - 100.0, 100.0); + + ui.allocate_ui_with_layout( + egui::Vec2::new(ui.available_width(), remaining_height), + Layout::left_to_right(Align::Min), + |ui| { + self.render_editor(state, ui, ctx); + ui.separator(); + self.render_output(state, ui, ctx); + }, + ); + + ui.label(format!("Ln {}, Col {}", self.cursor_line, self.cursor_col)); + }); + } +} + +impl Editor { + pub fn new(sender: Sender) -> Self { + Self { + filename: String::new(), + text: String::new(), + output: Vec::new(), + sender, + cursor_col: 1, + cursor_line: 1, + visible: false, + load_offset: 0, + offset_str: String::new(), + error: None, + } + } + + fn render_output(&mut self, _state: &mut State, ui: &mut Ui, _ctx: &Context) { + // Output area with synchronized scrolling + egui::ScrollArea::vertical() + .id_salt("output_scroll") + .max_width(300.0) + .show(ui, |ui| { + if self.output.is_empty() { + ui.label( + egui::RichText::new("No output data") + .font(egui::FontId::monospace(12.0)) + .color(egui::Color32::GRAY), + ); + return; + } + + egui::Grid::new("output_grid") + .num_columns(4) + .spacing([20.0, 2.0]) // Horizontal and vertical spacing + .striped(false) + .show(ui, |ui| { + // Process bytes in chunks of 4 + for (line_num, chunk) in self.output.chunks(4).enumerate() { + let address = line_num * 4; + + // Convert chunk to u32 (little-endian) + let mut bytes = [0u8; 4]; + for (i, &byte) in chunk.iter().enumerate() { + if i < 4 { + bytes[i] = byte; + } + } + let value = u32::from_le_bytes(bytes); + + // Address column + ui.with_layout( + egui::Layout::left_to_right(egui::Align::Center), + |ui| { + ui.set_min_width(80.0); + let style = ui.style_mut(); + style.visuals.widgets.inactive.bg_fill = + egui::Color32::from_gray(30); + ui.label( + egui::RichText::new(format!("0x{:04X}", address)) + .font(egui::FontId::monospace(12.0)), + ); + }, + ); + + // Individual bytes column + let byte_str = chunk + .iter() + .map(|b| format!("{:02X}", b)) + .collect::>() + .join(" "); + + ui.label( + egui::RichText::new(format!("{:<11}", byte_str)) + .font(egui::FontId::monospace(12.0)) + .color(egui::Color32::from_rgb(200, 200, 255)), + ); + + // Hex column + ui.label( + egui::RichText::new(format!("0x{:08X}", value)) + .font(egui::FontId::monospace(12.0)) + .color(egui::Color32::from_rgb(255, 200, 200)), + ); + + // Decimal column + ui.label( + egui::RichText::new(format!("{:10}", value)) + .font(egui::FontId::monospace(12.0)) + .color(egui::Color32::from_rgb(200, 255, 200)), + ); + + ui.end_row(); + } + }); + }); + } + + fn render_editor(&mut self, _state: &mut State, ui: &mut Ui, _ctx: &Context) { + let available_width = ui.available_width(); + + // Main editor area with synchronized scrolling + egui::ScrollArea::vertical() + .max_width(available_width - 400.0) + .id_salt("editor_scroll") + .show(ui, |ui| { + ui.horizontal(|ui| { + // Line numbers column + let line_count = self.text.lines().count(); + ui.vertical(|ui| { + ui.set_width(50.0); + ui.style_mut().visuals.widgets.inactive.bg_fill = + egui::Color32::from_gray(30); + + // Calculate line height to match text editor + let line_height = ui.text_style_height(&egui::TextStyle::Monospace); + + for line_num in 1..=line_count { + let line_response = ui.allocate_response( + egui::vec2(50.0, line_height), + egui::Sense::hover(), + ); + + ui.painter().text( + line_response.rect.left_center() + egui::vec2(5.0, 0.0), + egui::Align2::LEFT_CENTER, + format!("{:3}", line_num), + egui::FontId::monospace(12.0), + ui.style().visuals.text_color(), + ); + } + }); + + ui.separator(); + + // Text editor area + ui.vertical(|ui| { + let available_size = ui.available_size(); + let response = ui.add_sized( + available_size, + egui::TextEdit::multiline(&mut self.text) + .font(egui::TextStyle::Monospace) + .margin(egui::vec2(5.0, 0.0)) + .code_editor(), + ); + + // Update cursor position when text changes + if response.changed() { + // Simple but functional cursor tracking + let lines = self.text.lines().collect::>(); + self.cursor_line = lines.len().max(1); + + // Get the length of the last line for column position + if let Some(last_line) = lines.last() { + self.cursor_col = last_line.chars().count() + 1; + } else { + self.cursor_col = 1; + } + } + }); + }); + }); + } + + fn render_toolbar(&mut self, _state: &mut State, ui: &mut Ui, _ctx: &Context) { + ui.horizontal(|ui| { + // current filename + ui.label(format!("File: {}", self.filename)); + + // error display + ui.label( + egui::RichText::new(self.error.clone().unwrap_or("".to_string())) + .color(egui::Color32::RED), + ); + + // number of lines in the file + ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { + let line_count = self.text.lines().count(); + ui.label(format!("Lines: {}", line_count)); + }); + }); + + ui.horizontal(|ui| { + ui.spacing_mut().button_padding = egui::vec2(8.0, 4.0); + ui.spacing_mut().item_spacing.x = 6.0; + + // Opens a file + if ui.button("Open").clicked() { + if let Some(path) = FileDialog::new() + .add_filter("dsafiles", &["dsa", "dsb", "dsc", "dsd"]) + .add_filter("all", &["*"]) + .set_directory(std::env::current_dir().unwrap()) + .pick_file() + { + if let Ok(content) = std::fs::read_to_string(&path) { + self.text = content; + self.filename = path.display().to_string(); + } + } + + self.output = Vec::new(); + } + + // Saves the current file + if ui.button("Save").clicked() { + if let Some(path) = FileDialog::new() + .add_filter("dsafiles", &["dsa", "dsb", "dsc", "dsd"]) + .add_filter("all", &["*"]) + .set_directory(std::env::current_dir().unwrap()) + .save_file() + { + if let Err(e) = std::fs::write(&path, &self.text) { + self.error = Some(format!("Failed to save file: {}", e)); + } else { + self.filename = path.display().to_string(); + } + } + } + + // builds the current file + if ui.button("Build").clicked() { + self.output = vec![0x00; 256]; + } + + // Loads the generated binary into the assembler at the provided offset + if ui.button("Load").clicked() { + if self.error.is_some() { + self.error = Some("Can't load program at invalid offset!".to_string()); + } + + self.sender + .send(Command::Write(self.load_offset, self.output.clone())) + .unwrap_or_else(|_| self.error = Some("Failed to send command".to_string())); + } + + // Entry widget to enter a load offset + if ui.text_edit_singleline(&mut self.offset_str).changed() { + if let Some(offset) = parse_address(&self.offset_str) { + self.load_offset = offset; + self.error = None; + } else { + self.error = Some("Invalid offset".to_string()); + } + } + + // Resets the emulator and all attached devices + if ui.button("Reset Emulator").clicked() { + self.sender + .send(Command::Reset) + .unwrap_or_else(|_| self.error = Some("Failed to send command".to_string())); + } + }); + } +} + +fn parse_address(address: &str) -> Option { + if address.starts_with("0x") { + u32::from_str_radix(&address[2..], 16).ok() + } else if address.starts_with("0b") { + u32::from_str_radix(&address[2..], 2).ok() + } else if address.starts_with("0o") { + u32::from_str_radix(&address[2..], 8).ok() + } else { + address.parse::().ok() + } +} diff --git a/src/emulator/ui/mod.rs b/src/emulator/ui/mod.rs index 2c87b7f..178c392 100644 --- a/src/emulator/ui/mod.rs +++ b/src/emulator/ui/mod.rs @@ -1,4 +1,5 @@ pub mod control_unit; +pub mod editor; pub mod interface; pub mod memory_inspector; pub mod menu; diff --git a/src/main.rs b/src/main.rs index 5f3e652..b1189e9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,10 @@ -use std::{ - sync::{Arc, Mutex}, - thread, -}; +use std::thread; use dsa_rs::emulator::{ system::{emulator::run_emulator, memory::MainStore, processor::Processor}, ui::{ - control_unit::ControlPanel, interface::EmulatorUI, memory_inspector::MemoryInspector, - stack_inspector::StackInspector, + control_unit::ControlPanel, editor::Editor, interface::EmulatorUI, + memory_inspector::MemoryInspector, stack_inspector::StackInspector, }, }; @@ -36,6 +33,9 @@ fn main() -> Result<(), eframe::Error> { let stack_inspector = StackInspector::new(); ui.add_component(Box::new(stack_inspector)); + let editor = Editor::new(cmd_sender.clone()); + ui.add_component(Box::new(editor)); + // Run UI let options = eframe::NativeOptions { viewport: egui::ViewportBuilder::default().with_inner_size([800.0, 600.0]),