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 datapath, and the reports that leave the processor.

use super::{Cpu, Wide, high_byte, low_byte, wide_value};
use crate::consts::flag;
use crate::control::mask;
use crate::extended::{self};
use crate::host::Host;
use crate::mcode::Decoded;
use crate::types::Z80nCommand;

impl Cpu {
    pub(super) fn run_extended(&mut self, decoded: &Decoded) {
        let Some(command) = decoded.z80n_command else {
            return;
        };
        let cycle = self.machine_cycle.number();
        let last = self.t_state == 4;

        match command {
            Z80nCommand::SwapNibbleA if self.t_state == 3 => {
                let swapped = extended::swap_nibbles(self.accumulator());
                self.set_accumulator(swapped);
            }
            Z80nCommand::MirrorA if self.t_state == 3 => {
                let mirrored = extended::mirror(self.accumulator());
                self.set_accumulator(mirrored);
            }
            Z80nCommand::MulDe if last => {
                let product = extended::multiply(self.registers.de);
                self.registers.de = product;
            }
            Z80nCommand::AddHlA | Z80nCommand::AddDeA | Z80nCommand::AddBcA if last => {
                let register = match command {
                    Z80nCommand::AddHlA => Wide::Hl,
                    Z80nCommand::AddDeA => Wide::De,
                    _ => Wide::Bc,
                };
                let sum = extended::add_accumulator(self.wide(register), self.accumulator());
                self.set_wide(register, sum);
                self.set_flags(self.flags() & !flag::C_MASK);
            }
            Z80nCommand::SetAE if last => {
                let selected = extended::bit_from_low_three(low_byte(self.registers.de));
                self.set_accumulator(selected);
            }
            Z80nCommand::PixelAddress if last => {
                self.registers.hl = extended::pixel_address(self.registers.de);
            }
            Z80nCommand::PixelDown if last => {
                self.registers.hl = extended::pixel_down(self.registers.hl);
            }
            Z80nCommand::BslaDeB
            | Z80nCommand::BsraDeB
            | Z80nCommand::BsrlDeB
            | Z80nCommand::BsrfDeB
            | Z80nCommand::BrlcDeB
                if last =>
            {
                let kind = match command {
                    Z80nCommand::BslaDeB => extended::Barrel::Left,
                    Z80nCommand::BsraDeB => extended::Barrel::ArithmeticRight,
                    Z80nCommand::BsrlDeB => extended::Barrel::LogicalRight,
                    Z80nCommand::BsrfDeB => extended::Barrel::FillRight,
                    _ => extended::Barrel::Rotate,
                };
                let count = high_byte(self.registers.bc);
                self.registers.de = extended::barrel(self.registers.de, count, kind);
            }
            Z80nCommand::AddHlImmediate
            | Z80nCommand::AddDeImmediate
            | Z80nCommand::AddBcImmediate
                if cycle == 3 && last =>
            {
                let register = match command {
                    Z80nCommand::AddHlImmediate => Wide::Hl,
                    Z80nCommand::AddDeImmediate => Wide::De,
                    _ => Wide::Bc,
                };
                let sum = extended::add_immediate(self.wide(register), self.tmp_addr);
                self.set_wide(register, sum);
            }
            Z80nCommand::LdPirx => {
                if cycle == 1 && last {
                    self.address = (self.registers.hl & mask::PATTERN_BASE)
                        | (self.registers.de & mask::PATTERN_OFFSET);
                } else if cycle == 2 {
                    self.address = self.registers.de;
                }
            }
            Z80nCommand::JpC if cycle == 2 && last => {
                let target = (u16::from(self.data_latch) << mask::JUMP_SHIFT) & mask::JUMP_TARGET;
                self.registers.pc = (self.registers.pc & mask::JUMP_REGION) | target;
                self.address = self.registers.pc;
            }
            _ => {}
        }
    }

    pub(super) fn intercepts_its_own_cycle(command: Z80nCommand) -> bool {
        matches!(
            command,
            Z80nCommand::NmiAcknowledgeHigh
                | Z80nCommand::NmiAcknowledgeLow
                | Z80nCommand::ReturnFromNmiLow
                | Z80nCommand::ReturnFromNmiHigh
        )
    }

    pub(super) fn report_intercepted<H: Host>(&mut self, decoded: &Decoded, host: &mut H) {
        let Some(command) = decoded.z80n_command else {
            return;
        };
        if Self::intercepts_its_own_cycle(command) && self.last_command != decoded.z80n_command {
            self.last_command = decoded.z80n_command;
            host.z80n_command(command, self.z80n_operand);
        }
    }

    pub(super) fn export_extended<H: Host>(&mut self, decoded: &Decoded, host: &mut H) {
        if decoded.z80n_strobe_lo() {
            self.z80n_operand = wide_value(high_byte(self.z80n_operand), decoded.z80n_data);
        }
        if decoded.z80n_strobe_hi() {
            self.z80n_operand = wide_value(decoded.z80n_data, low_byte(self.z80n_operand));
        }

        if decoded.z80n_command != self.last_command {
            self.last_command = decoded.z80n_command;
            if let Some(command) = decoded.z80n_command {
                host.z80n_command(command, self.z80n_operand);
            }
        }
    }
}