diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..750ef1a --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: mre +patreon: hellorust diff --git a/.github/workflows/continuous_testing.yaml b/.github/workflows/continuous_testing.yaml new file mode 100644 index 0000000..cb8fa97 --- /dev/null +++ b/.github/workflows/continuous_testing.yaml @@ -0,0 +1,24 @@ +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build_and_test: + name: test + strategy: + matrix: + os: [ubuntu-latest, macOS-latest, windows-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@master + - name: Install cargo + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + - name: Run test + uses: actions-rs/cargo@v1 + with: + command: test diff --git a/Cargo.lock b/Cargo.lock index 0664e70..236936b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,383 +1,411 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. [[package]] -name = "backtrace" -version = "0.3.9" +name = "autocfg" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "backtrace-sys" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" [[package]] name = "bitflags" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bitflags" -version = "1.0.4" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "bytecount" -version = "0.3.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0017894339f586ccb943b01b9555de56770c11cda818e7e3d8bd93f4ed7f46e" [[package]] name = "cargo_metadata" -version = "0.5.8" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8de60b887edf6d74370fc8eb177040da4847d971d6234c7b13a6da324ef0caf" dependencies = [ - "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", + "semver", + "serde", + "serde_derive", + "serde_json", ] -[[package]] -name = "cc" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "cfg-if" -version = "0.1.6" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "error-chain" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ab49e9dcb602294bc42f9a7dfc9bc6e936fca4418ea300dbfb84fe16de0b7d9" dependencies = [ - "backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check", ] [[package]] -name = "fuchsia-zircon" -version = "0.3.3" +name = "getrandom" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" dependencies = [ - "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10", + "libc", + "wasi", ] -[[package]] -name = "fuchsia-zircon-sys" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "glob" -version = "0.2.11" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" [[package]] name = "itoa" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" [[package]] name = "libc" -version = "0.2.43" +version = "0.2.80" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614" [[package]] name = "log" -version = "0.4.6" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" dependencies = [ - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 1.0.0", ] [[package]] name = "mos6502" -version = "0.1.0" +version = "0.2.0" dependencies = [ - "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "num 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "skeptic 0.13.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags", + "log", + "num", + "skeptic", ] [[package]] name = "num" -version = "0.2.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b7a8e9be5e039e2ff869df49155f1c06bd01ade2117ec783e56ab0932b67a8f" dependencies = [ - "num-complex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", - "num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", - "num-rational 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", ] [[package]] name = "num-complex" -version = "0.2.1" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "747d632c0c558b87dbabbe6a82f3b4ae03720d0646ac5b7b4dae89394be5f2c5" dependencies = [ - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits", ] [[package]] name = "num-integer" -version = "0.1.39" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" dependencies = [ - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg", + "num-traits", ] [[package]] name = "num-iter" -version = "0.1.37" +version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" dependencies = [ - "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg", + "num-integer", + "num-traits", ] [[package]] name = "num-rational" -version = "0.2.1" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5fa6d5f418879385b213d905f7cf5bf4aa553d4c380f0152d1d4f2749186fa9" dependencies = [ - "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg", + "num-integer", + "num-traits", ] [[package]] name = "num-traits" -version = "0.2.6" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20" [[package]] name = "proc-macro2" -version = "0.4.20" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" dependencies = [ - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid", ] [[package]] name = "pulldown-cmark" -version = "0.1.2" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eef52fac62d0ea7b9b4dc7da092aa64ea7ec3d90af6679422d3d7e0e14b6ee15" dependencies = [ - "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags", ] [[package]] name = "quote" -version = "0.6.9" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" dependencies = [ - "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", ] [[package]] name = "rand" -version = "0.4.3" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ - "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom", + "libc", + "rand_chacha", + "rand_core", + "rand_hc", ] +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + [[package]] name = "remove_dir_all" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" dependencies = [ - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi", ] -[[package]] -name = "rustc-demangle" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "ryu" -version = "0.2.6" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997" [[package]] name = "same-file" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421" dependencies = [ - "winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util", ] [[package]] name = "semver" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" dependencies = [ - "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", + "semver-parser", + "serde", ] [[package]] name = "semver-parser" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.80" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9796c9b7ba2ffe7a9ce53c2287dfc48080f4b2b362fcc245a259b3a7201119dd" [[package]] name = "serde_derive" -version = "1.0.80" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e" dependencies = [ - "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.18 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "serde_json" -version = "1.0.32" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f72eb2a68a7dc3f9a691bfda9305a1c017a6215e5a4545c258500d2099a37c2" dependencies = [ - "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa", + "ryu", + "serde", ] [[package]] name = "skeptic" -version = "0.13.3" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6deb8efaf3ad8fd784139db8bbd51806bfbcee87c7be7578e9c930981fb808" dependencies = [ - "bytecount 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "cargo_metadata 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", - "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "walkdir 2.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "bytecount", + "cargo_metadata", + "error-chain", + "glob", + "pulldown-cmark", + "tempfile", + "walkdir", ] [[package]] name = "syn" -version = "0.15.18" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac" dependencies = [ - "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "unicode-xid", ] [[package]] -name = "tempdir" -version = "0.3.7" +name = "tempfile" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" dependencies = [ - "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10", + "libc", + "rand", + "redox_syscall", + "remove_dir_all", + "winapi", ] [[package]] name = "unicode-xid" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" + +[[package]] +name = "version_check" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" [[package]] name = "walkdir" -version = "2.2.6" +version = "2.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9658c94fa8b940eab2250bd5a457f9c48b748420d71293b165c8cdbe2f55f71e" dependencies = [ - "same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "same-file", + "winapi", + "winapi-util", ] [[package]] -name = "winapi" -version = "0.3.6" +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" dependencies = [ - "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "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.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" dependencies = [ - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" - -[metadata] -"checksum backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a" -"checksum backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "c66d56ac8dabd07f6aacdaf633f4b8262f5b3601a810a0dcddffd5c22c69daa0" -"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" -"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" -"checksum bytecount 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f861d9ce359f56dbcb6e0c2a1cb84e52ad732cadb57b806adeb3c7668caccbd8" -"checksum cargo_metadata 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1efca0b863ca03ed4c109fb1c55e0bc4bbeb221d3e103d86251046b06a526bd0" -"checksum cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "f159dfd43363c4d08055a07703eb7a3406b0dac4d0584d96965a3262db3c9d16" -"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4" -"checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3" -"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" -"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" -"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" -"checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" -"checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d" -"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" -"checksum num 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cf4825417e1e1406b3782a8ce92f4d53f26ec055e3622e1881ca8e9f5f9e08db" -"checksum num-complex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "107b9be86cd2481930688277b675b0114578227f034674726605b8a482d8baf8" -"checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" -"checksum num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "af3fdbbc3291a5464dc57b03860ec37ca6bf915ed6ee385e7c6c052c422b2124" -"checksum num-rational 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e96f040177bb3da242b5b1ecf3f54b5d5af3efbbfb18608977a5d2767b22f10" -"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" -"checksum proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)" = "3d7b7eaaa90b4a90a932a9ea6666c95a389e424eff347f0f793979289429feee" -"checksum pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d6fdf85cda6cadfae5428a54661d431330b312bc767ddbc57adbedc24da66e32" -"checksum quote 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "63b5829244f52738cfee93b3a165c1911388675be000c888d2fae620dee8fa5b" -"checksum rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8356f47b32624fef5b3301c1be97e5944ecdd595409cc5da11d05f211db6cfbd" -"checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" -"checksum rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395" -"checksum ryu 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7153dd96dade874ab973e098cb62fcdbb89a03682e46b144fd09550998d4a4a7" -"checksum same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8f20c4be53a8a1ff4c1f1b2bd14570d2f634628709752f0702ecdd2b3f9a5267" -"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)" = "15c141fc7027dd265a47c090bf864cf62b42c4d228bbcf4e51a0c9e2b0d3f7ef" -"checksum serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)" = "225de307c6302bec3898c51ca302fc94a7a1697ef0845fcee6448f33c032249c" -"checksum serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)" = "43344e7ce05d0d8280c5940cabb4964bea626aa58b1ec0e8c73fa2a8512a38ce" -"checksum skeptic 0.13.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c4474d6da9593171bcb086890fc344a3a12783cb24e5b141f8a5d0e43561f4b6" -"checksum syn 0.15.18 (registry+https://github.com/rust-lang/crates.io-index)" = "90c39a061e2f412a9f869540471ab679e85e50c6b05604daf28bc3060f75c430" -"checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" -"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" -"checksum walkdir 2.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0ffb549f212c31e19f3667c55a7f515b983a84aef10fd0a4d1f9c125425115f3" -"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" -"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "afc5508759c5bf4285e61feb862b6083c8480aec864fa17a81fdec6f69b461ab" -"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml index 8ec4aeb..0fc8f2d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright (C) 2014 The 6502-rs Developers +# Copyright (C) 2014-2021 The 6502-rs Developers # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -30,20 +30,21 @@ name = "mos6502" description = "A MOS 6502 Emulator" license = "BSD-3-Clause" -version = "0.1.0" +version = "0.2.0" authors = ["The 6502-rs Developers"] build = "build.rs" +exclude = ["examples/**"] [lib] # This will look in src/lib.rs name = "mos6502" [dependencies] -bitflags = "0.9.1" -log = "0.4.6" +bitflags = "1" +log = "0.4.14" [dependencies.num] -version = "0.2" +version = "0.3" # no_std support default-features = false diff --git a/README.md b/README.md index cb8b539..4362a9a 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,11 @@ # mos6502 -[![Build Status](https://travis-ci.org/mre/mos6502.svg?branch=master)](https://travis-ci.org/mre/mos6502) +![](https://github.com/mre/mos6502/workflows/test/badge.svg) +[![docs.rs](https://docs.rs/mos6502/badge.svg)](https://docs.rs/mos6502) -An emulator for the [MOS 6502 CPU](https://en.wikipedia.org/wiki/MOS_Technology_6502) written in Rust. -This started off as a fork of [6502-rs](https://github.com/amw-zero/6502-rs), -which seems to be unmaintained at this point. +An emulator for the [MOS 6502 CPU](https://en.wikipedia.org/wiki/MOS_Technology_6502) written in Rust. +This started off as a fork of [amw-zero/6502-rs](https://github.com/amw-zero/6502-rs), +which seems to be [unmaintained](https://github.com/amw-zero/6502-rs/pull/36) at this point. It builds with the latest stable Rust and supports `#[no_std]` targets. (See `no-std-example` folder for more info.) @@ -12,106 +13,44 @@ It builds with the latest stable Rust and supports `#[no_std]` targets. (See `no ```rust extern crate mos6502; - -use mos6502::cpu; use mos6502::address::Address; +use mos6502::cpu; fn main() { - let mut cpu = cpu::CPU::new(); - let zero_page_data = [ - // ZeroPage data start - 0x00, - 0x02, // ADC ZeroPage target - 0x00, - 0x04, // ADC ZeroPageX target - 0x00, - 0x00, - 0x00, - 0x00, - 0x10, // ADC IndexedIndirectX address - 0x80, // ADC IndexedIndirectX address - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x08, // ADC IndirectIndexedY address - 0x80, // ADC IndirectIndexedY address - ]; + let zero_page_data = [56, 49]; let program = [ - // Code start - 0xA9, // LDA Immediate - 0x01, // Immediate operand - 0x69, // ADC Immediate - 0x07, // Immediate operand - 0x65, // ADC ZeroPage - 0x01, // ZeroPage operand - 0xA2, // LDX Immediate - 0x01, // Immediate operand - 0x75, // ADC ZeroPageX - 0x02, // ZeroPageX operand - 0x6D, // ADC Absolute - 0x01, // Absolute operand - 0x80, // Absolute operand - 0xA2, // LDX immediate - 0x08, // Immediate operand - 0x7D, // ADC AbsoluteX - 0x00, // AbsoluteX operand - 0x80, // AbsoluteX operand - 0xA0, // LDY immediate - 0x04, // Immediate operand - 0x79, // ADC AbsoluteY - 0x00, // AbsoluteY operand - 0x80, // AbsoluteY operand - 0xA2, // LDX immediate - 0x05, // Immediate operand - 0x61, // ADC IndexedIndirectX - 0x03, // IndexedIndirectX operand - 0xA0, // LDY immediate - 0x10, // Immediate operand - 0x71, // ADC IndirectIndexedY - 0x0F, // IndirectIndexedY operand - 0xEA, // NOP :) - 0xFF, // Something invalid -- the end! + // (F)irst | (S)econd + // .algo + 0xa5, 0x00, // Load from F to A + // .algo_ + 0x38, // Set carry flag + 0xe5, 0x01, // Substract S from number in A (from F) + 0xf0, 0x07, // Jump to .end if diff is zero + 0x30, 0x08, // Jump to .swap if diff is negative + 0x85, 0x00, // Load A to F + 0x4c, 0x12, 0x00, // Jump to .algo_ + // .end + 0xa5, 0x00, // Load from S to A + 0xff, + // .swap + 0xa6, 0x00, // load F to X + 0xa4, 0x01, // load S to Y + 0x86, 0x01, // Store X to F + 0x84, 0x00, // Store Y to S + 0x4c, 0x10, 0x00, // Jump to .algo ]; - let data: [u8; 25] = [ - 0x00, - 0x09, // ADC Absolute target - 0x00, - 0x00, - 0x40, // ADC AbsoluteY target - 0x00, - 0x00, - 0x00, - 0x11, // ADC AbsoluteX target - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x12, // ADC IndexedIndirectX target - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x06, // ADC IndirectIndexedY target - ]; + let mut cpu = cpu::CPU::new(); - // "Load" a program - cpu.memory.set_bytes(Address(0x0000), &zero_page_data); - cpu.memory.set_bytes(Address(0x4000), &program); - cpu.memory.set_bytes(Address(0x8000), &data); - - cpu.registers.program_counter = Address(0x4000); + cpu.memory.set_bytes(Address(0x00), &zero_page_data); + cpu.memory.set_bytes(Address(0x10), &program); + cpu.registers.program_counter = Address(0x10); cpu.run(); + + assert_eq!(7, cpu.registers.accumulator); + } ``` diff --git a/examples/euclidean_algo.rs b/examples/euclidean_algo.rs new file mode 100644 index 0000000..2344ccd --- /dev/null +++ b/examples/euclidean_algo.rs @@ -0,0 +1,47 @@ +extern crate mos6502; +use mos6502::address::Address; +use mos6502::cpu; + +fn main() { + println!("Enter two numbers (< 128) to know their GCD:"); + let mut input = String::new(); + std::io::stdin().read_line(&mut input).unwrap(); + + let zero_page_data = input + .trim() + .split(' ') + .map(|s| s.parse::().unwrap()) + .collect::>(); + + let program = [ + // (F)irst | (S)econd + // .algo + 0xa5, 0x00, // Load from F to A + // .algo_ + 0x38, // Set carry flag + 0xe5, 0x01, // Substract S from number in A (from F) + 0xf0, 0x07, // Jump to .end if diff is zero + 0x30, 0x08, // Jump to .swap if diff is negative + 0x85, 0x00, // Load A to F + 0x4c, 0x12, 0x00, // Jump to .algo_ + // .end + 0xa5, 0x00, // Load from S to A + 0xff, + // .swap + 0xa6, 0x00, // load F to X + 0xa4, 0x01, // load S to Y + 0x86, 0x01, // Store X to F + 0x84, 0x00, // Store Y to S + 0x4c, 0x10, 0x00, // Jump to .algo + ]; + + let mut cpu = cpu::CPU::new(); + + cpu.memory.set_bytes(Address(0x00), &zero_page_data); + cpu.memory.set_bytes(Address(0x10), &program); + cpu.registers.program_counter = Address(0x10); + + cpu.run(); + + println!("GCD is {:?}", cpu.registers.accumulator); +} diff --git a/src/bin/mos6502.rs b/examples/mos6502.rs similarity index 100% rename from src/bin/mos6502.rs rename to examples/mos6502.rs diff --git a/src/address.rs b/src/address.rs index b3a00f4..98a1c19 100644 --- a/src/address.rs +++ b/src/address.rs @@ -73,21 +73,19 @@ impl Add for Address { } impl Address { - pub fn to_u16(&self) -> u16 { - match *self { - Address(address_) => address_, - } + pub fn to_u16(self) -> u16 { + self.0 } - pub fn to_usize(&self) -> usize { + pub fn to_usize(self) -> usize { self.to_u16() as usize } - pub fn get_page_number(&self) -> u8 { + pub fn get_page_number(self) -> u8 { (self.to_u16() & 0xff00 >> 8) as u8 } - pub fn get_offset(&self) -> u8 { + pub fn get_offset(self) -> u8 { (self.to_u16() & 0x00ff) as u8 } } diff --git a/src/cpu.rs b/src/cpu.rs index b4a6e55..f6e3f62 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -30,9 +30,6 @@ use instruction; use instruction::{DecodedInstr, Instruction, OpInput}; use memory::Memory; use registers::{Registers, StackPointer, Status, StatusArgs}; -use registers::{ - PS_CARRY, PS_DECIMAL_MODE, PS_DISABLE_INTERRUPTS, PS_NEGATIVE, PS_OVERFLOW, PS_ZERO, -}; #[derive(Clone)] pub struct CPU { @@ -40,6 +37,12 @@ pub struct CPU { pub memory: Memory, } +impl Default for CPU { + fn default() -> Self { + Self::new() + } +} + impl CPU { pub fn new() -> CPU { CPU { @@ -137,7 +140,7 @@ impl CPU { let bit6 = 0 != (0x40 & res); self.registers.status.set_with_mask( - PS_ZERO | PS_NEGATIVE | PS_OVERFLOW, + Status::PS_ZERO | Status::PS_NEGATIVE | Status::PS_OVERFLOW, Status::new(StatusArgs { zero: is_zero, negative: bit7, @@ -169,16 +172,16 @@ impl CPU { } (Instruction::CLC, OpInput::UseImplied) => { - self.registers.status.and(!PS_CARRY); + self.registers.status.and(!Status::PS_CARRY); } (Instruction::CLD, OpInput::UseImplied) => { - self.registers.status.and(!PS_DECIMAL_MODE); + self.registers.status.and(!Status::PS_DECIMAL_MODE); } (Instruction::CLI, OpInput::UseImplied) => { - self.registers.status.and(!PS_DISABLE_INTERRUPTS); + self.registers.status.and(!Status::PS_DISABLE_INTERRUPTS); } (Instruction::CLV, OpInput::UseImplied) => { - self.registers.status.and(!PS_OVERFLOW); + self.registers.status.and(!Status::PS_OVERFLOW); } (Instruction::CMP, OpInput::UseImmediate(val)) => { @@ -348,13 +351,13 @@ impl CPU { } (Instruction::SEC, OpInput::UseImplied) => { - self.registers.status.or(PS_CARRY); + self.registers.status.or(Status::PS_CARRY); } (Instruction::SED, OpInput::UseImplied) => { - self.registers.status.or(PS_DECIMAL_MODE); + self.registers.status.or(Status::PS_DECIMAL_MODE); } (Instruction::SEI, OpInput::UseImplied) => { - self.registers.status.or(PS_DISABLE_INTERRUPTS); + self.registers.status.or(Status::PS_DISABLE_INTERRUPTS); } (Instruction::STA, OpInput::UseAddress(addr)) => { @@ -419,7 +422,7 @@ impl CPU { let is_negative = value < 0; status.set_with_mask( - PS_ZERO | PS_NEGATIVE, + Status::PS_ZERO | Status::PS_NEGATIVE, Status::new(StatusArgs { zero: is_zero, negative: is_negative, @@ -434,7 +437,7 @@ impl CPU { let shifted = (*p_val & !(1 << 7)) << 1; *p_val = shifted; status.set_with_mask( - PS_CARRY, + Status::PS_CARRY, Status::new(StatusArgs { carry: is_bit_7_set, ..StatusArgs::none() @@ -448,7 +451,7 @@ impl CPU { let is_bit_0_set = (*p_val & mask) == mask; *p_val >>= 1; status.set_with_mask( - PS_CARRY, + Status::PS_CARRY, Status::new(StatusArgs { carry: is_bit_0_set, ..StatusArgs::none() @@ -458,13 +461,13 @@ impl CPU { } fn rotate_left_with_flags(p_val: &mut u8, status: &mut Status) { - let is_carry_set = status.contains(PS_CARRY); + let is_carry_set = status.contains(Status::PS_CARRY); let mask = 1 << 7; let is_bit_7_set = (*p_val & mask) == mask; let shifted = (*p_val & !(1 << 7)) << 1; *p_val = shifted + if is_carry_set { 1 } else { 0 }; status.set_with_mask( - PS_CARRY, + Status::PS_CARRY, Status::new(StatusArgs { carry: is_bit_7_set, ..StatusArgs::none() @@ -474,13 +477,13 @@ impl CPU { } fn rotate_right_with_flags(p_val: &mut u8, status: &mut Status) { - let is_carry_set = status.contains(PS_CARRY); + let is_carry_set = status.contains(Status::PS_CARRY); let mask = 1; let is_bit_0_set = (*p_val & mask) == mask; let shifted = *p_val >> 1; *p_val = shifted + if is_carry_set { 1 << 7 } else { 0 }; status.set_with_mask( - PS_CARRY, + Status::PS_CARRY, Status::new(StatusArgs { carry: is_bit_0_set, ..StatusArgs::none() @@ -519,43 +522,56 @@ impl CPU { } fn add_with_carry(&mut self, value: i8) { - if self.registers.status.contains(PS_DECIMAL_MODE) { - // TODO akeeton: Implement binary-coded decimal. - debug!("binary-coded decimal not implemented for add_with_carry"); + let a_before: i8 = self.registers.accumulator; + let c_before: i8 = if self.registers.status.contains(Status::PS_CARRY) { + 1 } else { - let a_before: i8 = self.registers.accumulator; - let c_before: i8 = if self.registers.status.contains(PS_CARRY) { - 1 - } else { - 0 - }; - let a_after: i8 = a_before.wrapping_add(c_before).wrapping_add(value); + 0 + }; + let a_after: i8 = a_before.wrapping_add(c_before).wrapping_add(value); - debug_assert_eq!( - a_after as u8, - a_before.wrapping_add(c_before).wrapping_add(value) as u8 - ); + debug_assert_eq!( + a_after as u8, + a_before.wrapping_add(c_before).wrapping_add(value) as u8 + ); - let did_carry = (a_after as u8) < (a_before as u8); + let bcd1: i8 = if (a_after & 0x0f) as u8 > 0x09 { + 0x06 + } else { + 0x00 + }; - let did_overflow = (a_before < 0 && value < 0 && a_after >= 0) - || (a_before > 0 && value > 0 && a_after <= 0); + let bcd2: i8 = if (a_after.wrapping_add(bcd1) as u8 & 0xf0) as u8 > 0x90 { + 0x60 + } else { + 0x00 + }; - let mask = PS_CARRY | PS_OVERFLOW; + let result: i8 = if self.registers.status.contains(Status::PS_DECIMAL_MODE) { + a_after.wrapping_add(bcd1).wrapping_add(bcd2) + } else { + a_after + }; - self.registers.status.set_with_mask( - mask, - Status::new(StatusArgs { - carry: did_carry, - overflow: did_overflow, - ..StatusArgs::none() - }), - ); + let did_carry = (result as u8) < (a_before as u8); - self.load_accumulator(a_after); + let did_overflow = (a_before < 0 && value < 0 && a_after >= 0) + || (a_before > 0 && value > 0 && a_after <= 0); - debug!("accumulator: {}", self.registers.accumulator); - } + let mask = Status::PS_CARRY | Status::PS_OVERFLOW; + + self.registers.status.set_with_mask( + mask, + Status::new(StatusArgs { + carry: did_carry, + overflow: did_overflow, + ..StatusArgs::none() + }), + ); + + self.load_accumulator(result); + + debug!("accumulator: {}", self.registers.accumulator); } fn and(&mut self, value: i8) { @@ -563,56 +579,66 @@ impl CPU { self.load_accumulator(a_after); } - // TODO: Implement binary-coded decimal fn subtract_with_carry(&mut self, value: i8) { - if self.registers.status.contains(PS_DECIMAL_MODE) { - debug!( - "binary-coded decimal not implemented for \ - subtract_with_carry" - ); + // A - M - (1 - C) + + // nc -- 'not carry' + let nc: i8 = if self.registers.status.contains(Status::PS_CARRY) { + 0 } else { - // A - M - (1 - C) + 1 + }; - // nc -- 'not carry' - let nc: i8 = if self.registers.status.contains(PS_CARRY) { - 0 - } else { - 1 - }; + let a_before: i8 = self.registers.accumulator; - let a_before: i8 = self.registers.accumulator; + let a_after = a_before.wrapping_sub(value).wrapping_sub(nc); - let a_after = a_before.wrapping_sub(value).wrapping_sub(nc); + // The overflow flag is set on two's-complement overflow. + // + // range of A is -128 to 127 + // range of - M - (1 - C) is -128 to 128 + // -(127 + 1) to -(-128 + 0) + // + let over = + ((nc == 0 && value < 0) || (nc == 1 && value < -1)) && a_before >= 0 && a_after < 0; - // The carry flag is set on unsigned overflow. - let did_carry = (a_after as u8) > (a_before as u8); + let under = (a_before < 0) && (-value - nc < 0) && a_after >= 0; - // The overflow flag is set on two's-complement overflow. - // - // range of A is -128 to 127 - // range of - M - (1 - C) is -128 to 128 - // -(127 + 1) to -(-128 + 0) - // - let over = - ((nc == 0 && value < 0) || (nc == 1 && value < -1)) && a_before >= 0 && a_after < 0; + let did_overflow = over || under; - let under = (a_before < 0) && (-value - nc < 0) && a_after >= 0; + let mask = Status::PS_CARRY | Status::PS_OVERFLOW; - let did_overflow = over || under; + let bcd1: i8 = if (a_before & 0x0f).wrapping_sub(nc) < (value & 0x0f) { + 0x06 + } else { + 0x00 + }; - let mask = PS_CARRY | PS_OVERFLOW; + let bcd2: i8 = if (a_after.wrapping_sub(bcd1) as u8 & 0xf0) as u8 > 0x90 { + 0x60 + } else { + 0x00 + }; - self.registers.status.set_with_mask( - mask, - Status::new(StatusArgs { - carry: did_carry, - overflow: did_overflow, - ..StatusArgs::none() - }), - ); + let result: i8 = if self.registers.status.contains(Status::PS_DECIMAL_MODE) { + a_after.wrapping_sub(bcd1).wrapping_sub(bcd2) + } else { + a_after + }; - self.load_accumulator(a_after); - } + // The carry flag is set on unsigned overflow. + let did_carry = (result as u8) > (a_before as u8); + + self.registers.status.set_with_mask( + mask, + Status::new(StatusArgs { + carry: did_carry, + overflow: did_overflow, + ..StatusArgs::none() + }), + ); + + self.load_accumulator(result); } fn decrement_memory(&mut self, addr: Address) { @@ -624,7 +650,7 @@ impl CPU { let is_zero = value_new == 0; self.registers.status.set_with_mask( - PS_NEGATIVE | PS_ZERO, + Status::PS_NEGATIVE | Status::PS_ZERO, Status::new(StatusArgs { negative: is_negative, zero: is_zero, @@ -643,43 +669,43 @@ impl CPU { } fn branch_if_carry_clear(&mut self, addr: Address) { - if !self.registers.status.contains(PS_CARRY) { + if !self.registers.status.contains(Status::PS_CARRY) { self.registers.program_counter = addr; } } fn branch_if_carry_set(&mut self, addr: Address) { - if self.registers.status.contains(PS_CARRY) { + if self.registers.status.contains(Status::PS_CARRY) { self.registers.program_counter = addr; } } fn branch_if_equal(&mut self, addr: Address) { - if self.registers.status.contains(PS_ZERO) { + if self.registers.status.contains(Status::PS_ZERO) { self.registers.program_counter = addr; } } fn branch_if_minus(&mut self, addr: Address) { - if self.registers.status.contains(PS_NEGATIVE) { + if self.registers.status.contains(Status::PS_NEGATIVE) { self.registers.program_counter = addr; } } fn branch_if_positive(&mut self, addr: Address) { - if !self.registers.status.contains(PS_NEGATIVE) { + if !self.registers.status.contains(Status::PS_NEGATIVE) { self.registers.program_counter = addr; } } fn branch_if_overflow_clear(&mut self, addr: Address) { - if !self.registers.status.contains(PS_OVERFLOW) { + if !self.registers.status.contains(Status::PS_OVERFLOW) { self.registers.program_counter = addr; } } fn branch_if_overflow_set(&mut self, addr: Address) { - if self.registers.status.contains(PS_OVERFLOW) { + if self.registers.status.contains(Status::PS_OVERFLOW) { self.registers.program_counter = addr; } } @@ -693,22 +719,22 @@ impl CPU { // The N flag contains most significant bit of the subtraction result. fn compare(&mut self, r: i8, val: u8) { if r as u8 >= val as u8 { - self.registers.status.insert(PS_CARRY); + self.registers.status.insert(Status::PS_CARRY); } else { - self.registers.status.remove(PS_CARRY); + self.registers.status.remove(Status::PS_CARRY); } if r as i8 == val as i8 { - self.registers.status.insert(PS_ZERO); + self.registers.status.insert(Status::PS_ZERO); } else { - self.registers.status.remove(PS_ZERO); + self.registers.status.remove(Status::PS_ZERO); } let diff: i8 = r.wrapping_sub(val as i8); if diff < 0 { - self.registers.status.insert(PS_NEGATIVE); + self.registers.status.insert(Status::PS_NEGATIVE); } else { - self.registers.status.remove(PS_NEGATIVE); + self.registers.status.remove(Status::PS_NEGATIVE); } } @@ -769,77 +795,126 @@ mod tests { use super::*; use num::range_inclusive; + #[test] + fn decimal_add_test() { + let mut cpu = CPU::new(); + cpu.registers.status.or(Status::PS_DECIMAL_MODE); + + cpu.add_with_carry(0x09); + assert_eq!(cpu.registers.accumulator, 0x09); + assert_eq!(cpu.registers.status.contains(Status::PS_CARRY), false); + assert_eq!(cpu.registers.status.contains(Status::PS_ZERO), false); + assert_eq!(cpu.registers.status.contains(Status::PS_NEGATIVE), false); + assert_eq!(cpu.registers.status.contains(Status::PS_OVERFLOW), false); + + cpu.add_with_carry(0x43); + assert_eq!(cpu.registers.accumulator, 0x52); + assert_eq!(cpu.registers.status.contains(Status::PS_CARRY), false); + assert_eq!(cpu.registers.status.contains(Status::PS_ZERO), false); + assert_eq!(cpu.registers.status.contains(Status::PS_NEGATIVE), false); + assert_eq!(cpu.registers.status.contains(Status::PS_OVERFLOW), false); + + cpu.add_with_carry(0x48); + assert_eq!(cpu.registers.accumulator, 0x00); + assert_eq!(cpu.registers.status.contains(Status::PS_CARRY), true); + assert_eq!(cpu.registers.status.contains(Status::PS_ZERO), true); + assert_eq!(cpu.registers.status.contains(Status::PS_NEGATIVE), false); + assert_eq!(cpu.registers.status.contains(Status::PS_OVERFLOW), true); + } + + #[test] + fn decimal_subtract_test() { + let mut cpu = CPU::new(); + cpu.registers + .status + .or(Status::PS_DECIMAL_MODE | Status::PS_CARRY); + cpu.registers.accumulator = 0; + + cpu.subtract_with_carry(0x48); + assert_eq!(cpu.registers.accumulator as u8, 0x52); + assert_eq!(cpu.registers.status.contains(Status::PS_CARRY), true); + assert_eq!(cpu.registers.status.contains(Status::PS_ZERO), false); + assert_eq!(cpu.registers.status.contains(Status::PS_NEGATIVE), false); + assert_eq!(cpu.registers.status.contains(Status::PS_OVERFLOW), false); + + cpu.subtract_with_carry(0x43); + assert_eq!(cpu.registers.status.contains(Status::PS_CARRY), false); + assert_eq!(cpu.registers.status.contains(Status::PS_ZERO), false); + assert_eq!(cpu.registers.status.contains(Status::PS_NEGATIVE), false); + assert_eq!(cpu.registers.status.contains(Status::PS_OVERFLOW), false); + } + #[test] fn add_with_carry_test() { let mut cpu = CPU::new(); cpu.add_with_carry(1); assert_eq!(cpu.registers.accumulator, 1); - assert_eq!(cpu.registers.status.contains(PS_CARRY), false); - assert_eq!(cpu.registers.status.contains(PS_ZERO), false); - assert_eq!(cpu.registers.status.contains(PS_NEGATIVE), false); - assert_eq!(cpu.registers.status.contains(PS_OVERFLOW), false); + assert_eq!(cpu.registers.status.contains(Status::PS_CARRY), false); + assert_eq!(cpu.registers.status.contains(Status::PS_ZERO), false); + assert_eq!(cpu.registers.status.contains(Status::PS_NEGATIVE), false); + assert_eq!(cpu.registers.status.contains(Status::PS_OVERFLOW), false); cpu.add_with_carry(-1); assert_eq!(cpu.registers.accumulator, 0); - assert_eq!(cpu.registers.status.contains(PS_CARRY), true); - assert_eq!(cpu.registers.status.contains(PS_ZERO), true); - assert_eq!(cpu.registers.status.contains(PS_NEGATIVE), false); - assert_eq!(cpu.registers.status.contains(PS_OVERFLOW), false); + assert_eq!(cpu.registers.status.contains(Status::PS_CARRY), true); + assert_eq!(cpu.registers.status.contains(Status::PS_ZERO), true); + assert_eq!(cpu.registers.status.contains(Status::PS_NEGATIVE), false); + assert_eq!(cpu.registers.status.contains(Status::PS_OVERFLOW), false); cpu.add_with_carry(1); assert_eq!(cpu.registers.accumulator, 2); - assert_eq!(cpu.registers.status.contains(PS_CARRY), false); - assert_eq!(cpu.registers.status.contains(PS_ZERO), false); - assert_eq!(cpu.registers.status.contains(PS_NEGATIVE), false); - assert_eq!(cpu.registers.status.contains(PS_OVERFLOW), false); + assert_eq!(cpu.registers.status.contains(Status::PS_CARRY), false); + assert_eq!(cpu.registers.status.contains(Status::PS_ZERO), false); + assert_eq!(cpu.registers.status.contains(Status::PS_NEGATIVE), false); + assert_eq!(cpu.registers.status.contains(Status::PS_OVERFLOW), false); let mut cpu = CPU::new(); cpu.add_with_carry(127); assert_eq!(cpu.registers.accumulator, 127); - assert_eq!(cpu.registers.status.contains(PS_CARRY), false); - assert_eq!(cpu.registers.status.contains(PS_ZERO), false); - assert_eq!(cpu.registers.status.contains(PS_NEGATIVE), false); - assert_eq!(cpu.registers.status.contains(PS_OVERFLOW), false); + assert_eq!(cpu.registers.status.contains(Status::PS_CARRY), false); + assert_eq!(cpu.registers.status.contains(Status::PS_ZERO), false); + assert_eq!(cpu.registers.status.contains(Status::PS_NEGATIVE), false); + assert_eq!(cpu.registers.status.contains(Status::PS_OVERFLOW), false); cpu.add_with_carry(-127); assert_eq!(cpu.registers.accumulator, 0); - assert_eq!(cpu.registers.status.contains(PS_CARRY), true); - assert_eq!(cpu.registers.status.contains(PS_ZERO), true); - assert_eq!(cpu.registers.status.contains(PS_NEGATIVE), false); - assert_eq!(cpu.registers.status.contains(PS_OVERFLOW), false); + assert_eq!(cpu.registers.status.contains(Status::PS_CARRY), true); + assert_eq!(cpu.registers.status.contains(Status::PS_ZERO), true); + assert_eq!(cpu.registers.status.contains(Status::PS_NEGATIVE), false); + assert_eq!(cpu.registers.status.contains(Status::PS_OVERFLOW), false); - cpu.registers.status.remove(PS_CARRY); + cpu.registers.status.remove(Status::PS_CARRY); cpu.add_with_carry(-128); assert_eq!(cpu.registers.accumulator, -128); - assert_eq!(cpu.registers.status.contains(PS_CARRY), false); - assert_eq!(cpu.registers.status.contains(PS_ZERO), false); - assert_eq!(cpu.registers.status.contains(PS_NEGATIVE), true); - assert_eq!(cpu.registers.status.contains(PS_OVERFLOW), false); + assert_eq!(cpu.registers.status.contains(Status::PS_CARRY), false); + assert_eq!(cpu.registers.status.contains(Status::PS_ZERO), false); + assert_eq!(cpu.registers.status.contains(Status::PS_NEGATIVE), true); + assert_eq!(cpu.registers.status.contains(Status::PS_OVERFLOW), false); cpu.add_with_carry(127); assert_eq!(cpu.registers.accumulator, -1); - assert_eq!(cpu.registers.status.contains(PS_CARRY), false); - assert_eq!(cpu.registers.status.contains(PS_ZERO), false); - assert_eq!(cpu.registers.status.contains(PS_NEGATIVE), true); - assert_eq!(cpu.registers.status.contains(PS_OVERFLOW), false); + assert_eq!(cpu.registers.status.contains(Status::PS_CARRY), false); + assert_eq!(cpu.registers.status.contains(Status::PS_ZERO), false); + assert_eq!(cpu.registers.status.contains(Status::PS_NEGATIVE), true); + assert_eq!(cpu.registers.status.contains(Status::PS_OVERFLOW), false); let mut cpu = CPU::new(); cpu.add_with_carry(127); assert_eq!(cpu.registers.accumulator, 127); - assert_eq!(cpu.registers.status.contains(PS_CARRY), false); - assert_eq!(cpu.registers.status.contains(PS_ZERO), false); - assert_eq!(cpu.registers.status.contains(PS_NEGATIVE), false); - assert_eq!(cpu.registers.status.contains(PS_OVERFLOW), false); + assert_eq!(cpu.registers.status.contains(Status::PS_CARRY), false); + assert_eq!(cpu.registers.status.contains(Status::PS_ZERO), false); + assert_eq!(cpu.registers.status.contains(Status::PS_NEGATIVE), false); + assert_eq!(cpu.registers.status.contains(Status::PS_OVERFLOW), false); cpu.add_with_carry(1); assert_eq!(cpu.registers.accumulator, -128); - assert_eq!(cpu.registers.status.contains(PS_CARRY), false); - assert_eq!(cpu.registers.status.contains(PS_ZERO), false); - assert_eq!(cpu.registers.status.contains(PS_NEGATIVE), true); - assert_eq!(cpu.registers.status.contains(PS_OVERFLOW), true); + assert_eq!(cpu.registers.status.contains(Status::PS_CARRY), false); + assert_eq!(cpu.registers.status.contains(Status::PS_ZERO), false); + assert_eq!(cpu.registers.status.contains(Status::PS_NEGATIVE), true); + assert_eq!(cpu.registers.status.contains(Status::PS_OVERFLOW), true); } #[test] @@ -849,26 +924,26 @@ mod tests { cpu.registers.accumulator = 0; cpu.and(-1); assert_eq!(cpu.registers.accumulator, 0); - assert_eq!(cpu.registers.status.contains(PS_ZERO), true); - assert_eq!(cpu.registers.status.contains(PS_NEGATIVE), false); + assert_eq!(cpu.registers.status.contains(Status::PS_ZERO), true); + assert_eq!(cpu.registers.status.contains(Status::PS_NEGATIVE), false); cpu.registers.accumulator = -1; cpu.and(0); assert_eq!(cpu.registers.accumulator, 0); - assert_eq!(cpu.registers.status.contains(PS_ZERO), true); - assert_eq!(cpu.registers.status.contains(PS_NEGATIVE), false); + assert_eq!(cpu.registers.status.contains(Status::PS_ZERO), true); + assert_eq!(cpu.registers.status.contains(Status::PS_NEGATIVE), false); cpu.registers.accumulator = -1; cpu.and(0x0f); assert_eq!(cpu.registers.accumulator, 0x0f); - assert_eq!(cpu.registers.status.contains(PS_ZERO), false); - assert_eq!(cpu.registers.status.contains(PS_NEGATIVE), false); + assert_eq!(cpu.registers.status.contains(Status::PS_ZERO), false); + assert_eq!(cpu.registers.status.contains(Status::PS_NEGATIVE), false); cpu.registers.accumulator = -1; cpu.and(-128); assert_eq!(cpu.registers.accumulator, -128); - assert_eq!(cpu.registers.status.contains(PS_ZERO), false); - assert_eq!(cpu.registers.status.contains(PS_NEGATIVE), true); + assert_eq!(cpu.registers.status.contains(Status::PS_ZERO), false); + assert_eq!(cpu.registers.status.contains(Status::PS_NEGATIVE), true); } #[test] @@ -880,55 +955,55 @@ mod tests { cpu.subtract_with_carry(1); assert_eq!(cpu.registers.accumulator, -1); - assert_eq!(cpu.registers.status.contains(PS_CARRY), true); - assert_eq!(cpu.registers.status.contains(PS_ZERO), false); - assert_eq!(cpu.registers.status.contains(PS_NEGATIVE), true); - assert_eq!(cpu.registers.status.contains(PS_OVERFLOW), false); + assert_eq!(cpu.registers.status.contains(Status::PS_CARRY), true); + assert_eq!(cpu.registers.status.contains(Status::PS_ZERO), false); + assert_eq!(cpu.registers.status.contains(Status::PS_NEGATIVE), true); + assert_eq!(cpu.registers.status.contains(Status::PS_OVERFLOW), false); cpu.execute_instruction((Instruction::SEC, OpInput::UseImplied)); cpu.registers.accumulator = -128; cpu.subtract_with_carry(1); assert_eq!(cpu.registers.accumulator, 127); - assert_eq!(cpu.registers.status.contains(PS_CARRY), false); - assert_eq!(cpu.registers.status.contains(PS_ZERO), false); - assert_eq!(cpu.registers.status.contains(PS_NEGATIVE), false); - assert_eq!(cpu.registers.status.contains(PS_OVERFLOW), true); + assert_eq!(cpu.registers.status.contains(Status::PS_CARRY), false); + assert_eq!(cpu.registers.status.contains(Status::PS_ZERO), false); + assert_eq!(cpu.registers.status.contains(Status::PS_NEGATIVE), false); + assert_eq!(cpu.registers.status.contains(Status::PS_OVERFLOW), true); cpu.execute_instruction((Instruction::SEC, OpInput::UseImplied)); cpu.registers.accumulator = 127; cpu.subtract_with_carry(-1); assert_eq!(cpu.registers.accumulator, -128); - assert_eq!(cpu.registers.status.contains(PS_CARRY), true); - assert_eq!(cpu.registers.status.contains(PS_ZERO), false); - assert_eq!(cpu.registers.status.contains(PS_NEGATIVE), true); - assert_eq!(cpu.registers.status.contains(PS_OVERFLOW), true); + assert_eq!(cpu.registers.status.contains(Status::PS_CARRY), true); + assert_eq!(cpu.registers.status.contains(Status::PS_ZERO), false); + assert_eq!(cpu.registers.status.contains(Status::PS_NEGATIVE), true); + assert_eq!(cpu.registers.status.contains(Status::PS_OVERFLOW), true); cpu.execute_instruction((Instruction::CLC, OpInput::UseImplied)); cpu.registers.accumulator = -64; cpu.subtract_with_carry(64); assert_eq!(cpu.registers.accumulator, 127); - assert_eq!(cpu.registers.status.contains(PS_CARRY), false); - assert_eq!(cpu.registers.status.contains(PS_ZERO), false); - assert_eq!(cpu.registers.status.contains(PS_NEGATIVE), false); - assert_eq!(cpu.registers.status.contains(PS_OVERFLOW), true); + assert_eq!(cpu.registers.status.contains(Status::PS_CARRY), false); + assert_eq!(cpu.registers.status.contains(Status::PS_ZERO), false); + assert_eq!(cpu.registers.status.contains(Status::PS_NEGATIVE), false); + assert_eq!(cpu.registers.status.contains(Status::PS_OVERFLOW), true); cpu.execute_instruction((Instruction::SEC, OpInput::UseImplied)); cpu.registers.accumulator = 0; cpu.subtract_with_carry(-128); assert_eq!(cpu.registers.accumulator, -128); - assert_eq!(cpu.registers.status.contains(PS_CARRY), true); - assert_eq!(cpu.registers.status.contains(PS_ZERO), false); - assert_eq!(cpu.registers.status.contains(PS_NEGATIVE), true); - assert_eq!(cpu.registers.status.contains(PS_OVERFLOW), true); + assert_eq!(cpu.registers.status.contains(Status::PS_CARRY), true); + assert_eq!(cpu.registers.status.contains(Status::PS_ZERO), false); + assert_eq!(cpu.registers.status.contains(Status::PS_NEGATIVE), true); + assert_eq!(cpu.registers.status.contains(Status::PS_OVERFLOW), true); cpu.execute_instruction((Instruction::CLC, OpInput::UseImplied)); cpu.registers.accumulator = 0; cpu.subtract_with_carry(127); assert_eq!(cpu.registers.accumulator, -128); - assert_eq!(cpu.registers.status.contains(PS_CARRY), true); - assert_eq!(cpu.registers.status.contains(PS_ZERO), false); - assert_eq!(cpu.registers.status.contains(PS_NEGATIVE), true); - assert_eq!(cpu.registers.status.contains(PS_OVERFLOW), false); + assert_eq!(cpu.registers.status.contains(Status::PS_CARRY), true); + assert_eq!(cpu.registers.status.contains(Status::PS_ZERO), false); + assert_eq!(cpu.registers.status.contains(Status::PS_NEGATIVE), true); + assert_eq!(cpu.registers.status.contains(Status::PS_OVERFLOW), false); } #[test] @@ -940,25 +1015,25 @@ mod tests { cpu.decrement_memory(addr); assert_eq!(cpu.memory.get_byte(addr), 4); - assert_eq!(cpu.registers.status.contains(PS_ZERO), false); - assert_eq!(cpu.registers.status.contains(PS_NEGATIVE), false); + assert_eq!(cpu.registers.status.contains(Status::PS_ZERO), false); + assert_eq!(cpu.registers.status.contains(Status::PS_NEGATIVE), false); cpu.decrement_memory(addr); assert_eq!(cpu.memory.get_byte(addr), 3); - assert_eq!(cpu.registers.status.contains(PS_ZERO), false); - assert_eq!(cpu.registers.status.contains(PS_NEGATIVE), false); + assert_eq!(cpu.registers.status.contains(Status::PS_ZERO), false); + assert_eq!(cpu.registers.status.contains(Status::PS_NEGATIVE), false); cpu.decrement_memory(addr); cpu.decrement_memory(addr); cpu.decrement_memory(addr); assert_eq!(cpu.memory.get_byte(addr), 0); - assert_eq!(cpu.registers.status.contains(PS_ZERO), true); - assert_eq!(cpu.registers.status.contains(PS_NEGATIVE), false); + assert_eq!(cpu.registers.status.contains(Status::PS_ZERO), true); + assert_eq!(cpu.registers.status.contains(Status::PS_NEGATIVE), false); cpu.decrement_memory(addr); assert_eq!(cpu.memory.get_byte(addr) as i8, -1); - assert_eq!(cpu.registers.status.contains(PS_ZERO), false); - assert_eq!(cpu.registers.status.contains(PS_NEGATIVE), true); + assert_eq!(cpu.registers.status.contains(Status::PS_ZERO), false); + assert_eq!(cpu.registers.status.contains(Status::PS_NEGATIVE), true); } #[test] @@ -969,34 +1044,34 @@ mod tests { cpu.execute_instruction((Instruction::LDA, OpInput::UseImmediate(0))); cpu.execute_instruction((Instruction::LSR, OpInput::UseImplied)); assert_eq!(cpu.registers.accumulator, 0); - assert_eq!(cpu.registers.status.contains(PS_CARRY), false); - assert_eq!(cpu.registers.status.contains(PS_ZERO), true); - assert_eq!(cpu.registers.status.contains(PS_NEGATIVE), false); - assert_eq!(cpu.registers.status.contains(PS_OVERFLOW), false); + assert_eq!(cpu.registers.status.contains(Status::PS_CARRY), false); + assert_eq!(cpu.registers.status.contains(Status::PS_ZERO), true); + assert_eq!(cpu.registers.status.contains(Status::PS_NEGATIVE), false); + assert_eq!(cpu.registers.status.contains(Status::PS_OVERFLOW), false); cpu.execute_instruction((Instruction::LDA, OpInput::UseImmediate(1))); cpu.execute_instruction((Instruction::LSR, OpInput::UseImplied)); assert_eq!(cpu.registers.accumulator, 0); - assert_eq!(cpu.registers.status.contains(PS_CARRY), true); - assert_eq!(cpu.registers.status.contains(PS_ZERO), true); - assert_eq!(cpu.registers.status.contains(PS_NEGATIVE), false); - assert_eq!(cpu.registers.status.contains(PS_OVERFLOW), false); + assert_eq!(cpu.registers.status.contains(Status::PS_CARRY), true); + assert_eq!(cpu.registers.status.contains(Status::PS_ZERO), true); + assert_eq!(cpu.registers.status.contains(Status::PS_NEGATIVE), false); + assert_eq!(cpu.registers.status.contains(Status::PS_OVERFLOW), false); cpu.execute_instruction((Instruction::LDA, OpInput::UseImmediate(255))); cpu.execute_instruction((Instruction::LSR, OpInput::UseImplied)); assert_eq!(cpu.registers.accumulator, 0x7F); - assert_eq!(cpu.registers.status.contains(PS_CARRY), true); - assert_eq!(cpu.registers.status.contains(PS_ZERO), false); - assert_eq!(cpu.registers.status.contains(PS_NEGATIVE), false); - assert_eq!(cpu.registers.status.contains(PS_OVERFLOW), false); + assert_eq!(cpu.registers.status.contains(Status::PS_CARRY), true); + assert_eq!(cpu.registers.status.contains(Status::PS_ZERO), false); + assert_eq!(cpu.registers.status.contains(Status::PS_NEGATIVE), false); + assert_eq!(cpu.registers.status.contains(Status::PS_OVERFLOW), false); cpu.execute_instruction((Instruction::LDA, OpInput::UseImmediate(254))); cpu.execute_instruction((Instruction::LSR, OpInput::UseImplied)); assert_eq!(cpu.registers.accumulator, 0x7F); - assert_eq!(cpu.registers.status.contains(PS_CARRY), false); - assert_eq!(cpu.registers.status.contains(PS_ZERO), false); - assert_eq!(cpu.registers.status.contains(PS_NEGATIVE), false); - assert_eq!(cpu.registers.status.contains(PS_OVERFLOW), false); + assert_eq!(cpu.registers.status.contains(Status::PS_CARRY), false); + assert_eq!(cpu.registers.status.contains(Status::PS_ZERO), false); + assert_eq!(cpu.registers.status.contains(Status::PS_NEGATIVE), false); + assert_eq!(cpu.registers.status.contains(Status::PS_OVERFLOW), false); } #[test] @@ -1005,25 +1080,25 @@ mod tests { cpu.dec_x(); assert_eq!(cpu.registers.index_x, -1); - assert_eq!(cpu.registers.status.contains(PS_CARRY), false); - assert_eq!(cpu.registers.status.contains(PS_ZERO), false); - assert_eq!(cpu.registers.status.contains(PS_NEGATIVE), true); - assert_eq!(cpu.registers.status.contains(PS_OVERFLOW), false); + assert_eq!(cpu.registers.status.contains(Status::PS_CARRY), false); + assert_eq!(cpu.registers.status.contains(Status::PS_ZERO), false); + assert_eq!(cpu.registers.status.contains(Status::PS_NEGATIVE), true); + assert_eq!(cpu.registers.status.contains(Status::PS_OVERFLOW), false); cpu.dec_x(); assert_eq!(cpu.registers.index_x, -2); - assert_eq!(cpu.registers.status.contains(PS_CARRY), false); - assert_eq!(cpu.registers.status.contains(PS_ZERO), false); - assert_eq!(cpu.registers.status.contains(PS_NEGATIVE), true); - assert_eq!(cpu.registers.status.contains(PS_OVERFLOW), false); + assert_eq!(cpu.registers.status.contains(Status::PS_CARRY), false); + assert_eq!(cpu.registers.status.contains(Status::PS_ZERO), false); + assert_eq!(cpu.registers.status.contains(Status::PS_NEGATIVE), true); + assert_eq!(cpu.registers.status.contains(Status::PS_OVERFLOW), false); cpu.load_x_register(5); cpu.dec_x(); assert_eq!(cpu.registers.index_x, 4); - assert_eq!(cpu.registers.status.contains(PS_CARRY), false); - assert_eq!(cpu.registers.status.contains(PS_ZERO), false); - assert_eq!(cpu.registers.status.contains(PS_NEGATIVE), false); - assert_eq!(cpu.registers.status.contains(PS_OVERFLOW), false); + assert_eq!(cpu.registers.status.contains(Status::PS_CARRY), false); + assert_eq!(cpu.registers.status.contains(Status::PS_ZERO), false); + assert_eq!(cpu.registers.status.contains(Status::PS_NEGATIVE), false); + assert_eq!(cpu.registers.status.contains(Status::PS_OVERFLOW), false); cpu.dec_x(); cpu.dec_x(); @@ -1031,17 +1106,17 @@ mod tests { cpu.dec_x(); assert_eq!(cpu.registers.index_x, 0); - assert_eq!(cpu.registers.status.contains(PS_CARRY), false); - assert_eq!(cpu.registers.status.contains(PS_ZERO), true); - assert_eq!(cpu.registers.status.contains(PS_NEGATIVE), false); - assert_eq!(cpu.registers.status.contains(PS_OVERFLOW), false); + assert_eq!(cpu.registers.status.contains(Status::PS_CARRY), false); + assert_eq!(cpu.registers.status.contains(Status::PS_ZERO), true); + assert_eq!(cpu.registers.status.contains(Status::PS_NEGATIVE), false); + assert_eq!(cpu.registers.status.contains(Status::PS_OVERFLOW), false); cpu.dec_x(); assert_eq!(cpu.registers.index_x, -1); - assert_eq!(cpu.registers.status.contains(PS_CARRY), false); - assert_eq!(cpu.registers.status.contains(PS_ZERO), false); - assert_eq!(cpu.registers.status.contains(PS_NEGATIVE), true); - assert_eq!(cpu.registers.status.contains(PS_OVERFLOW), false); + assert_eq!(cpu.registers.status.contains(Status::PS_CARRY), false); + assert_eq!(cpu.registers.status.contains(Status::PS_ZERO), false); + assert_eq!(cpu.registers.status.contains(Status::PS_NEGATIVE), true); + assert_eq!(cpu.registers.status.contains(Status::PS_OVERFLOW), false); } #[test] @@ -1086,7 +1161,7 @@ mod tests { cpu.branch_if_equal(Address(0xABCD)); assert_eq!(cpu.registers.program_counter, Address(0)); - cpu.registers.status.or(PS_ZERO); + cpu.registers.status.or(Status::PS_ZERO); cpu.branch_if_equal(Address(0xABCD)); assert_eq!(cpu.registers.program_counter, Address(0xABCD)); } @@ -1105,7 +1180,7 @@ mod tests { { let mut cpu = CPU::new(); - cpu.registers.status.or(PS_NEGATIVE); + cpu.registers.status.or(Status::PS_NEGATIVE); let registers_before = cpu.registers; cpu.branch_if_minus(Address(0xABCD)); @@ -1118,11 +1193,11 @@ mod tests { fn branch_if_positive_test() { let mut cpu = CPU::new(); - cpu.registers.status.insert(PS_NEGATIVE); + cpu.registers.status.insert(Status::PS_NEGATIVE); cpu.branch_if_positive(Address(0xABCD)); assert_eq!(cpu.registers.program_counter, Address(0)); - cpu.registers.status.remove(PS_NEGATIVE); + cpu.registers.status.remove(Status::PS_NEGATIVE); cpu.branch_if_positive(Address(0xABCD)); assert_eq!(cpu.registers.program_counter, Address(0xABCD)); } @@ -1131,11 +1206,11 @@ mod tests { fn branch_if_overflow_clear_test() { let mut cpu = CPU::new(); - cpu.registers.status.insert(PS_OVERFLOW); + cpu.registers.status.insert(Status::PS_OVERFLOW); cpu.branch_if_overflow_clear(Address(0xABCD)); assert_eq!(cpu.registers.program_counter, Address(0)); - cpu.registers.status.remove(PS_OVERFLOW); + cpu.registers.status.remove(Status::PS_OVERFLOW); cpu.branch_if_overflow_clear(Address(0xABCD)); assert_eq!(cpu.registers.program_counter, Address(0xABCD)); } @@ -1147,7 +1222,7 @@ mod tests { cpu.branch_if_overflow_set(Address(0xABCD)); assert_eq!(cpu.registers.program_counter, Address(0)); - cpu.registers.status.insert(PS_OVERFLOW); + cpu.registers.status.insert(Status::PS_OVERFLOW); cpu.branch_if_overflow_set(Address(0xABCD)); assert_eq!(cpu.registers.program_counter, Address(0xABCD)); } @@ -1162,44 +1237,44 @@ mod tests { cpu.execute_instruction((load_instruction, OpInput::UseImmediate(127))); compare(&mut cpu, 127); - assert!(cpu.registers.status.contains(PS_ZERO)); - assert!(cpu.registers.status.contains(PS_CARRY)); - assert!(!cpu.registers.status.contains(PS_NEGATIVE)); + assert!(cpu.registers.status.contains(Status::PS_ZERO)); + assert!(cpu.registers.status.contains(Status::PS_CARRY)); + assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE)); cpu.execute_instruction((load_instruction, OpInput::UseImmediate(127))); compare(&mut cpu, 1); - assert!(!cpu.registers.status.contains(PS_ZERO)); - assert!(cpu.registers.status.contains(PS_CARRY)); - assert!(!cpu.registers.status.contains(PS_NEGATIVE)); + assert!(!cpu.registers.status.contains(Status::PS_ZERO)); + assert!(cpu.registers.status.contains(Status::PS_CARRY)); + assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE)); cpu.execute_instruction((load_instruction, OpInput::UseImmediate(1))); compare(&mut cpu, 2); - assert!(!cpu.registers.status.contains(PS_ZERO)); - assert!(!cpu.registers.status.contains(PS_CARRY)); - assert!(cpu.registers.status.contains(PS_NEGATIVE)); + assert!(!cpu.registers.status.contains(Status::PS_ZERO)); + assert!(!cpu.registers.status.contains(Status::PS_CARRY)); + assert!(cpu.registers.status.contains(Status::PS_NEGATIVE)); cpu.execute_instruction((load_instruction, OpInput::UseImmediate(20))); compare(&mut cpu, -50i8 as u8); - assert!(!cpu.registers.status.contains(PS_ZERO)); - assert!(!cpu.registers.status.contains(PS_CARRY)); - assert!(!cpu.registers.status.contains(PS_NEGATIVE)); + assert!(!cpu.registers.status.contains(Status::PS_ZERO)); + assert!(!cpu.registers.status.contains(Status::PS_CARRY)); + assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE)); cpu.execute_instruction((load_instruction, OpInput::UseImmediate(1))); compare(&mut cpu, -1i8 as u8); - assert!(!cpu.registers.status.contains(PS_ZERO)); - assert!(!cpu.registers.status.contains(PS_CARRY)); - assert!(!cpu.registers.status.contains(PS_NEGATIVE)); + assert!(!cpu.registers.status.contains(Status::PS_ZERO)); + assert!(!cpu.registers.status.contains(Status::PS_CARRY)); + assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE)); cpu.execute_instruction((load_instruction, OpInput::UseImmediate(127))); compare(&mut cpu, -128i8 as u8); - assert!(!cpu.registers.status.contains(PS_ZERO)); - assert!(!cpu.registers.status.contains(PS_CARRY)); - assert!(cpu.registers.status.contains(PS_NEGATIVE)); + assert!(!cpu.registers.status.contains(Status::PS_ZERO)); + assert!(!cpu.registers.status.contains(Status::PS_CARRY)); + assert!(cpu.registers.status.contains(Status::PS_NEGATIVE)); } #[test] @@ -1246,15 +1321,15 @@ mod tests { assert_eq!(cpu.registers.accumulator, a_after as i8); if a_after == 0 { - assert!(cpu.registers.status.contains(PS_ZERO)); + assert!(cpu.registers.status.contains(Status::PS_ZERO)); } else { - assert!(!cpu.registers.status.contains(PS_ZERO)); + assert!(!cpu.registers.status.contains(Status::PS_ZERO)); } if (a_after as i8) < 0 { - assert!(cpu.registers.status.contains(PS_NEGATIVE)); + assert!(cpu.registers.status.contains(Status::PS_NEGATIVE)); } else { - assert!(!cpu.registers.status.contains(PS_NEGATIVE)); + assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE)); } } } @@ -1274,15 +1349,15 @@ mod tests { assert_eq!(cpu.registers.accumulator, a_after as i8); if a_after == 0 { - assert!(cpu.registers.status.contains(PS_ZERO)); + assert!(cpu.registers.status.contains(Status::PS_ZERO)); } else { - assert!(!cpu.registers.status.contains(PS_ZERO)); + assert!(!cpu.registers.status.contains(Status::PS_ZERO)); } if (a_after as i8) < 0 { - assert!(cpu.registers.status.contains(PS_NEGATIVE)); + assert!(cpu.registers.status.contains(Status::PS_NEGATIVE)); } else { - assert!(!cpu.registers.status.contains(PS_NEGATIVE)); + assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE)); } } } diff --git a/src/memory.rs b/src/memory.rs index 21d3402..a70f8cd 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -49,20 +49,22 @@ pub const IRQ_INTERRUPT_VECTOR_HI: Address = Address(0xFFFF); const MEMORY_SIZE: usize = (ADDR_HI_BARE - ADDR_LO_BARE) as usize + 1usize; // FIXME: Should this use indirection for `bytes`? -#[derive(Copy)] +#[derive(Copy, Clone)] pub struct Memory { bytes: [u8; MEMORY_SIZE], } -impl Clone for Memory { - fn clone(&self) -> Self { - *self +impl Default for Memory { + fn default() -> Self { + Self::new() } } impl Memory { pub fn new() -> Memory { - Memory { bytes: [0; MEMORY_SIZE] } + Memory { + bytes: [0; MEMORY_SIZE], + } } pub fn get_byte(&self, address: Address) -> u8 { @@ -73,11 +75,8 @@ impl Memory { &mut self.bytes[address.to_usize()] } - pub fn get_slice(&self, Address(start): Address, AddressDiff(diff): AddressDiff) -> &[u8] { - let start = start as usize; - let diff = diff as usize; - let end = start + diff; - &self.bytes[start..end] + pub fn get_slice(&self, start: Address, diff: AddressDiff) -> &[u8] { + &self.bytes[start.to_usize()..(start + diff).to_usize()] } // Sets the byte at the given address to the given value and returns the @@ -88,20 +87,38 @@ impl Memory { old_value } - pub fn set_bytes(&mut self, Address(start): Address, values: &[u8]) { - let start = start as usize; + pub fn set_bytes(&mut self, start: Address, values: &[u8]) { + let start = start.to_usize(); // This panics if the range is invalid let end = start + values.len(); - let slice = &mut self.bytes[start..end]; - // JAM: Is this the best way to do this copy? - for (dest, src) in slice.iter_mut().zip(values.iter()) { - *dest = *src; - } + self.bytes[start..end].copy_from_slice(values); } - pub fn is_stack_address(address: &Address) -> bool { - STACK_ADDRESS_LO <= *address && *address <= STACK_ADDRESS_HI + pub fn is_stack_address(address: Address) -> bool { + (STACK_ADDRESS_LO..=STACK_ADDRESS_HI).contains(&address) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_memory_set_bytes() { + let mut memory = Memory::new(); + memory.set_bytes(Address(0x0100), &[1, 2, 3, 4, 5]); + assert_eq!( + memory.get_slice(Address(0x00FF), AddressDiff(7)), + &[0, 1, 2, 3, 4, 5, 0] + ); + } + + #[test] + #[should_panic] + fn test_memory_overflow_panic() { + let mut memory = Memory::new(); + memory.set_bytes(Address(0xFFFE), &[1, 2, 3]); } } diff --git a/src/registers.rs b/src/registers.rs index 485e923..96f12df 100644 --- a/src/registers.rs +++ b/src/registers.rs @@ -26,7 +26,7 @@ // POSSIBILITY OF SUCH DAMAGE. use address::{Address, AddressDiff}; -use memory::{STACK_ADDRESS_LO, STACK_ADDRESS_HI}; +use memory::{STACK_ADDRESS_HI, STACK_ADDRESS_LO}; // Useful for constructing Status instances #[derive(Copy, Clone)] @@ -88,40 +88,43 @@ impl Status { }) } - pub fn new(StatusArgs { negative, - overflow, - unused, - brk, - decimal_mode, - disable_interrupts, - zero, - carry }: StatusArgs) -> Status - { + pub fn new( + StatusArgs { + negative, + overflow, + unused, + brk, + decimal_mode, + disable_interrupts, + zero, + carry, + }: StatusArgs, + ) -> Status { let mut out = Status::empty(); if negative { - out |= PS_NEGATIVE + out |= Status::PS_NEGATIVE } if overflow { - out |= PS_OVERFLOW + out |= Status::PS_OVERFLOW } if unused { - out |= PS_UNUSED + out |= Status::PS_UNUSED } if brk { - out |= PS_BRK + out |= Status::PS_BRK } if decimal_mode { - out |= PS_DECIMAL_MODE + out |= Status::PS_DECIMAL_MODE } if disable_interrupts { - out |= PS_DISABLE_INTERRUPTS + out |= Status::PS_DISABLE_INTERRUPTS } if zero { - out |= PS_ZERO + out |= Status::PS_ZERO } if carry { - out |= PS_CARRY + out |= Status::PS_CARRY } out @@ -144,21 +147,18 @@ impl Status { pub struct StackPointer(pub u8); impl StackPointer { - pub fn to_address(&self) -> Address { - let StackPointer(sp) = *self; - STACK_ADDRESS_LO + AddressDiff(i32::from(sp)) + pub fn to_address(self) -> Address { + STACK_ADDRESS_LO + AddressDiff(i32::from(self.0)) } // JAM: FIXME: Should we prevent overflow here? What would a 6502 do? pub fn decrement(&mut self) { - let StackPointer(val) = *self; - *self = StackPointer(val - 1); + self.0 -= 1; } pub fn increment(&mut self) { - let StackPointer(val) = *self; - *self = StackPointer(val + 1); + self.0 += 1; } } @@ -172,6 +172,12 @@ pub struct Registers { pub status: Status, } +impl Default for Registers { + fn default() -> Self { + Self::new() + } +} + impl Registers { pub fn new() -> Registers { // TODO akeeton: Revisit these defaults.