use crate::alu::{self, AluOp};
use crate::consts::flag;
use crate::control::mask;
use crate::extended::{self, Barrel};
use crate::flags;
use crate::host::{BusCycle, BusRequest, Host};
use crate::mcode::InstructionSet;
use crate::types::{InterruptMode, Registers, UndocumentedFlags, Z80nCommand};
const FETCH_AT: u32 = 2;
const MEMORY_AT: u32 = 3;
const PORT_AT: u32 = 4;
#[cfg(test)]
#[derive(Clone, Copy, Default, Debug)]
pub(crate) struct Seen {
pub base: [u64; 4],
pub bits: [u64; 4],
pub extended: [u64; 4],
pub z80n: [u64; 4],
pub indexed: [u64; 4],
pub indexed_bits: [u64; 4],
}
#[cfg(test)]
fn mark(table: &mut [u64; 4], opcode: u8) {
table[usize::from(opcode >> 6)] |= 1 << (opcode & 0x3F);
}
#[cfg(test)]
pub(crate) fn was_seen(table: &[u64; 4], opcode: u8) -> bool {
table[usize::from(opcode >> 6)] & (1 << (opcode & 0x3F)) != 0
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub(crate) enum Index {
Ix,
Iy,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub(crate) enum Ran {
Yes(u32),
NotYet,
}
#[derive(Clone, Debug)]
pub(crate) struct Stepped {
pub registers: Registers,
pub undocumented_flags: UndocumentedFlags,
pub(crate) pending: Option<(BusRequest, u32)>,
pub(crate) bus_address: u16,
index: Option<Index>,
pub(crate) z80n_operand: u16,
#[cfg(test)]
pub seen: Seen,
signals: u8,
}
mod signal {
pub const HALTED: u8 = 1 << 0;
pub const DISPLACED: u8 = 1 << 1;
pub const INTERRUPT_REQUESTED: u8 = 1 << 2;
pub const NMI_REQUESTED: u8 = 1 << 3;
pub const AFTER_EI: u8 = 1 << 4;
pub const TAKE_NMI: u8 = 1 << 5;
pub const TAKE_INTERRUPT: u8 = 1 << 6;
pub const Z80N: u8 = 1 << 7;
}
impl Default for Stepped {
fn default() -> Self {
Self::new()
}
}
impl Stepped {
pub(crate) fn new() -> Self {
Self {
registers: Registers::default(),
undocumented_flags: UndocumentedFlags::Combined,
pending: None,
bus_address: 0,
index: None,
z80n_operand: 0,
signals: 0,
#[cfg(test)]
seen: Seen::default(),
}
}
const fn on(&self, signal: u8) -> bool {
self.signals & signal != 0
}
const fn set(&mut self, signal: u8, on: bool) {
if on {
self.signals |= signal;
} else {
self.signals &= !signal;
}
}
pub(crate) const fn set_interrupt_requested(&mut self, requested: bool) {
self.set(signal::INTERRUPT_REQUESTED, requested);
}
pub(crate) const fn is_halted(&self) -> bool {
self.on(signal::HALTED)
}
pub(crate) const fn set_halted(&mut self, halted: bool) {
self.set(signal::HALTED, halted);
}
pub(crate) const fn set_z80n_enabled(&mut self, enabled: bool) {
self.set(signal::Z80N, enabled);
}
pub(crate) const fn latches(&self) -> (bool, bool, bool) {
(
self.on(signal::AFTER_EI),
self.on(signal::TAKE_NMI),
self.on(signal::TAKE_INTERRUPT),
)
}
pub(crate) const fn set_latches(&mut self, (after_ei, nmi, interrupt): (bool, bool, bool)) {
self.set(signal::AFTER_EI, after_ei);
self.set(signal::TAKE_NMI, nmi);
self.set(signal::TAKE_INTERRUPT, interrupt);
}
pub(crate) const fn is_interrupt_requested(&self) -> bool {
self.on(signal::INTERRUPT_REQUESTED)
}
pub(crate) const fn is_nmi_requested(&self) -> bool {
self.on(signal::NMI_REQUESTED)
}
pub(crate) const fn set_nmi_requested(&mut self, requested: bool) {
self.set(signal::NMI_REQUESTED, requested);
}
fn accumulator(&self) -> u8 {
self.registers.af.to_be_bytes()[0]
}
fn set_accumulator(&mut self, value: u8) {
self.registers.af = u16::from_be_bytes([value, self.flags()]);
}
fn flags(&self) -> u8 {
self.registers.af.to_be_bytes()[1]
}
fn set_flags(&mut self, value: u8) {
self.registers.af = u16::from_be_bytes([self.accumulator(), value]);
}
fn alu(&mut self, op: AluOp, ir: u8, left: u8, right: u8, preserve_carry: bool) -> u8 {
self.alu_in(
alu::Inputs {
op,
ir,
instruction_set: InstructionSet::Base,
bus_a: left,
bus_b: right,
flags: 0,
preserve_result_flags: false,
combine_zero: false,
},
preserve_carry,
)
}
fn alu_holding(
&mut self,
op: AluOp,
ir: u8,
left: u8,
right: u8,
preserve_carry: bool,
holding: bool,
) -> u8 {
self.alu_in(
alu::Inputs {
op,
ir,
instruction_set: InstructionSet::Base,
bus_a: left,
bus_b: right,
flags: 0,
preserve_result_flags: holding,
combine_zero: false,
},
preserve_carry,
)
}
fn alu_in(&mut self, inputs: alu::Inputs, preserve_carry: bool) -> u8 {
let (result, produced) = alu::execute(alu::Inputs {
flags: self.flags(),
ir: inputs.ir & mask::ALU_OPCODE,
..inputs
});
let flags = if preserve_carry {
(produced & !flag::C_MASK) | (self.flags() & flag::C_MASK)
} else {
produced
};
self.set_flags(flags);
result
}
fn register(&self, code: u8) -> u8 {
if code == 7 {
return self.accumulator();
}
let [high, low] = match code {
0 | 1 => self.registers.bc,
2 | 3 => self.registers.de,
_ => self.pair_for_hl(),
}
.to_be_bytes();
if code & 1 == 0 { high } else { low }
}
fn pair_for_hl(&self) -> u16 {
match self.index {
Some(Index::Ix) if !self.on(signal::DISPLACED) => self.registers.ix,
Some(Index::Iy) if !self.on(signal::DISPLACED) => self.registers.iy,
_ => self.registers.hl,
}
}
fn set_pair_for_hl(&mut self, value: u16) {
*match self.index {
Some(Index::Ix) if !self.on(signal::DISPLACED) => &mut self.registers.ix,
Some(Index::Iy) if !self.on(signal::DISPLACED) => &mut self.registers.iy,
_ => &mut self.registers.hl,
} = value;
}
fn displaced<H: Host>(&mut self, host: &mut H) -> (u16, u32) {
let base = self.pair_for_hl();
let (displacement, read) = self.immediate(host);
self.set(signal::DISPLACED, true);
let address = base.wrapping_add_signed(i16::from(displacement.cast_signed()));
self.registers.wz = address;
(address, read)
}
fn memory_operand<H: Host>(&mut self, host: &mut H, taken: &mut u32) -> u16 {
if self.index.is_none() {
return self.registers.hl;
}
let (address, read) = self.displaced(host);
*taken += read + self.internal(host, 5);
address
}
fn set_register(&mut self, code: u8, value: u8) {
if code == 7 {
self.set_accumulator(value);
return;
}
if code == 4 || code == 5 {
let [high, low] = self.pair_for_hl().to_be_bytes();
let pair = if code == 4 {
u16::from_be_bytes([value, low])
} else {
u16::from_be_bytes([high, value])
};
self.set_pair_for_hl(pair);
return;
}
let pair = match code {
0 | 1 => &mut self.registers.bc,
_ => &mut self.registers.de,
};
let [high, low] = pair.to_be_bytes();
*pair = if code & 1 == 0 {
u16::from_be_bytes([value, low])
} else {
u16::from_be_bytes([high, value])
};
}
fn open<H: Host>(&mut self, host: &mut H, request: BusRequest) -> u32 {
if let Some((request, t_states)) = self.pending.take() {
host.bus_cycle(&BusCycle { request, t_states });
}
host.wait_states(&request)
}
fn close(&mut self, request: BusRequest, t_states: u32) {
self.pending = Some((request, t_states));
}
fn fetch<H: Host>(&mut self, host: &mut H) -> (u8, u32) {
let address = self.registers.pc;
let request = BusRequest::OpcodeFetch { address };
let waits = self.open(host, request);
let opcode = host.fetch(address, FETCH_AT + waits);
self.registers.pc = self.registers.pc.wrapping_add(1);
self.refresh();
let t_states = 4 + waits;
self.close(request, t_states);
(opcode, t_states)
}
fn read<H: Host>(&mut self, host: &mut H, address: u16) -> (u8, u32) {
self.read_taking(host, address, 3)
}
fn read_taking<H: Host>(&mut self, host: &mut H, address: u16, base: u32) -> (u8, u32) {
let request = BusRequest::MemoryRead { address };
let waits = self.open(host, request);
let value = host.read(address, MEMORY_AT + waits);
self.bus_address = address;
let t_states = base + waits;
self.close(request, t_states);
(value, t_states)
}
fn write<H: Host>(&mut self, host: &mut H, address: u16, value: u8) -> u32 {
let request = BusRequest::MemoryWrite { address, value };
let waits = self.open(host, request);
host.write(address, value, MEMORY_AT + waits);
self.bus_address = address;
let t_states = 3 + waits;
self.close(request, t_states);
t_states
}
fn internal<H: Host>(&mut self, host: &mut H, t_states: u32) -> u32 {
let request = BusRequest::Internal {
address: self.bus_address,
};
let waits = self.open(host, request);
let took = t_states + waits;
self.close(request, took);
took
}
fn lengthen_fetch(&mut self, extra: u32) {
if let Some((request, t_states)) = self.pending.take() {
self.pending = Some((request, t_states + extra));
}
}
fn immediate<H: Host>(&mut self, host: &mut H) -> (u8, u32) {
let address = self.registers.pc;
let (value, t_states) = self.read(host, address);
self.registers.pc = self.registers.pc.wrapping_add(1);
(value, t_states)
}
fn pair(&self, code: u8) -> u16 {
match code {
0 => self.registers.bc,
1 => self.registers.de,
2 => self.pair_for_hl(),
_ => self.registers.sp,
}
}
fn set_pair(&mut self, code: u8, value: u16) {
if code == 2 {
self.set_pair_for_hl(value);
return;
}
*match code {
0 => &mut self.registers.bc,
1 => &mut self.registers.de,
_ => &mut self.registers.sp,
} = value;
}
fn stacked(&self, code: u8) -> u16 {
if code == 3 {
self.registers.af
} else {
self.pair(code)
}
}
fn set_stacked(&mut self, code: u8, value: u16) {
if code == 3 {
self.registers.af = value;
} else {
self.set_pair(code, value);
}
}
fn push<H: Host>(&mut self, host: &mut H, value: u16) -> u32 {
let [high, low] = value.to_be_bytes();
self.registers.sp = self.registers.sp.wrapping_sub(1);
let first = self.write(host, self.registers.sp, high);
self.registers.sp = self.registers.sp.wrapping_sub(1);
let second = self.write(host, self.registers.sp, low);
first + second
}
fn pop<H: Host>(&mut self, host: &mut H) -> (u16, u32) {
let (low, first) = self.read(host, self.registers.sp);
self.registers.sp = self.registers.sp.wrapping_add(1);
let (high, second) = self.read(host, self.registers.sp);
self.registers.sp = self.registers.sp.wrapping_add(1);
(u16::from_be_bytes([high, low]), first + second)
}
fn write_taking<H: Host>(&mut self, host: &mut H, address: u16, value: u8, base: u32) -> u32 {
let request = BusRequest::MemoryWrite { address, value };
let waits = self.open(host, request);
host.write(address, value, MEMORY_AT + waits);
self.bus_address = address;
let t_states = base + waits;
self.close(request, t_states);
t_states
}
fn port_read<H: Host>(&mut self, host: &mut H, port: u16) -> (u8, u32) {
self.port_read_taking(host, port, 4)
}
fn port_read_taking<H: Host>(&mut self, host: &mut H, port: u16, base: u32) -> (u8, u32) {
let request = BusRequest::PortRead { port };
let waits = self.open(host, request);
let value = host.input(port, PORT_AT + waits);
self.bus_address = port;
let t_states = base + waits;
self.close(request, t_states);
(value, t_states)
}
fn port_write<H: Host>(&mut self, host: &mut H, port: u16, value: u8) -> u32 {
let request = BusRequest::PortWrite { port, value };
let waits = self.open(host, request);
host.output(port, value, PORT_AT + waits);
self.bus_address = port;
let t_states = 4 + waits;
self.close(request, t_states);
t_states
}
fn immediate_taking<H: Host>(&mut self, host: &mut H, base: u32) -> (u8, u32) {
let address = self.registers.pc;
let (value, t_states) = self.read_taking(host, address, base);
self.registers.pc = self.registers.pc.wrapping_add(1);
(value, t_states)
}
fn immediate_word<H: Host>(&mut self, host: &mut H) -> (u16, u32) {
let (low, first) = self.immediate(host);
let (high, second) = self.immediate(host);
(u16::from_be_bytes([high, low]), first + second)
}
fn flush<H: Host>(&mut self, host: &mut H) {
if let Some((request, t_states)) = self.pending.take() {
host.bus_cycle(&BusCycle { request, t_states });
}
}
pub(crate) fn step<H: Host>(&mut self, host: &mut H) -> Ran {
if self.on(signal::TAKE_NMI) {
self.set(signal::TAKE_NMI, false);
let taken = self.accept_nmi(host);
self.flush(host);
return Ran::Yes(taken);
}
if self.on(signal::TAKE_INTERRUPT) {
self.set(signal::TAKE_INTERRUPT, false);
let taken = self.accept_interrupt(host);
self.flush(host);
return Ran::Yes(taken);
}
if self.on(signal::HALTED) {
let taken = self.halted_fetch(host);
self.sample();
self.flush(host);
return Ran::Yes(taken);
}
let (opcode, mut taken) = self.fetch(host);
if self.execute(host, opcode, &mut taken) {
self.sample();
self.flush(host);
Ran::Yes(taken)
} else {
Ran::NotYet
}
}
fn sample(&mut self) {
if self.on(signal::NMI_REQUESTED) {
self.set(signal::NMI_REQUESTED, false);
self.registers.iff1 = false;
self.set(signal::TAKE_NMI, true);
} else if self.registers.iff1
&& self.on(signal::INTERRUPT_REQUESTED)
&& !self.on(signal::AFTER_EI)
{
self.registers.iff1 = false;
self.registers.iff2 = false;
self.set(signal::TAKE_INTERRUPT, true);
}
self.set(signal::AFTER_EI, false);
}
fn halted_fetch<H: Host>(&mut self, host: &mut H) -> u32 {
let address = self.registers.pc;
let request = BusRequest::OpcodeFetch { address };
let waits = self.open(host, request);
host.fetch(address, FETCH_AT + waits);
self.refresh();
let t_states = 4 + waits;
self.close(request, t_states);
t_states
}
fn refresh(&mut self) {
self.registers.r = (self.registers.r & 0x80) | (self.registers.r.wrapping_add(1) & 0x7F);
self.bus_address = u16::from_be_bytes([self.registers.i, self.registers.r]);
}
fn accept_nmi<H: Host>(&mut self, host: &mut H) -> u32 {
self.set(signal::HALTED, false);
let address = self.registers.pc;
let request = BusRequest::OpcodeFetch { address };
let waits = self.open(host, request);
host.fetch(address, FETCH_AT + waits);
self.refresh();
let mut taken = 5 + waits;
self.close(request, taken);
let announced = self.on(signal::Z80N);
let [high, low] = address.to_be_bytes();
if announced {
self.report_z80n(host, Z80nCommand::NmiAcknowledgeHigh);
}
self.registers.sp = self.registers.sp.wrapping_sub(1);
taken += self.write(host, self.registers.sp, high);
if announced {
self.report_z80n(host, Z80nCommand::NmiAcknowledgeLow);
}
self.registers.sp = self.registers.sp.wrapping_sub(1);
taken += self.write(host, self.registers.sp, low);
self.registers.pc = 0x0066;
taken
}
fn acknowledge<H: Host>(&mut self, host: &mut H) -> (u8, u32) {
let address = self.registers.pc;
let request = BusRequest::InterruptAcknowledge { address };
let waits = self.open(host, request);
let vector = host.interrupt_vector();
self.refresh();
let t_states = 6 + waits;
self.close(request, t_states);
(vector, t_states)
}
fn accept_interrupt<H: Host>(&mut self, host: &mut H) -> u32 {
self.set(signal::HALTED, false);
let (vector, mut taken) = self.acknowledge(host);
match self.registers.interrupt_mode {
InterruptMode::Mode0 => {
self.execute(host, vector, &mut taken);
}
InterruptMode::Mode1 => {
self.execute(host, 0xFF, &mut taken);
}
InterruptMode::Mode2 => {
self.lengthen_fetch(1);
taken += 1 + self.push(host, self.registers.pc);
let table = u16::from_be_bytes([self.registers.i, vector]);
let (low, first) = self.read(host, table);
let (high, second) = self.read(host, table.wrapping_add(1));
taken += first + second;
self.registers.pc = u16::from_be_bytes([high, low]);
self.registers.wz = self.registers.pc;
}
}
taken
}
#[allow(
clippy::too_many_lines,
reason = "a dispatch table; splitting it once to halve it measured 0.8% slower and would \
still be over the threshold"
)]
fn execute<H: Host>(&mut self, host: &mut H, opcode: u8, taken: &mut u32) -> bool {
#[cfg(test)]
if self.index.is_some() {
mark(&mut self.seen.indexed, opcode);
} else {
mark(&mut self.seen.base, opcode);
}
match opcode {
0x76 => {
self.set(signal::HALTED, true);
true
}
0xF3 => {
self.registers.iff1 = false;
self.registers.iff2 = false;
true
}
0xFB => {
self.registers.iff1 = true;
self.registers.iff2 = true;
self.set(signal::AFTER_EI, true);
true
}
0x40..=0x7F if opcode & 0x07 != 6 && (opcode >> 3) & 0x07 != 6 => {
let value = self.register(opcode & 0x07);
self.set_register((opcode >> 3) & 0x07, value);
true
}
0x46 | 0x4E | 0x56 | 0x5E | 0x66 | 0x6E | 0x7E => {
let address = self.memory_operand(host, taken);
let (value, t) = self.read(host, address);
*taken += t;
self.set_register((opcode >> 3) & 0x07, value);
true
}
0x70..=0x75 | 0x77 => {
let address = self.memory_operand(host, taken);
let value = self.register(opcode & 0x07);
*taken += self.write(host, address, value);
true
}
0x06 | 0x0E | 0x16 | 0x1E | 0x26 | 0x2E | 0x3E => {
let (value, t) = self.immediate(host);
*taken += t;
self.set_register((opcode >> 3) & 0x07, value);
true
}
0x36 => {
if self.index.is_some() {
let (address, first) = self.displaced(host);
let (value, second) = self.immediate_taking(host, 5);
let third = self.write(host, address, value);
*taken += first + second + third;
} else {
let (value, first) = self.immediate(host);
let second = self.write(host, self.registers.hl, value);
*taken += first + second;
}
true
}
0x0A | 0x1A => {
let address = if opcode == 0x0A {
self.registers.bc
} else {
self.registers.de
};
let (value, t) = self.read(host, address);
*taken += t;
self.set_accumulator(value);
self.registers.wz = address.wrapping_add(1);
true
}
0x02 | 0x12 => {
let address = if opcode == 0x02 {
self.registers.bc
} else {
self.registers.de
};
let value = self.accumulator();
*taken += self.write(host, address, value);
self.registers.wz =
u16::from_be_bytes([value, address.wrapping_add(1).to_be_bytes()[1]]);
true
}
0x3A => {
let (address, first) = self.immediate_word(host);
let (value, second) = self.read(host, address);
*taken += first + second;
self.set_accumulator(value);
self.registers.wz = address.wrapping_add(1);
true
}
0x32 => {
let (address, first) = self.immediate_word(host);
let value = self.accumulator();
let second = self.write(host, address, value);
*taken += first + second;
self.registers.wz =
u16::from_be_bytes([value, address.wrapping_add(1).to_be_bytes()[1]]);
true
}
0x80..=0xBF if opcode & 0x07 != 6 => {
let right = self.register(opcode & 0x07);
self.arithmetic(opcode, right);
true
}
0x86 | 0x8E | 0x96 | 0x9E | 0xA6 | 0xAE | 0xB6 | 0xBE => {
let address = self.memory_operand(host, taken);
let (right, t) = self.read(host, address);
*taken += t;
self.arithmetic(opcode, right);
true
}
0xC6 | 0xCE | 0xD6 | 0xDE | 0xE6 | 0xEE | 0xF6 | 0xFE => {
let (right, t) = self.immediate(host);
*taken += t;
self.arithmetic(opcode, right);
true
}
0x04 | 0x0C | 0x14 | 0x1C | 0x24 | 0x2C | 0x3C | 0x05 | 0x0D | 0x15 | 0x1D | 0x25
| 0x2D | 0x3D => {
let code = (opcode >> 3) & 0x07;
let op = if opcode & 1 == 0 {
AluOp::Add
} else {
AluOp::Sub
};
let value = self.alu(op, opcode, self.register(code), 1, true);
self.set_register(code, value);
true
}
0x34 | 0x35 => {
let address = self.memory_operand(host, taken);
let (value, first) = self.read_taking(host, address, 4);
let op = if opcode == 0x34 {
AluOp::Add
} else {
AluOp::Sub
};
let result = self.alu(op, opcode, value, 1, true);
let second = self.write(host, address, result);
*taken += first + second;
true
}
0x01 | 0x11 | 0x21 | 0x31 => {
let (value, t) = self.immediate_word(host);
*taken += t;
self.set_pair((opcode >> 4) & 0x03, value);
true
}
0x03 | 0x13 | 0x23 | 0x33 | 0x0B | 0x1B | 0x2B | 0x3B => {
let code = (opcode >> 4) & 0x03;
let value = self.pair(code);
self.set_pair(
code,
if opcode & 0x08 == 0 {
value.wrapping_add(1)
} else {
value.wrapping_sub(1)
},
);
*taken += 2;
self.lengthen_fetch(2);
true
}
0xF9 => {
self.registers.sp = self.pair_for_hl();
*taken += 2;
self.lengthen_fetch(2);
true
}
0xC5 | 0xD5 | 0xE5 | 0xF5 => {
let value = self.stacked((opcode >> 4) & 0x03);
*taken += 1;
self.lengthen_fetch(1);
*taken += self.push(host, value);
true
}
0xC1 | 0xD1 | 0xE1 | 0xF1 => {
let (value, t) = self.pop(host);
*taken += t;
self.set_stacked((opcode >> 4) & 0x03, value);
true
}
0x2A => {
let (address, first) = self.immediate_word(host);
let (low, second) = self.read(host, address);
self.registers.wz = address.wrapping_add(1);
let (high, third) = self.read(host, self.registers.wz);
*taken += first + second + third;
self.set_pair_for_hl(u16::from_be_bytes([high, low]));
true
}
0x22 => {
let (address, first) = self.immediate_word(host);
let [high, low] = self.pair_for_hl().to_be_bytes();
let second = self.write(host, address, low);
self.registers.wz = address.wrapping_add(1);
let third = self.write(host, self.registers.wz, high);
*taken += first + second + third;
true
}
0xE3 => {
let sp = self.registers.sp;
let (low, first) = self.read(host, sp);
let (high, second) = self.read_taking(host, sp.wrapping_add(1), 4);
let [held_high, held_low] = self.pair_for_hl().to_be_bytes();
let third = self.write(host, sp.wrapping_add(1), held_high);
let fourth = self.write_taking(host, sp, held_low, 5);
*taken += first + second + third + fourth;
let swapped = u16::from_be_bytes([high, low]);
self.set_pair_for_hl(swapped);
self.registers.wz = swapped;
true
}
0xEB => {
core::mem::swap(&mut self.registers.de, &mut self.registers.hl);
true
}
0x08 => {
core::mem::swap(&mut self.registers.af, &mut self.registers.af_alt);
true
}
0xD9 => {
core::mem::swap(&mut self.registers.bc, &mut self.registers.bc_alt);
core::mem::swap(&mut self.registers.de, &mut self.registers.de_alt);
core::mem::swap(&mut self.registers.hl, &mut self.registers.hl_alt);
true
}
0x09 | 0x19 | 0x29 | 0x39 => {
let [high, low] = self.pair((opcode >> 4) & 0x03).to_be_bytes();
let [left_high, left_low] = self.pair_for_hl().to_be_bytes();
let result_low = self.alu_holding(AluOp::Add, opcode, left_low, low, false, true);
let result_high =
self.alu_holding(AluOp::Adc, opcode, left_high, high, false, true);
self.registers.wz = self.pair_for_hl().wrapping_add(1);
self.set_pair_for_hl(u16::from_be_bytes([result_high, result_low]));
*taken += self.internal(host, 4) + self.internal(host, 3);
true
}
0x07 | 0x0F | 0x17 | 0x1F => {
let value = self.accumulator();
let result = self.alu(AluOp::Rotate, opcode, value, value, false);
self.set_accumulator(result);
true
}
0xDD | 0xFD => {
self.index = Some(if opcode == 0xDD { Index::Ix } else { Index::Iy });
self.set(signal::DISPLACED, false);
let (next, t) = self.fetch(host);
*taken += t;
let ran = if next == 0xCB {
self.indexed_bit(host, taken);
true
} else {
self.execute(host, next, taken)
};
self.index = None;
self.set(signal::DISPLACED, false);
ran
}
0xDB => {
let (low, first) = self.immediate(host);
let port = u16::from_be_bytes([self.accumulator(), low]);
let (value, second) = self.port_read(host, port);
*taken += first + second;
self.set_accumulator(value);
self.registers.wz = port.wrapping_add(1);
true
}
0xD3 => {
let (low, first) = self.immediate(host);
let accumulator = self.accumulator();
let port = u16::from_be_bytes([accumulator, low]);
*taken += first + self.port_write(host, port, accumulator);
self.registers.wz = u16::from_be_bytes([accumulator, low.wrapping_add(1)]);
true
}
0xCB => {
let (prefixed, t) = self.fetch(host);
*taken += t;
self.prefixed(host, prefixed, taken);
true
}
0x27 => {
let value = self.accumulator();
let result = self.alu(AluOp::Daa, opcode, value, value, false);
self.set_accumulator(result);
true
}
0x2F | 0x37 | 0x3F => {
self.accumulator_flags(opcode);
true
}
0xC3 | 0xC2 | 0xCA | 0xD2 | 0xDA | 0xE2 | 0xEA | 0xF2 | 0xFA => {
let (target, t) = self.immediate_word(host);
*taken += t;
self.registers.wz = target;
if opcode == 0xC3 || self.condition((opcode >> 3) & 0x07) {
self.registers.pc = target;
}
true
}
0xE9 => {
self.registers.pc = self.pair_for_hl();
true
}
0x18 | 0x20 | 0x28 | 0x30 | 0x38 => {
let (displacement, t) = self.immediate(host);
*taken += t;
let jump = opcode == 0x18 || self.condition((opcode >> 3) & mask::CONDITION);
if jump {
*taken += self.jump_relative(host, displacement);
}
true
}
0x10 => {
*taken += 1;
self.lengthen_fetch(1);
let counter = self.register(0).wrapping_sub(1);
self.set_register(0, counter);
let (displacement, t) = self.immediate(host);
*taken += t;
if counter != 0 {
*taken += self.jump_relative(host, displacement);
}
true
}
0xCD | 0xC4 | 0xCC | 0xD4 | 0xDC | 0xE4 | 0xEC | 0xF4 | 0xFC => {
let (low, first) = self.immediate(host);
let calling = opcode == 0xCD || self.condition((opcode >> 3) & 0x07);
let (high, second) = self.immediate_taking(host, if calling { 4 } else { 3 });
*taken += first + second;
let target = u16::from_be_bytes([high, low]);
self.registers.wz = target;
if calling {
*taken += self.push(host, self.registers.pc);
self.registers.pc = target;
}
true
}
0xC9 => {
let (target, t) = self.pop(host);
*taken += t;
self.registers.wz = target;
self.registers.pc = target;
true
}
0xC0 | 0xC8 | 0xD0 | 0xD8 | 0xE0 | 0xE8 | 0xF0 | 0xF8 => {
*taken += 1;
self.lengthen_fetch(1);
if self.condition((opcode >> 3) & 0x07) {
let (target, t) = self.pop(host);
*taken += t;
self.registers.wz = target;
self.registers.pc = target;
}
true
}
0xC7 | 0xCF | 0xD7 | 0xDF | 0xE7 | 0xEF | 0xF7 | 0xFF => {
*taken += 1;
self.lengthen_fetch(1);
*taken += self.push(host, self.registers.pc);
let target = u16::from(opcode & mask::RESTART_TARGET);
self.registers.wz = target;
self.registers.pc = target;
true
}
0xED => {
let (next, t) = self.fetch(host);
*taken += t;
self.extended(host, next, taken)
}
0x00 => true,
_ => false,
}
}
}
impl Stepped {
fn extended<H: Host>(&mut self, host: &mut H, opcode: u8, taken: &mut u32) -> bool {
#[cfg(test)]
mark(&mut self.seen.extended, opcode);
if self.on(signal::Z80N) && self.z80n(host, opcode, taken) {
#[cfg(test)]
mark(&mut self.seen.z80n, opcode);
return true;
}
match opcode {
0xA0 | 0xA8 | 0xB0 | 0xB8 => {
self.block_transfer(host, opcode, taken);
true
}
0xA1 | 0xA9 | 0xB1 | 0xB9 => {
self.block_compare(host, opcode, taken);
true
}
0xA2 | 0xAA | 0xB2 | 0xBA | 0xA3 | 0xAB | 0xB3 | 0xBB => {
self.port_block(host, opcode, taken);
true
}
_ => self.extended_regular(host, opcode, taken),
}
}
fn extended_regular<H: Host>(&mut self, host: &mut H, opcode: u8, taken: &mut u32) -> bool {
match opcode {
0x57 | 0x5F | 0x47 | 0x4F => {
self.interrupt_register(opcode, taken);
true
}
0x4B | 0x5B | 0x6B | 0x7B => {
self.wide_load(host, opcode, taken);
true
}
0x43 | 0x53 | 0x63 | 0x73 => {
self.wide_store(host, opcode, taken);
true
}
0x46 | 0x4E | 0x66 | 0x6E => {
self.registers.interrupt_mode = InterruptMode::Mode0;
true
}
0x56 | 0x76 => {
self.registers.interrupt_mode = InterruptMode::Mode1;
true
}
0x5E | 0x7E => {
self.registers.interrupt_mode = InterruptMode::Mode2;
true
}
0x4A | 0x5A | 0x6A | 0x7A | 0x42 | 0x52 | 0x62 | 0x72 => {
self.wide_arithmetic(host, opcode, taken);
true
}
0x44 | 0x4C | 0x54 | 0x5C | 0x64 | 0x6C | 0x74 | 0x7C => {
let value = self.accumulator();
let result = self.alu(AluOp::Sub, opcode, 0, value, false);
self.set_accumulator(result);
true
}
0x6F | 0x67 => {
self.nibble_rotate(host, opcode, taken);
true
}
0x4D | 0x45 | 0x55 | 0x5D | 0x65 | 0x6D | 0x75 | 0x7D => {
let announced = self.on(signal::Z80N) && opcode != 0x4D;
if announced {
self.report_z80n(host, Z80nCommand::ReturnFromNmiLow);
}
let (low, first) = self.read(host, self.registers.sp);
self.registers.sp = self.registers.sp.wrapping_add(1);
if announced {
self.report_z80n(host, Z80nCommand::ReturnFromNmiHigh);
}
let (high, second) = self.read(host, self.registers.sp);
self.registers.sp = self.registers.sp.wrapping_add(1);
let target = u16::from_be_bytes([high, low]);
*taken += first + second;
self.registers.wz = target;
self.registers.pc = target;
self.registers.iff1 = self.registers.iff2;
if opcode == 0x4D {
host.return_from_interrupt();
}
true
}
0x40 | 0x48 | 0x50 | 0x58 | 0x60 | 0x68 | 0x70 | 0x78 => {
self.port_in(host, opcode, taken);
true
}
0x41 | 0x49 | 0x51 | 0x59 | 0x61 | 0x69 | 0x71 | 0x79 => {
self.port_out(host, opcode, taken);
true
}
_ => true,
}
}
fn report_z80n<H: Host>(&mut self, host: &mut H, command: Z80nCommand) {
host.z80n_command(command, self.z80n_operand);
}
fn z80n<H: Host>(&mut self, host: &mut H, opcode: u8, taken: &mut u32) -> bool {
match opcode {
0x23 => {
let swapped = extended::swap_nibbles(self.accumulator());
self.set_accumulator(swapped);
self.report_z80n(host, Z80nCommand::SwapNibbleA);
}
0x24 => {
let mirrored = extended::mirror(self.accumulator());
self.set_accumulator(mirrored);
self.report_z80n(host, Z80nCommand::MirrorA);
}
0x30 => {
self.registers.de = extended::multiply(self.registers.de);
self.report_z80n(host, Z80nCommand::MulDe);
}
0x31..=0x33 => {
let (command, code) = match opcode {
0x31 => (Z80nCommand::AddHlA, 2),
0x32 => (Z80nCommand::AddDeA, 1),
_ => (Z80nCommand::AddBcA, 0),
};
let sum = extended::add_accumulator(self.pair(code), self.accumulator());
self.set_pair(code, sum);
self.set_flags(self.flags() & !flag::C_MASK);
self.report_z80n(host, command);
}
0x93 => {
self.registers.hl = extended::pixel_down(self.registers.hl);
self.report_z80n(host, Z80nCommand::PixelDown);
}
0x94 => {
self.registers.hl = extended::pixel_address(self.registers.de);
self.report_z80n(host, Z80nCommand::PixelAddress);
}
0x95 => {
let selected = extended::bit_from_low_three(self.registers.de.to_be_bytes()[1]);
self.set_accumulator(selected);
self.report_z80n(host, Z80nCommand::SetAE);
}
0x28..=0x2C => {
let (command, kind) = match opcode {
0x28 => (Z80nCommand::BslaDeB, Barrel::Left),
0x29 => (Z80nCommand::BsraDeB, Barrel::ArithmeticRight),
0x2A => (Z80nCommand::BsrlDeB, Barrel::LogicalRight),
0x2B => (Z80nCommand::BsrfDeB, Barrel::FillRight),
_ => (Z80nCommand::BrlcDeB, Barrel::Rotate),
};
let count = self.registers.bc.to_be_bytes()[0];
self.registers.de = extended::barrel(self.registers.de, count, kind);
self.report_z80n(host, command);
}
_ => return self.z80n_operands(host, opcode, taken),
}
true
}
fn write_extended_register<H: Host>(&mut self, host: &mut H, opcode: u8, taken: &mut u32) {
let (register, first) = if opcode == 0x91 {
self.immediate(host)
} else {
self.read(host, self.registers.pc)
};
let (value, second) = if opcode == 0x91 {
self.read(host, self.registers.pc)
} else {
(self.accumulator(), 0)
};
self.z80n_operand = u16::from_be_bytes([register, value]);
let (_, third) = self.read(host, self.registers.pc);
self.report_z80n(host, Z80nCommand::NextRegWrite);
let (_, fourth) = self.immediate(host);
*taken += first + second + third + fourth;
}
fn z80n_operands<H: Host>(&mut self, host: &mut H, opcode: u8, taken: &mut u32) -> bool {
match opcode {
0x27 => {
let (value, t) = self.immediate(host);
*taken += t;
let left = self.accumulator();
self.alu(AluOp::And, opcode, left, value, false);
}
0x34..=0x36 => {
let (command, code) = match opcode {
0x34 => (Z80nCommand::AddHlImmediate, 2),
0x35 => (Z80nCommand::AddDeImmediate, 1),
_ => (Z80nCommand::AddBcImmediate, 0),
};
self.report_z80n(host, command);
let (low, first) = self.immediate_taking(host, 4);
let (high, second) = self.immediate_taking(host, 4);
*taken += first + second;
let sum = extended::add_immediate(self.pair(code), u16::from_be_bytes([high, low]));
self.set_pair(code, sum);
}
0x8A => {
let (high, first) = self.immediate(host);
self.registers.sp = self.registers.sp.wrapping_sub(1);
let second = self.write(host, self.registers.sp, high);
let (low, third) = self.read(host, self.registers.pc);
self.registers.sp = self.registers.sp.wrapping_sub(1);
let fourth = self.write(host, self.registers.sp, low);
let (_, fifth) = self.immediate(host);
*taken += first + second + third + fourth + fifth;
}
0x90 => {
self.lengthen_fetch(1);
let (value, first) = self.read(host, self.registers.hl);
self.registers.hl = self.registers.hl.wrapping_add(1);
*taken += 1 + first + self.port_write(host, self.registers.bc, value);
}
0x91 | 0x92 => self.write_extended_register(host, opcode, taken),
0x98 => {
self.report_z80n(host, Z80nCommand::JpC);
let (value, t) = self.port_read_taking(host, self.registers.bc, 5);
*taken += t;
let target = (u16::from(value) << mask::JUMP_SHIFT) & mask::JUMP_TARGET;
self.registers.pc = (self.registers.pc & mask::JUMP_REGION) | target;
}
0xA4 | 0xB4 | 0xAC | 0xBC => {
let up = opcode & 0x08 == 0;
let (value, first) = self.read(host, self.registers.hl);
*taken += first;
let step = if up { 1 } else { 1u16.wrapping_neg() };
self.registers.hl = self.registers.hl.wrapping_add(step);
self.copy_out(host, value, false, taken);
self.repeat_block(host, opcode, taken, !up);
}
0xA5 => {
let (value, first) = self.read(host, self.registers.hl);
let second = self.write(host, self.registers.de, value);
*taken += first + second;
let [high, low] = self.registers.hl.to_be_bytes();
let stepped = self.alu(AluOp::Add, opcode, low, 1, false);
self.registers.hl = u16::from_be_bytes([high, stepped]);
let [high, low] = self.registers.de.to_be_bytes();
let stepped = self.alu(AluOp::Add, opcode, high, 1, false);
self.registers.de = u16::from_be_bytes([stepped, low]);
}
0xB6 => {
self.report_z80n(host, Z80nCommand::LdirScale);
let (value, first) = self.read(host, self.registers.hl);
*taken += first;
self.registers.hl = self.registers.hl.wrapping_add(1);
self.copy_out(host, value, false, taken);
self.repeat_block(host, opcode, taken, true);
}
0xB7 => {
self.report_z80n(host, Z80nCommand::LdPirx);
let pattern = (self.registers.hl & mask::PATTERN_BASE)
| (self.registers.de & mask::PATTERN_OFFSET);
let (value, first) = self.read(host, pattern);
*taken += first;
self.copy_out(host, value, true, taken);
self.repeat_block(host, opcode, taken, true);
}
_ => return false,
}
true
}
fn copy_out<H: Host>(&mut self, host: &mut H, value: u8, from_pattern: bool, taken: &mut u32) {
*taken += if self.accumulator() == value {
self.read_taking(host, self.registers.de, 5).1
} else {
self.write_taking(host, self.registers.de, value, 5)
};
self.registers.de = self.registers.de.wrapping_add(1);
self.registers.bc = self.registers.bc.wrapping_sub(1);
let reported = if from_pattern {
self.registers.bc.to_be_bytes()[0] | value
} else {
self.accumulator().wrapping_add(value)
};
self.report_block(
reported,
flag::XY_MASK | flag::H_MASK | flag::N_MASK | flag::P_MASK,
);
}
fn repeat_block<H: Host>(&mut self, host: &mut H, opcode: u8, taken: &mut u32, on_pc: bool) {
if opcode & 0x10 == 0 || self.registers.bc == 0 {
return;
}
if on_pc {
self.bus_address = self.registers.pc;
}
*taken += self.internal(host, 5);
self.registers.pc = self.registers.pc.wrapping_sub(2);
}
fn interrupt_register(&mut self, opcode: u8, taken: &mut u32) {
*taken += 1;
self.lengthen_fetch(1);
match opcode {
0x47 => self.registers.i = self.accumulator(),
0x4F => self.registers.r = self.accumulator(),
_ => {
let value = if opcode == 0x57 {
self.registers.i
} else {
self.registers.r
};
self.set_accumulator(value);
self.report_byte(value, self.registers.iff2);
}
}
}
fn wide_load<H: Host>(&mut self, host: &mut H, opcode: u8, taken: &mut u32) {
let (address, first) = self.immediate_word(host);
let (low, second) = self.read(host, address);
self.registers.wz = address.wrapping_add(1);
let (high, third) = self.read(host, self.registers.wz);
*taken += first + second + third;
self.set_pair((opcode >> 4) & 0x03, u16::from_be_bytes([high, low]));
}
fn wide_store<H: Host>(&mut self, host: &mut H, opcode: u8, taken: &mut u32) {
let (address, first) = self.immediate_word(host);
let [high, low] = self.pair((opcode >> 4) & 0x03).to_be_bytes();
let second = self.write(host, address, low);
self.registers.wz = address.wrapping_add(1);
let third = self.write(host, self.registers.wz, high);
*taken += first + second + third;
}
fn wide_arithmetic<H: Host>(&mut self, host: &mut H, opcode: u8, taken: &mut u32) {
let op = if opcode & 0x08 == 0 {
AluOp::Sbc
} else {
AluOp::Adc
};
let [high, low] = self.pair((opcode >> 4) & 0x03).to_be_bytes();
let [left_high, left_low] = self.registers.hl.to_be_bytes();
let result_low = self.alu_in(
alu::Inputs {
op,
ir: opcode,
instruction_set: InstructionSet::Ed,
bus_a: left_low,
bus_b: low,
flags: 0,
preserve_result_flags: false,
combine_zero: false,
},
false,
);
let result_high = self.alu_zeroing(op, opcode, left_high, high);
self.registers.wz = self.registers.hl.wrapping_add(1);
self.registers.hl = u16::from_be_bytes([result_high, result_low]);
*taken += self.internal(host, 4) + self.internal(host, 3);
}
fn nibble_rotate<H: Host>(&mut self, host: &mut H, opcode: u8, taken: &mut u32) {
let address = self.registers.hl;
let (value, first) = self.read(host, address);
let accumulator = self.accumulator();
let (kept, stored) = if opcode == 0x6F {
(
(accumulator & 0xF0) | (value >> 4),
(value << 4) | (accumulator & 0x0F),
)
} else {
(
(accumulator & 0xF0) | (value & 0x0F),
(accumulator << 4) | (value >> 4),
)
};
self.set_accumulator(kept);
self.report_value(kept);
self.registers.wz = address.wrapping_add(1);
*taken += first + self.internal(host, 4) + self.write(host, address, stored);
}
fn port_in<H: Host>(&mut self, host: &mut H, opcode: u8, taken: &mut u32) {
let port = self.registers.bc;
let (value, t) = self.port_read(host, port);
*taken += t;
self.registers.wz = port.wrapping_add(1);
let code = (opcode >> 3) & 0x07;
if code != 6 {
self.set_register(code, value);
}
self.report_value(value);
}
fn port_out<H: Host>(&mut self, host: &mut H, opcode: u8, taken: &mut u32) {
let port = self.registers.bc;
let code = (opcode >> 3) & 0x07;
let value = if code == 6 { 0 } else { self.register(code) };
*taken += self.port_write(host, port, value);
self.registers.wz = port.wrapping_add(1);
}
fn block_transfer<H: Host>(&mut self, host: &mut H, opcode: u8, taken: &mut u32) {
let up = opcode & 0x08 == 0;
let (value, first) = self.read(host, self.registers.hl);
let second = self.write_taking(host, self.registers.de, value, 5);
*taken += first + second;
let step = if up { 1u16.wrapping_neg() } else { 1 };
self.registers.hl = self.registers.hl.wrapping_sub(step);
self.registers.de = self.registers.de.wrapping_sub(step);
self.registers.bc = self.registers.bc.wrapping_sub(1);
let reported = self.accumulator().wrapping_add(value);
self.report_block(
reported,
flag::XY_MASK | flag::H_MASK | flag::N_MASK | flag::P_MASK,
);
let repeating = opcode & 0x10 != 0;
if repeating && self.registers.bc != 0 {
*taken += self.internal(host, 5);
self.registers.pc = self.registers.pc.wrapping_sub(2);
self.registers.wz = self.registers.pc.wrapping_add(1);
}
}
fn indexed_bit<H: Host>(&mut self, host: &mut H, taken: &mut u32) {
let (address, first) = self.displaced(host);
let (opcode, second) = self.immediate_taking(host, 5);
#[cfg(test)]
mark(&mut self.seen.indexed_bits, opcode);
let (value, third) = self.read_taking(host, address, 4);
*taken += first + second + third;
let op = match opcode {
0x00..=0x3F => AluOp::Rotate,
0x40..=0x7F => AluOp::Bit,
0x80..=0xBF => AluOp::Res,
_ => AluOp::Set,
};
let result = self.alu_in(
alu::Inputs {
op,
ir: opcode,
instruction_set: InstructionSet::Cb,
bus_a: value,
bus_b: value,
flags: 0,
preserve_result_flags: false,
combine_zero: false,
},
false,
);
if matches!(op, AluOp::Bit) {
let flags = self.flags();
let high = self.registers.wz.to_be_bytes()[0];
self.set_flags((flags & !flag::XY_MASK) | (high & flag::XY_MASK));
return;
}
*taken += self.write(host, address, result);
let code = opcode & 0x07;
if code != 6 {
self.set_register(code, result);
}
}
fn block_compare<H: Host>(&mut self, host: &mut H, opcode: u8, taken: &mut u32) {
let up = opcode & 0x08 == 0;
let (value, first) = self.read(host, self.registers.hl);
*taken += first;
let left = self.accumulator();
self.alu(AluOp::Cp, opcode, left, value, true);
let step = if up { 1u16 } else { 1u16.wrapping_neg() };
self.registers.hl = self.registers.hl.wrapping_add(step);
self.registers.bc = self.registers.bc.wrapping_sub(1);
self.registers.wz = self.registers.wz.wrapping_add(step);
let borrowed = self.flags() & flag::H_MASK != 0;
let reported = left.wrapping_sub(value).wrapping_sub(u8::from(borrowed));
self.report_block(reported, flag::XY_MASK | flag::P_MASK);
*taken += self.internal(host, 5);
let repeating = opcode & 0x10 != 0;
let matched = self.flags() & flag::Z_MASK != 0;
if repeating && self.registers.bc != 0 && !matched {
*taken += self.internal(host, 5);
self.registers.pc = self.registers.pc.wrapping_sub(2);
self.registers.wz = self.registers.pc.wrapping_add(1);
}
}
fn report_value(&mut self, value: u8) {
self.report_byte(value, value.count_ones().is_multiple_of(2));
}
fn report_byte(&mut self, value: u8, parity: bool) {
let reported = flags::report_byte(self.flags(), value, parity);
self.set_flags(flags::take_undocumented(reported, value));
}
fn alu_zeroing(&mut self, op: AluOp, ir: u8, left: u8, right: u8) -> u8 {
let (result, produced) = alu::execute(alu::Inputs {
op,
ir: ir & mask::ALU_OPCODE,
instruction_set: InstructionSet::Ed,
bus_a: left,
bus_b: right,
flags: self.flags(),
preserve_result_flags: false,
combine_zero: true,
});
self.set_flags(produced);
result
}
fn port_block<H: Host>(&mut self, host: &mut H, opcode: u8, taken: &mut u32) {
let up = opcode & 0x08 == 0;
let outward = opcode & 0x01 != 0;
let before = self.registers.bc;
let counter = self.register(0).wrapping_sub(1);
self.set_register(0, counter);
*taken += 1;
self.lengthen_fetch(1);
let (value, port) = if outward {
let (byte, read) = self.read(host, self.registers.hl);
*taken += read;
let port = self.registers.bc;
*taken += self.port_write(host, port, byte);
(byte, port)
} else {
let (byte, read) = self.port_read(host, before);
*taken += read;
*taken += self.write(host, self.registers.hl, byte);
(byte, before)
};
let step = if up { 1u16 } else { 1u16.wrapping_neg() };
self.registers.hl = self.registers.hl.wrapping_add(step);
self.registers.wz = port.wrapping_add(step);
self.report_port_block(opcode, value);
let repeating = opcode & 0x10 != 0;
if repeating && counter != 0 {
*taken += self.internal(host, 5);
self.registers.pc = self.registers.pc.wrapping_sub(2);
}
}
fn report_port_block(&mut self, opcode: u8, value: u8) {
let reported = flags::port_block(
self.flags(),
self.registers.bc,
self.registers.hl,
opcode,
value,
);
self.set_flags(reported);
}
fn prefixed<H: Host>(&mut self, host: &mut H, opcode: u8, taken: &mut u32) {
#[cfg(test)]
mark(&mut self.seen.bits, opcode);
let code = opcode & 0x07;
let through_memory = code == 6;
let op = match opcode {
0x00..=0x3F => AluOp::Rotate,
0x40..=0x7F => AluOp::Bit,
0x80..=0xBF => AluOp::Res,
_ => AluOp::Set,
};
let (value, address) = if through_memory {
let address = self.registers.hl;
let (value, t) = self.read_taking(host, address, 4);
*taken += t;
(value, Some(address))
} else {
(self.register(code), None)
};
let result = self.alu_in(
alu::Inputs {
op,
ir: opcode,
instruction_set: InstructionSet::Cb,
bus_a: value,
bus_b: value,
flags: 0,
preserve_result_flags: false,
combine_zero: false,
},
false,
);
if matches!(op, AluOp::Bit) {
if through_memory {
let flags = self.flags();
let high = self.registers.wz.to_be_bytes()[0];
self.set_flags((flags & !flag::XY_MASK) | (high & flag::XY_MASK));
}
return;
}
match address {
Some(address) => *taken += self.write(host, address, result),
None => self.set_register(code, result),
}
}
fn undocumented_carry_source(&self, accumulator: u8, flags: u8) -> u8 {
flags::undocumented_carry_source(self.undocumented_flags, accumulator, flags)
}
fn accumulator_flags(&mut self, opcode: u8) {
let accumulator = self.accumulator();
let flags = self.flags();
if opcode == 0x2F {
self.set_accumulator(!accumulator);
let taken = (self.flags() & !flag::XY_MASK) | (!accumulator & flag::XY_MASK);
self.set_flags(taken | flag::H_MASK | flag::N_MASK);
return;
}
let reported = self.undocumented_carry_source(accumulator, flags);
let mut taken = (flags & !flag::XY_MASK) | (reported & flag::XY_MASK);
taken &= !(flag::N_MASK | flag::H_MASK);
if opcode == 0x3F && flags & flag::C_MASK != 0 {
taken = (taken | flag::H_MASK) & !flag::C_MASK;
} else {
taken |= flag::C_MASK;
}
self.set_flags(taken);
}
fn jump_relative<H: Host>(&mut self, host: &mut H, displacement: u8) -> u32 {
let taken = self.internal(host, 5);
self.registers.pc = self
.registers
.pc
.wrapping_add_signed(i16::from(displacement.cast_signed()));
self.registers.wz = self.registers.pc;
taken
}
fn report_block(&mut self, reported: u8, cleared: u8) {
let mut flags = self.flags() & !cleared;
flags |= reported & flag::X_MASK;
if reported & 0x02 != 0 {
flags |= flag::Y_MASK;
}
if self.registers.bc != 0 {
flags |= flag::P_MASK;
}
self.set_flags(flags);
}
fn condition(&self, code: u8) -> bool {
flags::condition_holds(self.flags(), code)
}
fn arithmetic(&mut self, opcode: u8, right: u8) {
let op = AluOp::from_bits((opcode >> 3) & 0x07);
let left = self.accumulator();
let result = self.alu(op, opcode, left, right, false);
if !matches!(op, AluOp::Cp) {
self.set_accumulator(result);
}
}
}
#[cfg(test)]
#[path = "stepped_tests.rs"]
mod tests;