1 /**
2  * Intel 8080 assembler.
3  */
4 module a80.i80;
5 import std.stdio;
6 import std.algorithm;
7 import std.string;
8 import std.ascii;
9 import std.conv;
10 import std.exception;
11 
12 /**
13  * Line number.
14  */
15 static size_t line;
16 
17 /**
18  * Pass.
19  */
20 static int pass;
21 
22 /**
23  * Output stored in memory until we're finished.
24  */
25 static ubyte[] output;
26 
27 /**
28  * Address for labels.
29  */
30 static ushort addr;
31 
32 /**
33  * Symbol table.
34  */
35 struct symtab
36 {
37     string name;        /// Symbol name
38     ushort value;       /// Symbol address
39 };
40 
41 /**
42  * Symbol table is an array of entries.
43  */
44 static symtab[] stab;
45 
46 /**
47  * 8 and 16 bit immediates
48  */
49 enum IMM8 = 8;
50 enum IMM16 = 16;
51 
52 /**
53  * Intel 8080 assembler instruction.
54  */
55 class i80
56 {
57     string lab;         /// Label
58     string op;          /// Instruction mnemonic
59     string a1;          /// First argument
60     string a2;          /// Second argument
61     string comm;        /// Comment
62 
63     /**
64      * Parse each line into (up to) five tokens.
65      */
66     void parse(string line) {
67         auto dbFix = 0;
68         auto preprocess = stripLeft(line);
69 
70         auto splitcomm = preprocess.findSplit(";");
71         if (!splitcomm[2].empty)
72             comm = strip(splitcomm[2]);
73 
74         auto splita2 = splitcomm[0].findSplit(",");
75         if (!splita2[2].empty)
76             a2 = strip(splita2[2]);
77 
78         auto splita1 = splita2[0].findSplit("\t");
79         if (!splita1[2].empty) {
80             a1 = strip(splita1[2]);
81         } else {
82             splita1 = splita2[0].findSplit(" ");
83             if (!splita1[2].empty) {
84                 a1 = strip(splita1[2]);
85             }
86         }
87 
88         /**
89          * Fixup for the db 'string$' case.
90          */
91         if ((!a1.empty && (a1[0] == '\'' || a1[a1.length - 1] == '\'')) ||
92             (!a2.empty && (a2[0] == '\'' || a2[a2.length - 1] == '\''))) {
93             splita1 = splitcomm[0].findSplit("'");
94             a1 = strip(chompPrefix(chop(strip(splita1[2])), "'"));
95             a2 = null;
96             dbFix = 1;
97         }
98 
99         auto splitop = splita1[0].findSplit(":");
100         if (!splitop[1].empty) {
101             op = strip(splitop[2]);
102             lab = strip(splitop[0]);
103         } else {
104             op = strip(splitop[0]);
105         }
106 
107         /**
108          * Fixup for the label: op case.
109          */
110         if (dbFix == 0) {
111             auto opFix = a1.findSplit("\t");
112             if (!opFix[1].empty) {
113                 op = strip(opFix[0]);
114                 a1 = strip(opFix[2]);
115             } else {
116                 opFix = a1.findSplit(" ");
117                 if (!opFix[1].empty) {
118                     op = strip(opFix[0]);
119                     a1 = strip(opFix[2]);
120                 }
121             }
122         }
123     }
124 }
125 
126 /**
127  * Top-level assembly function.
128  * Everything cascades downward from here.
129  * Repeat the parsing twice.
130  * Pass 1 gathers symbols and their addresses/values.
131  * Pass 2 emits code.
132  */
133 void assemble(string[] s, string name)
134 {
135     /* Pass 1 */
136     pass = 1;
137     for (line = 0; line < s.length; line++) {
138         i80 insn = new i80;
139 
140         insn.parse(s[line]);
141         process(insn);
142     }
143 
144     /* Pass 2 */
145     pass = 2;
146     for (line = 0; line < s.length; line++) {
147         i80 insn = new i80;
148 
149         insn.parse(s[line]);
150         process(insn);
151     }
152 
153     fileWrite(name);
154 }
155 
156 /**
157  * After all code is emitted, write it out to a file.
158  */
159 static void fileWrite(string name) {
160     import std.file : write;
161 
162     write(name, output);
163 }
164 
165 /**
166  * Figure out which op we have.
167  */
168 static void process(i80 insn)
169 {
170     auto op = insn.op;
171 
172     /**
173      * Special case for if you put a label by itself on a line.
174      * Or have a totally blank line.
175      */
176     if (insn.op.empty && insn.a1.empty && insn.a2.empty) {
177         passAct(0, -1, insn);
178         return;
179     }
180 
181     /**
182      * Remember: we're trying to demystify.
183      * You (the reader) can do better.
184      * Perhaps try a hash table?
185      */
186     if (op == "nop")
187         nop(insn);
188     else if (op == "lxi")
189         lxi(insn);
190     else if (op == "stax")
191         stax(insn);
192     else if (op == "inx")
193         inx(insn);
194     else if (op == "inr")
195         inr(insn);
196     else if (op == "dcr")
197         dcr(insn);
198     else if (op == "mvi")
199         mvi(insn);
200     else if (op == "rlc")
201         rlc(insn);
202     else if (op == "dad")
203         dad(insn);
204     else if (op == "ldax")
205         ldax(insn);
206     else if (op == "dcx")
207         dcx(insn);
208     else if (op == "rrc")
209         rrc(insn);
210     else if (op == "ral")
211         ral(insn);
212     else if (op == "rar")
213         rar(insn);
214     else if (op == "shld")
215         shld(insn);
216     else if (op == "daa")
217         daa(insn);
218     else if (op == "lhld")
219         lhld(insn);
220     else if (op == "cma")
221         cma(insn);
222     else if (op == "sta")
223         sta(insn);
224     else if (op == "stc")
225         stc(insn);
226     else if (op == "lda")
227         lda(insn);
228     else if (op == "cmc")
229         cmc(insn);
230     else if (op == "mov")
231         mov(insn);
232     else if (op == "hlt")
233         hlt(insn);
234     else if (op == "add")
235         add(insn);
236     else if (op == "adc")
237         adc(insn);
238     else if (op == "sub")
239         sub(insn);
240     else if (op == "sbb")
241         sbb(insn);
242     else if (op == "ana")
243         ana(insn);
244     else if (op == "xra")
245         xra(insn);
246     else if (op == "ora")
247         ora(insn);
248     else if (op == "cmp")
249         cmp(insn);
250     else if (op == "rnz")
251         rnz(insn);
252     else if (op == "pop")
253         pop(insn);
254     else if (op == "jnz")
255         jnz(insn);
256     else if (op == "jmp")
257         jmp(insn);
258     else if (op == "cnz")
259         cnz(insn);
260     else if (op == "push")
261         push(insn);
262     else if (op == "adi")
263         adi(insn);
264     else if (op == "rst")
265         rst(insn);
266     else if (op == "rz")
267         rz(insn);
268     else if (op == "jz")
269         jz(insn);
270     else if (op == "cz")
271         cz(insn);
272     else if (op == "call")
273         call(insn);
274     else if (op == "aci")
275         aci(insn);
276     else if (op == "rnc")
277         rnc(insn);
278     else if (op == "jnc")
279         jnc(insn);
280     else if (op == "out")
281         i80_out(insn);
282     else if (op == "cnc")
283         cnc(insn);
284     else if (op == "sui")
285         sui(insn);
286     else if (op == "rc")
287         rc(insn);
288     else if (op == "jc")
289         jc(insn);
290     else if (op == "in")
291         i80_in(insn);
292     else if (op == "cc")
293         cc(insn);
294     else if (op == "sbi")
295         sbi(insn);
296     else if (op == "rpo")
297         rpo(insn);
298     else if (op == "jpo")
299         jpo(insn);
300     else if (op == "xthl")
301         xthl(insn);
302     else if (op == "cpo")
303         cpo(insn);
304     else if (op == "ani")
305         ani(insn);
306     else if (op == "rpe")
307         rpe(insn);
308     else if (op == "pchl")
309         pchl(insn);
310     else if (op == "jpe")
311         jpe(insn);
312     else if (op == "xchg")
313         xchg(insn);
314     else if (op == "cpe")
315         cpe(insn);
316     else if (op == "xri")
317         xri(insn);
318     else if (op == "rp")
319         rp(insn);
320     else if (op == "jp")
321         jp(insn);
322     else if (op == "di")
323         di(insn);
324     else if (op == "cp")
325         cp(insn);
326     else if (op == "ori")
327         ori(insn);
328     else if (op == "rm")
329         rm(insn);
330     else if (op == "sphl")
331         sphl(insn);
332     else if (op == "jm")
333         jm(insn);
334     else if (op == "ei")
335         ei(insn);
336     else if (op == "cm")
337         cm(insn);
338     else if (op == "cpi")
339         cpi(insn);
340     else if (op == "equ")
341         equ(insn);
342     else if (op == "db")
343         db(insn);
344     else if (op == "org")
345         org(insn);
346     else
347         err("unknown opcode: " ~ op);
348 }
349 
350 /**
351  * Take action depending on which pass this is.
352  */
353 static void passAct(ushort a, int b, i80 insn)
354 {
355     if (pass == 1) {
356         if (!insn.lab.empty)
357             addsym(insn.lab, addr);
358         addr += a;
359     } else {
360         if (b != -1)
361             output ~= cast(ubyte)b;
362     }
363 }
364 
365 /**
366  * Add a symbol to the symbol table.
367  */
368 static void addsym(string lab, ushort a)
369 {
370     for (size_t i = 0; i < stab.length; i++) {
371         if (lab == stab[i].name)
372             err("duplicate label: " ~ lab);
373     }
374 
375     symtab nsym = { lab, a };
376     stab ~= nsym;
377 }
378 
379 /**
380  * nop (0x00)
381  */
382 static void nop(i80 insn)
383 {
384     argcheck(insn.a1.empty && insn.a2.empty);
385     passAct(1, 0x00, insn);
386 }
387 
388 /**
389  * lxi (0x01 + 16 bit register offset)
390  */
391 static void lxi(i80 insn)
392 {
393     argcheck(!insn.a1.empty && !insn.a2.empty);
394     passAct(3, 0x01 + regMod16(insn), insn);
395     imm(insn, IMM16);
396 }
397 
398 /**
399  * stax (0x02 + 16 bit register offset)
400  */
401 static void stax(i80 insn)
402 {
403     argcheck(!insn.a1.empty && insn.a2.empty);
404     if (insn.a1 == "b")
405         passAct(1, 0x02, insn);
406     else if (insn.a1 == "d")
407         passAct(1, 0x12, insn);
408     else
409         err("stax only takes b or d");
410 }
411 
412 /**
413  * inx (0x03 + 16 bit register offset)
414  */
415 static void inx(i80 insn)
416 {
417     argcheck(!insn.a1.empty && insn.a2.empty);
418     passAct(1, 0x03 + regMod16(insn), insn);
419 }
420 
421 /**
422  * inr (0x04 + 8 bit register offset)
423  */
424 static void inr(i80 insn)
425 {
426     argcheck(!insn.a1.empty && insn.a2.empty);
427     passAct(1, 0x04 + regMod8(insn.a1), insn);
428 }
429 
430 /**
431  * dcr (0x05 + 8 bit register offset)
432  */
433 static void dcr(i80 insn)
434 {
435     argcheck(!insn.a1.empty && insn.a2.empty);
436     passAct(1, 0x05 + regMod8(insn.a1), insn);
437 }
438 
439 /**
440  * mvi (0x06 + (8 bit register offset << 3))
441  */
442 static void mvi(i80 insn)
443 {
444     argcheck(!insn.a1.empty && !insn.a2.empty);
445     passAct(2, 0x06 + (regMod8(insn.a1) << 3), insn);
446     imm(insn, IMM8);
447 }
448 
449 /**
450  * rcl (0x07)
451  */
452 static void rlc(i80 insn)
453 {
454     argcheck(insn.a1.empty && insn.a2.empty);
455     passAct(1, 0x07, insn);
456 }
457 
458 /**
459  * dad (0x09 + 16 bit register offset)
460  */
461 static void dad(i80 insn)
462 {
463     argcheck(!insn.a1.empty && insn.a2.empty);
464     passAct(1, 0x09 + regMod16(insn), insn);
465 }
466 
467 /**
468  * ldax (0x0a + 16 bit register offset)
469  */
470 static void ldax(i80 insn)
471 {
472     argcheck(!insn.a1.empty && insn.a2.empty);
473     if (insn.a1 == "b")
474         passAct(1, 0x0a, insn);
475     else if (insn.a1 == "d")
476         passAct(1, 0x1a, insn);
477     else
478         err("ldax only takes b or d");
479 }
480 
481 /**
482  * dcx (0x0b + 16 bit register offset)
483  */
484 static void dcx(i80 insn)
485 {
486     argcheck(!insn.a1.empty && insn.a2.empty);
487     passAct(1, 0x0b + regMod16(insn), insn);
488 }
489 
490 /**
491  * rrc (0x0f)
492  */
493 static void rrc(i80 insn)
494 {
495     argcheck(insn.a1.empty && insn.a2.empty);
496     passAct(1, 0x0f, insn);
497 }
498 
499 /**
500  * ral (0x17)
501  */
502 static void ral(i80 insn)
503 {
504     argcheck(insn.a1.empty && insn.a2.empty);
505     passAct(1, 0x17, insn);
506 }
507 
508 /**
509  * rar (0x1f)
510  */
511 static void rar(i80 insn)
512 {
513     argcheck(insn.a1.empty && insn.a2.empty);
514     passAct(1, 0x1f, insn);
515 }
516 
517 /**
518  * shld (0x22)
519  */
520 static void shld(i80 insn)
521 {
522     argcheck(!insn.a1.empty && insn.a2.empty);
523     passAct(3, 0x22, insn);
524     a16(insn);
525 }
526 
527 /**
528  * daa (0x27)
529  */
530 static void daa(i80 insn)
531 {
532     argcheck(insn.a1.empty && insn.a2.empty);
533     passAct(1, 0x27, insn);
534     imm(insn, IMM16);
535 }
536 
537 /**
538  * lhld (0x2a)
539  */
540 static void lhld(i80 insn)
541 {
542     argcheck(!insn.a1.empty && insn.a2.empty);
543     passAct(3, 0x2a, insn);
544     a16(insn);
545 }
546 
547 /**
548  * cma (0x2f)
549  */
550 static void cma(i80 insn)
551 {
552     argcheck(insn.a1.empty && insn.a2.empty);
553     passAct(1, 0x2f, insn);
554 }
555 
556 /**
557  * sta (0x32)
558  */
559 static void sta(i80 insn)
560 {
561     argcheck(!insn.a1.empty && insn.a2.empty);
562     passAct(3, 0x32, insn);
563     a16(insn);
564 }
565 
566 /**
567  * stc (0x37)
568  */
569 static void stc(i80 insn)
570 {
571     argcheck(insn.a1.empty && insn.a2.empty);
572     passAct(1, 0x37, insn);
573 }
574 
575 /**
576  * lda (0x3a)
577  */
578 static void lda(i80 insn)
579 {
580     argcheck(!insn.a1.empty && insn.a2.empty);
581     passAct(3, 0x3a, insn);
582     a16(insn);
583 }
584 
585 /**
586  * cmc (0x3f)
587  */
588 static void cmc(i80 insn)
589 {
590     argcheck(insn.a1.empty && insn.a2.empty);
591     passAct(1, 0x3f, insn);
592 }
593 
594 /**
595  * mov (0x40 + (8-bit register offset << 3) + 8-bit register offset
596  * We allow mov m, m (0x76)
597  * But that will result in HLT.
598  */
599 static void mov(i80 insn)
600 {
601     argcheck(!insn.a1.empty && !insn.a2.empty);
602     passAct(1, 0x40 + (regMod8(insn.a1) << 3) + regMod8(insn.a2), insn);
603 }
604 
605 /**
606  * hlt (0x76)
607  */
608 static void hlt(i80 insn)
609 {
610     argcheck(insn.a1.empty && insn.a2.empty);
611     passAct(1, 0x76, insn);
612 }
613 
614 /**
615  * add (0x80 + 8-bit register offset)
616  */
617 static void add(i80 insn)
618 {
619     argcheck(!insn.a1.empty && insn.a2.empty);
620     passAct(1, 0x80 + regMod8(insn.a1), insn);
621 }
622 
623 /**
624  * adc (0x88 + 8-bit register offset)
625  */
626 static void adc(i80 insn)
627 {
628     argcheck(!insn.a1.empty && insn.a2.empty);
629     passAct(1, 0x88 + regMod8(insn.a1), insn);
630 }
631 
632 /**
633  * sub (0x90 + 8-bit register offset)
634  */
635 static void sub(i80 insn)
636 {
637     argcheck(!insn.a1.empty && insn.a2.empty);
638     passAct(1, 0x90 + regMod8(insn.a1), insn);
639 }
640 
641 /**
642  * sbb (0x98 + 8-bit register offset)
643  */
644 static void sbb(i80 insn)
645 {
646     argcheck(!insn.a1.empty && insn.a2.empty);
647     passAct(1, 0x98 + regMod8(insn.a1), insn);
648 }
649 
650 /**
651  * ana (0xa0 + 8-bit register offset)
652  */
653 static void ana(i80 insn)
654 {
655     argcheck(!insn.a1.empty && insn.a2.empty);
656     passAct(1, 0xa0 + regMod8(insn.a1), insn);
657 }
658 
659 /**
660  * xra (0xa8 + 8-bit register offset)
661  */
662 static void xra(i80 insn)
663 {
664     argcheck(!insn.a1.empty && insn.a2.empty);
665     passAct(1, 0xa8 + regMod8(insn.a1), insn);
666 }
667 
668 /**
669  * ora (0xb0 + 8-bit register offset)
670  */
671 static void ora(i80 insn)
672 {
673     argcheck(!insn.a1.empty && insn.a2.empty);
674     passAct(1, 0xb0 + regMod8(insn.a1), insn);
675 }
676 
677 /**
678  * cmp (0xb8 + 8-bit register offset)
679  */
680 static void cmp(i80 insn)
681 {
682     argcheck(!insn.a1.empty && insn.a2.empty);
683     passAct(1, 0xb8 + regMod8(insn.a1), insn);
684 }
685 
686 /**
687  * rnz (0xc0)
688  */
689 static void rnz(i80 insn)
690 {
691     argcheck(insn.a1.empty && insn.a2.empty);
692     passAct(1, 0xc0, insn);
693 }
694 
695 /**
696  * pop (0xc1 + 16 bit register offset)
697  */
698 static void pop(i80 insn)
699 {
700     argcheck(!insn.a1.empty && insn.a2.empty);
701     passAct(1, 0xc1 + regMod16(insn), insn);
702 }
703 
704 /**
705  * jnz (0xc2)
706  */
707 static void jnz(i80 insn)
708 {
709     argcheck(!insn.a1.empty && insn.a2.empty);
710     passAct(3, 0xc2, insn);
711     a16(insn);
712 }
713 
714 /**
715  * jmp (0xc3)
716  */
717 static void jmp(i80 insn)
718 {
719     argcheck(!insn.a1.empty && insn.a2.empty);
720     passAct(3, 0xc3, insn);
721     a16(insn);
722 }
723 
724 /**
725  * cnz (0xc4)
726  */
727 static void cnz(i80 insn)
728 {
729     argcheck(!insn.a1.empty && insn.a2.empty);
730     passAct(3, 0xc4, insn);
731     a16(insn);
732 }
733 
734 /**
735  * push (0xc5 + 16 bit register offset)
736  */
737 static void push(i80 insn)
738 {
739     argcheck(!insn.a1.empty && insn.a2.empty);
740     passAct(1, 0xc5 + regMod16(insn), insn);
741 }
742 
743 /**
744  * adi (0xc6)
745  */
746 static void adi(i80 insn)
747 {
748     argcheck(!insn.a1.empty && insn.a2.empty);
749     passAct(2, 0xc6, insn);
750     imm(insn, IMM8);
751 }
752 
753 /**
754  * rst (0xc7 + offset)
755  */
756 static void rst(i80 insn)
757 {
758     argcheck(!insn.a1.empty && insn.a2.empty);
759     auto offset = to!int(insn.a1, 10);
760     if (offset >= 0 && offset <= 7)
761         passAct(1, 0xc7 + (offset * 8), insn);
762     else
763         err("invalid reset vector: " ~ to!string(offset));
764 }
765 
766 /**
767  * rz (0xc8)
768  */
769 static void rz(i80 insn)
770 {
771     argcheck(insn.a1.empty && insn.a2.empty);
772     passAct(1, 0xc8, insn);
773 }
774 
775 /**
776  * jz (0xca)
777  */
778 static void jz(i80 insn)
779 {
780     argcheck(!insn.a1.empty && insn.a2.empty);
781     passAct(3, 0xca, insn);
782     a16(insn);
783 }
784 
785 /**
786  * cz (0xcc)
787  */
788 static void cz(i80 insn)
789 {
790     argcheck(!insn.a1.empty && insn.a2.empty);
791     passAct(3, 0xcc, insn);
792     a16(insn);
793 }
794 
795 /**
796  * call (0xcd)
797  */
798 static void call(i80 insn)
799 {
800     argcheck(!insn.a1.empty && insn.a2.empty);
801     passAct(3, 0xcd, insn);
802     a16(insn);
803 }
804 
805 /**
806  * aci (0xce)
807  */
808 static void aci(i80 insn)
809 {
810     argcheck(!insn.a1.empty && insn.a2.empty);
811     passAct(2, 0xce, insn);
812     imm(insn, IMM8);
813 }
814 
815 /**
816  * rnc (0xd0)
817  */
818 static void rnc(i80 insn)
819 {
820     argcheck(insn.a1.empty && insn.a2.empty);
821     passAct(1, 0xd0, insn);
822 }
823 
824 /**
825  * jnc (0xd2)
826  */
827 static void jnc(i80 insn)
828 {
829     argcheck(!insn.a1.empty && insn.a2.empty);
830     passAct(3, 0xd2, insn);
831     a16(insn);
832 }
833 
834 /**
835  * out (0xd3)
836  */
837 static void i80_out(i80 insn)
838 {
839     argcheck(!insn.a1.empty && insn.a2.empty);
840     passAct(2, 0xd3, insn);
841     imm(insn, IMM8);
842 }
843 
844 /**
845  * cnc (0xd4)
846  */
847 static void cnc(i80 insn)
848 {
849     argcheck(!insn.a1.empty && insn.a2.empty);
850     passAct(3, 0xd4, insn);
851     a16(insn);
852 }
853 
854 /**
855  * sui (0xd6)
856  */
857 static void sui(i80 insn)
858 {
859     argcheck(!insn.a1.empty && insn.a2.empty);
860     passAct(2, 0xd6, insn);
861     imm(insn, IMM8);
862 }
863 
864 /**
865  * rc (0xd8)
866  */
867 static void rc(i80 insn)
868 {
869     argcheck(insn.a1.empty && insn.a2.empty);
870     passAct(1, 0xd8, insn);
871 }
872 
873 /**
874  * jc (0xda)
875  */
876 static void jc(i80 insn)
877 {
878     argcheck(!insn.a1.empty && insn.a2.empty);
879     passAct(3, 0xda, insn);
880     a16(insn);
881 }
882 
883 /**
884  * in (0xdb)
885  */
886 static void i80_in(i80 insn)
887 {
888     argcheck(!insn.a1.empty && insn.a2.empty);
889     passAct(2, 0xdb, insn);
890     imm(insn, IMM8);
891 }
892 
893 /**
894  * cc (0xdc)
895  */
896 static void cc(i80 insn)
897 {
898     argcheck(!insn.a1.empty && insn.a2.empty);
899     passAct(3, 0xdc, insn);
900     a16(insn);
901 }
902 
903 /**
904  * sbi (0xde)
905  */
906 static void sbi(i80 insn)
907 {
908     argcheck(!insn.a1.empty && insn.a2.empty);
909     passAct(2, 0xde, insn);
910     imm(insn, IMM8);
911 }
912 
913 /**
914  * rpo (0xe0)
915  */
916 static void rpo(i80 insn)
917 {
918     argcheck(insn.a1.empty && insn.a2.empty);
919     passAct(1, 0xe0, insn);
920 }
921 
922 /**
923  * jpo (0xe2)
924  */
925 static void jpo(i80 insn)
926 {
927     argcheck(!insn.a1.empty && insn.a2.empty);
928     passAct(3, 0xe2, insn);
929     a16(insn);
930 }
931 
932 /**
933  * xthl (0xe3)
934  */
935 static void xthl(i80 insn)
936 {
937     argcheck(insn.a1.empty && insn.a2.empty);
938     passAct(1, 0xe3, insn);
939 }
940 
941 /**
942  * cpo (0xe4)
943  */
944 static void cpo(i80 insn)
945 {
946     argcheck(!insn.a1.empty && insn.a2.empty);
947     passAct(3, 0xe4, insn);
948     a16(insn);
949 }
950 
951 /**
952  * ani (0xe6)
953  */
954 static void ani(i80 insn)
955 {
956     argcheck(!insn.a1.empty && insn.a2.empty);
957     passAct(2, 0xe6, insn);
958     imm(insn, IMM8);
959 }
960 
961 /**
962  * rpe (0xe8)
963  */
964 static void rpe(i80 insn)
965 {
966     argcheck(insn.a1.empty && insn.a2.empty);
967     passAct(1, 0xe8, insn);
968 }
969 
970 /**
971  * pchl (0xe9)
972  */
973 static void pchl(i80 insn)
974 {
975     argcheck(insn.a1.empty && insn.a2.empty);
976     passAct(1, 0xe9, insn);
977 }
978 
979 /**
980  * jpe (0xea)
981  */
982 static void jpe(i80 insn)
983 {
984     argcheck(!insn.a1.empty && insn.a2.empty);
985     passAct(3, 0xea, insn);
986     a16(insn);
987 }
988 
989 /**
990  * xchg (0xeb)
991  */
992 static void xchg(i80 insn)
993 {
994     argcheck(insn.a1.empty && insn.a2.empty);
995     passAct(1, 0xeb, insn);
996 }
997 
998 /**
999  * cpe (0xec)
1000  */
1001 static void cpe(i80 insn)
1002 {
1003     argcheck(!insn.a1.empty && insn.a2.empty);
1004     passAct(3, 0xec, insn);
1005     a16(insn);
1006 }
1007 
1008 /**
1009  * xri (0xee)
1010  */
1011 static void xri(i80 insn)
1012 {
1013     argcheck(!insn.a1.empty && insn.a2.empty);
1014     passAct(2, 0xee, insn);
1015     imm(insn, IMM8);
1016 }
1017 
1018 /**
1019  * rp (0xf0)
1020  */
1021 static void rp(i80 insn)
1022 {
1023     argcheck(insn.a1.empty && insn.a2.empty);
1024     passAct(1, 0xf0, insn);
1025 }
1026 
1027 /**
1028  * jp (0xf2)
1029  */
1030 static void jp(i80 insn)
1031 {
1032     argcheck(!insn.a1.empty && insn.a2.empty);
1033     passAct(3, 0xf2, insn);
1034     a16(insn);
1035 }
1036 
1037 /**
1038  * di (0xf3)
1039  */
1040 static void di(i80 insn)
1041 {
1042     argcheck(insn.a1.empty && insn.a2.empty);
1043     passAct(1, 0xf3, insn);
1044 }
1045 
1046 /**
1047  * cp (0xf4)
1048  */
1049 static void cp(i80 insn)
1050 {
1051     argcheck(!insn.a1.empty && insn.a2.empty);
1052     passAct(3, 0xf4, insn);
1053     a16(insn);
1054 }
1055 
1056 /**
1057  * ori (0xf6)
1058  */
1059 static void ori(i80 insn)
1060 {
1061     argcheck(!insn.a1.empty && insn.a2.empty);
1062     passAct(2, 0xf6, insn);
1063     imm(insn, IMM8);
1064 }
1065 
1066 /**
1067  * rm (0xf8)
1068  */
1069 static void rm(i80 insn)
1070 {
1071     argcheck(insn.a1.empty && insn.a2.empty);
1072     passAct(1, 0xf8, insn);
1073 }
1074 
1075 /**
1076  * sphl (0xf9)
1077  */
1078 static void sphl(i80 insn)
1079 {
1080     argcheck(insn.a1.empty && insn.a2.empty);
1081     passAct(1, 0xf9, insn);
1082 }
1083 
1084 /**
1085  * jm (0xfa)
1086  */
1087 static void jm(i80 insn)
1088 {
1089     argcheck(!insn.a1.empty && insn.a2.empty);
1090     passAct(3, 0xfa, insn);
1091     a16(insn);
1092 }
1093 
1094 /**
1095  * ei (0xfb)
1096  */
1097 static void ei(i80 insn)
1098 {
1099     argcheck(insn.a1.empty && insn.a2.empty);
1100     passAct(1, 0xfb, insn);
1101 }
1102 
1103 /**
1104  * cm (0xfc)
1105  */
1106 static void cm(i80 insn)
1107 {
1108     argcheck(!insn.a1.empty && insn.a2.empty);
1109     passAct(3, 0xfc, insn);
1110     a16(insn);
1111 }
1112 
1113 /**
1114  * cpi (0xfe)
1115  */
1116 static void cpi(i80 insn)
1117 {
1118     argcheck(!insn.a1.empty && insn.a2.empty);
1119     passAct(2, 0xfe, insn);
1120     imm(insn, IMM8);
1121 }
1122 
1123 /**
1124  * Define a constant.
1125  */
1126 static void equ(i80 insn)
1127 {
1128     if (insn.lab.empty)
1129         err("must have a label in equ statement");
1130 
1131     if (insn.a1[insn.a1.length - 1] != 'h')
1132         err("number must end with 'h'");
1133     auto a = to!ushort(chop(insn.a1), 16);
1134     if (pass == 1)
1135        addsym(insn.lab, a);
1136 }
1137 
1138 /**
1139  * Place a byte.
1140  */
1141 static void db(i80 insn)
1142 {
1143     argcheck(!insn.a1.empty && insn.a2.empty);
1144     if (isDigit(insn.a1[0]) && insn.a1.length > 1) {
1145         if (insn.a1[insn.a1.length - 1] != 'h')
1146             err("number must end with 'h'");
1147         passAct(1, to!ubyte(chop(insn.a1), 16), insn);
1148     } else {
1149         if (pass == 1) {
1150             if (!insn.lab.empty)
1151                 addsym(insn.lab, addr);
1152             addr += insn.a1.length;
1153         } else {
1154             for (size_t i = 0; i < insn.a1.length; i++)
1155                 output ~= cast(ubyte)insn.a1[i];
1156         }
1157     }
1158 }
1159 
1160 /**
1161  * Force updated the address counter.
1162  */
1163 static void org(i80 insn)
1164 {
1165     argcheck(insn.lab.empty && !insn.a1.empty && insn.a2.empty);
1166     if (isDigit(insn.a1[0])) {
1167         if (insn.a1[insn.a1.length - 1] != 'h')
1168             err("number must end with 'h'");
1169         if (pass == 1)
1170             addr = to!ushort(chop(insn.a1), 16);
1171     } else {
1172         err("org must take a number");
1173     }
1174 }
1175 
1176 /**
1177  * Return the 16 bit register offset.
1178  */
1179 static int regMod16(i80 insn)
1180 {
1181     if (insn.a1 == "b") {
1182         return 0x00;
1183     } else if (insn.a1 == "d") {
1184         return 0x10;
1185     } else if (insn.a1 == "h") {
1186         return 0x20;
1187     } else if (insn.a1 == "psw") {
1188         if (insn.op == "pop" || insn.op == "push")
1189             return 0x30;
1190         else
1191             err("psw may not be used with " ~ insn.op);
1192     } else if (insn.a1 == "sp") {
1193         if (insn.op != "pop" && insn.op != "push")
1194             return 0x30;
1195         else
1196             err("sp may not be used with " ~ insn.op);
1197     } else {
1198         err("invalid register for " ~ insn.op);
1199     }
1200 
1201     /* This will never be reached, but quiets gdc.  */
1202     return 0;
1203 }
1204 
1205 /**
1206  * Return the 8 bit register offset.
1207  */
1208 static int regMod8(string reg)
1209 {
1210     if (reg == "b")
1211         return 0x00;
1212     else if (reg == "c")
1213         return 0x01;
1214     else if (reg == "d")
1215         return 0x02;
1216     else if (reg == "e")
1217         return 0x03;
1218     else if (reg == "h")
1219         return 0x04;
1220     else if (reg == "l")
1221         return 0x05;
1222     else if (reg == "m")
1223         return 0x06;
1224     else if (reg == "a")
1225         return 0x07;
1226     else
1227         err("invalid register " ~ reg);
1228 
1229     /* This will never be reached, but quiets gdc.  */
1230     return 0;
1231 }
1232 
1233 /**
1234  * Get an 8 or 16 bit immediate.
1235  */
1236 static void imm(i80 insn, int immtype)
1237 {
1238     ushort dig;
1239     string check;
1240     bool found = false;
1241 
1242     if (insn.op == "lxi" || insn.op == "mvi")
1243         check = insn.a2;
1244     else
1245         check = insn.a1;
1246 
1247     if (isDigit(check[0])) {
1248         if (check[check.length - 1] != 'h')
1249             err("number must end with 'h'");
1250         dig = to!ushort(chop(check), 16);
1251     } else {
1252         for (size_t i = 0; i < stab.length; i++) {
1253             if (check == stab[i].name) {
1254                 dig = stab[i].value;
1255                 found = true;
1256                 break;
1257             }
1258         }
1259         if (pass == 2) {
1260             if (!found)
1261                 err("label " ~ check ~ " not defined");
1262         }
1263     }
1264 
1265     if (pass == 2) {
1266         output ~= cast(ubyte)(dig & 0xff);
1267         if (immtype == IMM16)
1268             output ~= cast(ubyte)((dig >> 8) & 0xff);
1269     }
1270 }
1271 
1272 /**
1273  * Get a 16-bit address.
1274  */
1275 static void a16(i80 insn)
1276 {
1277     ushort dig;
1278     bool found = false;
1279 
1280     if (isDigit(insn.a1[0])) {
1281         if (insn.a1[insn.a1.length - 1] != 'h')
1282             err("number must end with 'h'");
1283         dig = to!ushort(chop(insn.a1), 16);
1284     } else {
1285         for (size_t i = 0; i < stab.length; i++) {
1286             if (insn.a1 == stab[i].name) {
1287                 dig = stab[i].value;
1288                 found = true;
1289                 break;
1290             }
1291         }
1292         if (pass == 2) {
1293             if (!found)
1294                 err("label " ~ insn.a1 ~ " not defined");
1295         }
1296     }
1297 
1298     if (pass == 2) {
1299         output ~= cast(ubyte)(dig & 0xff);
1300         output ~= cast(ubyte)((dig >> 8) & 0xff);
1301     }
1302 }
1303 
1304 /**
1305  * Nice error messages.
1306  */
1307 static void err(string msg)
1308 {
1309     writeln("a80: " ~ to!string(line + 1) ~ ": " ~ msg);
1310     enforce(0);
1311 }
1312 
1313 /**
1314  * Check arguments.
1315  */
1316 static void argcheck(bool passed)
1317 {
1318     if (passed == false)
1319         err("arguments not correct for opcode");
1320 }