mod access;
mod flagwrite;
mod latch;
mod registers;
mod reporting;
mod z80n;
use crate::alu::{self, AluOp};
use crate::consts::flag;
use crate::control::{destination, mask, step};
use crate::host::{BusCycle, BusRequest, Host};
use crate::mcode::{
self, AddressSource, Context, Decoded, IndexState, InstructionSet, SpecialLoad,
};
use crate::stepped::{self, Stepped};
use crate::types::{
ClockEdge, InterruptMode, MachineCycle, Registers, UndocumentedFlags, Z80nCommand,
};
const SURVIVES_ABANDON: u32 = 0b111 | (0b111 << 17);
const FETCH_AT: u32 = 2;
const MEMORY_AT: u32 = 3;
const PORT_AT: u32 = 4;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
enum Wide {
Bc,
De,
Hl,
Ix,
Iy,
}
fn high_byte(value: u16) -> u8 {
value.to_be_bytes()[0]
}
fn low_byte(value: u16) -> u8 {
value.to_be_bytes()[1]
}
fn wide_value(high: u8, low: u8) -> u16 {
u16::from_be_bytes([high, low])
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
enum Variant {
#[default]
Z80,
Z80n,
}
const MEMO_WAYS: usize = 64;
#[derive(Clone, Copy, Debug)]
struct Memo([(u64, Decoded); MEMO_WAYS]);
impl Default for Memo {
fn default() -> Self {
Self([(u64::MAX, Decoded::default()); MEMO_WAYS])
}
}
impl PartialEq for Memo {
fn eq(&self, _other: &Self) -> bool {
true
}
}
impl Eq for Memo {}
#[derive(Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(C)]
pub struct Cpu {
opened_cycle: Option<BusRequest>,
finished_cycle: Option<(BusRequest, u32)>,
cycle_t_states: u32,
cycle_waits: u32,
signals: u32,
registers: Registers,
machine_cycle: MachineCycle,
t_state: u8,
clock_edge: ClockEdge,
variant: Variant,
index_state: IndexState,
data_latch: u8,
ir: u8,
instruction_set: InstructionSet,
machine_cycles: u8,
resumed_cycle: u8,
tmp_addr: u16,
address: u16,
data_out: u8,
data_bus: u8,
wait_states: u32,
read_to_reg_r: u8,
alu_op_r: AluOp,
bus_a: u8,
bus_b: u8,
undocumented_flags: UndocumentedFlags,
z80n_operand: u16,
last_command: Option<Z80nCommand>,
#[cfg_attr(feature = "serde", serde(skip))]
stepped: Engine,
#[cfg_attr(feature = "serde", serde(skip))]
memo: Memo,
}
#[derive(Clone, Debug, Default)]
struct Engine(Stepped);
impl PartialEq for Engine {
fn eq(&self, _other: &Self) -> bool {
true
}
}
impl Eq for Engine {}
impl Cpu {
pub(super) const fn driven_by_edge(&self) -> bool {
self.signals & (1 << 20) != 0
}
pub(super) const fn set_driven_by_edge(&mut self) {
self.signals |= 1 << 20;
}
pub(super) const fn after_ei(&self) -> bool {
self.signals & (1 << 17) != 0
}
pub(super) const fn take_nmi(&self) -> bool {
self.signals & (1 << 18) != 0
}
pub(super) const fn take_interrupt(&self) -> bool {
self.signals & (1 << 19) != 0
}
pub(super) const fn set_latches_to(&mut self, (after_ei, nmi, interrupt): (bool, bool, bool)) {
self.signals &= !(0b111 << 17);
self.signals |= (after_ei as u32) << 17;
self.signals |= (nmi as u32) << 18;
self.signals |= (interrupt as u32) << 19;
}
pub(super) const fn halted(&self) -> bool {
self.signals & (1 << 0) != 0
}
pub(super) const fn set_halted_to(&mut self, on: bool) {
if on {
self.signals |= 1 << 0;
} else {
self.signals &= !(1 << 0);
}
}
pub(super) const fn interrupt_requested(&self) -> bool {
self.signals & (1 << 1) != 0
}
pub(super) const fn set_interrupt_requested_to(&mut self, on: bool) {
if on {
self.signals |= 1 << 1;
} else {
self.signals &= !(1 << 1);
}
}
pub(super) const fn nmi_requested(&self) -> bool {
self.signals & (1 << 2) != 0
}
pub(super) const fn set_nmi_requested_to(&mut self, on: bool) {
if on {
self.signals |= 1 << 2;
} else {
self.signals &= !(1 << 2);
}
}
pub(super) const fn index_displaced(&self) -> bool {
self.signals & (1 << 3) != 0
}
pub(super) const fn set_index_displaced_to(&mut self, on: bool) {
if on {
self.signals |= 1 << 3;
} else {
self.signals &= !(1 << 3);
}
}
pub(super) const fn arith16_r(&self) -> bool {
self.signals & (1 << 4) != 0
}
pub(super) const fn set_arith16_r_to(&mut self, on: bool) {
if on {
self.signals |= 1 << 4;
} else {
self.signals &= !(1 << 4);
}
}
pub(super) const fn combine_zero_r(&self) -> bool {
self.signals & (1 << 5) != 0
}
pub(super) const fn set_combine_zero_r_to(&mut self, on: bool) {
if on {
self.signals |= 1 << 5;
} else {
self.signals &= !(1 << 5);
}
}
pub(super) const fn save_alu_r(&self) -> bool {
self.signals & (1 << 6) != 0
}
pub(super) const fn set_save_alu_r_to(&mut self, on: bool) {
if on {
self.signals |= 1 << 6;
} else {
self.signals &= !(1 << 6);
}
}
pub(super) const fn preserve_c_r(&self) -> bool {
self.signals & (1 << 7) != 0
}
pub(super) const fn set_preserve_c_r_to(&mut self, on: bool) {
if on {
self.signals |= 1 << 7;
} else {
self.signals &= !(1 << 7);
}
}
pub(super) const fn interrupt_acknowledge(&self) -> bool {
self.signals & (1 << 8) != 0
}
pub(super) const fn set_interrupt_acknowledge_to(&mut self, on: bool) {
if on {
self.signals |= 1 << 8;
} else {
self.signals &= !(1 << 8);
}
}
pub(super) const fn nmi_acknowledge(&self) -> bool {
self.signals & (1 << 9) != 0
}
pub(super) const fn set_nmi_acknowledge_to(&mut self, on: bool) {
if on {
self.signals |= 1 << 9;
} else {
self.signals &= !(1 << 9);
}
}
pub(super) const fn end_block(&self) -> bool {
self.signals & (1 << 10) != 0
}
pub(super) const fn set_end_block_to(&mut self, on: bool) {
if on {
self.signals |= 1 << 10;
} else {
self.signals &= !(1 << 10);
}
}
pub(super) const fn repeat_block(&self) -> bool {
self.signals & (1 << 11) != 0
}
pub(super) const fn set_repeat_block_to(&mut self, on: bool) {
if on {
self.signals |= 1 << 11;
} else {
self.signals &= !(1 << 11);
}
}
pub(super) const fn auto_wait_t1(&self) -> bool {
self.signals & (1 << 12) != 0
}
pub(super) const fn set_auto_wait_t1_to(&mut self, on: bool) {
if on {
self.signals |= 1 << 12;
} else {
self.signals &= !(1 << 12);
}
}
pub(super) const fn auto_wait_t2(&self) -> bool {
self.signals & (1 << 13) != 0
}
pub(super) const fn set_auto_wait_t2_to(&mut self, on: bool) {
if on {
self.signals |= 1 << 13;
} else {
self.signals &= !(1 << 13);
}
}
pub(super) const fn counted_to_zero(&self) -> bool {
self.signals & (1 << 14) != 0
}
pub(super) const fn set_counted_to_zero_to(&mut self, on: bool) {
if on {
self.signals |= 1 << 14;
} else {
self.signals &= !(1 << 14);
}
}
pub(super) const fn nibble_rotate_held(&self) -> bool {
self.signals & (1 << 15) != 0
}
pub(super) const fn set_nibble_rotate_held_to(&mut self, on: bool) {
if on {
self.signals |= 1 << 15;
} else {
self.signals &= !(1 << 15);
}
}
#[cfg(test)]
pub(super) const fn fetch_settled(&self) -> bool {
self.signals & (1 << 16) != 0
}
pub(super) const fn set_fetch_settled_to(&mut self, on: bool) {
if on {
self.signals |= 1 << 16;
} else {
self.signals &= !(1 << 16);
}
}
}
impl Default for Cpu {
fn default() -> Self {
Self::new()
}
}
impl Cpu {
#[must_use]
pub fn new() -> Self {
Self {
memo: Memo::default(),
opened_cycle: None,
finished_cycle: None,
cycle_t_states: 0,
cycle_waits: 0,
signals: 0,
registers: Registers::default(),
machine_cycle: MachineCycle::M1,
t_state: 1,
clock_edge: ClockEdge::Falling,
variant: Variant::Z80,
index_state: IndexState::None,
data_latch: 0,
ir: 0,
instruction_set: InstructionSet::Base,
machine_cycles: 1,
resumed_cycle: 1,
tmp_addr: 0,
address: 0,
data_out: 0,
data_bus: 0,
wait_states: 0,
read_to_reg_r: 0,
alu_op_r: AluOp::Add,
bus_a: 0,
bus_b: 0,
undocumented_flags: UndocumentedFlags::Combined,
z80n_operand: 0,
last_command: None,
stepped: Engine::default(),
}
}
pub fn reset(&mut self) {
self.memo = Memo::default();
self.opened_cycle = None;
self.finished_cycle = None;
self.cycle_t_states = 0;
self.cycle_waits = 0;
self.signals = 0;
self.registers.pc = 0;
self.registers.sp = 0xFFFF;
self.registers.af = 0xFFFF;
self.registers.af_alt = 0xFFFF;
self.registers.i = 0;
self.registers.r = 0;
self.registers.iff1 = false;
self.registers.iff2 = false;
self.registers.interrupt_mode = InterruptMode::Mode0;
self.machine_cycle = MachineCycle::M1;
self.t_state = 1;
self.clock_edge = ClockEdge::Falling;
self.index_state = IndexState::None;
self.data_latch = 0;
self.ir = 0;
self.instruction_set = InstructionSet::Base;
self.machine_cycles = 1;
self.resumed_cycle = 1;
self.tmp_addr = 0;
self.address = 0;
self.data_out = 0;
self.data_bus = 0;
self.wait_states = 0;
self.read_to_reg_r = 0;
self.alu_op_r = AluOp::Add;
self.bus_a = 0;
self.bus_b = 0;
self.z80n_operand = 0;
self.last_command = None;
}
pub fn abandon_instruction(&mut self) {
self.signals &= SURVIVES_ABANDON;
self.opened_cycle = None;
self.finished_cycle = None;
self.cycle_t_states = 0;
self.cycle_waits = 0;
self.machine_cycle = MachineCycle::M1;
self.t_state = 1;
self.clock_edge = ClockEdge::Falling;
self.index_state = IndexState::None;
self.data_latch = 0;
self.ir = 0;
self.instruction_set = InstructionSet::Base;
self.machine_cycles = 1;
self.resumed_cycle = 1;
self.tmp_addr = 0;
self.address = self.registers.pc;
self.data_out = 0;
self.data_bus = 0;
self.wait_states = 0;
self.read_to_reg_r = 0;
self.alu_op_r = AluOp::Add;
self.bus_a = 0;
self.bus_b = 0;
self.z80n_operand = 0;
self.last_command = None;
}
pub fn tick<H: Host>(&mut self, host: &mut H) {
self.set_driven_by_edge();
match self.clock_edge {
ClockEdge::Falling => {
self.falling_edge(host);
self.clock_edge = ClockEdge::Rising;
}
ClockEdge::Rising => {
self.rising_edge(host);
self.clock_edge = ClockEdge::Falling;
}
}
}
fn decode_now(&mut self) -> Decoded {
let key = self.decode_key();
let slot = usize::from(Self::memo_slot(key)) & (MEMO_WAYS - 1);
let (asked, decoded) = self.memo.0[slot];
if asked == key {
return decoded;
}
let decoded = mcode::decode(self.context());
self.memo.0[slot] = (key, decoded);
decoded
}
fn memo_slot(key: u64) -> u8 {
(key ^ (key >> 29)).to_le_bytes()[0]
}
fn decode_key(&self) -> u64 {
u64::from(self.ir)
| u64::from(self.flags()) << 8
| u64::from(self.accumulator()) << 16
| u64::from(self.data_latch) << 24
| (self.machine_cycle as u64) << 32
| (self.instruction_set as u64) << 35
| (self.index_state as u64) << 37
| u64::from(self.nmi_acknowledge()) << 39
| u64::from(self.interrupt_acknowledge()) << 40
| u64::from(matches!(self.variant, Variant::Z80n)) << 41
}
fn context(&self) -> Context {
Context {
ir: self.ir,
instruction_set: self.instruction_set,
machine_cycle: self.machine_cycle,
flags: self.flags(),
nmi_cycle: self.nmi_acknowledge(),
interrupt_cycle: self.interrupt_acknowledge(),
index_state: self.index_state,
accumulator: self.accumulator(),
data: self.data_latch,
z80n_enabled: matches!(self.variant, Variant::Z80n),
}
}
fn next_is_index_fetch(&self, decoded: &Decoded) -> bool {
!matches!(self.index_state, IndexState::None)
&& !self.index_displaced()
&& (matches!(decoded.set_addr_to, AddressSource::IndexOrHl)
|| (matches!(self.machine_cycle, MachineCycle::M1)
&& (self.ir == 0xCB || self.ir == 0x36)))
}
fn bus_request(&self, decoded: &Decoded) -> BusRequest {
if matches!(self.machine_cycle, MachineCycle::M1) {
let acknowledging = self.interrupt_acknowledge();
return match self.t_state {
1 | 2 if acknowledging => BusRequest::InterruptAcknowledge {
address: self.address,
},
1 | 2 => BusRequest::OpcodeFetch {
address: self.address,
},
3 | 4 => BusRequest::Refresh {
address: self.address,
},
_ => BusRequest::Internal {
address: self.address,
},
};
}
if decoded.no_read() && !decoded.write() {
return BusRequest::Internal {
address: self.address,
};
}
match (decoded.write(), decoded.iorq()) {
(true, true) => BusRequest::PortWrite {
port: self.address,
value: self.data_out,
},
(true, false) => BusRequest::MemoryWrite {
address: self.address,
value: self.data_out,
},
(false, true) => BusRequest::PortRead { port: self.address },
(false, false) => BusRequest::MemoryRead {
address: self.address,
},
}
}
fn transfer_at(&self, base: u32) -> u32 {
base + self.cycle_waits
}
fn falling_edge<H: Host>(&mut self, host: &mut H) {
if matches!(self.machine_cycle, MachineCycle::M1) && self.t_state == 1 {
self.address = self.registers.pc;
}
let decoded = self.decode_now();
let request = self.bus_request(&decoded);
self.cycle_t_states += 1;
host.bus_edge(&request);
self.report_intercepted(&decoded, host);
match self.t_state {
1 if self.opened_cycle.is_none() => {
if let Some((request, t_states)) = self.finished_cycle.take() {
host.bus_cycle(&BusCycle { request, t_states });
}
self.opened_cycle = Some(request);
self.wait_states = host.wait_states(&request);
self.cycle_waits = self.wait_states;
if self.interrupt_acknowledge() && matches!(self.machine_cycle, MachineCycle::M1) {
self.data_bus = host.interrupt_vector();
} else if !decoded.no_read() && !decoded.write() && !decoded.iorq() {
self.data_bus = if matches!(request, BusRequest::OpcodeFetch { .. }) {
host.fetch(self.address, self.transfer_at(FETCH_AT))
} else {
host.read(self.address, self.transfer_at(MEMORY_AT))
};
}
}
2 => {
if decoded.iorq() && !decoded.write() && self.wait_states == 0 {
self.data_bus = host.input(self.address, self.transfer_at(PORT_AT));
}
}
3 => {
self.data_latch = self.data_bus;
if decoded.write() {
if decoded.iorq() {
host.output(self.address, self.data_out, self.transfer_at(PORT_AT));
} else {
host.write(self.address, self.data_out, self.transfer_at(MEMORY_AT));
}
}
}
_ => {}
}
}
fn address_register(&self, decoded: &Decoded) -> Wide {
if matches!(self.machine_cycle, MachineCycle::IndexDisplacement) {
return self.index_register();
}
if decoded.jump_xy() || decoded.ld_sp_hl() {
return if matches!(self.index_state, IndexState::None) {
Wide::Hl
} else {
self.index_register()
};
}
let field = match decoded.set_addr_to {
AddressSource::Bc | AddressSource::Port => 0b00,
AddressSource::De | AddressSource::Sp => 0b01,
AddressSource::IndexOrHl | AddressSource::Latch => 0b10,
AddressSource::None => 0b11,
};
self.addressed_pair(field, false)
}
fn drive_address(&mut self, decoded: &Decoded) {
let from_register = self.wide(self.address_register(decoded));
if decoded.jump() {
self.registers.pc = wide_value(self.data_latch, low_byte(self.tmp_addr));
self.address = self.registers.pc;
} else if decoded.jump_xy() {
self.registers.pc = from_register;
self.address = from_register;
} else if decoded.call() || decoded.rst_p() {
self.registers.pc = self.tmp_addr;
self.address = self.tmp_addr;
} else if self.machine_cycle.number() == self.machine_cycles && self.nmi_acknowledge() {
self.registers.pc = 0x0066;
self.address = 0x0066;
} else if matches!(self.machine_cycle, MachineCycle::M3)
&& self.interrupt_acknowledge()
&& matches!(self.registers.interrupt_mode, InterruptMode::Mode2)
{
self.registers.pc = wide_value(self.registers.i, low_byte(self.tmp_addr));
self.address = self.registers.pc;
} else {
match decoded.set_addr_to {
AddressSource::IndexOrHl => {
self.address = if matches!(self.index_state, IndexState::None) {
from_register
} else if self.next_is_index_fetch(decoded) {
self.registers.pc
} else {
self.tmp_addr
};
}
AddressSource::Port => {
self.address = wide_value(self.accumulator(), self.data_latch);
}
AddressSource::Sp => self.address = self.registers.sp,
AddressSource::Bc | AddressSource::De => self.address = from_register,
AddressSource::Latch => {
self.address = if decoded.inc_wz() {
self.tmp_addr.wrapping_add(1)
} else {
wide_value(self.data_latch, low_byte(self.tmp_addr))
};
}
AddressSource::None => {
let holds_port_address = self.repeating_port_block();
if !holds_port_address
&& (!decoded.no_pc()
|| self.end_block()
|| (decoded.i_djnz() && self.counted_to_zero()))
{
self.address = self.registers.pc;
}
}
}
}
}
pub fn step<H: Host>(&mut self, host: &mut H) -> u32 {
debug_assert!(
!self.driven_by_edge(),
"step() after tick() or run_cycle() without abandon_instruction(): the sequencer is \
holding an instruction that cannot be handed over"
);
self.hand_to_engine();
let taken = match self.stepped.0.step(host) {
stepped::Ran::Yes(taken) => taken,
stepped::Ran::NotYet => 0,
};
self.take_back_from_engine();
taken
}
#[cfg(test)]
pub(crate) fn step_by_edges<H: Host>(&mut self, host: &mut H) -> u32 {
if self.at_fetch_start() {
self.tick(host);
self.tick(host);
}
let mut t_states = 0;
if matches!(self.clock_edge, ClockEdge::Rising) {
t_states += 1;
self.tick(host);
if self.at_settled_boundary() && !self.mid_prefix() {
return t_states;
}
}
loop {
self.falling_edge(host);
self.rising_edge(host);
t_states += 1;
if self.fetch_settled() && !self.mid_prefix() {
return t_states;
}
}
}
fn hand_to_engine(&mut self) {
let z80n = matches!(self.variant, Variant::Z80n);
let latches = (self.after_ei(), self.take_nmi(), self.take_interrupt());
let (halted, interrupt, nmi) = (
self.halted(),
self.interrupt_requested(),
self.nmi_requested(),
);
let (registers, flags) = (self.registers, self.undocumented_flags);
let (operand, pending, address) = (self.z80n_operand, self.finished_cycle, self.address);
let engine = &mut self.stepped.0;
engine.registers = registers;
engine.undocumented_flags = flags;
engine.set_z80n_enabled(z80n);
engine.set_halted(halted);
engine.set_interrupt_requested(interrupt);
engine.set_nmi_requested(nmi);
engine.set_latches(latches);
engine.z80n_operand = operand;
engine.pending = pending;
engine.bus_address = address;
}
fn take_back_from_engine(&mut self) {
let engine = &self.stepped.0;
let registers = engine.registers;
let (halted, interrupt, nmi) = (
engine.is_halted(),
engine.is_interrupt_requested(),
engine.is_nmi_requested(),
);
let latches = engine.latches();
let (operand, pending, address) = (engine.z80n_operand, engine.pending, engine.bus_address);
self.registers = registers;
self.abandon_instruction();
self.set_halted_to(halted);
self.set_interrupt_requested_to(interrupt);
self.set_nmi_requested_to(nmi);
self.set_latches_to(latches);
self.z80n_operand = operand;
self.finished_cycle = pending;
self.address = address;
}
pub fn run_cycle<H: Host>(&mut self, host: &mut H) -> u32 {
let mut t_states = 0;
if matches!(self.clock_edge, ClockEdge::Rising) {
t_states += 1;
self.tick(host);
if self.cycle_t_states == 0 {
return t_states;
}
}
loop {
self.falling_edge(host);
self.rising_edge(host);
t_states += 1;
if self.cycle_t_states == 0 {
return t_states;
}
}
}
#[cfg(test)]
fn at_fetch_start(&self) -> bool {
matches!(self.clock_edge, ClockEdge::Falling)
&& matches!(self.machine_cycle, MachineCycle::M1)
&& self.t_state == 1
}
#[cfg(test)]
fn at_settled_boundary(&self) -> bool {
matches!(self.clock_edge, ClockEdge::Falling) && self.fetch_settled()
}
#[cfg(test)]
fn mid_prefix(&self) -> bool {
matches!(self.instruction_set, InstructionSet::Base)
&& matches!(self.ir, 0xCB | 0xED | 0xDD | 0xFD)
}
fn interrupt_register_transfer(&mut self, decoded: &Decoded) {
let reported = match decoded.special_ld {
SpecialLoad::AccumulatorFromVector => self.registers.i,
SpecialLoad::AccumulatorFromRefresh => self.registers.r,
SpecialLoad::VectorFromAccumulator => {
self.registers.i = self.accumulator();
return;
}
SpecialLoad::RefreshFromAccumulator => {
self.registers.r = self.accumulator();
return;
}
SpecialLoad::None => return,
};
self.set_accumulator(reported);
let flags = Self::report_byte(self.flags(), reported, self.registers.iff2);
self.set_flags(Self::take_undocumented(flags, reported));
}
#[allow(clippy::too_many_lines)]
fn rising_edge<H: Host>(&mut self, host: &mut H) {
let decoded = self.decode_now();
let t_res = self.t_state == decoded.t_states;
let waiting = self.t_state == 2 && self.wait_states > 0;
let released = !waiting;
let consumed = self.save_alu_r()
|| matches!(self.alu_op_r, AluOp::Bit)
|| decoded.xy_bit_undoc()
|| (self.t_state == 1 && (decoded.i_bt() || decoded.i_bc()));
let (alu_result, alu_flags) = if consumed {
alu::execute(alu::Inputs {
op: self.alu_op_r,
ir: self.ir & mask::ALU_OPCODE,
instruction_set: self.instruction_set,
bus_a: self.bus_a,
bus_b: self.bus_b,
flags: self.flags(),
preserve_result_flags: self.arith16_r(),
combine_zero: self.combine_zero_r(),
})
} else {
(0, 0)
};
let save_mux = if decoded.exchange_rp() {
self.bus_b
} else if self.save_alu_r() {
alu_result
} else {
self.data_latch
};
let was_save_alu = self.save_alu_r();
let was_alu_op = self.alu_op_r;
let was_read_to_reg = self.read_to_reg_r;
let was_preserve_c = self.preserve_c_r();
let was_auto_wait_t1 = self.auto_wait_t1();
let was_auto_wait_t2 = self.auto_wait_t2();
let was_nibble_held = self.nibble_rotate_held();
let prefix_opcode = self.ir;
self.alu_op_r = AluOp::Add;
self.set_save_alu_r_to(false);
self.read_to_reg_r = 0;
self.machine_cycles = decoded.machine_cycles;
if let Some(mode) = decoded.interrupt_mode {
self.registers.interrupt_mode = mode;
}
self.set_arith16_r_to(decoded.arith16());
self.set_preserve_c_r_to(decoded.preserve_c());
self.set_combine_zero_r_to(
matches!(self.instruction_set, InstructionSet::Ed)
&& matches!(decoded.alu_op, AluOp::Adc | AluOp::Sbc)
&& matches!(self.machine_cycle, MachineCycle::M3),
);
if matches!(self.machine_cycle, MachineCycle::M1) && self.t_state < 4 {
if self.t_state == 2 && released {
self.registers.r = (self.registers.r & mask::REFRESH_HELD)
| (self.registers.r.wrapping_add(1) & mask::REFRESH_COUNTED);
self.address = wide_value(self.registers.i, self.registers.r);
if !decoded.jump()
&& !decoded.call()
&& !self.nmi_acknowledge()
&& !self.interrupt_acknowledge()
&& !self.halted()
&& !decoded.halt()
{
self.registers.pc = self.registers.pc.wrapping_add(1);
}
let vectored = matches!(self.registers.interrupt_mode, InterruptMode::Mode2);
let restarting = matches!(self.registers.interrupt_mode, InterruptMode::Mode1);
self.ir = if self.interrupt_acknowledge() && restarting {
0xFF
} else if self.halted()
|| (self.interrupt_acknowledge() && vectored)
|| self.nmi_acknowledge()
{
0x00
} else {
self.data_bus
};
if self.interrupt_acknowledge() && vectored {
self.tmp_addr = wide_value(high_byte(self.tmp_addr), self.data_bus);
}
self.instruction_set = InstructionSet::Base;
match decoded.prefix {
InstructionSet::Base => {
self.index_state = IndexState::None;
self.set_index_displaced_to(false);
}
InstructionSet::DdFd => {
self.index_state = if prefix_opcode & 0x20 == 0 {
IndexState::Ix
} else {
IndexState::Iy
};
}
other => {
if matches!(other, InstructionSet::Ed) {
self.index_state = IndexState::None;
self.set_index_displaced_to(false);
}
self.instruction_set = other;
}
}
}
} else {
if matches!(self.machine_cycle, MachineCycle::IndexDisplacement) {
self.set_index_displaced_to(true);
if matches!(decoded.prefix, InstructionSet::Cb) {
self.instruction_set = InstructionSet::Cb;
}
}
if t_res {
self.set_repeat_block_to(
(decoded.i_bt() || decoded.i_bc() || decoded.i_btr()) && !self.end_block(),
);
self.drive_address(&decoded);
self.set_save_alu_r_to(decoded.save_alu());
self.alu_op_r = decoded.alu_op;
self.accumulator_flag_instructions(&decoded);
}
if self.t_state == 2 && released {
if matches!(self.instruction_set, InstructionSet::Cb)
&& matches!(self.machine_cycle, MachineCycle::IndexAddition)
{
self.ir = self.data_bus;
}
if decoded.jump_e() {
let displacement = i16::from(self.data_latch.cast_signed());
self.registers.pc = self.registers.pc.wrapping_add_signed(displacement);
} else if decoded.inc_pc() {
self.registers.pc = self.registers.pc.wrapping_add(1);
}
if self.repeat_block() {
self.registers.pc = self.registers.pc.wrapping_sub(2);
}
if decoded.rst_p() {
self.tmp_addr = u16::from(self.ir & mask::RESTART_TARGET);
}
}
if self.t_state == 3 && matches!(self.machine_cycle, MachineCycle::IndexDisplacement) {
let base = self.wide(self.address_register(&decoded));
let displacement = i16::from(self.data_latch.cast_signed());
self.tmp_addr = base.wrapping_add_signed(displacement);
}
if ((self.t_state == 2 && released)
|| (self.t_state == 4 && matches!(self.machine_cycle, MachineCycle::M1)))
&& step::is_stack_pointer(decoded.inc_dec_16)
{
self.registers.sp = if decoded.inc_dec_16 & 0b1000 == 0 {
self.registers.sp.wrapping_add(1)
} else {
self.registers.sp.wrapping_sub(1)
};
}
if decoded.ld_sp_hl() {
self.registers.sp = self.wide(self.address_register(&decoded));
}
if decoded.exchange_af() {
core::mem::swap(&mut self.registers.af, &mut self.registers.af_alt);
}
if decoded.exchange_rs() {
self.exchange_register_set();
}
}
if self.t_state == 3 {
if decoded.ldz() {
self.tmp_addr = wide_value(high_byte(self.tmp_addr), self.data_latch);
}
if decoded.ldw() {
self.tmp_addr = wide_value(self.data_latch, low_byte(self.tmp_addr));
}
self.interrupt_register_transfer(&decoded);
}
if t_res {
self.update_latch(&decoded, self.machine_cycle.number());
}
if (!decoded.i_djnz() && was_save_alu) || matches!(was_alu_op, AluOp::Bit) {
let mut flags = if was_preserve_c {
(alu_flags & !flag::C_MASK) | (self.flags() & flag::C_MASK)
} else {
alu_flags
};
if matches!(was_alu_op, AluOp::Bit) && self.bit_reaches_memory() {
flags = Self::take_undocumented(flags, high_byte(self.registers.wz));
}
self.set_flags(flags);
}
if t_res && decoded.i_inrc() {
let value = self.data_latch;
let parity = value.count_ones().is_multiple_of(2);
let flags = Self::report_byte(self.flags(), value, parity);
self.set_flags(Self::take_undocumented(flags, value));
}
if t_res && decoded.i_btr() && self.counts_down_the_port_block() {
self.report_port_block(self.data_latch);
}
self.run_extended(&decoded);
if t_res {
self.export_extended(&decoded, host);
if decoded.i_retn()
&& matches!(self.instruction_set, InstructionSet::Ed)
&& self.ir == 0x4D
{
host.return_from_interrupt();
}
}
if self.t_state == 1 && !was_auto_wait_t1 {
self.set_nibble_rotate_held_to(decoded.i_rld() || decoded.i_rrd());
if !was_nibble_held {
self.data_out = self.bus_b;
}
if decoded.i_rld() {
self.data_out = ((self.bus_b & 0x0F) << 4) | (self.bus_a & 0x0F);
}
if decoded.i_rrd() {
self.data_out = ((self.bus_a & 0x0F) << 4) | (self.bus_b >> 4);
}
}
if t_res {
self.read_to_reg_r = if decoded.read_to_reg() {
destination::ENABLED | (decoded.set_bus_a_to & destination::TARGET)
} else {
decoded.set_bus_a_to & destination::TARGET
};
if decoded.read_to_acc() {
self.read_to_reg_r = destination::ACCUMULATOR;
}
}
if self.t_state == 1 && decoded.i_bt() {
let mut flags = self.flags() & !(flag::XY_MASK | flag::H_MASK | flag::N_MASK);
if alu_result & 0x08 != 0 {
flags |= flag::X_MASK;
}
if alu_result & 0x02 != 0 {
flags |= flag::Y_MASK;
}
self.set_flags(flags);
}
if self.t_state == 1 && decoded.i_bc() {
let borrowed = alu_flags & flag::H_MASK != 0;
let difference = alu_result.wrapping_sub(u8::from(borrowed));
let mut flags = self.flags() & !flag::XY_MASK;
if difference & 0x08 != 0 {
flags |= flag::X_MASK;
}
if difference & 0x02 != 0 {
flags |= flag::Y_MASK;
}
self.set_flags(flags);
}
if decoded.i_bc() || decoded.i_bt() {
let mut flags = self.flags() & !flag::P_MASK;
if self.counted_to_zero() {
flags |= flag::P_MASK;
}
self.set_flags(flags);
}
if (self.t_state == 1 && !was_save_alu && !was_auto_wait_t1)
|| (was_save_alu && !matches!(was_alu_op, AluOp::Cp))
{
self.write_back(was_read_to_reg, save_mux);
if decoded.xy_bit_undoc() {
self.data_out = alu_result;
}
}
if step::is_register_pair(decoded.inc_dec_16)
&& ((self.t_state == 2 && released && !matches!(self.machine_cycle, MachineCycle::M1))
|| (self.t_state == 3 && matches!(self.machine_cycle, MachineCycle::M1)))
{
self.step_pair(decoded.inc_dec_16);
}
if decoded.i_djnz() && was_save_alu {
self.set_counted_to_zero_to(alu_flags & flag::Z_MASK != 0);
}
if (self.t_state == 2
|| (self.t_state == 3 && matches!(self.machine_cycle, MachineCycle::M1)))
&& step::enabled(decoded.inc_dec_16)
&& step::pair(decoded.inc_dec_16) == 0
{
self.set_counted_to_zero_to(self.wide(Wide::Bc) != 0);
}
if decoded.exchange_dh() && self.t_state == 4 {
core::mem::swap(&mut self.registers.de, &mut self.registers.hl);
}
if decoded.exchange_wh() && self.t_state == 4 {
let register = if matches!(self.index_state, IndexState::None) {
Wide::Hl
} else {
self.index_register()
};
self.set_wide(register, self.tmp_addr);
}
let (bus_a, bus_b) = self.operands(
decoded.set_bus_a_to,
decoded.set_bus_b_to,
decoded.xy_bit_undoc(),
);
self.bus_a = bus_a;
self.bus_b = bus_b;
self.advance(&decoded, t_res, waiting, was_auto_wait_t1, was_auto_wait_t2);
}
#[allow(clippy::fn_params_excessive_bools)]
fn advance(
&mut self,
decoded: &Decoded,
t_res: bool,
waiting: bool,
was_auto_wait_t1: bool,
was_auto_wait_t2: bool,
) {
let acknowledging =
self.interrupt_acknowledge() && matches!(self.machine_cycle, MachineCycle::M1);
self.set_auto_wait_t2_to(self.auto_wait_t1());
if t_res {
self.set_auto_wait_t1_to(false);
self.set_auto_wait_t2_to(false);
} else {
self.set_auto_wait_t1_to(acknowledging || decoded.iorq());
}
let repeats = self.ir & 0x10 != 0;
let flags = self.flags();
self.set_end_block_to(
(decoded.i_bt() && (!repeats || flags & flag::P_MASK == 0))
|| (decoded.i_bc()
&& (!repeats || flags & flag::Z_MASK != 0 || flags & flag::P_MASK == 0))
|| (decoded.i_btr() && (!repeats || flags & flag::Z_MASK != 0)),
);
if self.t_state == 2 && decoded.i_retn() {
self.registers.iff1 = self.registers.iff2;
}
if self.t_state == 3 {
if decoded.set_ei() {
self.registers.iff1 = true;
self.registers.iff2 = true;
}
if decoded.set_di() {
self.registers.iff1 = false;
self.registers.iff2 = false;
}
}
if self.interrupt_acknowledge() || self.nmi_acknowledge() {
self.set_halted_to(false);
}
self.set_fetch_settled_to(false);
if waiting {
self.wait_states -= 1;
return;
}
if !t_res {
let acknowledge_wait = acknowledging && !was_auto_wait_t2;
let port_wait = decoded.iorq() && !was_auto_wait_t1;
if !(acknowledge_wait || port_wait) {
self.t_state += 1;
self.set_fetch_settled_to(
self.t_state == 2 && matches!(self.machine_cycle, MachineCycle::M1),
);
}
return;
}
if decoded.halt() {
self.set_halted_to(true);
}
self.t_state = 1;
if let Some(request) = self.opened_cycle.take() {
self.finished_cycle = Some((request, self.cycle_t_states));
}
self.cycle_t_states = 0;
let cycle = self.machine_cycle.number();
if self.next_is_index_fetch(decoded) {
self.resumed_cycle = if self.ir == 0x36 { 2 } else { cycle };
self.machine_cycle = MachineCycle::IndexDisplacement;
} else if cycle == self.machine_cycles
|| self.end_block()
|| (cycle == 2 && decoded.i_djnz() && self.counted_to_zero())
|| (cycle == 7 && self.machine_cycles == 1 && self.resumed_cycle == 1)
{
self.machine_cycle = MachineCycle::M1;
self.set_interrupt_acknowledge_to(false);
self.set_nmi_acknowledge_to(false);
let unprefixed = matches!(decoded.prefix, InstructionSet::Base);
if self.nmi_requested() && unprefixed {
self.set_nmi_acknowledge_to(true);
self.set_nmi_requested_to(false);
self.registers.iff1 = false;
} else if self.registers.iff1
&& self.interrupt_requested()
&& unprefixed
&& !decoded.set_ei()
{
self.set_interrupt_acknowledge_to(true);
self.registers.iff1 = false;
self.registers.iff2 = false;
}
} else if cycle == 7 {
self.machine_cycle = MachineCycle::from_number(self.resumed_cycle + 1);
} else {
self.machine_cycle = MachineCycle::from_number(cycle + 1);
}
}
}
#[cfg(test)]
#[path = "../core_tests/mod.rs"]
mod tests;