use crate::alu::AluOp;
use crate::control::bus;
use crate::types::{InterruptMode, MachineCycle, Z80nCommand};
mod base;
mod bit;
mod extended;
mod z80n;
use base::base;
use bit::bit;
use extended::extended;
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub(crate) enum InstructionSet {
#[default]
Base,
Cb,
Ed,
DdFd,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub(crate) enum IndexState {
#[default]
None,
Ix,
Iy,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub(crate) enum AddressSource {
#[default]
None,
Bc,
De,
IndexOrHl,
Port,
Sp,
Latch,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub(crate) enum SpecialLoad {
#[default]
None,
AccumulatorFromVector,
AccumulatorFromRefresh,
VectorFromAccumulator,
RefreshFromAccumulator,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub(crate) struct Context {
pub ir: u8,
pub instruction_set: InstructionSet,
pub machine_cycle: MachineCycle,
pub flags: u8,
pub nmi_cycle: bool,
pub interrupt_cycle: bool,
pub index_state: IndexState,
pub accumulator: u8,
pub data: u8,
pub z80n_enabled: bool,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub(crate) struct Decoded {
flags: u64,
pub machine_cycles: u8,
pub t_states: u8,
pub prefix: InstructionSet,
pub inc_dec_16: u8,
pub set_bus_a_to: u8,
pub set_bus_b_to: u8,
pub alu_op: AluOp,
pub set_addr_to: AddressSource,
pub special_ld: SpecialLoad,
pub interrupt_mode: Option<InterruptMode>,
pub z80n_command: Option<Z80nCommand>,
pub z80n_data: u8,
}
impl Decoded {
pub(crate) const fn inc_pc(&self) -> bool {
self.flags & (1 << 0) != 0
}
pub(crate) const fn set_inc_pc(&mut self) {
self.flags |= 1 << 0;
}
pub(crate) const fn inc_wz(&self) -> bool {
self.flags & (1 << 1) != 0
}
pub(crate) const fn set_inc_wz(&mut self) {
self.flags |= 1 << 1;
}
pub(crate) const fn read_to_reg(&self) -> bool {
self.flags & (1 << 2) != 0
}
pub(crate) const fn set_read_to_reg(&mut self) {
self.flags |= 1 << 2;
}
pub(crate) const fn read_to_acc(&self) -> bool {
self.flags & (1 << 3) != 0
}
pub(crate) const fn set_read_to_acc(&mut self) {
self.flags |= 1 << 3;
}
pub(crate) const fn save_alu(&self) -> bool {
self.flags & (1 << 4) != 0
}
pub(crate) const fn set_save_alu(&mut self) {
self.flags |= 1 << 4;
}
pub(crate) const fn preserve_c(&self) -> bool {
self.flags & (1 << 5) != 0
}
pub(crate) const fn set_preserve_c(&mut self) {
self.flags |= 1 << 5;
}
pub(crate) const fn arith16(&self) -> bool {
self.flags & (1 << 6) != 0
}
pub(crate) const fn set_arith16(&mut self) {
self.flags |= 1 << 6;
}
pub(crate) const fn iorq(&self) -> bool {
self.flags & (1 << 7) != 0
}
pub(crate) const fn set_iorq(&mut self) {
self.flags |= 1 << 7;
}
pub(crate) const fn jump(&self) -> bool {
self.flags & (1 << 8) != 0
}
pub(crate) const fn set_jump(&mut self) {
self.flags |= 1 << 8;
}
pub(crate) const fn jump_e(&self) -> bool {
self.flags & (1 << 9) != 0
}
pub(crate) const fn set_jump_e(&mut self) {
self.flags |= 1 << 9;
}
pub(crate) const fn jump_xy(&self) -> bool {
self.flags & (1 << 10) != 0
}
pub(crate) const fn set_jump_xy(&mut self) {
self.flags |= 1 << 10;
}
pub(crate) const fn call(&self) -> bool {
self.flags & (1 << 11) != 0
}
pub(crate) const fn set_call(&mut self) {
self.flags |= 1 << 11;
}
pub(crate) const fn rst_p(&self) -> bool {
self.flags & (1 << 12) != 0
}
pub(crate) const fn set_rst_p(&mut self) {
self.flags |= 1 << 12;
}
pub(crate) const fn ldz(&self) -> bool {
self.flags & (1 << 13) != 0
}
pub(crate) const fn set_ldz(&mut self) {
self.flags |= 1 << 13;
}
pub(crate) const fn ldw(&self) -> bool {
self.flags & (1 << 14) != 0
}
pub(crate) const fn set_ldw(&mut self) {
self.flags |= 1 << 14;
}
pub(crate) const fn ld_sp_hl(&self) -> bool {
self.flags & (1 << 15) != 0
}
pub(crate) const fn set_ld_sp_hl(&mut self) {
self.flags |= 1 << 15;
}
pub(crate) const fn exchange_dh(&self) -> bool {
self.flags & (1 << 16) != 0
}
pub(crate) const fn set_exchange_dh(&mut self) {
self.flags |= 1 << 16;
}
pub(crate) const fn exchange_rp(&self) -> bool {
self.flags & (1 << 17) != 0
}
pub(crate) const fn set_exchange_rp(&mut self) {
self.flags |= 1 << 17;
}
pub(crate) const fn exchange_af(&self) -> bool {
self.flags & (1 << 18) != 0
}
pub(crate) const fn set_exchange_af(&mut self) {
self.flags |= 1 << 18;
}
pub(crate) const fn exchange_rs(&self) -> bool {
self.flags & (1 << 19) != 0
}
pub(crate) const fn set_exchange_rs(&mut self) {
self.flags |= 1 << 19;
}
pub(crate) const fn exchange_wh(&self) -> bool {
self.flags & (1 << 20) != 0
}
pub(crate) const fn set_exchange_wh(&mut self) {
self.flags |= 1 << 20;
}
pub(crate) const fn i_djnz(&self) -> bool {
self.flags & (1 << 21) != 0
}
pub(crate) const fn set_i_djnz(&mut self) {
self.flags |= 1 << 21;
}
pub(crate) const fn i_cpl(&self) -> bool {
self.flags & (1 << 22) != 0
}
pub(crate) const fn set_i_cpl(&mut self) {
self.flags |= 1 << 22;
}
pub(crate) const fn i_ccf(&self) -> bool {
self.flags & (1 << 23) != 0
}
pub(crate) const fn set_i_ccf(&mut self) {
self.flags |= 1 << 23;
}
pub(crate) const fn i_scf(&self) -> bool {
self.flags & (1 << 24) != 0
}
pub(crate) const fn set_i_scf(&mut self) {
self.flags |= 1 << 24;
}
pub(crate) const fn i_retn(&self) -> bool {
self.flags & (1 << 25) != 0
}
pub(crate) const fn set_i_retn(&mut self) {
self.flags |= 1 << 25;
}
pub(crate) const fn i_bt(&self) -> bool {
self.flags & (1 << 26) != 0
}
pub(crate) const fn set_i_bt(&mut self) {
self.flags |= 1 << 26;
}
pub(crate) const fn i_bc(&self) -> bool {
self.flags & (1 << 27) != 0
}
pub(crate) const fn set_i_bc(&mut self) {
self.flags |= 1 << 27;
}
pub(crate) const fn i_btr(&self) -> bool {
self.flags & (1 << 28) != 0
}
pub(crate) const fn set_i_btr(&mut self) {
self.flags |= 1 << 28;
}
pub(crate) const fn i_rld(&self) -> bool {
self.flags & (1 << 29) != 0
}
pub(crate) const fn set_i_rld(&mut self) {
self.flags |= 1 << 29;
}
pub(crate) const fn i_rrd(&self) -> bool {
self.flags & (1 << 30) != 0
}
pub(crate) const fn set_i_rrd(&mut self) {
self.flags |= 1 << 30;
}
pub(crate) const fn i_inrc(&self) -> bool {
self.flags & (1 << 31) != 0
}
pub(crate) const fn set_i_inrc(&mut self) {
self.flags |= 1 << 31;
}
pub(crate) const fn set_di(&self) -> bool {
self.flags & (1 << 32) != 0
}
pub(crate) const fn set_set_di(&mut self) {
self.flags |= 1 << 32;
}
pub(crate) const fn set_ei(&self) -> bool {
self.flags & (1 << 33) != 0
}
pub(crate) const fn set_set_ei(&mut self) {
self.flags |= 1 << 33;
}
pub(crate) const fn halt(&self) -> bool {
self.flags & (1 << 34) != 0
}
pub(crate) const fn set_halt(&mut self) {
self.flags |= 1 << 34;
}
pub(crate) const fn no_read(&self) -> bool {
self.flags & (1 << 35) != 0
}
pub(crate) const fn set_no_read(&mut self) {
self.flags |= 1 << 35;
}
pub(crate) const fn write(&self) -> bool {
self.flags & (1 << 36) != 0
}
pub(crate) const fn set_write(&mut self) {
self.flags |= 1 << 36;
}
pub(crate) const fn set_write_to(&mut self, on: bool) {
if on {
self.flags |= 1 << 36;
} else {
self.flags &= !(1 << 36);
}
}
pub(crate) const fn no_pc(&self) -> bool {
self.flags & (1 << 37) != 0
}
pub(crate) const fn set_no_pc(&mut self) {
self.flags |= 1 << 37;
}
pub(crate) const fn xy_bit_undoc(&self) -> bool {
self.flags & (1 << 38) != 0
}
pub(crate) const fn set_xy_bit_undoc(&mut self) {
self.flags |= 1 << 38;
}
#[allow(dead_code)]
pub(crate) const fn z80n_dout(&self) -> bool {
self.flags & (1 << 39) != 0
}
pub(crate) const fn set_z80n_dout(&mut self) {
self.flags |= 1 << 39;
}
pub(crate) const fn z80n_strobe_lo(&self) -> bool {
self.flags & (1 << 40) != 0
}
pub(crate) const fn set_z80n_strobe_lo(&mut self) {
self.flags |= 1 << 40;
}
pub(crate) const fn z80n_strobe_hi(&self) -> bool {
self.flags & (1 << 41) != 0
}
pub(crate) const fn set_z80n_strobe_hi(&mut self) {
self.flags |= 1 << 41;
}
}
pub(crate) fn decode(context: Context) -> Decoded {
let mut out = defaults(context);
match context.instruction_set {
InstructionSet::Base => base(context, &mut out),
InstructionSet::Cb => bit(context, &mut out),
InstructionSet::Ed | InstructionSet::DdFd => extended(context, &mut out),
}
index_cycles(context, &mut out);
out
}
fn defaults(context: Context) -> Decoded {
Decoded {
flags: 0,
machine_cycles: 1,
t_states: if matches!(context.machine_cycle, MachineCycle::M1) {
4
} else {
3
},
prefix: InstructionSet::Base,
inc_dec_16: 0,
set_bus_a_to: 0,
set_bus_b_to: 0,
alu_op: AluOp::from_bits(destination(context.ir)),
set_addr_to: AddressSource::None,
special_ld: SpecialLoad::None,
interrupt_mode: None,
z80n_command: None,
z80n_data: 0,
}
}
fn destination(ir: u8) -> u8 {
(ir >> 3) & 0x07
}
fn source(ir: u8) -> u8 {
ir & 0x07
}
fn pair(ir: u8) -> u8 {
(ir >> 4) & 0x03
}
use crate::flags::condition_holds;
fn pair_low(field: u8) -> u8 {
if field == bus::FOURTH_PAIR {
bus::STACK_LOW
} else {
(field << 1) | 1
}
}
fn pair_high(field: u8) -> u8 {
if field == bus::FOURTH_PAIR {
bus::STACK_HIGH
} else {
field << 1
}
}
fn stacked_low(field: u8) -> u8 {
if field == bus::FOURTH_PAIR {
bus::FLAGS
} else {
(field << 1) | 1
}
}
fn stacked_high(field: u8) -> u8 {
if field == bus::FOURTH_PAIR {
bus::ACCUMULATOR
} else {
field << 1
}
}
fn index_cycles(context: Context, out: &mut Decoded) {
let ir = context.ir;
let prefixed_bit = matches!(context.instruction_set, InstructionSet::Cb);
match context.machine_cycle {
MachineCycle::IndexDisplacement => {
out.set_inc_pc();
if ir == 0x36 || ir == 0xCB {
out.set_addr_to = AddressSource::None;
}
if !(ir == 0x36 || prefixed_bit || ir == 0x8A) {
out.set_no_pc();
}
}
MachineCycle::IndexAddition => {
out.t_states = 5;
if !prefixed_bit {
out.set_addr_to = AddressSource::IndexOrHl;
}
out.set_bus_b_to = source(ir);
if ir == 0x36 || prefixed_bit {
out.set_inc_pc();
} else {
out.set_no_read();
}
}
_ => {}
}
}
#[cfg(test)]
#[path = "../mcode_tests.rs"]
mod tests;