feat: add opcode logging
This commit is contained in:
parent
8c5c1c4b23
commit
96bc5e117d
31 changed files with 4461 additions and 2469 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,4 +1,4 @@
|
|||
/target
|
||||
**/target
|
||||
/roms/
|
||||
/bmp/
|
||||
config.toml
|
||||
|
|
142
Cargo.lock
generated
142
Cargo.lock
generated
|
@ -422,6 +422,12 @@ dependencies = [
|
|||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cty"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35"
|
||||
|
||||
[[package]]
|
||||
name = "d3d12"
|
||||
version = "0.4.1"
|
||||
|
@ -439,8 +445,18 @@ version = "0.10.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
"darling_core 0.10.2",
|
||||
"darling_macro 0.10.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0d720b8683f8dd83c65155f0530560cba68cd2bf395f6513a483caee57ff7f4"
|
||||
dependencies = [
|
||||
"darling_core 0.13.1",
|
||||
"darling_macro 0.13.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -453,7 +469,21 @@ dependencies = [
|
|||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"strsim 0.9.3",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a340f241d2ceed1deb47ae36c4144b2707ec7dd0b649f894cb39bb595986324"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim 0.10.0",
|
||||
"syn",
|
||||
]
|
||||
|
||||
|
@ -463,7 +493,18 @@ version = "0.10.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_core 0.10.2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72c41b3b7352feb3211a0d743dc5700a4e3b60f51bd2b368892d1e0f9a95f44b"
|
||||
dependencies = [
|
||||
"darling_core 0.13.1",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
@ -476,6 +517,7 @@ dependencies = [
|
|||
"bmp",
|
||||
"chrono",
|
||||
"config",
|
||||
"deemgee-opcode",
|
||||
"env_logger",
|
||||
"log",
|
||||
"paste",
|
||||
|
@ -487,6 +529,16 @@ dependencies = [
|
|||
"winit_input_helper",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deemgee-opcode"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"darling 0.13.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derivative"
|
||||
version = "2.2.0"
|
||||
|
@ -778,9 +830,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.108"
|
||||
version = "0.2.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119"
|
||||
checksum = "f98a04dce437184842841303488f70d0188c5f51437d2a834dc097eafa909a01"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
|
@ -852,9 +904,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.6.4"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9"
|
||||
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
@ -915,9 +967,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "naga"
|
||||
version = "0.7.1"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eda66d09f712e1f0a6ab436137da4fac312f78301f6d4ac7cb8bfe96e988734f"
|
||||
checksum = "63765d243f5d32ece09b2ff95c1f50ec7353266024a2ce89619a09e1b6aa4cce"
|
||||
dependencies = [
|
||||
"bit-set",
|
||||
"bitflags",
|
||||
|
@ -963,7 +1015,7 @@ version = "0.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05d1c6307dc424d0f65b9b06e94f88248e6305726b14729fd67a5e47b2dc481d"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"darling 0.10.2",
|
||||
"proc-macro-crate 0.1.5",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -972,9 +1024,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ndk-sys"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c44922cb3dbb1c70b5e5f443d63b64363a898564d739ba5198e3a9138442868d"
|
||||
checksum = "e1bcdd74c20ad5d95aacd60ef9ba40fdf77f767051040541df557b7a9b2a2121"
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
|
@ -1160,7 +1212,7 @@ checksum = "94ab1e297051c39cc7b7511e7e2b3ab151f14aff6a44e73bdde652b1e2190950"
|
|||
dependencies = [
|
||||
"bytemuck",
|
||||
"pollster",
|
||||
"raw-window-handle",
|
||||
"raw-window-handle 0.3.4",
|
||||
"thiserror",
|
||||
"ultraviolet",
|
||||
"wgpu",
|
||||
|
@ -1168,9 +1220,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.22"
|
||||
version = "0.3.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f"
|
||||
checksum = "d1a3ea4f0dd7f1f3e512cf97bf100819aa547f36a6eccac8dbaae839eb92363e"
|
||||
|
||||
[[package]]
|
||||
name = "pollster"
|
||||
|
@ -1199,9 +1251,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.32"
|
||||
version = "1.0.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
|
||||
checksum = "fb37d2df5df740e582f28f8560cf425f52bb267d872fe58358eadb554909f07a"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
@ -1229,11 +1281,21 @@ checksum = "63e935c45e09cc6dcf00d2f0b2d630a58f4095320223d47fc68918722f0538b6"
|
|||
|
||||
[[package]]
|
||||
name = "raw-window-handle"
|
||||
version = "0.3.3"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a441a7a6c80ad6473bd4b74ec1c9a4c951794285bf941c2126f607c72e48211"
|
||||
checksum = "e28f55143d0548dad60bb4fbdc835a3d7ac6acc3324506450c5fdd6e42903a76"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"raw-window-handle 0.4.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "raw-window-handle"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fba75eee94a9d5273a68c9e1e105d9cffe1ef700532325788389e5a83e2522b7"
|
||||
dependencies = [
|
||||
"cty",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1296,9 +1358,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.5"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
||||
checksum = "3c9613b5a66ab9ba26415184cfc41156594925a9cf3a2057e57f31ff145f6568"
|
||||
|
||||
[[package]]
|
||||
name = "safe_arch"
|
||||
|
@ -1370,9 +1432,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.71"
|
||||
version = "1.0.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "063bf466a64011ac24040a49009724ee60a57da1b437617ceb32e53ad61bfb19"
|
||||
checksum = "d0ffa0837f2dfa6fb90868c2b5468cad482e175f7dad97e7421951e663f2b527"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
|
@ -1442,10 +1504,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.81"
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -1708,15 +1776,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wgpu"
|
||||
version = "0.11.0"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d1577ecc4f6992b9e965878ac594efb24eed2bdf089c11f45b3d1c5f216e2e30"
|
||||
checksum = "eae7181fe6ba5f4b632a9079cc9e922a64555156c87def72c063f94b180c7d68"
|
||||
dependencies = [
|
||||
"arrayvec 0.7.2",
|
||||
"js-sys",
|
||||
"log",
|
||||
"parking_lot",
|
||||
"raw-window-handle",
|
||||
"raw-window-handle 0.3.4",
|
||||
"smallvec",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
|
@ -1728,9 +1796,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wgpu-core"
|
||||
version = "0.11.2"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3bdcbfa4885b32c2b1feb2faeb8b6a76065b752b8f08751b82f994e937687f46"
|
||||
checksum = "35600627b6c718ad0e23ed75fb6140bfe32cdf21c8f539ce3c9ab8180e2cb38e"
|
||||
dependencies = [
|
||||
"arrayvec 0.7.2",
|
||||
"bitflags",
|
||||
|
@ -1741,7 +1809,7 @@ dependencies = [
|
|||
"naga",
|
||||
"parking_lot",
|
||||
"profiling",
|
||||
"raw-window-handle",
|
||||
"raw-window-handle 0.3.4",
|
||||
"smallvec",
|
||||
"thiserror",
|
||||
"wgpu-hal",
|
||||
|
@ -1750,9 +1818,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wgpu-hal"
|
||||
version = "0.11.4"
|
||||
version = "0.11.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e493835d9edb153d5c8a9d8d016e1811dbe32ddb707a110be1453c7b051d3ec"
|
||||
checksum = "af28b29ef0b44cd22dd9895d4349b9d5a687df42f58da234871198637eabe328"
|
||||
dependencies = [
|
||||
"arrayvec 0.7.2",
|
||||
"ash",
|
||||
|
@ -1777,7 +1845,7 @@ dependencies = [
|
|||
"parking_lot",
|
||||
"profiling",
|
||||
"range-alloc",
|
||||
"raw-window-handle",
|
||||
"raw-window-handle 0.3.4",
|
||||
"renderdoc-sys",
|
||||
"thiserror",
|
||||
"wasm-bindgen",
|
||||
|
@ -1860,7 +1928,7 @@ dependencies = [
|
|||
"objc",
|
||||
"parking_lot",
|
||||
"percent-encoding",
|
||||
"raw-window-handle",
|
||||
"raw-window-handle 0.3.4",
|
||||
"scopeguard",
|
||||
"serde 1.0.130",
|
||||
"smithay-client-toolkit",
|
||||
|
|
24
Cargo.toml
24
Cargo.toml
|
@ -1,21 +1,3 @@
|
|||
[package]
|
||||
name = "deemgee"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
argh = "0.1.6"
|
||||
bmp = "0.5.0"
|
||||
chrono = "0.4.19"
|
||||
config = "0.11.0"
|
||||
env_logger = "0.9.0"
|
||||
log = "0.4.14"
|
||||
paste = "1.0.6"
|
||||
pixels = "0.7.0"
|
||||
serde = { version = "1.0.130", features = ["derive"] }
|
||||
sha1 = { version = "0.6.0", features = ["std"] }
|
||||
thiserror = "1.0.30"
|
||||
winit = { version = "0.25.0", features = ["serde"] }
|
||||
winit_input_helper = "0.10.0"
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = ["deemgee", "deemgee-opcode"]
|
|
@ -8,4 +8,5 @@ down = "Down"
|
|||
left = "Left"
|
||||
right = "Right"
|
||||
pause = "P"
|
||||
exit = "Escape"
|
||||
exit = "Escape"
|
||||
log_ops = "L"
|
14
deemgee-opcode/Cargo.toml
Normal file
14
deemgee-opcode/Cargo.toml
Normal file
|
@ -0,0 +1,14 @@
|
|||
[package]
|
||||
name = "deemgee-opcode"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
darling = "0.13.1"
|
||||
proc-macro2 = "1.0.33"
|
||||
quote = "1.0.10"
|
||||
syn = "1.0.82"
|
111
deemgee-opcode/src/lib.rs
Normal file
111
deemgee-opcode/src/lib.rs
Normal file
|
@ -0,0 +1,111 @@
|
|||
use proc_macro::TokenStream;
|
||||
use proc_macro2::Ident;
|
||||
use syn::{
|
||||
braced, parse::Parse, parse_macro_input, punctuated::Punctuated, Expr, ExprMacro, LitBool,
|
||||
LitInt, LitStr, Stmt, Token,
|
||||
};
|
||||
|
||||
struct OpcodeImpl {
|
||||
pub cycle: LitInt,
|
||||
pub block: syn::Block,
|
||||
}
|
||||
|
||||
impl Parse for OpcodeImpl {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let cycle = input.parse()?;
|
||||
input.parse::<syn::Token!(=>)>()?;
|
||||
let block = input.parse()?;
|
||||
Ok(Self { cycle, block })
|
||||
}
|
||||
}
|
||||
|
||||
struct OpcodeArgs {
|
||||
pub name: syn::Ident,
|
||||
pub opcode: LitInt,
|
||||
pub readable: Expr,
|
||||
pub extended: LitBool,
|
||||
pub implementation: Punctuated<OpcodeImpl, syn::Token!(,)>,
|
||||
}
|
||||
|
||||
impl Parse for OpcodeArgs {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let name = input.parse()?;
|
||||
input.parse::<Token![,]>()?;
|
||||
let opcode = input.parse()?;
|
||||
input.parse::<Token![,]>()?;
|
||||
let readable = input.parse()?;
|
||||
input.parse::<Token![,]>()?;
|
||||
let extended = input.parse()?;
|
||||
input.parse::<Token![,]>()?;
|
||||
let implementation_pb;
|
||||
braced!(implementation_pb in input);
|
||||
let implementation = Punctuated::parse_separated_nonempty(&implementation_pb)?;
|
||||
Ok(Self { name, opcode, readable, extended, implementation })
|
||||
}
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn opcode(item: TokenStream) -> TokenStream {
|
||||
let OpcodeArgs { name, opcode, readable, extended, implementation } =
|
||||
parse_macro_input!(item as OpcodeArgs);
|
||||
|
||||
let opcode = opcode.base10_parse::<u8>().expect("Failed to parse opcode as u8");
|
||||
|
||||
let fn_sig = quote::quote! {
|
||||
pub fn #name(state: &mut Gameboy) -> CycleResult
|
||||
};
|
||||
|
||||
let mut cycle = Vec::new();
|
||||
let mut block = Vec::new();
|
||||
|
||||
for op_impl in implementation {
|
||||
cycle.push(op_impl.cycle);
|
||||
block.push(op_impl.block);
|
||||
}
|
||||
|
||||
/*if !cycle.is_empty() {
|
||||
if cycle[0].base10_parse::<u8>().expect("Expected u8") == 0u8 {
|
||||
block[0].stmts.insert(0, Stmt::Semi(Expr::Macro(ExprMacro::)))
|
||||
} else {
|
||||
|
||||
}
|
||||
}*/
|
||||
|
||||
let regs = quote::quote! {
|
||||
log::trace!("-- Registers --\nAF: {:04X}\nBC: {:04X}\nDE: {:04X}\nHL: {:04X}\nSP: {:04X}\nPC: {:04X}\nZero: {}\nSubtract: {}\nHalf-Carry: {}\nCarry: {}", state.registers.get_af(), state.registers.get_bc(), state.registers.get_de(), state.registers.get_hl(), state.registers.get_sp(), state.registers.pc, state.registers.get_zero(), state.registers.get_subtract(), state.registers.get_half_carry(), state.registers.get_carry());
|
||||
};
|
||||
|
||||
let match_statement = quote::quote! {
|
||||
match state.registers.cycle {
|
||||
#(#cycle => {
|
||||
#block
|
||||
}),*
|
||||
cycle => unreachable!("Entered cycle {} for opcode {}", cycle, #readable),
|
||||
}
|
||||
};
|
||||
|
||||
let log = if extended.value {
|
||||
quote::quote! {
|
||||
if state.registers.cycle == 1 && state.log_instructions {
|
||||
log::debug!("Prefixed OP {} ({:#02X})", #readable, #opcode);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote::quote! {
|
||||
if state.registers.cycle == 0 && state.log_instructions {
|
||||
log::debug!("OP {} ({:#02X})", #readable, #opcode);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let out = quote::quote! {
|
||||
#fn_sig {
|
||||
#log
|
||||
#regs
|
||||
|
||||
#match_statement
|
||||
}
|
||||
};
|
||||
|
||||
out.into()
|
||||
}
|
1923
deemgee/Cargo.lock
generated
Normal file
1923
deemgee/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
22
deemgee/Cargo.toml
Normal file
22
deemgee/Cargo.toml
Normal file
|
@ -0,0 +1,22 @@
|
|||
[package]
|
||||
name = "deemgee"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
argh = "0.1.6"
|
||||
bmp = "0.5.0"
|
||||
chrono = "0.4.19"
|
||||
config = "0.11.0"
|
||||
deemgee-opcode = { path = "../deemgee-opcode" }
|
||||
env_logger = "0.9.0"
|
||||
log = "0.4.14"
|
||||
paste = "1.0.6"
|
||||
pixels = "0.7.0"
|
||||
serde = { version = "1.0.130", features = ["derive"] }
|
||||
sha1 = { version = "0.6.0", features = ["std"] }
|
||||
thiserror = "1.0.30"
|
||||
winit = { version = "0.25.0", features = ["serde"] }
|
||||
winit_input_helper = "0.10.0"
|
|
@ -52,6 +52,7 @@ pub struct Gameboy {
|
|||
pub mem_read_breakpoints: [bool; u16::MAX as usize + 1],
|
||||
pub mem_write_breakpoints: [bool; u16::MAX as usize + 1],
|
||||
trigger_bp: bool,
|
||||
pub log_instructions: bool,
|
||||
}
|
||||
|
||||
impl Gameboy {
|
||||
|
@ -72,6 +73,7 @@ impl Gameboy {
|
|||
mem_read_breakpoints: [false; u16::MAX as usize + 1],
|
||||
mem_write_breakpoints: [false; u16::MAX as usize + 1],
|
||||
trigger_bp: false,
|
||||
log_instructions: false,
|
||||
}
|
||||
}
|
||||
|
|
@ -166,7 +166,6 @@ pub fn tick_cpu(state: &mut Gameboy) {
|
|||
Some(opcode) => opcode,
|
||||
None => match state.registers.mem_read_hold.take() {
|
||||
Some(opcode) => {
|
||||
log::debug!("Executing instruction {:#X}", opcode);
|
||||
state.registers.current_opcode = Some(opcode);
|
||||
opcode
|
||||
}
|
865
deemgee/src/gameboy/cpu/alu.rs
Normal file
865
deemgee/src/gameboy/cpu/alu.rs
Normal file
|
@ -0,0 +1,865 @@
|
|||
use deemgee_opcode::opcode;
|
||||
|
||||
use super::CycleResult;
|
||||
use crate::gameboy::Gameboy;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CarryResult {
|
||||
pub result: u8,
|
||||
pub half_carry: bool,
|
||||
pub carry: bool,
|
||||
}
|
||||
|
||||
pub fn sub_with_carry(lhs: u8, rhs: u8, carry: bool) -> CarryResult {
|
||||
let carry_u8 = carry as u8;
|
||||
|
||||
let (first_res, first_carry) = lhs.overflowing_sub(rhs);
|
||||
let (result, second_carry) = first_res.overflowing_sub(carry_u8);
|
||||
|
||||
let carry = first_carry || second_carry;
|
||||
|
||||
let first_hc_res = (lhs & 0xF).overflowing_sub(rhs & 0xF).0;
|
||||
let first_half_carry = (first_hc_res >> 4) & 0b1 == 1;
|
||||
let second_half_carry = ((first_hc_res.overflowing_sub(carry_u8).0) >> 4) & 0b1 == 1;
|
||||
|
||||
let half_carry = first_half_carry || second_half_carry;
|
||||
|
||||
CarryResult { result, carry, half_carry }
|
||||
}
|
||||
|
||||
pub fn add_with_carry(lhs: u8, rhs: u8, carry: bool) -> CarryResult {
|
||||
let carry_u8 = carry as u8;
|
||||
|
||||
let (first_res, first_carry) = lhs.overflowing_add(rhs);
|
||||
let (result, second_carry) = first_res.overflowing_add(carry_u8);
|
||||
|
||||
let carry = first_carry || second_carry;
|
||||
|
||||
let first_hc_res = (lhs & 0xF) + (rhs & 0xF);
|
||||
let first_half_carry = (first_hc_res >> 4) & 0b1 == 1;
|
||||
let second_half_carry = ((first_hc_res + carry_u8) >> 4) & 0b1 == 1;
|
||||
|
||||
let half_carry = first_half_carry || second_half_carry;
|
||||
|
||||
CarryResult { result, carry, half_carry }
|
||||
}
|
||||
|
||||
pub fn add(lhs: u8, rhs: u8) -> CarryResult {
|
||||
let (result, carry) = lhs.overflowing_add(rhs);
|
||||
let half_carry = (((lhs & 0xF) + (rhs & 0xF)) >> 4) & 0b1 == 1;
|
||||
|
||||
CarryResult { result, carry, half_carry }
|
||||
}
|
||||
|
||||
pub fn sub(lhs: u8, rhs: u8) -> CarryResult {
|
||||
let (result, carry) = lhs.overflowing_sub(rhs);
|
||||
let half_carry = (((lhs & 0xF).overflowing_sub(rhs & 0xF).0) >> 4) & 0b1 == 1;
|
||||
|
||||
CarryResult { result, carry, half_carry }
|
||||
}
|
||||
|
||||
macro_rules! define_xor_reg {
|
||||
($op:literal, $reg:ident) => {
|
||||
paste::paste! {
|
||||
opcode!([<xor_a_ $reg>], $op, std::concat!("XOR A,",std::stringify!($reg)), false, {
|
||||
0 => {
|
||||
state.registers.a ^= state.registers.$reg;
|
||||
state.registers.set_zero(state.registers.a == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(false);
|
||||
state.registers.set_carry(false);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_xor_reg!(0xAF, a);
|
||||
define_xor_reg!(0xA8, b);
|
||||
define_xor_reg!(0xA9, c);
|
||||
define_xor_reg!(0xAA, d);
|
||||
define_xor_reg!(0xAB, e);
|
||||
define_xor_reg!(0xAC, h);
|
||||
define_xor_reg!(0xAD, l);
|
||||
|
||||
opcode!(xor_a_deref_hl, 0xAE, "XOR A,(HL)", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.get_hl());
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
state.registers.a ^= state.registers.take_mem();
|
||||
state.registers.set_zero(state.registers.a == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(false);
|
||||
state.registers.set_carry(false);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(xor_a_imm_u8, 0xEE, "XOR A,u8", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc + 1);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
state.registers.a ^= state.registers.take_mem();
|
||||
state.registers.set_zero(state.registers.a == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(false);
|
||||
state.registers.set_carry(false);
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
macro_rules! define_sbc_reg {
|
||||
($op:literal, $reg:ident) => {
|
||||
paste::paste! {
|
||||
opcode!([<sbc_a_ $reg>], $op, std::concat!("SBC A,", std::stringify!($reg)), false, {
|
||||
0 => {
|
||||
let CarryResult { result, half_carry, carry } = sub_with_carry(state.registers.a, state.registers.$reg, state.registers.get_carry());
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(true);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.set_carry(carry);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_sbc_reg!(0x9F, a);
|
||||
define_sbc_reg!(0x98, b);
|
||||
define_sbc_reg!(0x99, c);
|
||||
define_sbc_reg!(0x9A, d);
|
||||
define_sbc_reg!(0x9B, e);
|
||||
define_sbc_reg!(0x9C, h);
|
||||
define_sbc_reg!(0x9D, l);
|
||||
|
||||
opcode!(sbc_a_deref_hl, 0x9E, "SBC A,(HL)", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.get_hl());
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let CarryResult { result, half_carry, carry } = sub_with_carry(
|
||||
state.registers.a,
|
||||
state.registers.take_mem(),
|
||||
state.registers.get_carry(),
|
||||
);
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(true);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.set_carry(carry);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(sbc_a_imm_u8, 0xDE, "SBC A,u8", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc + 1);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let CarryResult { result, half_carry, carry } = sub_with_carry(
|
||||
state.registers.a,
|
||||
state.registers.take_mem(),
|
||||
state.registers.get_carry(),
|
||||
);
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(true);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.set_carry(carry);
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
macro_rules! define_add_reg {
|
||||
($op:literal, $reg:ident) => {
|
||||
paste::paste! {
|
||||
opcode!([<add_a_ $reg>], $op, std::concat!("ADD A,", std::stringify!($reg)), false, {
|
||||
0 => {
|
||||
let CarryResult { result, half_carry, carry } = add(state.registers.a, state.registers.$reg);
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.set_carry(carry);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_add_reg!(0x87, a);
|
||||
define_add_reg!(0x80, b);
|
||||
define_add_reg!(0x81, c);
|
||||
define_add_reg!(0x82, d);
|
||||
define_add_reg!(0x83, e);
|
||||
define_add_reg!(0x84, h);
|
||||
define_add_reg!(0x85, l);
|
||||
|
||||
opcode!(add_a_deref_hl, 0x86, "ADD A,(HL)", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.get_hl());
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let CarryResult { result, half_carry, carry } =
|
||||
add(state.registers.a, state.registers.take_mem());
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.set_carry(carry);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(add_a_imm_u8, 0xC6, "ADD A,u8", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc + 1);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let CarryResult { result, half_carry, carry } =
|
||||
add(state.registers.a, state.registers.take_mem());
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.set_carry(carry);
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
macro_rules! define_adc_reg {
|
||||
($op:literal, $reg:ident) => {
|
||||
paste::paste! {
|
||||
opcode!([<adc_a_ $reg>], $op, std::concat!("ADC A,", std::stringify!($reg)), false, {
|
||||
0 => {
|
||||
let CarryResult { result, half_carry, carry } = add_with_carry(state.registers.a, state.registers.$reg, state.registers.get_carry());
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.set_carry(carry);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_adc_reg!(0x8F, a);
|
||||
define_adc_reg!(0x88, b);
|
||||
define_adc_reg!(0x89, c);
|
||||
define_adc_reg!(0x8A, d);
|
||||
define_adc_reg!(0x8B, e);
|
||||
define_adc_reg!(0x8C, h);
|
||||
define_adc_reg!(0x8D, l);
|
||||
|
||||
opcode!(adc_a_deref_hl, 0x8E, "ADC A,(HL)", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.get_hl());
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let CarryResult { result, half_carry, carry } = add_with_carry(
|
||||
state.registers.a,
|
||||
state.registers.take_mem(),
|
||||
state.registers.get_carry(),
|
||||
);
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.set_carry(carry);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(adc_a_imm_u8, 0xCE, "ADC A,u8", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc + 1);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let CarryResult { result, half_carry, carry } = add_with_carry(
|
||||
state.registers.a,
|
||||
state.registers.take_mem(),
|
||||
state.registers.get_carry(),
|
||||
);
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.set_carry(carry);
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
macro_rules! define_sub_reg {
|
||||
($op:literal, $reg:ident) => {
|
||||
paste::paste! {
|
||||
opcode!([<sub_a_ $reg>], $op, std::concat!("SUB A,", std::stringify!($reg)), false, {
|
||||
0 => {
|
||||
let CarryResult { result, half_carry, carry } = sub(state.registers.a, state.registers.$reg);
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(true);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.set_carry(carry);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_sub_reg!(0x97, a);
|
||||
define_sub_reg!(0x90, b);
|
||||
define_sub_reg!(0x91, c);
|
||||
define_sub_reg!(0x92, d);
|
||||
define_sub_reg!(0x93, e);
|
||||
define_sub_reg!(0x94, h);
|
||||
define_sub_reg!(0x95, l);
|
||||
|
||||
opcode!(sub_a_deref_hl, 0x96, "SUB A,(HL)", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.get_hl());
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let CarryResult { result, half_carry, carry } =
|
||||
sub(state.registers.a, state.registers.take_mem());
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(true);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.set_carry(carry);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(sub_a_imm_u8, 0xD6, "SUB A,u8", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc + 1);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let CarryResult { result, half_carry, carry } =
|
||||
sub(state.registers.a, state.registers.take_mem());
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(true);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.set_carry(carry);
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
macro_rules! define_inc_reg {
|
||||
($op:literal, $reg:ident) => {
|
||||
paste::paste! {
|
||||
opcode!([<inc_ $reg>], $op, std::concat!("INC ", std::stringify!($reg)), false, {
|
||||
0 => {
|
||||
let CarryResult { result, half_carry, .. } = add(
|
||||
state.registers.$reg,
|
||||
1,
|
||||
);
|
||||
|
||||
state.registers.$reg = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_inc_reg!(0x04, b);
|
||||
define_inc_reg!(0x0C, c);
|
||||
define_inc_reg!(0x14, d);
|
||||
define_inc_reg!(0x1C, e);
|
||||
define_inc_reg!(0x24, h);
|
||||
define_inc_reg!(0x2C, l);
|
||||
define_inc_reg!(0x3C, a);
|
||||
|
||||
opcode!(inc_deref_hl, 0x34, "INC (HL)", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.get_hl());
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let CarryResult { result, half_carry, .. } = add(state.registers.take_mem(), 1);
|
||||
|
||||
state.cpu_write_u8(state.registers.get_hl(), result);
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
2 => {
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
macro_rules! define_dec_reg {
|
||||
($op:literal, $reg:ident) => {
|
||||
paste::paste! {
|
||||
opcode!([<dec_ $reg>], $op, std::concat!("DEC ", std::stringify!($reg)), false, {
|
||||
0 => {
|
||||
let CarryResult { result, half_carry, .. } = sub(
|
||||
state.registers.$reg,
|
||||
1,
|
||||
);
|
||||
|
||||
state.registers.$reg = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(true);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_dec_reg!(0x05, b);
|
||||
define_dec_reg!(0x0D, c);
|
||||
define_dec_reg!(0x15, d);
|
||||
define_dec_reg!(0x1D, e);
|
||||
define_dec_reg!(0x25, h);
|
||||
define_dec_reg!(0x2D, l);
|
||||
define_dec_reg!(0x3D, a);
|
||||
|
||||
opcode!(dec_deref_hl, 0x35, "DEC (HL)", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.get_hl());
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let CarryResult { result, half_carry, .. } = sub(state.registers.take_mem(), 1);
|
||||
|
||||
state.cpu_write_u8(state.registers.get_hl(), result);
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
2 => {
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(rla, 0x17, "RLA", false, {
|
||||
0 => {
|
||||
let carry = state.registers.a >> 7 == 1;
|
||||
state.registers.a <<= 1;
|
||||
|
||||
if state.registers.get_carry() {
|
||||
state.registers.a = state.registers.a.wrapping_add(1);
|
||||
}
|
||||
|
||||
state.registers.set_zero(false);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(false);
|
||||
state.registers.set_carry(carry);
|
||||
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
macro_rules! define_inc_u16_reg {
|
||||
($op:literal, $lreg:ident, $rreg:ident) => {
|
||||
paste::paste! {
|
||||
opcode!([<inc_ $lreg $rreg>], $op, std::concat!("INC ", std::stringify!($lreg), std::stringify!($rreg)), false, {
|
||||
0 => {
|
||||
let (res, carry) = state.registers.$rreg.overflowing_add(1);
|
||||
state.registers.$rreg = res;
|
||||
state.registers.set_hold(carry as u16);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
if state.registers.take_hold() != 0 {
|
||||
let (res, _) = state.registers.$lreg.overflowing_add(1);
|
||||
state.registers.$lreg = res;
|
||||
}
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
define_inc_u16_reg!(0x03, b, c);
|
||||
define_inc_u16_reg!(0x13, d, e);
|
||||
define_inc_u16_reg!(0x23, h, l);
|
||||
|
||||
opcode!(inc_sp, 0x33, "INC SP", false, {
|
||||
0 => {
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let (res, _) = state.registers.sp.overflowing_add(1);
|
||||
state.registers.sp = res;
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
macro_rules! define_dec_u16_reg {
|
||||
($op:literal, $lreg:ident, $rreg:ident) => {
|
||||
paste::paste! {
|
||||
opcode!([<dec_ $lreg $rreg>], $op, std::concat!("DEC ", std::stringify!($lreg), std::stringify!($rreg)), false, {
|
||||
0 => {
|
||||
let (res, carry) = state.registers.$rreg.overflowing_sub(1);
|
||||
state.registers.$rreg = res;
|
||||
state.registers.set_hold(carry as u16);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
if state.registers.take_hold() != 0 {
|
||||
let (res, _) = state.registers.$lreg.overflowing_sub(1);
|
||||
state.registers.$lreg = res;
|
||||
}
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_dec_u16_reg!(0x0B, b, c);
|
||||
define_dec_u16_reg!(0x1B, d, e);
|
||||
define_dec_u16_reg!(0x2B, h, l);
|
||||
|
||||
opcode!(dec_sp, 0x3B, "DEC SP", false, {
|
||||
0 => {
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let (res, _) = state.registers.sp.overflowing_sub(1);
|
||||
state.registers.sp = res;
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
macro_rules! define_cp_reg_reg {
|
||||
($op:literal, $lreg:ident, $rreg:ident) => {
|
||||
paste::paste! {
|
||||
opcode!([<cp_ $lreg _ $rreg>], $op, std::concat!("CP ", std::stringify!($lreg), ",", std::stringify!($rreg)), false, {
|
||||
0 => {
|
||||
let CarryResult { result, half_carry, carry } = sub(state.registers.$lreg, state.registers.$rreg);
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(true);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.set_carry(carry);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_cp_reg_reg!(0xB8, a, b);
|
||||
define_cp_reg_reg!(0xB9, a, c);
|
||||
define_cp_reg_reg!(0xBA, a, d);
|
||||
define_cp_reg_reg!(0xBB, a, e);
|
||||
define_cp_reg_reg!(0xBC, a, h);
|
||||
define_cp_reg_reg!(0xBD, a, l);
|
||||
define_cp_reg_reg!(0xBF, a, a);
|
||||
|
||||
opcode!(cp_a_imm_u8, 0xFE, "CP A,u8", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let CarryResult { result, half_carry, carry } =
|
||||
sub(state.registers.a, state.registers.take_mem());
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(true);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.set_carry(carry);
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(cp_a_deref_hl, 0xBE, "CP A,(HL)", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.get_hl());
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let CarryResult { result, half_carry, carry } =
|
||||
sub(state.registers.a, state.registers.take_mem());
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(true);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.set_carry(carry);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
macro_rules! define_or_reg {
|
||||
($op:literal, $reg:ident) => {
|
||||
paste::paste! {
|
||||
opcode!([<or_a_ $reg>], $op, std::concat!("OR A,", std::stringify!($reg)), false, {
|
||||
0 => {
|
||||
let result = state.registers.a | state.registers.$reg;
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(false);
|
||||
state.registers.set_carry(false);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_or_reg!(0xB7, a);
|
||||
define_or_reg!(0xB0, b);
|
||||
define_or_reg!(0xB1, c);
|
||||
define_or_reg!(0xB2, d);
|
||||
define_or_reg!(0xB3, e);
|
||||
define_or_reg!(0xB4, h);
|
||||
define_or_reg!(0xB5, l);
|
||||
|
||||
opcode!(or_a_deref_hl, 0xB6, "OR A,(HL)", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.get_hl());
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let result = state.registers.a | state.registers.take_mem();
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(false);
|
||||
state.registers.set_carry(false);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(or_a_imm_u8, 0xF6, "OR A,u8", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc + 1);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let result = state.registers.a | state.registers.take_mem();
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(false);
|
||||
state.registers.set_carry(false);
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
macro_rules! define_and_reg {
|
||||
($op:literal, $reg:ident) => {
|
||||
paste::paste! {
|
||||
opcode!([<and_a_ $reg>], $op, std::concat!("AND A,", std::stringify!($reg)), false, {
|
||||
0 => {
|
||||
let result = state.registers.a & state.registers.$reg;
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(true);
|
||||
state.registers.set_carry(false);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_and_reg!(0xA7, a);
|
||||
define_and_reg!(0xA0, b);
|
||||
define_and_reg!(0xA1, c);
|
||||
define_and_reg!(0xA2, d);
|
||||
define_and_reg!(0xA3, e);
|
||||
define_and_reg!(0xA4, h);
|
||||
define_and_reg!(0xA5, l);
|
||||
|
||||
opcode!(and_a_deref_hl, 0xA6, "AND A,(hl)", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.get_hl());
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let result = state.registers.a & state.registers.take_mem();
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(true);
|
||||
state.registers.set_carry(false);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(and_a_imm_u8, 0xE6, "AND A,u8", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc + 1);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let result = state.registers.a & state.registers.take_mem();
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(true);
|
||||
state.registers.set_carry(false);
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(cpl, 0x2F, "CPL", false, {
|
||||
0 => {
|
||||
state.registers.a = !state.registers.a;
|
||||
state.registers.set_subtract(true);
|
||||
state.registers.set_half_carry(true);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(ccf, 0x3F, "CCF", false, {
|
||||
0 => {
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(false);
|
||||
state.registers.set_carry(!state.registers.get_carry());
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(scf, 0x37, "SCF", false, {
|
||||
0 => {
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(false);
|
||||
state.registers.set_carry(true);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
macro_rules! define_add_hl_u16_reg {
|
||||
($op:literal, $lreg:ident, $rreg:ident) => {
|
||||
paste::paste! {
|
||||
opcode!([<add_hl_ $lreg $rreg>], $op, std::concat!("ADD HL, ", std::stringify!($lreg), std::stringify!($rreg)), false, {
|
||||
0 => {
|
||||
let CarryResult { result, carry, .. } = add(state.registers.l, state.registers.$rreg);
|
||||
state.registers.l = result;
|
||||
state.registers.set_hold(carry as u16);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
if state.registers.take_hold() != 0 {
|
||||
let CarryResult { result, carry, half_carry } = add(state.registers.h, state.registers.$lreg);
|
||||
state.registers.h = result;
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.set_carry(carry);
|
||||
} else {
|
||||
state.registers.set_half_carry(false);
|
||||
state.registers.set_carry(false);
|
||||
}
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_add_hl_u16_reg!(0x09, b, c);
|
||||
define_add_hl_u16_reg!(0x19, d, e);
|
||||
define_add_hl_u16_reg!(0x29, h, l);
|
||||
opcode!(add_hl_sp, 0x39, "ADD HL, SP", false, {
|
||||
0 => {
|
||||
let CarryResult { result, carry, .. } =
|
||||
add(state.registers.l, state.registers.sp as u8);
|
||||
state.registers.l = result;
|
||||
state.registers.set_hold(carry as u16);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
if state.registers.take_hold() != 0 {
|
||||
let CarryResult { result, carry, half_carry } =
|
||||
add(state.registers.h, (state.registers.sp >> 8) as u8);
|
||||
state.registers.h = result;
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.set_carry(carry);
|
||||
} else {
|
||||
state.registers.set_half_carry(false);
|
||||
state.registers.set_carry(false);
|
||||
}
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
661
deemgee/src/gameboy/cpu/flow.rs
Normal file
661
deemgee/src/gameboy/cpu/flow.rs
Normal file
|
@ -0,0 +1,661 @@
|
|||
use deemgee_opcode::opcode;
|
||||
|
||||
use super::CycleResult;
|
||||
use crate::gameboy::Gameboy;
|
||||
|
||||
opcode!(jr_nz_i8, 0x20, "JR NZ,i8", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
if state.registers.get_zero() {
|
||||
state.registers.take_mem();
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
},
|
||||
2 => {
|
||||
let relative = state.registers.take_mem() as i8;
|
||||
|
||||
if relative >= 0 {
|
||||
state.registers.pc = state.registers.pc.overflowing_add(relative as u16).0;
|
||||
} else {
|
||||
state.registers.pc = state.registers.pc.overflowing_sub(relative.abs() as u16).0;
|
||||
}
|
||||
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(jr_nc_i8, 0x30, "JR NC,i8", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
if state.registers.get_carry() {
|
||||
state.registers.take_mem();
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
},
|
||||
2 => {
|
||||
let relative = state.registers.take_mem() as i8;
|
||||
|
||||
if relative >= 0 {
|
||||
state.registers.pc = state.registers.pc.overflowing_add(relative as u16).0;
|
||||
} else {
|
||||
state.registers.pc = state.registers.pc.overflowing_sub(relative.abs() as u16).0;
|
||||
}
|
||||
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(jr_z_i8, 0x28, "JR Z,i8", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
if !state.registers.get_zero() {
|
||||
state.registers.take_mem();
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
},
|
||||
2 => {
|
||||
let relative = state.registers.take_mem() as i8;
|
||||
|
||||
if relative >= 0 {
|
||||
state.registers.pc = state.registers.pc.overflowing_add(relative as u16).0;
|
||||
} else {
|
||||
state.registers.pc = state.registers.pc.overflowing_sub(relative.abs() as u16).0;
|
||||
}
|
||||
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(jr_c_i8, 0x38, "JR C,i8", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
if !state.registers.get_carry() {
|
||||
state.registers.take_mem();
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
},
|
||||
2 => {
|
||||
let relative = state.registers.take_mem() as i8;
|
||||
|
||||
if relative >= 0 {
|
||||
state.registers.pc = state.registers.pc.overflowing_add(relative as u16).0;
|
||||
} else {
|
||||
state.registers.pc = state.registers.pc.overflowing_sub(relative.abs() as u16).0;
|
||||
}
|
||||
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(jr_i8, 0x18, "JR i8", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
2 => {
|
||||
let relative = state.registers.take_mem() as i8;
|
||||
|
||||
if relative >= 0 {
|
||||
state.registers.pc = state.registers.pc.overflowing_add(relative as u16).0;
|
||||
} else {
|
||||
state.registers.pc = state.registers.pc.overflowing_sub(relative.abs() as u16).0;
|
||||
}
|
||||
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(jp_u16, 0xC3, "JP u16", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let lower = state.registers.take_mem() as u16;
|
||||
state.registers.set_hold(lower);
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(2).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
2 => {
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
3 => {
|
||||
let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
|
||||
state.registers.pc = address;
|
||||
state.registers.opcode_bytecount = Some(0);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(jp_hl, 0xE9, "JP HL", false, {
|
||||
0 => {
|
||||
state.registers.pc = state.registers.get_hl();
|
||||
state.registers.opcode_bytecount = Some(0);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(jp_nz_u16, 0xC2, "JP NZ,u16", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let lower = state.registers.take_mem() as u16;
|
||||
state.registers.set_hold(lower);
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(2).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
2 => {
|
||||
if state.registers.get_zero() {
|
||||
state.registers.take_mem();
|
||||
state.registers.take_hold();
|
||||
state.registers.opcode_bytecount = Some(3);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
},
|
||||
3 => {
|
||||
let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
|
||||
state.registers.pc = address;
|
||||
state.registers.opcode_bytecount = Some(0);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(jp_nc_u16, 0xD2, "JP NC,u16", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let lower = state.registers.take_mem() as u16;
|
||||
state.registers.set_hold(lower);
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(2).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
2 => {
|
||||
if state.registers.get_carry() {
|
||||
state.registers.take_mem();
|
||||
state.registers.take_hold();
|
||||
state.registers.opcode_bytecount = Some(3);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
},
|
||||
3 => {
|
||||
let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
|
||||
state.registers.pc = address;
|
||||
state.registers.opcode_bytecount = Some(0);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(jp_z_u16, 0xCA, "JP Z,u16", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let lower = state.registers.take_mem() as u16;
|
||||
state.registers.set_hold(lower);
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(2).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
2 => {
|
||||
if !state.registers.get_zero() {
|
||||
state.registers.take_mem();
|
||||
state.registers.take_hold();
|
||||
state.registers.opcode_bytecount = Some(3);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
},
|
||||
3 => {
|
||||
let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
|
||||
state.registers.pc = address;
|
||||
state.registers.opcode_bytecount = Some(0);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(jp_c_u16, 0xDA, "JP C,u16", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let lower = state.registers.take_mem() as u16;
|
||||
state.registers.set_hold(lower);
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(2).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
2 => {
|
||||
if !state.registers.get_carry() {
|
||||
state.registers.take_mem();
|
||||
state.registers.take_hold();
|
||||
state.registers.opcode_bytecount = Some(3);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
},
|
||||
3 => {
|
||||
let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
|
||||
state.registers.pc = address;
|
||||
state.registers.opcode_bytecount = Some(0);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(call_u16, 0xCD, "CALL u16", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let lower = state.registers.take_mem() as u16;
|
||||
state.registers.set_hold(lower);
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(2).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
2 => {
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
3 => {
|
||||
state.cpu_push_stack((state.registers.pc.overflowing_add(3).0 >> 8) as u8);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
4 => {
|
||||
state.cpu_push_stack(state.registers.pc.overflowing_add(3).0 as u8);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
5 => {
|
||||
let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
|
||||
state.registers.pc = address;
|
||||
state.registers.opcode_bytecount = Some(0);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(call_nz_u16, 0xC4, "CALL NZ,u16", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let lower = state.registers.take_mem() as u16;
|
||||
state.registers.set_hold(lower);
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(2).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
2 => {
|
||||
if state.registers.get_zero() {
|
||||
state.registers.take_mem();
|
||||
state.registers.take_hold();
|
||||
state.registers.opcode_bytecount = Some(3);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
},
|
||||
3 => {
|
||||
state.cpu_push_stack((state.registers.pc.overflowing_add(3).0 >> 8) as u8);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
4 => {
|
||||
state.cpu_push_stack(state.registers.pc.overflowing_add(3).0 as u8);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
5 => {
|
||||
let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
|
||||
state.registers.pc = address;
|
||||
state.registers.opcode_bytecount = Some(0);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(call_nc_u16, 0xD4, "CALL NC,u16", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let lower = state.registers.take_mem() as u16;
|
||||
state.registers.set_hold(lower);
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(2).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
2 => {
|
||||
if state.registers.get_carry() {
|
||||
state.registers.take_mem();
|
||||
state.registers.take_hold();
|
||||
state.registers.opcode_bytecount = Some(3);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
},
|
||||
3 => {
|
||||
state.cpu_push_stack((state.registers.pc.overflowing_add(3).0 >> 8) as u8);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
4 => {
|
||||
state.cpu_push_stack(state.registers.pc.overflowing_add(3).0 as u8);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
5 => {
|
||||
let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
|
||||
state.registers.pc = address;
|
||||
state.registers.opcode_bytecount = Some(0);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(call_z_u16, 0xCC, "CALL Z,u16", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let lower = state.registers.take_mem() as u16;
|
||||
state.registers.set_hold(lower);
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(2).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
2 => {
|
||||
if !state.registers.get_zero() {
|
||||
state.registers.take_mem();
|
||||
state.registers.take_hold();
|
||||
state.registers.opcode_bytecount = Some(3);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
},
|
||||
3 => {
|
||||
state.cpu_push_stack((state.registers.pc.overflowing_add(3).0 >> 8) as u8);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
4 => {
|
||||
state.cpu_push_stack(state.registers.pc.overflowing_add(3).0 as u8);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
5 => {
|
||||
let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
|
||||
state.registers.pc = address;
|
||||
state.registers.opcode_bytecount = Some(0);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(call_c_u16, 0xDC, "CALL C,u16", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let lower = state.registers.take_mem() as u16;
|
||||
state.registers.set_hold(lower);
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(2).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
2 => {
|
||||
if !state.registers.get_carry() {
|
||||
state.registers.take_mem();
|
||||
state.registers.take_hold();
|
||||
state.registers.opcode_bytecount = Some(3);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
},
|
||||
3 => {
|
||||
state.cpu_push_stack((state.registers.pc.overflowing_add(3).0 >> 8) as u8);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
4 => {
|
||||
state.cpu_push_stack(state.registers.pc.overflowing_add(3).0 as u8);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
5 => {
|
||||
let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
|
||||
state.registers.pc = address;
|
||||
state.registers.opcode_bytecount = Some(0);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(ret, 0xC9, "RET", false, {
|
||||
0 => {
|
||||
state.cpu_pop_stack();
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let lsb = state.registers.take_mem() as u16;
|
||||
state.registers.set_hold(lsb);
|
||||
state.cpu_pop_stack();
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
2 => {
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
3 => {
|
||||
let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
|
||||
state.registers.pc = address;
|
||||
state.registers.opcode_bytecount = Some(0);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(reti, 0xD9, "RETI", false, {
|
||||
0 => {
|
||||
state.cpu_pop_stack();
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let lsb = state.registers.take_mem() as u16;
|
||||
state.registers.set_hold(lsb);
|
||||
state.cpu_pop_stack();
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
2 => {
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
3 => {
|
||||
state.interrupts.ime = true;
|
||||
let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
|
||||
state.registers.pc = address;
|
||||
state.registers.opcode_bytecount = Some(0);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(ret_nz, 0xC0, "RET NZ", false, {
|
||||
0 => {
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
if state.registers.get_zero() {
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
state.cpu_pop_stack();
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
},
|
||||
2 => {
|
||||
let lsb = state.registers.take_mem() as u16;
|
||||
state.registers.set_hold(lsb);
|
||||
state.cpu_pop_stack();
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
3 => {
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
4 => {
|
||||
let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
|
||||
state.registers.pc = address;
|
||||
state.registers.opcode_bytecount = Some(0);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(ret_nc, 0xD0, "RET NC", false, {
|
||||
0 => {
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
if state.registers.get_carry() {
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
state.cpu_pop_stack();
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
},
|
||||
2 => {
|
||||
let lsb = state.registers.take_mem() as u16;
|
||||
state.registers.set_hold(lsb);
|
||||
state.cpu_pop_stack();
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
3 => {
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
4 => {
|
||||
let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
|
||||
state.registers.pc = address;
|
||||
state.registers.opcode_bytecount = Some(0);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(ret_z, 0xC8, "RET Z", false, {
|
||||
0 => {
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
if !state.registers.get_zero() {
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
state.cpu_pop_stack();
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
},
|
||||
2 => {
|
||||
let lsb = state.registers.take_mem() as u16;
|
||||
state.registers.set_hold(lsb);
|
||||
state.cpu_pop_stack();
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
3 => {
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
4 => {
|
||||
let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
|
||||
state.registers.pc = address;
|
||||
state.registers.opcode_bytecount = Some(0);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(ret_c, 0xD8, "RET C", false, {
|
||||
0 => {
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
if !state.registers.get_carry() {
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
state.cpu_pop_stack();
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
},
|
||||
2 => {
|
||||
let lsb = state.registers.take_mem() as u16;
|
||||
state.registers.set_hold(lsb);
|
||||
state.cpu_pop_stack();
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
3 => {
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
4 => {
|
||||
let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
|
||||
state.registers.pc = address;
|
||||
state.registers.opcode_bytecount = Some(0);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
macro_rules! define_rst {
|
||||
($op:literal, $addr:literal) => {
|
||||
paste::paste! {
|
||||
opcode!([<rst_ $addr>], $op, std::concat!("RST ", std::stringify!($addr)), false, {
|
||||
0 => {
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
state.cpu_push_stack((state.registers.pc >> 8) as u8);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
2 => {
|
||||
state.cpu_push_stack((state.registers.pc & 0xFF) as u8);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
3 => {
|
||||
state.registers.pc = $addr;
|
||||
state.registers.opcode_bytecount = Some(0);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_rst!(0xC7, 0x0);
|
||||
define_rst!(0xCF, 0x08);
|
||||
define_rst!(0xD7, 0x10);
|
||||
define_rst!(0xDF, 0x18);
|
||||
define_rst!(0xE7, 0x20);
|
||||
define_rst!(0xEF, 0x28);
|
||||
define_rst!(0xF7, 0x30);
|
||||
define_rst!(0xFF, 0x38);
|
444
deemgee/src/gameboy/cpu/load_store_move.rs
Normal file
444
deemgee/src/gameboy/cpu/load_store_move.rs
Normal file
|
@ -0,0 +1,444 @@
|
|||
use deemgee_opcode::opcode;
|
||||
|
||||
use crate::gameboy::{cpu::CycleResult, Gameboy};
|
||||
|
||||
macro_rules! define_ld_reg_imm_u16 {
|
||||
($op:literal, $reg:ident) => {
|
||||
paste::paste! {
|
||||
opcode!([<ld_ $reg _imm_u16>], $op, std::concat!("LD ", std::stringify!($reg), ",u16"), false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let mut reg = state.registers.[<get_ $reg>]();
|
||||
reg &= 0xFF00;
|
||||
reg |= state.registers.take_mem() as u16;
|
||||
state.registers.[<set_ $reg>](reg);
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(2).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
2 => {
|
||||
let mut reg = state.registers.[<get_ $reg>]();
|
||||
reg &= 0xFF;
|
||||
reg |= (state.registers.take_mem() as u16) << 8;
|
||||
state.registers.[<set_ $reg>](reg);
|
||||
state.registers.opcode_bytecount = Some(3);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_ld_reg_imm_u16!(0x01, bc);
|
||||
define_ld_reg_imm_u16!(0x11, de);
|
||||
define_ld_reg_imm_u16!(0x21, hl);
|
||||
define_ld_reg_imm_u16!(0x31, sp);
|
||||
|
||||
opcode!(ld_sp_hl, 0xF9, "LD SP,HL", false, {
|
||||
0 => {
|
||||
state.registers.sp &= 0xFF00;
|
||||
state.registers.sp |= state.registers.l as u16;
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
state.registers.sp &= 0xFF;
|
||||
state.registers.sp |= (state.registers.h as u16) << 8;
|
||||
state.registers.opcode_bytecount = Some(3);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(ld_deref_imm_u16_sp, 0x08, "LD (u16),SP", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let low_addr = state.registers.take_mem() as u16;
|
||||
state.registers.set_hold(low_addr);
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(2).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
2 => {
|
||||
let addr = ((state.registers.take_mem() as u16) << 8) | state.registers.take_hold();
|
||||
state.registers.set_hold(addr);
|
||||
state.cpu_write_u8(addr, state.registers.sp as u8);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
3 => {
|
||||
let addr = state.registers.take_hold().overflowing_add(1).0;
|
||||
state.cpu_write_u8(addr, (state.registers.sp >> 8) as u8);
|
||||
state.registers.opcode_bytecount = Some(3);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
macro_rules! define_ld_reg_reg {
|
||||
($opcode:literal, $lreg:ident, $rreg:ident) => {
|
||||
paste::paste! {
|
||||
opcode!([<ld_ $lreg _ $rreg>], $opcode, std::concat!("LD ", std::stringify!($lreg), ",", std::stringify!($rreg)), false, {
|
||||
0 => {
|
||||
let res = state.registers.$rreg;
|
||||
state.registers.$lreg = res;
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_ld_reg_reg!(0x40, b, b);
|
||||
define_ld_reg_reg!(0x41, b, c);
|
||||
define_ld_reg_reg!(0x42, b, d);
|
||||
define_ld_reg_reg!(0x43, b, e);
|
||||
define_ld_reg_reg!(0x44, b, h);
|
||||
define_ld_reg_reg!(0x45, b, l);
|
||||
define_ld_reg_reg!(0x47, b, a);
|
||||
define_ld_reg_reg!(0x48, c, b);
|
||||
define_ld_reg_reg!(0x49, c, c);
|
||||
define_ld_reg_reg!(0x4A, c, d);
|
||||
define_ld_reg_reg!(0x4B, c, e);
|
||||
define_ld_reg_reg!(0x4C, c, h);
|
||||
define_ld_reg_reg!(0x4D, c, l);
|
||||
define_ld_reg_reg!(0x4F, c, a);
|
||||
define_ld_reg_reg!(0x50, d, b);
|
||||
define_ld_reg_reg!(0x51, d, c);
|
||||
define_ld_reg_reg!(0x52, d, d);
|
||||
define_ld_reg_reg!(0x53, d, e);
|
||||
define_ld_reg_reg!(0x54, d, h);
|
||||
define_ld_reg_reg!(0x55, d, l);
|
||||
define_ld_reg_reg!(0x57, d, a);
|
||||
define_ld_reg_reg!(0x58, e, b);
|
||||
define_ld_reg_reg!(0x59, e, c);
|
||||
define_ld_reg_reg!(0x5A, e, d);
|
||||
define_ld_reg_reg!(0x5B, e, e);
|
||||
define_ld_reg_reg!(0x5C, e, h);
|
||||
define_ld_reg_reg!(0x5D, e, l);
|
||||
define_ld_reg_reg!(0x5F, e, a);
|
||||
define_ld_reg_reg!(0x60, h, b);
|
||||
define_ld_reg_reg!(0x61, h, c);
|
||||
define_ld_reg_reg!(0x62, h, d);
|
||||
define_ld_reg_reg!(0x63, h, e);
|
||||
define_ld_reg_reg!(0x64, h, h);
|
||||
define_ld_reg_reg!(0x65, h, l);
|
||||
define_ld_reg_reg!(0x67, h, a);
|
||||
define_ld_reg_reg!(0x68, l, b);
|
||||
define_ld_reg_reg!(0x69, l, c);
|
||||
define_ld_reg_reg!(0x6A, l, d);
|
||||
define_ld_reg_reg!(0x6B, l, e);
|
||||
define_ld_reg_reg!(0x6C, l, h);
|
||||
define_ld_reg_reg!(0x6D, l, l);
|
||||
define_ld_reg_reg!(0x6F, l, a);
|
||||
define_ld_reg_reg!(0x78, a, b);
|
||||
define_ld_reg_reg!(0x79, a, c);
|
||||
define_ld_reg_reg!(0x7A, a, d);
|
||||
define_ld_reg_reg!(0x7B, a, e);
|
||||
define_ld_reg_reg!(0x7C, a, h);
|
||||
define_ld_reg_reg!(0x7D, a, l);
|
||||
define_ld_reg_reg!(0x7F, a, a);
|
||||
|
||||
macro_rules! define_ld_reg_deref {
|
||||
($op:literal, $lreg:ident, $rreg:ident) => {
|
||||
paste::paste! {
|
||||
opcode!([<ld_ $lreg _deref_ $rreg>], $op, std::concat!("LD ", std::stringify!($lreg), ",(", std::stringify!($rreg), ")"), false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.[<get_ $rreg>]());
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
state.registers.$lreg = state.registers.take_mem();
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_ld_reg_deref!(0x46, b, hl);
|
||||
define_ld_reg_deref!(0x4E, c, hl);
|
||||
define_ld_reg_deref!(0x56, d, hl);
|
||||
define_ld_reg_deref!(0x5E, e, hl);
|
||||
define_ld_reg_deref!(0x66, h, hl);
|
||||
define_ld_reg_deref!(0x6E, l, hl);
|
||||
define_ld_reg_deref!(0x7E, a, hl);
|
||||
define_ld_reg_deref!(0x0A, a, bc);
|
||||
define_ld_reg_deref!(0x1A, a, de);
|
||||
|
||||
opcode!(ld_hl_plus_a, 0x22, "LD (HL+),A", false, {
|
||||
0 => {
|
||||
state.cpu_write_u8(state.registers.get_hl(), state.registers.a);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let reg = state.registers.get_hl().overflowing_add(1).0;
|
||||
state.registers.set_hl(reg);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(ld_hl_minus_a, 0x32, "LD (HL-),A", false, {
|
||||
0 => {
|
||||
state.cpu_write_u8(state.registers.get_hl(), state.registers.a);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let reg = state.registers.get_hl().overflowing_sub(1).0;
|
||||
state.registers.set_hl(reg);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(ld_a_hl_plus, 0x2A, "LD A,(HL+)", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.get_hl());
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
state.registers.a = state.registers.take_mem();
|
||||
let reg = state.registers.get_hl().overflowing_add(1).0;
|
||||
state.registers.set_hl(reg);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(ld_a_hl_minus, 0x3A, "LD A,(HL-)", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.get_hl());
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
state.registers.a = state.registers.take_mem();
|
||||
let reg = state.registers.get_hl().overflowing_sub(1).0;
|
||||
state.registers.set_hl(reg);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
macro_rules! define_ld_reg_imm_u8 {
|
||||
($op:literal, $lreg:ident) => {
|
||||
paste::paste! {
|
||||
opcode!([<ld_ $lreg _imm_u8>], $op, std::concat!("LD ", std::stringify!($lreg), ",u8"), false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
state.registers.$lreg = state.registers.take_mem();
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_ld_reg_imm_u8!(0x06, b);
|
||||
define_ld_reg_imm_u8!(0x0E, c);
|
||||
define_ld_reg_imm_u8!(0x16, d);
|
||||
define_ld_reg_imm_u8!(0x1E, e);
|
||||
define_ld_reg_imm_u8!(0x26, h);
|
||||
define_ld_reg_imm_u8!(0x2E, l);
|
||||
define_ld_reg_imm_u8!(0x3E, a);
|
||||
|
||||
opcode!(ld_deref_hl_imm_u8, 0x36, "LD (HL),u8", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let imm = state.registers.take_mem();
|
||||
state.cpu_write_u8(state.registers.get_hl(), imm);
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
2 => {
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(ldh_a_imm_u8, 0xF0, "LDH A,(u8)", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let imm = state.registers.take_mem();
|
||||
let addr = 0xFF00u16 | imm as u16;
|
||||
state.cpu_read_u8(addr);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
2 => {
|
||||
state.registers.a = state.registers.take_mem();
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(ldh_imm_u8_a, 0xE0, "LDH (u8),A", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let imm = state.registers.take_mem();
|
||||
let addr = 0xFF00u16 | imm as u16;
|
||||
state.cpu_write_u8(addr, state.registers.a);
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
2 => {
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(ldh_a_deref_c, 0xF2, "LDH A,(C)", false, {
|
||||
0 => {
|
||||
let imm = state.registers.c;
|
||||
let addr = 0xFF00u16 | imm as u16;
|
||||
state.cpu_read_u8(addr);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
state.registers.a = state.registers.take_mem();
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(ldh_deref_c_a, 0xE2, "LDH (C),A", false, {
|
||||
0 => {
|
||||
let addr = 0xFF00u16 | state.registers.c as u16;
|
||||
state.cpu_write_u8(addr, state.registers.a);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(ld_a_deref_imm_u16, 0xFA, "LD A,(u16)", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let lsb = state.registers.take_mem() as u16;
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(2).0);
|
||||
state.registers.set_hold(lsb);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
2 => {
|
||||
let addr = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
|
||||
state.cpu_read_u8(addr);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
3 => {
|
||||
state.registers.a = state.registers.take_mem();
|
||||
state.registers.opcode_bytecount = Some(3);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(ld_deref_imm_u16_a, 0xEA, "LD (u16),A", false, {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let lsb = state.registers.take_mem() as u16;
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(2).0);
|
||||
state.registers.set_hold(lsb);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
2 => {
|
||||
let addr = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
|
||||
state.cpu_write_u8(addr, state.registers.a);
|
||||
state.registers.opcode_bytecount = Some(3);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
3 => {
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
macro_rules! define_ld_deref_hl_reg {
|
||||
($op:literal, $lreg:ident) => {
|
||||
paste::paste! {
|
||||
opcode!([<ld_deref_hl_ $lreg>], $op, std::concat!("LD (HL),", std::stringify!($lreg)), false, {
|
||||
0 => {
|
||||
state.cpu_write_u8(state.registers.get_hl(), state.registers.$lreg);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_ld_deref_hl_reg!(0x70, b);
|
||||
define_ld_deref_hl_reg!(0x71, c);
|
||||
define_ld_deref_hl_reg!(0x72, d);
|
||||
define_ld_deref_hl_reg!(0x73, e);
|
||||
define_ld_deref_hl_reg!(0x74, h);
|
||||
define_ld_deref_hl_reg!(0x75, l);
|
||||
define_ld_deref_hl_reg!(0x77, a);
|
||||
|
||||
macro_rules! define_push_pop_reg {
|
||||
($push_op:literal, $pop_op:literal, $reg:ident) => {
|
||||
paste::paste! {
|
||||
opcode!([<push_ $reg>], $push_op, std::concat!("PUSH ", std::stringify!($reg)), false, {
|
||||
0 => {
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
state.cpu_push_stack((state.registers.[<get_ $reg>]() >> 8) as u8);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
2 => {
|
||||
state.cpu_push_stack(state.registers.[<get_ $reg>]() as u8);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
3 => {
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!([<pop_ $reg>], $pop_op, std::concat!("POP ", std::stringify!($reg)), false, {
|
||||
0 => {
|
||||
state.cpu_pop_stack();
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let lsb = state.registers.take_mem() as u16;
|
||||
state.registers.set_hold(lsb);
|
||||
state.cpu_pop_stack();
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
2 => {
|
||||
let val = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
|
||||
state.registers.[<set_ $reg>](val);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_push_pop_reg!(0xC5, 0xC1, bc);
|
||||
define_push_pop_reg!(0xD5, 0xD1, de);
|
||||
define_push_pop_reg!(0xE5, 0xE1, hl);
|
||||
define_push_pop_reg!(0xF5, 0xF1, af);
|
27
deemgee/src/gameboy/cpu/misc.rs
Normal file
27
deemgee/src/gameboy/cpu/misc.rs
Normal file
|
@ -0,0 +1,27 @@
|
|||
use deemgee_opcode::opcode;
|
||||
|
||||
use super::CycleResult;
|
||||
use crate::gameboy::Gameboy;
|
||||
|
||||
opcode!(nop, 0x00, "NOP", false, {
|
||||
0 => {
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(di, 0xF3, "DI", false, {
|
||||
0 => {
|
||||
state.interrupts.ime = false;
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
|
||||
opcode!(ei, 0xFB, "EI", false, {
|
||||
0 => {
|
||||
state.interrupts.ime = true;
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
269
deemgee/src/gameboy/cpu/prefixed.rs
Normal file
269
deemgee/src/gameboy/cpu/prefixed.rs
Normal file
|
@ -0,0 +1,269 @@
|
|||
use deemgee_opcode::opcode;
|
||||
|
||||
use super::CycleResult;
|
||||
use crate::gameboy::Gameboy;
|
||||
|
||||
pub fn prefixed_handler(state: &mut Gameboy) -> CycleResult {
|
||||
let opcode = match state.registers.current_prefixed_opcode {
|
||||
Some(prefixed_opcode) => prefixed_opcode,
|
||||
None => match state.registers.mem_read_hold.take() {
|
||||
Some(opcode) => {
|
||||
state.registers.current_prefixed_opcode = Some(opcode);
|
||||
opcode
|
||||
}
|
||||
None => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
return CycleResult::NeedsMore;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let res: CycleResult = match opcode {
|
||||
0x10 => rl_b,
|
||||
0x11 => rl_c,
|
||||
0x12 => rl_d,
|
||||
0x13 => rl_e,
|
||||
0x14 => rl_h,
|
||||
0x15 => rl_l,
|
||||
0x17 => rl_a,
|
||||
0x30 => swap_b,
|
||||
0x31 => swap_c,
|
||||
0x32 => swap_d,
|
||||
0x33 => swap_e,
|
||||
0x34 => swap_h,
|
||||
0x35 => swap_l,
|
||||
0x37 => swap_a,
|
||||
0x40 => bit_0_b,
|
||||
0x41 => bit_0_c,
|
||||
0x42 => bit_0_d,
|
||||
0x43 => bit_0_e,
|
||||
0x44 => bit_0_h,
|
||||
0x45 => bit_0_l,
|
||||
0x46 => bit_0_deref_hl,
|
||||
0x47 => bit_0_a,
|
||||
0x48 => bit_1_b,
|
||||
0x49 => bit_1_c,
|
||||
0x4a => bit_1_d,
|
||||
0x4b => bit_1_e,
|
||||
0x4c => bit_1_h,
|
||||
0x4d => bit_1_l,
|
||||
0x4e => bit_1_deref_hl,
|
||||
0x4f => bit_1_a,
|
||||
0x50 => bit_2_b,
|
||||
0x51 => bit_2_c,
|
||||
0x52 => bit_2_d,
|
||||
0x53 => bit_2_e,
|
||||
0x54 => bit_2_h,
|
||||
0x55 => bit_2_l,
|
||||
0x56 => bit_2_deref_hl,
|
||||
0x57 => bit_2_a,
|
||||
0x58 => bit_3_b,
|
||||
0x59 => bit_3_c,
|
||||
0x5a => bit_3_d,
|
||||
0x5b => bit_3_e,
|
||||
0x5c => bit_3_h,
|
||||
0x5d => bit_3_l,
|
||||
0x5e => bit_3_deref_hl,
|
||||
0x5f => bit_3_a,
|
||||
0x60 => bit_4_b,
|
||||
0x61 => bit_4_c,
|
||||
0x62 => bit_4_d,
|
||||
0x63 => bit_4_e,
|
||||
0x64 => bit_4_h,
|
||||
0x65 => bit_4_l,
|
||||
0x66 => bit_4_deref_hl,
|
||||
0x67 => bit_4_a,
|
||||
0x68 => bit_5_b,
|
||||
0x69 => bit_5_c,
|
||||
0x6a => bit_5_d,
|
||||
0x6b => bit_5_e,
|
||||
0x6c => bit_5_h,
|
||||
0x6d => bit_5_l,
|
||||
0x6e => bit_5_deref_hl,
|
||||
0x6f => bit_5_a,
|
||||
0x70 => bit_6_b,
|
||||
0x71 => bit_6_c,
|
||||
0x72 => bit_6_d,
|
||||
0x73 => bit_6_e,
|
||||
0x74 => bit_6_h,
|
||||
0x75 => bit_6_l,
|
||||
0x76 => bit_6_deref_hl,
|
||||
0x77 => bit_6_a,
|
||||
0x78 => bit_7_b,
|
||||
0x79 => bit_7_c,
|
||||
0x7a => bit_7_d,
|
||||
0x7b => bit_7_e,
|
||||
0x7c => bit_7_h,
|
||||
0x7d => bit_7_l,
|
||||
0x7e => bit_7_deref_hl,
|
||||
0x7f => bit_7_a,
|
||||
unknown => panic!(
|
||||
"Unrecognized prefixed opcode: {:#X}\nRegisters: {:#?}",
|
||||
unknown, state.registers
|
||||
),
|
||||
}(state);
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
macro_rules! define_bit_reg {
|
||||
($op:literal, $bit:literal, $reg:ident) => {
|
||||
paste::paste! {
|
||||
opcode!([<bit_ $bit _ $reg>], $op, std::concat!("BIT ", std::stringify!($bit), ",", std::stringify!($reg)), false, {
|
||||
1 => {
|
||||
state.registers.set_zero(state.registers.$reg & (1 << $bit) == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(true);
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_bit_reg!(0x40, 0, b);
|
||||
define_bit_reg!(0x48, 1, b);
|
||||
define_bit_reg!(0x50, 2, b);
|
||||
define_bit_reg!(0x58, 3, b);
|
||||
define_bit_reg!(0x60, 4, b);
|
||||
define_bit_reg!(0x68, 5, b);
|
||||
define_bit_reg!(0x70, 6, b);
|
||||
define_bit_reg!(0x78, 7, b);
|
||||
define_bit_reg!(0x41, 0, c);
|
||||
define_bit_reg!(0x49, 1, c);
|
||||
define_bit_reg!(0x51, 2, c);
|
||||
define_bit_reg!(0x59, 3, c);
|
||||
define_bit_reg!(0x61, 4, c);
|
||||
define_bit_reg!(0x69, 5, c);
|
||||
define_bit_reg!(0x71, 6, c);
|
||||
define_bit_reg!(0x79, 7, c);
|
||||
define_bit_reg!(0x42, 0, d);
|
||||
define_bit_reg!(0x4a, 1, d);
|
||||
define_bit_reg!(0x52, 2, d);
|
||||
define_bit_reg!(0x5a, 3, d);
|
||||
define_bit_reg!(0x62, 4, d);
|
||||
define_bit_reg!(0x6a, 5, d);
|
||||
define_bit_reg!(0x72, 6, d);
|
||||
define_bit_reg!(0x7a, 7, d);
|
||||
define_bit_reg!(0x43, 0, e);
|
||||
define_bit_reg!(0x4b, 1, e);
|
||||
define_bit_reg!(0x53, 2, e);
|
||||
define_bit_reg!(0x5b, 3, e);
|
||||
define_bit_reg!(0x63, 4, e);
|
||||
define_bit_reg!(0x6b, 5, e);
|
||||
define_bit_reg!(0x73, 6, e);
|
||||
define_bit_reg!(0x7b, 7, e);
|
||||
define_bit_reg!(0x44, 0, h);
|
||||
define_bit_reg!(0x4c, 1, h);
|
||||
define_bit_reg!(0x54, 2, h);
|
||||
define_bit_reg!(0x5c, 3, h);
|
||||
define_bit_reg!(0x64, 4, h);
|
||||
define_bit_reg!(0x6c, 5, h);
|
||||
define_bit_reg!(0x74, 6, h);
|
||||
define_bit_reg!(0x7c, 7, h);
|
||||
define_bit_reg!(0x45, 0, l);
|
||||
define_bit_reg!(0x4d, 1, l);
|
||||
define_bit_reg!(0x55, 2, l);
|
||||
define_bit_reg!(0x5d, 3, l);
|
||||
define_bit_reg!(0x65, 4, l);
|
||||
define_bit_reg!(0x6d, 5, l);
|
||||
define_bit_reg!(0x75, 6, l);
|
||||
define_bit_reg!(0x7d, 7, l);
|
||||
define_bit_reg!(0x47, 0, a);
|
||||
define_bit_reg!(0x4f, 1, a);
|
||||
define_bit_reg!(0x57, 2, a);
|
||||
define_bit_reg!(0x5f, 3, a);
|
||||
define_bit_reg!(0x67, 4, a);
|
||||
define_bit_reg!(0x6f, 5, a);
|
||||
define_bit_reg!(0x77, 6, a);
|
||||
define_bit_reg!(0x7f, 7, a);
|
||||
|
||||
macro_rules! define_bit_deref_hl {
|
||||
($op:literal, $bit:literal) => {
|
||||
paste::paste! {
|
||||
opcode!([<bit_ $bit _deref_hl>], $op, std::concat!("BIT ", std::stringify!($bit), ",(HL)"), false, {
|
||||
1 => {
|
||||
state.cpu_read_u8(state.registers.get_hl());
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
2 => {
|
||||
let mem_read = state.registers.take_mem();
|
||||
state.registers.set_zero(mem_read & (1 << $bit) == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(true);
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_bit_deref_hl!(0x46, 0);
|
||||
define_bit_deref_hl!(0x4e, 1);
|
||||
define_bit_deref_hl!(0x56, 2);
|
||||
define_bit_deref_hl!(0x5e, 3);
|
||||
define_bit_deref_hl!(0x66, 4);
|
||||
define_bit_deref_hl!(0x6e, 5);
|
||||
define_bit_deref_hl!(0x76, 6);
|
||||
define_bit_deref_hl!(0x7e, 7);
|
||||
|
||||
macro_rules! define_rl_reg {
|
||||
($op:literal, $reg:ident) => {
|
||||
paste::paste! {
|
||||
opcode!([<rl_ $reg>], $op, std::concat!("RL ", std::stringify!($reg)), false, {
|
||||
1 => {
|
||||
let carry = state.registers.$reg >> 7 == 1;
|
||||
state.registers.$reg <<= 1;
|
||||
|
||||
if state.registers.get_carry() {
|
||||
state.registers.$reg |= 1;
|
||||
}
|
||||
|
||||
state.registers.set_zero(state.registers.$reg == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(false);
|
||||
state.registers.set_carry(carry);
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_rl_reg!(0x10, b);
|
||||
define_rl_reg!(0x11, c);
|
||||
define_rl_reg!(0x12, d);
|
||||
define_rl_reg!(0x13, e);
|
||||
define_rl_reg!(0x14, h);
|
||||
define_rl_reg!(0x15, l);
|
||||
define_rl_reg!(0x17, a);
|
||||
|
||||
macro_rules! define_swap_reg {
|
||||
($op:literal, $reg:ident) => {
|
||||
paste::paste! {
|
||||
opcode!([<swap_ $reg>], $op, std::concat!("SWAP ", std::stringify!($reg)), false, {
|
||||
1 => {
|
||||
state.registers.$reg = (state.registers.$reg >> 4) | (state.registers.$reg << 4);
|
||||
|
||||
state.registers.set_zero(state.registers.$reg == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(false);
|
||||
state.registers.set_carry(false);
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_swap_reg!(0x30, b);
|
||||
define_swap_reg!(0x31, c);
|
||||
define_swap_reg!(0x32, d);
|
||||
define_swap_reg!(0x33, e);
|
||||
define_swap_reg!(0x34, h);
|
||||
define_swap_reg!(0x35, l);
|
||||
define_swap_reg!(0x37, a);
|
|
@ -40,7 +40,7 @@ pub enum DmgError {
|
|||
IO(#[from] std::io::Error),
|
||||
}
|
||||
|
||||
fn main() -> Result<(), DmgError> {
|
||||
fn main() {
|
||||
env_logger::init();
|
||||
|
||||
let args: CliArgs = argh::from_env();
|
||||
|
@ -49,11 +49,11 @@ fn main() -> Result<(), DmgError> {
|
|||
let (window_side_tx, gb_side_rx) = channel::<WindowEvent>();
|
||||
let (gb_side_tx, window_side_rx) = channel::<GameboyEvent>();
|
||||
|
||||
let jh = std::thread::spawn(move || run_gameboy(config, args, gb_side_rx, gb_side_tx));
|
||||
let jh = std::thread::spawn(move || run_gameboy(config, args, gb_side_rx, gb_side_tx).unwrap());
|
||||
|
||||
window::run_window(config, window_side_rx, window_side_tx);
|
||||
|
||||
jh.join().unwrap()
|
||||
jh.join().unwrap();
|
||||
}
|
||||
|
||||
pub fn run_gameboy(
|
||||
|
@ -112,6 +112,9 @@ pub fn run_gameboy(
|
|||
window::WindowEvent::LeftToggle => gameboy.joypad.left = !gameboy.joypad.left,
|
||||
window::WindowEvent::RightToggle => gameboy.joypad.right = !gameboy.joypad.right,
|
||||
window::WindowEvent::PauseToggle => paused = !paused,
|
||||
window::WindowEvent::LogToggle => {
|
||||
gameboy.log_instructions = !gameboy.log_instructions
|
||||
}
|
||||
window::WindowEvent::Exit => break 'outer,
|
||||
}
|
||||
}
|
|
@ -26,4 +26,5 @@ pub struct Bindings {
|
|||
|
||||
pub pause: VirtualKeyCode,
|
||||
pub exit: VirtualKeyCode,
|
||||
pub log_ops: VirtualKeyCode,
|
||||
}
|
|
@ -39,6 +39,7 @@ pub enum WindowEvent {
|
|||
LeftToggle,
|
||||
RightToggle,
|
||||
PauseToggle,
|
||||
LogToggle,
|
||||
Exit,
|
||||
}
|
||||
|
||||
|
@ -139,6 +140,10 @@ pub fn run_window(config: DeemgeeConfig, rx: Receiver<GameboyEvent>, tx: Sender<
|
|||
tx.send(WindowEvent::PauseToggle).unwrap();
|
||||
}
|
||||
|
||||
if input.key_pressed(config.bindings.log_ops) {
|
||||
tx.send(WindowEvent::LogToggle).unwrap();
|
||||
}
|
||||
|
||||
define_keypress!(input, config, keymap, tx, a, AToggle);
|
||||
define_keypress!(input, config, keymap, tx, b, BToggle);
|
||||
define_keypress!(input, config, keymap, tx, start, StartToggle);
|
|
@ -1,970 +0,0 @@
|
|||
use super::CycleResult;
|
||||
use crate::gameboy::Gameboy;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CarryResult {
|
||||
pub result: u8,
|
||||
pub half_carry: bool,
|
||||
pub carry: bool,
|
||||
}
|
||||
|
||||
pub fn sub_with_carry(lhs: u8, rhs: u8, carry: bool) -> CarryResult {
|
||||
let carry_u8 = carry as u8;
|
||||
|
||||
let (first_res, first_carry) = lhs.overflowing_sub(rhs);
|
||||
let (result, second_carry) = first_res.overflowing_sub(carry_u8);
|
||||
|
||||
let carry = first_carry || second_carry;
|
||||
|
||||
let first_hc_res = (lhs & 0xF).overflowing_sub(rhs & 0xF).0;
|
||||
let first_half_carry = (first_hc_res >> 4) & 0b1 == 1;
|
||||
let second_half_carry = ((first_hc_res.overflowing_sub(carry_u8).0) >> 4) & 0b1 == 1;
|
||||
|
||||
let half_carry = first_half_carry || second_half_carry;
|
||||
|
||||
CarryResult { result, carry, half_carry }
|
||||
}
|
||||
|
||||
pub fn add_with_carry(lhs: u8, rhs: u8, carry: bool) -> CarryResult {
|
||||
let carry_u8 = carry as u8;
|
||||
|
||||
let (first_res, first_carry) = lhs.overflowing_add(rhs);
|
||||
let (result, second_carry) = first_res.overflowing_add(carry_u8);
|
||||
|
||||
let carry = first_carry || second_carry;
|
||||
|
||||
let first_hc_res = (lhs & 0xF) + (rhs & 0xF);
|
||||
let first_half_carry = (first_hc_res >> 4) & 0b1 == 1;
|
||||
let second_half_carry = ((first_hc_res + carry_u8) >> 4) & 0b1 == 1;
|
||||
|
||||
let half_carry = first_half_carry || second_half_carry;
|
||||
|
||||
CarryResult { result, carry, half_carry }
|
||||
}
|
||||
|
||||
pub fn add(lhs: u8, rhs: u8) -> CarryResult {
|
||||
let (result, carry) = lhs.overflowing_add(rhs);
|
||||
let half_carry = (((lhs & 0xF) + (rhs & 0xF)) >> 4) & 0b1 == 1;
|
||||
|
||||
CarryResult { result, carry, half_carry }
|
||||
}
|
||||
|
||||
pub fn sub(lhs: u8, rhs: u8) -> CarryResult {
|
||||
let (result, carry) = lhs.overflowing_sub(rhs);
|
||||
let half_carry = (((lhs & 0xF).overflowing_sub(rhs & 0xF).0) >> 4) & 0b1 == 1;
|
||||
|
||||
CarryResult { result, carry, half_carry }
|
||||
}
|
||||
|
||||
macro_rules! define_xor_reg {
|
||||
($reg:ident) => {
|
||||
paste::paste! {
|
||||
pub fn [<xor_a_ $reg>](state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.registers.a ^= state.registers.$reg;
|
||||
state.registers.set_zero(state.registers.a == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(false);
|
||||
state.registers.set_carry(false);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_xor_reg!(a);
|
||||
define_xor_reg!(b);
|
||||
define_xor_reg!(c);
|
||||
define_xor_reg!(d);
|
||||
define_xor_reg!(e);
|
||||
define_xor_reg!(h);
|
||||
define_xor_reg!(l);
|
||||
|
||||
pub fn xor_a_deref_hl(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.get_hl());
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
state.registers.a ^= state.registers.take_mem();
|
||||
state.registers.set_zero(state.registers.a == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(false);
|
||||
state.registers.set_carry(false);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn xor_a_imm_u8(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc + 1);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
state.registers.a ^= state.registers.take_mem();
|
||||
state.registers.set_zero(state.registers.a == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(false);
|
||||
state.registers.set_carry(false);
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! define_sbc_reg {
|
||||
($reg:ident) => {
|
||||
paste::paste! {
|
||||
pub fn [<sbc_a_ $reg>](state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
let CarryResult { result, half_carry, carry } = sub_with_carry(state.registers.a, state.registers.$reg, state.registers.get_carry());
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(true);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.set_carry(carry);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_sbc_reg!(a);
|
||||
define_sbc_reg!(b);
|
||||
define_sbc_reg!(c);
|
||||
define_sbc_reg!(d);
|
||||
define_sbc_reg!(e);
|
||||
define_sbc_reg!(h);
|
||||
define_sbc_reg!(l);
|
||||
|
||||
pub fn sbc_a_deref_hl(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.get_hl());
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let CarryResult { result, half_carry, carry } = sub_with_carry(
|
||||
state.registers.a,
|
||||
state.registers.take_mem(),
|
||||
state.registers.get_carry(),
|
||||
);
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(true);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.set_carry(carry);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sbc_a_imm_u8(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc + 1);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let CarryResult { result, half_carry, carry } = sub_with_carry(
|
||||
state.registers.a,
|
||||
state.registers.take_mem(),
|
||||
state.registers.get_carry(),
|
||||
);
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(true);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.set_carry(carry);
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! define_add_reg {
|
||||
($reg:ident) => {
|
||||
paste::paste! {
|
||||
pub fn [<add_a_ $reg>](state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
let CarryResult { result, half_carry, carry } = add(state.registers.a, state.registers.$reg);
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.set_carry(carry);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_add_reg!(a);
|
||||
define_add_reg!(b);
|
||||
define_add_reg!(c);
|
||||
define_add_reg!(d);
|
||||
define_add_reg!(e);
|
||||
define_add_reg!(h);
|
||||
define_add_reg!(l);
|
||||
|
||||
pub fn add_a_deref_hl(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.get_hl());
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let CarryResult { result, half_carry, carry } =
|
||||
add(state.registers.a, state.registers.take_mem());
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.set_carry(carry);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_a_imm_u8(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc + 1);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let CarryResult { result, half_carry, carry } =
|
||||
add(state.registers.a, state.registers.take_mem());
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.set_carry(carry);
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! define_adc_reg {
|
||||
($reg:ident) => {
|
||||
paste::paste! {
|
||||
pub fn [<adc_a_ $reg>](state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
let CarryResult { result, half_carry, carry } = add_with_carry(state.registers.a, state.registers.$reg, state.registers.get_carry());
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.set_carry(carry);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_adc_reg!(a);
|
||||
define_adc_reg!(b);
|
||||
define_adc_reg!(c);
|
||||
define_adc_reg!(d);
|
||||
define_adc_reg!(e);
|
||||
define_adc_reg!(h);
|
||||
define_adc_reg!(l);
|
||||
|
||||
pub fn adc_a_deref_hl(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.get_hl());
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let CarryResult { result, half_carry, carry } = add_with_carry(
|
||||
state.registers.a,
|
||||
state.registers.take_mem(),
|
||||
state.registers.get_carry(),
|
||||
);
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.set_carry(carry);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn adc_a_imm_u8(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc + 1);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let CarryResult { result, half_carry, carry } = add_with_carry(
|
||||
state.registers.a,
|
||||
state.registers.take_mem(),
|
||||
state.registers.get_carry(),
|
||||
);
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.set_carry(carry);
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! define_sub_reg {
|
||||
($reg:ident) => {
|
||||
paste::paste! {
|
||||
pub fn [<sub_a_ $reg>](state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
let CarryResult { result, half_carry, carry } = sub(state.registers.a, state.registers.$reg);
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(true);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.set_carry(carry);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_sub_reg!(a);
|
||||
define_sub_reg!(b);
|
||||
define_sub_reg!(c);
|
||||
define_sub_reg!(d);
|
||||
define_sub_reg!(e);
|
||||
define_sub_reg!(h);
|
||||
define_sub_reg!(l);
|
||||
|
||||
pub fn sub_a_deref_hl(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.get_hl());
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let CarryResult { result, half_carry, carry } =
|
||||
sub(state.registers.a, state.registers.take_mem());
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(true);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.set_carry(carry);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sub_a_imm_u8(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc + 1);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let CarryResult { result, half_carry, carry } =
|
||||
sub(state.registers.a, state.registers.take_mem());
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(true);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.set_carry(carry);
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! define_inc_reg {
|
||||
($reg:ident) => {
|
||||
paste::paste! {
|
||||
pub fn [<inc_ $reg>](state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
let CarryResult { result, half_carry, .. } = add(
|
||||
state.registers.$reg,
|
||||
1,
|
||||
);
|
||||
|
||||
state.registers.$reg = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_inc_reg!(b);
|
||||
define_inc_reg!(c);
|
||||
define_inc_reg!(d);
|
||||
define_inc_reg!(e);
|
||||
define_inc_reg!(h);
|
||||
define_inc_reg!(l);
|
||||
define_inc_reg!(a);
|
||||
|
||||
pub fn inc_deref_hl(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.get_hl());
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let CarryResult { result, half_carry, .. } = add(state.registers.take_mem(), 1);
|
||||
|
||||
state.cpu_write_u8(state.registers.get_hl(), result);
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
2 => CycleResult::Finished,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! define_dec_reg {
|
||||
($reg:ident) => {
|
||||
paste::paste! {
|
||||
pub fn [<dec_ $reg>](state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
let CarryResult { result, half_carry, .. } = sub(
|
||||
state.registers.$reg,
|
||||
1,
|
||||
);
|
||||
|
||||
state.registers.$reg = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(true);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_dec_reg!(b);
|
||||
define_dec_reg!(c);
|
||||
define_dec_reg!(d);
|
||||
define_dec_reg!(e);
|
||||
define_dec_reg!(h);
|
||||
define_dec_reg!(l);
|
||||
define_dec_reg!(a);
|
||||
|
||||
pub fn dec_deref_hl(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.get_hl());
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let CarryResult { result, half_carry, .. } = sub(state.registers.take_mem(), 1);
|
||||
|
||||
state.cpu_write_u8(state.registers.get_hl(), result);
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
2 => CycleResult::Finished,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rla(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
let carry = state.registers.a >> 7 == 1;
|
||||
state.registers.a <<= 1;
|
||||
|
||||
if state.registers.get_carry() {
|
||||
state.registers.a = state.registers.a.wrapping_add(1);
|
||||
}
|
||||
|
||||
state.registers.set_zero(false);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(false);
|
||||
state.registers.set_carry(carry);
|
||||
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! define_inc_u16_reg {
|
||||
($lreg:ident, $rreg:ident) => {
|
||||
paste::paste! {
|
||||
pub fn [<inc_ $lreg $rreg>](state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
let (res, carry) = state.registers.$rreg.overflowing_add(1);
|
||||
state.registers.$rreg = res;
|
||||
state.registers.set_hold(carry as u16);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
if state.registers.take_hold() != 0 {
|
||||
let (res, _) = state.registers.$lreg.overflowing_add(1);
|
||||
state.registers.$lreg = res;
|
||||
}
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_inc_u16_reg!(b, c);
|
||||
define_inc_u16_reg!(d, e);
|
||||
define_inc_u16_reg!(h, l);
|
||||
|
||||
pub fn inc_sp(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => CycleResult::NeedsMore,
|
||||
1 => {
|
||||
let (res, _) = state.registers.sp.overflowing_add(1);
|
||||
state.registers.sp = res;
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! define_dec_u16_reg {
|
||||
($lreg:ident, $rreg:ident) => {
|
||||
paste::paste! {
|
||||
pub fn [<dec_ $lreg $rreg>](state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
let (res, carry) = state.registers.$rreg.overflowing_sub(1);
|
||||
state.registers.$rreg = res;
|
||||
state.registers.set_hold(carry as u16);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
if state.registers.take_hold() != 0 {
|
||||
let (res, _) = state.registers.$lreg.overflowing_sub(1);
|
||||
state.registers.$lreg = res;
|
||||
}
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_dec_u16_reg!(b, c);
|
||||
define_dec_u16_reg!(d, e);
|
||||
define_dec_u16_reg!(h, l);
|
||||
|
||||
pub fn dec_sp(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => CycleResult::NeedsMore,
|
||||
1 => {
|
||||
let (res, _) = state.registers.sp.overflowing_sub(1);
|
||||
state.registers.sp = res;
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! define_cp_reg_reg {
|
||||
($lreg:ident, $rreg:ident) => {
|
||||
paste::paste! {
|
||||
pub fn [<cp_ $lreg _ $rreg>](state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
let CarryResult { result, half_carry, carry } = sub(state.registers.$lreg, state.registers.$rreg);
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(true);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.set_carry(carry);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_cp_reg_reg!(a, b);
|
||||
define_cp_reg_reg!(a, c);
|
||||
define_cp_reg_reg!(a, d);
|
||||
define_cp_reg_reg!(a, e);
|
||||
define_cp_reg_reg!(a, h);
|
||||
define_cp_reg_reg!(a, l);
|
||||
define_cp_reg_reg!(a, a);
|
||||
|
||||
pub fn cp_a_imm_u8(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let CarryResult { result, half_carry, carry } =
|
||||
sub(state.registers.a, state.registers.take_mem());
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(true);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.set_carry(carry);
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cp_a_deref_hl(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.get_hl());
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let CarryResult { result, half_carry, carry } =
|
||||
sub(state.registers.a, state.registers.take_mem());
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(true);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.set_carry(carry);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! define_or_reg {
|
||||
($reg:ident) => {
|
||||
paste::paste! {
|
||||
pub fn [<or_a_ $reg>](state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
let result = state.registers.a | state.registers.$reg;
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(false);
|
||||
state.registers.set_carry(false);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_or_reg!(a);
|
||||
define_or_reg!(b);
|
||||
define_or_reg!(c);
|
||||
define_or_reg!(d);
|
||||
define_or_reg!(e);
|
||||
define_or_reg!(h);
|
||||
define_or_reg!(l);
|
||||
|
||||
pub fn or_a_deref_hl(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.get_hl());
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let result = state.registers.a | state.registers.take_mem();
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(false);
|
||||
state.registers.set_carry(false);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn or_a_imm_u8(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc + 1);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let result = state.registers.a | state.registers.take_mem();
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(false);
|
||||
state.registers.set_carry(false);
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! define_and_reg {
|
||||
($reg:ident) => {
|
||||
paste::paste! {
|
||||
pub fn [<and_a_ $reg>](state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
let result = state.registers.a & state.registers.$reg;
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(true);
|
||||
state.registers.set_carry(false);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_and_reg!(a);
|
||||
define_and_reg!(b);
|
||||
define_and_reg!(c);
|
||||
define_and_reg!(d);
|
||||
define_and_reg!(e);
|
||||
define_and_reg!(h);
|
||||
define_and_reg!(l);
|
||||
|
||||
pub fn and_a_deref_hl(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.get_hl());
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let result = state.registers.a & state.registers.take_mem();
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(true);
|
||||
state.registers.set_carry(false);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn and_a_imm_u8(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc + 1);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let result = state.registers.a & state.registers.take_mem();
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(true);
|
||||
state.registers.set_carry(false);
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cpl(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.registers.a = !state.registers.a;
|
||||
state.registers.set_subtract(true);
|
||||
state.registers.set_half_carry(true);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ccf(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(false);
|
||||
state.registers.set_carry(!state.registers.get_carry());
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scf(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(false);
|
||||
state.registers.set_carry(true);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! define_add_hl_u16_reg {
|
||||
($lreg:ident, $rreg:ident) => {
|
||||
paste::paste! {
|
||||
pub fn [<add_hl_ $lreg $rreg>](state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
let CarryResult { result, carry, .. } = add(state.registers.l, state.registers.$rreg);
|
||||
state.registers.l = result;
|
||||
state.registers.set_hold(carry as u16);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
if state.registers.take_hold() != 0 {
|
||||
let CarryResult { result, carry, half_carry } = add(state.registers.h, state.registers.$lreg);
|
||||
state.registers.h = result;
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.set_carry(carry);
|
||||
} else {
|
||||
state.registers.set_half_carry(false);
|
||||
state.registers.set_carry(false);
|
||||
}
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_add_hl_u16_reg!(b, c);
|
||||
define_add_hl_u16_reg!(d, e);
|
||||
define_add_hl_u16_reg!(h, l);
|
||||
|
||||
pub fn add_hl_sp(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
let CarryResult { result, carry, .. } =
|
||||
add(state.registers.l, state.registers.sp as u8);
|
||||
state.registers.l = result;
|
||||
state.registers.set_hold(carry as u16);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
if state.registers.take_hold() != 0 {
|
||||
let CarryResult { result, carry, half_carry } =
|
||||
add(state.registers.h, (state.registers.sp >> 8) as u8);
|
||||
state.registers.h = result;
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.set_carry(carry);
|
||||
} else {
|
||||
state.registers.set_half_carry(false);
|
||||
state.registers.set_carry(false);
|
||||
}
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
|
@ -1,700 +0,0 @@
|
|||
use super::CycleResult;
|
||||
use crate::gameboy::Gameboy;
|
||||
|
||||
pub fn jr_nz_i8(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
if state.registers.get_zero() {
|
||||
state.registers.take_mem();
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
let relative = state.registers.take_mem() as i8;
|
||||
|
||||
if relative >= 0 {
|
||||
state.registers.pc = state.registers.pc.overflowing_add(relative as u16).0;
|
||||
} else {
|
||||
state.registers.pc = state.registers.pc.overflowing_sub(relative.abs() as u16).0;
|
||||
}
|
||||
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn jr_nc_i8(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
if state.registers.get_carry() {
|
||||
state.registers.take_mem();
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
let relative = state.registers.take_mem() as i8;
|
||||
|
||||
if relative >= 0 {
|
||||
state.registers.pc = state.registers.pc.overflowing_add(relative as u16).0;
|
||||
} else {
|
||||
state.registers.pc = state.registers.pc.overflowing_sub(relative.abs() as u16).0;
|
||||
}
|
||||
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn jr_z_i8(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
if !state.registers.get_zero() {
|
||||
state.registers.take_mem();
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
let relative = state.registers.take_mem() as i8;
|
||||
|
||||
if relative >= 0 {
|
||||
state.registers.pc = state.registers.pc.overflowing_add(relative as u16).0;
|
||||
} else {
|
||||
state.registers.pc = state.registers.pc.overflowing_sub(relative.abs() as u16).0;
|
||||
}
|
||||
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn jr_c_i8(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
if !state.registers.get_carry() {
|
||||
state.registers.take_mem();
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
let relative = state.registers.take_mem() as i8;
|
||||
|
||||
if relative >= 0 {
|
||||
state.registers.pc = state.registers.pc.overflowing_add(relative as u16).0;
|
||||
} else {
|
||||
state.registers.pc = state.registers.pc.overflowing_sub(relative.abs() as u16).0;
|
||||
}
|
||||
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn jr_i8(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => CycleResult::NeedsMore,
|
||||
2 => {
|
||||
let relative = state.registers.take_mem() as i8;
|
||||
|
||||
if relative >= 0 {
|
||||
state.registers.pc = state.registers.pc.overflowing_add(relative as u16).0;
|
||||
} else {
|
||||
state.registers.pc = state.registers.pc.overflowing_sub(relative.abs() as u16).0;
|
||||
}
|
||||
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn jp_u16(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let lower = state.registers.take_mem() as u16;
|
||||
state.registers.set_hold(lower);
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(2).0);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
2 => CycleResult::NeedsMore,
|
||||
3 => {
|
||||
let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
|
||||
state.registers.pc = address;
|
||||
state.registers.opcode_bytecount = Some(0);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn jp_hl(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.registers.pc = state.registers.get_hl();
|
||||
state.registers.opcode_bytecount = Some(0);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn jp_nz_u16(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let lower = state.registers.take_mem() as u16;
|
||||
state.registers.set_hold(lower);
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(2).0);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
2 => {
|
||||
if state.registers.get_zero() {
|
||||
state.registers.take_mem();
|
||||
state.registers.take_hold();
|
||||
state.registers.opcode_bytecount = Some(3);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
}
|
||||
3 => {
|
||||
let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
|
||||
state.registers.pc = address;
|
||||
state.registers.opcode_bytecount = Some(0);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn jp_nc_u16(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let lower = state.registers.take_mem() as u16;
|
||||
state.registers.set_hold(lower);
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(2).0);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
2 => {
|
||||
if state.registers.get_carry() {
|
||||
state.registers.take_mem();
|
||||
state.registers.take_hold();
|
||||
state.registers.opcode_bytecount = Some(3);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
}
|
||||
3 => {
|
||||
let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
|
||||
state.registers.pc = address;
|
||||
state.registers.opcode_bytecount = Some(0);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn jp_z_u16(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let lower = state.registers.take_mem() as u16;
|
||||
state.registers.set_hold(lower);
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(2).0);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
2 => {
|
||||
if !state.registers.get_zero() {
|
||||
state.registers.take_mem();
|
||||
state.registers.take_hold();
|
||||
state.registers.opcode_bytecount = Some(3);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
}
|
||||
3 => {
|
||||
let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
|
||||
state.registers.pc = address;
|
||||
state.registers.opcode_bytecount = Some(0);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn jp_c_u16(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let lower = state.registers.take_mem() as u16;
|
||||
state.registers.set_hold(lower);
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(2).0);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
2 => {
|
||||
if !state.registers.get_carry() {
|
||||
state.registers.take_mem();
|
||||
state.registers.take_hold();
|
||||
state.registers.opcode_bytecount = Some(3);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
}
|
||||
3 => {
|
||||
let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
|
||||
state.registers.pc = address;
|
||||
state.registers.opcode_bytecount = Some(0);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call_u16(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let lower = state.registers.take_mem() as u16;
|
||||
state.registers.set_hold(lower);
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(2).0);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
2 => CycleResult::NeedsMore,
|
||||
3 => {
|
||||
state.cpu_push_stack((state.registers.pc.overflowing_add(3).0 >> 8) as u8);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
4 => {
|
||||
state.cpu_push_stack(state.registers.pc.overflowing_add(3).0 as u8);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
5 => {
|
||||
let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
|
||||
state.registers.pc = address;
|
||||
state.registers.opcode_bytecount = Some(0);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call_nz_u16(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let lower = state.registers.take_mem() as u16;
|
||||
state.registers.set_hold(lower);
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(2).0);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
2 => {
|
||||
if state.registers.get_zero() {
|
||||
state.registers.take_mem();
|
||||
state.registers.take_hold();
|
||||
state.registers.opcode_bytecount = Some(3);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
}
|
||||
3 => {
|
||||
state.cpu_push_stack((state.registers.pc.overflowing_add(3).0 >> 8) as u8);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
4 => {
|
||||
state.cpu_push_stack(state.registers.pc.overflowing_add(3).0 as u8);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
5 => {
|
||||
let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
|
||||
state.registers.pc = address;
|
||||
state.registers.opcode_bytecount = Some(0);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call_nc_u16(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let lower = state.registers.take_mem() as u16;
|
||||
state.registers.set_hold(lower);
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(2).0);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
2 => {
|
||||
if state.registers.get_carry() {
|
||||
state.registers.take_mem();
|
||||
state.registers.take_hold();
|
||||
state.registers.opcode_bytecount = Some(3);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
}
|
||||
3 => {
|
||||
state.cpu_push_stack((state.registers.pc.overflowing_add(3).0 >> 8) as u8);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
4 => {
|
||||
state.cpu_push_stack(state.registers.pc.overflowing_add(3).0 as u8);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
5 => {
|
||||
let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
|
||||
state.registers.pc = address;
|
||||
state.registers.opcode_bytecount = Some(0);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call_z_u16(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let lower = state.registers.take_mem() as u16;
|
||||
state.registers.set_hold(lower);
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(2).0);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
2 => {
|
||||
if !state.registers.get_zero() {
|
||||
state.registers.take_mem();
|
||||
state.registers.take_hold();
|
||||
state.registers.opcode_bytecount = Some(3);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
}
|
||||
3 => {
|
||||
state.cpu_push_stack((state.registers.pc.overflowing_add(3).0 >> 8) as u8);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
4 => {
|
||||
state.cpu_push_stack(state.registers.pc.overflowing_add(3).0 as u8);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
5 => {
|
||||
let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
|
||||
state.registers.pc = address;
|
||||
state.registers.opcode_bytecount = Some(0);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call_c_u16(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let lower = state.registers.take_mem() as u16;
|
||||
state.registers.set_hold(lower);
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(2).0);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
2 => {
|
||||
if !state.registers.get_carry() {
|
||||
state.registers.take_mem();
|
||||
state.registers.take_hold();
|
||||
state.registers.opcode_bytecount = Some(3);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
}
|
||||
3 => {
|
||||
state.cpu_push_stack((state.registers.pc.overflowing_add(3).0 >> 8) as u8);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
4 => {
|
||||
state.cpu_push_stack(state.registers.pc.overflowing_add(3).0 as u8);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
5 => {
|
||||
let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
|
||||
state.registers.pc = address;
|
||||
state.registers.opcode_bytecount = Some(0);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ret(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_pop_stack();
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let lsb = state.registers.take_mem() as u16;
|
||||
state.registers.set_hold(lsb);
|
||||
state.cpu_pop_stack();
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
2 => CycleResult::NeedsMore,
|
||||
3 => {
|
||||
let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
|
||||
state.registers.pc = address;
|
||||
state.registers.opcode_bytecount = Some(0);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reti(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_pop_stack();
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let lsb = state.registers.take_mem() as u16;
|
||||
state.registers.set_hold(lsb);
|
||||
state.cpu_pop_stack();
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
2 => CycleResult::NeedsMore,
|
||||
3 => {
|
||||
state.interrupts.ime = true;
|
||||
let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
|
||||
state.registers.pc = address;
|
||||
state.registers.opcode_bytecount = Some(0);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ret_nz(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => CycleResult::NeedsMore,
|
||||
1 => {
|
||||
if state.registers.get_zero() {
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
state.cpu_pop_stack();
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
let lsb = state.registers.take_mem() as u16;
|
||||
state.registers.set_hold(lsb);
|
||||
state.cpu_pop_stack();
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
3 => CycleResult::NeedsMore,
|
||||
4 => {
|
||||
let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
|
||||
state.registers.pc = address;
|
||||
state.registers.opcode_bytecount = Some(0);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ret_nc(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => CycleResult::NeedsMore,
|
||||
1 => {
|
||||
if state.registers.get_carry() {
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
state.cpu_pop_stack();
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
let lsb = state.registers.take_mem() as u16;
|
||||
state.registers.set_hold(lsb);
|
||||
state.cpu_pop_stack();
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
3 => CycleResult::NeedsMore,
|
||||
4 => {
|
||||
let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
|
||||
state.registers.pc = address;
|
||||
state.registers.opcode_bytecount = Some(0);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ret_z(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => CycleResult::NeedsMore,
|
||||
1 => {
|
||||
if !state.registers.get_zero() {
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
state.cpu_pop_stack();
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
let lsb = state.registers.take_mem() as u16;
|
||||
state.registers.set_hold(lsb);
|
||||
state.cpu_pop_stack();
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
3 => CycleResult::NeedsMore,
|
||||
4 => {
|
||||
let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
|
||||
state.registers.pc = address;
|
||||
state.registers.opcode_bytecount = Some(0);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ret_c(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => CycleResult::NeedsMore,
|
||||
1 => {
|
||||
if !state.registers.get_carry() {
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
state.cpu_pop_stack();
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
let lsb = state.registers.take_mem() as u16;
|
||||
state.registers.set_hold(lsb);
|
||||
state.cpu_pop_stack();
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
3 => CycleResult::NeedsMore,
|
||||
4 => {
|
||||
let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
|
||||
state.registers.pc = address;
|
||||
state.registers.opcode_bytecount = Some(0);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! define_rst {
|
||||
($addr:literal) => {
|
||||
paste::paste! {
|
||||
pub fn [<rst_ $addr>](state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => CycleResult::NeedsMore,
|
||||
1 => {
|
||||
state.cpu_push_stack((state.registers.pc >> 8) as u8);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
2 => {
|
||||
state.cpu_push_stack((state.registers.pc & 0xFF) as u8);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
3 => {
|
||||
state.registers.pc = $addr;
|
||||
state.registers.opcode_bytecount = Some(0);
|
||||
CycleResult::Finished
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_rst!(0x0);
|
||||
define_rst!(0x08);
|
||||
define_rst!(0x10);
|
||||
define_rst!(0x18);
|
||||
define_rst!(0x20);
|
||||
define_rst!(0x28);
|
||||
define_rst!(0x30);
|
||||
define_rst!(0x38);
|
|
@ -1,458 +0,0 @@
|
|||
use crate::gameboy::{cpu::CycleResult, Gameboy};
|
||||
|
||||
macro_rules! define_ld_reg_imm_u16 {
|
||||
($reg:ident) => {
|
||||
paste::paste! {
|
||||
pub fn [<ld_ $reg _imm_u16>](state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let mut reg = state.registers.[<get_ $reg>]();
|
||||
reg &= 0xFF00;
|
||||
reg |= state.registers.take_mem() as u16;
|
||||
state.registers.[<set_ $reg>](reg);
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(2).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
2 => {
|
||||
let mut reg = state.registers.[<get_ $reg>]();
|
||||
reg &= 0xFF;
|
||||
reg |= (state.registers.take_mem() as u16) << 8;
|
||||
state.registers.[<set_ $reg>](reg);
|
||||
state.registers.opcode_bytecount = Some(3);
|
||||
CycleResult::Finished
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_ld_reg_imm_u16!(bc);
|
||||
define_ld_reg_imm_u16!(de);
|
||||
define_ld_reg_imm_u16!(hl);
|
||||
define_ld_reg_imm_u16!(sp);
|
||||
|
||||
pub fn ld_sp_hl(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.registers.sp &= 0xFF00;
|
||||
state.registers.sp |= state.registers.l as u16;
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
state.registers.sp &= 0xFF;
|
||||
state.registers.sp |= (state.registers.h as u16) << 8;
|
||||
state.registers.opcode_bytecount = Some(3);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ld_deref_imm_u16_sp(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let low_addr = state.registers.take_mem() as u16;
|
||||
state.registers.set_hold(low_addr);
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(2).0);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
2 => {
|
||||
let addr = ((state.registers.take_mem() as u16) << 8) | state.registers.take_hold();
|
||||
state.registers.set_hold(addr);
|
||||
state.cpu_write_u8(addr, state.registers.sp as u8);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
3 => {
|
||||
let addr = state.registers.take_hold().overflowing_add(1).0;
|
||||
state.cpu_write_u8(addr, (state.registers.sp >> 8) as u8);
|
||||
state.registers.opcode_bytecount = Some(3);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! define_ld_reg_reg {
|
||||
($lreg:ident, $rreg:ident) => {
|
||||
paste::paste! {
|
||||
pub fn [<ld_ $lreg _ $rreg>](state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
let res = state.registers.$rreg;
|
||||
state.registers.$lreg = res;
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! define_ld_reg_regs {
|
||||
($lreg:ident) => {
|
||||
define_ld_reg_reg!($lreg, b);
|
||||
define_ld_reg_reg!($lreg, c);
|
||||
define_ld_reg_reg!($lreg, d);
|
||||
define_ld_reg_reg!($lreg, e);
|
||||
define_ld_reg_reg!($lreg, h);
|
||||
define_ld_reg_reg!($lreg, l);
|
||||
define_ld_reg_reg!($lreg, a);
|
||||
};
|
||||
}
|
||||
|
||||
define_ld_reg_regs!(b);
|
||||
define_ld_reg_regs!(c);
|
||||
define_ld_reg_regs!(d);
|
||||
define_ld_reg_regs!(e);
|
||||
define_ld_reg_regs!(h);
|
||||
define_ld_reg_regs!(l);
|
||||
define_ld_reg_regs!(a);
|
||||
|
||||
macro_rules! define_ld_reg_deref {
|
||||
($lreg:ident, $rreg:ident) => {
|
||||
paste::paste! {
|
||||
pub fn [<ld_ $lreg _deref_ $rreg>](state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.[<get_ $rreg>]());
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
state.registers.$lreg = state.registers.take_mem();
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_ld_reg_deref!(b, hl);
|
||||
define_ld_reg_deref!(c, hl);
|
||||
define_ld_reg_deref!(d, hl);
|
||||
define_ld_reg_deref!(e, hl);
|
||||
define_ld_reg_deref!(h, hl);
|
||||
define_ld_reg_deref!(l, hl);
|
||||
define_ld_reg_deref!(a, hl);
|
||||
define_ld_reg_deref!(a, bc);
|
||||
define_ld_reg_deref!(a, de);
|
||||
|
||||
pub fn ld_hl_minus_a(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_write_u8(state.registers.get_hl(), state.registers.a);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let reg = state.registers.get_hl().overflowing_sub(1).0;
|
||||
state.registers.set_hl(reg);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ld_a_hl_minus(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.get_hl());
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
state.registers.a = state.registers.take_mem();
|
||||
let reg = state.registers.get_hl().overflowing_sub(1).0;
|
||||
state.registers.set_hl(reg);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ld_hl_plus_a(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_write_u8(state.registers.get_hl(), state.registers.a);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let reg = state.registers.get_hl().overflowing_add(1).0;
|
||||
state.registers.set_hl(reg);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ld_a_hl_plus(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.get_hl());
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
state.registers.a = state.registers.take_mem();
|
||||
let reg = state.registers.get_hl().overflowing_add(1).0;
|
||||
state.registers.set_hl(reg);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! define_ld_reg_imm_u8 {
|
||||
($lreg:ident) => {
|
||||
paste::paste! {
|
||||
pub fn [<ld_ $lreg _imm_u8>](state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
state.registers.$lreg = state.registers.take_mem();
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_ld_reg_imm_u8!(b);
|
||||
define_ld_reg_imm_u8!(c);
|
||||
define_ld_reg_imm_u8!(d);
|
||||
define_ld_reg_imm_u8!(e);
|
||||
define_ld_reg_imm_u8!(h);
|
||||
define_ld_reg_imm_u8!(l);
|
||||
define_ld_reg_imm_u8!(a);
|
||||
|
||||
pub fn ld_deref_hl_imm_u8(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let imm = state.registers.take_mem();
|
||||
state.cpu_write_u8(state.registers.get_hl(), imm);
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
2 => CycleResult::Finished,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ldh_a_imm_u8(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let imm = state.registers.take_mem();
|
||||
let addr = 0xFF00u16 | imm as u16;
|
||||
state.cpu_read_u8(addr);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
2 => {
|
||||
state.registers.a = state.registers.take_mem();
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ldh_imm_u8_a(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let imm = state.registers.take_mem();
|
||||
let addr = 0xFF00u16 | imm as u16;
|
||||
state.cpu_write_u8(addr, state.registers.a);
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
2 => CycleResult::Finished,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ldh_a_deref_c(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
let imm = state.registers.c;
|
||||
let addr = 0xFF00u16 | imm as u16;
|
||||
state.cpu_read_u8(addr);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
state.registers.a = state.registers.take_mem();
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ldh_deref_c_a(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
let addr = 0xFF00u16 | state.registers.c as u16;
|
||||
state.cpu_write_u8(addr, state.registers.a);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => CycleResult::Finished,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ld_a_deref_imm_u16(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let lsb = state.registers.take_mem() as u16;
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(2).0);
|
||||
state.registers.set_hold(lsb);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
2 => {
|
||||
let addr = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
|
||||
state.cpu_read_u8(addr);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
3 => {
|
||||
state.registers.a = state.registers.take_mem();
|
||||
state.registers.opcode_bytecount = Some(3);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ld_deref_imm_u16_a(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let lsb = state.registers.take_mem() as u16;
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(2).0);
|
||||
state.registers.set_hold(lsb);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
2 => {
|
||||
let addr = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
|
||||
state.cpu_write_u8(addr, state.registers.a);
|
||||
state.registers.opcode_bytecount = Some(3);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
3 => CycleResult::Finished,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! define_ld_deref_hl_reg {
|
||||
($lreg:ident) => {
|
||||
paste::paste! {
|
||||
pub fn [<ld_deref_hl_ $lreg>](state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_write_u8(state.registers.get_hl(), state.registers.$lreg);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => CycleResult::Finished,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_ld_deref_hl_reg!(b);
|
||||
define_ld_deref_hl_reg!(c);
|
||||
define_ld_deref_hl_reg!(d);
|
||||
define_ld_deref_hl_reg!(e);
|
||||
define_ld_deref_hl_reg!(h);
|
||||
define_ld_deref_hl_reg!(l);
|
||||
define_ld_deref_hl_reg!(a);
|
||||
|
||||
macro_rules! define_push_pop_reg {
|
||||
($reg:ident) => {
|
||||
paste::paste! {
|
||||
pub fn [<push_ $reg>](state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => CycleResult::NeedsMore,
|
||||
1 => {
|
||||
state.cpu_push_stack((state.registers.[<get_ $reg>]() >> 8) as u8);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
2 => {
|
||||
state.cpu_push_stack(state.registers.[<get_ $reg>]() as u8);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
3 => CycleResult::Finished,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn [<pop_ $reg>](state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_pop_stack();
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
let lsb = state.registers.take_mem() as u16;
|
||||
state.registers.set_hold(lsb);
|
||||
state.cpu_pop_stack();
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
2 => {
|
||||
let val = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
|
||||
state.registers.[<set_ $reg>](val);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_push_pop_reg!(bc);
|
||||
define_push_pop_reg!(de);
|
||||
define_push_pop_reg!(hl);
|
||||
define_push_pop_reg!(af);
|
|
@ -1,34 +0,0 @@
|
|||
use super::CycleResult;
|
||||
use crate::gameboy::Gameboy;
|
||||
|
||||
pub fn nop(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn di(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.interrupts.ime = false;
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ei(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.interrupts.ime = true;
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
|
@ -1,243 +0,0 @@
|
|||
use super::CycleResult;
|
||||
use crate::gameboy::Gameboy;
|
||||
|
||||
pub fn prefixed_handler(state: &mut Gameboy) -> CycleResult {
|
||||
let opcode = match state.registers.current_prefixed_opcode {
|
||||
Some(prefixed_opcode) => prefixed_opcode,
|
||||
None => match state.registers.mem_read_hold.take() {
|
||||
Some(opcode) => {
|
||||
state.registers.current_prefixed_opcode = Some(opcode);
|
||||
opcode
|
||||
}
|
||||
None => {
|
||||
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
|
||||
return CycleResult::NeedsMore;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let res: CycleResult = match opcode {
|
||||
0x10 => rl_b,
|
||||
0x11 => rl_c,
|
||||
0x12 => rl_d,
|
||||
0x13 => rl_e,
|
||||
0x14 => rl_h,
|
||||
0x15 => rl_l,
|
||||
0x17 => rl_a,
|
||||
0x30 => swap_b,
|
||||
0x31 => swap_c,
|
||||
0x32 => swap_d,
|
||||
0x33 => swap_e,
|
||||
0x34 => swap_h,
|
||||
0x35 => swap_l,
|
||||
0x37 => swap_a,
|
||||
0x40 => bit_0_b,
|
||||
0x41 => bit_0_c,
|
||||
0x42 => bit_0_d,
|
||||
0x43 => bit_0_e,
|
||||
0x44 => bit_0_h,
|
||||
0x45 => bit_0_l,
|
||||
0x46 => bit_0_deref_hl,
|
||||
0x47 => bit_0_a,
|
||||
0x48 => bit_1_b,
|
||||
0x49 => bit_1_c,
|
||||
0x4a => bit_1_d,
|
||||
0x4b => bit_1_e,
|
||||
0x4c => bit_1_h,
|
||||
0x4d => bit_1_l,
|
||||
0x4e => bit_1_deref_hl,
|
||||
0x4f => bit_1_a,
|
||||
0x50 => bit_2_b,
|
||||
0x51 => bit_2_c,
|
||||
0x52 => bit_2_d,
|
||||
0x53 => bit_2_e,
|
||||
0x54 => bit_2_h,
|
||||
0x55 => bit_2_l,
|
||||
0x56 => bit_2_deref_hl,
|
||||
0x57 => bit_2_a,
|
||||
0x58 => bit_3_b,
|
||||
0x59 => bit_3_c,
|
||||
0x5a => bit_3_d,
|
||||
0x5b => bit_3_e,
|
||||
0x5c => bit_3_h,
|
||||
0x5d => bit_3_l,
|
||||
0x5e => bit_3_deref_hl,
|
||||
0x5f => bit_3_a,
|
||||
0x60 => bit_4_b,
|
||||
0x61 => bit_4_c,
|
||||
0x62 => bit_4_d,
|
||||
0x63 => bit_4_e,
|
||||
0x64 => bit_4_h,
|
||||
0x65 => bit_4_l,
|
||||
0x66 => bit_4_deref_hl,
|
||||
0x67 => bit_4_a,
|
||||
0x68 => bit_5_b,
|
||||
0x69 => bit_5_c,
|
||||
0x6a => bit_5_d,
|
||||
0x6b => bit_5_e,
|
||||
0x6c => bit_5_h,
|
||||
0x6d => bit_5_l,
|
||||
0x6e => bit_5_deref_hl,
|
||||
0x6f => bit_5_a,
|
||||
0x70 => bit_6_b,
|
||||
0x71 => bit_6_c,
|
||||
0x72 => bit_6_d,
|
||||
0x73 => bit_6_e,
|
||||
0x74 => bit_6_h,
|
||||
0x75 => bit_6_l,
|
||||
0x76 => bit_6_deref_hl,
|
||||
0x77 => bit_6_a,
|
||||
0x78 => bit_7_b,
|
||||
0x79 => bit_7_c,
|
||||
0x7a => bit_7_d,
|
||||
0x7b => bit_7_e,
|
||||
0x7c => bit_7_h,
|
||||
0x7d => bit_7_l,
|
||||
0x7e => bit_7_deref_hl,
|
||||
0x7f => bit_7_a,
|
||||
unknown => panic!(
|
||||
"Unrecognized prefixed opcode: {:#X}\nRegisters: {:#?}",
|
||||
unknown, state.registers
|
||||
),
|
||||
}(state);
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
macro_rules! define_bit_reg {
|
||||
($bit:literal, $reg:ident) => {
|
||||
paste::paste! {
|
||||
pub fn [<bit_ $bit _ $reg>](state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
1 => {
|
||||
state.registers.set_zero(state.registers.$reg & (1 << $bit) == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(true);
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! define_bits_reg {
|
||||
($reg:ident) => {
|
||||
define_bit_reg!(0, $reg);
|
||||
define_bit_reg!(1, $reg);
|
||||
define_bit_reg!(2, $reg);
|
||||
define_bit_reg!(3, $reg);
|
||||
define_bit_reg!(4, $reg);
|
||||
define_bit_reg!(5, $reg);
|
||||
define_bit_reg!(6, $reg);
|
||||
define_bit_reg!(7, $reg);
|
||||
};
|
||||
}
|
||||
|
||||
define_bits_reg!(b);
|
||||
define_bits_reg!(c);
|
||||
define_bits_reg!(d);
|
||||
define_bits_reg!(e);
|
||||
define_bits_reg!(h);
|
||||
define_bits_reg!(l);
|
||||
define_bits_reg!(a);
|
||||
|
||||
macro_rules! define_bit_deref_hl {
|
||||
($bit:literal) => {
|
||||
paste::paste! {
|
||||
pub fn [<bit_ $bit _deref_hl>](state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
1 => {
|
||||
state.cpu_read_u8(state.registers.get_hl());
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
2 => {
|
||||
let mem_read = state.registers.take_mem();
|
||||
state.registers.set_zero(mem_read & (1 << $bit) == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(true);
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_bit_deref_hl!(0);
|
||||
define_bit_deref_hl!(1);
|
||||
define_bit_deref_hl!(2);
|
||||
define_bit_deref_hl!(3);
|
||||
define_bit_deref_hl!(4);
|
||||
define_bit_deref_hl!(5);
|
||||
define_bit_deref_hl!(6);
|
||||
define_bit_deref_hl!(7);
|
||||
|
||||
macro_rules! define_rl_reg {
|
||||
($reg:ident) => {
|
||||
paste::paste! {
|
||||
pub fn [<rl_ $reg>](state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
1 => {
|
||||
let carry = state.registers.$reg >> 7 == 1;
|
||||
state.registers.$reg <<= 1;
|
||||
|
||||
if state.registers.get_carry() {
|
||||
state.registers.$reg |= 1;
|
||||
}
|
||||
|
||||
state.registers.set_zero(state.registers.$reg == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(false);
|
||||
state.registers.set_carry(carry);
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_rl_reg!(b);
|
||||
define_rl_reg!(c);
|
||||
define_rl_reg!(d);
|
||||
define_rl_reg!(e);
|
||||
define_rl_reg!(h);
|
||||
define_rl_reg!(l);
|
||||
define_rl_reg!(a);
|
||||
|
||||
macro_rules! define_swap_reg {
|
||||
($reg:ident) => {
|
||||
paste::paste! {
|
||||
pub fn [<swap_ $reg>](state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
1 => {
|
||||
state.registers.$reg = (state.registers.$reg >> 4) | (state.registers.$reg << 4);
|
||||
|
||||
state.registers.set_zero(state.registers.$reg == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(false);
|
||||
state.registers.set_carry(false);
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_swap_reg!(b);
|
||||
define_swap_reg!(c);
|
||||
define_swap_reg!(d);
|
||||
define_swap_reg!(e);
|
||||
define_swap_reg!(h);
|
||||
define_swap_reg!(l);
|
||||
define_swap_reg!(a);
|
Loading…
Reference in a new issue