Skip to main content

yagbas_asmtypes/
lib.rs

1#![forbid(unsafe_code)]
2#![warn(missing_debug_implementations)]
3#![warn(missing_copy_implementations)]
4#![warn(missing_docs)]
5
6//! The types for assembly manipulation by the [yagbas](https://docs.rs/yagbas)
7//! compiler.
8//!
9//! This crate is a sub-crate of yagbas. Use in any other contexts is not
10//! intentionally supported.
11
12use str_id::StrID;
13
14#[test]
15fn check_type_size() {
16  // Note(Lokathor): It's not an absolute requirement that the size never
17  // change, but we should definitely at least be aware of any change in size.
18  assert_eq!(size_of::<Asm>(), size_of::<[usize; 2]>());
19}
20
21/// A single line of assembly code.
22///
23/// Instructions that use addresses have both a [u16] "literal" form as well as
24/// a [StrID] "symbol" form, which keeps the size of the `Asm` type smaller. The
25/// address of a symbol is always a 16-bit value, but the final value generally
26/// isn't known until linking is performed.
27///
28/// Instructions that use "the HL target" are directed at the 8-bit memory
29/// location that is currently in the `HL` register.
30#[derive(Debug, Clone)]
31#[non_exhaustive]
32pub enum Asm {
33  /// A label within the code.
34  ///
35  /// The [Label Declaration][label-dec] rules of RGBDS allow standard C-style
36  /// names, and also `#` and `@` are allowed. The `.` is a special character
37  /// used for local labels, read their docs for an explanation.
38  ///
39  /// [label-dec]: https://rgbds.gbdev.io/docs/v0.9.1/rgbasm.5#Label_declaration
40  Label(StrID),
41
42  /// `ld reg8, reg8`
43  ///
44  /// On some emulators, certain operations which load a value back into the
45  /// same register are used to help with debugging.
46  LdReg8Reg8(Reg8, Reg8),
47
48  /// `ld reg8, imm8`
49  LdReg8Imm8(Reg8, u8),
50
51  /// `ld reg16, imm16`
52  LdReg16Lit(Reg16, u16),
53
54  /// `ld reg16, sym`
55  LdReg16Sym(Reg16, StrID),
56
57  /// `ld [hl], reg8`
58  LdHltReg8(Reg8),
59
60  /// `ld [hl], imm8`
61  LdHltImm8(u8),
62
63  /// `ld reg8, [hl]`
64  LdReg8Hlt(Reg8),
65
66  /// `ld [reg16], a`
67  LdReg16tA(Reg16),
68
69  /// `ld [imm16], a`
70  LdLitA(u16),
71
72  /// `ld [sym], a`
73  LdSymA(StrID),
74
75  /// `ldh [imm16], a`
76  LdhLitA(u16),
77
78  /// `ldh [sym], a`
79  LdhSymA(StrID),
80
81  /// `ldh [c], a`
82  LdhCA,
83
84  /// `ld a, [reg16]`
85  LdAReg16t(Reg16),
86
87  /// `ld a, [imm16]`
88  LdALit(u16),
89
90  /// `ld a, [sym]`
91  LdASym(StrID),
92
93  /// `ldh a, [imm16]`
94  LdhALit(u16),
95
96  /// `ldh a, [sym]`
97  LdhASym(StrID),
98
99  /// `ldh a, [c]`
100  LdhAC,
101
102  /// `ld [hli], a`
103  LdHliA,
104
105  /// `ld [hld], a`
106  LdHldA,
107
108  /// `ld a, [hli]`
109  LdAHli,
110
111  /// `ld a, [hld]`
112  LdAHld,
113
114  /// `bin_op a, reg8`
115  BinOpReg8(BinOp, Reg8),
116
117  /// `bin_op a, [hl]`
118  BinOpHlt(BinOp),
119
120  /// `bin_op a, imm8`
121  BinOpImm8(BinOp, u8),
122
123  /// `dec reg8`
124  DecReg8(Reg8),
125
126  /// `dec [hl]`
127  DecHlt,
128
129  /// `inc reg8`
130  IncReg8(Reg8),
131
132  /// `inc [hl]`
133  IncHlt,
134
135  /// `add hl, reg16`
136  AddHlReg16(Reg16),
137
138  /// `dec reg16`
139  DecReg16(Reg16),
140
141  /// `inc reg16`
142  IncReg16(Reg16),
143
144  /// `cpl a`, "compliment A"
145  Cpl,
146
147  /// `bit u3, reg8`
148  BitTestReg8(U3, Reg8),
149
150  /// `bit u3, [hl]`
151  BitTestHlt(U3),
152
153  /// `res u3, reg8`
154  ResetReg8(U3, Reg8),
155
156  /// `res u3, [hl]`
157  ResetHlt(U3),
158
159  /// `set u3, reg8`
160  SetReg8(U3, Reg8),
161
162  /// `set u3, [hl]`
163  SetHlt(U3),
164
165  /// `un_op reg8`
166  UnOpReg8(UnOp, Reg8),
167
168  /// `un_op [hl]`
169  UnOpHlt(UnOp),
170
171  /// `rla`
172  Rla,
173
174  /// `rlca`
175  Rlca,
176
177  /// `rra`
178  Rra,
179
180  /// `rrca`
181  Rrca,
182
183  /// `call lit`
184  CallLit(u16),
185
186  /// `call sym`
187  CallSym(StrID),
188
189  /// `call cond, lit`
190  CallCondLit(Cond, u16),
191
192  /// `call cond, sym`
193  CallCondSym(Cond, StrID),
194
195  /// `jp hl`
196  JumpHl,
197
198  /// `jp lit`
199  JumpLit(u16),
200
201  /// `jp sym`
202  JumpSym(StrID),
203
204  /// `jp cond, lit`
205  JumpCondLit(Cond, u16),
206
207  /// `jp cond, sym`
208  JumpCondSym(Cond, StrID),
209
210  /// `jr lit`
211  JumpRelLit(u16),
212
213  /// `jp sym`
214  JumpRelSym(StrID),
215
216  /// `jr cond, lit`
217  JumpRelCondLit(Cond, u16),
218
219  /// `jr cond, sym`
220  JumpRelCondSym(Cond, StrID),
221
222  /// `ret cond`
223  ReturnCond(Cond),
224
225  /// `ret`
226  Return,
227
228  /// `reti`
229  ReturnFromInterrupt,
230
231  /// `rst vec`
232  Reset(RstVec),
233
234  /// `ccf`, "compliment carry flag"
235  Ccf,
236
237  /// `scf`, "set carry flag"
238  Scf,
239
240  /// `add hl, sp`
241  AddHlSp,
242
243  /// `add sp, i8`
244  AddSpDelta(i8),
245
246  /// `dec sp`
247  DecSp,
248
249  /// `inc sp`
250  IncSp,
251
252  /// `ld sp, lit`
253  LdSpLit(u16),
254
255  /// `ld sp, sym`
256  LdSpSym(StrID),
257
258  /// `ld [lit], sp`
259  LdLitSp(u16),
260
261  /// `ld [sym], sp`
262  LdSymSp(StrID),
263
264  /// `ld hl, sp+i8`
265  LdHlSpDelta(i8),
266
267  /// `ld sp, hl`
268  LdSpHl,
269
270  /// `pop af`
271  PopAF,
272
273  /// `pop reg16`
274  PopReg16(Reg16),
275
276  /// `push af`
277  PushAF,
278
279  /// `push reg16`
280  PushReg16(Reg16),
281
282  /// `di`
283  DI,
284
285  /// `ei`
286  EI,
287
288  /// `halt`
289  Halt,
290
291  /// `daa`, "Decimal Adjust Accumulator".
292  DAA,
293
294  /// `nop`, "no operation"
295  ///
296  /// This instruction does nothing but spend some time.
297  Nop,
298
299  /// `stop`
300  ///
301  /// * This instruction is 1 byte itself but should *always* be followed by a
302  ///   `nop`, because the instruction after `stop` is executed or not depending
303  ///   on some very hard to predict factors.
304  /// * See [Pandocs: Using the STOP Instruction](https://gbdev.io/pandocs/Reducing_Power_Consumption.html#using-the-stop-instruction)
305  Stop,
306
307  /// A sequence of data bytes that should appear directly in the assembly.
308  ///
309  /// This is generally static data of some form, but it could also be code
310  /// you've encoded by hand if you need to be sure the assembly optimizer won't
311  /// shorten a certain code segment.
312  ///
313  /// The double-indirection of the payload data lets us keep the size of the
314  /// overall `Asm` type as small as possible.
315  DataBytes(Box<Vec<u8>>),
316
317  /// A sequence of pixel indexes for tile data.
318  ///
319  /// A tile is always 8 pixels tall, so this is *expected* to always contain a
320  /// multiple of 8 values, but that is not a hard requirement.
321  ///
322  /// GB tiles are index-mapped, with 2 bits per pixel. Each row of eight
323  /// indexes is 2 bytes, but instead of being directly encoded as two `[u2;4]`
324  /// values in a row, they are split into "bitplanes". The first byte stores
325  /// each low bit of the eight indexs, and the second byte stores the high bits
326  /// of the eight indexes. Because this is complicated to reason about when
327  /// trying to enter data by hand, `rgbasm` allows for using the \` character
328  /// followed by a series of plain digits to directly output index values, and
329  /// it will convert the data for you.
330  ///
331  /// ```text
332  /// dw `01230123 ; This is equivalent to `db $55,$33`
333  /// ```
334  ///
335  /// When using the backtick syntax, indexes are written with the digits left
336  /// to right as they would appear in the actual tile, so `10002222` has an
337  /// index of `1` in the leftmost pixel and an index of `2` in all four of the
338  /// right side pixels. This means that a `u32` is large enough to hold any
339  /// pixel row we need to store, but we must always output the value using
340  /// `{val:08}` so that any leading zeroes are properly preserved.
341  TileIndexes(Box<Vec<u32>>),
342}
343impl Asm {
344  /// Determines the size of the instruction within a rom.
345  #[inline]
346  #[must_use]
347  pub fn rom_size(&self) -> usize {
348    match self {
349      Asm::Label(_) => 0,
350      Asm::LdReg8Reg8(_, _) => 1,
351      Asm::LdReg8Imm8(_, _) => 2,
352      Asm::LdReg16Lit(_, _) => 3,
353      Asm::LdReg16Sym(_, _) => 3,
354      Asm::LdHltReg8(_) => 1,
355      Asm::LdHltImm8(_) => 2,
356      Asm::LdReg8Hlt(_) => 1,
357      Asm::LdReg16tA(_) => 3,
358      Asm::LdLitA(_) => 3,
359      Asm::LdSymA(_) => 3,
360      Asm::LdhLitA(_) => 2,
361      Asm::LdhSymA(_) => 2,
362      Asm::LdhCA => 1,
363      Asm::LdAReg16t(_) => 3,
364      Asm::LdALit(_) => 3,
365      Asm::LdASym(_) => 3,
366      Asm::LdhALit(_) => 2,
367      Asm::LdhASym(_) => 2,
368      Asm::LdhAC => 1,
369      Asm::LdHliA => 1,
370      Asm::LdHldA => 1,
371      Asm::LdAHli => 1,
372      Asm::LdAHld => 1,
373      Asm::BinOpReg8(_, _) => 1,
374      Asm::BinOpHlt(_) => 1,
375      Asm::BinOpImm8(_, _) => 2,
376      Asm::DecReg8(_) => 1,
377      Asm::DecHlt => 1,
378      Asm::IncReg8(_) => 1,
379      Asm::IncHlt => 1,
380      Asm::AddHlReg16(_) => 1,
381      Asm::DecReg16(_) => 1,
382      Asm::IncReg16(_) => 1,
383      Asm::Cpl => 1,
384      Asm::BitTestReg8(_, _) => 2,
385      Asm::BitTestHlt(_) => 2,
386      Asm::ResetReg8(_, _) => 2,
387      Asm::ResetHlt(_) => 2,
388      Asm::SetReg8(_, _) => 2,
389      Asm::SetHlt(_) => 2,
390      Asm::UnOpReg8(_, _) => 2,
391      Asm::UnOpHlt(_) => 2,
392      Asm::Rla => 1,
393      Asm::Rlca => 1,
394      Asm::Rra => 1,
395      Asm::Rrca => 1,
396      Asm::CallLit(_) => 3,
397      Asm::CallSym(_) => 3,
398      Asm::CallCondLit(_, _) => 3,
399      Asm::CallCondSym(_, _) => 3,
400      Asm::JumpHl => 1,
401      Asm::JumpLit(_) => 3,
402      Asm::JumpSym(_) => 3,
403      Asm::JumpCondLit(_, _) => 3,
404      Asm::JumpCondSym(_, _) => 3,
405      Asm::JumpRelLit(_) => 2,
406      Asm::JumpRelSym(_) => 2,
407      Asm::JumpRelCondLit(_, _) => 2,
408      Asm::JumpRelCondSym(_, _) => 2,
409      Asm::ReturnCond(_) => 1,
410      Asm::Return => 1,
411      Asm::ReturnFromInterrupt => 1,
412      Asm::Reset(_) => 1,
413      Asm::Ccf => 1,
414      Asm::Scf => 1,
415      Asm::AddHlSp => 1,
416      Asm::AddSpDelta(_) => 1,
417      Asm::DecSp => 1,
418      Asm::IncSp => 1,
419      Asm::LdSpLit(_) => 3,
420      Asm::LdSpSym(_) => 3,
421      Asm::LdLitSp(_) => 3,
422      Asm::LdSymSp(_) => 3,
423      Asm::LdHlSpDelta(_) => 2,
424      Asm::LdSpHl => 1,
425      Asm::PopAF => 1,
426      Asm::PopReg16(_) => 1,
427      Asm::PushAF => 1,
428      Asm::PushReg16(_) => 1,
429      Asm::DI => 1,
430      Asm::EI => 1,
431      Asm::Halt => 1,
432      Asm::DAA => 1,
433      Asm::Nop => 1,
434      Asm::Stop => 1,
435      Asm::DataBytes(items) => items.len(),
436      Asm::TileIndexes(items) => items.len() * core::mem::size_of::<u16>(),
437    }
438  }
439}
440impl core::fmt::Display for Asm {
441  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
442    match self {
443      Asm::Label(label) => write!(f, "{label}:"),
444      Asm::LdReg8Reg8(dst, src) => write!(f, "ld {dst}, {src}"),
445      Asm::LdReg8Imm8(reg, imm) => write!(f, "ld {reg}, ${imm:02X}"),
446      Asm::LdReg16Lit(reg, lit) => write!(f, "ld {reg}, ${lit:04X}"),
447      Asm::LdReg16Sym(reg, sym) => write!(f, "ld {reg}, {sym}"),
448      Asm::LdHltReg8(reg) => write!(f, "ld [hl], {reg}"),
449      Asm::LdHltImm8(imm) => write!(f, "ld [hl], ${imm:02X}"),
450      Asm::LdReg8Hlt(reg) => write!(f, "ld {reg}, [hl]"),
451      Asm::LdReg16tA(reg) => write!(f, "ld [{reg}], a"),
452      Asm::LdLitA(lit) => write!(f, "ld [${lit:04X}], a"),
453      Asm::LdSymA(sym) => write!(f, "ld [{sym}], a"),
454      Asm::LdhLitA(lit) => write!(f, "ldh ${lit:04X}, a"),
455      Asm::LdhSymA(sym) => write!(f, "ldh {sym}, a"),
456      Asm::LdhCA => write!(f, "ld [c], a"),
457      Asm::LdAReg16t(reg) => write!(f, "ld a, [{reg}]"),
458      Asm::LdALit(lit) => write!(f, "ld a, [${lit:04X}]"),
459      Asm::LdASym(sym) => write!(f, "ld a, [{sym}]"),
460      Asm::LdhALit(lit) => write!(f, "ldh a, [${lit:04X}]"),
461      Asm::LdhASym(sym) => write!(f, "ldh a, [{sym}]"),
462      Asm::LdhAC => write!(f, "ldh a, [c]"),
463      Asm::LdHliA => write!(f, "ld [hli], a"),
464      Asm::LdHldA => write!(f, "ld [hld], a"),
465      Asm::LdAHli => write!(f, "ld a, [hli]"),
466      Asm::LdAHld => write!(f, "ld a, [hld]"),
467      Asm::BinOpReg8(op, reg) => write!(f, "{op} a, {reg}"),
468      Asm::BinOpHlt(op) => write!(f, "{op} a, [hl]"),
469      Asm::BinOpImm8(op, imm) => write!(f, "{op} a, ${imm:02X}"),
470      Asm::DecReg8(reg) => write!(f, "dec {reg}"),
471      Asm::DecHlt => write!(f, "dec [hl]"),
472      Asm::IncReg8(reg) => write!(f, "inc {reg}"),
473      Asm::IncHlt => write!(f, "inc [hl]"),
474      Asm::AddHlReg16(reg) => write!(f, "add hl, {reg}"),
475      Asm::DecReg16(reg) => write!(f, "dec {reg}"),
476      Asm::IncReg16(reg) => write!(f, "inc {reg}"),
477      Asm::Cpl => write!(f, "cpl a"),
478      Asm::BitTestReg8(u3, reg) => write!(f, "bit {u3}, {reg}"),
479      Asm::BitTestHlt(u3) => write!(f, "bit {u3}, [hl]"),
480      Asm::ResetReg8(u3, reg) => write!(f, "res {u3}, {reg}"),
481      Asm::ResetHlt(u3) => write!(f, "res {u3}, [hl]"),
482      Asm::SetReg8(u3, reg) => write!(f, "set {u3}, {reg}"),
483      Asm::SetHlt(u3) => write!(f, "set {u3}, [hl]"),
484      Asm::UnOpReg8(op, reg) => write!(f, "{op} {reg}"),
485      Asm::UnOpHlt(op) => write!(f, "{op} [hl]"),
486      Asm::Rla => write!(f, "rla"),
487      Asm::Rlca => write!(f, "rlca"),
488      Asm::Rra => write!(f, "rra"),
489      Asm::Rrca => write!(f, "rrca"),
490      Asm::CallLit(lit) => write!(f, "call ${lit:04X}"),
491      Asm::CallSym(sym) => write!(f, "call {sym}"),
492      Asm::CallCondLit(cond, lit) => write!(f, "call {cond}, ${lit:04X}"),
493      Asm::CallCondSym(cond, sym) => write!(f, "call {cond}, {sym}"),
494      Asm::JumpHl => write!(f, "jp hl"),
495      Asm::JumpLit(lit) => write!(f, "jp ${lit:04X}"),
496      Asm::JumpSym(sym) => write!(f, "jp {sym}"),
497      Asm::JumpCondLit(cond, lit) => write!(f, "jp {cond}, ${lit:04X}"),
498      Asm::JumpCondSym(cond, sym) => write!(f, "jp {cond}, {sym}"),
499      Asm::JumpRelLit(lit) => write!(f, "jr ${lit:04X}"),
500      Asm::JumpRelSym(sym) => write!(f, "jr {sym}"),
501      Asm::JumpRelCondLit(cond, lit) => write!(f, "jp {cond}, ${lit:04X}"),
502      Asm::JumpRelCondSym(cond, sym) => write!(f, "jp {cond}, {sym}"),
503      Asm::ReturnCond(cond) => write!(f, "ret {cond}"),
504      Asm::Return => write!(f, "ret"),
505      Asm::ReturnFromInterrupt => write!(f, "reti"),
506      Asm::Reset(vec) => write!(f, "rst {vec}"),
507      Asm::Ccf => write!(f, "ccf"),
508      Asm::Scf => write!(f, "scf"),
509      Asm::AddHlSp => write!(f, "add hl, sp"),
510      Asm::AddSpDelta(i) => write!(f, "add sp, {i}"),
511      Asm::DecSp => write!(f, "dec sp"),
512      Asm::IncSp => write!(f, "inc sp"),
513      Asm::LdSpLit(lit) => write!(f, "ld sp, ${lit:04X}"),
514      Asm::LdSpSym(sym) => write!(f, "ld sp, {sym}"),
515      Asm::LdLitSp(lit) => write!(f, "ld ${lit:04X}, sp"),
516      Asm::LdSymSp(sym) => write!(f, "ld {sym}, sp"),
517      Asm::LdHlSpDelta(i) => write!(f, "ld hl, sp{i:+}"),
518      Asm::LdSpHl => write!(f, "ld sp, hl"),
519      Asm::PopAF => write!(f, "pop af"),
520      Asm::PopReg16(reg) => write!(f, "pop {reg}"),
521      Asm::PushAF => write!(f, "push af"),
522      Asm::PushReg16(reg) => write!(f, "push {reg}"),
523      Asm::DI => write!(f, "di"),
524      Asm::EI => write!(f, "ei"),
525      Asm::Halt => write!(f, "halt"),
526      Asm::DAA => write!(f, "daa"),
527      Asm::Nop => write!(f, "nop"),
528      Asm::Stop => write!(f, "stop"),
529      Asm::DataBytes(items) => {
530        for (i, chunk) in items.chunks(16).enumerate() {
531          if i > 0 {
532            writeln!(f)?;
533          }
534          write!(f, "db ")?;
535          for (i, c) in chunk.iter().enumerate() {
536            if i > 0 {
537              write!(f, ", ")?;
538            }
539            write!(f, "${c:02X}")?;
540          }
541        }
542        Ok(())
543      }
544      Asm::TileIndexes(items) => {
545        for (i, item) in items.iter().enumerate() {
546          if i > 0 {
547            writeln!(f)?;
548          }
549          if *item > 99999999_u32 {
550            eprintln!("Warning: Illegal pixel index value: {item}");
551          }
552          write!(f, "dw `{item:08}")?;
553        }
554        Ok(())
555      }
556    }
557  }
558}
559
560/// The 8-bit registers usable with most 8-bit instructions.
561#[derive(Debug, Clone, Copy)]
562#[allow(missing_docs)]
563pub enum Reg8 {
564  A,
565  B,
566  C,
567  D,
568  E,
569  H,
570  L,
571}
572impl core::fmt::Display for Reg8 {
573  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
574    write!(
575      f,
576      "{}",
577      match self {
578        Reg8::A => "a",
579        Reg8::B => "b",
580        Reg8::C => "c",
581        Reg8::D => "d",
582        Reg8::E => "e",
583        Reg8::H => "h",
584        Reg8::L => "l",
585      }
586    )
587  }
588}
589
590/// The three 16-bit registers available for use with most 16-bit instructions.
591#[derive(Debug, Clone, Copy)]
592#[allow(missing_docs)]
593pub enum Reg16 {
594  BC,
595  DE,
596  HL,
597}
598impl core::fmt::Display for Reg16 {
599  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
600    write!(
601      f,
602      "{}",
603      match self {
604        Reg16::BC => "bc",
605        Reg16::DE => "de",
606        Reg16::HL => "hl",
607      }
608    )
609  }
610}
611
612/// An unsigned 3-bit value, a value in `0 ..= 7`.
613#[derive(Debug, Clone, Copy)]
614#[allow(missing_docs)]
615pub enum U3 {
616  _0,
617  _1,
618  _2,
619  _3,
620  _4,
621  _5,
622  _6,
623  _7,
624}
625impl core::fmt::Display for U3 {
626  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
627    write!(
628      f,
629      "{}",
630      match self {
631        U3::_0 => "0",
632        U3::_1 => "1",
633        U3::_2 => "2",
634        U3::_3 => "3",
635        U3::_4 => "4",
636        U3::_5 => "5",
637        U3::_6 => "6",
638        U3::_7 => "7",
639      }
640    )
641  }
642}
643
644/// A CPU condition.
645#[derive(Debug, Clone, Copy)]
646pub enum Cond {
647  /// Zero
648  Z,
649  /// Non-zero
650  NZ,
651  /// Carry
652  C,
653  /// No-carry
654  NC,
655}
656impl core::fmt::Display for Cond {
657  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
658    write!(
659      f,
660      "{}",
661      match self {
662        Cond::Z => "z",
663        Cond::NZ => "nz",
664        Cond::C => "c",
665        Cond::NC => "nc",
666      }
667    )
668  }
669}
670
671/// One of the reset vector addresses.
672///
673/// The values are written using hexadecimal.
674#[derive(Debug, Clone, Copy)]
675#[allow(missing_docs)]
676pub enum RstVec {
677  X00,
678  X08,
679  X10,
680  X18,
681  X20,
682  X28,
683  X30,
684  X38,
685}
686impl core::fmt::Display for RstVec {
687  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
688    write!(
689      f,
690      "{}",
691      match self {
692        RstVec::X00 => "$00",
693        RstVec::X08 => "$08",
694        RstVec::X10 => "$10",
695        RstVec::X18 => "$18",
696        RstVec::X20 => "$20",
697        RstVec::X28 => "$28",
698        RstVec::X30 => "$30",
699        RstVec::X38 => "$38",
700      }
701    )
702  }
703}
704
705/// An unary operation, that operates on a single register.
706#[derive(Debug, Clone, Copy)]
707pub enum UnOp {
708  /// Rotate left (with carry).
709  Rl,
710  /// Rotate left (carryless).
711  Rlc,
712  /// Rotate right (with carry).
713  Rr,
714  /// Rotate right (carryless).
715  Rrc,
716  /// Shift left arithmetic.
717  Sla,
718  /// Shift right arithmetic.
719  Sra,
720  /// Shift right logical.
721  Srl,
722  /// Swap the high and low 4-bit chunks.
723  Swap,
724}
725impl core::fmt::Display for UnOp {
726  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
727    write!(
728      f,
729      "{}",
730      match self {
731        UnOp::Rl => "rl",
732        UnOp::Rlc => "rlc",
733        UnOp::Rr => "rr",
734        UnOp::Rrc => "rrc",
735        UnOp::Sla => "sla",
736        UnOp::Sra => "sra",
737        UnOp::Srl => "srl",
738        UnOp::Swap => "swap",
739      }
740    )
741  }
742}
743
744/// A binary operation, using `A` and another register, outputting to `A`.
745#[derive(Debug, Clone, Copy)]
746pub enum BinOp {
747  /// Add with carry
748  Adc,
749  /// Add
750  Add,
751  /// BitAnd
752  And,
753  /// Compare (sets flags based on `a - x` result)
754  ///
755  /// * Zero flag is set when `x` is equal to `a`
756  /// * Carry flag is set when `x` is greater than `a`
757  Cp,
758  /// BitOr
759  Or,
760  /// Subtract with carry
761  Sbc,
762  /// Subtract
763  Sub,
764  /// BitXor
765  Xor,
766}
767impl core::fmt::Display for BinOp {
768  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
769    write!(
770      f,
771      "{}",
772      match self {
773        BinOp::Adc => "adc",
774        BinOp::Add => "add",
775        BinOp::And => "and",
776        BinOp::Cp => "cp",
777        BinOp::Or => "or",
778        BinOp::Sbc => "sbc",
779        BinOp::Sub => "sub",
780        BinOp::Xor => "xor",
781      }
782    )
783  }
784}