much better cli! more ergonomic screen struct! window screen type still broken but working on it!

This commit is contained in:
august kline 2024-06-30 22:49:54 -04:00
parent 8890853656
commit ac4619406d
9 changed files with 931 additions and 440 deletions

549
Cargo.lock generated
View File

@ -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"

View File

@ -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"] }

View File

@ -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<String>,
char_rom: Option<String>,
rom: String,
screen: ScreenType,
}
pub fn get_input(memory: &mut Mem) {
let args: Vec<String> = 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 <command>: print help info for any command");
println!(" rom <path>: 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<String>,
}
fn help(command: Option<String>) {
let executable: String = env::args().next().unwrap();
if let Some(command) = command {
match &command as &str {
"rom" | "--rom" | "-r" => {
println!("{executable} {command} <path>\n\nload a rom/binary from path");
exit(0);
}
"screen" | "--screen" | "-s" => {
println!("{executable} {command} <path>\n\nload a rom/binary from path");
exit(0);
}
"help" | "--help" | "-h" => {
println!("{executable} {command} <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 <path>\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 <command>: print help info for any command");
println!(" rom, -r, --rom <path>: load a rom/binary from path");
println!(" screen, -s, --screen <type>: 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()
}

View File

@ -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);
}
}

View File

@ -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;
}
// }
}
}

View File

@ -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<P>(&mut self, rom: P) -> Result<()>
where
P: AsRef<Path>,
{
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}");

View File

@ -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

View File

@ -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<CpuState>,
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<CpuState>) -> Self {
Self {
cpu,
memory,
running: true,
table_state: TableState::default(),
state,
}
}
pub fn update(&mut self, terminal: &mut Terminal<impl Backend>) -> Result<()> {
self.draw(terminal)?;
self.handle_events()?;
Ok(())
}
/// Draw a single frame of the app.
fn draw(&self, terminal: &mut Terminal<impl Backend>) -> 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<Row> = self.memory.data()[0..table_height * 16]
// .chunks(16)
// .map(|chunk| {
// chunk
// .iter()
// .map(|content| Cell::from(Text::from(format!("{content:x}"))))
// .collect::<Row>()
// })
// .collect();
let zero = self.memory.read(0);
let rows: Vec<Row> = 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),
// // };
// // }
// }

View File

@ -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<P>(char_rom: Option<P>) -> Vec<u8>
const WIDTH: usize = 512;
const HEIGHT: usize = 380;
pub fn get_char_bin<P>(char_rom: Option<P>) -> [u8; 0x8000]
where
P: AsRef<Path>,
{
@ -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<u8>,
window: Sender<Vec<u32>>,
char_rom: [u8; 0x8000],
window: Window,
memory: MemHandle,
}
impl WindowRenderer {
pub fn new<P>(memory: MemHandle, char_rom: Option<P>, window: Sender<Vec<u32>>) -> Self
pub fn new<P>(memory: MemHandle, char_rom: Option<P>, window: Window) -> Self
where
P: AsRef<Path>,
{
@ -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<dyn Renderer>,
renderer: TerminalRenderer<'a>,
// pub struct Screen<'a> {
pub struct Screen {
renderer: Box<dyn Renderer>,
// renderer: TerminalRenderer<'a>,
controller: CpuController,
}
impl<'a> Screen<'a> {
// pub fn new(cpu_controller: CpuController, renderer: Box<dyn Renderer>) -> 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<dyn Renderer> = 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,