feat: add opcode logging

This commit is contained in:
EliseZeroTwo 2021-12-14 20:31:33 +01:00
parent 8c5c1c4b23
commit 96bc5e117d
No known key found for this signature in database
GPG key ID: E6D56A6F7B7991DE
31 changed files with 4461 additions and 2469 deletions

2
.gitignore vendored
View file

@ -1,4 +1,4 @@
/target
**/target
/roms/
/bmp/
config.toml

142
Cargo.lock generated
View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

22
deemgee/Cargo.toml Normal file
View 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"

View file

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

View file

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

View 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
}
});

View 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);

View 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);

View 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
}
});

View 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);

View file

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

View file

@ -26,4 +26,5 @@ pub struct Bindings {
pub pause: VirtualKeyCode,
pub exit: VirtualKeyCode,
pub log_ops: VirtualKeyCode,
}

View file

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

View file

@ -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!(),
}
}

View file

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

View file

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

View file

@ -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!(),
}
}

View file

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