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#[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, 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, Addr16,
72 Addr16Val16, 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, Push,
98 Pop,
99
100 Inc,
101 Inc16,
102 Dec,
103 Dec16,
104
105 Add,
106 Add16,
107 AddSPSigned8,
108 Adc,
109 Cp, 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}