You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1080 lines
31 KiB
1080 lines
31 KiB
/* Small compiler - Binary code generation (the "assembler") |
|
* |
|
* Copyright (c) ITB CompuPhase, 1997-2003 |
|
* |
|
* This software is provided "as-is", without any express or implied warranty. |
|
* In no event will the authors be held liable for any damages arising from |
|
* the use of this software. |
|
* |
|
* Permission is granted to anyone to use this software for any purpose, |
|
* including commercial applications, and to alter it and redistribute it |
|
* freely, subject to the following restrictions: |
|
* |
|
* 1. The origin of this software must not be misrepresented; you must not |
|
* claim that you wrote the original software. If you use this software in |
|
* a product, an acknowledgment in the product documentation would be |
|
* appreciated but is not required. |
|
* 2. Altered source versions must be plainly marked as such, and must not be |
|
* misrepresented as being the original software. |
|
* 3. This notice may not be removed or altered from any source distribution. |
|
* |
|
* Version: $Id$ |
|
*/ |
|
|
|
|
|
#ifdef HAVE_CONFIG_H |
|
# include <config.h> |
|
#endif |
|
|
|
#include <assert.h> |
|
#include <stdio.h> |
|
#include <stdlib.h> /* for macro max() */ |
|
#include <string.h> |
|
#include <ctype.h> |
|
|
|
#include <Eina.h> |
|
|
|
#include "embryo_cc_sc.h" |
|
|
|
typedef cell(*OPCODE_PROC) (FILE * fbin, char *params, cell opcode); |
|
|
|
typedef struct |
|
{ |
|
cell opcode; |
|
char *name; |
|
int segment; /* sIN_CSEG=parse in cseg, sIN_DSEG=parse in dseg */ |
|
OPCODE_PROC func; |
|
} OPCODE; |
|
|
|
static cell codeindex; /* similar to "code_idx" */ |
|
static cell *lbltab; /* label table */ |
|
static int writeerror; |
|
static int bytes_in, bytes_out; |
|
|
|
/* apparently, strtol() does not work correctly on very large (unsigned) |
|
* hexadecimal values */ |
|
static ucell |
|
hex2long(char *s, char **n) |
|
{ |
|
unsigned long result = 0L; |
|
int negate = FALSE; |
|
int digit; |
|
|
|
/* ignore leading whitespace */ |
|
while (*s == ' ' || *s == '\t') |
|
s++; |
|
|
|
/* allow a negation sign to create the two's complement of numbers */ |
|
if (*s == '-') |
|
{ |
|
negate = TRUE; |
|
s++; |
|
} /* if */ |
|
|
|
assert((*s >= '0' && *s <= '9') || (*s >= 'a' && *s <= 'f') |
|
|| (*s >= 'a' && *s <= 'f')); |
|
for (;;) |
|
{ |
|
if (*s >= '0' && *s <= '9') |
|
digit = *s - '0'; |
|
else if (*s >= 'a' && *s <= 'f') |
|
digit = *s - 'a' + 10; |
|
else if (*s >= 'A' && *s <= 'F') |
|
digit = *s - 'A' + 10; |
|
else |
|
break; /* probably whitespace */ |
|
result = (result << 4) | digit; |
|
s++; |
|
} /* for */ |
|
if (n) |
|
*n = s; |
|
if (negate) |
|
result = (~result) + 1; /* take two's complement of the result */ |
|
return (ucell) result; |
|
} |
|
|
|
#ifdef WORDS_BIGENDIAN |
|
static short * |
|
align16(short *v) |
|
{ |
|
unsigned char *s = (unsigned char *)v; |
|
unsigned char t; |
|
|
|
/* swap two bytes */ |
|
t = s[0]; |
|
s[0] = s[1]; |
|
s[1] = t; |
|
return v; |
|
} |
|
|
|
static long * |
|
align32(long *v) |
|
{ |
|
unsigned char *s = (unsigned char *)v; |
|
unsigned char t; |
|
|
|
/* swap outer two bytes */ |
|
t = s[0]; |
|
s[0] = s[3]; |
|
s[3] = t; |
|
/* swap inner two bytes */ |
|
t = s[1]; |
|
s[1] = s[2]; |
|
s[2] = t; |
|
return v; |
|
} |
|
#if defined BIT16 |
|
#define aligncell(v) align16(v) |
|
#else |
|
#define aligncell(v) align32(v) |
|
#endif |
|
#else |
|
#define align16(v) (v) |
|
#define align32(v) (v) |
|
#define aligncell(v) (v) |
|
#endif |
|
|
|
static char * |
|
skipwhitespace(char *str) |
|
{ |
|
while (sc_isspace(*str)) |
|
str++; |
|
return str; |
|
} |
|
|
|
static char * |
|
stripcomment(char *str) |
|
{ |
|
char *ptr = strchr(str, ';'); |
|
|
|
if (ptr) |
|
{ |
|
*ptr++ = '\n'; /* terminate the line, but leave the '\n' */ |
|
*ptr = '\0'; |
|
} /* if */ |
|
return str; |
|
} |
|
|
|
static void |
|
write_encoded(FILE * fbin, ucell * c, int num) |
|
{ |
|
assert(sizeof(cell) <= 4); /* code must be adjusted for larger cells */ |
|
assert(fbin != NULL); |
|
while (num-- > 0) |
|
{ |
|
if (sc_compress) |
|
{ |
|
ucell p = (ucell) * c; |
|
unsigned char t[5]; /* a 32-bit cell is encoded in max. 5 bytes (3 bytes for a 16-bit cell) */ |
|
unsigned char code; |
|
int idx; |
|
|
|
for (idx = 0; idx < 5; idx++) |
|
{ |
|
t[idx] = (unsigned char)(p & 0x7f); /* store 7 bits */ |
|
p >>= 7; |
|
} /* for */ |
|
/* skip leading zeros */ |
|
while (idx > 1 && t[idx - 1] == 0 |
|
&& (t[idx - 2] & 0x40) == 0) |
|
idx--; |
|
/* skip leading -1s *//* ??? for BIT16, check for idx==3 && t[idx-1]==0x03 */ |
|
if (idx == 5 && t[idx - 1] == 0x0f |
|
&& (t[idx - 2] & 0x40) != 0) |
|
idx--; |
|
while (idx > 1 && t[idx - 1] == 0x7f |
|
&& (t[idx - 2] & 0x40) != 0) |
|
idx--; |
|
/* write high byte first, write continuation bits */ |
|
assert(idx > 0); |
|
while (idx-- > 0) |
|
{ |
|
code = |
|
(unsigned char)((idx == 0) ? t[idx] |
|
: (t[idx] | 0x80)); |
|
writeerror |= !sc_writebin(fbin, &code, 1); |
|
bytes_out++; |
|
} /* while */ |
|
bytes_in += sizeof *c; |
|
assert(AMX_EXPANDMARGIN > 2); |
|
if (bytes_out - bytes_in >= AMX_EXPANDMARGIN - 2) |
|
error(106); /* compression buffer overflow */ |
|
} |
|
else |
|
{ |
|
assert((sc_lengthbin(fbin) % sizeof(cell)) == 0); |
|
writeerror |= !sc_writebin(fbin, aligncell(c), sizeof *c); |
|
} /* if */ |
|
c++; |
|
} /* while */ |
|
} |
|
|
|
#if defined __BORLANDC__ || defined __WATCOMC__ |
|
#pragma argsused |
|
#endif |
|
|
|
static cell |
|
noop(FILE * fbin EINA_UNUSED, char *params EINA_UNUSED, cell opcode EINA_UNUSED) |
|
{ |
|
return 0; |
|
} |
|
|
|
#if defined __BORLANDC__ || defined __WATCOMC__ |
|
#pragma argsused |
|
#endif |
|
|
|
static cell |
|
parm0(FILE * fbin, char *params EINA_UNUSED, cell opcode) |
|
{ |
|
if (fbin) |
|
write_encoded(fbin, (ucell *) & opcode, 1); |
|
return opcodes(1); |
|
} |
|
|
|
static cell |
|
parm1(FILE * fbin, char *params, cell opcode) |
|
{ |
|
ucell p = hex2long(params, NULL); |
|
|
|
if (fbin) |
|
{ |
|
write_encoded(fbin, (ucell *) & opcode, 1); |
|
write_encoded(fbin, &p, 1); |
|
} /* if */ |
|
return opcodes(1) + opargs(1); |
|
} |
|
|
|
static cell |
|
parm2(FILE * fbin, char *params, cell opcode) |
|
{ |
|
ucell p[2]; |
|
|
|
p[0] = hex2long(params, ¶ms); |
|
p[1] = hex2long(params, NULL); |
|
if (fbin) |
|
{ |
|
write_encoded(fbin, (ucell *) & opcode, 1); |
|
write_encoded(fbin, p, 2); |
|
} /* if */ |
|
return opcodes(1) + opargs(2); |
|
} |
|
|
|
#if defined __BORLANDC__ || defined __WATCOMC__ |
|
#pragma argsused |
|
#endif |
|
|
|
static cell |
|
do_dump(FILE * fbin, char *params, cell opcode EINA_UNUSED) |
|
{ |
|
ucell p; |
|
int num = 0; |
|
|
|
while (*params != '\0') |
|
{ |
|
p = hex2long(params, ¶ms); |
|
if (fbin) |
|
write_encoded(fbin, &p, 1); |
|
num++; |
|
while (sc_isspace(*params)) |
|
params++; |
|
} /* while */ |
|
return num * sizeof(cell); |
|
} |
|
|
|
static cell |
|
do_call(FILE * fbin, char *params, cell opcode) |
|
{ |
|
char name[sNAMEMAX + 1]; |
|
int i; |
|
symbol *sym; |
|
ucell p; |
|
|
|
for (i = 0; !sc_isspace(*params); i++, params++) |
|
{ |
|
assert(*params != '\0'); |
|
assert(i < sNAMEMAX); |
|
name[i] = *params; |
|
} /* for */ |
|
name[i] = '\0'; |
|
|
|
/* look up the function address; note that the correct file number must |
|
* already have been set (in order for static globals to be found). |
|
*/ |
|
sym = findglb(name); |
|
assert(sym != NULL); |
|
assert(sym->ident == iFUNCTN || sym->ident == iREFFUNC); |
|
assert(sym->vclass == sGLOBAL); |
|
|
|
p = sym->addr; |
|
if (fbin) |
|
{ |
|
write_encoded(fbin, (ucell *) & opcode, 1); |
|
write_encoded(fbin, &p, 1); |
|
} /* if */ |
|
return opcodes(1) + opargs(1); |
|
} |
|
|
|
static cell |
|
do_jump(FILE * fbin, char *params, cell opcode) |
|
{ |
|
int i; |
|
ucell p; |
|
|
|
i = (int)hex2long(params, NULL); |
|
assert(i >= 0 && i < labnum); |
|
|
|
if (fbin) |
|
{ |
|
assert(lbltab != NULL); |
|
p = lbltab[i]; |
|
write_encoded(fbin, (ucell *) & opcode, 1); |
|
write_encoded(fbin, &p, 1); |
|
} /* if */ |
|
return opcodes(1) + opargs(1); |
|
} |
|
|
|
static cell |
|
do_file(FILE * fbin, char *params, cell opcode) |
|
{ |
|
ucell p, clen; |
|
int len; |
|
|
|
p = hex2long(params, ¶ms); |
|
|
|
/* remove leading and trailing white space from the filename */ |
|
while (sc_isspace(*params)) |
|
params++; |
|
len = strlen(params); |
|
while (len > 0 && sc_isspace(params[len - 1])) |
|
len--; |
|
params[len++] = '\0'; /* zero-terminate */ |
|
while (len % sizeof(cell) != 0) |
|
params[len++] = '\0'; /* pad with zeros up to full cell */ |
|
assert(len > 0 && len < 256); |
|
clen = len + sizeof(cell); /* add size of file ordinal */ |
|
|
|
if (fbin) |
|
{ |
|
write_encoded(fbin, (ucell *) & opcode, 1); |
|
write_encoded(fbin, &clen, 1); |
|
write_encoded(fbin, &p, 1); |
|
write_encoded(fbin, (ucell *) params, len / sizeof(cell)); |
|
} /* if */ |
|
return opcodes(1) + opargs(1) + clen; /* other argument is in clen */ |
|
} |
|
|
|
static cell |
|
do_symbol(FILE * fbin, char *params, cell opcode) |
|
{ |
|
char *endptr; |
|
ucell offset, clen, flags; |
|
int len; |
|
unsigned char mclass, type; |
|
|
|
for (endptr = params; !sc_isspace(*endptr) && endptr != '\0'; endptr++) |
|
/* nothing */ ; |
|
assert(*endptr == ' '); |
|
|
|
len = (int)(endptr - params); |
|
assert(len > 0 && len < sNAMEMAX); |
|
/* first get the other parameters from the line */ |
|
offset = hex2long(endptr, &endptr); |
|
mclass = (unsigned char)hex2long(endptr, &endptr); |
|
type = (unsigned char)hex2long(endptr, NULL); |
|
flags = type + 256 * mclass; |
|
/* now finish up the name (overwriting the input line) */ |
|
params[len++] = '\0'; /* zero-terminate */ |
|
while (len % sizeof(cell) != 0) |
|
params[len++] = '\0'; /* pad with zeros up to full cell */ |
|
clen = len + 2 * sizeof(cell); /* add size of symbol address and flags */ |
|
|
|
if (fbin) |
|
{ |
|
write_encoded(fbin, (ucell *) & opcode, 1); |
|
write_encoded(fbin, &clen, 1); |
|
write_encoded(fbin, &offset, 1); |
|
write_encoded(fbin, &flags, 1); |
|
write_encoded(fbin, (ucell *) params, len / sizeof(cell)); |
|
} /* if */ |
|
|
|
#if !defined NDEBUG |
|
/* function should start right after the symbolic information */ |
|
if (!fbin && mclass == 0 && type == iFUNCTN) |
|
assert(offset == codeindex + opcodes(1) + opargs(1) + clen); |
|
#endif |
|
|
|
return opcodes(1) + opargs(1) + clen; /* other 2 arguments are in clen */ |
|
} |
|
|
|
static cell |
|
do_switch(FILE * fbin, char *params, cell opcode) |
|
{ |
|
int i; |
|
ucell p; |
|
|
|
i = (int)hex2long(params, NULL); |
|
assert(i >= 0 && i < labnum); |
|
|
|
if (fbin) |
|
{ |
|
assert(lbltab != NULL); |
|
p = lbltab[i]; |
|
write_encoded(fbin, (ucell *) & opcode, 1); |
|
write_encoded(fbin, &p, 1); |
|
} /* if */ |
|
return opcodes(1) + opargs(1); |
|
} |
|
|
|
#if defined __BORLANDC__ || defined __WATCOMC__ |
|
#pragma argsused |
|
#endif |
|
|
|
static cell |
|
do_case(FILE * fbin, char *params, cell opcode EINA_UNUSED) |
|
{ |
|
int i; |
|
ucell p, v; |
|
|
|
v = hex2long(params, ¶ms); |
|
i = (int)hex2long(params, NULL); |
|
assert(i >= 0 && i < labnum); |
|
|
|
if (fbin) |
|
{ |
|
assert(lbltab != NULL); |
|
p = lbltab[i]; |
|
write_encoded(fbin, &v, 1); |
|
write_encoded(fbin, &p, 1); |
|
} /* if */ |
|
return opcodes(0) + opargs(2); |
|
} |
|
|
|
#if defined __BORLANDC__ || defined __WATCOMC__ |
|
#pragma argsused |
|
#endif |
|
|
|
static cell |
|
curfile(FILE * fbin EINA_UNUSED, char *params, cell opcode EINA_UNUSED) |
|
{ |
|
fcurrent = (int)hex2long(params, NULL); |
|
return 0; |
|
} |
|
|
|
static OPCODE opcodelist[] = { |
|
/* node for "invalid instruction" */ |
|
{0, NULL, 0, noop}, |
|
/* opcodes in sorted order */ |
|
{78, "add", sIN_CSEG, parm0}, |
|
{87, "add.c", sIN_CSEG, parm1}, |
|
{14, "addr.alt", sIN_CSEG, parm1}, |
|
{13, "addr.pri", sIN_CSEG, parm1}, |
|
{30, "align.alt", sIN_CSEG, parm1}, |
|
{29, "align.pri", sIN_CSEG, parm1}, |
|
{81, "and", sIN_CSEG, parm0}, |
|
{121, "bounds", sIN_CSEG, parm1}, |
|
{49, "call", sIN_CSEG, do_call}, |
|
{50, "call.pri", sIN_CSEG, parm0}, |
|
{0, "case", sIN_CSEG, do_case}, |
|
{130, "casetbl", sIN_CSEG, parm0}, /* version 1 */ |
|
{118, "cmps", sIN_CSEG, parm1}, |
|
{0, "code", 0, noop}, |
|
{12, "const.alt", sIN_CSEG, parm1}, |
|
{11, "const.pri", sIN_CSEG, parm1}, |
|
{0, "curfile", sIN_CSEG, curfile}, |
|
{0, "data", 0, noop}, |
|
{114, "dec", sIN_CSEG, parm1}, |
|
{113, "dec.alt", sIN_CSEG, parm0}, |
|
{116, "dec.i", sIN_CSEG, parm0}, |
|
{112, "dec.pri", sIN_CSEG, parm0}, |
|
{115, "dec.s", sIN_CSEG, parm1}, |
|
{0, "dump", sIN_DSEG, do_dump}, |
|
{95, "eq", sIN_CSEG, parm0}, |
|
{106, "eq.c.alt", sIN_CSEG, parm1}, |
|
{105, "eq.c.pri", sIN_CSEG, parm1}, |
|
{124, "file", sIN_CSEG, do_file}, |
|
{119, "fill", sIN_CSEG, parm1}, |
|
{100, "geq", sIN_CSEG, parm0}, |
|
{99, "grtr", sIN_CSEG, parm0}, |
|
{120, "halt", sIN_CSEG, parm1}, |
|
{45, "heap", sIN_CSEG, parm1}, |
|
{27, "idxaddr", sIN_CSEG, parm0}, |
|
{28, "idxaddr.b", sIN_CSEG, parm1}, |
|
{109, "inc", sIN_CSEG, parm1}, |
|
{108, "inc.alt", sIN_CSEG, parm0}, |
|
{111, "inc.i", sIN_CSEG, parm0}, |
|
{107, "inc.pri", sIN_CSEG, parm0}, |
|
{110, "inc.s", sIN_CSEG, parm1}, |
|
{86, "invert", sIN_CSEG, parm0}, |
|
{55, "jeq", sIN_CSEG, do_jump}, |
|
{60, "jgeq", sIN_CSEG, do_jump}, |
|
{59, "jgrtr", sIN_CSEG, do_jump}, |
|
{58, "jleq", sIN_CSEG, do_jump}, |
|
{57, "jless", sIN_CSEG, do_jump}, |
|
{56, "jneq", sIN_CSEG, do_jump}, |
|
{54, "jnz", sIN_CSEG, do_jump}, |
|
{52, "jrel", sIN_CSEG, parm1}, /* always a number */ |
|
{64, "jsgeq", sIN_CSEG, do_jump}, |
|
{63, "jsgrtr", sIN_CSEG, do_jump}, |
|
{62, "jsleq", sIN_CSEG, do_jump}, |
|
{61, "jsless", sIN_CSEG, do_jump}, |
|
{51, "jump", sIN_CSEG, do_jump}, |
|
{128, "jump.pri", sIN_CSEG, parm0}, /* version 1 */ |
|
{53, "jzer", sIN_CSEG, do_jump}, |
|
{31, "lctrl", sIN_CSEG, parm1}, |
|
{98, "leq", sIN_CSEG, parm0}, |
|
{97, "less", sIN_CSEG, parm0}, |
|
{25, "lidx", sIN_CSEG, parm0}, |
|
{26, "lidx.b", sIN_CSEG, parm1}, |
|
{125, "line", sIN_CSEG, parm2}, |
|
{2, "load.alt", sIN_CSEG, parm1}, |
|
{9, "load.i", sIN_CSEG, parm0}, |
|
{1, "load.pri", sIN_CSEG, parm1}, |
|
{4, "load.s.alt", sIN_CSEG, parm1}, |
|
{3, "load.s.pri", sIN_CSEG, parm1}, |
|
{10, "lodb.i", sIN_CSEG, parm1}, |
|
{6, "lref.alt", sIN_CSEG, parm1}, |
|
{5, "lref.pri", sIN_CSEG, parm1}, |
|
{8, "lref.s.alt", sIN_CSEG, parm1}, |
|
{7, "lref.s.pri", sIN_CSEG, parm1}, |
|
{34, "move.alt", sIN_CSEG, parm0}, |
|
{33, "move.pri", sIN_CSEG, parm0}, |
|
{117, "movs", sIN_CSEG, parm1}, |
|
{85, "neg", sIN_CSEG, parm0}, |
|
{96, "neq", sIN_CSEG, parm0}, |
|
{134, "nop", sIN_CSEG, parm0}, /* version 6 */ |
|
{84, "not", sIN_CSEG, parm0}, |
|
{82, "or", sIN_CSEG, parm0}, |
|
{43, "pop.alt", sIN_CSEG, parm0}, |
|
{42, "pop.pri", sIN_CSEG, parm0}, |
|
{46, "proc", sIN_CSEG, parm0}, |
|
{40, "push", sIN_CSEG, parm1}, |
|
{37, "push.alt", sIN_CSEG, parm0}, |
|
{39, "push.c", sIN_CSEG, parm1}, |
|
{36, "push.pri", sIN_CSEG, parm0}, |
|
{38, "push.r", sIN_CSEG, parm1}, |
|
{41, "push.s", sIN_CSEG, parm1}, |
|
{133, "pushaddr", sIN_CSEG, parm1}, /* version 4 */ |
|
{47, "ret", sIN_CSEG, parm0}, |
|
{48, "retn", sIN_CSEG, parm0}, |
|
{32, "sctrl", sIN_CSEG, parm1}, |
|
{73, "sdiv", sIN_CSEG, parm0}, |
|
{74, "sdiv.alt", sIN_CSEG, parm0}, |
|
{104, "sgeq", sIN_CSEG, parm0}, |
|
{103, "sgrtr", sIN_CSEG, parm0}, |
|
{65, "shl", sIN_CSEG, parm0}, |
|
{69, "shl.c.alt", sIN_CSEG, parm1}, |
|
{68, "shl.c.pri", sIN_CSEG, parm1}, |
|
{66, "shr", sIN_CSEG, parm0}, |
|
{71, "shr.c.alt", sIN_CSEG, parm1}, |
|
{70, "shr.c.pri", sIN_CSEG, parm1}, |
|
{94, "sign.alt", sIN_CSEG, parm0}, |
|
{93, "sign.pri", sIN_CSEG, parm0}, |
|
{102, "sleq", sIN_CSEG, parm0}, |
|
{101, "sless", sIN_CSEG, parm0}, |
|
{72, "smul", sIN_CSEG, parm0}, |
|
{88, "smul.c", sIN_CSEG, parm1}, |
|
{127, "srange", sIN_CSEG, parm2}, /* version 1 */ |
|
{20, "sref.alt", sIN_CSEG, parm1}, |
|
{19, "sref.pri", sIN_CSEG, parm1}, |
|
{22, "sref.s.alt", sIN_CSEG, parm1}, |
|
{21, "sref.s.pri", sIN_CSEG, parm1}, |
|
{67, "sshr", sIN_CSEG, parm0}, |
|
{44, "stack", sIN_CSEG, parm1}, |
|
{0, "stksize", 0, noop}, |
|
{16, "stor.alt", sIN_CSEG, parm1}, |
|
{23, "stor.i", sIN_CSEG, parm0}, |
|
{15, "stor.pri", sIN_CSEG, parm1}, |
|
{18, "stor.s.alt", sIN_CSEG, parm1}, |
|
{17, "stor.s.pri", sIN_CSEG, parm1}, |
|
{24, "strb.i", sIN_CSEG, parm1}, |
|
{79, "sub", sIN_CSEG, parm0}, |
|
{80, "sub.alt", sIN_CSEG, parm0}, |
|
{132, "swap.alt", sIN_CSEG, parm0}, /* version 4 */ |
|
{131, "swap.pri", sIN_CSEG, parm0}, /* version 4 */ |
|
{129, "switch", sIN_CSEG, do_switch}, /* version 1 */ |
|
{126, "symbol", sIN_CSEG, do_symbol}, |
|
{136, "symtag", sIN_CSEG, parm1}, /* version 7 */ |
|
{123, "sysreq.c", sIN_CSEG, parm1}, |
|
{135, "sysreq.d", sIN_CSEG, parm1}, /* version 7, not generated directly */ |
|
{122, "sysreq.pri", sIN_CSEG, parm0}, |
|
{76, "udiv", sIN_CSEG, parm0}, |
|
{77, "udiv.alt", sIN_CSEG, parm0}, |
|
{75, "umul", sIN_CSEG, parm0}, |
|
{35, "xchg", sIN_CSEG, parm0}, |
|
{83, "xor", sIN_CSEG, parm0}, |
|
{91, "zero", sIN_CSEG, parm1}, |
|
{90, "zero.alt", sIN_CSEG, parm0}, |
|
{89, "zero.pri", sIN_CSEG, parm0}, |
|
{92, "zero.s", sIN_CSEG, parm1}, |
|
}; |
|
|
|
#define MAX_INSTR_LEN 30 |
|
static int |
|
findopcode(char *instr, int maxlen) |
|
{ |
|
int low, high, mid, cmp; |
|
char str[MAX_INSTR_LEN]; |
|
|
|
if (maxlen >= MAX_INSTR_LEN) |
|
return 0; |
|
strncpy(str, instr, maxlen); |
|
str[maxlen] = '\0'; /* make sure the string is zero terminated */ |
|
/* look up the instruction with a binary search |
|
* the assembler is case insensitive to instructions (but case sensitive |
|
* to symbols) |
|
*/ |
|
low = 1; /* entry 0 is reserved (for "not found") */ |
|
high = (sizeof opcodelist / sizeof opcodelist[0]) - 1; |
|
while (low < high) |
|
{ |
|
mid = (low + high) / 2; |
|
assert(opcodelist[mid].name != NULL); |
|
cmp = strcasecmp(str, opcodelist[mid].name); |
|
if (cmp > 0) |
|
low = mid + 1; |
|
else |
|
high = mid; |
|
} /* while */ |
|
|
|
assert(low == high); |
|
if (strcasecmp(str, opcodelist[low].name) == 0) |
|
return low; /* found */ |
|
return 0; /* not found, return special index */ |
|
} |
|
|
|
void |
|
assemble(FILE * fout, FILE * fin) |
|
{ |
|
typedef struct tagFUNCSTUB |
|
{ |
|
unsigned int address, nameofs; |
|
} FUNCSTUB; |
|
AMX_HEADER hdr; |
|
FUNCSTUB func; |
|
int numpublics, numnatives, numlibraries, numpubvars, |
|
numtags, padding; |
|
long nametablesize, nameofs; |
|
char line[256], *instr, *params; |
|
int i, pass; |
|
short count; |
|
symbol *sym, **nativelist; |
|
constvalue *constptr; |
|
cell mainaddr; |
|
int nametable, tags, libraries, publics, natives, pubvars; |
|
int cod; |
|
|
|
#if !defined NDEBUG |
|
/* verify that the opcode list is sorted (skip entry 1; it is reserved |
|
* for a non-existent opcode) |
|
*/ |
|
assert(opcodelist[1].name != NULL); |
|
for (i = 2; i < (int)(sizeof(opcodelist) / sizeof(opcodelist[0])); i++) |
|
{ |
|
assert(opcodelist[i].name != NULL); |
|
assert(strcasecmp(opcodelist[i].name, opcodelist[i - 1].name) > 0); |
|
} /* for */ |
|
#endif |
|
|
|
writeerror = FALSE; |
|
nametablesize = sizeof(short); |
|
numpublics = 0; |
|
numnatives = 0; |
|
numpubvars = 0; |
|
mainaddr = -1; |
|
/* count number of public and native functions and public variables */ |
|
for (sym = glbtab.next; sym; sym = sym->next) |
|
{ |
|
char alias[sNAMEMAX + 1] = ""; |
|
int match = 0; |
|
|
|
if (sym->ident == iFUNCTN) |
|
{ |
|
assert(strlen(sym->name) <= sNAMEMAX); |
|
if ((sym->usage & uNATIVE) != 0 && (sym->usage & uREAD) != 0 |
|
&& sym->addr >= 0) |
|
{ |
|
match = ++numnatives; |
|
if (!lookup_alias(alias, sym->name)) |
|
strcpy(alias, sym->name); |
|
} /* if */ |
|
if ((sym->usage & uPUBLIC) != 0 && (sym->usage & uDEFINE) != 0) |
|
{ |
|
match = ++numpublics; |
|
strcpy(alias, sym->name); |
|
} /* if */ |
|
if (strcmp(sym->name, uMAINFUNC) == 0) |
|
{ |
|
assert(sym->vclass == sGLOBAL); |
|
mainaddr = sym->addr; |
|
} /* if */ |
|
} |
|
else if (sym->ident == iVARIABLE) |
|
{ |
|
if ((sym->usage & uPUBLIC) != 0) |
|
{ |
|
match = ++numpubvars; |
|
strcpy(alias, sym->name); |
|
} /* if */ |
|
} /* if */ |
|
if (match) |
|
{ |
|
assert(alias[0] != '\0'); |
|
nametablesize += strlen(alias) + 1; |
|
} /* if */ |
|
} /* for */ |
|
assert(numnatives == ntv_funcid); |
|
|
|
/* count number of libraries */ |
|
numlibraries = 0; |
|
for (constptr = libname_tab.next; constptr; |
|
constptr = constptr->next) |
|
{ |
|
if (constptr->value > 0) |
|
{ |
|
assert(constptr->name[0] != '\0'); |
|
numlibraries++; |
|
nametablesize += strlen(constptr->name) + 1; |
|
} /* if */ |
|
} /* for */ |
|
|
|
/* count number of public tags */ |
|
numtags = 0; |
|
for (constptr = tagname_tab.next; constptr; |
|
constptr = constptr->next) |
|
{ |
|
if ((constptr->value & PUBLICTAG) != 0) |
|
{ |
|
assert(constptr->name[0] != '\0'); |
|
numtags++; |
|
nametablesize += strlen(constptr->name) + 1; |
|
} /* if */ |
|
} /* for */ |
|
|
|
/* pad the header to sc_dataalign |
|
* => thereby the code segment is aligned |
|
* => since the code segment is padded to a sc_dataalign boundary, the data segment is aligned |
|
* => and thereby the stack top is aligned too |
|
*/ |
|
assert(sc_dataalign != 0); |
|
padding = sc_dataalign - (sizeof hdr + nametablesize) % sc_dataalign; |
|
if (padding == sc_dataalign) |
|
padding = 0; |
|
|
|
/* write the abstract machine header */ |
|
memset(&hdr, 0, sizeof hdr); |
|
hdr.magic = (unsigned short)0xF1E0; |
|
hdr.file_version = CUR_FILE_VERSION; |
|
hdr.amx_version = MIN_AMX_VERSION; |
|
hdr.flags = (short)(sc_debug & sSYMBOLIC); |
|
if (charbits == 16) |
|
hdr.flags |= AMX_FLAG_CHAR16; |
|
if (sc_compress) |
|
hdr.flags |= AMX_FLAG_COMPACT; |
|
if (sc_debug == 0) |
|
hdr.flags |= AMX_FLAG_NOCHECKS; |
|
// #ifdef WORDS_BIGENDIAN |
|
// hdr.flags|=AMX_FLAG_BIGENDIAN; |
|
// #endif |
|
hdr.defsize = sizeof(FUNCSTUB); |
|
assert((hdr.defsize % sizeof(cell)) == 0); |
|
publics = hdr.publics = sizeof hdr; /* public table starts right after the header */ |
|
natives = hdr.natives = hdr.publics + numpublics * sizeof(FUNCSTUB); |
|
libraries = hdr.libraries = hdr.natives + numnatives * sizeof(FUNCSTUB); |
|
pubvars = hdr.pubvars = hdr.libraries + numlibraries * sizeof(FUNCSTUB); |
|
tags = hdr.tags = hdr.pubvars + numpubvars * sizeof(FUNCSTUB); |
|
nametable = hdr.nametable = hdr.tags + numtags * sizeof(FUNCSTUB); |
|
cod = hdr.cod = hdr.nametable + nametablesize + padding; |
|
hdr.dat = hdr.cod + code_idx; |
|
hdr.hea = hdr.dat + glb_declared * sizeof(cell); |
|
hdr.stp = hdr.hea + sc_stksize * sizeof(cell); |
|
hdr.cip = mainaddr; |
|
hdr.size = hdr.hea; /* preset, this is incorrect in case of compressed output */ |
|
#ifdef WORDS_BIGENDIAN |
|
align32(&hdr.size); |
|
align16(&hdr.magic); |
|
align16(&hdr.flags); |
|
align16(&hdr.defsize); |
|
align32(&hdr.cod); |
|
align32(&hdr.dat); |
|
align32(&hdr.hea); |
|
align32(&hdr.stp); |
|
align32(&hdr.cip); |
|
align32(&hdr.publics); |
|
align32(&hdr.natives); |
|
align32(&hdr.libraries); |
|
align32(&hdr.pubvars); |
|
align32(&hdr.tags); |
|
align32(&hdr.nametable); |
|
#endif |
|
sc_writebin(fout, &hdr, sizeof hdr); |
|
|
|
/* dump zeros up to the rest of the header, so that we can easily "seek" */ |
|
for (nameofs = sizeof hdr; nameofs < cod; nameofs++) |
|
putc(0, fout); |
|
nameofs = nametable + sizeof(short); |
|
|
|
/* write the public functions table */ |
|
count = 0; |
|
for (sym = glbtab.next; sym; sym = sym->next) |
|
{ |
|
if (sym->ident == iFUNCTN |
|
&& (sym->usage & uPUBLIC) != 0 && (sym->usage & uDEFINE) != 0) |
|
{ |
|
assert(sym->vclass == sGLOBAL); |
|
func.address = sym->addr; |
|
func.nameofs = nameofs; |
|
#ifdef WORDS_BIGENDIAN |
|
align32(&func.address); |
|
align32(&func.nameofs); |
|
#endif |
|
fseek(fout, publics + count * sizeof(FUNCSTUB), SEEK_SET); |
|
sc_writebin(fout, &func, sizeof func); |
|
fseek(fout, nameofs, SEEK_SET); |
|
sc_writebin(fout, sym->name, strlen(sym->name) + 1); |
|
nameofs += strlen(sym->name) + 1; |
|
count++; |
|
} /* if */ |
|
} /* for */ |
|
|
|
/* write the natives table */ |
|
/* The native functions must be written in sorted order. (They are |
|
* sorted on their "id", not on their name). A nested loop to find |
|
* each successive function would be an O(n^2) operation. But we |
|
* do not really need to sort, because the native function id's |
|
* are sequential and there are no duplicates. So we first walk |
|
* through the complete symbol list and store a pointer to every |
|
* native function of interest in a temporary table, where its id |
|
* serves as the index in the table. Now we can walk the table and |
|
* have all native functions in sorted order. |
|
*/ |
|
if (numnatives > 0) |
|
{ |
|
nativelist = (symbol **) malloc(numnatives * sizeof(symbol *)); |
|
if (!nativelist) |
|
error(103); /* insufficient memory */ |
|
#if !defined NDEBUG |
|
memset(nativelist, 0, numnatives * sizeof(symbol *)); /* for NULL checking */ |
|
#endif |
|
for (sym = glbtab.next; sym; sym = sym->next) |
|
{ |
|
if (sym->ident == iFUNCTN && (sym->usage & uNATIVE) != 0 |
|
&& (sym->usage & uREAD) != 0 && sym->addr >= 0) |
|
{ |
|
assert(sym->addr < numnatives); |
|
nativelist[(int)sym->addr] = sym; |
|
} /* if */ |
|
} /* for */ |
|
count = 0; |
|
for (i = 0; i < numnatives; i++) |
|
{ |
|
char alias[sNAMEMAX + 1]; |
|
|
|
sym = nativelist[i]; |
|
assert(sym != NULL); |
|
if (!lookup_alias(alias, sym->name)) |
|
{ |
|
assert(strlen(sym->name) <= sNAMEMAX); |
|
strcpy(alias, sym->name); |
|
} /* if */ |
|
assert(sym->vclass == sGLOBAL); |
|
func.address = 0; |
|
func.nameofs = nameofs; |
|
#ifdef WORDS_BIGENDIAN |
|
align32(&func.address); |
|
align32(&func.nameofs); |
|
#endif |
|
fseek(fout, natives + count * sizeof(FUNCSTUB), SEEK_SET); |
|
sc_writebin(fout, &func, sizeof func); |
|
fseek(fout, nameofs, SEEK_SET); |
|
sc_writebin(fout, alias, strlen(alias) + 1); |
|
nameofs += strlen(alias) + 1; |
|
count++; |
|
} /* for */ |
|
free(nativelist); |
|
} /* if */ |
|
|
|
/* write the libraries table */ |
|
count = 0; |
|
for (constptr = libname_tab.next; constptr; |
|
constptr = constptr->next) |
|
{ |
|
if (constptr->value > 0) |
|
{ |
|
assert(constptr->name[0] != '\0'); |
|
func.address = 0; |
|
func.nameofs = nameofs; |
|
#ifdef WORDS_BIGENDIAN |
|
align32(&func.address); |
|
align32(&func.nameofs); |
|
#endif |
|
fseek(fout, libraries + count * sizeof(FUNCSTUB), SEEK_SET); |
|
sc_writebin(fout, &func, sizeof func); |
|
fseek(fout, nameofs, SEEK_SET); |
|
sc_writebin(fout, constptr->name, strlen(constptr->name) + 1); |
|
nameofs += strlen(constptr->name) + 1; |
|
count++; |
|
} /* if */ |
|
} /* for */ |
|
|
|
/* write the public variables table */ |
|
count = 0; |
|
for (sym = glbtab.next; sym; sym = sym->next) |
|
{ |
|
if (sym->ident == iVARIABLE && (sym->usage & uPUBLIC) != 0) |
|
{ |
|
assert((sym->usage & uDEFINE) != 0); |
|
assert(sym->vclass == sGLOBAL); |
|
func.address = sym->addr; |
|
func.nameofs = nameofs; |
|
#ifdef WORDS_BIGENDIAN |
|
align32(&func.address); |
|
align32(&func.nameofs); |
|
#endif |
|
fseek(fout, pubvars + count * sizeof(FUNCSTUB), SEEK_SET); |
|
sc_writebin(fout, &func, sizeof func); |
|
fseek(fout, nameofs, SEEK_SET); |
|
sc_writebin(fout, sym->name, strlen(sym->name) + 1); |
|
nameofs += strlen(sym->name) + 1; |
|
count++; |
|
} /* if */ |
|
} /* for */ |
|
|
|
/* write the public tagnames table */ |
|
count = 0; |
|
for (constptr = tagname_tab.next; constptr; |
|
constptr = constptr->next) |
|
{ |
|
if ((constptr->value & PUBLICTAG) != 0) |
|
{ |
|
assert(constptr->name[0] != '\0'); |
|
func.address = constptr->value & TAGMASK; |
|
func.nameofs = nameofs; |
|
#ifdef WORDS_BIGENDIAN |
|
align32(&func.address); |
|
align32(&func.nameofs); |
|
#endif |
|
fseek(fout, tags + count * sizeof(FUNCSTUB), SEEK_SET); |
|
sc_writebin(fout, &func, sizeof func); |
|
fseek(fout, nameofs, SEEK_SET); |
|
sc_writebin(fout, constptr->name, strlen(constptr->name) + 1); |
|
nameofs += strlen(constptr->name) + 1; |
|
count++; |
|
} /* if */ |
|
} /* for */ |
|
|
|
/* write the "maximum name length" field in the name table */ |
|
assert(nameofs == nametable + nametablesize); |
|
fseek(fout, nametable, SEEK_SET); |
|
count = sNAMEMAX; |
|
#ifdef WORDS_BIGENDIAN |
|
align16(&count); |
|
#endif |
|
sc_writebin(fout, &count, sizeof count); |
|
fseek(fout, cod, SEEK_SET); |
|
|
|
/* First pass: relocate all labels */ |
|
/* This pass is necessary because the code addresses of labels is only known |
|
* after the peephole optimization flag. Labels can occur inside expressions |
|
* (e.g. the conditional operator), which are optimized. |
|
*/ |
|
lbltab = NULL; |
|
if (labnum > 0) |
|
{ |
|
/* only very short programs have zero labels; no first pass is needed |
|
* if there are no labels */ |
|
lbltab = (cell *) malloc(labnum * sizeof(cell)); |
|
if (!lbltab) |
|
error(103); /* insufficient memory */ |
|
codeindex = 0; |
|
sc_resetasm(fin); |
|
while (sc_readasm(fin, line, sizeof line)) |
|
{ |
|
stripcomment(line); |
|
instr = skipwhitespace(line); |
|
/* ignore empty lines */ |
|
if (*instr == '\0') |
|
continue; |
|
if (tolower(*instr) == 'l' && *(instr + 1) == '.') |
|
{ |
|
int lindex = (int)hex2long(instr + 2, NULL); |
|
|
|
assert(lindex < labnum); |
|
lbltab[lindex] = codeindex; |
|
} |
|
else |
|
{ |
|
/* get to the end of the instruction (make use of the '\n' that fgets() |
|
* added at the end of the line; this way we will *always* drop on a |
|
* whitespace character) */ |
|
for (params = instr; *params != '\0' && !sc_isspace(*params); |
|
params++) |
|
/* nothing */ ; |
|
assert(params > instr); |
|
i = findopcode(instr, (int)(params - instr)); |
|
if (!opcodelist[i].name) |
|
{ |
|
*params = '\0'; |
|
error(104, instr); /* invalid assembler instruction */ |
|
} /* if */ |
|
if (opcodelist[i].segment == sIN_CSEG) |
|
codeindex += |
|
opcodelist[i].func(NULL, skipwhitespace(params), |
|
opcodelist[i].opcode); |
|
} /* if */ |
|
} /* while */ |
|
} /* if */ |
|
|
|
/* Second pass (actually 2 more passes, one for all code and one for all data) */ |
|
bytes_in = 0; |
|
bytes_out = 0; |
|
for (pass = sIN_CSEG; pass <= sIN_DSEG; pass++) |
|
{ |
|
sc_resetasm(fin); |
|
while (sc_readasm(fin, line, sizeof line)) |
|
{ |
|
stripcomment(line); |
|
instr = skipwhitespace(line); |
|
/* ignore empty lines and labels (labels have a special syntax, so these |
|
* must be parsed separately) */ |
|
if (*instr == '\0' || (tolower(*instr) == 'l' |
|
&& *(instr + 1) == '.')) |
|
continue; |
|
/* get to the end of the instruction (make use of the '\n' that fgets() |
|
* added at the end of the line; this way we will *always* drop on a |
|
* whitespace character) */ |
|
for (params = instr; *params != '\0' && !sc_isspace(*params); |
|
params++) |
|
/* nothing */ ; |
|
assert(params > instr); |
|
i = findopcode(instr, (int)(params - instr)); |
|
assert(opcodelist[i].name != NULL); |
|
if (opcodelist[i].segment == pass) |
|
opcodelist[i].func(fout, skipwhitespace(params), |
|
opcodelist[i].opcode); |
|
} /* while */ |
|
} /* for */ |
|
if (bytes_out - bytes_in > 0) |
|
error(106); /* compression buffer overflow */ |
|
|
|
if (lbltab) |
|
{ |
|
free(lbltab); |
|
#if !defined NDEBUG |
|
lbltab = NULL; |
|
#endif |
|
} /* if */ |
|
|
|
if (writeerror) |
|
error(101, "disk full"); |
|
|
|
/* adjust the header */ |
|
if (sc_compress) |
|
{ |
|
hdr.size = sc_lengthbin(fout); |
|
#ifdef WORDS_BIGENDIAN |
|
align32(&hdr.size); |
|
#endif |
|
sc_resetbin(fout); /* "size" is the very first field */ |
|
sc_writebin(fout, &hdr.size, sizeof hdr.size); |
|
} /* if */ |
|
}
|
|
|