efl/src/bin/embryo/embryo_cc_sc6.c

1081 lines
31 KiB
C

/* 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, &params);
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, &params);
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, &params);
/* 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, &params);
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, defsize;
#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
defsize = 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 */
}