Skip to main content

k580_core/machine_cycle/
mod.rs

1//! Machine-cycle / T-phase layout per opcode for the schematic readout.
2//! Numbers come from the Intel 8080A Datasheet ("STATES" column and the
3//! "Machine Cycle" section). Conditional opcodes carry both taken and
4//! not-taken sequences; undocumented opcodes return an empty layout.
5
6mod tables;
7#[cfg(test)]
8mod tests;
9
10#[cfg(test)]
11pub(crate) use tables::kinds_for;
12pub use tables::{kind_at, layout_for};
13
14pub type MachineCycleLengths = &'static [u8];
15
16#[derive(Clone, Copy, Debug, PartialEq, Eq)]
17pub struct MachineCycleLayout {
18    pub taken: MachineCycleLengths,
19    pub not_taken: Option<MachineCycleLengths>,
20}
21
22impl MachineCycleLayout {
23    pub const fn fixed(cycles: MachineCycleLengths) -> Self {
24        Self {
25            taken: cycles,
26            not_taken: None,
27        }
28    }
29
30    pub(crate) const fn branch(taken: MachineCycleLengths, not_taken: MachineCycleLengths) -> Self {
31        Self {
32            taken,
33            not_taken: Some(not_taken),
34        }
35    }
36
37    pub fn total_t_states(self, branch_taken: bool) -> u8 {
38        let cycles = if branch_taken {
39            self.taken
40        } else {
41            self.not_taken.unwrap_or(self.taken)
42        };
43        let mut sum = 0u8;
44        let mut i = 0;
45        while i < cycles.len() {
46            sum += cycles[i];
47            i += 1;
48        }
49        sum
50    }
51}
52
53#[derive(Clone, Copy, Debug, PartialEq, Eq)]
54pub struct MachineCyclePosition {
55    pub m_cycle: u8,
56    pub t_in_cycle: u8,
57    pub m_cycle_length: u8,
58}
59
60pub fn position_for(
61    layout: MachineCycleLayout,
62    branch_taken: bool,
63    linear_phase: u8,
64) -> Option<MachineCyclePosition> {
65    let cycles = if branch_taken {
66        layout.taken
67    } else {
68        layout.not_taken.unwrap_or(layout.taken)
69    };
70    if cycles.is_empty() {
71        return None;
72    }
73    let mut consumed = 0u8;
74    for (idx, &length) in cycles.iter().enumerate() {
75        if linear_phase < consumed + length {
76            return Some(MachineCyclePosition {
77                m_cycle: (idx as u8) + 1,
78                t_in_cycle: linear_phase - consumed + 1,
79                m_cycle_length: length,
80            });
81        }
82        consumed += length;
83    }
84    None
85}
86
87/// 8080 M-cycle kind. `status_byte()` returns the byte the chip latches on
88/// T1 of each M-cycle (Intel 8080A datasheet, "Status Information").
89/// Bits: D7 MEMR, D6 INP, D5 M1, D4 OUT, D3 HLTA, D2 STACK, D1 WO, D0 INTA.
90/// `WO` is inverted relative to read/write (1 = read/input).
91#[derive(Clone, Copy, Debug, PartialEq, Eq)]
92pub enum MachineCycleKind {
93    M1Fetch,
94    MemoryRead,
95    MemoryWrite,
96    StackRead,
97    StackWrite,
98    IoRead,
99    IoWrite,
100    InterruptAck,
101    HaltAck,
102    /// Internal idle cycle (DAD, INX/DCX): bus is not driven.
103    BusIdle,
104}
105
106impl MachineCycleKind {
107    pub fn status_byte(self) -> u8 {
108        match self {
109            Self::M1Fetch => 0b1010_0010,
110            Self::MemoryRead => 0b1000_0010,
111            Self::MemoryWrite => 0b0000_0000,
112            Self::StackRead => 0b1000_0110,
113            Self::StackWrite => 0b0000_0100,
114            Self::IoRead => 0b0100_0010,
115            Self::IoWrite => 0b0001_0000,
116            Self::InterruptAck => 0b0010_0011,
117            Self::HaltAck => 0b1000_1010,
118            Self::BusIdle => 0,
119        }
120    }
121
122    pub fn label_ru(self) -> &'static str {
123        match self {
124            Self::M1Fetch => "Загрузка опкода",
125            Self::MemoryRead => "Чтение памяти",
126            Self::MemoryWrite => "Запись в память",
127            Self::StackRead => "Чтение из стека",
128            Self::StackWrite => "Запись в стек",
129            Self::IoRead => "Чтение из порта",
130            Self::IoWrite => "Запись в порт",
131            Self::InterruptAck => "Подтв. прерывания",
132            Self::HaltAck => "Подтв. останова",
133            Self::BusIdle => "Внутренний цикл",
134        }
135    }
136
137    pub fn label_en(self) -> &'static str {
138        match self {
139            Self::M1Fetch => "Opcode fetch",
140            Self::MemoryRead => "Memory read",
141            Self::MemoryWrite => "Memory write",
142            Self::StackRead => "Stack read",
143            Self::StackWrite => "Stack write",
144            Self::IoRead => "Port read",
145            Self::IoWrite => "Port write",
146            Self::InterruptAck => "Interrupt ack",
147            Self::HaltAck => "Halt ack",
148            Self::BusIdle => "Internal cycle",
149        }
150    }
151}
152
153pub type MachineCycleKinds = &'static [MachineCycleKind];