From 8ac0cbc57bce354981a461c882a4d26bb072f770 Mon Sep 17 00:00:00 2001 From: august kline Date: Sun, 1 Sep 2024 23:13:06 -0400 Subject: [PATCH] Very messy but we're running on the web now! (not in this crate, but will add web-sys to georgeemu bin in a bit) --- Cargo.lock | 292 ++++++-------------------------- Cargo.toml | 26 ++- george.toml | 2 +- run.sh | 2 +- src/{cli.rs => bin/cli/mod.rs} | 14 +- src/bin/main.rs | 33 ++++ src/cpu.rs | 104 ++++-------- src/instructions.rs | 296 ++++++++++++++++++--------------- src/keyboard.rs | 4 + src/lib.rs | 271 ++++++++++++++++++++++++++++++ src/main.rs | 74 --------- src/memory.rs | 61 ++++--- src/roms/demo.asm | 123 ++++++++++++++ src/roms/demo.rom | Bin 0 -> 32768 bytes src/types.rs | 4 - src/video.rs | 165 ++++++------------ 16 files changed, 787 insertions(+), 684 deletions(-) rename src/{cli.rs => bin/cli/mod.rs} (96%) create mode 100644 src/bin/main.rs create mode 100644 src/lib.rs delete mode 100644 src/main.rs create mode 100644 src/roms/demo.asm create mode 100644 src/roms/demo.rom delete mode 100644 src/types.rs diff --git a/Cargo.lock b/Cargo.lock index b43c63d..c2128e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,31 +14,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" -[[package]] -name = "bdf" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f550a6818e6f42ccd5883f44e45fff4f68415a0d09abdc81e7d1d78e0780af14" -dependencies = [ - "bit-set", - "thiserror", -] - -[[package]] -name = "bit-set" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84527c7b0452f22545cc010e72d366a435561d2b28b978035550b3778c4d428d" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - [[package]] name = "bitflags" version = "1.3.2" @@ -70,28 +45,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "crossterm" -version = "0.27.0" +name = "console_error_panic_hook" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" dependencies = [ - "bitflags 2.5.0", - "crossterm_winapi", - "libc", - "mio", - "parking_lot", - "signal-hook", - "signal-hook-mio", - "winapi", -] - -[[package]] -name = "crossterm_winapi" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" -dependencies = [ - "winapi", + "cfg-if", + "wasm-bindgen", ] [[package]] @@ -122,7 +82,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -225,8 +185,7 @@ name = "georgeemu" version = "0.1.0" dependencies = [ "anyhow", - "bdf", - "crossterm", + "console_error_panic_hook", "minifb", "serde", "termion", @@ -261,6 +220,12 @@ dependencies = [ "web-sys", ] +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + [[package]] name = "js-sys" version = "0.3.69" @@ -289,7 +254,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d" dependencies = [ "cfg-if", - "windows-targets 0.52.5", + "windows-targets", ] [[package]] @@ -300,7 +265,7 @@ checksum = "3af92c55d7d839293953fcd0fda5ecfe93297cfde6ffbdec13b41d99c0ba6607" dependencies = [ "bitflags 2.5.0", "libc", - "redox_syscall 0.4.1", + "redox_syscall", ] [[package]] @@ -309,16 +274,6 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" -[[package]] -name = "lock_api" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" -dependencies = [ - "autocfg", - "scopeguard", -] - [[package]] name = "log" version = "0.4.21" @@ -343,10 +298,9 @@ dependencies = [ [[package]] name = "minifb" version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c470a74618b43cd182c21b3dc1e6123501249f3bad9a0085e95d1304ca2478" dependencies = [ "cc", + "console_error_panic_hook", "dlib", "futures", "instant", @@ -358,26 +312,16 @@ dependencies = [ "serde", "serde_derive", "tempfile", + "wasm-bindgen", "wasm-bindgen-futures", "wayland-client", "wayland-cursor", "wayland-protocols", + "web-sys", "winapi", "x11-dl", ] -[[package]] -name = "mio" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys 0.48.0", -] - [[package]] name = "nix" version = "0.24.3" @@ -414,29 +358,6 @@ dependencies = [ "sdl2-sys", ] -[[package]] -name = "parking_lot" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.5.2", - "smallvec", - "windows-targets 0.52.5", -] - [[package]] name = "pin-project-lite" version = "0.2.14" @@ -488,15 +409,6 @@ 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 2.5.0", -] - [[package]] name = "redox_termios" version = "0.1.3" @@ -513,21 +425,21 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys", ] +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + [[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" @@ -571,6 +483,18 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.127" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + [[package]] name = "serde_spanned" version = "0.6.6" @@ -580,36 +504,6 @@ dependencies = [ "serde", ] -[[package]] -name = "signal-hook" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[package]] -name = "signal-hook-mio" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" -dependencies = [ - "libc", - "mio", - "signal-hook", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" -dependencies = [ - "libc", -] - [[package]] name = "slab" version = "0.4.9" @@ -645,7 +539,7 @@ dependencies = [ "cfg-if", "fastrand", "rustix", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -660,26 +554,6 @@ dependencies = [ "redox_termios", ] -[[package]] -name = "thiserror" -version = "1.0.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "toml" version = "0.8.14" @@ -726,12 +600,6 @@ 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" @@ -739,6 +607,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", + "serde", + "serde_json", "wasm-bindgen-macro", ] @@ -903,37 +773,13 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-targets", ] [[package]] @@ -942,46 +788,28 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.5" @@ -994,48 +822,24 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.5" diff --git a/Cargo.toml b/Cargo.toml index 9690b4d..06d2cee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,15 +3,29 @@ name = "georgeemu" version = "0.1.0" edition = "2021" +[features] +debug = [] +term = [] +web = ["minifb/web"] + +[[bin]] +path = "src/bin/main.rs" +name = "georgeemu" + + +[target.'cfg(target_arch = "wasm32")'.lib] +crate-type = ["cdylib", "rlib"] + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] anyhow = "1.0.81" -bdf = "0.6.0" -crossterm = "0.27.0" -minifb = "0.27.0" -# minifb = "0.25.0" -# ratatui = "0.26.3" +console_error_panic_hook = "0.1.7" +# minifb = { git = "https://github.com/augustkline/rust_minifb" } +minifb = { path = "/Users/august/projects/rust_minifb_fork/" } serde = { version = "1.0.197", features = ["serde_derive", "derive"] } + + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] termion = "4.0.2" -toml = "0.8.12" +toml = { version = "0.8.12" } diff --git a/george.toml b/george.toml index c9d237f..f6f9271 100644 --- a/george.toml +++ b/george.toml @@ -1,3 +1,3 @@ char_rom = "./src/roms/cozette.rom" -rom = "./src/roms/george.rom" +rom = "./src/roms/demo.rom" screen = "Window" diff --git a/run.sh b/run.sh index 56d858c..74b1cb2 100755 --- a/run.sh +++ b/run.sh @@ -8,5 +8,5 @@ fi set -e vasm6502_oldstyle ./src/roms/$1.asm -dotdir -wdc02 -ldots -Fbin -o ./src/roms/$1.rom; -cargo run -- rom "./src/roms/$1.rom"; +cargo run --features debug -- rom "./src/roms/$1.rom"; # hexdump -C ./cpu_dump.bin; diff --git a/src/cli.rs b/src/bin/cli/mod.rs similarity index 96% rename from src/cli.rs rename to src/bin/cli/mod.rs index 5d14f99..977823a 100644 --- a/src/cli.rs +++ b/src/bin/cli/mod.rs @@ -1,14 +1,13 @@ use std::{ - env, + default, env, fs::File, io::{ErrorKind, Read}, process::exit, }; +use georgeemu::{Config, ScreenType}; use serde::{Deserialize, Serialize}; -use crate::video::ScreenType; - #[derive(Serialize, Deserialize, Debug)] struct ConfigBuilder { rom: Option, @@ -27,7 +26,7 @@ impl ConfigBuilder { fn build(self) -> Config { let rom = match self.rom { - Some(rom) => rom, + Some(rom) => Some(rom), None => { println!("no rom was provided :("); exit(1); @@ -44,12 +43,6 @@ impl ConfigBuilder { } } -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 { @@ -101,6 +94,7 @@ pub fn get_input() -> Config { } Err(_) => { println!("couldn't find a `george.toml` in the current directory!"); + println!("\nuse `{executable} --help` to see all config options"); exit(1); } }; diff --git a/src/bin/main.rs b/src/bin/main.rs new file mode 100644 index 0000000..74df6ef --- /dev/null +++ b/src/bin/main.rs @@ -0,0 +1,33 @@ +mod cli; + +use cli::get_input; +use georgeemu::GeorgeEmu; +use minifb::{Scale, ScaleMode, Window, WindowOptions}; + +fn main() { + let mut window = Window::new( + "ʕ·ᴥ·ʔ-☆", + 512, + 380, + WindowOptions { + resize: true, + borderless: true, + title: true, + transparency: false, + scale: Scale::FitScreen, + scale_mode: ScaleMode::AspectRatioStretch, + topmost: false, + none: true, + }, + ) + .unwrap(); + let config = get_input(); + #[cfg(not(feature = "term"))] + let mut emu = GeorgeEmu::builder() + .window(&mut window) + .rom("/Users/august/projects/george-emu/src/roms/demo.rom") + .build(); + #[cfg(feature = "term")] + let mut emu = GeorgeEmu::builder().build(); + emu.run(); +} diff --git a/src/cpu.rs b/src/cpu.rs index 1f171fe..145a823 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -5,8 +5,6 @@ use std::sync::mpsc::{channel, Receiver, Sender}; use std::thread::sleep; use std::time::Duration; -use termion::cursor::Goto; - #[derive(Clone, Copy)] pub enum StatusFlag { Negative = 0b1000_0000, @@ -19,7 +17,7 @@ pub enum StatusFlag { Carry = 0b0000_0001, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct CpuController(Sender); pub enum CpuControl { @@ -51,6 +49,7 @@ impl CpuController { } } +#[derive(Debug)] pub struct CpuReceiver(Receiver); impl CpuReceiver { pub fn new(receiver: Receiver) -> Self { @@ -58,6 +57,7 @@ impl CpuReceiver { } } +#[derive(Debug)] pub struct Cpu { pub a: u8, // Accumulator Register pub x: u8, // X Register @@ -71,8 +71,7 @@ pub struct Cpu { pub pending_cycles: usize, receiver: Option, stopped: bool, - cycle: bool, // cycle_count: usize, - // state_tx: Sender, + cycle: bool, } impl Display for Cpu { @@ -94,7 +93,6 @@ impl MemoryWriter for Cpu { impl Cpu { pub fn new(memory: MemHandle) -> Self { - // pub fn new(memory: MemHandle) -> Self { Cpu { a: 0x00, x: 0x00, @@ -179,13 +177,6 @@ impl Cpu { value & 0b1000_0000 == 0b1000_0000 } - // pub fn execute(&mut self) { - // self.cycle(); - // while self.pending_cycles != 0 { - // self.cycle(); - // } - // } - pub fn wait_for_interrupt(&self) { unimplemented!() } @@ -198,6 +189,10 @@ impl Cpu { self.pc = self.read_word(0xFFFE); } + pub fn toggle_stopped(&mut self) { + self.stopped = !self.stopped; + } + fn receive_control(&mut self) { match &self.receiver { Some(receiver) => { @@ -213,19 +208,21 @@ impl Cpu { self.stopped = !self.stopped; } CpuControl::Cycle => self.cycle = true, - CpuControl::Data => {} // self - // .state_tx - // .send(CpuState { - // a: self.a.clone(), // Accumulator Register - // x: self.x.clone(), // X Register - // y: self.y.clone(), // Y Register - // pc: self.pc.clone(), // Program Counter - // s: self.s.clone(), // Stack Pointer - // p: self.p.clone(), // Status Register - // irq: self.irq.clone(), - // nmi: self.nmi.clone(), - // }) - // .unwrap(), + CpuControl::Data => { + // self + // .state_tx + // .send(CpuState { + // a: self.a.clone(), // Accumulator Register + // x: self.x.clone(), // X Register + // y: self.y.clone(), // Y Register + // pc: self.pc.clone(), // Program Counter + // s: self.s.clone(), // Stack Pointer + // p: self.p.clone(), // Status Register + // irq: self.irq.clone(), + // nmi: self.nmi.clone(), + // }) + // .unwrap(), + } } } None => {} @@ -233,64 +230,33 @@ impl Cpu { } pub fn cycle(&mut self) { - self.receive_control(); + // self.receive_control(); - if self.stopped & !self.cycle { - self.set_flag(StatusFlag::IrqDisable, true); - return; - } - self.cycle = false; + // if self.stopped & !self.cycle { + // self.set_flag(StatusFlag::IrqDisable, true); + // return; + // } + // self.cycle = false; + #[cfg(not(target_arch = "wasm32"))] while self.pending_cycles != 0 { // roughly cycle-accurate timing sleep(Duration::from_nanos(500)); self.pending_cycles -= 1; } - if !self.get_flag(StatusFlag::IrqDisable) && self.irq { - self.interrupt(); - } + // if !self.get_flag(StatusFlag::IrqDisable) && self.irq { + // self.interrupt(); + // } let opcode = self.read(self.pc); let instruction = get_instruction(opcode); instruction.call(self); - // println!("a: {a:#04x}, x: {x:#04x}, y: {y:#04x}, pc: {pc:#06x}, sp: {s:#04x}, sr: {p:#010b}, irq: {irq:?}, nmi: {nmi:?}", a = self.a, x = self.x, y = self.y, pc = self.pc, s = self.s, p = self.p, irq = self.irq, nmi = self.nmi); - // println!( - // "Instruction: {:?}, {:#04x}", - // valid_instruction.opcode, opcode - // ); - // println!(""); } pub fn stop(&mut self) { self.stopped = true; } + + #[cfg(feature = "debug")] pub fn breakpoint(&mut self) { - // println!("a: {a:#04x}, x: {x:#04x}, y: {y:#04x}, pc: {pc:#06x}, sp: {s:#04x}, sr: {p:#010b}, irq: {irq:?}, nmi: {nmi:?}", a = self.a, x = self.x, y = self.y, pc = self.pc, s = self.s, p = self.p, irq = self.irq, nmi = self.nmi); - // println!( - // "Instruction: {:?}, {:#04x}", - // valid_instruction.opcode, opcode - // ); - // println!(""); - // println!( - // "{}{:#04x}: {:#02x}, {:#04x}: {:#02x}", - // Goto(1, 35), - // 0x20, - // self.read(0x20), - // 0x21, - // self.read(0x21) - // ); - // println!( - // "{}str_ptr {:#04x}: {:#02x}, {:#04x}: {:#02x}", - // Goto(1, 36), - // 0x30, - // self.read(0x30), - // 0x31, - // self.read(0x31) - // ); - // println!( - // "{}cursor - x: {:#02x}, y: {:#02x}", - // Goto(1, 37), - // self.read(0x300), - // self.read(0x301) - // ); self.stop(); } } diff --git a/src/instructions.rs b/src/instructions.rs index 18a9ffd..edbde25 100644 --- a/src/instructions.rs +++ b/src/instructions.rs @@ -1,10 +1,7 @@ #![allow(clippy::upper_case_acronyms)] -use core::panic; -use std::process::exit; - -use termion::cursor::Goto; -use termion::{clear, color}; +#[cfg(feature = "debug")] +use termion::{clear, color, cursor::Goto}; use crate::cpu::{Cpu, StatusFlag}; use crate::memory::{MemoryReader, MemoryWriter}; @@ -23,47 +20,12 @@ pub struct Instruction<'a> { impl Instruction<'_> { pub fn call(&self, cpu: &mut Cpu) { - // TODO: add flag to print these - // print!("{}{}a: {a:#04x}, x: {x:#04x}, y: {y:#04x}, pc: {pc:#06x}, sp: {s:#04x}, sr: {p:#010b}, irq: {irq:?}, nmi: {nmi:?}", Goto(0, 31),clear::UntilNewline, a = cpu.a, x = cpu.x, y = cpu.y, pc = cpu.pc, s = cpu.s, p = cpu.p, irq = cpu.irq, nmi = cpu.nmi); - // print!( - // "{}instruction: {:?}, pc: {:#04x}", - // Goto(0, 31), - // self.name, - // cpu.pc - // ); - // print!( - // "{}{}instruction: {:?}, mode: {:?}", - // Goto(0, 32), - // clear::UntilNewline, - // self.name, - // self.addr_mode - // ); - // print!( - // "{}{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {}{}{:02x}{}{} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}", - // Goto(0, 33), - // cpu.read(cpu.pc.wrapping_sub(8)), - // cpu.read(cpu.pc.wrapping_sub(7)), - // cpu.read(cpu.pc.wrapping_sub(6)), - // cpu.read(cpu.pc.wrapping_sub(5)), - // cpu.read(cpu.pc.wrapping_sub(4)), - // cpu.read(cpu.pc.wrapping_sub(3)), - // cpu.read(cpu.pc.wrapping_sub(2)), - // cpu.read(cpu.pc.wrapping_sub(1)), - // color::Bg(color::Rgb(0xFF, 0xCC, 0x00)), - // color::Fg(color::Rgb(0x11, 0x05, 0x00)), - // cpu.read(cpu.pc), - // color::Fg(color::Rgb(0xFF, 0xCC, 0x00)), - // color::Bg(color::Rgb(0x11, 0x05, 0x00)), - // cpu.read(cpu.pc.wrapping_add(1)), - // cpu.read(cpu.pc.wrapping_add(2)), - // cpu.read(cpu.pc.wrapping_add(3)), - // cpu.read(cpu.pc.wrapping_add(4)), - // cpu.read(cpu.pc.wrapping_add(5)), - // cpu.read(cpu.pc.wrapping_add(6)), - // cpu.read(cpu.pc.wrapping_add(7)), - // cpu.read(cpu.pc.wrapping_add(8)), - // ); + #[cfg(feature = "debug")] + self.debug(cpu); + cpu.pc = cpu.pc.wrapping_add(1); // read instruction byte + + #[allow(clippy::single_match)] match self.instr_fn { // existence of instr_fn means this is a valid instruction Some(instr_fn) => { @@ -82,17 +44,75 @@ impl Instruction<'_> { } } None => { - panic!( - "An invalid instruction was called at {:04x}, with opcode {:02x}", - cpu.pc, - cpu.read(cpu.pc) - ) - } // idk if we should panic here, if we don't it's undefined behavior, which - // might actually be what we want in the cases where it's an undocumented - // 6502 instruction, but otherwise we should panic prolly, for now i'm just - // gonna panic + #[cfg(feature = "debug")] + { + println!( + "{}An invalid instruction was called at {:04x}, with opcode {:02x}", + Goto(0, 35), + cpu.pc, + cpu.read(cpu.pc) + ); + cpu.breakpoint(); + } + } } } + + #[cfg(feature = "debug")] + fn debug(&self, cpu: &Cpu) { + // cpu state + print!("{}{}a: {a:#04x}, x: {x:#04x}, y: {y:#04x}, pc: {pc:#06x}, sp: {s:#04x}, sr: {p:#010b}, irq: {irq:?}, nmi: {nmi:?}", Goto(0, 31),clear::UntilNewline, a = cpu.a, x = cpu.x, y = cpu.y, pc = cpu.pc, s = cpu.s, p = cpu.p, irq = cpu.irq, nmi = cpu.nmi); + print!( + "{}instruction: {:?}, pc: {:#04x}", + Goto(0, 31), + self.name, + cpu.pc + ); + + // current instruction and addressing mode + print!( + "{}{}instruction: {:?}, mode: {:?}", + Goto(0, 32), + clear::UntilNewline, + self.name, + self.addr_mode + ); + + // local memory + print!( + "{}{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {}{}{:02x}{}{} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}", + Goto(0, 33), + cpu.read(cpu.pc.wrapping_sub(8)), + cpu.read(cpu.pc.wrapping_sub(7)), + cpu.read(cpu.pc.wrapping_sub(6)), + cpu.read(cpu.pc.wrapping_sub(5)), + cpu.read(cpu.pc.wrapping_sub(4)), + cpu.read(cpu.pc.wrapping_sub(3)), + cpu.read(cpu.pc.wrapping_sub(2)), + cpu.read(cpu.pc.wrapping_sub(1)), + color::Bg(color::Rgb(0xFF, 0xCC, 0x00)), + color::Fg(color::Rgb(0x11, 0x05, 0x00)), + cpu.read(cpu.pc), + color::Fg(color::Rgb(0xFF, 0xCC, 0x00)), + color::Bg(color::Rgb(0x11, 0x05, 0x00)), + cpu.read(cpu.pc.wrapping_add(1)), + cpu.read(cpu.pc.wrapping_add(2)), + cpu.read(cpu.pc.wrapping_add(3)), + cpu.read(cpu.pc.wrapping_add(4)), + cpu.read(cpu.pc.wrapping_add(5)), + cpu.read(cpu.pc.wrapping_add(6)), + cpu.read(cpu.pc.wrapping_add(7)), + cpu.read(cpu.pc.wrapping_add(8)), + ); + // scratchpad + print!( + "{} {:02x} {:02x} {:02x}", + Goto(0, 34), + cpu.read(0x200), + cpu.read(0x201), + cpu.read(0x202) + ) + } } fn accumulator(cpu: &mut Cpu) -> u16 { @@ -209,7 +229,6 @@ fn zero_page_indirect_indexed_with_y( let address = cpu.read_word(zp as u16); let address: u16 = address.wrapping_add(cpu.y as u16); cpu.pc = cpu.pc.wrapping_add(1); - println!("{}indirect addr: {:#04x}", Goto(1, 39), address); address } @@ -255,7 +274,8 @@ fn branch(cpu: &mut Cpu, condition: bool, address: u16) { cpu.pc = cpu.pc.wrapping_add(1); } -fn brkpt(cpu: &mut Cpu, address: Option) { +#[cfg(feature = "debug")] +fn breakpoint(cpu: &mut Cpu, _address: Option) { cpu.breakpoint() } @@ -264,7 +284,7 @@ fn adc(cpu: &mut Cpu, address: Option) { let byte = cpu.read(address); let carry = cpu.get_flag(StatusFlag::Carry); let result = cpu.a as u16 + byte as u16 + carry as u16; - cpu.set_flag(StatusFlag::Carry, result > u16::from(u8::max_value())); + cpu.set_flag(StatusFlag::Carry, result > u16::from(u8::MAX)); cpu.set_flag(StatusFlag::Zero, result as u8 == 0); cpu.set_flag( StatusFlag::Overflow, @@ -786,7 +806,7 @@ fn sbc(cpu: &mut Cpu, address: Option) { let byte = cpu.read(address); let carry = cpu.get_flag(StatusFlag::Carry); let result = cpu.a as u16 + byte as u16 + !carry as u16; - cpu.set_flag(StatusFlag::Carry, result > u16::from(u8::max_value())); + cpu.set_flag(StatusFlag::Carry, result > u16::from(u8::MAX)); cpu.set_flag(StatusFlag::Zero, result as u8 == 0); cpu.set_flag( StatusFlag::Overflow, @@ -948,7 +968,7 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 7, name: "brk", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(ora), @@ -958,18 +978,24 @@ pub const OPCODES: [Instruction; 256] = [ addr_mode: "zero_page_indexed_indirect", }, Instruction { - instr_fn: Some(brkpt), + #[cfg(feature = "debug")] + instr_fn: Some(breakpoint), + #[cfg(not(feature = "debug"))] + instr_fn: None, address_fn: None, cycles: 0, - name: "brkpt", - addr_mode: "none", + #[cfg(not(feature = "debug"))] + name: "none", + #[cfg(feature = "debug")] + name: "breakpoint", + addr_mode: "implied", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(tsb), @@ -1004,7 +1030,7 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 3, name: "php", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(ora), @@ -1025,7 +1051,7 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(tsb), @@ -1081,7 +1107,7 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(trb), @@ -1116,7 +1142,7 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 2, name: "clc", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(ora), @@ -1137,7 +1163,7 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(trb), @@ -1186,14 +1212,14 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(bit), @@ -1228,7 +1254,7 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 4, name: "plp", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(and), @@ -1249,7 +1275,7 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(bit), @@ -1305,7 +1331,7 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(bit), @@ -1340,7 +1366,7 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 2, name: "sec", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(and), @@ -1361,7 +1387,7 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(bit), @@ -1396,7 +1422,7 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 6, name: "rti", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(eor), @@ -1410,21 +1436,21 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(eor), @@ -1452,7 +1478,7 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 3, name: "pha", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(eor), @@ -1473,7 +1499,7 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(jmp), @@ -1529,14 +1555,14 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(eor), @@ -1564,7 +1590,7 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 2, name: "cli", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(eor), @@ -1578,21 +1604,21 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 3, name: "phy", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(eor), @@ -1620,7 +1646,7 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 6, name: "rts", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(adc), @@ -1634,14 +1660,14 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(stz), @@ -1676,7 +1702,7 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 4, name: "pla", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(adc), @@ -1697,7 +1723,7 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(jmp), @@ -1753,7 +1779,7 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(stz), @@ -1788,7 +1814,7 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 2, name: "sei", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(adc), @@ -1802,14 +1828,14 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 4, name: "ply", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(jmp), @@ -1858,14 +1884,14 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(sty), @@ -1900,7 +1926,7 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 2, name: "dey", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(bit), @@ -1914,14 +1940,14 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 2, name: "txa", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(sty), @@ -1977,7 +2003,7 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(sty), @@ -2012,7 +2038,7 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 2, name: "tya", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(sta), @@ -2026,14 +2052,14 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 2, name: "txs", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(stz), @@ -2089,7 +2115,7 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(ldy), @@ -2124,7 +2150,7 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 2, name: "tay", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(lda), @@ -2138,14 +2164,14 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 2, name: "tax", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(ldy), @@ -2201,7 +2227,7 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(ldy), @@ -2236,7 +2262,7 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 2, name: "clv", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(lda), @@ -2250,14 +2276,14 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 2, name: "tsx", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(ldy), @@ -2306,14 +2332,14 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(cpy), @@ -2348,7 +2374,7 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 2, name: "iny", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(cmp), @@ -2362,14 +2388,14 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 2, name: "dex", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(wai), address_fn: None, cycles: 3, name: "wai", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(cpy), @@ -2425,14 +2451,14 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(cmp), @@ -2460,7 +2486,7 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 2, name: "cld", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(cmp), @@ -2474,21 +2500,21 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 3, name: "phx", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(stp), address_fn: None, cycles: 3, name: "stp", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(cmp), @@ -2530,14 +2556,14 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(cpx), @@ -2572,7 +2598,7 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 2, name: "inx", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(sbc), @@ -2586,14 +2612,14 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 2, name: "nop", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(cpx), @@ -2649,14 +2675,14 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(sbc), @@ -2684,7 +2710,7 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 2, name: "sed", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(sbc), @@ -2698,21 +2724,21 @@ pub const OPCODES: [Instruction; 256] = [ address_fn: None, cycles: 4, name: "plx", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", - addr_mode: "none", + addr_mode: "implied", }, Instruction { instr_fn: Some(sbc), diff --git a/src/keyboard.rs b/src/keyboard.rs index ba9fbd3..f17509b 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -1,9 +1,12 @@ use minifb::InputCallback; use minifb::Key as MKey; + +#[cfg(feature = "term")] use termion::event::Key; use crate::memory::{MemHandle, MemoryWriter}; +#[derive(Debug, Clone)] pub struct Keyboard { memory: MemHandle, } @@ -22,6 +25,7 @@ impl Keyboard { self.memory.write(0x4405, 0x00); } + #[cfg(feature = "term")] pub fn read_keys(&self, key: Key) { let mut row0 = 0; let mut row1 = 0; diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..c5ba450 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,271 @@ +pub mod cpu; +pub mod error; +pub mod instructions; +pub mod keyboard; +pub mod memory; +pub mod video; + +use crate::cpu::Cpu; +use crate::keyboard::Keyboard; +use crate::memory::Mem; +use crate::video::Renderer; + +use core::panic; +use cpu::CpuController; +use memory::MemHandle; +use minifb::Window; +use serde::{Deserialize, Serialize}; +use std::fmt::Debug; +use std::fs::File; +use std::io::Read; +use std::path::Path; +use std::thread::sleep; +use std::time::{Duration, Instant}; + +#[cfg(any(feature = "term", feature = "debug"))] +use std::{io::stdout, process::exit}; +#[cfg(any(feature = "term", feature = "debug"))] +use termion::{ + async_stdin, clear, + cursor::{self, Goto}, + event::Key, + input::TermRead, + raw::IntoRawMode, + AsyncReader, +}; + +#[derive(Serialize, Deserialize, Default, Debug)] +pub enum ScreenType { + Terminal, + #[default] + Window, +} + +pub struct Config { + pub rom: Option, + pub screen: ScreenType, + pub char_rom: Option, +} + +pub struct GeorgeEmuBuilder<'a> { + cpu: Cpu, + renderer: Option, + input: Option, + config: Option, + cpu_controller: CpuController, + memory_handle: MemHandle, + memory: Mem, + window: Option<&'a mut Window>, +} + +impl<'a> GeorgeEmuBuilder<'a> { + pub fn new() -> Self { + let mut memory = Mem::new(); + // TODO: once the memory goes to the handle you can't load a rom, + // so the method that loads a rom needs to have the handle uninitialized first, + // so make everything here be None and set everything at the end + memory.load_bytes(include_bytes!( + "/Users/august/projects/george-emu/src/roms/demo.rom" + )); + let memory_handle = MemHandle::new(memory); + let cpu_memory = memory_handle.clone(); + let (cpu, cpu_controller) = Cpu::new_with_control(cpu_memory); + let input = None; + let config = None; + let renderer = None; + let window = None; + GeorgeEmuBuilder { + cpu, + cpu_controller, + memory, + memory_handle, + config, + renderer, + window, + input, + } + } + + pub fn config(mut self, config: Config) -> Self { + self.config = Some(config); + self + } + + /// Adds a keyboard input to the emulator + pub fn keyboard(mut self) -> Self { + let input_memory = self.memory_handle.clone(); + let keyboard = Keyboard::new(input_memory); + self.input = Some(keyboard); + self + } + + /// Adds a minifb window to the emulator + #[cfg(not(feature = "term"))] + pub fn window(mut self, window: &'a mut Window) -> Self { + let renderer = Renderer::new(self.cpu_controller.clone(), self.memory_handle.clone()); + self.renderer = Some(renderer); + self.window = Some(window); + self + } + + pub fn rom

(mut self, path: P) -> Self + where + P: AsRef + Debug + Copy, + { + let rom = match File::open(path) { + Ok(file) => { + file + // let mut bin = vec![0; 0x8000]; + // file.read_exact(&mut bin).unwrap(); + // // println!("reading char rom"); + // &bin + } + Err(error) => panic!("Couldn't read file at {path:?}: {error:?}"), + }; + self.memory.read_from_bin(rom).unwrap(); + self + } + + pub fn build(mut self) -> GeorgeEmu<'a> { + #[cfg(not(any(feature = "term", feature = "web")))] + { + match self.window { + Some(ref mut window) => { + if let Some(ref input) = self.input { + window.set_input_callback(Box::new(input.clone())) + } + } + None => panic!( + "Tried to call `GeorgeEmuBuilder::build()` before `GeorgeEmuBuilder::window()`!" + ), + } + GeorgeEmu::new(self.cpu, self.renderer.unwrap(), self.input, self.window) + } + #[cfg(feature = "term")] + { + let renderer = Renderer::new(self.cpu_controller.clone(), self.memory_handle.clone()); + GeorgeEmu::new(self.cpu, renderer, self.input, self.window) + } + #[cfg(feature = "web")] + { + let renderer = Renderer::new(self.cpu_controller.clone(), self.memory_handle.clone()); + GeorgeEmu::new(self.cpu, renderer, self.input, self.window) + } + } +} + +#[derive(Debug)] +pub struct GeorgeEmu<'a> { + pub cpu: Cpu, + pub renderer: Renderer, + // in the future these might be set with feature flags, + // but this is simpler + input: Option, + pub window: Option<&'a mut Window>, +} + +impl<'a> GeorgeEmu<'a> { + pub fn builder() -> GeorgeEmuBuilder<'a> { + GeorgeEmuBuilder::new() + } + + fn new( + mut cpu: Cpu, + renderer: Renderer, + input: Option, + window: Option<&'a mut Window>, + ) -> Self { + cpu.reset(); + Self { + cpu, + input, + renderer, + window, + } + } + + pub fn run(&mut self) { + #[cfg(any(feature = "term", feature = "debug"))] + { + let _ = stdout().into_raw_mode(); + print!("{}{}", cursor::Hide, clear::All,); + } + #[cfg(any(feature = "term", feature = "debug"))] + let mut stdin = async_stdin(); + let clock_interval = Duration::from_nanos(500); + let frame_interval = Duration::from_millis(16); + let mut next_clock = Instant::now() + clock_interval; + let mut next_frame = Instant::now() + frame_interval; + + loop { + let now = Instant::now(); + + if now >= next_clock { + self.cpu.cycle(); + next_clock += clock_interval; + } + + if now >= next_frame { + #[cfg(any(feature = "term", feature = "debug"))] + self.handle_input(&mut stdin); + #[cfg(not(target_arch = "wasm32"))] + self.draw(); + next_frame += frame_interval; + } + + let next_run = std::cmp::min(next_clock, next_frame); + let sleep_dur = next_run - now; + sleep(sleep_dur) + } + } + + #[cfg(not(target_arch = "wasm32"))] + fn draw(&mut self) { + #[cfg(feature = "term")] + self.renderer.render(); + #[cfg(not(feature = "term"))] + match self.window { + Some(ref mut window) => self.renderer.render(window), + None => todo!(), + } + } + #[cfg(target_arch = "wasm32")] + pub fn draw(&self, window: &mut Window) { + #[cfg(feature = "term")] + self.renderer.render(); + #[cfg(not(feature = "term"))] + self.renderer.render(window); + } + + #[cfg(any(feature = "term", feature = "debug"))] + fn handle_input(&mut self, stdin: &mut AsyncReader) { + match &self.input { + Some(input) => { + let mut stdin = stdin.keys(); + if let Some(Ok(key)) = stdin.next() { + match key { + Key::Char('q') => { + print!("{}{}{}", clear::All, cursor::Show, Goto(1, 1)); + exit(0); + } + Key::Char('`') => self.cpu.toggle_stopped(), + Key::Char('\n') => self.cpu.cycle(), + Key::Char('i') => self.cpu.interrupt(), + _ => { + #[cfg(feature = "term")] + input.read_keys(key); + } + } + } else { + #[cfg(feature = "term")] + input.clear_keys(); + } + } + None => {} + } + } + + pub fn cycle(&mut self) { + self.cpu.cycle() + } +} diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index 1bb2206..0000000 --- a/src/main.rs +++ /dev/null @@ -1,74 +0,0 @@ -mod cli; -mod cpu; -mod error; -mod instructions; -mod keyboard; -mod memory; -mod types; -mod video; - -use crate::cpu::Cpu; -use crate::keyboard::Keyboard; -use crate::memory::Mem; -use crate::video::Screen; - -use cli::get_input; -use crossterm::cursor; -use memory::MemHandle; -use std::io::stdout; -use std::thread::{self, sleep}; -use std::time::Duration; -use termion::cursor::Goto; -use termion::event::Key; -use termion::input::TermRead; -use termion::raw::IntoRawMode; -use termion::{async_stdin, clear}; - -fn main() { - let _stdout = stdout().into_raw_mode(); - let config = get_input(); - - let mut memory = Mem::new(); - let _ = memory.load_rom(&config.rom); - - let shared_memory = MemHandle::new(memory); - let screen_memory = shared_memory.clone(); - let cpu_memory = shared_memory.clone(); - let keyboard_memory = shared_memory.clone(); - - let keyboard = Keyboard::new(keyboard_memory); - - let (mut cpu, cpu_controller) = Cpu::new_with_control(cpu_memory); - cpu.reset(); - - thread::spawn(move || loop { - cpu.cycle(); - }); - - let screen_remote = cpu_controller.clone(); - - let mut screen = Screen::new(&config, screen_remote, screen_memory); - - let mut stdin = async_stdin().keys(); - print!("{}{}", cursor::Hide, clear::All,); - - loop { - screen.draw(); - if let Some(Ok(key)) = stdin.next() { - keyboard.read_keys(key); - match key { - Key::Char('q') => { - break; - } - Key::Char('`') => cpu_controller.toggle(), - Key::Char('\n') => cpu_controller.cycle(), - Key::Char('i') => cpu_controller.irq(), - _ => {} - } - } else { - keyboard.clear_keys(); - } - sleep(Duration::from_millis(16)); - } - print!("{}{}{}", clear::All, cursor::Show, Goto(1, 1)); -} diff --git a/src/memory.rs b/src/memory.rs index 58df25b..68314c3 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,10 +1,7 @@ use anyhow::{bail, Result}; -use core::panic; -use std::cell::RefCell; use std::io::{self, Write}; +use std::panic; use std::path::{Path, PathBuf}; -use std::process::exit; -use std::rc::Rc; use std::str::FromStr; use std::sync::{Arc, Mutex}; use std::{fs::File, io::Read}; @@ -16,11 +13,11 @@ impl MemHandle { Self(Arc::new(Mutex::new(memory))) } pub fn read(&self, address: u16) -> u8 { - let memory = self.0.lock().unwrap(); + let memory = self.0.try_lock().unwrap(); memory.read(address) } pub fn write(&self, address: u16, data: u8) { - let mut memory = self.0.lock().unwrap(); + let mut memory = self.0.try_lock().unwrap(); memory.write(address, data); } @@ -49,15 +46,25 @@ pub trait MemoryWriter { // 0x10000 elements pub struct Mem([u8; 0x10000]); -impl Default for Mem { - fn default() -> Self { - Self([0; 0x10000]) - } -} +// impl Default for Mem { +// fn default() -> Self { +// let bytes = include_bytes!("/Users/august/projects/george-emu/src/roms/george.rom"); +// let padding = [0; 0x8000]; +// let rom: [u8; 0x10000] = { +// let mut rom: [u8; 0x10000] = [0; 0x10000]; +// let (one, two) = rom.split_at_mut(padding.len()); +// one.copy_from_slice(&padding); +// two.copy_from_slice(bytes); +// rom +// }; + +// Self(rom) +// } +// } impl Mem { pub fn new() -> Self { - Mem::default() + Self([0; 0x10000]) } pub fn dump(&self, path: PathBuf) -> io::Result<()> { let mut outfile = File::create(path)?; @@ -97,16 +104,22 @@ impl Mem { pub fn poke(&self, address: u16) { println!("{:02x}", self.read(address)); } - //pub fn read_from_bin(&mut self, f: File) -> Result<()> { - // let bytes = f.bytes(); - // for (address, byte) in bytes.enumerate() { - // match byte { - // Ok(value) => self.write(address as u16, value), - // Err(_) => { - // bail!("couldn't write byte {:#04x}", address); - // } - // } - // } - // Ok(()) - //} + pub fn load_bytes(&mut self, bytes: &[u8]) { + for (address, byte) in bytes.into_iter().enumerate() { + self.write(address as u16 + 0x8000, *byte) + } + } + + pub fn read_from_bin(&mut self, f: File) -> Result<()> { + let bytes = f.bytes(); + for (address, byte) in bytes.enumerate() { + match byte { + Ok(value) => self.write(address as u16 + 0x8000, value), + Err(_) => { + bail!("couldn't write byte {:#04x}", address); + } + } + } + Ok(()) + } } diff --git a/src/roms/demo.asm b/src/roms/demo.asm new file mode 100644 index 0000000..dcd9933 --- /dev/null +++ b/src/roms/demo.asm @@ -0,0 +1,123 @@ +; .setcpu "65C02" + .include "./macro.inc" + +; okay so rn i wanna set up a very basic system init, and write a few subroutines to draw characters at x,y coordinates +n = $01 ; temporary storage for data stack operations + +key_row = $200 ; used for character lookup when key pressed +key_col = $201 +cursor = $202 + +char_buffer = $300 ; 256 byte character buffer + +kb_row = $4400 ; keyboard hardware register +kb_row_cache = $203 ; cache + + .org $8000 + +reset: + sei + ldx #0; initialize data stack pointer + +; initdisplay: +; lda #0 +; ldy #0 + +; cleardisplay: +; sta $6000,y +; sta $6100,y +; sta $6200,y +; sta $6300,y +; sta $6400,y +; sta $6500,y +; sta $6600,y +; sta $6700,y ; this goes slightly over but it's fine +; iny +; bne cleardisplay +; cli + +main: + jsr draw + jmp main + +draw: +char = $200 +x = $201 +y = $202 + .y_loop: + .x_loop: + inc char + lda x + push + sta 0, x + stz 1, x + lda y + push + sta 0, x + sta 1, x + lda char + push + sta 0, x + sta 1, x + jsr draw_char + inc x + lda #64 + cmp x + bne .x_loop + inc y + lda #30 + cmp y + bne .y_loop + .end: + rts + +draw_char: ; draw a character c at (x, y) (n1: x n2: y n3: c -- ) + lda 0, x ; load a with character to draw + pop ; and pop it off the stack + jsr get_char_address ; calculate where to put the character in memory + sta (0, x) ; store a at the address pointed to on the stack + rts + +get_char_address: ; gets vram address for a character at (x, y), + ; (n1: x n2: y -- n: $6000 + x + (64 * y)) + ;jsr push_lit ; push 64 onto stack, low byte first + ;.byte 64 + ;.byte 0 + pha + lda #64 + push ; doing this instead until `push_lit` is fixed + sta 0, x + stz 1, x + jsr mult ; multiply 64 with y (n2) + jsr plus ; add result with x (n1) + + ;jsr push_lit ; push vram address onto the stack + ;.byte $00 + ;.byte $60 + lda #$60 + push + sta 1, x + stz 0, x + jsr plus ; add vram start address to result + + pla + rts + +fill: ; fills an area from (x1, y1) to (x2, y2) will character c, (n1: c n2: x1 n3: y1 n4: x2 n5: y2 -- ) + jsr get_char_address + +isr: ; interrupt service routine + pha + phx + phy + ; jsr keyboard + ply + plx + pla + rti + + .include "math.inc" + + .org $fffc + .word reset + .word isr diff --git a/src/roms/demo.rom b/src/roms/demo.rom new file mode 100644 index 0000000000000000000000000000000000000000..8a65037f25b5d7e2e4e9c46816f6b1e96702209a GIT binary patch literal 32768 zcmeIup-#h47zN;aZxum9rbwRx%j8JVh^Y$19A+hChKra-&;&_HmQXR-ml#EUp+igj4Vr#ikKhtt@lqhj~DicNYPii;{Z0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ a009C72oNAZfB*pk1PBly@J|AHmcIembwF(char_rom: Option

) -> [u8; 0x8000] -where - P: AsRef, -{ - match char_rom { - Some(path) => { - let mut file = File::open(path).unwrap(); - let mut bin = vec![0; 0x8000]; - file.read_exact(&mut bin).unwrap(); - // println!("reading char rom"); - bin.try_into().unwrap() - } - None => *include_bytes!("./roms/cozette.rom"), - } -} +// pub fn get_char_bin

(char_rom: Option

) -> [u8; 0x8000] +// where +// P: AsRef, +// { +// match char_rom { +// Some(path) => { +// let mut file = File::open(path).unwrap(); +// let mut bin = vec![0; 0x8000]; +// file.read_exact(&mut bin).unwrap(); +// // println!("reading char rom"); +// bin.try_into().unwrap() +// } +// None => *include_bytes!("./roms/cozette.rom"), +// } +// } -trait Renderer { - fn render(&mut self) {} -} - -#[derive(Serialize, Deserialize, Debug, Default)] -pub enum ScreenType { - Window, - #[default] - Terminal, -} - -pub struct WindowRenderer { +#[cfg(not(feature = "term"))] +#[derive(Debug)] +pub struct Renderer { char_rom: [u8; 0x8000], - window: Window, memory: MemHandle, + controller: CpuController, } -impl WindowRenderer { - pub fn new

(memory: MemHandle, char_rom: Option

, window: Window) -> Self - where - P: AsRef, - { - let char_rom = get_char_bin(char_rom); +#[cfg(not(feature = "term"))] +impl Renderer { + // pub fn new

(controller: CpuController, memory: MemHandle, char_rom: Option

) -> Self + // where + // P: AsRef, + pub fn new(controller: CpuController, memory: MemHandle) -> Self { + // let char_rom = get_char_bin(char_rom); + let char_rom = *include_bytes!("./roms/cozette.rom"); Self { memory, - window, char_rom, + controller, } } -} -impl Renderer for WindowRenderer { - fn render(&mut self) { + pub fn render(&self, window: &mut Window) { // the rest of this function is arcane wizardry // based on the specifics of george's weird // display and characters... don't fuck around w it @@ -99,34 +86,24 @@ impl Renderer for WindowRenderer { } } } - self.window - .update_with_buffer(&buffer, WIDTH, HEIGHT) - .unwrap(); + self.controller.irq(); + window.update_with_buffer(&buffer, WIDTH, HEIGHT).unwrap(); } } -impl MemoryReader for WindowRenderer { - fn read(&self, address: Word) -> Byte { +impl MemoryReader for Renderer { + fn read(&self, address: u16) -> u8 { self.memory.read(address) } } -pub struct TerminalRenderer { +#[cfg(feature = "term")] +pub struct Renderer { memory: MemHandle, + controller: CpuController, } -impl TerminalRenderer { - pub fn new(memory: MemHandle) -> Self { - Self { memory } - } -} - -impl MemoryReader for TerminalRenderer { - fn read(&self, address: Word) -> Byte { - self.memory.read(address) - } -} - +#[cfg(feature = "term")] const ASCII_LOOPUP: [&str; 256] = [ " ", "░", "▒", "▓", "♡", "♥", "⭐", "✭", "", "✦", "✨", "♀", "♂", "⚢", "⚣", "⚥", "♩", "♪", "♫", "♬", "ﱝ", "", "", "", "奄", "奔", "婢", "ﱜ", "ﱛ", "", "", "", " ", "!", "\"", "#", @@ -144,8 +121,12 @@ const ASCII_LOOPUP: [&str; 256] = [ "", "", "", "", "🎁", "", "", "", "", "⚐", "⚑", "", "", "", ]; -impl Renderer for TerminalRenderer { - fn render(&mut self) { +#[cfg(feature = "term")] +impl Renderer { + pub fn new(controller: CpuController, memory: MemHandle) -> Self { + Self { controller, memory } + } + pub fn render(&self) { let mut stdout = io::stdout(); let mut i = 0; for char_row in 0..29 { @@ -164,54 +145,6 @@ impl Renderer for TerminalRenderer { ); } } - } -} - -pub struct Screen { - renderer: Box, - controller: CpuController, -} - -impl Screen { - pub fn new(config: &Config, controller: CpuController, memory: MemHandle) -> Self { - let renderer: Box = match config.screen { - ScreenType::Window => { - let mut window = Window::new( - "ʕ·ᴥ·ʔ-☆", - 512, - 380, - WindowOptions { - resize: true, - borderless: true, - title: true, - transparency: false, - scale: Scale::FitScreen, - scale_mode: ScaleMode::AspectRatioStretch, - topmost: false, - none: true, - }, - ) - .unwrap(); - window.set_input_callback(Box::new(Keyboard::new(memory.clone()))); - Box::new(WindowRenderer::new(memory, config.char_rom.clone(), window)) - } - ScreenType::Terminal => Box::new(TerminalRenderer::new(memory)), - }; - - Self { - renderer, - controller, - } - } - pub fn draw(&mut self) { self.controller.irq(); - self.renderer.render(); } - - // pub fn run(&mut self) { - // loop { - // // sleep(Duration::from_millis(16)); - // self.draw(); - // } - // } }