feo3boy_opcodes/opcode/
defs.rs

1//! Provides the definitions of the opcodes in terms of microcode.
2
3use crate::compiler::instr::builder::{InstrBuilder, MicrocodeReadable, MicrocodeWritable};
4use crate::compiler::instr::{InstrDef, InstrId};
5use crate::gbz80types::Flags;
6use crate::microcode::args::{Reg16, Reg8};
7use crate::microcode::combocodes::{
8    GbStack16, HandleHalt, HlDec, HlInc, Immediate16, Immediate8, LoadAndExecute, Mem,
9    ServiceInterrupt,
10};
11use crate::microcode::Microcode;
12use crate::opcode::args::{AluOp, AluUnaryOp, ConditionCode, Operand16, Operand8};
13use crate::opcode::{CBOpcode, CBOperation, InternalFetch, Opcode};
14
15impl From<InternalFetch> for InstrDef {
16    fn from(_: InternalFetch) -> Self {
17        // Check if halted (which may just yield and return).
18        InstrBuilder::first(HandleHalt)
19            // Service interrupts (which may jump to an interrupt and return).
20            .then(ServiceInterrupt)
21            // Load and execute the next instruction.
22            .then(LoadAndExecute)
23            .build(InstrId::InternalFetch)
24    }
25}
26
27impl From<Opcode> for InstrDef {
28    fn from(value: Opcode) -> Self {
29        let builder = match value {
30            Opcode::Nop => InstrBuilder::new(),
31            Opcode::Stop => Microcode::Stop.into(),
32            Opcode::JumpRelative(cond) => jump_relative(cond),
33            Opcode::Inc8(operand) => inc8(operand),
34            Opcode::Dec8(operand) => dec8(operand),
35            Opcode::Load8 { dest, source } => InstrBuilder::read(source).then_write(dest),
36            Opcode::Inc16(operand) => inc16(operand),
37            Opcode::Dec16(operand) => dec16(operand),
38            Opcode::Load16 { dest, source } => load16(dest, source),
39            Opcode::Add16(operand) => add16(operand),
40            Opcode::Halt => Microcode::Halt.into(),
41            Opcode::AluOp { operand, op } => InstrBuilder::read(operand).then(op),
42            Opcode::AluUnary(op) => op.into(),
43            Opcode::Call(cond) => call(cond),
44            Opcode::Jump(cond) => jump(cond),
45            Opcode::Ret(cond) => ret(cond),
46            Opcode::Push(operand) => InstrBuilder::read(operand)
47                // Push has an extra delay before writing.
48                .then_yield()
49                .then_write(GbStack16),
50            Opcode::Pop(operand) => InstrBuilder::read(GbStack16).then_write(operand),
51            Opcode::PrefixCB => InstrBuilder::read(Immediate8).then(Microcode::ParseCBOpcode),
52            Opcode::DisableInterrupts => Microcode::DisableInterrupts.into(),
53            Opcode::EnableInterrupts => Microcode::EnableInterrupts { immediate: false }.into(),
54            Opcode::RetInterrupt => interrupt_return(),
55            Opcode::OffsetSp => offset_sp(),
56            Opcode::AddressOfOffsetSp => address_of_offset_sp(),
57            Opcode::JumpHL => InstrBuilder::read(Reg16::HL).then_write(Reg16::Pc),
58            Opcode::Reset(dest) => reset(dest),
59            // A brief note on this doc page:
60            // https://gbdev.io/pandocs/CPU_Comparison_with_Z80.html
61            // says that the unused opcodes will lock up the CPU, rather than behave as a
62            // no-op.
63            // However, we just build them as an empty instruction, which is a no-op.
64            Opcode::MissingInstruction(_) => InstrBuilder::new(),
65        };
66        builder.build(InstrId::Opcode(value))
67    }
68}
69
70/// Build microcode for the relative jump instruction.
71fn jump_relative(cond: ConditionCode) -> InstrBuilder {
72    // stack: ...|
73    // Read the jump offset from the next immediate.
74    // Loading the offset also moves the program counter over the next instruction, which
75    // is good because the jump is relative to the following instruction.
76    // stack: ...|i8  |
77    InstrBuilder::read(Immediate8)
78        // Read the PC address of the instruction after this one onto the stack.
79        // stack: ...|i8  |pcl |pch |
80        .then_read(Reg16::Pc)
81        // Apply the PC offset.
82        // Read the PC address of the instruction after this one onto the stack.
83        // stack: ...|dstl|dsth|flag|
84        .then(Microcode::OffsetAddr)
85        // Discard the flags since JR doesn't use them.
86        // stack: ...|dstl|dsth|
87        .then(Microcode::Discard8)
88        // Apply the jump only if the condition matches.
89        // First yield, because there's an extra dely in JR instructions when branching,
90        // then pop the destination off the stack and into the PC register.
91        .then(cond.cond(
92            // If the condition matches, apply it to the PC.
93            InstrBuilder::r#yield().then_write(Reg16::Pc),
94            // If it doesn't match, discard the computed new PC value.
95            Microcode::Discard16,
96        ))
97}
98
99/// Provides microcode for an 8 bit increment instruction.
100fn inc8(operand: Operand8) -> InstrBuilder {
101    // Inc doesn't set the carry flag.
102    const MASK: Flags = Flags::all().difference(Flags::CARRY);
103
104    // Start by putting a 1 (the right-hand-side for our ALU sub) onto the stack, since
105    // binary ops operate with the top of the stack being the LHS.
106    // stack: ...|1|
107    InstrBuilder::first(Microcode::Append { val: 1 })
108        // Then fetch the operand.
109        // stack: ...|1|v|
110        .then_read(operand)
111        // Apply the operation
112        // stack: ...|r|f|
113        .then(Microcode::Add)
114        // Write out the flags which are modified
115        // stack: ...|r|
116        .then_write(MASK)
117        // Write the result back to the same operand.
118        // stack: ...|
119        .then_write(operand)
120}
121
122/// Provides microcode for an 8 bit decrement instruction.
123fn dec8(operand: Operand8) -> InstrBuilder {
124    // Dec doesn't set the carry flag.
125    const MASK: Flags = Flags::all().difference(Flags::CARRY);
126
127    // Start by putting a 1 (the right-hand-side for our ALU sub) onto the stack, since
128    // binary ops operate with the top of the stack being the LHS.
129    // stack: ...|1|
130    InstrBuilder::first(Microcode::Append { val: 1 })
131        // Then fetch the operand.
132        // stack: ...|1|v|
133        .then_read(operand)
134        // Apply the operation
135        // stack: ...|r|f|
136        .then(Microcode::Sub)
137        // Write out the flags which are modified
138        // stack: ...|r|
139        .then_write(MASK)
140        // Write the result back to the same operand.
141        // stack: ...|
142        .then_write(operand)
143}
144
145/// Provides microcode for a 16 bit increment instruction.
146fn inc16(operand: Operand16) -> InstrBuilder {
147    InstrBuilder::read(operand)
148        .then(Microcode::Inc16)
149        // 16 bit inc doesn't set any flags, and all actual operands are always registers,
150        // but it does delay by 1 additional M cycle, probably because it has to operate
151        // on two bytes.
152        .then(Microcode::Yield)
153        .then_write(operand)
154}
155
156/// Provides microcode for a 16 bit decrement instruction.
157fn dec16(operand: Operand16) -> InstrBuilder {
158    InstrBuilder::read(operand)
159        .then(Microcode::Dec16)
160        // 16 bit dec doesn't set any flags, and all actual operands are always registers,
161        // but it does delay by 1 additional M cycle, probably because it has to operate
162        // on two bytes.
163        .then(Microcode::Yield)
164        .then_write(operand)
165}
166
167/// Provides microcode for 16 bit load operations.
168fn load16(dest: Operand16, source: Operand16) -> InstrBuilder {
169    InstrBuilder::read(source)
170        .then(if (dest, source) == (Operand16::Sp, Operand16::HL) {
171            // Most of the 16 bit loads are <Pair>,<Immediate> and take time based on
172            // number of memory accesses. There are two exceptions. LD (u16),SP, which is
173            // also just timed based on the number of memory accesses, and LD SP,HL, which
174            // is all registers but still takes an extra 1m cycle, which isn't
175            // automatically provided by Operand16 register interactions, so we insert it
176            // here.
177            Some(Microcode::Yield)
178        } else {
179            None
180        })
181        .then_write(dest)
182}
183
184/// Generates microcode for a 16 bit register add into HL. Never sets the zero flag and
185/// clears the subtract flag, but does set carry and half-carry based on the upper byte of
186/// the operation (as if it was performed by running the pseudo-instructions `add
187/// l,<arg-low>; adc h,<arg-high>`.
188fn add16(arg: Operand16) -> InstrBuilder {
189    // 16 bit add never modifies the zero flag.
190    const MASK: Flags = Flags::all().difference(Flags::ZERO);
191
192    // RHS goes on the stack first.
193    InstrBuilder::read(arg)
194        // Then LHS goes on the stack.
195        .then_read(Reg16::HL)
196        // This produces the unmasked flags on top of the stack with the result
197        // underneath.
198        .then(Microcode::Add16)
199        // Add an extra delay cycle, since this op is an 8t not 4t, probably because it is
200        // operating on two bytes, and that extra delay doesn't come from memory accesses.
201        .then_yield()
202        .then_write(MASK)
203        .then_write(Reg16::HL)
204}
205
206/// Build the microcode for a conditional or unconditional call.
207fn call(cond: ConditionCode) -> InstrBuilder {
208    // Conveniently, unconditional call behaves exactly the same as a conditional call
209    // with a true value, down to the timing.
210    // First, load the destination address onto the microcode stack.
211    // stack: ...|dstl|dsth|
212    InstrBuilder::read(Immediate16)
213        // Evaluate conditionally:
214        // stack: ...|dstl|dsth|
215        .then(
216            cond.cond(
217                // If true:
218                // Read the PC onto the microcode stack so we can push it to the gameboy
219                // stack.
220                // stack: ...|dstl|dsth|pcl |pch |
221                InstrBuilder::read(Reg16::Pc)
222                    // Conditional jump has an extra internal delay if the condition is
223                    // true before pushing the return address to the GB stack.
224                    .then_yield()
225                    // Write the return address to the Gameboy Stack.
226                    // stack: ...|dstl|dsth|
227                    .then_write(GbStack16)
228                    // Write the destination of the jump to the PC.
229                    // stack: ...|
230                    .then_write(Reg16::Pc),
231                // If false:
232                // Discard the destination address from the microcode stack.
233                Microcode::Discard16,
234            ),
235        )
236}
237
238/// Builds microcode for a conditional absolute jump.
239fn jump(cond: ConditionCode) -> InstrBuilder {
240    // Conveniently, unconditional jump behaves exactly the same as a conditional jump
241    // with a true value, down to the timing.
242
243    // Fetch the destination of the jump from the immediate.
244    // stack: ...|dstl|dsth|
245    InstrBuilder::read(Immediate16)
246        // Evaluate conditionally:
247        .then(
248            cond.cond(
249                // If true:
250                // Delay by one extra cycle because branching adds an extra cycle despite not
251                // accessing memory.
252                InstrBuilder::r#yield()
253                    // Write the desintation address to the PC.
254                    // stack: ...|
255                    .then_write(Reg16::Pc),
256                // If false:
257                // Discard the destination address.
258                Microcode::Discard16,
259            ),
260        )
261}
262
263/// Performs a conditional return.
264fn ret(cond: ConditionCode) -> InstrBuilder {
265    // First do a yield if this is a conditional return. The conditional returns have an
266    // extra delay at the beginning which isn't part of the unconditional return.
267    InstrBuilder::first(match cond {
268        ConditionCode::Unconditional => None,
269        _ => Some(Microcode::Yield),
270    })
271    // Apply the actual return only if the condition is true.
272    .then(
273        cond.if_true(
274            // Pop the return address off the Gameboy stack and onto the microcode stack.
275            // This takes two m cycles.
276            // stack: ...|retl|reth|
277            InstrBuilder::read(GbStack16)
278                // Apply the additional yield that happens after the GB stack pop and
279                // before the return is applied to the program counter.
280                .then_yield()
281                // Pop the return address off the microcode stack and into the pc.
282                .then_write(Reg16::Pc),
283        ),
284    )
285}
286
287/// Build microcode to enable interrupts and return.
288fn interrupt_return() -> InstrBuilder {
289    // Pop the return address off the GB stack and onto the microcode stack.
290    InstrBuilder::read(GbStack16)
291        // Delay by one additional cycle since there's one extra delay in RETI.
292        .then_yield()
293        // Write the return address to the PC.
294        .then_write(Reg16::Pc)
295        // Enable interrupts immediately.
296        .then(Microcode::EnableInterrupts { immediate: true })
297}
298
299/// Get microcode to offset the stack pointer by an immediate value.
300fn offset_sp() -> InstrBuilder {
301    // stack: ...|off|
302    InstrBuilder::read(Immediate8)
303        // stack: ...|off|spl|sph|
304        .then_read(Reg16::Sp)
305        // stack: ...|SPL|SPH|flg|
306        .then(Microcode::OffsetAddr)
307        // This instruction takes two more cycles after loading the offset.
308        .then_yield()
309        .then_yield()
310        // stack: ...|SPL|SPH|
311        .then_write(Flags::all())
312        .then_write(Reg16::Sp)
313}
314
315/// Create microcode to load the result of offsetting the stack pointer by an immediate
316/// value into HL.
317fn address_of_offset_sp() -> InstrBuilder {
318    // stack: ...|off|
319    InstrBuilder::read(Immediate8)
320        // stack: ...|off|spl|sph|
321        .then_read(Reg16::Sp)
322        // stack: ...|SPL|SPH|flg|
323        .then(Microcode::OffsetAddr)
324        // This instruction takes one more cycle after loading the offset.
325        // Interestingly, this instruction is actually faster than `ADD SP,i8`.
326        .then_yield()
327        // stack: ...|SPL|SPH|
328        .then_write(Flags::all())
329        .then_write(Reg16::HL)
330}
331
332/// Builds microcode for the reset instruction. Similar to call with a fixed destination.
333fn reset(dest: u8) -> InstrBuilder {
334    // There's an extra delay at the start of an RST instruction.
335    InstrBuilder::r#yield()
336        // Push the PC onto the gameboy stack.
337        .then_read(Reg16::Pc)
338        .then_write(GbStack16)
339        // Push the reset destination low byte onto the stack.
340        // stack: ...|addrl|
341        .then(Microcode::Append { val: dest })
342        // Push the high order bytes of the dest address (always 0).
343        // stack: ...|addrl|addrh|
344        .then(Microcode::Append { val: 0 })
345        // Set the PC to the specified address.
346        .then_write(Reg16::Pc)
347}
348
349impl From<CBOpcode> for InstrDef {
350    fn from(value: CBOpcode) -> Self {
351        // Builds microcode for the CB Opcodes that takes a value and affects flags.
352        fn cb_unary_op(operand: Operand8, operator: Microcode) -> InstrBuilder {
353            // First, read the operand onto the microcode stack.
354            // stack: ...|val|
355            InstrBuilder::read(operand)
356                // Rotate left/right 9 require the current flags value in addition to the
357                // operand.
358                .then(
359                    if matches!(operator, Microcode::RotateLeft9 | Microcode::RotateRight9) {
360                        Some(Microcode::GetFlagsMasked { mask: Flags::all() })
361                    } else {
362                        None
363                    },
364                )
365                // Apply the operator, generating the result and flags.
366                // stack: ...|res|flags|
367                .then(operator)
368                // Apply the flags, overwriting all flags in the register.
369                .then_write(Flags::all())
370                .then_write(operand)
371        }
372        let builder = match value.op {
373            CBOperation::RotateLeft8 => cb_unary_op(value.operand, Microcode::RotateLeft8),
374            CBOperation::RotateLeft9 => cb_unary_op(value.operand, Microcode::RotateLeft9),
375            CBOperation::RotateRight8 => cb_unary_op(value.operand, Microcode::RotateRight8),
376            CBOperation::RotateRight9 => cb_unary_op(value.operand, Microcode::RotateRight9),
377            CBOperation::ShiftLeft => cb_unary_op(value.operand, Microcode::ShiftLeft),
378            CBOperation::ShiftRight => cb_unary_op(value.operand, Microcode::ShiftRight),
379            CBOperation::ShiftRightSignExt => {
380                cb_unary_op(value.operand, Microcode::ShiftRightSignExt)
381            }
382            CBOperation::Swap => cb_unary_op(value.operand, Microcode::Swap),
383            CBOperation::TestBit(bit) => {
384                // Doesn't affect the carry flag.
385                const MASK: Flags = Flags::all().difference(Flags::CARRY);
386                InstrBuilder::read(value.operand)
387                    .then(Microcode::TestBit { bit })
388                    .then_write(MASK)
389            }
390            CBOperation::SetBit(bit) => InstrBuilder::read(value.operand)
391                .then(Microcode::SetBit { bit })
392                .then_write(value.operand),
393            CBOperation::ResetBit(bit) => InstrBuilder::read(value.operand)
394                .then(Microcode::ResetBit { bit })
395                .then_write(value.operand),
396        };
397        builder.build(InstrId::CBOpcode(value))
398    }
399}
400
401/// Builds the microcode for a single ALU operation. This assumes that the ALU operation's
402/// single u8 arg is already on the stack and handles fetching the accumulator and flags
403/// (if needed) and applying the results.
404impl From<AluOp> for InstrBuilder {
405    fn from(op: AluOp) -> Self {
406        // stack: ...|val|
407        // Fetch the accumulator.
408        // stack: ...|val|acc|
409        InstrBuilder::read(Reg8::Acc)
410            // If the operation requires flags, fetch the flags register.
411            .then(if matches!(op, AluOp::AddCarry | AluOp::SubCarry) {
412                Some(Microcode::GetFlagsMasked { mask: Flags::all() })
413            } else {
414                None
415            })
416            // Apply the appropriate BinaryOp.
417            // stack: ...|res|flg|
418            .then(match op {
419                AluOp::Add => Microcode::Add,
420                AluOp::AddCarry => Microcode::Adc,
421                AluOp::Sub => Microcode::Sub,
422                AluOp::SubCarry => Microcode::Sbc,
423                AluOp::And => Microcode::And,
424                AluOp::Or => Microcode::Or,
425                AluOp::Xor => Microcode::Xor,
426                AluOp::Compare => Microcode::Sub,
427            })
428            // Write the resulting flags (overwriting all flags)
429            // stack: ...|res|
430            .then_write(Flags::all())
431            // Either put the result into the Acc or discard it (in case of cmp).
432            // stack: ...|
433            .then(match op {
434                AluOp::Compare => Microcode::Discard8,
435                _ => Microcode::WriteReg { reg: Reg8::Acc },
436            })
437    }
438}
439
440/// Builds the microcode for a single ALU unary operation. This handles reading the
441/// accumulator and flags as needed, and handles applying the results.
442impl From<AluUnaryOp> for InstrBuilder {
443    fn from(value: AluUnaryOp) -> Self {
444        match value {
445            AluUnaryOp::RotateLeft8 => InstrBuilder::read(Reg8::Acc)
446                .then(Microcode::RotateLeft8)
447                .then_write(Flags::all())
448                // Unlike CB Opcodes, this Rotate always clears the zero flag, so we need to
449                // clear that here. To do that, we load an all-zero byte and apply it masked
450                // to just the zero flag.
451                .then(Microcode::Append { val: 0 })
452                .then_write(Flags::ZERO)
453                .then_write(Reg8::Acc),
454            AluUnaryOp::RotateLeft9 => InstrBuilder::read(Reg8::Acc)
455                .then_read(Flags::all())
456                .then(Microcode::RotateLeft9)
457                .then_write(Flags::all())
458                // Unlike CB Opcodes, this Rotate always clears the zero flag, so we need to
459                // clear that here. To do that, we load an all-zero byte and apply it masked
460                // to just the zero flag.
461                .then(Microcode::Append { val: 0 })
462                .then_write(Flags::ZERO)
463                .then_write(Reg8::Acc),
464            AluUnaryOp::RotateRight8 => InstrBuilder::read(Reg8::Acc)
465                .then(Microcode::RotateRight8)
466                .then_write(Flags::all())
467                // Unlike CB Opcodes, this Rotate always clears the zero flag, so we need to
468                // clear that here. To do that, we load an all-zero byte and apply it masked
469                // to just the zero flag.
470                .then(Microcode::Append { val: 0 })
471                .then_write(Flags::ZERO)
472                .then_write(Reg8::Acc),
473            AluUnaryOp::RotateRight9 => InstrBuilder::read(Reg8::Acc)
474                .then_read(Flags::all())
475                .then(Microcode::RotateRight9)
476                .then_write(Flags::all())
477                // Unlike CB Opcodes, this Rotate always clears the zero flag, so we need to
478                // clear that here. To do that, we load an all-zero byte and apply it masked
479                // to just the zero flag.
480                .then(Microcode::Append { val: 0 })
481                .then_write(Flags::ZERO)
482                .then_write(Reg8::Acc),
483            AluUnaryOp::DecimalAdjust => {
484                // Does not modify SUB.
485                const MASK: Flags = Flags::all().difference(Flags::SUB);
486                InstrBuilder::read(Reg8::Acc)
487                    .then_read(Flags::all())
488                    .then(Microcode::DecimalAdjust)
489                    .then_write(MASK)
490                    .then_write(Reg8::Acc)
491            }
492            AluUnaryOp::Compliment => {
493                // Always sets only SUB and HALFCARRY to 1.
494                const MASK: Flags = Flags::SUB.union(Flags::HALFCARRY);
495                InstrBuilder::read(Reg8::Acc)
496                    .then(Microcode::Compliment)
497                    .then_write(MASK)
498                    .then_write(Reg8::Acc)
499            }
500            AluUnaryOp::SetCarryFlag => {
501                const MASK: Flags = Flags::all().difference(Flags::ZERO);
502                InstrBuilder::first(Microcode::Append {
503                    val: Flags::CARRY.bits(),
504                })
505                .then_write(MASK)
506            }
507            AluUnaryOp::ComplimentCarryFlag => {
508                // stack: ...|
509                // Fetch the carry flag from the flags register.
510                // stack: ...|000C|
511                InstrBuilder::read(Flags::CARRY)
512                    // Compliment the carry flags.
513                    // stack: ...|111c|flgs|
514                    .then(Microcode::Compliment)
515                    // Discard the flags from the complement operation.
516                    // stack: ...|111c|
517                    .then(Microcode::Discard8)
518                    // Overwrite the carry flag:
519                    // stack: ...|
520                    .then_write(Flags::CARRY)
521                    // Add a 0 to use to clear the SUB and HALFCARRY flags, as CCF always
522                    // sets those to 0.
523                    // stack: ...|0000|
524                    .then(Microcode::Append { val: 0 })
525                    // Overwrite the SUB and HALFCARRY flags.
526                    // stack: ...|
527                    .then_write(Flags::SUB.union(Flags::HALFCARRY))
528            }
529        }
530    }
531}
532
533/// As a MicrocodeReadable, `Operand8` will result in a u8 being pushed onto the microcode
534/// stack from the appropriate source.
535impl MicrocodeReadable for Operand8 {
536    fn to_read(self) -> InstrBuilder {
537        match self {
538            Self::A => InstrBuilder::read(Reg8::Acc),
539            Self::B => InstrBuilder::read(Reg8::B),
540            Self::C => InstrBuilder::read(Reg8::C),
541            Self::D => InstrBuilder::read(Reg8::D),
542            Self::E => InstrBuilder::read(Reg8::E),
543            Self::H => InstrBuilder::read(Reg8::H),
544            Self::L => InstrBuilder::read(Reg8::L),
545            Self::AddrHL => InstrBuilder::read(Reg16::HL).then_read(Mem),
546            Self::AddrBC => InstrBuilder::read(Reg16::BC).then_read(Mem),
547            Self::AddrDE => InstrBuilder::read(Reg16::DE).then_read(Mem),
548            Self::AddrHLInc => {
549                // stack: ...|
550                // Retrieve HL and increment it.
551                // stack: ...|h|l|
552                InstrBuilder::read(HlInc)
553                    // Use hl to read the value from memory.
554                    // stack: ...|v|
555                    .then_read(Mem)
556            }
557            Self::AddrHLDec => {
558                // stack: ...|
559                // Retrieve HL and decrement it.
560                // stack: ...|h|l|
561                InstrBuilder::read(HlDec)
562                    // Use the original hl value to read the value from memory.
563                    // stack: ...|v|
564                    .then_read(Mem)
565            }
566            Self::Immediate => InstrBuilder::read(Immediate8),
567            Self::AddrImmediate => InstrBuilder::read(Immediate16).then_read(Mem),
568            Self::AddrRelC => InstrBuilder::read(Reg8::C)
569                // We want to build a pair |l|h| on the top of the stack as the address to
570                // read from, so after reading C as the low byte of the address, we just
571                // push the 0xff as the high byte.
572                .then(Microcode::Append { val: 0xff })
573                .then_read(Mem),
574            Self::AddrRelImmediate => InstrBuilder::read(Immediate8)
575                // We want to build a pair |l|h| on the top of the stack as the address to
576                // read from, so after reading an immediate as the low byte of the
577                // address, we just push the 0xff as the high byte.
578                .then(Microcode::Append { val: 0xff })
579                .then_read(Mem),
580        }
581    }
582}
583
584/// As a MicrocodeWritable, `Operand8` will result in a u8 popped off of the stack and
585/// written to the appropriate destination.
586impl MicrocodeWritable for Operand8 {
587    fn to_write(self) -> InstrBuilder {
588        match self {
589            Self::A => InstrBuilder::write(Reg8::Acc),
590            Self::B => InstrBuilder::write(Reg8::B),
591            Self::C => InstrBuilder::write(Reg8::C),
592            Self::D => InstrBuilder::write(Reg8::D),
593            Self::E => InstrBuilder::write(Reg8::E),
594            Self::H => InstrBuilder::write(Reg8::H),
595            Self::L => InstrBuilder::write(Reg8::L),
596            Self::AddrHL => InstrBuilder::read(Reg16::HL).then_write(Mem),
597            Self::AddrBC => InstrBuilder::read(Reg16::BC).then_write(Mem),
598            Self::AddrDE => InstrBuilder::read(Reg16::DE).then_write(Mem),
599            Self::AddrHLInc => {
600                // stack: ...|v|
601                // Retrieve HL and increment it.
602                // stack: ...|v|h|l|
603                InstrBuilder::read(HlInc)
604                    // Use hl to write the value to memory.
605                    // stack: ...|
606                    .then_write(Mem)
607            }
608            Self::AddrHLDec => {
609                // stack: ...|v|
610                // Retrieve HL and decrement it.
611                // stack: ...|v|h|l|
612                InstrBuilder::read(HlDec)
613                    // Use hl to write the value to memory.
614                    // stack: ...|
615                    .then_write(Mem)
616            }
617            Self::Immediate => panic!("Immediates cannot be used as store destinations"),
618            Self::AddrImmediate => InstrBuilder::read(Immediate16).then_write(Mem),
619            Self::AddrRelC => InstrBuilder::read(Reg8::C)
620                // We want to build a pair |l|h| on the top of the stack as the address to
621                // write to, so after reading C as the low byte of the address, we just
622                // push the 0xff as the high byte.
623                .then(Microcode::Append { val: 0xff })
624                .then_write(Mem),
625            Self::AddrRelImmediate => InstrBuilder::read(Immediate8)
626                // We want to build a pair |l|h| on the top of the stack as the address to
627                // write to, so after reading an immediate as the low byte of the address,
628                // we just push the 0xff as the high byte.
629                .then(Microcode::Append { val: 0xff })
630                .then_write(Mem),
631        }
632    }
633}
634
635/// As a MicrocodeReadable, `Operand16` will result in a u16 being pushed onto the
636/// microcode stack from the appropriate source.
637impl MicrocodeReadable for Operand16 {
638    fn to_read(self) -> InstrBuilder {
639        match self {
640            Self::BC => InstrBuilder::read(Reg16::BC),
641            Self::DE => InstrBuilder::read(Reg16::DE),
642            Self::HL => InstrBuilder::read(Reg16::HL),
643            Self::AF => InstrBuilder::read(Reg16::AF),
644            Self::Sp => InstrBuilder::read(Reg16::Sp),
645            Self::Immediate => InstrBuilder::read(Immediate16),
646            Self::AddrImmediate => {
647                panic!("No actual operation uses (u16) as the source for a 16 bit load")
648            }
649        }
650    }
651}
652
653/// As a MicrocodeWritable, `Operand16` will result in a u16 popped off of the stack and
654/// written to the appropriate destination.
655impl MicrocodeWritable for Operand16 {
656    fn to_write(self) -> InstrBuilder {
657        match self {
658            Self::BC => InstrBuilder::write(Reg16::BC),
659            Self::DE => InstrBuilder::write(Reg16::DE),
660            Self::HL => InstrBuilder::write(Reg16::HL),
661            Self::AF => InstrBuilder::write(Reg16::AF),
662            Self::Sp => InstrBuilder::write(Reg16::Sp),
663            Self::Immediate => panic!("Immediates cannot be used as store destinations"),
664            // First get the address to write to.
665            // stack: ...|vall|valh|destl|desth|
666            Self::AddrImmediate => InstrBuilder::read(Immediate16)
667                // Intersperse inverts the order of the value bytes and puts copies of the
668                // address between them, which sets us up perfectly for the needed writes.
669                // stack: ...|valh|destl|desth|vall|destl|desth|
670                .then(Microcode::Intersperse)
671                // Write the low byte to memory at the specified address.
672                // stack: ...|valh|destl|desth|
673                .then_write(Mem)
674                // Increment the destination address to get the address for the second
675                // byte.
676                // stack: ...|valh|DESTL|DESTH|
677                .then(Microcode::Inc16)
678                // Write the high byte to memory at the specified address.
679                // stack: ...|
680                .then_write(Mem),
681        }
682    }
683}
684
685impl ConditionCode {
686    /// Wrap the provided microcode in a conditional handling. If unconditional, just
687    /// returns the `code_if_condition_true`. If conditional, fetches the appropriate flag
688    /// and applies a micorcode conditional to run the provided code only when the
689    /// condition matches.
690    fn cond(
691        self,
692        code_if_condition_true: impl Into<InstrBuilder>,
693        code_if_condition_false: impl Into<InstrBuilder>,
694    ) -> InstrBuilder {
695        match self {
696            ConditionCode::Unconditional => code_if_condition_true.into(),
697            // To implement the inverted flags (NonZero and NoCarry), we execute the
698            // "code_if_condition_true" in the "false" case of the InstrBuilder::cond.
699            ConditionCode::NonZero => InstrBuilder::read(Flags::ZERO).then(InstrBuilder::cond(
700                code_if_condition_false,
701                code_if_condition_true,
702            )),
703            ConditionCode::Zero => InstrBuilder::read(Flags::ZERO).then(InstrBuilder::cond(
704                code_if_condition_true,
705                code_if_condition_false,
706            )),
707            ConditionCode::NoCarry => InstrBuilder::read(Flags::CARRY).then(InstrBuilder::cond(
708                code_if_condition_false,
709                code_if_condition_true,
710            )),
711            ConditionCode::Carry => InstrBuilder::read(Flags::CARRY).then(InstrBuilder::cond(
712                code_if_condition_true,
713                code_if_condition_false,
714            )),
715        }
716    }
717
718    /// Wrap the given code to run only if this conditional evaluates to true. If
719    /// unconditional, returns the microcode unchanged, otherwise runs the specified code
720    /// only if the appropriate flag matches.
721    fn if_true(self, code_if_condition_true: impl Into<InstrBuilder>) -> InstrBuilder {
722        self.cond(code_if_condition_true, InstrBuilder::new())
723    }
724}