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 operations the extended instruction set adds.
//!
//! Each is a pure function of the registers it reads, returning the value written back. They sit
//! apart from the sequencer so each one is testable on its own, and because the machine cycle that
//! runs them is unremarkable: a single fetch, with the work done inside it.

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub(crate) enum Barrel {
    Left,
    ArithmeticRight,
    LogicalRight,
    FillRight,
    Rotate,
}

pub(crate) fn swap_nibbles(accumulator: u8) -> u8 {
    accumulator.rotate_left(4)
}

pub(crate) fn mirror(accumulator: u8) -> u8 {
    accumulator.reverse_bits()
}

pub(crate) fn multiply(de: u16) -> u16 {
    let [high, low] = de.to_be_bytes();
    u16::from(high) * u16::from(low)
}

pub(crate) fn add_accumulator(pair: u16, accumulator: u8) -> u16 {
    pair.wrapping_add(u16::from(accumulator))
}

pub(crate) fn add_immediate(pair: u16, immediate: u16) -> u16 {
    pair.wrapping_add(immediate)
}

pub(crate) fn bit_from_low_three(e: u8) -> u8 {
    0x80 >> (e & 0x07)
}

pub(crate) fn pixel_address(de: u16) -> u16 {
    let [d, e] = de.to_be_bytes();
    let high = 0b0100_0000 | ((d & 0b1100_0000) >> 3) | (d & 0b0000_0111);
    let low = ((d & 0b0011_1000) << 2) | ((e & 0b1111_1000) >> 3);
    u16::from_be_bytes([high, low])
}

pub(crate) fn pixel_down(hl: u16) -> u16 {
    let [h, l] = hl.to_be_bytes();
    let counted = ((h & 0b0001_1000) << 3) | ((l & 0b1110_0000) >> 2) | (h & 0b0000_0111);
    let stepped = counted.wrapping_add(1);

    let high = (h & 0b1110_0000) | ((stepped & 0b1100_0000) >> 3) | (stepped & 0b0000_0111);
    let low = ((stepped & 0b0011_1000) << 2) | (l & 0b0001_1111);
    u16::from_be_bytes([high, low])
}

pub(crate) fn barrel(de: u16, count: u8, kind: Barrel) -> u16 {
    let places = u32::from(count & 0x1F);

    match kind {
        Barrel::Rotate => de.rotate_left(places % 16),
        Barrel::Left => de.checked_shl(places).unwrap_or(0),
        Barrel::LogicalRight => de.checked_shr(places).unwrap_or(0),
        Barrel::ArithmeticRight => {
            let signed = de.cast_signed();
            if places >= 16 {
                if signed < 0 { 0xFFFF } else { 0 }
            } else {
                (signed >> places).cast_unsigned()
            }
        }
        Barrel::FillRight => {
            if places >= 16 {
                0xFFFF
            } else if places == 0 {
                de
            } else {
                (de >> places) | !(u16::MAX >> places)
            }
        }
    }
}

#[cfg(test)]
#[path = "extended_tests.rs"]
mod tests;