/* Pawn compiler - Binary code generation (the "assembler") * * Copyright (c) ITB CompuPhase, 1997-2005 * * 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. */ #include <assert.h> #include <stdio.h> #include <stdlib.h> /* for macro max() */ #include <string.h> #include <ctype.h> #if defined FORTIFY #include "fortify.h" #endif #include "sc.h" #include "amxdbg.h" #if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ || defined __APPLE__ #include <sclinux.h> #endif static void append_dbginfo(FILE *fout); 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; static jmp_buf compact_err; /* apparently, strtol() does not work correctly on very large (unsigned) * hexadecimal values */ static ucell hex2long(char *s,char **n) { ucell 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!=NULL) *n=s; if (negate) result=(~result)+1; /* take two's complement of the result */ return (ucell)result; } static ucell getparam(char *s,char **n) { ucell result=0; for ( ;; ) { result+=hex2long(s,&s); if (*s!='+') break; s++; } /* for */ if (n!=NULL) *n=s; return result; } #if BYTE_ORDER==BIG_ENDIAN static uint16_t *align16(uint16_t *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 uint32_t *align32(uint32_t *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 PAWN_CELL_SIZE>=64 static uint64_t *align64(uint64_t *v) { unsigned char *s = (unsigned char *)v; unsigned char t; t=s[0]; s[0]=s[7]; s[7]=t; t=s[1]; s[1]=s[6]; s[6]=t; t=s[2]; s[2]=s[5]; s[5]=t; t=s[3]; s[3]=s[4]; s[4]=t; return v; } #endif #if PAWN_CELL_SIZE==16 #define aligncell(v) align16(v) #elif PAWN_CELL_SIZE==32 #define aligncell(v) align32(v) #elif PAWN_CELL_SIZE==64 #define aligncell(v) align64(v) #endif #else #define align16(v) (v) #define align32(v) (v) #define aligncell(v) (v) #endif static char *skipwhitespace(char *str) { while (isspace(*str)) str++; return str; } static char *stripcomment(char *str) { char *ptr=strchr(str,';'); if (ptr!=NULL) { *ptr++='\n'; /* terminate the line, but leave the '\n' */ *ptr='\0'; } /* if */ return str; } static void write_encoded(FILE *fbin,ucell *c,int num) { #if PAWN_CELL_SIZE == 16 #define ENC_MAX 3 /* a 16-bit cell is encoded in max. 3 bytes */ #define ENC_MASK 0x03 /* after 2x7 bits, 2 bits remain to make 16 bits */ #elif PAWN_CELL_SIZE == 32 #define ENC_MAX 5 /* a 32-bit cell is encoded in max. 5 bytes */ #define ENC_MASK 0x0f /* after 4x7 bits, 4 bits remain to make 32 bits */ #elif PAWN_CELL_SIZE == 64 #define ENC_MAX 10 /* a 32-bit cell is encoded in max. 10 bytes */ #define ENC_MASK 0x01 /* after 9x7 bits, 1 bit remains to make 64 bits */ #endif assert(fbin!=NULL); while (num-->0) { if (sc_compress) { ucell p=(ucell)*c; unsigned char t[ENC_MAX]; unsigned char code; int index; for (index=0; index<ENC_MAX; index++) { t[index]=(unsigned char)(p & 0x7f); /* store 7 bits */ p>>=7; } /* for */ /* skip leading zeros */ while (index>1 && t[index-1]==0 && (t[index-2] & 0x40)==0) index--; /* skip leading -1s */ if (index==ENC_MAX && t[index-1]==ENC_MASK && (t[index-2] & 0x40)!=0) index--; while (index>1 && t[index-1]==0x7f && (t[index-2] & 0x40)!=0) index--; /* write high byte first, write continuation bits */ assert(index>0); while (index-->0) { code=(unsigned char)((index==0) ? t[index] : (t[index]|0x80)); writeerror |= !pc_writebin(fbin,&code,1); bytes_out++; } /* while */ bytes_in+=sizeof *c; assert(AMX_COMPACTMARGIN>2); if (bytes_out-bytes_in>=AMX_COMPACTMARGIN-2) longjmp(compact_err,1); } else { assert((pc_lengthbin(fbin) % sizeof(cell)) == 0); writeerror |= !pc_writebin(fbin,aligncell(c),sizeof *c); } /* if */ c++; } /* while */ } #if defined __BORLANDC__ || defined __WATCOMC__ #pragma argsused #endif static cell noop(FILE *fbin,char *params,cell opcode) { return 0; } #if defined __BORLANDC__ || defined __WATCOMC__ #pragma argsused #endif static cell parm0(FILE *fbin,char *params,cell opcode) { if (fbin!=NULL) write_encoded(fbin,(ucell*)&opcode,1); return opcodes(1); } static cell parm1(FILE *fbin,char *params,cell opcode) { ucell p=getparam(params,NULL); if (fbin!=NULL) { 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_dump(FILE *fbin,char *params,cell opcode) { ucell p; int num = 0; while (*params!='\0') { p=getparam(params,¶ms); if (fbin!=NULL) write_encoded(fbin,&p,1); num++; while (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; !isspace(*params); i++,params++) { assert(*params!='\0'); assert(i<sNAMEMAX); name[i]=*params; } /* for */ name[i]='\0'; if (name[0]=='l' && name[1]=='.') { /* this is a label, not a function symbol */ i=(int)hex2long(name+2,NULL); assert(i>=0 && i<sc_labnum); if (fbin!=NULL) { assert(lbltab!=NULL); p=lbltab[i]; } /* if */ } else { /* 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 */ if (fbin!=NULL) { 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<sc_labnum); if (fbin!=NULL) { 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_switch(FILE *fbin,char *params,cell opcode) { int i; ucell p; i=(int)hex2long(params,NULL); assert(i>=0 && i<sc_labnum); if (fbin!=NULL) { 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) { int i; ucell p,v; v=hex2long(params,¶ms); i=(int)hex2long(params,NULL); assert(i>=0 && i<sc_labnum); if (fbin!=NULL) { assert(lbltab!=NULL); p=lbltab[i]; write_encoded(fbin,&v,1); write_encoded(fbin,&p,1); } /* if */ return opcodes(0)+opargs(2); } 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 }, {137, "break", sIN_CSEG, parm0 }, /* version 8 */ { 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, "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=stricmp(str,opcodelist[mid].name); if (cmp>0) low=mid+1; else high=mid; } /* while */ assert(low==high); if (stricmp(str,opcodelist[low].name)==0) return low; /* found */ return 0; /* not found, return special index */ } SC_FUNC int assemble(FILE *fout,FILE *fin) { AMX_HEADER hdr; AMX_FUNCSTUBNT func; int numpublics,numnatives,numlibraries,numpubvars,numtags,padding; long nametablesize,nameofs; #if PAWN_CELL_SIZE > 32 char line[512]; #else char line[256]; #endif char *instr,*params; int i,pass,size; int16_t count; symbol *sym, **nativelist; constvalue *constptr; cell mainaddr; fcurrent = -1; /* if compression failed, restart the assembly with compaction switched off */ if (setjmp(compact_err)!=0) { assert(sc_compress); /* cannot arrive here if compact encoding was disabled */ sc_compress=FALSE; pc_resetbin(fout,0); error(232); /* disabled compact encoding */ } /* if */ #if !defined NDEBUG /* verify that the opcode list is sorted (skip entry 1; it is reserved * for a non-existant opcode) */ assert(opcodelist[1].name!=NULL); for (i=2; i<(sizeof opcodelist / sizeof opcodelist[0]); i++) { assert(opcodelist[i].name!=NULL); assert(stricmp(opcodelist[i].name,opcodelist[i-1].name)>0); } /* for */ #endif writeerror=FALSE; nametablesize=sizeof(int16_t); numpublics=0; numnatives=0; numpubvars=0; mainaddr=-1; /* count number of public and native functions and public variables */ for (sym=glbtab.next; sym!=NULL; sym=sym->next) { int match=0; if (sym->ident==iFUNCTN) { if ((sym->usage & uNATIVE)!=0 && (sym->usage & uREAD)!=0 && sym->addr>=0) match=++numnatives; if ((sym->usage & uPUBLIC)!=0 && (sym->usage & uDEFINE)!=0) match=++numpublics; if (strcmp(sym->name,uMAINFUNC)==0) { assert(sym->vclass==sGLOBAL); mainaddr=sym->addr; } /* if */ } else if (sym->ident==iVARIABLE || sym->ident == iARRAY || sym->ident == iREFARRAY) { if ((sym->usage & uPUBLIC)!=0 && (sym->usage & (uREAD | uWRITTEN))!=0) match=++numpubvars; } /* if */ if (match) { char alias[sNAMEMAX+1]; assert(sym!=NULL); if ((sym->usage & uNATIVE)==0 || !lookup_alias(alias,sym->name)) { assert(strlen(sym->name)<=sNAMEMAX); strcpy(alias,sym->name); } /* if */ nametablesize+=strlen(alias)+1; } /* if */ } /* for */ assert(numnatives==ntv_funcid); /* count number of libraries */ numlibraries=0; if (pc_addlibtable) { for (constptr=libname_tab.next; constptr!=NULL; constptr=constptr->next) { if (constptr->value>0) { assert(strlen(constptr->name)>0); numlibraries++; nametablesize+=strlen(constptr->name)+1; } /* if */ } /* for */ } /* if */ /* count number of public tags */ numtags=0; for (constptr=tagname_tab.next; constptr!=NULL; constptr=constptr->next) { if ((constptr->value & PUBLICTAG)!=0) { assert(strlen(constptr->name)>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= (int)(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)AMX_MAGIC; hdr.file_version=CUR_FILE_VERSION; hdr.amx_version=MIN_AMX_VERSION; hdr.flags=(short)(sc_debug & sSYMBOLIC); if (sc_compress) hdr.flags|=AMX_FLAG_COMPACT; if (sc_debug==0) hdr.flags|=AMX_FLAG_NOCHECKS; hdr.defsize=sizeof(AMX_FUNCSTUBNT); hdr.publics=sizeof hdr; /* public table starts right after the header */ hdr.natives=hdr.publics + numpublics*sizeof(AMX_FUNCSTUBNT); hdr.libraries=hdr.natives + numnatives*sizeof(AMX_FUNCSTUBNT); hdr.pubvars=hdr.libraries + numlibraries*sizeof(AMX_FUNCSTUBNT); hdr.tags=hdr.pubvars + numpubvars*sizeof(AMX_FUNCSTUBNT); hdr.nametable=hdr.tags + numtags*sizeof(AMX_FUNCSTUBNT); 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 */ pc_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<hdr.cod; nameofs++) putc(0,fout); nameofs=hdr.nametable+sizeof(int16_t); /* write the public functions table */ count=0; for (sym=glbtab.next; sym!=NULL; 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; #if BYTE_ORDER==BIG_ENDIAN align32(&func.address); align32(&func.nameofs); #endif pc_resetbin(fout,hdr.publics+count*sizeof(AMX_FUNCSTUBNT)); pc_writebin(fout,&func,sizeof func); pc_resetbin(fout,nameofs); pc_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==NULL) error(103); /* insufficient memory */ #if !defined NDEBUG memset(nativelist,0,numnatives*sizeof(symbol *)); /* for NULL checking */ #endif for (sym=glbtab.next; sym!=NULL; 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; #if BYTE_ORDER==BIG_ENDIAN align32(&func.address); align32(&func.nameofs); #endif pc_resetbin(fout,hdr.natives+count*sizeof(AMX_FUNCSTUBNT)); pc_writebin(fout,&func,sizeof func); pc_resetbin(fout,nameofs); pc_writebin(fout,alias,strlen(alias)+1); nameofs+=strlen(alias)+1; count++; } /* for */ free(nativelist); } /* if */ /* write the libraries table */ if (pc_addlibtable) { count=0; for (constptr=libname_tab.next; constptr!=NULL; constptr=constptr->next) { if (constptr->value>0) { assert(strlen(constptr->name)>0); func.address=0; func.nameofs=nameofs; #if BYTE_ORDER==BIG_ENDIAN align32(&func.address); align32(&func.nameofs); #endif pc_resetbin(fout,hdr.libraries+count*sizeof(AMX_FUNCSTUBNT)); pc_writebin(fout,&func,sizeof func); pc_resetbin(fout,nameofs); pc_writebin(fout,constptr->name,strlen(constptr->name)+1); nameofs+=strlen(constptr->name)+1; count++; } /* if */ } /* for */ } /* if */ /* write the public variables table */ count=0; for (sym=glbtab.next; sym!=NULL; sym=sym->next) { if ((sym->ident==iVARIABLE || sym->ident==iARRAY || sym->ident==iREFARRAY) && (sym->usage & uPUBLIC)!=0 && (sym->usage & (uREAD | uWRITTEN))!=0) { assert((sym->usage & uDEFINE)!=0); assert(sym->vclass==sGLOBAL); func.address=sym->addr; func.nameofs=nameofs; #if BYTE_ORDER==BIG_ENDIAN align32(&func.address); align32(&func.nameofs); #endif pc_resetbin(fout,hdr.pubvars+count*sizeof(AMX_FUNCSTUBNT)); pc_writebin(fout,&func,sizeof func); pc_resetbin(fout,nameofs); pc_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!=NULL; constptr=constptr->next) { if ((constptr->value & PUBLICTAG)!=0) { assert(strlen(constptr->name)>0); func.address=constptr->value & TAGMASK; func.nameofs=nameofs; #if BYTE_ORDER==BIG_ENDIAN align32(&func.address); align32(&func.nameofs); #endif pc_resetbin(fout,hdr.tags+count*sizeof(AMX_FUNCSTUBNT)); pc_writebin(fout,&func,sizeof func); pc_resetbin(fout,nameofs); pc_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==hdr.nametable+nametablesize); pc_resetbin(fout,hdr.nametable); count=sNAMEMAX; #if BYTE_ORDER==BIG_ENDIAN align16(&count); #endif pc_writebin(fout,&count,sizeof count); pc_resetbin(fout,hdr.cod); /* 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 (sc_labnum>0) { /* only very short programs have zero labels; no first pass is needed * if there are no labels */ lbltab=(cell *)malloc(sc_labnum*sizeof(cell)); if (lbltab==NULL) error(103); /* insufficient memory */ codeindex=0; pc_resetasm(fin); while (pc_readasm(fin,line,sizeof line)!=NULL) { 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>=0 && lindex<sc_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' && !isspace(*params); params++) /* nothing */; assert(params>instr); i=findopcode(instr,(int)(params-instr)); if (opcodelist[i].name==NULL) { *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++) { pc_resetasm(fin); while (pc_readasm(fin,line,sizeof line)!=NULL) { 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' && !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!=NULL) { free(lbltab); #if !defined NDEBUG lbltab=NULL; #endif } /* if */ if (sc_compress) hdr.size=pc_lengthbin(fout);/* get this value before appending debug info */ if (!writeerror && (sc_debug & sSYMBOLIC)!=0) append_dbginfo(fout); /* optionally append debug file */ if (writeerror) error(101,"disk full"); /* adjust the header */ size=(int)hdr.cod; /* save, the value in the header may be swapped */ #if BYTE_ORDER==BIG_ENDIAN align32(&hdr.size); align16(&hdr.magic); align16(&hdr.flags); align16(&hdr.defsize); align32(&hdr.publics); align32(&hdr.natives); align32(&hdr.libraries); align32(&hdr.pubvars); align32(&hdr.tags); align32(&hdr.nametable); align32(&hdr.cod); align32(&hdr.dat); align32(&hdr.hea); align32(&hdr.stp); align32(&hdr.cip); #endif pc_resetbin(fout,0); pc_writebin(fout,&hdr,sizeof hdr); /* return the size of the header (including name tables, but excluding code * or data sections) */ return size; } static void append_dbginfo(FILE *fout) { AMX_DBG_HDR dbghdr; AMX_DBG_LINE dbgline; AMX_DBG_SYMBOL dbgsym; AMX_DBG_SYMDIM dbgidxtag[sDIMEN_MAX]; int index,dim; char *str,*prevstr,*name,*prevname; ucell codeidx,previdx; constvalue *constptr; char symname[2*sNAMEMAX+16]; int16_t id1,id2; ucell address; /* header with general information */ memset(&dbghdr, 0, sizeof dbghdr); dbghdr.size=sizeof dbghdr; dbghdr.magic=AMX_DBG_MAGIC; dbghdr.file_version=CUR_FILE_VERSION; dbghdr.amx_version=MIN_AMX_VERSION; /* first pass: collect the number of items in various tables */ /* file table */ previdx=0; prevstr=NULL; prevname=NULL; for (index=0; (str=get_dbgstring(index))!=NULL; index++) { assert(str!=NULL); assert(str[0]!='\0' && str[1]==':'); if (str[0]=='F') { codeidx=hex2long(str+2,&name); if (codeidx!=previdx) { if (prevstr!=NULL) { assert(prevname!=NULL); dbghdr.files++; dbghdr.size+=sizeof(cell)+strlen(prevname)+1; } /* if */ previdx=codeidx; } /* if */ prevstr=str; prevname=skipwhitespace(name); } /* if */ } /* for */ if (prevstr!=NULL) { assert(prevname!=NULL); dbghdr.files++; dbghdr.size+=sizeof(cell)+strlen(prevname)+1; } /* if */ /* line number table */ for (index=0; (str=get_dbgstring(index))!=NULL; index++) { assert(str!=NULL); assert(str[0]!='\0' && str[1]==':'); if (str[0]=='L') { dbghdr.lines++; dbghdr.size+=sizeof(AMX_DBG_LINE); } /* if */ } /* for */ /* symbol table */ for (index=0; (str=get_dbgstring(index))!=NULL; index++) { assert(str!=NULL); assert(str[0]!='\0' && str[1]==':'); if (str[0]=='S') { dbghdr.symbols++; name=strchr(str+2,':'); assert(name!=NULL); dbghdr.size+=sizeof(AMX_DBG_SYMBOL)+strlen(skipwhitespace(name+1)); if ((prevstr=strchr(name,'['))!=NULL) while ((prevstr=strchr(prevstr+1,':'))!=NULL) dbghdr.size+=sizeof(AMX_DBG_SYMDIM); } /* if */ } /* for */ /* tag table */ for (constptr=tagname_tab.next; constptr!=NULL; constptr=constptr->next) { assert(strlen(constptr->name)>0); dbghdr.tags++; dbghdr.size+=sizeof(AMX_DBG_TAG)+strlen(constptr->name); } /* for */ /* automaton table */ for (constptr=sc_automaton_tab.next; constptr!=NULL; constptr=constptr->next) { assert(constptr->index==0 && strlen(constptr->name)==0 || strlen(constptr->name)>0); dbghdr.automatons++; dbghdr.size+=sizeof(AMX_DBG_MACHINE)+strlen(constptr->name); } /* for */ /* state table */ for (constptr=sc_state_tab.next; constptr!=NULL; constptr=constptr->next) { assert(strlen(constptr->name)>0); dbghdr.states++; dbghdr.size+=sizeof(AMX_DBG_STATE)+strlen(constptr->name); } /* for */ /* pass 2: generate the tables */ writeerror |= !pc_writebin(fout,&dbghdr,sizeof dbghdr); /* file table */ previdx=0; prevstr=NULL; prevname=NULL; for (index=0; (str=get_dbgstring(index))!=NULL; index++) { assert(str!=NULL); assert(str[0]!='\0' && str[1]==':'); if (str[0]=='F') { codeidx=hex2long(str+2,&name); if (codeidx!=previdx) { if (prevstr!=NULL) { assert(prevname!=NULL); writeerror |= !pc_writebin(fout,&previdx,sizeof previdx); writeerror |= !pc_writebin(fout,prevname,strlen(prevname)+1); } /* if */ previdx=codeidx; } /* if */ prevstr=str; prevname=skipwhitespace(name); } /* if */ } /* for */ if (prevstr!=NULL) { assert(prevname!=NULL); writeerror |= !pc_writebin(fout,&previdx,sizeof previdx); writeerror |= !pc_writebin(fout,prevname,strlen(prevname)+1); } /* if */ /* line number table */ for (index=0; (str=get_dbgstring(index))!=NULL; index++) { assert(str!=NULL); assert(str[0]!='\0' && str[1]==':'); if (str[0]=='L') { dbgline.address=hex2long(str+2,&str); dbgline.line=(int32_t)hex2long(str,NULL); writeerror |= !pc_writebin(fout,&dbgline,sizeof dbgline); } /* if */ } /* for */ /* symbol table */ for (index=0; (str=get_dbgstring(index))!=NULL; index++) { assert(str!=NULL); assert(str[0]!='\0' && str[1]==':'); if (str[0]=='S') { dbgsym.address=hex2long(str+2,&str); dbgsym.tag=(int16_t)hex2long(str,&str); str=skipwhitespace(str); assert(*str==':'); name=skipwhitespace(str+1); str=strchr(name,' '); assert(str!=NULL); assert((int)(str-name)<sizeof symname); strncpy(symname,name,(int)(str-name)); symname[(int)(str-name)]='\0'; dbgsym.codestart=hex2long(str,&str); dbgsym.codeend=hex2long(str,&str); dbgsym.ident=(char)hex2long(str,&str); dbgsym.vclass=(char)hex2long(str,&str); dbgsym.dim=0; str=skipwhitespace(str); if (*str=='[') { while (*(str=skipwhitespace(str+1))!=']') { dbgidxtag[dbgsym.dim].tag=(int16_t)hex2long(str,&str); str=skipwhitespace(str); assert(*str==':'); dbgidxtag[dbgsym.dim].size=hex2long(str+1,&str); dbgsym.dim++; } /* while */ } /* if */ writeerror |= !pc_writebin(fout,&dbgsym,sizeof dbgsym-1); writeerror |= !pc_writebin(fout,symname,strlen(symname)+1); for (dim=0; dim<dbgsym.dim; dim++) writeerror |= !pc_writebin(fout,&dbgidxtag[dim],sizeof dbgidxtag[dim]); } /* if */ } /* for */ /* tag table */ for (constptr=tagname_tab.next; constptr!=NULL; constptr=constptr->next) { assert(strlen(constptr->name)>0); id1=(int16_t)(constptr->value & TAGMASK); writeerror |= !pc_writebin(fout,&id1,sizeof id1); writeerror |= !pc_writebin(fout,constptr->name,strlen(constptr->name)+1); } /* for */ /* automaton table */ for (constptr=sc_automaton_tab.next; constptr!=NULL; constptr=constptr->next) { assert(constptr->index==0 && strlen(constptr->name)==0 || strlen(constptr->name)>0); id1=(int16_t)constptr->index; address=(ucell)constptr->value; writeerror |= !pc_writebin(fout,&id1,sizeof id1); writeerror |= !pc_writebin(fout,&address,sizeof address); writeerror |= !pc_writebin(fout,constptr->name,strlen(constptr->name)+1); } /* for */ /* state table */ for (constptr=sc_state_tab.next; constptr!=NULL; constptr=constptr->next) { assert(strlen(constptr->name)>0); id1=(int16_t)constptr->value; id2=(int16_t)constptr->index; address=(ucell)constptr->value; writeerror |= !pc_writebin(fout,&id1,sizeof id1); writeerror |= !pc_writebin(fout,&id2,sizeof id2); writeerror |= !pc_writebin(fout,constptr->name,strlen(constptr->name)+1); } /* for */ delete_dbgstringtable(); }