diff --git a/Cargo.lock b/Cargo.lock index 36c6fd3..2cd09db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,12 +39,30 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "cc" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2755ff20a1d93490d26ba33a6f092a38a508398a5320df5d4b3014fcccce9410" + [[package]] name = "cfg-if" version = "1.0.0" @@ -57,7 +75,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" dependencies = [ - "bitflags", + "bitflags 2.5.0", "crossterm_winapi", "libc", "mio", @@ -76,12 +94,132 @@ dependencies = [ "winapi", ] +[[package]] +name = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + [[package]] name = "georgeemu" version = "0.1.0" @@ -89,6 +227,7 @@ dependencies = [ "anyhow", "bdf", "crossterm", + "minifb", "serde", "toml", ] @@ -109,12 +248,66 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "libc" version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +[[package]] +name = "libloading" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d" +dependencies = [ + "cfg-if", + "windows-targets 0.52.5", +] + +[[package]] +name = "libredox" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3af92c55d7d839293953fcd0fda5ecfe93297cfde6ffbdec13b41d99c0ba6607" +dependencies = [ + "bitflags 2.5.0", + "libc", + "redox_syscall 0.4.1", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + [[package]] name = "lock_api" version = "0.4.12" @@ -137,6 +330,41 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "minifb" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c470a74618b43cd182c21b3dc1e6123501249f3bad9a0085e95d1304ca2478" +dependencies = [ + "cc", + "dlib", + "futures", + "instant", + "js-sys", + "lazy_static", + "libc", + "orbclient", + "raw-window-handle", + "serde", + "serde_derive", + "tempfile", + "wasm-bindgen-futures", + "wayland-client", + "wayland-cursor", + "wayland-protocols", + "winapi", + "x11-dl", +] + [[package]] name = "mio" version = "0.8.11" @@ -146,7 +374,37 @@ dependencies = [ "libc", "log", "wasi", - "windows-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "nix" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "orbclient" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52f0d54bde9774d3a51dcf281a5def240c71996bc6ca05d2c847ec8b2b216166" +dependencies = [ + "libc", + "libredox", + "sdl2", + "sdl2-sys", ] [[package]] @@ -167,11 +425,29 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.2", "smallvec", "windows-targets 0.52.5", ] +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + [[package]] name = "proc-macro2" version = "1.0.86" @@ -190,21 +466,78 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_syscall" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ - "bitflags", + "bitflags 2.5.0", ] +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sdl2" +version = "0.35.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7959277b623f1fb9e04aea73686c3ca52f01b2145f8ea16f4ff30d8b7623b1a" +dependencies = [ + "bitflags 1.3.2", + "lazy_static", + "libc", + "sdl2-sys", +] + +[[package]] +name = "sdl2-sys" +version = "0.35.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3586be2cf6c0a8099a79a12b4084357aa9b3e0b0d7980e3b67aaf7a9d55f9f0" +dependencies = [ + "cfg-if", + "libc", + "version-compare", +] + [[package]] name = "serde" version = "1.0.203" @@ -264,6 +597,15 @@ dependencies = [ "libc", ] +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + [[package]] name = "smallvec" version = "1.13.2" @@ -281,6 +623,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + [[package]] name = "thiserror" version = "1.0.61" @@ -341,12 +695,167 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "version-compare" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "wayland-client" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715" +dependencies = [ + "bitflags 1.3.2", + "downcast-rs", + "libc", + "nix", + "scoped-tls", + "wayland-commons", + "wayland-scanner", + "wayland-sys", +] + +[[package]] +name = "wayland-commons" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902" +dependencies = [ + "nix", + "once_cell", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-cursor" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661" +dependencies = [ + "nix", + "wayland-client", + "xcursor", +] + +[[package]] +name = "wayland-protocols" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6" +dependencies = [ + "bitflags 1.3.2", + "wayland-client", + "wayland-commons", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53" +dependencies = [ + "proc-macro2", + "quote", + "xml-rs", +] + +[[package]] +name = "wayland-sys" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be12ce1a3c39ec7dba25594b97b42cb3195d54953ddb9d3d95a7c3902bc6e9d4" +dependencies = [ + "dlib", + "lazy_static", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "winapi" version = "0.3.9" @@ -378,6 +887,15 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.5", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -507,3 +1025,26 @@ checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" dependencies = [ "memchr", ] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "xcursor" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a0ccd7b4a5345edfcd0c3535718a4e9ff7798ffc536bb5b5a0e26ff84732911" + +[[package]] +name = "xml-rs" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791978798f0597cfc70478424c2b4fdc2b7a8024aaff78497ef00f24ef674193" diff --git a/Cargo.toml b/Cargo.toml index 7f3e624..44993c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ edition = "2021" anyhow = "1.0.81" bdf = "0.6.0" crossterm = "0.27.0" +minifb = "0.27.0" # minifb = "0.25.0" # ratatui = "0.26.3" serde = { version = "1.0.197", features = ["serde_derive", "derive"] } diff --git a/src/cli.rs b/src/cli.rs index 83e01ad..5d14f99 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -7,116 +7,163 @@ use std::{ use serde::{Deserialize, Serialize}; -use crate::memory::Mem; +use crate::video::ScreenType; #[derive(Serialize, Deserialize, Debug)] -struct Config { +struct ConfigBuilder { + rom: Option, char_rom: Option, - rom: String, + screen: ScreenType, } -pub fn get_input(memory: &mut Mem) { - let args: Vec = env::args().collect(); +impl ConfigBuilder { + fn new() -> Self { + Self { + rom: None, + char_rom: None, + screen: ScreenType::default(), + } + } - match args.len() { - 0 => { - println!("george-emu must be run in the terminal, don't know what went wrong here!"); - exit(1) + fn build(self) -> Config { + let rom = match self.rom { + Some(rom) => rom, + None => { + println!("no rom was provided :("); + exit(1); + } + }; + let char_rom = self.char_rom; + let screen = self.screen; + + Config { + rom, + screen, + char_rom, } - 1 => { - let config: Config = match File::open("./george.toml") { - Ok(mut file) => { - let mut string = String::new(); - file.read_to_string(&mut string).unwrap(); - toml::from_str(string.as_str()).unwrap() - } - Err(_) => { - println!("couldn't find a `george.toml` in the current directory!"); - exit(1); - } - }; - let rom = match std::fs::File::open(config.rom) { - Ok(file) => file, - Err(error) => panic!("Couldn't open main rom! {:?}", error), - }; - if let Err(error) = memory.load_rom(rom) { - println!("{:?}", error); - }; - } - 2 => match &args[1] as &str { - "help" => { - println!("ʕ·ᴥ·ʔ- george-emu is an emulator for george:"); - println!("https://git.augustkline.com/august/george\n"); - println!("commands:"); - println!(" help: print this help screen"); - println!(" help : print help info for any command"); - println!(" rom : load a rom/binary from path\n"); - println!("configuration:"); - println!(" george-emu searches for a `george.toml` in the current directory. in `george.toml` you can specify a path for the character rom using the key `char_rom` and the main rom/binary with the key `rom`"); + } +} + +pub struct Config { + pub rom: String, + pub screen: ScreenType, + pub char_rom: Option, +} + +fn help(command: Option) { + let executable: String = env::args().next().unwrap(); + if let Some(command) = command { + match &command as &str { + "rom" | "--rom" | "-r" => { + println!("{executable} {command} \n\nload a rom/binary from path"); + exit(0); + } + "screen" | "--screen" | "-s" => { + println!("{executable} {command} \n\nload a rom/binary from path"); + exit(0); + } + "help" | "--help" | "-h" => { + println!("{executable} {command} \n\nshow help info for a given command"); exit(0); } _ => { println!( - "{:?} isn't a valid command!\n\nuse `{} help` to see all valid commands", - &args[1], &args[0] + "{command:?} isn't a valid command!\n\nuse `{executable} help` to see all valid commands", ); exit(1); } - }, - 3 => match &args[1] as &str { - "help" => match &args[2] as &str { - "rom" => { - println!("{} rom \n\nload a rom/binary from path", &args[0]); - exit(0); - } - _ => { - println!( - "{:?} isn't a valid command!\n\nuse `{} help` to see all valid commands", - &args[2], &args[0] - ); - exit(1); - } - }, - "rom" => { - let rom = match std::fs::File::open(&args[2]) { - Ok(file) => file, - Err(error) => { - match error.kind() { - ErrorKind::NotFound => { - println!("couldn't find the rom at {:?}", &args[2]); - } - ErrorKind::PermissionDenied => { - println!( - "didn't have sufficient permissions to open the rom at {:?}", - &args[2] - ); - } - _ => { - println!("something went wrong! try again in a moment? really not sure why you're getting this error"); - } - } - exit(1); - } - }; - if let Err(error) = memory.load_rom(rom) { - println!("oh no! this rom couldn't be loaded: {:?}", error); - exit(1); - }; - } - _ => { - println!( - "{:?} isn't a valid command!\n\nuse `{} help` to see all valid commands", - &args[1], &args[0] - ); - exit(1); - } - }, - _ => { - println!( - "too many arguments were provided!\n\nuse `{} help` to see all valid commands", - &args[0] - ); - exit(1); } + } else { + println!("ʕ·ᴥ·ʔ- {executable} is an emulator for george:"); + println!("https://git.augustkline.com/august/george\n"); + println!("commands:"); + println!(" help, -h, --help : print help info for any command"); + println!(" rom, -r, --rom : load a rom/binary from path"); + println!(" screen, -s, --screen : use the \"terminal\" or \"window\" display type"); + println!("\nconfiguration:"); + println!(" george-emu searches for a `george.toml` in the current directory.\n in `george.toml` you can specify a path for the character rom using the key `char_rom` and the main rom/binary with the key `rom`"); + exit(0); } } + +pub fn get_input() -> Config { + let executable: String = env::args().next().unwrap(); + let mut config = ConfigBuilder::new(); + + let len = env::args().len(); + + if len == 1 { + config = match File::open("./george.toml") { + Ok(mut file) => { + let mut string = String::new(); + file.read_to_string(&mut string).unwrap(); + toml::from_str(string.as_str()).unwrap() + } + Err(_) => { + println!("couldn't find a `george.toml` in the current directory!"); + exit(1); + } + }; + return config.build(); + } + + let mut args = env::args().skip(1); + + while let Some(arg) = args.next() { + match &arg[..] { + "--help" | "-h" | "help" => help(args.next()), + "--rom" | "-r" | "rom" => { + if let Some(path) = args.next() { + match std::fs::File::open(&path) { + Ok(_) => { + config.rom = Some(path); + } + Err(error) => { + match error.kind() { + ErrorKind::NotFound => { + println!("couldn't find the rom at {path:?}"); + } + ErrorKind::PermissionDenied => { + println!( + "didn't have sufficient permissions to open the rom at {path:?}" + ); + } + _ => { + println!("something went wrong! try again in a moment? really not sure why you're getting this error"); + } + } + exit(1); + } + }; + } else { + println!("no rom specified!"); + exit(1); + } + } + "--screen" | "-s" | "screen" => { + let kind = args.next(); + match kind { + Some(kind) => match &kind as &str { + "terminal" | "Terminal" | "TERMINAL" | "t" | "term" => {} + "window" | "Window" | "WINDOW" | "w" | "win" => { + config.screen = ScreenType::Window + } + _ => { + println!("{kind:?} isn't a valid screen type!\n\nuse \"{executable} --help screen\" to see all valid screen types"); + exit(1); + } + }, + None => { + println!("no screen type was provided!\n\nuse \"{executable} --help screen\" to see all valid screen types"); + exit(1); + } + } + } + _ => { + println!("{arg:?} isn't a valid command!\n\nuse \"{executable} help\" to see all valid commands"); + exit(1); + } + } + } + config.build() +} diff --git a/src/keyboard.rs b/src/keyboard.rs index 1efca0c..0998463 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -1,5 +1,5 @@ -use crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyModifiers}; -// use minifb::{InputCallback, Key}; +use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; +use minifb::{InputCallback, Key}; use crate::memory::{MemHandle, MemoryWriter}; @@ -18,56 +18,61 @@ impl Keyboard { let mut row3 = 0; let mut row4 = 0; let mut row5 = 0; - if key.kind == KeyEventKind::Press || key.kind == KeyEventKind::Repeat { - match key.modifiers { - KeyModifiers::SHIFT => row2 ^= 0b1000_0000, - KeyModifiers::CONTROL => row3 ^= 0b1000_0000, - KeyModifiers::ALT => row4 ^= 0b1000_0000, - KeyModifiers::META => row5 ^= 0b1000_0000, - KeyModifiers::SUPER => row5 ^= 0b0010_0000, - _ => {} - }; + match key.modifiers { + KeyModifiers::SHIFT => row2 ^= 0b1000_0000, + KeyModifiers::CONTROL => row3 ^= 0b1000_0000, + KeyModifiers::ALT => row4 ^= 0b1000_0000, + KeyModifiers::META => row5 ^= 0b1000_0000, + KeyModifiers::SUPER => row5 ^= 0b0010_0000, + _ => {} + }; - match key.code { - KeyCode::Esc => row0 ^= 0b1000_0000, - KeyCode::Char('w') => row0 ^= 0b0100_0000, - KeyCode::Char('e') => row0 ^= 0b0010_0000, - KeyCode::Char('r') => row0 ^= 0b0001_0000, - KeyCode::Char('t') => row0 ^= 0b0000_1000, - KeyCode::Char('u') => row0 ^= 0b0000_0100, - KeyCode::Char('o') => row0 ^= 0b0000_0010, - KeyCode::Backspace => row0 ^= 0b0000_0001, - KeyCode::Tab => row1 ^= 0b1000_0000, - KeyCode::Char('q') => row1 ^= 0b0100_0000, - KeyCode::Char('s') => row1 ^= 0b0010_0000, - KeyCode::Char('g') => row1 ^= 0b0001_0000, - KeyCode::Char('y') => row1 ^= 0b0000_1000, - KeyCode::Char('i') => row1 ^= 0b0000_0100, - KeyCode::Char('p') => row1 ^= 0b0000_0010, - KeyCode::Enter => row1 ^= 0b0000_0001, - KeyCode::Char('d') => row2 ^= 0b0100_0000, - KeyCode::Char('v') => row2 ^= 0b0010_0000, - KeyCode::Char('h') => row2 ^= 0b0001_0000, - KeyCode::Char('k') => row2 ^= 0b0000_1000, - KeyCode::Char('\'') => row2 ^= 0b0000_0100, - KeyCode::Char('/') => row2 ^= 0b0000_0010, - KeyCode::Char('a') => row2 ^= 0b0000_0001, - KeyCode::Char('z') => row3 ^= 0b0100_0000, - KeyCode::Char('f') => row3 ^= 0b0010_0000, - KeyCode::Char('b') => row3 ^= 0b0001_0000, - KeyCode::Char('j') => row3 ^= 0b0000_1000, - KeyCode::Char('l') => row3 ^= 0b0000_0100, - KeyCode::Char('2') => row3 ^= 0b0000_0010, - KeyCode::Char('4') => row3 ^= 0b0000_0001, - KeyCode::Char('x') => row4 ^= 0b0100_0000, - KeyCode::Char('c') => row4 ^= 0b0010_0000, - KeyCode::Char('n') => row4 ^= 0b0001_0000, - KeyCode::Char('m') => row4 ^= 0b0000_1000, - KeyCode::Char(',') => row4 ^= 0b0000_0100, - KeyCode::Char('1') => row4 ^= 0b0000_0010, - KeyCode::Char('3') => row4 ^= 0b0000_0001, - KeyCode::Char(' ') => row5 ^= 0b0100_0000, - _ => {} + match key.code { + KeyCode::Esc => row0 ^= 0b1000_0000, + KeyCode::Char('w') => row0 ^= 0b0100_0000, + KeyCode::Char('e') => row0 ^= 0b0010_0000, + KeyCode::Char('r') => row0 ^= 0b0001_0000, + KeyCode::Char('t') => row0 ^= 0b0000_1000, + KeyCode::Char('u') => row0 ^= 0b0000_0100, + KeyCode::Char('o') => row0 ^= 0b0000_0010, + KeyCode::Backspace => row0 ^= 0b0000_0001, + KeyCode::Tab => row1 ^= 0b1000_0000, + KeyCode::Char('q') => row1 ^= 0b0100_0000, + KeyCode::Char('s') => row1 ^= 0b0010_0000, + KeyCode::Char('g') => row1 ^= 0b0001_0000, + KeyCode::Char('y') => row1 ^= 0b0000_1000, + KeyCode::Char('i') => row1 ^= 0b0000_0100, + KeyCode::Char('p') => row1 ^= 0b0000_0010, + KeyCode::Enter => row1 ^= 0b0000_0001, + KeyCode::Char('d') => row2 ^= 0b0100_0000, + KeyCode::Char('v') => row2 ^= 0b0010_0000, + KeyCode::Char('h') => row2 ^= 0b0001_0000, + KeyCode::Char('k') => row2 ^= 0b0000_1000, + KeyCode::Char('\'') => row2 ^= 0b0000_0100, + KeyCode::Char('/') => row2 ^= 0b0000_0010, + KeyCode::Char('a') => row2 ^= 0b0000_0001, + KeyCode::Char('z') => row3 ^= 0b0100_0000, + KeyCode::Char('f') => row3 ^= 0b0010_0000, + KeyCode::Char('b') => row3 ^= 0b0001_0000, + KeyCode::Char('j') => row3 ^= 0b0000_1000, + KeyCode::Char('l') => row3 ^= 0b0000_0100, + KeyCode::Char('2') => row3 ^= 0b0000_0010, + KeyCode::Char('4') => row3 ^= 0b0000_0001, + KeyCode::Char('x') => row4 ^= 0b0100_0000, + KeyCode::Char('c') => row4 ^= 0b0010_0000, + KeyCode::Char('n') => row4 ^= 0b0001_0000, + KeyCode::Char('m') => row4 ^= 0b0000_1000, + KeyCode::Char(',') => row4 ^= 0b0000_0100, + KeyCode::Char('1') => row4 ^= 0b0000_0010, + KeyCode::Char('3') => row4 ^= 0b0000_0001, + KeyCode::Char(' ') => row5 ^= 0b0100_0000, + _ => { + row0 = 0; + row1 = 0; + row2 = 0; + row3 = 0; + row4 = 0; + row5 = 0; } }; @@ -86,68 +91,68 @@ impl MemoryWriter for Keyboard { } } -// impl InputCallback for Keyboard { -// fn add_char(&mut self, _uni_char: u32) {} -// fn set_key_state(&mut self, key: Key, _state: bool) { -// let mut row0 = 0; -// let mut row1 = 0; -// let mut row2 = 0; -// let mut row3 = 0; -// let mut row4 = 0; -// let mut row5 = 0; +impl InputCallback for Keyboard { + fn add_char(&mut self, _uni_char: u32) {} + fn set_key_state(&mut self, key: Key, _state: bool) { + let mut row0 = 0; + let mut row1 = 0; + let mut row2 = 0; + let mut row3 = 0; + let mut row4 = 0; + let mut row5 = 0; -// match key { -// Key::Escape => row0 ^= 0b1000_0000, -// Key::W => row0 ^= 0b0100_0000, -// Key::E => row0 ^= 0b0010_0000, -// Key::R => row0 ^= 0b0001_0000, -// Key::T => row0 ^= 0b0000_1000, -// Key::U => row0 ^= 0b0000_0100, -// Key::O => row0 ^= 0b0000_0010, -// Key::Backspace => row0 ^= 0b0000_0001, -// Key::Tab => row1 ^= 0b1000_0000, -// Key::Q => row1 ^= 0b0100_0000, -// Key::S => row1 ^= 0b0010_0000, -// Key::G => row1 ^= 0b0001_0000, -// Key::Y => row1 ^= 0b0000_1000, -// Key::I => row1 ^= 0b0000_0100, -// Key::P => row1 ^= 0b0000_0010, -// Key::Enter => row1 ^= 0b0000_0001, -// Key::LeftShift | Key::RightShift => row2 ^= 0b1000_0000, -// Key::D => row2 ^= 0b0100_0000, -// Key::V => row2 ^= 0b0010_0000, -// Key::H => row2 ^= 0b0001_0000, -// Key::K => row2 ^= 0b0000_1000, -// Key::Apostrophe => row2 ^= 0b0000_0100, -// Key::Slash => row2 ^= 0b0000_0010, -// Key::A => row2 ^= 0b0000_0001, -// Key::LeftCtrl | Key::RightCtrl => row3 ^= 0b1000_0000, -// Key::Z => row3 ^= 0b0100_0000, -// Key::F => row3 ^= 0b0010_0000, -// Key::B => row3 ^= 0b0001_0000, -// Key::J => row3 ^= 0b0000_1000, -// Key::L => row3 ^= 0b0000_0100, -// Key::Key2 => row3 ^= 0b0000_0010, -// Key::Key4 => row3 ^= 0b0000_0001, -// Key::LeftAlt | Key::RightAlt => row4 ^= 0b1000_0000, -// Key::X => row4 ^= 0b0100_0000, -// Key::C => row4 ^= 0b0010_0000, -// Key::N => row4 ^= 0b0001_0000, -// Key::M => row4 ^= 0b0000_1000, -// Key::Comma => row4 ^= 0b0000_0100, -// Key::Key1 => row4 ^= 0b0000_0010, -// Key::Key3 => row4 ^= 0b0000_0001, -// Key::LeftSuper => row5 ^= 0b1000_0000, -// Key::Space => row5 ^= 0b0100_0000, -// Key::RightSuper => row5 ^= 0b0010_0000, -// _ => {} -// }; + match key { + Key::Escape => row0 ^= 0b1000_0000, + Key::W => row0 ^= 0b0100_0000, + Key::E => row0 ^= 0b0010_0000, + Key::R => row0 ^= 0b0001_0000, + Key::T => row0 ^= 0b0000_1000, + Key::U => row0 ^= 0b0000_0100, + Key::O => row0 ^= 0b0000_0010, + Key::Backspace => row0 ^= 0b0000_0001, + Key::Tab => row1 ^= 0b1000_0000, + Key::Q => row1 ^= 0b0100_0000, + Key::S => row1 ^= 0b0010_0000, + Key::G => row1 ^= 0b0001_0000, + Key::Y => row1 ^= 0b0000_1000, + Key::I => row1 ^= 0b0000_0100, + Key::P => row1 ^= 0b0000_0010, + Key::Enter => row1 ^= 0b0000_0001, + Key::LeftShift | Key::RightShift => row2 ^= 0b1000_0000, + Key::D => row2 ^= 0b0100_0000, + Key::V => row2 ^= 0b0010_0000, + Key::H => row2 ^= 0b0001_0000, + Key::K => row2 ^= 0b0000_1000, + Key::Apostrophe => row2 ^= 0b0000_0100, + Key::Slash => row2 ^= 0b0000_0010, + Key::A => row2 ^= 0b0000_0001, + Key::LeftCtrl | Key::RightCtrl => row3 ^= 0b1000_0000, + Key::Z => row3 ^= 0b0100_0000, + Key::F => row3 ^= 0b0010_0000, + Key::B => row3 ^= 0b0001_0000, + Key::J => row3 ^= 0b0000_1000, + Key::L => row3 ^= 0b0000_0100, + Key::Key2 => row3 ^= 0b0000_0010, + Key::Key4 => row3 ^= 0b0000_0001, + Key::LeftAlt | Key::RightAlt => row4 ^= 0b1000_0000, + Key::X => row4 ^= 0b0100_0000, + Key::C => row4 ^= 0b0010_0000, + Key::N => row4 ^= 0b0001_0000, + Key::M => row4 ^= 0b0000_1000, + Key::Comma => row4 ^= 0b0000_0100, + Key::Key1 => row4 ^= 0b0000_0010, + Key::Key3 => row4 ^= 0b0000_0001, + Key::LeftSuper => row5 ^= 0b1000_0000, + Key::Space => row5 ^= 0b0100_0000, + Key::RightSuper => row5 ^= 0b0010_0000, + _ => {} + }; -// self.memory.write(0x4400, row0); -// self.memory.write(0x4401, row1); -// self.memory.write(0x4402, row2); -// self.memory.write(0x4403, row3); -// self.memory.write(0x4404, row4); -// self.memory.write(0x4405, row5); -// } -// } + self.memory.write(0x4400, row0); + self.memory.write(0x4401, row1); + self.memory.write(0x4402, row2); + self.memory.write(0x4403, row3); + self.memory.write(0x4404, row4); + self.memory.write(0x4405, row5); + } +} diff --git a/src/main.rs b/src/main.rs index d2718a1..fd128b3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,7 +17,7 @@ use crossterm::execute; use crossterm::terminal::{size, Clear, ClearType, SetSize}; // use cpu::CpuController; use memory::MemHandle; -// use minifb::{Scale, ScaleMode, Window, WindowOptions}; +use minifb::{Scale, ScaleMode, Window, WindowOptions}; use std::io::{stdout, Result}; use std::thread::{self, sleep}; use std::time::Duration; @@ -61,9 +61,10 @@ fn main() -> Result<()> { let mut stdout = stdout(); let (cols, rows) = size()?; - let mut memory = Mem::new(); + let config = get_input(); - get_input(&mut memory); + let mut memory = Mem::new(); + let _ = memory.load_rom(&config.rom); execute!(stdout, SetSize(64, 29), cursor::Hide, EnterAlternateScreen)?; enable_raw_mode()?; @@ -82,20 +83,18 @@ fn main() -> Result<()> { cpu.cycle(); }); - let stdout_lock = stdout.lock(); - let renderer = TerminalRenderer::new(screen_memory, stdout_lock); - let mut screen = Screen::new(renderer, cpu_controller); + let mut screen = Screen::new(&config, cpu_controller, screen_memory); loop { sleep(Duration::from_millis(16)); screen.draw(); - if event::poll(std::time::Duration::from_millis(16))? { - if let event::Event::Key(key) = event::read()? { - keyboard.read_keys(key); - if key.kind == KeyEventKind::Press && key.code == KeyCode::Char('q') { - break; - } + // if event::poll(std::time::Duration::from_millis(16))? { + if let event::Event::Key(key) = event::read()? { + keyboard.read_keys(key); + if key.kind == KeyEventKind::Press && key.code == KeyCode::Char('q') { + break; } + // } } } diff --git a/src/memory.rs b/src/memory.rs index 2f65fa2..175327d 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,7 +1,7 @@ use anyhow::{bail, Result}; use std::cell::RefCell; use std::io::{self, Write}; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::rc::Rc; use std::str::FromStr; use std::sync::{Arc, Mutex}; @@ -71,7 +71,14 @@ impl Mem { self.0[address as usize] = data; } - pub fn load_rom(&mut self, rom: File) -> Result<()> { + pub fn load_rom

