use super::z80n::extended_z80n;
use super::{
AddressSource, AluOp, Context, Decoded, InterruptMode, SpecialLoad, Z80nCommand, destination,
pair, pair_high, pair_low,
};
use crate::control::{bus, step};
#[allow(clippy::too_many_lines)]
pub(super) fn extended(context: Context, out: &mut Decoded) {
let ir = context.ir;
let cycle = context.machine_cycle.number();
match ir {
0x57 => {
out.special_ld = SpecialLoad::AccumulatorFromVector;
out.t_states = 5;
}
0x5F => {
out.special_ld = SpecialLoad::AccumulatorFromRefresh;
out.t_states = 5;
}
0x47 => {
out.special_ld = SpecialLoad::VectorFromAccumulator;
out.t_states = 5;
}
0x4F => {
out.special_ld = SpecialLoad::RefreshFromAccumulator;
out.t_states = 5;
}
0x4B | 0x5B | 0x6B | 0x7B => {
out.machine_cycles = 5;
match cycle {
2 => {
out.set_inc_pc();
out.set_ldz();
}
3 => {
out.set_addr_to = AddressSource::Latch;
out.set_inc_pc();
out.set_ldw();
}
4 => {
out.set_read_to_reg();
out.set_bus_a_to = pair_low(pair(ir));
out.set_inc_wz();
out.set_addr_to = AddressSource::Latch;
}
5 => {
out.set_read_to_reg();
out.set_bus_a_to = pair_high(pair(ir));
}
_ => {}
}
}
0x43 | 0x53 | 0x63 | 0x73 => {
out.machine_cycles = 5;
match cycle {
2 => {
out.set_inc_pc();
out.set_ldz();
}
3 => {
out.set_addr_to = AddressSource::Latch;
out.set_inc_pc();
out.set_ldw();
out.set_bus_b_to = pair_low(pair(ir));
}
4 => {
out.set_inc_wz();
out.set_addr_to = AddressSource::Latch;
out.set_write();
out.set_bus_b_to = pair_high(pair(ir));
}
5 => out.set_write(),
_ => {}
}
}
0xA0 | 0xA8 | 0xB0 | 0xB8 => block_transfer(context, out),
0xA1 | 0xA9 | 0xB1 | 0xB9 => {
out.machine_cycles = 4;
match cycle {
1 => {
out.set_addr_to = AddressSource::IndexOrHl;
out.inc_dec_16 = step::down(step::BC);
}
2 => {
out.set_bus_b_to = bus::DATA_LATCH;
out.set_bus_a_to = bus::ACCUMULATOR;
out.alu_op = AluOp::Cp;
out.set_save_alu();
out.set_preserve_c();
out.inc_dec_16 = if ir & 0x08 == 0 {
step::up(step::HL)
} else {
step::down(step::HL)
};
out.set_no_pc();
}
3 => {
out.set_no_read();
out.set_i_bc();
out.t_states = 5;
out.set_no_pc();
}
4 => {
out.set_no_read();
out.t_states = 5;
}
_ => {}
}
}
0x44 | 0x4C | 0x54 | 0x5C | 0x64 | 0x6C | 0x74 | 0x7C => {
out.alu_op = AluOp::Sub;
out.set_bus_b_to = bus::ACCUMULATOR;
out.set_bus_a_to = bus::ONE;
out.set_read_to_acc();
out.set_save_alu();
}
0x46 | 0x4E | 0x66 | 0x6E => out.interrupt_mode = Some(InterruptMode::Mode0),
0x56 | 0x76 => out.interrupt_mode = Some(InterruptMode::Mode1),
0x5E | 0x7E => out.interrupt_mode = Some(InterruptMode::Mode2),
0x4A | 0x5A | 0x6A | 0x7A => wide_arithmetic(context, out, AluOp::Adc),
0x42 | 0x52 | 0x62 | 0x72 => wide_arithmetic(context, out, AluOp::Sbc),
0x6F => nibble_rotate(context, out, AluOp::Rld),
0x67 => nibble_rotate(context, out, AluOp::Rrd),
0x4D => {
out.machine_cycles = 3;
match cycle {
1 => out.set_addr_to = AddressSource::Sp,
2 => {
out.inc_dec_16 = step::up(step::STACK);
out.set_addr_to = AddressSource::Sp;
out.set_ldz();
}
3 => {
out.set_jump();
out.inc_dec_16 = step::up(step::STACK);
out.set_i_retn();
}
_ => {}
}
}
0x45 | 0x55 | 0x5D | 0x65 | 0x6D | 0x75 | 0x7D => {
out.machine_cycles = 3;
match cycle {
1 => out.set_addr_to = AddressSource::Sp,
2 => {
out.inc_dec_16 = step::up(step::STACK);
out.set_addr_to = AddressSource::Sp;
out.set_ldz();
if context.z80n_enabled {
out.z80n_command = Some(Z80nCommand::ReturnFromNmiLow);
}
}
3 => {
out.set_jump();
out.inc_dec_16 = step::up(step::STACK);
out.set_i_retn();
if context.z80n_enabled {
out.z80n_command = Some(Z80nCommand::ReturnFromNmiHigh);
}
}
_ => {}
}
}
0x40 | 0x48 | 0x50 | 0x58 | 0x60 | 0x68 | 0x70 | 0x78 => {
out.machine_cycles = 2;
match cycle {
1 => out.set_addr_to = AddressSource::Bc,
2 => {
out.set_iorq();
if destination(ir) != bus::DATA_LATCH {
out.set_read_to_reg();
out.set_bus_a_to = destination(ir);
}
out.set_i_inrc();
}
_ => {}
}
}
0x41 | 0x49 | 0x51 | 0x59 | 0x61 | 0x69 | 0x71 | 0x79 => {
out.machine_cycles = 2;
match cycle {
1 => {
out.set_addr_to = AddressSource::Bc;
out.set_bus_b_to = destination(ir);
if destination(ir) == bus::DATA_LATCH {
out.set_bus_b_to |= bus::STACK_LOW;
}
}
2 => {
out.set_write();
out.set_iorq();
}
_ => {}
}
}
0xA2 | 0xAA | 0xB2 | 0xBA => {
out.machine_cycles = 4;
match cycle {
1 => {
out.t_states = 5;
out.set_addr_to = AddressSource::Bc;
out.set_bus_b_to = bus::ONE;
out.set_bus_a_to = bus::B;
out.set_read_to_reg();
out.set_save_alu();
out.alu_op = AluOp::Sub;
out.inc_dec_16 = ir & 0x08;
}
2 => {
out.set_iorq();
out.set_bus_b_to = bus::DATA_LATCH;
out.set_addr_to = AddressSource::IndexOrHl;
}
3 => {
out.inc_dec_16 = if ir & 0x08 == 0 {
step::up(step::HL)
} else {
step::down(step::HL)
};
out.set_write();
out.set_i_btr();
out.set_no_pc();
}
4 => {
out.set_no_read();
out.t_states = 5;
}
_ => {}
}
}
0xA3 | 0xAB | 0xB3 | 0xBB => port_block_out(context, out, false),
_ => extended_z80n(context, out),
}
}
pub(super) fn block_transfer(context: Context, out: &mut Decoded) {
let ir = context.ir;
let skips_matching = matches!(ir, 0xA4 | 0xB4);
out.machine_cycles = 4;
match context.machine_cycle.number() {
1 => {
out.set_addr_to = AddressSource::IndexOrHl;
out.inc_dec_16 = step::down(step::BC);
}
2 => {
out.set_bus_b_to = bus::DATA_LATCH;
out.set_bus_a_to = bus::ACCUMULATOR;
out.alu_op = AluOp::Add;
out.set_addr_to = AddressSource::De;
out.inc_dec_16 = if ir & 0x08 == 0 {
step::up(step::HL)
} else {
step::down(step::HL)
};
}
3 => {
out.set_i_bt();
out.t_states = 5;
out.set_write_to(!skips_matching || context.accumulator != context.data);
out.inc_dec_16 = if ir & 0x08 == 0 {
step::up(step::DE)
} else {
step::down(step::DE)
};
out.set_no_pc();
}
4 => {
out.set_no_read();
out.t_states = 5;
}
_ => {}
}
}
fn wide_arithmetic(context: Context, out: &mut Decoded, op: AluOp) {
let dpair = pair(context.ir);
out.machine_cycles = 3;
match context.machine_cycle.number() {
1 => out.set_no_pc(),
2 => {
out.set_no_read();
out.alu_op = op;
out.set_read_to_reg();
out.set_save_alu();
out.set_bus_a_to = bus::L;
out.set_bus_b_to = pair_low(dpair);
out.t_states = 4;
out.set_no_pc();
}
3 => {
out.set_no_read();
out.set_read_to_reg();
out.set_save_alu();
out.alu_op = op;
out.set_bus_a_to = bus::H;
out.set_bus_b_to = pair_high(dpair);
}
_ => {}
}
}
fn nibble_rotate(context: Context, out: &mut Decoded, op: AluOp) {
out.machine_cycles = 4;
match context.machine_cycle.number() {
1 => out.set_addr_to = AddressSource::IndexOrHl,
2 => {
out.set_read_to_reg();
out.set_bus_b_to = bus::DATA_LATCH;
out.set_bus_a_to = bus::ACCUMULATOR;
out.alu_op = op;
out.set_save_alu();
out.set_no_pc();
}
3 => {
out.t_states = 4;
if matches!(op, AluOp::Rld) {
out.set_i_rld();
} else {
out.set_i_rrd();
}
out.set_no_read();
out.set_addr_to = AddressSource::IndexOrHl;
}
4 => out.set_write(),
_ => {}
}
}
pub(super) fn port_block_out(context: Context, out: &mut Decoded, single_step: bool) {
let ir = context.ir;
out.machine_cycles = if single_step { 3 } else { 4 };
match context.machine_cycle.number() {
1 => {
out.t_states = 5;
out.set_addr_to = AddressSource::IndexOrHl;
out.set_bus_b_to = bus::ONE;
out.set_bus_a_to = bus::B;
if !single_step {
out.set_read_to_reg();
out.set_save_alu();
out.alu_op = AluOp::Sub;
}
}
2 => {
out.set_bus_b_to = bus::DATA_LATCH;
out.set_addr_to = AddressSource::Bc;
out.inc_dec_16 = ir & 0x08;
}
3 => {
out.inc_dec_16 = if ir & 0x08 == 0 {
step::up(step::HL)
} else {
step::down(step::HL)
};
out.set_iorq();
out.set_write();
out.set_i_btr();
}
4 => {
out.set_no_read();
out.t_states = 5;
}
_ => {}
}
}