hg80 1.0.0

Z80 and Z80N CPU core, stepped one clock edge at a time
Documentation
// Portions of this file are derived from the T80 Z80-compatible microprocessor core,
// Copyright (c) 2001-2002 Daniel Wallner, and from the T80N modifications made for the
// ZX Spectrum Next Project, Copyright 2020 Fabio Belavenuto, Victor Trucco, Charlie Ingley,
// Garry Lancaster, ACX. Redistributed under the three-clause BSD licence reproduced in NOTICE.

//! The Z80N extended instructions.

use super::extended::{block_transfer, port_block_out};
use super::{AddressSource, AluOp, Context, Decoded, Z80nCommand};
use crate::control::{bus, step};

#[allow(clippy::too_many_lines)]
pub(super) fn extended_z80n(context: Context, out: &mut Decoded) {
    if !context.z80n_enabled {
        return;
    }

    let ir = context.ir;
    let cycle = context.machine_cycle.number();

    match ir {
        0x91 => {
            out.machine_cycles = 5;
            match cycle {
                1 | 5 => out.set_inc_pc(),
                2 => {
                    out.set_inc_pc();
                    out.z80n_data = context.data;
                    out.set_z80n_strobe_hi();
                }
                3 => {
                    out.z80n_data = context.data;
                    out.set_z80n_strobe_lo();
                }
                4 => {
                    out.z80n_command = Some(Z80nCommand::NextRegWrite);
                    out.set_z80n_dout();
                }
                _ => {}
            }
        }
        0x92 => {
            out.machine_cycles = 4;
            match cycle {
                1 => {
                    out.set_inc_pc();
                    out.z80n_data = context.accumulator;
                    out.set_z80n_strobe_lo();
                }
                2 => {
                    out.z80n_data = context.data;
                    out.set_z80n_strobe_hi();
                }
                3 => {
                    out.z80n_command = Some(Z80nCommand::NextRegWrite);
                    out.set_z80n_dout();
                }
                4 => out.set_inc_pc(),
                _ => {}
            }
        }
        0x30 => simple_z80n(out, Z80nCommand::MulDe),
        0x31 => simple_z80n(out, Z80nCommand::AddHlA),
        0x32 => simple_z80n(out, Z80nCommand::AddDeA),
        0x33 => simple_z80n(out, Z80nCommand::AddBcA),
        0x23 => simple_z80n(out, Z80nCommand::SwapNibbleA),
        0x24 => simple_z80n(out, Z80nCommand::MirrorA),
        0x93 => simple_z80n(out, Z80nCommand::PixelDown),
        0x94 => simple_z80n(out, Z80nCommand::PixelAddress),
        0x95 => simple_z80n(out, Z80nCommand::SetAE),
        0x28 => simple_z80n(out, Z80nCommand::BslaDeB),
        0x29 => simple_z80n(out, Z80nCommand::BsraDeB),
        0x2A => simple_z80n(out, Z80nCommand::BsrlDeB),
        0x2B => simple_z80n(out, Z80nCommand::BsrfDeB),
        0x2C => simple_z80n(out, Z80nCommand::BrlcDeB),
        0x27 => {
            out.machine_cycles = 2;
            if cycle == 2 {
                out.set_inc_pc();
                out.set_save_alu();
                out.set_bus_b_to = bus::DATA_LATCH;
                out.set_bus_a_to = bus::ACCUMULATOR;
            }
        }
        0x98 => {
            out.machine_cycles = 2;
            out.t_states = 4;
            out.z80n_command = Some(Z80nCommand::JpC);
            match cycle {
                1 => out.set_addr_to = AddressSource::Bc,
                2 => out.set_iorq(),
                _ => {}
            }
        }
        0x34..=0x36 => {
            out.t_states = 4;
            out.z80n_command = Some(match ir {
                0x34 => Z80nCommand::AddHlImmediate,
                0x35 => Z80nCommand::AddDeImmediate,
                _ => Z80nCommand::AddBcImmediate,
            });
            out.machine_cycles = 3;
            match cycle {
                1 => out.set_inc_pc(),
                2 => {
                    out.set_inc_pc();
                    out.set_ldz();
                }
                3 => {
                    out.set_inc_pc();
                    out.set_ldw();
                }
                _ => {}
            }
        }
        0x8A => {
            out.machine_cycles = 6;
            match cycle {
                1 => {
                    out.set_inc_pc();
                    out.set_ldz();
                }
                2 | 4 => {
                    out.inc_dec_16 = step::down(step::STACK);
                    out.set_addr_to = AddressSource::Sp;
                    out.set_bus_b_to = bus::DATA_LATCH;
                }
                3 => {
                    out.set_inc_pc();
                    out.set_ldz();
                    out.set_write();
                }
                5 => out.set_write(),
                _ => {}
            }
        }
        0xB7 => {
            out.z80n_command = Some(Z80nCommand::LdPirx);
            out.machine_cycles = 4;
            match cycle {
                1 => {
                    out.set_ldz();
                    out.set_addr_to = AddressSource::IndexOrHl;
                    out.inc_dec_16 = step::down(step::BC);
                }
                2 => out.set_bus_b_to = bus::DATA_LATCH,
                3 => {
                    out.set_i_bt();
                    out.t_states = 5;
                    out.set_write_to(context.accumulator != context.data);
                    out.inc_dec_16 = step::up(step::DE);
                }
                4 => {
                    out.set_no_read();
                    out.t_states = 5;
                }
                _ => {}
            }
        }
        0xA4 | 0xB4 => block_transfer(context, out),
        0xA5 => {
            out.machine_cycles = 3;
            match cycle {
                1 => {
                    out.set_addr_to = AddressSource::IndexOrHl;
                    out.set_bus_b_to = bus::ONE;
                    out.set_bus_a_to = bus::L;
                    out.set_read_to_reg();
                    out.set_save_alu();
                    out.alu_op = AluOp::Add;
                }
                2 => {
                    out.set_addr_to = AddressSource::De;
                    out.set_bus_b_to = bus::DATA_LATCH;
                    out.set_bus_a_to = bus::ACCUMULATOR;
                    out.alu_op = AluOp::Add;
                }
                3 => {
                    out.set_i_bt();
                    out.t_states = 3;
                    out.set_write();
                    out.set_bus_b_to = bus::ONE;
                    out.set_bus_a_to = bus::D;
                    out.set_read_to_reg();
                    out.set_save_alu();
                    out.alu_op = AluOp::Add;
                }
                _ => {}
            }
        }
        0xB6 => {
            out.z80n_command = Some(Z80nCommand::LdirScale);
            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::Add;
                    out.set_addr_to = AddressSource::De;
                    out.inc_dec_16 = step::up(step::HL);
                }
                3 => {
                    out.set_i_bt();
                    out.t_states = 5;
                    out.set_write_to(context.accumulator != context.data);
                    out.inc_dec_16 = step::up(step::DE);
                }
                4 => {
                    out.set_no_read();
                    out.t_states = 5;
                }
                _ => {}
            }
        }
        0xAC | 0xBC => {
            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::Add;
                    out.set_addr_to = AddressSource::De;
                    out.inc_dec_16 = step::down(step::HL);
                }
                3 => {
                    out.set_i_bt();
                    out.t_states = 5;
                    out.set_write_to(context.accumulator != context.data);
                    out.inc_dec_16 = step::up(step::DE);
                }
                4 => {
                    out.set_no_read();
                    out.t_states = 5;
                }
                _ => {}
            }
        }
        0x90 => port_block_out(context, out, true),
        _ => {}
    }
}

fn simple_z80n(out: &mut Decoded, command: Z80nCommand) {
    out.t_states = 4;
    out.z80n_command = Some(command);
}