initial commit

This commit is contained in:
Daniel Bauer
2019-12-23 16:31:09 +01:00
commit d54427c2b3
9 changed files with 1349 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/target
**/*.rs.bk

394
Cargo.lock generated Normal file
View File

@@ -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"

14
Cargo.toml Normal file
View File

@@ -0,0 +1,14 @@
[package]
name = "NESemu"
version = "0.1.0"
authors = ["Daniel Bauer <bauer@cbs.tu-darmstadt.de>"]
edition = "2018"
[features]
test = []
[dependencies]
log = "*"
simple_logger = "*"
phf = { version = "0.8", features = ['macros'] }
failure = "*"

36
src/bus.rs Normal file
View File

@@ -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;
}
}

543
src/cpu.rs Normal file
View File

@@ -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<T: Bus>(&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<T: Bus>(&self, bus: &T, addr: Addr) -> Byte {
bus.readb(addr)
}
fn readw<T: Bus>(&self, bus: &T, addr: Addr) -> Word {
bus.readw(addr)
}
fn writeb<T: Bus>(&mut self, bus: &mut T, addr: Addr, data: Byte) {
bus.writeb(addr, data)
}
// read the next opcode and increment pc
fn readb_pc<T: Bus>(&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<T: Bus>(&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<T: Bus>(&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<T: Bus>(&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<T: Bus>(&mut self, bus: &T) -> (Word, bool) {
(0, false)
}
// Immediate, next byte comes from pc
fn am_IMM<T: Bus>(&mut self, bus: &T) -> (Word, bool) {
let addr = self.regs.pc;
self.regs.pc += 1;
(addr, false)
}
fn am_ZP0<T: Bus>(&mut self, bus: &T) -> (Word, bool) {
unimplemented!()
}
fn am_ZPX<T: Bus>(&mut self, bus: &T) -> (Word, bool) {
unimplemented!()
}
fn am_ZPY<T: Bus>(&mut self, bus: &T) -> (Word, bool) {
unimplemented!()
}
fn am_REL<T: Bus>(&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<T: Bus>(&mut self, bus: &T) -> (Word, bool) {
let addr = self.readw_pc(bus);
(addr, false)
}
fn am_ABX<T: Bus>(&mut self, bus: &T) -> (Word, bool) {
unimplemented!()
}
fn am_ABY<T: Bus>(&mut self, bus: &T) -> (Word, bool) {
unimplemented!()
}
fn am_IND<T: Bus>(&mut self, bus: &T) -> (Word, bool) {
unimplemented!()
}
fn am_IZX<T: Bus>(&mut self, bus: &T) -> (Word, bool) {
unimplemented!()
}
fn am_IZY<T: Bus>(&mut self, bus: &T) -> (Word, bool) {
unimplemented!()
}
// Operations
fn op_ADC<T: Bus>(&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<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_ASL<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_BCC<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_BCS<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_BEQ<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_BIT<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_BMI<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_BNE<T: Bus>(&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<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_BRK<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_BVC<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_BVS<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_CLC(&mut self) {
self.set_flag(CARRY, false);
}
fn op_CLD<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_CLI<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_CLV<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_CMP<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_CPX<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_CPY<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_DEC<T: Bus>(&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<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_INC<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_INX<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_INY<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_JMP<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_JSR<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_LDA<T: Bus>(&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<T: Bus>(&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<T: Bus>(&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<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_NOP<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_ORA<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_PHA<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_PHP<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_PLA<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_PLP<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_ROL<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_ROR<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_RTI<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_RTS<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_SBC<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_SEC<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_SED<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_SEI<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_STA<T: Bus>(&mut self, bus: &mut T, addr: Word) {
self.writeb(bus, addr, self.regs.a)
}
fn op_STX<T: Bus>(&mut self, bus: &mut T, addr: Word) {
self.writeb(bus, addr, self.regs.x)
}
fn op_STY<T: Bus>(&mut self, bus: &mut T, addr: Word) {
self.writeb(bus, addr, self.regs.y)
}
fn op_TAX<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_TAY<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_TSX<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_TXA<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_TXS<T: Bus>(&mut self, bus: &T, val: Word) {
unimplemented!()
}
fn op_TYA<T: Bus>(&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)
}
}

View File

@@ -0,0 +1,4 @@
use crate::types::*;
// ADDRESS MODES //

312
src/cpu/instructions.rs Normal file
View File

@@ -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<u8, Instruction> = 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])
}
}
}

41
src/main.rs Normal file
View File

@@ -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))
}
}

3
src/types.rs Normal file
View File

@@ -0,0 +1,3 @@
pub type Byte = u8;
pub type Word = u16;
pub type Addr = u16;