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 interface between the core and the machine it sits in.

use crate::types::Z80nCommand;

/// What the core is doing on the bus during a given T-state.
///
/// Passed to [`Host::wait_states`] and [`Host::bus_edge`], so a host can add wait states or watch
/// activity at the exact point in an instruction the hardware would.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub enum BusRequest {
    /// An opcode fetch, with `M1` asserted.
    OpcodeFetch {
        /// The address being fetched from.
        address: u16,
    },
    /// A memory read that is not an opcode fetch.
    MemoryRead {
        /// The address being read.
        address: u16,
    },
    /// A memory write.
    MemoryWrite {
        /// The address being written.
        address: u16,
        /// The value on the data bus.
        value: u8,
    },
    /// A read from a port.
    PortRead {
        /// The full 16-bit address on the bus.
        port: u16,
    },
    /// A write to a port.
    PortWrite {
        /// The full 16-bit address on the bus.
        port: u16,
        /// The value on the data bus.
        value: u8,
    },
    /// The refresh half of an opcode fetch, during which the refresh counter is on the address
    /// bus and no transfer takes place.
    Refresh {
        /// The address formed from the interrupt vector base and the refresh counter.
        address: u16,
    },
    /// A T-state where the core is working internally and no transfer happens.
    ///
    /// The address bus still carries a value. A machine that contends its memory needs it to
    /// decide whether to stall the processor.
    Internal {
        /// The value the address bus carries.
        address: u16,
    },
    /// The acknowledgement cycle of an accepted maskable interrupt, during which the interrupting
    /// device supplies a byte in place of memory.
    InterruptAcknowledge {
        /// The value the address bus carries, which is the program counter.
        address: u16,
    },
}

/// A whole machine cycle, reported once it has run.
///
/// A machine that works a bus cycle at a time — charging contention, advancing a clocked
/// peripheral — wants one report per cycle rather than one per edge, and wants the cycle's length
/// so it can advance by that much in a single step. Coalescing edges by hand to get there is easy
/// to get wrong: a peripheral clocked once per cycle that is ticked once per T-state runs several
/// times too fast.
///
/// The report arrives at the first edge of the *following* cycle, because a cycle's length is not
/// known until it has run. An opcode fetch is the plain case: the opcode has not been read at the
/// point the fetch begins, so how long the fetch takes cannot be known then either. Reports arrive
/// in order, before that following cycle's own transfer, so a clock advanced by them stays in
/// step.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[non_exhaustive]
pub struct BusCycle {
    /// What the cycle does, and the address it carries, as it stands when the cycle opens.
    ///
    /// An opcode fetch reports once, as a fetch, covering the refresh half as well.
    pub request: BusRequest,
    /// How long the cycle runs, counting any T-states the host asked to have inserted.
    pub t_states: u32,
}

/// The machine the core sits in: its memory, its ports, and the signals reaching the CPU.
///
/// Only the four transfer methods are required. The rest cover things many machines don't have —
/// wait states, interrupt vectors, per-edge observation, telling an opcode fetch from a data read —
/// and default to a machine without them.
///
/// # When a transfer happens
///
/// Every transfer carries `at`: how far into its machine cycle the transfer **completes on the
/// bus**, counted in whole T-states from the cycle opening — the same opening [`Host::wait_states`]
/// is asked at. A machine that timestamps memory activity adds it to the time at which it saw that
/// cycle open.
///
/// | cycle | `at` |
/// |---|---|
/// | opcode fetch | `2 + waits` |
/// | memory read | `3 + waits` |
/// | memory write | `3 + waits` |
/// | port read | `4 + waits` |
/// | port write | `4 + waits` |
///
/// `waits` is what [`Host::wait_states`] returned for the cycle. Two things about this table are
/// easy to assume wrongly. **A port transfer is one T-state later than a memory transfer**, because
/// a port cycle carries an extra wait of its own. And **an opcode fetch completes a T-state earlier
/// than a data read**, because the processor takes the opcode at the end of `T2` so it can decode
/// during the refresh half.
///
/// This is a property of the cycle, not a report of when the core happened to call. The core may
/// read a byte earlier than `at` and hold it, which is an implementation detail; `at` is when the
/// hardware would have completed the transfer.
pub trait Host {
    /// Reads a byte of memory.
    ///
    /// Every read that isn't an opcode fetch comes through here — operands, displacements, the
    /// halves of a returned address. [`Host::fetch`] takes the fetches.
    ///
    /// `at` is the transfer's position within its machine cycle, in T-states — see the trait's
    /// documentation, which gives the value for every kind of cycle.
    fn read(&mut self, address: u16, at: u32) -> u8;

