feat: integrate eui and pixels-rs for debug info

This commit is contained in:
Rekai Nyangadzayi Musuka 2021-06-02 01:50:16 -05:00
parent 96cf705966
commit b1bf6c5868
7 changed files with 790 additions and 182 deletions

350
Cargo.lock generated
View File

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

View File

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

View File

@ -11,6 +11,7 @@ pub struct Cpu {
reg: Registers,
flags: Flags,
ime: ImeState,
// TODO: Merge halted and state properties
halted: Option<HaltState>,
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(())
}

356
src/gui.rs Normal file
View File

@ -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<ClippedMesh>,
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<u16>,
}
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,
}
}
}

View File

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

View File

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

View File

@ -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<Window> {
.build(&event_loop)?)
}
fn create_pixels(window: &Window) -> Result<Pixels> {
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;