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 register file, and the values the core reports about itself.

/// The complete architectural state of the processor.
///
/// Every field is public so that a caller restoring a saved machine can write the state directly,
/// which is otherwise awkward to express through accessors. The core reads these fields as it
/// executes, so writing them mid-instruction is permitted but will be observed immediately.
///
/// The register pairs are held as 16-bit values with the high byte in the upper half: the high
/// byte of `af` is the accumulator and the low byte is the flags.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Registers {
    /// Program counter.
    pub pc: u16,
    /// Stack pointer.
    pub sp: u16,
    /// Accumulator and flags.
    pub af: u16,
    /// The `BC` pair.
    pub bc: u16,
    /// The `DE` pair.
    pub de: u16,
    /// The `HL` pair.
    pub hl: u16,
    /// The alternate accumulator and flags, reached with `EX AF, AF'`.
    pub af_alt: u16,
    /// The alternate `BC` pair, reached with `EXX`.
    pub bc_alt: u16,
    /// The alternate `DE` pair, reached with `EXX`.
    pub de_alt: u16,
    /// The alternate `HL` pair, reached with `EXX`.
    pub hl_alt: u16,
    /// The `IX` index register.
    pub ix: u16,
    /// The `IY` index register.
    pub iy: u16,
    /// The internal address latch, commonly written `WZ` or `MEMPTR`.
    ///
    /// No instruction reads it directly, but `BIT n, (HL)` copies two of its bits into the
    /// undocumented flags. That makes it observable, so it's worth modelling.
    pub wz: u16,
    /// Interrupt vector base, the high byte of the vector address in interrupt mode 2.
    pub i: u8,
    /// Memory refresh counter. Only the low seven bits count — bit 7 is held across increments.
    pub r: u8,
    /// The selected interrupt mode.
    pub interrupt_mode: InterruptMode,
    /// Whether maskable interrupts are currently accepted.
    pub iff1: bool,
    /// The copy of `iff1` saved when a non-maskable interrupt is accepted. `RETN` restores it, and
    /// `LD A, I` or `LD A, R` make it readable through the parity flag.
    pub iff2: bool,
}

/// Where `SCF` and `CCF` take the two undocumented flags from.
///
/// Bits 3 and 5 of the flag register mean nothing architecturally, but these two instructions write
/// them, and parts differ in what they write. Nothing else in the instruction set depends on the
/// choice, and it's independent of whether the extended instructions are decoded.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum UndocumentedFlags {
    /// The accumulator combined with the flags the instruction started from.
    ///
    /// What a program written for the original part sees. The default.
    #[default]
    Combined,
    /// The accumulator alone, so the flags the instruction started from are discarded.
    Accumulator,
}

/// How the processor forms the address of a maskable interrupt handler.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum InterruptMode {
    /// Execute the instruction placed on the data bus by the interrupting device.
    #[default]
    Mode0,
    /// Restart at address `0x0038`, ignoring the data bus.
    Mode1,
    /// Read the handler address from the table at `I << 8`, indexed by the byte the interrupting
    /// device places on the data bus.
    Mode2,
}

/// The machine cycle the sequencer is currently in.
///
/// A Z80 instruction is a sequence of machine cycles, each several T-states long. The first is
/// always an opcode fetch; the rest depend on the instruction.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum MachineCycle {
    /// Opcode fetch, during which `M1` is asserted and the refresh counter is placed on the
    /// address bus.
    #[default]
    M1,
    /// The second machine cycle.
    M2,
    /// The third machine cycle.
    M3,
    /// The fourth machine cycle.
    M4,
    /// The fifth machine cycle.
    M5,
    /// The cycle that fetches the signed displacement following an index prefix.
    ///
    /// It does not appear in the numbered sequence because an indexed instruction inserts it
    /// between the opcode fetch and the machine cycles the opcode itself calls for.
    IndexDisplacement,
    /// The internal cycle that adds the displacement to the index register.
    IndexAddition,
}

impl MachineCycle {
    pub(crate) fn number(self) -> u8 {
        match self {
            Self::M1 => 1,
            Self::M2 => 2,
            Self::M3 => 3,
            Self::M4 => 4,
            Self::M5 => 5,
            Self::IndexDisplacement => 6,
            Self::IndexAddition => 7,
        }
    }

    pub(crate) fn from_number(number: u8) -> Self {
        match number {
            2 => Self::M2,
            3 => Self::M3,
            4 => Self::M4,
            5 => Self::M5,
            6 => Self::IndexDisplacement,
            7 => Self::IndexAddition,
            _ => Self::M1,
        }
    }
}

/// Which half of the clock cycle the core is on.
///
/// The processor advances its state on one edge and latches the data bus on the other, so a caller
/// stepping edge by edge needs to know which of the two a [`crate::Cpu::tick`] performed.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum ClockEdge {
    /// The edge on which the sequencer advances and register writes are committed.
    #[default]
    Rising,
    /// The edge on which the data bus is sampled.
    Falling,
}

/// A Z80N instruction, reported to the machine as the core executes it.
///
/// The core decodes, sequences and carries out these instructions itself, including their timing.
/// It reports each one because a machine watching the extended bus sees them, and because the few
/// whose effect reaches past the processor — writing an extended register, and the hooks a
/// stackless non-maskable interrupt uses — are the host's to act on.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub enum Z80nCommand {
    /// Write to an extended register.
    NextRegWrite,
    /// Multiply the two halves of `DE`.
    MulDe,
    /// Add the accumulator to `HL`.
    AddHlA,
    /// Add the accumulator to `DE`.
    AddDeA,
    /// Add the accumulator to `BC`.
    AddBcA,
    /// Exchange the two nibbles of the accumulator.
    SwapNibbleA,
    /// Advance an address by one pixel row.
    PixelDown,
    /// Set the accumulator to the single bit that the low three bits of `E` select, counting down
    /// from the most significant.
    SetAE,
    /// Form a display address from a pixel coordinate.
    PixelAddress,
    /// Reverse the bit order of the accumulator.
    MirrorA,
    /// Block copy with a pattern lookup.
    LdPirx,
    /// Add an immediate 16-bit value to `HL`.
    AddHlImmediate,
    /// Add an immediate 16-bit value to `DE`.
    AddDeImmediate,
    /// Add an immediate 16-bit value to `BC`.
    AddBcImmediate,
    /// Block copy that repeats without scaling, despite the name — the design decodes the opcode
    /// but the scaled source step it is named for is not in it.
    LdirScale,
    /// Shift `DE` left by `B`.
    BslaDeB,
    /// Shift `DE` right arithmetically by `B`.
    BsraDeB,
    /// Shift `DE` right logically by `B`.
    BsrlDeB,
    /// Shift `DE` right by `B`, filling the vacated bits with ones.
    BsrfDeB,
    /// Rotate `DE` left by `B`.
    BrlcDeB,
    /// Jump within the current 16K, to a target formed from a byte read from port `BC`.
    ///
    /// The byte does not become the address: it supplies bits 13 to 6 of `PC` and the top two bits
    /// are kept, so the target is 64-byte aligned and inside the region already executing.
    JpC,
    /// Report the low byte of the address captured when a non-maskable interrupt was accepted.
    NmiAcknowledgeLow,
    /// Report the high byte of the address captured when a non-maskable interrupt was accepted.
    NmiAcknowledgeHigh,
    /// Request the low byte of the address to return to from a non-maskable interrupt.
    ReturnFromNmiLow,
    /// Request the high byte of the address to return to from a non-maskable interrupt.
    ReturnFromNmiHigh,
}