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!"); -}