amxmodx/compiler/libpc300/sc6.c
Arkshine 52c73126e1 VFS: Fix various things
- The "ALL" fake pathID is replaced by what does SM, having a public var NULL_STRING which will acts as NULL when needed.
  To make compiler accepting public array, this patch was needed: https://hg.alliedmods.net/sourcemod-central/rev/b12f329def09
- The offset thing in read_dir: considering that's something very specific to this native and that implementation in CDirectory doesn't make sense because of the offset compatibility for windows, all code is now in the native.
2015-03-25 13:50:07 +01:00

1165 lines
36 KiB
C
Executable File

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