#![macro_use]
macro_rules! fetch_next_opcode_ext {
($cpu:ident, $control:ident, $pc:ident, $tsc:ident) => {
{ let (pc_1, code) = $cpu.fetch_next_opcode($control, $tsc, $pc);
$pc = pc_1;
code
}
};
}
macro_rules! indexed_address {
($ii:expr, $d:expr) => {
$ii.wrapping_add($d as i8 as i16 as u16)
};
}
macro_rules! define_cpu_debug_scoped {
([$dol:tt] $deb:expr; $maybe_prefix:ident, $pc:ident) => {
macro_rules! cpu_debug {
(@deb $debugger:ident, [$dol($code:expr),+] str($mnemonic:expr) $dol($id:ident:$e:tt),*) => {
CpuDebug::debug_instruction(
$mnemonic.into(),
cpu_debug!(@args $dol($id:$e),*),
$maybe_prefix,
&[$dol($code),+],
$pc,
$debugger)
};
(@deb $debugger:ident, [$dol($code:expr),+] $mnemonic:ident $dol($id:ident:$e:tt),*) => {
cpu_debug!(@deb $debugger, [$dol($code),+] str(stringify!($mnemonic)) $dol($id:$e),*)
};
([$dol($code:expr),+] str($mnemonic:expr) $dol($id:ident:$e:tt),*) => {
if let Some(debugger) = $deb {
cpu_debug!(@deb debugger, [$dol($code),+] str($mnemonic) $dol($id:$e),*)
}
};
([$dol($code:expr),+] $mnemonic:literal $dol($id:ident:$e:tt),*) => {
cpu_debug!([$dol($code),+] str($mnemonic) $dol($id:$e),*)
};
([$dol($code:expr),+] $mnemonic:ident $dol($id:ident:$e:tt),*) => {
cpu_debug!([$dol($code),+] str(stringify!($mnemonic)) $dol($id:$e),*)
};
([$dol($code:expr),+] op8($op:expr) $id:ident:$e:tt) => {
if let Some(debugger) = $deb {
match $op {
Ops8::ADD|
Ops8::ADC|
Ops8::SBC => cpu_debug!(@deb debugger, [$dol($code),+] str($op) r:A, $id:$e),
_ => cpu_debug!(@deb debugger, [$dol($code),+] str($op) $id:$e)
}
}
};
([$dol($code:expr),+] bitops($bops:expr)) => {
if let Some(debugger) = $deb {
match $bops {
BitOps::Rot(rot, arg) => cpu_debug!(@deb debugger, [$dol($code),+] str(rot) r_addr:arg),
BitOps::Bit(b, arg) => cpu_debug!(@deb debugger, [$dol($code),+] BIT b:b, r_addr:arg),
BitOps::Res(b, arg) => cpu_debug!(@deb debugger, [$dol($code),+] RES b:b, r_addr:arg),
BitOps::Set(b, arg) => cpu_debug!(@deb debugger, [$dol($code),+] SET b:b, r_addr:arg)
}
}
};
([$dol($code:expr),+] bitops($bops:expr) ii:$d:expr) => {
if let Some(debugger) = $deb {
match $bops {
BitOps::Rot(rot, arg) => cpu_debug!(@deb debugger, [$dol($code),+] str(rot) ii:$d, maybe_r:arg),
BitOps::Bit(b, _) => cpu_debug!(@deb debugger, [$dol($code),+] BIT b:b, ii:$d),
BitOps::Res(b, arg) => cpu_debug!(@deb debugger, [$dol($code),+] RES b:b, ii:$d, maybe_r:arg),
BitOps::Set(b, arg) => cpu_debug!(@deb debugger, [$dol($code),+] SET b:b, ii:$d, maybe_r:arg)
}
}
};
(@args) => { CpuDebugArgs::None };
(@args ii:$d:expr, maybe_r:$res:expr) => {
match $res {
Ok(reg) => CpuDebugArgs::Double(cpu_debug!(@arg ii:$d), cpu_debug!(@arg r:reg)),
Err(_) => CpuDebugArgs::Single(cpu_debug!(@arg ii:$d))
}
};
(@args b:$b:expr, ii:$d:expr, maybe_r:$res:expr) => {
match $res {
Ok(reg) => CpuDebugArgs::BitOpExt($b, cpu_debug!(@arg ii:$d), reg),
Err(_) => CpuDebugArgs::Double(cpu_debug!(@arg b:$b), cpu_debug!(@arg ii:$d)),
}
};
(@args maybe_r:$reg:expr, $id:ident:$e:tt) => {
match $reg {
Ok(reg) => CpuDebugArgs::Double(cpu_debug!(@arg r:reg), cpu_debug!(@arg $id:$e)),
Err(_) => CpuDebugArgs::Single(cpu_debug!(@arg $id:$e))
}
};
(@args $id:ident:$e:tt, maybe_r:$reg:expr) => {
match $reg {
Ok(reg) => CpuDebugArgs::Double(cpu_debug!(@arg $id:$e), cpu_debug!(@arg r:reg)),
Err(_) => CpuDebugArgs::Single(cpu_debug!(@arg $id:$e))
}
};
(@args $id1:ident:$e1:tt) => { CpuDebugArgs::Single(cpu_debug!(@arg $id1:$e1)) };
(@args $id1:ident:$e1:tt, $id2:ident:$e2:tt) => {
CpuDebugArgs::Double(cpu_debug!(@arg $id1:$e1), cpu_debug!(@arg $id2:$e2))
};
(@arg n:$n:expr) => { CpuDebugArg::Imm8($n) };
(@arg b:$b:expr) => { CpuDebugArg::Bit($b) };
(@arg m:$m:tt) => { CpuDebugArg::IntMode(interrupt_mode!($m)) };
(@arg nn:$nn:expr) => { CpuDebugArg::Imm16($nn) };
(@arg p:$nn:expr) => { CpuDebugArg::Imm8($nn as u8) };
(@arg nn_0:$nn:expr) => { CpuDebugArg::Imm16($nn.0) };
(@arg r:A) => { CpuDebugArg::Reg8(None, Reg8::A) };
(@arg r:I) => { CpuDebugArg::I };
(@arg r:R) => { CpuDebugArg::R };
(@arg r:$reg:expr) => { CpuDebugArg::Reg8(None, $reg) };
(@arg q:$reg:expr) => { CpuDebugArg::Reg8($maybe_prefix, $reg) };
(@arg rr:HL) => { CpuDebugArg::Reg16(None, Reg16::HL) };
(@arg rr:DE) => { CpuDebugArg::Reg16(None, Reg16::DE) };
(@arg rr:SP) => { CpuDebugArg::Reg16(None, Reg16::SP) };
(@arg rr:$reg:expr) => { CpuDebugArg::Reg16(None, $reg) };
(@arg qq:ii) => { CpuDebugArg::Reg16($maybe_prefix, Reg16::HL) };
(@arg qq:$reg:expr) => { CpuDebugArg::Reg16($maybe_prefix, $reg) };
(@arg ss:AF) => { CpuDebugArg::Stk16(StkReg16::AF) };
(@arg ss:$reg:expr) => { CpuDebugArg::Stk16($reg) };
(@arg addr:HL) => { CpuDebugArg::Addr(CpuDebugAddr::RegAddr(Reg16::HL)) };
(@arg addr:bc) => { CpuDebugArg::Addr(CpuDebugAddr::RegAddr(Reg16::BC)) };
(@arg addr:de) => { CpuDebugArg::Addr(CpuDebugAddr::RegAddr(Reg16::DE)) };
(@arg addr:SP) => { CpuDebugArg::Addr(CpuDebugAddr::RegAddr(Reg16::SP)) };
(@arg addr:ii) => {
match $maybe_prefix {
Some(prefix) => CpuDebugArg::Addr(CpuDebugAddr::IndexAddr(prefix, None)),
None => panic!("addr:ii requires a prefix")
}
};
(@arg addr:$reg:expr) => { CpuDebugArg::Addr(CpuDebugAddr::RegAddr($reg)) };
(@arg ii:$d:expr) => { CpuDebugArg::Addr(CpuDebugAddr::IndexAddr($maybe_prefix.unwrap(), Some($d as i8))) };
(@arg adnn:$nn:expr) => { CpuDebugArg::Addr(CpuDebugAddr::ImmAddr($nn)) };
(@arg port:C) => { CpuDebugArg::Port(CpuDebugPort::RegPort) };
(@arg port:$n:expr) => { CpuDebugArg::Port(CpuDebugPort::ImmPort($n)) };
(@arg cc:$cond:expr) => { CpuDebugArg::Cond($cond) };
(@arg rel:$e:expr) => { CpuDebugArg::Imm16(relative_jump_address!($e).0) };
(@arg r_addr:$res:expr) => {
match $res {
Ok(reg) => CpuDebugArg::Reg8(None, reg),
Err(_) => CpuDebugArg::Addr(CpuDebugAddr::RegAddr(Reg16::HL))
}
};
}
};
}
macro_rules! define_helpers_scoped {
([$dol:tt] $flags:ident, $pc:ident, $cpu:ident, $control:ident, $tsc:ident) => {
macro_rules! fetch_next_opcode {
() => { fetch_next_opcode_ext!($cpu, $control, $pc, $tsc) };
}
macro_rules! fetch_next_imm8 {
(@x $add_ts_more:expr) => {
{ let res = $cpu.fetch_next_imm8::<_, _, {$add_ts_more}>($control, $tsc, $pc);
$pc = res.0;
res.1
}
};
() => {
fetch_next_imm8!(@x 0)
};
(no_mreq: $add_ts_more:expr) => {
fetch_next_imm8!(@x $add_ts_more.get())
};
}
macro_rules! fetch_next_imm16 {
(@x $add_ts_more:expr) => {
{ let res = $cpu.fetch_next_imm16::<_, _, {$add_ts_more}>($control, $tsc, $pc);
$pc = res.0;
res.1
}
};
() => {
fetch_next_imm16!(@x 0)
};
(no_mreq: $add_ts_more:expr) => {
fetch_next_imm16!(@x $add_ts_more.get())
};
}
macro_rules! read_mem8_reg16 {
(<- [hl] @x $add_ts_more:expr) => { $cpu.read_mem8_hl::<_, _, {$add_ts_more}>($control, $tsc)
};
(<- [hl]) => {
read_mem8_reg16!(<- [hl] @x 0)
};
(<- [hl] ;no_mreq: $add_ts_more:expr) => {
read_mem8_reg16!(<- [hl] @x $add_ts_more.get())
};
(<- [$prefix:ident+$index8:ident] memptr=ii+d) => {
$cpu.read_mem8_ii_d($control, $tsc, $prefix, $index8)
};
}
macro_rules! write_mem8_reg16 {
([hl] <- $val:expr) => { $cpu.write_mem8_hl($control, $tsc, $val)
};
([$prefix:ident+$index8:ident] <- $val:expr; memptr=ii+d) => {
$cpu.write_mem8_ii_d($control, $tsc, $prefix, $index8, $val)
};
}
macro_rules! read_mem16_addr16 {
(<- [$addr:expr] memptr=addr+1) => {
$cpu.read_mem16_addr16($control, $tsc, $addr)
};
}
macro_rules! write_mem16_addr16 {
([$addr:expr] <- $val2:expr; memptr=addr+1) => {
$cpu.write_mem16_addr16($control, $tsc, $addr, $val2)
};
}
macro_rules! push2 {
($vhi:expr, $vlo:expr) => {
$cpu.push2($vhi, $vlo, $control, $tsc)
};
}
macro_rules! push16 {
($val:expr) => {
$cpu.push16($val, $control, $tsc)
};
}
macro_rules! pop16 {
() => {
$cpu.pop16($control, $tsc)
};
}
macro_rules! ex_sp_nn {
($val2:expr) => {
$cpu.ex_sp_nn($control, $tsc, $val2)
};
}
macro_rules! op16_reg16 {
($op16:ident: $reg16:expr, $nn:expr) => {
{ $reg16 = $cpu.op16_reg16($tsc, &mut $flags, ops::$op16, $reg16, $nn);
}
};
}
macro_rules! instr_rxd {
($rxd:path) => {
{
$cpu.instr_rxd($control, $tsc, &mut $flags, $rxd);
}
};
}
macro_rules! flags_op {
() => {
$cpu.flavour.flags_modified();
}
}
macro_rules! relative_jump_address {
($e:expr) => {
$pc + Wrapping($e as i8 as i16 as u16)
};
}
macro_rules! acc_op_str {
(daa) => { "DAA" };
(cpl) => { "CPL" };
(neg) => { "NEG" };
(rlca) => { "RLCA" };
(rrca) => { "RRCA" };
(rla) => { "RLA" };
(rra) => { "RRA" };
}
macro_rules! incdec_str {
(inc) => { "INC" };
(dec) => { "DEC" };
}
macro_rules! interrupt_mode {
(0) => { InterruptMode::Mode0 };
(1) => { InterruptMode::Mode1 };
(2) => { InterruptMode::Mode2 };
}
};
}
macro_rules! debug_unreachable_unchecked {
() => {
{
#[cfg(debug_assertions)]
{ unreachable!() }
#[cfg(not(debug_assertions))]
unsafe { core::hint::unreachable_unchecked() }
}
};
}