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 bit, shift and rotate table behind the `CB` prefix.

use super::{AddressSource, AluOp, Context, Decoded, IndexState, source};
use crate::control::bus;

fn read_modify_write(out: &mut Decoded, cycle: u8, op: AluOp) {
    out.machine_cycles = 3;
    match cycle {
        1 | 7 => out.set_addr_to = AddressSource::IndexOrHl,
        2 => {
            out.alu_op = op;
            out.set_read_to_reg();
            out.set_save_alu();
            out.set_addr_to = AddressSource::IndexOrHl;
            out.t_states = 4;
        }
        3 => out.set_write(),
        _ => {}
    }
}

fn read_and_test(out: &mut Decoded, cycle: u8) {
    out.machine_cycles = 2;
    match cycle {
        1 | 7 => out.set_addr_to = AddressSource::IndexOrHl,
        2 => {
            out.alu_op = AluOp::Bit;
            out.t_states = 4;
        }
        _ => {}
    }
}

pub(super) fn bit(context: Context, out: &mut Decoded) {
    let ir = context.ir;
    let sss = source(ir);
    let cycle = context.machine_cycle.number();
    let indexed = !matches!(context.index_state, IndexState::None);

    out.set_bus_a_to = sss;
    out.set_bus_b_to = sss;

    match ir {
        0x00..=0x3F if sss != bus::DATA_LATCH => {
            if indexed {
                out.set_xy_bit_undoc();
                read_modify_write(out, cycle, AluOp::Rotate);
            } else if cycle == 1 || cycle == 7 {
                out.alu_op = AluOp::Rotate;
                out.set_read_to_reg();
                out.set_save_alu();
            }
        }
        0x06 | 0x0E | 0x16 | 0x1E | 0x26 | 0x2E | 0x36 | 0x3E => {
            read_modify_write(out, cycle, AluOp::Rotate);
        }
        0x40..=0x7F if sss != bus::DATA_LATCH => {
            if indexed {
                out.set_xy_bit_undoc();
                read_and_test(out, cycle);
            } else if cycle == 1 || cycle == 7 {
                out.set_bus_b_to = sss;
                out.alu_op = AluOp::Bit;
            }
        }
        0x46 | 0x4E | 0x56 | 0x5E | 0x66 | 0x6E | 0x76 | 0x7E => read_and_test(out, cycle),
        0xC0..=0xFF if sss != bus::DATA_LATCH => bit_write_back(context, out, AluOp::Set),
        0xC6 | 0xCE | 0xD6 | 0xDE | 0xE6 | 0xEE | 0xF6 | 0xFE => {
            read_modify_write(out, cycle, AluOp::Set);
        }
        0x80..=0xBF if sss != bus::DATA_LATCH => bit_write_back(context, out, AluOp::Res),
        0x86 | 0x8E | 0x96 | 0x9E | 0xA6 | 0xAE | 0xB6 | 0xBE => {
            read_modify_write(out, cycle, AluOp::Res);
        }
        _ => {}
    }
}

fn bit_write_back(context: Context, out: &mut Decoded, op: AluOp) {
    let cycle = context.machine_cycle.number();

    if matches!(context.index_state, IndexState::None) {
        if cycle == 1 {
            out.alu_op = op;
            out.set_read_to_reg();
            out.set_save_alu();
        } else {
            out.machine_cycles = 4;
            match cycle {
                2 => {
                    out.set_bus_b_to = bus::DATA_LATCH;
                    out.alu_op = op;
                    out.set_read_to_reg();
                    out.set_save_alu();
                    out.set_addr_to = AddressSource::IndexOrHl;
                    out.t_states = 4;
                }
                3 | 7 => out.set_addr_to = AddressSource::IndexOrHl,
                4 => out.set_write(),
                _ => {}
            }
        }
    } else {
        out.set_xy_bit_undoc();
        read_modify_write(out, cycle, op);
    }
}