/* 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 #endif #include #include #include /* for macro max() */ #include #include #include #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 */ }