From e9c83a37c44c0b8ad79e3c34c4c656e2f38c44c1 Mon Sep 17 00:00:00 2001 From: Cole Landers Date: Thu, 29 Jan 2026 20:23:03 -0600 Subject: [PATCH] Chip8-em init commit --- .gitignore | 1 + Cargo.lock | 1469 +++++++++++++++++++++++ Cargo.toml | 10 + assets/pluck.wav | Bin 0 -> 19280 bytes programs/1-chip8-logo.ch8 | Bin 0 -> 260 bytes programs/2-ibm-logo.ch8 | Bin 0 -> 132 bytes programs/4-flags.ch8 | Bin 0 -> 1041 bytes programs/5-quirks.ch8 | Bin 0 -> 3232 bytes programs/6-keypad.ch8 | Bin 0 -> 913 bytes programs/7-beep.ch8 | Bin 0 -> 110 bytes programs/Airplane.ch8 | Bin 0 -> 356 bytes programs/Nim [Carmelo Cortez, 1978].ch8 | Bin 0 -> 182 bytes programs/br8kout.ch8 | Bin 0 -> 199 bytes programs/corax.ch8 | Bin 0 -> 761 bytes programs/snake.ch8 | Bin 0 -> 1438 bytes programs/test.ch8 | Bin 0 -> 30 bytes src/chip8.rs | 149 +++ src/chip8/cpu.rs | 159 +++ src/chip8/debug.rs | 27 + src/chip8/gpu.rs | 135 +++ src/chip8/input.rs | 78 ++ src/chip8/memory.rs | 45 + src/chip8/renderer.rs | 28 + src/main.rs | 27 + 24 files changed, 2128 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 assets/pluck.wav create mode 100644 programs/1-chip8-logo.ch8 create mode 100644 programs/2-ibm-logo.ch8 create mode 100644 programs/4-flags.ch8 create mode 100644 programs/5-quirks.ch8 create mode 100644 programs/6-keypad.ch8 create mode 100644 programs/7-beep.ch8 create mode 100644 programs/Airplane.ch8 create mode 100644 programs/Nim [Carmelo Cortez, 1978].ch8 create mode 100644 programs/br8kout.ch8 create mode 100644 programs/corax.ch8 create mode 100644 programs/snake.ch8 create mode 100644 programs/test.ch8 create mode 100644 src/chip8.rs create mode 100644 src/chip8/cpu.rs create mode 100644 src/chip8/debug.rs create mode 100644 src/chip8/gpu.rs create mode 100644 src/chip8/input.rs create mode 100644 src/chip8/memory.rs create mode 100644 src/chip8/renderer.rs create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..968b2bb --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1469 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "alsa" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed7572b7ba83a31e20d1b48970ee402d2e3e0537dcfe0a3ff4d6eb7508617d43" +dependencies = [ + "alsa-sys", + "bitflags 2.10.0", + "cfg-if", + "libc", +] + +[[package]] +name = "alsa-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8fee663d06c4e303404ef5f40488a53e062f89ba8bfed81f42325aafad1527" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "anstream" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "bindgen" +version = "0.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" +dependencies = [ + "bitflags 2.10.0", + "cexpr", + "clang-sys", + "itertools", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "bumpalo" +version = "3.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" + +[[package]] +name = "bytemuck" +version = "1.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" + +[[package]] +name = "bytes" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" + +[[package]] +name = "cc" +version = "1.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6354c81bbfd62d9cfa9cb3c773c2b7b2a3a482d569de977fd0e961f6e7c00583" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chip-8-em" +version = "0.1.0" +dependencies = [ + "clap", + "rand", + "raylib", + "rodio", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "4.5.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e34525d5bbbd55da2bb745d34b36121baac88d07619a9a09cfcf4a6c0832785" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59a20016a20a3da95bef50ec7238dbd09baeef4311dcdd38ec15aba69812fb61" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" + +[[package]] +name = "cmake" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" +dependencies = [ + "cc", +] + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "coreaudio-rs" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aae284fbaf7d27aa0e292f7677dfbe26503b0d555026f702940805a630eac17" +dependencies = [ + "bitflags 1.3.2", + "libc", + "objc2-audio-toolbox", + "objc2-core-audio", + "objc2-core-audio-types", + "objc2-core-foundation", +] + +[[package]] +name = "cpal" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbd307f43cc2a697e2d1f8bc7a1d824b5269e052209e28883e5bc04d095aaa3f" +dependencies = [ + "alsa", + "coreaudio-rs", + "dasp_sample", + "jni", + "js-sys", + "libc", + "mach2", + "ndk", + "ndk-context", + "num-derive", + "num-traits", + "objc2-audio-toolbox", + "objc2-core-audio", + "objc2-core-audio-types", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows", +] + +[[package]] +name = "dasp_sample" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f" + +[[package]] +name = "dispatch2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" +dependencies = [ + "bitflags 2.10.0", + "objc2", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "extended" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af9673d8203fcb076b19dfd17e38b3d4ae9f44959416ea532ce72415a6020365" + +[[package]] +name = "find-msvc-tools" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[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 = "js-sys" +version = "0.3.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.180" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "mach2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44" +dependencies = [ + "libc", +] + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "ndk" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" +dependencies = [ + "bitflags 2.10.0", + "jni-sys", + "log", + "ndk-sys", + "num_enum", + "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.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "objc2" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-audio-toolbox" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6948501a91121d6399b79abaa33a8aa4ea7857fe019f341b8c23ad6e81b79b08" +dependencies = [ + "bitflags 2.10.0", + "libc", + "objc2", + "objc2-core-audio", + "objc2-core-audio-types", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-audio" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1eebcea8b0dbff5f7c8504f3107c68fc061a3eb44932051c8cf8a68d969c3b2" +dependencies = [ + "dispatch2", + "objc2", + "objc2-core-audio-types", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-core-audio-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a89f2ec274a0cf4a32642b2991e8b351a404d290da87bb6a9a9d8632490bd1c" +dependencies = [ + "bitflags 2.10.0", + "objc2", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags 2.10.0", + "dispatch2", + "objc2", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "objc2", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[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.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro-crate" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "raylib" +version = "5.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5c54335590d1b6e6fbdbccee09dafdfd76a1111fc3c709eca949e71e81f7a8a" +dependencies = [ + "cfg-if", + "paste", + "raylib-sys", + "seq-macro", + "thiserror", +] + +[[package]] +name = "raylib-sys" +version = "5.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ce5adc950b042db67f1f78f24f7e76563652ce24db032afe9ca9e534d8b7a13" +dependencies = [ + "bindgen", + "cc", + "cmake", +] + +[[package]] +name = "regex" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + +[[package]] +name = "rodio" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e40ecf59e742e03336be6a3d53755e789fd05a059fa22dfa0ed624722319e183" +dependencies = [ + "cpal", + "dasp_sample", + "num-rational", + "symphonia", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[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 = "seq-macro" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc" + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "symphonia" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5773a4c030a19d9bfaa090f49746ff35c75dfddfa700df7a5939d5e076a57039" +dependencies = [ + "lazy_static", + "symphonia-bundle-flac", + "symphonia-bundle-mp3", + "symphonia-codec-aac", + "symphonia-codec-pcm", + "symphonia-codec-vorbis", + "symphonia-core", + "symphonia-format-isomp4", + "symphonia-format-ogg", + "symphonia-format-riff", + "symphonia-metadata", +] + +[[package]] +name = "symphonia-bundle-flac" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c91565e180aea25d9b80a910c546802526ffd0072d0b8974e3ebe59b686c9976" +dependencies = [ + "log", + "symphonia-core", + "symphonia-metadata", + "symphonia-utils-xiph", +] + +[[package]] +name = "symphonia-bundle-mp3" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4872dd6bb56bf5eac799e3e957aa1981086c3e613b27e0ac23b176054f7c57ed" +dependencies = [ + "lazy_static", + "log", + "symphonia-core", + "symphonia-metadata", +] + +[[package]] +name = "symphonia-codec-aac" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c263845aa86881416849c1729a54c7f55164f8b96111dba59de46849e73a790" +dependencies = [ + "lazy_static", + "log", + "symphonia-core", +] + +[[package]] +name = "symphonia-codec-pcm" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e89d716c01541ad3ebe7c91ce4c8d38a7cf266a3f7b2f090b108fb0cb031d95" +dependencies = [ + "log", + "symphonia-core", +] + +[[package]] +name = "symphonia-codec-vorbis" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f025837c309cd69ffef572750b4a2257b59552c5399a5e49707cc5b1b85d1c73" +dependencies = [ + "log", + "symphonia-core", + "symphonia-utils-xiph", +] + +[[package]] +name = "symphonia-core" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea00cc4f79b7f6bb7ff87eddc065a1066f3a43fe1875979056672c9ef948c2af" +dependencies = [ + "arrayvec", + "bitflags 1.3.2", + "bytemuck", + "lazy_static", + "log", +] + +[[package]] +name = "symphonia-format-isomp4" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243739585d11f81daf8dac8d9f3d18cc7898f6c09a259675fc364b382c30e0a5" +dependencies = [ + "encoding_rs", + "log", + "symphonia-core", + "symphonia-metadata", + "symphonia-utils-xiph", +] + +[[package]] +name = "symphonia-format-ogg" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b4955c67c1ed3aa8ae8428d04ca8397fbef6a19b2b051e73b5da8b1435639cb" +dependencies = [ + "log", + "symphonia-core", + "symphonia-metadata", + "symphonia-utils-xiph", +] + +[[package]] +name = "symphonia-format-riff" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2d7c3df0e7d94efb68401d81906eae73c02b40d5ec1a141962c592d0f11a96f" +dependencies = [ + "extended", + "log", + "symphonia-core", + "symphonia-metadata", +] + +[[package]] +name = "symphonia-metadata" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36306ff42b9ffe6e5afc99d49e121e0bd62fe79b9db7b9681d48e29fa19e6b16" +dependencies = [ + "encoding_rs", + "lazy_static", + "log", + "symphonia-core", +] + +[[package]] +name = "symphonia-utils-xiph" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27c85ab799a338446b68eec77abf42e1a6f1bb490656e121c6e27bfbab9f16" +dependencies = [ + "symphonia-core", + "symphonia-metadata", +] + +[[package]] +name = "syn" +version = "2.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.23.10+spec-1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" +dependencies = [ + "indexmap", + "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.0.6+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" +dependencies = [ + "winnow", +] + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[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 = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" +dependencies = [ + "cfg-if", + "futures-util", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "windows" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49" +dependencies = [ + "windows-core", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +dependencies = [ + "windows-targets 0.52.6", +] + +[[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.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[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.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[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.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[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.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[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.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[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.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[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.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[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.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[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.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" + +[[package]] +name = "zerocopy" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdea86ddd5568519879b8187e1cf04e24fce28f7fe046ceecbce472ff19a2572" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c15e1b46eff7c6c91195752e0eeed8ef040e391cdece7c25376957d5f15df22" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..cd63060 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "chip-8-em" +version = "0.1.0" +edition = "2024" + +[dependencies] +clap = { version = "4.5", features = ["derive"]} +raylib = "5.5.1" +rand = "0.9" +rodio = "0.21" diff --git a/assets/pluck.wav b/assets/pluck.wav new file mode 100644 index 0000000000000000000000000000000000000000..449014fc6d83c87f2152f4be795fb08f4f22791f GIT binary patch literal 19280 zcmX`xWmHt%*8p&4=#Ck>VdzFmx&=W{k?s-$q+1CEK?MaANh#^>9J)Ja=%Ho?hVC5T zeg1xUKkT*cy&vxSt-JQw_nfoOb6st19X%WzQ#BK>b(B6kAbR&W+} zzTHy$TEX_jn)3AP;Z{pBQ|`gYMIi^{E2-(?OGaI>WLpDgUEIL+t7pO0S~H;;^JY&D zKMhlTP|J}Qs(gJpJ;j;Fg~A&jB}n48ZGFfB9ODzQqr4z9@qI+j9_?^=toEtyRWokU z5>a$T-B`kG0cFi}q9tO5p<+KQ%kzb#* zd8%C;uJ!>r_zwbRXV8^8vSJKR0j|k`&IXb1@9*zJvlcxJtYO6Yy?aPoHLrn~xBRO5fh2-=T6-FBOP-o$_Ntw{DJe^5KUMukB;gRU{?PsH%o z$yjzc?A(o}WBMq43RiJ)e-D3yBS=?a|JB_aHeyq$bAa(;C$p;5@!gdJ`<3{o#l}s2 z9VOh#XZ!Z-LFMl=KK#!gYZ726++$B?^y>6spw7GN0QcOI2P0h5zs64Z{BE4tDHeG} z|BBjE4Tm36;2V-1Ykm6J)Tm)x%4{c8L!v%4_w|wvE{jHwrL^+H_!8(BA&jp!J`S&s z?oS?OZihBT;yctvdDN4}{!GU!yb&>}Wg=h5)YJTt`O%GJYIHmc{`wfxIgPrE;t^G9bHf<8{Qpb^+2O~#?vek)5B^?|74&CLi zd#!$wde2j8lr&rFud{VZv7e91(<5JO5D)|_cSeW)&+lk%{!B#3mf4D4>EQ;xGEgGa~3yLc=r$QUPKRHNNZf%~ltFVE()$9FUo#&4enuB@% znk000ojt)iEt?*+BB!L^jZ+Bh=5+HAe$gFN`pP;_9=H5{qiD7gc<8m(amV?zr@-HR|XGgm!`M~5@t@Z!?x|u+unVo6& zhRRU63B$hIF;@PDfeWpL-wE-&P3(>kWu-!~&jGiz<2zrecPN`rPltYV3=D`=_rI`c zvI^~4u>5#EAN~7$S`*?ip9*}n@ zPtEr>@5#k#etA^p^wlSYc_nw7f#`&lIk`B8GqIr~w)Mq>tB~ijkAMVTpI-8^h78k!b zO7nh~cH&uKJQlNH5T+NNp`vLQ)j3m~XY5j038}$LuW7z=;a}{EA6iHUQNZ|~S}m+N zJYD@JZ;akUn@lEb&FA0Qdd5bu=EQbb;{4jfQ7z?uB-`+4J2zi|?R_Cget)`kuWOz% zrGKz+_jDfU^6vbBZaiCp&hpLROFh<{Q|X&#{f3DqB-`b*8YjyVIQt;I>bZ8j@r@b1 z=i~qPORxvM*a#Tob|!3>ed#A~?}k)`iUim$tJ=-+w0tn+T_TVY=V{Sqm^A)IBLo>a zGpCL3W~u9{)u+B`DVqDVc=aM?abS`b-o&f0NNFyxW*O0g{t>-4xgkna7>+mi{q}G1 zcU=OpM3?LR(kq?fhUd}a1q3i)ae}<`jA@k00`xK4uwL@*LYaljMaa4YTiI@3;>d8b0n>qd-i|)e?N2$Vq5^o;d%eiMs})m_#QXC zpv{(a3SmAH#3O0W%jfpto%r9c28`U|H#AH++UH8~=RJCNS9PV`60Mf*vx`Bss!Nwm z6tj%(AJ9d_~#C)Nzvg1wV$>41UirCzRQ#f*X}*cM6#v`jzl- z_|Ckbu3pgm?l|&N@p_RV*WmJf<}~YUll47!+&oL7Ss1p`VrFK-H|r`LBFo%*4LJMS zG@#}5v(|#hqWb^+9-O_#&+9Gu_`Pps!eCA~YXj#UXkV$-wnZV-0I5p3(-%T(JZr5;v;g! zRKoi4nfa-4X=gS`p+bV?Au!*@ysYb@cc!zY@BPbX)>W7n$!wBGYyEbXd|&ZgT}Jt_ zZgZaEeE*!m;<8M4duQAMM;aMknxLe}90xrHL9A%YU|? zE;DF(h7%Sk)~);}YAtA^4I+a|5A)2No&6+*|ej-*ZluC)e_` zc1$eE@GGMVJ+1aU9xFG6tN-_Zd9h`}$`d8nwWcUzd-4IWxct0V$b4ko?#d1_aI7c* zay=6#%)e)D`$G-3OZjv`__%Mt;l`qg>L{q=u()t3xPunK(pEU%O00@7TEau>-7;@E z5*JMKYe|(p-AwtJKI@RM%x;!qbgWRpsLs`t^y9dstK?-xSqLSJjcaDnUt?yx|EdfT zo0fO2bkfcOp@reL81S$_n+5JNn#~rr3}SE8+OUjzxYXFmRBElI`(CU1VCsK!hyT@| zSI=Bc_uOxU#DbTl!Q%G-#R>cVI3HZ3fH5xQ(r`@R{2`sh=b%28F?VaQ8}lq}{s((Q7n9s^AauaL{rK<^i1PJY+8m`(6Gk-|OMe5pMNV z_)UF9uGG7xe5&PAv6|nNvP^QYO9Gyy8^YxY?-yY!BiN@m^&u}=K!IHNu^%^?6XXi+ zeuxq-oo}#WnfU%N(oTrnO+H%CWXm+QCST(Ef4}p}rl}~2r3e5WA|0C+2t0mbJt$W$ zg7V+fD2`5ip5|HL zqIFM?$p8B%JS@$6sR~3xbU39C>h^%-irT|*O?v41FkNnkvtB{3>PHe>G(v11CG$owLQ2jeOfT#1q)=-m?E&Y2kvg_oP|)c^jd zHufvw*J;|S<*&+J%G0Jr)r4}lg5m1;gkYG~o9ZP=@_{MOD&jSYrjOVxg9c{S^bdpt zc5j%IHe>FUo{Ovm$MD?7?rt-%Zh?V=M&6qG{?Bb5Crkdn|D|WY*;(AG*icW8)cbQw zT&y?isGW+_GC~Nl9R?H}0k9wBJvfJ?0 zN=HKI(%bvI)%bHkboYmYEs49WnNR6C6<+-%Nl2&I#8myZ^oqfY%Et)N=2;q>idW22 zH5MXCFv0bMrKY-)Y0k9Hwb3$?+mJ+i=DhY;BBK{V%&P?F_xPRptN4?DZux=93{?E< zxbNLGG{f9mZNh3K{@-7#Eo?6Tg-Ajo@U@iSNDr=5&&JqWg!5L!rvxrr*{?#jP-lrx zR5t8W_gb`(k)&6%DO6+O4sva$*%v+AN=eI*2J6+UMwVsy4#71W%0FnlX%J?5n;&Ls z<6LxCMRS~HF{v(o;bJK*ksyqGaQJlwmHo`PA-V)M3=Q*f~>S^(48n4vx znCoGq`&+O8RocOuZi-`5u=yEnPj>@Y7k`(Y*-LDe$#JZUgro#X}C zZ<%g|TGi9!d(8rcGSy8l{2HX>k+9#A)yv<|lkmX=V`P@V&|MZMJ(K)B7a6=?lxb7g z5#W4wg5um(x)1v4Meq1mhJv-Tj@_RRH~;=$fBLFJ{pmt2n0ZL0QH~@Jy3!xw z+c!GE=)<{oqrJpAQ7iGnHX0rh!aJIrxZzW@JC86|Qcu53eiMl8bj?}pkCLK5v;`Ed zeLhlN36{KE?-`}va^v&BaLAp&F1J}~1|<&D=u5VeXF~vG6u`f<{I7DSR>A|i#9 zdcsTN5+HA<+`azKe>>(A8uh5xA~SwV`Y57aG8Peo|6OebE>VB8OR~PeQE&nkPCS1t zVVGRN=}x*$n+&77P7r%GW&8747sIQt0X2UJ;xXIq+7IEn6?>4?#*&Tm7R6jJ=A}9l z{P;m*&3sf%dO*#ml#BYVtbOK@>Xt$27IiM)n$kxkO^BFlSp3(k<)v$pIUZhr6hm^r zy|8v5Bl`3w`EqU*Bk<)d-~yqug%H`hZz4XU`{lMxMjdLP@d5tUu9LCvpI=p9D%4Ju zz*5$sNv8g}ljQT(CH%@GgSmW?(LMXj&m4?HD;fLwHmx3nqus@J6q)YC6_ zD7%^P*#>Fl6Yi;Pnf(HN^sm|{n{WVk3V?xjHwFRFS5ZQ zL^W@J8nNMlRsw;i}W@1Nf<=qMyjQ{A#oJw$eI79{B#EI>eNUOoQ| z{d>Q#{s9N?>W%PYrWX=p+b_6goXP3d@LyeD2M$he1P^rEBWnjg*XknLDV)|{`+fg! zNP>{p!~0vXnJA2GqZOQ0bFubFi#J2cz9cm$ZY6uLRkfxm*Q@2NOiFF=tDxrgXd<`> z{sTm8@SAxFzHqc|3n@;R%qoM<&Ll-5tqh~xS`?7%&TacB{VR^3MFibXUJJ_BAQBCu z#wNS!z3_kj7xASb)V~5PlVGxPR;;}eKTNy{MsP?LfhFb#9Fpto^}SRg>KIK4`=BhY z?G8pdT1d?Grz^CEyY!*EXmsvwN3_ame)gE9Nf+e@X;Ct+}drsq2#XO}GYzd;GPu*58X@%PjFIoKa2lqIKP`xd+jHyM- zT?+(AbVoH4*u6DbnyaNfv}H8rHkUC8;LKwR#xGGjaUfuHk^$Q z$)~+U^tBDH8$>-_yZ+FCygzfmB!+y(JPnVB>lUHw@>|0)#Zx=eGJ23XQulc^DxdON zR)6Nzp{<8n3Tr9hdaZT{joPmZG=sjI=B!aTqA)#%#GmVw{&7$ShBQ3f;|7;qW+($7 zESsIKeN~?#QmXagp%Il2%7z%qYJ%rHraVW5x`?23GX^f?ov--x;Q?} zHDRC&{L2HszSY2);hc>32;n&P4XMrBHRDNLRJ>CU=Gk;0rr~=tykcjmUgk}27Ujc% zbTD@2?*QIi4Jpw?3oIkO-u2Bb^u+c*e^dkl?`QntDW2(;@9ZAVJPwpz9cP#--b;%i zRo@r46;FO&y(tBNAnK!4xr_!SMkJ_v8Ial+@Xh?&KlwZp^`YaDu+^WeM)_&}bn(w@ z9|;@Iu`4=I$gyr_F`I2GzsObGfjFM@cWz@%5js>U?#=S-J>0BG(sQP@Vgu;&JVl^plp`fUt(w{&a0EMHF!8RVL!FI(*3@ix=}V zQyt)#C{FKE|AX2dcaq-3cM(@DXzL)sj}jQy>qomm{DQX3+*+ca+py+DfVdVH@=R)^@>7!v73_yropqs=zBVJCcaymyi}W@#LA ztD}zwDqUqpYv24tXply2(BrPIc=&5VY{f{92;yz9 z!Kw>9HP4@5Ws)Wo!8WFd-~A?lZ1Qo@Ine6du*Vk7l}#=TysrE^x-yxJSgZTHVQa{} zK}@fPPHQvW7E#o~#1S3B)p`>etM+LBCf${0u>T3oBg~7f6%L?=G7=j#9O56qgZ|(cW$ngD{NItJy3l2 zIS(W^kcmzy*Yf6YRb*b03--DN}uf;zXuo{|4Vp- z;(24y&&)^(+Uus%~qm<^g=2 zUFbj`-_v}a;iK1SbHro3kT`Y?EC+JrP}6$k)X*DdfYsHK@$K!By8rp-Xzzn9BAcyf z0+^I2&z^}*Qxy|w=UAXfe)FGAW3*ZMotK4+4ot<*$5bE)a%wt`A-9{<>9YC4?6bjv z6tA&G+-yX02yEjSbLPe~!6LM*W9&AC00)L%;T{g*G--P5#glt5ADhX1p_DIfGElpa zF9^*A&^5)>;&t2(+{5+-x)G#MUWAKa(sprlJAl)Um#(~|m4@izkSeJl{T z0)!fzg0lzTFeu{QslAZCvXhdD`sY8DE)U+D9JK<;hAKsx(uxgStrCG*jnQ4=b!YpA zc&uKZx`hRYrNsAiO(6FK1axY-AvbR`1Qv)pJ`C|z%#8g3OqaD$iwA%H>*RMM1#Um_JeZxHk&%siOAS(nv3nt+ z`_C^vY!z(eg0OOKT~;~|TM#8{q$7^i(%FoxQ96&3>1H7-%op~4L?upr{|X`!c}3g( zH z9QBBQyGU&%8FZafrhQ_0M;Rq-61**@${PUj$lswwtfb#W#nUdX@VKg2ib6b8539T8jlFp^nPS%>D&;yHQet|M80gEb1`e}n3N{MLm|hjKKl z#cOuXYaGD7rcEG>b!1j(@uGKL72g7m1Ygq<=vjm4Sf0^6u(-#oSzkK+`Qa6Aq-7yk z@Y0Hj)}c*ph_cI0D0v|8f8#f)s4+-)|H6v%%@^fov~NWDK%vCp!;F~kVjBPTQMFh! zj-7;U-gk=$o_>N%eO{uy_AI{XX8F9xS_d4V8rdAjNhv~nOlC(i_jw?_p=O)J7IWJU zs(o9Pl4P^9>2=L4HB9-NERxv|E`Jq_jtbOS_&kTog1noFd-FRlxG=EOhtr4$@4u{! z8cyxxc#s2KutK!FHBq2ES5~?YFSzjAWdhEkRe$1QhZDdt&L>QJuZz`I9g6LsYM=l4 z=NMiFMX~^_s|9D3WqqYYs~4(>RqHP?KH(h~B$Gre*^#Qk7@IV)h}0NJxatV)6l24U zi}uMPu}R2?Q=QTT_;)datXu?1p_zevmmjj}ZHU_D6EE9R`am}OLzkfCy?R`JMRQ_y z_1Z=O|DbXm7t{h8*Vol7%7Wk3GO`X!r-30(jptWpXBc<=h?s#!4F_Ny#WE1&+cquv zo(8_~KKZ#}8x+^^E(Dxh_K{io?zdWzeVkoTh5A2#jxH#OnViVlYLi_>^ps+o_|>syB?naxz|G;yMYC{hemo*qyE$o`=>;octFfnYVEhEh;4l1r>QD ziS8g$>TS+ufb!wCu>Q}juu;<49Qzk7G(g;fzqS|Ieb(ZIX1UMnzJxhIzqC@e4E5V~ zZ5b@VWEJ3uuQV4=_$NGq)9FKM0^Ys@83wv`p^mgxo*b!11c0Q*P1IBC`QUM z0*+AQlJl{vySxqfU;Y^Cbp&QDkXgUja8xN)8xy&{0h6#ma(4n4sxC{Xe=?Vc9|?T` z>xj7uBtkYb3u%|HMsKuOdzSQd+(tQv)+RXeN)U;!wUCY%M@SLAo=u3()i%5&V9QLN zWY(21tK|{MyC9z1Gza-3sPJK+b6xAyYv=}}MT^CGeHW-623zTeBcgNNS6T40clmV1 zfu`_4@Wl8C7Uo<}HAhMlJI@D=|NJLHZh=oT zD6N%k{;I&U{)nvmX^~WMdhQw(LatK%ESbYJ`-S#NSjA3U5+RkIiL^8(b2r@#{L9Ca ztE1$~;*$?%N)SythR6VS1{CMn#^&d5I6D|8>n-p+;jD=zrX?eBwP1;*F2^&at8m-a zzb>)M0~&vm-GZDZ>8>XqhY1YB5szQhua;w0cWWNX0k6bW!S-IbV4+46+K&&j@!OXw zE?8qE@wk1SfL&CGS*icps!fv1*!lT*{`0@PWDI2Ypt835g;0r0eC?@MBqCbu`Q zoOo3q0$~nyO%s|BI1&{U`U&Ck4X2f%TE7V}{IX29@@&jC=>25hdlJ>tqyD>?LR5r#7hRWc;}4w)+iYR{YT2!BHvm&L zfg`9ru2)|LvF&~PrVQkFAOvfxGJt(Z_-MVT+wi+SOI)_CnBtM+se-k=Ls?mf4b(JE z!FKlJ0{{GkyEx zDv>^|$d)!|zJ8^(+euCrfOEh9a4YL}`2f0#CmeJu)k-BbxXG zmEC*`wPQK$mSt{%5v$K2_NRi@81a7YWejQqab%J~nKrWE3(_I*X2&W1$G?16WfWO> zi;e=|u`)}xkK00O=LRFTXUxg}{6CF30#q1?tb2L`R73tN9rSJh(n>s!gOHc->&T8M zrh{5ap$m%FqGs125HTw?S_uI5CO7jB0!)@P-a%&MECT4Z;pN4d>QXK_>bwK)64M{g%0v!jKRke!^$COzu?_s`es+hq) zzk<@JKm85B8cdL<3IuftkMN0+7Wr`>I_N3h4Dp&UY0V-8!|mlod&PVp)YF2r9Z+2C zCh!a~M}0q@n2<6>BU*xxi19!Y#IuC45WW2#vtslxic zR-`i4Q-?hRnKyKB9;O!rWrEq9q@q-DE)c^J5G^GgDK%j=N=;UiUoS6>jEAmLQ7w9w~lZ3 z^yID-z-;iw5m&f^>#F6F`#-C7fmTm0XfC!5z&sY3;A?tS0-~Qm*BW3#d}~E&@T0hJ z_AvY#)q5Wk+py3^KSr>3sO4Kee_Y8Us|V;4RfB3DVLY2J|E;FpA3eXo!CDQRGa6e5 z2@+T?im=F0hW~#L<#JQUZ8{dMNR0Ybttu|XLGK8P(!xR z(e@d?KZR(kfdx9XGq$I~e=v2VE0WyD@Ahr6K^Fsz6=&Lly$mHH$||~$=>Q~{Y>)vv z6|%p=!6`I(soWFy=O5%k#%pIrLYnqO@kO!cZDaFM9})5%6oB=j!LaiJdku8v26<@Si{SW*>C^ z`W=Cb<>MQ2gbw}`DIu8sOpK$YU{Mt}0%+UW$l>>3bs)rE>!aUs--4Aip{$xeMp&4D zeVw$}tM^!~2a0`YozBRxNh4_C93*1aDGPDX7y&y;v137PN~`=1-zKZ8)25%D7b6OO zy+>|$aiG6kG;NiR0e496jW_3Q=Ha;Uyls{8W+fq}ZFy=NH^pZLH+4@B@}PA`L#>{F z!g{|D{)T~d1`s{#ed|07UHe%!8o*RMYZ@T6A^4GVFDUGD1ObV9*-Zh#Ykd7*xZq~v za1Lqh5mh~jSsQ+l8DGOWuaFRcxSwwNsMV*q7iv|+q{5%^D9BO=98bn$>2EDhJQyn< zAq46l>xy7zAds838t|qa5B6I4+p6X=W>U9sZdy*P1VPRcf)uTgLCet(Z>=ZaZolo6 z-F)IY1%Iby(>9h^RU$7UVCH zPeAdu1R@Tu|HN7ay&08kOZPeH)#UvK%X-(1xQ^i6u*qON*t?epYUEr|H>hiZtE_xL z4m0HhSADOsqZEbsEv5IM?=Fm-d3fcje`P~#I_@9(4r=y(kQa~f4a}>s($|bu8y6fF zLZ3O2d1)n{&OGkFElgr%3@Gpx*up~#lM>xl+lI;kH*1Rr!>&?aDaO4UI84A)CWn?brTE}bYqF``|xXd??K&O zcAT%7GgXahp4#Ym@A^>AyM3T4k@tObnqd`Rk)igPxlG81G>)vZg8xjwk>yS~<&2?w zSV@3=r$*TIDU$nr-77F%wG5WQ>h&7A=FQYc#^{+tcqyX)Iu!ZI-4kt3bGbz)hS}!2 zSVFf(L*ZPFw`~G>gxlTF5X{P> zh@IA{Tw7B#nw~Z2n8E8SLwL=FBE8o>qAi*5F@YDW+ZO|E=;pgxxaBADcD6ynvb{_7 zf~FPXQfg+odQ#gisJB#4n*b!C@9m%Wuo59Cf*ex3VS<-%@YGEJhzxj5J!H!ZmbksA zX*8(+m+<(JWGcYIC1({EM#_`+U9|t7+fL{h#St$3ai6{C9 z%}1cfW&*X&d#pO|b`}d6LY_|YZyXs2r=-_#JM~C|fBLIoJKE3I$Qz2LZ38c6Dt5{d zk}pG%vgSY08}if`agMod$EqxJwS5^pLC&F_ebT(_wmi1LV^6U(dsd_V?D+sx4|3e5 z+uq#=)d_%Q%{C$QA=4XJ|AvDxcP^kqsXR4}A|rS$YJp}c155Bzvh$V`&Pgyb4g}{! zb9050d8^8F^jO=z0s2@sI=^q4eB@)4_0ozo1*WD;VIh=yKuMkr9zV~vroNA%ieQ)+ zOXXMOWD#EOO5w(3W&u-`Yhxi357*IcchegABCw9fl?bV_5ajqq4tg_$1v6GVwq0NU z5lz{c2X90sw1<8xE)x!%EvRC5FYWzkSdV%+1|8jGYySm1=|jr-z)X`G5d0I@8&8TC z50X!rfb&6Q)SKlb;IoE0nvPKl!iIXPyFXu42p;9*g9(Xjxum{2slp(M*0bcb-X?P7 z?{fk#ybZRWS>bXc)j0E1g(~^v$shbRzR(}Cy&u-KV>ps<;veS32$kQ3aC@5r!QCTz zSfA+k>*99LXExUyU=^q;1Wx@2WOQs5y6cb^Bhk^b4MjMi8wfMtq`$h_9q_Kp{@QXB zMt;mH)#A3SC(D?D;>SF0M+R{9Yq@*C*51`2dSp0}VFh;wL!dCo7C zE9OLwqdI?4Sy_7jEnJD=@n;c!;_EsgLR&}f*O6PGfZ3;5tMk@%NPh0jscaG~v#kaZ z75yHG@6e8Zlq!yi*KgkL7JYCLM`bn!3Wbd6jJIH;QrNI3vy|* zz=C*{HbNh3!NP5BVd)Z$h^B%-B(#1G&D5lZ;i1Ugo-rpww*#Z#MLhZ)(k5%=i-hNe zUlhg5c5y=LnRqSIu!oMFreeLjBrI#p+eE`5u37r$}jaClm}3&>HT)) z@$vpjMkiRsT^WL1q5@g=CiJj3>;S;UFG{s}RsjO+5YW&*bR=w&`fx8EJ3&zWh7FwA z<;1Pg$D;aRH`}W5P|!T4=@d@nd<%UM zs}UcK@|NJS^&Z##N*Jgn$O*gB*03RmU4`+yv4bmDv?7XB{E&hV4$$@-PcbDbG23wS zDU`;VKm4xZZwDE%U&W*7u%i6-m@<@mVm+4W99r^)wqu#|vR|{s9ya*C6wya8f#k&f zb2v7G0;HSZQhggv1=%&Bs3i})2q_sl?laDI39dDGz)xEexShi(RHs8dtilzRJgs#u z1NXF9yzp3hER7#9sUMuR3MLLdppXktzm6qi0616vq5svx!Z(srE4U~>#&v%F5=3eE z8awZHvhj#o1pay{4xT>KfpB#3K^pmAq1PNtFjVl+?ZWChRHds2{KwO=jw^Uu#b9Yq z5er*$nTkeseUHg4wAw+T!^e_+z(3y>X3AZHpx-z^(&aTB&Y>0oD_jB)9enou1XJL z=7SfKl>>Lvgu)8L!0x?0_BaKlNBJ7A`|z?Ovyh;&l9{M@^#EP=K(wsBt{bnl)WxnN zB3f(U;i5H+`m_jh9mK69M z9fn)?=18TGo!m-#OW8B1T077)CCszt_PeEn7pM9lFjcT08cET*#dqUokp=MDcBD5; zspO?JepDE@&k~^z8BW5%LUnj=!83U9l%=;`VmD@9!NiFvdt6$dyF0D%WW*= z0}5}%9!}uJ*?AZFq*8O?aj}CUsGLcvseZwMtko$yqT{so&47`)70msx5TQITj4J0p zJY+KM2Vk!vDS4wzKs>&Y)GRnzgikp_aBd~l2#M*Gz}|V(5Uu(R73+tKma9gG9)2YI z0opnbJU<3{TkcqJs{>8H2&#vaQz%L$-N-br1LD+m=nrRtcnxb91*Lwvamn~Gf>2O@ ztU*u~Qnr9(c1M$K_7Qp*A^qhY64eIY6l!tCaBi4wXKL7^qVg=@vcSikwSGS0Z*8ZQSac7v&~%NAMoOlNzfh9?GuKSg_ZS(;jsK7gph!U6?J znP(}I4DqmC=iI<(XA$~c11(-x8ni(9OMR}()_a=JnD^L1?Kxzf^Yhs!;`*~>+!F}@ z&#uUrZkA1z!Pgk6H+tI}g<2?;3?n%D(6bZpajsI`Vysxn{!MvG`eZ$+0!ypJ%uq*# zT=f8xxCLy{qX3a?{}M$Z$9)uwQw_*nm7?7F%L5wNeMHUd4JF*kKE?6N3?S@uHw2sR zSwnW%`&A;?{4GgcjXbnjl>(HjQ#{m)AnHwlZv;R6yr&S>cgNN9_)dG2Af?QArG)Gl%$Yztd8-^aLwB2AL zh_YYw{UT2V{89e24i1HDn^u+j5;#O850$~{Wq z$cnB2kU~OE8D>TX!fXprU&s#;T4jg;T)2J_LLZrf6-L4#sl$yb&8M1{e>m_x0@soL zp>9t+Ub(VbN^EnfKT@y~+}z}&%$1+RzTp20JU_pp8vrfx=zJ7d#lrR_Cl=kw#oGgoNj~h^;;AdKJM!b&BB3!$w(`SYy z2kq_US@GNT21w!7)Q1$E{qpAnBfRD?iPC&TVOKgznab#BpD-Q3U4o)`A9GC8^^%PG zck?V^oRBjB7+FC`^VJdTuaXOKb}v?mZKJV-oYlKe!D9URKLR|M3hymGY;dS6v>OPT zGd!il5mLDg7g+=HSgz68y~^id^3oHqc#r1fr>ddZCI5yk@d-k)mvYZ}=g&+`! zsF%pHbBRp~25(H(4Ci)n!YR_*Oa;Cth3=gHoKt0t`d(r;jVV{szNpuXmTuKJ6777B zOF!sMW)AxnpO46%t4F<eqdQw%2DjmE^`(hce8C%8;J8nC!{mK+@{r2UrcE(Xgi5} z0XeiL2j5*H?J`wHR^6|UmY^a*6+j4HgO$B<>w6Nz&LBeB!97`Xm|Q_VLQZi4rGg1P z`VkumfdAK==Fe)Sc{4Ia)wO*@Xeu@V=-opR?#l*(D$Ny)$XSAo46qWdp)HcNq)ZaJ6bWLQ-X(fjT6A?_3tAk!u)@?9?x; z?#FdhNO197NQc0jwJQeUQ{jM|K2h4dVfV+_JjdUz19l+QeoDgq0TNv>F`nvT1F}-9 zCB=&O!qg49?vGkizrE`$F*F&pC^U!p?Byf;PPb5xwG)pb_=5nymAoiUe&x_~R@PGu zXWkL+x8nk*&TxnbQX;@Ub+|le_E43#*kFrlqtSO~dy#&YaCZ0FcYiG;;I!(a>4Jh6 zll_$7j8}K&I*GV=&1rP6?MQe^f~)vTQ$BFoX}ZwNw5DL4oM_QJF}rg~sW0ZDA0Hsv z;dV#?8;#AFZvmL*wR6mkcs`Q(oiMzlz__c&&bfMB=y9p_qH_g_JzE1s&C}L`pxDmc z4WGeLD+`#pQvrgC=)aTW=S4@9W$yq?Ir8 zQxSuF=gkYZn+fbPf#+5ehD4RhMSZVW>Jn-Q5VB}>UM}g}7)}^`^1>2^CM`s~{=k6# zd*6D5%jO7p?!`w@67iOXZ#9*azEK5&&a@MTH;*^D*Au1GU9yQoCyR{C1 zHamxEMh5kXY+&BIMF^)nEp(?O^2lmj4{-7XL5^$xfQCHxF_jF37?BC24_I~gmZ(On z4m?uf#j{nTsX`!AW4@uA?AARb=v&lz;a2pP%R+(ZR_%KUt{|14Gu6}DrF-Y_06git zM|AA2Dv&>2D87;B`y3IAxHJlNWmu7!d33Y=?te$UZswji60flGSs>%mj5nwAgD@yr zC?=ay9jWk*7XGY!qstlBwkG45Vwq_>L8T+WxIuoxzqRcMx9dsg*5KbRTi6Ap1YzN5 zi57M_Ke7g^0wRo?$t!N?XzEMFsh+0D6RBZOfIc5S6a9j8flC&?@erL!t0;4an?s}Z z+-fUld=wJP-PU9P7HisPYQ&y50^8v+RGwwhINJghc*XHjbbkuTA*RALe6z$u9PeK2 zP{;IDV>vX5H?xfe7m$&V`Se(-m6l2~WS+D6W~NmTrpvJmLozLZoK&ZRH*pYj16tB* zY=8Tgp*2M-2{|1bCK*4r5}R>!H7MZ?HT^gL7mY1Nw4`{Ty@`pA2@RzHY@NT!^pLBG$5qZq&!YKHyxQEM7j99!^8^(EB@AjkOS;=WWhz) zfqf?e#V$2eEbOs3r2rJ3XagtRwEhxzd21M-U|JrB#o-k7+DZfV1C88ff27O8*`faY z#? z@(m^36N?h=tN;T(L(*`LyNYIN1wbpdO5^2b`T5@k6CB_9i!AmPX$li$EWh1m4mtqi z@aG2x{PcR`k0JpawV>N=t_rG6E88ur`SHFo<|MS?nRZg^>bPkaecHpJNavTZ-R%lQ z{-+eQZn3~|X(X=BB4 z^)rV$R7Wo3{!|%B1-sz9wtqjAA5^rpL2?f9t1>Sa} zXboxwB6-U@E&f)L6?|_H;?HTN-1h3yi!vHQf?mU3uT&v^sFtDIs~#SoO;G_n9*dG& zE&Qb(t{|69=6HTZhoyikkVwv(97gHrxohWBNgikpbaz^ zzMfD^Ia?9%6XTzkOXHukB-SS$$4`9Gl+aa#hw z{?y{nRxQ$&6EJgst!v~>Yop=78TO(s{l5S|3c&T0%>0K@?dPxfRNE^h>)8hkINxXF zQskL+Xy+2Lbm{TTV(CV6wd0(U_|-MKtiufX!L$w7n673?@UU5I~kmX%YMLoDYDLdkELW@&n#bBMDy+6cu-G z5GM2{+&(^IR#-9AgKL;`)^bR9{%bKlGg%7?yg}$U)hUgaa}l)DECR5r<^_Cq%N4A| z>mt`EhbHRM?H}eITM;JJ(fnXG8R(yuj@#4W5!%A0K;PmIQsnW-YUi@ea_Jb`Q|YE6 zpW^{z^wkyu&BKlj`?TO!(XP^Q7O^($ySD@N!@acO!NMzmp30DRa@7dTxZeB9<>o5Z zX7au??EqyO$qbr?Oc5KIg%4J}g$O1n8U*VYJqZF|A{EffBPLJt%s%+=Bv{tGIBP11 zf^tfew`<$&4_RXMut8Ia&?yFvnGxCTdjb>%7zQu4yA{gtydpZzRVH`Y$RF95LlN{G z*ZftHMCg}^$J-20INCOQN#8+PQRJTJZ09boaOt4yMCoiJiQ}?j@YNIe?86O{GqrVT z39p}MJh97W%eRr4&%OV6+rlNEw#xVgcGU)RyWacE_vY>Mbn>}V;Q*}?s0=7pFA*@6 zZV#2Yj|h>zKm@(URteA4Fcs2uHYV$>y*|L3^H<2{?P>_}E^_n#aBHYu>{!r$r9qHN z%qg>4zY#se$pRm)NCs|gtQCigjv^nYBqop5qaPhPED_ji-2B32Z|FYN|JxCAUfM{A zQr{K9P~=tDZs#FcZs|z?Ht9fhbK|9v?A6Pj48*C7Yqi!6L9g8}VzKZN+qWQX+r4+V z^ujz=&dQ+5dDYuQzTU}c3Fn=lg7U6&)d22chYZUi5)nY=R}V{xnF#ETW&|o)a0$=$$XJ=KnL&Yf$SDE+_qP6!)c&~lQhp}z&>bLrC=e=R04a1tL<;qjTebvE0!QSGe z8t1UzkMfO`$pE(oXAFPv^blT(K@WQ^qX@vCiv$MKi3uqxP8EgESte-ho<93bkyp3~ zS!!&_h;dHHJE9ZQ)oboTay#S#9MhpPM z*ASN93%H~D z|GoByKEvoV6U!1zhSiU=$KD(RJm)wfsq$Rnu>eX-Ck)G-x)4Xi6%Ql#wFsA+(*!eg zya^ThYZZmEdM3!^e?IYkFIRCs#c4VKMhjDa*duiq+F7 z%iee_Oy~V8werXgrT|{(2Mi^Ioe*_a01s{+zX(~9_5=%@)d_zIdlm6UiY8x0a6U@P z{#Fpfdujh5l5k$X6l!EnH&|&kXF;(My(vD^c@o$ZJp^zpG6!LfW)=1nts!m{G9`+Q z>K^Kzxezf{_x!{aKIsPe58dSI7~4nlg5RQ_PvkWCb?4f>U+In8>*%_^3*)0a+|}ET zro@+@d$z?iQLwX$9I}B;EV!OF8NO6LZo}7JKg+h3kJaJC&fb~;T<6Ht!15SCngEa0 z=nEHlfDm&u>kiu;$OskT7zJaf?g{cQiWQtsnI`R%U_LH5&Q=ocIUB6T1eBO?{7Mdi)omT?N?MuOhL?nxhah?!xEj& z$pn)?iwAgbO%=WGRUx>Z(j>NtpdQ56j1ay+0{ww|jp?Pcc-^k8VcW^Im*2n)P~?ov zcjt()Sm{54&gipfz8*3~9P;>4^0<+iz6yRZ)gX0nkQPPp70Grlz3oWrC-YRl%C znbq@c*4{R>dgt2?*Yd2hf&e_5s|)s-Mi3~?!47Ar*$8EuTLpY=APTm)s1@+iwI(b3 zKt2S1ZC1DcplD2O=Mwaq4h7iiwg*&iK@|xDD4l6P~_txdFQVTQR$mwvFNEqzTzlq($!J~9mUItOt(%4B(Z<- zuCh^~aJWCGO}?Ua%ELSPlgoQdq}Al7-rnjmnCD66>+&W+YXFLpZwurU4G?01nGO$~ z>IgIPn*}(*Q3}(7#}#^H&?Yt;AwIKI4OUE)5NK-){%&FMzi7b5eOHYkB0<83wkhM{ zFB9G|l?C073<&0bDivvS)*!tzMI;y4F&@~@N)TVb68)@C0qO{-5#DzK&D-KSxZf6} zQRF3~dFPP@P3fuZqUevMtKtu4(A72yI>phSf49ZcSFxZr)3W9$g18hYTfT(m;KRyl zsLR0QsnrDe;@*LTrsr(a_422ZUjX3!Q47X-@DK69g$@Sc@(9mEy9L-kY6?dC))fc- F+$O|qMKk~a literal 0 HcmV?d00001 diff --git a/programs/1-chip8-logo.ch8 b/programs/1-chip8-logo.ch8 new file mode 100644 index 0000000000000000000000000000000000000000..19c5cf30f469f86fce62351b46c5a68454d93429 GIT binary patch literal 260 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 B8T|kN literal 0 HcmV?d00001 diff --git a/programs/6-keypad.ch8 b/programs/6-keypad.ch8 new file mode 100644 index 0000000000000000000000000000000000000000..4d1ecdccdf6968522529fff4a18492cd31032600 GIT binary patch literal 913 zcmZ9IPfQa*6vk(#jkGJmFblR((rkA9(?bu492&ESg%(92hG3gCdKqAw5VRXYz;IAA z5koM+gv02;G-QjrSL1<$2SfJemBbr~aSq%}npVMhAk>)>FU(};_s#p>yqSGkCjq>M zqYH|SKpjWRVB2UGw_i4k*vKh+*nj{WrA}wFJPUsJ9d-->ocGN=?5Kx*jGR z)F{AH%PrjRbc-E%t7EdP;QqBqM?I)v>VQRMNfFMi=l7 z+yB{SuYY7z=La8O4qor?%-;&$?C%hN`s)nDXnIR~I9((44_o3dcyO~-_HnCN!EPkT ziL=;MgYh5d zsop5c7gx@?7lMdUC6Pv7Lv4240NO8J)BXbPUnQyO_?}ilZcor-R1Mfr4XAZi)ZmhK zT!{tO0Cz>)PoL)f_rci_hF(ZL+vA?cL)+$D4>>0~_qeX}P;4j{F`-372re^2sVU{5 zCuB&44k@7?5theA4WjF!s^?LWBhE?LwnUKH9w*WyvbJs8!Xn}m6q#ZbQWT)P{m#?X zj-HPcq9{^|_CknHzGl7KkxxJ#De9^NN5@p))b|Yz`o1uSYX1Z>ak79oD{L%NTA3jEwqCGGR%Xe{o^6k~qmv;v1bSJL^|LI{*%g*% v7RazD8(=m_CJ;ev6&%-J4+^;XHm!}xsR!f(r<15An`#|N$1w~k3h1BDab&GPl(~kLLrL} z>;?=%It=d;7!qxgxEUUR)P9tBv{1+er03B*As->ELJ=_AMpElU_X#aY E02O^Dc>n+a literal 0 HcmV?d00001 diff --git a/programs/Airplane.ch8 b/programs/Airplane.ch8 new file mode 100644 index 0000000000000000000000000000000000000000..cac880477e824b0948ed65d195c4cc44896577d0 GIT binary patch literal 356 zcmc~|$Y#l5%w@=9QdUz|NH9ob{2=;){iB2dgOG@lld?iBBT$s%!9ry&F9spI#Ys0N zRx;k0=+7wh#ZyA)iP9z^IUh!)93Lj7Dj#O04j&e!X+Eq$))H3$le;T{}>+lu;nmjF$i%buzirI{_o8o z%mMN@!vm0*Fozc-!@C5g4-#379TzGXJ0F1j3A8Qb?jweGDU!(yi^DEE7IPRo2Gj=j3rE$9U0y={P^GSK%k)GonZN@6$=^|0Amq% A0ssI2 literal 0 HcmV?d00001 diff --git a/programs/Nim [Carmelo Cortez, 1978].ch8 b/programs/Nim [Carmelo Cortez, 1978].ch8 new file mode 100644 index 0000000000000000000000000000000000000000..680c96a31b577f21eb375e8b19aa54a770cc22b8 GIT binary patch literal 182 zcmc~(&Sg?c`orbJFC>x0*tb+EsduR#gHTfjGyF zl7O<4G7El+{$#gg5VD$aK`5ZTq3uF@sy~BJKrKset1W|2R4$7UPv2FgBp@yEOM>CS z;(&j~$pYC7pHe?)Ufx>F`VmZi0+I~x7A;C(NEE#wx@dU<#|2TL35(VM`2s-xMlgRt ZcW39y+nbEE)Ry&ib#=Xa*YQT<9ROQ-MuPwV literal 0 HcmV?d00001 diff --git a/programs/br8kout.ch8 b/programs/br8kout.ch8 new file mode 100644 index 0000000000000000000000000000000000000000..7e675f02405c4d5b99068f0e84de304cf53135d7 GIT binary patch literal 199 zcmWfd|L0G`BBr|s8Qv{oxxElbGAA%4vbHm{30x~I8E2BVjJ#(#fCC1)>rrB;S_3=gv9vP6_vmAI7z8Q%S4w_^~RoX7GHXjiUW mj$F1wmh4GJPln9@8UG<_64)OsRI*Os1d_%|$x4%jwgLe9phWTj literal 0 HcmV?d00001 diff --git a/programs/corax.ch8 b/programs/corax.ch8 new file mode 100644 index 0000000000000000000000000000000000000000..9fc874c917a5a6957c4affe5ac285211abb749b2 GIT binary patch literal 761 zcmY+?KZw&%7y$6sn)b9Elu|?#G~D3OF1ZLnyd?@Yt4sHsnxucAV>u8sE(r%6T-=WO zB430%IN=}%-f$3y8ywuvo1TM%pmG)L;!wZuHIdwsy!^iJeeZi8Y1Hfz`(>9K@0jT6 z!|O0RNqYC-4?fHN>YzR>IW4yh2PX;q{E|Ryv>`f8hE+JsBzo9_7^MzJxpZwQSKv68 z@uDNMo>ky;Zm(q)%3daM;}zBJHTqWvH-;tHU%Jg!%MhE4Vwk;y`jt8{%MfWy+9$Lf zw8&|BEPQ0;XOZubyLGP>Zdh&V3u}rh4~ z-Xwf_Zr4%fo1tU1T4$p+IGsJU*@iaj!t~5}$>7ZyWoww7PmS{EW>a@-e&@G?lPfj#wF^yMUuo*7 z_cLXvRn&@9rJxwm71ZSfsot_%Wqwo6^gD|DL)^u9RHr1n>L10uZ7-p(p|$KmzK1c3e(Pq=47l2Z7_vV@bRSiF_uJg(;{d) z-AO^#Z8FZ@roLRGh5DA274{$_dzr%rjgGaYPp2=V1Bo~3gNowyd-c!QzF)rI^F806 zpY!GDn;ib9|Mmu$5RQ-@GtN1aLI^22vZuJk>%#IfgrtkO5)0EpoxoLozJAL zSZo{`josZ$CV&6*sUMWY@4M}>Pd@nf+dv@D)7x|H*SfR)@X@^3_b2rRLAI)< z7W%2AhR&9JC<+QefpcmhT-3ece#u(^NaoRVCMa}oU!}4P` zkQ>YoMAqG4%3o4b-6dZtP*PF{N`&EGph_raDmA5fsO(QFp_9*U>3@{FlO*&RS`W&B zq#8Pd*2D6FB%(m;Ns=s*q_RYkzC|~wF1b^*0Mp?jOu2>lv|Bt9>7=0yHD%}L6iMTZ X8 literal 0 HcmV?d00001 diff --git a/src/chip8.rs b/src/chip8.rs new file mode 100644 index 0000000..22b77ba --- /dev/null +++ b/src/chip8.rs @@ -0,0 +1,149 @@ +mod cpu; +mod debug; +mod gpu; +mod input; +mod memory; +mod renderer; + +use rodio::source::{SineWave, Source}; + +static MEMORY_LIMIT: i32 = 4096; +static STACK_LIMIT: i32 = 16; +static VARIABLE_REGISTER_COUNT: i32 = 16; +static TIMER_TICK_RATE: u32 = 60; +static DESIRED_FPS: u32 = 165; +static CYCLES_PER_FRAME: u32 = 10; + +#[derive(Clone)] +struct Chip8State { + // Flags + eti_600_flag: bool, + + // Memory + mem: [u8; MEMORY_LIMIT as usize], + stack: [u16; STACK_LIMIT as usize], + + // Registers + r_v: [u8; VARIABLE_REGISTER_COUNT as usize], // General Purpose + r_i: u16, // Memory Addresses + r_dt: u8, // Delay Timer + r_st: u8, // Sound Timer + r_pc: u16, // Program Counter + r_sp: u8, // Stack Pointer + + // Display + display: [[bool; gpu::CHIP8_DISPLAY_WIDTH as usize]; gpu::CHIP8_DISPLAY_HEIGHT as usize], + + // Input + input: [bool; 16], +} + +pub fn run>(chip8_executable_filepath: S, debug_mode: bool) { + let mut state = Chip8State { + eti_600_flag: false, + mem: [0; 4096], + stack: [0; 16], + r_v: [0; 16], + r_i: 0, + r_dt: 2, + r_st: 0, + r_pc: 0, + r_sp: 0, + display: [[false; 64]; 32], + input: [false; 16], + }; + + if !state.eti_600_flag { + state.r_pc = 0x200; + } else { + state.r_pc = 0x600; + } + + // Load Program + let _ = memory::load_file_to_memory(&mut state, chip8_executable_filepath.as_ref()); + + // Run Program + start(&mut state, debug_mode); +} + +fn start(state: &mut Chip8State, debug_mode: bool) { + // TODO rip out as much RL stuff from here and put into renderer + // Init Rendering Pipeline + let (mut rl, thread) = raylib::init() + .size(renderer::DISPLAY_WIDTH, renderer::DISPLAY_HEIGHT) + .title("Chip8 Emu") + .build(); + rl.set_target_fps(DESIRED_FPS); // Should see if i can get the users hz + if !debug_mode { + rl.set_trace_log(raylib::ffi::TraceLogLevel::LOG_NONE); + } + + // initialize timer + let mut timer_accumulator: f32 = 0.0f32; + let timer_increment: f32 = TIMER_TICK_RATE as f32 / DESIRED_FPS as f32; + + // initialize builtin sprites + gpu::load_builtin_sprites(state); + + // initialize sound system look into struct and impl for functions + let stream_handle = + rodio::OutputStreamBuilder::open_default_stream().expect("open default audio stream"); + let sink = rodio::Sink::connect_new(&stream_handle.mixer()); + + let source = SineWave::new(440.0) + .amplify(0.2) // Volume (0.0 to 1.0) + .repeat_infinite(); + + sink.append(source); + sink.play(); + + while !rl.window_should_close() { + if state.r_pc > 4095 { + break; + } + + let sound_volume = sink.volume(); + if state.r_st > 0 { + if sound_volume < 1.0f32 { + sink.set_volume(0.2f32); + } + } else { + sink.set_volume(0.0f32); + } + + input::handle_input(state, &mut rl); + + for _ in 0..CYCLES_PER_FRAME { + let instruction_bytes = + memory::read_n_bytes(&state.mem, state.mem.len(), state.r_pc as usize, 2); + let instruction: u16 = + ((instruction_bytes[0] as u16) << 8) | instruction_bytes[1] as u16; + state.r_pc += 2; + + if debug_mode { + debug::print_debug(state, instruction); + } + + cpu::execute_instruction(state, instruction); + } + + // move to timers.rs + // timers::tick(); + if (state.r_dt | state.r_st) > 0 { + timer_accumulator += timer_increment; + while timer_accumulator >= 1.0f32 { + if state.r_dt > 0 { + state.r_dt -= 1; + }; + if state.r_st > 0 { + state.r_st -= 1; + } + timer_accumulator -= 1.0f32; + } + } else { + timer_accumulator = 0.0f32; + } + + renderer::render(&state, &mut rl, &thread); + } +} diff --git a/src/chip8/cpu.rs b/src/chip8/cpu.rs new file mode 100644 index 0000000..ddf10e3 --- /dev/null +++ b/src/chip8/cpu.rs @@ -0,0 +1,159 @@ +use crate::chip8::gpu; + +use super::Chip8State; +use super::memory::read_n_bytes; + +// Need to come back and add good comments for each of the match patterns +pub fn execute_instruction(state: &mut Chip8State, instruction: u16) { + let c = ((instruction & 0xF000) >> 12) as u8; + let x = ((instruction & 0x0F00) >> 8) as u8; + let y = ((instruction & 0x00F0) >> 4) as u8; + let d = (instruction & 0x000F) as u8; + let n = d; + let kk = (instruction & 0x00FF) as u8; + let nnn = (instruction & 0x0FFF) as u16; + + match (c, x, y, d) { + (0x0, _, 0xE, 0x0) => { + for row in state.display.iter_mut() { + for col in row { + *col = false; + } + } + } + (0x0, _, 0xE, 0xE) => { + state.r_sp -= 1; + state.r_pc = state.stack[state.r_sp as usize]; + } + (0x1, _, _, _) => state.r_pc = nnn, + (0x2, _, _, _) => { + state.stack[state.r_sp as usize] = state.r_pc; + state.r_sp += 1; + state.r_pc = nnn; + } + (0x3, _, _, _) => { + if state.r_v[x as usize] == kk { + state.r_pc += 2; + } + } + (0x4, _, _, _) => { + if state.r_v[x as usize] != kk { + state.r_pc += 2; + } + } + (0x5, _, _, _) => { + if state.r_v[x as usize] == state.r_v[y as usize] { + state.r_pc += 2; + } + } + (0x6, _, _, _) => state.r_v[x as usize] = kk, + (0x7, _, _, _) => state.r_v[x as usize] = state.r_v[x as usize].wrapping_add(kk), + (0x8, _, _, 0x0) => state.r_v[x as usize] = state.r_v[y as usize], + (0x8, _, _, 0x1) => state.r_v[x as usize] |= state.r_v[y as usize], + (0x8, _, _, 0x2) => state.r_v[x as usize] &= state.r_v[y as usize], + (0x8, _, _, 0x3) => state.r_v[x as usize] ^= state.r_v[y as usize], + (0x8, _, _, 0x4) => { + let val: u16 = state.r_v[x as usize] as u16 + state.r_v[y as usize] as u16; + if val > u8::MAX as u16 { + state.r_v[x as usize] = (val & 0xFFFF) as u8; + state.r_v[0xF] = 1; + } else { + state.r_v[x as usize] = val as u8; + state.r_v[0xF] = 0; + } + } + (0x8, _, _, 0x5) => { + let flag = state.r_v[x as usize] > state.r_v[y as usize]; + state.r_v[x as usize] = state.r_v[x as usize].wrapping_sub(state.r_v[y as usize]); + state.r_v[0xF] = if flag { 1 } else { 0 }; + } + (0x8, _, _, 0x6) => { + let flag = (state.r_v[x as usize] & 0b00000001) == 1; + state.r_v[0xF] = if flag { 1 } else { 0 }; + state.r_v[x as usize] = state.r_v[x as usize] / 2; + } + (0x8, _, _, 0x7) => { + let flag = state.r_v[x as usize] < state.r_v[y as usize]; + state.r_v[x as usize] = state.r_v[y as usize].wrapping_sub(state.r_v[x as usize]); + state.r_v[0xF] = if flag { 1 } else { 0 }; + } + (0x8, _, _, 0xE) => { + let flag = ((state.r_v[x as usize] & 0b10000000) >> 7) == 1; + state.r_v[0xF] = if flag { 1 } else { 0 }; + state.r_v[x as usize] = state.r_v[x as usize].wrapping_mul(2); + } + (0x9, _, _, _) => { + if state.r_v[x as usize] != state.r_v[y as usize] { + state.r_pc += 2 + } + } + (0xA, _, _, _) => state.r_i = nnn, + (0xB, _, _, _) => state.r_pc = nnn + state.r_v[0] as u16, + (0xC, _, _, _) => { + let rng = rand::random_range(0..256) as u8; + let result = rng & kk; + state.r_v[x as usize] = result; + } + (0xD, _, _, _) => { + state.r_v[0xF] = 0; + let bytes = read_n_bytes(&state.mem, state.mem.len(), state.r_i as usize, n as usize); + gpu::draw(state, x, y, &bytes, n); + } + (0xE, _, _, 0xE) => { + let key_index = state.r_v[x as usize]; + if state.input[key_index as usize] { + state.r_pc += 2; + } + } + (0xE, _, _, 0x1) => { + let key_index = state.r_v[x as usize]; + if !state.input[key_index as usize] { + state.r_pc += 2; + } + } + (0xF, _, 0x0, 0x7) => state.r_v[x as usize] = state.r_dt, + (0xF, _, 0x0, 0xA) => { + let mut is_key_pressed = false; + for i in 0..state.input.len() { + if state.input[i] { + state.r_v[x as usize] = i as u8; + is_key_pressed = true; + break; + } + } + if !is_key_pressed { + state.r_pc -= 2; // set the pc back to this instruction, otherwise can't handle input + } + } + (0xF, _, 0x1, 0x5) => state.r_dt = state.r_v[x as usize], + (0xF, _, 0x1, 0x8) => state.r_st = state.r_v[x as usize], + (0xF, _, 0x1, 0xE) => state.r_i = state.r_i + state.r_v[x as usize] as u16, + (0xF, _, 0x2, 0x9) => state.r_i = gpu::get_builtin_sprite_addr(x) as u16, + (0xF, _, 0x3, 0x3) => { + let mut decimal = state.r_v[x as usize]; + let mut i = 3; + loop { + i = i - 1; + state.mem[(state.r_i + i) as usize] = decimal % 10; + decimal = decimal / 10; + + if i == 0 { + break; + } + } + } + (0xF, _, _, 0x5) => { + let mut i = 0; + while i <= x { + match y { + 0x5 => state.mem[(state.r_i + i as u16) as usize] = state.r_v[i as usize], + 0x6 => state.r_v[i as usize] = state.mem[(state.r_i + i as u16) as usize], + _ => panic!("Unmatched OPCODE 0xFx{}5", y), + } + + i = i + 1; + } + } + _ => {} + } +} diff --git a/src/chip8/debug.rs b/src/chip8/debug.rs new file mode 100644 index 0000000..22fc0f0 --- /dev/null +++ b/src/chip8/debug.rs @@ -0,0 +1,27 @@ +use std::io::{self, Read}; + +use super::Chip8State; + +pub fn print_debug(state: &Chip8State, current_instruction: u16) { + print!("\x1b[H\x1b[J"); + println!("---- DEBUG ----"); + println!("PC: {:04X}", state.r_pc - 2); // -2 because of where i put this log in the main loop + println!("SP: {}", state.r_sp); + println!("I: {:04X}", state.r_i); + println!("DT: {}", state.r_dt); + println!("ST: {}", state.r_st); + for x in 0..state.r_v.len() { + if state.r_v[x] > 0 { + println!("V{}: {}", x, state.r_v[x]); + } + } + for x in 0..state.input.len() { + if state.input[x] == true { + println!("Pressed: {}", x); + } + } + println!("Current Instruction: {:04X}", current_instruction); + println!("----------------"); + println!("Press Enter to continue..."); + let _ = io::stdin().read(&mut [0u8]).unwrap(); +} diff --git a/src/chip8/gpu.rs b/src/chip8/gpu.rs new file mode 100644 index 0000000..c12f2e2 --- /dev/null +++ b/src/chip8/gpu.rs @@ -0,0 +1,135 @@ +use crate::chip8::memory; + +use super::Chip8State; + +pub static CHIP8_DISPLAY_WIDTH: i32 = 64; +pub static CHIP8_DISPLAY_HEIGHT: i32 = 32; +pub static SPRITE_WIDTH: u8 = 8; + +// I probably don't need state here +// refactor to just receive a mutatable buffer +pub fn draw(state: &mut Chip8State, vx: u8, vy: u8, bytes_to_draw: &[u8], bytes_to_draw_len: u8) { + let mut bytes_idx = 0; + let start_x = state.r_v[vx as usize]; + let start_y = state.r_v[vy as usize]; + for y in start_y..start_y + bytes_to_draw_len { + if (y as i32) < CHIP8_DISPLAY_HEIGHT { + // 8 is the hardcoded sprite width, has to atleast have 1 8 bit value to display + let mut bit_idx = 0; + for x in start_x..start_x + SPRITE_WIDTH { + if (x as i32) < CHIP8_DISPLAY_WIDTH { + let sprite_pixel = ((bytes_to_draw[bytes_idx] >> (7 - bit_idx)) & 1) == 1; + let current_pixel = state.display[y as usize][x as usize]; + let new_pixel = current_pixel ^ sprite_pixel; + + state.display[y as usize][x as usize] = new_pixel; + + if new_pixel != current_pixel { + state.r_v[0xF] = 1; + } + + bit_idx += 1; + } + } + } + bytes_idx += 1; + } +} + +pub fn load_builtin_sprites(state: &mut Chip8State) { + memory::load_bytes( + state, + &BUILTIN_SPIRTES, + BUILTIN_SPIRTES.len(), + BUILTIN_SPRITES_ADDR as usize, + ); +} + +pub fn get_builtin_sprite_addr(sprite_index: u8) -> u8 { + return BUILTIN_SPRITES_ADDR + (sprite_index * BUILTIN_SPRITES_SIZE); +} + +static BUILTIN_SPRITES_ADDR: u8 = 0; +static BUILTIN_SPRITES_SIZE: u8 = 5; +static BUILTIN_SPIRTES: [u8; 80] = [ + 0xF0, // 0 + 0x90, // 0 + 0x90, // 0 + 0x90, // 0 + 0xF0, // 0 + 0x20, // 1 + 0x60, // 1 + 0x20, // 1 + 0x20, // 1 + 0x70, // 1 + 0xF0, // 2 + 0x10, // 2 + 0xF0, // 2 + 0x80, // 2 + 0xF0, // 2 + 0xF0, // 3 + 0x10, // 3 + 0xF0, // 3 + 0x10, // 3 + 0xF0, // 3 + 0x90, // 4 + 0x90, // 4 + 0xF0, // 4 + 0x10, // 4 + 0x10, // 4 + 0xF0, // 5 + 0x80, // 5 + 0xF0, // 5 + 0x10, // 5 + 0xF0, // 5 + 0xF0, // 6 + 0x80, // 6 + 0xF0, // 6 + 0x90, // 6 + 0xF0, // 6 + 0xF0, // 7 + 0x10, // 7 + 0x20, // 7 + 0x40, // 7 + 0x40, // 7 + 0xF0, // 8 + 0x90, // 8 + 0xF0, // 8 + 0x90, // 8 + 0xF0, // 8 + 0xF0, // 9 + 0x90, // 9 + 0xF0, // 9 + 0x10, // 9 + 0xF0, // 9 + 0xF0, // A + 0x90, // A + 0xF0, // A + 0x90, // A + 0x90, // A + 0xE0, // B + 0x90, // B + 0xE0, // B + 0x90, // B + 0xE0, // B + 0xF0, // C + 0x80, // C + 0x80, // C + 0x80, // C + 0xF0, // C + 0xE0, // D + 0x90, // D + 0x90, // D + 0x90, // D + 0xE0, // D + 0xF0, // E + 0x80, // E + 0xF0, // E + 0x80, // E + 0xF0, // E + 0xF0, // F + 0x80, // F + 0xF0, // F + 0x80, // F + 0x80, // F +]; diff --git a/src/chip8/input.rs b/src/chip8/input.rs new file mode 100644 index 0000000..3a86ccb --- /dev/null +++ b/src/chip8/input.rs @@ -0,0 +1,78 @@ +use raylib::{RaylibHandle, ffi::KeyboardKey}; + +use super::Chip8State; + +static RL_KEY_LAYOUT: [KeyboardKey; 16] = [ + KeyboardKey::KEY_ONE, + KeyboardKey::KEY_TWO, + KeyboardKey::KEY_THREE, + KeyboardKey::KEY_FOUR, + KeyboardKey::KEY_Q, + KeyboardKey::KEY_W, + KeyboardKey::KEY_E, + KeyboardKey::KEY_R, + KeyboardKey::KEY_A, + KeyboardKey::KEY_S, + KeyboardKey::KEY_D, + KeyboardKey::KEY_F, + KeyboardKey::KEY_Z, + KeyboardKey::KEY_X, + KeyboardKey::KEY_C, + KeyboardKey::KEY_V, +]; + +pub fn handle_input(state: &mut Chip8State, rl_hdl: &mut RaylibHandle) { + for key in RL_KEY_LAYOUT { + let mapped_key = qwerty_to_chip8(key); + // let chip8_input_index = chip8_to_index(mapped_key); + if rl_hdl.is_key_down(key) { + state.input[mapped_key as usize] = true; + } else { + state.input[mapped_key as usize] = false; + } + } +} + +fn qwerty_to_chip8(keycode: KeyboardKey) -> u8 { + match keycode { + KeyboardKey::KEY_ONE => return 0x1, + KeyboardKey::KEY_TWO => return 0x2, + KeyboardKey::KEY_THREE => return 0x3, + KeyboardKey::KEY_FOUR => return 0xC, + KeyboardKey::KEY_Q => return 0x4, + KeyboardKey::KEY_W => return 0x5, + KeyboardKey::KEY_E => return 0x6, + KeyboardKey::KEY_R => return 0xD, + KeyboardKey::KEY_A => return 0x7, + KeyboardKey::KEY_S => return 0x8, + KeyboardKey::KEY_D => return 0x9, + KeyboardKey::KEY_F => return 0xE, + KeyboardKey::KEY_Z => return 0xA, + KeyboardKey::KEY_X => return 0x0, + KeyboardKey::KEY_C => return 0xB, + KeyboardKey::KEY_V => return 0xF, + _ => return 0, + } +} + +// fn chip8_to_index(chip8_key: u8) -> u8 { +// match chip8_key { +// 0x1 => return 0x0, +// 0x2 => return 0x1, +// 0x3 => return 0x2, +// 0xC => return 0x3, +// 0x4 => return 0x4, +// 0x5 => return 0x5, +// 0x6 => return 0x6, +// 0xD => return 0x7, +// 0x7 => return 0x8, +// 0x8 => return 0x9, +// 0x9 => return 0xA, +// 0xE => return 0xB, +// 0xA => return 0xC, +// 0x0 => return 0xD, +// 0xB => return 0xE, +// 0xF => return 0xF, +// _ => panic!("Unknown Chip8 Key {}", chip8_key), +// } +// } diff --git a/src/chip8/memory.rs b/src/chip8/memory.rs new file mode 100644 index 0000000..49093eb --- /dev/null +++ b/src/chip8/memory.rs @@ -0,0 +1,45 @@ +use super::Chip8State; +use std::{io, path::Path}; + +pub fn read_n_bytes( + buffer: &[u8], + buffer_len: usize, + start_addr: usize, + n_bytes: usize, +) -> Vec { + let mut addr = start_addr; + let mut bytes = Vec::new(); + while addr != start_addr + n_bytes { + if addr >= buffer_len { + panic!( + "Couldn't read from Address {} exceeds buffer length {}", + addr, buffer_len + ) // nice error handling + } + bytes.push(buffer[addr]); + + addr += 1; + } + + return bytes as Vec; +} + +pub fn load_bytes(state: &mut Chip8State, data: &[u8], data_len: usize, start_addr: usize) { + for i in 0..data_len { + state.mem[start_addr + i] = data[i]; + } +} + +pub fn load_file_to_memory>(state: &mut Chip8State, filepath: P) -> io::Result<()> { + let fp = filepath.as_ref(); + + // read file to Vec(u8) + let program = std::fs::read(fp)?; + + for i in 0..program.len() { + state.mem[state.r_pc as usize + i] = program[i]; + } + + // Should return Ok or Err + Ok(()) +} diff --git a/src/chip8/renderer.rs b/src/chip8/renderer.rs new file mode 100644 index 0000000..6e37d1e --- /dev/null +++ b/src/chip8/renderer.rs @@ -0,0 +1,28 @@ +use super::Chip8State; +use super::gpu::{CHIP8_DISPLAY_HEIGHT, CHIP8_DISPLAY_WIDTH}; +use raylib::{RaylibHandle, RaylibThread, color::Color, prelude::RaylibDraw}; + +pub static DISPLAY_WIDTH: i32 = 640; +pub static DISPLAY_HEIGHT: i32 = 480; + +pub fn render(state: &Chip8State, rl_handle: &mut RaylibHandle, rl_thread: &RaylibThread) { + let mut d = rl_handle.begin_drawing(&rl_thread); + + // d.clear_background(Color::BLACK); + + let scale_x = DISPLAY_WIDTH / CHIP8_DISPLAY_WIDTH; + let scale_y = DISPLAY_HEIGHT / CHIP8_DISPLAY_HEIGHT; + + for y in 0..CHIP8_DISPLAY_HEIGHT { + for x in 0..CHIP8_DISPLAY_WIDTH { + // fix to render color based on exact bit for pixels + let color: Color; + if state.display[y as usize][x as usize] { + color = Color::WHITE; + } else { + color = Color::GREEN; + } + d.draw_rectangle(x * scale_x, y * scale_y, scale_x, scale_y, color); + } + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..7d397c1 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,27 @@ +use clap::Parser; + +mod chip8; + +#[derive(Parser, Debug)] +#[command(version, about = "Chip 8 Emulator")] +struct Args { + /// Turn on debug mode + #[arg(short, long, action = clap::ArgAction::SetTrue)] + debug: bool, + /// Set path for chip8 binary file to run + #[arg(short, long)] + file: Option, +} + +fn main() { + let args = Args::parse(); + // TODO: should define my raylib/sound/etc handles here eventually and pass them down + + if let Some(filepath) = args.file { + if args.debug { + chip8::run(filepath, true); + } else { + chip8::run(filepath, false); + } + } +}