quakeforge/tools/gas2masm/gas2masm.c
2001-09-28 07:09:38 +00:00

1081 lines
19 KiB
C

/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
static const char rcsid[] =
"$Id$";
//
// gas to MASM source code converter
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_TOKENS 100
#define MAX_TOKEN_LENGTH 1024
#define LF 0x0A
typedef enum {NOT_WHITESPACE, WHITESPACE, TOKEN_AVAILABLE, LINE_DONE, FILE_DONE, PARSED_OKAY} tokenstat;
typedef enum {NOSEG, DATASEG, TEXTSEG} segtype;
int tokennum;
int linein, lineout;
char *token;
char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH+1];
segtype currentseg = NOSEG;
typedef struct {
char *text;
char *emit;
int numtokens;
void (*parsefunc) (void);
} parsefield;
void errorexit (void);
//==============================================
typedef struct {
char *text;
char *emit;
int len;
} regdesc;
regdesc reglist[] = {
{"%eax", "eax", 4},
{"%ebx", "ebx", 4},
{"%ecx", "ecx", 4},
{"%edx", "edx", 4},
{"%esi", "esi", 4},
{"%edi", "edi", 4},
{"%ebp", "ebp", 4},
{"%esp", "esp", 4},
{"%ax", "ax", 3},
{"%bx", "bx", 3},
{"%cx", "cx", 3},
{"%dx", "dx", 3},
{"%si", "si", 3},
{"%di", "di", 3},
{"%bp", "bp", 3},
{"%sp", "sp", 3},
{"%al", "al", 3},
{"%bl", "bl", 3},
{"%cl", "cl", 3},
{"%dl", "dl", 3},
{"%ah", "ah", 3},
{"%bh", "bh", 3},
{"%ch", "ch", 3},
{"%dh", "dh", 3},
{"%st(0)", "st(0)", 6},
{"%st(1)", "st(1)", 6},
{"%st(2)", "st(2)", 6},
{"%st(3)", "st(3)", 6},
{"%st(4)", "st(4)", 6},
{"%st(5)", "st(5)", 6},
{"%st(6)", "st(6)", 6},
{"%st(7)", "st(7)", 6},
};
int numregs = sizeof (reglist) / sizeof (reglist[0]);
//==============================================
void emitanoperand (int tnum, char *type, int notdata)
{
int i, index, something_outside_parens, regfound;
int parencount;
char *pt;
char temp[MAX_TOKEN_LENGTH+1];
pt = tokens[tnum];
if (pt[0] == '%')
{
// register
for (i=0 ; i<numregs ; i++)
{
if (!strcmpi (pt, reglist[i].text))
{
printf ("%s", reglist[i].emit);
return;
}
}
fprintf (stderr, "Error: bad register %s\n", pt);
errorexit ();
}
else if (pt[0] == '$')
{
// constant
if (pt[1] == '(')
{
if ((pt[2] > '9') || (pt[2] < '0'))
{
i = 2;
printf ("offset ");
parencount = 1;
while ((pt[i] != ')') || (parencount > 1))
{
if (!pt[i])
{
fprintf (stderr, "mismatched parens");
errorexit ();
}
if (pt[i] == ')')
parencount--;
else if (pt[i] == '(')
parencount++;
printf ("%c", pt[i]);
i++;
}
}
else
{
pt++;
parencount = 1;
for (i=1 ; (pt[i] != ')') || (parencount > 1) ; i++)
{
if (!pt[i])
{
fprintf (stderr, "mismatched parens");
errorexit ();
}
if (pt[i] == ')')
parencount--;
else if (pt[i] == '(')
parencount++;
}
pt[i] = 0;
if ((pt[1] == '0') && ((pt[2] == 'x') || (pt[2] == 'X')))
{
printf ("0%sh", &pt[3]);
}
else
{
printf ("%s", &pt[1]);
}
}
}
else if ((pt[1] == '0') && ((pt[2] == 'x') || (pt[2] == 'X')))
{
printf ("0%sh", &pt[3]);
}
else if ((pt[1] >= '0') && (pt[1] <= '9'))
{
printf ("%s", &pt[1]);
}
else
{
printf ("offset %s", &pt[1]);
}
}
else if (!notdata && ((pt[0] >= '0') && (pt[0] <= '9')))
{
pt--;
if ((pt[1] == '0') && ((pt[2] == 'x') || (pt[2] == 'X')))
{
printf ("0%sh", &pt[3]);
}
else
{
printf ("%s", &pt[1]);
}
}
else
{
// must be a memory location
strcpy (temp, type);
index = strlen (temp);
if (notdata)
temp[index++] = '[';
something_outside_parens = 0;
while (*pt)
{
if (index > (MAX_TOKEN_LENGTH - 10))
{
fprintf (stderr, "Error: operand too long %s\n",
tokens[tnum]);
errorexit ();
}
if (*pt != ')')
{
if (*pt == '(')
{
if (something_outside_parens)
temp[index++] = '+';
}
else if (*pt == '%')
{
regfound = 0;
for (i=0 ; i<numregs ; i++)
{
if (!strnicmp (pt, reglist[i].text,
reglist[i].len))
{
strcpy (&temp[index], reglist[i].emit);
index += strlen (reglist[i].emit);
pt += strlen (reglist[i].text) - 1;
regfound = 1;
break;
}
}
if (!regfound)
{
fprintf (stderr, "Error: bad register %s\n", pt);
errorexit ();
}
}
else if (*pt == ',')
{
pt++;
if ((*pt >= '1') && (*pt <= '8'))
{
temp[index++] = '*';
temp[index++] = *pt;
}
else if (*pt != ')')
{
if (temp[index-1] != '+')
temp[index++] = '+';
}
}
else
{
something_outside_parens = 1;
// handle hexadecimal constants in addresses
if ((*pt == '0') &&
((*(pt+1) == 'x') || (*(pt+1) == 'X')))
{
pt += 2;
do
{
temp[index++] = *pt++;
} while (((*pt >= '0') && (*pt <= '9')) ||
((*pt >= 'a') && (*pt <= 'f')) ||
((*pt >= 'A') && (*pt <= 'F')));
pt--;
temp[index++] = 'h';
}
else
{
temp[index++] = *pt;
}
}
}
pt++;
}
if (notdata)
temp[index++] = ']';
temp[index] = 0;
printf ("%s", temp);
}
}
void datasegstart (void)
{
if (currentseg == DATASEG)
return;
if (currentseg == TEXTSEG)
printf ("_TEXT ENDS\n");
printf ("_DATA SEGMENT");
currentseg = DATASEG;
}
void textsegstart (void)
{
if (currentseg == TEXTSEG)
return;
if (currentseg == DATASEG)
printf ("_DATA ENDS\n");
printf ("_TEXT SEGMENT");
currentseg = TEXTSEG;
}
void emitdata (void)
{
int i;
for (i=1 ; i<(tokennum-1) ; i++)
printf (" %s,", tokens[i]);
printf (" %s", tokens[tokennum-1]);
}
void emitonedata (void)
{
printf (" %s", tokens[1]);
}
void emitonecalldata (void)
{
int i, isaddr, len;
if (tokens[1][0] == '*')
{
printf (" dword ptr[%s]", &tokens[1][1]);
}
else
{
isaddr = 0;
len = strlen(tokens[1]);
for (i=0 ; i<len ; i++)
{
if (tokens[1][i] == '(')
{
isaddr = 1;
break;
}
}
if (!isaddr)
{
printf (" near ptr %s", tokens[1]);
}
else
{
emitanoperand (1, " dword ptr", 1);
}
}
}
void emitonejumpdata (void)
{
/*jmp *Ljmptab(,%eax,4)
jmp dword ptr[Ljmptab(,%eax,4)]
jmp dword ptr[Ljmptab+eax*4]
*/
int i, isaddr, len;
isaddr = 0;
len = strlen(tokens[1]);
if (tokens[1][0] == '*') {
for (i=0 ; i<len ; i++) {
if (tokens[1][i] == '(') {
isaddr = 1;
break;
}
}
memmove(&tokens[1][0],&tokens[1][1],strlen(tokens[1]));
if ( !isaddr ) {
//printf (" dword ptr [%s]", &tokens[1][1]);
printf (" dword ptr [");
emitanoperand (1, "", 1);
printf ("]");
} else {
emitanoperand (1, " dword ptr", 1);
}
} else {
for (i=0 ; i<len ; i++) {
if (tokens[1][i] == '(') {
isaddr = 1;
break;
}
}
if (!isaddr) {
printf (" %s", tokens[1]);
} else {
emitanoperand (1, " dword ptr", 1);
}
}
}
void emitexterndef (void)
{
printf (" %s:dword", tokens[1]);
}
void nooperands (void)
{
}
void emitoneoperandl (void)
{
printf (" ");
emitanoperand (1, "ds:dword ptr", 1);
}
void emitoneoperandb (void)
{
printf (" ");
emitanoperand (1, "ds:byte ptr", 1);
}
void emitoneoperandw (void)
{
printf (" ");
emitanoperand (1, "ds:word ptr", 1);
}
void emittwooperandsl (void)
{
printf (" ");
emitanoperand (2, "ds:dword ptr", 1);
printf (",");
emitanoperand (1, "ds:dword ptr", 1);
}
void emittwooperandsb (void)
{
printf (" ");
emitanoperand (2, "ds:byte ptr", 1);
printf (",");
emitanoperand (1, "ds:byte ptr", 1);
}
void emittwooperandsw (void)
{
printf (" ");
emitanoperand (2, "ds:word ptr", 1);
printf (",");
emitanoperand (1, "ds:word ptr", 1);
}
void emit_0_or_1_operandsl (void)
{
if (tokennum == 2)
{
printf (" ");
emitanoperand (1, "ds:dword ptr", 1);
}
}
void emit_1_or_2_operandsl (void)
{
int j;
if (tokennum == 2)
{
printf (" ");
emitanoperand (1, "ds:dword ptr", 1);
}
else if (tokennum == 3)
{
printf (" ");
emitanoperand (2, "ds:dword ptr", 1);
printf (",");
emitanoperand (1, "ds:dword ptr", 1);
}
else
{
fprintf (stderr, "Error: too many operands\n");
for (j=0 ; j<tokennum ; j++)
fprintf (stderr, "%s\n", tokens[j]);
fprintf (stderr, "\n");
errorexit ();
}
}
void emit_1_or_2_operandsl_vartext (char *str0, char *str1)
{
int j;
if (tokennum == 2)
{
printf (" %s ", str0);
emitanoperand (1, "ds:dword ptr", 1);
}
else if (tokennum == 3)
{
if (!strcmpi (tokens[2], "%st(0)"))
printf (" %s ", str0);
else
printf (" %s ", str1);
emitanoperand (2, "ds:dword ptr", 1);
printf (",");
emitanoperand (1, "ds:dword ptr", 1);
}
else
{
fprintf (stderr, "Error: too many operands\n");
for (j=0 ; j<tokennum ; j++)
fprintf (stderr, "%s\n", tokens[j]);
fprintf (stderr, "\n");
errorexit ();
}
}
void special_fdivl (void)
{
emit_1_or_2_operandsl_vartext ("fdiv", "fdivr");
}
void special_fdivpl (void)
{
emit_1_or_2_operandsl_vartext ("fdivp", "fdivrp");
}
void special_fdivrl (void)
{
emit_1_or_2_operandsl_vartext ("fdivr", "fdiv");
}
void special_fdivrpl (void)
{
emit_1_or_2_operandsl_vartext ("fdivrp", "fdivp");
}
void special_fsubl (void)
{
emit_1_or_2_operandsl_vartext ("fsub", "fsubr");
}
void special_fsubpl (void)
{
emit_1_or_2_operandsl_vartext ("fsubp", "fsubrp");
}
void special_fsubrl (void)
{
emit_1_or_2_operandsl_vartext ("fsubr", "fsub");
}
void special_fsubrpl (void)
{
emit_1_or_2_operandsl_vartext ("fsubrp", "fsubp");
}
void emit_multiple_data (void)
{
int i;
printf (" ");
for (i=1 ; i<(tokennum-1) ; i++)
{
emitanoperand (i, "", 0);
printf (", ");
}
emitanoperand (i, "", 0);
}
//==============================================
parsefield parsedata[] = {
{".align", " align", 2, emitonedata},
{".byte", " db", -2, emit_multiple_data},
{".data", "", 1, datasegstart},
{".extern"," externdef", 2, emitexterndef},
{".globl", " public", -2, emit_multiple_data},
{".long", " dd", -2, emit_multiple_data},
{".single"," dd", -2, emit_multiple_data},
{".text", "", 1, textsegstart},
{"adcl", " adc", 3, emittwooperandsl},
{"addb", " add", 3, emittwooperandsb},
{"addl", " add", 3, emittwooperandsl},
{"andb", " and", 3, emittwooperandsb},
{"andl", " and", 3, emittwooperandsl},
{"call", " call", 2, emitonecalldata},
{"cmpb", " cmp", 3, emittwooperandsb},
{"cmpl", " cmp", 3, emittwooperandsl},
{"cmpw", " cmp", 3, emittwooperandsw},
{"decl", " dec", 2, emitoneoperandl},
{"decw", " dec", 2, emitoneoperandw},
{"divl", " div", 2, emitoneoperandl},
{"fadd", " fadd", -2, emit_1_or_2_operandsl},
{"faddp", " faddp", -2, emit_1_or_2_operandsl},
{"faddps", " faddp", -2, emit_1_or_2_operandsl},
{"fadds", " fadd", -2, emit_1_or_2_operandsl},
{"fcom", " fcom", 2, emitoneoperandl},
{"fcoms", " fcom", 2, emitoneoperandl},
{"fcomp", " fcomp", 2, emitoneoperandl},
{"fcomps", " fcomp", 2, emitoneoperandl},
{"fdiv", "", -2, special_fdivl},
{"fdivp", "", -2, special_fdivpl},
{"fdivr", "", -2, special_fdivrl},
{"fdivrp", "", -2, special_fdivrpl},
{"fdivrs", "", -2, special_fdivrl},
{"fildl", " fild", 2, emitoneoperandl},
{"fistl", " fist", 2, emitoneoperandl},
{"fistpl", " fistp", 2, emitoneoperandl},
{"fld", " fld", 2, emitoneoperandl},
{"fldcw", " fldcw", 2, emitoneoperandw},
{"fldenv", " fldenv", 2, emitoneoperandl},
{"flds", " fld", 2, emitoneoperandl},
{"fmul", " fmul", -2, emit_1_or_2_operandsl},
{"fmulp", " fmulp", -2, emit_1_or_2_operandsl},
{"fmulps", " fmulp", -2, emit_1_or_2_operandsl},
{"fmuls", " fmul", -2, emit_1_or_2_operandsl},
{"fnstcw", " fnstcw", 2, emitoneoperandw},
{"fnstenv"," fnstenv", 2, emitoneoperandl},
{"fnstsw", " fnstsw", 2, emitoneoperandw},
{"fstp", " fstp", 2, emitoneoperandl},
{"fstps", " fstp", 2, emitoneoperandl},
{"fsts", " fst", 2, emitoneoperandl},
{"fsubr", "", -2, special_fsubrl},
{"fsubrp", "", -2, special_fsubrpl},
{"fsubrs", "", -2, special_fsubrl},
{"fsub", "", -2, special_fsubl},
{"fsubp", "", -2, special_fsubpl},
{"fsubps", "", -2, special_fsubpl},
{"fsubs", "", -2, special_fsubl},
{"fxch", " fxch", 2, emitoneoperandl},
{"imull", " imul", -2, emit_1_or_2_operandsl},
{"incl", " inc", 2, emitoneoperandl},
{"ja", " ja", 2, emitonedata},
{"jae", " jae", 2, emitonedata},
{"jb", " jb", 2, emitonedata},
{"jbe", " jbe", 2, emitonedata},
{"jc", " jc", 2, emitonedata},
{"je", " je", 2, emitonedata},
{"jg", " jg", 2, emitonedata},
{"jge", " jge", 2, emitonedata},
{"jl", " jl", 2, emitonedata},
{"jle", " jle", 2, emitonedata},
{"jmp", " jmp", 2, emitonejumpdata},
{"jna", " jna", 2, emitonedata},
{"jnae", " jnae", 2, emitonedata},
{"jnb", " jnb", 2, emitonedata},
{"jnbe", " jnbe", 2, emitonedata},
{"jnc", " jnc", 2, emitonedata},
{"jne", " jne", 2, emitonedata},
{"jng", " jng", 2, emitonedata},
{"jnge", " jnge", 2, emitonedata},
{"jnl", " jnl", 2, emitonedata},
{"jnle", " jnle", 2, emitonedata},
{"jns", " jns", 2, emitonedata},
{"jnz", " jnz", 2, emitonedata},
{"js", " js", 2, emitonedata},
{"jz", " jz", 2, emitonedata},
{"leal", " lea", 3, emittwooperandsl},
{"movb", " mov", 3, emittwooperandsb},
{"movl", " mov", 3, emittwooperandsl},
{"movw", " mov", 3, emittwooperandsw},
{"negl", " neg", 2, emitoneoperandl},
{"orb", " or", 3, emittwooperandsb},
{"orl", " or", 3, emittwooperandsl},
{"popl", " pop", 2, emitoneoperandl},
{"pushl", " push", 2, emitoneoperandl},
{"ret", " ret", -1, emit_0_or_1_operandsl},
{"rorl", " ror", 3, emittwooperandsl},
{"sarl", " sar", 3, emittwooperandsl},
{"sbbl", " sbb", 3, emittwooperandsl},
{"shll", " shl", 3, emittwooperandsl},
{"shrl", " shr", 3, emittwooperandsl},
{"subl", " sub", 3, emittwooperandsl},
{"testb", " test", 3, emittwooperandsb},
{"testl", " test", 3, emittwooperandsl},
{"xorb", " xor", 3, emittwooperandsb},
{"xorl", " xor", 3, emittwooperandsl},
};
int numparse = sizeof (parsedata) / sizeof (parsedata[0]);
//==============================================
void errorexit (void)
{
fprintf (stderr, "In line: %d, out line: %d\n", linein, lineout);
exit (1);
}
tokenstat whitespace (int c)
{
if (c == '\n')
return LINE_DONE;
if ((c <= ' ') ||
//(c > 127) ||
(c == ','))
{
return WHITESPACE;
}
return NOT_WHITESPACE;
}
int gettoken (void)
{
int c;
int count, parencount;
tokenstat stat;
do
{
if ((c = getchar ()) == EOF)
return FILE_DONE;
if ((stat = whitespace (c)) == LINE_DONE)
return LINE_DONE;
} while (stat == WHITESPACE);
if (c == '#')
{
do
{
if ((c = getchar()) == EOF)
return FILE_DONE;
} while (whitespace(c) != LINE_DONE);
return LINE_DONE;
}
token[0] = c;
count = 1;
if (c == '~')
{
count--;
token[count++] = 'n';
token[count++] = 'o';
token[count++] = 't';
token[count++] = ' ';
}
if (c == '(')
{
do
{
if ((c = getchar ()) == EOF)
{
fprintf (stderr, "EOF in middle of parentheses\n");
errorexit ();
}
token[count++] = c;
} while (c != ')');
}
for ( ;; )
{
if ((c = getchar ()) == EOF)
{
token[count] = 0;
return TOKEN_AVAILABLE;
}
if (whitespace (c) == LINE_DONE)
{
if (ungetc (c, stdin) == EOF)
{
fprintf (stderr, "Couldn't unget character\n");
errorexit ();
}
token[count] = 0;
return TOKEN_AVAILABLE;
}
if (whitespace (c) == WHITESPACE)
{
token[count] = 0;
return TOKEN_AVAILABLE;
}
if (count >= MAX_TOKEN_LENGTH)
{
fprintf (stderr, "Error: token too long\n");
errorexit ();
}
token[count++] = c;
if (c == '~')
{
count--;
token[count++] = 'n';
token[count++] = 'o';
token[count++] = 't';
token[count++] = ' ';
}
else if (c == '(')
{
parencount = 1;
do
{
if ((c = getchar ()) == EOF)
{
fprintf (stderr, "EOF in middle of parentheses\n");
errorexit ();
}
if (c == '(')
parencount++;
else if (c == ')')
parencount--;
if (c == '~')
{
token[count++] = 'n';
token[count++] = 'o';
token[count++] = 't';
token[count++] = ' ';
}
else
{
token[count++] = c;
}
} while ((c != ')') || (parencount > 0));
}
}
}
tokenstat parseline (void)
{
tokenstat stat;
int i, j, firsttoken, labelfound;
int mnemfound;
firsttoken = 1;
tokennum = 0;
labelfound = 0;
for ( ;; )
{
token = tokens[tokennum];
stat = gettoken ();
switch (stat)
{
case FILE_DONE:
return FILE_DONE;
case LINE_DONE:
if (!firsttoken && tokennum)
{
mnemfound = 0;
for (i=0 ; i<numparse; i++)
{
if (!strcmpi (tokens[0], parsedata[i].text))
{
if (((parsedata[i].numtokens > 0) &&
(parsedata[i].numtokens != tokennum)) ||
((parsedata[i].numtokens < 0) &&
(tokennum < -parsedata[i].numtokens)))
{
fprintf (stderr, "mismatched number of tokens\n");
for (j=0 ; j<tokennum ; j++)
fprintf (stderr, "%s\n", tokens[j]);
fprintf (stderr, "\n");
errorexit ();
}
printf ("%s", parsedata[i].emit);
(*parsedata[i].parsefunc) ();
mnemfound = 1;
break;
}
}
if (!mnemfound)
{
fprintf (stderr, "Error: unknown mnemonic\n");
for (j=0 ; j<tokennum ; j++)
fprintf (stderr, "%s\n", tokens[j]);
fprintf (stderr, "\n");
errorexit ();
}
}
if (!firsttoken)
{
if ((currentseg == DATASEG) && labelfound && !tokennum)
printf (":\n");
else
printf ("\n");
lineout++;
}
return PARSED_OKAY;
case TOKEN_AVAILABLE:
if (firsttoken)
{
if (token[strlen(token) - 1] == ':')
{
labelfound = 1;
if (currentseg == DATASEG)
{
token[strlen(token) - 1] = 0;
printf ("%s", token);
}
else if (currentseg == TEXTSEG)
{
printf ("%s", token);
}
else
{
fprintf (stderr, "Error: not in segment block\n");
errorexit ();
}
firsttoken = 0;
break;
}
}
firsttoken = 0;
if (tokennum >= MAX_TOKENS)
{
fprintf (stderr, "Error: too many tokens\n");
exit (0);
}
tokennum++;
break;
default:
fprintf (stderr, "Error: unknown tokenstat %d\n", stat);
exit (0);
}
}
}
int main ()
{
tokenstat stat;
printf (" .386P\n"
" .model FLAT\n");
linein = 1;
lineout = 3;
for ( ;; )
{
stat = parseline ();
linein++;
switch (stat)
{
case FILE_DONE:
if (currentseg == TEXTSEG)
printf ("_TEXT ENDS\n");
else if (currentseg == DATASEG)
printf ("_DATA ENDS\n");
printf (" END\n");
return 0;
case PARSED_OKAY:
break;
default:
fprintf (stderr, "Error: unknown tokenstat %d\n", stat);
return 1;
}
}
}