dvander@27: /* Pawn compiler - Binary code generation (the "assembler") dvander@27: * dvander@27: * Copyright (c) ITB CompuPhase, 1997-2006 dvander@27: * dvander@27: * This software is provided "as-is", without any express or implied warranty. dvander@27: * In no event will the authors be held liable for any damages arising from dvander@27: * the use of this software. dvander@27: * dvander@27: * Permission is granted to anyone to use this software for any purpose, dvander@27: * including commercial applications, and to alter it and redistribute it dvander@27: * freely, subject to the following restrictions: dvander@27: * dvander@27: * 1. The origin of this software must not be misrepresented; you must not dvander@27: * claim that you wrote the original software. If you use this software in dvander@27: * a product, an acknowledgment in the product documentation would be dvander@27: * appreciated but is not required. dvander@27: * 2. Altered source versions must be plainly marked as such, and must not be dvander@27: * misrepresented as being the original software. dvander@27: * 3. This notice may not be removed or altered from any source distribution. dvander@27: * faluco@62: * Version: $Id: sc6.c 3633 2006-08-11 16:20:18Z thiadmer $ dvander@27: */ dvander@27: #include dvander@27: #include dvander@27: #include /* for macro max() */ dvander@27: #include /* for macro offsetof() */ dvander@27: #include dvander@27: #include dvander@27: #if defined FORTIFY dvander@27: #include dvander@27: #endif dvander@27: #include "lstring.h" dvander@27: #include "sc.h" dvander@27: #include "amxdbg.h" dvander@27: #if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ dvander@27: #include dvander@27: #endif dvander@27: dvander@27: dvander@27: static void append_dbginfo(FILE *fout); dvander@27: dvander@27: dvander@27: typedef cell (*OPCODE_PROC)(FILE *fbin,char *params,cell opcode); dvander@27: dvander@27: typedef struct { dvander@27: cell opcode; dvander@27: char *name; dvander@27: int segment; /* sIN_CSEG=parse in cseg, sIN_DSEG=parse in dseg */ dvander@27: OPCODE_PROC func; dvander@27: } OPCODEC; dvander@27: dvander@27: static cell codeindex; /* similar to "code_idx" */ dvander@27: static cell *lbltab; /* label table */ dvander@27: static int writeerror; dvander@27: static int bytes_in, bytes_out; dvander@27: static jmp_buf compact_err; dvander@27: dvander@27: /* apparently, strtol() does not work correctly on very large (unsigned) dvander@27: * hexadecimal values */ dvander@27: static ucell hex2long(const char *s,char **n) dvander@27: { dvander@27: ucell result=0L; dvander@27: int negate=FALSE; dvander@27: int digit; dvander@27: dvander@27: /* ignore leading whitespace */ dvander@27: while (*s==' ' || *s=='\t') dvander@27: s++; dvander@27: dvander@27: /* allow a negation sign to create the two's complement of numbers */ dvander@27: if (*s=='-') { dvander@27: negate=TRUE; dvander@27: s++; dvander@27: } /* if */ dvander@27: dvander@27: assert((*s>='0' && *s<='9') || (*s>='a' && *s<='f') || (*s>='a' && *s<='f')); dvander@27: for ( ;; ) { dvander@27: if (*s>='0' && *s<='9') dvander@27: digit=*s-'0'; dvander@27: else if (*s>='a' && *s<='f') dvander@27: digit=*s-'a' + 10; dvander@27: else if (*s>='A' && *s<='F') dvander@27: digit=*s-'A' + 10; dvander@27: else dvander@27: break; /* probably whitespace */ dvander@27: result=(result<<4) | digit; dvander@27: s++; dvander@27: } /* for */ dvander@27: if (n!=NULL) dvander@27: *n=(char*)s; dvander@27: if (negate) dvander@27: result=(~result)+1; /* take two's complement of the result */ dvander@27: return (ucell)result; dvander@27: } dvander@27: dvander@27: static ucell getparam(const char *s,char **n) dvander@27: { dvander@27: ucell result=0; dvander@27: for ( ;; ) { dvander@27: result+=hex2long(s,(char**)&s); dvander@27: if (*s!='+') dvander@27: break; dvander@27: s++; dvander@27: } /* for */ dvander@27: if (n!=NULL) dvander@27: *n=(char*)s; dvander@27: return result; dvander@27: } dvander@27: dvander@27: #if BYTE_ORDER==BIG_ENDIAN dvander@27: static uint16_t *align16(uint16_t *v) dvander@27: { dvander@27: unsigned char *s = (unsigned char *)v; dvander@27: unsigned char t; dvander@27: dvander@27: /* swap two bytes */ dvander@27: t=s[0]; dvander@27: s[0]=s[1]; dvander@27: s[1]=t; dvander@27: return v; dvander@27: } dvander@27: dvander@27: static uint32_t *align32(uint32_t *v) dvander@27: { dvander@27: unsigned char *s = (unsigned char *)v; dvander@27: unsigned char t; dvander@27: dvander@27: /* swap outer two bytes */ dvander@27: t=s[0]; dvander@27: s[0]=s[3]; dvander@27: s[3]=t; dvander@27: /* swap inner two bytes */ dvander@27: t=s[1]; dvander@27: s[1]=s[2]; dvander@27: s[2]=t; dvander@27: return v; dvander@27: } dvander@27: dvander@27: #if PAWN_CELL_SIZE>=64 dvander@27: static uint64_t *align64(uint64_t *v) dvander@27: { dvander@27: unsigned char *s = (unsigned char *)v; dvander@27: unsigned char t; dvander@27: dvander@27: t=s[0]; dvander@27: s[0]=s[7]; dvander@27: s[7]=t; dvander@27: dvander@27: t=s[1]; dvander@27: s[1]=s[6]; dvander@27: s[6]=t; dvander@27: dvander@27: t=s[2]; dvander@27: s[2]=s[5]; dvander@27: s[5]=t; dvander@27: dvander@27: t=s[3]; dvander@27: s[3]=s[4]; dvander@27: s[4]=t; dvander@27: dvander@27: return v; dvander@27: } dvander@27: #endif dvander@27: dvander@27: #if PAWN_CELL_SIZE==16 dvander@27: #define aligncell(v) align16(v) dvander@27: #elif PAWN_CELL_SIZE==32 dvander@27: #define aligncell(v) align32(v) dvander@27: #elif PAWN_CELL_SIZE==64 dvander@27: #define aligncell(v) align64(v) dvander@27: #endif dvander@27: #else dvander@27: #define align16(v) (v) dvander@27: #define align32(v) (v) dvander@27: #define aligncell(v) (v) dvander@27: #endif dvander@27: dvander@27: static char *skipwhitespace(char *str) dvander@27: { dvander@27: while (isspace(*str)) dvander@27: str++; dvander@27: return str; dvander@27: } dvander@27: dvander@27: static char *stripcomment(char *str) dvander@27: { dvander@27: char *ptr=strchr(str,';'); dvander@27: if (ptr!=NULL) { dvander@27: *ptr++='\n'; /* terminate the line, but leave the '\n' */ dvander@27: *ptr='\0'; dvander@27: } /* if */ dvander@27: return str; dvander@27: } dvander@27: dvander@27: static void write_encoded(FILE *fbin,ucell *c,int num) dvander@27: { dvander@27: #if PAWN_CELL_SIZE == 16 dvander@27: #define ENC_MAX 3 /* a 16-bit cell is encoded in max. 3 bytes */ dvander@27: #define ENC_MASK 0x03 /* after 2x7 bits, 2 bits remain to make 16 bits */ dvander@27: #elif PAWN_CELL_SIZE == 32 dvander@27: #define ENC_MAX 5 /* a 32-bit cell is encoded in max. 5 bytes */ dvander@27: #define ENC_MASK 0x0f /* after 4x7 bits, 4 bits remain to make 32 bits */ dvander@27: #elif PAWN_CELL_SIZE == 64 dvander@27: #define ENC_MAX 10 /* a 32-bit cell is encoded in max. 10 bytes */ dvander@27: #define ENC_MASK 0x01 /* after 9x7 bits, 1 bit remains to make 64 bits */ dvander@27: #endif dvander@27: dvander@27: assert(fbin!=NULL); dvander@27: while (num-->0) { dvander@27: if (sc_compress) { dvander@27: ucell p=(ucell)*c; dvander@27: unsigned char t[ENC_MAX]; dvander@27: unsigned char code; dvander@27: int index; dvander@27: for (index=0; index>=7; dvander@27: } /* for */ dvander@27: /* skip leading zeros */ dvander@27: while (index>1 && t[index-1]==0 && (t[index-2] & 0x40)==0) dvander@27: index--; dvander@27: /* skip leading -1s */ dvander@27: if (index==ENC_MAX && t[index-1]==ENC_MASK && (t[index-2] & 0x40)!=0) dvander@27: index--; dvander@27: while (index>1 && t[index-1]==0x7f && (t[index-2] & 0x40)!=0) dvander@27: index--; dvander@27: /* write high byte first, write continuation bits */ dvander@27: assert(index>0); dvander@27: while (index-->0) { dvander@27: code=(unsigned char)((index==0) ? t[index] : (t[index]|0x80)); dvander@27: writeerror |= !pc_writebin(fbin,&code,1); dvander@27: bytes_out++; dvander@27: } /* while */ dvander@27: bytes_in+=sizeof *c; dvander@27: assert(AMX_COMPACTMARGIN>2); dvander@27: if (bytes_out-bytes_in>=AMX_COMPACTMARGIN-2) dvander@27: longjmp(compact_err,1); dvander@27: } else { dvander@27: assert((pc_lengthbin(fbin) % sizeof(cell)) == 0); dvander@27: writeerror |= !pc_writebin(fbin,aligncell(c),sizeof *c); dvander@27: } /* if */ dvander@27: c++; dvander@27: } /* while */ dvander@27: } dvander@27: dvander@27: #if defined __BORLANDC__ || defined __WATCOMC__ dvander@27: #pragma argsused dvander@27: #endif dvander@27: static cell noop(FILE *fbin,char *params,cell opcode) dvander@27: { dvander@27: return 0; dvander@27: } dvander@27: dvander@27: #if defined __BORLANDC__ || defined __WATCOMC__ dvander@27: #pragma argsused dvander@27: #endif dvander@27: static cell set_currentfile(FILE *fbin,char *params,cell opcode) dvander@27: { dvander@27: fcurrent=(short)getparam(params,NULL); dvander@27: return 0; dvander@27: } dvander@27: dvander@27: #if defined __BORLANDC__ || defined __WATCOMC__ dvander@27: #pragma argsused dvander@27: #endif dvander@27: static cell parm0(FILE *fbin,char *params,cell opcode) dvander@27: { dvander@27: if (fbin!=NULL) dvander@27: write_encoded(fbin,(ucell*)&opcode,1); dvander@27: return opcodes(1); dvander@27: } dvander@27: dvander@27: static cell parm1(FILE *fbin,char *params,cell opcode) dvander@27: { dvander@27: ucell p=getparam(params,NULL); dvander@27: if (fbin!=NULL) { dvander@27: write_encoded(fbin,(ucell*)&opcode,1); dvander@27: write_encoded(fbin,&p,1); dvander@27: } /* if */ dvander@27: return opcodes(1)+opargs(1); dvander@27: } dvander@27: dvander@27: static cell parm2(FILE *fbin,char *params,cell opcode) dvander@27: { dvander@27: ucell p1=getparam(params,¶ms); dvander@27: ucell p2=getparam(params,NULL); dvander@27: if (fbin!=NULL) { dvander@27: write_encoded(fbin,(ucell*)&opcode,1); dvander@27: write_encoded(fbin,&p1,1); dvander@27: write_encoded(fbin,&p2,1); dvander@27: } /* if */ dvander@27: return opcodes(1)+opargs(2); dvander@27: } dvander@27: dvander@27: static cell parm3(FILE *fbin,char *params,cell opcode) dvander@27: { dvander@27: ucell p1=getparam(params,¶ms); dvander@27: ucell p2=getparam(params,¶ms); dvander@27: ucell p3=getparam(params,NULL); dvander@27: if (fbin!=NULL) { dvander@27: write_encoded(fbin,(ucell*)&opcode,1); dvander@27: write_encoded(fbin,&p1,1); dvander@27: write_encoded(fbin,&p2,1); dvander@27: write_encoded(fbin,&p3,1); dvander@27: } /* if */ dvander@27: return opcodes(1)+opargs(3); dvander@27: } dvander@27: dvander@27: static cell parm4(FILE *fbin,char *params,cell opcode) dvander@27: { dvander@27: ucell p1=getparam(params,¶ms); dvander@27: ucell p2=getparam(params,¶ms); dvander@27: ucell p3=getparam(params,¶ms); dvander@27: ucell p4=getparam(params,NULL); dvander@27: if (fbin!=NULL) { dvander@27: write_encoded(fbin,(ucell*)&opcode,1); dvander@27: write_encoded(fbin,&p1,1); dvander@27: write_encoded(fbin,&p2,1); dvander@27: write_encoded(fbin,&p3,1); dvander@27: write_encoded(fbin,&p4,1); dvander@27: } /* if */ dvander@27: return opcodes(1)+opargs(4); dvander@27: } dvander@27: dvander@27: static cell parm5(FILE *fbin,char *params,cell opcode) dvander@27: { dvander@27: ucell p1=getparam(params,¶ms); dvander@27: ucell p2=getparam(params,¶ms); dvander@27: ucell p3=getparam(params,¶ms); dvander@27: ucell p4=getparam(params,¶ms); dvander@27: ucell p5=getparam(params,NULL); dvander@27: if (fbin!=NULL) { dvander@27: write_encoded(fbin,(ucell*)&opcode,1); dvander@27: write_encoded(fbin,&p1,1); dvander@27: write_encoded(fbin,&p2,1); dvander@27: write_encoded(fbin,&p3,1); dvander@27: write_encoded(fbin,&p4,1); dvander@27: write_encoded(fbin,&p5,1); dvander@27: } /* if */ dvander@27: return opcodes(1)+opargs(5); dvander@27: } dvander@27: dvander@27: #if defined __BORLANDC__ || defined __WATCOMC__ dvander@27: #pragma argsused dvander@27: #endif dvander@27: static cell do_dump(FILE *fbin,char *params,cell opcode) dvander@27: { dvander@27: ucell p; dvander@27: int num = 0; dvander@27: dvander@27: while (*params!='\0') { dvander@27: p=getparam(params,¶ms); dvander@27: if (fbin!=NULL) dvander@27: write_encoded(fbin,&p,1); dvander@27: num++; dvander@27: while (isspace(*params)) dvander@27: params++; dvander@27: } /* while */ dvander@27: return num*sizeof(cell); dvander@27: } dvander@27: dvander@27: static cell do_call(FILE *fbin,char *params,cell opcode) dvander@27: { dvander@27: char name[sNAMEMAX+1]; dvander@27: int i; dvander@27: symbol *sym; dvander@27: ucell p; dvander@27: dvander@27: for (i=0; !isspace(*params); i++,params++) { dvander@27: assert(*params!='\0'); dvander@27: assert(i=0 && iident==iFUNCTN || sym->ident==iREFFUNC); dvander@27: assert(sym->vclass==sGLOBAL); dvander@27: p=sym->addr; dvander@27: } /* if */ dvander@27: dvander@27: if (fbin!=NULL) { dvander@27: write_encoded(fbin,(ucell*)&opcode,1); dvander@27: write_encoded(fbin,&p,1); dvander@27: } /* if */ dvander@27: return opcodes(1)+opargs(1); dvander@27: } dvander@27: dvander@27: static cell do_jump(FILE *fbin,char *params,cell opcode) dvander@27: { dvander@27: int i; dvander@27: ucell p; dvander@27: dvander@27: i=(int)hex2long(params,NULL); dvander@27: assert(i>=0 && i=0 && i=0 && i=MAX_INSTR_LEN) dvander@27: return 0; dvander@27: strlcpy(str,instr,maxlen+1); dvander@27: /* look up the instruction with a binary search dvander@27: * the assembler is case insensitive to instructions (but case sensitive dvander@27: * to symbols) dvander@27: */ dvander@27: low=1; /* entry 0 is reserved (for "not found") */ dvander@27: high=(sizeof opcodelist / sizeof opcodelist[0])-1; dvander@27: while (low0) dvander@27: low=mid+1; dvander@27: else dvander@27: high=mid; dvander@27: } /* while */ dvander@27: dvander@27: assert(low==high); dvander@27: if (stricmp(str,opcodelist[low].name)==0) dvander@27: return low; /* found */ dvander@27: return 0; /* not found, return special index */ dvander@27: } dvander@27: dvander@27: SC_FUNC int assemble(FILE *fout,FILE *fin) dvander@27: { dvander@27: AMX_HEADER hdr; dvander@27: AMX_FUNCSTUBNT func; dvander@27: int numpublics,numnatives,numlibraries,numpubvars,numtags,padding; dvander@27: long nametablesize,nameofs; dvander@27: #if PAWN_CELL_SIZE > 32 dvander@27: char line[512]; dvander@27: #else dvander@27: char line[256]; dvander@27: #endif dvander@27: char *instr,*params; dvander@27: int i,pass,size; dvander@27: int16_t count; dvander@27: symbol *sym, **nativelist; dvander@27: constvalue *constptr; dvander@27: cell mainaddr; faluco@62: char nullchar; dvander@27: dvander@27: /* if compression failed, restart the assembly with compaction switched off */ dvander@27: if (setjmp(compact_err)!=0) { dvander@27: assert(sc_compress); /* cannot arrive here if compact encoding was disabled */ dvander@27: sc_compress=FALSE; dvander@27: pc_resetbin(fout,0); dvander@27: error(232); /* disabled compact encoding */ dvander@27: } /* if */ dvander@27: dvander@27: #if !defined NDEBUG dvander@27: /* verify that the opcode list is sorted (skip entry 1; it is reserved dvander@27: * for a non-existant opcode) dvander@27: */ dvander@27: assert(opcodelist[1].name!=NULL); dvander@27: for (i=2; i<(sizeof opcodelist / sizeof opcodelist[0]); i++) { dvander@27: assert(opcodelist[i].name!=NULL); dvander@27: assert(stricmp(opcodelist[i].name,opcodelist[i-1].name)>0); dvander@27: } /* for */ dvander@27: #endif dvander@27: dvander@27: writeerror=FALSE; dvander@27: nametablesize=sizeof(int16_t); dvander@27: numpublics=0; dvander@27: numnatives=0; dvander@27: numpubvars=0; dvander@27: mainaddr=-1; dvander@27: /* count number of public and native functions and public variables */ dvander@27: for (sym=glbtab.next; sym!=NULL; sym=sym->next) { dvander@27: int match=0; dvander@27: if (sym->ident==iFUNCTN) { dvander@27: if ((sym->usage & uNATIVE)!=0 && (sym->usage & uREAD)!=0 && sym->addr>=0) dvander@27: match=++numnatives; dvander@27: if ((sym->usage & uPUBLIC)!=0 && (sym->usage & uDEFINE)!=0) dvander@27: match=++numpublics; dvander@27: if (strcmp(sym->name,uMAINFUNC)==0) { dvander@27: assert(sym->vclass==sGLOBAL); dvander@27: mainaddr=sym->addr; dvander@27: } /* if */ dvander@27: } else if (sym->ident==iVARIABLE) { dvander@27: if ((sym->usage & uPUBLIC)!=0 && (sym->usage & (uREAD | uWRITTEN))!=0) dvander@27: match=++numpubvars; dvander@27: } /* if */ dvander@27: if (match) { dvander@27: char alias[sNAMEMAX+1]; dvander@27: assert(sym!=NULL); dvander@27: if ((sym->usage & uNATIVE)==0 || !lookup_alias(alias,sym->name)) { dvander@27: assert(strlen(sym->name)<=sNAMEMAX); dvander@27: strcpy(alias,sym->name); dvander@27: } /* if */ dvander@27: nametablesize+=strlen(alias)+1; dvander@27: } /* if */ dvander@27: } /* for */ dvander@27: assert(numnatives==ntv_funcid); dvander@27: dvander@27: /* count number of libraries */ dvander@27: numlibraries=0; dvander@27: if (pc_addlibtable) { dvander@27: for (constptr=libname_tab.next; constptr!=NULL; constptr=constptr->next) { dvander@27: if (constptr->value>0) { dvander@27: assert(strlen(constptr->name)>0); dvander@27: numlibraries++; dvander@27: nametablesize+=strlen(constptr->name)+1; dvander@27: } /* if */ dvander@27: } /* for */ dvander@27: } /* if */ dvander@27: dvander@27: /* count number of public tags */ dvander@27: numtags=0; dvander@27: for (constptr=tagname_tab.next; constptr!=NULL; constptr=constptr->next) { dvander@27: if ((constptr->value & PUBLICTAG)!=0) { dvander@27: assert(strlen(constptr->name)>0); dvander@27: numtags++; dvander@27: nametablesize+=strlen(constptr->name)+1; dvander@27: } /* if */ dvander@27: } /* for */ dvander@27: dvander@27: /* pad the header to sc_dataalign dvander@27: * => thereby the code segment is aligned dvander@27: * => since the code segment is padded to a sc_dataalign boundary, the data segment is aligned dvander@27: * => and thereby the stack top is aligned too dvander@27: */ dvander@27: assert(sc_dataalign!=0); dvander@27: padding= (int)(sc_dataalign - (sizeof hdr + nametablesize) % sc_dataalign); dvander@27: if (padding==sc_dataalign) dvander@27: padding=0; dvander@27: dvander@27: /* write the abstract machine header */ dvander@27: memset(&hdr, 0, sizeof hdr); dvander@27: hdr.magic=(unsigned short)AMX_MAGIC; dvander@27: hdr.file_version=(char)((pc_optimize<=sOPTIMIZE_NOMACRO) ? MAX_FILE_VER_JIT : CUR_FILE_VERSION); dvander@27: hdr.amx_version=(char)((pc_optimize<=sOPTIMIZE_NOMACRO) ? MIN_AMX_VER_JIT : MIN_AMX_VERSION); dvander@27: hdr.flags=(short)(sc_debug & sSYMBOLIC); dvander@27: if (sc_compress) dvander@27: hdr.flags|=AMX_FLAG_COMPACT; dvander@27: if (sc_debug==0) dvander@27: hdr.flags|=AMX_FLAG_NOCHECKS; dvander@27: if (pc_memflags & suSLEEP_INSTR) dvander@27: hdr.flags|=AMX_FLAG_SLEEP; dvander@27: hdr.defsize=sizeof(AMX_FUNCSTUBNT); dvander@27: hdr.publics=sizeof hdr; /* public table starts right after the header */ dvander@27: hdr.natives=hdr.publics + numpublics*sizeof(AMX_FUNCSTUBNT); dvander@27: hdr.libraries=hdr.natives + numnatives*sizeof(AMX_FUNCSTUBNT); dvander@27: hdr.pubvars=hdr.libraries + numlibraries*sizeof(AMX_FUNCSTUBNT); dvander@27: hdr.tags=hdr.pubvars + numpubvars*sizeof(AMX_FUNCSTUBNT); dvander@27: hdr.nametable=hdr.tags + numtags*sizeof(AMX_FUNCSTUBNT); dvander@27: hdr.cod=hdr.nametable + nametablesize + padding; dvander@27: hdr.dat=hdr.cod + code_idx; dvander@27: hdr.hea=hdr.dat + glb_declared*sizeof(cell); dvander@27: hdr.stp=hdr.hea + pc_stksize*sizeof(cell); dvander@27: hdr.cip=mainaddr; dvander@27: hdr.size=hdr.hea; /* preset, this is incorrect in case of compressed output */ dvander@27: pc_writebin(fout,&hdr,sizeof hdr); dvander@27: dvander@27: /* dump zeros up to the rest of the header, so that we can easily "seek" */ faluco@62: nullchar='\0'; dvander@27: for (nameofs=sizeof hdr; nameofsnext) { dvander@27: if (sym->ident==iFUNCTN dvander@27: && (sym->usage & uPUBLIC)!=0 && (sym->usage & uDEFINE)!=0) dvander@27: { dvander@27: assert(sym->vclass==sGLOBAL); dvander@27: func.address=sym->addr; dvander@27: func.nameofs=nameofs; dvander@27: #if BYTE_ORDER==BIG_ENDIAN dvander@27: align32(&func.address); dvander@27: align32(&func.nameofs); dvander@27: #endif dvander@27: pc_resetbin(fout,hdr.publics+count*sizeof(AMX_FUNCSTUBNT)); dvander@27: pc_writebin(fout,&func,sizeof func); dvander@27: pc_resetbin(fout,nameofs); dvander@27: pc_writebin(fout,sym->name,strlen(sym->name)+1); dvander@27: nameofs+=strlen(sym->name)+1; dvander@27: count++; dvander@27: } /* if */ dvander@27: } /* for */ dvander@27: dvander@27: /* write the natives table */ dvander@27: /* The native functions must be written in sorted order. (They are dvander@27: * sorted on their "id", not on their name). A nested loop to find dvander@27: * each successive function would be an O(n^2) operation. But we dvander@27: * do not really need to sort, because the native function id's dvander@27: * are sequential and there are no duplicates. So we first walk dvander@27: * through the complete symbol list and store a pointer to every dvander@27: * native function of interest in a temporary table, where its id dvander@27: * serves as the index in the table. Now we can walk the table and dvander@27: * have all native functions in sorted order. dvander@27: */ dvander@27: if (numnatives>0) { dvander@27: nativelist=(symbol **)malloc(numnatives*sizeof(symbol *)); dvander@27: if (nativelist==NULL) dvander@27: error(103); /* insufficient memory */ dvander@27: #if !defined NDEBUG dvander@27: memset(nativelist,0,numnatives*sizeof(symbol *)); /* for NULL checking */ dvander@27: #endif dvander@27: for (sym=glbtab.next; sym!=NULL; sym=sym->next) { dvander@27: if (sym->ident==iFUNCTN && (sym->usage & uNATIVE)!=0 && (sym->usage & uREAD)!=0 && sym->addr>=0) { dvander@27: assert(sym->addr < numnatives); dvander@27: nativelist[(int)sym->addr]=sym; dvander@27: } /* if */ dvander@27: } /* for */ dvander@27: count=0; dvander@27: for (i=0; iname)) { dvander@27: assert(strlen(sym->name)<=sNAMEMAX); dvander@27: strcpy(alias,sym->name); dvander@27: } /* if */ dvander@27: assert(sym->vclass==sGLOBAL); dvander@27: func.address=0; dvander@27: func.nameofs=nameofs; dvander@27: #if BYTE_ORDER==BIG_ENDIAN dvander@27: align32(&func.address); dvander@27: align32(&func.nameofs); dvander@27: #endif dvander@27: pc_resetbin(fout,hdr.natives+count*sizeof(AMX_FUNCSTUBNT)); dvander@27: pc_writebin(fout,&func,sizeof func); dvander@27: pc_resetbin(fout,nameofs); dvander@27: pc_writebin(fout,alias,strlen(alias)+1); dvander@27: nameofs+=strlen(alias)+1; dvander@27: count++; dvander@27: } /* for */ dvander@27: free(nativelist); dvander@27: } /* if */ dvander@27: dvander@27: /* write the libraries table */ dvander@27: if (pc_addlibtable) { dvander@27: count=0; dvander@27: for (constptr=libname_tab.next; constptr!=NULL; constptr=constptr->next) { dvander@27: if (constptr->value>0) { dvander@27: assert(strlen(constptr->name)>0); dvander@27: func.address=0; dvander@27: func.nameofs=nameofs; dvander@27: #if BYTE_ORDER==BIG_ENDIAN dvander@27: align32(&func.address); dvander@27: align32(&func.nameofs); dvander@27: #endif dvander@27: pc_resetbin(fout,hdr.libraries+count*sizeof(AMX_FUNCSTUBNT)); dvander@27: pc_writebin(fout,&func,sizeof func); dvander@27: pc_resetbin(fout,nameofs); dvander@27: pc_writebin(fout,constptr->name,strlen(constptr->name)+1); dvander@27: nameofs+=strlen(constptr->name)+1; dvander@27: count++; dvander@27: } /* if */ dvander@27: } /* for */ dvander@27: } /* if */ dvander@27: dvander@27: /* write the public variables table */ dvander@27: count=0; dvander@27: for (sym=glbtab.next; sym!=NULL; sym=sym->next) { dvander@27: if (sym->ident==iVARIABLE && (sym->usage & uPUBLIC)!=0 && (sym->usage & (uREAD | uWRITTEN))!=0) { dvander@27: assert((sym->usage & uDEFINE)!=0); dvander@27: assert(sym->vclass==sGLOBAL); dvander@27: func.address=sym->addr; dvander@27: func.nameofs=nameofs; dvander@27: #if BYTE_ORDER==BIG_ENDIAN dvander@27: align32(&func.address); dvander@27: align32(&func.nameofs); dvander@27: #endif dvander@27: pc_resetbin(fout,hdr.pubvars+count*sizeof(AMX_FUNCSTUBNT)); dvander@27: pc_writebin(fout,&func,sizeof func); dvander@27: pc_resetbin(fout,nameofs); dvander@27: pc_writebin(fout,sym->name,strlen(sym->name)+1); dvander@27: nameofs+=strlen(sym->name)+1; dvander@27: count++; dvander@27: } /* if */ dvander@27: } /* for */ dvander@27: dvander@27: /* write the public tagnames table */ dvander@27: count=0; dvander@27: for (constptr=tagname_tab.next; constptr!=NULL; constptr=constptr->next) { dvander@27: if ((constptr->value & PUBLICTAG)!=0) { dvander@27: assert(strlen(constptr->name)>0); dvander@27: func.address=constptr->value & TAGMASK; dvander@27: func.nameofs=nameofs; dvander@27: #if BYTE_ORDER==BIG_ENDIAN dvander@27: align32(&func.address); dvander@27: align32(&func.nameofs); dvander@27: #endif dvander@27: pc_resetbin(fout,hdr.tags+count*sizeof(AMX_FUNCSTUBNT)); dvander@27: pc_writebin(fout,&func,sizeof func); dvander@27: pc_resetbin(fout,nameofs); dvander@27: pc_writebin(fout,constptr->name,strlen(constptr->name)+1); dvander@27: nameofs+=strlen(constptr->name)+1; dvander@27: count++; dvander@27: } /* if */ dvander@27: } /* for */ dvander@27: dvander@27: /* write the "maximum name length" field in the name table */ dvander@27: assert(nameofs==hdr.nametable+nametablesize); dvander@27: pc_resetbin(fout,hdr.nametable); dvander@27: count=sNAMEMAX; dvander@27: #if BYTE_ORDER==BIG_ENDIAN dvander@27: align16(&count); dvander@27: #endif dvander@27: pc_writebin(fout,&count,sizeof count); dvander@27: pc_resetbin(fout,hdr.cod); dvander@27: dvander@27: /* First pass: relocate all labels */ dvander@27: /* This pass is necessary because the code addresses of labels is only known dvander@27: * after the peephole optimization flag. Labels can occur inside expressions dvander@27: * (e.g. the conditional operator), which are optimized. dvander@27: */ dvander@27: lbltab=NULL; dvander@27: if (sc_labnum>0) { dvander@27: /* only very short programs have zero labels; no first pass is needed dvander@27: * if there are no labels */ dvander@27: lbltab=(cell *)malloc(sc_labnum*sizeof(cell)); dvander@27: if (lbltab==NULL) dvander@27: error(103); /* insufficient memory */ dvander@27: codeindex=0; dvander@27: pc_resetasm(fin); dvander@27: while (pc_readasm(fin,line,sizeof line)!=NULL) { dvander@27: stripcomment(line); dvander@27: instr=skipwhitespace(line); dvander@27: /* ignore empty lines */ dvander@27: if (*instr=='\0') dvander@27: continue; dvander@27: if (tolower(*instr)=='l' && *(instr+1)=='.') { dvander@27: int lindex=(int)hex2long(instr+2,NULL); dvander@27: assert(lindex>=0 && lindexinstr); dvander@27: i=findopcode(instr,(int)(params-instr)); dvander@27: if (opcodelist[i].name==NULL) { dvander@27: *params='\0'; dvander@27: error(104,instr); /* invalid assembler instruction */ dvander@27: } /* if */ dvander@27: if (opcodelist[i].segment==sIN_CSEG) dvander@27: codeindex+=opcodelist[i].func(NULL,skipwhitespace(params),opcodelist[i].opcode); dvander@27: } /* if */ dvander@27: } /* while */ dvander@27: } /* if */ dvander@27: dvander@27: /* Second pass (actually 2 more passes, one for all code and one for all data) */ dvander@27: bytes_in=0; dvander@27: bytes_out=0; dvander@27: for (pass=sIN_CSEG; pass<=sIN_DSEG; pass++) { dvander@27: pc_resetasm(fin); dvander@27: while (pc_readasm(fin,line,sizeof line)!=NULL) { dvander@27: stripcomment(line); dvander@27: instr=skipwhitespace(line); dvander@27: /* ignore empty lines and labels (labels have a special syntax, so these dvander@27: * must be parsed separately) */ dvander@27: if (*instr=='\0' || tolower(*instr)=='l' && *(instr+1)=='.') dvander@27: continue; dvander@27: /* get to the end of the instruction (make use of the '\n' that fgets() dvander@27: * added at the end of the line; this way we will *always* drop on a dvander@27: * whitespace character) */ dvander@27: for (params=instr; *params!='\0' && !isspace(*params); params++) dvander@27: /* nothing */; dvander@27: assert(params>instr); dvander@27: i=findopcode(instr,(int)(params-instr)); dvander@27: assert(opcodelist[i].name!=NULL); dvander@27: if (opcodelist[i].segment==pass) dvander@27: opcodelist[i].func(fout,skipwhitespace(params),opcodelist[i].opcode); dvander@27: } /* while */ dvander@27: } /* for */ dvander@27: if (bytes_out-bytes_in>0) dvander@27: error(106); /* compression buffer overflow */ dvander@27: dvander@27: if (lbltab!=NULL) { dvander@27: free(lbltab); dvander@27: #if !defined NDEBUG dvander@27: lbltab=NULL; dvander@27: #endif dvander@27: } /* if */ dvander@27: dvander@27: if (sc_compress) dvander@27: hdr.size=pc_lengthbin(fout);/* get this value before appending debug info */ dvander@27: if (!writeerror && (sc_debug & sSYMBOLIC)!=0) dvander@27: append_dbginfo(fout); /* optionally append debug file */ dvander@27: dvander@27: if (writeerror) dvander@27: error(101,"disk full"); dvander@27: dvander@27: /* adjust the header */ dvander@27: size=(int)hdr.cod; /* save, the value in the header may be swapped */ dvander@27: #if BYTE_ORDER==BIG_ENDIAN dvander@27: align32(&hdr.size); dvander@27: align16(&hdr.magic); dvander@27: align16(&hdr.flags); dvander@27: align16(&hdr.defsize); dvander@27: align32(&hdr.publics); dvander@27: align32(&hdr.natives); dvander@27: align32(&hdr.libraries); dvander@27: align32(&hdr.pubvars); dvander@27: align32(&hdr.tags); dvander@27: align32(&hdr.nametable); dvander@27: align32(&hdr.cod); dvander@27: align32(&hdr.dat); dvander@27: align32(&hdr.hea); dvander@27: align32(&hdr.stp); dvander@27: align32(&hdr.cip); dvander@27: #endif dvander@27: pc_resetbin(fout,0); dvander@27: pc_writebin(fout,&hdr,sizeof hdr); dvander@27: dvander@27: /* return the size of the header (including name tables, but excluding code dvander@27: * or data sections) dvander@27: */ dvander@27: return size; dvander@27: } dvander@27: dvander@27: static void append_dbginfo(FILE *fout) dvander@27: { dvander@27: AMX_DBG_HDR dbghdr; dvander@27: AMX_DBG_LINE dbgline; dvander@27: AMX_DBG_SYMBOL dbgsym; dvander@27: AMX_DBG_SYMDIM dbgidxtag[sDIMEN_MAX]; dvander@27: int index,dim,dbgsymdim; dvander@27: char *str,*prevstr,*name,*prevname; dvander@27: ucell codeidx,previdx; dvander@27: constvalue *constptr; dvander@27: char symname[2*sNAMEMAX+16]; dvander@27: int16_t id1,id2; dvander@27: ucell address; dvander@27: dvander@27: /* header with general information */ dvander@27: memset(&dbghdr, 0, sizeof dbghdr); dvander@27: dbghdr.size=sizeof dbghdr; dvander@27: dbghdr.magic=AMX_DBG_MAGIC; dvander@27: dbghdr.file_version=CUR_FILE_VERSION; dvander@27: dbghdr.amx_version=MIN_AMX_VERSION; dvander@27: dvander@27: /* first pass: collect the number of items in various tables */ dvander@27: dvander@27: /* file table */ dvander@27: previdx=0; dvander@27: prevstr=NULL; dvander@27: prevname=NULL; dvander@27: for (index=0; (str=get_dbgstring(index))!=NULL; index++) { dvander@27: assert(str!=NULL); dvander@27: assert(str[0]!='\0' && str[1]==':'); dvander@27: if (str[0]=='F') { dvander@27: codeidx=hex2long(str+2,&name); dvander@27: if (codeidx!=previdx) { dvander@27: if (prevstr!=NULL) { dvander@27: assert(prevname!=NULL); dvander@27: dbghdr.files++; dvander@27: dbghdr.size+=sizeof(cell)+strlen(prevname)+1; dvander@27: } /* if */ dvander@27: previdx=codeidx; dvander@27: } /* if */ dvander@27: prevstr=str; dvander@27: prevname=skipwhitespace(name); dvander@27: } /* if */ dvander@27: } /* for */ dvander@27: if (prevstr!=NULL) { dvander@27: assert(prevname!=NULL); dvander@27: dbghdr.files++; dvander@27: dbghdr.size+=sizeof(cell)+strlen(prevname)+1; dvander@27: } /* if */ dvander@27: dvander@27: /* line number table */ dvander@27: for (index=0; (str=get_dbgstring(index))!=NULL; index++) { dvander@27: assert(str!=NULL); dvander@27: assert(str[0]!='\0' && str[1]==':'); dvander@27: if (str[0]=='L') { dvander@27: dbghdr.lines++; dvander@27: dbghdr.size+=sizeof(AMX_DBG_LINE); dvander@27: } /* if */ dvander@27: } /* for */ dvander@27: dvander@27: /* symbol table */ dvander@27: for (index=0; (str=get_dbgstring(index))!=NULL; index++) { dvander@27: assert(str!=NULL); dvander@27: assert(str[0]!='\0' && str[1]==':'); dvander@27: if (str[0]=='S') { dvander@27: dbghdr.symbols++; dvander@27: name=strchr(str+2,':'); dvander@27: assert(name!=NULL); dvander@27: dbghdr.size+=sizeof(AMX_DBG_SYMBOL)+strlen(skipwhitespace(name+1)); dvander@27: if ((prevstr=strchr(name,'['))!=NULL) dvander@27: while ((prevstr=strchr(prevstr+1,':'))!=NULL) dvander@27: dbghdr.size+=sizeof(AMX_DBG_SYMDIM); dvander@27: } /* if */ dvander@27: } /* for */ dvander@27: dvander@27: /* tag table */ dvander@27: for (constptr=tagname_tab.next; constptr!=NULL; constptr=constptr->next) { dvander@27: assert(strlen(constptr->name)>0); dvander@27: dbghdr.tags++; dvander@27: dbghdr.size+=sizeof(AMX_DBG_TAG)+strlen(constptr->name); dvander@27: } /* for */ dvander@27: dvander@27: /* automaton table */ dvander@27: for (constptr=sc_automaton_tab.next; constptr!=NULL; constptr=constptr->next) { dvander@27: assert(constptr->index==0 && strlen(constptr->name)==0 || strlen(constptr->name)>0); dvander@27: dbghdr.automatons++; dvander@27: dbghdr.size+=sizeof(AMX_DBG_MACHINE)+strlen(constptr->name); dvander@27: } /* for */ dvander@27: dvander@27: /* state table */ dvander@27: for (constptr=sc_state_tab.next; constptr!=NULL; constptr=constptr->next) { dvander@27: assert(strlen(constptr->name)>0); dvander@27: dbghdr.states++; dvander@27: dbghdr.size+=sizeof(AMX_DBG_STATE)+strlen(constptr->name); dvander@27: } /* for */ dvander@27: dvander@27: dvander@27: /* pass 2: generate the tables */ dvander@27: #if BYTE_ORDER==BIG_ENDIAN dvander@27: align32((uint32_t*)&dbghdr.size); dvander@27: align16(&dbghdr.magic); dvander@27: align16(&dbghdr.flags); dvander@27: align16(&dbghdr.files); dvander@27: align16(&dbghdr.lines); dvander@27: align16(&dbghdr.symbols); dvander@27: align16(&dbghdr.tags); dvander@27: align16(&dbghdr.automatons); dvander@27: align16(&dbghdr.states); dvander@27: #endif dvander@27: writeerror |= !pc_writebin(fout,&dbghdr,sizeof dbghdr); dvander@27: dvander@27: /* file table */ dvander@27: previdx=0; dvander@27: prevstr=NULL; dvander@27: prevname=NULL; dvander@27: for (index=0; (str=get_dbgstring(index))!=NULL; index++) { dvander@27: assert(str!=NULL); dvander@27: assert(str[0]!='\0' && str[1]==':'); dvander@27: if (str[0]=='F') { dvander@27: codeidx=hex2long(str+2,&name); dvander@27: if (codeidx!=previdx) { dvander@27: if (prevstr!=NULL) { dvander@27: assert(prevname!=NULL); dvander@27: #if BYTE_ORDER==BIG_ENDIAN dvander@27: aligncell(&previdx); dvander@27: #endif dvander@27: writeerror |= !pc_writebin(fout,&previdx,sizeof previdx); dvander@27: writeerror |= !pc_writebin(fout,prevname,strlen(prevname)+1); dvander@27: } /* if */ dvander@27: previdx=codeidx; dvander@27: } /* if */ dvander@27: prevstr=str; dvander@27: prevname=skipwhitespace(name); dvander@27: } /* if */ dvander@27: } /* for */ dvander@27: if (prevstr!=NULL) { dvander@27: assert(prevname!=NULL); dvander@27: #if BYTE_ORDER==BIG_ENDIAN dvander@27: aligncell(&previdx); dvander@27: #endif dvander@27: writeerror |= !pc_writebin(fout,&previdx,sizeof previdx); dvander@27: writeerror |= !pc_writebin(fout,prevname,strlen(prevname)+1); dvander@27: } /* if */ dvander@27: dvander@27: /* line number table */ dvander@27: for (index=0; (str=get_dbgstring(index))!=NULL; index++) { dvander@27: assert(str!=NULL); dvander@27: assert(str[0]!='\0' && str[1]==':'); dvander@27: if (str[0]=='L') { dvander@27: dbgline.address=hex2long(str+2,&str); dvander@27: dbgline.line=(int32_t)hex2long(str,NULL); dvander@27: #if BYTE_ORDER==BIG_ENDIAN dvander@27: aligncell(&dbgline.address); dvander@27: align32(&dbgline.line); dvander@27: #endif dvander@27: writeerror |= !pc_writebin(fout,&dbgline,sizeof dbgline); dvander@27: } /* if */ dvander@27: } /* for */ dvander@27: dvander@27: /* symbol table */ dvander@27: for (index=0; (str=get_dbgstring(index))!=NULL; index++) { dvander@27: assert(str!=NULL); dvander@27: assert(str[0]!='\0' && str[1]==':'); dvander@27: if (str[0]=='S') { dvander@27: dbgsym.address=hex2long(str+2,&str); dvander@27: dbgsym.tag=(int16_t)hex2long(str,&str); dvander@27: str=skipwhitespace(str); dvander@27: assert(*str==':'); dvander@27: name=skipwhitespace(str+1); dvander@27: str=strchr(name,' '); dvander@27: assert(str!=NULL); dvander@27: assert((int)(str-name)next) { dvander@27: assert(strlen(constptr->name)>0); dvander@27: id1=(int16_t)(constptr->value & TAGMASK); dvander@27: #if BYTE_ORDER==BIG_ENDIAN dvander@27: align16(&id1); dvander@27: #endif dvander@27: writeerror |= !pc_writebin(fout,&id1,sizeof id1); dvander@27: writeerror |= !pc_writebin(fout,constptr->name,strlen(constptr->name)+1); dvander@27: } /* for */ dvander@27: dvander@27: /* automaton table */ dvander@27: for (constptr=sc_automaton_tab.next; constptr!=NULL; constptr=constptr->next) { dvander@27: assert(constptr->index==0 && strlen(constptr->name)==0 || strlen(constptr->name)>0); dvander@27: id1=(int16_t)constptr->index; dvander@27: address=(ucell)constptr->value; dvander@27: #if BYTE_ORDER==BIG_ENDIAN dvander@27: align16(&id1); dvander@27: aligncell(&address); dvander@27: #endif dvander@27: writeerror |= !pc_writebin(fout,&id1,sizeof id1); dvander@27: writeerror |= !pc_writebin(fout,&address,sizeof address); dvander@27: writeerror |= !pc_writebin(fout,constptr->name,strlen(constptr->name)+1); dvander@27: } /* for */ dvander@27: dvander@27: /* state table */ dvander@27: for (constptr=sc_state_tab.next; constptr!=NULL; constptr=constptr->next) { dvander@27: assert(strlen(constptr->name)>0); dvander@27: id1=(int16_t)constptr->value; dvander@27: id2=(int16_t)constptr->index; dvander@27: address=(ucell)constptr->value; dvander@27: #if BYTE_ORDER==BIG_ENDIAN dvander@27: align16(&id1); dvander@27: align16(&id2); dvander@27: #endif dvander@27: writeerror |= !pc_writebin(fout,&id1,sizeof id1); dvander@27: writeerror |= !pc_writebin(fout,&id2,sizeof id2); dvander@27: writeerror |= !pc_writebin(fout,constptr->name,strlen(constptr->name)+1); dvander@27: } /* for */ dvander@27: dvander@27: delete_dbgstringtable(); dvander@27: }