#![allow(
dead_code,
reason = "the instruction-stepped layer is what consumes this; until then only its tests do"
)]
use crate::mcode::{self, Context, IndexState, InstructionSet};
use crate::types::MachineCycle;
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub(crate) enum CycleKind {
#[default]
Fetch,
MemoryRead,
MemoryWrite,
PortRead,
PortWrite,
Internal,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub(crate) struct PlannedCycle {
pub kind: CycleKind,
pub t_states: u8,
}
pub(crate) const MOST_CYCLES: usize = 7;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub(crate) struct Plan {
cycles: [PlannedCycle; MOST_CYCLES],
count: u8,
pub varies: bool,
}
impl Plan {
pub(crate) fn cycles(&self) -> &[PlannedCycle] {
&self.cycles[..self.count as usize]
}
pub(crate) fn t_states(&self) -> u32 {
self.cycles().iter().map(|c| u32::from(c.t_states)).sum()
}
}
fn context(set: InstructionSet, ir: u8, cycle: MachineCycle, seed: u8, z80n: bool) -> Context {
Context {
ir,
instruction_set: set,
machine_cycle: cycle,
flags: seed,
nmi_cycle: false,
interrupt_cycle: false,
index_state: IndexState::None,
accumulator: seed,
data: seed,
z80n_enabled: z80n,
}
}
fn kind_of(decoded: &mcode::Decoded, cycle: MachineCycle) -> CycleKind {
if matches!(cycle, MachineCycle::M1) {
return CycleKind::Fetch;
}
if decoded.no_read() && !decoded.write() {
return CycleKind::Internal;
}
match (decoded.write(), decoded.iorq()) {
(true, true) => CycleKind::PortWrite,
(true, false) => CycleKind::MemoryWrite,
(false, true) => CycleKind::PortRead,
(false, false) => CycleKind::MemoryRead,
}
}
fn shape(
set: InstructionSet,
ir: u8,
seed: u8,
z80n: bool,
) -> ([PlannedCycle; MOST_CYCLES], u8, bool) {
let mut cycles = [PlannedCycle::default(); MOST_CYCLES];
let mut count = 0u8;
let mut repeats = false;
for (number, slot) in (1u8..).zip(cycles.iter_mut()) {
let cycle = MachineCycle::from_number(number);
let decoded = mcode::decode(context(set, ir, cycle, seed, z80n));
let kind = kind_of(&decoded, cycle);
let port = matches!(kind, CycleKind::PortRead | CycleKind::PortWrite);
*slot = PlannedCycle {
kind,
t_states: decoded.t_states + u8::from(port),
};
repeats |= decoded.i_bt() || decoded.i_bc() || decoded.i_btr();
count = number;
if count >= decoded.machine_cycles {
break;
}
}
(cycles, count, repeats)
}
pub(crate) fn plan(set: InstructionSet, ir: u8, z80n: bool) -> Plan {
let (cycles, count, repeats) = shape(set, ir, 0x00, z80n);
let varies = repeats
|| [0xFFu8, 0x55, 0xAA]
.into_iter()
.any(|seed| shape(set, ir, seed, z80n) != (cycles, count, repeats));
Plan {
cycles,
count,
varies,
}
}
#[cfg(test)]
#[path = "plan_tests.rs"]
mod tests;