commit d54427c2b3fd124d78a431636c4aefb85018522d Author: Daniel Bauer Date: Mon Dec 23 16:31:09 2019 +0100 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..53eaa21 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +**/*.rs.bk diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..b37feea --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,394 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "NESemu" +version = "0.1.0" +dependencies = [ + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "phf 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "simple_logger 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "atty" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "autocfg" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "backtrace" +version = "0.3.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "backtrace-sys" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "c2-chacha" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cc" +version = "1.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "chrono" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "colored" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "failure" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "failure_derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "getrandom" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.66" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "log" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-integer" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-traits" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "phf" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "phf_macros 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_shared 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "phf_generator" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "phf_shared 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "phf_macros" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "phf_generator 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_shared 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "phf_shared" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "siphasher 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "proc-macro-hack" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "proc-macro2" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_pcg 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_chacha" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "redox_syscall" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rustc-demangle" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "simple_logger" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "colored 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "siphasher" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "syn" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "synstructure" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "time" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "wasi" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +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)", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" +"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" +"checksum backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)" = "924c76597f0d9ca25d762c25a4d369d51267536465dc5064bdf0eb073ed477ea" +"checksum backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491" +"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" +"checksum cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)" = "f52a465a666ca3d838ebbf08b241383421412fe7ebb463527bba275526d89f76" +"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +"checksum chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "31850b4a4d6bae316f7a09e691c944c28299298837edc0a03f755618c23cbc01" +"checksum colored 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "433e7ac7d511768127ed85b0c4947f47a254131e37864b2dc13f52aa32cd37e5" +"checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9" +"checksum failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08" +"checksum getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407" +"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" +"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +"checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" +"checksum num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c81ffc11c212fa327657cb19dd85eb7419e163b5b076bede2bdb5c974c07e4" +"checksum phf 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" +"checksum phf_generator 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" +"checksum phf_macros 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" +"checksum phf_shared 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" +"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" +"checksum proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5" +"checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27" +"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +"checksum rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412" +"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" +"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +"checksum rand_pcg 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" +"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" +"checksum simple_logger 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3a4756ecc75607ba957820ac0a2413a6c27e6c61191cda0c62c6dcea4da88870" +"checksum siphasher 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "83da420ee8d1a89e640d0948c646c1c088758d3a3c538f943bfa97bdac17929d" +"checksum syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "dff0acdb207ae2fe6d5976617f887eb1e35a2ba52c13c7234c790960cdad9238" +"checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" +"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" +"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d" +"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..f99747a --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "NESemu" +version = "0.1.0" +authors = ["Daniel Bauer "] +edition = "2018" + +[features] +test = [] + +[dependencies] +log = "*" +simple_logger = "*" +phf = { version = "0.8", features = ['macros'] } +failure = "*" \ No newline at end of file diff --git a/src/bus.rs b/src/bus.rs new file mode 100644 index 0000000..15a8380 --- /dev/null +++ b/src/bus.rs @@ -0,0 +1,36 @@ +use crate::types::*; + +pub trait Bus { + fn readb(&self, addr: Addr) -> Byte; + fn readw(&self, addr: Addr) -> Word; + fn writeb(&mut self, addr: Addr, data: Byte); +} + +pub struct MemoryBus { + ram: [Byte; 0xFFFF] +} + +impl MemoryBus { + pub fn new() -> MemoryBus { + MemoryBus { + ram: [0; 0xFFFF] + } + } +} + +impl Bus for MemoryBus { + + fn readb(&self, addr: Addr) -> Byte { + self.ram[addr as usize] + } + + fn readw(&self, addr: Addr) -> Word { + let lo = self.readb(addr); + let hi = self.readb(addr+1); + (hi as Word) << 8 | lo as Word + } + + fn writeb(&mut self, addr: Addr, data: Byte) { + self.ram[addr as usize] = data; + } +} diff --git a/src/cpu.rs b/src/cpu.rs new file mode 100644 index 0000000..1ba3a4d --- /dev/null +++ b/src/cpu.rs @@ -0,0 +1,543 @@ +mod instructions; + +use instructions::{Instruction,Operation,AddrMode}; +use core::fmt::{Debug,Formatter,Result}; +use crate::bus::Bus; +use crate::types::*; +use log::{debug}; + +pub struct Registers { + a: Byte, + x: Byte, + y: Byte, + sp: Byte, + pc: Addr, + flags: Byte +} + +impl Registers { + pub fn new() -> Registers { + // TODO true initial state of registers before reset? + Registers { + a: 0, + x: 0, + y: 0, + sp: 0, + pc: 0, + flags: 0, + } + } +} + +impl Debug for Registers { + fn fmt(&self, f: &mut Formatter) -> Result { + write!(f, "{{ a: {:#x}, x: {:#x}, y: {:#x}, sp: {:#x}, pc: {:#x}, flags: {:#010b} }}", + self.a, self.x, self.y, self.sp, self.pc, self.flags) + } +} + + +const CARRY: Byte = 1 << 0; +const ZERO: Byte = 1 << 1; +const IRQ: Byte = 1 << 2; +const DECIMAL: Byte = 1 << 3; +const BREAK: Byte = 1 << 4; +const UNUSED: Byte = 1 << 5; +const OVERFLOW: Byte = 1 << 6; +const NEGATIVE: Byte = 1 << 7; + +pub struct CPU { + regs: Registers, + curr_op: Byte, // current operation + cycles: u8, // number of clock clycles the CPU is ahead of global clock +} + +impl CPU { + pub fn new() -> CPU { + CPU { + regs: Registers::new(), + cycles: 0, + curr_op: 0x00, + } + } + + // Brings the CPU to a known state. Resets all registers and flags + // Read location of pc from 0xfffc + pub fn reset(&mut self, bus: &T) { + // reset registers + self.regs.a = 0; + self.regs.x = 0; + self.regs.y = 0; + self.regs.sp = 0xfd; + self.regs.pc = 0x00; + self.regs.flags = 0x00 | UNUSED; + + // 0xfffc and 0xfffc+1 stores the location of the first op code (where + // the program starts). Read it and set pc accordingly. + let addr: u16 = 0xfffc; + let lo = bus.readb(addr); + let hi = bus.readb(addr + 1); + self.regs.pc = (hi as u16) << 8 | lo as u16; + + // A reset takes 8 CPU clocks + self.cycles = 8; + + // reset internal variables + self.curr_op = 0x00; + } + + fn readb(&self, bus: &T, addr: Addr) -> Byte { + bus.readb(addr) + } + + fn readw(&self, bus: &T, addr: Addr) -> Word { + bus.readw(addr) + } + + fn writeb(&mut self, bus: &mut T, addr: Addr, data: Byte) { + bus.writeb(addr, data) + } + + // read the next opcode and increment pc + fn readb_pc(&mut self, bus: &T) -> Byte { + let val = self.readb(bus, self.regs.pc); + self.regs.pc += 1; + val + } + + // read whole Word from pc + fn readw_pc(&mut self, bus: &T) -> Word { + let val = self.readw(bus, self.regs.pc); + self.regs.pc += 2; + val + } + + // Set the flag with the corresponding mask + fn set_flag(&mut self, flag: Byte, val: bool) { + debug!("{}set flag: {:08b}", if val { "" } else {"un"}, flag); + if val { + self.regs.flags |= flag; + } else { + self.regs.flags &= !flag; + } + } + + fn get_flag(&mut self, flag: Byte) -> Byte { + if self.regs.flags & flag > 0 { + 1 + } else { + 0 + } + } + + + pub fn clock(&mut self, bus: &mut T) { + if self.cycles == 0 { + let opcode = self.readb_pc(bus); + self.curr_op = opcode; + let instruction = Instruction::decode_op(opcode).unwrap(); // TODO error handling + self.cycles = self.run_instruction(bus, instruction); + } + debug!("{:?}", self); + self.cycles -= 1; + + } + + fn run_instruction(&mut self, bus: &mut T, i: &Instruction) -> u8 { + let (value, page_cross) = match &i.addr_mode { + AddrMode::IMP => self.am_IMP(bus), + AddrMode::IMM => self.am_IMM(bus), + AddrMode::ZP0 => self.am_ZP0(bus), + AddrMode::ZPX => self.am_ZPX(bus), + AddrMode::ZPY => self.am_ZPY(bus), + AddrMode::REL => self.am_REL(bus), + AddrMode::ABS => self.am_ABS(bus), + AddrMode::ABX => self.am_ABX(bus), + AddrMode::ABY => self.am_ABY(bus), + AddrMode::IND => self.am_IND(bus), + AddrMode::IZX => self.am_IZX(bus), + AddrMode::IZY => self.am_IZY(bus), + }; + debug!("{:?}, Operand: {:#x}", i, value); + + match i.operation { + Operation::ADC => self.op_ADC(bus, value), + Operation::AND => self.op_AND(bus, value), + Operation::ASL => self.op_ASL(bus, value), + Operation::BCC => self.op_BCC(bus, value), + Operation::BCS => self.op_BCS(bus, value), + Operation::BEQ => self.op_BEQ(bus, value), + Operation::BIT => self.op_BIT(bus, value), + Operation::BMI => self.op_BMI(bus, value), + Operation::BNE => self.op_BNE(bus, value), + Operation::BPL => self.op_BPL(bus, value), + Operation::BRK => self.op_BRK(bus, value), + Operation::BVC => self.op_BVC(bus, value), + Operation::BVS => self.op_BVS(bus, value), + Operation::CLC => self.op_CLC(), + Operation::CLD => self.op_CLD(bus, value), + Operation::CLI => self.op_CLI(bus, value), + Operation::CLV => self.op_CLV(bus, value), + Operation::CMP => self.op_CMP(bus, value), + Operation::CPX => self.op_CPX(bus, value), + Operation::CPY => self.op_CPY(bus, value), + Operation::DEC => self.op_DEC(bus, value), + Operation::DEX => self.op_DEX(), + Operation::DEY => self.op_DEY(), + Operation::EOR => self.op_EOR(bus, value), + Operation::INC => self.op_INC(bus, value), + Operation::INX => self.op_INX(bus, value), + Operation::INY => self.op_INY(bus, value), + Operation::JMP => self.op_JMP(bus, value), + Operation::JSR => self.op_JSR(bus, value), + Operation::LDA => self.op_LDA(bus, value), + Operation::LDX => self.op_LDX(bus, value), + Operation::LDY => self.op_LDY(bus, value), + Operation::LSR => self.op_LSR(bus, value), + Operation::NOP => self.op_NOP(bus, value), + Operation::ORA => self.op_ORA(bus, value), + Operation::PHA => self.op_PHA(bus, value), + Operation::PHP => self.op_PHP(bus, value), + Operation::PLA => self.op_PLA(bus, value), + Operation::PLP => self.op_PLP(bus, value), + Operation::ROL => self.op_ROL(bus, value), + Operation::ROR => self.op_ROR(bus, value), + Operation::RTI => self.op_RTI(bus, value), + Operation::RTS => self.op_RTS(bus, value), + Operation::SBC => self.op_SBC(bus, value), + Operation::SEC => self.op_SEC(bus, value), + Operation::SED => self.op_SED(bus, value), + Operation::SEI => self.op_SEI(bus, value), + Operation::STA => self.op_STA(bus, value), + Operation::STX => self.op_STX(bus, value), + Operation::STY => self.op_STY(bus, value), + Operation::TAX => self.op_TAX(bus, value), + Operation::TAY => self.op_TAY(bus, value), + Operation::TSX => self.op_TSX(bus, value), + Operation::TXA => self.op_TXA(bus, value), + Operation::TXS => self.op_TXS(bus, value), + Operation::TYA => self.op_TYA(bus, value), + } + + if page_cross { + i.cycles[0] + i.cycles[1] + } else { + i.cycles[0] + } + } + + // Implied aka no target + fn am_IMP(&mut self, bus: &T) -> (Word, bool) { + (0, false) + } + + // Immediate, next byte comes from pc + fn am_IMM(&mut self, bus: &T) -> (Word, bool) { + let addr = self.regs.pc; + self.regs.pc += 1; + (addr, false) + } + + fn am_ZP0(&mut self, bus: &T) -> (Word, bool) { + unimplemented!() + } + + fn am_ZPX(&mut self, bus: &T) -> (Word, bool) { + unimplemented!() + } + + fn am_ZPY(&mut self, bus: &T) -> (Word, bool) { + unimplemented!() + } + + fn am_REL(&mut self, bus: &T) -> (Word, bool) { + let rel_addr = self.readb_pc(bus) as Word; + if rel_addr < 0x80 { + (self.regs.pc + rel_addr, false) + } else { + (self.regs.pc + rel_addr - 256, false) + } + } + + fn am_ABS(&mut self, bus: &T) -> (Word, bool) { + let addr = self.readw_pc(bus); + (addr, false) + } + + fn am_ABX(&mut self, bus: &T) -> (Word, bool) { + unimplemented!() + } + + fn am_ABY(&mut self, bus: &T) -> (Word, bool) { + unimplemented!() + } + + fn am_IND(&mut self, bus: &T) -> (Word, bool) { + unimplemented!() + } + + fn am_IZX(&mut self, bus: &T) -> (Word, bool) { + unimplemented!() + } + + fn am_IZY(&mut self, bus: &T) -> (Word, bool) { + unimplemented!() + } + + // Operations + fn op_ADC(&mut self, bus: &T, addr: Word) { + let val = self.readb(bus, addr) as Word; + let tmp = (self.regs.a as Word) + val + (self.get_flag(CARRY) as Word); + self.set_flag(CARRY, (tmp & 0xFF) > 255); + self.set_flag(ZERO, tmp == 0); + self.set_flag(NEGATIVE, (tmp & 0x80) == 1); + self.set_flag(OVERFLOW, !(((self.regs.a as Word) ^ tmp) & !((self.regs.a as Word) ^ val) & 0x80) == 1); + self.regs.a = tmp as Byte; + } + + fn op_AND(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_ASL(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_BCC(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_BCS(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_BEQ(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_BIT(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_BMI(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_BNE(&mut self, bus: &T, addr: Word) { + if self.get_flag(ZERO) == 0 { + let old_addr = self.regs.pc; + self.regs.pc = addr; + println!("Jumping from {:#x} to {:#x}", old_addr, addr); + } + } + + fn op_BPL(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_BRK(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_BVC(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_BVS(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_CLC(&mut self) { + self.set_flag(CARRY, false); + } + + fn op_CLD(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_CLI(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_CLV(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_CMP(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_CPX(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_CPY(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_DEC(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_DEX(&mut self) { + self.regs.x -= 1; + self.set_flag(ZERO, self.regs.x == 0); + self.set_flag(NEGATIVE, (self.regs.x & 0x80) != 0) + } + + fn op_DEY(&mut self) { + self.regs.y -= 1; + self.set_flag(ZERO, self.regs.y == 0); + self.set_flag(NEGATIVE, (self.regs.y & 0x80) != 0) + } + + fn op_EOR(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_INC(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_INX(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_INY(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_JMP(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_JSR(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_LDA(&mut self, bus: &T, addr: Word) { + let val = bus.readb(addr); + self.regs.a = val; + self.set_flag(ZERO, val == 0); + self.set_flag(NEGATIVE, (val & 0x80) != 0); + } + + fn op_LDX(&mut self, bus: &T, addr: Word) { + let val = bus.readb(addr); + self.regs.x = val; + self.set_flag(ZERO, val == 0); + self.set_flag(NEGATIVE, (val & 0x80) != 0); + } + + fn op_LDY(&mut self, bus: &T, addr: Word) { + let val = bus.readb(addr); + self.regs.y = val; + self.set_flag(ZERO, val == 0); + self.set_flag(NEGATIVE, (val & 0x80) != 0); + } + + fn op_LSR(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_NOP(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_ORA(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_PHA(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_PHP(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_PLA(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_PLP(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_ROL(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_ROR(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_RTI(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_RTS(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_SBC(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_SEC(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_SED(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_SEI(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_STA(&mut self, bus: &mut T, addr: Word) { + self.writeb(bus, addr, self.regs.a) + } + + fn op_STX(&mut self, bus: &mut T, addr: Word) { + self.writeb(bus, addr, self.regs.x) + } + + fn op_STY(&mut self, bus: &mut T, addr: Word) { + self.writeb(bus, addr, self.regs.y) + } + + fn op_TAX(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_TAY(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_TSX(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_TXA(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_TXS(&mut self, bus: &T, val: Word) { + unimplemented!() + } + + fn op_TYA(&mut self, bus: &T, val: Word) { + unimplemented!() + } +} + +impl Debug for CPU { + fn fmt(&self, f: &mut Formatter) -> Result { + write!(f, "{:?}, op: {:x}, cycle: {:?}", + self.regs, self.curr_op, self.cycles) + } +} + + diff --git a/src/cpu/atomic_operations.rs b/src/cpu/atomic_operations.rs new file mode 100644 index 0000000..76f0ba8 --- /dev/null +++ b/src/cpu/atomic_operations.rs @@ -0,0 +1,4 @@ +use crate::types::*; + + +// ADDRESS MODES // diff --git a/src/cpu/instructions.rs b/src/cpu/instructions.rs new file mode 100644 index 0000000..4b2485b --- /dev/null +++ b/src/cpu/instructions.rs @@ -0,0 +1,312 @@ +use failure::err_msg; +use phf::{Map,phf_map}; +use failure::{Error}; +use std::fmt; +use std::fmt::Debug; + +#[derive(Debug)] +pub enum AddrMode { + IMP, + IMM, + ZP0, + ZPX, + ZPY, + REL, + ABS, + ABX, + ABY, + IND, + IZX, + IZY, +} + + +#[derive(Debug)] +pub enum Operation { + ADC, + AND, + ASL, + BCC, + BCS, + BEQ, + BIT, + BMI, + BNE, + BPL, + BRK, + BVC, + BVS, + CLC, + CLD, + CLI, + CLV, + CMP, + CPX, + CPY, + DEC, + DEX, + DEY, + EOR, + INC, + INX, + INY, + JMP, + JSR, + LDA, + LDX, + LDY, + LSR, + NOP, + ORA, + PHA, + PHP, + PLA, + PLP, + ROL, + ROR, + RTI, + RTS, + SBC, + SEC, + SED, + SEI, + STA, + STX, + STY, + TAX, + TAY, + TSX, + TXA, + TXS, + TYA, +} + +static INSTRUCTION_SET: Map = phf_map! { + // 0x00 + 0x00u8 => Instruction { opcode: 0x00, addr_mode: AddrMode::IMM, operation: Operation::BRK, cycles: [7, 0] }, + 0x01u8 => Instruction { opcode: 0x01, addr_mode: AddrMode::IZX, operation: Operation::ORA, cycles: [6, 0] }, + 0x04u8 => Instruction { opcode: 0x04, addr_mode: AddrMode::IMP, operation: Operation::NOP, cycles: [3, 0] }, + 0x05u8 => Instruction { opcode: 0x05, addr_mode: AddrMode::ZP0, operation: Operation::ORA, cycles: [3, 0] }, + 0x06u8 => Instruction { opcode: 0x06, addr_mode: AddrMode::ZP0, operation: Operation::ASL, cycles: [5, 0] }, + 0x08u8 => Instruction { opcode: 0x08, addr_mode: AddrMode::IMP, operation: Operation::PHP, cycles: [3, 0] }, + 0x09u8 => Instruction { opcode: 0x09, addr_mode: AddrMode::IMM, operation: Operation::ORA, cycles: [2, 0] }, + 0x0au8 => Instruction { opcode: 0x0a, addr_mode: AddrMode::IMP, operation: Operation::ASL, cycles: [2, 0] }, + 0x0cu8 => Instruction { opcode: 0x0c, addr_mode: AddrMode::IMP, operation: Operation::NOP, cycles: [4, 0] }, + 0x0du8 => Instruction { opcode: 0x0d, addr_mode: AddrMode::ABS, operation: Operation::ORA, cycles: [4, 0] }, + 0x0eu8 => Instruction { opcode: 0x0e, addr_mode: AddrMode::ABS, operation: Operation::ASL, cycles: [6, 0] }, + // 0x10 + 0x10u8 => Instruction { opcode: 0x10, addr_mode: AddrMode::REL, operation: Operation::BPL, cycles: [2, 0] }, + 0x11u8 => Instruction { opcode: 0x11, addr_mode: AddrMode::IZY, operation: Operation::ORA, cycles: [5, 0] }, + 0x14u8 => Instruction { opcode: 0x14, addr_mode: AddrMode::IMP, operation: Operation::NOP, cycles: [4, 0] }, + 0x15u8 => Instruction { opcode: 0x15, addr_mode: AddrMode::ZPX, operation: Operation::ORA, cycles: [4, 0] }, + 0x16u8 => Instruction { opcode: 0x16, addr_mode: AddrMode::ZPX, operation: Operation::ASL, cycles: [6, 0] }, + 0x18u8 => Instruction { opcode: 0x18, addr_mode: AddrMode::IMP, operation: Operation::CLC, cycles: [2, 0] }, + 0x19u8 => Instruction { opcode: 0x19, addr_mode: AddrMode::ABY, operation: Operation::ORA, cycles: [4, 0] }, + 0x1au8 => Instruction { opcode: 0x1a, addr_mode: AddrMode::IMP, operation: Operation::NOP, cycles: [2, 0] }, + 0x1cu8 => Instruction { opcode: 0x1c, addr_mode: AddrMode::IMP, operation: Operation::NOP, cycles: [4, 0] }, + 0x1du8 => Instruction { opcode: 0x1d, addr_mode: AddrMode::ABX, operation: Operation::ORA, cycles: [4, 0] }, + 0x1eu8 => Instruction { opcode: 0x1e, addr_mode: AddrMode::ABX, operation: Operation::ASL, cycles: [7, 0] }, + // 0x20 + 0x20u8 => Instruction { opcode: 0x20, addr_mode: AddrMode::ABS, operation: Operation::JSR, cycles: [6, 0] }, + 0x21u8 => Instruction { opcode: 0x21, addr_mode: AddrMode::IZX, operation: Operation::AND, cycles: [6, 0] }, + 0x24u8 => Instruction { opcode: 0x24, addr_mode: AddrMode::ZP0, operation: Operation::BIT, cycles: [3, 0] }, + 0x25u8 => Instruction { opcode: 0x25, addr_mode: AddrMode::ZP0, operation: Operation::AND, cycles: [3, 0] }, + 0x26u8 => Instruction { opcode: 0x26, addr_mode: AddrMode::ZP0, operation: Operation::ROL, cycles: [5, 0] }, + 0x28u8 => Instruction { opcode: 0x28, addr_mode: AddrMode::IMP, operation: Operation::PLP, cycles: [4, 0] }, + 0x29u8 => Instruction { opcode: 0x29, addr_mode: AddrMode::IMM, operation: Operation::AND, cycles: [2, 0] }, + 0x2au8 => Instruction { opcode: 0x2a, addr_mode: AddrMode::IMP, operation: Operation::ROL, cycles: [2, 0] }, + 0x2cu8 => Instruction { opcode: 0x2c, addr_mode: AddrMode::ABS, operation: Operation::BIT, cycles: [4, 0] }, + 0x2du8 => Instruction { opcode: 0x2d, addr_mode: AddrMode::ABS, operation: Operation::AND, cycles: [4, 0] }, + 0x2eu8 => Instruction { opcode: 0x2e, addr_mode: AddrMode::ABS, operation: Operation::ROL, cycles: [6, 0] }, + // 0x30 + 0x30u8 => Instruction { opcode: 0x30, addr_mode: AddrMode::REL, operation: Operation::BMI, cycles: [2, 0] }, + 0x31u8 => Instruction { opcode: 0x31, addr_mode: AddrMode::IZY, operation: Operation::AND, cycles: [5, 0] }, + 0x34u8 => Instruction { opcode: 0x34, addr_mode: AddrMode::IMP, operation: Operation::NOP, cycles: [4, 0] }, + 0x35u8 => Instruction { opcode: 0x35, addr_mode: AddrMode::ZPX, operation: Operation::AND, cycles: [4, 0] }, + 0x36u8 => Instruction { opcode: 0x36, addr_mode: AddrMode::ZPX, operation: Operation::ROL, cycles: [6, 0] }, + 0x38u8 => Instruction { opcode: 0x38, addr_mode: AddrMode::IMP, operation: Operation::SEC, cycles: [2, 0] }, + 0x39u8 => Instruction { opcode: 0x39, addr_mode: AddrMode::ABY, operation: Operation::AND, cycles: [4, 0] }, + 0x3au8 => Instruction { opcode: 0x3a, addr_mode: AddrMode::IMP, operation: Operation::NOP, cycles: [2, 0] }, + 0x3cu8 => Instruction { opcode: 0x3c, addr_mode: AddrMode::IMP, operation: Operation::NOP, cycles: [4, 0] }, + 0x3du8 => Instruction { opcode: 0x3d, addr_mode: AddrMode::ABX, operation: Operation::AND, cycles: [4, 0] }, + 0x3eu8 => Instruction { opcode: 0x3e, addr_mode: AddrMode::ABX, operation: Operation::ROL, cycles: [7, 0] }, + // 0x40 + 0x40u8 => Instruction { opcode: 0x40, addr_mode: AddrMode::IMP, operation: Operation::RTI, cycles: [6, 0] }, + 0x41u8 => Instruction { opcode: 0x41, addr_mode: AddrMode::IZX, operation: Operation::EOR, cycles: [6, 0] }, + 0x44u8 => Instruction { opcode: 0x44, addr_mode: AddrMode::IMP, operation: Operation::NOP, cycles: [3, 0] }, + 0x45u8 => Instruction { opcode: 0x45, addr_mode: AddrMode::ZP0, operation: Operation::EOR, cycles: [3, 0] }, + 0x46u8 => Instruction { opcode: 0x46, addr_mode: AddrMode::ZP0, operation: Operation::LSR, cycles: [5, 0] }, + 0x48u8 => Instruction { opcode: 0x48, addr_mode: AddrMode::IMP, operation: Operation::PHA, cycles: [3, 0] }, + 0x49u8 => Instruction { opcode: 0x49, addr_mode: AddrMode::IMM, operation: Operation::EOR, cycles: [2, 0] }, + 0x4au8 => Instruction { opcode: 0x4a, addr_mode: AddrMode::IMP, operation: Operation::LSR, cycles: [2, 0] }, + 0x4cu8 => Instruction { opcode: 0x4c, addr_mode: AddrMode::ABS, operation: Operation::JMP, cycles: [3, 0] }, + 0x4du8 => Instruction { opcode: 0x4d, addr_mode: AddrMode::ABS, operation: Operation::EOR, cycles: [4, 0] }, + 0x4eu8 => Instruction { opcode: 0x4e, addr_mode: AddrMode::ABS, operation: Operation::LSR, cycles: [6, 0] }, + // 0x50 + 0x50u8 => Instruction { opcode: 0x50, addr_mode: AddrMode::REL, operation: Operation::BVC, cycles: [2, 0] }, + 0x51u8 => Instruction { opcode: 0x51, addr_mode: AddrMode::IZY, operation: Operation::EOR, cycles: [5, 0] }, + 0x54u8 => Instruction { opcode: 0x54, addr_mode: AddrMode::IMP, operation: Operation::NOP, cycles: [4, 0] }, + 0x55u8 => Instruction { opcode: 0x55, addr_mode: AddrMode::ZPX, operation: Operation::EOR, cycles: [4, 0] }, + 0x56u8 => Instruction { opcode: 0x56, addr_mode: AddrMode::ZPX, operation: Operation::LSR, cycles: [6, 0] }, + 0x58u8 => Instruction { opcode: 0x58, addr_mode: AddrMode::IMP, operation: Operation::CLI, cycles: [2, 0] }, + 0x59u8 => Instruction { opcode: 0x59, addr_mode: AddrMode::ABY, operation: Operation::EOR, cycles: [4, 0] }, + 0x5au8 => Instruction { opcode: 0x5a, addr_mode: AddrMode::IMP, operation: Operation::NOP, cycles: [2, 0] }, + 0x5cu8 => Instruction { opcode: 0x5c, addr_mode: AddrMode::IMP, operation: Operation::NOP, cycles: [4, 0] }, + 0x5du8 => Instruction { opcode: 0x5d, addr_mode: AddrMode::ABX, operation: Operation::EOR, cycles: [4, 0] }, + 0x5eu8 => Instruction { opcode: 0x5e, addr_mode: AddrMode::ABX, operation: Operation::LSR, cycles: [7, 0] }, + // 0x60 + 0x60u8 => Instruction { opcode: 0x60, addr_mode: AddrMode::IMP, operation: Operation::RTS, cycles: [6, 0] }, + 0x61u8 => Instruction { opcode: 0x61, addr_mode: AddrMode::IZX, operation: Operation::ADC, cycles: [6, 0] }, + 0x64u8 => Instruction { opcode: 0x64, addr_mode: AddrMode::IMP, operation: Operation::NOP, cycles: [3, 0] }, + 0x65u8 => Instruction { opcode: 0x65, addr_mode: AddrMode::ZP0, operation: Operation::ADC, cycles: [3, 0] }, + 0x66u8 => Instruction { opcode: 0x66, addr_mode: AddrMode::ZP0, operation: Operation::ROR, cycles: [5, 0] }, + 0x68u8 => Instruction { opcode: 0x68, addr_mode: AddrMode::IMP, operation: Operation::PLA, cycles: [4, 0] }, + 0x69u8 => Instruction { opcode: 0x69, addr_mode: AddrMode::IMM, operation: Operation::ADC, cycles: [2, 0] }, + 0x6au8 => Instruction { opcode: 0x6a, addr_mode: AddrMode::IMP, operation: Operation::ROR, cycles: [2, 0] }, + 0x6cu8 => Instruction { opcode: 0x6c, addr_mode: AddrMode::IND, operation: Operation::JMP, cycles: [5, 0] }, + 0x6du8 => Instruction { opcode: 0x6d, addr_mode: AddrMode::ABS, operation: Operation::ADC, cycles: [4, 0] }, + 0x6eu8 => Instruction { opcode: 0x6e, addr_mode: AddrMode::ABS, operation: Operation::ROR, cycles: [6, 0] }, + // 0x70 + 0x70u8 => Instruction { opcode: 0x70, addr_mode: AddrMode::REL, operation: Operation::BVS, cycles: [2, 0] }, + 0x71u8 => Instruction { opcode: 0x71, addr_mode: AddrMode::IZY, operation: Operation::ADC, cycles: [5, 0] }, + 0x74u8 => Instruction { opcode: 0x74, addr_mode: AddrMode::IMP, operation: Operation::NOP, cycles: [4, 0] }, + 0x75u8 => Instruction { opcode: 0x75, addr_mode: AddrMode::ZPX, operation: Operation::ADC, cycles: [4, 0] }, + 0x76u8 => Instruction { opcode: 0x76, addr_mode: AddrMode::ZPX, operation: Operation::ROR, cycles: [6, 0] }, + 0x78u8 => Instruction { opcode: 0x78, addr_mode: AddrMode::IMP, operation: Operation::SEI, cycles: [2, 0] }, + 0x79u8 => Instruction { opcode: 0x79, addr_mode: AddrMode::ABY, operation: Operation::ADC, cycles: [4, 0] }, + 0x7au8 => Instruction { opcode: 0x7a, addr_mode: AddrMode::IMP, operation: Operation::NOP, cycles: [2, 0] }, + 0x7cu8 => Instruction { opcode: 0x7c, addr_mode: AddrMode::IMP, operation: Operation::NOP, cycles: [4, 0] }, + 0x7du8 => Instruction { opcode: 0x7d, addr_mode: AddrMode::ABX, operation: Operation::ADC, cycles: [4, 0] }, + 0x7eu8 => Instruction { opcode: 0x7e, addr_mode: AddrMode::ABX, operation: Operation::ROR, cycles: [7, 0] }, + // 0x80 + 0x80u8 => Instruction { opcode: 0x80, addr_mode: AddrMode::IMP, operation: Operation::NOP, cycles: [2, 0] }, + 0x81u8 => Instruction { opcode: 0x81, addr_mode: AddrMode::IZX, operation: Operation::STA, cycles: [6, 0] }, + 0x82u8 => Instruction { opcode: 0x82, addr_mode: AddrMode::IMP, operation: Operation::NOP, cycles: [2, 0] }, + 0x84u8 => Instruction { opcode: 0x84, addr_mode: AddrMode::ZP0, operation: Operation::STY, cycles: [3, 0] }, + 0x85u8 => Instruction { opcode: 0x85, addr_mode: AddrMode::ZP0, operation: Operation::STA, cycles: [3, 0] }, + 0x86u8 => Instruction { opcode: 0x86, addr_mode: AddrMode::ZP0, operation: Operation::STX, cycles: [3, 0] }, + 0x88u8 => Instruction { opcode: 0x88, addr_mode: AddrMode::IMP, operation: Operation::DEY, cycles: [2, 0] }, + 0x89u8 => Instruction { opcode: 0x89, addr_mode: AddrMode::IMP, operation: Operation::NOP, cycles: [2, 0] }, + 0x8au8 => Instruction { opcode: 0x8a, addr_mode: AddrMode::IMP, operation: Operation::TXA, cycles: [2, 0] }, + 0x8cu8 => Instruction { opcode: 0x8c, addr_mode: AddrMode::ABS, operation: Operation::STY, cycles: [4, 0] }, + 0x8du8 => Instruction { opcode: 0x8d, addr_mode: AddrMode::ABS, operation: Operation::STA, cycles: [4, 0] }, + 0x8eu8 => Instruction { opcode: 0x8e, addr_mode: AddrMode::ABS, operation: Operation::STX, cycles: [4, 0] }, + // 0x90 + 0x90u8 => Instruction { opcode: 0x90, addr_mode: AddrMode::REL, operation: Operation::BCC, cycles: [2, 0] }, + 0x91u8 => Instruction { opcode: 0x91, addr_mode: AddrMode::IZY, operation: Operation::STA, cycles: [6, 0] }, + 0x94u8 => Instruction { opcode: 0x94, addr_mode: AddrMode::ZPX, operation: Operation::STY, cycles: [4, 0] }, + 0x95u8 => Instruction { opcode: 0x95, addr_mode: AddrMode::ZPX, operation: Operation::STA, cycles: [4, 0] }, + 0x96u8 => Instruction { opcode: 0x96, addr_mode: AddrMode::ZPY, operation: Operation::STX, cycles: [4, 0] }, + 0x98u8 => Instruction { opcode: 0x98, addr_mode: AddrMode::IMP, operation: Operation::TYA, cycles: [2, 0] }, + 0x99u8 => Instruction { opcode: 0x99, addr_mode: AddrMode::ABY, operation: Operation::STA, cycles: [5, 0] }, + 0x9au8 => Instruction { opcode: 0x9a, addr_mode: AddrMode::IMP, operation: Operation::TXS, cycles: [2, 0] }, + 0x9cu8 => Instruction { opcode: 0x9c, addr_mode: AddrMode::IMP, operation: Operation::NOP, cycles: [5, 0] }, + 0x9du8 => Instruction { opcode: 0x9d, addr_mode: AddrMode::ABX, operation: Operation::STA, cycles: [5, 0] }, + // 0xa0 + 0xa0u8 => Instruction { opcode: 0xa0, addr_mode: AddrMode::IMM, operation: Operation::LDY, cycles: [2, 0] }, + 0xa1u8 => Instruction { opcode: 0xa1, addr_mode: AddrMode::IZX, operation: Operation::LDA, cycles: [6, 0] }, + 0xa2u8 => Instruction { opcode: 0xa2, addr_mode: AddrMode::IMM, operation: Operation::LDX, cycles: [2, 0] }, + 0xa4u8 => Instruction { opcode: 0xa4, addr_mode: AddrMode::ZP0, operation: Operation::LDY, cycles: [3, 0] }, + 0xa5u8 => Instruction { opcode: 0xa5, addr_mode: AddrMode::ZP0, operation: Operation::LDA, cycles: [3, 0] }, + 0xa6u8 => Instruction { opcode: 0xa6, addr_mode: AddrMode::ZP0, operation: Operation::LDX, cycles: [3, 0] }, + 0xa8u8 => Instruction { opcode: 0xa8, addr_mode: AddrMode::IMP, operation: Operation::TAY, cycles: [2, 0] }, + 0xa9u8 => Instruction { opcode: 0xa9, addr_mode: AddrMode::IMM, operation: Operation::LDA, cycles: [2, 0] }, + 0xaau8 => Instruction { opcode: 0xaa, addr_mode: AddrMode::IMP, operation: Operation::TAX, cycles: [2, 0] }, + 0xacu8 => Instruction { opcode: 0xac, addr_mode: AddrMode::ABS, operation: Operation::LDY, cycles: [4, 0] }, + 0xadu8 => Instruction { opcode: 0xad, addr_mode: AddrMode::ABS, operation: Operation::LDA, cycles: [4, 0] }, + 0xaeu8 => Instruction { opcode: 0xae, addr_mode: AddrMode::ABS, operation: Operation::LDX, cycles: [4, 0] }, + // 0xb0 + 0xb0u8 => Instruction { opcode: 0xb0, addr_mode: AddrMode::REL, operation: Operation::BCS, cycles: [2, 0] }, + 0xb1u8 => Instruction { opcode: 0xb1, addr_mode: AddrMode::IZY, operation: Operation::LDA, cycles: [5, 0] }, + 0xb4u8 => Instruction { opcode: 0xb4, addr_mode: AddrMode::ZPX, operation: Operation::LDY, cycles: [4, 0] }, + 0xb5u8 => Instruction { opcode: 0xb5, addr_mode: AddrMode::ZPX, operation: Operation::LDA, cycles: [4, 0] }, + 0xb6u8 => Instruction { opcode: 0xb6, addr_mode: AddrMode::ZPY, operation: Operation::LDX, cycles: [4, 0] }, + 0xb8u8 => Instruction { opcode: 0xb8, addr_mode: AddrMode::IMP, operation: Operation::CLV, cycles: [2, 0] }, + 0xb9u8 => Instruction { opcode: 0xb9, addr_mode: AddrMode::ABY, operation: Operation::LDA, cycles: [4, 0] }, + 0xbau8 => Instruction { opcode: 0xba, addr_mode: AddrMode::IMP, operation: Operation::TSX, cycles: [2, 0] }, + 0xbcu8 => Instruction { opcode: 0xbc, addr_mode: AddrMode::ABX, operation: Operation::LDY, cycles: [4, 0] }, + 0xbdu8 => Instruction { opcode: 0xbd, addr_mode: AddrMode::ABX, operation: Operation::LDA, cycles: [4, 0] }, + 0xbeu8 => Instruction { opcode: 0xbe, addr_mode: AddrMode::ABY, operation: Operation::LDX, cycles: [4, 0] }, + // 0xc0 + 0xc0u8 => Instruction { opcode: 0xc0, addr_mode: AddrMode::IMM, operation: Operation::CPY, cycles: [2, 0] }, + 0xc1u8 => Instruction { opcode: 0xc1, addr_mode: AddrMode::IZX, operation: Operation::CMP, cycles: [6, 0] }, + 0xc2u8 => Instruction { opcode: 0xc2, addr_mode: AddrMode::IMP, operation: Operation::NOP, cycles: [2, 0] }, + 0xc4u8 => Instruction { opcode: 0xc4, addr_mode: AddrMode::ZP0, operation: Operation::CPY, cycles: [3, 0] }, + 0xc5u8 => Instruction { opcode: 0xc5, addr_mode: AddrMode::ZP0, operation: Operation::CMP, cycles: [3, 0] }, + 0xc6u8 => Instruction { opcode: 0xc6, addr_mode: AddrMode::ZP0, operation: Operation::DEC, cycles: [5, 0] }, + 0xc8u8 => Instruction { opcode: 0xc8, addr_mode: AddrMode::IMP, operation: Operation::INY, cycles: [2, 0] }, + 0xc9u8 => Instruction { opcode: 0xc9, addr_mode: AddrMode::IMM, operation: Operation::CMP, cycles: [2, 0] }, + 0xcau8 => Instruction { opcode: 0xca, addr_mode: AddrMode::IMP, operation: Operation::DEX, cycles: [2, 0] }, + 0xccu8 => Instruction { opcode: 0xcc, addr_mode: AddrMode::ABS, operation: Operation::CPY, cycles: [4, 0] }, + 0xcdu8 => Instruction { opcode: 0xcd, addr_mode: AddrMode::ABS, operation: Operation::CMP, cycles: [4, 0] }, + 0xceu8 => Instruction { opcode: 0xce, addr_mode: AddrMode::ABS, operation: Operation::DEC, cycles: [6, 0] }, + // 0xd0 + 0xd0u8 => Instruction { opcode: 0xd0, addr_mode: AddrMode::REL, operation: Operation::BNE, cycles: [2, 0] }, + 0xd1u8 => Instruction { opcode: 0xd1, addr_mode: AddrMode::IZY, operation: Operation::CMP, cycles: [5, 0] }, + 0xd4u8 => Instruction { opcode: 0xd4, addr_mode: AddrMode::IMP, operation: Operation::NOP, cycles: [4, 0] }, + 0xd5u8 => Instruction { opcode: 0xd5, addr_mode: AddrMode::ZPX, operation: Operation::CMP, cycles: [4, 0] }, + 0xd6u8 => Instruction { opcode: 0xd6, addr_mode: AddrMode::ZPX, operation: Operation::DEC, cycles: [6, 0] }, + 0xd8u8 => Instruction { opcode: 0xd8, addr_mode: AddrMode::IMP, operation: Operation::CLD, cycles: [2, 0] }, + 0xd9u8 => Instruction { opcode: 0xd9, addr_mode: AddrMode::ABY, operation: Operation::CMP, cycles: [4, 0] }, + 0xdau8 => Instruction { opcode: 0xda, addr_mode: AddrMode::IMP, operation: Operation::NOP, cycles: [2, 0] }, + 0xdcu8 => Instruction { opcode: 0xdc, addr_mode: AddrMode::IMP, operation: Operation::NOP, cycles: [4, 0] }, + 0xddu8 => Instruction { opcode: 0xdd, addr_mode: AddrMode::ABX, operation: Operation::CMP, cycles: [4, 0] }, + 0xdeu8 => Instruction { opcode: 0xde, addr_mode: AddrMode::ABX, operation: Operation::DEC, cycles: [7, 0] }, + // 0xe0 + 0xe0u8 => Instruction { opcode: 0xe0, addr_mode: AddrMode::IMM, operation: Operation::CPX, cycles: [2, 0] }, + 0xe1u8 => Instruction { opcode: 0xe1, addr_mode: AddrMode::IZX, operation: Operation::SBC, cycles: [6, 0] }, + 0xe2u8 => Instruction { opcode: 0xe2, addr_mode: AddrMode::IMP, operation: Operation::NOP, cycles: [2, 0] }, + 0xe4u8 => Instruction { opcode: 0xe4, addr_mode: AddrMode::ZP0, operation: Operation::CPX, cycles: [3, 0] }, + 0xe5u8 => Instruction { opcode: 0xe5, addr_mode: AddrMode::ZP0, operation: Operation::SBC, cycles: [3, 0] }, + 0xe6u8 => Instruction { opcode: 0xe6, addr_mode: AddrMode::ZP0, operation: Operation::INC, cycles: [5, 0] }, + 0xe8u8 => Instruction { opcode: 0xe8, addr_mode: AddrMode::IMP, operation: Operation::INX, cycles: [2, 0] }, + 0xe9u8 => Instruction { opcode: 0xe9, addr_mode: AddrMode::IMM, operation: Operation::SBC, cycles: [2, 0] }, + 0xeau8 => Instruction { opcode: 0xea, addr_mode: AddrMode::IMP, operation: Operation::NOP, cycles: [2, 0] }, + 0xebu8 => Instruction { opcode: 0xeb, addr_mode: AddrMode::IMP, operation: Operation::SBC, cycles: [2, 0] }, + 0xecu8 => Instruction { opcode: 0xec, addr_mode: AddrMode::ABS, operation: Operation::CPX, cycles: [4, 0] }, + 0xedu8 => Instruction { opcode: 0xed, addr_mode: AddrMode::ABS, operation: Operation::SBC, cycles: [4, 0] }, + 0xeeu8 => Instruction { opcode: 0xee, addr_mode: AddrMode::ABS, operation: Operation::INC, cycles: [6, 0] }, + // 0xf0 + 0xf0u8 => Instruction { opcode: 0xf0, addr_mode: AddrMode::REL, operation: Operation::BEQ, cycles: [2, 0] }, + 0xf1u8 => Instruction { opcode: 0xf1, addr_mode: AddrMode::IZY, operation: Operation::SBC, cycles: [5, 0] }, + 0xf4u8 => Instruction { opcode: 0xf4, addr_mode: AddrMode::IMP, operation: Operation::NOP, cycles: [4, 0] }, + 0xf5u8 => Instruction { opcode: 0xf5, addr_mode: AddrMode::ZPX, operation: Operation::SBC, cycles: [4, 0] }, + 0xf6u8 => Instruction { opcode: 0xf6, addr_mode: AddrMode::ZPX, operation: Operation::INC, cycles: [6, 0] }, + 0xf8u8 => Instruction { opcode: 0xf8, addr_mode: AddrMode::IMP, operation: Operation::SED, cycles: [2, 0] }, + 0xf9u8 => Instruction { opcode: 0xf9, addr_mode: AddrMode::ABY, operation: Operation::SBC, cycles: [4, 0] }, + 0xfau8 => Instruction { opcode: 0xfa, addr_mode: AddrMode::IMP, operation: Operation::NOP, cycles: [2, 0] }, + 0xfcu8 => Instruction { opcode: 0xfc, addr_mode: AddrMode::IMP, operation: Operation::NOP, cycles: [4, 0] }, + 0xfdu8 => Instruction { opcode: 0xfd, addr_mode: AddrMode::ABX, operation: Operation::SBC, cycles: [4, 0] }, + 0xfeu8 => Instruction { opcode: 0xfe, addr_mode: AddrMode::ABX, operation: Operation::INC, cycles: [7, 0] }, +}; + +pub struct Instruction { + pub opcode: u8, + pub addr_mode: AddrMode, + pub operation: Operation, + + // number of cycles required + // first value: number of cycles + // second value: additional cycles on page cross + pub cycles: [u8; 2], +} + +impl Instruction { + pub fn decode_op(opcode: u8) -> Result<&'static Instruction, Error> { + INSTRUCTION_SET.get(&opcode).ok_or(err_msg(format!("Illegal opcode: {:#x}", opcode))) + } +} + +impl Debug for Instruction { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if self.cycles[1] == 0 { + write!(f, "Instruction {{ opcode: {:#x}, op/addr: {:?}/{:?}, cycles: {:?} }}", + self.opcode, self.operation, self.addr_mode, self.cycles[0]) + } else { + write!(f, "Instruction {{ opcode: {:#x}, op/addr: {:?}/{:?}, cycles: {:?}(+{:?}) }}", + self.opcode, self.operation, self.addr_mode, self.cycles[0],self.cycles[1]) + } + + } +} + diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..8e5b901 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,41 @@ +#[macro_use] extern crate log; +#[macro_use] extern crate failure; +extern crate simple_logger; +extern crate phf; + +#[allow(non_snake_case)] +mod cpu; +mod bus; +mod types; + +use types::*; +use cpu::CPU; +use bus::{MemoryBus, Bus}; + +fn main() { + simple_logger::init().unwrap(); + + let mut bus = MemoryBus::new(); + + // Load little test program into ram + let test_prog: [Byte; 28] = [0xA2, 0x0A, 0x8E, 0x00, 0x00, 0xA2, 0x03, 0x8E, 0x01, 0x00, 0xAC, 0x00, 0x00, 0xA9, + 0x00, 0x18, 0x6D, 0x01, 0x00, 0x88, 0xD0, 0xFA, 0x8D, 0x02, 0x00, 0xEA, 0xEA, 0xEA]; + let offset = 0x8000; + for i in 0..test_prog.len() { + let addr = i + offset; + bus.writeb(addr as u16, test_prog[i]) + } + + // hint program location to processor + bus.writeb(0xfffc, offset as Byte); + bus.writeb(0xfffc + 1, (offset >> 8) as Byte); + + let mut cpu: CPU = CPU::new(); + cpu.reset(&bus); + for _ in 0..500+10 { + cpu.clock(&mut bus); + debug!("Mem: {} {} {}", bus.readb(0x00), bus.readb(0x01), bus.readb(0x02)) + } + + +} diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 0000000..94d00ae --- /dev/null +++ b/src/types.rs @@ -0,0 +1,3 @@ +pub type Byte = u8; +pub type Word = u16; +pub type Addr = u16;