mizu_core/cpu/
instruction.rs

1use super::instructions_table;
2use std::fmt::Display;
3
4#[derive(Debug)]
5pub(super) struct Instruction {
6    pub pc: u16,
7    pub opcode: Opcode,
8    pub src: OperandType,
9    pub dest: OperandType,
10}
11
12/// This is the location the operands will come from,
13/// a basic usage can be something like this
14///
15/// ```ignore
16/// # use mizu_core::cpu::instruction::OperandType;
17/// struct CPU {
18///   A: u8,
19/// }
20///
21/// impl CPU {
22///     fn write_operand(&mut self, dest: OperandType, data: u8) {
23///         match dest {
24///             OperandType::RegA => self.A = data,
25///             _ => {}
26///         }
27///     }
28///
29///     fn read_operand(&self, src: OperandType) -> u8 {
30///         match src {
31///             OperandType::RegA => self.A,
32///             _ => unreachable!(),
33///         }
34///     }
35///
36///     // implementation of the Ld instruction
37///     fn ld(&mut self, src: OperandType, dest: OperandType) {
38///         self.write_operand(dest, self.read_operand(src));
39///     }
40/// }
41/// ```
42#[derive(PartialEq, Copy, Clone, Debug)]
43pub enum OperandType {
44    RegA,
45    RegB,
46    RegC,
47    RegD,
48    RegE,
49    RegH,
50    RegL,
51
52    AddrHL, // this is used in many places with reg8
53    AddrHLDec,
54    AddrHLInc,
55    AddrBC,
56    AddrDE,
57
58    RegAF,
59    RegBC,
60    RegDE,
61    RegHL,
62
63    RegSP,
64
65    Imm8,
66    Imm8Signed,
67    Imm16,
68
69    HighAddr8,
70    HighAddrC, // only for the C register
71    Addr16,
72    Addr16Val16, // write 16bit value to address
73
74    // Also for instructions with one operand as a fill
75    Implied,
76}
77
78#[derive(PartialEq, Clone, Copy, Debug)]
79pub enum Condition {
80    NC,
81    C,
82    NZ,
83    Z,
84    Unconditional,
85}
86
87#[derive(PartialEq, Clone, Copy, Debug)]
88pub enum Opcode {
89    Nop,
90    Stop,
91
92    Ld,
93    LdSPHL,
94    LdHLSPSigned8,
95    LdBB, // used for breakpoint
96
97    Push,
98    Pop,
99
100    Inc,
101    Inc16,
102    Dec,
103    Dec16,
104
105    Add,
106    Add16,
107    AddSPSigned8,
108    Adc,
109    Cp, // = Sub (Implied, Reg8)
110    Sub,
111    Sbc,
112    And,
113    Xor,
114    Or,
115    Jp(Condition),
116    JpHL,
117    Jr(Condition),
118
119    Call(Condition),
120    Ret(Condition),
121
122    Reti,
123
124    Rst(u8),
125
126    Di,
127    Ei,
128    Ccf,
129    Scf,
130    Daa,
131    Cpl,
132
133    Rlca,
134    Rla,
135    Rrca,
136    Rra,
137
138    Prefix,
139
140    Rlc,
141    Rrc,
142    Rl,
143    Rr,
144    Sla,
145    Sra,
146    Swap,
147    Srl,
148
149    Bit(u8),
150    Res(u8),
151    Set(u8),
152
153    Illegal,
154
155    Halt,
156}
157
158impl Instruction {
159    pub fn from_byte(byte: u8, pc: u16) -> Self {
160        let (opcode, operand_types) = instructions_table::INSTRUCTIONS[byte as usize];
161
162        Instruction {
163            pc,
164            opcode,
165            src: operand_types.1,
166            dest: operand_types.0,
167        }
168    }
169
170    pub fn from_prefix(byte: u8, pc: u16) -> Self {
171        let (opcode, operand_types) = instructions_table::PREFIXED_INSTRUCTIONS[byte as usize];
172
173        Instruction {
174            pc,
175            opcode,
176            src: operand_types.1,
177            dest: operand_types.0,
178        }
179    }
180}
181
182fn operand_str(operand: OperandType) -> String {
183    match operand {
184        OperandType::RegA => "A".into(),
185        OperandType::RegB => "B".into(),
186        OperandType::RegC => "C".into(),
187        OperandType::RegD => "D".into(),
188        OperandType::RegE => "E".into(),
189        OperandType::RegH => "H".into(),
190        OperandType::RegL => "L".into(),
191        OperandType::AddrHL => "(HL)".into(),
192        OperandType::AddrHLDec => "(HL-)".into(),
193        OperandType::AddrHLInc => "(HL+)".into(),
194        OperandType::AddrBC => "(BC)".into(),
195        OperandType::AddrDE => "(DE)".into(),
196        OperandType::RegAF => "AF".into(),
197        OperandType::RegBC => "BC".into(),
198        OperandType::RegDE => "DE".into(),
199        OperandType::RegHL => "HL".into(),
200        OperandType::RegSP => "SP".into(),
201        OperandType::Imm8 => "d8".into(),
202        OperandType::Imm8Signed => "r8".into(),
203        OperandType::Imm16 => "d16".into(),
204        OperandType::HighAddr8 => "(a8)".into(),
205        OperandType::HighAddrC => "(C)".into(),
206        OperandType::Addr16 => "(a16)".into(),
207        OperandType::Addr16Val16 => "(a16)".into(),
208        OperandType::Implied => "".into(),
209    }
210}
211
212impl Display for Instruction {
213    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
214        let opcode: String = match self.opcode {
215            Opcode::Nop => "NOP".into(),
216            Opcode::Stop => "STOP".into(),
217            Opcode::Ld => "LD".into(),
218            Opcode::LdSPHL => "LD SP, HL".into(),
219            Opcode::LdHLSPSigned8 => "LDHLSP".into(),
220            Opcode::LdBB => "LD B,B".into(),
221            Opcode::Push => "PUSH".into(),
222            Opcode::Pop => "POP".into(),
223            Opcode::Inc => "INC".into(),
224            Opcode::Inc16 => "INC".into(),
225            Opcode::Dec => "DEC".into(),
226            Opcode::Dec16 => "DEC".into(),
227            Opcode::Add => "ADD".into(),
228            Opcode::Add16 => "ADD".into(),
229            Opcode::AddSPSigned8 => "ADD".into(),
230            Opcode::Adc => "ADC".into(),
231            Opcode::Cp => "CP".into(),
232            Opcode::Sub => "SUB".into(),
233            Opcode::Sbc => "SBC".into(),
234            Opcode::And => "AND".into(),
235            Opcode::Xor => "XOR".into(),
236            Opcode::Or => "OR".into(),
237            Opcode::Jp(Condition::Unconditional) => "JP".into(),
238            Opcode::Jp(cond) => format!("JP {cond:?},"),
239            Opcode::JpHL => "JP".into(),
240            Opcode::Jr(Condition::Unconditional) => "JR".into(),
241            Opcode::Jr(cond) => format!("JR {cond:?},"),
242            Opcode::Call(Condition::Unconditional) => "CALL".into(),
243            Opcode::Call(cond) => format!("CALL {cond:?},"),
244            Opcode::Ret(Condition::Unconditional) => "RET".into(),
245            Opcode::Ret(cond) => format!("RET {cond:?},"),
246            Opcode::Reti => "RETI".into(),
247            Opcode::Rst(loc) => format!("RST {loc:02X}"),
248            Opcode::Di => "DI".into(),
249            Opcode::Ei => "EI".into(),
250            Opcode::Ccf => "CCF".into(),
251            Opcode::Scf => "SCF".into(),
252            Opcode::Daa => "DAA".into(),
253            Opcode::Cpl => "CPL".into(),
254            Opcode::Rlca => "RLCA".into(),
255            Opcode::Rla => "RLA".into(),
256            Opcode::Rrca => "RRCA".into(),
257            Opcode::Rra => "RRA".into(),
258            Opcode::Prefix => "PREFIX".into(),
259            Opcode::Rlc => "RLC".into(),
260            Opcode::Rrc => "RRC".into(),
261            Opcode::Rl => "RL".into(),
262            Opcode::Rr => "RR".into(),
263            Opcode::Sla => "SLA".into(),
264            Opcode::Sra => "SRA".into(),
265            Opcode::Swap => "SWAP".into(),
266            Opcode::Srl => "SRL".into(),
267            Opcode::Bit(n) => format!("BIT {n},"),
268            Opcode::Res(n) => format!("RES {n},"),
269            Opcode::Set(n) => format!("SET {n},"),
270            Opcode::Illegal => "ILLEGAL".into(),
271            Opcode::Halt => "HALT".into(),
272        };
273
274        let mut operands = operand_str(self.dest);
275        if operands.is_empty() {
276            operands = operand_str(self.src);
277        } else if !operand_str(self.src).is_empty() {
278            operands += &format!(",{}", operand_str(self.src));
279        }
280
281        write!(f, "{opcode} {operands}")
282    }
283}
284
285#[cfg(test)]
286mod tests {
287    use super::Instruction;
288
289    #[test]
290    fn available_instructions() {
291        for i in 0..=255u8 {
292            Instruction::from_byte(i, 0);
293        }
294    }
295
296    #[test]
297    fn available_instructions_with_prefix_cb() {
298        for i in 0..=255u8 {
299            Instruction::from_prefix(i, 0);
300        }
301    }
302}