trapezoid_core/cpu/
instruction.rs

1use std::fmt;
2
3use super::instructions_table::{PRIMARY_OPCODES, SECONDARY_OPCODES};
4use super::RegisterType;
5
6#[derive(Copy, Clone, Debug, PartialEq, Eq)]
7pub enum Opcode {
8    #[doc(hidden)]
9    /// This is a special opcode used when parsing the instruction,
10    /// This is not a valid instruction and not avilable in any documentation,
11    /// but is used as a middle step to parse the instruction if the instruction
12    /// has a secondary opcode metadata
13    SecondaryOpcode,
14    Invalid,
15
16    Nop,
17
18    // (u) means unsigned, (t) means overflow trap, (i) means immediate
19    Lb,
20    Lbu,
21    Lh,
22    Lhu,
23    Lw,
24
25    Lwl,
26    Lwr,
27
28    Sb,
29    Sh,
30    Sw,
31
32    Swl,
33    Swr,
34
35    Slt,
36    Sltu,
37    Slti,
38    Sltiu,
39
40    Addu,
41    Add,
42    Subu,
43    Sub,
44    Addiu,
45    Addi,
46
47    And,
48    Or,
49    Xor,
50    Nor,
51    Andi,
52    Ori,
53    Xori,
54
55    Sllv,
56    Srlv,
57    Srav,
58    Sll,
59    Srl,
60    Sra,
61    Lui,
62
63    Mult,
64    Multu,
65    Div,
66    Divu,
67
68    Mfhi,
69    Mthi,
70    Mflo,
71    Mtlo,
72
73    J,
74    Jal,
75
76    Jr,
77    Jalr,
78
79    Beq,
80    Bne,
81    Bgtz,
82    Blez,
83    // depending on the value of `rt` it will execute:
84    //  rt   | instr
85    // ---------------
86    //  0x00 | Bltz
87    //  0x01 | Bgez
88    //  0x10 | Bltzal
89    //  0x11 | Bgezal
90    Bcondz,
91    Bltz,
92    Bgez,
93    Bltzal,
94    Bgezal,
95
96    Syscall,
97    Break,
98
99    Cop(u8),
100    Mfc(u8),
101    Cfc(u8),
102    Mtc(u8),
103    Ctc(u8),
104
105    Bcf(u8),
106    Bct(u8),
107
108    // only for COP0
109    Rfe,
110
111    Lwc(u8),
112    Swc(u8),
113}
114
115/// MIPS Instruction representation.
116///
117/// Do note that the fields may not always make sense as there are fields used
118/// by some instructions and some not used.
119///
120/// For example, the instruction `Lw` may have the value for `rd` set to
121/// `RegisterType::A1`, or `RegisterType::T1` and it shouldn't matter
122/// since that instruction only uses `rs` and `rt`. So keep that in mind when
123/// accessing the fields.
124#[derive(Debug, Clone)]
125pub struct Instruction {
126    pub pc: u32,
127
128    pub opcode: Opcode,
129
130    pub instruction: u32,
131    pub(crate) rd_raw: u8,
132    pub(crate) rt_raw: u8,
133    pub(crate) rs_raw: u8,
134}
135
136impl Instruction {
137    pub fn from_u32(instruction: u32, pc: u32) -> Self {
138        if instruction == 0 {
139            return Self {
140                pc,
141
142                opcode: Opcode::Nop,
143                instruction,
144                rd_raw: 0,
145                rt_raw: 0,
146                rs_raw: 0,
147            };
148        }
149
150        let primary_identifier = (instruction >> 26) as u8;
151        let rd_raw = (instruction >> 11) as u8 & 0x1F;
152        let rt_raw = (instruction >> 16) as u8 & 0x1F;
153        let rs_raw = (instruction >> 21) as u8 & 0x1F;
154
155        let opcode = Self::get_opcode_from_primary(primary_identifier);
156
157        let opcode = match opcode {
158            Opcode::SecondaryOpcode => {
159                let secondary_identifier = instruction as u8 & 0x3F;
160                Self::get_opcode_from_secondary(secondary_identifier)
161            }
162            Opcode::Cop(n) => {
163                let secondary_identifier = instruction as u8 & 0x3F;
164                Self::get_cop_opcode(n, secondary_identifier, rt_raw, rs_raw)
165            }
166            Opcode::Bcondz => Self::get_bcondz_opcode(rt_raw),
167            _ => opcode,
168        };
169
170        Self {
171            pc,
172
173            opcode,
174            instruction,
175            rd_raw,
176            rt_raw,
177            rs_raw,
178        }
179    }
180
181    pub fn is_branch(&self) -> bool {
182        matches!(
183            self.opcode,
184            Opcode::J
185                | Opcode::Jal
186                | Opcode::Jalr
187                | Opcode::Jr
188                | Opcode::Beq
189                | Opcode::Bne
190                | Opcode::Bgtz
191                | Opcode::Blez
192                | Opcode::Bltz
193                | Opcode::Bgez
194                | Opcode::Bltzal
195                | Opcode::Bgezal
196        )
197    }
198
199    #[inline]
200    pub fn rd(&self) -> RegisterType {
201        RegisterType::from(self.rd_raw)
202    }
203
204    #[inline]
205    pub fn rt(&self) -> RegisterType {
206        RegisterType::from(self.rt_raw)
207    }
208
209    #[inline]
210    pub fn rs(&self) -> RegisterType {
211        RegisterType::from(self.rs_raw)
212    }
213
214    #[inline]
215    pub const fn imm5(&self) -> u8 {
216        (self.instruction >> 6) as u8 & 0x1F
217    }
218
219    #[inline]
220    pub const fn imm16(&self) -> u16 {
221        self.instruction as u16
222    }
223
224    #[inline]
225    pub const fn imm25(&self) -> u32 {
226        self.instruction & 0x1FFFFFF
227    }
228
229    #[inline]
230    pub const fn imm26(&self) -> u32 {
231        self.instruction & 0x3FFFFFF
232    }
233}
234
235impl Instruction {
236    fn get_opcode_from_primary(primary: u8) -> Opcode {
237        PRIMARY_OPCODES[primary as usize & 0x3F]
238    }
239
240    fn get_opcode_from_secondary(secondary: u8) -> Opcode {
241        SECONDARY_OPCODES[secondary as usize & 0x3F]
242    }
243
244    fn get_bcondz_opcode(rt_raw: u8) -> Opcode {
245        match rt_raw {
246            0x10 => Opcode::Bltzal,
247            0x11 => Opcode::Bgezal,
248            x if x & 1 == 0 => Opcode::Bltz,
249            // x if x & 1 == 1
250            _ => Opcode::Bgez,
251        }
252    }
253
254    fn get_cop_opcode(cop_n: u8, secondary: u8, part_20_16: u8, part_25_21: u8) -> Opcode {
255        match part_25_21 {
256            0 if secondary == 0 => Opcode::Mfc(cop_n),
257            2 if secondary == 0 => Opcode::Cfc(cop_n),
258            4 if secondary == 0 => Opcode::Mtc(cop_n),
259            6 if secondary == 0 => Opcode::Ctc(cop_n),
260            8 => match part_20_16 {
261                0 => Opcode::Bcf(cop_n),
262                1 => Opcode::Bct(cop_n),
263                _ => Opcode::Invalid,
264            },
265            _ if part_25_21 & 0x10 != 0 => {
266                if cop_n == 0 {
267                    if secondary == 0x10 && part_25_21 == 0x10 {
268                        // TODO: should we use a separate opcode, or just forward
269                        //  Cop(n)cmd imm25 into Cop0 which should produce `RFE`?
270                        Opcode::Rfe
271                    } else {
272                        Opcode::Invalid
273                    }
274                } else {
275                    Opcode::Cop(cop_n)
276                }
277            }
278            _ => Opcode::Invalid,
279        }
280    }
281}
282
283const fn opcode_str(opcode: Opcode) -> &'static str {
284    match opcode {
285        Opcode::Nop => "nop",
286        Opcode::Lb => "lb",
287        Opcode::Lh => "lh",
288        Opcode::Lw => "lw",
289        Opcode::Lbu => "lbu",
290        Opcode::Lhu => "lhu",
291        Opcode::Sb => "sb",
292        Opcode::Lwl => "lwl",
293        Opcode::Lwr => "lwr",
294        Opcode::Sh => "sh",
295        Opcode::Sw => "sw",
296        Opcode::Swl => "swl",
297        Opcode::Swr => "swr",
298        Opcode::Slt => "slt",
299        Opcode::Sltu => "sltu",
300        Opcode::Slti => "slti",
301        Opcode::Sltiu => "sltiu",
302        Opcode::Addu => "addu",
303        Opcode::Add => "add",
304        Opcode::Subu => "subu",
305        Opcode::Sub => "sub",
306        Opcode::Addiu => "addiu",
307        Opcode::Addi => "addi",
308        Opcode::And => "and",
309        Opcode::Or => "or",
310        Opcode::Xor => "xor",
311        Opcode::Nor => "nor",
312        Opcode::Andi => "andi",
313        Opcode::Ori => "ori",
314        Opcode::Xori => "xori",
315        Opcode::Sllv => "sllv",
316        Opcode::Srlv => "srlv",
317        Opcode::Srav => "srav",
318        Opcode::Sll => "sll",
319        Opcode::Srl => "srl",
320        Opcode::Sra => "sra",
321        Opcode::Lui => "lui",
322        Opcode::Mult => "mult",
323        Opcode::Multu => "multu",
324        Opcode::Div => "div",
325        Opcode::Divu => "divu",
326        Opcode::Mfhi => "mfhi",
327        Opcode::Mthi => "mthi",
328        Opcode::Mflo => "mflo",
329        Opcode::Mtlo => "mtlo",
330        Opcode::J => "j",
331        Opcode::Jal => "jal",
332        Opcode::Jr => "jr",
333        Opcode::Jalr => "jalr",
334        Opcode::Beq => "beq",
335        Opcode::Bne => "bne",
336        Opcode::Bgtz => "bgtz",
337        Opcode::Blez => "blez",
338        Opcode::Bcondz => "bcondz",
339        Opcode::Bltz => "bltz",
340        Opcode::Bgez => "bgez",
341        Opcode::Bltzal => "bltzal",
342        Opcode::Bgezal => "bgezal",
343        Opcode::Syscall => "syscall",
344        Opcode::Break => "break",
345        Opcode::Cop(0) => "cop0",
346        Opcode::Cop(1) => "cop1",
347        Opcode::Cop(2) => "cop2",
348        Opcode::Cop(3) => "cop3",
349        Opcode::Mfc(0) => "mfc0",
350        Opcode::Mfc(1) => "mfc1",
351        Opcode::Mfc(2) => "mfc2",
352        Opcode::Mfc(3) => "mfc3",
353        Opcode::Cfc(0) => "cfc0",
354        Opcode::Cfc(1) => "cfc1",
355        Opcode::Cfc(2) => "cfc2",
356        Opcode::Cfc(3) => "cfc3",
357        Opcode::Mtc(0) => "mtc0",
358        Opcode::Mtc(1) => "mtc1",
359        Opcode::Mtc(2) => "mtc2",
360        Opcode::Mtc(3) => "mtc3",
361        Opcode::Ctc(0) => "ctc0",
362        Opcode::Ctc(1) => "ctc1",
363        Opcode::Ctc(2) => "ctc2",
364        Opcode::Ctc(3) => "ctc3",
365        Opcode::Bcf(0) => "bcf0",
366        Opcode::Bcf(1) => "bcf1",
367        Opcode::Bcf(2) => "bcf2",
368        Opcode::Bcf(3) => "bcf3",
369        Opcode::Bct(0) => "bct0",
370        Opcode::Bct(1) => "bct1",
371        Opcode::Bct(2) => "bct2",
372        Opcode::Bct(3) => "bct3",
373        Opcode::Rfe => "rfe",
374        Opcode::Lwc(0) => "lwc0",
375        Opcode::Lwc(1) => "lwc1",
376        Opcode::Lwc(2) => "lwc2",
377        Opcode::Lwc(3) => "lwc3",
378        Opcode::Swc(0) => "swc0",
379        Opcode::Swc(1) => "swc1",
380        Opcode::Swc(2) => "swc2",
381        Opcode::Swc(3) => "swc3",
382        _ => unreachable!(),
383    }
384}
385
386fn format_load_store(f: &mut fmt::Formatter, instr: &Instruction) -> fmt::Result {
387    let opcode = instr.opcode;
388
389    let src = instr.rs();
390    let off = instr.imm16();
391    let dst = instr.rt();
392
393    write!(f, "{} {}, 0x{:04X}({})", opcode_str(opcode), dst, off, src)
394}
395
396fn format_alu(f: &mut fmt::Formatter, instr: &Instruction, imm: bool) -> fmt::Result {
397    let opcode = instr.opcode;
398
399    let (first, third) = if imm {
400        (instr.rt(), format!("0x{:04X}", instr.imm16()))
401    } else {
402        (instr.rd(), format!("{}", instr.rt()))
403    };
404
405    write!(
406        f,
407        "{} {}, {}, {}",
408        opcode_str(opcode),
409        first,
410        instr.rs(),
411        third
412    )
413}
414
415fn format_shift(f: &mut fmt::Formatter, instr: &Instruction, imm: bool) -> fmt::Result {
416    let opcode = instr.opcode;
417
418    let third = if imm {
419        format!("0x{:02X}", instr.imm5())
420    } else {
421        format!("{}", instr.rs())
422    };
423
424    write!(
425        f,
426        "{} {}, {}, {}",
427        opcode_str(opcode),
428        instr.rd(),
429        instr.rt(),
430        third
431    )
432}
433
434fn format_mult_div(f: &mut fmt::Formatter, instr: &Instruction) -> fmt::Result {
435    let opcode = instr.opcode;
436
437    write!(f, "{} {}, {}", opcode_str(opcode), instr.rs(), instr.rt())
438}
439
440fn format_branch(f: &mut fmt::Formatter, instr: &Instruction, rt: bool) -> fmt::Result {
441    let opcode = instr.opcode;
442
443    let dest = instr.imm16();
444
445    if rt {
446        write!(
447            f,
448            "{} {}, {}, 0x{:04X} => 0x{:08X}",
449            opcode_str(opcode),
450            instr.rs(),
451            instr.rt(),
452            dest,
453            instr
454                .pc
455                .wrapping_add((dest as i16 as i32 as u32).wrapping_mul(4))
456                .wrapping_add(4)
457        )
458    } else {
459        write!(
460            f,
461            "{} {}, 0x{:04X} => 0x{:08X}",
462            opcode_str(opcode),
463            instr.rs(),
464            dest,
465            instr
466                .pc
467                .wrapping_add((dest as i16 as i32 as u32).wrapping_mul(4))
468                .wrapping_add(4)
469        )
470    }
471}
472
473fn format_cop_ops(f: &mut fmt::Formatter, instr: &Instruction) -> fmt::Result {
474    let opcode = instr.opcode;
475
476    write!(f, "{} {}, {}", opcode_str(opcode), instr.rt(), instr.rd())
477}
478
479impl fmt::Display for Instruction {
480    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
481        match self.opcode {
482            Opcode::Lb
483            | Opcode::Lbu
484            | Opcode::Lh
485            | Opcode::Lhu
486            | Opcode::Lw
487            | Opcode::Lwl
488            | Opcode::Lwr
489            | Opcode::Sb
490            | Opcode::Sh
491            | Opcode::Sw
492            | Opcode::Swl
493            | Opcode::Swr => format_load_store(f, self),
494            Opcode::Slt | Opcode::Sltu => format_alu(f, self, false),
495            Opcode::Slti | Opcode::Sltiu => format_alu(f, self, true),
496            Opcode::Addu | Opcode::Add | Opcode::Subu | Opcode::Sub => format_alu(f, self, false),
497            Opcode::Addiu | Opcode::Addi => format_alu(f, self, true),
498            Opcode::And | Opcode::Or | Opcode::Xor | Opcode::Nor => format_alu(f, self, false),
499            Opcode::Andi | Opcode::Ori | Opcode::Xori => format_alu(f, self, true),
500            Opcode::Sllv | Opcode::Srlv | Opcode::Srav => format_shift(f, self, false),
501            Opcode::Sll | Opcode::Srl | Opcode::Sra => format_shift(f, self, true),
502            Opcode::Lui => write!(
503                f,
504                "{} {}, 0x{:04X}",
505                opcode_str(self.opcode),
506                self.rt(),
507                self.imm16()
508            ),
509            Opcode::Mult | Opcode::Multu | Opcode::Div | Opcode::Divu => format_mult_div(f, self),
510            Opcode::Mfhi => write!(f, "{} {}", opcode_str(self.opcode), self.rd()),
511            Opcode::Mthi => write!(f, "{} {}", opcode_str(self.opcode), self.rs()),
512            Opcode::Mflo => write!(f, "{} {}", opcode_str(self.opcode), self.rd()),
513            Opcode::Mtlo => write!(f, "{} {}", opcode_str(self.opcode), self.rs()),
514            Opcode::J => write!(
515                f,
516                "{} 0x{:07X} => 0x{:08X}",
517                opcode_str(self.opcode),
518                self.imm26(),
519                (self.pc & 0xF0000000) | (self.imm26() * 4)
520            ),
521            Opcode::Jal => write!(
522                f,
523                "{} 0x{:07X} => 0x{:08X}",
524                opcode_str(self.opcode),
525                self.imm26(),
526                (self.pc & 0xF0000000) | (self.imm26() * 4)
527            ),
528            Opcode::Jr => write!(f, "{} {}", opcode_str(self.opcode), self.rs()),
529            // some specs says "jalr rd,rs"
530            Opcode::Jalr => write!(
531                f,
532                "{} {}, {}",
533                opcode_str(self.opcode),
534                self.rs(),
535                self.rd()
536            ),
537            Opcode::Beq | Opcode::Bne => format_branch(f, self, true),
538            Opcode::Bgtz
539            | Opcode::Blez
540            | Opcode::Bltz
541            | Opcode::Bgez
542            | Opcode::Bltzal
543            | Opcode::Bgezal => format_branch(f, self, false),
544            Opcode::Nop | Opcode::Syscall | Opcode::Break | Opcode::Rfe => {
545                f.write_str(opcode_str(self.opcode))
546            }
547            Opcode::Cop(_) => write!(f, "{} 0x{:07X}", opcode_str(self.opcode), self.imm25()),
548            Opcode::Mfc(_) | Opcode::Cfc(_) | Opcode::Mtc(_) | Opcode::Ctc(_) => {
549                format_cop_ops(f, self)
550            }
551            Opcode::Bcf(_) => todo!(),
552            Opcode::Bct(_) => todo!(),
553            Opcode::Lwc(_) | Opcode::Swc(_) => format_load_store(f, self),
554            Opcode::Invalid => write!(f, "Invalid instruction"),
555            _ => unreachable!(),
556        }
557    }
558}