trapezoid_core/cpu/
register.rs

1use std::fmt;
2
3#[derive(Copy, Clone, Debug, PartialEq)]
4#[repr(u8)]
5pub enum RegisterType {
6    Zero = 0,
7    At,
8    V0,
9    V1,
10    A0,
11    A1,
12    A2,
13    A3,
14    T0,
15    T1,
16    T2,
17    T3,
18    T4,
19    T5,
20    T6,
21    T7,
22    S0,
23    S1,
24    S2,
25    S3,
26    S4,
27    S5,
28    S6,
29    S7,
30    T8,
31    T9,
32    K0,
33    K1,
34    Gp,
35    Sp,
36    Fp,
37    Ra,
38    Pc,
39    Hi,
40    Lo,
41}
42
43const REG_TYPES: [RegisterType; 35] = [
44    RegisterType::Zero,
45    RegisterType::At,
46    RegisterType::V0,
47    RegisterType::V1,
48    RegisterType::A0,
49    RegisterType::A1,
50    RegisterType::A2,
51    RegisterType::A3,
52    RegisterType::T0,
53    RegisterType::T1,
54    RegisterType::T2,
55    RegisterType::T3,
56    RegisterType::T4,
57    RegisterType::T5,
58    RegisterType::T6,
59    RegisterType::T7,
60    RegisterType::S0,
61    RegisterType::S1,
62    RegisterType::S2,
63    RegisterType::S3,
64    RegisterType::S4,
65    RegisterType::S5,
66    RegisterType::S6,
67    RegisterType::S7,
68    RegisterType::T8,
69    RegisterType::T9,
70    RegisterType::K0,
71    RegisterType::K1,
72    RegisterType::Gp,
73    RegisterType::Sp,
74    RegisterType::Fp,
75    RegisterType::Ra,
76    RegisterType::Pc,
77    RegisterType::Hi,
78    RegisterType::Lo,
79];
80
81pub const ALL_REG_NAMES: [&str; 35] = [
82    "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", "t0", "t1", "t2", "t3", "t4", "t5", "t6",
83    "t7", "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "t8", "t9", "k0", "k1", "gp", "sp", "fp",
84    "ra", "pc", "hi", "lo",
85];
86
87impl From<u8> for RegisterType {
88    fn from(value: u8) -> Self {
89        REG_TYPES[value as usize]
90    }
91}
92
93impl fmt::Display for RegisterType {
94    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
95        f.write_str(ALL_REG_NAMES[*self as usize])
96    }
97}
98
99pub struct Registers {
100    pub(crate) general_regs: [u32; 32],
101    pub(crate) pc: u32,
102    pub(crate) hi: u32,
103    pub(crate) lo: u32,
104
105    /// load delay slots
106    /// When executing a `load` instruction, the data will be here during the execution
107    load_delay_slot_running: Option<(u8, u32)>,
108    /// When the instruction is done, the slot is moved here
109    /// which is committed to the next cycle
110    load_delay_slot_committing: Option<(u8, u32)>,
111}
112
113impl Registers {
114    pub(crate) fn new() -> Self {
115        Self {
116            general_regs: [0; 32],
117            load_delay_slot_running: None,
118            load_delay_slot_committing: None,
119
120            pc: 0xBFC00000,
121            hi: 0,
122            lo: 0,
123        }
124    }
125
126    #[inline]
127    pub fn read(&self, ty: RegisterType) -> u32 {
128        match ty {
129            RegisterType::Zero => 0,
130            RegisterType::Pc => self.pc,
131            RegisterType::Hi => self.hi,
132            RegisterType::Lo => self.lo,
133            _ => self.read_general(ty as u8),
134        }
135    }
136
137    #[inline]
138    pub fn write(&mut self, ty: RegisterType, data: u32) {
139        match ty {
140            RegisterType::Zero => {}
141            RegisterType::Pc => self.pc = data,
142            RegisterType::Hi => self.hi = data,
143            RegisterType::Lo => self.lo = data,
144            _ => self.write_general(ty as u8, data),
145        }
146    }
147
148    #[inline]
149    pub(crate) fn read_general(&self, idx: u8) -> u32 {
150        assert!(idx < 32);
151        if let Some((i, _)) = self.load_delay_slot_committing {
152            if idx == i {
153                log::warn!(
154                    "Reg `{}` is still in the load delay slot, reading old value, could be a bug",
155                    REG_TYPES[idx as usize]
156                );
157            }
158        }
159        self.general_regs[idx as usize]
160    }
161
162    /// Used by Lwl and Lwr to accumulate the delay but work correctly
163    /// for same register access
164    #[inline]
165    pub(crate) fn read_general_latest(&self, idx: u8) -> u32 {
166        assert!(idx < 32);
167        if let Some((i, d)) = self.load_delay_slot_committing {
168            if idx == i {
169                return d;
170            }
171        }
172        self.general_regs[idx as usize]
173    }
174
175    #[inline]
176    pub(crate) fn write_general(&mut self, idx: u8, data: u32) {
177        assert!(idx < 32);
178        self.general_regs[idx as usize] = data;
179        self.general_regs[0] = 0;
180
181        // cancel the load, otherwise it will overwrite what we are writing now
182        // and we don't want that
183        if let Some((i, _)) = self.load_delay_slot_committing {
184            if i == idx {
185                self.load_delay_slot_committing = None;
186            }
187        }
188    }
189
190    #[inline]
191    pub(crate) fn write_delayed(&mut self, idx: u8, data: u32) {
192        assert!(idx < 32);
193        assert!(self.load_delay_slot_running.is_none());
194        // if we are about to commit the same register, ignore that
195        if let Some((i, _)) = self.load_delay_slot_committing {
196            if i == idx {
197                self.load_delay_slot_committing = None;
198            }
199        }
200        self.load_delay_slot_running = Some((idx, data));
201    }
202
203    #[inline]
204    pub(crate) fn handle_delayed_load(&mut self) {
205        if let Some((idx, data)) = self.load_delay_slot_committing.take() {
206            self.write_general(idx, data);
207        }
208        self.load_delay_slot_committing = self.load_delay_slot_running.take();
209    }
210
211    #[inline]
212    pub(crate) fn flush_delayed_load(&mut self) {
213        self.handle_delayed_load();
214        self.handle_delayed_load();
215    }
216
217    // special function, since the cpu is writing to ra directly on function calls
218    // and returns
219    #[inline]
220    pub(crate) fn write_ra(&mut self, data: u32) {
221        // go through the normal write function to handle the load delay slot
222        self.write_general(RegisterType::Ra as u8, data);
223    }
224}
225
226impl std::fmt::Debug for Registers {
227    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
228        writeln!(f, "Registers:")?;
229
230        // print PC and AT
231        writeln!(
232            f,
233            "pc: {:08X}\t{:>4}: {:08X}",
234            self.pc,
235            RegisterType::from(1),
236            self.general_regs[1]
237        )?;
238        // HI and LO
239        writeln!(f, "hi: {:08X}\tlo: {:08X}", self.hi, self.lo)?;
240
241        // print all other registers except the last two
242        for i in 2..32 / 2 {
243            writeln!(
244                f,
245                "{:>4}: {:08X}\t{:>4}: {:08X}",
246                RegisterType::from(i),
247                self.general_regs[i as usize],
248                // -2 offset because we are not printing 0 (ZERO) and 1 (AT)
249                RegisterType::from(i + 32 / 2 - 2),
250                self.general_regs[(i + 32 / 2 - 2) as usize]
251            )?;
252        }
253        // print the last two registers
254        writeln!(
255            f,
256            "{:>4}: {:08X}\t{:>4}: {:08X}",
257            RegisterType::from(30),
258            self.general_regs[30],
259            RegisterType::from(31),
260            self.general_regs[31]
261        )
262    }
263}