1use std::fmt;
5
6use crate::opcode::args::{AluOp, AluUnaryOp, ConditionCode, Operand16, Operand8};
7
8pub mod args;
9pub mod defs;
10
11pub struct InternalFetch;
13
14impl fmt::Display for InternalFetch {
15 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
16 f.write_str("[Internal Fetch]")
17 }
18}
19
20#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
30pub enum Opcode {
31 Nop,
33 Stop,
35 JumpRelative(ConditionCode),
38 Inc8(Operand8),
40 Dec8(Operand8),
42 Load8 { dest: Operand8, source: Operand8 },
44 Inc16(Operand16),
46 Dec16(Operand16),
48 Load16 { dest: Operand16, source: Operand16 },
50 Add16(Operand16),
52 Halt,
54 AluOp { op: AluOp, operand: Operand8 },
57 AluUnary(AluUnaryOp),
60 Call(ConditionCode),
63 Jump(ConditionCode),
66 Ret(ConditionCode),
68 Push(Operand16),
70 Pop(Operand16),
72 PrefixCB,
74 DisableInterrupts,
76 EnableInterrupts,
78 RetInterrupt,
80 OffsetSp,
82 AddressOfOffsetSp,
84 JumpHL,
86 Reset(u8),
88 MissingInstruction(u8),
91}
92
93impl Opcode {
94 pub fn decode(opcode: u8) -> Self {
97 let x = (opcode & 0b11000000) >> 6;
100 let p = (opcode & 0b00110000) >> 4;
101 let y = (opcode & 0b00111000) >> 3;
102 let q = (opcode & 0b00001000) != 0;
103 let z = opcode & 0b00000111;
104 match x {
105 0 => match z {
106 0 => match y {
107 0 => Self::Nop,
108 1 => Self::Load16 {
109 dest: Operand16::AddrImmediate,
110 source: Operand16::Sp,
111 },
112 2 => Self::Stop,
113 3..=7 => Self::JumpRelative(ConditionCode::from_relative_cond_code(y)),
114 _ => unreachable!(),
115 },
116 1 => match q {
117 false => Self::Load16 {
118 dest: Operand16::from_pair_code_sp(p),
119 source: Operand16::Immediate,
120 },
121 true => Self::Add16(Operand16::from_pair_code_sp(p)),
122 },
123 2 => match q {
124 false => Self::Load8 {
125 dest: Operand8::from_indirect(p),
126 source: Operand8::A,
127 },
128 true => Self::Load8 {
129 dest: Operand8::A,
130 source: Operand8::from_indirect(p),
131 },
132 },
133 3 => match q {
134 false => Self::Inc16(Operand16::from_pair_code_sp(p)),
135 true => Self::Dec16(Operand16::from_pair_code_sp(p)),
136 },
137 4 => Self::Inc8(Operand8::from_regcode(y)),
138 5 => Self::Dec8(Operand8::from_regcode(y)),
139 6 => Self::Load8 {
140 dest: Operand8::from_regcode(y),
141 source: Operand8::Immediate,
142 },
143 7 => Self::AluUnary(AluUnaryOp::from_ycode(y)),
144 _ => unreachable!(),
145 },
146 1 => match (z, y) {
147 (6, 6) => Self::Halt,
148 _ => Self::Load8 {
149 dest: Operand8::from_regcode(y),
150 source: Operand8::from_regcode(z),
151 },
152 },
153 2 => Self::AluOp {
154 op: AluOp::from_ycode(y),
155 operand: Operand8::from_regcode(z),
156 },
157 3 => match z {
158 0 => match y {
159 0..=3 => Self::Ret(ConditionCode::from_absolute_cond_code(y)),
160 4 => Self::Load8 {
163 dest: Operand8::AddrRelImmediate,
164 source: Operand8::A,
165 },
166 5 => Self::OffsetSp,
167 6 => Self::Load8 {
168 dest: Operand8::A,
169 source: Operand8::AddrRelImmediate,
170 },
171 7 => Self::AddressOfOffsetSp,
172 _ => unreachable!(),
173 },
174 1 => match q {
175 false => Opcode::Pop(Operand16::from_pair_code_af(p)),
176 true => match p {
177 0 => Opcode::Ret(ConditionCode::Unconditional),
178 1 => Opcode::RetInterrupt,
179 2 => Opcode::JumpHL,
180 3 => Opcode::Load16 {
181 dest: Operand16::Sp,
182 source: Operand16::HL,
183 },
184 _ => unreachable!(),
185 },
186 },
187 2 => match y {
188 0..=3 => Self::Jump(ConditionCode::from_absolute_cond_code(y)),
189 4 => Self::Load8 {
192 dest: Operand8::AddrRelC,
193 source: Operand8::A,
194 },
195 5 => Self::Load8 {
196 dest: Operand8::AddrImmediate,
197 source: Operand8::A,
198 },
199 6 => Self::Load8 {
200 dest: Operand8::A,
201 source: Operand8::AddrRelC,
202 },
203 7 => Self::Load8 {
204 dest: Operand8::A,
205 source: Operand8::AddrImmediate,
206 },
207 _ => unreachable!(),
208 },
209 3 => match y {
210 0 => Self::Jump(ConditionCode::Unconditional),
211 1 => Self::PrefixCB,
212 2..=5 => Self::MissingInstruction(opcode),
214 6 => Self::DisableInterrupts,
215 7 => Self::EnableInterrupts,
216 _ => unreachable!(),
217 },
218 4 => match y {
219 0..=3 => Self::Call(ConditionCode::from_absolute_cond_code(y)),
220 4..=7 => Self::MissingInstruction(opcode),
222 _ => unreachable!(),
223 },
224 5 => match q {
225 false => Opcode::Push(Operand16::from_pair_code_af(p)),
226 true => match p {
227 0 => Opcode::Call(ConditionCode::Unconditional),
228 1..=3 => Opcode::MissingInstruction(opcode),
230 _ => unreachable!(),
231 },
232 },
233 6 => Self::AluOp {
234 op: AluOp::from_ycode(y),
235 operand: Operand8::Immediate,
236 },
237 7 => Opcode::Reset(y * 8),
238 _ => unreachable!(),
239 },
240 _ => unreachable!(),
241 }
242 }
243}
244
245impl fmt::Display for Opcode {
246 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
247 match *self {
248 Self::Nop => f.write_str("NOP"),
249 Self::Stop => f.write_str("STOP"),
250 Self::JumpRelative(ConditionCode::Unconditional) => f.write_str("JR i8"),
251 Self::JumpRelative(code) => write!(f, "JR {},i8", code),
252 Self::Inc8(operand) => write!(f, "INC {}", operand),
253 Self::Dec8(operand) => write!(f, "DEC {}", operand),
254 Self::Load8 { dest, source } => write!(f, "LD {},{}", dest, source),
255 Self::Inc16(operand) => write!(f, "INC {}", operand),
256 Self::Dec16(operand) => write!(f, "DEC {}", operand),
257 Self::Load16 { dest, source } => write!(f, "LD {},{}", dest, source),
258 Self::Add16(operand) => write!(f, "ADD HL,{}", operand),
259 Self::Halt => f.write_str("HALT"),
260 Self::AluOp { op, operand } => write!(f, "{} A,{}", op, operand),
261 Self::AluUnary(op) => fmt::Display::fmt(&op, f),
262 Self::Call(ConditionCode::Unconditional) => f.write_str("CALL u16"),
263 Self::Call(code) => write!(f, "CALL {},u16", code),
264 Self::Jump(ConditionCode::Unconditional) => f.write_str("JP u16"),
265 Self::Jump(code) => write!(f, "JP {},u16", code),
266 Self::Ret(ConditionCode::Unconditional) => f.write_str("RET"),
267 Self::Ret(code) => write!(f, "RET {}", code),
268 Self::Pop(operand) => write!(f, "POP {}", operand),
269 Self::Push(operand) => write!(f, "PUSH {}", operand),
270 Self::PrefixCB => f.write_str("PREFIX CB"),
271 Self::DisableInterrupts => f.write_str("DI"),
272 Self::EnableInterrupts => f.write_str("EI"),
273 Self::RetInterrupt => f.write_str("RETI"),
274 Self::OffsetSp => f.write_str("ADD SP,i8"),
275 Self::AddressOfOffsetSp => f.write_str("LD HL,SP+i8"),
276 Self::JumpHL => f.write_str("JP HL"),
277 Self::Reset(target) => write!(f, "RST {:02X}h", target),
278 Self::MissingInstruction(opcode) => write!(f, "<Missing Instruction {:2X}>", opcode),
279 }
280 }
281}
282
283#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
289pub struct CBOpcode {
290 pub operand: Operand8,
292 pub op: CBOperation,
294}
295
296impl CBOpcode {
297 pub fn decode(opcode: u8) -> Self {
299 let x = (opcode & 0b11000000) >> 6;
300 let y = (opcode & 0b00111000) >> 3;
301 let z = opcode & 0b00000111;
302 let operand = Operand8::from_regcode(z);
303 let op = match x {
304 0 => match y {
305 0 => CBOperation::RotateLeft8,
306 1 => CBOperation::RotateRight8,
307 2 => CBOperation::RotateLeft9,
308 3 => CBOperation::RotateRight9,
309 4 => CBOperation::ShiftLeft,
310 5 => CBOperation::ShiftRightSignExt,
311 6 => CBOperation::Swap,
312 7 => CBOperation::ShiftRight,
313 _ => unreachable!(),
314 },
315 1 => CBOperation::TestBit(y),
316 2 => CBOperation::ResetBit(y),
317 3 => CBOperation::SetBit(y),
318 _ => unreachable!(),
319 };
320 Self { operand, op }
321 }
322}
323
324impl fmt::Display for CBOpcode {
325 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
326 if self.op.is_bit_op() {
327 write!(f, "{},{}", self.op, self.operand)
328 } else {
329 write!(f, "{} {}", self.op, self.operand)
330 }
331 }
332}
333
334#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
336pub enum CBOperation {
337 RotateLeft8,
339 RotateLeft9,
341 RotateRight8,
343 RotateRight9,
345 ShiftLeft,
347 ShiftRight,
349 ShiftRightSignExt,
352 Swap,
354 TestBit(u8),
356 SetBit(u8),
358 ResetBit(u8),
360}
361
362impl CBOperation {
363 fn is_bit_op(self) -> bool {
366 matches!(self, Self::TestBit(_) | Self::SetBit(_) | Self::ResetBit(_))
367 }
368}
369
370impl fmt::Display for CBOperation {
371 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
372 match *self {
373 Self::RotateLeft8 => f.write_str("RLC"),
374 Self::RotateRight8 => f.write_str("RRC"),
375 Self::RotateLeft9 => f.write_str("RL"),
376 Self::RotateRight9 => f.write_str("RR"),
377 Self::ShiftLeft => f.write_str("SLA"),
378 Self::ShiftRightSignExt => f.write_str("SRA"),
379 Self::Swap => f.write_str("SWAP"),
380 Self::ShiftRight => f.write_str("SRL"),
381 Self::TestBit(bit) => write!(f, "BIT {}", bit),
382 Self::ResetBit(bit) => write!(f, "RES {}", bit),
383 Self::SetBit(bit) => write!(f, "SET {}", bit),
384 }
385 }
386}
387
388#[cfg(test)]
389mod tests {
390 use super::*;
391
392 #[test]
393 fn can_decode_any_opcode() {
394 for op in 0u8..=0xff {
395 Opcode::decode(op);
396 }
397 }
398
399 #[test]
400 fn can_decode_any_cb_opcode() {
401 for op in 0u8..=0xff {
402 CBOpcode::decode(op);
403 }
404 }
405}