forget difference between big and small commands - obsolete with vm.
[minix.git] / commands / i386 / asmconv / emit_gnu.c
blobba8c148bf35feb1aede5dd419df96770d3fcc576
1 /* emit_gnu.c - emit GNU assembly Author: Kees J. Bot
2 * 28 Dec 1993
3 */
4 #define nil 0
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <stdarg.h>
8 #include <string.h>
9 #include <assert.h>
10 #include "asmconv.h"
11 #include "token.h"
12 #include "asm86.h"
13 #include "languages.h"
15 typedef struct mnemonic { /* GNU as386 mnemonics translation table. */
16 opcode_t opcode;
17 char *name;
18 } mnemonic_t;
20 static mnemonic_t mnemtab[] = {
21 { AAA, "aaa" },
22 { AAD, "aad" },
23 { AAM, "aam" },
24 { AAS, "aas" },
25 { ADC, "adc%" },
26 { ADD, "add%" },
27 { AND, "and%" },
28 { ARPL, "arpl" },
29 { BOUND, "bound%" },
30 { BSF, "bsf%" },
31 { BSR, "bsr%" },
32 { BSWAP, "bswap" },
33 { BT, "bt%" },
34 { BTC, "btc%" },
35 { BTR, "btr%" },
36 { BTS, "bts%" },
37 { CALL, "call" },
38 { CALLF, "lcall" },
39 { CBW, "cbtw" },
40 { CLC, "clc" },
41 { CLD, "cld" },
42 { CLI, "cli" },
43 { CLTS, "clts" },
44 { CMC, "cmc" },
45 { CMP, "cmp%" },
46 { CMPS, "cmps%" },
47 { CMPXCHG, "cmpxchg" },
48 { CWD, "cwtd" },
49 { DAA, "daa" },
50 { DAS, "das" },
51 { DEC, "dec%" },
52 { DIV, "div%" },
53 { DOT_ALIGN, ".align" },
54 { DOT_ASCII, ".ascii" },
55 { DOT_ASCIZ, ".asciz" },
56 { DOT_ASSERT, ".assert" },
57 { DOT_BASE, ".base" },
58 { DOT_BSS, ".bss" },
59 { DOT_COMM, ".comm" },
60 { DOT_DATA, ".data" },
61 { DOT_DATA1, ".byte" },
62 { DOT_DATA2, ".short" },
63 { DOT_DATA4, ".long" },
64 { DOT_DEFINE, ".globl" },
65 { DOT_EXTERN, ".globl" },
66 { DOT_FILE, ".file" },
67 { DOT_LCOMM, ".lcomm" },
68 { DOT_LINE, ".line" },
69 { DOT_LIST, ".list" },
70 { DOT_NOLIST, ".nolist" },
71 { DOT_ROM, ".data" }, /* Minix -- separate I&D. */
72 { DOT_SPACE, ".space" },
73 { DOT_SYMB, ".symb" },
74 { DOT_TEXT, ".text" },
75 { DOT_USE16, ".use16" },
76 { DOT_USE32, ".use32" },
77 { ENTER, "enter" },
78 { F2XM1, "f2xm1" },
79 { FABS, "fabs" },
80 { FADD, "fadd" },
81 { FADDD, "faddl" },
82 { FADDP, "faddp" },
83 { FADDS, "fadds" },
84 { FBLD, "fbld" },
85 { FBSTP, "fbstp" },
86 { FCHS, "fchs" },
87 { FCLEX, "fnclex" },
88 { FCOMD, "fcoml" },
89 { FCOMPD, "fcompl" },
90 { FCOMPP, "fcompp" },
91 { FCOMPS, "fcomps" },
92 { FCOMS, "fcoms" },
93 { FCOS, "fcos" },
94 { FDECSTP, "fdecstp" },
95 { FDIVD, "fdivl" },
96 { FDIVP, "fdivp" },
97 { FDIVRD, "fdivrl" },
98 { FDIVRP, "fdivrp" },
99 { FDIVRS, "fdivrs" },
100 { FDIVS, "fdivs" },
101 { FFREE, "ffree" },
102 { FIADDL, "fiaddl" },
103 { FIADDS, "fiadds" },
104 { FICOM, "ficom" },
105 { FICOMP, "ficomp" },
106 { FIDIVL, "fidivl" },
107 { FIDIVRL, "fidivrl" },
108 { FIDIVRS, "fidivrs" },
109 { FIDIVS, "fidivs" },
110 { FILDL, "fildl" },
111 { FILDQ, "fildq" },
112 { FILDS, "filds" },
113 { FIMULL, "fimull" },
114 { FIMULS, "fimuls" },
115 { FINCSTP, "fincstp" },
116 { FINIT, "fninit" },
117 { FISTL, "fistl" },
118 { FISTP, "fistp" },
119 { FISTS, "fists" },
120 { FISUBL, "fisubl" },
121 { FISUBRL, "fisubrl" },
122 { FISUBRS, "fisubrs" },
123 { FISUBS, "fisubs" },
124 { FLD1, "fld1" },
125 { FLDCW, "fldcw" },
126 { FLDD, "fldl" },
127 { FLDENV, "fldenv" },
128 { FLDL2E, "fldl2e" },
129 { FLDL2T, "fldl2t" },
130 { FLDLG2, "fldlg2" },
131 { FLDLN2, "fldln2" },
132 { FLDPI, "fldpi" },
133 { FLDS, "flds" },
134 { FLDX, "fldt" },
135 { FLDZ, "fldz" },
136 { FMULD, "fmull" },
137 { FMULP, "fmulp" },
138 { FMULS, "fmuls" },
139 { FNOP, "fnop" },
140 { FPATAN, "fpatan" },
141 { FPREM, "fprem" },
142 { FPREM1, "fprem1" },
143 { FPTAN, "fptan" },
144 { FRNDINT, "frndint" },
145 { FRSTOR, "frstor" },
146 { FSAVE, "fnsave" },
147 { FSCALE, "fscale" },
148 { FSIN, "fsin" },
149 { FSINCOS, "fsincos" },
150 { FSQRT, "fsqrt" },
151 { FSTCW, "fnstcw" },
152 { FSTD, "fstl" },
153 { FSTENV, "fnstenv" },
154 { FSTP, "fstp" },
155 { FSTPD, "fstpl" },
156 { FSTPS, "fstps" },
157 { FSTPX, "fstpt" },
158 { FSTS, "fsts" },
159 { FSTSW, "fstsw" },
160 { FSUBD, "fsubl" },
161 { FSUBP, "fsubp" },
162 { FSUBPR, "fsubpr" },
163 { FSUBRD, "fsubrl" },
164 { FSUBRS, "fsubrs" },
165 { FSUBS, "fsubs" },
166 { FTST, "ftst" },
167 { FUCOM, "fucom" },
168 { FUCOMP, "fucomp" },
169 { FUCOMPP, "fucompp" },
170 { FXAM, "fxam" },
171 { FXCH, "fxch" },
172 { FXTRACT, "fxtract" },
173 { FYL2X, "fyl2x" },
174 { FYL2XP1, "fyl2xp1" },
175 { HLT, "hlt" },
176 { IDIV, "idiv%" },
177 { IMUL, "imul%" },
178 { IN, "in%" },
179 { INC, "inc%" },
180 { INS, "ins%" },
181 { INT, "int" },
182 { INTO, "into" },
183 { INVD, "invd" },
184 { INVLPG, "invlpg" },
185 { IRET, "iret" },
186 { IRETD, "iret" },
187 { JA, "ja" },
188 { JAE, "jae" },
189 { JB, "jb" },
190 { JBE, "jbe" },
191 { JCXZ, "jcxz" },
192 { JE, "je" },
193 { JG, "jg" },
194 { JGE, "jge" },
195 { JL, "jl" },
196 { JLE, "jle" },
197 { JMP, "jmp" },
198 { JMPF, "ljmp" },
199 { JNE, "jne" },
200 { JNO, "jno" },
201 { JNP, "jnp" },
202 { JNS, "jns" },
203 { JO, "jo" },
204 { JP, "jp" },
205 { JS, "js" },
206 { LAHF, "lahf" },
207 { LAR, "lar" },
208 { LDS, "lds" },
209 { LEA, "lea%" },
210 { LEAVE, "leave" },
211 { LES, "les" },
212 { LFS, "lfs" },
213 { LGDT, "lgdt" },
214 { LGS, "lgs" },
215 { LIDT, "lidt" },
216 { LLDT, "lldt" },
217 { LMSW, "lmsw" },
218 { LOCK, "lock" },
219 { LODS, "lods%" },
220 { LOOP, "loop" },
221 { LOOPE, "loope" },
222 { LOOPNE, "loopne" },
223 { LSL, "lsl" },
224 { LSS, "lss" },
225 { LTR, "ltr" },
226 { MOV, "mov%" },
227 { MOVS, "movs%" },
228 { MOVSX, "movswl" },
229 { MOVSXB, "movsb%" },
230 { MOVZX, "movzwl" },
231 { MOVZXB, "movzb%" },
232 { MUL, "mul%" },
233 { NEG, "neg%" },
234 { NOP, "nop" },
235 { NOT, "not%" },
236 { OR, "or%" },
237 { OUT, "out%" },
238 { OUTS, "outs%" },
239 { POP, "pop%" },
240 { POPA, "popa%" },
241 { POPF, "popf%" },
242 { PUSH, "push%" },
243 { PUSHA, "pusha%" },
244 { PUSHF, "pushf%" },
245 { RCL, "rcl%" },
246 { RCR, "rcr%" },
247 { RET, "ret" },
248 { RETF, "lret" },
249 { ROL, "rol%" },
250 { ROR, "ror%" },
251 { SAHF, "sahf" },
252 { SAL, "sal%" },
253 { SAR, "sar%" },
254 { SBB, "sbb%" },
255 { SCAS, "scas%" },
256 { SETA, "setab" },
257 { SETAE, "setaeb" },
258 { SETB, "setbb" },
259 { SETBE, "setbeb" },
260 { SETE, "seteb" },
261 { SETG, "setgb" },
262 { SETGE, "setgeb" },
263 { SETL, "setlb" },
264 { SETLE, "setleb" },
265 { SETNE, "setneb" },
266 { SETNO, "setnob" },
267 { SETNP, "setnpb" },
268 { SETNS, "setnsb" },
269 { SETO, "setob" },
270 { SETP, "setpb" },
271 { SETS, "setsb" },
272 { SGDT, "sgdt" },
273 { SHL, "shl%" },
274 { SHLD, "shld%" },
275 { SHR, "shr%" },
276 { SHRD, "shrd%" },
277 { SIDT, "sidt" },
278 { SLDT, "sldt" },
279 { SMSW, "smsw" },
280 { STC, "stc" },
281 { STD, "std" },
282 { STI, "sti" },
283 { STOS, "stos%" },
284 { STR, "str" },
285 { SUB, "sub%" },
286 { TEST, "test%" },
287 { VERR, "verr" },
288 { VERW, "verw" },
289 { WAIT, "wait" },
290 { WBINVD, "wbinvd" },
291 { XADD, "xadd" },
292 { XCHG, "xchg%" },
293 { XLAT, "xlat" },
294 { XOR, "xor%" },
297 static FILE *ef;
298 static long eline= 1;
299 static char *efile;
300 static char *orig_efile;
301 static char *opcode2name_tab[N_OPCODES];
303 static void gnu_putchar(int c)
304 /* LOOK, this programmer checks the return code of putc! What an idiot, noone
305 * does that!
308 if (putc(c, ef) == EOF) fatal(orig_efile);
311 static void gnu_printf(const char *fmt, ...)
313 va_list ap;
315 va_start(ap, fmt);
316 if (vfprintf(ef, fmt, ap) == EOF) fatal(orig_efile);
317 va_end(ap);
320 void gnu_emit_init(char *file, const char *banner)
321 /* Prepare producing a GNU assembly file. */
323 mnemonic_t *mp;
325 if (file == nil) {
326 file= "stdout";
327 ef= stdout;
328 } else {
329 if ((ef= fopen(file, "w")) == nil) fatal(file);
331 orig_efile= file;
332 efile= file;
333 gnu_printf("/ %s", banner);
335 /* Initialize the opcode to mnemonic translation table. */
336 for (mp= mnemtab; mp < arraylimit(mnemtab); mp++) {
337 assert(opcode2name_tab[mp->opcode] == nil);
338 opcode2name_tab[mp->opcode]= mp->name;
342 #define opcode2name(op) (opcode2name_tab[op] + 0)
344 static void gnu_put_string(const char *s, size_t n)
345 /* Emit a string with weird characters quoted. */
347 while (n > 0) {
348 int c= *s;
350 if (c < ' ' || c > 0177) {
351 gnu_printf("\\%03o", c);
352 } else
353 if (c == '"' || c == '\\') {
354 gnu_printf("\\%c", c & 0xFF);
355 } else {
356 gnu_putchar(c);
358 s++;
359 n--;
363 static void gnu_put_expression(asm86_t *a, expression_t *e, int deref)
364 /* Send an expression, i.e. instruction operands, to the output file. Deref
365 * is true when the rewrite of "x" -> "#x" or "(x)" -> "x" may be made.
368 assert(e != nil);
370 switch (e->operator) {
371 case ',':
372 if (is_pseudo(a->opcode)) {
373 /* Pseudo's are normal. */
374 gnu_put_expression(a, e->left, deref);
375 gnu_printf(", ");
376 gnu_put_expression(a, e->right, deref);
377 } else {
378 /* He who invented GNU assembly has seen one VAX too
379 * many, operands are given in the wrong order. This
380 * makes coding from an Intel databook a real delight.
381 * A good thing this program allows us to write the
382 * more normal ACK assembly.
384 gnu_put_expression(a, e->right, deref);
385 gnu_printf(", ");
386 gnu_put_expression(a, e->left, deref);
388 break;
389 case 'O':
390 if (deref && a->optype == JUMP) gnu_putchar('*');
391 if (e->left != nil) gnu_put_expression(a, e->left, 0);
392 gnu_putchar('(');
393 if (e->middle != nil) gnu_put_expression(a, e->middle, 0);
394 if (e->right != nil) {
395 gnu_putchar(',');
396 gnu_put_expression(a, e->right, 0);
398 gnu_putchar(')');
399 break;
400 case '(':
401 if (!deref) gnu_putchar('(');
402 if (deref && a->optype == JUMP) gnu_putchar('*');
403 gnu_put_expression(a, e->middle, 0);
404 if (!deref) gnu_putchar(')');
405 break;
406 case 'B':
407 gnu_printf("%%%s", e->name);
408 break;
409 case '1':
410 case '2':
411 case '4':
412 case '8':
413 gnu_printf("%%%s,%c", e->name, e->operator);
414 break;
415 case '+':
416 case '-':
417 case '~':
418 if (e->middle != nil) {
419 if (deref && a->optype >= BYTE) gnu_putchar('$');
420 gnu_putchar(e->operator);
421 gnu_put_expression(a, e->middle, 0);
422 break;
424 /*FALL THROUGH*/
425 case '*':
426 case '/':
427 case '%':
428 case '&':
429 case '|':
430 case '^':
431 case S_LEFTSHIFT:
432 case S_RIGHTSHIFT:
433 if (deref && a->optype >= BYTE) gnu_putchar('$');
434 gnu_put_expression(a, e->left, 0);
435 if (e->operator == S_LEFTSHIFT) {
436 gnu_printf("<<");
437 } else
438 if (e->operator == S_RIGHTSHIFT) {
439 gnu_printf(">>");
440 } else {
441 gnu_putchar(e->operator);
443 gnu_put_expression(a, e->right, 0);
444 break;
445 case '[':
446 if (deref && a->optype >= BYTE) gnu_putchar('$');
447 gnu_putchar('(');
448 gnu_put_expression(a, e->middle, 0);
449 gnu_putchar(')');
450 break;
451 case 'W':
452 if (isregister(e->name)) {
453 if (a->optype == JUMP) gnu_putchar('*');
454 gnu_printf("%%%s", e->name);
455 } else {
456 if (deref && a->optype >= BYTE) gnu_putchar('$');
457 gnu_printf("%s", e->name);
459 break;
460 case 'S':
461 gnu_putchar('"');
462 gnu_put_string(e->name, e->len);
463 gnu_putchar('"');
464 break;
465 default:
466 fprintf(stderr,
467 "asmconv: internal error, unknown expression operator '%d'\n",
468 e->operator);
469 exit(EXIT_FAILURE);
473 void gnu_emit_instruction(asm86_t *a)
474 /* Output one instruction and its operands. */
476 int same= 0;
477 char *p;
479 if (a == nil) {
480 /* Last call */
481 gnu_putchar('\n');
482 return;
485 if (use16()) {
486 fprintf(stderr,
487 "asmconv: the GNU assembler can't translate 8086 code\n");
488 exit(EXIT_FAILURE);
491 /* Make sure the line number of the line to be emitted is ok. */
492 if ((a->file != efile && strcmp(a->file, efile) != 0)
493 || a->line < eline || a->line > eline+10) {
494 gnu_putchar('\n');
495 gnu_printf("# %ld \"%s\"\n", a->line, a->file);
496 efile= a->file;
497 eline= a->line;
498 } else {
499 if (a->line == eline) {
500 gnu_printf("; ");
501 same= 1;
503 while (eline < a->line) {
504 gnu_putchar('\n');
505 eline++;
509 if (a->opcode == DOT_END) {
510 /* Ignore .end in gnu mode. */
511 } else
512 if (a->opcode == DOT_LABEL) {
513 assert(a->args->operator == ':');
514 gnu_printf("%s:", a->args->name);
515 } else
516 if (a->opcode == DOT_EQU) {
517 assert(a->args->operator == '=');
518 gnu_printf("\t%s = ", a->args->name);
519 gnu_put_expression(a, a->args->middle, 0);
520 } else
521 if (a->opcode == DOT_ALIGN) {
522 /* GNU .align thinks in powers of two. */
523 unsigned long n;
524 unsigned s;
526 assert(a->args->operator == 'W' && isanumber(a->args->name));
527 n= strtoul(a->args->name, nil, 0);
528 for (s= 0; s <= 16 && (1 << s) < n; s++) {}
529 gnu_printf(".align\t%u", s);
530 } else
531 if ((p= opcode2name(a->opcode)) != nil) {
532 if (!is_pseudo(a->opcode) && !same) gnu_putchar('\t');
534 switch (a->rep) {
535 case ONCE: break;
536 case REP: gnu_printf("rep; "); break;
537 case REPE: gnu_printf("repe; "); break;
538 case REPNE: gnu_printf("repne; "); break;
539 default: assert(0);
541 switch (a->seg) {
542 /* Kludge to avoid knowing where to put the "%es:" */
543 case DEFSEG: break;
544 case CSEG: gnu_printf(".byte 0x2e; "); break;
545 case DSEG: gnu_printf(".byte 0x3e; "); break;
546 case ESEG: gnu_printf(".byte 0x26; "); break;
547 case FSEG: gnu_printf(".byte 0x64; "); break;
548 case GSEG: gnu_printf(".byte 0x65; "); break;
549 case SSEG: gnu_printf(".byte 0x36; "); break;
550 default: assert(0);
553 /* Exceptions, exceptions... */
554 if (a->opcode == CBW) {
555 if (!(a->oaz & OPZ)) p= "cwtl";
556 a->oaz&= ~OPZ;
558 if (a->opcode == CWD) {
559 if (!(a->oaz & OPZ)) p= "cltd";
560 a->oaz&= ~OPZ;
563 if (a->opcode == RET || a->opcode == RETF) {
564 /* Argument of RET needs a '$'. */
565 a->optype= WORD;
568 if (a->opcode == MUL && a->args != nil
569 && a->args->operator == ',') {
570 /* Two operand MUL is an IMUL? */
571 p="imul%";
574 /* GAS doesn't understand the interesting combinations. */
575 if (a->oaz & ADZ) gnu_printf(".byte 0x67; ");
576 if (a->oaz & OPZ && strchr(p, '%') == nil)
577 gnu_printf(".byte 0x66; ");
579 /* Unsupported instructions that Minix code needs. */
580 if (a->opcode == JMPF && a->args != nil
581 && a->args->operator == ',') {
582 /* JMPF seg:off. */
583 gnu_printf(".byte 0xEA; .long ");
584 gnu_put_expression(a, a->args->right, 0);
585 gnu_printf("; .short ");
586 gnu_put_expression(a, a->args->left, 0);
587 return;
589 if (a->opcode == JMPF && a->args != nil
590 && a->args->operator == 'O'
591 && a->args->left != nil
592 && a->args->right == nil
593 && a->args->middle != nil
594 && a->args->middle->operator == 'B'
595 && strcmp(a->args->middle->name, "esp") == 0
597 /* JMPF offset(ESP). */
598 gnu_printf(".byte 0xFF,0x6C,0x24,");
599 gnu_put_expression(a, a->args->left, 0);
600 return;
602 if (a->opcode == MOV && a->args != nil
603 && a->args->operator == ','
604 && a->args->left != nil
605 && a->args->left->operator == 'W'
606 && (strcmp(a->args->left->name, "ds") == 0
607 || strcmp(a->args->left->name, "es") == 0)
608 && a->args->right->operator == 'O'
609 && a->args->right->left != nil
610 && a->args->right->right == nil
611 && a->args->right->middle != nil
612 && a->args->right->middle->operator == 'B'
613 && strcmp(a->args->right->middle->name, "esp") == 0
615 /* MOV DS, offset(ESP); MOV ES, offset(ESP) */
616 gnu_printf(".byte 0x8E,0x%02X,0x24,",
617 a->args->left->name[0] == 'd' ? 0x5C : 0x44);
618 gnu_put_expression(a, a->args->right->left, 0);
619 return;
621 if (a->opcode == MOV && a->args != nil
622 && a->args->operator == ','
623 && a->args->left != nil
624 && a->args->left->operator == 'W'
625 && (strcmp(a->args->left->name, "ds") == 0
626 || strcmp(a->args->left->name, "es") == 0)
627 && a->args->right->operator == '('
628 && a->args->right->middle != nil
630 /* MOV DS, (memory); MOV ES, (memory) */
631 gnu_printf(".byte 0x8E,0x%02X; .long ",
632 a->args->left->name[0] == 'd' ? 0x1D : 0x05);
633 gnu_put_expression(a, a->args->right->middle, 0);
634 return;
637 while (*p != 0) {
638 if (*p == '%') {
639 if (a->optype == BYTE) {
640 gnu_putchar('b');
641 } else
642 if (a->optype == WORD) {
643 gnu_putchar((a->oaz & OPZ) ? 'w' : 'l');
644 } else {
645 assert(0);
647 } else {
648 gnu_putchar(*p);
650 p++;
653 if (a->args != nil) {
654 static char *aregs[] = { "al", "ax", "eax" };
656 gnu_putchar('\t');
657 switch (a->opcode) {
658 case IN:
659 gnu_put_expression(a, a->args, 1);
660 gnu_printf(", %%%s", aregs[a->optype - BYTE]);
661 break;
662 case OUT:
663 gnu_printf("%%%s, ", aregs[a->optype - BYTE]);
664 gnu_put_expression(a, a->args, 1);
665 break;
666 default:
667 gnu_put_expression(a, a->args, 1);
670 if (a->opcode == DOT_USE16) set_use16();
671 if (a->opcode == DOT_USE32) set_use32();
672 } else {
673 fprintf(stderr,
674 "asmconv: internal error, unknown opcode '%d'\n",
675 a->opcode);
676 exit(EXIT_FAILURE);