diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 0000000..5f4e56f
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,27 @@
+name: main
+
+on:
+ push:
+ branches: [ main ]
+ pull_request:
+ branches: [ main ]
+
+env:
+ CARGO_TERM_COLOR: always
+ RUSTFLAGS: -D warnings
+
+
+jobs:
+ CI:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: Swatinem/rust-cache@v2
+ - name: Version
+ run: rustc --version
+ - name: Run rustfmt
+ run: cargo fmt --check
+ - name: Unit tests
+ run: cargo test -- --nocapture
+ - name : Clippy
+ run: cargo clippy --all-features
diff --git a/.gitignore b/.gitignore
index ea8c4bf..a2191a0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,11 @@
/target
+.idea
+.vscode/
+.zed
+roms
+TODO.md
+.fpt_debug_history
+*.pgm
+*.ppm
+screenshots/
+.DS_Store
diff --git a/.rustfmt.toml b/.rustfmt.toml
new file mode 100644
index 0000000..6510695
--- /dev/null
+++ b/.rustfmt.toml
@@ -0,0 +1,2 @@
+unstable_features = true
+group_imports = "StdExternalCrate"
diff --git a/.tool-versions b/.tool-versions
deleted file mode 100644
index 1b69962..0000000
--- a/.tool-versions
+++ /dev/null
@@ -1 +0,0 @@
-rust 1.72.1
diff --git a/Cargo.lock b/Cargo.lock
index a4ed501..f69ce55 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2,6 +2,2401 @@
# It is not intended for manual editing.
version = 3
+[[package]]
+name = "ab_glyph"
+version = "0.2.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f90148830dac590fac7ccfe78ec4a8ea404c60f75a24e16407a71f0f40de775"
+dependencies = [
+ "ab_glyph_rasterizer",
+ "owned_ttf_parser",
+]
+
+[[package]]
+name = "ab_glyph_rasterizer"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046"
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "ahash"
+version = "0.8.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
+dependencies = [
+ "cfg-if",
+ "getrandom",
+ "once_cell",
+ "version_check",
+ "zerocopy",
+]
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "android-activity"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee91c0c2905bae44f84bfa4e044536541df26b7703fd0888deeb9060fcc44289"
+dependencies = [
+ "android-properties",
+ "bitflags 2.5.0",
+ "cc",
+ "cesu8",
+ "jni",
+ "jni-sys",
+ "libc",
+ "log",
+ "ndk",
+ "ndk-context",
+ "ndk-sys",
+ "num_enum",
+ "thiserror",
+]
+
+[[package]]
+name = "android-properties"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04"
+
+[[package]]
+name = "anstream"
+version = "0.6.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "is_terminal_polyfill",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5"
+dependencies = [
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
+dependencies = [
+ "anstyle",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "arboard"
+version = "3.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fb4009533e8ff8f1450a5bcbc30f4242a1d34442221f72314bea1f5dc9c7f89"
+dependencies = [
+ "clipboard-win",
+ "log",
+ "objc2 0.5.1",
+ "objc2-app-kit",
+ "objc2-foundation",
+ "parking_lot",
+ "x11rb",
+]
+
+[[package]]
+name = "as-raw-xcb-connection"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b"
+
+[[package]]
+name = "atomic-waker"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
+
+[[package]]
+name = "autocfg"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
+
+[[package]]
+name = "block"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
+
+[[package]]
+name = "block-sys"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae85a0696e7ea3b835a453750bf002770776609115e6d25c6d2ff28a8200f7e7"
+dependencies = [
+ "objc-sys",
+]
+
+[[package]]
+name = "block2"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15b55663a85f33501257357e6421bb33e769d5c9ffb5ba0921c975a123e35e68"
+dependencies = [
+ "block-sys",
+ "objc2 0.4.1",
+]
+
+[[package]]
+name = "block2"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43ff7d91d3c1d568065b06c899777d1e48dcf76103a672a0adbc238a7f247f1e"
+dependencies = [
+ "objc2 0.5.1",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
+
+[[package]]
+name = "bytemuck"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15"
+dependencies = [
+ "bytemuck_derive",
+]
+
+[[package]]
+name = "bytemuck_derive"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4da9a32f3fed317401fa3c862968128267c3106685286e15d5aaa3d7389c2f60"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+[[package]]
+name = "bytes"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
+
+[[package]]
+name = "calloop"
+version = "0.12.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fba7adb4dd5aa98e5553510223000e7148f621165ec5f9acd7113f6ca4995298"
+dependencies = [
+ "bitflags 2.5.0",
+ "log",
+ "polling",
+ "rustix",
+ "slab",
+ "thiserror",
+]
+
+[[package]]
+name = "calloop-wayland-source"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f0ea9b9476c7fad82841a8dbb380e2eae480c21910feba80725b46931ed8f02"
+dependencies = [
+ "calloop",
+ "rustix",
+ "wayland-backend",
+ "wayland-client",
+]
+
+[[package]]
+name = "cc"
+version = "1.0.96"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "065a29261d53ba54260972629f9ca6bffa69bac13cd1fed61420f7fa68b9f8bd"
+dependencies = [
+ "jobserver",
+ "libc",
+ "once_cell",
+]
+
+[[package]]
+name = "cesu8"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "cfg_aliases"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
+
+[[package]]
+name = "cgl"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "clipboard-win"
+version = "5.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79f4473f5144e20d9aceaf2972478f06ddf687831eafeeb434fbaf0acc4144ad"
+dependencies = [
+ "error-code",
+]
+
+[[package]]
+name = "cocoa"
+version = "0.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c"
+dependencies = [
+ "bitflags 1.3.2",
+ "block",
+ "cocoa-foundation",
+ "core-foundation",
+ "core-graphics",
+ "foreign-types",
+ "libc",
+ "objc",
+]
+
+[[package]]
+name = "cocoa-foundation"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7"
+dependencies = [
+ "bitflags 1.3.2",
+ "block",
+ "core-foundation",
+ "core-graphics-types",
+ "libc",
+ "objc",
+]
+
+[[package]]
+name = "color_quant"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
+
+[[package]]
+name = "colorchoice"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
+
+[[package]]
+name = "combine"
+version = "4.6.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
+dependencies = [
+ "bytes",
+ "memchr",
+]
+
+[[package]]
+name = "concurrent-queue"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "core-foundation"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
+
+[[package]]
+name = "core-graphics"
+version = "0.23.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation",
+ "core-graphics-types",
+ "foreign-types",
+ "libc",
+]
+
+[[package]]
+name = "core-graphics-types"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation",
+ "libc",
+]
+
+[[package]]
+name = "crc32fast"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
+
+[[package]]
+name = "cursor-icon"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991"
+
+[[package]]
+name = "dispatch"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
+
+[[package]]
+name = "dlib"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412"
+dependencies = [
+ "libloading",
+]
+
+[[package]]
+name = "document-features"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef5282ad69563b5fc40319526ba27e0e7363d552a896f0297d54f767717f9b95"
+dependencies = [
+ "litrs",
+]
+
+[[package]]
+name = "downcast-rs"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
+
+[[package]]
+name = "ecolor"
+version = "0.26.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03cfe80b1890e1a8cdbffc6044d6872e814aaf6011835a2a5e2db0e5c5c4ef4e"
+dependencies = [
+ "bytemuck",
+]
+
+[[package]]
+name = "eframe"
+version = "0.26.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c456c1bb6d13bf68b780257484703d750c70a23ff891ba35f4d6e23a4dbdf26f"
+dependencies = [
+ "bytemuck",
+ "cocoa",
+ "document-features",
+ "egui",
+ "egui-winit",
+ "egui_glow",
+ "glow",
+ "glutin",
+ "glutin-winit",
+ "image",
+ "js-sys",
+ "log",
+ "objc",
+ "parking_lot",
+ "percent-encoding",
+ "raw-window-handle 0.5.2",
+ "raw-window-handle 0.6.1",
+ "static_assertions",
+ "thiserror",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "web-time",
+ "winapi",
+ "winit",
+]
+
+[[package]]
+name = "egui"
+version = "0.26.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "180f595432a5b615fc6b74afef3955249b86cfea72607b40740a4cd60d5297d0"
+dependencies = [
+ "ahash",
+ "epaint",
+ "log",
+ "nohash-hasher",
+]
+
+[[package]]
+name = "egui-winit"
+version = "0.26.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa4d44f8d89f70d4480545eb2346b76ea88c3022e9f4706cebc799dbe8b004a2"
+dependencies = [
+ "arboard",
+ "egui",
+ "log",
+ "raw-window-handle 0.6.1",
+ "smithay-clipboard",
+ "web-time",
+ "webbrowser",
+ "winit",
+]
+
+[[package]]
+name = "egui_glow"
+version = "0.26.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a08e3be8728b4c59493dbfec041c657e6725bdeafdbd49aef3f1dbb9e551fa01"
+dependencies = [
+ "bytemuck",
+ "egui",
+ "glow",
+ "log",
+ "memoffset",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "emath"
+version = "0.26.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6916301ecf80448f786cdf3eb51d9dbdd831538732229d49119e2d4312eaaf09"
+dependencies = [
+ "bytemuck",
+]
+
+[[package]]
+name = "env_filter"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea"
+dependencies = [
+ "log",
+ "regex",
+]
+
+[[package]]
+name = "env_logger"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "env_filter",
+ "humantime",
+ "log",
+]
+
+[[package]]
+name = "epaint"
+version = "0.26.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77b9fdf617dd7f58b0c8e6e9e4a1281f730cde0831d40547da446b2bb76a47af"
+dependencies = [
+ "ab_glyph",
+ "ahash",
+ "bytemuck",
+ "ecolor",
+ "emath",
+ "log",
+ "nohash-hasher",
+ "parking_lot",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
+[[package]]
+name = "errno"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
+dependencies = [
+ "libc",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "error-code"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0474425d51df81997e2f90a21591180b38eccf27292d755f3e30750225c175b"
+
+[[package]]
+name = "fdeflate"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645"
+dependencies = [
+ "simd-adler32",
+]
+
+[[package]]
+name = "flate2"
+version = "1.0.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "foreign-types"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
+dependencies = [
+ "foreign-types-macros",
+ "foreign-types-shared",
+]
+
+[[package]]
+name = "foreign-types-macros"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "foreign-types-shared"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
+dependencies = [
+ "percent-encoding",
+]
+
[[package]]
name = "fpt"
version = "0.1.0"
+dependencies = [
+ "rstest",
+]
+
+[[package]]
+name = "fpt-egui"
+version = "0.1.0"
+dependencies = [
+ "eframe",
+ "egui",
+ "env_logger",
+ "fpt",
+ "js-sys",
+ "log",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
+[[package]]
+name = "futures"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
+
+[[package]]
+name = "futures-task"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
+
+[[package]]
+name = "futures-timer"
+version = "3.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24"
+
+[[package]]
+name = "futures-util"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "gethostname"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818"
+dependencies = [
+ "libc",
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "gl_generator"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d"
+dependencies = [
+ "khronos_api",
+ "log",
+ "xml-rs",
+]
+
+[[package]]
+name = "glob"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
+
+[[package]]
+name = "glow"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd348e04c43b32574f2de31c8bb397d96c9fcfa1371bd4ca6d8bdc464ab121b1"
+dependencies = [
+ "js-sys",
+ "slotmap",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "glutin"
+version = "0.31.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18fcd4ae4e86d991ad1300b8f57166e5be0c95ef1f63f3f5b827f8a164548746"
+dependencies = [
+ "bitflags 2.5.0",
+ "cfg_aliases",
+ "cgl",
+ "core-foundation",
+ "dispatch",
+ "glutin_egl_sys",
+ "glutin_glx_sys",
+ "glutin_wgl_sys",
+ "icrate",
+ "libloading",
+ "objc2 0.4.1",
+ "once_cell",
+ "raw-window-handle 0.5.2",
+ "wayland-sys",
+ "windows-sys 0.48.0",
+ "x11-dl",
+]
+
+[[package]]
+name = "glutin-winit"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ebcdfba24f73b8412c5181e56f092b5eff16671c514ce896b258a0a64bd7735"
+dependencies = [
+ "cfg_aliases",
+ "glutin",
+ "raw-window-handle 0.5.2",
+ "winit",
+]
+
+[[package]]
+name = "glutin_egl_sys"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77cc5623f5309ef433c3dd4ca1223195347fe62c413da8e2fdd0eb76db2d9bcd"
+dependencies = [
+ "gl_generator",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "glutin_glx_sys"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a165fd686c10dcc2d45380b35796e577eacfd43d4660ee741ec8ebe2201b3b4f"
+dependencies = [
+ "gl_generator",
+ "x11-dl",
+]
+
+[[package]]
+name = "glutin_wgl_sys"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c8098adac955faa2d31079b65dc48841251f69efd3ac25477903fc424362ead"
+dependencies = [
+ "gl_generator",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.14.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
+
+[[package]]
+name = "home"
+version = "0.5.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
+dependencies = [
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "humantime"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
+
+[[package]]
+name = "icrate"
+version = "0.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99d3aaff8a54577104bafdf686ff18565c3b6903ca5782a2026ef06e2c7aa319"
+dependencies = [
+ "block2 0.3.0",
+ "dispatch",
+ "objc2 0.4.1",
+]
+
+[[package]]
+name = "idna"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
+dependencies = [
+ "unicode-bidi",
+ "unicode-normalization",
+]
+
+[[package]]
+name = "image"
+version = "0.24.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d"
+dependencies = [
+ "bytemuck",
+ "byteorder",
+ "color_quant",
+ "num-traits",
+ "png",
+]
+
+[[package]]
+name = "indexmap"
+version = "2.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
+[[package]]
+name = "is_terminal_polyfill"
+version = "1.70.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
+
+[[package]]
+name = "jni"
+version = "0.21.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
+dependencies = [
+ "cesu8",
+ "cfg-if",
+ "combine",
+ "jni-sys",
+ "log",
+ "thiserror",
+ "walkdir",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "jni-sys"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
+
+[[package]]
+name = "jobserver"
+version = "0.1.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "js-sys"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "khronos_api"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
+
+[[package]]
+name = "libc"
+version = "0.2.154"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346"
+
+[[package]]
+name = "libloading"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
+dependencies = [
+ "cfg-if",
+ "windows-targets 0.52.5",
+]
+
+[[package]]
+name = "libredox"
+version = "0.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3af92c55d7d839293953fcd0fda5ecfe93297cfde6ffbdec13b41d99c0ba6607"
+dependencies = [
+ "bitflags 2.5.0",
+ "libc",
+ "redox_syscall 0.4.1",
+]
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
+
+[[package]]
+name = "litrs"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5"
+
+[[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"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
+
+[[package]]
+name = "malloc_buf"
+version = "0.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "memchr"
+version = "2.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
+
+[[package]]
+name = "memmap2"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "memoffset"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "miniz_oxide"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
+dependencies = [
+ "adler",
+ "simd-adler32",
+]
+
+[[package]]
+name = "ndk"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7"
+dependencies = [
+ "bitflags 2.5.0",
+ "jni-sys",
+ "log",
+ "ndk-sys",
+ "num_enum",
+ "raw-window-handle 0.5.2",
+ "raw-window-handle 0.6.1",
+ "thiserror",
+]
+
+[[package]]
+name = "ndk-context"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b"
+
+[[package]]
+name = "ndk-sys"
+version = "0.5.0+25.2.9519653"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691"
+dependencies = [
+ "jni-sys",
+]
+
+[[package]]
+name = "nohash-hasher"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
+
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num_enum"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845"
+dependencies = [
+ "num_enum_derive",
+]
+
+[[package]]
+name = "num_enum_derive"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b"
+dependencies = [
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "objc"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
+dependencies = [
+ "malloc_buf",
+]
+
+[[package]]
+name = "objc-sys"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da284c198fb9b7b0603f8635185e85fbd5b64ee154b1ed406d489077de2d6d60"
+
+[[package]]
+name = "objc2"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "559c5a40fdd30eb5e344fbceacf7595a81e242529fb4e21cf5f43fb4f11ff98d"
+dependencies = [
+ "objc-sys",
+ "objc2-encode 3.0.0",
+]
+
+[[package]]
+name = "objc2"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4b25e1034d0e636cd84707ccdaa9f81243d399196b8a773946dcffec0401659"
+dependencies = [
+ "objc-sys",
+ "objc2-encode 4.0.1",
+]
+
+[[package]]
+name = "objc2-app-kit"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb79768a710a9a1798848179edb186d1af7e8a8679f369e4b8d201dd2a034047"
+dependencies = [
+ "block2 0.5.0",
+ "objc2 0.5.1",
+ "objc2-core-data",
+ "objc2-foundation",
+]
+
+[[package]]
+name = "objc2-core-data"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e092bc42eaf30a08844e6a076938c60751225ec81431ab89f5d1ccd9f958d6c"
+dependencies = [
+ "block2 0.5.0",
+ "objc2 0.5.1",
+ "objc2-foundation",
+]
+
+[[package]]
+name = "objc2-encode"
+version = "3.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d079845b37af429bfe5dfa76e6d087d788031045b25cfc6fd898486fd9847666"
+
+[[package]]
+name = "objc2-encode"
+version = "4.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88658da63e4cc2c8adb1262902cd6af51094df0488b760d6fd27194269c0950a"
+
+[[package]]
+name = "objc2-foundation"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfaefe14254871ea16c7d88968c0ff14ba554712a20d76421eec52f0a7fb8904"
+dependencies = [
+ "block2 0.5.0",
+ "objc2 0.5.1",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+
+[[package]]
+name = "orbclient"
+version = "0.3.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52f0d54bde9774d3a51dcf281a5def240c71996bc6ca05d2c847ec8b2b216166"
+dependencies = [
+ "libredox",
+]
+
+[[package]]
+name = "owned_ttf_parser"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4586edfe4c648c71797a74c84bacb32b52b212eff5dfe2bb9f2c599844023e7"
+dependencies = [
+ "ttf-parser",
+]
+
+[[package]]
+name = "parking_lot"
+version = "0.12.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb"
+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.1",
+ "smallvec",
+ "windows-targets 0.52.5",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
+
+[[package]]
+name = "png"
+version = "0.17.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1"
+dependencies = [
+ "bitflags 1.3.2",
+ "crc32fast",
+ "fdeflate",
+ "flate2",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "polling"
+version = "3.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "645493cf344456ef24219d02a768cf1fb92ddf8c92161679ae3d91b91a637be3"
+dependencies = [
+ "cfg-if",
+ "concurrent-queue",
+ "hermit-abi",
+ "pin-project-lite",
+ "rustix",
+ "tracing",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "proc-macro-crate"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284"
+dependencies = [
+ "toml_edit",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.81"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quick-xml"
+version = "0.31.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "raw-window-handle"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9"
+
+[[package]]
+name = "raw-window-handle"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8cc3bcbdb1ddfc11e700e62968e6b4cc9c75bb466464ad28fb61c5b2c964418b"
+
+[[package]]
+name = "redox_syscall"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e"
+dependencies = [
+ "bitflags 2.5.0",
+]
+
+[[package]]
+name = "regex"
+version = "1.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
+
+[[package]]
+name = "relative-path"
+version = "1.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2"
+
+[[package]]
+name = "rstest"
+version = "0.18.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97eeab2f3c0a199bc4be135c36c924b6590b88c377d416494288c14f2db30199"
+dependencies = [
+ "futures",
+ "futures-timer",
+ "rstest_macros",
+ "rustc_version",
+]
+
+[[package]]
+name = "rstest_macros"
+version = "0.18.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d428f8247852f894ee1be110b375111b586d4fa431f6c46e64ba5a0dcccbe605"
+dependencies = [
+ "cfg-if",
+ "glob",
+ "proc-macro2",
+ "quote",
+ "regex",
+ "relative-path",
+ "rustc_version",
+ "syn",
+ "unicode-ident",
+]
+
+[[package]]
+name = "rustc_version"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "rustix"
+version = "0.38.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
+dependencies = [
+ "bitflags 2.5.0",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[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 = "semver"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca"
+
+[[package]]
+name = "serde"
+version = "1.0.200"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.200"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "simd-adler32"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
+
+[[package]]
+name = "slab"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "slotmap"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a"
+dependencies = [
+ "version_check",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
+
+[[package]]
+name = "smithay-client-toolkit"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "922fd3eeab3bd820d76537ce8f582b1cf951eceb5475c28500c7457d9d17f53a"
+dependencies = [
+ "bitflags 2.5.0",
+ "calloop",
+ "calloop-wayland-source",
+ "cursor-icon",
+ "libc",
+ "log",
+ "memmap2",
+ "rustix",
+ "thiserror",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-csd-frame",
+ "wayland-cursor",
+ "wayland-protocols",
+ "wayland-protocols-wlr",
+ "wayland-scanner",
+ "xkeysym",
+]
+
+[[package]]
+name = "smithay-clipboard"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c091e7354ea8059d6ad99eace06dd13ddeedbb0ac72d40a9a6e7ff790525882d"
+dependencies = [
+ "libc",
+ "smithay-client-toolkit",
+ "wayland-backend",
+]
+
+[[package]]
+name = "smol_str"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6845563ada680337a52d43bb0b29f396f2d911616f6573012645b9e3d048a49"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "syn"
+version = "2.0.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.59"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.59"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tinyvec"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
+
+[[package]]
+name = "toml_datetime"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
+
+[[package]]
+name = "toml_edit"
+version = "0.21.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1"
+dependencies = [
+ "indexmap",
+ "toml_datetime",
+ "winnow",
+]
+
+[[package]]
+name = "tracing"
+version = "0.1.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
+dependencies = [
+ "pin-project-lite",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
+
+[[package]]
+name = "ttf-parser"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4"
+
+[[package]]
+name = "unicode-bidi"
+version = "0.3.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "unicode-normalization"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5"
+dependencies = [
+ "tinyvec",
+]
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
+
+[[package]]
+name = "url"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+]
+
+[[package]]
+name = "utf8parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "walkdir"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.42"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
+
+[[package]]
+name = "wayland-backend"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d50fa61ce90d76474c87f5fc002828d81b32677340112b4ef08079a9d459a40"
+dependencies = [
+ "cc",
+ "downcast-rs",
+ "rustix",
+ "scoped-tls",
+ "smallvec",
+ "wayland-sys",
+]
+
+[[package]]
+name = "wayland-client"
+version = "0.31.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82fb96ee935c2cea6668ccb470fb7771f6215d1691746c2d896b447a00ad3f1f"
+dependencies = [
+ "bitflags 2.5.0",
+ "rustix",
+ "wayland-backend",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-csd-frame"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e"
+dependencies = [
+ "bitflags 2.5.0",
+ "cursor-icon",
+ "wayland-backend",
+]
+
+[[package]]
+name = "wayland-cursor"
+version = "0.31.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "71ce5fa868dd13d11a0d04c5e2e65726d0897be8de247c0c5a65886e283231ba"
+dependencies = [
+ "rustix",
+ "wayland-client",
+ "xcursor",
+]
+
+[[package]]
+name = "wayland-protocols"
+version = "0.31.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4"
+dependencies = [
+ "bitflags 2.5.0",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-protocols-plasma"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23803551115ff9ea9bce586860c5c5a971e360825a0309264102a9495a5ff479"
+dependencies = [
+ "bitflags 2.5.0",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-protocols",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-protocols-wlr"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6"
+dependencies = [
+ "bitflags 2.5.0",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-protocols",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-scanner"
+version = "0.31.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63b3a62929287001986fb58c789dce9b67604a397c15c611ad9f747300b6c283"
+dependencies = [
+ "proc-macro2",
+ "quick-xml",
+ "quote",
+]
+
+[[package]]
+name = "wayland-sys"
+version = "0.31.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15a0c8eaff5216d07f226cb7a549159267f3467b289d9a2e52fd3ef5aae2b7af"
+dependencies = [
+ "dlib",
+ "log",
+ "once_cell",
+ "pkg-config",
+]
+
+[[package]]
+name = "web-sys"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "web-time"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa30049b1c872b72c89866d458eae9f20380ab280ffd1b1e18df2d3e2d98cfe0"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "webbrowser"
+version = "0.8.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db67ae75a9405634f5882791678772c94ff5f16a66535aae186e26aa0841fc8b"
+dependencies = [
+ "core-foundation",
+ "home",
+ "jni",
+ "log",
+ "ndk-context",
+ "objc",
+ "raw-window-handle 0.5.2",
+ "url",
+ "web-sys",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b"
+dependencies = [
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
+dependencies = [
+ "windows-targets 0.42.2",
+]
+
+[[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.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
+dependencies = [
+ "windows_aarch64_gnullvm 0.42.2",
+ "windows_aarch64_msvc 0.42.2",
+ "windows_i686_gnu 0.42.2",
+ "windows_i686_msvc 0.42.2",
+ "windows_x86_64_gnu 0.42.2",
+ "windows_x86_64_gnullvm 0.42.2",
+ "windows_x86_64_msvc 0.42.2",
+]
+
+[[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",
+]
+
+[[package]]
+name = "windows-targets"
+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_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",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
+
+[[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.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
+
+[[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.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
+
+[[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"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
+
+[[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.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
+
+[[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.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
+
+[[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.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
+
+[[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"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
+
+[[package]]
+name = "winit"
+version = "0.29.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d59ad965a635657faf09c8f062badd885748428933dad8e8bdd64064d92e5ca"
+dependencies = [
+ "ahash",
+ "android-activity",
+ "atomic-waker",
+ "bitflags 2.5.0",
+ "bytemuck",
+ "calloop",
+ "cfg_aliases",
+ "core-foundation",
+ "core-graphics",
+ "cursor-icon",
+ "icrate",
+ "js-sys",
+ "libc",
+ "log",
+ "memmap2",
+ "ndk",
+ "ndk-sys",
+ "objc2 0.4.1",
+ "once_cell",
+ "orbclient",
+ "percent-encoding",
+ "raw-window-handle 0.5.2",
+ "raw-window-handle 0.6.1",
+ "redox_syscall 0.3.5",
+ "rustix",
+ "smithay-client-toolkit",
+ "smol_str",
+ "unicode-segmentation",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-protocols",
+ "wayland-protocols-plasma",
+ "web-sys",
+ "web-time",
+ "windows-sys 0.48.0",
+ "x11-dl",
+ "x11rb",
+ "xkbcommon-dl",
+]
+
+[[package]]
+name = "winnow"
+version = "0.5.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "x11-dl"
+version = "2.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f"
+dependencies = [
+ "libc",
+ "once_cell",
+ "pkg-config",
+]
+
+[[package]]
+name = "x11rb"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12"
+dependencies = [
+ "as-raw-xcb-connection",
+ "gethostname",
+ "libc",
+ "libloading",
+ "once_cell",
+ "rustix",
+ "x11rb-protocol",
+]
+
+[[package]]
+name = "x11rb-protocol"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d"
+
+[[package]]
+name = "xcursor"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a0ccd7b4a5345edfcd0c3535718a4e9ff7798ffc536bb5b5a0e26ff84732911"
+
+[[package]]
+name = "xkbcommon-dl"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5"
+dependencies = [
+ "bitflags 2.5.0",
+ "dlib",
+ "log",
+ "once_cell",
+ "xkeysym",
+]
+
+[[package]]
+name = "xkeysym"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "054a8e68b76250b253f671d1268cb7f1ae089ec35e195b2efb2a4e9a836d0621"
+
+[[package]]
+name = "xml-rs"
+version = "0.8.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "791978798f0597cfc70478424c2b4fdc2b7a8024aaff78497ef00f24ef674193"
+
+[[package]]
+name = "zerocopy"
+version = "0.7.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "087eca3c1eaf8c47b94d02790dd086cd594b912d2043d4de4bfdd466b3befb7c"
+dependencies = [
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.7.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f4b6c273f496d8fd4eaf18853e6b448760225dc030ff2c485a786859aea6393"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
diff --git a/Cargo.toml b/Cargo.toml
index 1383d3f..6ebd16a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,8 +1,7 @@
-[package]
-name = "fpt"
-version = "0.1.0"
-edition = "2021"
+[workspace]
+resolver = "2"
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-
-[dependencies]
+members = [
+ "fpt",
+ "fpt-egui",
+]
diff --git a/README.md b/README.md
index ca3ecc4..c8d09c9 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,60 @@
-# supposedly some ~~forth implementation~~ gameboy color emulator
+# Supposedly some ~~forth implementation~~ gameboy color emulator
+## GUI
+
+Natively:
+
+`cargo run -p fpt-egui`
+
+WASM:
+
+```
+rustup target add wasm32-unknown-unknown
+cargo install trunk
+cd fpt-egui
+RUSTFLAGS='--cfg=web_sys_unstable_apis' trunk serve
+```
+
+## Testing
+
+`cargo test`
+
+## References
+
+### Opcodes
+
+- [the best table - gbdev](https://gbdev.io/gb-opcodes/optables/)
+- [pastraiser.com — Gameboy CPU (LR35902) instruction set](https://www.pastraiser.com/cpu/gameboy/gameboy_opcodes.html)
+- [RGBDS docs — gbz80(7) — CPU opcode reference](https://rgbds.gbdev.io/docs/v0.7.0/gbz80.7)
+- [Dinu, Christian — Decoding Z80 Opcodes](http://z80.info/decoding.htm)
+
+### Manuals
+
+- :book: [Gameboy Programming Manual v1.1](https://ia803208.us.archive.org/9/items/GameBoyProgManVer1.1/GameBoyProgManVer1.1.pdf)
+- :book: [Game Boy CPU Manual](http://marc.rawer.de/Gameboy/Docs/GBCPUman.pdf)
+- :book: [gekkio — Game Boy: Complete Technical Reference](https://gekkio.fi/files/gb-docs/gbctr.pdf)
+
+### Misc
+
+- [Game boy dev pandocs](https://gbdev.io/pandocs/)
+- [Copetti — Game Boy Architecture](https://www.copetti.org/writings/consoles/game-boy/)
+- [DMG-01: How to Emulate a Game Boy](https://rylev.github.io/DMG-01/public/book/cpu/introduction.html)
+- :movie_camera: [The Ultimate Game Boy Talk (33c3)](https://www.youtube.com/watch?v=HyzD8pNlpwI)
+- :movie_camera: The Game Boy, a hardware autopsy
+ ([Part 1](https://www.youtube.com/watch?v=RZUDEaLa5Nw),
+ [Part 1.5](https://www.youtube.com/watch?v=t0V-D2YMhrs),
+ [Part 2](https://www.youtube.com/watch?v=ecTQVa42sJc))
+- [Tetris disassembly](https://github.com/alexsteb/tetris_disassembly/blob/master/main.asm)
+
+## Resources
+
+### Test ROMs
+
+- [Blargg's tests](https://gbdev.gg8.se/wiki/articles/Test_ROMs)
+- [Mooneye Test Suite](https://github.com/Gekkio/mooneye-test-suite/tree/main)
+- [Wilbert Pol’s tests](https://github.com/wilbertpol/mooneye-gb/tree/master/tests/acceptance)
+
+### Debuggers
+
+- [BGB](https://bgb.bircd.org/)
+- [SameBoy](https://sameboy.github.io/)
diff --git a/bin/pre-commit b/bin/pre-commit
new file mode 100755
index 0000000..49bc634
--- /dev/null
+++ b/bin/pre-commit
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+set -euo pipefail
+FILES=$(git diff --cached --name-only --diff-filter=ACMR | sed 's| |\\ |g')
+cargo fmt
+cargo clippy --all-features
+echo "$FILES" | xargs git add
diff --git a/bin/setup-hooks b/bin/setup-hooks
new file mode 100755
index 0000000..1a7f3ff
--- /dev/null
+++ b/bin/setup-hooks
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+set -euo pipefail
+REPO=$(git rev-parse --show-toplevel)
+cp "$REPO/bin/pre-commit" "$(git rev-parse --git-path hooks)/pre-commit"
+chmod +x "$(git rev-parse --git-path hooks)/pre-commit"
diff --git a/fpt-egui/.gitignore b/fpt-egui/.gitignore
new file mode 100644
index 0000000..1521c8b
--- /dev/null
+++ b/fpt-egui/.gitignore
@@ -0,0 +1 @@
+dist
diff --git a/fpt-egui/Cargo.toml b/fpt-egui/Cargo.toml
new file mode 100644
index 0000000..d7cec2d
--- /dev/null
+++ b/fpt-egui/Cargo.toml
@@ -0,0 +1,21 @@
+[package]
+name = "fpt-egui"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+fpt = { path = "../fpt" }
+egui = "0.26.2"
+eframe = { version = "0.26.2", default-features = false, features = ["glow"] }
+log = "0.4.14"
+
+[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
+env_logger = "0.11.2"
+
+[target.'cfg(target_arch = "wasm32")'.dependencies]
+wasm-bindgen = "0.2.88"
+wasm-bindgen-futures = "0.4.38"
+js-sys = "0.3.65"
+web-sys = { version = "0.3.65", features = ["Performance"] }
diff --git a/fpt-egui/assets/favicon.ico b/fpt-egui/assets/favicon.ico
new file mode 100644
index 0000000..e69de29
diff --git a/fpt-egui/index.html b/fpt-egui/index.html
new file mode 100644
index 0000000..da67bae
--- /dev/null
+++ b/fpt-egui/index.html
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+ eframe template
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/fpt-egui/src/main.rs b/fpt-egui/src/main.rs
new file mode 100644
index 0000000..9567956
--- /dev/null
+++ b/fpt-egui/src/main.rs
@@ -0,0 +1,587 @@
+#![feature(lazy_cell)]
+#![feature(array_chunks)]
+
+use std::time::Duration;
+
+use eframe::Frame;
+use egui::{
+ menu, CentralPanel, Color32, ColorImage, Context, Grid, Key, RichText, ScrollArea, SidePanel,
+ TextureHandle, TextureOptions, TopBottomPanel, Ui, Vec2, ViewportBuilder, ViewportCommand,
+};
+use fpt::ppu::tile::Tile;
+use fpt::{bitwise, Gameboy};
+use log::info;
+
+// TODO: the gameboy doesn't run at exactly 60fps
+const SIXTY_FPS_FRAMETIME: f64 = 0.016666666667;
+const T_CYCLE: f64 = 0.0000002384185791015625;
+
+const TEXTURE_SCALE_FACTOR: f32 = 3.0;
+
+const GREY: Color32 = Color32::from_rgb(120, 120, 120);
+
+const WIDTH: usize = fpt::ppu::WIDTH;
+const HEIGHT: usize = fpt::ppu::HEIGHT;
+
+const PALETTE: [Color32; 4] = [
+ Color32::from_rgb(0, 63, 0),
+ Color32::from_rgb(46, 115, 32),
+ Color32::from_rgb(140, 191, 10),
+ Color32::from_rgb(160, 207, 10),
+];
+
+// Debug view Tile Viewer (TV)
+const TILE_SIZE: usize = fpt::ppu::tile::TILE_PIXEL_SIZE;
+const TV_BORDER_SIZE: usize = 1;
+const TV_COLS: usize = 16;
+const TV_NUM_VBORDERS: usize = TV_COLS + 1;
+const TV_ROWS: usize = 24;
+const TV_NUM_HBORDERS: usize = TV_ROWS + 1;
+const TV_X_SIZE: usize = TILE_SIZE * TV_COLS + TV_BORDER_SIZE * TV_NUM_VBORDERS;
+const TV_Y_SIZE: usize = TILE_SIZE * TV_ROWS + TV_BORDER_SIZE * TV_NUM_HBORDERS;
+const TV_TEXTURE_SCALE: f32 = 1.0;
+
+// Debug view Background Map Viewer (BMV)
+const BMV_BORDER_SIZE: usize = 1;
+const BMV_TILES_PER: usize = 32;
+const BMV_X_SIZE: usize = 256 + BMV_BORDER_SIZE * 2;
+const BMV_Y_SIZE: usize = 256 + BMV_BORDER_SIZE * 2;
+const BMV_TEXTURE_SCALE: f32 = 1.0;
+
+#[cfg(target_arch = "wasm32")]
+#[allow(dead_code)]
+fn now() -> f64 {
+ use wasm_bindgen::JsCast;
+ use wasm_bindgen::JsValue;
+ js_sys::Reflect::get(&js_sys::global(), &JsValue::from_str("performance"))
+ .expect("failed to get performance from global object")
+ .unchecked_into::()
+ .now()
+}
+
+#[cfg(not(target_arch = "wasm32"))]
+#[allow(dead_code)]
+static APP_START: std::sync::LazyLock =
+ std::sync::LazyLock::new(std::time::Instant::now);
+
+#[cfg(not(target_arch = "wasm32"))]
+#[allow(dead_code)]
+fn now() -> f64 {
+ APP_START.elapsed().as_secs_f64() * 1000.0
+}
+
+pub struct FPT {
+ gb: Gameboy,
+ cycles_since_last_frame: u32,
+ accum_time: f64,
+ egui_frame_count: u64,
+ gb_frame_count: u64,
+
+ paused: bool,
+ slow_factor: f64,
+
+ debug_console: Vec,
+ debug_console_cmd: String,
+ debug_console_last_cmd: String,
+ debug_console_was_focused: bool,
+
+ image: ColorImage,
+ texture: Option,
+
+ tiles: ColorImage,
+ tiles_texture: Option,
+
+ bg_map: ColorImage,
+ bg_map_texture: Option,
+}
+
+impl Default for FPT {
+ fn default() -> Self {
+ Self {
+ gb: Gameboy::new(),
+ cycles_since_last_frame: 0,
+ accum_time: 0.0,
+ egui_frame_count: 0,
+ gb_frame_count: 0,
+
+ paused: false,
+ slow_factor: 1.0,
+
+ debug_console: vec![],
+ debug_console_cmd: String::new(),
+ debug_console_last_cmd: String::new(),
+ debug_console_was_focused: false,
+
+ image: ColorImage::new([WIDTH, HEIGHT], Color32::TRANSPARENT),
+ texture: None,
+
+ tiles: ColorImage::new([TV_X_SIZE, TV_Y_SIZE], Color32::TRANSPARENT),
+ tiles_texture: None,
+
+ bg_map: ColorImage::new([BMV_X_SIZE, BMV_Y_SIZE], Color32::TRANSPARENT),
+ bg_map_texture: None,
+ }
+ }
+}
+
+impl FPT {
+ /// Called once before the first frame.
+ pub fn new(_cc: &eframe::CreationContext) -> Self {
+ let mut app = FPT::default();
+ #[cfg(not(target_arch = "wasm32"))]
+ if std::env::var("CI").is_err() {
+ const ROM_PATH: &str = "roms/Tetris_World_Rev_1.gb";
+ if let Ok(rom) = std::fs::read(ROM_PATH) {
+ app.gb.load_rom(&rom);
+ } else {
+ panic!("Unable to open {}", ROM_PATH);
+ }
+ }
+ app
+ }
+
+ fn top_panel(&mut self, ctx: &Context) {
+ #[cfg(not(target_arch = "wasm32"))]
+ TopBottomPanel::top("top_panel").show(ctx, |ui| {
+ menu::bar(ui, |ui| {
+ ui.menu_button("File", |ui| {
+ if ui.button("Quit").clicked() {
+ ctx.send_viewport_cmd(ViewportCommand::Close)
+ }
+ });
+ ui.add_space(16.0);
+ });
+ });
+ }
+
+ fn emulator(&mut self, ui: &mut Ui) {
+ self.egui_frame_count += 1;
+ let mut frame: Option = None;
+ let delta_time = ui.input(|i| i.unstable_dt) as f64;
+ self.accum_time += delta_time;
+
+ let cycles_want = self.accum_time.div_euclid(T_CYCLE * self.slow_factor) as u32;
+ let mut cycles_ran = 0;
+ while cycles_ran < cycles_want {
+ // TODO: care for double speed mode
+ self.gb.cpu_mut().t_cycle();
+ self.gb.ppu_mut().step(1);
+ self.cycles_since_last_frame += 1;
+ if self.cycles_since_last_frame == self.gb.cycles_in_one_frame() {
+ frame = Some(*self.gb.get_frame()); // Copies the whole [u8; WIDTH * HEIGHT] into frame
+ self.gb_frame_count += 1;
+ self.cycles_since_last_frame = 0;
+ }
+ cycles_ran += 1;
+ // if let Some(inst) = ran_inst {
+ // // TODO: check breakpoints
+ // // TODO: this breaks *after* the instruction has been executed
+ // }
+ }
+ self.accum_time -= cycles_ran as f64 * T_CYCLE * self.slow_factor;
+ if let Some(frame) = frame {
+ for (i, &gb_pixel) in frame.iter().enumerate() {
+ self.image.pixels[i] = PALETTE[gb_pixel as usize];
+ }
+ }
+ }
+
+ #[allow(dead_code)]
+ fn sleep(&mut self, ctx: &Context, frame_start: f64, gb_frame_count_before: u64) {
+ let mut _ccc = false;
+ if self.gb_frame_count - gb_frame_count_before > 1 {
+ info!("more than one gb_frame");
+ _ccc = true;
+ }
+ let b = now();
+ info!("a {:.8}", frame_start);
+ info!("b {:.8}", b);
+ let time_taken = (b - frame_start) / 1000.0;
+ info!("time_taken {:.8}", time_taken);
+ let time_taken = (time_taken * 1000.0).ceil() / 1000.0;
+ if _ccc {
+ info!("time_taken2 {:.8}", time_taken);
+ }
+ let sleep_time = SIXTY_FPS_FRAMETIME - time_taken;
+ info!("sleep_time {:.8}", sleep_time);
+ if sleep_time < 0.0 {
+ ctx.request_repaint();
+ } else {
+ // ctx.request_repaint_after(Duration::from_secs_f64(sleep_time - 0.005));
+ ctx.request_repaint_after(Duration::from_secs_f64(sleep_time));
+ }
+ }
+
+ #[allow(dead_code)]
+ fn timing_info(&self, ui: &mut Ui) {
+ Grid::new("my_grid").striped(true).show(ui, |ui| {
+ macro_rules! stat {
+ ($label:literal : $fmt:literal, $value:expr) => {
+ ui.colored_label(Color32::LIGHT_GRAY, $label);
+ ui.monospace(format!($fmt, $value));
+ ui.code(stringify!($value));
+ ui.end_row();
+ };
+ }
+ let time = ui.input(|i| i.time);
+ let delta_time = ui.input(|i| i.unstable_dt) as f64;
+ stat!("time" : "{:>9.3}" , time);
+ stat!("dt" : "{:>9.3}" , delta_time);
+ stat!("accum. time" : "{:>9.3}" , self.accum_time);
+ stat!("Ideal count" : "{:>9.3}" , time / SIXTY_FPS_FRAMETIME);
+ stat!("Frame count" : "{:>5}" , self.gb_frame_count);
+ stat!("UI updates" : "{:>5}" , self.egui_frame_count);
+ });
+ }
+
+ fn get_tile(&self, tile_i: usize) -> Tile {
+ let bus = self.gb.bus();
+ let lcdc4 = bitwise::test_bit8::<4>(bus.lcdc());
+ let tile_address = 16 * tile_i
+ + if lcdc4 || tile_i > 127 {
+ 0x8000
+ } else {
+ 0x8800
+ };
+ bus.with_span(tile_address, Tile::load)
+ }
+
+ fn debug_panel(&mut self, ctx: &Context, ui: &mut Ui) {
+ ui.collapsing("VRAM", |ui| {
+ ui.horizontal_wrapped(|ui| self.vram_viewer(ui));
+ ui.horizontal(|ui| self.vram_registers(ui));
+ });
+ ui.horizontal(|ui| {
+ if ui
+ .button(if self.paused { "Continue" } else { "Pause" })
+ .clicked()
+ {
+ self.paused = !self.paused;
+ }
+ ui.horizontal(|ui| {
+ ui.monospace("Slow factor:");
+ ui.radio_value(&mut self.slow_factor, 0.1f64, "0.1");
+ ui.radio_value(&mut self.slow_factor, 1f64, "1");
+ ui.radio_value(&mut self.slow_factor, 10f64, "10");
+ ui.radio_value(&mut self.slow_factor, 100_000f64, "100_000");
+ ui.radio_value(&mut self.slow_factor, 1_000_000f64, "1_000_000");
+ });
+ });
+ ui.horizontal_wrapped(|ui| {
+ macro_rules! cpu_register {
+ ($ui:expr, $high_label:literal : $high_value:expr, $low_label:literal : $low_value:expr) => {
+ $ui.colored_label(Color32::LIGHT_BLUE, $high_label);
+ $ui.monospace(format!("{:08b}", $high_value));
+ $ui.code(format!("{:04X}", bitwise::word16($high_value, $low_value)));
+ $ui.monospace(format!("{:08b}", $low_value));
+ $ui.colored_label(Color32::LIGHT_BLUE, $low_label);
+ }
+ }
+ let cpu = self.gb.cpu();
+ ui.vertical(|ui| {
+ Grid::new("cpu_registers_a-e").num_columns(4).min_col_width(10.0).striped(true).show(ui, |ui| {
+ ui.colored_label(Color32::LIGHT_BLUE, "A");
+ ui.monospace(format!("{:08b}", cpu.a()));
+ ui.code(format!("{:#04X}", cpu.a()));
+ ui.end_row();
+ cpu_register!(ui, "B": cpu.b(), "C": cpu.c()); ui.end_row();
+ cpu_register!(ui, "D": cpu.d(), "E": cpu.e()); ui.end_row();
+ cpu_register!(ui, "H": cpu.a(), "L": cpu.f()); ui.end_row();
+ });
+ });
+ ui.separator();
+ ui.vertical(|ui| {
+ Grid::new("flags").num_columns(1).min_col_width(10.0).striped(true).show(ui, |ui| {
+ ui.colored_label(Color32::LIGHT_BLUE, "Z");
+ ui.code(if cpu.z_flag() { "1" } else { "0" });
+ ui.colored_label(Color32::LIGHT_BLUE, "N");
+ ui.code(if cpu.n_flag() { "1" } else { "0" });
+ ui.end_row();
+ ui.colored_label(Color32::LIGHT_BLUE, "H");
+ ui.code(if cpu.h_flag() { "1" } else { "0" });
+ ui.colored_label(Color32::LIGHT_BLUE, "C");
+ ui.code(if cpu.c_flag() { "1" } else { "0" });
+ });
+ ui.horizontal(|ui| {
+ ui.colored_label(Color32::LIGHT_BLUE, "SP");
+ ui.code(format!("{:#06X}", cpu.sp()));
+ });
+ ui.horizontal(|ui| {
+ ui.colored_label(Color32::LIGHT_BLUE, "PC");
+ ui.code(format!("{:#06X}", cpu.pc()));
+ });
+ });
+ });
+ // TODO: scroll into line of current pc (need to find index)
+ // TODO: differentiate current pc
+ ui.collapsing("Code", |ui| {
+ let mem = self.gb.bus().memory();
+ let code_flat: Vec<&String> = mem.code_listing().iter().flatten().collect();
+ ScrollArea::vertical().show_rows(
+ ui,
+ ui.text_style_height(&egui::TextStyle::Body),
+ code_flat.len(),
+ |ui, row_range| {
+ for row in row_range {
+ ui.label(RichText::new(code_flat[row].clone()).monospace());
+ }
+ },
+ );
+ });
+ ui.collapsing("Console", |ui| {
+ ScrollArea::vertical()
+ .auto_shrink(false)
+ .stick_to_bottom(true)
+ // TODO: dirty hack to make the console input always stick to the bottom
+ .max_height(ui.available_rect_before_wrap().height() - 24.0)
+ .show_rows(
+ ui,
+ ui.text_style_height(&egui::TextStyle::Body),
+ self.debug_console.len(),
+ |ui, row_range| {
+ for row in row_range {
+ ui.label(RichText::new(self.debug_console[row].clone()).monospace());
+ }
+ },
+ );
+ let edit = egui::TextEdit::multiline(&mut self.debug_console_cmd)
+ .desired_rows(1)
+ .font(egui::TextStyle::Monospace)
+ .desired_width(f32::INFINITY);
+ let response = ui.add(edit);
+ if self.debug_console_was_focused {
+ response.request_focus();
+ self.debug_console_was_focused = false;
+ }
+ if response.has_focus() && ctx.input(|i| i.key_pressed(Key::Enter)) {
+ self.debug_console_was_focused = true;
+ self.debug_console_cmd = self.debug_console_cmd.trim().to_string();
+ if self.debug_console_cmd.is_empty() {
+ self.debug_console_cmd
+ .clone_from(&self.debug_console_last_cmd);
+ }
+ self.debug_console
+ .push(format!("> {}", self.debug_console_cmd));
+ if self.debug_console_cmd == "d" {
+ self.gb.cpu().decode_ahead(5).iter().for_each(|(pc, inst)| {
+ let args = self
+ .gb
+ .bus()
+ .copy_range((*pc as usize)..((pc + inst.size as u16) as usize))
+ .iter()
+ .fold(String::new(), |acc, &b| acc + &format!("{:#02X} ", b))
+ .trim()
+ .to_string();
+ self.debug_console
+ .push(format!("{:#06X}: {} ({})", pc, inst.mnemonic, args));
+ });
+ }
+ self.debug_console_last_cmd
+ .clone_from(&self.debug_console_cmd);
+ self.debug_console_cmd = String::new();
+ }
+ });
+ }
+
+ fn vram_registers(&mut self, ui: &mut Ui) {
+ let bus = self.gb.bus();
+ Grid::new("VRAM-registers-parent")
+ .striped(true)
+ .show(ui, |ui| {
+ ui.horizontal(|ui| {
+ Grid::new("VRAM-registers-1").striped(true).show(ui, |ui| {
+ ui.monospace("LCDC");
+ ui.monospace(format!("{:08b}", bus.lcdc()));
+ ui.end_row();
+ ui.monospace("STAT");
+ ui.monospace(format!("{:08b}", bus.stat()));
+ ui.end_row();
+ });
+ ui.separator();
+ });
+ ui.horizontal(|ui| {
+ Grid::new("VRAM-registers-2").striped(true).show(ui, |ui| {
+ ui.monospace("LY");
+ ui.monospace(format!("{:08b}", bus.ly()));
+ ui.end_row();
+ ui.monospace("LYC");
+ ui.monospace(format!("{:08b}", bus.lyc()));
+ ui.end_row();
+ });
+ ui.separator();
+ });
+ ui.horizontal(|ui| {
+ Grid::new("VRAM-registers-3").striped(true).show(ui, |ui| {
+ ui.monospace("SCX");
+ ui.monospace(format!("{:08b}", bus.scx()));
+ ui.end_row();
+ ui.monospace("SCY");
+ ui.monospace(format!("{:08b}", bus.scy()));
+ ui.end_row();
+ });
+ });
+ });
+ }
+
+ fn vram_viewer(&mut self, ui: &mut Ui) {
+ for tile_i in 0..fpt::ppu::tile::NUM_TILES {
+ let tile = self.get_tile(tile_i);
+ for y in 0..TILE_SIZE {
+ let yy =
+ y + (tile_i / TV_COLS + 1) * TV_BORDER_SIZE + (tile_i / TV_COLS) * TILE_SIZE;
+ for x in 0..TILE_SIZE {
+ let pixel = tile.get_pixel(y, x);
+ let xx = x
+ + (tile_i % TV_COLS + 1) * TV_BORDER_SIZE
+ + (tile_i % TV_COLS) * TILE_SIZE;
+ self.tiles[(xx, yy)] = PALETTE[pixel as usize];
+ }
+ }
+ }
+ for b in 0..TV_NUM_HBORDERS {
+ for y in 0..TV_BORDER_SIZE {
+ for x in 0..TV_X_SIZE {
+ self.tiles[(x, y + b * (TILE_SIZE + TV_BORDER_SIZE))] = GREY;
+ }
+ }
+ }
+ for b in 0..TV_NUM_VBORDERS {
+ for x in 0..TV_BORDER_SIZE {
+ for y in 0..TV_Y_SIZE {
+ self.tiles[(x + b * (TILE_SIZE + TV_BORDER_SIZE), y)] = GREY;
+ }
+ }
+ }
+ let texture: &mut TextureHandle = self.tiles_texture.get_or_insert_with(|| {
+ ui.ctx()
+ .load_texture("tile_viewer", self.tiles.clone(), TextureOptions::NEAREST)
+ });
+ texture.set(self.tiles.clone(), TextureOptions::NEAREST);
+ ui.vertical(|ui| {
+ ui.label("Tile data");
+ ui.image((texture.id(), TV_TEXTURE_SCALE * texture.size_vec2()));
+ });
+
+ let lcdc = self.gb.bus().lcdc();
+ let bg_map_area = match bitwise::test_bit8::<3>(lcdc) {
+ false => 0x9800..0x9C00,
+ true => 0x9C00..0xA000,
+ };
+ let bg_map_iter = bg_map_area.map(|addr| self.gb.bus().read(addr));
+
+ for (i, tile_i) in bg_map_iter.enumerate() {
+ let tile = self.get_tile(tile_i as usize);
+ for y in 0..TILE_SIZE {
+ let yy = y + (i / BMV_TILES_PER) * TILE_SIZE + BMV_BORDER_SIZE;
+ for x in 0..TILE_SIZE {
+ let pixel = tile.get_pixel(y, x);
+ let xx = x + (i % BMV_TILES_PER) * TILE_SIZE + BMV_BORDER_SIZE;
+ self.bg_map[(xx, yy)] = PALETTE[pixel as usize];
+ }
+ }
+ }
+ // clear edges of bg_map viewer
+ for x in 0..BMV_X_SIZE {
+ self.bg_map[(x, 0)] = Color32::TRANSPARENT;
+ self.bg_map[(x, BMV_Y_SIZE - 1)] = Color32::TRANSPARENT;
+ }
+ for y in 0..BMV_Y_SIZE {
+ self.bg_map[(0, y)] = Color32::TRANSPARENT;
+ self.bg_map[(BMV_X_SIZE - 1, y)] = Color32::TRANSPARENT;
+ }
+ let top = self.gb.bus().scy() as usize;
+ let left = self.gb.bus().scx() as usize;
+ let bottom = ((self.gb.bus().scy() as u16 + 143u16) % 256u16) as usize;
+ let right = ((self.gb.bus().scx() as u16 + 159u16) % 256u16) as usize;
+ let btop = top;
+ let bleft = left;
+ let bbottom = bottom + 2 * BMV_BORDER_SIZE;
+ let bright = right + 2 * BMV_BORDER_SIZE;
+ for x in bleft..(bright + 1) {
+ self.bg_map[(x, btop)] = GREY;
+ self.bg_map[(x, bbottom)] = GREY;
+ }
+ for y in btop..(bbottom + 1) {
+ self.bg_map[(bleft, y)] = GREY;
+ self.bg_map[(bright, y)] = GREY;
+ }
+ let texture: &mut TextureHandle = self.bg_map_texture.get_or_insert_with(|| {
+ ui.ctx().load_texture(
+ "bg_map_viewer",
+ self.bg_map.clone(),
+ TextureOptions::NEAREST,
+ )
+ });
+ texture.set(self.bg_map.clone(), TextureOptions::NEAREST);
+ ui.vertical(|ui| {
+ ui.label("BG Map");
+ ui.image((texture.id(), BMV_TEXTURE_SCALE * texture.size_vec2()));
+ });
+ }
+
+ fn central_panel(&mut self, ctx: &Context, ui: &mut Ui) {
+ if !self.paused {
+ self.emulator(ui);
+ }
+ // TODO repeated work in 1st repaint
+ // TODO: should be in new?
+ let texture: &mut TextureHandle = self.texture.get_or_insert_with(|| {
+ ui.ctx()
+ .load_texture("my-image", self.image.clone(), TextureOptions::NEAREST)
+ });
+ texture.set(self.image.clone(), TextureOptions::NEAREST);
+ ui.image((texture.id(), TEXTURE_SCALE_FACTOR * texture.size_vec2()));
+ // TODO: fix sleep timings for displays > 60hz. til then we burn cpu
+ // self.sleep(ctx, frame_start, gb_frame_count_before);
+ ctx.request_repaint();
+ }
+}
+
+impl eframe::App for FPT {
+ fn update(&mut self, ctx: &Context, _frame: &mut Frame) {
+ self.top_panel(ctx);
+ SidePanel::right("right_panel")
+ .resizable(true)
+ .show(ctx, |ui| {
+ // self.timing_info(ui);
+ self.debug_panel(ctx, ui);
+ });
+
+ CentralPanel::default().show(ctx, |ui| {
+ self.central_panel(ctx, ui);
+ });
+ }
+}
+
+#[cfg(not(target_arch = "wasm32"))]
+fn main() -> eframe::Result<()> {
+ // Log to stderr (if you run with `RUST_LOG=debug`).
+ env_logger::init();
+
+ let native_options = eframe::NativeOptions {
+ viewport: ViewportBuilder {
+ inner_size: Some(Vec2::new(950.0, 700.0)),
+ ..Default::default()
+ },
+ ..Default::default()
+ };
+ eframe::run_native("FPT", native_options, Box::new(|cc| Box::new(FPT::new(cc))))
+}
+
+#[cfg(target_arch = "wasm32")]
+fn main() {
+ eframe::WebLogger::init(log::LevelFilter::Debug).ok();
+
+ let web_options = eframe::WebOptions::default();
+
+ wasm_bindgen_futures::spawn_local(async {
+ eframe::WebRunner::new()
+ .start(
+ "the_canvas_id",
+ web_options,
+ Box::new(|cc| Box::new(FPT::new(cc))),
+ )
+ .await
+ .expect("failed to start eframe");
+ });
+}
diff --git a/fpt/Cargo.toml b/fpt/Cargo.toml
new file mode 100644
index 0000000..3ce17be
--- /dev/null
+++ b/fpt/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "fpt"
+version = "0.1.0"
+edition = "2021"
+
+[dev-dependencies]
+rstest = "0.18"
+
diff --git a/fpt/dmg0.asm b/fpt/dmg0.asm
new file mode 100644
index 0000000..70f7f78
--- /dev/null
+++ b/fpt/dmg0.asm
@@ -0,0 +1,235 @@
+INCLUDE "hardware.inc/hardware.inc"
+INCLUDE "header.inc"
+
+
+SECTION "Boot ROM", ROM0[$000]
+
+EntryPoint:
+ ld sp, hStackBottom
+
+ xor a
+ ld hl, $9FFF
+.clearVRAM
+ ld [hld], a
+ bit 7, h
+ jr nz, .clearVRAM
+
+ ld hl, rNR52
+ ld c, LOW(rNR11) ; CH1 length
+ ; Enable APU
+ ; This sets (roughly) all audio registers to 0
+ ld a, $80
+ ld [hld], a
+ ; hl = rNR51
+ ; Set CH1 duty cycle to 25%
+ ldh [c], a
+ inc c ; ld c, LOW(rNR11) ; CH1 envelope
+ ld a, $F3 ; Initial volume 15, 3 decreasing sweep
+ ldh [c], a
+ ; Route all channels to left speaker, CH2 and CH1 to right speaker
+ ld [hld], a
+ ; hl = rNR50
+ ; Set volume on both speakers to 7, disable VIN on both speakers
+ ld a, $77
+ ld [hl], a
+
+ ld a, $FC
+ ldh [rBGP], a
+
+ ld hl, HeaderLogo
+ push hl
+ ld de, Logo
+.checkLogo
+ ld a, [de]
+ inc de
+ cp [hl]
+ jr nz, Lockup
+ inc hl
+ ld a, l
+ cp LOW(HeaderTitle)
+ jr nz, .checkLogo
+ ld b, HeaderChecksum - HeaderTitle
+ ld a, b
+.computeChecksum
+ add a, [hl]
+ inc hl
+ dec b
+ jr nz, .computeChecksum
+ add a, [hl]
+ jr nz, Lockup
+ pop de ; ld de, HeaderLogo
+ ld hl, vLogoTiles
+.decompressLogo
+ ld a, [de]
+ call DecompressFirstNibble
+ call DecompressSecondNibble
+ inc de
+ ld a, e
+ cp LOW(HeaderTitle)
+ jr nz, .decompressLogo
+
+ ld a, $18
+
+ ld hl, vMainTilemap + SCRN_VX_B * 9 + 15
+.writeTilemapRow
+ ld c, 12
+.writeTilemapByte
+ ld [hld], a
+ dec a
+ jr z, ScrollLogo
+ dec c
+ jr nz, .writeTilemapByte
+ ; Go to previous row
+ ld de, -(SCRN_VX_B - 12)
+ add hl, de
+ jr .writeTilemapRow
+
+
+ScrollLogo:
+ ; a = 0
+ ld h, a ; ld h, 0
+ ld a, $64
+ ld d, a
+ ldh [rSCY], a
+ ld a, LCDCF_ON | LCDCF_BLK01 | LCDCF_BGON
+ ldh [rLCDC], a
+ inc b ; ld b, 1
+
+ ; h = Number of times the logo was scrolled up
+ ; d = How many frames before exiting the loop
+ ; b = Whether to scroll the logo
+
+.loop
+ ld e, 2
+ call DelayFrames
+ ld c, LOW(rNR13) ; CH1 frequency low byte
+ inc h
+ ld a, h
+ ld e, $83
+ cp $62
+ jr z, .playSound
+ ld e, $C1
+ cp $64
+ jr nz, .dontPlaySound
+.playSound
+ ld a, e
+ ldh [c], a
+ inc c ; ld c, LOW(rNR14) ; CH1 frequency high byte
+ ; Set frequency to $7XX and restart channel
+ ld a, $87
+ ldh [c], a
+.dontPlaySound
+ ldh a, [rSCY]
+ sub b
+ ldh [rSCY], a
+ dec d
+ jr nz, .loop
+
+ dec b
+ jr nz, Done
+ ld d, $20
+ jr .loop
+
+
+Lockup:
+ ld a, LCDCF_ON | LCDCF_BLK01 | LCDCF_BGON
+ ldh [rLCDC], a
+.loop
+ ld e, 20
+ call DelayFrames
+ ldh a, [rBGP]
+ xor a, $FF
+ ldh [rBGP], a
+ jr .loop
+
+
+DecompressFirstNibble:
+ ld c, a
+DecompressSecondNibble:
+ ld b, 8 / 2 ; Set all 8 bits of a, "consuming" 4 bits of c
+.loop
+ push bc
+ rl c ; Extract MSB of c
+ rla ; Into LSB of a
+ pop bc
+ rl c ; Extract that same bit
+ rla ; So that bit is inserted twice in a (= horizontally doubled)
+ dec b
+ jr nz, .loop
+ ld [hli], a
+ inc hl ; Skip second plane
+ ld [hli], a ; Also double vertically
+ inc hl
+ ret
+
+
+DelayFrames:
+ ld c, 12
+.loop
+ ldh a, [rLY]
+ cp SCRN_Y
+ jr nz, .loop
+ dec c
+ jr nz, .loop
+ dec e
+ jr nz, DelayFrames
+ ret
+
+
+; Each tile is encoded using 2 (!) bytes
+; How to read: the logo is split into two halves (top and bottom), each half being encoded
+; separately. Each half must be read in columns.
+; So, the first byte is `db %XX.._XXX.`, then `db %XXX._XX.X`, matching the
+; `db $CE, $ED` found in many places. And so on! :)
+MACRO logo_row_gfx
+ ASSERT _NARG % 4 == 0
+ PUSHO
+ OPT b.X
+ FOR N1, 1, _NARG / 4 + 1 ; N1, N2, N3, and N4 iterate through the 4 equally-sized rows
+ DEF N2 = N1 + _NARG / 4
+ DEF N3 = N2 + _NARG / 4
+ DEF N4 = N3 + _NARG / 4
+ db %\\, %\\
+ ENDR
+ POPO
+ENDM
+
+; Whitespace is not stripped after line continuations until RGBDS v0.6.0, so rows are not indented
+ Logo: logo_row_gfx \
+XX.., .XX., XX.., ...., ...., ...., ...., ...., ...., ...X, X..., ...., \
+XXX., .XX., XX.., ...., ..XX, ...., ...., ...., ...., ...X, X..., ...., \
+XXX., .XX., ...., ...., .XXX, X..., ...., ...., ...., ...X, X..., ...., \
+XX.X, .XX., XX.X, X.XX, ..XX, ..XX, XX.., XX.X, X..., XXXX, X..X, XXX.
+ logo_row_gfx \
+XX.X, .XX., XX.X, XX.X, X.XX, .XX., .XX., XXX., XX.X, X..X, X.XX, ..XX, \
+XX.., XXX., XX.X, X..X, X.XX, .XXX, XXX., XX.., XX.X, X..X, X.XX, ..XX, \
+XX.., XXX., XX.X, X..X, X.XX, .XX., ...., XX.., XX.X, X..X, X.XX, ..XX, \
+XX.., .XX., XX.X, X..X, X.XX, ..XX, XXX., XX.., XX.., XXXX, X..X, XXX.
+
+
+ ds 2
+Done:
+ inc a
+ ldh [$FF50], a
+ assert @ == $100 ; Execution now falls through to the cartridge's header
+
+
+SECTION "VRAM tiles", VRAM[$8000],BANK[0]
+
+vBlankTile:
+ ds $10
+vLogoTiles:
+ ds $10 * (HeaderTitle - HeaderLogo) / 2
+vRTile:
+ ds $10
+
+SECTION "VRAM tilemap", VRAM[$9800],BANK[0]
+
+vMainTilemap:
+ ds SCRN_VX_B * SCRN_VY_B
+
+
+SECTION "HRAM", HRAM[$FFEE]
+
+ ds $10
+hStackBottom:
diff --git a/fpt/dmg0.bin b/fpt/dmg0.bin
new file mode 100644
index 0000000..a558044
Binary files /dev/null and b/fpt/dmg0.bin differ
diff --git a/fpt/dmg0.disasm b/fpt/dmg0.disasm
new file mode 100644
index 0000000..c4908ff
--- /dev/null
+++ b/fpt/dmg0.disasm
@@ -0,0 +1,121 @@
+0x0: LD SP,d16 (0x31 0xFE 0xFF)
+0x3: XOR A (0xAF)
+0x4: LD HL,d16 (0x21 0xFF 0x9F)
+0x7: LD (HL-),A (0x32)
+0x8: PREFIX CB (0xCB)
+0x9: LD A,H (0x7C)
+0xA: JR NZ,r8 (0x20 0xFB)
+0xC: LD HL,d16 (0x21 0x26 0xFF)
+0xF: LD C,d8 (0xE 0x11)
+0x11: LD A,d8 (0x3E 0x80)
+0x13: LD (HL-),A (0x32)
+0x14: LD (C),A (0xE2)
+0x15: INC C (0xC)
+0x16: LD A,d8 (0x3E 0xF3)
+0x18: LD (C),A (0xE2)
+0x19: LD (HL-),A (0x32)
+0x1A: LD A,d8 (0x3E 0x77)
+0x1C: LD (HL),A (0x77)
+0x1D: LD A,d8 (0x3E 0xFC)
+0x1F: LDH (a8),A (0xE0 0x47)
+0x21: LD HL,d16 (0x21 0x4 0x1)
+0x24: PUSH HL (0xE5)
+0x25: LD DE,d16 (0x11 0xCB 0x0)
+0x28: LD A,(DE) (0x1A)
+0x29: INC DE (0x13)
+0x2A: CP (HL) (0xBE)
+0x2B: JR NZ,r8 (0x20 0x6B)
+0x2D: INC HL (0x23)
+0x2E: LD A,L (0x7D)
+0x2F: CP d8 (0xFE 0x34)
+0x31: JR NZ,r8 (0x20 0xF5)
+0x33: LD B,d8 (0x6 0x19)
+0x35: LD A,B (0x78)
+0x36: ADD A,(HL) (0x86)
+0x37: INC HL (0x23)
+0x38: DEC B (0x5)
+0x39: JR NZ,r8 (0x20 0xFB)
+0x3B: ADD A,(HL) (0x86)
+0x3C: JR NZ,r8 (0x20 0x5A)
+0x3E: POP DE (0xD1)
+0x3F: LD HL,d16 (0x21 0x10 0x80)
+0x42: LD A,(DE) (0x1A)
+0x43: CALL a16 (0xCD 0xA9 0x0)
+0x46: CALL a16 (0xCD 0xAA 0x0)
+0x49: INC DE (0x13)
+0x4A: LD A,E (0x7B)
+0x4B: CP d8 (0xFE 0x34)
+0x4D: JR NZ,r8 (0x20 0xF3)
+0x4F: LD A,d8 (0x3E 0x18)
+0x51: LD HL,d16 (0x21 0x2F 0x99)
+0x54: LD C,d8 (0xE 0xC)
+0x56: LD (HL-),A (0x32)
+0x57: DEC A (0x3D)
+0x58: JR Z,r8 (0x28 0x9)
+0x5A: DEC C (0xD)
+0x5B: JR NZ,r8 (0x20 0xF9)
+0x5D: LD DE,d16 (0x11 0xEC 0xFF)
+0x60: ADD HL,DE (0x19)
+0x61: JR r8 (0x18 0xF1)
+0x63: LD H,A (0x67)
+0x64: LD A,d8 (0x3E 0x64)
+0x66: LD D,A (0x57)
+0x67: LDH (a8),A (0xE0 0x42)
+0x69: LD A,d8 (0x3E 0x91)
+0x6B: LDH (a8),A (0xE0 0x40)
+0x6D: INC B (0x4)
+0x6E: LD E,d8 (0x1E 0x2)
+0x70: CALL a16 (0xCD 0xBC 0x0)
+0x73: LD C,d8 (0xE 0x13)
+0x75: INC H (0x24)
+0x76: LD A,H (0x7C)
+0x77: LD E,d8 (0x1E 0x83)
+0x79: CP d8 (0xFE 0x62)
+0x7B: JR Z,r8 (0x28 0x6)
+0x7D: LD E,d8 (0x1E 0xC1)
+0x7F: CP d8 (0xFE 0x64)
+0x81: JR NZ,r8 (0x20 0x6)
+0x83: LD A,E (0x7B)
+0x84: LD (C),A (0xE2)
+0x85: INC C (0xC)
+0x86: LD A,d8 (0x3E 0x87)
+0x88: LD (C),A (0xE2)
+0x89: LDH A,(a8) (0xF0 0x42)
+0x8B: SUB B (0x90)
+0x8C: LDH (a8),A (0xE0 0x42)
+0x8E: DEC D (0x15)
+0x8F: JR NZ,r8 (0x20 0xDD)
+0x91: DEC B (0x5)
+0x92: JR NZ,r8 (0x20 0x69)
+0x94: LD D,d8 (0x16 0x20)
+0x96: JR r8 (0x18 0xD6)
+0x98: LD A,d8 (0x3E 0x91)
+0x9A: LDH (a8),A (0xE0 0x40)
+0x9C: LD E,d8 (0x1E 0x14)
+0x9E: CALL a16 (0xCD 0xBC 0x0)
+0xA1: LDH A,(a8) (0xF0 0x47)
+0xA3: XOR d8 (0xEE 0xFF)
+0xA5: LDH (a8),A (0xE0 0x47)
+0xA7: JR r8 (0x18 0xF3)
+0xA9: LD C,A (0x4F)
+0xAA: LD B,d8 (0x6 0x4)
+0xAC: PUSH BC (0xC5)
+0xAD: PREFIX CB (0xCB)
+0xAE: LD DE,d16 (0x11 0x17 0xC1)
+0xB1: PREFIX CB (0xCB)
+0xB2: LD DE,d16 (0x11 0x17 0x5)
+0xB5: JR NZ,r8 (0x20 0xF5)
+0xB7: LD (HL+),A (0x22)
+0xB8: INC HL (0x23)
+0xB9: LD (HL+),A (0x22)
+0xBA: INC HL (0x23)
+0xBB: RET (0xC9)
+0xBC: LD C,d8 (0xE 0xC)
+0xBE: LDH A,(a8) (0xF0 0x44)
+0xC0: CP d8 (0xFE 0x90)
+0xC2: JR NZ,r8 (0x20 0xFA)
+0xC4: DEC C (0xD)
+0xC5: JR NZ,r8 (0x20 0xF7)
+0xC7: DEC E (0x1D)
+0xC8: JR NZ,r8 (0x20 0xF2)
+0xCA: RET (0xC9)
diff --git a/fpt/extract.js b/fpt/extract.js
new file mode 100644
index 0000000..e4cdbf8
--- /dev/null
+++ b/fpt/extract.js
@@ -0,0 +1,66 @@
+const ops = (cb) => {
+ let table = document.querySelector(`body > table:nth-child(${cb?10:4})`)
+ return [...table.querySelectorAll('td')].map((x) => {
+ let kind = "";
+ let bgcolor = document.defaultView.getComputedStyle(x, null).getPropertyValue('background-color');
+ switch (bgcolor) {
+ case 'rgb(255, 153, 204)':
+ kind = 'InstructionKind::Control';
+ break;
+ case 'rgb(255, 204, 153)':
+ kind = 'InstructionKind::Jump';
+ break;
+ case 'rgb(204, 204, 255)':
+ kind = 'InstructionKind::LSM8Bit';
+ break;
+ case 'rgb(204, 255, 204)':
+ kind = 'InstructionKind::LSM16Bit';
+ break;
+ case 'rgb(255, 255, 153)':
+ kind = 'InstructionKind::AL8Bit';
+ break;
+ case 'rgb(255, 204, 204)':
+ kind = 'InstructionKind::AL16Bit';
+ break;
+ case 'rgb(128, 255, 255)':
+ kind = 'InstructionKind::RSB8Bit';
+ break;
+ }
+ if (x.innerHTML === ' ') {
+ return ['NI', 0, 0, 0, cb, 'InstructionKind::NI'];
+ }
+ x = x.innerText.split('\n');
+ if (/[A-Z]/.test(x[0][0])) {
+ let a = x[1].split('\u00A0')
+ let size = a[0]
+ let cycles = a[2];
+ let cycles_not_taken = 0;
+ if (a[2].includes('/')) {
+ [cycles, cycles_not_taken] = a[2].split('/')
+ }
+ if (cb) {
+ // table includes cycles and size of the `PREFIX CB` instruction.
+ // internally we also count `PREFIX CB` individually, so subtract it.
+ cycles -= 4
+ size -= 1
+ }
+ return [x[0], size, cycles, cycles_not_taken, cb, kind]
+ }
+ return undefined;
+ }).filter((x) => (x !== undefined)).slice(1)
+}
+
+const instruction = (x, i) => {
+ console.log(`Instruction { opcode: 0x${x[4]?'1':''}${i.toString(16).padStart(2,'0').toUpperCase()}, mnemonic: "${x[0]}", size: ${x[1]}, cycles: ${x[2]}, cycles_not_taken: ${x[3]}, kind: ${x[5]} },`);
+}
+
+ops(false).forEach((x, i) => instruction(x, i));
+ops(true).forEach((x, i) => instruction(x, i));
+
+//const match = (x, i) => {
+// console.log(`0x${x[4]?'1':''}${i.toString(16).padStart(2,'0').toUpperCase()} => {\n// ${x[0]}\nunimplemented!()}`);
+//}
+//
+//ops(false).forEach((x, i) => match(x, i));
+//ops(true).forEach((x, i) => match(x, i));
+//
diff --git a/fpt/screenshots/ffmpeg-script/ffmpeg_geq_gb.ps1 b/fpt/screenshots/ffmpeg-script/ffmpeg_geq_gb.ps1
new file mode 100644
index 0000000..adf18d1
--- /dev/null
+++ b/fpt/screenshots/ffmpeg-script/ffmpeg_geq_gb.ps1
@@ -0,0 +1,19 @@
+$out = 'why_do_i_do_this.mp4'
+
+$filter = @'
+ geq=r='if(lt(p(X,Y),191),if(lt(p(X,Y),128),if(lt(p(X,Y),64), 0, 46), 140), 160)'
+ :g='if(lt(p(X,Y),191),if(lt(p(X,Y),128),if(lt(p(X,Y),64), 63, 115), 191), 207)'
+ :b='if(lt(p(X,Y),191),if(lt(p(X,Y),128),if(lt(p(X,Y),64), 0, 32), 10), 10)',
+ scale=iw*5:ih*5:flags=neighbor
+'@
+
+if (Test-Path $out) { del $out }
+
+ffmpeg -hide_banner -y `
+ -framerate 120 `
+ -i '../test_one_tile_to_vram-ly_%5d.pgm' `
+ -vf "$filter" `
+ -c:v libx264 `
+ $out
+
+if (Test-Path $out) { start $out }
diff --git a/fpt/src/bitwise.rs b/fpt/src/bitwise.rs
new file mode 100644
index 0000000..3e51002
--- /dev/null
+++ b/fpt/src/bitwise.rs
@@ -0,0 +1,70 @@
+pub fn get_byte16(word: u16) -> u8 {
+ ((word >> (8 * INDEX)) & 0xFF) as u8
+}
+
+pub fn test_bit8(word: u8) -> bool {
+ let mask: u8 = 1 << INDEX;
+ word & mask == mask
+}
+
+pub fn test_bit16(word: u16) -> bool {
+ let mask: u16 = 1 << INDEX;
+ word & mask == mask
+}
+
+#[allow(unused)]
+pub fn get_bit8(word: u8) -> u8 {
+ (word >> INDEX) & 0x1
+}
+
+pub fn set_bit8(word: u8, value: bool) -> u8 {
+ word & !(1 << INDEX) | (u8::from(value) << INDEX)
+}
+
+pub fn set_bit16(word: u16, value: bool) -> u16 {
+ word & !(1 << INDEX) | (u16::from(value) << INDEX)
+}
+
+pub fn set_byte16(word: u16, byte: u8) -> u16 {
+ let mask = 0xFF << (INDEX * 8);
+ let word = word & !mask;
+ word | ((byte as u16) << (INDEX * 8))
+}
+
+pub fn word16(msb: u8, lsb: u8) -> u16 {
+ ((msb as u16) << 8) | (lsb as u16)
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_get_byte16() {
+ assert_eq!(get_byte16::<1>(0xABDC), 0xAB);
+ }
+
+ #[test]
+ #[allow(clippy::bool_assert_comparison)]
+ fn test_test_bit16() {
+ assert_eq!(test_bit16::<0>(0x1234), false);
+ assert_eq!(test_bit16::<1>(0x1234), false);
+ assert_eq!(test_bit16::<2>(0x1234), true);
+ }
+
+ #[test]
+ #[allow(clippy::bool_assert_comparison)]
+ fn test_set_bit16() {
+ assert_eq!(set_bit16::<0>(0x0000, true), 0x0001);
+ assert_eq!(set_bit16::<1>(0x0000, true), 0x0002);
+ assert_eq!(set_bit16::<8>(0x0101, false), 0x0001);
+ }
+
+ #[test]
+ fn test_set_byte16() {
+ assert_eq!(set_byte16::<0>(0x0000, 0xAB), 0x00AB);
+ assert_eq!(set_byte16::<1>(0x0000, 0xAB), 0xAB00);
+ assert_eq!(set_byte16::<0>(0xABDC, 0xAB), 0xABAB);
+ assert_eq!(set_byte16::<1>(0xABCD, 0xAB), 0xABCD);
+ }
+}
diff --git a/fpt/src/lib.rs b/fpt/src/lib.rs
new file mode 100644
index 0000000..dac317c
--- /dev/null
+++ b/fpt/src/lib.rs
@@ -0,0 +1,90 @@
+#![feature(bigint_helper_methods)]
+#![feature(exclusive_range_pattern)]
+#![feature(array_chunks)]
+#![feature(iter_intersperse)]
+#![feature(new_uninit)]
+#![feature(ptr_as_uninit)]
+
+use lr35902::LR35902;
+use memory::Bus;
+use ppu::{Frame, Ppu, DOTS_IN_ONE_FRAME};
+
+pub mod bitwise;
+pub mod lr35902;
+pub mod memory;
+pub mod ppu;
+
+pub struct Gameboy {
+ bus: Bus,
+ cpu: LR35902,
+ ppu: Ppu,
+}
+
+impl Gameboy {
+ #[allow(clippy::new_without_default)]
+ pub fn new() -> Self {
+ let bus = Bus::new();
+ Self {
+ bus: bus.clone(),
+ cpu: LR35902::new(bus.clone()),
+ ppu: Ppu::new(bus),
+ }
+ }
+
+ pub fn unsafely_optimized_new() -> Self {
+ let bus = Bus::unsafely_optimized_new();
+ Self {
+ bus: bus.clone(),
+ cpu: LR35902::new(bus.clone()),
+ ppu: Ppu::new(bus),
+ }
+ }
+
+ pub fn load_rom(&mut self, rom: &[u8]) {
+ self.bus.load_cartridge(rom);
+ }
+
+ pub fn bus(&self) -> &Bus {
+ &self.bus
+ }
+
+ pub fn cpu(&self) -> &LR35902 {
+ &self.cpu
+ }
+
+ pub fn cpu_mut(&mut self) -> &mut LR35902 {
+ &mut self.cpu
+ }
+
+ pub fn ppu(&self) -> &Ppu {
+ &self.ppu
+ }
+ pub fn ppu_mut(&mut self) -> &mut Ppu {
+ &mut self.ppu
+ }
+
+ pub fn instruction(&mut self) -> u32 {
+ let cycles = self.cpu.instruction() as u32;
+ // TODO: care for double speed mode (need to run half as much dots)
+ self.ppu.step(cycles);
+ cycles
+ }
+
+ pub fn advance_frame(&mut self) -> &Frame {
+ for _ in 0..DOTS_IN_ONE_FRAME {
+ // TODO: care for double speed mode (need to run two cpu t_cycles)
+ self.cpu.t_cycle();
+ self.ppu.step(1);
+ }
+ self.ppu.get_frame()
+ }
+
+ pub fn get_frame(&self) -> &Frame {
+ self.ppu.get_frame()
+ }
+
+ pub fn cycles_in_one_frame(&self) -> u32 {
+ // TODO: care for double speed mode
+ DOTS_IN_ONE_FRAME
+ }
+}
diff --git a/fpt/src/lr35902.rs b/fpt/src/lr35902.rs
new file mode 100644
index 0000000..0fa7512
--- /dev/null
+++ b/fpt/src/lr35902.rs
@@ -0,0 +1,2916 @@
+use std::fmt;
+
+use instructions::{Instruction, InstructionKind, INSTRUCTIONS};
+
+use super::memory::{Address, Bus};
+use crate::{bitwise as bw, memory};
+
+pub mod instructions;
+
+#[derive(Clone, PartialEq)]
+pub struct LR35902 {
+ af: u16,
+ bc: u16,
+ de: u16,
+ hl: u16,
+ sp: u16,
+ pc: u16,
+ ime: bool,
+ imenc: bool,
+ mem: Bus,
+ prefix_cb: bool,
+ clock_cycles: u64,
+ inst_cycle_count: u8,
+ branch_taken: bool,
+}
+
+impl Default for LR35902 {
+ fn default() -> Self {
+ Self::new(Bus::new())
+ }
+}
+
+impl fmt::Debug for LR35902 {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "LR35902 {{ a: {:#04X}, f: {:#06b}, bc: {:#06X}, de: {:#06X}, hl: {:#06X}, sp: {:#06X}, pc: {:#06X}, clock_cycles: {} }} ", self.a(), self.f() >> 4, self.bc, self.de, self.hl, self.sp, self.pc, self.clock_cycles)
+ }
+}
+
+impl LR35902 {
+ pub fn new(memory: Bus) -> Self {
+ let mut cpu = Self {
+ af: 0,
+ bc: 0,
+ de: 0,
+ hl: 0,
+ sp: 0,
+ pc: 0,
+ ime: false,
+ imenc: false,
+ mem: memory,
+ prefix_cb: false,
+ clock_cycles: 0,
+ inst_cycle_count: 0,
+ branch_taken: false,
+ };
+ // TODO: should be done elsewhere, separation of concerns yada-yada?
+ cpu.mem.load_bootrom();
+ cpu
+ }
+
+ // Registers
+ pub fn a(&self) -> u8 {
+ bw::get_byte16::<1>(self.af)
+ }
+
+ pub fn set_a(&mut self, value: u8) {
+ self.af = bw::set_byte16::<1>(self.af, value);
+ }
+
+ pub fn f(&self) -> u8 {
+ bw::get_byte16::<0>(self.af)
+ }
+
+ pub fn set_f(&mut self, value: u8) {
+ self.af = bw::set_byte16::<0>(self.af, value);
+ }
+
+ pub fn af(&self) -> u16 {
+ self.af
+ }
+
+ pub fn set_af(&mut self, af: u16) {
+ self.af = af;
+ }
+
+ pub fn b(&self) -> u8 {
+ bw::get_byte16::<1>(self.bc)
+ }
+
+ pub fn set_b(&mut self, value: u8) {
+ self.bc = bw::set_byte16::<1>(self.bc, value);
+ }
+
+ pub fn c(&self) -> u8 {
+ bw::get_byte16::<0>(self.bc)
+ }
+
+ pub fn set_c(&mut self, value: u8) {
+ self.bc = bw::set_byte16::<0>(self.bc, value);
+ }
+
+ pub fn bc(&self) -> u16 {
+ self.bc
+ }
+
+ pub fn set_bc(&mut self, bc: u16) {
+ self.bc = bc;
+ }
+
+ pub fn d(&self) -> u8 {
+ bw::get_byte16::<1>(self.de)
+ }
+
+ pub fn set_d(&mut self, value: u8) {
+ self.de = bw::set_byte16::<1>(self.de, value);
+ }
+
+ pub fn e(&self) -> u8 {
+ bw::get_byte16::<0>(self.de)
+ }
+
+ pub fn set_e(&mut self, value: u8) {
+ self.de = bw::set_byte16::<0>(self.de, value);
+ }
+
+ pub fn de(&self) -> u16 {
+ self.de
+ }
+
+ pub fn set_de(&mut self, de: u16) {
+ self.de = de;
+ }
+
+ pub fn h(&self) -> u8 {
+ bw::get_byte16::<1>(self.hl)
+ }
+
+ pub fn set_h(&mut self, value: u8) {
+ self.hl = bw::set_byte16::<1>(self.hl, value);
+ }
+
+ pub fn l(&self) -> u8 {
+ bw::get_byte16::<0>(self.hl)
+ }
+
+ pub fn set_l(&mut self, value: u8) {
+ self.hl = bw::set_byte16::<0>(self.hl, value);
+ }
+
+ pub fn hl(&self) -> u16 {
+ self.hl
+ }
+
+ pub fn set_hl(&mut self, hl: u16) {
+ self.hl = hl;
+ }
+
+ pub fn sp(&self) -> u16 {
+ self.sp
+ }
+
+ pub fn set_sp(&mut self, sp: u16) {
+ self.sp = sp;
+ }
+
+ // flags
+ pub fn z_flag(&self) -> bool {
+ bw::test_bit16::<7>(self.af)
+ }
+
+ pub fn set_z_flag(&mut self, value: bool) {
+ self.af = bw::set_bit16::<7>(self.af, value);
+ }
+
+ pub fn n_flag(&self) -> bool {
+ bw::test_bit16::<6>(self.af)
+ }
+
+ pub fn set_n_flag(&mut self, value: bool) {
+ self.af = bw::set_bit16::<6>(self.af, value);
+ }
+
+ pub fn h_flag(&self) -> bool {
+ bw::test_bit16::<5>(self.af)
+ }
+
+ pub fn set_h_flag(&mut self, value: bool) {
+ self.af = bw::set_bit16::<5>(self.af, value);
+ }
+
+ pub fn c_flag(&self) -> bool {
+ bw::test_bit16::<4>(self.af)
+ }
+
+ pub fn set_c_flag(&mut self, value: bool) {
+ self.af = bw::set_bit16::<4>(self.af, value);
+ }
+
+ // other getters/setters
+ pub fn pc(&self) -> u16 {
+ self.pc
+ }
+
+ pub fn set_pc(&mut self, pc: u16) {
+ self.pc = pc;
+ }
+
+ pub fn interrupt_master_enable(&self) -> bool {
+ self.ime
+ }
+
+ pub fn set_interrupt_master_enable(&mut self, ime: bool) {
+ self.ime = ime;
+ }
+
+ pub fn set_interrupt_master_enable_next_instruction(&mut self) {
+ self.imenc = true;
+ }
+
+ pub fn clock_cycles(&self) -> u64 {
+ self.clock_cycles
+ }
+
+ pub fn set_clock_cycles(&mut self, clock_cycles: u64) {
+ self.clock_cycles = clock_cycles;
+ }
+
+ pub fn mutated_pc(&self) -> bool {
+ self.branch_taken
+ }
+
+ pub fn set_mutated_pc(&mut self, branch_taken: bool) {
+ self.branch_taken = branch_taken;
+ }
+
+ pub fn inst_cycle_count(&self) -> u8 {
+ self.inst_cycle_count
+ }
+
+ pub fn set_inst_cycle_count(&mut self, inst_cycle_count: u8) {
+ self.inst_cycle_count = inst_cycle_count;
+ }
+
+ // Memory
+ pub fn mem8(&self, index: u16) -> u8 {
+ self.mem.memory()[index as Address]
+ }
+
+ pub fn mem16(&self, index: u16) -> u16 {
+ bw::word16(self.mem8(index + 1), self.mem8(index))
+ }
+
+ pub fn set_mem8(&mut self, index: u16, value: u8) {
+ self.register_write_triggers(index, value);
+ self.mem.write(index, value);
+ }
+
+ pub fn set_mem16(&mut self, index: u16, value: u16) {
+ self.set_mem8(index + 1, bw::get_byte16::<1>(value));
+ self.set_mem8(index, bw::get_byte16::<0>(value));
+ }
+
+ fn register_write_triggers(&mut self, index: u16, value: u8) {
+ if index == memory::map::BANK as u16 && value != 0 {
+ self.mem.unload_bootrom();
+ }
+ }
+
+ // Decoding
+ /// get 8 bit immediate at position pc + 1 + pos
+ fn get_d8(&self, pos: u8) -> u8 {
+ self.mem8(self.pc + pos as u16 + 1)
+ }
+
+ /// get 8 bit immediate at position pc + 1 + pos
+ fn get_r8(&self, pos: u8) -> i8 {
+ self.mem8(self.pc + pos as u16 + 1) as i8
+ }
+
+ /// get 16 bit immediate at position pc + 1 + pos
+ fn get_d16(&self, pos: u8) -> u16 {
+ // little-endian: the first byte in memory is the LSB
+ ((self.get_d8(pos + 1) as u16) << 8) + self.get_d8(pos) as u16
+ }
+
+ fn hl_ind(&self) -> u8 {
+ self.mem8(self.hl())
+ }
+
+ fn set_hl_ind(&mut self, value: u8) {
+ self.set_mem8(self.hl(), value);
+ }
+
+ // Instruction logic
+ fn half_carry8(&self, x: u8, y: u8) -> bool {
+ ((x & 0x0f) + (y & 0x0f)) > 0x0f
+ }
+
+ fn half_carryc8(&self, x: u8, y: u8, c: u8) -> bool {
+ ((x & 0x0f) + (y & 0x0f) + c) > 0x0f
+ }
+
+ fn half_carry16(&self, x: u16, y: u16) -> bool {
+ ((x & 0x0fff) + (y & 0x0fff)) > 0x0fff
+ }
+
+ fn half_carry16i(&self, x: u16, y: i8) -> bool {
+ (x & 0x0fff).wrapping_add_signed(y as i16) > 0x0fff
+ }
+
+ fn inc8(&mut self, x: u8) -> u8 {
+ let (result, _overflow) = x.overflowing_add(1);
+ self.set_z_flag(result == 0);
+ self.set_n_flag(false);
+ self.set_h_flag(self.half_carry8(x, 1));
+ // INC r8 instructions don't set the C (carry) flag
+ result
+ }
+
+ fn dec8(&mut self, x: u8) -> u8 {
+ let (result, _overflow) = x.overflowing_sub(1);
+ self.set_z_flag(result == 0);
+ self.set_n_flag(true);
+ // There was a carry in bit 3 if the result's least significant nibble
+ // is all 0s (should we use a generalization to the half-carry logic?)
+ self.set_h_flag(result & 0xF == 0);
+ // DEC r8 instructions don't set the C (carry) flag
+ result
+ }
+
+ fn inc16(&mut self, x: u16) -> u16 {
+ let (result, _overflow) = x.overflowing_add(1);
+ // No flags affected
+ result
+ }
+
+ fn dec16(&mut self, x: u16) -> u16 {
+ let (result, _overflow) = x.overflowing_sub(1);
+ // No flags affected
+ result
+ }
+
+ fn add8(&mut self, x: u8, y: u8) -> u8 {
+ let (result, overflow) = x.overflowing_add(y);
+ self.set_z_flag(result == 0);
+ self.set_n_flag(false);
+ self.set_h_flag(self.half_carry8(x, y));
+ self.set_c_flag(overflow);
+ result
+ }
+
+ fn addc8(&mut self, x: u8, y: u8) -> u8 {
+ let (result, overflow) = x.carrying_add(y, self.c_flag());
+ self.set_z_flag(result == 0);
+ self.set_n_flag(false);
+ self.set_h_flag(self.half_carryc8(x, y, self.c_flag() as u8));
+ self.set_c_flag(overflow);
+ result
+ }
+
+ fn sub8(&mut self, x: u8, y: u8) -> u8 {
+ // every day I'm grateful for overflowing_sub
+ let (result, overflow) = x.overflowing_sub(y);
+ self.set_z_flag(result == 0);
+ self.set_n_flag(true);
+ self.set_h_flag((x & 0x0f).overflowing_sub(y & 0x0f).1);
+ self.set_c_flag(overflow);
+ result
+ }
+
+ fn subc8(&mut self, x: u8, y: u8) -> u8 {
+ let (result, overflow) = x.borrowing_sub(y, self.c_flag());
+ self.set_z_flag(result == 0);
+ self.set_n_flag(true);
+ self.set_h_flag((x & 0x0f).borrowing_sub(y & 0x0f, self.c_flag()).1);
+ self.set_c_flag(overflow);
+ result
+ }
+
+ fn add16(&mut self, x: u16, y: u16) -> u16 {
+ let (result, overflow) = x.overflowing_add(y);
+ // z flag is not set
+ self.set_n_flag(false);
+ self.set_h_flag(self.half_carry16(x, y));
+ self.set_c_flag(overflow);
+ result
+ }
+
+ fn add16i(&mut self, x: u16, y: i8) -> u16 {
+ let (result, overflow) = x.overflowing_add_signed(y as i16);
+ self.set_z_flag(false);
+ self.set_n_flag(false);
+ self.set_h_flag(self.half_carry16i(x, y));
+ self.set_c_flag(overflow);
+ result
+ }
+
+ fn xor8(&mut self, x: u8) -> u8 {
+ let result = self.a() ^ x;
+ self.set_z_flag(result == 0);
+ self.set_n_flag(false);
+ self.set_h_flag(false);
+ self.set_c_flag(false);
+ result
+ }
+
+ fn and8(&mut self, x: u8) -> u8 {
+ let result = self.a() & x;
+ self.set_z_flag(result == 0);
+ self.set_n_flag(false);
+ self.set_h_flag(true);
+ self.set_c_flag(false);
+ result
+ }
+
+ fn or8(&mut self, x: u8) -> u8 {
+ let result = self.a() | x;
+ self.set_z_flag(result == 0);
+ self.set_n_flag(false);
+ self.set_h_flag(false);
+ self.set_c_flag(false);
+ result
+ }
+
+ fn rlc8(&mut self, x: u8) -> u8 {
+ let result = x.rotate_left(1);
+ self.set_z_flag(result == 0);
+ self.set_n_flag(false);
+ self.set_h_flag(false);
+ self.set_c_flag(bw::test_bit8::<7>(x));
+ result
+ }
+
+ fn rl8(&mut self, x: u8) -> u8 {
+ let c_result = x.rotate_left(1);
+ let result = bw::set_bit8::<0>(c_result, self.c_flag());
+ self.set_z_flag(result == 0);
+ self.set_n_flag(false);
+ self.set_h_flag(false);
+ self.set_c_flag(bw::test_bit8::<0>(c_result));
+ result
+ }
+
+ fn rrc8(&mut self, x: u8) -> u8 {
+ let result = x.rotate_right(1);
+ self.set_z_flag(result == 0);
+ self.set_n_flag(false);
+ self.set_h_flag(false);
+ self.set_c_flag(bw::test_bit8::<0>(x));
+ result
+ }
+
+ fn rr8(&mut self, x: u8) -> u8 {
+ let c_result = x.rotate_right(1);
+ let result = bw::set_bit8::<7>(c_result, self.c_flag());
+ self.set_z_flag(result == 0);
+ self.set_n_flag(false);
+ self.set_h_flag(false);
+ self.set_c_flag(bw::test_bit8::<7>(c_result));
+ result
+ }
+
+ fn sla8(&mut self, x: u8) -> u8 {
+ let result = x << 1;
+ self.set_z_flag(result == 0);
+ self.set_n_flag(false);
+ self.set_h_flag(false);
+ self.set_c_flag(bw::test_bit8::<7>(x));
+ result
+ }
+
+ fn sra8(&mut self, x: u8) -> u8 {
+ let msb_result = x >> 1;
+ let result = bw::set_bit8::<7>(msb_result, bw::test_bit8::<7>(x));
+ self.set_z_flag(result == 0);
+ self.set_n_flag(false);
+ self.set_h_flag(false);
+ self.set_c_flag(bw::test_bit8::<0>(x));
+ result
+ }
+
+ fn srl8(&mut self, x: u8) -> u8 {
+ let result = x >> 1;
+ self.set_z_flag(result == 0);
+ self.set_n_flag(false);
+ self.set_h_flag(false);
+ self.set_c_flag(bw::test_bit8::<0>(x));
+ result
+ }
+
+ fn swap8(&mut self, x: u8) -> u8 {
+ let lo = x & 0x0f;
+ let hi = x & 0xf0;
+ let result = (lo << 4) + (hi >> 4);
+ self.set_z_flag(result == 0);
+ self.set_n_flag(false);
+ self.set_h_flag(false);
+ self.set_c_flag(false);
+ result
+ }
+
+ fn push(&mut self, value: u16) {
+ self.set_sp(self.sp() - 2);
+ self.set_mem16(self.sp(), value);
+ }
+
+ fn pop(&mut self) -> u16 {
+ let r = self.mem16(self.sp());
+ self.set_sp(self.sp() + 2);
+ r
+ }
+
+ fn calc_jr_address(&self, pc: u16, offset: i8) -> u16 {
+ // pc + 2 because relative address are based off
+ // the end of the JR instruction.
+ let r = (pc + 2) as i32 + offset as i32;
+ if !(0..=65535).contains(&r) {
+ panic!();
+ }
+
+ r as u16
+ }
+
+ fn jump(&mut self, address: u16) {
+ self.set_pc(address);
+ self.set_mutated_pc(true);
+ }
+
+ fn call(&mut self, address: u16) {
+ // pc + 3 because CALLs have size == 3 bytes
+ self.push(self.pc() + 3);
+ self.jump(address);
+ }
+
+ fn ret(&mut self) {
+ let address = self.pop();
+ self.jump(address);
+ }
+
+ fn rst(&mut self, address: u16) {
+ // pc + 1 because RSTs have size == 1 byte
+ self.push(self.pc() + 1);
+ self.jump(address);
+ }
+
+ fn reti(&mut self) {
+ // TODO: The interrupt master enable flag is returned to its pre-interrupt status.
+ // BUT: https://gbdev.io/pandocs/Interrupts.htm claims that RETI is EI followed by RET
+ self.set_interrupt_master_enable_next_instruction();
+
+ // RET
+ let address = self.pop();
+ self.jump(address);
+ }
+
+ fn bit(&mut self, x: u8) {
+ if !bw::test_bit8::(x) {
+ self.set_z_flag(true);
+ }
+ self.set_n_flag(false);
+ self.set_h_flag(true);
+ }
+
+ fn decode(&self) -> Instruction {
+ let mut opcode = self.mem8(self.pc()) as u16;
+ if self.prefix_cb {
+ opcode += 0x100;
+ }
+ INSTRUCTIONS[opcode as usize]
+ }
+
+ pub fn decode_ahead(&self, n: usize) -> Vec<(u16, Instruction)> {
+ let mut ret = Vec::<(u16, Instruction)>::with_capacity(n + 1);
+ ret.push((self.pc(), self.decode()));
+ // TODO: decode can return "PREFIX CB" - not good for the disasm
+ // if ret[0].mnemonic == "PREFIX CB" {}
+ for i in 1..(n + 1) {
+ let last_inst = ret[i - 1];
+ let next_pc = last_inst.0 + last_inst.1.size as u16;
+ let mut next_inst_opcode_index = self.mem8(next_pc) as usize;
+ if last_inst.1.mnemonic == "PREFIX CB" {
+ next_inst_opcode_index += 0x100;
+ }
+ ret.push((next_pc, INSTRUCTIONS[next_inst_opcode_index]));
+ }
+ ret
+ }
+
+ /// Run one t-cycle - from actual crystal @ 4 or 8 MHz (double speed mode)
+ /// Returns a instruction if we actually mutated CPU state, since we only
+ /// execute the instruction itself on its last t-cycle
+ pub fn t_cycle(&mut self) {
+ let inst = self.decode();
+ self.set_inst_cycle_count(self.inst_cycle_count() + 1);
+ // Only actually mutate CPU state on the last t-cycle of the instruction
+ if self.inst_cycle_count() < inst.cycles {
+ return;
+ }
+ self.update_code_listing(inst);
+ if self.imenc {
+ self.set_interrupt_master_enable(true);
+ self.imenc = false;
+ }
+ if self.prefix_cb {
+ self.prefix_cb = false;
+ }
+ self.execute(inst);
+ // TODO: should `self.execute` return `mutated_pc`?
+ if !self.mutated_pc() {
+ self.set_pc(self.pc() + inst.size as u16);
+ }
+ let cycles = if inst.kind == InstructionKind::Jump && !self.mutated_pc() {
+ inst.cycles_not_taken
+ } else {
+ inst.cycles
+ };
+ self.set_clock_cycles(self.clock_cycles() + cycles as u64);
+ self.set_mutated_pc(false);
+ self.set_inst_cycle_count(0);
+ }
+
+ fn update_code_listing(&mut self, inst: Instruction) {
+ if self.mem.memory().code_listing()[self.pc() as usize].is_some() {
+ return;
+ }
+ let result: Vec = (1..inst.size)
+ .map(|i| format!("{:#02X}", self.mem8(self.pc() + i as u16)))
+ .collect();
+ let str = format!(
+ "{:#06X}: {} ({:#02X}{}{})",
+ self.pc(),
+ inst.mnemonic,
+ inst.opcode,
+ if result.is_empty() { "" } else { " " },
+ result.join(" ")
+ );
+ self.mem.memory_mut().set_code_listing_at(self.pc(), str);
+ }
+
+ /// Run one complete instruction - NOT a machine cycle (4 t-cycles)
+ pub fn instruction(&mut self) -> u8 {
+ let instruction = self.decode();
+ for _ in 0..instruction.cycles {
+ self.t_cycle();
+ }
+ instruction.cycles
+ }
+
+ fn execute(&mut self, instruction: Instruction) {
+ match instruction.opcode {
+ 0x00 => {
+ // NOP
+ }
+ 0x01 => {
+ // LD BC,d16
+ self.set_bc(self.get_d16(0));
+ }
+ 0x02 => {
+ // LD (BC),A
+ self.set_mem8(self.bc(), self.a());
+ }
+ 0x03 => {
+ // INC BC
+ let result = self.inc16(self.bc());
+ self.set_bc(result);
+ }
+ 0x04 => {
+ // INC B
+ let result = self.inc8(self.b());
+ self.set_b(result);
+ }
+ 0x05 => {
+ // DEC B
+ let result = self.dec8(self.b());
+ self.set_b(result);
+ }
+ 0x06 => {
+ // LD B,d8
+ self.set_b(self.get_d8(0));
+ }
+ 0x07 => {
+ // RLCA
+ let result = self.rlc8(self.a());
+ self.set_z_flag(false);
+ self.set_a(result);
+ }
+ 0x08 => {
+ // LD (a16),SP
+ self.set_mem16(self.get_d16(0), self.sp());
+ }
+ 0x09 => {
+ // ADD HL,BC
+ let result = self.add16(self.hl(), self.bc());
+ self.set_hl(result);
+ }
+ 0x0A => {
+ // LD A,(BC)
+ self.set_a(self.mem8(self.bc()));
+ }
+ 0x0B => {
+ // DEC BC
+ let result = self.dec16(self.bc());
+ self.set_bc(result);
+ }
+ 0x0C => {
+ // INC C
+ let result = self.inc8(self.c());
+ self.set_c(result);
+ }
+ 0x0D => {
+ // DEC C
+ let result = self.dec8(self.c());
+ self.set_c(result);
+ }
+ 0x0E => {
+ // LD C,d8
+ self.set_c(self.get_d8(0));
+ }
+ 0x0F => {
+ // RRCA
+ let result = self.rrc8(self.a());
+ self.set_z_flag(false);
+ self.set_a(result);
+ }
+ 0x10 => {
+ // STOP 0
+ todo!("0x10 STOP 0")
+ }
+ 0x11 => {
+ // LD DE,d16
+ self.set_de(self.get_d16(0));
+ }
+ 0x12 => {
+ // LD (DE),A
+ self.set_mem8(self.de(), self.a());
+ }
+ 0x13 => {
+ // INC DE
+ let result = self.inc16(self.de());
+ self.set_de(result);
+ }
+ 0x14 => {
+ // INC D
+ let result: u8 = self.inc8(self.d());
+ self.set_d(result);
+ }
+ 0x15 => {
+ // DEC D
+ let result = self.dec8(self.d());
+ self.set_d(result);
+ }
+ 0x16 => {
+ // LD D,d8
+ self.set_d(self.get_d8(0));
+ }
+ 0x17 => {
+ // RLA
+ let result = self.rl8(self.a());
+ self.set_z_flag(false);
+ self.set_a(result);
+ }
+ 0x18 => {
+ // JR r8
+ self.jump(self.calc_jr_address(self.pc(), self.get_r8(0)));
+ }
+ 0x19 => {
+ // ADD HL,DE
+ let result = self.add16(self.hl(), self.de());
+ self.set_hl(result);
+ }
+ 0x1A => {
+ // LD A,(DE)
+ self.set_a(self.mem8(self.de()));
+ }
+ 0x1B => {
+ // DEC DE
+ let result = self.dec16(self.de());
+ self.set_de(result);
+ }
+ 0x1C => {
+ // INC E
+ let result = self.inc8(self.e());
+ self.set_e(result);
+ }
+ 0x1D => {
+ // DEC E
+ let result = self.dec8(self.e());
+ self.set_e(result);
+ }
+ 0x1E => {
+ // LD E,d8
+ self.set_e(self.get_d8(0));
+ }
+ 0x1F => {
+ // RRA
+ let result = self.rr8(self.a());
+ self.set_z_flag(false);
+ self.set_a(result);
+ }
+ 0x20 => {
+ // JR NZ,r8
+ if !self.z_flag() {
+ self.jump(self.calc_jr_address(self.pc(), self.get_r8(0)));
+ }
+ }
+ 0x21 => {
+ // LD HL,d16
+ self.set_hl(self.get_d16(0));
+ }
+ 0x22 => {
+ // LD (HL+),A
+ self.set_mem8(self.hl(), self.a());
+ self.set_hl(self.hl().overflowing_add(1).0);
+ }
+ 0x23 => {
+ // INC HL
+ let result = self.inc16(self.hl());
+ self.set_hl(result);
+ }
+ 0x24 => {
+ // INC H
+ let result = self.inc8(self.h());
+ self.set_h(result);
+ }
+ 0x25 => {
+ // DEC H
+ let result = self.dec8(self.h());
+ self.set_h(result);
+ }
+ 0x26 => {
+ // LD H,d8
+ self.set_h(self.get_d8(0));
+ }
+ 0x27 => {
+ // DAA
+ todo!("0x27 DAA")
+ }
+ 0x28 => {
+ // JR Z,r8
+ if self.z_flag() {
+ self.jump(self.calc_jr_address(self.pc(), self.get_r8(0)));
+ }
+ }
+ 0x29 => {
+ // ADD HL,HL
+ let result = self.add16(self.hl(), self.hl());
+ self.set_hl(result);
+ }
+ 0x2A => {
+ // LD A,(HL+)
+ self.set_a(self.hl_ind());
+ self.set_hl(self.hl().overflowing_add(1).0);
+ }
+ 0x2B => {
+ // DEC HL
+ let result = self.dec16(self.hl());
+ self.set_hl(result);
+ }
+ 0x2C => {
+ // INC L
+ let result = self.inc8(self.l());
+ self.set_l(result);
+ }
+ 0x2D => {
+ // DEC L
+ let result = self.dec8(self.l());
+ self.set_l(result);
+ }
+ 0x2E => {
+ // LD L,d8
+ self.set_l(self.get_d8(0));
+ }
+ 0x2F => {
+ // CPL
+ self.set_a(!self.a());
+ self.set_n_flag(true);
+ self.set_h_flag(true);
+ }
+ 0x30 => {
+ // JR NC,r8
+ if !self.c_flag() {
+ self.jump(self.calc_jr_address(self.pc(), self.get_r8(0)));
+ }
+ }
+ 0x31 => {
+ // LD SP,d16
+ self.set_sp(self.get_d16(0));
+ }
+ 0x32 => {
+ // LD (HL-),A
+ self.set_mem8(self.hl(), self.a());
+ self.set_hl(self.hl().overflowing_sub(1).0)
+ }
+ 0x33 => {
+ // INC SP
+ let result = self.inc16(self.sp());
+ self.set_sp(result);
+ }
+ 0x34 => {
+ // INC (HL)
+ let result = self.inc8(self.mem8(self.hl()));
+ self.set_mem8(self.hl(), result);
+ }
+ 0x35 => {
+ // DEC (HL)
+ let result = self.dec8(self.mem8(self.hl()));
+ self.set_mem8(self.hl(), result);
+ }
+ 0x36 => {
+ // LD (HL),d8
+ self.set_mem8(self.hl(), self.get_d8(0));
+ }
+ 0x37 => {
+ // SCF
+ self.set_n_flag(false);
+ self.set_h_flag(false);
+ self.set_c_flag(true);
+ }
+ 0x38 => {
+ // JR C,r8
+ if self.c_flag() {
+ self.jump(self.calc_jr_address(self.pc(), self.get_r8(0)));
+ }
+ }
+ 0x39 => {
+ // ADD HL,SP
+ let result = self.add16(self.hl(), self.sp());
+ self.set_hl(result);
+ }
+ 0x3A => {
+ // LD A,(HL-)
+ self.set_a(self.hl_ind());
+ self.set_hl(self.hl().overflowing_sub(1).0);
+ }
+ 0x3B => {
+ // DEC SP
+ let result = self.dec16(self.sp());
+ self.set_sp(result);
+ }
+ 0x3C => {
+ // INC A
+ let result = self.inc8(self.a());
+ self.set_a(result);
+ }
+ 0x3D => {
+ // DEC A
+ let result = self.dec8(self.a());
+ self.set_a(result);
+ }
+ 0x3E => {
+ // LD A,d8
+ self.set_a(self.get_d8(0));
+ }
+ 0x3F => {
+ // CCF
+ self.set_n_flag(false);
+ self.set_h_flag(false);
+ self.set_c_flag(!self.c_flag());
+ }
+ 0x40 => {
+ // LD B,B
+ self.set_b(self.b());
+ }
+ 0x41 => {
+ // LD B,C
+ self.set_b(self.c());
+ }
+ 0x42 => {
+ // LD B,D
+ self.set_b(self.d());
+ }
+ 0x43 => {
+ // LD B,E
+ self.set_b(self.e());
+ }
+ 0x44 => {
+ // LD B,H
+ self.set_b(self.h());
+ }
+ 0x45 => {
+ // LD B,L
+ self.set_b(self.l());
+ }
+ 0x46 => {
+ // LD B,(HL)
+ self.set_b(self.hl_ind());
+ }
+ 0x47 => {
+ // LD B,A
+ self.set_b(self.a());
+ }
+ 0x48 => {
+ // LD C,B
+ self.set_c(self.b());
+ }
+ 0x49 => {
+ // LD C,C
+ self.set_c(self.c());
+ }
+ 0x4A => {
+ // LD C,D
+ self.set_c(self.d());
+ }
+ 0x4B => {
+ // LD C,E
+ self.set_c(self.e());
+ }
+ 0x4C => {
+ // LD C,H
+ self.set_c(self.h());
+ }
+ 0x4D => {
+ // LD C,L
+ self.set_c(self.l());
+ }
+ 0x4E => {
+ // LD C,(HL)
+ self.set_c(self.hl_ind());
+ }
+ 0x4F => {
+ // LD C,A
+ self.set_c(self.a());
+ }
+ 0x50 => {
+ // LD D,B
+ self.set_d(self.b());
+ }
+ 0x51 => {
+ // LD D,C
+ self.set_d(self.c());
+ }
+ 0x52 => {
+ // LD D,D
+ self.set_d(self.d());
+ }
+ 0x53 => {
+ // LD D,E
+ self.set_d(self.e());
+ }
+ 0x54 => {
+ // LD D,H
+ self.set_d(self.h());
+ }
+ 0x55 => {
+ // LD D,L
+ self.set_d(self.l());
+ }
+ 0x56 => {
+ // LD D,(HL)
+ self.set_d(self.hl_ind());
+ }
+ 0x57 => {
+ // LD D,A
+ self.set_d(self.a());
+ }
+ 0x58 => {
+ // LD E,B
+ self.set_e(self.b());
+ }
+ 0x59 => {
+ // LD E,C
+ self.set_e(self.c());
+ }
+ 0x5A => {
+ // LD E,D
+ self.set_e(self.d());
+ }
+ 0x5B => {
+ // LD E,E
+ self.set_e(self.e());
+ }
+ 0x5C => {
+ // LD E,H
+ self.set_e(self.h());
+ }
+ 0x5D => {
+ // LD E,L
+ self.set_e(self.l());
+ }
+ 0x5E => {
+ // LD E,(HL)
+ self.set_e(self.hl_ind());
+ }
+ 0x5F => {
+ // LD E,A
+ self.set_e(self.a());
+ }
+ 0x60 => {
+ // LD H,B
+ self.set_h(self.b());
+ }
+ 0x61 => {
+ // LD H,C
+ self.set_h(self.c());
+ }
+ 0x62 => {
+ // LD H,D
+ self.set_h(self.d());
+ }
+ 0x63 => {
+ // LD H,E
+ self.set_h(self.e());
+ }
+ 0x64 => {
+ // LD H,H
+ self.set_h(self.h());
+ }
+ 0x65 => {
+ // LD H,L
+ self.set_h(self.l());
+ }
+ 0x66 => {
+ // LD H,(HL)
+ self.set_h(self.hl_ind());
+ }
+ 0x67 => {
+ // LD H,A
+ self.set_h(self.a());
+ }
+ 0x68 => {
+ // LD L,B
+ self.set_l(self.b());
+ }
+ 0x69 => {
+ // LD L,C
+ self.set_l(self.c());
+ }
+ 0x6A => {
+ // LD L,D
+ self.set_l(self.d());
+ }
+ 0x6B => {
+ // LD L,E
+ self.set_l(self.e());
+ }
+ 0x6C => {
+ // LD L,H
+ self.set_l(self.h());
+ }
+ 0x6D => {
+ // LD L,L
+ self.set_l(self.l());
+ }
+ 0x6E => {
+ // LD L,(HL)
+ self.set_l(self.hl_ind());
+ }
+ 0x6F => {
+ // LD L,A
+ self.set_l(self.a());
+ }
+ 0x70 => {
+ // LD (HL),B
+ self.set_mem8(self.hl(), self.b());
+ }
+ 0x71 => {
+ // LD (HL),C
+ self.set_mem8(self.hl(), self.c());
+ }
+ 0x72 => {
+ // LD (HL),D
+ self.set_mem8(self.hl(), self.d());
+ }
+ 0x73 => {
+ // LD (HL),E
+ self.set_mem8(self.hl(), self.e());
+ }
+ 0x74 => {
+ // LD (HL),H
+ self.set_mem8(self.hl(), self.h());
+ }
+ 0x75 => {
+ // LD (HL),L
+ self.set_mem8(self.hl(), self.l());
+ }
+ 0x76 => {
+ // HALT
+ // Take care for halt bug: https://gbdev.io/pandocs/halt.html
+ // https://rgbds.gbdev.io/docs/v0.6.1/gbz80.7/#HALT
+ todo!("0x76 HALT")
+ }
+ 0x77 => {
+ // LD (HL),A
+ self.set_mem8(self.hl(), self.a());
+ }
+ 0x78 => {
+ // LD A,B
+ self.set_a(self.b());
+ }
+ 0x79 => {
+ // LD A,C
+ self.set_a(self.c());
+ }
+ 0x7A => {
+ // LD A,D
+ self.set_a(self.d());
+ }
+ 0x7B => {
+ // LD A,E
+ self.set_a(self.e());
+ }
+ 0x7C => {
+ // LD A,H
+ self.set_a(self.h());
+ }
+ 0x7D => {
+ // LD A,L
+ self.set_a(self.l());
+ }
+ 0x7E => {
+ // LD A,(HL)
+ self.set_a(self.hl_ind());
+ }
+ 0x7F => {
+ // LD A,A
+ self.set_a(self.a());
+ }
+ 0x80 => {
+ // ADD A,B
+ let result = self.add8(self.a(), self.b());
+ self.set_a(result);
+ }
+ 0x81 => {
+ // ADD A,C
+ let result = self.add8(self.a(), self.c());
+ self.set_a(result);
+ }
+ 0x82 => {
+ // ADD A,D
+ let result = self.add8(self.a(), self.d());
+ self.set_a(result);
+ }
+ 0x83 => {
+ // ADD A,E
+ let result = self.add8(self.a(), self.e());
+ self.set_a(result);
+ }
+ 0x84 => {
+ // ADD A,H
+ let result = self.add8(self.a(), self.h());
+ self.set_a(result);
+ }
+ 0x85 => {
+ // ADD A,L
+ let result = self.add8(self.a(), self.l());
+ self.set_a(result);
+ }
+ 0x86 => {
+ // ADD A,(HL)
+ let result = self.add8(self.a(), self.hl_ind());
+ self.set_a(result);
+ }
+ 0x87 => {
+ // ADD A,A
+ let result = self.add8(self.a(), self.a());
+ self.set_a(result);
+ }
+ 0x88 => {
+ // ADC A,B
+ let result = self.addc8(self.a(), self.b());
+ self.set_a(result);
+ }
+ 0x89 => {
+ // ADC A,C
+ let result = self.addc8(self.a(), self.c());
+ self.set_a(result);
+ }
+ 0x8A => {
+ // ADC A,D
+ let result = self.addc8(self.a(), self.d());
+ self.set_a(result);
+ }
+ 0x8B => {
+ // ADC A,E
+ let result = self.addc8(self.a(), self.e());
+ self.set_a(result);
+ }
+ 0x8C => {
+ // ADC A,H
+ let result = self.addc8(self.a(), self.h());
+ self.set_a(result);
+ }
+ 0x8D => {
+ // ADC A,L
+ let result = self.addc8(self.a(), self.l());
+ self.set_a(result);
+ }
+ 0x8E => {
+ // ADC A,(HL)
+ let result = self.addc8(self.a(), self.hl_ind());
+ self.set_a(result);
+ }
+ 0x8F => {
+ // ADC A,A
+ let result = self.addc8(self.a(), self.a());
+ self.set_a(result);
+ }
+ 0x90 => {
+ // SUB B
+ let result = self.sub8(self.a(), self.b());
+ self.set_a(result);
+ }
+ 0x91 => {
+ // SUB C
+ let result = self.sub8(self.a(), self.c());
+ self.set_a(result);
+ }
+ 0x92 => {
+ // SUB D
+ let result = self.sub8(self.a(), self.d());
+ self.set_a(result);
+ }
+ 0x93 => {
+ // SUB E
+ let result = self.sub8(self.a(), self.e());
+ self.set_a(result);
+ }
+ 0x94 => {
+ // SUB H
+ let result = self.sub8(self.a(), self.h());
+ self.set_a(result);
+ }
+ 0x95 => {
+ // SUB L
+ let result = self.sub8(self.a(), self.l());
+ self.set_a(result);
+ }
+ 0x96 => {
+ // SUB (HL)
+ let result = self.sub8(self.a(), self.hl_ind());
+ self.set_a(result);
+ }
+ 0x97 => {
+ // SUB A
+ let result = self.sub8(self.a(), self.a());
+ self.set_a(result);
+ }
+ 0x98 => {
+ // SBC A,B
+ let result = self.subc8(self.a(), self.b());
+ self.set_a(result);
+ }
+ 0x99 => {
+ // SBC A,C
+ let result = self.subc8(self.a(), self.c());
+ self.set_a(result);
+ }
+ 0x9A => {
+ // SBC A,D
+ let result = self.subc8(self.a(), self.d());
+ self.set_a(result);
+ }
+ 0x9B => {
+ // SBC A,E
+ let result = self.subc8(self.a(), self.e());
+ self.set_a(result);
+ }
+ 0x9C => {
+ // SBC A,H
+ let result = self.subc8(self.a(), self.h());
+ self.set_a(result);
+ }
+ 0x9D => {
+ // SBC A,L
+ let result = self.subc8(self.a(), self.l());
+ self.set_a(result);
+ }
+ 0x9E => {
+ // SBC A,(HL)
+ let result = self.subc8(self.a(), self.hl_ind());
+ self.set_a(result);
+ }
+ 0x9F => {
+ // SBC A,A
+ let result = self.subc8(self.a(), self.a());
+ self.set_a(result);
+ }
+ 0xA0 => {
+ // AND B
+ let result = self.and8(self.b());
+ self.set_a(result);
+ }
+ 0xA1 => {
+ // AND C
+ let result = self.and8(self.c());
+ self.set_a(result);
+ }
+ 0xA2 => {
+ // AND D
+ let result = self.and8(self.d());
+ self.set_a(result);
+ }
+ 0xA3 => {
+ // AND E
+ let result = self.and8(self.e());
+ self.set_a(result);
+ }
+ 0xA4 => {
+ // AND H
+ let result = self.and8(self.h());
+ self.set_a(result);
+ }
+ 0xA5 => {
+ // AND L
+ let result = self.and8(self.l());
+ self.set_a(result);
+ }
+ 0xA6 => {
+ // AND (HL)
+ let result = self.and8(self.hl_ind());
+ self.set_a(result);
+ }
+ 0xA7 => {
+ // AND A
+ let result = self.and8(self.a());
+ self.set_a(result);
+ }
+ 0xA8 => {
+ // XOR B
+ let result = self.xor8(self.b());
+ self.set_a(result);
+ }
+ 0xA9 => {
+ // XOR C
+ let result = self.xor8(self.c());
+ self.set_a(result);
+ }
+ 0xAA => {
+ // XOR D
+ let result = self.xor8(self.d());
+ self.set_a(result);
+ }
+ 0xAB => {
+ // XOR E
+ let result = self.xor8(self.e());
+ self.set_a(result);
+ }
+ 0xAC => {
+ // XOR H
+ let result = self.xor8(self.h());
+ self.set_a(result);
+ }
+ 0xAD => {
+ // XOR L
+ let result = self.xor8(self.l());
+ self.set_a(result);
+ }
+ 0xAE => {
+ // XOR (HL)
+ let result = self.xor8(self.hl_ind());
+ self.set_a(result);
+ }
+ 0xAF => {
+ // XOR A
+ let result = self.xor8(self.a());
+ self.set_a(result);
+ }
+ 0xB0 => {
+ // OR B
+ let result = self.or8(self.b());
+ self.set_a(result);
+ }
+ 0xB1 => {
+ // OR C
+ let result = self.or8(self.c());
+ self.set_a(result);
+ }
+ 0xB2 => {
+ // OR D
+ let result = self.or8(self.d());
+ self.set_a(result);
+ }
+ 0xB3 => {
+ // OR E
+ let result = self.or8(self.e());
+ self.set_a(result);
+ }
+ 0xB4 => {
+ // OR H
+ let result = self.or8(self.h());
+ self.set_a(result);
+ }
+ 0xB5 => {
+ // OR L
+ let result = self.or8(self.l());
+ self.set_a(result);
+ }
+ 0xB6 => {
+ // OR (HL)
+ let result = self.or8(self.hl_ind());
+ self.set_a(result);
+ }
+ 0xB7 => {
+ // OR A
+ let result = self.or8(self.a());
+ self.set_a(result);
+ }
+ 0xB8 => {
+ // CP B
+ self.sub8(self.a(), self.b());
+ }
+ 0xB9 => {
+ // CP C
+ self.sub8(self.a(), self.c());
+ }
+ 0xBA => {
+ // CP D
+ self.sub8(self.a(), self.d());
+ }
+ 0xBB => {
+ // CP E
+ self.sub8(self.a(), self.e());
+ }
+ 0xBC => {
+ // CP H
+ self.sub8(self.a(), self.h());
+ }
+ 0xBD => {
+ // CP L
+ self.sub8(self.a(), self.l());
+ }
+ 0xBE => {
+ // CP (HL)
+ self.sub8(self.a(), self.hl_ind());
+ }
+ 0xBF => {
+ // CP A
+ self.sub8(self.a(), self.a());
+ }
+ 0xC0 => {
+ // RET NZ
+ if !self.z_flag() {
+ self.ret()
+ }
+ }
+ 0xC1 => {
+ // POP BC
+ let value = self.pop();
+ self.set_bc(value);
+ }
+ 0xC2 => {
+ // JP NZ,a16
+ if !self.z_flag() {
+ self.jump(self.get_d16(0));
+ }
+ }
+ 0xC3 => {
+ // JP a16
+ self.jump(self.get_d16(0));
+ }
+ 0xC4 => {
+ // CALL NZ,a16
+ if !self.z_flag() {
+ self.call(self.get_d16(0));
+ }
+ }
+ 0xC5 => {
+ // PUSH BC
+ self.push(self.bc());
+ }
+ 0xC6 => {
+ // ADD A,d8
+ let result = self.add8(self.a(), self.get_d8(0));
+ self.set_a(result);
+ }
+ 0xC7 => {
+ // RST 00H
+ self.rst(0x00);
+ }
+ 0xC8 => {
+ // RET Z
+ if self.z_flag() {
+ self.ret()
+ }
+ }
+ 0xC9 => {
+ // RET
+ self.ret();
+ }
+ 0xCA => {
+ // JP Z,a16
+ if self.z_flag() {
+ self.jump(self.get_d16(0));
+ }
+ }
+ 0xCB => {
+ // PREFIX CB
+ self.prefix_cb = true;
+ }
+ 0xCC => {
+ // CALL Z,a16
+ if self.z_flag() {
+ self.call(self.get_d16(0));
+ }
+ }
+ 0xCD => {
+ // CALL a16
+ self.call(self.get_d16(0));
+ }
+ 0xCE => {
+ // ADC A,d8
+ let result = self.addc8(self.a(), self.get_d8(0));
+ self.set_a(result);
+ }
+ 0xCF => {
+ // RST 08H
+ self.rst(0x08);
+ }
+ 0xD0 => {
+ // RET NC
+ if !self.c_flag() {
+ self.ret();
+ }
+ }
+ 0xD1 => {
+ // POP DE
+ let value = self.pop();
+ self.set_de(value);
+ }
+ 0xD2 => {
+ // JP NC,a16
+ if !self.c_flag() {
+ self.jump(self.get_d16(0));
+ }
+ }
+ 0xD3 => {
+ // Not implemented
+ unimplemented!()
+ }
+ 0xD4 => {
+ // CALL NC,a16
+ if !self.c_flag() {
+ self.call(self.get_d16(0));
+ }
+ }
+ 0xD5 => {
+ // PUSH DE
+ self.push(self.de());
+ }
+ 0xD6 => {
+ // SUB d8
+ let result = self.sub8(self.a(), self.get_d8(0));
+ self.set_a(result);
+ }
+ 0xD7 => {
+ // RST 10H
+ self.rst(0x10);
+ }
+ 0xD8 => {
+ // RET C
+ if self.c_flag() {
+ self.ret();
+ }
+ }
+ 0xD9 => {
+ // RETI
+ self.reti();
+ }
+ 0xDA => {
+ // JP C,a16
+ if self.c_flag() {
+ self.jump(self.get_d16(0));
+ }
+ }
+ 0xDB => {
+ // Not implemented
+ unimplemented!()
+ }
+ 0xDC => {
+ // CALL C,a16
+ if self.c_flag() {
+ self.call(self.get_d16(0));
+ }
+ }
+ 0xDD => {
+ // Not implemented
+ unimplemented!()
+ }
+ 0xDE => {
+ // SBC A,d8
+ let result = self.subc8(self.a(), self.get_d8(0));
+ self.set_a(result);
+ }
+ 0xDF => {
+ // RST 18H
+ self.rst(0x18);
+ }
+ 0xE0 => {
+ // LDH (a8),A
+ self.set_mem8(0xFF00 | self.get_d8(0) as u16, self.a());
+ }
+ 0xE1 => {
+ // POP HL
+ let value = self.pop();
+ self.set_hl(value);
+ }
+ 0xE2 => {
+ // LD (C),A
+ self.set_mem8(0xFF00 + self.c() as u16, self.a());
+ }
+ 0xE3 => {
+ // Not implemented
+ unimplemented!()
+ }
+ 0xE4 => {
+ // Not implemented
+ unimplemented!()
+ }
+ 0xE5 => {
+ // PUSH HL
+ self.push(self.hl());
+ }
+ 0xE6 => {
+ // AND d8
+ let result = self.and8(self.get_d8(0));
+ self.set_a(result);
+ }
+ 0xE7 => {
+ // RST 20H
+ self.rst(0x20);
+ }
+ 0xE8 => {
+ // ADD SP,r8
+ let result = self.add16i(self.sp(), self.get_r8(0));
+ self.set_sp(result);
+ }
+ 0xE9 => {
+ // JP (HL)
+ self.jump(self.hl());
+ }
+ 0xEA => {
+ // LD (a16),A
+ self.set_mem8(self.get_d16(0), self.a());
+ }
+ 0xEB => {
+ // Not implemented
+ unimplemented!()
+ }
+ 0xEC => {
+ // Not implemented
+ unimplemented!()
+ }
+ 0xED => {
+ // Not implemented
+ unimplemented!()
+ }
+ 0xEE => {
+ // XOR d8
+ let result = self.xor8(self.get_d8(0));
+ self.set_a(result);
+ }
+ 0xEF => {
+ // RST 28H
+ self.rst(0x28);
+ }
+ 0xF0 => {
+ // LDH A,(a8)
+ self.set_a(self.mem8(0xFF00 | self.get_d8(0) as u16));
+ }
+ 0xF1 => {
+ // POP AF
+ let value = self.pop();
+ self.set_af(value);
+ }
+ 0xF2 => {
+ // LD A,(C)
+ self.set_a(self.mem8(0xFF00 | self.c() as u16));
+ }
+ 0xF3 => {
+ // DI
+ self.set_interrupt_master_enable(false);
+ }
+ 0xF4 => {
+ // Not implemented
+ unimplemented!()
+ }
+ 0xF5 => {
+ // PUSH AF
+ self.push(self.af());
+ }
+ 0xF6 => {
+ // OR d8
+ let result = self.or8(self.get_d8(0));
+ self.set_a(result);
+ }
+ 0xF7 => {
+ // RST 30H
+ self.rst(0x30);
+ }
+ 0xF8 => {
+ // LD HL,SP+r8
+ let result = self.add16i(self.sp(), self.get_r8(0));
+ self.set_hl(result);
+ }
+ 0xF9 => {
+ // LD SP,HL
+ self.set_sp(self.hl());
+ }
+ 0xFA => {
+ // LD A,(a16)
+ self.set_a(self.mem8(self.get_d16(0)));
+ }
+ 0xFB => {
+ // EI
+ self.set_interrupt_master_enable_next_instruction();
+ }
+ 0xFC => {
+ // Not implemented
+ unimplemented!()
+ }
+ 0xFD => {
+ // Not implemented
+ unimplemented!()
+ }
+ 0xFE => {
+ // CP d8
+ self.sub8(self.a(), self.get_d8(0));
+ }
+ 0xFF => {
+ // RST 38H
+ self.rst(0x38);
+ }
+ 0x100 => {
+ // RLC B
+ let result = self.rlc8(self.b());
+ self.set_b(result);
+ }
+ 0x101 => {
+ // RLC C
+ let result = self.rlc8(self.c());
+ self.set_c(result);
+ }
+ 0x102 => {
+ // RLC D
+ let result = self.rlc8(self.d());
+ self.set_d(result);
+ }
+ 0x103 => {
+ // RLC E
+ let result = self.rlc8(self.e());
+ self.set_e(result);
+ }
+ 0x104 => {
+ // RLC H
+ let result = self.rlc8(self.h());
+ self.set_h(result);
+ }
+ 0x105 => {
+ // RLC L
+ let result = self.rlc8(self.l());
+ self.set_l(result);
+ }
+ 0x106 => {
+ // RLC (HL)
+ let result = self.rlc8(self.hl_ind());
+ self.set_hl_ind(result);
+ }
+ 0x107 => {
+ // RLC A
+ let result = self.rlc8(self.a());
+ self.set_a(result);
+ }
+ 0x108 => {
+ // RRC B
+ let result = self.rrc8(self.b());
+ self.set_b(result);
+ }
+ 0x109 => {
+ // RRC C
+ let result = self.rrc8(self.c());
+ self.set_c(result);
+ }
+ 0x10A => {
+ // RRC D
+ let result = self.rrc8(self.d());
+ self.set_d(result);
+ }
+ 0x10B => {
+ // RRC E
+ let result = self.rrc8(self.e());
+ self.set_e(result);
+ }
+ 0x10C => {
+ // RRC H
+ let result = self.rrc8(self.h());
+ self.set_h(result);
+ }
+ 0x10D => {
+ // RRC L
+ let result = self.rrc8(self.l());
+ self.set_l(result);
+ }
+ 0x10E => {
+ // RRC (HL)
+ let result = self.rrc8(self.hl_ind());
+ self.set_hl_ind(result);
+ }
+ 0x10F => {
+ // RRC A
+ let result = self.rrc8(self.a());
+ self.set_a(result);
+ }
+ 0x110 => {
+ // RL B
+ let result = self.rl8(self.b());
+ self.set_b(result);
+ }
+ 0x111 => {
+ // RL C
+ let result = self.rl8(self.c());
+ self.set_c(result);
+ }
+ 0x112 => {
+ // RL D
+ let result = self.rl8(self.d());
+ self.set_d(result);
+ }
+ 0x113 => {
+ // RL E
+ let result = self.rl8(self.e());
+ self.set_e(result);
+ }
+ 0x114 => {
+ // RL H
+ let result = self.rl8(self.h());
+ self.set_h(result);
+ }
+ 0x115 => {
+ // RL L
+ let result = self.rl8(self.l());
+ self.set_l(result);
+ }
+ 0x116 => {
+ // RL (HL)
+ let result = self.rl8(self.hl_ind());
+ self.set_hl_ind(result);
+ }
+ 0x117 => {
+ // RL A
+ let result = self.rl8(self.a());
+ self.set_a(result);
+ }
+ 0x118 => {
+ // RR B
+ let result = self.rr8(self.b());
+ self.set_b(result);
+ }
+ 0x119 => {
+ // RR C
+ let result = self.rr8(self.c());
+ self.set_c(result);
+ }
+ 0x11A => {
+ // RR D
+ let result = self.rr8(self.d());
+ self.set_d(result);
+ }
+ 0x11B => {
+ // RR E
+ let result = self.rr8(self.e());
+ self.set_e(result);
+ }
+ 0x11C => {
+ // RR H
+ let result = self.rr8(self.h());
+ self.set_h(result);
+ }
+ 0x11D => {
+ // RR L
+ let result = self.rr8(self.l());
+ self.set_l(result);
+ }
+ 0x11E => {
+ // RR (HL)
+ let result = self.rr8(self.hl_ind());
+ self.set_hl_ind(result);
+ }
+ 0x11F => {
+ // RR A
+ let result = self.rr8(self.a());
+ self.set_a(result);
+ }
+ 0x120 => {
+ // SLA B
+ let result = self.sla8(self.b());
+ self.set_b(result);
+ }
+ 0x121 => {
+ // SLA C
+ let result = self.sla8(self.c());
+ self.set_c(result);
+ }
+ 0x122 => {
+ // SLA D
+ let result = self.sla8(self.d());
+ self.set_d(result);
+ }
+ 0x123 => {
+ // SLA E
+ let result = self.sla8(self.e());
+ self.set_e(result);
+ }
+ 0x124 => {
+ // SLA H
+ let result = self.sla8(self.h());
+ self.set_h(result);
+ }
+ 0x125 => {
+ // SLA L
+ let result = self.sla8(self.l());
+ self.set_l(result);
+ }
+ 0x126 => {
+ // SLA (HL)
+ let result = self.sla8(self.hl_ind());
+ self.set_hl_ind(result);
+ }
+ 0x127 => {
+ // SLA A
+ let result = self.sla8(self.a());
+ self.set_a(result);
+ }
+ 0x128 => {
+ // SRA B
+ let result = self.sra8(self.b());
+ self.set_b(result);
+ }
+ 0x129 => {
+ // SRA C
+ let result = self.sra8(self.c());
+ self.set_c(result);
+ }
+ 0x12A => {
+ // SRA D
+ let result = self.sra8(self.d());
+ self.set_d(result);
+ }
+ 0x12B => {
+ // SRA E
+ let result = self.sra8(self.e());
+ self.set_e(result);
+ }
+ 0x12C => {
+ // SRA H
+ let result = self.sra8(self.h());
+ self.set_h(result);
+ }
+ 0x12D => {
+ // SRA L
+ let result = self.sra8(self.l());
+ self.set_l(result);
+ }
+ 0x12E => {
+ // SRA (HL)
+ let result = self.sra8(self.hl_ind());
+ self.set_hl_ind(result);
+ }
+ 0x12F => {
+ // SRA A
+ let result = self.sra8(self.a());
+ self.set_a(result);
+ }
+ 0x130 => {
+ // SWAP B
+ let result = self.swap8(self.b());
+ self.set_b(result);
+ }
+ 0x131 => {
+ // SWAP C
+ let result = self.swap8(self.c());
+ self.set_c(result);
+ }
+ 0x132 => {
+ // SWAP D
+ let result = self.swap8(self.d());
+ self.set_d(result);
+ }
+ 0x133 => {
+ // SWAP E
+ let result = self.swap8(self.e());
+ self.set_e(result);
+ }
+ 0x134 => {
+ // SWAP H
+ let result = self.swap8(self.h());
+ self.set_h(result);
+ }
+ 0x135 => {
+ // SWAP L
+ let result = self.swap8(self.l());
+ self.set_l(result);
+ }
+ 0x136 => {
+ // SWAP (HL)
+ let result = self.swap8(self.hl_ind());
+ self.set_hl_ind(result);
+ }
+ 0x137 => {
+ // SWAP A
+ let result = self.swap8(self.a());
+ self.set_a(result);
+ }
+ 0x138 => {
+ // SRL B
+ let result = self.srl8(self.b());
+ self.set_b(result);
+ }
+ 0x139 => {
+ // SRL C
+ let result = self.srl8(self.c());
+ self.set_c(result);
+ }
+ 0x13A => {
+ // SRL D
+ let result = self.srl8(self.d());
+ self.set_d(result);
+ }
+ 0x13B => {
+ // SRL E
+ let result = self.srl8(self.e());
+ self.set_e(result);
+ }
+ 0x13C => {
+ // SRL H
+ let result = self.srl8(self.h());
+ self.set_h(result);
+ }
+ 0x13D => {
+ // SRL L
+ let result = self.srl8(self.l());
+ self.set_l(result);
+ }
+ 0x13E => {
+ // SRL (HL)
+ let result = self.srl8(self.hl_ind());
+ self.set_hl_ind(result);
+ }
+ 0x13F => {
+ // SRL A
+ let result = self.srl8(self.a());
+ self.set_a(result);
+ }
+ 0x140 => {
+ // BIT 0,B
+ self.bit::<0>(self.b());
+ }
+ 0x141 => {
+ // BIT 0,C
+ self.bit::<0>(self.c());
+ }
+ 0x142 => {
+ // BIT 0,D
+ self.bit::<0>(self.d());
+ }
+ 0x143 => {
+ // BIT 0,E
+ self.bit::<0>(self.e());
+ }
+ 0x144 => {
+ // BIT 0,H
+ self.bit::<0>(self.h());
+ }
+ 0x145 => {
+ // BIT 0,L
+ self.bit::<0>(self.l());
+ }
+ 0x146 => {
+ // BIT 0,(HL)
+ self.bit::<0>(self.hl_ind());
+ }
+ 0x147 => {
+ // BIT 0,A
+ self.bit::<0>(self.a());
+ }
+ 0x148 => {
+ // BIT 1,B
+ self.bit::<1>(self.b());
+ }
+ 0x149 => {
+ // BIT 1,C
+ self.bit::<1>(self.c());
+ }
+ 0x14A => {
+ // BIT 1,D
+ self.bit::<1>(self.d());
+ }
+ 0x14B => {
+ // BIT 1,E
+ self.bit::<1>(self.e());
+ }
+ 0x14C => {
+ // BIT 1,H
+ self.bit::<1>(self.h());
+ }
+ 0x14D => {
+ // BIT 1,L
+ self.bit::<1>(self.l());
+ }
+ 0x14E => {
+ // BIT 1,(HL)
+ self.bit::<1>(self.hl_ind());
+ }
+ 0x14F => {
+ // BIT 1,A
+ self.bit::<1>(self.a());
+ }
+ 0x150 => {
+ // BIT 2,B
+ self.bit::<2>(self.b());
+ }
+ 0x151 => {
+ // BIT 2,C
+ self.bit::<2>(self.c());
+ }
+ 0x152 => {
+ // BIT 2,D
+ self.bit::<2>(self.d());
+ }
+ 0x153 => {
+ // BIT 2,E
+ self.bit::<2>(self.e());
+ }
+ 0x154 => {
+ // BIT 2,H
+ self.bit::<2>(self.h());
+ }
+ 0x155 => {
+ // BIT 2,L
+ self.bit::<2>(self.l());
+ }
+ 0x156 => {
+ // BIT 2,(HL)
+ self.bit::<2>(self.hl_ind());
+ }
+ 0x157 => {
+ // BIT 2,A
+ self.bit::<2>(self.a());
+ }
+ 0x158 => {
+ // BIT 3,B
+ self.bit::<3>(self.b());
+ }
+ 0x159 => {
+ // BIT 3,C
+ self.bit::<3>(self.c());
+ }
+ 0x15A => {
+ // BIT 3,D
+ self.bit::<3>(self.d());
+ }
+ 0x15B => {
+ // BIT 3,E
+ self.bit::<3>(self.e());
+ }
+ 0x15C => {
+ // BIT 3,H
+ self.bit::<3>(self.h());
+ }
+ 0x15D => {
+ // BIT 3,L
+ self.bit::<3>(self.l());
+ }
+ 0x15E => {
+ // BIT 3,(HL)
+ self.bit::<3>(self.hl_ind());
+ }
+ 0x15F => {
+ // BIT 3,A
+ self.bit::<3>(self.a());
+ }
+ 0x160 => {
+ // BIT 4,B
+ self.bit::<4>(self.b());
+ }
+ 0x161 => {
+ // BIT 4,C
+ self.bit::<4>(self.c());
+ }
+ 0x162 => {
+ // BIT 4,D
+ self.bit::<4>(self.d());
+ }
+ 0x163 => {
+ // BIT 4,E
+ self.bit::<4>(self.e());
+ }
+ 0x164 => {
+ // BIT 4,H
+ self.bit::<4>(self.h());
+ }
+ 0x165 => {
+ // BIT 4,L
+ self.bit::<4>(self.l());
+ }
+ 0x166 => {
+ // BIT 4,(HL)
+ self.bit::<4>(self.hl_ind());
+ }
+ 0x167 => {
+ // BIT 4,A
+ self.bit::<4>(self.a());
+ }
+ 0x168 => {
+ // BIT 5,B
+ self.bit::<5>(self.b());
+ }
+ 0x169 => {
+ // BIT 5,C
+ self.bit::<5>(self.c());
+ }
+ 0x16A => {
+ // BIT 5,D
+ self.bit::<5>(self.d());
+ }
+ 0x16B => {
+ // BIT 5,E
+ self.bit::<5>(self.e());
+ }
+ 0x16C => {
+ // BIT 5,H
+ self.bit::<5>(self.h());
+ }
+ 0x16D => {
+ // BIT 5,L
+ self.bit::<5>(self.l());
+ }
+ 0x16E => {
+ // BIT 5,(HL)
+ self.bit::<5>(self.hl_ind());
+ }
+ 0x16F => {
+ // BIT 5,A
+ self.bit::<5>(self.a());
+ }
+ 0x170 => {
+ // BIT 6,B
+ self.bit::<6>(self.b());
+ }
+ 0x171 => {
+ // BIT 6,C
+ self.bit::<6>(self.c());
+ }
+ 0x172 => {
+ // BIT 6,D
+ self.bit::<6>(self.d());
+ }
+ 0x173 => {
+ // BIT 6,E
+ self.bit::<6>(self.e());
+ }
+ 0x174 => {
+ // BIT 6,H
+ self.bit::<6>(self.h());
+ }
+ 0x175 => {
+ // BIT 6,L
+ self.bit::<6>(self.l());
+ }
+ 0x176 => {
+ // BIT 6,(HL)
+ self.bit::<6>(self.hl_ind());
+ }
+ 0x177 => {
+ // BIT 6,A
+ self.bit::<6>(self.a());
+ }
+ 0x178 => {
+ // BIT 7,B
+ self.bit::<7>(self.b());
+ }
+ 0x179 => {
+ // BIT 7,C
+ self.bit::<7>(self.c());
+ }
+ 0x17A => {
+ // BIT 7,D
+ self.bit::<7>(self.d());
+ }
+ 0x17B => {
+ // BIT 7,E
+ self.bit::<7>(self.e());
+ }
+ 0x17C => {
+ // BIT 7,H
+ self.bit::<7>(self.h());
+ }
+ 0x17D => {
+ // BIT 7,L
+ self.bit::<7>(self.l());
+ }
+ 0x17E => {
+ // BIT 7,(HL)
+ self.bit::<7>(self.hl_ind());
+ }
+ 0x17F => {
+ // BIT 7,A
+ self.bit::<7>(self.a());
+ }
+ 0x180 => {
+ // RES 0,B
+ self.set_b(bw::set_bit8::<0>(self.b(), false));
+ }
+ 0x181 => {
+ // RES 0,C
+ self.set_c(bw::set_bit8::<0>(self.c(), false));
+ }
+ 0x182 => {
+ // RES 0,D
+ self.set_d(bw::set_bit8::<0>(self.d(), false));
+ }
+ 0x183 => {
+ // RES 0,E
+ self.set_e(bw::set_bit8::<0>(self.e(), false));
+ }
+ 0x184 => {
+ // RES 0,H
+ self.set_h(bw::set_bit8::<0>(self.h(), false));
+ }
+ 0x185 => {
+ // RES 0,L
+ self.set_l(bw::set_bit8::<0>(self.l(), false));
+ }
+ 0x186 => {
+ // RES 0,(HL)
+ self.set_mem8(self.hl(), bw::set_bit8::<0>(self.hl_ind(), false));
+ }
+ 0x187 => {
+ // RES 0,A
+ self.set_a(bw::set_bit8::<0>(self.a(), false));
+ }
+ 0x188 => {
+ // RES 1,B
+ self.set_b(bw::set_bit8::<1>(self.b(), false));
+ }
+ 0x189 => {
+ // RES 1,C
+ self.set_c(bw::set_bit8::<1>(self.c(), false));
+ }
+ 0x18A => {
+ // RES 1,D
+ self.set_d(bw::set_bit8::<1>(self.d(), false));
+ }
+ 0x18B => {
+ // RES 1,E
+ self.set_e(bw::set_bit8::<1>(self.e(), false));
+ }
+ 0x18C => {
+ // RES 1,H
+ self.set_h(bw::set_bit8::<1>(self.h(), false));
+ }
+ 0x18D => {
+ // RES 1,L
+ self.set_l(bw::set_bit8::<1>(self.l(), false));
+ }
+ 0x18E => {
+ // RES 1,(HL)
+ self.set_mem8(self.hl(), bw::set_bit8::<1>(self.hl_ind(), false));
+ }
+ 0x18F => {
+ // RES 1,A
+ self.set_a(bw::set_bit8::<1>(self.a(), false));
+ }
+ 0x190 => {
+ // RES 2,B
+ self.set_b(bw::set_bit8::<2>(self.b(), false));
+ }
+ 0x191 => {
+ // RES 2,C
+ self.set_c(bw::set_bit8::<2>(self.c(), false));
+ }
+ 0x192 => {
+ // RES 2,D
+ self.set_d(bw::set_bit8::<2>(self.d(), false));
+ }
+ 0x193 => {
+ // RES 2,E
+ self.set_e(bw::set_bit8::<2>(self.e(), false));
+ }
+ 0x194 => {
+ // RES 2,H
+ self.set_h(bw::set_bit8::<2>(self.h(), false));
+ }
+ 0x195 => {
+ // RES 2,L
+ self.set_l(bw::set_bit8::<2>(self.l(), false));
+ }
+ 0x196 => {
+ // RES 2,(HL)
+ self.set_mem8(self.hl(), bw::set_bit8::<2>(self.hl_ind(), false));
+ }
+ 0x197 => {
+ // RES 2,A
+ self.set_a(bw::set_bit8::<2>(self.a(), false));
+ }
+ 0x198 => {
+ // RES 3,B
+ self.set_b(bw::set_bit8::<3>(self.b(), false));
+ }
+ 0x199 => {
+ // RES 3,C
+ self.set_c(bw::set_bit8::<3>(self.c(), false));
+ }
+ 0x19A => {
+ // RES 3,D
+ self.set_d(bw::set_bit8::<3>(self.d(), false));
+ }
+ 0x19B => {
+ // RES 3,E
+ self.set_e(bw::set_bit8::<3>(self.e(), false));
+ }
+ 0x19C => {
+ // RES 3,H
+ self.set_h(bw::set_bit8::<3>(self.h(), false));
+ }
+ 0x19D => {
+ // RES 3,L
+ self.set_l(bw::set_bit8::<3>(self.l(), false));
+ }
+ 0x19E => {
+ // RES 3,(HL)
+ self.set_mem8(self.hl(), bw::set_bit8::<3>(self.hl_ind(), false));
+ }
+ 0x19F => {
+ // RES 3,A
+ self.set_a(bw::set_bit8::<3>(self.a(), false));
+ }
+ 0x1A0 => {
+ // RES 4,B
+ self.set_b(bw::set_bit8::<4>(self.b(), false));
+ }
+ 0x1A1 => {
+ // RES 4,C
+ self.set_c(bw::set_bit8::<4>(self.c(), false));
+ }
+ 0x1A2 => {
+ // RES 4,D
+ self.set_d(bw::set_bit8::<4>(self.d(), false));
+ }
+ 0x1A3 => {
+ // RES 4,E
+ self.set_e(bw::set_bit8::<4>(self.e(), false));
+ }
+ 0x1A4 => {
+ // RES 4,H
+ self.set_h(bw::set_bit8::<4>(self.h(), false));
+ }
+ 0x1A5 => {
+ // RES 4,L
+ self.set_l(bw::set_bit8::<4>(self.l(), false));
+ }
+ 0x1A6 => {
+ // RES 4,(HL)
+ self.set_mem8(self.hl(), bw::set_bit8::<4>(self.hl_ind(), false));
+ }
+ 0x1A7 => {
+ // RES 4,A
+ self.set_a(bw::set_bit8::<4>(self.a(), false));
+ }
+ 0x1A8 => {
+ // RES 5,B
+ self.set_b(bw::set_bit8::<5>(self.b(), false));
+ }
+ 0x1A9 => {
+ // RES 5,C
+ self.set_c(bw::set_bit8::<5>(self.c(), false));
+ }
+ 0x1AA => {
+ // RES 5,D
+ self.set_d(bw::set_bit8::<5>(self.d(), false));
+ }
+ 0x1AB => {
+ // RES 5,E
+ self.set_e(bw::set_bit8::<5>(self.e(), false));
+ }
+ 0x1AC => {
+ // RES 5,H
+ self.set_h(bw::set_bit8::<5>(self.h(), false));
+ }
+ 0x1AD => {
+ // RES 5,L
+ self.set_l(bw::set_bit8::<5>(self.l(), false));
+ }
+ 0x1AE => {
+ // RES 5,(HL)
+ self.set_mem8(self.hl(), bw::set_bit8::<5>(self.hl_ind(), false));
+ }
+ 0x1AF => {
+ // RES 5,A
+ self.set_a(bw::set_bit8::<5>(self.a(), false));
+ }
+ 0x1B0 => {
+ // RES 6,B
+ self.set_b(bw::set_bit8::<6>(self.b(), false));
+ }
+ 0x1B1 => {
+ // RES 6,C
+ self.set_c(bw::set_bit8::<6>(self.c(), false));
+ }
+ 0x1B2 => {
+ // RES 6,D
+ self.set_d(bw::set_bit8::<6>(self.d(), false));
+ }
+ 0x1B3 => {
+ // RES 6,E
+ self.set_e(bw::set_bit8::<6>(self.e(), false));
+ }
+ 0x1B4 => {
+ // RES 6,H
+ self.set_h(bw::set_bit8::<6>(self.h(), false));
+ }
+ 0x1B5 => {
+ // RES 6,L
+ self.set_l(bw::set_bit8::<6>(self.l(), false));
+ }
+ 0x1B6 => {
+ // RES 6,(HL)
+ self.set_mem8(self.hl(), bw::set_bit8::<6>(self.hl_ind(), false));
+ }
+ 0x1B7 => {
+ // RES 6,A
+ self.set_a(bw::set_bit8::<6>(self.a(), false));
+ }
+ 0x1B8 => {
+ // RES 7,B
+ self.set_b(bw::set_bit8::<7>(self.b(), false));
+ }
+ 0x1B9 => {
+ // RES 7,C
+ self.set_c(bw::set_bit8::<7>(self.c(), false));
+ }
+ 0x1BA => {
+ // RES 7,D
+ self.set_d(bw::set_bit8::<7>(self.d(), false));
+ }
+ 0x1BB => {
+ // RES 7,E
+ self.set_e(bw::set_bit8::<7>(self.e(), false));
+ }
+ 0x1BC => {
+ // RES 7,H
+ self.set_h(bw::set_bit8::<7>(self.h(), false));
+ }
+ 0x1BD => {
+ // RES 7,L
+ self.set_l(bw::set_bit8::<7>(self.l(), false));
+ }
+ 0x1BE => {
+ // RES 7,(HL)
+ self.set_mem8(self.hl(), bw::set_bit8::<7>(self.hl_ind(), false));
+ }
+ 0x1BF => {
+ // RES 7,A
+ self.set_a(bw::set_bit8::<7>(self.a(), false));
+ }
+ 0x1C0 => {
+ // SET 0,B
+ self.set_b(bw::set_bit8::<0>(self.b(), true));
+ }
+ 0x1C1 => {
+ // SET 0,C
+ self.set_c(bw::set_bit8::<0>(self.c(), true));
+ }
+ 0x1C2 => {
+ // SET 0,D
+ self.set_d(bw::set_bit8::<0>(self.d(), true));
+ }
+ 0x1C3 => {
+ // SET 0,E
+ self.set_e(bw::set_bit8::<0>(self.e(), true));
+ }
+ 0x1C4 => {
+ // SET 0,H
+ self.set_h(bw::set_bit8::<0>(self.h(), true));
+ }
+ 0x1C5 => {
+ // SET 0,L
+ self.set_l(bw::set_bit8::<0>(self.l(), true));
+ }
+ 0x1C6 => {
+ // SET 0,(HL)
+ self.set_mem8(self.hl(), bw::set_bit8::<0>(self.hl_ind(), true));
+ }
+ 0x1C7 => {
+ // SET 0,A
+ self.set_a(bw::set_bit8::<0>(self.a(), true));
+ }
+ 0x1C8 => {
+ // SET 1,B
+ self.set_b(bw::set_bit8::<1>(self.b(), true));
+ }
+ 0x1C9 => {
+ // SET 1,C
+ self.set_c(bw::set_bit8::<1>(self.c(), true));
+ }
+ 0x1CA => {
+ // SET 1,D
+ self.set_d(bw::set_bit8::<1>(self.d(), true));
+ }
+ 0x1CB => {
+ // SET 1,E
+ self.set_e(bw::set_bit8::<1>(self.e(), true));
+ }
+ 0x1CC => {
+ // SET 1,H
+ self.set_h(bw::set_bit8::<1>(self.h(), true));
+ }
+ 0x1CD => {
+ // SET 1,L
+ self.set_l(bw::set_bit8::<1>(self.l(), true));
+ }
+ 0x1CE => {
+ // SET 1,(HL)
+ self.set_mem8(self.hl(), bw::set_bit8::<1>(self.hl_ind(), true));
+ }
+ 0x1CF => {
+ // SET 1,A
+ self.set_a(bw::set_bit8::<1>(self.a(), true));
+ }
+ 0x1D0 => {
+ // SET 2,B
+ self.set_b(bw::set_bit8::<2>(self.b(), true));
+ }
+ 0x1D1 => {
+ // SET 2,C
+ self.set_c(bw::set_bit8::<2>(self.c(), true));
+ }
+ 0x1D2 => {
+ // SET 2,D
+ self.set_d(bw::set_bit8::<2>(self.d(), true));
+ }
+ 0x1D3 => {
+ // SET 2,E
+ self.set_e(bw::set_bit8::<2>(self.e(), true));
+ }
+ 0x1D4 => {
+ // SET 2,H
+ self.set_h(bw::set_bit8::<2>(self.h(), true));
+ }
+ 0x1D5 => {
+ // SET 2,L
+ self.set_l(bw::set_bit8::<2>(self.l(), true));
+ }
+ 0x1D6 => {
+ // SET 2,(HL)
+ self.set_mem8(self.hl(), bw::set_bit8::<2>(self.hl_ind(), true));
+ }
+ 0x1D7 => {
+ // SET 2,A
+ self.set_a(bw::set_bit8::<2>(self.a(), true));
+ }
+ 0x1D8 => {
+ // SET 3,B
+ self.set_b(bw::set_bit8::<3>(self.b(), true));
+ }
+ 0x1D9 => {
+ // SET 3,C
+ self.set_c(bw::set_bit8::<3>(self.c(), true));
+ }
+ 0x1DA => {
+ // SET 3,D
+ self.set_d(bw::set_bit8::<3>(self.d(), true));
+ }
+ 0x1DB => {
+ // SET 3,E
+ self.set_e(bw::set_bit8::<3>(self.e(), true));
+ }
+ 0x1DC => {
+ // SET 3,H
+ self.set_h(bw::set_bit8::<3>(self.h(), true));
+ }
+ 0x1DD => {
+ // SET 3,L
+ self.set_l(bw::set_bit8::<3>(self.l(), true));
+ }
+ 0x1DE => {
+ // SET 3,(HL)
+ self.set_mem8(self.hl(), bw::set_bit8::<3>(self.hl_ind(), true));
+ }
+ 0x1DF => {
+ // SET 3,A
+ self.set_a(bw::set_bit8::<3>(self.a(), true));
+ }
+ 0x1E0 => {
+ // SET 4,B
+ self.set_b(bw::set_bit8::<4>(self.b(), true));
+ }
+ 0x1E1 => {
+ // SET 4,C
+ self.set_c(bw::set_bit8::<4>(self.c(), true));
+ }
+ 0x1E2 => {
+ // SET 4,D
+ self.set_d(bw::set_bit8::<4>(self.d(), true));
+ }
+ 0x1E3 => {
+ // SET 4,E
+ self.set_e(bw::set_bit8::<4>(self.e(), true));
+ }
+ 0x1E4 => {
+ // SET 4,H
+ self.set_h(bw::set_bit8::<4>(self.h(), true));
+ }
+ 0x1E5 => {
+ // SET 4,L
+ self.set_l(bw::set_bit8::<4>(self.l(), true));
+ }
+ 0x1E6 => {
+ // SET 4,(HL)
+ self.set_mem8(self.hl(), bw::set_bit8::<4>(self.hl_ind(), true));
+ }
+ 0x1E7 => {
+ // SET 4,A
+ self.set_a(bw::set_bit8::<4>(self.a(), true));
+ }
+ 0x1E8 => {
+ // SET 5,B
+ self.set_b(bw::set_bit8::<5>(self.b(), true));
+ }
+ 0x1E9 => {
+ // SET 5,C
+ self.set_c(bw::set_bit8::<5>(self.c(), true));
+ }
+ 0x1EA => {
+ // SET 5,D
+ self.set_d(bw::set_bit8::<5>(self.d(), true));
+ }
+ 0x1EB => {
+ // SET 5,E
+ self.set_e(bw::set_bit8::<5>(self.e(), true));
+ }
+ 0x1EC => {
+ // SET 5,H
+ self.set_h(bw::set_bit8::<5>(self.h(), true));
+ }
+ 0x1ED => {
+ // SET 5,L
+ self.set_l(bw::set_bit8::<5>(self.l(), true));
+ }
+ 0x1EE => {
+ // SET 5,(HL)
+ self.set_mem8(self.hl(), bw::set_bit8::<5>(self.hl_ind(), true));
+ }
+ 0x1EF => {
+ // SET 5,A
+ self.set_a(bw::set_bit8::<5>(self.a(), true));
+ }
+ 0x1F0 => {
+ // SET 6,B
+ self.set_b(bw::set_bit8::<6>(self.b(), true));
+ }
+ 0x1F1 => {
+ // SET 6,C
+ self.set_c(bw::set_bit8::<6>(self.c(), true));
+ }
+ 0x1F2 => {
+ // SET 6,D
+ self.set_d(bw::set_bit8::<6>(self.d(), true));
+ }
+ 0x1F3 => {
+ // SET 6,E
+ self.set_e(bw::set_bit8::<6>(self.e(), true));
+ }
+ 0x1F4 => {
+ // SET 6,H
+ self.set_h(bw::set_bit8::<6>(self.h(), true));
+ }
+ 0x1F5 => {
+ // SET 6,L
+ self.set_l(bw::set_bit8::<6>(self.l(), true));
+ }
+ 0x1F6 => {
+ // SET 6,(HL)
+ self.set_mem8(self.hl(), bw::set_bit8::<6>(self.hl_ind(), true));
+ }
+ 0x1F7 => {
+ // SET 6,A
+ self.set_a(bw::set_bit8::<6>(self.a(), true));
+ }
+ 0x1F8 => {
+ // SET 7,B
+ self.set_b(bw::set_bit8::<7>(self.b(), true));
+ }
+ 0x1F9 => {
+ // SET 7,C
+ self.set_c(bw::set_bit8::<7>(self.c(), true));
+ }
+ 0x1FA => {
+ // SET 7,D
+ self.set_d(bw::set_bit8::<7>(self.d(), true));
+ }
+ 0x1FB => {
+ // SET 7,E
+ self.set_e(bw::set_bit8::<7>(self.e(), true));
+ }
+ 0x1FC => {
+ // SET 7,H
+ self.set_h(bw::set_bit8::<7>(self.h(), true));
+ }
+ 0x1FD => {
+ // SET 7,L
+ self.set_l(bw::set_bit8::<7>(self.l(), true));
+ }
+ 0x1FE => {
+ // SET 7,(HL)
+ self.set_mem8(self.hl(), bw::set_bit8::<7>(self.hl_ind(), true));
+ }
+ 0x1FF => {
+ // SET 7,A
+ self.set_a(bw::set_bit8::<7>(self.a(), true));
+ }
+ i => {
+ unimplemented!("no idea what opcode {i} is")
+ }
+ }
+ }
+}
diff --git a/fpt/src/lr35902/instructions.rs b/fpt/src/lr35902/instructions.rs
new file mode 100644
index 0000000..b3021c6
--- /dev/null
+++ b/fpt/src/lr35902/instructions.rs
@@ -0,0 +1,4134 @@
+#[derive(Clone, Copy, PartialEq, Debug)]
+pub struct Instruction {
+ pub opcode: u16,
+ pub mnemonic: &'static str,
+ pub size: u8,
+ pub cycles: u8,
+ pub cycles_not_taken: u8,
+ pub kind: InstructionKind,
+}
+
+impl Default for Instruction {
+ fn default() -> Self {
+ Self {
+ opcode: 0,
+ mnemonic: "",
+ size: 0,
+ cycles: 0,
+ cycles_not_taken: 0,
+ kind: InstructionKind::NI,
+ }
+ }
+}
+
+#[derive(Clone, Copy, PartialEq, Debug)]
+pub enum InstructionKind {
+ Control,
+ Jump,
+ LSM8Bit, // Load/Store/Move
+ LSM16Bit,
+ AL8Bit, // Arithmetic/Logic
+ AL16Bit,
+ RSB8Bit, // Rotate, Shift and Bit
+ NI, // Not Implemented
+}
+
+// TODO: use better table json (better mnemonics) to gen code
+pub static INSTRUCTIONS: [Instruction; 512] = [
+ Instruction {
+ opcode: 0x00,
+ mnemonic: "NOP",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::Control,
+ },
+ Instruction {
+ opcode: 0x01,
+ mnemonic: "LD BC,d16",
+ size: 3,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM16Bit,
+ },
+ Instruction {
+ opcode: 0x02,
+ mnemonic: "LD (BC),A",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x03,
+ mnemonic: "INC BC",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL16Bit,
+ },
+ Instruction {
+ opcode: 0x04,
+ mnemonic: "INC B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x05,
+ mnemonic: "DEC B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x06,
+ mnemonic: "LD B,d8",
+ size: 2,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x07,
+ mnemonic: "RLCA",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x08,
+ mnemonic: "LD (a16),SP",
+ size: 3,
+ cycles: 20,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM16Bit,
+ },
+ Instruction {
+ opcode: 0x09,
+ mnemonic: "ADD HL,BC",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL16Bit,
+ },
+ Instruction {
+ opcode: 0x0A,
+ mnemonic: "LD A,(BC)",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x0B,
+ mnemonic: "DEC BC",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL16Bit,
+ },
+ Instruction {
+ opcode: 0x0C,
+ mnemonic: "INC C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x0D,
+ mnemonic: "DEC C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x0E,
+ mnemonic: "LD C,d8",
+ size: 2,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x0F,
+ mnemonic: "RRCA",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x10,
+ mnemonic: "STOP 0",
+ size: 2,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::Control,
+ },
+ Instruction {
+ opcode: 0x11,
+ mnemonic: "LD DE,d16",
+ size: 3,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM16Bit,
+ },
+ Instruction {
+ opcode: 0x12,
+ mnemonic: "LD (DE),A",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x13,
+ mnemonic: "INC DE",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL16Bit,
+ },
+ Instruction {
+ opcode: 0x14,
+ mnemonic: "INC D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x15,
+ mnemonic: "DEC D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x16,
+ mnemonic: "LD D,d8",
+ size: 2,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x17,
+ mnemonic: "RLA",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x18,
+ mnemonic: "JR r8",
+ size: 2,
+ cycles: 12,
+ cycles_not_taken: 12,
+ kind: InstructionKind::Jump,
+ },
+ Instruction {
+ opcode: 0x19,
+ mnemonic: "ADD HL,DE",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL16Bit,
+ },
+ Instruction {
+ opcode: 0x1A,
+ mnemonic: "LD A,(DE)",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x1B,
+ mnemonic: "DEC DE",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL16Bit,
+ },
+ Instruction {
+ opcode: 0x1C,
+ mnemonic: "INC E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x1D,
+ mnemonic: "DEC E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x1E,
+ mnemonic: "LD E,d8",
+ size: 2,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x1F,
+ mnemonic: "RRA",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x20,
+ mnemonic: "JR NZ,r8",
+ size: 2,
+ cycles: 12,
+ cycles_not_taken: 8,
+ kind: InstructionKind::Jump,
+ },
+ Instruction {
+ opcode: 0x21,
+ mnemonic: "LD HL,d16",
+ size: 3,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM16Bit,
+ },
+ Instruction {
+ opcode: 0x22,
+ mnemonic: "LD (HL+),A",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x23,
+ mnemonic: "INC HL",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL16Bit,
+ },
+ Instruction {
+ opcode: 0x24,
+ mnemonic: "INC H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x25,
+ mnemonic: "DEC H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x26,
+ mnemonic: "LD H,d8",
+ size: 2,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x27,
+ mnemonic: "DAA",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x28,
+ mnemonic: "JR Z,r8",
+ size: 2,
+ cycles: 12,
+ cycles_not_taken: 8,
+ kind: InstructionKind::Jump,
+ },
+ Instruction {
+ opcode: 0x29,
+ mnemonic: "ADD HL,HL",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL16Bit,
+ },
+ Instruction {
+ opcode: 0x2A,
+ mnemonic: "LD A,(HL+)",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x2B,
+ mnemonic: "DEC HL",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL16Bit,
+ },
+ Instruction {
+ opcode: 0x2C,
+ mnemonic: "INC L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x2D,
+ mnemonic: "DEC L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x2E,
+ mnemonic: "LD L,d8",
+ size: 2,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x2F,
+ mnemonic: "CPL",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x30,
+ mnemonic: "JR NC,r8",
+ size: 2,
+ cycles: 12,
+ cycles_not_taken: 8,
+ kind: InstructionKind::Jump,
+ },
+ Instruction {
+ opcode: 0x31,
+ mnemonic: "LD SP,d16",
+ size: 3,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM16Bit,
+ },
+ Instruction {
+ opcode: 0x32,
+ mnemonic: "LD (HL-),A",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x33,
+ mnemonic: "INC SP",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL16Bit,
+ },
+ Instruction {
+ opcode: 0x34,
+ mnemonic: "INC (HL)",
+ size: 1,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x35,
+ mnemonic: "DEC (HL)",
+ size: 1,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x36,
+ mnemonic: "LD (HL),d8",
+ size: 2,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x37,
+ mnemonic: "SCF",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x38,
+ mnemonic: "JR C,r8",
+ size: 2,
+ cycles: 12,
+ cycles_not_taken: 8,
+ kind: InstructionKind::Jump,
+ },
+ Instruction {
+ opcode: 0x39,
+ mnemonic: "ADD HL,SP",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL16Bit,
+ },
+ Instruction {
+ opcode: 0x3A,
+ mnemonic: "LD A,(HL-)",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x3B,
+ mnemonic: "DEC SP",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL16Bit,
+ },
+ Instruction {
+ opcode: 0x3C,
+ mnemonic: "INC A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x3D,
+ mnemonic: "DEC A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x3E,
+ mnemonic: "LD A,d8",
+ size: 2,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x3F,
+ mnemonic: "CCF",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x40,
+ mnemonic: "LD B,B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x41,
+ mnemonic: "LD B,C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x42,
+ mnemonic: "LD B,D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x43,
+ mnemonic: "LD B,E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x44,
+ mnemonic: "LD B,H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x45,
+ mnemonic: "LD B,L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x46,
+ mnemonic: "LD B,(HL)",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x47,
+ mnemonic: "LD B,A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x48,
+ mnemonic: "LD C,B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x49,
+ mnemonic: "LD C,C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x4A,
+ mnemonic: "LD C,D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x4B,
+ mnemonic: "LD C,E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x4C,
+ mnemonic: "LD C,H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x4D,
+ mnemonic: "LD C,L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x4E,
+ mnemonic: "LD C,(HL)",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x4F,
+ mnemonic: "LD C,A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x50,
+ mnemonic: "LD D,B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x51,
+ mnemonic: "LD D,C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x52,
+ mnemonic: "LD D,D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x53,
+ mnemonic: "LD D,E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x54,
+ mnemonic: "LD D,H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x55,
+ mnemonic: "LD D,L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x56,
+ mnemonic: "LD D,(HL)",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x57,
+ mnemonic: "LD D,A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x58,
+ mnemonic: "LD E,B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x59,
+ mnemonic: "LD E,C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x5A,
+ mnemonic: "LD E,D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x5B,
+ mnemonic: "LD E,E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x5C,
+ mnemonic: "LD E,H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x5D,
+ mnemonic: "LD E,L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x5E,
+ mnemonic: "LD E,(HL)",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x5F,
+ mnemonic: "LD E,A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x60,
+ mnemonic: "LD H,B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x61,
+ mnemonic: "LD H,C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x62,
+ mnemonic: "LD H,D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x63,
+ mnemonic: "LD H,E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x64,
+ mnemonic: "LD H,H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x65,
+ mnemonic: "LD H,L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x66,
+ mnemonic: "LD H,(HL)",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x67,
+ mnemonic: "LD H,A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x68,
+ mnemonic: "LD L,B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x69,
+ mnemonic: "LD L,C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x6A,
+ mnemonic: "LD L,D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x6B,
+ mnemonic: "LD L,E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x6C,
+ mnemonic: "LD L,H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x6D,
+ mnemonic: "LD L,L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x6E,
+ mnemonic: "LD L,(HL)",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x6F,
+ mnemonic: "LD L,A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x70,
+ mnemonic: "LD (HL),B",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x71,
+ mnemonic: "LD (HL),C",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x72,
+ mnemonic: "LD (HL),D",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x73,
+ mnemonic: "LD (HL),E",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x74,
+ mnemonic: "LD (HL),H",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x75,
+ mnemonic: "LD (HL),L",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x76,
+ mnemonic: "HALT",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::Control,
+ },
+ Instruction {
+ opcode: 0x77,
+ mnemonic: "LD (HL),A",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x78,
+ mnemonic: "LD A,B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x79,
+ mnemonic: "LD A,C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x7A,
+ mnemonic: "LD A,D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x7B,
+ mnemonic: "LD A,E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x7C,
+ mnemonic: "LD A,H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x7D,
+ mnemonic: "LD A,L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x7E,
+ mnemonic: "LD A,(HL)",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x7F,
+ mnemonic: "LD A,A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0x80,
+ mnemonic: "ADD A,B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x81,
+ mnemonic: "ADD A,C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x82,
+ mnemonic: "ADD A,D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x83,
+ mnemonic: "ADD A,E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x84,
+ mnemonic: "ADD A,H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x85,
+ mnemonic: "ADD A,L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x86,
+ mnemonic: "ADD A,(HL)",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x87,
+ mnemonic: "ADD A,A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x88,
+ mnemonic: "ADC A,B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x89,
+ mnemonic: "ADC A,C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x8A,
+ mnemonic: "ADC A,D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x8B,
+ mnemonic: "ADC A,E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x8C,
+ mnemonic: "ADC A,H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x8D,
+ mnemonic: "ADC A,L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x8E,
+ mnemonic: "ADC A,(HL)",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x8F,
+ mnemonic: "ADC A,A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x90,
+ mnemonic: "SUB B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x91,
+ mnemonic: "SUB C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x92,
+ mnemonic: "SUB D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x93,
+ mnemonic: "SUB E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x94,
+ mnemonic: "SUB H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x95,
+ mnemonic: "SUB L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x96,
+ mnemonic: "SUB (HL)",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x97,
+ mnemonic: "SUB A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x98,
+ mnemonic: "SBC A,B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x99,
+ mnemonic: "SBC A,C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x9A,
+ mnemonic: "SBC A,D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x9B,
+ mnemonic: "SBC A,E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x9C,
+ mnemonic: "SBC A,H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x9D,
+ mnemonic: "SBC A,L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x9E,
+ mnemonic: "SBC A,(HL)",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0x9F,
+ mnemonic: "SBC A,A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xA0,
+ mnemonic: "AND B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xA1,
+ mnemonic: "AND C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xA2,
+ mnemonic: "AND D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xA3,
+ mnemonic: "AND E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xA4,
+ mnemonic: "AND H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xA5,
+ mnemonic: "AND L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xA6,
+ mnemonic: "AND (HL)",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xA7,
+ mnemonic: "AND A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xA8,
+ mnemonic: "XOR B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xA9,
+ mnemonic: "XOR C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xAA,
+ mnemonic: "XOR D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xAB,
+ mnemonic: "XOR E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xAC,
+ mnemonic: "XOR H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xAD,
+ mnemonic: "XOR L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xAE,
+ mnemonic: "XOR (HL)",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xAF,
+ mnemonic: "XOR A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xB0,
+ mnemonic: "OR B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xB1,
+ mnemonic: "OR C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xB2,
+ mnemonic: "OR D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xB3,
+ mnemonic: "OR E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xB4,
+ mnemonic: "OR H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xB5,
+ mnemonic: "OR L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xB6,
+ mnemonic: "OR (HL)",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xB7,
+ mnemonic: "OR A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xB8,
+ mnemonic: "CP B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xB9,
+ mnemonic: "CP C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xBA,
+ mnemonic: "CP D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xBB,
+ mnemonic: "CP E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xBC,
+ mnemonic: "CP H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xBD,
+ mnemonic: "CP L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xBE,
+ mnemonic: "CP (HL)",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xBF,
+ mnemonic: "CP A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xC0,
+ mnemonic: "RET NZ",
+ size: 1,
+ cycles: 20,
+ cycles_not_taken: 8,
+ kind: InstructionKind::Jump,
+ },
+ Instruction {
+ opcode: 0xC1,
+ mnemonic: "POP BC",
+ size: 1,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM16Bit,
+ },
+ Instruction {
+ opcode: 0xC2,
+ mnemonic: "JP NZ,a16",
+ size: 3,
+ cycles: 16,
+ cycles_not_taken: 12,
+ kind: InstructionKind::Jump,
+ },
+ Instruction {
+ opcode: 0xC3,
+ mnemonic: "JP a16",
+ size: 3,
+ cycles: 16,
+ cycles_not_taken: 16,
+ kind: InstructionKind::Jump,
+ },
+ Instruction {
+ opcode: 0xC4,
+ mnemonic: "CALL NZ,a16",
+ size: 3,
+ cycles: 24,
+ cycles_not_taken: 12,
+ kind: InstructionKind::Jump,
+ },
+ Instruction {
+ opcode: 0xC5,
+ mnemonic: "PUSH BC",
+ size: 1,
+ cycles: 16,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM16Bit,
+ },
+ Instruction {
+ opcode: 0xC6,
+ mnemonic: "ADD A,d8",
+ size: 2,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xC7,
+ mnemonic: "RST 00H",
+ size: 1,
+ cycles: 16,
+ cycles_not_taken: 0,
+ kind: InstructionKind::Jump,
+ },
+ Instruction {
+ opcode: 0xC8,
+ mnemonic: "RET Z",
+ size: 1,
+ cycles: 20,
+ cycles_not_taken: 8,
+ kind: InstructionKind::Jump,
+ },
+ Instruction {
+ opcode: 0xC9,
+ mnemonic: "RET",
+ size: 1,
+ cycles: 16,
+ cycles_not_taken: 0,
+ kind: InstructionKind::Jump,
+ },
+ Instruction {
+ opcode: 0xCA,
+ mnemonic: "JP Z,a16",
+ size: 3,
+ cycles: 16,
+ cycles_not_taken: 12,
+ kind: InstructionKind::Jump,
+ },
+ Instruction {
+ opcode: 0xCB,
+ mnemonic: "PREFIX CB",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::Control,
+ },
+ Instruction {
+ opcode: 0xCC,
+ mnemonic: "CALL Z,a16",
+ size: 3,
+ cycles: 24,
+ cycles_not_taken: 12,
+ kind: InstructionKind::Jump,
+ },
+ Instruction {
+ opcode: 0xCD,
+ mnemonic: "CALL a16",
+ size: 3,
+ cycles: 24,
+ cycles_not_taken: 0,
+ kind: InstructionKind::Jump,
+ },
+ Instruction {
+ opcode: 0xCE,
+ mnemonic: "ADC A,d8",
+ size: 2,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xCF,
+ mnemonic: "RST 08H",
+ size: 1,
+ cycles: 16,
+ cycles_not_taken: 0,
+ kind: InstructionKind::Jump,
+ },
+ Instruction {
+ opcode: 0xD0,
+ mnemonic: "RET NC",
+ size: 1,
+ cycles: 20,
+ cycles_not_taken: 8,
+ kind: InstructionKind::Jump,
+ },
+ Instruction {
+ opcode: 0xD1,
+ mnemonic: "POP DE",
+ size: 1,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM16Bit,
+ },
+ Instruction {
+ opcode: 0xD2,
+ mnemonic: "JP NC,a16",
+ size: 3,
+ cycles: 16,
+ cycles_not_taken: 12,
+ kind: InstructionKind::Jump,
+ },
+ Instruction {
+ opcode: 0xD3,
+ mnemonic: "NI",
+ size: 0,
+ cycles: 0,
+ cycles_not_taken: 0,
+ kind: InstructionKind::NI,
+ },
+ Instruction {
+ opcode: 0xD4,
+ mnemonic: "CALL NC,a16",
+ size: 3,
+ cycles: 24,
+ cycles_not_taken: 12,
+ kind: InstructionKind::Jump,
+ },
+ Instruction {
+ opcode: 0xD5,
+ mnemonic: "PUSH DE",
+ size: 1,
+ cycles: 16,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM16Bit,
+ },
+ Instruction {
+ opcode: 0xD6,
+ mnemonic: "SUB d8",
+ size: 2,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xD7,
+ mnemonic: "RST 10H",
+ size: 1,
+ cycles: 16,
+ cycles_not_taken: 0,
+ kind: InstructionKind::Jump,
+ },
+ Instruction {
+ opcode: 0xD8,
+ mnemonic: "RET C",
+ size: 1,
+ cycles: 20,
+ cycles_not_taken: 8,
+ kind: InstructionKind::Jump,
+ },
+ Instruction {
+ opcode: 0xD9,
+ mnemonic: "RETI",
+ size: 1,
+ cycles: 16,
+ cycles_not_taken: 0,
+ kind: InstructionKind::Jump,
+ },
+ Instruction {
+ opcode: 0xDA,
+ mnemonic: "JP C,a16",
+ size: 3,
+ cycles: 16,
+ cycles_not_taken: 12,
+ kind: InstructionKind::Jump,
+ },
+ Instruction {
+ opcode: 0xDB,
+ mnemonic: "NI",
+ size: 0,
+ cycles: 0,
+ cycles_not_taken: 0,
+ kind: InstructionKind::NI,
+ },
+ Instruction {
+ opcode: 0xDC,
+ mnemonic: "CALL C,a16",
+ size: 3,
+ cycles: 24,
+ cycles_not_taken: 12,
+ kind: InstructionKind::Jump,
+ },
+ Instruction {
+ opcode: 0xDD,
+ mnemonic: "NI",
+ size: 0,
+ cycles: 0,
+ cycles_not_taken: 0,
+ kind: InstructionKind::NI,
+ },
+ Instruction {
+ opcode: 0xDE,
+ mnemonic: "SBC A,d8",
+ size: 2,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xDF,
+ mnemonic: "RST 18H",
+ size: 1,
+ cycles: 16,
+ cycles_not_taken: 0,
+ kind: InstructionKind::Jump,
+ },
+ Instruction {
+ opcode: 0xE0,
+ mnemonic: "LDH (a8),A",
+ size: 2,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0xE1,
+ mnemonic: "POP HL",
+ size: 1,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM16Bit,
+ },
+ Instruction {
+ opcode: 0xE2,
+ mnemonic: "LD (C),A",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0xE3,
+ mnemonic: "NI",
+ size: 0,
+ cycles: 0,
+ cycles_not_taken: 0,
+ kind: InstructionKind::NI,
+ },
+ Instruction {
+ opcode: 0xE4,
+ mnemonic: "NI",
+ size: 0,
+ cycles: 0,
+ cycles_not_taken: 0,
+ kind: InstructionKind::NI,
+ },
+ Instruction {
+ opcode: 0xE5,
+ mnemonic: "PUSH HL",
+ size: 1,
+ cycles: 16,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM16Bit,
+ },
+ Instruction {
+ opcode: 0xE6,
+ mnemonic: "AND d8",
+ size: 2,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xE7,
+ mnemonic: "RST 20H",
+ size: 1,
+ cycles: 16,
+ cycles_not_taken: 0,
+ kind: InstructionKind::Jump,
+ },
+ Instruction {
+ opcode: 0xE8,
+ mnemonic: "ADD SP,r8",
+ size: 2,
+ cycles: 16,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL16Bit,
+ },
+ Instruction {
+ opcode: 0xE9,
+ mnemonic: "JP (HL)",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::Jump,
+ },
+ Instruction {
+ opcode: 0xEA,
+ mnemonic: "LD (a16),A",
+ size: 3,
+ cycles: 16,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0xEB,
+ mnemonic: "NI",
+ size: 0,
+ cycles: 0,
+ cycles_not_taken: 0,
+ kind: InstructionKind::NI,
+ },
+ Instruction {
+ opcode: 0xEC,
+ mnemonic: "NI",
+ size: 0,
+ cycles: 0,
+ cycles_not_taken: 0,
+ kind: InstructionKind::NI,
+ },
+ Instruction {
+ opcode: 0xED,
+ mnemonic: "NI",
+ size: 0,
+ cycles: 0,
+ cycles_not_taken: 0,
+ kind: InstructionKind::NI,
+ },
+ Instruction {
+ opcode: 0xEE,
+ mnemonic: "XOR d8",
+ size: 2,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xEF,
+ mnemonic: "RST 28H",
+ size: 1,
+ cycles: 16,
+ cycles_not_taken: 0,
+ kind: InstructionKind::Jump,
+ },
+ Instruction {
+ opcode: 0xF0,
+ mnemonic: "LDH A,(a8)",
+ size: 2,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0xF1,
+ mnemonic: "POP AF",
+ size: 1,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM16Bit,
+ },
+ Instruction {
+ opcode: 0xF2,
+ mnemonic: "LD A,(C)",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0xF3,
+ mnemonic: "DI",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::Control,
+ },
+ Instruction {
+ opcode: 0xF4,
+ mnemonic: "NI",
+ size: 0,
+ cycles: 0,
+ cycles_not_taken: 0,
+ kind: InstructionKind::NI,
+ },
+ Instruction {
+ opcode: 0xF5,
+ mnemonic: "PUSH AF",
+ size: 1,
+ cycles: 16,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM16Bit,
+ },
+ Instruction {
+ opcode: 0xF6,
+ mnemonic: "OR d8",
+ size: 2,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xF7,
+ mnemonic: "RST 30H",
+ size: 1,
+ cycles: 16,
+ cycles_not_taken: 0,
+ kind: InstructionKind::Jump,
+ },
+ Instruction {
+ opcode: 0xF8,
+ mnemonic: "LD HL,SP+r8",
+ size: 2,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM16Bit,
+ },
+ Instruction {
+ opcode: 0xF9,
+ mnemonic: "LD SP,HL",
+ size: 1,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM16Bit,
+ },
+ Instruction {
+ opcode: 0xFA,
+ mnemonic: "LD A,(a16)",
+ size: 3,
+ cycles: 16,
+ cycles_not_taken: 0,
+ kind: InstructionKind::LSM8Bit,
+ },
+ Instruction {
+ opcode: 0xFB,
+ mnemonic: "EI",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::Control,
+ },
+ Instruction {
+ opcode: 0xFC,
+ mnemonic: "NI",
+ size: 0,
+ cycles: 0,
+ cycles_not_taken: 0,
+ kind: InstructionKind::Control,
+ },
+ Instruction {
+ opcode: 0xFD,
+ mnemonic: "NI",
+ size: 0,
+ cycles: 0,
+ cycles_not_taken: 0,
+ kind: InstructionKind::NI,
+ },
+ Instruction {
+ opcode: 0xFE,
+ mnemonic: "CP d8",
+ size: 2,
+ cycles: 8,
+ cycles_not_taken: 0,
+ kind: InstructionKind::AL8Bit,
+ },
+ Instruction {
+ opcode: 0xFF,
+ mnemonic: "RST 38H",
+ size: 1,
+ cycles: 16,
+ cycles_not_taken: 0,
+ kind: InstructionKind::Jump,
+ },
+ Instruction {
+ opcode: 0x100,
+ mnemonic: "RLC B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x101,
+ mnemonic: "RLC C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x102,
+ mnemonic: "RLC D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x103,
+ mnemonic: "RLC E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x104,
+ mnemonic: "RLC H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x105,
+ mnemonic: "RLC L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x106,
+ mnemonic: "RLC (HL)",
+ size: 1,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x107,
+ mnemonic: "RLC A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x108,
+ mnemonic: "RRC B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x109,
+ mnemonic: "RRC C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x10A,
+ mnemonic: "RRC D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x10B,
+ mnemonic: "RRC E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x10C,
+ mnemonic: "RRC H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x10D,
+ mnemonic: "RRC L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x10E,
+ mnemonic: "RRC (HL)",
+ size: 1,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x10F,
+ mnemonic: "RRC A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x110,
+ mnemonic: "RL B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x111,
+ mnemonic: "RL C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x112,
+ mnemonic: "RL D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x113,
+ mnemonic: "RL E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x114,
+ mnemonic: "RL H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x115,
+ mnemonic: "RL L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x116,
+ mnemonic: "RL (HL)",
+ size: 1,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x117,
+ mnemonic: "RL A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x118,
+ mnemonic: "RR B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x119,
+ mnemonic: "RR C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x11A,
+ mnemonic: "RR D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x11B,
+ mnemonic: "RR E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x11C,
+ mnemonic: "RR H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x11D,
+ mnemonic: "RR L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x11E,
+ mnemonic: "RR (HL)",
+ size: 1,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x11F,
+ mnemonic: "RR A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x120,
+ mnemonic: "SLA B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x121,
+ mnemonic: "SLA C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x122,
+ mnemonic: "SLA D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x123,
+ mnemonic: "SLA E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x124,
+ mnemonic: "SLA H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x125,
+ mnemonic: "SLA L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x126,
+ mnemonic: "SLA (HL)",
+ size: 1,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x127,
+ mnemonic: "SLA A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x128,
+ mnemonic: "SRA B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x129,
+ mnemonic: "SRA C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x12A,
+ mnemonic: "SRA D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x12B,
+ mnemonic: "SRA E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x12C,
+ mnemonic: "SRA H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x12D,
+ mnemonic: "SRA L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x12E,
+ mnemonic: "SRA (HL)",
+ size: 1,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x12F,
+ mnemonic: "SRA A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x130,
+ mnemonic: "SWAP B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x131,
+ mnemonic: "SWAP C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x132,
+ mnemonic: "SWAP D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x133,
+ mnemonic: "SWAP E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x134,
+ mnemonic: "SWAP H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x135,
+ mnemonic: "SWAP L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x136,
+ mnemonic: "SWAP (HL)",
+ size: 1,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x137,
+ mnemonic: "SWAP A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x138,
+ mnemonic: "SRL B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x139,
+ mnemonic: "SRL C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x13A,
+ mnemonic: "SRL D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x13B,
+ mnemonic: "SRL E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x13C,
+ mnemonic: "SRL H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x13D,
+ mnemonic: "SRL L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x13E,
+ mnemonic: "SRL (HL)",
+ size: 1,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x13F,
+ mnemonic: "SRL A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x140,
+ mnemonic: "BIT 0,B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x141,
+ mnemonic: "BIT 0,C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x142,
+ mnemonic: "BIT 0,D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x143,
+ mnemonic: "BIT 0,E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x144,
+ mnemonic: "BIT 0,H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x145,
+ mnemonic: "BIT 0,L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x146,
+ mnemonic: "BIT 0,(HL)",
+ size: 1,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x147,
+ mnemonic: "BIT 0,A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x148,
+ mnemonic: "BIT 1,B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x149,
+ mnemonic: "BIT 1,C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x14A,
+ mnemonic: "BIT 1,D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x14B,
+ mnemonic: "BIT 1,E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x14C,
+ mnemonic: "BIT 1,H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x14D,
+ mnemonic: "BIT 1,L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x14E,
+ mnemonic: "BIT 1,(HL)",
+ size: 1,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x14F,
+ mnemonic: "BIT 1,A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x150,
+ mnemonic: "BIT 2,B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x151,
+ mnemonic: "BIT 2,C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x152,
+ mnemonic: "BIT 2,D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x153,
+ mnemonic: "BIT 2,E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x154,
+ mnemonic: "BIT 2,H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x155,
+ mnemonic: "BIT 2,L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x156,
+ mnemonic: "BIT 2,(HL)",
+ size: 1,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x157,
+ mnemonic: "BIT 2,A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x158,
+ mnemonic: "BIT 3,B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x159,
+ mnemonic: "BIT 3,C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x15A,
+ mnemonic: "BIT 3,D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x15B,
+ mnemonic: "BIT 3,E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x15C,
+ mnemonic: "BIT 3,H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x15D,
+ mnemonic: "BIT 3,L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x15E,
+ mnemonic: "BIT 3,(HL)",
+ size: 1,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x15F,
+ mnemonic: "BIT 3,A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x160,
+ mnemonic: "BIT 4,B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x161,
+ mnemonic: "BIT 4,C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x162,
+ mnemonic: "BIT 4,D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x163,
+ mnemonic: "BIT 4,E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x164,
+ mnemonic: "BIT 4,H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x165,
+ mnemonic: "BIT 4,L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x166,
+ mnemonic: "BIT 4,(HL)",
+ size: 1,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x167,
+ mnemonic: "BIT 4,A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x168,
+ mnemonic: "BIT 5,B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x169,
+ mnemonic: "BIT 5,C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x16A,
+ mnemonic: "BIT 5,D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x16B,
+ mnemonic: "BIT 5,E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x16C,
+ mnemonic: "BIT 5,H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x16D,
+ mnemonic: "BIT 5,L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x16E,
+ mnemonic: "BIT 5,(HL)",
+ size: 1,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x16F,
+ mnemonic: "BIT 5,A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x170,
+ mnemonic: "BIT 6,B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x171,
+ mnemonic: "BIT 6,C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x172,
+ mnemonic: "BIT 6,D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x173,
+ mnemonic: "BIT 6,E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x174,
+ mnemonic: "BIT 6,H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x175,
+ mnemonic: "BIT 6,L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x176,
+ mnemonic: "BIT 6,(HL)",
+ size: 1,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x177,
+ mnemonic: "BIT 6,A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x178,
+ mnemonic: "BIT 7,B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x179,
+ mnemonic: "BIT 7,C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x17A,
+ mnemonic: "BIT 7,D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x17B,
+ mnemonic: "BIT 7,E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x17C,
+ mnemonic: "BIT 7,H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x17D,
+ mnemonic: "BIT 7,L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x17E,
+ mnemonic: "BIT 7,(HL)",
+ size: 1,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x17F,
+ mnemonic: "BIT 7,A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x180,
+ mnemonic: "RES 0,B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x181,
+ mnemonic: "RES 0,C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x182,
+ mnemonic: "RES 0,D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x183,
+ mnemonic: "RES 0,E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x184,
+ mnemonic: "RES 0,H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x185,
+ mnemonic: "RES 0,L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x186,
+ mnemonic: "RES 0,(HL)",
+ size: 1,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x187,
+ mnemonic: "RES 0,A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x188,
+ mnemonic: "RES 1,B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x189,
+ mnemonic: "RES 1,C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x18A,
+ mnemonic: "RES 1,D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x18B,
+ mnemonic: "RES 1,E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x18C,
+ mnemonic: "RES 1,H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x18D,
+ mnemonic: "RES 1,L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x18E,
+ mnemonic: "RES 1,(HL)",
+ size: 1,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x18F,
+ mnemonic: "RES 1,A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x190,
+ mnemonic: "RES 2,B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x191,
+ mnemonic: "RES 2,C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x192,
+ mnemonic: "RES 2,D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x193,
+ mnemonic: "RES 2,E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x194,
+ mnemonic: "RES 2,H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x195,
+ mnemonic: "RES 2,L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x196,
+ mnemonic: "RES 2,(HL)",
+ size: 1,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x197,
+ mnemonic: "RES 2,A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x198,
+ mnemonic: "RES 3,B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x199,
+ mnemonic: "RES 3,C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x19A,
+ mnemonic: "RES 3,D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x19B,
+ mnemonic: "RES 3,E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x19C,
+ mnemonic: "RES 3,H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x19D,
+ mnemonic: "RES 3,L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x19E,
+ mnemonic: "RES 3,(HL)",
+ size: 1,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x19F,
+ mnemonic: "RES 3,A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1A0,
+ mnemonic: "RES 4,B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1A1,
+ mnemonic: "RES 4,C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1A2,
+ mnemonic: "RES 4,D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1A3,
+ mnemonic: "RES 4,E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1A4,
+ mnemonic: "RES 4,H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1A5,
+ mnemonic: "RES 4,L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1A6,
+ mnemonic: "RES 4,(HL)",
+ size: 1,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1A7,
+ mnemonic: "RES 4,A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1A8,
+ mnemonic: "RES 5,B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1A9,
+ mnemonic: "RES 5,C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1AA,
+ mnemonic: "RES 5,D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1AB,
+ mnemonic: "RES 5,E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1AC,
+ mnemonic: "RES 5,H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1AD,
+ mnemonic: "RES 5,L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1AE,
+ mnemonic: "RES 5,(HL)",
+ size: 1,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1AF,
+ mnemonic: "RES 5,A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1B0,
+ mnemonic: "RES 6,B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1B1,
+ mnemonic: "RES 6,C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1B2,
+ mnemonic: "RES 6,D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1B3,
+ mnemonic: "RES 6,E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1B4,
+ mnemonic: "RES 6,H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1B5,
+ mnemonic: "RES 6,L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1B6,
+ mnemonic: "RES 6,(HL)",
+ size: 1,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1B7,
+ mnemonic: "RES 6,A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1B8,
+ mnemonic: "RES 7,B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1B9,
+ mnemonic: "RES 7,C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1BA,
+ mnemonic: "RES 7,D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1BB,
+ mnemonic: "RES 7,E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1BC,
+ mnemonic: "RES 7,H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1BD,
+ mnemonic: "RES 7,L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1BE,
+ mnemonic: "RES 7,(HL)",
+ size: 1,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1BF,
+ mnemonic: "RES 7,A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1C0,
+ mnemonic: "SET 0,B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1C1,
+ mnemonic: "SET 0,C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1C2,
+ mnemonic: "SET 0,D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1C3,
+ mnemonic: "SET 0,E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1C4,
+ mnemonic: "SET 0,H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1C5,
+ mnemonic: "SET 0,L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1C6,
+ mnemonic: "SET 0,(HL)",
+ size: 1,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1C7,
+ mnemonic: "SET 0,A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1C8,
+ mnemonic: "SET 1,B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1C9,
+ mnemonic: "SET 1,C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1CA,
+ mnemonic: "SET 1,D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1CB,
+ mnemonic: "SET 1,E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1CC,
+ mnemonic: "SET 1,H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1CD,
+ mnemonic: "SET 1,L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1CE,
+ mnemonic: "SET 1,(HL)",
+ size: 1,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1CF,
+ mnemonic: "SET 1,A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1D0,
+ mnemonic: "SET 2,B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1D1,
+ mnemonic: "SET 2,C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1D2,
+ mnemonic: "SET 2,D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1D3,
+ mnemonic: "SET 2,E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1D4,
+ mnemonic: "SET 2,H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1D5,
+ mnemonic: "SET 2,L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1D6,
+ mnemonic: "SET 2,(HL)",
+ size: 1,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1D7,
+ mnemonic: "SET 2,A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1D8,
+ mnemonic: "SET 3,B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1D9,
+ mnemonic: "SET 3,C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1DA,
+ mnemonic: "SET 3,D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1DB,
+ mnemonic: "SET 3,E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1DC,
+ mnemonic: "SET 3,H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1DD,
+ mnemonic: "SET 3,L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1DE,
+ mnemonic: "SET 3,(HL)",
+ size: 1,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1DF,
+ mnemonic: "SET 3,A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1E0,
+ mnemonic: "SET 4,B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1E1,
+ mnemonic: "SET 4,C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1E2,
+ mnemonic: "SET 4,D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1E3,
+ mnemonic: "SET 4,E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1E4,
+ mnemonic: "SET 4,H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1E5,
+ mnemonic: "SET 4,L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1E6,
+ mnemonic: "SET 4,(HL)",
+ size: 1,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1E7,
+ mnemonic: "SET 4,A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1E8,
+ mnemonic: "SET 5,B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1E9,
+ mnemonic: "SET 5,C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1EA,
+ mnemonic: "SET 5,D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1EB,
+ mnemonic: "SET 5,E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1EC,
+ mnemonic: "SET 5,H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1ED,
+ mnemonic: "SET 5,L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1EE,
+ mnemonic: "SET 5,(HL)",
+ size: 1,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1EF,
+ mnemonic: "SET 5,A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1F0,
+ mnemonic: "SET 6,B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1F1,
+ mnemonic: "SET 6,C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1F2,
+ mnemonic: "SET 6,D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1F3,
+ mnemonic: "SET 6,E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1F4,
+ mnemonic: "SET 6,H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1F5,
+ mnemonic: "SET 6,L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1F6,
+ mnemonic: "SET 6,(HL)",
+ size: 1,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1F7,
+ mnemonic: "SET 6,A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1F8,
+ mnemonic: "SET 7,B",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1F9,
+ mnemonic: "SET 7,C",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1FA,
+ mnemonic: "SET 7,D",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1FB,
+ mnemonic: "SET 7,E",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1FC,
+ mnemonic: "SET 7,H",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1FD,
+ mnemonic: "SET 7,L",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1FE,
+ mnemonic: "SET 7,(HL)",
+ size: 1,
+ cycles: 12,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+ Instruction {
+ opcode: 0x1FF,
+ mnemonic: "SET 7,A",
+ size: 1,
+ cycles: 4,
+ cycles_not_taken: 0,
+ kind: InstructionKind::RSB8Bit,
+ },
+];
diff --git a/fpt/src/memory.rs b/fpt/src/memory.rs
new file mode 100644
index 0000000..38dab7e
--- /dev/null
+++ b/fpt/src/memory.rs
@@ -0,0 +1,461 @@
+use std::cell::{Ref, RefCell, RefMut};
+use std::ops::{Deref, DerefMut, Range};
+use std::ptr;
+use std::rc::Rc;
+
+pub type Address = usize;
+pub type GBAddress = u16;
+pub type MemoryRange = Range;
+
+/// You can access these consts like this:
+/// ```
+/// assert_eq!(fpt::memory::map::ROM_DATA.start, 0x0100);
+/// ```
+pub mod map {
+ use super::{Address, MemoryRange};
+
+ //-------------------------------------------------------------------------
+ // Memory map
+ //-------------------------------------------------------------------------
+
+ /// This is where the bootrom lives
+ pub const BOOTROM: MemoryRange = 0x0000..0x0100;
+
+ /// The Cartridge Header
+ pub const ROM_DATA: MemoryRange = 0x0100..0x0150;
+
+ /// User Program Area (32 KB)
+ /// 0x0000..0x4000 From cartridge, usually a fixed bank
+ /// 0x4000..0x8000 From cartridge, switchable bank via mapper (if any) pub const USER_PROGRAM: MemoryRange = 0x0000..0x8000;
+
+ /// Video RAM (8 KB) - In CGB mode, switchable bank 0/1
+ pub const VRAM: MemoryRange = 0x8000..0xA000;
+
+ /// External Expansion Working RAM (8 KB) - From cartridge, switchable bank if any
+ pub const EXT_WRAM: MemoryRange = 0xA000..0xC000;
+
+ /// Unit Working RAM (8 KB)
+ pub const WRAM: MemoryRange = 0xC000..0xE000;
+
+ /// Not usable (Mirror of C000~DDFF (ECHO RAM)) https://gbdev.io/pandocs/Memory_Map.html#echo-ram
+ pub const NOT_USABLE1: MemoryRange = 0xE000..0xFE00;
+
+ /// Object Attribute Memory (40 OBJs, 40 x 32 bits)
+ pub const OAM: MemoryRange = 0xFE00..0xFEA0;
+
+ /// Not usable https://gbdev.io/pandocs/Memory_Map.html#fea0-feff-range
+ pub const NOT_USABLE2: MemoryRange = 0xFEA0..0xFF00;
+
+ //-------------------------------------------------------------------------
+ // I/O Registers
+ //-------------------------------------------------------------------------
+
+ /// Joypad
+ pub const P1: Address = 0xFF00;
+ /// Serial transfer data
+ pub const SB: Address = 0xFF01;
+ /// Serial transfer control
+ pub const SC: Address = 0xFF02;
+ /// Divider register
+ pub const DIV: Address = 0xFF04;
+ /// Timer counter
+ pub const TIMA: Address = 0xFF05;
+ /// Timer modulo
+ pub const TMA: Address = 0xFF06;
+ /// Timer control
+ pub const TAC: Address = 0xFF07;
+
+ //-------------------------------------------------------------------------
+ // I/O: Sound
+ //-------------------------------------------------------------------------
+
+ /// Sound channel 1 sweep
+ pub const NR10: Address = 0xFF10;
+ /// Sound channel 1 length timer & duty cycle
+ pub const NR11: Address = 0xFF11;
+ /// Sound channel 1 volume & envelope
+ pub const NR12: Address = 0xFF12;
+ /// Sound channel 1 period low
+ pub const NR13: Address = 0xFF13;
+ /// Sound channel 1 period high & control
+ pub const NR14: Address = 0xFF14;
+ /// Sound channel 2 length timer & duty cycle
+ pub const NR21: Address = 0xFF16;
+ /// Sound channel 2 volume & envelope
+ pub const NR22: Address = 0xFF17;
+ /// Sound channel 2 period low
+ pub const NR23: Address = 0xFF18;
+ /// Sound channel 2 period high & control
+ pub const NR24: Address = 0xFF19;
+ /// Sound channel 3 DAC enable
+ pub const NR30: Address = 0xFF1A;
+ /// Sound channel 3 length timer
+ pub const NR31: Address = 0xFF1B;
+ /// Sound channel 3 output level
+ pub const NR32: Address = 0xFF1C;
+ /// Sound channel 3 period low
+ pub const NR33: Address = 0xFF1D;
+ /// Sound channel 3 period high & control
+ pub const NR34: Address = 0xFF1E;
+ /// Sound channel 4 length timer
+ pub const NR41: Address = 0xFF20;
+ /// Sound channel 4 volume & envelope
+ pub const NR42: Address = 0xFF21;
+ /// Sound channel 4 frequency & randomness
+ pub const NR43: Address = 0xFF22;
+ /// Sound channel 4 control
+ pub const NR44: Address = 0xFF23;
+ /// Master volume & VIN panning
+ pub const NR50: Address = 0xFF24;
+ /// Sound panning
+ pub const NR51: Address = 0xFF25;
+ /// Sound on/off
+ pub const NR52: Address = 0xFF26;
+ /// Wave RAM
+ pub const WAVE_RAM: MemoryRange = 0xFF30..0xFF40;
+
+ //-------------------------------------------------------------------------
+ // IO: PPU
+ //-------------------------------------------------------------------------
+
+ /// LCD control
+ pub const LCDC: Address = 0xFF40;
+ /// LCD status
+ pub const STAT: Address = 0xFF41;
+ /// Viewport Y position
+ pub const SCY: Address = 0xFF42;
+ /// Viewport X position
+ pub const SCX: Address = 0xFF43;
+ /// LCD Y coordinate
+ pub const LY: Address = 0xFF44;
+ /// LY compare
+ pub const LYC: Address = 0xFF45;
+ /// OAM DMA source address & start
+ pub const DMA: Address = 0xFF46;
+ /// BG palette data (DMG)
+ pub const BGP: Address = 0xFF47;
+ /// OBJ palette 0 data (DMG)
+ pub const OBP0: Address = 0xFF48;
+ /// OBJ palette 1 data (DMG)
+ pub const OBP1: Address = 0xFF49;
+ /// Window Y position
+ pub const WY: Address = 0xFF4A;
+ /// Window X position plus 7
+ pub const WX: Address = 0xFF4B;
+
+ /// BANK register: Set to non-zero to disable boot ROM
+ pub const BANK: Address = 0xFF50;
+
+ //-------------------------------------------------------------------------
+ // CGB extra
+ // https://gbdev.io/pandocs/CGB_Registers.html
+ //-------------------------------------------------------------------------
+
+ /// Prepare speed switch (CGB)
+ pub const KEY1: Address = 0xFF4C;
+ /// VRAM bank (CGB)
+ pub const VBK: Address = 0xFF4F;
+ /// VRAM DMA source high (CGB)
+ pub const HDMA1: Address = 0xFF51;
+ /// VRAM DMA source low (CGB)
+ pub const HDMA2: Address = 0xFF52;
+ /// VRAM DMA destination high (CGB)
+ pub const HDMA3: Address = 0xFF53;
+ /// VRAM DMA destination low (CGB)
+ pub const HDMA4: Address = 0xFF54;
+ /// VRAM DMA length/mode/start (CGB)
+ pub const HDMA5: Address = 0xFF55;
+ /// Infrared communications port (GGB)
+ pub const RP: Address = 0xFF56;
+ /// Background color palette specification / Background palette index (CGB)
+ pub const BCPS: Address = 0xFF68;
+ /// Background color palette data / Background palette data (CGB)
+ pub const BCPD: Address = 0xFF69;
+ /// OBJ color palette specification / OBJ palette index (CGB)
+ pub const OCPS: Address = 0xFF6A;
+ /// OBJ color palette data / OBJ palette data (CGB)
+ pub const OCPD: Address = 0xFF6B;
+ /// Object priority mode (CGB)
+ pub const OPRI: Address = 0xFF6C;
+ /// WRAM bank (CGB) pub const SVBK: Address = 0xFF70;
+ /// Audio digital outputs 1 & 2 (CGB)
+ pub const PCM12: Address = 0xFF76;
+ /// Audio digital outputs 3 & 4 (CGB)
+ pub const PCM34: Address = 0xFF77;
+
+ //-------------------------------------------------------------------------
+ // High RAM
+ //-------------------------------------------------------------------------
+
+ /// Working & Stack RAM (127 bytes)
+ pub const HRAM: MemoryRange = 0xFF80..0xFFFF;
+
+ //-------------------------------------------------------------------------
+ // Interrupts
+ //-------------------------------------------------------------------------
+
+ /// Interrupt enable
+ pub const IE: Address = 0xFFFF;
+ /// Interrupt flag
+ pub const IF: Address = 0xFF0F;
+}
+
+#[derive(Clone)]
+pub struct Memory {
+ address_space: [u8; 65536],
+ cartridge: Vec,
+ bootrom: &'static [u8; 256],
+ code_listing: [Option; 0xffff + 1],
+}
+
+impl Default for Memory {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+/// Makes Memory act like the underlying `address_space` slice when calling
+/// methods and indexing and slicing with `[]`, making the following equivalent:
+///
+/// - `mem1[map::WRAM] == mem2[map::WRAM]`
+/// - `mem1.address_space[map::WRAM] == mem2.address_space[map::WRAM]`
+///
+/// This should make most of the helper methods in `Memory` and `Bus` redundant.
+///
+/// This also exploses `address_space` outside this module, so anyone with a
+/// reference to `Memory` can do whatever slice operations they want on
+/// `address_space`. For example, here's `LR35902::mem8` directly reading a byte
+/// from `address_space` (adapted to doctest):
+///
+/// pub fn mem8(self_mem: &fpt::memory::Bus, index: u16) -> u8 {
+/// self_mem.memory()[index as fpt::memory::Address]
+/// }
+impl Deref for Memory {
+ type Target = [u8; 65536];
+
+ fn deref(&self) -> &Self::Target {
+ return &self.address_space;
+ }
+}
+
+/// In combination with implementing `Deref`, this further allows the following:
+///
+/// - `mem.fill(0)`
+/// - `mem[map::BOOTROM].clone_from_slice(mem.bootrom)`
+impl DerefMut for Memory {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ return &mut self.address_space;
+ }
+}
+
+/// The memories' whole address space is compared bit by bit
+/// TODO or maybe this is too much?
+impl PartialEq for Memory {
+ fn eq(&self, other: &Self) -> bool {
+ self.address_space[..] == other.address_space[..]
+ }
+}
+
+impl Memory {
+ const BOOTROM: &'static [u8; 256] = include_bytes!("../dmg0.bin");
+
+ pub fn new() -> Self {
+ const ARRAY_REPEAT_VALUE: Option = None;
+ Self {
+ address_space: [0; 65536],
+ cartridge: Vec::new(),
+ bootrom: Self::BOOTROM,
+ code_listing: [ARRAY_REPEAT_VALUE; 0xffff + 1],
+ }
+ }
+
+ /// Takes a pointer to zero-initialized memory and manually initializes
+ /// `Memory` fields that shouldn't remain zero-initialized.
+ ///
+ /// # Safety
+ ///
+ /// idk. This function was made to be caled from `Bus::unsafely_optmized_new()`.
+ /// And we're only calling `Bus::unsafely_optmized_new()` from tests, right?
+ /// (Clippy is forcing me to write this `# Safety` section, so here you are)
+ pub unsafe fn initialize_from_zero(ptr_to_mem: *mut Memory) {
+ // Writes a properly initialized Vec to ptr_to_mem->cartridge without
+ // dropping a zero-initialized Vec, which would be undefined behaviour
+ let ptr_to_cartridge = ptr::addr_of_mut!((*ptr_to_mem).cartridge);
+ ptr::write(ptr_to_cartridge, Vec::new());
+
+ // Point ptr_to_mem->bootrom (a dangling reference if zero-initialized) to BOOTROM
+ (*ptr_to_mem).bootrom = Self::BOOTROM;
+
+ // It *might* be fine to leave code_listing: [Option; 0xffff + 1] zero-initialized.
+ // None, being the first variant in the Option enum, should have discriminant = 0, I guess.
+ }
+
+ pub fn array_ref(&self, from: Address) -> &[u8; N] {
+ self.address_space[from..from + N].try_into().unwrap() // guaranteed to have size N
+ }
+
+ pub fn code_listing(&self) -> &[Option; 0xffff + 1] {
+ &self.code_listing
+ }
+
+ pub fn set_code_listing_at(&mut self, pc: u16, v: String) {
+ self.code_listing[pc as usize] = Some(v);
+ }
+}
+
+/// An Rc-RefCell wrapper around Memory that allows it to be shared by the CPU, the PPU, etc.
+#[derive(Clone, PartialEq)]
+pub struct Bus(Rc>);
+
+/// Constructors
+impl Bus {
+ #[allow(clippy::new_without_default)]
+ pub fn new() -> Self {
+ Bus(Rc::new(RefCell::new(Memory::new())))
+ }
+
+ pub fn unsafely_optimized_new() -> Self {
+ let rc = Rc::>::new_zeroed();
+ let rc = unsafe {
+ let ptr_to_zeroed_memory = (*rc.as_ptr()).as_ptr();
+ Memory::initialize_from_zero(ptr_to_zeroed_memory);
+ rc.assume_init()
+ };
+
+ Bus(rc)
+ }
+}
+
+/// Accessors
+impl Bus {
+ pub fn memory(&self) -> Ref {
+ self.0.borrow()
+ }
+
+ pub fn memory_mut(&self) -> RefMut {
+ self.0.borrow_mut()
+ }
+
+ /// Immutably borrow the inner `Memory` in a scoped way.
+ pub fn borrow(&self, borrower: impl FnOnce(&Memory) -> R) -> R {
+ borrower(&self.0.borrow()) // reborrow
+ }
+
+ /// Mutably borrow the inner `Memory` in a scoped way.
+ pub fn borrow_mut(&self, borrower: impl FnOnce(&mut Memory) -> R) -> R {
+ borrower(&mut self.0.borrow_mut()) // reborrow
+ }
+
+ /// Do something with the VRAM
+ pub fn with_vram(&self, reader: impl FnOnce(&[u8]) -> R) -> R {
+ self.borrow(|mem| reader(&mem.address_space[map::VRAM]))
+ }
+}
+
+/// Operations
+impl Bus {
+ pub fn load_bootrom(&mut self) {
+ let mem = &mut *self.0.borrow_mut();
+ mem.address_space[map::BOOTROM].clone_from_slice(mem.bootrom);
+ mem.code_listing[map::BOOTROM].fill(None);
+ }
+
+ pub fn unload_bootrom(&mut self) {
+ let mem = &mut *self.0.borrow_mut();
+ mem.address_space[map::BOOTROM].clone_from_slice(&mem.cartridge[map::BOOTROM]);
+ }
+
+ pub fn load_cartridge(&mut self, cartridge: &[u8]) {
+ const CARTRIDGE_AREA: MemoryRange = 0x0100..0x8000;
+ let mem = &mut *self.0.borrow_mut();
+ mem.address_space[CARTRIDGE_AREA].clone_from_slice(&cartridge[CARTRIDGE_AREA]);
+ }
+
+ pub fn read(&self, address: GBAddress) -> u8 {
+ self.memory_mut().address_space[address as Address]
+ }
+
+ pub fn write(&mut self, address: GBAddress, value: u8) {
+ self.memory_mut().address_space[address as Address] = value;
+ }
+
+ pub fn copy_range(&self, range: MemoryRange) -> Vec {
+ self.memory_mut().address_space[range.start..range.end].to_vec()
+ }
+
+ pub fn with_slice(&self, range: MemoryRange, reader: impl FnOnce(&[u8]) -> T) -> T {
+ reader(&self.memory().address_space[range])
+ }
+
+ /// Runs closure `reader` with access to a fixed-size slice of `N` bytes.
+ pub fn with_span(
+ &self,
+ start: Address,
+ reader: impl FnOnce(&[u8; N]) -> T,
+ ) -> T {
+ reader(self.memory().array_ref(start))
+ }
+
+ pub fn each_byte(&self) -> std::iter::Enumerate> {
+ self.memory_mut().address_space.into_iter().enumerate()
+ }
+}
+
+/// Register accessors
+impl Bus {
+ fn _read(&self, address: Address) -> u8 {
+ self.memory_mut().address_space[address]
+ }
+
+ fn _write(&mut self, address: Address, value: u8) {
+ self.memory_mut().address_space[address] = value;
+ }
+
+ pub fn lcdc(&self) -> u8 {
+ self._read(map::LCDC)
+ }
+
+ pub fn set_lcdc(&mut self, value: u8) {
+ self._write(map::LCDC, value);
+ }
+
+ pub fn stat(&self) -> u8 {
+ self._read(map::STAT)
+ }
+
+ pub fn set_stat(&mut self, value: u8) {
+ self._write(map::STAT, value);
+ }
+
+ pub fn scy(&self) -> u8 {
+ self._read(map::SCY)
+ }
+
+ pub fn set_scy(&mut self, value: u8) {
+ self._write(map::SCY, value);
+ }
+
+ pub fn scx(&self) -> u8 {
+ self._read(map::SCX)
+ }
+
+ pub fn set_scx(&mut self, value: u8) {
+ self._write(map::SCX, value);
+ }
+
+ pub fn ly(&self) -> u8 {
+ self._read(map::LY)
+ }
+
+ pub fn set_ly(&mut self, value: u8) {
+ self._write(map::LY, value);
+ }
+
+ pub fn lyc(&self) -> u8 {
+ self._read(map::LYC)
+ }
+
+ pub fn set_lyc(&mut self, value: u8) {
+ self._write(map::LYC, value)
+ }
+}
diff --git a/fpt/src/ppu.rs b/fpt/src/ppu.rs
new file mode 100644
index 0000000..6598875
--- /dev/null
+++ b/fpt/src/ppu.rs
@@ -0,0 +1,191 @@
+use std::fmt::{Display, Formatter};
+
+use tile::VRamContents;
+
+use crate::memory::Bus;
+
+pub mod tile;
+
+pub const WIDTH: usize = 160;
+pub const HEIGHT: usize = 144;
+pub type Frame = [u8; WIDTH * HEIGHT]; // TODO: wasteful, each pixel is 2 bits only
+
+//#[derive(Clone, PartialEq)]
+#[allow(unused)]
+pub struct Ppu {
+ bus: Bus,
+ frame: Frame,
+ dots_this_frame: u32,
+ frame_counter: u32,
+ mode: Mode,
+ tilemap: VRamContents,
+}
+
+#[repr(u8)]
+#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug)]
+pub enum Mode {
+ HBlank = 0,
+ VBlank = 1,
+ OamScan = 2,
+ PixelTransfer = 3,
+}
+
+impl Display for Mode {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ write!(f, "Mode {} ({:#?})", *self as u8, self)
+ }
+}
+
+impl From for Mode {
+ fn from(value: u8) -> Self {
+ match value {
+ 0 => Mode::HBlank,
+ 1 => Mode::VBlank,
+ 2 => Mode::OamScan,
+ 3 => Mode::PixelTransfer,
+ n => panic!("Tried to convert {n} to a ppu::Mode (valid values are 0, 1, 2 and 3)"),
+ }
+ }
+}
+
+pub const DOTS_IN_ONE_FRAME: u32 = 70224;
+
+impl Ppu {
+ pub fn new(mut bus: Bus) -> Self {
+ // Make STAT's MODE bits consistent with the PPU's initial mode
+ bus.set_stat(bus.stat() & 0b11111100 | Mode::OamScan as u8);
+
+ Ppu {
+ bus,
+ frame: [0b00; WIDTH * HEIGHT],
+ dots_this_frame: 0,
+ frame_counter: 0,
+ mode: Mode::OamScan,
+ tilemap: VRamContents::default(),
+ }
+ }
+
+ pub fn step(&mut self, cycles: u32) {
+ for _ in 0..cycles {
+ self.dot();
+ }
+ }
+
+ fn set_mode(&mut self, mode: Mode) {
+ self.mode = mode;
+ }
+
+ fn oam_scan(&mut self) {
+ if self.dots_this_frame % 456 == (80 - 1) {
+ self.tilemap = self.bus.with_vram(VRamContents::load);
+ self.set_mode(Mode::PixelTransfer);
+ }
+ }
+
+ /// Currently only draws the background pixels, not the window or sprites
+ #[allow(clippy::format_collect)]
+ fn pixel_transfer(&mut self) {
+ if self.dots_this_frame % 456 == (80 + 160) as u32 {
+ self.set_mode(Mode::HBlank);
+ return;
+ }
+ // TODO: LCDC.3
+ let x = ((self.dots_this_frame % 456) - 80) as usize; // TODO I'm pretending the PPU never stalls
+ let y = self.bus.ly() as usize;
+ let xx = ((x as u8 + self.bus.scx()) as u16 % 256u16) as usize;
+ let yy = ((self.bus.ly() + self.bus.scy()) as u16 % 256u16) as usize;
+ // TODO: LCDC.4
+ let tile_i = xx / 8 + yy / 8 * 32;
+ let tile_data_address = self.tilemap.tile_map0[tile_i];
+ let tile = self.tilemap.tile_data[tile_data_address as usize];
+ let pixel = tile.get_pixel(yy % 8, xx % 8);
+ self.frame[WIDTH * y + x] = pixel;
+ }
+
+ fn h_blank(&mut self) {
+ if self.dots_this_frame >= (456 * HEIGHT - 1) as u32 {
+ self.set_mode(Mode::VBlank);
+ } else if self.dots_this_frame % 456 == 455 {
+ self.set_mode(Mode::OamScan);
+ }
+ }
+
+ fn v_blank(&mut self) {
+ if self.dots_this_frame == DOTS_IN_ONE_FRAME - 1 {
+ self.set_mode(Mode::OamScan);
+ }
+ }
+
+ /// Simulates a "dot", as described in https://gbdev.io/pandocs/Rendering.html.
+ /// A "dot" either draws a single pixel (in Mode 3) or is stalled for $REASONS.
+ /// A "dot" = one 2^22 Hz time unit, so there's 4 dots per machine cycle,
+ /// or 1 dot each t-cycle. dot timings don't change on double speed mode.
+ fn dot(&mut self) {
+ // Update LY register
+ self.bus.set_ly((self.dots_this_frame / 456) as u8);
+
+ // The timing of a frame consists of
+ // * 144 actual scanlines lasting 456 dots each, where:
+ // - the first 80 dots are mode 2 (OAM scan)
+ // - the next 172 to 289 dots are mode 3 (drawing pixels)
+ // - the remaining 87 to 204 dots are mode 0 (H-blank)
+ // * 10 "scanlines" (4560 dots) for mode 1 (V-blank)
+ //let ppu_mode = if self.bus.ly() < HEIGHT as u8 {
+ // match self.dots_this_frame % 456 {
+ // 0..80 => Mode::OamScan, // Mode 2
+ // 80..240 => Mode::PixelTransfer, // Mode 3 (TODO lasts between 172 and 289 dots)
+ // 240.. => Mode::HBlank, // Mode 0
+ // }
+ //} else {
+ // Mode::VBlank // Mode 1
+ //};
+
+ match self.mode {
+ Mode::OamScan => self.oam_scan(),
+ Mode::PixelTransfer => self.pixel_transfer(),
+ Mode::HBlank => self.h_blank(),
+ Mode::VBlank => self.v_blank(),
+ };
+
+ // Update "LYC == LY" and "PPU mode" flags in STAT register
+ self.bus.set_stat(
+ self.bus.stat() & 0b11111000
+ | ((self.bus.ly() == self.bus.lyc()) as u8) << 2
+ | self.mode as u8,
+ );
+
+ // TODO actually draw some actual background, window and sprites
+
+ // Advance one "dot"
+ self.dots_this_frame = (self.dots_this_frame + 1) % DOTS_IN_ONE_FRAME;
+ if self.dots_this_frame == 0 {
+ self.frame_counter += 1;
+ }
+ }
+
+ pub fn get_frame(&self) -> &Frame {
+ &self.frame
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::Gameboy;
+
+ #[test]
+ fn test_ppu_modes() {
+ let mut gb: Gameboy = Gameboy::unsafely_optimized_new();
+ assert_eq!(gb.ppu.mode, Mode::OamScan);
+ gb.ppu.step(80);
+ assert_eq!(gb.ppu.mode, Mode::PixelTransfer);
+ gb.ppu.step(300);
+ assert_eq!(gb.ppu.mode, Mode::HBlank);
+ gb.ppu.step(76);
+ assert_eq!(gb.ppu.mode, Mode::OamScan);
+ gb.ppu.step(65208);
+ assert_eq!(gb.ppu.mode, Mode::VBlank);
+ gb.ppu.step(4560);
+ assert_eq!(gb.ppu.mode, Mode::OamScan);
+ }
+}
diff --git a/fpt/src/ppu/tile.rs b/fpt/src/ppu/tile.rs
new file mode 100644
index 0000000..fcf0d4a
--- /dev/null
+++ b/fpt/src/ppu/tile.rs
@@ -0,0 +1,173 @@
+use std::fmt;
+use std::fs::File;
+use std::io::Write;
+
+use crate::ppu::Frame;
+
+pub const NUM_TILES: usize = 384;
+pub const TILE_PIXEL_SIZE: usize = 8;
+
+/// Holds a 8x8 tile image as it appears in VRAM
+/// (2 bytes for each 8 pixel row)
+#[derive(Copy, Clone, Eq, PartialEq)]
+pub struct Tile {
+ pub bytes: [u8; 16],
+}
+
+impl Tile {
+ #[allow(unused)]
+ pub fn load(data: &[u8; 16]) -> Tile {
+ Tile { bytes: *data }
+ }
+
+ // Returns the color of the pixel - a byte with only the last two bits set
+ // TODO: should return a bitfield
+ pub fn get_pixel(&self, y: usize, x: usize) -> u8 {
+ let low_bit = (self.bytes[2 * y] >> (7 - x)) & 1;
+ let high_bit = (self.bytes[2 * y + 1] >> (7 - x)) & 1;
+
+ (high_bit << 1) + low_bit
+ }
+}
+
+impl fmt::Debug for Tile {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ for i in 0..8 {
+ for j in 0..8 {
+ write!(f, "{}", self.get_pixel(i, j))?;
+ }
+ writeln!(f)?;
+ }
+ write!(f, "")
+ }
+}
+
+/// Represents the data that lives in VRAM:
+/// 3 * 128 tile blocks and two 32x32 tile maps
+pub struct VRamContents {
+ /// Three blocks of 128 tiles shared by the BG/Win tiles and OBJ tiles
+ pub tile_data: [Tile; NUM_TILES],
+ /// The first 32x32 tile map, accessed when either LCDC.3 or LCDC.6 are 0
+ pub tile_map0: [u8; 1024],
+ /// The second 32x32 tile map, accessed when either LCDC.3 or LCDC.6 are 1
+ pub tile_map1: [u8; 1024],
+}
+
+impl Default for VRamContents {
+ fn default() -> VRamContents {
+ VRamContents {
+ tile_map0: [0; 1024],
+ tile_map1: [0; 1024],
+ tile_data: [Tile { bytes: [0; 16] }; 384],
+ }
+ }
+}
+
+impl VRamContents {
+ pub fn load(vram: &[u8]) -> VRamContents {
+ let mut tilemap = VRamContents::default();
+
+ for i in 0..NUM_TILES {
+ tilemap.tile_data[i]
+ .bytes
+ .clone_from_slice(&vram[(16 * i)..(16 * (i + 1))]);
+ }
+
+ tilemap.tile_map0.clone_from_slice(&vram[0x1800..0x1c00]);
+ tilemap.tile_map1.clone_from_slice(&vram[0x1c00..0x2000]);
+
+ tilemap
+ }
+}
+
+/// Writes a Gameboy frame to a PGM file
+pub fn write_pgm_screenshot(frame: &Frame, filename: &str) {
+ // TODO: code dedup
+ let mut file = File::create(filename).unwrap();
+
+ // Write the header for a 160x144 PGM image with 4 shades of gray
+ write!(file, "P2\n# Game Boy screenshot: {filename}\n160 144\n3\n").unwrap();
+
+ // Our Game Boy's framebuffer seems to have a direct correspondence to this!
+ for line in frame.array_chunks::<160>() {
+ let pgm_line = line
+ .iter()
+ .map(|p| (b'3' - *p) as char) // ASCII from '0' to '3'
+ .intersperse(' ')
+ .collect::()
+ + "\n";
+
+ file.write_all(pgm_line.as_bytes()).unwrap();
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::memory::map::VRAM;
+ use crate::Gameboy;
+
+ // Looks like a game boy
+ #[rustfmt::skip]
+ const THE_TILE: [u8; 16] = [
+ 0x3c, 0x7e,
+ 0x42, 0x42,
+ 0x42, 0x42,
+ 0x42, 0x42,
+ 0x7e, 0x5e,
+ 0x7e, 0x0a,
+ 0x7c, 0x56,
+ 0x38, 0x7c,
+ ];
+
+ #[test]
+ #[rustfmt::skip]
+ fn test_pixel_render() {
+ let tile = Tile::load(&THE_TILE);
+
+ let formatted = format!("{:?}", tile);
+
+ assert_eq!(
+ formatted,
+ ["02333320",
+ "03000030",
+ "03000030",
+ "03000030",
+ "03133330",
+ "01113130",
+ "03131320",
+ "02333200\n"].join("\n")
+ )
+ }
+
+ #[test]
+ fn test_one_tile_to_vram() {
+ let gb: Gameboy = Gameboy::unsafely_optimized_new();
+
+ // Initialize VRAM with THE_TILE, then parse it with our structs
+ let tm: VRamContents = gb.bus.borrow_mut(|mem| {
+ mem[VRAM.start..VRAM.start + 16].clone_from_slice(&THE_TILE[..]);
+ VRamContents::load(&mem[VRAM])
+ });
+
+ assert_eq!(
+ tm.tile_data[tm.tile_map0[0] as usize],
+ Tile::load(&THE_TILE)
+ );
+ }
+
+ #[test]
+ fn test_photograph_ppu_frame_rendering_progress() {
+ let mut gb: Gameboy = Gameboy::unsafely_optimized_new();
+ gb.bus.memory_mut()[VRAM.start..VRAM.start + 16].clone_from_slice(&THE_TILE[..]);
+
+ std::fs::create_dir_all("screenshots").unwrap();
+ for ly in 0..154 {
+ write_pgm_screenshot(
+ gb.get_frame(),
+ &format!("screenshots/test_one_tile_to_vram-ly_{ly:05}.pgm"),
+ );
+ gb.ppu.step(456);
+ }
+ }
+}
diff --git a/fpt/tests/lr35902.rs b/fpt/tests/lr35902.rs
new file mode 100644
index 0000000..9af3362
--- /dev/null
+++ b/fpt/tests/lr35902.rs
@@ -0,0 +1,1931 @@
+use fpt::lr35902::LR35902;
+use fpt::memory::Bus;
+use rstest::*;
+
+#[derive(Clone)]
+struct LR35902Builder {
+ lr35902: LR35902,
+}
+
+impl LR35902Builder {
+ pub fn new() -> Self {
+ Self {
+ lr35902: LR35902::new(Bus::unsafely_optimized_new()),
+ }
+ }
+
+ pub fn with_reg8(self, register: &str, value: u8) -> Self {
+ match register {
+ "b" => self.with_b(value),
+ "c" => self.with_c(value),
+ "d" => self.with_d(value),
+ "e" => self.with_e(value),
+ "h" => self.with_h(value),
+ "l" => self.with_l(value),
+ "a" => self.with_a(value),
+ _ => panic!(),
+ }
+ }
+
+ pub fn with_reg16(self, register: &str, value: u16) -> Self {
+ match register {
+ "bc" => self.with_bc(value),
+ "de" => self.with_de(value),
+ "hl" => self.with_hl(value),
+ "af" => self.with_af(value),
+ "sp" => self.with_sp(value),
+ _ => panic!(),
+ }
+ }
+
+ pub fn with_flag(self, flag: &str, value: bool) -> Self {
+ match flag {
+ "z" => self.with_z_flag(value),
+ "c" => self.with_c_flag(value),
+ "n" => self.with_n_flag(value),
+ "h" => self.with_h_flag(value),
+ _ => panic!(),
+ }
+ }
+
+ pub fn with_a(mut self, a: u8) -> Self {
+ self.lr35902.set_a(a);
+ self
+ }
+
+ pub fn with_f(mut self, f: u8) -> Self {
+ self.lr35902.set_f(f);
+ self
+ }
+
+ pub fn with_af(mut self, af: u16) -> Self {
+ self.lr35902.set_af(af);
+ self
+ }
+
+ pub fn with_b(mut self, b: u8) -> Self {
+ self.lr35902.set_b(b);
+ self
+ }
+
+ pub fn with_c(mut self, c: u8) -> Self {
+ self.lr35902.set_c(c);
+ self
+ }
+
+ pub fn with_bc(mut self, bc: u16) -> Self {
+ self.lr35902.set_bc(bc);
+ self
+ }
+
+ pub fn with_d(mut self, d: u8) -> Self {
+ self.lr35902.set_d(d);
+ self
+ }
+
+ pub fn with_e(mut self, e: u8) -> Self {
+ self.lr35902.set_e(e);
+ self
+ }
+
+ pub fn with_de(mut self, de: u16) -> Self {
+ self.lr35902.set_de(de);
+ self
+ }
+
+ pub fn with_h(mut self, h: u8) -> Self {
+ self.lr35902.set_h(h);
+ self
+ }
+
+ pub fn with_l(mut self, l: u8) -> Self {
+ self.lr35902.set_l(l);
+ self
+ }
+
+ pub fn with_hl(mut self, hl: u16) -> Self {
+ self.lr35902.set_hl(hl);
+ self
+ }
+
+ pub fn with_sp(mut self, sp: u16) -> Self {
+ self.lr35902.set_sp(sp);
+ self
+ }
+
+ pub fn with_pc(mut self, pc: u16) -> Self {
+ self.lr35902.set_pc(pc);
+ self
+ }
+
+ pub fn with_z_flag(mut self, z: bool) -> Self {
+ self.lr35902.set_z_flag(z);
+ self
+ }
+
+ pub fn with_c_flag(mut self, c: bool) -> Self {
+ self.lr35902.set_c_flag(c);
+ self
+ }
+
+ pub fn with_n_flag(mut self, n: bool) -> Self {
+ self.lr35902.set_n_flag(n);
+ self
+ }
+
+ pub fn with_h_flag(mut self, h: bool) -> Self {
+ self.lr35902.set_h_flag(h);
+ self
+ }
+
+ pub fn with_clock_cycles(mut self, clock_cycles: u64) -> LR35902Builder {
+ self.lr35902.set_clock_cycles(clock_cycles);
+ self
+ }
+
+ pub fn with_mem8(mut self, index: u16, value: u8) -> LR35902Builder {
+ self.lr35902.set_mem8(index, value);
+ self
+ }
+
+ pub fn with_mem16(mut self, index: u16, value: u16) -> LR35902Builder {
+ self.lr35902.set_mem16(index, value);
+ self
+ }
+
+ pub fn build(self) -> LR35902 {
+ self.lr35902
+ }
+}
+
+#[test]
+fn test_instr_0x000_nop() {
+ // Given
+ let builder = LR35902Builder::new().with_mem8(0x0000, 0x0);
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+
+ // Then
+ let expected = builder.with_pc(1).with_clock_cycles(4).build();
+ assert_eq!(sut, expected);
+}
+
+#[rstest]
+#[case(2, 1, 0x0102)]
+fn test_instr_0x001_ld_bc_d16(#[case] lsb: u8, #[case] msb: u8, #[case] result: u16) {
+ // Given
+ let builder = LR35902Builder::new()
+ .with_mem8(0x0000, 0x1) // instruction LD BC,d16
+ .with_mem8(0x0001, lsb) // lsb of immediate16
+ .with_mem8(0x0002, msb); // msb of immediate16
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+
+ // Then
+ let expected = builder
+ .with_pc(3)
+ .with_bc(result) // (1 << 8) + 2 == 0x0102
+ .with_clock_cycles(12)
+ .build();
+ assert_eq!(sut, expected);
+}
+
+#[rstest]
+#[case(0x02, "bc")]
+#[case(0x12, "de")]
+fn test_instr_ld_pointer_from_a(#[case] opcode: u8, #[case] register: &str) {
+ // Given
+ let builder = LR35902Builder::new()
+ .with_mem8(0x0000, opcode)
+ .with_a(0x01)
+ .with_reg16(register, 0xFF00);
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+
+ // Then
+ let expected = builder
+ .with_pc(1)
+ .with_mem16(0xFF00, 0x01)
+ .with_clock_cycles(8)
+ .build();
+ assert_eq!(sut, expected);
+}
+
+#[test]
+fn test_instr_0x008_ld_pointer_immediate16_from_sp() {
+ // Given
+ let builder = LR35902Builder::new()
+ .with_mem8(0x0000, 0x8)
+ .with_mem16(0x0001, 0xFF00)
+ .with_sp(0x01);
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+
+ // Then
+ let expected = builder
+ .with_pc(3)
+ .with_mem16(0xFF00, 0x01)
+ .with_clock_cycles(20)
+ .build();
+ assert_eq!(sut, expected);
+}
+
+#[rstest]
+#[case(2, 1, 0x0102)]
+fn test_instr_0x011_ld_de_d16(#[case] lsb: u8, #[case] msb: u8, #[case] result: u16) {
+ // Given
+ let builder = LR35902Builder::new()
+ .with_mem8(0x0000, 0x11) // instruction LD DE,d16
+ .with_mem8(0x0001, lsb) // lsb of immediate16
+ .with_mem8(0x0002, msb); // msb of immediate16
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+
+ // Then
+ let expected = builder
+ .with_pc(3)
+ .with_de(result) // (1 << 8) + 2 == 0x0102
+ .with_clock_cycles(12)
+ .build();
+ assert_eq!(sut, expected);
+}
+
+#[rstest]
+#[case(0xa, "bc", 0xFF00, 0x01)]
+#[case(0x1a, "de", 0xFF00, 0x01)]
+fn test_instr_ld_register_a_from_pointer(
+ #[case] opcode: u8,
+ #[case] register: &str,
+ #[case] address: u16,
+ #[case] value: u8,
+) {
+ // Given
+ let builder = LR35902Builder::new()
+ .with_mem8(0x0000, opcode) // instruction LD (HL-), a
+ .with_mem8(address, value)
+ .with_reg16(register, address);
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+
+ // Then
+ let expected = builder
+ .with_pc(1)
+ .with_clock_cycles(8)
+ .with_a(value)
+ .build();
+ assert_eq!(sut, expected);
+}
+
+#[rstest]
+#[case(0x2, 0x1, 0x0102)]
+fn test_instr_0x021_ld_hl_d16(#[case] lsb: u8, #[case] msb: u8, #[case] result: u16) {
+ // Given
+ let builder = LR35902Builder::new()
+ .with_mem8(0x0000, 0x21) // instruction LD HL,d16
+ .with_mem8(0x0001, lsb) // lsb of immediate16
+ .with_mem8(0x0002, msb); // msb of immediate16
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+
+ // Then
+ let expected = builder
+ .with_pc(3)
+ .with_hl(result) // (1 << 8) + 2 == 0x0102
+ .with_clock_cycles(12)
+ .build();
+ assert_eq!(sut, expected);
+}
+
+#[rstest]
+#[case(0xFF00, 0xFF01)]
+#[case(0xFFFF, 0x0000)]
+fn test_instr_0x022_ld_pointer_hl_increment_from_a(#[case] hl: u16, #[case] hl_inc: u16) {
+ // Given
+ let builder = LR35902Builder::new()
+ .with_mem8(0x0000, 0x22)
+ .with_hl(hl)
+ .with_a(0x1);
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+
+ // Then
+ let expected = builder
+ .with_pc(1)
+ .with_hl(hl_inc)
+ .with_mem8(hl, 0x1)
+ .with_clock_cycles(8)
+ .build();
+ assert_eq!(sut, expected);
+}
+
+#[test]
+fn test_instr_0x2a_ld_register_a_from_hli() {
+ // Given
+ let hl = 0xFF00;
+ let builder = LR35902Builder::new()
+ .with_mem8(0x0000, 0x2a) // instruction LD (HL-), a
+ .with_mem8(hl, 0x01)
+ .with_hl(hl);
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+
+ // Then
+ let expected = builder
+ .with_pc(1)
+ .with_hl(hl + 1) // hl gets decremented
+ .with_clock_cycles(8)
+ .with_a(0x01)
+ .build();
+ assert_eq!(sut, expected);
+}
+
+#[rstest]
+#[case(0x2, 0x1, 0x0102)]
+#[case(0xFF, 0xFF, 0xFFFF)]
+fn test_instr_0x031_ld_sp_d16(#[case] lsb: u8, #[case] msb: u8, #[case] result: u16) {
+ // Given
+ let builder = LR35902Builder::new()
+ .with_mem8(0x0000, 0x31) // instruction LD SP,d16
+ .with_mem8(0x0001, lsb) // lsb of immediate16
+ .with_mem8(0x0002, msb); // msb of immediate16
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+
+ // Then
+ let expected = builder
+ .with_pc(3)
+ .with_sp(result) // (msb << 8) + lsb == 0x0102
+ .with_clock_cycles(12)
+ .build();
+ assert_eq!(sut, expected);
+}
+
+#[rstest]
+#[case(0x100, 0xFF)]
+#[case(0x0, 0xFFFF)]
+fn test_instr_0x032_ld_hld_a(#[case] hl: u16, #[case] hl_after: u16) {
+ // Given
+ let builder = LR35902Builder::new()
+ .with_mem8(0x0000, 0x32) // instruction LD (HL-), a
+ .with_a(0xca)
+ .with_hl(hl);
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+
+ // Then
+ let expected = builder
+ .with_pc(1)
+ .with_hl(hl_after) // hl gets decremented
+ .with_clock_cycles(8)
+ .with_mem8(hl, 0xca)
+ .build();
+ assert_eq!(sut, expected);
+}
+
+#[test]
+fn test_instr_0x3a_ld_register_a_from_hld() {
+ // Given
+ let hl = 0xFF00;
+ let builder = LR35902Builder::new()
+ .with_mem8(0x0000, 0x3a) // instruction LD (HL-), a
+ .with_mem8(hl, 0x01)
+ .with_hl(hl);
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+
+ // Then
+ let expected = builder
+ .with_pc(1)
+ .with_hl(hl - 1) // hl gets decremented
+ .with_clock_cycles(8)
+ .with_a(0x01)
+ .build();
+ assert_eq!(sut, expected);
+}
+
+#[rstest]
+#[case(0x40, "b", "b", 0x01)] // 1
+#[case(0x40, "b", "b", 0xFF)] // 2
+#[case(0x41, "b", "c", 0x01)] // 3
+#[case(0x41, "b", "c", 0xFF)] // 4
+#[case(0x42, "b", "d", 0x01)] // 5
+#[case(0x42, "b", "d", 0xFF)] // 6
+#[case(0x43, "b", "e", 0x01)] // 7
+#[case(0x43, "b", "e", 0xFF)] // 8
+#[case(0x44, "b", "h", 0x01)] // 9
+#[case(0x44, "b", "h", 0xFF)] // 10
+#[case(0x45, "b", "l", 0x01)] // 11
+#[case(0x45, "b", "l", 0xFF)] // 12
+#[case(0x47, "b", "a", 0x01)] // 13
+#[case(0x47, "b", "a", 0xFF)] // 14
+#[case(0x48, "c", "b", 0x01)] // 15
+#[case(0x48, "c", "b", 0xFF)] // 16
+#[case(0x49, "c", "c", 0x01)] // 17
+#[case(0x49, "c", "c", 0xFF)] // 18
+#[case(0x4A, "c", "d", 0x01)] // 19
+#[case(0x4A, "c", "d", 0xFF)] // 20
+#[case(0x4B, "c", "e", 0x01)] // 21
+#[case(0x4B, "c", "e", 0xFF)] // 22
+#[case(0x4C, "c", "h", 0x01)] // 23
+#[case(0x4C, "c", "h", 0xFF)] // 24
+#[case(0x4D, "c", "l", 0x01)] // 25
+#[case(0x4D, "c", "l", 0xFF)] // 26
+#[case(0x4F, "c", "a", 0x01)] // 27
+#[case(0x4F, "c", "a", 0xFF)] // 28
+#[case(0x50, "d", "b", 0x01)] // 29
+#[case(0x50, "d", "b", 0xFF)] // 30
+#[case(0x51, "d", "c", 0x01)] // 31
+#[case(0x51, "d", "c", 0xFF)] // 32
+#[case(0x52, "d", "d", 0x01)] // 33
+#[case(0x52, "d", "d", 0xFF)] // 34
+#[case(0x53, "d", "e", 0x01)] // 35
+#[case(0x53, "d", "e", 0xFF)] // 36
+#[case(0x54, "d", "h", 0x01)] // 37
+#[case(0x54, "d", "h", 0xFF)] // 38
+#[case(0x55, "d", "l", 0x01)] // 39
+#[case(0x55, "d", "l", 0xFF)] // 40
+#[case(0x57, "d", "a", 0x01)] // 41
+#[case(0x57, "d", "a", 0xFF)] // 42
+#[case(0x58, "e", "b", 0x01)] // 43
+#[case(0x58, "e", "b", 0xFF)] // 44
+#[case(0x59, "e", "c", 0x01)] // 45
+#[case(0x59, "e", "c", 0xFF)] // 46
+#[case(0x5a, "e", "d", 0x01)] // 47
+#[case(0x5a, "e", "d", 0xFF)] // 48
+#[case(0x5b, "e", "e", 0x01)] // 49
+#[case(0x5b, "e", "e", 0xFF)] // 50
+#[case(0x5c, "e", "h", 0x01)] // 51
+#[case(0x5c, "e", "h", 0xFF)] // 52
+#[case(0x5d, "e", "l", 0x01)] // 53
+#[case(0x5d, "e", "l", 0xFF)] // 54
+#[case(0x5f, "e", "a", 0x01)] // 55
+#[case(0x5f, "e", "a", 0xFF)] // 56
+#[case(0x60, "h", "b", 0x01)] // 57
+#[case(0x60, "h", "b", 0xFF)] // 58
+#[case(0x61, "h", "c", 0x01)] // 59
+#[case(0x61, "h", "c", 0xFF)] // 60
+#[case(0x62, "h", "d", 0x01)] // 61
+#[case(0x62, "h", "d", 0xFF)] // 62
+#[case(0x63, "h", "e", 0x01)] // 63
+#[case(0x63, "h", "e", 0xFF)] // 64
+#[case(0x64, "h", "h", 0x01)] // 65
+#[case(0x64, "h", "h", 0xFF)] // 66
+#[case(0x65, "h", "l", 0x01)] // 67
+#[case(0x65, "h", "l", 0xFF)] // 68
+#[case(0x67, "h", "a", 0x01)] // 69
+#[case(0x67, "h", "a", 0xFF)] // 70
+#[case(0x68, "l", "b", 0x01)] // 71
+#[case(0x68, "l", "b", 0xFF)] // 72
+#[case(0x69, "l", "c", 0x01)] // 73
+#[case(0x69, "l", "c", 0xFF)] // 74
+#[case(0x6a, "l", "d", 0x01)] // 75
+#[case(0x6a, "l", "d", 0xFF)] // 76
+#[case(0x6b, "l", "e", 0x01)] // 77
+#[case(0x6b, "l", "e", 0xFF)] // 78
+#[case(0x6c, "l", "h", 0x01)] // 79
+#[case(0x6c, "l", "h", 0xFF)] // 80
+#[case(0x6d, "l", "l", 0x01)] // 81
+#[case(0x6d, "l", "l", 0xFF)] // 82
+#[case(0x6f, "l", "a", 0x01)] // 83
+#[case(0x6f, "l", "a", 0xFF)] // 84
+#[case(0x78, "a", "b", 0x01)] // 85
+#[case(0x78, "a", "b", 0xFF)] // 86
+#[case(0x79, "a", "c", 0x01)] // 87
+#[case(0x79, "a", "c", 0xFF)] // 88
+#[case(0x7a, "a", "d", 0x01)] // 89
+#[case(0x7a, "a", "d", 0xFF)] // 90
+#[case(0x7b, "a", "e", 0x01)] // 91
+#[case(0x7b, "a", "e", 0xFF)] // 92
+#[case(0x7c, "a", "h", 0x01)] // 93
+#[case(0x7c, "a", "h", 0xFF)] // 94
+#[case(0x7d, "a", "l", 0x01)] // 95
+#[case(0x7d, "a", "l", 0xFF)] // 96
+#[case(0x7f, "a", "a", 0x01)] // 97
+#[case(0x7f, "a", "a", 0xFF)] // 98
+fn test_load_8_bit_reg_to_8_bit_reg(
+ #[case] opcode: u8,
+ #[case] dst_reg: &str,
+ #[case] src_reg: &str,
+ #[case] value: u8,
+) {
+ // Given
+ let builder = LR35902Builder::new()
+ .with_mem8(0x0000, opcode)
+ .with_reg8(src_reg, value);
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+
+ // Then
+ let expected = builder
+ .with_pc(1)
+ .with_clock_cycles(4)
+ .with_reg8(dst_reg, value)
+ .build();
+ assert_eq!(sut, expected);
+}
+
+#[rstest]
+#[case(0x46, "b", 0x0100, 0x01)] // 1
+#[case(0x46, "b", 0x0100, 0xFF)] // 2
+#[case(0x4E, "c", 0x0100, 0x01)] // 3
+#[case(0x4E, "c", 0x0100, 0xFF)] // 4
+#[case(0x56, "d", 0x0100, 0x01)] // 5
+#[case(0x56, "d", 0x0100, 0xFF)] // 6
+#[case(0x5e, "e", 0x0100, 0x01)] // 7
+#[case(0x5e, "e", 0x0100, 0xFF)] // 8
+#[case(0x66, "h", 0x0100, 0x01)] // 9
+#[case(0x66, "h", 0x0100, 0xFF)] // 10
+#[case(0x6E, "l", 0x0100, 0x01)] // 11
+#[case(0x6E, "l", 0x0100, 0xFF)] // 12
+#[case(0x7E, "a", 0x0100, 0x01)] // 13
+#[case(0x7E, "a", 0x0100, 0xFF)] // 14
+fn test_load_8_bit_reg_from_hl_pointer(
+ #[case] opcode: u8,
+ #[case] dst_reg: &str,
+ #[case] hl: u16,
+ #[case] value: u8,
+) {
+ // Given
+ let builder = LR35902Builder::new()
+ .with_mem8(0x0000, opcode)
+ .with_mem8(hl, value)
+ .with_hl(hl);
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+
+ // Then
+ let expected = builder
+ .with_pc(1)
+ .with_clock_cycles(8)
+ .with_reg8(dst_reg, value)
+ .build();
+ assert_eq!(sut, expected);
+}
+
+#[rstest]
+#[case(0x70, "b", 0x0100, 0x01)] // 1
+#[case(0x70, "b", 0x0100, 0xFF)] // 2
+#[case(0x71, "c", 0x0100, 0x01)] // 3
+#[case(0x71, "c", 0x0100, 0xFF)] // 4
+#[case(0x72, "d", 0x0100, 0x01)] // 5
+#[case(0x72, "d", 0x0100, 0xFF)] // 6
+#[case(0x73, "e", 0x0100, 0x01)] // 7
+#[case(0x73, "e", 0x0100, 0xFF)] // 8
+#[case(0x74, "h", 0x0100, 0x01)] // 9
+#[case(0x74, "h", 0x0100, 0xFF)] // 10
+#[case(0x75, "l", 0x0100, 0x01)] // 11
+#[case(0x75, "l", 0x0100, 0xFF)] // 12
+#[case(0x77, "a", 0x0100, 0x01)] // 13
+#[case(0x77, "a", 0x0100, 0xFF)] // 14
+fn test_load_hl_pointer_from_8_bit_reg(
+ #[case] opcode: u8,
+ #[case] src_reg: &str,
+ #[case] hl: u16,
+ #[case] value: u8,
+) {
+ // Given
+ let builder = LR35902Builder::new()
+ .with_mem8(0x0000, opcode)
+ .with_hl(hl)
+ .with_reg8(src_reg, value);
+
+ let mut sut = builder.clone().build();
+ let hl = sut.hl();
+
+ // When
+ sut.instruction();
+
+ // Then
+ let expected = builder
+ .with_pc(1)
+ .with_clock_cycles(8)
+ .with_mem8(hl, value)
+ .build();
+ assert_eq!(sut, expected);
+}
+
+#[rstest]
+#[case(0x01)]
+#[case(0xFF)]
+fn test_load_register_from_immediate(
+ #[values(
+ (0x06, "b"),
+ (0x16, "d"),
+ (0x26, "h"),
+ (0x0e, "c"),
+ (0x1e, "e"),
+ (0x2e, "l"),
+ (0x3e, "a"))]
+ _opcode_reg @ (opcode, reg): (u8, &str),
+ #[case] d8: u8,
+) {
+ // Given
+ let builder = LR35902Builder::new()
+ .with_mem8(0x0000, opcode)
+ .with_mem8(0x0001, d8);
+
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+
+ // Then
+ let expected = builder
+ .with_pc(2)
+ .with_clock_cycles(8)
+ .with_reg8(reg, d8)
+ .build();
+ assert_eq!(sut, expected);
+}
+
+#[rstest]
+#[case(0x01, 0x0100)]
+#[case(0xFF, 0x0100)]
+fn test_instr_0x36_ld_hl_d8(#[case] d8: u8, #[case] hl: u16) {
+ // Given
+ let builder = LR35902Builder::new()
+ .with_mem8(0x0000, 0x36)
+ .with_mem8(0x0001, d8)
+ .with_hl(hl);
+
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+
+ // Then
+ let expected = builder
+ .with_pc(2)
+ .with_clock_cycles(12)
+ .with_mem8(hl, d8)
+ .build();
+ assert_eq!(sut, expected);
+}
+
+// TODO e8 is signed
+#[rstest]
+fn test_instr_0xf8_ld_hl_sp_plus_e8() {
+ // Given
+ let builder = LR35902Builder::new()
+ .with_mem8(0x0000, 0xf8)
+ .with_mem8(0x0001, 0x05) // e8 = 5
+ .with_sp(0x1000);
+
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+
+ // Then
+ let expected = builder
+ .with_pc(2)
+ .with_clock_cycles(12)
+ .with_hl(0x1005)
+ .build();
+ assert_eq!(sut, expected);
+}
+
+#[test]
+fn test_instr_0xe0_ld_immediate8_pointer_from_register_a() {
+ // Given
+ let builder = LR35902Builder::new()
+ .with_mem8(0x0000, 0xe0)
+ .with_mem8(0x0001, 0xFF)
+ .with_a(0x01);
+
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+
+ // Then
+ let expected = builder
+ .with_pc(2)
+ .with_clock_cycles(12)
+ .with_mem8(0xFFFF, 0x01)
+ .build();
+ assert_eq!(sut, expected);
+}
+
+#[test]
+fn test_instr_0xe2_ld_pointer_c_from_register_a() {
+ // Given
+ let address = 0xFF;
+
+ let builder = LR35902Builder::new()
+ .with_mem8(0x0000, 0xe2)
+ .with_c(address)
+ .with_a(0x01);
+
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+
+ sut.mem8(0xFF00 + (address as u16));
+
+ // Then
+ let expected = builder
+ .with_pc(1)
+ .with_clock_cycles(8)
+ .with_mem8(0xFF00 + (address as u16), 0x01)
+ .build();
+ assert_eq!(sut, expected);
+}
+
+#[test]
+fn test_instr_0xea_ld_immediate16_pointer_from_register_a() {
+ // Given
+ let builder = LR35902Builder::new()
+ .with_mem8(0x0000, 0xea)
+ .with_mem16(0x0001, 0xFFFF)
+ .with_a(0x01);
+
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+
+ // Then
+ let expected = builder
+ .with_pc(3)
+ .with_clock_cycles(16)
+ .with_mem8(0xFFFF, 0x01)
+ .build();
+ assert_eq!(sut, expected);
+}
+
+#[test]
+fn test_instr_0xf0_ld_register_a_from_immediate_pointer() {
+ // Given
+ let builder = LR35902Builder::new()
+ .with_mem8(0x0000, 0xf0)
+ .with_mem8(0x0001, 0xFF)
+ .with_mem8(0xFFFF, 0x01);
+
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+
+ // Then
+ let expected = builder
+ .with_pc(2)
+ .with_clock_cycles(12)
+ .with_a(0x01)
+ .build();
+ assert_eq!(sut, expected);
+}
+
+#[test]
+fn test_instr_0xf2_ld_from_register_a_from_c_pointer() {
+ // Given
+ let address = 0xFF;
+
+ let builder = LR35902Builder::new()
+ .with_mem8(0x0000, 0xf2)
+ .with_mem8(0xFFFF, 0x01)
+ .with_c(address);
+
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+
+ sut.mem8(0xFF00 + (address as u16));
+
+ // Then
+ let expected = builder.with_pc(1).with_clock_cycles(8).with_a(0x01).build();
+ assert_eq!(sut, expected);
+}
+
+#[rstest]
+// RLCA
+#[case(0x07, 0b10000101, 0b00001011, 0b0000, 0b0001)]
+#[case(0x07, 0b10000101, 0b00001011, 0b0001, 0b0001)]
+#[case(0x07, 0b00000000, 0b00000000, 0b0000, 0b0000)]
+// RRCA
+#[case(0x0F, 0b00111011, 0b10011101, 0b0000, 0b0001)]
+#[case(0x0F, 0b00111011, 0b10011101, 0b0001, 0b0001)]
+#[case(0x0F, 0b00000000, 0b00000000, 0b0000, 0b0000)]
+// RLA
+#[case(0x17, 0b10010101, 0b00101011, 0b0001, 0b0001)]
+#[case(0x17, 0b10010101, 0b00101010, 0b0000, 0b0001)]
+#[case(0x17, 0b10000000, 0b00000000, 0b0000, 0b0001)]
+// RRA
+#[case(0x1F, 0b10000001, 0b01000000, 0b0000, 0b0001)]
+#[case(0x1F, 0b10000001, 0b11000000, 0b0001, 0b0001)]
+#[case(0x1F, 0b00000001, 0b00000000, 0b0000, 0b0001)]
+fn test_alu8_reg(
+ #[case] opcode: u8,
+ #[case] a: u8,
+ #[case] result: u8,
+ #[case] flags_before: u8,
+ #[case] flags_after: u8,
+) {
+ // Given
+ let builder = LR35902Builder::new()
+ .with_mem8(0x0000, opcode)
+ .with_a(a)
+ .with_f(flags_before << 4);
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+
+ // Then
+ let expected = builder
+ .with_pc(1)
+ .with_a(result)
+ .with_f(flags_after << 4)
+ .with_clock_cycles(4)
+ .build();
+ assert_eq!(sut, expected);
+}
+
+#[rstest]
+// ADD A,r8
+#[case(0x80, 0xfe, "b", 0x01, 0xff, 0b0000, 0b0000)]
+#[case(0x80, 0x0f, "b", 0x01, 0x10, 0b0000, 0b0010)]
+#[case(0x80, 0xff, "b", 0x01, 0x00, 0b0000, 0b1011)]
+#[case(0x81, 0xff, "c", 0x01, 0x00, 0b0000, 0b1011)]
+#[case(0x82, 0xff, "d", 0x01, 0x00, 0b0000, 0b1011)]
+#[case(0x83, 0xff, "e", 0x01, 0x00, 0b0000, 0b1011)]
+#[case(0x84, 0xff, "h", 0x01, 0x00, 0b0000, 0b1011)]
+#[case(0x85, 0xff, "l", 0x01, 0x00, 0b0000, 0b1011)]
+#[case(0x87, 0x80, "a", 0x80, 0x00, 0b0000, 0b1001)]
+#[case(0x87, 0x88, "a", 0x88, 0x10, 0b0000, 0b0011)]
+// XOR r8
+#[case(0xA8, 0xca, "b", 0xfe, 0x34, 0b0000, 0b0000)]
+#[case(0xA8, 0xca, "b", 0xca, 0x00, 0b0000, 0b1000)]
+#[case(0xA9, 0xca, "c", 0xfe, 0x34, 0b0000, 0b0000)]
+#[case(0xAA, 0xca, "d", 0xfe, 0x34, 0b0000, 0b0000)]
+#[case(0xAB, 0xca, "e", 0xfe, 0x34, 0b0000, 0b0000)]
+#[case(0xAC, 0xca, "h", 0xfe, 0x34, 0b0000, 0b0000)]
+#[case(0xAD, 0xca, "l", 0xfe, 0x34, 0b0000, 0b0000)]
+#[case(0xAF, 0xca, "a", 0xca, 0x00, 0b0000, 0b1000)]
+// AND r8
+#[case(0xA0, 0xca, "b", 0xfe, 0xca, 0b0000, 0b0010)]
+#[case(0xA0, 0xfe, "b", 0x01, 0x00, 0b0000, 0b1010)]
+#[case(0xA1, 0xca, "c", 0xfe, 0xca, 0b0000, 0b0010)]
+#[case(0xA2, 0xca, "d", 0xfe, 0xca, 0b0000, 0b0010)]
+#[case(0xA3, 0xca, "e", 0xfe, 0xca, 0b0000, 0b0010)]
+#[case(0xA4, 0xca, "h", 0xfe, 0xca, 0b0000, 0b0010)]
+#[case(0xA5, 0xca, "l", 0xfe, 0xca, 0b0000, 0b0010)]
+#[case(0xA7, 0xca, "a", 0xca, 0xca, 0b0000, 0b0010)]
+// OR r8
+#[case(0xB0, 0xca, "b", 0xfe, 0xfe, 0b0000, 0b0000)]
+#[case(0xB0, 0x00, "b", 0x00, 0x00, 0b0000, 0b1000)]
+#[case(0xB1, 0xca, "c", 0xfe, 0xfe, 0b0000, 0b0000)]
+#[case(0xB2, 0xca, "d", 0xfe, 0xfe, 0b0000, 0b0000)]
+#[case(0xB3, 0xca, "e", 0xfe, 0xfe, 0b0000, 0b0000)]
+#[case(0xB4, 0xca, "h", 0xfe, 0xfe, 0b0000, 0b0000)]
+#[case(0xB5, 0xca, "l", 0xfe, 0xfe, 0b0000, 0b0000)]
+#[case(0xB7, 0xca, "a", 0xca, 0xca, 0b0000, 0b0000)]
+// ADC A,r8
+#[case(0x88, 0xfe, "b", 0x01, 0xff, 0b0000, 0b0000)]
+#[case(0x88, 0xfd, "b", 0x01, 0xff, 0b0001, 0b0000)]
+#[case(0x88, 0x0f, "b", 0x01, 0x10, 0b0000, 0b0010)]
+#[case(0x88, 0x0e, "b", 0x01, 0x10, 0b0001, 0b0010)]
+#[case(0x88, 0xff, "b", 0x01, 0x00, 0b0000, 0b1011)]
+#[case(0x88, 0xfe, "b", 0x01, 0x00, 0b0001, 0b1011)]
+#[case(0x89, 0xff, "c", 0x01, 0x00, 0b0000, 0b1011)]
+#[case(0x89, 0xfe, "c", 0x01, 0x00, 0b0001, 0b1011)]
+#[case(0x8A, 0xff, "d", 0x01, 0x00, 0b0000, 0b1011)]
+#[case(0x8A, 0xfe, "d", 0x01, 0x00, 0b0001, 0b1011)]
+#[case(0x8B, 0xff, "e", 0x01, 0x00, 0b0000, 0b1011)]
+#[case(0x8B, 0xfe, "e", 0x01, 0x00, 0b0001, 0b1011)]
+#[case(0x8C, 0xff, "h", 0x01, 0x00, 0b0000, 0b1011)]
+#[case(0x8C, 0xfe, "h", 0x01, 0x00, 0b0001, 0b1011)]
+#[case(0x8D, 0xff, "l", 0x01, 0x00, 0b0000, 0b1011)]
+#[case(0x8D, 0xfe, "l", 0x01, 0x00, 0b0001, 0b1011)]
+#[case(0x8F, 0x80, "a", 0x80, 0x00, 0b0000, 0b1001)]
+#[case(0x8F, 0x80, "a", 0x80, 0x01, 0b0001, 0b0001)]
+#[case(0x8F, 0x88, "a", 0x88, 0x10, 0b0000, 0b0011)]
+// SUB A,r8
+#[case::sub01(0x90, 0x3E, "b", 0x3E, 0x00, 0b0000, 0b1100)]
+#[case::sub02(0x90, 0x3E, "b", 0x0F, 0x2F, 0b0000, 0b0110)]
+#[case::sub03(0x90, 0x3E, "b", 0x40, 0xFE, 0b0000, 0b0101)]
+#[case::sub04(0x90, 0x01, "b", 0xF1, 0x10, 0b0000, 0b0101)]
+#[case::sub05(0x91, 0x3E, "c", 0x0F, 0x2F, 0b0000, 0b0110)]
+#[case::sub06(0x92, 0x3E, "d", 0x0F, 0x2F, 0b0000, 0b0110)]
+#[case::sub07(0x93, 0x3E, "e", 0x0F, 0x2F, 0b0000, 0b0110)]
+#[case::sub08(0x94, 0x3E, "h", 0x0F, 0x2F, 0b0000, 0b0110)]
+#[case::sub09(0x95, 0x3E, "l", 0x0F, 0x2F, 0b0000, 0b0110)]
+#[case::sub10(0x97, 0x3E, "a", 0x3E, 0x00, 0b0000, 0b1100)]
+// SBC A,r8
+#[case::sbc01(0x98, 0x3E, "b", 0x3E, 0x00, 0b0000, 0b1100)]
+#[case::sbc01_c(0x98, 0x3E, "b", 0x3D, 0x00, 0b0001, 0b1100)]
+#[case::sbc02(0x98, 0x3E, "b", 0x0F, 0x2F, 0b0000, 0b0110)]
+#[case::sbc02_c(0x98, 0x3E, "b", 0x0E, 0x2F, 0b0001, 0b0110)]
+#[case::sbc03(0x98, 0x3E, "b", 0x40, 0xFE, 0b0000, 0b0101)]
+#[case::sbc03_c(0x98, 0x3F, "b", 0x40, 0xFE, 0b0001, 0b0101)]
+#[case::sbc04(0x98, 0x01, "b", 0xF1, 0x10, 0b0000, 0b0101)]
+#[case::sbc04_c(0x98, 0x01, "b", 0xF0, 0x10, 0b0001, 0b0101)]
+#[case::sbc05(0x99, 0x3E, "c", 0x0E, 0x2F, 0b0001, 0b0110)]
+#[case::sbc06(0x9A, 0x3E, "d", 0x0E, 0x2F, 0b0001, 0b0110)]
+#[case::sbc07(0x9B, 0x3E, "e", 0x0E, 0x2F, 0b0001, 0b0110)]
+#[case::sbc08(0x9C, 0x3E, "h", 0x0E, 0x2F, 0b0001, 0b0110)]
+#[case::sbc09(0x9D, 0x3E, "l", 0x0E, 0x2F, 0b0001, 0b0110)]
+#[case::sbc10(0x9F, 0x3E, "a", 0x3E, 0xFF, 0b0001, 0b0111)]
+fn test_alu8_reg_reg(
+ #[case] opcode: u8,
+ #[case] a: u8,
+ #[case] src_reg: &str,
+ #[case] value: u8,
+ #[case] result: u8,
+ #[case] flags_before: u8,
+ #[case] flags_after: u8,
+) {
+ // Given
+ let builder = LR35902Builder::new()
+ .with_mem8(0x0000, opcode)
+ .with_a(a)
+ .with_reg8(src_reg, value)
+ .with_f(flags_before << 4);
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+
+ // Then
+ let expected = builder
+ .with_pc(1)
+ .with_a(result)
+ .with_f(flags_after << 4)
+ .with_clock_cycles(4)
+ .build();
+ assert_eq!(sut, expected);
+}
+
+#[rstest]
+// ADD A,(HL)
+#[case(0x86, 0xfe, 0x0001, 0x01, 0xff, 0b0000, 0b0000)]
+#[case(0x86, 0xff, 0x0001, 0x01, 0x00, 0b0000, 0b1011)]
+#[case(0x86, 0xff, 0xcafe, 0x01, 0x00, 0b0000, 0b1011)]
+// ADC A,(HL)
+#[case(0x8E, 0xfd, 0x0001, 0x01, 0xff, 0b0001, 0b0000)]
+#[case(0x8E, 0xfe, 0x0001, 0x01, 0x00, 0b0001, 0b1011)]
+#[case(0x8E, 0xfe, 0xcafe, 0x01, 0x00, 0b0001, 0b1011)]
+// XOR (HL)
+#[case(0xAE, 0xca, 0x0001, 0xfe, 0x34, 0b0000, 0b0000)]
+#[case(0xAE, 0x01, 0xcafe, 0x01, 0x00, 0b0000, 0b1000)]
+// AND (HL)
+#[case(0xA6, 0xca, 0x0001, 0xfe, 0xca, 0b0000, 0b0010)]
+#[case(0xA6, 0xfe, 0xcafe, 0x01, 0x00, 0b0000, 0b1010)]
+// OR (HL)
+#[case(0xB6, 0xca, 0x0001, 0xfe, 0xfe, 0b0000, 0b0000)]
+#[case(0xB6, 0x00, 0xcafe, 0x00, 0x00, 0b0000, 0b1000)]
+// SUB (HL)
+#[case(0x96, 0x3E, 0xcafe, 0x0F, 0x2F, 0b0000, 0b0110)]
+// SBC A,(HL)
+#[case(0x9E, 0x3E, 0xcafe, 0x0F, 0x2E, 0b0001, 0b0110)]
+fn test_alu8_reg_addr(
+ #[case] opcode: u8,
+ #[case] a: u8,
+ #[case] hl_addr: u16,
+ #[case] value: u8,
+ #[case] result: u8,
+ #[case] flags_before: u8,
+ #[case] flags_after: u8,
+) {
+ // Given
+ let builder = LR35902Builder::new()
+ .with_mem8(0x0000, opcode)
+ .with_a(a)
+ .with_hl(hl_addr)
+ .with_mem8(hl_addr, value)
+ .with_f(flags_before << 4);
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+
+ // Then
+ let expected = builder
+ .with_pc(1)
+ .with_a(result)
+ .with_f(flags_after << 4)
+ .with_clock_cycles(8)
+ .build();
+ assert_eq!(sut, expected);
+}
+
+#[rstest]
+// ADD A,d8
+#[case(0xC6, 0xfe, 0x01, 0xff, 0b0000, 0b0000)]
+#[case(0xC6, 0x0f, 0x01, 0x10, 0b0000, 0b0010)]
+#[case(0xC6, 0xff, 0x01, 0x00, 0b0000, 0b1011)]
+// XOR d8
+#[case(0xEE, 0xca, 0xfe, 0x34, 0b0000, 0b0000)]
+#[case(0xEE, 0xca, 0xca, 0x00, 0b0000, 0b1000)]
+// AND d8
+#[case(0xE6, 0xca, 0xfe, 0xca, 0b0000, 0b0010)]
+#[case(0xE6, 0xfe, 0x01, 0x00, 0b0000, 0b1010)]
+// OR d8
+#[case(0xF6, 0xca, 0xfe, 0xfe, 0b0000, 0b0000)]
+#[case(0xF6, 0x00, 0x00, 0x00, 0b0000, 0b1000)]
+// ADC A,d8
+#[case(0xCE, 0xfe, 0x01, 0xff, 0b0000, 0b0000)]
+#[case(0xCE, 0xfd, 0x01, 0xff, 0b0001, 0b0000)]
+#[case(0xCE, 0x0f, 0x01, 0x10, 0b0000, 0b0010)]
+#[case(0xCE, 0x0e, 0x01, 0x10, 0b0001, 0b0010)]
+#[case(0xCE, 0xff, 0x01, 0x00, 0b0000, 0b1011)]
+#[case(0xCE, 0xfe, 0x01, 0x00, 0b0001, 0b1011)]
+// SUB A,d8
+#[case(0xD6, 0x3E, 0x0F, 0x2F, 0b0000, 0b0110)]
+#[case(0xD6, 99, 100, 0xFF, 0b0000, 0b0111)]
+#[case(0xD6, 100, 100, 0x00, 0b0000, 0b1100)]
+// SBC A,d8
+#[case(0xDE, 0x3E, 0x0F, 0x2E, 0b0001, 0b0110)]
+fn test_alu8_reg_imm(
+ #[case] opcode: u8,
+ #[case] a: u8,
+ #[case] value: u8,
+ #[case] result: u8,
+ #[case] flags_before: u8,
+ #[case] flags_after: u8,
+) {
+ // Given
+ let builder = LR35902Builder::new()
+ .with_mem8(0x0000, opcode)
+ .with_mem8(0x0001, value)
+ .with_a(a)
+ .with_f(flags_before << 4);
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+
+ // Then
+ let expected = builder
+ .with_pc(2)
+ .with_a(result)
+ .with_f(flags_after << 4)
+ .with_clock_cycles(8)
+ .build();
+ assert_eq!(sut, expected);
+}
+
+#[rstest]
+// ADD HL,r16
+#[case(0x09, 0xffff, "bc", 0x0001, 0x0, 0b0000, 0b0011)]
+#[case(0x09, 0xffff, "bc", 0x0001, 0x0, 0b1000, 0b1011)]
+#[case(0x19, 0xffff, "de", 0x0001, 0x0, 0b0000, 0b0011)]
+#[case(0x19, 0xffff, "de", 0x0001, 0x0, 0b1000, 0b1011)]
+#[case(0x29, 0x8000, "hl", 0x8000, 0x0, 0b0000, 0b0001)]
+#[case(0x29, 0x8000, "hl", 0x8000, 0x0, 0b1000, 0b1001)]
+#[case(0x39, 0xffff, "sp", 0x0001, 0x0, 0b0000, 0b0011)]
+#[case(0x39, 0xffff, "sp", 0x0001, 0x0, 0b1000, 0b1011)]
+fn test_alu16_reg_reg(
+ #[case] opcode: u8,
+ #[case] hl: u16,
+ #[case] src_reg: &str,
+ #[case] value: u16,
+ #[case] result: u16,
+ #[case] flags_before: u8,
+ #[case] flags_after: u8,
+) {
+ // Given
+ let builder = LR35902Builder::new()
+ .with_mem8(0x0000, opcode)
+ .with_f(flags_before << 4)
+ .with_hl(hl)
+ .with_reg16(src_reg, value);
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+
+ // Then
+ let expected = builder
+ .with_pc(1)
+ .with_hl(result)
+ .with_f(flags_after << 4)
+ .with_clock_cycles(8)
+ .build();
+ assert_eq!(sut, expected);
+}
+
+#[rstest]
+#[rustfmt::skip]
+// ADD SP,r8
+#[case(0xE8, 0x0FFF, 1i8, 0x1000, 0b0000, 0b0010)]
+#[case(0xE8, 0x0FFF, -128i8, 0x0F7F, 0b0000, 0b0000)]
+#[case(0xE8, 0x0FFF, 127i8, 0x107E, 0b0000, 0b0010)]
+#[case(0xE8, 0xFFFF, 1i8, 0x0000, 0b0000, 0b0011)]
+#[case(0xE8, 0x0000, -1i8, 0xFFFF, 0b0000, 0b0011)]
+fn test_alu16_reg_imm(
+ #[case] opcode: u8,
+ #[case] sp: u16,
+ #[case] value: i8,
+ #[case] result: u16,
+ #[case] flags_before: u8,
+ #[case] flags_after: u8,
+) {
+ // Given
+ let builder = LR35902Builder::new()
+ .with_mem8(0x0000, opcode)
+ .with_mem8(0x0001, value as u8)
+ .with_sp(sp)
+ .with_f(flags_before << 4);
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+
+ // Then
+ let expected = builder
+ .with_pc(2)
+ .with_sp(result)
+ .with_f(flags_after << 4)
+ .with_clock_cycles(16)
+ .build();
+ assert_eq!(sut, expected);
+}
+
+#[rstest]
+// RLC r8
+#[case(0x00, "b", 0b10000101, 0b00001011, 0b0000, 0b0001)]
+#[case(0x00, "b", 0b01110101, 0b11101010, 0b0000, 0b0000)]
+#[case(0x00, "b", 0b00000000, 0b00000000, 0b0000, 0b1000)]
+#[case(0x01, "c", 0b10000101, 0b00001011, 0b0000, 0b0001)]
+#[case(0x01, "c", 0b01110101, 0b11101010, 0b0000, 0b0000)]
+#[case(0x01, "c", 0b00000000, 0b00000000, 0b0000, 0b1000)]
+#[case(0x02, "d", 0b10000101, 0b00001011, 0b0000, 0b0001)]
+#[case(0x02, "d", 0b01110101, 0b11101010, 0b0000, 0b0000)]
+#[case(0x02, "d", 0b00000000, 0b00000000, 0b0000, 0b1000)]
+#[case(0x03, "e", 0b10000101, 0b00001011, 0b0000, 0b0001)]
+#[case(0x03, "e", 0b01110101, 0b11101010, 0b0000, 0b0000)]
+#[case(0x03, "e", 0b00000000, 0b00000000, 0b0000, 0b1000)]
+#[case(0x04, "h", 0b10000101, 0b00001011, 0b0000, 0b0001)]
+#[case(0x04, "h", 0b01110101, 0b11101010, 0b0000, 0b0000)]
+#[case(0x04, "h", 0b00000000, 0b00000000, 0b0000, 0b1000)]
+#[case(0x05, "l", 0b10000101, 0b00001011, 0b0000, 0b0001)]
+#[case(0x05, "l", 0b01110101, 0b11101010, 0b0000, 0b0000)]
+#[case(0x05, "l", 0b00000000, 0b00000000, 0b0000, 0b1000)]
+#[case(0x07, "a", 0b10000101, 0b00001011, 0b0000, 0b0001)]
+#[case(0x07, "a", 0b01110101, 0b11101010, 0b0000, 0b0000)]
+#[case(0x07, "a", 0b00000000, 0b00000000, 0b0000, 0b1000)]
+// RRC r8
+#[case(0x08, "b", 0b00000001, 0b10000000, 0b0000, 0b0001)]
+#[case(0x08, "b", 0b01110100, 0b00111010, 0b0000, 0b0000)]
+#[case(0x08, "b", 0b00000000, 0b00000000, 0b0000, 0b1000)]
+#[case(0x09, "c", 0b00000001, 0b10000000, 0b0000, 0b0001)]
+#[case(0x09, "c", 0b01110100, 0b00111010, 0b0000, 0b0000)]
+#[case(0x09, "c", 0b00000000, 0b00000000, 0b0000, 0b1000)]
+#[case(0x0A, "d", 0b00000001, 0b10000000, 0b0000, 0b0001)]
+#[case(0x0A, "d", 0b01110100, 0b00111010, 0b0000, 0b0000)]
+#[case(0x0A, "d", 0b00000000, 0b00000000, 0b0000, 0b1000)]
+#[case(0x0B, "e", 0b00000001, 0b10000000, 0b0000, 0b0001)]
+#[case(0x0B, "e", 0b01110100, 0b00111010, 0b0000, 0b0000)]
+#[case(0x0B, "e", 0b00000000, 0b00000000, 0b0000, 0b1000)]
+#[case(0x0C, "h", 0b00000001, 0b10000000, 0b0000, 0b0001)]
+#[case(0x0C, "h", 0b01110100, 0b00111010, 0b0000, 0b0000)]
+#[case(0x0C, "h", 0b00000000, 0b00000000, 0b0000, 0b1000)]
+#[case(0x0D, "l", 0b00000001, 0b10000000, 0b0000, 0b0001)]
+#[case(0x0D, "l", 0b01110100, 0b00111010, 0b0000, 0b0000)]
+#[case(0x0D, "l", 0b00000000, 0b00000000, 0b0000, 0b1000)]
+#[case(0x0F, "a", 0b00000001, 0b10000000, 0b0000, 0b0001)]
+#[case(0x0F, "a", 0b01110100, 0b00111010, 0b0000, 0b0000)]
+#[case(0x0F, "a", 0b00000000, 0b00000000, 0b0000, 0b1000)]
+// RL r8
+#[case(0x10, "b", 0b10000000, 0b00000000, 0b0000, 0b1001)]
+#[case(0x10, "b", 0b10010101, 0b00101011, 0b0001, 0b0001)]
+#[case(0x10, "b", 0b10010101, 0b00101010, 0b0000, 0b0001)]
+#[case(0x11, "c", 0b10000000, 0b00000000, 0b0000, 0b1001)]
+#[case(0x11, "c", 0b10010101, 0b00101011, 0b0001, 0b0001)]
+#[case(0x11, "c", 0b10010101, 0b00101010, 0b0000, 0b0001)]
+#[case(0x12, "d", 0b10000000, 0b00000000, 0b0000, 0b1001)]
+#[case(0x12, "d", 0b10010101, 0b00101011, 0b0001, 0b0001)]
+#[case(0x12, "d", 0b10010101, 0b00101010, 0b0000, 0b0001)]
+#[case(0x13, "e", 0b10000000, 0b00000000, 0b0000, 0b1001)]
+#[case(0x13, "e", 0b10010101, 0b00101011, 0b0001, 0b0001)]
+#[case(0x13, "e", 0b10010101, 0b00101010, 0b0000, 0b0001)]
+#[case(0x14, "h", 0b10000000, 0b00000000, 0b0000, 0b1001)]
+#[case(0x14, "h", 0b10010101, 0b00101011, 0b0001, 0b0001)]
+#[case(0x14, "h", 0b10010101, 0b00101010, 0b0000, 0b0001)]
+#[case(0x15, "l", 0b10000000, 0b00000000, 0b0000, 0b1001)]
+#[case(0x15, "l", 0b10010101, 0b00101011, 0b0001, 0b0001)]
+#[case(0x15, "l", 0b10010101, 0b00101010, 0b0000, 0b0001)]
+#[case(0x17, "a", 0b10000000, 0b00000000, 0b0000, 0b1001)]
+#[case(0x17, "a", 0b10010101, 0b00101011, 0b0001, 0b0001)]
+#[case(0x17, "a", 0b10010101, 0b00101010, 0b0000, 0b0001)]
+// RR r8
+#[case(0x18, "b", 0b00000001, 0b00000000, 0b0000, 0b1001)]
+#[case(0x18, "b", 0b10000001, 0b01000000, 0b0000, 0b0001)]
+#[case(0x18, "b", 0b10000001, 0b11000000, 0b0001, 0b0001)]
+#[case(0x19, "c", 0b00000001, 0b00000000, 0b0000, 0b1001)]
+#[case(0x19, "c", 0b10000001, 0b01000000, 0b0000, 0b0001)]
+#[case(0x19, "c", 0b10000001, 0b11000000, 0b0001, 0b0001)]
+#[case(0x1A, "d", 0b00000001, 0b00000000, 0b0000, 0b1001)]
+#[case(0x1A, "d", 0b10000001, 0b01000000, 0b0000, 0b0001)]
+#[case(0x1A, "d", 0b10000001, 0b11000000, 0b0001, 0b0001)]
+#[case(0x1B, "e", 0b00000001, 0b00000000, 0b0000, 0b1001)]
+#[case(0x1B, "e", 0b10000001, 0b01000000, 0b0000, 0b0001)]
+#[case(0x1B, "e", 0b10000001, 0b11000000, 0b0001, 0b0001)]
+#[case(0x1C, "h", 0b00000001, 0b00000000, 0b0000, 0b1001)]
+#[case(0x1C, "h", 0b10000001, 0b01000000, 0b0000, 0b0001)]
+#[case(0x1C, "h", 0b10000001, 0b11000000, 0b0001, 0b0001)]
+#[case(0x1D, "l", 0b00000001, 0b00000000, 0b0000, 0b1001)]
+#[case(0x1D, "l", 0b10000001, 0b01000000, 0b0000, 0b0001)]
+#[case(0x1D, "l", 0b10000001, 0b11000000, 0b0001, 0b0001)]
+#[case(0x1F, "a", 0b00000001, 0b00000000, 0b0000, 0b1001)]
+#[case(0x1F, "a", 0b10000001, 0b01000000, 0b0000, 0b0001)]
+#[case(0x1F, "a", 0b10000001, 0b11000000, 0b0001, 0b0001)]
+// SLA r8
+#[case(0x20, "b", 0b10000000, 0b00000000, 0b0000, 0b1001)]
+#[case(0x20, "b", 0b11000000, 0b10000000, 0b0000, 0b0001)]
+#[case(0x21, "c", 0b10000000, 0b00000000, 0b0000, 0b1001)]
+#[case(0x22, "d", 0b10000000, 0b00000000, 0b0000, 0b1001)]
+#[case(0x23, "e", 0b10000000, 0b00000000, 0b0000, 0b1001)]
+#[case(0x24, "h", 0b10000000, 0b00000000, 0b0000, 0b1001)]
+#[case(0x25, "l", 0b10000000, 0b00000000, 0b0000, 0b1001)]
+#[case(0x27, "a", 0b10000000, 0b00000000, 0b0000, 0b1001)]
+// SRA r8
+#[case(0x28, "b", 0b10001010, 0b11000101, 0b0000, 0b0000)]
+#[case(0x28, "b", 0b00000001, 0b00000000, 0b0000, 0b1001)]
+#[case(0x29, "c", 0b10001010, 0b11000101, 0b0000, 0b0000)]
+#[case(0x29, "c", 0b00000001, 0b00000000, 0b0000, 0b1001)]
+#[case(0x2A, "d", 0b10001010, 0b11000101, 0b0000, 0b0000)]
+#[case(0x2A, "d", 0b00000001, 0b00000000, 0b0000, 0b1001)]
+#[case(0x2B, "e", 0b10001010, 0b11000101, 0b0000, 0b0000)]
+#[case(0x2B, "e", 0b00000001, 0b00000000, 0b0000, 0b1001)]
+#[case(0x2C, "h", 0b10001010, 0b11000101, 0b0000, 0b0000)]
+#[case(0x2C, "h", 0b00000001, 0b00000000, 0b0000, 0b1001)]
+#[case(0x2D, "l", 0b10001010, 0b11000101, 0b0000, 0b0000)]
+#[case(0x2D, "l", 0b00000001, 0b00000000, 0b0000, 0b1001)]
+#[case(0x2F, "a", 0b10001010, 0b11000101, 0b0000, 0b0000)]
+#[case(0x2F, "a", 0b00000001, 0b00000000, 0b0000, 0b1001)]
+// SRL r8
+#[case(0x38, "b", 0b00000001, 0b00000000, 0b0000, 0b1001)]
+#[case(0x38, "b", 0b10000000, 0b01000000, 0b0000, 0b0000)]
+#[case(0x39, "c", 0b00000001, 0b00000000, 0b0000, 0b1001)]
+#[case(0x39, "c", 0b10000000, 0b01000000, 0b0000, 0b0000)]
+#[case(0x3A, "d", 0b00000001, 0b00000000, 0b0000, 0b1001)]
+#[case(0x3A, "d", 0b10000000, 0b01000000, 0b0000, 0b0000)]
+#[case(0x3B, "e", 0b00000001, 0b00000000, 0b0000, 0b1001)]
+#[case(0x3B, "e", 0b10000000, 0b01000000, 0b0000, 0b0000)]
+#[case(0x3C, "h", 0b00000001, 0b00000000, 0b0000, 0b1001)]
+#[case(0x3C, "h", 0b10000000, 0b01000000, 0b0000, 0b0000)]
+#[case(0x3D, "l", 0b00000001, 0b00000000, 0b0000, 0b1001)]
+#[case(0x3D, "l", 0b10000000, 0b01000000, 0b0000, 0b0000)]
+#[case(0x3F, "a", 0b00000001, 0b00000000, 0b0000, 0b1001)]
+#[case(0x3F, "a", 0b10000000, 0b01000000, 0b0000, 0b0000)]
+// SWAP r8
+#[case(0x30, "b", 0b00000000, 0b00000000, 0b0000, 0b1000)]
+#[case(0x30, "b", 0b11110000, 0b00001111, 0b0000, 0b0000)]
+#[case(0x31, "c", 0b00000000, 0b00000000, 0b0000, 0b1000)]
+#[case(0x31, "c", 0b11110000, 0b00001111, 0b0000, 0b0000)]
+#[case(0x32, "d", 0b00000000, 0b00000000, 0b0000, 0b1000)]
+#[case(0x32, "d", 0b11110000, 0b00001111, 0b0000, 0b0000)]
+#[case(0x33, "e", 0b00000000, 0b00000000, 0b0000, 0b1000)]
+#[case(0x33, "e", 0b11110000, 0b00001111, 0b0000, 0b0000)]
+#[case(0x34, "h", 0b00000000, 0b00000000, 0b0000, 0b1000)]
+#[case(0x34, "h", 0b11110000, 0b00001111, 0b0000, 0b0000)]
+#[case(0x35, "l", 0b00000000, 0b00000000, 0b0000, 0b1000)]
+#[case(0x35, "l", 0b11110000, 0b00001111, 0b0000, 0b0000)]
+#[case(0x37, "a", 0b00000000, 0b00000000, 0b0000, 0b1000)]
+#[case(0x37, "a", 0b11110000, 0b00001111, 0b0000, 0b0000)]
+fn test_rot_reg_reg(
+ #[case] opcode: u16,
+ #[case] src_reg: &str,
+ #[case] value: u8,
+ #[case] result: u8,
+ #[case] flags_before: u8,
+ #[case] flags_after: u8,
+) {
+ // Given
+ let builder = LR35902Builder::new()
+ .with_mem16(0x0000, (opcode << 8) + 0xCB)
+ .with_reg8(src_reg, value)
+ .with_f(flags_before << 4);
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+ sut.instruction();
+
+ // Then
+ let expected = builder
+ .with_pc(2)
+ .with_reg8(src_reg, result)
+ .with_f(flags_after << 4)
+ .with_clock_cycles(8)
+ .build();
+ assert_eq!(sut, expected);
+}
+
+#[rstest]
+// RLC (HL)
+#[case(0x06, 0xcafe, 0b10000101, 0b00001011, 0b0000, 0b0001)]
+#[case(0x06, 0xcafe, 0b01110101, 0b11101010, 0b0000, 0b0000)]
+#[case(0x06, 0xcafe, 0b00000000, 0b00000000, 0b0000, 0b1000)]
+// RRC (HL)
+#[case(0x0E, 0xcafe, 0b00000001, 0b10000000, 0b0000, 0b0001)]
+#[case(0x0E, 0xcafe, 0b01110100, 0b00111010, 0b0000, 0b0000)]
+#[case(0x0E, 0xcafe, 0b00000000, 0b00000000, 0b0000, 0b1000)]
+// RL (HL)
+#[case(0x16, 0xcafe, 0b10000000, 0b00000000, 0b0000, 0b1001)]
+#[case(0x16, 0xcafe, 0b10010101, 0b00101011, 0b0001, 0b0001)]
+#[case(0x16, 0xcafe, 0b10010101, 0b00101010, 0b0000, 0b0001)]
+// RR (HL)
+#[case(0x1E, 0xcafe, 0b00000001, 0b00000000, 0b0000, 0b1001)]
+#[case(0x1E, 0xcafe, 0b10000001, 0b01000000, 0b0000, 0b0001)]
+#[case(0x1E, 0xcafe, 0b10000001, 0b11000000, 0b0001, 0b0001)]
+// SLA (HL)
+#[case(0x26, 0xcafe, 0b11111111, 0b11111110, 0b0000, 0b0001)]
+// SRA (HL)
+#[case(0x2E, 0xcafe, 0b00000001, 0b00000000, 0b0000, 0b1001)]
+// SRL (HL)
+#[case(0x3E, 0xcafe, 0b11111111, 0b01111111, 0b0000, 0b0001)]
+// SWAP (HL)
+#[case(0x36, 0xcafe, 0b00000000, 0b00000000, 0b0000, 0b1000)]
+#[case(0x36, 0xcafe, 0b11110000, 0b00001111, 0b0000, 0b0000)]
+fn test_rot_reg_addr(
+ #[case] opcode: u16,
+ #[case] hl_addr: u16,
+ #[case] value: u8,
+ #[case] result: u8,
+ #[case] flags_before: u8,
+ #[case] flags_after: u8,
+) {
+ // Given
+ let builder = LR35902Builder::new()
+ .with_mem16(0x0000, (opcode << 8) + 0xCB)
+ .with_hl(hl_addr)
+ .with_mem8(hl_addr, value)
+ .with_f(flags_before << 4);
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+ sut.instruction();
+
+ // Then
+ let expected = builder
+ .with_pc(2)
+ .with_mem8(hl_addr, result)
+ .with_f(flags_after << 4)
+ .with_clock_cycles(16)
+ .build();
+ assert_eq!(sut, expected);
+}
+
+#[rstest]
+#[case::not_zero(0x1, 0b0001, 0b0011)]
+#[case::zero(0x0, 0b0000, 0b1010)]
+// BIT n,REG
+fn test_bit_reg(
+ #[values(
+ (0x40, "b", 0),
+ (0x41, "c", 0),
+ (0x42, "d", 0),
+ (0x43, "e", 0),
+ (0x44, "h", 0),
+ (0x45, "l", 0),
+ (0x47, "a", 0),
+ (0x48, "b", 1),
+ (0x49, "c", 1),
+ (0x4A, "d", 1),
+ (0x4B, "e", 1),
+ (0x4C, "h", 1),
+ (0x4D, "l", 1),
+ (0x4F, "a", 1),
+ (0x50, "b", 2),
+ (0x51, "c", 2),
+ (0x52, "d", 2),
+ (0x53, "e", 2),
+ (0x54, "h", 2),
+ (0x55, "l", 2),
+ (0x57, "a", 2),
+ (0x58, "b", 3),
+ (0x59, "c", 3),
+ (0x5A, "d", 3),
+ (0x5B, "e", 3),
+ (0x5C, "h", 3),
+ (0x5D, "l", 3),
+ (0x5F, "a", 3),
+ (0x60, "b", 4),
+ (0x61, "c", 4),
+ (0x62, "d", 4),
+ (0x63, "e", 4),
+ (0x64, "h", 4),
+ (0x65, "l", 4),
+ (0x67, "a", 4),
+ (0x68, "b", 5),
+ (0x69, "c", 5),
+ (0x6A, "d", 5),
+ (0x6B, "e", 5),
+ (0x6C, "h", 5),
+ (0x6D, "l", 5),
+ (0x6F, "a", 5),
+ (0x70, "b", 6),
+ (0x71, "c", 6),
+ (0x72, "d", 6),
+ (0x73, "e", 6),
+ (0x74, "h", 6),
+ (0x75, "l", 6),
+ (0x77, "a", 6),
+ (0x78, "b", 7),
+ (0x79, "c", 7),
+ (0x7A, "d", 7),
+ (0x7B, "e", 7),
+ (0x7C, "h", 7),
+ (0x7D, "l", 7),
+ (0x7F, "a", 7))]
+ _opcode_src_reg_n @ (opcode, src_reg, n): (u16, &str, u8),
+ #[case] value: u8,
+ #[case] flags_before: u8,
+ #[case] flags_after: u8,
+) {
+ // Given
+ let builder = LR35902Builder::new()
+ .with_mem16(0x0000, (opcode << 8) + 0xCB)
+ .with_reg8(src_reg, value << n)
+ .with_f(flags_before << 4);
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+ sut.instruction();
+
+ // Then
+ let expected = builder
+ .with_pc(2)
+ .with_f(flags_after << 4)
+ .with_clock_cycles(8)
+ .build();
+ assert_eq!(sut, expected);
+}
+
+#[rstest]
+#[case::not_zero(0x1, 0b0001, 0b0011)]
+#[case::zero(0x0, 0b0000, 0b1010)]
+// BIT n,(HL)
+fn test_bit_addr(
+ #[values(
+ (0x46, 0),
+ (0x4E, 1),
+ (0x56, 2),
+ (0x5E, 3),
+ (0x66, 4),
+ (0x6E, 5),
+ (0x76, 6),
+ (0x7E, 7))]
+ _opcode_n @ (opcode, n): (u16, u8),
+ #[case] value: u8,
+ #[case] flags_before: u8,
+ #[case] flags_after: u8,
+) {
+ // Given
+ let builder = LR35902Builder::new()
+ .with_mem16(0x0000, (opcode << 8) + 0xCB)
+ .with_mem8(0x0002, value << n)
+ .with_reg16("hl", 0x0002)
+ .with_f(flags_before << 4);
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+ sut.instruction();
+
+ // Then
+ let expected = builder
+ .with_pc(2)
+ .with_f(flags_after << 4)
+ .with_clock_cycles(16)
+ .build();
+ assert_eq!(sut, expected);
+}
+
+#[rstest]
+#[case::not_zero(0x1, 0b0101, 0b0101)]
+#[case::zero(0x0, 0b1010, 0b1010)]
+fn test_bit_reg_reg(
+ #[values(
+ // RES n,REG
+ (0x80, "b", 0, 0),
+ (0x81, "c", 0, 0),
+ (0x82, "d", 0, 0),
+ (0x83, "e", 0, 0),
+ (0x84, "h", 0, 0),
+ (0x85, "l", 0, 0),
+ (0x87, "a", 0, 0),
+ (0x88, "b", 1, 0),
+ (0x89, "c", 1, 0),
+ (0x8A, "d", 1, 0),
+ (0x8B, "e", 1, 0),
+ (0x8C, "h", 1, 0),
+ (0x8D, "l", 1, 0),
+ (0x8F, "a", 1, 0),
+ (0x90, "b", 2, 0),
+ (0x91, "c", 2, 0),
+ (0x92, "d", 2, 0),
+ (0x93, "e", 2, 0),
+ (0x94, "h", 2, 0),
+ (0x95, "l", 2, 0),
+ (0x97, "a", 2, 0),
+ (0x98, "b", 3, 0),
+ (0x99, "c", 3, 0),
+ (0x9A, "d", 3, 0),
+ (0x9B, "e", 3, 0),
+ (0x9C, "h", 3, 0),
+ (0x9D, "l", 3, 0),
+ (0x9F, "a", 3, 0),
+ (0xA0, "b", 4, 0),
+ (0xA1, "c", 4, 0),
+ (0xA2, "d", 4, 0),
+ (0xA3, "e", 4, 0),
+ (0xA4, "h", 4, 0),
+ (0xA5, "l", 4, 0),
+ (0xA7, "a", 4, 0),
+ (0xA8, "b", 5, 0),
+ (0xA9, "c", 5, 0),
+ (0xAA, "d", 5, 0),
+ (0xAB, "e", 5, 0),
+ (0xAC, "h", 5, 0),
+ (0xAD, "l", 5, 0),
+ (0xAF, "a", 5, 0),
+ (0xB0, "b", 6, 0),
+ (0xB1, "c", 6, 0),
+ (0xB2, "d", 6, 0),
+ (0xB3, "e", 6, 0),
+ (0xB4, "h", 6, 0),
+ (0xB5, "l", 6, 0),
+ (0xB7, "a", 6, 0),
+ (0xB8, "b", 7, 0),
+ (0xB9, "c", 7, 0),
+ (0xBA, "d", 7, 0),
+ (0xBB, "e", 7, 0),
+ (0xBC, "h", 7, 0),
+ (0xBD, "l", 7, 0),
+ (0xBF, "a", 7, 0),
+ // SET n,REG
+ (0xC0, "b", 0, 1),
+ (0xC1, "c", 0, 1),
+ (0xC2, "d", 0, 1),
+ (0xC3, "e", 0, 1),
+ (0xC4, "h", 0, 1),
+ (0xC5, "l", 0, 1),
+ (0xC7, "a", 0, 1),
+ (0xC8, "b", 1, 1),
+ (0xC9, "c", 1, 1),
+ (0xCA, "d", 1, 1),
+ (0xCB, "e", 1, 1),
+ (0xCC, "h", 1, 1),
+ (0xCD, "l", 1, 1),
+ (0xCF, "a", 1, 1),
+ (0xD0, "b", 2, 1),
+ (0xD1, "c", 2, 1),
+ (0xD2, "d", 2, 1),
+ (0xD3, "e", 2, 1),
+ (0xD4, "h", 2, 1),
+ (0xD5, "l", 2, 1),
+ (0xD7, "a", 2, 1),
+ (0xD8, "b", 3, 1),
+ (0xD9, "c", 3, 1),
+ (0xDA, "d", 3, 1),
+ (0xDB, "e", 3, 1),
+ (0xDC, "h", 3, 1),
+ (0xDD, "l", 3, 1),
+ (0xDF, "a", 3, 1),
+ (0xE0, "b", 4, 1),
+ (0xE1, "c", 4, 1),
+ (0xE2, "d", 4, 1),
+ (0xE3, "e", 4, 1),
+ (0xE4, "h", 4, 1),
+ (0xE5, "l", 4, 1),
+ (0xE7, "a", 4, 1),
+ (0xE8, "b", 5, 1),
+ (0xE9, "c", 5, 1),
+ (0xEA, "d", 5, 1),
+ (0xEB, "e", 5, 1),
+ (0xEC, "h", 5, 1),
+ (0xED, "l", 5, 1),
+ (0xEF, "a", 5, 1),
+ (0xF0, "b", 6, 1),
+ (0xF1, "c", 6, 1),
+ (0xF2, "d", 6, 1),
+ (0xF3, "e", 6, 1),
+ (0xF4, "h", 6, 1),
+ (0xF5, "l", 6, 1),
+ (0xF7, "a", 6, 1),
+ (0xF8, "b", 7, 1),
+ (0xF9, "c", 7, 1),
+ (0xFA, "d", 7, 1),
+ (0xFB, "e", 7, 1),
+ (0xFC, "h", 7, 1),
+ (0xFD, "l", 7, 1),
+ (0xFF, "a", 7, 1))]
+ _opcode_src_reg_n_result @ (opcode, src_reg, n, result): (u16, &str, u8, u8),
+ #[case] value: u8,
+ #[case] flags_before: u8,
+ #[case] flags_after: u8,
+) {
+ // Given
+ let builder = LR35902Builder::new()
+ .with_mem16(0x0000, (opcode << 8) + 0xCB)
+ .with_reg8(src_reg, value << n)
+ .with_f(flags_before << 4);
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+ sut.instruction();
+
+ // Then
+ let expected = builder
+ .with_pc(2)
+ .with_reg8(src_reg, result << n)
+ .with_f(flags_after << 4)
+ .with_clock_cycles(8)
+ .build();
+ assert_eq!(sut, expected);
+}
+
+#[rstest]
+#[case::not_zero(0x1, 0b0101, 0b0101)]
+#[case::zero(0x0, 0b1010, 0b1010)]
+// RES n,(HL)
+fn test_bit_reg_addr(
+ #[values(
+ (0x86, 0, 0),
+ (0x8E, 1, 0),
+ (0x96, 2, 0),
+ (0x9E, 3, 0),
+ (0xA6, 4, 0),
+ (0xAE, 5, 0),
+ (0xB6, 6, 0),
+ (0xBE, 7, 0),
+ (0xC6, 0, 1),
+ (0xCE, 1, 1),
+ (0xD6, 2, 1),
+ (0xDE, 3, 1),
+ (0xE6, 4, 1),
+ (0xEE, 5, 1),
+ (0xF6, 6, 1),
+ (0xFE, 7, 1))]
+ _opcode_n_result @ (opcode, n, result): (u16, u8, u8),
+ #[case] value: u8,
+ #[case] flags_before: u8,
+ #[case] flags_after: u8,
+) {
+ // Given
+ let builder = LR35902Builder::new()
+ .with_mem16(0x0000, (opcode << 8) + 0xCB)
+ .with_mem8(0x02, value << n)
+ .with_reg16("hl", 0x0002)
+ .with_f(flags_before << 4);
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+ sut.instruction();
+
+ // Then
+ let expected = builder
+ .with_pc(2)
+ .with_mem8(0x02, result << n)
+ .with_f(flags_after << 4)
+ .with_clock_cycles(16)
+ .build();
+ assert_eq!(sut, expected);
+}
+
+#[rstest]
+#[case::base_case(0x00, 0x01, 0b0000, 0b0000)]
+#[case::overwrite(0x41, 0x42, 0b1111, 0b0001)]
+#[case::half_carry(0x0F, 0x10, 0b0010, 0b0010)]
+#[case::zero_flag(0xFF, 0x00, 0b0000, 0b1010)] // and no carry, unlike ADD 1
+fn test_inc_8_bit_reg(
+ #[values((0x04, "b"), (0x0C, "c"),
+ (0x14, "d"), (0x1C, "e"),
+ (0x24, "h"), (0x2C, "l"),
+ (0x3C, "a"))]
+ _opcode_reg @ (opcode, reg): (u8, &str),
+ #[case] value: u8,
+ #[case] result: u8,
+ #[case] flags_before: u8,
+ #[case] flags_after: u8,
+) {
+ // Given
+ let builder = LR35902Builder::new()
+ .with_mem8(0x0000, opcode)
+ .with_reg8(reg, value)
+ .with_f(flags_before << 4);
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+
+ // Then
+ let expected = builder
+ .with_pc(1)
+ .with_reg8(reg, result)
+ .with_f(flags_after << 4)
+ .with_clock_cycles(4)
+ .build();
+ assert_eq!(sut, expected);
+}
+
+#[rstest]
+#[case(0x0000, 0x0001)]
+#[case(0x00FF, 0x0100)]
+#[case(0xFFFF, 0x0000)]
+fn test_inc_16_bit_reg(
+ #[values((0x03, "bc"),
+ (0x13, "de"),
+ (0x23, "hl"),
+ (0x33, "sp"))]
+ _opcode_reg @ (opcode, reg): (u8, &str),
+ #[case] value: u16,
+ #[case] result: u16,
+ // Test flags are not changed
+ #[values(0b0000, 0b1111)] flags: u8,
+) {
+ // Given
+ let builder = LR35902Builder::new()
+ .with_mem8(0x0000, opcode)
+ .with_reg16(reg, value)
+ .with_f(flags << 4);
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+
+ // Then
+ let expected = builder
+ .with_pc(1)
+ .with_reg16(reg, result)
+ .with_clock_cycles(8)
+ .build();
+ assert_eq!(sut, expected);
+}
+
+#[rstest]
+// JP
+#[case(0xC2, 0xFF00, "z", true, 3, 12)] // 1
+#[case(0xC2, 0xFF00, "z", false, 0xFF00, 16)] // 2
+#[case(0xD2, 0xFF00, "c", true, 3, 12)] // 3
+#[case(0xD2, 0xFF00, "c", false, 0xFF00, 16)] // 4
+#[case(0xCA, 0xFF00, "z", true, 0xFF00, 16)] // 5
+#[case(0xCA, 0xFF00, "z", false, 3, 12)] // 6
+#[case(0xDA, 0xFF00, "c", true, 0xFF00, 16)] // 7
+#[case(0xDA, 0xFF00, "c", false, 3, 12)] // 8
+#[case(0xC3, 0xFF00, "z", false, 0xFF00, 16)] // 9
+#[case(0xC3, 0xFF00, "z", true, 0xFF00, 16)] // 10
+#[case(0xC3, 0xFF00, "c", false, 0xFF00, 16)] // 11
+#[case(0xC3, 0xFF00, "c", true, 0xFF00, 16)] // 12
+// JR
+#[case(0x20, 0x000F, "z", true, 2, 8)] // 13
+#[case(0x20, 0x000F, "z", false, 0x0011, 12)] // 14
+#[case(0x30, 0x000F, "c", true, 2, 8)] // 15
+#[case(0x30, 0x000F, "c", false, 0x0011, 12)] // 16
+#[case(0x28, 0x000F, "z", true, 0x0011, 12)] // 17
+#[case(0x28, 0x000F, "z", false, 2, 8)] // 18
+#[case(0x38, 0x000F, "c", true, 0x0011, 12)] // 19
+#[case(0x38, 0x000F, "c", false, 2, 8)] // 20
+#[case(0x18, 0x000F, "z", false, 0x0011, 12)] // 21
+#[case(0x18, 0x000F, "z", true, 0x0011, 12)] // 22
+#[case(0x18, 0x000F, "c", false, 0x0011, 12)] // 23
+#[case(0x18, 0x000F, "c", true, 0x0011, 12)] // 24
+fn test_jump(
+ #[case] opcode: u8,
+ #[case] address: u16,
+ #[case] flag: &str,
+ #[case] value: bool,
+ #[case] pc: u16,
+ #[case] clocks: u64,
+) {
+ // Given
+ let builder = LR35902Builder::new()
+ .with_mem8(0x0000, opcode)
+ .with_mem16(0x0001, address)
+ .with_flag(flag, value);
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+
+ // Then
+ let expected = builder.with_pc(pc).with_clock_cycles(clocks).build();
+ assert_eq!(sut, expected);
+}
+
+#[rstest]
+#[case(0xC5, "bc", 0xFFFF, 0xFF00)]
+#[case(0xC5, "bc", 0x0001, 0xFF00)]
+#[case(0xD5, "de", 0xFFFF, 0xFF00)]
+#[case(0xD5, "de", 0x0001, 0xFF00)]
+#[case(0xE5, "hl", 0xFFFF, 0xFF00)]
+#[case(0xE5, "hl", 0x0001, 0xFF00)]
+#[case(0xF5, "af", 0xFFFF, 0xFF00)]
+#[case(0xF5, "af", 0x0001, 0xFF00)]
+fn test_push(#[case] opcode: u8, #[case] register: &str, #[case] value: u16, #[case] sp: u16) {
+ // Given
+ let builder = LR35902Builder::new()
+ .with_mem8(0x0000, opcode)
+ .with_sp(sp)
+ .with_reg16(register, value);
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+
+ // Then
+ let expected = builder
+ .with_pc(0x0001)
+ .with_clock_cycles(16)
+ .with_mem16(sp - 2, value)
+ .with_sp(sp - 2)
+ .build();
+ assert_eq!(sut, expected);
+}
+
+#[rstest]
+#[case(0xC1, "bc", 0xFFFF, 0xFF00)]
+#[case(0xC1, "bc", 0x0001, 0xFF00)]
+#[case(0xD1, "de", 0xFFFF, 0xFF00)]
+#[case(0xD1, "de", 0x0001, 0xFF00)]
+#[case(0xE1, "hl", 0xFFFF, 0xFF00)]
+#[case(0xE1, "hl", 0x0001, 0xFF00)]
+#[case(0xF1, "af", 0xFFFF, 0xFF00)]
+#[case(0xF1, "af", 0x0001, 0xFF00)]
+fn test_pop(#[case] opcode: u8, #[case] register: &str, #[case] value: u16, #[case] sp: u16) {
+ // Given
+ let builder = LR35902Builder::new()
+ .with_mem8(0x0000, opcode)
+ .with_sp(sp)
+ .with_mem16(sp, value);
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+
+ // Then
+ let expected = builder
+ .with_pc(0x0001)
+ .with_clock_cycles(12)
+ .with_sp(sp + 2)
+ .with_reg16(register, value)
+ .build();
+ assert_eq!(sut, expected);
+}
+
+#[rstest]
+#[rustfmt::skip]
+#[case(0x10, 0x10, 0b1100)]
+#[case(0x10, 0x11, 0b0111)]
+fn test_cp(
+ #[values((0xB8, "b"), (0xB9, "c"), (0xBA, "d"), (0xBB, "e"), (0xBC, "h"), (0xBD, "l"))] _opcode_reg @ (opcode, reg): (u8, &str),
+ #[case] a: u8,
+ #[case] reg_value: u8,
+ #[case] flags: u8,
+) {
+ // Given
+ let builder = LR35902Builder::new()
+ .with_mem8(0x0000, opcode)
+ .with_a(a)
+ .with_reg8(reg, reg_value);
+ let mut sut = builder.clone().build();
+
+ // When
+ sut.instruction();
+
+ // Then
+ let expected = builder
+ .with_pc(0x0001)
+ .with_clock_cycles(4)
+ .with_f(flags << 4)
+ .build();
+ assert_eq!(sut, expected);
+}
diff --git a/rust-toolchain.toml b/rust-toolchain.toml
new file mode 100644
index 0000000..0c62282
--- /dev/null
+++ b/rust-toolchain.toml
@@ -0,0 +1,4 @@
+[toolchain]
+channel = "nightly-2024-05-02"
+components = [ "rustfmt", "rustc-dev" , "clippy"]
+profile = "minimal"
diff --git a/src/main.rs b/src/main.rs
deleted file mode 100644
index e7a11a9..0000000
--- a/src/main.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-fn main() {
- println!("Hello, world!");
-}