From b1bf6c5868d17b3647b936773b2aa24505cccf87 Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Wed, 2 Jun 2021 01:50:16 -0500 Subject: [PATCH] feat: integrate eui and pixels-rs for debug info --- Cargo.lock | 350 ++++++++++++++++++++++++++++---------------- Cargo.toml | 11 +- src/cpu.rs | 47 +++--- src/gui.rs | 356 +++++++++++++++++++++++++++++++++++++++++++++ src/instruction.rs | 98 ++++++++++++- src/lib.rs | 5 + src/main.rs | 105 ++++++++++--- 7 files changed, 790 insertions(+), 182 deletions(-) create mode 100644 src/gui.rs diff --git a/Cargo.lock b/Cargo.lock index bc0f76d..fef3023 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,6 +12,17 @@ version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e" +[[package]] +name = "ahash" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43bb833f0bf979d8475d38fbf09ed3b8a55e1885fe93ad3f93239fc6a4f17b98" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + [[package]] name = "andrew" version = "0.3.1" @@ -31,7 +42,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" dependencies = [ - "winapi", + "winapi 0.3.9", ] [[package]] @@ -55,6 +66,12 @@ dependencies = [ "libloading 0.6.7", ] +[[package]] +name = "atomic_refcell" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "681b971236e0f76b20fcafca0236b8718c9186ee778d67cd78bd5f28fd85427f" + [[package]] name = "atty" version = "0.2.14" @@ -63,7 +80,7 @@ checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ "hermit-abi", "libc", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -316,74 +333,6 @@ dependencies = [ "objc", ] -[[package]] -name = "crossbeam" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae5588f6b3c3cb05239e90bd110f257254aecd01e4635400391aeae07497845" -dependencies = [ - "cfg-if 1.0.0", - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-epoch", - "crossbeam-queue", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" -dependencies = [ - "cfg-if 1.0.0", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9" -dependencies = [ - "cfg-if 1.0.0", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" -dependencies = [ - "cfg-if 1.0.0", - "crossbeam-utils", - "lazy_static", - "memoffset", - "scopeguard", -] - -[[package]] -name = "crossbeam-queue" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b10ddc024425c88c2ad148c1b0fd53f4c6d38db9697c9f1588381212fa657c9" -dependencies = [ - "cfg-if 1.0.0", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" -dependencies = [ - "cfg-if 1.0.0", - "lazy_static", -] - [[package]] name = "d3d12" version = "0.3.2" @@ -392,7 +341,7 @@ checksum = "d0a60cceb22c7c53035f8980524fdc7f17cf49681a3c154e6757d30afbec6ec4" dependencies = [ "bitflags", "libloading 0.6.7", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -477,6 +426,61 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" +[[package]] +name = "egui" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04fa506a0f70b14bc9d04b59b862e621521728df5beae4403ce69a033658f78f" +dependencies = [ + "epaint", +] + +[[package]] +name = "egui_wgpu_backend" +version = "0.5.0" +source = "git+https://github.com/hasenbanck/egui_wgpu_backend.git?rev=9d03ad345d15d1e44165849b242d3562fdf3e859#9d03ad345d15d1e44165849b242d3562fdf3e859" +dependencies = [ + "bytemuck", + "epi", + "wgpu", +] + +[[package]] +name = "egui_winit_platform" +version = "0.5.0" +source = "git+https://github.com/hasenbanck/egui_winit_platform.git?rev=17298250e9721e8bf2c1d4a17b3e22777f8cb2e8#17298250e9721e8bf2c1d4a17b3e22777f8cb2e8" +dependencies = [ + "egui", + "winit", +] + +[[package]] +name = "emath" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "632c6ab1d569238921dc1e590640862ffb09226b0483f3b60950a3ec34f84b48" + +[[package]] +name = "epaint" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29cd86d676d3f0d6d348e66bb4ee7ccdff021d981fa2a4a9649416658a272ba" +dependencies = [ + "ahash 0.7.4", + "atomic_refcell", + "emath", + "rusttype", +] + +[[package]] +name = "epi" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3092dca4ab12b5f1ce942440526ae797d996bedc503b70f1ec484f8da408059e" +dependencies = [ + "egui", +] + [[package]] name = "fixedbitset" version = "0.2.0" @@ -504,6 +508,22 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +dependencies = [ + "bitflags", + "fuchsia-zircon-sys", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" + [[package]] name = "fxhash" version = "0.2.1" @@ -520,12 +540,26 @@ dependencies = [ "anyhow", "bitfield", "clap", + "egui", + "egui_wgpu_backend", + "egui_winit_platform", "gilrs", "pixels", "winit", "winit_input_helper", ] +[[package]] +name = "getrandom" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi", +] + [[package]] name = "gfx-auxil" version = "0.8.0" @@ -555,7 +589,7 @@ dependencies = [ "smallvec", "spirv_cross", "thunderdome", - "winapi", + "winapi 0.3.9", "wio", ] @@ -578,7 +612,7 @@ dependencies = [ "smallvec", "spirv_cross", "thunderdome", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -658,7 +692,7 @@ dependencies = [ "parking_lot", "raw-window-handle", "smallvec", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -702,7 +736,7 @@ dependencies = [ "stdweb", "uuid", "vec_map", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -764,7 +798,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" dependencies = [ - "ahash", + "ahash 0.4.7", ] [[package]] @@ -817,6 +851,15 @@ dependencies = [ "mach", ] +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + [[package]] name = "itoa" version = "0.4.7" @@ -847,6 +890,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + [[package]] name = "khronos-egl" version = "3.0.2" @@ -863,6 +916,12 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" version = "0.2.95" @@ -876,7 +935,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "351a32417a12d5f7e82c368a66781e307834dae04c6ce0cd4456d52989229883" dependencies = [ "cfg-if 1.0.0", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -886,7 +945,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f84d96438c15fcd6c3f244c8fce01d1e2b9c6b5623e9c711dc9286d8fc92d6a" dependencies = [ "cfg-if 1.0.0", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -956,15 +1015,6 @@ dependencies = [ "libc", ] -[[package]] -name = "memoffset" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" -dependencies = [ - "autocfg", -] - [[package]] name = "metal" version = "0.21.0" @@ -981,36 +1031,45 @@ dependencies = [ [[package]] name = "mio" -version = "0.7.11" +version = "0.6.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf80d3e903b34e0bd7282b218398aec54e082c840d9baf8339e0080a0c542956" +checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" dependencies = [ + "cfg-if 0.1.10", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec", + "kernel32-sys", "libc", "log", "miow", - "ntapi", - "winapi", + "net2", + "slab", + "winapi 0.2.8", ] [[package]] -name = "mio-misc" -version = "1.0.1" +name = "mio-extras" +version = "2.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d9bf3ad929ad9ec136d7bd55f68c62223561423474c21b5e34eb9322018d36" +checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19" dependencies = [ - "crossbeam", - "crossbeam-queue", + "lazycell", "log", "mio", + "slab", ] [[package]] name = "miow" -version = "0.3.7" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" dependencies = [ - "winapi", + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", ] [[package]] @@ -1031,9 +1090,9 @@ dependencies = [ [[package]] name = "ndk" -version = "0.3.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8794322172319b972f528bf90c6b467be0079f1fa82780ffb431088e741a73ab" +checksum = "5eb167c1febed0a496639034d0c76b3b74263636045db5489eee52143c246e73" dependencies = [ "jni-sys", "ndk-sys", @@ -1043,9 +1102,9 @@ dependencies = [ [[package]] name = "ndk-glue" -version = "0.3.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5caf0c24d51ac1c905c27d4eda4fa0635bbe0de596b8f79235e0b17a4d29385" +checksum = "bdf399b8b7a39c6fb153c4ec32c72fd5fe789df24a647f229c239aa7adb15241" dependencies = [ "lazy_static", "libc", @@ -1074,6 +1133,17 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c44922cb3dbb1c70b5e5f443d63b64363a898564d739ba5198e3a9138442868d" +[[package]] +name = "net2" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "winapi 0.3.9", +] + [[package]] name = "nix" version = "0.18.0" @@ -1108,15 +1178,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "ntapi" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" -dependencies = [ - "winapi", -] - [[package]] name = "num-traits" version = "0.2.14" @@ -1128,9 +1189,9 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.5.1" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226b45a5c2ac4dd696ed30fa6b94b057ad909c7b7fc2e0d0808192bced894066" +checksum = "ca565a7df06f3d4b485494f25ba05da1435950f4dc263440eda7a6fa9b8e36e4" dependencies = [ "derivative", "num_enum_derive", @@ -1138,9 +1199,9 @@ dependencies = [ [[package]] name = "num_enum_derive" -version = "0.5.1" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c0fd9eba1d5db0994a239e09c1be402d35622277e35468ba891aa5e3188ce7e" +checksum = "ffa5a33ddddfee04c0283a7653987d634e880347e96b5b2ed64de07efb59db9d" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -1204,7 +1265,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -1332,7 +1393,7 @@ checksum = "d2aa654bc32eb9ca14cce1a084abc9dfe43949a4547c35269a094c39272db3bb" dependencies = [ "lazy_static", "log", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -1420,6 +1481,12 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" +[[package]] +name = "slab" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527" + [[package]] name = "slotmap" version = "0.4.0" @@ -1683,10 +1750,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" dependencies = [ "same-file", - "winapi", + "winapi 0.3.9", "winapi-util", ] +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + [[package]] name = "wasm-bindgen" version = "0.2.69" @@ -1905,6 +1978,12 @@ dependencies = [ "safe_arch", ] +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + [[package]] name = "winapi" version = "0.3.9" @@ -1915,6 +1994,12 @@ dependencies = [ "winapi-x86_64-pc-windows-gnu", ] +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" @@ -1927,7 +2012,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ - "winapi", + "winapi 0.3.9", ] [[package]] @@ -1938,9 +2023,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "winit" -version = "0.25.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79610794594d5e86be473ef7763f604f2159cbac8c94debd00df8fb41e86c2f8" +checksum = "da4eda6fce0eb84bd0a33e3c8794eb902e1033d0a1d5a31bc4f19b1b4bbff597" dependencies = [ "bitflags", "cocoa", @@ -1953,7 +2038,7 @@ dependencies = [ "libc", "log", "mio", - "mio-misc", + "mio-extras", "ndk", "ndk-glue", "ndk-sys", @@ -1961,18 +2046,17 @@ dependencies = [ "parking_lot", "percent-encoding", "raw-window-handle", - "scopeguard", "smithay-client-toolkit", "wayland-client", - "winapi", + "winapi 0.3.9", "x11-dl", ] [[package]] name = "winit_input_helper" -version = "0.10.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d8af691f04e6d8a892e80a2176221b2c13d5832a8929d8c0fed1e3e3d4fe831" +checksum = "25f90925358446f7b9e52095b01513b8ed9243a2564f140d76bce2243a8fc210" dependencies = [ "winit", ] @@ -1983,7 +2067,17 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" dependencies = [ - "winapi", + "winapi 0.3.9", +] + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +dependencies = [ + "winapi 0.2.8", + "winapi-build", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 8310184..8927923 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,14 +6,21 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +# default = ["debug"] +debug = [] + [dependencies] anyhow = "^1.0" bitfield = "^0.13" clap = "^2.33" gilrs = "^0.8" pixels = "^0.3" -winit = "^0.25" -winit_input_helper = "^0.10" +winit = "^0.24" +winit_input_helper = "^0.9" +egui = "^0.10" +egui_wgpu_backend = { git = "https://github.com/hasenbanck/egui_wgpu_backend.git", rev = "9d03ad345d15d1e44165849b242d3562fdf3e859" } +egui_winit_platform = { git = "https://github.com/hasenbanck/egui_winit_platform.git", rev = "17298250e9721e8bf2c1d4a17b3e22777f8cb2e8" } [profile.release] debug = true diff --git a/src/cpu.rs b/src/cpu.rs index 9d488e8..4a628ff 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -11,6 +11,7 @@ pub struct Cpu { reg: Registers, flags: Flags, ime: ImeState, + // TODO: Merge halted and state properties halted: Option, state: State, } @@ -89,7 +90,10 @@ impl Cpu { pub fn step(&mut self) -> Cycle { // if !self.bus.boot_enabled() { - // self.log_state().unwrap(); + // let out = std::io::stdout(); + // let handle = out.lock(); + + // self.log_state(handle).unwrap(); // } let cycles = match self.halted() { @@ -123,13 +127,13 @@ impl Cpu { impl Cpu { pub fn read_imm_byte(&mut self, addr: u16) -> u8 { - self.inc_pc(); + self.inc_pc(); // NB: the addr read in the line below will be equal to PC - 1 after this function call self.bus.read_byte(addr) } pub fn read_imm_word(&mut self, addr: u16) -> u16 { self.inc_pc(); - self.inc_pc(); + self.inc_pc(); // NB: the addr read in the line below will be equal to PC - 2 after this function call self.bus.read_word(addr) } @@ -335,27 +339,22 @@ impl Cpu { } impl Cpu { - fn log_state(&self) -> std::io::Result<()> { - use std::io::Write; - - let out = std::io::stdout(); - let mut handle = out.lock(); - - write!(handle, "A: {:02X} ", self.reg.a)?; - write!(handle, "F: {:02X} ", u8::from(self.flags))?; - write!(handle, "B: {:02X} ", self.reg.b)?; - write!(handle, "C: {:02X} ", self.reg.c)?; - write!(handle, "D: {:02X} ", self.reg.d)?; - write!(handle, "E: {:02X} ", self.reg.e)?; - write!(handle, "H: {:02X} ", self.reg.h)?; - write!(handle, "L: {:02X} ", self.reg.l)?; - write!(handle, "SP: {:04X} ", self.reg.sp)?; - write!(handle, "PC: 00:{:04X} ", self.reg.pc)?; - write!(handle, "({:02X} ", self.read_byte(self.reg.pc))?; - write!(handle, "{:02X} ", self.read_byte(self.reg.pc + 1))?; - write!(handle, "{:02X} ", self.read_byte(self.reg.pc + 2))?; - writeln!(handle, "{:02X})", self.read_byte(self.reg.pc + 3))?; - handle.flush()?; + pub fn log_state(&self, mut writer: impl std::io::Write) -> std::io::Result<()> { + write!(writer, "A: {:02X} ", self.reg.a)?; + write!(writer, "F: {:02X} ", u8::from(self.flags))?; + write!(writer, "B: {:02X} ", self.reg.b)?; + write!(writer, "C: {:02X} ", self.reg.c)?; + write!(writer, "D: {:02X} ", self.reg.d)?; + write!(writer, "E: {:02X} ", self.reg.e)?; + write!(writer, "H: {:02X} ", self.reg.h)?; + write!(writer, "L: {:02X} ", self.reg.l)?; + write!(writer, "SP: {:04X} ", self.reg.sp)?; + write!(writer, "PC: 00:{:04X} ", self.reg.pc)?; + write!(writer, "({:02X} ", self.read_byte(self.reg.pc))?; + write!(writer, "{:02X} ", self.read_byte(self.reg.pc + 1))?; + write!(writer, "{:02X} ", self.read_byte(self.reg.pc + 2))?; + writeln!(writer, "{:02X})", self.read_byte(self.reg.pc + 3))?; + writer.flush()?; Ok(()) } diff --git a/src/gui.rs b/src/gui.rs new file mode 100644 index 0000000..f7b8087 --- /dev/null +++ b/src/gui.rs @@ -0,0 +1,356 @@ +use crate::cpu::Register; +use crate::cpu::RegisterPair; +use crate::LR35902; +use egui::{ClippedMesh, FontDefinitions}; +use egui_wgpu_backend::{RenderPass, ScreenDescriptor}; +use egui_winit_platform::{Platform, PlatformDescriptor}; +use pixels::{wgpu, PixelsContext}; +use std::time::Instant; +use wgpu::TextureFormat::Bgra8UnormSrgb; + +// Boilerplate code from: https://github.com/parasyte/pixels/blob/0.3.0/examples/egui-winit/src/gui.rs + +/// Manages all state required for rendering egui over `Pixels`. +pub struct Egui { + // State for egui. + start_time: Instant, + platform: Platform, + screen_desc: ScreenDescriptor, + render_pass: RenderPass, + paint_jobs: Vec, + + pub config: Configuration, + + show_flags: bool, + show_cpu_info: bool, + show_registers: bool, + + #[cfg(feature = "debug")] + show_disasm: bool, + #[cfg(feature = "debug")] + pub break_point: Option, +} + +impl Egui { + /// Create egui. + pub fn new(width: u32, height: u32, scale_factor: f64, context: &PixelsContext) -> Self { + let platform = Platform::new(PlatformDescriptor { + physical_width: width, + physical_height: height, + scale_factor, + font_definitions: FontDefinitions::default(), + style: Default::default(), + }); + + let screen_desc = ScreenDescriptor { + physical_width: width, + physical_height: height, + scale_factor: scale_factor as f32, + }; + + let render_pass = RenderPass::new(&context.device, Bgra8UnormSrgb); + + Self { + start_time: Instant::now(), + platform, + screen_desc, + render_pass, + paint_jobs: Vec::new(), + config: Default::default(), + show_flags: false, + show_cpu_info: false, + show_registers: false, + #[cfg(feature = "debug")] + show_disasm: false, + #[cfg(feature = "debug")] + break_point: None, + } + } + + /// Handle input events from the window manager. + pub fn handle_event(&mut self, event: &winit::event::Event<'_, ()>) { + self.platform.handle_event(event); + } + + /// Resize egui. + pub fn resize(&mut self, width: u32, height: u32) { + self.screen_desc.physical_width = width; + self.screen_desc.physical_height = height; + } + + /// Update scaling factor. + pub fn scale_factor(&mut self, scale_factor: f64) { + self.screen_desc.scale_factor = scale_factor as f32; + } + + /// Prepare egui. + pub fn prepare(&mut self, game_boy: &LR35902) { + self.platform + .update_time(self.start_time.elapsed().as_secs_f64()); + + // Begin the egui frame. + self.platform.begin_frame(); + + // Draw the demo application. + self.ui(&self.platform.context(), game_boy); + + // End the egui frame and create all paint jobs to prepare for rendering. + let (_output, paint_commands) = self.platform.end_frame(); + self.paint_jobs = self.platform.context().tessellate(paint_commands); + } + + /// Create the UI using egui. + fn ui(&mut self, ctx: &egui::CtxRef, game_boy: &LR35902) { + egui::TopPanel::top("menubar_container").show(ctx, |ui| { + egui::menu::bar(ui, |ui| { + egui::menu::menu(ui, "File", |ui| { + if ui.button("Configuration").clicked() { + self.config.show = true; + } + }); + + egui::menu::menu(ui, "Status", |ui| { + if ui.button("Flags").clicked() { + self.show_flags = true; + } + + if ui.button("CPU Information").clicked() { + self.show_cpu_info = true; + } + + if ui.button("Registers").clicked() { + self.show_registers = true; + } + }); + + #[cfg(feature = "debug")] + if ui.button("Disassembly").clicked() { + self.show_disasm = true; + } + }); + }); + + egui::Window::new("Cpu Flags") + .open(&mut self.show_flags) + .show(ctx, |ui| { + let flags = game_boy.flags(); + + ui.horizontal(|ui| { + let _ = ui.selectable_label(flags.z(), "Zero"); + let _ = ui.selectable_label(flags.n(), "Negative"); + let _ = ui.selectable_label(flags.h(), "Half-Carry"); + let _ = ui.selectable_label(flags.c(), "Carry"); + }); + }); + + egui::Window::new("Registers") + .open(&mut self.show_registers) + .show(ctx, |ui| { + ui.horizontal(|ui| { + ui.label("A"); + ui.monospace(format!("{:#04X}", game_boy.register(Register::A))); + + ui.label("F"); + let flag: u8 = game_boy.register(Register::Flag).into(); + ui.monospace(format!("{:#04X}", flag)); + }); + + ui.horizontal(|ui| { + ui.label("B"); + ui.monospace(format!("{:#04X}", game_boy.register(Register::B))); + + ui.label("C"); + ui.monospace(format!("{:#04X}", game_boy.register(Register::C))); + }); + + ui.horizontal(|ui| { + ui.label("D"); + ui.monospace(format!("{:#04X}", game_boy.register(Register::D))); + + ui.label("E"); + ui.monospace(format!("{:#04X}", game_boy.register(Register::E))); + }); + + ui.horizontal(|ui| { + ui.label("H"); + ui.monospace(format!("{:#04X}", game_boy.register(Register::H))); + + ui.label("L"); + ui.monospace(format!("{:#04X}", game_boy.register(Register::L))); + }); + + ui.horizontal(|ui| { + ui.label("AF"); + ui.monospace(format!("{:#06X}", game_boy.register_pair(RegisterPair::AF))); + }); + + ui.horizontal(|ui| { + ui.label("BC"); + ui.monospace(format!("{:#06X}", game_boy.register_pair(RegisterPair::BC))); + }); + + ui.horizontal(|ui| { + ui.label("DE"); + ui.monospace(format!("{:#06X}", game_boy.register_pair(RegisterPair::DE))); + }); + + ui.horizontal(|ui| { + ui.label("HL"); + ui.monospace(format!("{:#06X}", game_boy.register_pair(RegisterPair::HL))); + }); + }); + + egui::Window::new("Cpu Information") + .open(&mut self.show_cpu_info) + .show(ctx, |ui| { + ui.horizontal(|ui| { + ui.label("PC"); + ui.monospace(format!("{:#06X}", game_boy.register_pair(RegisterPair::PC))); + }); + + ui.horizontal(|ui| { + ui.label("SP"); + ui.monospace(format!("{:#06X}", game_boy.register_pair(RegisterPair::SP))); + }); + + ui.horizontal(|ui| { + ui.label("IME"); + ui.label(format!("{:?}", game_boy.ime())); + }); + + ui.horizontal(|ui| { + ui.label("HALT"); + ui.label(format!("{:?}", game_boy.halted())); + }); + }); + + #[cfg(feature = "debug")] + { + let mut show_disasm = self.show_disasm; + egui::Window::new("Disassembler") + .open(&mut show_disasm) + .show(ctx, |ui| { + // FIXME: This can't be efficient at all + // To fix this, maybe we should make Instruction::from_byte not mutate the state of the Cpu (which we SHOULD have done to begin with) + let mut emu_clone = game_boy.clone(); + + for i in 0..10 { + let pc = emu_clone.register_pair(RegisterPair::PC); + let opcode = emu_clone.fetch(); + emu_clone.inc_pc(); + + let instr = emu_clone.decode(opcode); + let res = ui.selectable_label(i == 0, format!("{:#06X}: {:?}", pc, instr)); + + if res.clicked() { + self.break_point = Some(pc); + } + } + + ui.horizontal(|ui| { + if ui.button("Reset Breakpoint").clicked() { + self.break_point = None; + } + + ui.selectable_label( + self.break_point.is_some(), + format!("{:04X?}", self.break_point), + ) + }); + }); + self.show_disasm = show_disasm; + } + + egui::Window::new("IRQ Information").show(ctx, |ui| { + let req = game_boy.read_byte(0xFF0F); + let enabled = game_boy.read_byte(0xFFFF); + + ui.heading("Interrupt Requests"); + ui.horizontal(|ui| { + let _ = ui.selectable_label(req & 0x01 == 0x01, "VBLANK"); + let _ = ui.selectable_label((req >> 1) & 0x01 == 0x01, "LCD STAT"); + let _ = ui.selectable_label((req >> 2) & 0x01 == 0x01, "TIMER"); + let _ = ui.selectable_label((req >> 3) & 0x01 == 0x01, "SERIAL"); + let _ = ui.selectable_label((req >> 4) & 0x01 == 0x01, "JOYPAD"); + }); + + ui.heading("Interrupt Enable"); + ui.horizontal(|ui| { + let _ = ui.selectable_label(enabled & 0x01 == 0x01, "VBLANK"); + let _ = ui.selectable_label((enabled >> 1) & 0x01 == 0x01, "LCD STAT"); + let _ = ui.selectable_label((enabled >> 2) & 0x01 == 0x01, "TIMER"); + let _ = ui.selectable_label((enabled >> 3) & 0x01 == 0x01, "SERIAL"); + let _ = ui.selectable_label((enabled >> 4) & 0x01 == 0x01, "JOYPAD"); + }); + }); + + #[cfg(feature = "debug")] + let mut spacebar_step = self.config.spacebar_step; + egui::Window::new("Configuration") + .open(&mut self.config.show) + .show(ctx, |ui| { + #[cfg(feature = "debug")] + ui.horizontal(|ui| { + ui.label("Spacebar Steps"); + ui.add(egui::Slider::u16(&mut spacebar_step, 0..=std::u16::MAX)); + }); + }); + + #[cfg(feature = "debug")] + { + self.config.spacebar_step = spacebar_step; + } + } + + /// Render egui. + pub fn render( + &mut self, + encoder: &mut wgpu::CommandEncoder, + render_target: &wgpu::TextureView, + context: &PixelsContext, + ) { + // Upload all resources to the GPU. + self.render_pass.update_texture( + &context.device, + &context.queue, + &self.platform.context().texture(), + ); + self.render_pass + .update_user_textures(&context.device, &context.queue); + self.render_pass.update_buffers( + &context.device, + &context.queue, + &self.paint_jobs, + &self.screen_desc, + ); + + // Record all render passes. + self.render_pass.execute( + encoder, + render_target, + &self.paint_jobs, + &self.screen_desc, + None, + ); + } +} + +pub struct Configuration { + /// Show Configuration egui menu + show: bool, + + /// How many [`LR35902`] .step() do we want to do at once + /// when pressing the spacebar key? + #[cfg(feature = "debug")] + pub spacebar_step: u16, +} + +impl Default for Configuration { + fn default() -> Self { + Self { + show: false, + #[cfg(feature = "debug")] + spacebar_step: 1, + } + } +} diff --git a/src/instruction.rs b/src/instruction.rs index 14afce7..24d0559 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -1,7 +1,7 @@ use super::cpu::{Cpu, Flags, HaltState, ImeState, Register, RegisterPair}; use std::{convert::TryFrom, fmt::Debug}; -#[derive(Debug, Copy, Clone)] +#[derive(Copy, Clone)] #[allow(clippy::upper_case_acronyms)] pub enum Instruction { NOP, @@ -93,7 +93,7 @@ pub enum InstrRegisterPair { DecrementHL, } -#[derive(Debug, Copy, Clone)] +#[derive(Copy, Clone)] pub enum InstrRegister { A, B, @@ -105,7 +105,7 @@ pub enum InstrRegister { IndirectHL, // (HL) } -#[derive(Debug, Copy, Clone)] +#[derive(Copy, Clone)] pub enum JumpCondition { NotZero, Zero, @@ -2017,15 +2017,15 @@ impl std::fmt::Debug for JPTarget { impl std::fmt::Debug for LDTarget { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match *self { - LDTarget::IndirectC => f.write_str("IndirectC"), + LDTarget::IndirectC => f.write_str("(0xFF00 + C)"), LDTarget::Register(reg) => write!(f, "{:?}", reg), - LDTarget::IndirectRegister(pair) => write!(f, "[{:?}]", pair), - LDTarget::ByteAtAddress(addr) => write!(f, "[{:#06X}]", addr), + LDTarget::IndirectRegister(pair) => write!(f, "({:?})", pair), + LDTarget::ByteAtAddress(addr) => write!(f, "({:#06X})", addr), LDTarget::ImmediateWord(word) => write!(f, "{:#06X}", word), LDTarget::ImmediateByte(byte) => write!(f, "{:#04X}", byte), LDTarget::RegisterPair(pair) => write!(f, "{:?}", pair), LDTarget::ByteAtAddressWithOffset(byte) => { - write!(f, "[0xFF00 + {:#04X}]", byte) + write!(f, "(0xFF00 + {:#04X})", byte) } } } @@ -2065,6 +2065,90 @@ impl std::fmt::Debug for Registers { } } +impl std::fmt::Debug for Instruction { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use Instruction::*; + + match *self { + NOP => f.write_str("NOP"), + LD(left, right) => write!(f, "LD {:?}, {:?}", left, right), + STOP => f.write_str("STOP"), + JR(cond, dist) => write!(f, "JR {:?}, {:?}", cond, dist), + ADD(left, right) => write!(f, "ADD {:?}, {:?}", left, right), + INC(register) => write!(f, "INC {:?}", register), + DEC(register) => write!(f, "DEC {:?}", register), + RLCA => f.write_str("RLCA"), + RRCA => f.write_str("RRCA"), + RLA => f.write_str("RLA"), + RRA => f.write_str("RRA"), + DAA => f.write_str("DAA"), + CPL => f.write_str("CPL"), + SCF => f.write_str("SCF"), + CCF => f.write_str("CCF"), + HALT => f.write_str("HALT"), + ADC(target) => write!(f, "ADC {:?}", target), + SUB(target) => write!(f, "SUB {:?}", target), + SBC(target) => write!(f, "SBC {:?}", target), + AND(target) => write!(f, "AND {:?}", target), + XOR(target) => write!(f, "XOR {:?}", target), + OR(target) => write!(f, "OR {:?}", target), + CP(target) => write!(f, "CP {:?}", target), + RET(cond) => write!(f, "RET {:?}", cond), + LDHL(value) => write!(f, "LDHL {:?}", value), + POP(pair) => write!(f, "POP {:?}", pair), + RETI => f.write_str("RETI"), + JP(cond, target) => write!(f, "JP {:?}, {:?}", cond, target), + DI => f.write_str("DI"), + EI => f.write_str("EI"), + CALL(cond, addr) => write!(f, "CALL {:?}, {:?}", cond, addr), + PUSH(pair) => write!(f, "PUSH {:?}", pair), + RST(vector) => write!(f, "RST {:?}", vector), + RLC(register) => write!(f, "RLC {:?}", register), + RRC(register) => write!(f, "RRC {:?}", register), + RL(register) => write!(f, "RL {:?}", register), + RR(register) => write!(f, "RR {:?}", register), + SLA(register) => write!(f, "SLA {:?}", register), + SRA(register) => write!(f, "SRA {:?}", register), + SWAP(register) => write!(f, "SWAP {:?}", register), + SRL(register) => write!(f, "SRL {:?}", register), + BIT(bit, register) => write!(f, "BIT {:?}, {:?}", bit, register), + RES(bit, register) => write!(f, "RES {:?}, {:?}", bit, register), + SET(bit, register) => write!(f, "SET {:?}, {:?}", bit, register), + } + } +} + +impl std::fmt::Debug for JumpCondition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use JumpCondition::*; + + match *self { + NotZero => f.write_str("NZ"), + Zero => f.write_str("Z"), + NotCarry => f.write_str("NC"), + Carry => f.write_str("C"), + Always => f.write_str(""), + } + } +} + +impl std::fmt::Debug for InstrRegister { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use InstrRegister::*; + + match *self { + A => f.write_str("A"), + B => f.write_str("B"), + C => f.write_str("C"), + D => f.write_str("D"), + E => f.write_str("E"), + H => f.write_str("H"), + L => f.write_str("L"), + IndirectHL => f.write_str("(HL)"), + } + } +} + impl Cycle { pub const fn new(num: u32) -> Self { Self(num) diff --git a/src/lib.rs b/src/lib.rs index b72fc61..d6997d2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,11 @@ pub use cpu::Cpu as LR35902; +pub use gui::Egui; pub use instruction::Cycle; pub use joypad::ButtonState; +#[cfg(feature = "debug")] +pub use cpu::RegisterPair; + pub const GB_WIDTH: usize = 160; pub const GB_HEIGHT: usize = 144; pub const LR35902_CLOCK_SPEED: u32 = 0x400000; // Hz | 4.194304Mhz @@ -9,6 +13,7 @@ pub const LR35902_CLOCK_SPEED: u32 = 0x400000; // Hz | 4.194304Mhz mod bus; mod cartridge; mod cpu; +mod gui; mod high_ram; mod instruction; mod interrupt; diff --git a/src/main.rs b/src/main.rs index 46f3f43..267010e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,9 @@ use anyhow::{anyhow, Result}; use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg}; +use gb::Egui; use gb::LR35902_CLOCK_SPEED; use gb::{ButtonState, Cycle, LR35902}; -use gilrs::{Button, Event as GamepadEvent, EventType as GamepadEventType, Gamepad, Gilrs}; +use gilrs::{Button, Event as GamepadEvent, EventType as GamepadEventType, Gilrs}; use pixels::{Pixels, SurfaceTexture}; use std::time::{Duration, Instant}; use winit::dpi::LogicalSize; @@ -11,6 +12,9 @@ use winit::event_loop::{ControlFlow, EventLoop}; use winit::window::{Window, WindowBuilder}; use winit_input_helper::WinitInputHelper; +#[cfg(feature = "debug")] +use gb::RegisterPair; + // 160 x 144 const GB_WIDTH: u32 = 160; const GB_HEIGHT: u32 = 144; @@ -19,6 +23,9 @@ const SCALE: f64 = 5.0; const LR35902_CYCLE_TIME: f64 = 1.0f64 / LR35902_CLOCK_SPEED as f64; const CYCLES_IN_FRAME: Cycle = Cycle::new(70224); +#[cfg(feature = "debug")] +const STEP_MODE_BY_DEFAULT: bool = true; + fn main() -> Result<()> { let app = App::new(crate_name!()) .version(crate_version!()) @@ -64,20 +71,46 @@ fn main() -> Result<()> { // Initialize Gamepad Support let mut gilrs = Gilrs::new().expect("Failed to initialize Gilrs"); - let mut active_gamepad = None; // Initialize GUI let event_loop = EventLoop::new(); let mut input = WinitInputHelper::new(); let window = create_window(&event_loop, cartridge_title)?; - let mut pixels = create_pixels(&window)?; + + let (mut pixels, mut egui) = { + let size = window.inner_size(); + let scale_factor = window.scale_factor(); + let surface_texture = SurfaceTexture::new(size.width, size.height, &window); + let pixels = Pixels::new(GB_WIDTH, GB_HEIGHT, surface_texture)?; + let egui = Egui::new(size.width, size.height, scale_factor, pixels.context()); + + (pixels, egui) + }; let mut now = Instant::now(); let mut cycles_in_frame: Cycle = Default::default(); + + #[cfg(feature = "debug")] + let mut step_mode = STEP_MODE_BY_DEFAULT; + event_loop.run(move |event, _, control_flow| { + // Update egui + egui.handle_event(&event); + if let Event::RedrawRequested(_) = event { - if pixels - .render() + // Prepare egui + egui.prepare(&game_boy); + + // Render everything together + let render_result = pixels.render_with(|encoder, target, ctx| { + // Render the texture + ctx.scaling_renderer.render(encoder, target); + + // Render egui + egui.render(encoder, target, ctx); + }); + + if render_result .map_err(|e| anyhow!("pixels.render() failed: {}", e)) .is_err() { @@ -92,39 +125,75 @@ fn main() -> Result<()> { return; } - if let Some(size) = input.window_resized() { - pixels.resize_surface(size.width, size.height); + #[cfg(feature = "debug")] + if input.key_pressed(VirtualKeyCode::S) { + step_mode = !step_mode; } - if active_gamepad.is_none() { - active_gamepad = gilrs.next_event().map(|e| e.id); + if let Some(scale_factor) = input.scale_factor() { + egui.scale_factor(scale_factor); + } + + if let Some(size) = input.window_resized() { + pixels.resize_surface(size.width, size.height); + egui.resize(size.width, size.height); } // Emulate Game Boy + let mut elapsed_cycles: Cycle = Default::default(); let delta = now.elapsed().subsec_nanos(); now = Instant::now(); + #[cfg(feature = "debug")] + if step_mode { + if input.key_pressed(VirtualKeyCode::Space) { + if let Some(event) = gilrs.next_event() { + handle_gamepad_input(&mut game_boy, event); + } + + for _ in 0..egui.config.spacebar_step { + elapsed_cycles += game_boy.step(); + } + + cycles_in_frame %= CYCLES_IN_FRAME; + } + + game_boy.get_ppu().copy_to_gui(pixels.get_frame()); + window.request_redraw(); + return; + } + let cycle_time = Duration::from_secs_f64(LR35902_CYCLE_TIME).subsec_nanos(); let pending_cycles = Cycle::new(delta / cycle_time); - let mut elapsed_cycles: Cycle = Default::default(); while elapsed_cycles <= pending_cycles { if let Some(event) = gilrs.next_event() { handle_gamepad_input(&mut game_boy, event); } elapsed_cycles += game_boy.step(); + + #[cfg(feature = "debug")] + { + let pc = game_boy.register_pair(RegisterPair::PC); + + if let Some(break_point) = egui.break_point { + if pc == break_point { + step_mode = true; + break; + } + } + } } cycles_in_frame += elapsed_cycles; if cycles_in_frame >= CYCLES_IN_FRAME { - let ppu = game_boy.get_ppu(); - let frame = pixels.get_frame(); - ppu.copy_to_gui(frame); - window.request_redraw(); + // Redraw + cycles_in_frame = Default::default(); - cycles_in_frame = Default::default() + game_boy.get_ppu().copy_to_gui(pixels.get_frame()); + window.request_redraw(); } } }); @@ -139,12 +208,6 @@ fn create_window(event_loop: &EventLoop<()>, title: &str) -> Result { .build(&event_loop)?) } -fn create_pixels(window: &Window) -> Result { - let window_size = window.inner_size(); - let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, window); - Ok(Pixels::new(GB_WIDTH, GB_HEIGHT, surface_texture)?) -} - fn handle_gamepad_input(game_boy: &mut LR35902, event: GamepadEvent) { use GamepadEventType::*; let joypad_status = &mut game_boy.bus.joypad.status;