use crate::core::sched::TickCursor;
use crate::core::space::{AddressSpace, MemAttrs};
use crate::core::value::Width;
use super::isa::{Cond, Insn, Op, Operand, Reg8, Reg16, decode, decode_cb};
use super::{Config, Lines, Regs, flags};
pub const VECTORS: [u16; 5] = [0x0040, 0x0048, 0x0050, 0x0058, 0x0060];
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub(super) enum Mode {
#[default]
Running,
Halted,
Stopped,
Locked,
}
impl Mode {
pub(super) const fn tag(self) -> u8 {
match self {
Mode::Running => 0,
Mode::Halted => 1,
Mode::Stopped => 2,
Mode::Locked => 3,
}
}
pub(super) const fn from_tag(tag: u8) -> Option<Mode> {
match tag {
0 => Some(Mode::Running),
1 => Some(Mode::Halted),
2 => Some(Mode::Stopped),
3 => Some(Mode::Locked),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) struct State {
pub regs: Regs,
pub cycles: u64,
pub ime: bool,
pub ei_pending: bool,
pub mode: Mode,
pub halt_bug: bool,
pub debt: u64,
pub faults: u64,
pub last_fault: u16,
}
impl State {
pub(super) const fn new() -> State {
State {
regs: Regs::new(),
cycles: 0,
ime: false,
ei_pending: false,
mode: Mode::Running,
halt_bug: false,
debt: 0,
faults: 0,
last_fault: 0,
}
}
}
pub(super) struct Exec<'a> {
state: &'a mut State,
space: &'a AddressSpace,
lines: &'a Lines,
attrs: MemAttrs,
cursor: Option<&'a TickCursor>,
used: u64,
}
impl<'a> Exec<'a> {
pub(super) fn new(
state: &'a mut State,
space: &'a AddressSpace,
cfg: &Config,
lines: &'a Lines,
) -> Exec<'a> {
Exec {
state,
space,
lines,
attrs: MemAttrs::DEFAULT.with_requester(cfg.requester),
cursor: None,
used: 0,
}
}
pub(super) fn with_cursor(mut self, cursor: Option<&'a TickCursor>) -> Exec<'a> {
self.cursor = cursor;
self
}
pub(super) fn step(&mut self) -> u64 {
match self.state.mode {
Mode::Locked => {
self.idle();
return self.used;
}
Mode::Halted => {
if self.pending() == 0 {
self.idle();
return self.used;
}
self.state.mode = Mode::Running;
}
Mode::Stopped => {
if !self.lines.stop_wake() {
self.idle();
return self.used;
}
self.state.mode = Mode::Running;
}
Mode::Running => {}
}
if self.state.ime && self.pending() != 0 {
self.dispatch_interrupt();
return self.used;
}
if self.state.ei_pending {
self.state.ime = true;
self.state.ei_pending = false;
}
self.instruction();
self.used
}
fn pending(&self) -> u8 {
self.lines.pending()
}
fn tick(&mut self) {
self.used += 1;
self.state.cycles = self.state.cycles.wrapping_add(1);
if let Some(cursor) = self.cursor {
cursor.set(self.state.cycles);
}
}
fn idle(&mut self) {
self.tick();
}
fn read(&mut self, addr: u16) -> u8 {
self.tick();
match self.space.read(u64::from(addr), Width::U8, self.attrs) {
Ok(v) => v as u8,
Err(_) => {
self.state.faults = self.state.faults.wrapping_add(1);
self.state.last_fault = addr;
0xff
}
}
}
fn write(&mut self, addr: u16, value: u8) {
self.tick();
if self
.space
.write(u64::from(addr), Width::U8, u64::from(value), self.attrs)
.is_err()
{
self.state.faults = self.state.faults.wrapping_add(1);
self.state.last_fault = addr;
}
}
fn fetch(&mut self) -> u8 {
let pc = self.state.regs.pc;
let byte = self.read(pc);
if self.state.halt_bug {
self.state.halt_bug = false;
} else {
self.state.regs.pc = pc.wrapping_add(1);
}
byte
}
fn fetch16(&mut self) -> u16 {
let lo = self.fetch();
let hi = self.fetch();
u16::from(lo) | (u16::from(hi) << 8)
}
fn push8(&mut self, value: u8) {
let sp = self.state.regs.sp.wrapping_sub(1);
self.state.regs.sp = sp;
self.write(sp, value);
}
fn push16(&mut self, value: u16) {
self.push8((value >> 8) as u8);
self.push8(value as u8);
}
fn pop8(&mut self) -> u8 {
let sp = self.state.regs.sp;
let byte = self.read(sp);
self.state.regs.sp = sp.wrapping_add(1);
byte
}
fn pop16(&mut self) -> u16 {
let lo = self.pop8();
let hi = self.pop8();
u16::from(lo) | (u16::from(hi) << 8)
}
fn dispatch_interrupt(&mut self) {
self.state.ime = false;
self.state.ei_pending = false;
self.idle(); self.idle(); let pc = self.state.regs.pc;
self.push8((pc >> 8) as u8);
let pending = self.pending();
self.push8(pc as u8);
let target = if pending == 0 {
0x0000
} else {
let bit = pending.trailing_zeros() as u8;
self.lines.clear_request(bit);
VECTORS[bit as usize]
};
self.state.regs.pc = target;
self.idle(); }
fn flag(&self, mask: u8) -> bool {
self.state.regs.f & mask != 0
}
fn set_flag(&mut self, mask: u8, on: bool) {
if on {
self.state.regs.f |= mask;
} else {
self.state.regs.f &= !mask;
}
}
fn set_flags(&mut self, z: bool, n: bool, h: bool, c: bool) {
let mut f = 0u8;
if z {
f |= flags::Z;
}
if n {
f |= flags::N;
}
if h {
f |= flags::H;
}
if c {
f |= flags::C;
}
self.state.regs.f = f;
}
fn cond(&self, c: Cond) -> bool {
match c {
Cond::Nz => !self.flag(flags::Z),
Cond::Z => self.flag(flags::Z),
Cond::Nc => !self.flag(flags::C),
Cond::C => self.flag(flags::C),
}
}
fn address(&mut self, operand: Operand) -> u16 {
match operand {
Operand::MemHl => self.state.regs.hl(),
Operand::MemReg16(r) => self.state.regs.get16(r),
Operand::MemHlInc => {
let hl = self.state.regs.hl();
self.state.regs.set_hl(hl.wrapping_add(1));
hl
}
Operand::MemHlDec => {
let hl = self.state.regs.hl();
self.state.regs.set_hl(hl.wrapping_sub(1));
hl
}
Operand::MemHighC => 0xff00 | u16::from(self.state.regs.c),
Operand::MemHighImm8 => 0xff00 | u16::from(self.fetch()),
Operand::MemImm16 => self.fetch16(),
other => unreachable!("{other:?} is not a memory operand"),
}
}
fn read8(&mut self, operand: Operand) -> u8 {
match operand {
Operand::Reg(r) => self.state.regs.get8(r),
Operand::Imm8 | Operand::Rel8 => self.fetch(),
_ => {
let addr = self.address(operand);
self.read(addr)
}
}
}
fn write8(&mut self, operand: Operand, value: u8) {
match operand {
Operand::Reg(r) => self.state.regs.set8(r, value),
_ => {
let addr = self.address(operand);
self.write(addr, value);
}
}
}
fn read16(&mut self, operand: Operand) -> u16 {
match operand {
Operand::Reg16(r) => self.state.regs.get16(r),
Operand::Imm16 => self.fetch16(),
Operand::SpRel8 => {
let e = self.fetch() as i8;
let sp = self.state.regs.sp;
let lo = u16::from(sp as u8) + u16::from(e as u8);
let h = (sp & 0x0f) + (u16::from(e as u8) & 0x0f) > 0x0f;
let c = lo > 0xff;
self.set_flags(false, false, h, c);
self.idle();
sp.wrapping_add(e as u16)
}
other => unreachable!("{other:?} is not a 16-bit source"),
}
}
fn write16(&mut self, operand: Operand, value: u16) {
match operand {
Operand::Reg16(r) => self.state.regs.set16(r, value),
Operand::MemImm16 => {
let addr = self.address(operand);
self.write(addr, value as u8);
self.write(addr.wrapping_add(1), (value >> 8) as u8);
}
other => unreachable!("{other:?} is not a 16-bit destination"),
}
}
fn instruction(&mut self) {
let opcode = self.fetch();
let insn = decode(opcode);
if insn.op == Op::PREFIX {
let second = self.fetch();
self.prefixed(decode_cb(second));
return;
}
self.execute(insn);
}
#[allow(clippy::too_many_lines)]
fn execute(&mut self, insn: Insn) {
let Insn { op, dst, src, .. } = insn;
match op {
Op::NOP => {}
Op::LD | Op::LDH => {
if dst.is_wide() || src.is_wide() {
if dst == Operand::SP && src == Operand::HL {
let hl = self.state.regs.hl();
self.idle();
self.state.regs.sp = hl;
} else {
let value = self.read16(src);
self.write16(dst, value);
}
} else {
let value = self.read8(src);
self.write8(dst, value);
}
}
Op::INC | Op::DEC => {
if dst.is_wide() {
let value = self.read16(dst);
self.idle();
let result = if op == Op::INC {
value.wrapping_add(1)
} else {
value.wrapping_sub(1)
};
self.write16(dst, result);
} else {
let value = self.read8(dst);
let result = if op == Op::INC {
let r = value.wrapping_add(1);
self.set_flag(flags::Z, r == 0);
self.set_flag(flags::N, false);
self.set_flag(flags::H, value & 0x0f == 0x0f);
r
} else {
let r = value.wrapping_sub(1);
self.set_flag(flags::Z, r == 0);
self.set_flag(flags::N, true);
self.set_flag(flags::H, value & 0x0f == 0);
r
};
self.write8(dst, result);
}
}
Op::ADD if dst == Operand::HL => {
let value = self.read16(src);
let hl = self.state.regs.hl();
let result = hl.wrapping_add(value);
self.set_flag(flags::N, false);
self.set_flag(flags::H, (hl & 0x0fff) + (value & 0x0fff) > 0x0fff);
self.set_flag(flags::C, u32::from(hl) + u32::from(value) > 0xffff);
self.idle();
self.state.regs.set_hl(result);
}
Op::ADD if dst == Operand::SP => {
let e = self.read8(src) as i8;
let sp = self.state.regs.sp;
let h = (sp & 0x0f) + (u16::from(e as u8) & 0x0f) > 0x0f;
let c = (sp & 0xff) + (u16::from(e as u8) & 0xff) > 0xff;
self.set_flags(false, false, h, c);
self.idle();
self.idle();
self.state.regs.sp = sp.wrapping_add(e as u16);
}
Op::ADD | Op::ADC | Op::SUB | Op::SBC | Op::AND | Op::XOR | Op::OR | Op::CP => {
let value = self.read8(src);
self.alu(op, value);
}
Op::RLCA | Op::RRCA | Op::RLA | Op::RRA => {
let a = self.state.regs.a;
let carry = self.flag(flags::C);
let (result, c) = match op {
Op::RLCA => (a.rotate_left(1), a & 0x80 != 0),
Op::RRCA => (a.rotate_right(1), a & 0x01 != 0),
Op::RLA => ((a << 1) | u8::from(carry), a & 0x80 != 0),
_ => ((a >> 1) | (u8::from(carry) << 7), a & 0x01 != 0),
};
self.set_flags(false, false, false, c);
self.state.regs.a = result;
}
Op::DAA => self.daa(),
Op::CPL => {
self.state.regs.a = !self.state.regs.a;
self.set_flag(flags::N, true);
self.set_flag(flags::H, true);
}
Op::SCF => {
self.set_flag(flags::N, false);
self.set_flag(flags::H, false);
self.set_flag(flags::C, true);
}
Op::CCF => {
let c = self.flag(flags::C);
self.set_flag(flags::N, false);
self.set_flag(flags::H, false);
self.set_flag(flags::C, !c);
}
Op::JR => {
let e = self.read8(Operand::Rel8) as i8;
if self.branch_taken(dst) {
self.idle();
self.state.regs.pc = self.state.regs.pc.wrapping_add(e as u16);
}
}
Op::JP => {
if src == Operand::HL {
self.state.regs.pc = self.state.regs.hl();
} else {
let target = self.fetch16();
if self.branch_taken(dst) {
self.idle();
self.state.regs.pc = target;
}
}
}
Op::CALL => {
let target = self.fetch16();
if self.branch_taken(dst) {
self.idle();
let ret = self.state.regs.pc;
self.push16(ret);
self.state.regs.pc = target;
}
}
Op::RET => {
if let Operand::Cond(c) = dst {
self.idle();
if !self.cond(c) {
return;
}
}
let target = self.pop16();
self.idle();
self.state.regs.pc = target;
}
Op::RETI => {
let target = self.pop16();
self.idle();
self.state.regs.pc = target;
self.state.ime = true;
self.state.ei_pending = false;
}
Op::RST => {
let Operand::Vector(v) = dst else {
unreachable!("decode fills in every RST vector");
};
self.idle();
let ret = self.state.regs.pc;
self.push16(ret);
self.state.regs.pc = u16::from(v);
}
Op::PUSH => {
let value = self.read16(dst);
self.idle();
self.push16(value);
}
Op::POP => {
let value = self.pop16();
self.write16(dst, value);
}
Op::DI => {
self.state.ime = false;
self.state.ei_pending = false;
}
Op::EI => self.state.ei_pending = true,
Op::HALT => {
if !self.state.ime && self.pending() != 0 {
self.state.halt_bug = true;
} else {
self.state.mode = Mode::Halted;
}
}
Op::STOP => {
let _ = self.read8(src);
self.state.mode = Mode::Stopped;
}
Op::LOCK => self.state.mode = Mode::Locked,
Op::PREFIX => unreachable!("the prefix is handled before dispatch"),
Op::RLC
| Op::RRC
| Op::RL
| Op::RR
| Op::SLA
| Op::SRA
| Op::SWAP
| Op::SRL
| Op::BIT
| Op::RES
| Op::SET => {
unreachable!("{op:?} is a $CB-page operation")
}
}
}
fn branch_taken(&self, dst: Operand) -> bool {
match dst {
Operand::Cond(c) => self.cond(c),
_ => true,
}
}
fn alu(&mut self, op: Op, value: u8) {
let a = self.state.regs.a;
let carry = u8::from(self.flag(flags::C));
match op {
Op::ADD | Op::ADC => {
let c = if op == Op::ADC { carry } else { 0 };
let sum = u16::from(a) + u16::from(value) + u16::from(c);
let result = sum as u8;
self.set_flags(
result == 0,
false,
(a & 0x0f) + (value & 0x0f) + c > 0x0f,
sum > 0xff,
);
self.state.regs.a = result;
}
Op::SUB | Op::SBC | Op::CP => {
let c = if op == Op::SBC { carry } else { 0 };
let diff = i16::from(a) - i16::from(value) - i16::from(c);
let result = diff as u8;
self.set_flags(
result == 0,
true,
i16::from(a & 0x0f) - i16::from(value & 0x0f) - i16::from(c) < 0,
diff < 0,
);
if op != Op::CP {
self.state.regs.a = result;
}
}
Op::AND => {
let result = a & value;
self.set_flags(result == 0, false, true, false);
self.state.regs.a = result;
}
Op::XOR => {
let result = a ^ value;
self.set_flags(result == 0, false, false, false);
self.state.regs.a = result;
}
Op::OR => {
let result = a | value;
self.set_flags(result == 0, false, false, false);
self.state.regs.a = result;
}
other => unreachable!("{other:?} is not an ALU operation"),
}
}
fn daa(&mut self) {
let n = self.flag(flags::N);
let h = self.flag(flags::H);
let mut carry = self.flag(flags::C);
let a = self.state.regs.a;
let mut adjust = 0u8;
if h || (!n && a & 0x0f > 0x09) {
adjust |= 0x06;
}
if carry || (!n && a > 0x99) {
adjust |= 0x60;
carry = true;
}
let result = if n {
a.wrapping_sub(adjust)
} else {
a.wrapping_add(adjust)
};
self.state.regs.a = result;
self.set_flag(flags::Z, result == 0);
self.set_flag(flags::H, false);
self.set_flag(flags::C, carry);
}
fn prefixed(&mut self, insn: Insn) {
let carry = self.flag(flags::C);
match insn.op {
Op::BIT => {
let Operand::Bit(bit) = insn.dst else {
unreachable!("BIT always carries its bit index");
};
let value = self.read8(insn.src);
self.set_flag(flags::Z, value & (1 << bit) == 0);
self.set_flag(flags::N, false);
self.set_flag(flags::H, true);
}
Op::RES | Op::SET => {
let Operand::Bit(bit) = insn.dst else {
unreachable!("RES/SET always carry their bit index");
};
let value = self.read8(insn.src);
let result = if insn.op == Op::SET {
value | (1 << bit)
} else {
value & !(1 << bit)
};
self.write8(insn.src, result);
}
op => {
let value = self.read8(insn.dst);
let (result, c) = match op {
Op::RLC => (value.rotate_left(1), value & 0x80 != 0),
Op::RRC => (value.rotate_right(1), value & 0x01 != 0),
Op::RL => ((value << 1) | u8::from(carry), value & 0x80 != 0),
Op::RR => ((value >> 1) | (u8::from(carry) << 7), value & 0x01 != 0),
Op::SLA => (value << 1, value & 0x80 != 0),
Op::SRA => ((value >> 1) | (value & 0x80), value & 0x01 != 0),
Op::SWAP => (value.rotate_left(4), false),
Op::SRL => (value >> 1, value & 0x01 != 0),
other => unreachable!("{other:?} is not a $CB shift"),
};
self.set_flags(result == 0, false, false, c);
self.write8(insn.dst, result);
}
}
}
}
impl Regs {
#[inline]
fn get8(&self, r: Reg8) -> u8 {
match r {
Reg8::B => self.b,
Reg8::C => self.c,
Reg8::D => self.d,
Reg8::E => self.e,
Reg8::H => self.h,
Reg8::L => self.l,
Reg8::A => self.a,
}
}
#[inline]
fn set8(&mut self, r: Reg8, value: u8) {
match r {
Reg8::B => self.b = value,
Reg8::C => self.c = value,
Reg8::D => self.d = value,
Reg8::E => self.e = value,
Reg8::H => self.h = value,
Reg8::L => self.l = value,
Reg8::A => self.a = value,
}
}
#[inline]
fn get16(&self, r: Reg16) -> u16 {
match r {
Reg16::Bc => (u16::from(self.b) << 8) | u16::from(self.c),
Reg16::De => (u16::from(self.d) << 8) | u16::from(self.e),
Reg16::Hl => self.hl(),
Reg16::Sp => self.sp,
Reg16::Af => (u16::from(self.a) << 8) | u16::from(self.f),
}
}
#[inline]
fn set16(&mut self, r: Reg16, value: u16) {
let (hi, lo) = ((value >> 8) as u8, value as u8);
match r {
Reg16::Bc => {
self.b = hi;
self.c = lo;
}
Reg16::De => {
self.d = hi;
self.e = lo;
}
Reg16::Hl => self.set_hl(value),
Reg16::Sp => self.sp = value,
Reg16::Af => {
self.a = hi;
self.f = lo & 0xf0;
}
}
}
}