    /// Reads the opcode byte of an instruction, during the `M1` cycle.
    ///
    /// This is the one read the processor makes with `M1` asserted, so a machine that pages on an
    /// instruction, watches for an opcode, or breaks on an entry address wants it separated from
    /// ordinary reads. Each prefix byte of a prefixed instruction is its own fetch, because the
    /// hardware gives each its own `M1` cycle.
    ///
    /// The byte an interrupting device supplies is not a fetch, even though it arrives during an
    /// `M1` cycle — that comes from [`Host::interrupt_vector`].
    ///
    /// The default treats it as an ordinary read, which is what a machine that doesn't care about
    /// the distinction wants.
    fn fetch(&mut self, address: u16, at: u32) -> u8 {
        self.read(address, at)
    }

    /// Writes a byte of memory.
    ///
    /// `at` is the transfer's position within its machine cycle, in T-states — see the trait's
    /// documentation.
    fn write(&mut self, address: u16, value: u8, at: u32);

    /// Reads a byte from a port.
    ///
    /// `port` is the **whole sixteen-bit address bus**, not an eight-bit port number. `IN r,(C)`
    /// puts `B` in the high byte and `IN A,(n)` puts the accumulator there, so a host that decodes
    /// on the low byte alone will answer some reads from the wrong device.
    ///
    /// `at` is the transfer's position within its machine cycle, in T-states — see the trait's
    /// documentation, and note a port transfer lands a T-state later than a memory one.
    fn input(&mut self, port: u16, at: u32) -> u8;

    /// Writes a byte to a port.
    ///
    /// `port` is the whole sixteen-bit address bus, as for [`Host::input`].
    fn output(&mut self, port: u16, value: u8, at: u32);

    /// How many T-states to stall the core before this bus request completes. This models the
    /// `WAIT` line.
    ///
    /// Asked **exactly once per machine cycle**, as the cycle opens and before anything in it
    /// happens, at the T-state where the hardware samples `WAIT`. That makes it the place to act
    /// on a cycle before it runs — paging on a fetch address, arming or disarming on one — as well
    /// as to stall. [`Host::bus_cycle`] reports the same cycles in the same order once each has
    /// run, so between them a host has both a before and an after for every cycle.
    ///
    /// The default returns zero: a machine that never stalls the processor.
    fn wait_states(&mut self, request: &BusRequest) -> u32 {
        let _ = request;
        0
    }

    /// Called once for each machine cycle that has run, with what it did and how long it took.
    ///
    /// This is the granularity a machine clocked by the bus wants: one call per cycle, carrying
    /// the length, so a peripheral driven by the processor clock advances the right number of
    /// steps. Use it in preference to counting [`Host::bus_edge`] calls, which fire once per
    /// T-state and would drive such a peripheral several times too fast.
    ///
    /// To act on a cycle *before* it runs — paging on a fetch address, say — use
    /// [`Host::wait_states`], which is asked as each cycle opens. The default ignores this.
    fn bus_cycle(&mut self, cycle: &BusCycle) {
        let _ = cycle;
    }

    /// Called on every clock edge with what the core is doing, transfer or not.
    ///
    /// Use this for anything that has to watch the bus continuously, rather than only when the core
    /// reads or writes. The default ignores it.
    ///
    /// **Only a core driven by [`crate::Cpu::tick`] or [`crate::Cpu::run_cycle`] reports edges.** [`crate::Cpu::step`]
    /// runs an instruction at a time and has none to report, so this is never called for it. A host
    /// that needs to watch the bus and to step by instruction should work from
    /// [`Host::bus_cycle`], which describes the same activity a cycle at a time and is reported
    /// whichever way the core is driven.
    fn bus_edge(&mut self, request: &BusRequest) {
        let _ = request;
    }

    /// Supplies the byte an interrupting device places on the data bus during an interrupt
    /// acknowledge cycle.
    ///
    /// Mode 0 executes it as an instruction, mode 2 uses it to index the vector table, mode 1
    /// ignores it. The default is what an undriven bus reads as.
    fn interrupt_vector(&mut self) -> u8 {
        0xFF
    }

    /// Reports that `RETI` has executed.
    ///
    /// A daisy chain of interrupting devices uses this to release the highest-priority request it
    /// holds, so a lower one can be taken. `RETN` doesn't report here — it returns from a
    /// non-maskable interrupt, which isn't part of the chain. The default ignores it.
    fn return_from_interrupt(&mut self) {}

    /// Reports a Z80N instruction to the machine as the core executes it.
    ///
    /// All of them are reported, not just the ones whose effect reaches past the processor, because
    /// a machine watching the extended bus sees them all. `data` is the operand the instruction
    /// assembled, where it has one. Each is reported once, on the machine cycle that names it.
    ///
    /// To answer the processor, use the other methods — an instruction that reads a value still
    /// does an ordinary bus cycle to fetch it. The default ignores the report.
    ///
    /// The four commands reporting a non-maskable interrupt being taken or returned from arrive
    /// **before** the transfer of their machine cycle, so a host can answer that transfer. The rest
    /// arrive at the end of theirs, once the operand is assembled.
    fn z80n_command(&mut self, command: Z80nCommand, data: u16) {
        let _ = (command, data);
    }
}