minix/commands/i386/asmconv/parse_bas.c
2009-11-09 10:26:00 +00:00

940 lines
22 KiB
C

/* parse_bas.c - parse BCC AS assembly Author: Kees J. Bot
* 13 Nov 1994
*/
#define nil 0
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "asmconv.h"
#include "token.h"
#include "asm86.h"
#include "languages.h"
typedef struct mnemonic { /* BAS mnemonics translation table. */
char *name;
opcode_t opcode;
optype_t optype;
} mnemonic_t;
static mnemonic_t mnemtab[] = { /* This array is sorted. */
{ ".align", DOT_ALIGN, PSEUDO },
{ ".ascii", DOT_ASCII, PSEUDO },
{ ".asciz", DOT_ASCIZ, PSEUDO },
{ ".assert", DOT_ASSERT, PSEUDO },
{ ".base", DOT_BASE, PSEUDO },
{ ".blkb", DOT_SPACE, PSEUDO },
{ ".bss", DOT_BSS, PSEUDO },
{ ".byte", DOT_DATA1, PSEUDO },
{ ".comm", DOT_COMM, PSEUDO },
{ ".data", DOT_DATA, PSEUDO },
{ ".define", DOT_DEFINE, PSEUDO },
{ ".end", DOT_END, PSEUDO },
{ ".even", DOT_ALIGN, PSEUDO },
{ ".extern", DOT_EXTERN, PSEUDO },
{ ".file", DOT_FILE, PSEUDO },
{ ".globl", DOT_DEFINE, PSEUDO },
{ ".lcomm", DOT_LCOMM, PSEUDO },
{ ".line", DOT_LINE, PSEUDO },
{ ".list", DOT_LIST, PSEUDO },
{ ".long", DOT_DATA4, PSEUDO },
{ ".nolist", DOT_NOLIST, PSEUDO },
{ ".rom", DOT_ROM, PSEUDO },
{ ".space", DOT_SPACE, PSEUDO },
{ ".symb", DOT_SYMB, PSEUDO },
{ ".text", DOT_TEXT, PSEUDO },
{ ".use16", DOT_USE16, PSEUDO },
{ ".use32", DOT_USE32, PSEUDO },
{ ".word", DOT_DATA2, PSEUDO },
{ ".zerob", DOT_SPACE, PSEUDO },
{ ".zerow", DOT_SPACE, PSEUDO },
{ "aaa", AAA, WORD },
{ "aad", AAD, WORD },
{ "aam", AAM, WORD },
{ "aas", AAS, WORD },
{ "adc", ADC, WORD },
{ "add", ADD, WORD },
{ "and", AND, WORD },
{ "arpl", ARPL, WORD },
{ "bc", JB, JUMP },
{ "beq", JE, JUMP },
{ "bge", JGE, JUMP },
{ "bgt", JG, JUMP },
{ "bhi", JA, JUMP },
{ "bhis", JAE, JUMP },
{ "ble", JLE, JUMP },
{ "blo", JB, JUMP },
{ "blos", JBE, JUMP },
{ "blt", JL, JUMP },
{ "bnc", JAE, JUMP },
{ "bne", JNE, JUMP },
{ "bound", BOUND, WORD },
{ "br", JMP, JUMP },
{ "bsf", BSF, WORD },
{ "bsr", BSR, WORD },
{ "bswap", BSWAP, WORD },
{ "bt", BT, WORD },
{ "btc", BTC, WORD },
{ "btr", BTR, WORD },
{ "bts", BTS, WORD },
{ "bz", JE, JUMP },
{ "call", CALL, JUMP },
{ "callf", CALLF, JUMP },
{ "cbw", CBW, WORD },
{ "cdq", CWD, WORD },
{ "clc", CLC, WORD },
{ "cld", CLD, WORD },
{ "cli", CLI, WORD },
{ "clts", CLTS, WORD },
{ "cmc", CMC, WORD },
{ "cmp", CMP, WORD },
{ "cmps", CMPS, WORD },
{ "cmpsb", CMPS, BYTE },
{ "cmpxchg", CMPXCHG, WORD },
{ "cwd", CWD, WORD },
{ "cwde", CBW, WORD },
{ "daa", DAA, WORD },
{ "das", DAS, WORD },
{ "dd", DOT_DATA4, PSEUDO },
{ "dec", DEC, WORD },
{ "div", DIV, WORD },
{ "enter", ENTER, WORD },
{ "export", DOT_DEFINE, PSEUDO },
{ "f2xm1", F2XM1, WORD },
{ "fabs", FABS, WORD },
{ "fadd", FADD, WORD },
{ "faddd", FADDD, WORD },
{ "faddp", FADDP, WORD },
{ "fadds", FADDS, WORD },
{ "fbld", FBLD, WORD },
{ "fbstp", FBSTP, WORD },
{ "fchs", FCHS, WORD },
{ "fclex", FCLEX, WORD },
{ "fcomd", FCOMD, WORD },
{ "fcompd", FCOMPD, WORD },
{ "fcompp", FCOMPP, WORD },
{ "fcomps", FCOMPS, WORD },
{ "fcoms", FCOMS, WORD },
{ "fcos", FCOS, WORD },
{ "fdecstp", FDECSTP, WORD },
{ "fdivd", FDIVD, WORD },
{ "fdivp", FDIVP, WORD },
{ "fdivrd", FDIVRD, WORD },
{ "fdivrp", FDIVRP, WORD },
{ "fdivrs", FDIVRS, WORD },
{ "fdivs", FDIVS, WORD },
{ "ffree", FFREE, WORD },
{ "fiaddl", FIADDL, WORD },
{ "fiadds", FIADDS, WORD },
{ "ficom", FICOM, WORD },
{ "ficomp", FICOMP, WORD },
{ "fidivl", FIDIVL, WORD },
{ "fidivrl", FIDIVRL, WORD },
{ "fidivrs", FIDIVRS, WORD },
{ "fidivs", FIDIVS, WORD },
{ "fildl", FILDL, WORD },
{ "fildq", FILDQ, WORD },
{ "filds", FILDS, WORD },
{ "fimull", FIMULL, WORD },
{ "fimuls", FIMULS, WORD },
{ "fincstp", FINCSTP, WORD },
{ "finit", FINIT, WORD },
{ "fistl", FISTL, WORD },
{ "fistp", FISTP, WORD },
{ "fists", FISTS, WORD },
{ "fisubl", FISUBL, WORD },
{ "fisubrl", FISUBRL, WORD },
{ "fisubrs", FISUBRS, WORD },
{ "fisubs", FISUBS, WORD },
{ "fld1", FLD1, WORD },
{ "fldcw", FLDCW, WORD },
{ "fldd", FLDD, WORD },
{ "fldenv", FLDENV, WORD },
{ "fldl2e", FLDL2E, WORD },
{ "fldl2t", FLDL2T, WORD },
{ "fldlg2", FLDLG2, WORD },
{ "fldln2", FLDLN2, WORD },
{ "fldpi", FLDPI, WORD },
{ "flds", FLDS, WORD },
{ "fldx", FLDX, WORD },
{ "fldz", FLDZ, WORD },
{ "fmuld", FMULD, WORD },
{ "fmulp", FMULP, WORD },
{ "fmuls", FMULS, WORD },
{ "fnop", FNOP, WORD },
{ "fpatan", FPATAN, WORD },
{ "fprem", FPREM, WORD },
{ "fprem1", FPREM1, WORD },
{ "fptan", FPTAN, WORD },
{ "frndint", FRNDINT, WORD },
{ "frstor", FRSTOR, WORD },
{ "fsave", FSAVE, WORD },
{ "fscale", FSCALE, WORD },
{ "fsin", FSIN, WORD },
{ "fsincos", FSINCOS, WORD },
{ "fsqrt", FSQRT, WORD },
{ "fstcw", FSTCW, WORD },
{ "fstd", FSTD, WORD },
{ "fstenv", FSTENV, WORD },
{ "fstpd", FSTPD, WORD },
{ "fstps", FSTPS, WORD },
{ "fstpx", FSTPX, WORD },
{ "fsts", FSTS, WORD },
{ "fstsw", FSTSW, WORD },
{ "fsubd", FSUBD, WORD },
{ "fsubp", FSUBP, WORD },
{ "fsubpr", FSUBPR, WORD },
{ "fsubrd", FSUBRD, WORD },
{ "fsubrs", FSUBRS, WORD },
{ "fsubs", FSUBS, WORD },
{ "ftst", FTST, WORD },
{ "fucom", FUCOM, WORD },
{ "fucomp", FUCOMP, WORD },
{ "fucompp", FUCOMPP, WORD },
{ "fxam", FXAM, WORD },
{ "fxch", FXCH, WORD },
{ "fxtract", FXTRACT, WORD },
{ "fyl2x", FYL2X, WORD },
{ "fyl2xp1", FYL2XP1, WORD },
{ "hlt", HLT, WORD },
{ "idiv", IDIV, WORD },
{ "imul", IMUL, WORD },
{ "in", IN, WORD },
{ "inb", IN, BYTE },
{ "inc", INC, WORD },
{ "ins", INS, WORD },
{ "insb", INS, BYTE },
{ "int", INT, WORD },
{ "into", INTO, JUMP },
{ "invd", INVD, WORD },
{ "invlpg", INVLPG, WORD },
{ "iret", IRET, JUMP },
{ "iretd", IRETD, JUMP },
{ "j", JMP, JUMP },
{ "ja", JA, JUMP },
{ "jae", JAE, JUMP },
{ "jb", JB, JUMP },
{ "jbe", JBE, JUMP },
{ "jc", JB, JUMP },
{ "jcxz", JCXZ, JUMP },
{ "je", JE, JUMP },
{ "jecxz", JCXZ, JUMP },
{ "jeq", JE, JUMP },
{ "jg", JG, JUMP },
{ "jge", JGE, JUMP },
{ "jgt", JG, JUMP },
{ "jhi", JA, JUMP },
{ "jhis", JAE, JUMP },
{ "jl", JL, JUMP },
{ "jle", JLE, JUMP },
{ "jlo", JB, JUMP },
{ "jlos", JBE, JUMP },
{ "jlt", JL, JUMP },
{ "jmp", JMP, JUMP },
{ "jmpf", JMPF, JUMP },
{ "jna", JBE, JUMP },
{ "jnae", JB, JUMP },
{ "jnb", JAE, JUMP },
{ "jnbe", JA, JUMP },
{ "jnc", JAE, JUMP },
{ "jne", JNE, JUMP },
{ "jng", JLE, JUMP },
{ "jnge", JL, JUMP },
{ "jnl", JGE, JUMP },
{ "jnle", JG, JUMP },
{ "jno", JNO, JUMP },
{ "jnp", JNP, JUMP },
{ "jns", JNS, JUMP },
{ "jnz", JNE, JUMP },
{ "jo", JO, JUMP },
{ "jp", JP, JUMP },
{ "js", JS, JUMP },
{ "jz", JE, JUMP },
{ "lahf", LAHF, WORD },
{ "lar", LAR, WORD },
{ "lds", LDS, WORD },
{ "lea", LEA, WORD },
{ "leave", LEAVE, WORD },
{ "les", LES, WORD },
{ "lfs", LFS, WORD },
{ "lgdt", LGDT, WORD },
{ "lgs", LGS, WORD },
{ "lidt", LIDT, WORD },
{ "lldt", LLDT, WORD },
{ "lmsw", LMSW, WORD },
{ "lock", LOCK, WORD },
{ "lods", LODS, WORD },
{ "lodsb", LODS, BYTE },
{ "loop", LOOP, JUMP },
{ "loope", LOOPE, JUMP },
{ "loopne", LOOPNE, JUMP },
{ "loopnz", LOOPNE, JUMP },
{ "loopz", LOOPE, JUMP },
{ "lsl", LSL, WORD },
{ "lss", LSS, WORD },
{ "ltr", LTR, WORD },
{ "mov", MOV, WORD },
{ "movs", MOVS, WORD },
{ "movsb", MOVS, BYTE },
{ "movsx", MOVSX, WORD },
{ "movzx", MOVZX, WORD },
{ "mul", MUL, WORD },
{ "neg", NEG, WORD },
{ "nop", NOP, WORD },
{ "not", NOT, WORD },
{ "or", OR, WORD },
{ "out", OUT, WORD },
{ "outb", OUT, BYTE },
{ "outs", OUTS, WORD },
{ "outsb", OUTS, BYTE },
{ "pop", POP, WORD },
{ "popa", POPA, WORD },
{ "popad", POPA, WORD },
{ "popf", POPF, WORD },
{ "popfd", POPF, WORD },
{ "push", PUSH, WORD },
{ "pusha", PUSHA, WORD },
{ "pushad", PUSHA, WORD },
{ "pushf", PUSHF, WORD },
{ "pushfd", PUSHF, WORD },
{ "rcl", RCL, WORD },
{ "rcr", RCR, WORD },
{ "ret", RET, JUMP },
{ "retf", RETF, JUMP },
{ "rol", ROL, WORD },
{ "ror", ROR, WORD },
{ "sahf", SAHF, WORD },
{ "sal", SAL, WORD },
{ "sar", SAR, WORD },
{ "sbb", SBB, WORD },
{ "scas", SCAS, WORD },
{ "seta", SETA, BYTE },
{ "setae", SETAE, BYTE },
{ "setb", SETB, BYTE },
{ "setbe", SETBE, BYTE },
{ "sete", SETE, BYTE },
{ "setg", SETG, BYTE },
{ "setge", SETGE, BYTE },
{ "setl", SETL, BYTE },
{ "setna", SETBE, BYTE },
{ "setnae", SETB, BYTE },
{ "setnb", SETAE, BYTE },
{ "setnbe", SETA, BYTE },
{ "setne", SETNE, BYTE },
{ "setng", SETLE, BYTE },
{ "setnge", SETL, BYTE },
{ "setnl", SETGE, BYTE },
{ "setnle", SETG, BYTE },
{ "setno", SETNO, BYTE },
{ "setnp", SETNP, BYTE },
{ "setns", SETNS, BYTE },
{ "seto", SETO, BYTE },
{ "setp", SETP, BYTE },
{ "sets", SETS, BYTE },
{ "setz", SETE, BYTE },
{ "sgdt", SGDT, WORD },
{ "shl", SHL, WORD },
{ "shld", SHLD, WORD },
{ "shr", SHR, WORD },
{ "shrd", SHRD, WORD },
{ "sidt", SIDT, WORD },
{ "sldt", SLDT, WORD },
{ "smsw", SMSW, WORD },
{ "stc", STC, WORD },
{ "std", STD, WORD },
{ "sti", STI, WORD },
{ "stos", STOS, WORD },
{ "stosb", STOS, BYTE },
{ "str", STR, WORD },
{ "sub", SUB, WORD },
{ "test", TEST, WORD },
{ "verr", VERR, WORD },
{ "verw", VERW, WORD },
{ "wait", WAIT, WORD },
{ "wbinvd", WBINVD, WORD },
{ "xadd", XADD, WORD },
{ "xchg", XCHG, WORD },
{ "xlat", XLAT, WORD },
{ "xor", XOR, WORD },
};
void bas_parse_init(char *file)
/* Prepare parsing of an BAS assembly file. */
{
tok_init(file, '!');
}
static void zap(void)
/* An error, zap the rest of the line. */
{
token_t *t;
while ((t= get_token(0))->type != T_EOF && t->symbol != ';')
skip_token(1);
}
static mnemonic_t *search_mnem(char *name)
/* Binary search for a mnemonic. (That's why the table is sorted.) */
{
int low, mid, high;
int cmp;
mnemonic_t *m;
low= 0;
high= arraysize(mnemtab)-1;
while (low <= high) {
mid= (low + high) / 2;
m= &mnemtab[mid];
if ((cmp= strcmp(name, m->name)) == 0) return m;
if (cmp < 0) high= mid-1; else low= mid+1;
}
return nil;
}
static expression_t *bas_get_C_expression(int *pn)
/* Read a "C-like" expression. Note that we don't worry about precedence,
* the expression is printed later like it is read. If the target language
* does not have all the operators (like ~) then this has to be repaired by
* changing the source file. (No problem, you still have one source file
* to maintain, not two.)
*/
{
expression_t *e, *a1, *a2;
token_t *t;
if ((t= get_token(*pn))->symbol == '(') {
/* ( expr ): grouping. */
(*pn)++;
if ((a1= bas_get_C_expression(pn)) == nil) return nil;
if (get_token(*pn)->symbol != ')') {
parse_err(1, t, "missing )\n");
del_expr(a1);
return nil;
}
(*pn)++;
e= new_expr();
e->operator= '[';
e->middle= a1;
} else
if (t->type == T_WORD || t->type == T_STRING) {
/* Label, number, or string. */
e= new_expr();
e->operator= t->type == T_WORD ? 'W' : 'S';
e->name= allocate(nil, (t->len+1) * sizeof(e->name[0]));
memcpy(e->name, t->name, t->len+1);
e->len= t->len;
(*pn)++;
} else
if (t->symbol == '+' || t->symbol == '-' || t->symbol == '~') {
/* Unary operator. */
(*pn)++;
if ((a1= bas_get_C_expression(pn)) == nil) return nil;
e= new_expr();
e->operator= t->symbol;
e->middle= a1;
} else
if (t->symbol == '$' && get_token(*pn + 1)->type == T_WORD) {
/* A hexadecimal number. */
t= get_token(*pn + 1);
e= new_expr();
e->operator= 'W';
e->name= allocate(nil, (t->len+3) * sizeof(e->name[0]));
strcpy(e->name, "0x");
memcpy(e->name+2, t->name, t->len+1);
e->len= t->len+2;
(*pn)+= 2;
} else {
parse_err(1, t, "expression syntax error\n");
return nil;
}
switch ((t= get_token(*pn))->symbol) {
case '+':
case '-':
case '*':
case '/':
case '%':
case '&':
case '|':
case '^':
case S_LEFTSHIFT:
case S_RIGHTSHIFT:
(*pn)++;
a1= e;
if ((a2= bas_get_C_expression(pn)) == nil) {
del_expr(a1);
return nil;
}
e= new_expr();
e->operator= t->symbol;
e->left= a1;
e->right= a2;
}
return e;
}
/* We want to know the sizes of the first two operands. */
static optype_t optypes[2];
static int op_idx;
static expression_t *bas_get_operand(int *pn)
/* Get something like: [memory], offset[base+index*scale], or simpler. */
{
expression_t *e, *offset, *base, *index;
token_t *t;
int c;
optype_t optype;
/* Prefixed by 'byte', 'word' or 'dword'? */
if ((t= get_token(*pn))->type == T_WORD && (
strcmp(t->name, "byte") == 0
|| strcmp(t->name, "word") == 0
|| strcmp(t->name, "dword") == 0)
) {
switch (t->name[0]) {
case 'b': optype= BYTE; break;
case 'w': optype= use16() ? WORD : OWORD; break;
case 'd': optype= use32() ? WORD : OWORD; break;
}
if (op_idx < arraysize(optypes)) optypes[op_idx++]= optype;
(*pn)++;
/* It may even be "byte ptr"... */
if ((t= get_token(*pn))->type == T_WORD
&& strcmp(t->name, "ptr") == 0) {
(*pn)++;
}
}
/* Is it [memory]? */
if (get_token(*pn)->symbol == '['
&& ((t= get_token(*pn + 1))->type != T_WORD
|| !isregister(t->name))
) {
/* A memory dereference. */
(*pn)++;
if ((offset= bas_get_C_expression(pn)) == nil) return nil;
if (get_token(*pn)->symbol != ']') {
parse_err(1, t, "operand syntax error\n");
del_expr(offset);
return nil;
}
(*pn)++;
e= new_expr();
e->operator= '(';
e->middle= offset;
return e;
}
/* #something? *something? */
if ((c= get_token(*pn)->symbol) == '#' || c == '*') {
/* '#' and '*' are often used to introduce some constant. */
(*pn)++;
}
/* Offset? */
if (get_token(*pn)->symbol != '[') {
/* There is an offset. */
if ((offset= bas_get_C_expression(pn)) == nil) return nil;
} else {
/* No offset. */
offset= nil;
}
/* [base]? [base+? base-? */
c= 0;
if (get_token(*pn)->symbol == '['
&& (t= get_token(*pn + 1))->type == T_WORD
&& isregister(t->name)
&& ((c= get_token(*pn + 2)->symbol) == ']' || c=='+' || c=='-')
) {
/* A base register expression. */
base= new_expr();
base->operator= 'B';
base->name= copystr(t->name);
(*pn)+= c == ']' ? 3 : 2;
} else {
/* No base register expression. */
base= nil;
}
/* +offset]? -offset]? */
if (offset == nil
&& (c == '+' || c == '-')
&& (t= get_token(*pn + 1))->type == T_WORD
&& !isregister(t->name)
) {
(*pn)++;
if ((offset= bas_get_C_expression(pn)) == nil) return nil;
if (get_token(*pn)->symbol != ']') {
parse_err(1, t, "operand syntax error\n");
del_expr(offset);
del_expr(base);
return nil;
}
(*pn)++;
c= 0;
}
/* [index*scale]? +index*scale]? */
if (c == '+' || get_token(*pn)->symbol == '[') {
/* An index most likely. */
token_t *m= nil;
if (!( /* This must be true: */
(t= get_token(*pn + 1))->type == T_WORD
&& isregister(t->name)
&& (get_token(*pn + 2)->symbol == ']' || (
get_token(*pn + 2)->symbol == '*'
&& (m= get_token(*pn + 3))->type == T_WORD
&& strchr("1248", m->name[0]) != nil
&& m->name[1] == 0
&& get_token(*pn + 4)->symbol == ']'
))
)) {
/* Alas it isn't */
parse_err(1, t, "operand syntax error\n");
del_expr(offset);
del_expr(base);
return nil;
}
/* Found an index. */
index= new_expr();
index->operator= m == nil ? '1' : m->name[0];
index->name= copystr(t->name);
(*pn)+= (m == nil ? 3 : 5);
} else {
/* No index. */
index= nil;
}
if (base == nil && index == nil) {
/* Return a lone offset as is. */
e= offset;
/* Lone registers tell operand size. */
if (offset->operator == 'W' && isregister(offset->name)) {
switch (isregister(offset->name)) {
case 1: optype= BYTE; break;
case 2: optype= use16() ? WORD : OWORD; break;
case 4: optype= use32() ? WORD : OWORD; break;
}
if (op_idx < arraysize(optypes))
optypes[op_idx++]= optype;
}
} else {
e= new_expr();
e->operator= 'O';
e->left= offset;
e->middle= base;
e->right= index;
}
return e;
}
static expression_t *bas_get_oplist(int *pn)
/* Get a comma (or colon for jmpf and callf) separated list of instruction
* operands.
*/
{
expression_t *e, *o1, *o2;
token_t *t;
if ((e= bas_get_operand(pn)) == nil) return nil;
if ((t= get_token(*pn))->symbol == ',' || t->symbol == ':') {
o1= e;
(*pn)++;
if ((o2= bas_get_oplist(pn)) == nil) {
del_expr(o1);
return nil;
}
e= new_expr();
e->operator= ',';
e->left= o1;
e->right= o2;
}
return e;
}
static asm86_t *bas_get_statement(void)
/* Get a pseudo op or machine instruction with arguments. */
{
token_t *t= get_token(0);
asm86_t *a;
mnemonic_t *m;
int n;
int prefix_seen;
assert(t->type == T_WORD);
if (strcmp(t->name, ".sect") == 0) {
/* .sect .text etc. Accept only four segment names. */
skip_token(1);
t= get_token(0);
if (t->type != T_WORD || (
strcmp(t->name, ".text") != 0
&& strcmp(t->name, ".rom") != 0
&& strcmp(t->name, ".data") != 0
&& strcmp(t->name, ".bss") != 0
&& strcmp(t->name, ".end") != 0
)) {
parse_err(1, t, "weird section name to .sect\n");
return nil;
}
}
a= new_asm86();
/* Process instruction prefixes. */
for (prefix_seen= 0;; prefix_seen= 1) {
if (strcmp(t->name, "rep") == 0
|| strcmp(t->name, "repe") == 0
|| strcmp(t->name, "repne") == 0
|| strcmp(t->name, "repz") == 0
|| strcmp(t->name, "repnz") == 0
) {
if (a->rep != ONCE) {
parse_err(1, t,
"can't have more than one rep\n");
}
switch (t->name[3]) {
case 0: a->rep= REP; break;
case 'e':
case 'z': a->rep= REPE; break;
case 'n': a->rep= REPNE; break;
}
} else
if (strcmp(t->name, "seg") == 0
&& get_token(1)->type == T_WORD) {
if (a->seg != DEFSEG) {
parse_err(1, t,
"can't have more than one segment prefix\n");
}
switch (get_token(1)->name[0]) {
case 'c': a->seg= CSEG; break;
case 'd': a->seg= DSEG; break;
case 'e': a->seg= ESEG; break;
case 'f': a->seg= FSEG; break;
case 'g': a->seg= GSEG; break;
case 's': a->seg= SSEG; break;
}
skip_token(1);
} else
if (!prefix_seen) {
/* No prefix here, get out! */
break;
} else {
/* No more prefixes, next must be an instruction. */
if (t->type != T_WORD
|| (m= search_mnem(t->name)) == nil
|| m->optype == PSEUDO
) {
parse_err(1, t,
"machine instruction expected after instruction prefix\n");
del_asm86(a);
return nil;
}
break;
}
/* Skip the prefix and extra newlines. */
do {
skip_token(1);
} while ((t= get_token(0))->symbol == ';');
}
/* All the readahead being done upsets the line counter. */
a->line= t->line;
/* Read a machine instruction or pseudo op. */
if ((m= search_mnem(t->name)) == nil) {
parse_err(1, t, "unknown instruction '%s'\n", t->name);
del_asm86(a);
return nil;
}
a->opcode= m->opcode;
a->optype= m->optype;
if (a->opcode == CBW || a->opcode == CWD) {
a->optype= (strcmp(t->name, "cbw") == 0
|| strcmp(t->name, "cwd") == 0) == use16() ? WORD : OWORD;
}
for (op_idx= 0; op_idx < arraysize(optypes); op_idx++)
optypes[op_idx]= m->optype;
op_idx= 0;
n= 1;
if (get_token(1)->symbol != ';'
&& (a->args= bas_get_oplist(&n)) == nil) {
del_asm86(a);
return nil;
}
if (m->optype == WORD) {
/* Does one of the operands overide the optype? */
for (op_idx= 0; op_idx < arraysize(optypes); op_idx++) {
if (optypes[op_idx] != m->optype)
a->optype= optypes[op_idx];
}
}
if (get_token(n)->symbol != ';') {
parse_err(1, t, "garbage at end of instruction\n");
del_asm86(a);
return nil;
}
switch (a->opcode) {
case DOT_ALIGN:
/* Restrict .align to have a single numeric argument, some
* assemblers think of the argument as a power of two, so
* we need to be able to change the value.
*/
if (strcmp(t->name, ".even") == 0 && a->args == nil) {
/* .even becomes .align 2. */
expression_t *e;
a->args= e= new_expr();
e->operator= 'W';
e->name= copystr("2");
e->len= 2;
}
if (a->args == nil || a->args->operator != 'W'
|| !isanumber(a->args->name)) {
parse_err(1, t,
".align is restricted to one numeric argument\n");
del_asm86(a);
return nil;
}
break;
case MOVSX:
case MOVZX:
/* Types of both operands tell the instruction type. */
a->optype= optypes[0];
if (optypes[1] == BYTE) {
a->opcode= a->opcode == MOVSX ? MOVSXB : MOVZXB;
}
break;
case SAL:
case SAR:
case SHL:
case SHR:
case RCL:
case RCR:
case ROL:
case ROR:
/* Only the first operand tells the operand size. */
a->optype= optypes[0];
break;
default:;
}
skip_token(n+1);
return a;
}
asm86_t *bas_get_instruction(void)
{
asm86_t *a= nil;
expression_t *e;
token_t *t;
while ((t= get_token(0))->symbol == ';')
skip_token(1);
if (t->type == T_EOF) return nil;
if (t->symbol == '#') {
/* Preprocessor line and file change. */
if ((t= get_token(1))->type != T_WORD || !isanumber(t->name)
|| get_token(2)->type != T_STRING
) {
parse_err(1, t, "file not preprocessed?\n");
zap();
} else {
set_file(get_token(2)->name,
strtol(get_token(1)->name, nil, 0) - 1);
/* GNU CPP adds extra cruft, simply zap the line. */
zap();
}
a= bas_get_instruction();
} else
if (t->type == T_WORD && get_token(1)->symbol == ':') {
/* A label definition. */
a= new_asm86();
a->line= t->line;
a->opcode= DOT_LABEL;
a->optype= PSEUDO;
a->args= e= new_expr();
e->operator= ':';
e->name= copystr(t->name);
skip_token(2);
} else
if (t->type == T_WORD && get_token(1)->symbol == '=') {
int n= 2;
if ((e= bas_get_C_expression(&n)) == nil) {
zap();
a= bas_get_instruction();
} else
if (get_token(n)->symbol != ';') {
parse_err(1, t, "garbage after assignment\n");
zap();
a= bas_get_instruction();
} else {
a= new_asm86();
a->line= t->line;
a->opcode= DOT_EQU;
a->optype= PSEUDO;
a->args= new_expr();
a->args->operator= '=';
a->args->name= copystr(t->name);
a->args->middle= e;
skip_token(n+1);
}
} else
if (t->type == T_WORD && get_token(1)->type == T_WORD
&& strcmp(get_token(1)->name, "lcomm") == 0) {
/* Local common block definition. */
int n= 2;
if ((e= bas_get_C_expression(&n)) == nil) {
zap();
a= bas_get_instruction();
} else
if (get_token(n)->symbol != ';') {
parse_err(1, t, "garbage after lcomm\n");
zap();
a= bas_get_instruction();
} else {
a= new_asm86();
a->line= t->line;
a->opcode= DOT_LCOMM;
a->optype= PSEUDO;
a->args= new_expr();
a->args->operator= ',';
a->args->right= e;
a->args->left= e= new_expr();
e->operator= 'W';
e->name= copystr(t->name);
e->len= strlen(e->name)+1;
skip_token(n+1);
}
} else
if (t->type == T_WORD) {
if ((a= bas_get_statement()) == nil) {
zap();
a= bas_get_instruction();
}
} else {
parse_err(1, t, "syntax error\n");
zap();
a= bas_get_instruction();
}
if (a->optype == OWORD) {
a->optype= WORD;
a->oaz|= OPZ;
}
return a;
}