1 /* emit_gnu.c - emit GNU assembly Author: Kees J. Bot
13 #include "languages.h"
15 typedef struct mnemonic
{ /* GNU as386 mnemonics translation table. */
20 static mnemonic_t mnemtab
[] = {
47 { CMPXCHG
, "cmpxchg" },
53 { DOT_ALIGN
, ".align" },
54 { DOT_ASCII
, ".ascii" },
55 { DOT_ASCIZ
, ".asciz" },
56 { DOT_ASSERT
, ".assert" },
57 { DOT_BASE
, ".base" },
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" },
94 { FDECSTP
, "fdecstp" },
102 { FIADDL
, "fiaddl" },
103 { FIADDS
, "fiadds" },
105 { FICOMP
, "ficomp" },
106 { FIDIVL
, "fidivl" },
107 { FIDIVRL
, "fidivrl" },
108 { FIDIVRS
, "fidivrs" },
109 { FIDIVS
, "fidivs" },
113 { FIMULL
, "fimull" },
114 { FIMULS
, "fimuls" },
115 { FINCSTP
, "fincstp" },
120 { FISUBL
, "fisubl" },
121 { FISUBRL
, "fisubrl" },
122 { FISUBRS
, "fisubrs" },
123 { FISUBS
, "fisubs" },
127 { FLDENV
, "fldenv" },
128 { FLDL2E
, "fldl2e" },
129 { FLDL2T
, "fldl2t" },
130 { FLDLG2
, "fldlg2" },
131 { FLDLN2
, "fldln2" },
140 { FPATAN
, "fpatan" },
142 { FPREM1
, "fprem1" },
144 { FRNDINT
, "frndint" },
145 { FRSTOR
, "frstor" },
147 { FSCALE
, "fscale" },
149 { FSINCOS
, "fsincos" },
153 { FSTENV
, "fnstenv" },
162 { FSUBPR
, "fsubpr" },
163 { FSUBRD
, "fsubrl" },
164 { FSUBRS
, "fsubrs" },
168 { FUCOMP
, "fucomp" },
169 { FUCOMPP
, "fucompp" },
172 { FXTRACT
, "fxtract" },
174 { FYL2XP1
, "fyl2xp1" },
184 { INVLPG
, "invlpg" },
222 { LOOPNE
, "loopne" },
229 { MOVSXB
, "movsb%" },
231 { MOVZXB
, "movzb%" },
290 { WBINVD
, "wbinvd" },
298 static long eline
= 1;
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
308 if (putc(c
, ef
) == EOF
) fatal(orig_efile
);
311 static void gnu_printf(const char *fmt
, ...)
316 if (vfprintf(ef
, fmt
, ap
) == EOF
) fatal(orig_efile
);
320 void gnu_emit_init(char *file
, const char *banner
)
321 /* Prepare producing a GNU assembly file. */
329 if ((ef
= fopen(file
, "w")) == nil
) fatal(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. */
350 if (c
< ' ' || c
> 0177) {
351 gnu_printf("\\%03o", c
);
353 if (c
== '"' || c
== '\\') {
354 gnu_printf("\\%c", c
& 0xFF);
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.
370 switch (e
->operator) {
372 if (is_pseudo(a
->opcode
)) {
373 /* Pseudo's are normal. */
374 gnu_put_expression(a
, e
->left
, deref
);
376 gnu_put_expression(a
, e
->right
, deref
);
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
);
386 gnu_put_expression(a
, e
->left
, deref
);
390 if (deref
&& a
->optype
== JUMP
) gnu_putchar('*');
391 if (e
->left
!= nil
) gnu_put_expression(a
, e
->left
, 0);
393 if (e
->middle
!= nil
) gnu_put_expression(a
, e
->middle
, 0);
394 if (e
->right
!= nil
) {
396 gnu_put_expression(a
, e
->right
, 0);
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(')');
407 gnu_printf("%%%s", e
->name
);
413 gnu_printf("%%%s,%c", e
->name
, e
->operator);
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);
433 if (deref
&& a
->optype
>= BYTE
) gnu_putchar('$');
434 gnu_put_expression(a
, e
->left
, 0);
435 if (e
->operator == S_LEFTSHIFT
) {
438 if (e
->operator == S_RIGHTSHIFT
) {
441 gnu_putchar(e
->operator);
443 gnu_put_expression(a
, e
->right
, 0);
446 if (deref
&& a
->optype
>= BYTE
) gnu_putchar('$');
448 gnu_put_expression(a
, e
->middle
, 0);
452 if (isregister(e
->name
)) {
453 if (a
->optype
== JUMP
) gnu_putchar('*');
454 gnu_printf("%%%s", e
->name
);
456 if (deref
&& a
->optype
>= BYTE
) gnu_putchar('$');
457 gnu_printf("%s", e
->name
);
462 gnu_put_string(e
->name
, e
->len
);
467 "asmconv: internal error, unknown expression operator '%d'\n",
473 void gnu_emit_instruction(asm86_t
*a
)
474 /* Output one instruction and its operands. */
487 "asmconv: the GNU assembler can't translate 8086 code\n");
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) {
495 gnu_printf("# %ld \"%s\"\n", a
->line
, a
->file
);
499 if (a
->line
== eline
) {
503 while (eline
< a
->line
) {
509 if (a
->opcode
== DOT_END
) {
510 /* Ignore .end in gnu mode. */
512 if (a
->opcode
== DOT_LABEL
) {
513 assert(a
->args
->operator == ':');
514 gnu_printf("%s:", a
->args
->name
);
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);
521 if (a
->opcode
== DOT_ALIGN
) {
522 /* GNU .align thinks in powers of two. */
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
);
531 if ((p
= opcode2name(a
->opcode
)) != nil
) {
532 if (!is_pseudo(a
->opcode
) && !same
) gnu_putchar('\t');
536 case REP
: gnu_printf("rep; "); break;
537 case REPE
: gnu_printf("repe; "); break;
538 case REPNE
: gnu_printf("repne; "); break;
542 /* Kludge to avoid knowing where to put the "%es:" */
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;
553 /* Exceptions, exceptions... */
554 if (a
->opcode
== CBW
) {
555 if (!(a
->oaz
& OPZ
)) p
= "cwtl";
558 if (a
->opcode
== CWD
) {
559 if (!(a
->oaz
& OPZ
)) p
= "cltd";
563 if (a
->opcode
== RET
|| a
->opcode
== RETF
) {
564 /* Argument of RET needs a '$'. */
568 if (a
->opcode
== MUL
&& a
->args
!= nil
569 && a
->args
->operator == ',') {
570 /* Two operand MUL is an 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 == ',') {
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);
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);
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);
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);
639 if (a
->optype
== BYTE
) {
642 if (a
->optype
== WORD
) {
643 gnu_putchar((a
->oaz
& OPZ
) ? 'w' : 'l');
653 if (a
->args
!= nil
) {
654 static char *aregs
[] = { "al", "ax", "eax" };
659 gnu_put_expression(a
, a
->args
, 1);
660 gnu_printf(", %%%s", aregs
[a
->optype
- BYTE
]);
663 gnu_printf("%%%s, ", aregs
[a
->optype
- BYTE
]);
664 gnu_put_expression(a
, a
->args
, 1);
667 gnu_put_expression(a
, a
->args
, 1);
670 if (a
->opcode
== DOT_USE16
) set_use16();
671 if (a
->opcode
== DOT_USE32
) set_use32();
674 "asmconv: internal error, unknown opcode '%d'\n",