1#![forbid(unsafe_code)]
2#![warn(missing_debug_implementations)]
3#![warn(missing_copy_implementations)]
4#![warn(missing_docs)]
5
6use str_id::StrID;
13
14#[test]
15fn check_type_size() {
16 assert_eq!(size_of::<Asm>(), size_of::<[usize; 2]>());
19}
20
21#[derive(Debug, Clone)]
31#[non_exhaustive]
32pub enum Asm {
33 Label(StrID),
41
42 LdReg8Reg8(Reg8, Reg8),
47
48 LdReg8Imm8(Reg8, u8),
50
51 LdReg16Lit(Reg16, u16),
53
54 LdReg16Sym(Reg16, StrID),
56
57 LdHltReg8(Reg8),
59
60 LdHltImm8(u8),
62
63 LdReg8Hlt(Reg8),
65
66 LdReg16tA(Reg16),
68
69 LdLitA(u16),
71
72 LdSymA(StrID),
74
75 LdhLitA(u16),
77
78 LdhSymA(StrID),
80
81 LdhCA,
83
84 LdAReg16t(Reg16),
86
87 LdALit(u16),
89
90 LdASym(StrID),
92
93 LdhALit(u16),
95
96 LdhASym(StrID),
98
99 LdhAC,
101
102 LdHliA,
104
105 LdHldA,
107
108 LdAHli,
110
111 LdAHld,
113
114 BinOpReg8(BinOp, Reg8),
116
117 BinOpHlt(BinOp),
119
120 BinOpImm8(BinOp, u8),
122
123 DecReg8(Reg8),
125
126 DecHlt,
128
129 IncReg8(Reg8),
131
132 IncHlt,
134
135 AddHlReg16(Reg16),
137
138 DecReg16(Reg16),
140
141 IncReg16(Reg16),
143
144 Cpl,
146
147 BitTestReg8(U3, Reg8),
149
150 BitTestHlt(U3),
152
153 ResetReg8(U3, Reg8),
155
156 ResetHlt(U3),
158
159 SetReg8(U3, Reg8),
161
162 SetHlt(U3),
164
165 UnOpReg8(UnOp, Reg8),
167
168 UnOpHlt(UnOp),
170
171 Rla,
173
174 Rlca,
176
177 Rra,
179
180 Rrca,
182
183 CallLit(u16),
185
186 CallSym(StrID),
188
189 CallCondLit(Cond, u16),
191
192 CallCondSym(Cond, StrID),
194
195 JumpHl,
197
198 JumpLit(u16),
200
201 JumpSym(StrID),
203
204 JumpCondLit(Cond, u16),
206
207 JumpCondSym(Cond, StrID),
209
210 JumpRelLit(u16),
212
213 JumpRelSym(StrID),
215
216 JumpRelCondLit(Cond, u16),
218
219 JumpRelCondSym(Cond, StrID),
221
222 ReturnCond(Cond),
224
225 Return,
227
228 ReturnFromInterrupt,
230
231 Reset(RstVec),
233
234 Ccf,
236
237 Scf,
239
240 AddHlSp,
242
243 AddSpDelta(i8),
245
246 DecSp,
248
249 IncSp,
251
252 LdSpLit(u16),
254
255 LdSpSym(StrID),
257
258 LdLitSp(u16),
260
261 LdSymSp(StrID),
263
264 LdHlSpDelta(i8),
266
267 LdSpHl,
269
270 PopAF,
272
273 PopReg16(Reg16),
275
276 PushAF,
278
279 PushReg16(Reg16),
281
282 DI,
284
285 EI,
287
288 Halt,
290
291 DAA,
293
294 Nop,
298
299 Stop,
306
307 DataBytes(Box<Vec<u8>>),
316
317 TileIndexes(Box<Vec<u32>>),
342}
343impl Asm {
344 #[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#[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#[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#[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#[derive(Debug, Clone, Copy)]
646pub enum Cond {
647 Z,
649 NZ,
651 C,
653 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#[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#[derive(Debug, Clone, Copy)]
707pub enum UnOp {
708 Rl,
710 Rlc,
712 Rr,
714 Rrc,
716 Sla,
718 Sra,
720 Srl,
722 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#[derive(Debug, Clone, Copy)]
746pub enum BinOp {
747 Adc,
749 Add,
751 And,
753 Cp,
758 Or,
760 Sbc,
762 Sub,
764 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}