hg80 1.0.0

Z80 and Z80N CPU core, stepped one clock edge at a time
Documentation
use super::{Cpu, Wide, high_byte, low_byte};
use crate::Z80nCommand;
use crate::mcode::IndexState;
use std::prelude::rust_2024::*;

mod blocks;
mod bus;
mod flags;
mod instructions;
mod interrupts;
mod registers;
mod z80n;

fn loaded() -> Cpu {
    let mut cpu = Cpu::new();
    let registers = cpu.registers_mut();
    registers.af = 0xA1F2;
    registers.bc = 0xB0C0;
    registers.de = 0xD0E0;
    registers.hl = 0x4050;
    registers.ix = 0x1122;
    registers.iy = 0x3344;
    registers.sp = 0x8090;
    registers.pc = 0x1234;
    cpu.data_latch = 0x5A;
    cpu
}

struct Ram {
    bytes: Box<[u8; 0x10000]>,
    ports: Box<[u8; 0x10000]>,
    written: Vec<(u16, u8)>,
    seen: Vec<crate::BusRequest>,
    commands: Vec<(crate::Z80nCommand, u16)>,
    returns: usize,
}

impl Ram {
    fn with(program: &[u8]) -> Self {
        let mut bytes = vec![0u8; 0x10000].into_boxed_slice();
        bytes[..program.len()].copy_from_slice(program);
        Self {
            bytes: bytes.try_into().unwrap(),
            ports: vec![0xFF; 0x10000].into_boxed_slice().try_into().unwrap(),
            written: Vec::new(),
            seen: Vec::new(),
            commands: Vec::new(),
            returns: 0,
        }
    }
}

impl crate::Host for Ram {
    fn read(&mut self, address: u16, _at: u32) -> u8 {
        self.bytes[address as usize]
    }
    fn write(&mut self, address: u16, value: u8, _at: u32) {
        self.bytes[address as usize] = value;
        self.written.push((address, value));
    }
    fn input(&mut self, port: u16, _at: u32) -> u8 {
        self.ports[port as usize]
    }
    fn output(&mut self, port: u16, value: u8, _at: u32) {
        self.ports[port as usize] = value;
    }
    fn z80n_command(&mut self, command: crate::Z80nCommand, data: u16) {
        self.commands.push((command, data));
    }
    fn return_from_interrupt(&mut self) {
        self.returns += 1;
    }
    fn bus_edge(&mut self, request: &crate::BusRequest) {
        if self.seen.last() != Some(request) {
            self.seen.push(*request);
        }
    }
}

fn run(program: &[u8], instructions: usize) -> (Cpu, Ram, u32) {
    run_from(program, |_| {}, instructions)
}

fn run_from(program: &[u8], setup: impl FnOnce(&mut Cpu), instructions: usize) -> (Cpu, Ram, u32) {
    let mut cpu = Cpu::new();
    cpu.reset();
    setup(&mut cpu);
    let mut ram = Ram::with(program);
    let mut total = 0;
    for _ in 0..instructions {
        total += cpu.step_by_edges(&mut ram);
    }
    (cpu, ram, total)
}

struct Stackless {
    bytes: Box<[u8; 0x10000]>,
    captured: Option<u16>,
    taking: Option<Z80nCommand>,
    supplying: Option<Z80nCommand>,
    writes: usize,
    reads_of_the_stack: usize,
}

impl Stackless {
    fn new(program: &[u8]) -> Self {
        let mut bytes = vec![0u8; 0x10000].into_boxed_slice();
        bytes[..program.len()].copy_from_slice(program);
        bytes[0x0066] = 0xED;
        bytes[0x0067] = 0x45;
        Self {
            bytes: bytes.try_into().unwrap(),
            captured: None,
            taking: None,
            supplying: None,
            writes: 0,
            reads_of_the_stack: 0,
        }
    }
}

impl crate::Host for Stackless {
    fn read(&mut self, address: u16, _at: u32) -> u8 {
        match self.supplying.take() {
            Some(Z80nCommand::ReturnFromNmiLow) => {
                self.reads_of_the_stack += 1;
                low_byte(self.captured.unwrap_or_default())
            }
            Some(Z80nCommand::ReturnFromNmiHigh) => {
                self.reads_of_the_stack += 1;
                let value = high_byte(self.captured.unwrap_or_default());
                self.captured = None;
                value
            }
            _ => self.bytes[address as usize],
        }
    }
    fn write(&mut self, address: u16, value: u8, _at: u32) {
        match self.taking.take() {
            Some(Z80nCommand::NmiAcknowledgeHigh) => {
                self.captured = Some(u16::from(value) << 8);
            }
            Some(Z80nCommand::NmiAcknowledgeLow) => {
                self.captured = Some(self.captured.unwrap_or_default() | u16::from(value));
            }
            _ => {
                self.bytes[address as usize] = value;
                self.writes += 1;
            }
        }
    }
    fn input(&mut self, _port: u16, _at: u32) -> u8 {
        0xFF
    }
    fn output(&mut self, _port: u16, _value: u8, _at: u32) {}
    fn z80n_command(&mut self, command: Z80nCommand, _data: u16) {
        match command {
            Z80nCommand::NmiAcknowledgeHigh | Z80nCommand::NmiAcknowledgeLow => {
                self.taking = Some(command);
            }
            Z80nCommand::ReturnFromNmiLow | Z80nCommand::ReturnFromNmiHigh
                if self.captured.is_some() =>
            {
                self.supplying = Some(command);
            }
            _ => {}
        }
    }
}

fn take_a_non_maskable_interrupt<H: crate::Host>(cpu: &mut Cpu, host: &mut H) -> u32 {
    cpu.request_nmi();
    let mut t_states = cpu.step_by_edges(host);
    t_states += cpu.step_by_edges(host);
    t_states
}

fn extended(program: &[u8], setup: impl FnOnce(&mut Cpu)) -> (Cpu, u32) {
    let mut cpu = Cpu::new();
    cpu.reset();
    cpu.set_z80n_enabled(true);
    setup(&mut cpu);
    let mut ram = Ram::with(program);
    let t = cpu.step_by_edges(&mut ram);
    (cpu, t)
}

fn extended_block(program: &[u8], setup: impl FnOnce(&mut Cpu), steps: usize) -> (Cpu, Ram, u32) {
    let mut cpu = Cpu::new();
    cpu.reset();
    cpu.set_z80n_enabled(true);
    setup(&mut cpu);
    let mut ram = Ram::with(program);
    let mut total = 0;
    for _ in 0..steps {
        total += cpu.step_by_edges(&mut ram);
    }
    (cpu, ram, total)
}

fn pattern(opcode: u8) -> Vec<u8> {
    let mut program = vec![0xED, opcode, 0x76];
    program.resize(0x40, 0);
    program[0x30] = 0x11;
    program[0x31] = 0x22;
    program
}

fn loaded_extended(opcode: u8, operands: &[u8]) -> (Cpu, Ram, u32) {
    let mut program = vec![0xED, opcode];
    program.extend_from_slice(operands);
    program.resize(0x80, 0);
    program[0x30] = 0x11;
    program[0x31] = 0x22;

    let mut cpu = Cpu::new();
    cpu.reset();
    cpu.set_z80n_enabled(true);
    {
        let registers = cpu.registers_mut();
        registers.af = 0x0A5A;
        registers.bc = 0x0401;
        registers.de = 0x0038;
        registers.hl = 0x0030;
        registers.sp = 0x0100;
    }
    let mut ram = Ram::with(&program);
    let t = cpu.step_by_edges(&mut ram);
    (cpu, ram, t)
}