(&mut self, rom: P) -> Result<()> + where + P: AsRef, + { + let rom = match File::open(rom) { + Ok(rom) => rom, + Err(_) => bail!("rom could not be opened!"), + }; let bytes = rom.bytes(); for (address, byte) in bytes.enumerate() { // println!("{address}"); diff --git a/src/roms/keyboard.asm b/src/roms/keyboard.asm index b3a9289..29c8d84 100644 --- a/src/roms/keyboard.asm +++ b/src/roms/keyboard.asm @@ -4,6 +4,7 @@ .org $8000 n = $01 ; temporary storage for data stack operations +key_buffer = $200 reset: sei @@ -27,10 +28,40 @@ cleardisplay: cli main: - ; jmp draw - jmp cleardisplay + ; jsr read_keys + ; lda key_buffer + lda $4402 + sta $6000 jmp main +; first, let's do the simplest case of just the letter a being pressed +; 1. check row 2 ($4402, where the a key is) for pressed keys +; 2. if any keys are pressed, check if the key is a (bit 0) +; 3. if the key is not a, store zero in the key buffer +; 4. if the key is a, store the ascii for a in the key buffer +; 5. return + +; so u don't have to scroll: key_buffer = $200 +; key buffer just shows the current state of the keyboard +; it is not a queue of keys to print + +read_keys: + lda $4402 ; check row 2 + beq check_key ; if there're any keys, check which + clear_buffer: + stz key_buffer + rts + check_key: ; if there is a key pressed, check if it's a + ror + bmi store_key + rts + store_key: + ; in a sec we'll set up ascii table lookup, + ; for now let's just load the ascii byte for a + lda #$61 + sta key_buffer +rts + draw: push_coords #0, #0 push_char #$af @@ -73,8 +104,6 @@ fill: ; fills an area from (x1, y1) to (x2, y2) will character c, (n1: c n2: x1 jsr get_char_address irq: - - keyboard: rts isr: ; interrupt service routine diff --git a/src/tui/mod.rs b/src/tui/mod.rs deleted file mode 100644 index 49c9793..0000000 --- a/src/tui/mod.rs +++ /dev/null @@ -1,175 +0,0 @@ -// use std::time::Duration; - -mod tabs; -mod term; -use std::{ - io::Result, - sync::{mpsc::Receiver, Arc, Mutex}, - time::Duration, -}; - -use crossterm::event::{self, poll, Event, KeyCode, KeyEvent, KeyEventKind}; -use ratatui::{ - layout::Layout, - prelude::*, - widgets::{Block, Cell, Paragraph, Row, Table, TableState}, -}; - -use crate::{ - cpu::{CpuController, CpuState}, - memory::{MemHandle, MemoryReader, MemoryWriter}, -}; - -pub struct App { - cpu: CpuController, - state: Receiver, - memory: MemHandle, - running: bool, - table_state: TableState, -} - -impl MemoryReader for App { - fn read(&self, address: u16) -> u8 { - self.memory.read(address) - } -} -impl MemoryWriter for App { - fn write(&self, address: u16, data: u8) { - self.memory.write(address, data) - } -} - -impl App { - pub fn new(cpu: CpuController, memory: MemHandle, state: Receiver) -> Self { - Self { - cpu, - memory, - running: true, - table_state: TableState::default(), - state, - } - } - pub fn update(&mut self, terminal: &mut Terminal) -> Result<()> { - self.draw(terminal)?; - self.handle_events()?; - Ok(()) - } - - /// Draw a single frame of the app. - fn draw(&self, terminal: &mut Terminal) -> Result<()> { - terminal - .draw(|frame| { - frame.render_widget(self, frame.size()); - }) - .unwrap(); - Ok(()) - } - - fn handle_events(&mut self) -> Result<()> { - if poll(Duration::from_secs_f32(1.0 / 25.0))? { - match event::read()? { - Event::Key(key) if key.kind == KeyEventKind::Press => self.handle_key_press(key), - _ => {} - } - } - Ok(()) - } - - fn handle_key_press(&mut self, key: KeyEvent) { - // let mut cpu = self.cpu.lock().unwrap(); - match key.code { - KeyCode::Enter => { - if !self.running { - // cpu.cycle() - } - } - KeyCode::Char(' ') => { - self.running = !self.running; - match self.running { - true => self.cpu.stop(), - false => self.cpu.resume(), - } - } - _ => {} - }; - } -} - -/// Implement Widget for &App rather than for App as we would otherwise have to clone or copy the -/// entire app state on every frame. For this example, the app state is small enough that it doesn't -/// matter, but for larger apps this can be a significant performance improvement. -impl Widget for &App { - fn render(self, area: Rect, buf: &mut Buffer) { - self.cpu.data(); - let [cpu_area, memory_area] = - Layout::horizontal([Constraint::Percentage(50), Constraint::Fill(1)]).areas(area); - let [memory_table, memory_info] = - Layout::vertical([Constraint::Fill(1), Constraint::Percentage(10)]).areas(memory_area); - - let cpu = self.state.recv().unwrap(); - let cpu_info = format!("a: {a:#04x}, x: {x:#04x}, y: {y:#04x}, pc: {pc:#06x}, sp: {s:#04x}, sr: {p:#010b}, irq: {irq:?}, nmi: {nmi:?}", a = cpu.a, x = cpu.x, y = cpu.y, pc = cpu.pc, s = cpu.s, p = cpu.p, irq = cpu.irq, nmi = cpu.nmi); - - Paragraph::new(cpu_info) - .block(Block::bordered().title("cpu info!")) - .render(cpu_area, buf); - - // let table_height = memory_table.rows().count() - 2; - // TODO: impl Iterator for MemHandle so we can get references to the underlying data from other threads without cloning - // let rows: Vec = self.memory.data()[0..table_height * 16] - // .chunks(16) - // .map(|chunk| { - // chunk - // .iter() - // .map(|content| Cell::from(Text::from(format!("{content:x}")))) - // .collect::() - // }) - // .collect(); - let zero = self.memory.read(0); - let rows: Vec = vec![Row::new([format!("{zero}")])]; - let widths = vec![Constraint::Length(2); 16]; - Widget::render( - Table::new(rows, widths) - .header( - Row::new(vec![ - "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", - "f", - ]) - .style(Style::new().bold()), // To add space between the header and the rest of the rows, specify the margin - // .bottom_margin(1), - ) - .block(Block::bordered().title_top("memory")), - memory_table, - buf, - ); - Paragraph::new(format!("program counter: {:#04x}", cpu.pc)) - .block(Block::bordered().title_top("info")) - .render(memory_info, buf); - } -} - -// impl Widget for App { -// fn render_title_bar(&self, area: Rect, buf: &mut Buffer) { -// // let layout = Layout::horizontal([Constraint::Min(0), Constraint::Length(43)]); -// // let [title] = layout.areas(area); - -// Span::styled("Ratatui", Style::default()).render(area, buf); -// // let titles = Tab::iter().map(Tab::title); -// // Tabs::new(titles) -// // .style(THEME.tabs) -// // .highlight_style(THEME.tabs_selected) -// // .select(self.tab as usize) -// // .divider("") -// // .padding("", "") -// // .render(tabs, buf); -// } - -// // fn render_selected_tab(&self, area: Rect, buf: &mut Buffer) { -// // match self.tab { -// // Tab::About => self.about_tab.render(area, buf), -// // Tab::Recipe => self.recipe_tab.render(area, buf), -// // Tab::Email => self.email_tab.render(area, buf), -// // Tab::Traceroute => self.traceroute_tab.render(area, buf), -// // Tab::Weather => self.weather_tab.render(area, buf), -// // }; -// // } -// } diff --git a/src/video.rs b/src/video.rs index e496c32..b0e08c9 100644 --- a/src/video.rs +++ b/src/video.rs @@ -3,28 +3,27 @@ use crossterm::{ execute, queue, style::{Color, PrintStyledContent, Stylize}, }; +use minifb::{Scale, ScaleMode, Window, WindowOptions}; +use serde::{Deserialize, Serialize}; use crate::{ + cli::Config, cpu::CpuController, memory::{MemHandle, MemoryReader}, types::{Byte, Word}, }; use std::{ fs::File, - io::{Read, StdoutLock, Write}, + io::{self, Read, Write}, path::Path, - process::exit, - sync::mpsc::Sender, - time::Instant, }; const FG_COLOR: u32 = 0xFFCC00; const BG_COLOR: u32 = 0x110500; -// // BGR for softbuffer -// const FG_COLOR: u32 = 0x00CCFF; -// const BG_COLOR: u32 = 0x000511; -// -pub fn get_char_bin

(char_rom: Option

) -> Vec +const WIDTH: usize = 512; +const HEIGHT: usize = 380; + +pub fn get_char_bin

(char_rom: Option

) -> [u8; 0x8000] where P: AsRef, { @@ -34,9 +33,9 @@ where let mut bin = vec![0; 0x8000]; file.read_exact(&mut bin).unwrap(); // println!("reading char rom"); - bin + bin.try_into().unwrap() } - None => include_bytes!("./roms/cozette.rom").to_vec(), + None => *include_bytes!("./roms/cozette.rom"), } } @@ -44,14 +43,21 @@ trait Renderer { fn render(&mut self) {} } +#[derive(Serialize, Deserialize, Debug, Default)] +pub enum ScreenType { + Window, + #[default] + Terminal, +} + pub struct WindowRenderer { - char_rom: Vec, - window: Sender>, + char_rom: [u8; 0x8000], + window: Window, memory: MemHandle, } impl WindowRenderer { - pub fn new

(memory: MemHandle, char_rom: Option

, window: Sender>) -> Self + pub fn new

(memory: MemHandle, char_rom: Option

, window: Window) -> Self where P: AsRef, { @@ -70,7 +76,7 @@ impl Renderer for WindowRenderer { // based on the specifics of george's weird // display and characters... don't fuck around w it let mut i = 0; - let mut buffer = vec![0; 512 * 380]; + let mut buffer = [0; 512 * 380]; for char_row in 0..29 { for char_col in 0..64 { let ascii = self.read(0x6000 + i); @@ -89,7 +95,9 @@ impl Renderer for WindowRenderer { } } - let _ = self.window.send(buffer.clone()); + self.window + .update_with_buffer(&buffer, WIDTH, HEIGHT) + .unwrap(); } } } @@ -100,19 +108,23 @@ impl MemoryReader for WindowRenderer { } } -pub struct TerminalRenderer<'a> { +// pub struct TerminalRenderer<'a> { +pub struct TerminalRenderer { memory: MemHandle, // stdout: Stdout, - stdout: StdoutLock<'a>, + // stdout: StdoutLock<'a>, } -impl<'a> TerminalRenderer<'a> { - pub fn new(memory: MemHandle, stdout: StdoutLock<'a>) -> Self { - Self { memory, stdout } +// impl<'a> TerminalRenderer<'a> { +impl TerminalRenderer { + // pub fn new(memory: MemHandle, stdout: StdoutLock<'a>) -> Self { + pub fn new(memory: MemHandle) -> Self { + Self { memory } } } -impl MemoryReader for TerminalRenderer<'_> { +// impl MemoryReader for TerminalRenderer<'_> { +impl MemoryReader for TerminalRenderer { fn read(&self, address: Word) -> Byte { self.memory.read(address) } @@ -135,10 +147,12 @@ const ASCII_LOOPUP: [&str; 256] = [ "", "", "", "", "🎁", "", "", "", "", "⚐", "⚑", "", "", "", ]; -impl Renderer for TerminalRenderer<'_> { +// impl Renderer for TerminalRenderer<'_> { +impl Renderer for TerminalRenderer { fn render(&mut self) { // let now = Instant::now(); - let _ = execute!(self.stdout, SavePosition); + let mut stdout = io::stdout(); + let _ = execute!(stdout, SavePosition); let mut i = 0; for char_row in 0..29 { for char_col in 0..64 { @@ -148,7 +162,7 @@ impl Renderer for TerminalRenderer<'_> { let _ = queue!( // FG_COLOR = 0xFFCC00 // BG_COLOR = 0x110500 - self.stdout, + stdout, MoveTo(char_col, char_row), PrintStyledContent( char.with(Color::Rgb { @@ -165,23 +179,46 @@ impl Renderer for TerminalRenderer<'_> { ); } } - let _ = self.stdout.flush(); - let _ = execute!(self.stdout, RestorePosition); + let _ = stdout.flush(); + let _ = execute!(stdout, RestorePosition); // let elapsed = now.elapsed(); // println!("{elapsed:?}"); // exit(0); } } -pub struct Screen<'a> { - // renderer: Box, - renderer: TerminalRenderer<'a>, +// pub struct Screen<'a> { +pub struct Screen { + renderer: Box, + // renderer: TerminalRenderer<'a>, controller: CpuController, } -impl<'a> Screen<'a> { - // pub fn new(cpu_controller: CpuController, renderer: Box) -> Self { - pub fn new(renderer: TerminalRenderer<'a>, controller: CpuController) -> Self { +impl Screen { + pub fn new(config: &Config, controller: CpuController, memory: MemHandle) -> Self { + let renderer: Box = match config.screen { + ScreenType::Window => { + let window = Window::new( + "ʕ·ᴥ·ʔ-☆", + 512, + 380, + WindowOptions { + resize: true, + borderless: true, + title: true, + transparency: false, + scale: Scale::X2, + scale_mode: ScaleMode::AspectRatioStretch, + topmost: false, + none: true, + }, + ) + .unwrap(); + Box::new(WindowRenderer::new(memory, config.char_rom.clone(), window)) + } + ScreenType::Terminal => Box::new(TerminalRenderer::new(memory)), + }; + Self { renderer, controller,