use std::mem;
use crate::gbz80core::ExecutorContext;
use crate::{interrupts::Interrupts, memdev::RootMemDevice};
use feo3boy_opcodes::{
gbz80types::Flags,
microcode::args::{Reg16, Reg8},
};
#[inline]
pub(super) fn read_reg(ctx: &mut impl ExecutorContext, reg: Reg8) -> u8 {
match reg {
Reg8::Acc => ctx.cpu().regs.acc,
Reg8::B => ctx.cpu().regs.b,
Reg8::C => ctx.cpu().regs.c,
Reg8::D => ctx.cpu().regs.d,
Reg8::E => ctx.cpu().regs.e,
Reg8::H => ctx.cpu().regs.h,
Reg8::L => ctx.cpu().regs.l,
}
}
#[inline]
pub(super) fn write_reg(ctx: &mut impl ExecutorContext, reg: Reg8, val: u8) {
match reg {
Reg8::Acc => ctx.cpu_mut().regs.acc = val,
Reg8::B => ctx.cpu_mut().regs.b = val,
Reg8::C => ctx.cpu_mut().regs.c = val,
Reg8::D => ctx.cpu_mut().regs.d = val,
Reg8::E => ctx.cpu_mut().regs.e = val,
Reg8::H => ctx.cpu_mut().regs.h = val,
Reg8::L => ctx.cpu_mut().regs.l = val,
}
}
#[inline]
pub(super) fn read_reg16(ctx: &mut impl ExecutorContext, reg: Reg16) -> u16 {
match reg {
Reg16::AF => ctx.cpu().regs.af(),
Reg16::BC => ctx.cpu().regs.bc(),
Reg16::DE => ctx.cpu().regs.de(),
Reg16::HL => ctx.cpu().regs.hl(),
Reg16::Sp => ctx.cpu().regs.sp,
Reg16::Pc => ctx.cpu().regs.pc,
}
}
#[inline]
pub(super) fn write_reg16(ctx: &mut impl ExecutorContext, reg: Reg16, val: u16) {
match reg {
Reg16::AF => ctx.cpu_mut().regs.set_af(val),
Reg16::BC => ctx.cpu_mut().regs.set_bc(val),
Reg16::DE => ctx.cpu_mut().regs.set_de(val),
Reg16::HL => ctx.cpu_mut().regs.set_hl(val),
Reg16::Sp => ctx.cpu_mut().regs.sp = val,
Reg16::Pc => ctx.cpu_mut().regs.pc = val,
}
}
#[inline]
pub(super) fn read_mem(ctx: &mut impl ExecutorContext, addr: u16) -> u8 {
ctx.mem().read(addr)
}
#[inline]
pub(super) fn write_mem(ctx: &mut impl ExecutorContext, addr: u16, val: u8) {
ctx.mem_mut().write(addr, val)
}
#[inline]
pub(super) fn get_flags_masked(ctx: &mut impl ExecutorContext, mask: Flags) -> Flags {
ctx.cpu().regs.flags & mask
}
#[inline]
pub(super) fn set_flags_masked(ctx: &mut impl ExecutorContext, mask: Flags, flags: Flags) {
ctx.cpu_mut().regs.flags.merge(flags, mask)
}
pub(super) fn halt(ctx: &mut impl ExecutorContext) {
if ctx.cpu().interrupt_master_enable.enabled() {
ctx.cpu_mut().halted = true;
} else {
let enabled_interrupts = ctx.interrupts().enabled();
let pending_interrupts = ctx.interrupts().queued();
if enabled_interrupts.intersects(pending_interrupts) {
ctx.cpu_mut().halt_bug = true;
} else {
ctx.cpu_mut().halted = true;
}
}
}
#[inline]
pub(super) fn enable_interrupts(ctx: &mut impl ExecutorContext, immediate: bool) {
if immediate {
ctx.cpu_mut().interrupt_master_enable.set();
} else {
ctx.cpu_mut().interrupt_master_enable.set_next_instruction();
}
}
#[inline]
pub(super) fn disable_interrupts(ctx: &mut impl ExecutorContext) {
ctx.cpu_mut().interrupt_master_enable.clear();
}
#[inline]
pub(super) fn check_halt(ctx: &mut impl ExecutorContext) -> bool {
ctx.cpu().halted
}
#[inline]
pub(super) fn clear_halt(ctx: &mut impl ExecutorContext) {
ctx.cpu_mut().halted = false;
}
#[inline]
pub(super) fn check_ime(ctx: &mut impl ExecutorContext) -> bool {
ctx.cpu().interrupt_master_enable.enabled()
}
#[inline]
pub(super) fn get_active_interrupts(ctx: &mut impl ExecutorContext) -> u8 {
ctx.interrupts().active().bits()
}
#[inline]
pub(super) fn pop_interrupt(ctx: &mut impl ExecutorContext) -> u16 {
match ctx.interrupts().active().iter().next() {
Some(interrupt) => {
ctx.interrupts_mut().clear(interrupt);
interrupt.handler_addr()
}
None => panic!(
"Must not use the PopInterrupt microcode instruction if there \
are no active interrupts."
),
}
}
#[inline]
pub(super) fn pop_halt_bug(ctx: &mut impl ExecutorContext) -> bool {
mem::replace(&mut ctx.cpu_mut().halt_bug, false)
}