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.

//! What the packed control fields of a decoded instruction mean.
//!
//! Three fields of the decode are small bit-packed words rather than single values, because that
//! is how the design carries them and the decode tables are kept writing the same numbers it does.
//! The layouts are here so that the sequencer reads them by name.

// The four-bit field that says whether a register pair is stepped, which one, and which way.
pub(crate) mod step {
    // Clear counts up; there is no separate bit for the direction.
    pub(crate) const DOWN: u8 = 0b1000;
    pub(crate) const ENABLED: u8 = 0b0100;
    pub(crate) const PAIR: u8 = 0b0011;
    // The stack pointer has no pair code of its own: it is the enable and both pair bits together,
    // which is why selecting it takes all three.
    pub(crate) const SELECTOR: u8 = ENABLED | PAIR;
    pub(crate) const STACK_POINTER: u8 = 0b0111;

    pub(crate) const fn enabled(field: u8) -> bool {
        field & ENABLED != 0
    }

    pub(crate) const fn counts_down(field: u8) -> bool {
        field & DOWN != 0
    }

    pub(crate) const fn is_stack_pointer(field: u8) -> bool {
        field & SELECTOR == STACK_POINTER
    }

    pub(crate) const fn is_register_pair(field: u8) -> bool {
        enabled(field) && field & PAIR != PAIR
    }

    pub(crate) const fn pair(field: u8) -> u8 {
        field & PAIR
    }

    pub(crate) const BC: u8 = 0b00;
    pub(crate) const DE: u8 = 0b01;
    // The index register stands in for it under a prefix.
    pub(crate) const HL: u8 = 0b10;
    pub(crate) const STACK: u8 = 0b11;

    pub(crate) const fn up(pair: u8) -> u8 {
        ENABLED | (pair & PAIR)
    }

    pub(crate) const fn down(pair: u8) -> u8 {
        DOWN | ENABLED | (pair & PAIR)
    }
}

// The four-bit code selecting what an operand bus reads.
//
// The low eight follow the Z80's own three-bit register encoding, with the code that names `(HL)`
// standing for the byte last latched instead. The rest are sources only the sequencer has.
#[allow(
    dead_code,
    reason = "the register encoding is a complete set; a gap would be a trap"
)]
pub(crate) mod bus {
    pub(crate) const B: u8 = 0b0000;
    pub(crate) const C: u8 = 0b0001;
    pub(crate) const D: u8 = 0b0010;
    pub(crate) const E: u8 = 0b0011;
    // Doubles as the index register's high half under a prefix.
    pub(crate) const H: u8 = 0b0100;
    // Doubles as the index register's low half under a prefix.
    pub(crate) const L: u8 = 0b0101;
    // The last code reachable through the Z80's own three-bit encoding; the rest are the
    // sequencer's own.
    pub(crate) const REGISTER_LAST: u8 = L;
    pub(crate) const DATA_LATCH: u8 = 0b0110;
    pub(crate) const ACCUMULATOR: u8 = 0b0111;
    pub(crate) const STACK_LOW: u8 = 0b1000;
    pub(crate) const STACK_HIGH: u8 = 0b1001;
    // The counting instructions add and subtract it.
    pub(crate) const ONE: u8 = 0b1010;
    pub(crate) const FLAGS: u8 = 0b1011;
    pub(crate) const PROGRAM_COUNTER_LOW: u8 = 0b1100;
    pub(crate) const PROGRAM_COUNTER_HIGH: u8 = 0b1101;
    pub(crate) const MASK: u8 = 0b1111;
    // The two-bit pair field's fourth value, which names `AF` in push and pop and the stack
    // pointer everywhere else.
    pub(crate) const FOURTH_PAIR: u8 = 0b11;
}

// The five-bit field naming where the result of a machine cycle is written back.
pub(crate) mod destination {
    // Clear when the cycle writes nothing back, which is not the same as writing zero.
    pub(crate) const ENABLED: u8 = 0b1_0000;
    // Uses the same codes as `super::bus`.
    pub(crate) const TARGET: u8 = 0b0_1111;
    // What the read-to-accumulator instructions select.
    pub(crate) const ACCUMULATOR: u8 = ENABLED | super::bus::ACCUMULATOR;
}

// Masks that are not part of a packed field but appear as bare numbers otherwise.
pub(crate) mod mask {
    // The bits of an opcode the ALU decodes, the rest naming the destination.
    pub(crate) const ALU_OPCODE: u8 = 0x3F;
    // The bit of the refresh counter the processor holds rather than counting.
    pub(crate) const REFRESH_HELD: u8 = 0x80;
    // The bits of the refresh counter that do count.
    pub(crate) const REFRESH_COUNTED: u8 = 0x7F;
    // The bits of an opcode naming which condition a conditional instruction tests.
    pub(crate) const CONDITION: u8 = 0b011;
    // The bits of a restart opcode giving its target address.
    pub(crate) const RESTART_TARGET: u8 = 0b0011_1000;
    // The bits of the source pointer the pattern-fill copy keeps.
    pub(crate) const PATTERN_BASE: u16 = 0xFFF8;
    // The bits of the destination that select a byte within the pattern.
    pub(crate) const PATTERN_OFFSET: u16 = 0x0007;
    // The bits of the program counter the port-driven jump keeps.
    pub(crate) const JUMP_REGION: u16 = 0xC000;
    // The bits of the program counter that jump replaces.
    pub(crate) const JUMP_TARGET: u16 = 0x3FC0;
    // How far the byte read is shifted to reach those bits.
    pub(crate) const JUMP_SHIFT: u32 = 6;
}