/* Pawn Abstract Machine (for the Pawn language) * * 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. */ #if BUILD_PLATFORM == WINDOWS && BUILD_TYPE == RELEASE && BUILD_COMPILER == MSVC && PAWN_CELL_SIZE == 64 /* bad bad workaround but we have to prevent a compiler crash :/ */ #pragma optimize("g",off) #endif #define WIN32_LEAN_AND_MEAN #if defined _UNICODE || defined __UNICODE__ || defined UNICODE # if !defined UNICODE /* for Windows API */ # define UNICODE # endif # if !defined _UNICODE /* for C library */ # define _UNICODE # endif #endif #include #include #include #include /* for wchar_t */ #include #include "osdefs.h" #if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ || defined __APPLE__ #include #if !defined AMX_NODYNALOAD #include #endif #if defined JIT #include #include #endif #endif #if defined __LCC__ #include /* for wcslen() */ #endif #include "amx.h" #if (defined _Windows && !defined AMX_NODYNALOAD) || (defined JIT && __WIN32__) #include #endif /* When one or more of the AMX_funcname macris are defined, we want * to compile only those functions. However, when none of these macros * is present, we want to compile everything. */ #if defined AMX_ALIGN || defined AMX_ALLOT || defined AMX_CLEANUP #define AMX_EXPLIT_FUNCTIONS #endif #if defined AMX_CLONE || defined AMX_EXEC || defined AMX_FLAGS #define AMX_EXPLIT_FUNCTIONS #endif #if defined AMX_GETADDR || defined AMX_INIT || defined AMX_MEMINFO #define AMX_EXPLIT_FUNCTIONS #endif #if defined AMX_NAMELENGTH || defined AMX_NATIVEINFO || defined AMX_RAISEERROR #define AMX_EXPLIT_FUNCTIONS #endif #if defined AMX_REGISTER || defined AMX_SETCALLBACK || defined AMX_SETDEBUGHOOK #define AMX_EXPLIT_FUNCTIONS #endif #if defined AMX_XXXNATIVES || defined AMX_XXXPUBLICS || defined AMX_XXXPUBVARS #define AMX_EXPLIT_FUNCTIONS #endif #if defined AMX_XXXSTRING || defined AMX_XXXTAGS || defined AMX_XXXUSERDATA #define AMX_EXPLIT_FUNCTIONS #endif #if defined AMX_UTF8XXX #define AMX_EXPLIT_FUNCTIONS #endif #if !defined AMX_EXPLIT_FUNCTIONS /* no constant set, set them all */ #define AMX_ALIGN /* amx_Align16(), amx_Align32() and amx_Align64() */ #define AMX_ALLOT /* amx_Allot() and amx_Release() */ #define AMX_CLEANUP /* amx_Cleanup() */ #define AMX_CLONE /* amx_Clone() */ #define AMX_EXEC /* amx_Exec() plus amx_Push(), amx_PushArray() and amx_PushString() */ #define AMX_FLAGS /* amx_Flags() */ #define AMX_GETADDR /* amx_GetAddr() */ #define AMX_INIT /* amx_Init() and amx_InitJIT() */ #define AMX_MEMINFO /* amx_MemInfo() */ #define AMX_NAMELENGTH /* amx_NameLength() */ #define AMX_NATIVEINFO /* amx_NativeInfo() */ #define AMX_RAISEERROR /* amx_RaiseError() */ #define AMX_REGISTER /* amx_Register() */ #define AMX_SETCALLBACK /* amx_SetCallback() */ #define AMX_SETDEBUGHOOK /* amx_SetDebugHook() */ #define AMX_XXXNATIVES /* amx_NumNatives(), amx_GetNative() and amx_FindNative() */ #define AMX_XXXPUBLICS /* amx_NumPublics(), amx_GetPublic() and amx_FindPublic() */ #define AMX_XXXPUBVARS /* amx_NumPubVars(), amx_GetPubVar() and amx_FindPubVar() */ #define AMX_XXXSTRING /* amx_StrLen(), amx_GetString() and amx_SetString() */ #define AMX_XXXTAGS /* amx_NumTags(), amx_GetTag() and amx_FindTagId() */ #define AMX_XXXUSERDATA /* amx_GetUserData() and amx_SetUserData() */ #define AMX_UTF8XXX /* amx_UTF8Get(), amx_UTF8Put(), amx_UTF8Check() */ #endif #undef AMX_EXPLIT_FUNCTIONS #if defined AMX_ANSIONLY #undef AMX_UTF8XXX /* no UTF-8 support in ANSI/ASCII-only version */ #endif typedef enum { OP_NONE, /* invalid opcode */ OP_LOAD_PRI, OP_LOAD_ALT, OP_LOAD_S_PRI, OP_LOAD_S_ALT, OP_LREF_PRI, OP_LREF_ALT, OP_LREF_S_PRI, OP_LREF_S_ALT, OP_LOAD_I, OP_LODB_I, OP_CONST_PRI, OP_CONST_ALT, OP_ADDR_PRI, OP_ADDR_ALT, OP_STOR_PRI, OP_STOR_ALT, OP_STOR_S_PRI, OP_STOR_S_ALT, OP_SREF_PRI, OP_SREF_ALT, OP_SREF_S_PRI, OP_SREF_S_ALT, OP_STOR_I, OP_STRB_I, OP_LIDX, OP_LIDX_B, OP_IDXADDR, OP_IDXADDR_B, OP_ALIGN_PRI, OP_ALIGN_ALT, OP_LCTRL, OP_SCTRL, OP_MOVE_PRI, OP_MOVE_ALT, OP_XCHG, OP_PUSH_PRI, OP_PUSH_ALT, OP_PUSH_R, OP_PUSH_C, OP_PUSH, OP_PUSH_S, OP_POP_PRI, OP_POP_ALT, OP_STACK, OP_HEAP, OP_PROC, OP_RET, OP_RETN, OP_CALL, OP_CALL_PRI, OP_JUMP, OP_JREL, OP_JZER, OP_JNZ, OP_JEQ, OP_JNEQ, OP_JLESS, OP_JLEQ, OP_JGRTR, OP_JGEQ, OP_JSLESS, OP_JSLEQ, OP_JSGRTR, OP_JSGEQ, OP_SHL, OP_SHR, OP_SSHR, OP_SHL_C_PRI, OP_SHL_C_ALT, OP_SHR_C_PRI, OP_SHR_C_ALT, OP_SMUL, OP_SDIV, OP_SDIV_ALT, OP_UMUL, OP_UDIV, OP_UDIV_ALT, OP_ADD, OP_SUB, OP_SUB_ALT, OP_AND, OP_OR, OP_XOR, OP_NOT, OP_NEG, OP_INVERT, OP_ADD_C, OP_SMUL_C, OP_ZERO_PRI, OP_ZERO_ALT, OP_ZERO, OP_ZERO_S, OP_SIGN_PRI, OP_SIGN_ALT, OP_EQ, OP_NEQ, OP_LESS, OP_LEQ, OP_GRTR, OP_GEQ, OP_SLESS, OP_SLEQ, OP_SGRTR, OP_SGEQ, OP_EQ_C_PRI, OP_EQ_C_ALT, OP_INC_PRI, OP_INC_ALT, OP_INC, OP_INC_S, OP_INC_I, OP_DEC_PRI, OP_DEC_ALT, OP_DEC, OP_DEC_S, OP_DEC_I, OP_MOVS, OP_CMPS, OP_FILL, OP_HALT, OP_BOUNDS, OP_SYSREQ_PRI, OP_SYSREQ_C, OP_FILE, /* obsolete */ OP_LINE, /* obsolete */ OP_SYMBOL, /* obsolete */ OP_SRANGE, /* obsolete */ OP_JUMP_PRI, OP_SWITCH, OP_CASETBL, OP_SWAP_PRI, OP_SWAP_ALT, OP_PUSHADDR, OP_NOP, OP_SYSREQ_D, OP_SYMTAG, /* obsolete */ OP_BREAK, /* ----- */ OP_NUM_OPCODES } OPCODE; #define USENAMETABLE(hdr) \ ((hdr)->defsize==sizeof(AMX_FUNCSTUBNT)) #define NUMENTRIES(hdr,field,nextfield) \ (unsigned)(((hdr)->nextfield - (hdr)->field) / (hdr)->defsize) #define GETENTRY(hdr,table,index) \ (AMX_FUNCSTUB *)((unsigned char*)(hdr) + (unsigned)(hdr)->table + (unsigned)index*(hdr)->defsize) #define GETENTRYNAME(hdr,entry) \ ( USENAMETABLE(hdr) \ ? (char *)((unsigned char*)(hdr) + (unsigned)((AMX_FUNCSTUBNT*)(entry))->nameofs) \ : ((AMX_FUNCSTUB*)(entry))->name ) #if !defined NDEBUG static int check_endian(void) { uint16_t val=0x00ff; unsigned char *ptr=(unsigned char *)&val; /* "ptr" points to the starting address of "val". If that address * holds the byte "0xff", the computer stored the low byte of "val" * at the lower address, and so the memory lay out is Little Endian. */ assert(*ptr==0xff || *ptr==0x00); #if BYTE_ORDER==BIG_ENDIAN return *ptr==0x00; /* return "true" if big endian */ #else return *ptr==0xff; /* return "true" if little endian */ #endif } #endif #if BYTE_ORDER==BIG_ENDIAN || PAWN_CELL_SIZE==16 static void swap16(uint16_t *v) { unsigned char *s = (unsigned char *)v; unsigned char t; assert(sizeof(*v)==2); /* swap two bytes */ t=s[0]; s[0]=s[1]; s[1]=t; } #endif #if BYTE_ORDER==BIG_ENDIAN || PAWN_CELL_SIZE==32 static void swap32(uint32_t *v) { unsigned char *s = (unsigned char *)v; unsigned char t; assert(sizeof(*v)==4); /* 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; } #endif #if (BYTE_ORDER==BIG_ENDIAN || PAWN_CELL_SIZE==64) && (defined _I64_MAX || defined HAVE_I64) static void swap64(uint64_t *v) { unsigned char *s = (unsigned char *)v; unsigned char t; assert(sizeof(*v)==8); 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; } #endif #if defined AMX_ALIGN || defined AMX_INIT uint16_t * AMXAPI amx_Align16(uint16_t *v) { assert(sizeof(*v)==2); assert(check_endian()); #if BYTE_ORDER==BIG_ENDIAN swap16(v); #endif return v; } uint32_t * AMXAPI amx_Align32(uint32_t *v) { assert(sizeof(*v)==4); assert(check_endian()); #if BYTE_ORDER==BIG_ENDIAN swap32(v); #endif return v; } #if defined _I64_MAX || defined HAVE_I64 uint64_t * AMXAPI amx_Align64(uint64_t *v) { assert(sizeof(*v)==8); assert(check_endian()); #if BYTE_ORDER==BIG_ENDIAN swap64(v); #endif return v; } #endif /* _I64_MAX || HAVE_I64 */ #endif /* AMX_ALIGN || AMX_INIT */ #if PAWN_CELL_SIZE==16 #define swapcell swap16 #elif PAWN_CELL_SIZE==32 #define swapcell swap32 #elif PAWN_CELL_SIZE==64 && (defined _I64_MAX || defined HAVE_I64) #define swapcell swap64 #else #error Unsupported cell size #endif #if defined AMX_FLAGS int AMXAPI amx_Flags(AMX *amx,uint16_t *flags) { AMX_HEADER *hdr; *flags=0; if (amx==NULL) return AMX_ERR_FORMAT; hdr=(AMX_HEADER *)amx->base; if (hdr->magic!=AMX_MAGIC) return AMX_ERR_FORMAT; if (hdr->file_version>CUR_FILE_VERSION || hdr->amx_versionflags; return AMX_ERR_NONE; } #endif /* AMX_FLAGS */ #if defined AMX_INIT int AMXAPI amx_Callback(AMX *amx, cell index, cell *result, cell *params) { AMX_HEADER *hdr; AMX_FUNCSTUB *func; AMX_NATIVE f; assert(amx!=NULL); hdr=(AMX_HEADER *)amx->base; assert(hdr!=NULL); assert(hdr->magic==AMX_MAGIC); assert(hdr->natives<=hdr->libraries); #if defined AMX_NATIVETABLE if (index=0 && index<(cell)NUMENTRIES(hdr,natives,libraries)); func=GETENTRY(hdr,natives,index); f=(AMX_NATIVE)func->address; #if defined AMX_NATIVETABLE } /* if */ #endif assert(f!=NULL); /* Now that we have found the function, patch the program so that any * subsequent call will call the function directly (bypassing this * callback). * This trick cannot work in the JIT, because the program would need to * be re-JIT-compiled after patching a P-code instruction. */ #if defined JIT && !defined NDEBUG if ((amx->flags & AMX_FLAG_JITC)!=0) assert(amx->sysreq_d==0); #endif if (amx->sysreq_d!=0) { /* at the point of the call, the CIP pseudo-register points directly * behind the SYSREQ instruction and its parameter. */ unsigned char *code=amx->base+(int)hdr->cod+(int)amx->cip-4; assert(amx->cip >= 4 && amx->cip < (hdr->dat - hdr->cod)); assert(sizeof(f)<=sizeof(cell)); /* function pointer must fit in a cell */ #if defined __GNUC__ || defined ASM32 if (*(cell*)code==index) { #else if (*(cell*)code!=OP_SYSREQ_PRI) { assert(*(cell*)(code-sizeof(cell))==OP_SYSREQ_C); assert(*(cell*)code==index); #endif *(cell*)(code-sizeof(cell))=amx->sysreq_d; *(cell*)code=(cell)f; } /* if */ } /* if */ /* Note: * params[0] == number of bytes for the additional parameters passed to the native function * params[1] == first argument * etc. */ amx->error=AMX_ERR_NONE; *result = f(amx,params); return amx->error; } #endif /* defined AMX_INIT */ #if defined JIT extern int AMXAPI getMaxCodeSize(void); extern int AMXAPI asm_runJIT(void *sourceAMXbase, void *jumparray, void *compiledAMXbase); #endif #if PAWN_CELL_SIZE==16 #define JUMPABS(base,ip) ((cell *)((base) + *(ip))) #define RELOC_ABS(base, off) #define RELOC_VALUE(base, v) #else #define JUMPABS(base, ip) ((cell *)*(ip)) #define RELOC_ABS(base, off) (*(ucell *)((base)+(int)(off)) += (ucell)(base)) #define RELOC_VALUE(base, v) ((v)+((ucell)(base))) #endif #define DBGPARAM(v) ( (v)=*(cell *)(code+(int)cip), cip+=sizeof(cell) ) #if defined AMX_INIT static int amx_BrowseRelocate(AMX *amx) { AMX_HEADER *hdr; unsigned char *code; cell cip; long codesize; OPCODE op; #if defined __GNUC__ || defined ASM32 || defined JIT cell *opcode_list; #endif #if defined JIT int opcode_count = 0; int reloc_count = 0; #endif assert(amx!=NULL); hdr=(AMX_HEADER *)amx->base; assert(hdr!=NULL); assert(hdr->magic==AMX_MAGIC); code=amx->base+(int)hdr->cod; codesize=hdr->dat - hdr->cod; amx->flags=AMX_FLAG_BROWSE; /* sanity checks */ assert(OP_PUSH_PRI==36); assert(OP_PROC==46); assert(OP_SHL==65); assert(OP_SMUL==72); assert(OP_EQ==95); assert(OP_INC_PRI==107); assert(OP_MOVS==117); assert(OP_SYMBOL==126); amx->sysreq_d=0; /* preset */ #if (defined __GNUC__ || defined ASM32 || defined JIT) && !defined __64BIT__ amx_Exec(amx, (cell*)(void*)&opcode_list, 0); /* to use direct system requests, a function pointer must fit in a cell; * because the native function's address will be stored as the parameter * of SYSREQ.D */ if ((amx->flags & AMX_FLAG_JITC)==0 && sizeof(AMX_NATIVE)<=sizeof(cell)) amx->sysreq_d=opcode_list[OP_SYSREQ_D]; #else /* ANSI C * to use direct system requests, a function pointer must fit in a cell; * see the comment above */ if (sizeof(AMX_NATIVE)<=sizeof(cell)) amx->sysreq_d=OP_SYSREQ_D; #endif /* start browsing code */ for (cip=0; cip0 && op=256) { amx->flags &= ~AMX_FLAG_BROWSE; return AMX_ERR_INVINSTR; } /* if */ #if defined __GNUC__ || defined ASM32 || defined JIT /* relocate opcode (only works if the size of an opcode is at least * as big as the size of a pointer (jump address); so basically we * rely on the opcode and a pointer being 32-bit */ *(cell *)(code+(int)cip) = opcode_list[op]; #endif #if defined JIT opcode_count++; #endif cip+=sizeof(cell); switch (op) { case OP_LOAD_PRI: /* instructions with 1 parameter */ case OP_LOAD_ALT: case OP_LOAD_S_PRI: case OP_LOAD_S_ALT: case OP_LREF_PRI: case OP_LREF_ALT: case OP_LREF_S_PRI: case OP_LREF_S_ALT: case OP_LODB_I: case OP_CONST_PRI: case OP_CONST_ALT: case OP_ADDR_PRI: case OP_ADDR_ALT: case OP_STOR_PRI: case OP_STOR_ALT: case OP_STOR_S_PRI: case OP_STOR_S_ALT: case OP_SREF_PRI: case OP_SREF_ALT: case OP_SREF_S_PRI: case OP_SREF_S_ALT: case OP_STRB_I: case OP_LIDX_B: case OP_IDXADDR_B: case OP_ALIGN_PRI: case OP_ALIGN_ALT: case OP_LCTRL: case OP_SCTRL: case OP_PUSH_R: case OP_PUSH_C: case OP_PUSH: case OP_PUSH_S: case OP_STACK: case OP_HEAP: case OP_JREL: case OP_SHL_C_PRI: case OP_SHL_C_ALT: case OP_SHR_C_PRI: case OP_SHR_C_ALT: case OP_ADD_C: case OP_SMUL_C: case OP_ZERO: case OP_ZERO_S: case OP_EQ_C_PRI: case OP_EQ_C_ALT: case OP_INC: case OP_INC_S: case OP_DEC: case OP_DEC_S: case OP_MOVS: case OP_CMPS: case OP_FILL: case OP_HALT: case OP_BOUNDS: case OP_SYSREQ_C: case OP_PUSHADDR: case OP_SYSREQ_D: cip+=sizeof(cell); break; case OP_LOAD_I: /* instructions without parameters */ case OP_STOR_I: case OP_LIDX: case OP_IDXADDR: case OP_MOVE_PRI: case OP_MOVE_ALT: case OP_XCHG: case OP_PUSH_PRI: case OP_PUSH_ALT: case OP_POP_PRI: case OP_POP_ALT: case OP_PROC: case OP_RET: case OP_RETN: case OP_CALL_PRI: case OP_SHL: case OP_SHR: case OP_SSHR: case OP_SMUL: case OP_SDIV: case OP_SDIV_ALT: case OP_UMUL: case OP_UDIV: case OP_UDIV_ALT: case OP_ADD: case OP_SUB: case OP_SUB_ALT: case OP_AND: case OP_OR: case OP_XOR: case OP_NOT: case OP_NEG: case OP_INVERT: case OP_ZERO_PRI: case OP_ZERO_ALT: case OP_SIGN_PRI: case OP_SIGN_ALT: case OP_EQ: case OP_NEQ: case OP_LESS: case OP_LEQ: case OP_GRTR: case OP_GEQ: case OP_SLESS: case OP_SLEQ: case OP_SGRTR: case OP_SGEQ: case OP_INC_PRI: case OP_INC_ALT: case OP_INC_I: case OP_DEC_PRI: case OP_DEC_ALT: case OP_DEC_I: case OP_SYSREQ_PRI: case OP_JUMP_PRI: case OP_SWAP_PRI: case OP_SWAP_ALT: case OP_NOP: case OP_BREAK: break; case OP_CALL: /* opcodes that need relocation */ case OP_JUMP: case OP_JZER: case OP_JNZ: case OP_JEQ: case OP_JNEQ: case OP_JLESS: case OP_JLEQ: case OP_JGRTR: case OP_JGEQ: case OP_JSLESS: case OP_JSLEQ: case OP_JSGRTR: case OP_JSGEQ: case OP_SWITCH: #if defined JIT reloc_count++; #endif RELOC_ABS(code, cip); cip+=sizeof(cell); break; case OP_FILE: case OP_SYMBOL: { cell num; DBGPARAM(num); cip+=num; break; } /* case */ case OP_LINE: case OP_SRANGE: cip+=2*sizeof(cell); break; case OP_SYMTAG: cip+=sizeof(cell); break; case OP_CASETBL: { cell num; int i; DBGPARAM(num); /* number of records follows the opcode */ for (i=0; i<=num; i++) { RELOC_ABS(code, cip+2*i*sizeof(cell)); #if defined JIT reloc_count++; #endif } /* for */ cip+=(2*num + 1)*sizeof(cell); break; } /* case */ default: amx->flags &= ~AMX_FLAG_BROWSE; return AMX_ERR_INVINSTR; } /* switch */ } /* for */ #if defined JIT amx->code_size = getMaxCodeSize()*opcode_count + hdr->cod + (hdr->stp - hdr->dat); amx->reloc_size = 2*sizeof(cell)*reloc_count; #endif amx->flags &= ~AMX_FLAG_BROWSE; amx->flags |= AMX_FLAG_RELOC; return AMX_ERR_NONE; } #if AMX_COMPACTMARGIN > 2 static void expand(unsigned char *code, long codesize, long memsize) { ucell c; struct { long memloc; ucell c; } spare[AMX_COMPACTMARGIN]; int sh=0,st=0,sc=0; int shift; /* for in-place expansion, move from the end backward */ assert(memsize % sizeof(cell) == 0); while (codesize>0) { c=0; shift=0; do { codesize--; /* no input byte should be shifted out completely */ assert(size_t(shift)<8*sizeof(cell)); /* we work from the end of a sequence backwards; the final code in * a sequence may not have the continuation bit set */ assert(shift>0 || (code[(size_t)codesize] & 0x80)==0); c|=(ucell)(code[(size_t)codesize] & 0x7f) << shift; shift+=7; } while (codesize>0 && (code[(size_t)codesize-1] & 0x80)!=0); /* sign expand */ if ((code[(size_t)codesize] & 0x40)!=0) { while (shift < (int)(8*sizeof(cell))) { c|=(ucell)0xff << shift; shift+=8; } /* while */ } /* if */ /* store */ while (sc && (spare[sh].memloc>codesize)) { *(ucell *)(code+(int)spare[sh].memloc)=spare[sh].c; sh=(sh+1)%AMX_COMPACTMARGIN; sc--; } /* while */ memsize -= sizeof(cell); assert(memsize>=0); if ((memsize>codesize)||((memsize==codesize)&&(memsize==0))) { *(ucell *)(code+(size_t)memsize)=c; } else { assert(scflags & AMX_FLAG_RELOC)!=0) return AMX_ERR_INIT; /* already initialized (may not do so twice) */ hdr=(AMX_HEADER *)program; /* the header is in Little Endian, on a Big Endian machine, swap all * multi-byte words */ assert(check_endian()); #if BYTE_ORDER==BIG_ENDIAN amx_Align32((uint32_t*)&hdr->size); amx_Align16(&hdr->magic); amx_Align16((uint16_t*)&hdr->flags); amx_Align16((uint16_t*)&hdr->defsize); amx_Align32((uint32_t*)&hdr->cod); amx_Align32((uint32_t*)&hdr->dat); amx_Align32((uint32_t*)&hdr->hea); amx_Align32((uint32_t*)&hdr->stp); amx_Align32((uint32_t*)&hdr->cip); amx_Align32((uint32_t*)&hdr->publics); amx_Align32((uint32_t*)&hdr->natives); amx_Align32((uint32_t*)&hdr->libraries); amx_Align32((uint32_t*)&hdr->pubvars); amx_Align32((uint32_t*)&hdr->tags); #endif if (hdr->magic!=AMX_MAGIC) return AMX_ERR_FORMAT; if (hdr->file_versionamx_version>CUR_FILE_VERSION) return AMX_ERR_VERSION; if (hdr->defsize!=sizeof(AMX_FUNCSTUB) && hdr->defsize!=sizeof(AMX_FUNCSTUBNT)) return AMX_ERR_FORMAT; if (USENAMETABLE(hdr)) { uint16_t *namelength; /* when there is a separate name table, check the maximum name length * in that table */ amx_Align32((uint32_t*)&hdr->nametable); namelength=(uint16_t*)((unsigned char*)program + (unsigned)hdr->nametable); amx_Align16(namelength); if (*namelength>sNAMEMAX) return AMX_ERR_FORMAT; } /* if */ if (hdr->stp<=0) return AMX_ERR_FORMAT; #if BYTE_ORDER==BIG_ENDIAN if ((hdr->flags & AMX_FLAG_COMPACT)==0) { ucell *code=(ucell *)((unsigned char *)program+(int)hdr->cod); while (code<(ucell *)((unsigned char *)program+(int)hdr->hea)) swapcell(code++); } /* if */ #endif assert((hdr->flags & AMX_FLAG_COMPACT)!=0 || hdr->hea == hdr->size); if ((hdr->flags & AMX_FLAG_COMPACT)!=0) { #if AMX_COMPACTMARGIN > 2 expand((unsigned char *)program+(int)hdr->cod, hdr->size - hdr->cod, hdr->hea - hdr->cod); #else return AMX_ERR_FORMAT; #endif } /* if */ amx->base=(unsigned char *)program; /* Set a zero cell at the top of the stack, which functions * as a sentinel for strings. */ * (cell *)(amx->base+(int)hdr->stp-sizeof(cell)) = 0; /* set initial values */ amx->hlw=hdr->hea - hdr->dat; /* stack and heap relative to data segment */ amx->stp=hdr->stp - hdr->dat - sizeof(cell); amx->hea=amx->hlw; amx->stk=amx->stp; if (amx->callback==NULL) amx->callback=amx_Callback; amx->data=NULL; /* also align all addresses in the public function, public variable, * public tag and native function tables --offsets into the name table * (if present) must also be swapped. */ #if BYTE_ORDER==BIG_ENDIAN { /* local */ AMX_FUNCSTUB *fs; int i,num; fs=GETENTRY(hdr,natives,0); num=NUMENTRIES(hdr,natives,libraries); for (i=0; iaddress); /* redundant, because it should be zero */ if (USENAMETABLE(hdr)) amx_Align32(&((AMX_FUNCSTUBNT*)fs)->nameofs); fs=(AMX_FUNCSTUB*)((unsigned char *)fs+hdr->defsize); } /* for */ fs=GETENTRY(hdr,publics,0); assert(hdr->publics<=hdr->natives); num=NUMENTRIES(hdr,publics,natives); for (i=0; iaddress); if (USENAMETABLE(hdr)) amx_Align32(&((AMX_FUNCSTUBNT*)fs)->nameofs); fs=(AMX_FUNCSTUB*)((unsigned char *)fs+hdr->defsize); } /* for */ fs=GETENTRY(hdr,pubvars,0); assert(hdr->pubvars<=hdr->tags); num=NUMENTRIES(hdr,pubvars,tags); for (i=0; iaddress); if (USENAMETABLE(hdr)) amx_Align32(&((AMX_FUNCSTUBNT*)fs)->nameofs); fs=(AMX_FUNCSTUB*)((unsigned char *)fs+hdr->defsize); } /* for */ fs=GETENTRY(hdr,tags,0); if (hdr->file_version<7) { assert(hdr->tags<=hdr->cod); num=NUMENTRIES(hdr,tags,cod); } else { assert(hdr->tags<=hdr->nametable); num=NUMENTRIES(hdr,tags,nametable); } /* if */ for (i=0; iaddress); if (USENAMETABLE(hdr)) amx_Align32(&((AMX_FUNCSTUBNT*)fs)->nameofs); fs=(AMX_FUNCSTUB*)((unsigned char *)fs+hdr->defsize); } /* for */ } /* local */ #endif /* relocate call and jump instructions */ amx_BrowseRelocate(amx); /* load any extension modules that the AMX refers to */ #if (defined _Windows || defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ || defined __APPLE__) && !defined AMX_NODYNALOAD hdr=(AMX_HEADER *)amx->base; numlibraries=NUMENTRIES(hdr,libraries,pubvars); for (i=0; iaddress=(ucell)hlib; } /* for */ #endif return AMX_ERR_NONE; } #if defined JIT #define CODESIZE_JIT 8192 /* approximate size of the code for the JIT */ #if defined __WIN32__ /* this also applies to Win32 "console" applications */ #define PROT_READ 0x1 /* page can be read */ #define PROT_WRITE 0x2 /* page can be written */ #define PROT_EXEC 0x4 /* page can be executed */ #define PROT_NONE 0x0 /* page can not be accessed */ static int mprotect(void *addr, size_t len, int prot) { DWORD prev, p = 0; if ((prot & PROT_WRITE)!=0) p = PAGE_EXECUTE_READWRITE; else p |= PAGE_EXECUTE_READ; return !VirtualProtect(addr, len, p, &prev); } #elif defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ || defined __APPLE__ /* Linux already has mprotect() */ #else // TODO: Add cases for Linux, Unix, OS/2, ... /* DOS32 has no imposed limits on its segments */ #define mprotect(addr, len, prot) (0) #endif /* #if defined __WIN32 __ */ int AMXAPI amx_InitJIT(AMX *amx, void *reloc_table, void *native_code) { int res; AMX_HEADER *hdr; if ((amx->flags & AMX_FLAG_JITC)==0) return AMX_ERR_INIT_JIT; /* flag not set, this AMX is not prepared for JIT */ /* Patching SYSREQ opcodes to SYSREQ_D cannot work in the JIT, because the * program would need to be re-JIT-compiled after patching a P-code * instruction. If this field is not zero, something went wrong with the * amx_BrowseRelocate(). */ assert(amx->sysreq_d==0); if (mprotect(asm_runJIT, CODESIZE_JIT, PROT_READ | PROT_WRITE | PROT_EXEC) != 0) return AMX_ERR_INIT_JIT; /* copy the prefix */ memcpy(native_code, amx->base, ((AMX_HEADER *)(amx->base))->cod); hdr = native_code; /* JIT rulz! (TM) */ /* MP: added check for correct compilation */ if ((res = asm_runJIT(amx->base, reloc_table, native_code)) != 0) { /* update the required memory size (the previous value was a * conservative estimate, now we know the exact size) */ amx->code_size = (hdr->dat + hdr->stp + 3) & ~3; /* The compiled code is relocatable, since only relative jumps are * used for destinations within the generated code and absoulute * addresses for jumps into the runtime, which is fixed in memory. */ amx->base = (unsigned char*) native_code; amx->cip = hdr->cip; amx->hea = hdr->hea; amx->stp = hdr->stp - sizeof(cell); /* also put a sentinel for strings at the top the stack */ *(cell *)((char*)native_code + hdr->dat + hdr->stp - sizeof(cell)) = 0; amx->stk = amx->stp; } /* if */ return (res == 0) ? AMX_ERR_NONE : AMX_ERR_INIT_JIT; } #else /* #if defined JIT */ int AMXAPI amx_InitJIT(AMX *amx,void *compiled_program,void *reloc_table) { (void)amx; (void)compiled_program; (void)reloc_table; return AMX_ERR_INIT_JIT; } #endif /* #if defined JIT */ #endif /* AMX_INIT */ #if defined AMX_CLEANUP int AMXAPI amx_Cleanup(AMX *amx) { #if (defined _Windows || defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ || defined __APPLE__) && !defined AMX_NODYNALOAD #if defined _Windows typedef int (FAR WINAPI *AMX_ENTRY)(AMX FAR *amx); #elif defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ || defined __APPLE__ typedef int (*AMX_ENTRY)(AMX *amx); #endif AMX_HEADER *hdr; int numlibraries,i; AMX_FUNCSTUB *lib; AMX_ENTRY libcleanup; #endif /* unload all extension modules */ #if (defined _Windows || defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ || defined __APPLE__) && !defined AMX_NODYNALOAD hdr=(AMX_HEADER *)amx->base; assert(hdr->magic==AMX_MAGIC); numlibraries=NUMENTRIES(hdr,libraries,pubvars); for (i=0; iaddress!=0) { char funcname[sNAMEMAX+12]; /* +1 for '\0', +4 for 'amx_', +7 for 'Cleanup' */ strcpy(funcname,"amx_"); strcat(funcname,GETENTRYNAME(hdr,lib)); strcat(funcname,"Cleanup"); #if defined _Windows libcleanup=(AMX_ENTRY)GetProcAddress((HINSTANCE)lib->address,funcname); #elif defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ || defined __APPLE__ libcleanup=(AMX_ENTRY)dlsym((void*)lib->address,funcname); #endif if (libcleanup!=NULL) libcleanup(amx); #if defined _Windows FreeLibrary((HINSTANCE)lib->address); #elif defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ || defined __APPLE__ dlclose((void*)lib->address); #endif } /* if */ } /* for */ #else (void)amx; #endif return AMX_ERR_NONE; } #endif /* AMX_CLEANUP */ #if defined AMX_CLONE int AMXAPI amx_Clone(AMX *amxClone, AMX *amxSource, void *data) { AMX_HEADER *hdr; unsigned char _FAR *dataSource; if (amxSource==NULL) return AMX_ERR_FORMAT; if (amxClone==NULL) return AMX_ERR_PARAMS; if ((amxSource->flags & AMX_FLAG_RELOC)==0) return AMX_ERR_INIT; hdr=(AMX_HEADER *)amxSource->base; if (hdr->magic!=AMX_MAGIC) return AMX_ERR_FORMAT; if (hdr->file_version>CUR_FILE_VERSION || hdr->amx_versionbase=amxSource->base; amxClone->hlw=hdr->hea - hdr->dat; /* stack and heap relative to data segment */ amxClone->stp=hdr->stp - hdr->dat - sizeof(cell); amxClone->hea=amxClone->hlw; amxClone->stk=amxClone->stp; if (amxClone->callback==NULL) amxClone->callback=amxSource->callback; if (amxClone->debug==NULL) amxClone->debug=amxSource->debug; amxClone->flags=amxSource->flags; /* copy the data segment; the stack and the heap can be left uninitialized */ assert(data!=NULL); amxClone->data=(unsigned char _FAR *)data; dataSource=(amxSource->data!=NULL) ? amxSource->data : amxSource->base+(int)hdr->dat; memcpy(amxClone->data,dataSource,(size_t)(hdr->hea-hdr->dat)); /* Set a zero cell at the top of the stack, which functions * as a sentinel for strings. */ * (cell *)(amxClone->data+(int)amxClone->stp) = 0; return AMX_ERR_NONE; } #endif /* AMX_CLONE */ #if defined AMX_MEMINFO int AMXAPI amx_MemInfo(AMX *amx, long *codesize, long *datasize, long *stackheap) { AMX_HEADER *hdr; if (amx==NULL) return AMX_ERR_FORMAT; hdr=(AMX_HEADER *)amx->base; if (hdr->magic!=AMX_MAGIC) return AMX_ERR_FORMAT; if (hdr->file_version>CUR_FILE_VERSION || hdr->amx_versiondat - hdr->cod; if (datasize!=NULL) *datasize=hdr->hea - hdr->dat; if (stackheap!=NULL) *stackheap=hdr->stp - hdr->hea; return AMX_ERR_NONE; } #endif /* AMX_MEMINFO */ #if defined AMX_NAMELENGTH int AMXAPI amx_NameLength(AMX *amx, int *length) { AMX_HEADER *hdr=(AMX_HEADER *)amx->base; assert(hdr!=NULL); assert(hdr->magic==AMX_MAGIC); if (USENAMETABLE(hdr)) { uint16_t *namelength=(uint16_t*)(amx->base + (unsigned)hdr->nametable); *length=*namelength; assert(hdr->file_version>=7); /* name table exists only for file version 7+ */ } else { *length=hdr->defsize - sizeof(ucell); } /* if */ return AMX_ERR_NONE; } #endif /* AMX_NAMELENGTH */ #if defined AMX_XXXNATIVES int AMXAPI amx_NumNatives(AMX *amx, int *number) { AMX_HEADER *hdr=(AMX_HEADER *)amx->base; assert(hdr!=NULL); assert(hdr->magic==AMX_MAGIC); assert(hdr->natives<=hdr->libraries); *number=NUMENTRIES(hdr,natives,libraries); return AMX_ERR_NONE; } int AMXAPI amx_GetNative(AMX *amx, int index, char *funcname) { AMX_HEADER *hdr; AMX_FUNCSTUB *func; hdr=(AMX_HEADER *)amx->base; assert(hdr!=NULL); assert(hdr->magic==AMX_MAGIC); assert(hdr->natives<=hdr->libraries); if (index>=(cell)NUMENTRIES(hdr,natives,libraries)) return AMX_ERR_INDEX; func=GETENTRY(hdr,natives,index); strcpy(funcname,GETENTRYNAME(hdr,func)); return AMX_ERR_NONE; } int AMXAPI amx_FindNative(AMX *amx, const char *name, int *index) { int first,last,mid,result; char pname[sNAMEMAX+1]; amx_NumNatives(amx, &last); last--; /* last valid index is 1 less than the number of functions */ first=0; /* binary search */ while (first<=last) { mid=(first+last)/2; amx_GetNative(amx, mid, pname); result=strcmp(pname,name); if (result>0) { last=mid-1; } else if (result<0) { first=mid+1; } else { *index=mid; return AMX_ERR_NONE; } /* if */ } /* while */ /* not found, set to an invalid index, so amx_Exec() will fail */ *index=INT_MAX; return AMX_ERR_NOTFOUND; } #endif /* AMX_XXXNATIVES */ #if defined AMX_XXXPUBLICS int AMXAPI amx_NumPublics(AMX *amx, int *number) { AMX_HEADER *hdr=(AMX_HEADER *)amx->base; assert(hdr!=NULL); assert(hdr->magic==AMX_MAGIC); assert(hdr->publics<=hdr->natives); *number=NUMENTRIES(hdr,publics,natives); return AMX_ERR_NONE; } int AMXAPI amx_GetPublic(AMX *amx, int index, char *funcname) { AMX_HEADER *hdr; AMX_FUNCSTUB *func; hdr=(AMX_HEADER *)amx->base; assert(hdr!=NULL); assert(hdr->magic==AMX_MAGIC); assert(hdr->publics<=hdr->natives); if (index>=(cell)NUMENTRIES(hdr,publics,natives)) return AMX_ERR_INDEX; func=GETENTRY(hdr,publics,index); strcpy(funcname,GETENTRYNAME(hdr,func)); return AMX_ERR_NONE; } int AMXAPI amx_FindPublic(AMX *amx, const char *name, int *index) { int first,last,mid,result; char pname[sNAMEMAX+1]; amx_NumPublics(amx, &last); last--; /* last valid index is 1 less than the number of functions */ first=0; /* binary search */ while (first<=last) { mid=(first+last)/2; amx_GetPublic(amx, mid, pname); result=strcmp(pname,name); if (result>0) { last=mid-1; } else if (result<0) { first=mid+1; } else { *index=mid; return AMX_ERR_NONE; } /* if */ } /* while */ /* not found, set to an invalid index, so amx_Exec() will fail */ *index=INT_MAX; return AMX_ERR_NOTFOUND; } #endif /* AMX_XXXPUBLICS */ #if defined AMX_XXXPUBVARS int AMXAPI amx_NumPubVars(AMX *amx, int *number) { AMX_HEADER *hdr=(AMX_HEADER *)amx->base; assert(hdr!=NULL); assert(hdr->magic==AMX_MAGIC); assert(hdr->pubvars<=hdr->tags); *number=NUMENTRIES(hdr,pubvars,tags); return AMX_ERR_NONE; } int AMXAPI amx_GetPubVar(AMX *amx, int index, char *varname, cell *amx_addr) { AMX_HEADER *hdr; AMX_FUNCSTUB *var; hdr=(AMX_HEADER *)amx->base; assert(hdr!=NULL); assert(hdr->magic==AMX_MAGIC); assert(hdr->pubvars<=hdr->tags); if (index>=(cell)NUMENTRIES(hdr,pubvars,tags)) return AMX_ERR_INDEX; var=GETENTRY(hdr,pubvars,index); strcpy(varname,GETENTRYNAME(hdr,var)); *amx_addr=var->address; return AMX_ERR_NONE; } int AMXAPI amx_FindPubVar(AMX *amx, const char *varname, cell *amx_addr) { int first,last,mid,result; char pname[sNAMEMAX+1]; cell paddr; amx_NumPubVars(amx, &last); last--; /* last valid index is 1 less than the number of functions */ first=0; /* binary search */ while (first<=last) { mid=(first+last)/2; amx_GetPubVar(amx, mid, pname, &paddr); result=strcmp(pname,varname); if (result>0) { last=mid-1; } else if (result<0) { first=mid+1; } else { *amx_addr=paddr; return AMX_ERR_NONE; } /* if */ } /* while */ /* not found */ *amx_addr=0; return AMX_ERR_NOTFOUND; } #endif /* AMX_XXXPUBVARS */ #if defined AMX_XXXTAGS int AMXAPI amx_NumTags(AMX *amx, int *number) { AMX_HEADER *hdr=(AMX_HEADER *)amx->base; assert(hdr!=NULL); assert(hdr->magic==AMX_MAGIC); if (hdr->file_version<5) { /* the tagname table appeared in file format 5 */ *number=0; return AMX_ERR_VERSION; } /* if */ if (hdr->file_version<7) { assert(hdr->tags<=hdr->cod); *number=NUMENTRIES(hdr,tags,cod); } else { assert(hdr->tags<=hdr->nametable); *number=NUMENTRIES(hdr,tags,nametable); } /* if */ return AMX_ERR_NONE; } int AMXAPI amx_GetTag(AMX *amx, int index, char *tagname, cell *tag_id) { AMX_HEADER *hdr; AMX_FUNCSTUB *tag; hdr=(AMX_HEADER *)amx->base; assert(hdr!=NULL); assert(hdr->magic==AMX_MAGIC); if (hdr->file_version<5) { /* the tagname table appeared in file format 5 */ *tagname='\0'; *tag_id=0; return AMX_ERR_VERSION; } /* if */ if (hdr->file_version<7) { assert(hdr->tags<=hdr->cod); if (index>=(cell)NUMENTRIES(hdr,tags,cod)) return AMX_ERR_INDEX; } else { assert(hdr->tags<=hdr->nametable); if (index>=(cell)NUMENTRIES(hdr,tags,nametable)) return AMX_ERR_INDEX; } /* if */ tag=GETENTRY(hdr,tags,index); strcpy(tagname,GETENTRYNAME(hdr,tag)); *tag_id=tag->address; return AMX_ERR_NONE; } int AMXAPI amx_FindTagId(AMX *amx, cell tag_id, char *tagname) { int first,last,mid; cell mid_id; #if !defined NDEBUG /* verify that the tagname table is sorted on the tag_id */ amx_NumTags(amx, &last); if (last>0) { cell cur_id; amx_GetTag(amx,0,tagname,&cur_id); for (first=1; firsttag_id) last=mid-1; else if (mid_idusertags[index]!=tag; index++) /* nothing */; if (index>=AMX_USERNUM) return AMX_ERR_USERDATA; *ptr=amx->userdata[index]; return AMX_ERR_NONE; } int AMXAPI amx_SetUserData(AMX *amx, long tag, void *ptr) { int index; assert(amx!=NULL); assert(tag!=0); /* try to find existing tag */ for (index=0; indexusertags[index]!=tag; index++) /* nothing */; /* if not found, try to find empty tag */ if (index>=AMX_USERNUM) for (index=0; indexusertags[index]!=0; index++) /* nothing */; /* if still not found, quit with error */ if (index>=AMX_USERNUM) return AMX_ERR_INDEX; /* set the tag and the value */ amx->usertags[index]=tag; amx->userdata[index]=ptr; return AMX_ERR_NONE; } #endif /* AMX_XXXUSERDATA */ #if defined AMX_REGISTER || defined AMX_EXEC || defined AMX_INIT static AMX_NATIVE findfunction(const char *name, const AMX_NATIVE_INFO *list, int number) { int i; assert(list!=NULL); for (i=0; list[i].name!=NULL && (ibase; assert(hdr!=NULL); assert(hdr->magic==AMX_MAGIC); assert(hdr->natives<=hdr->libraries); numnatives=NUMENTRIES(hdr,natives,libraries); err=AMX_ERR_NONE; func=GETENTRY(hdr,natives,0); for (i=0; iaddress==0) { /* this function is not yet located */ funcptr=(list!=NULL) ? findfunction(GETENTRYNAME(hdr,func),list,number) : NULL; if (funcptr!=NULL) func->address=(ucell)funcptr; else err=AMX_ERR_NOTFOUND; } /* if */ func=(AMX_FUNCSTUB*)((unsigned char*)func+hdr->defsize); } /* for */ if (err==AMX_ERR_NONE) amx->flags|=AMX_FLAG_NTVREG; return err; } #endif /* AMX_REGISTER || AMX_EXEC || AMX_INIT */ #if defined AMX_NATIVEINFO AMX_NATIVE_INFO * AMXAPI amx_NativeInfo(const char *name, AMX_NATIVE func) { static AMX_NATIVE_INFO n; n.name=name; n.func=func; return &n; } #endif /* AMX_NATIVEINFO */ #if defined AMX_EXEC || defined AMX_INIT #define STKMARGIN ((cell)(16*sizeof(cell))) int AMXAPI amx_Push(AMX *amx, cell value) { AMX_HEADER *hdr; unsigned char *data; if (amx->hea+STKMARGIN>amx->stk) return AMX_ERR_STACKERR; hdr=(AMX_HEADER *)amx->base; data=(amx->data!=NULL) ? amx->data : amx->base+(int)hdr->dat; amx->stk-=sizeof(cell); amx->paramcount+=1; *(cell *)(data+(int)amx->stk)=value; return AMX_ERR_NONE; } int AMXAPI amx_PushArray(AMX *amx, cell *amx_addr, cell **phys_addr, const cell array[], int numcells) { cell *paddr; int err; assert(amx!=NULL); assert(amx_addr!=NULL); assert(array!=NULL); err=amx_Allot(amx,numcells,amx_addr,&paddr); if (err==AMX_ERR_NONE) { if (phys_addr!=NULL) *phys_addr=paddr; memcpy(paddr,array,numcells*sizeof(cell)); err=amx_Push(amx,*amx_addr); } /* if */ return err; } int AMXAPI amx_PushString(AMX *amx, cell *amx_addr, cell **phys_addr, const char *string, int pack, int use_wchar) { cell *paddr; int numcells,err; assert(amx!=NULL); assert(amx_addr!=NULL); assert(string!=NULL); #if defined AMX_ANSIONLY numcells=strlen(string) + 1; #else numcells= (use_wchar ? wcslen((const wchar_t*)string) : strlen(string)) + 1; #endif if (pack) numcells=(numcells+sizeof(cell)-1)/sizeof(cell); err=amx_Allot(amx,numcells,amx_addr,&paddr); if (err==AMX_ERR_NONE) { if (phys_addr!=NULL) *phys_addr=paddr; amx_SetString(paddr,string,pack,use_wchar,UNLIMITED); err=amx_Push(amx,*amx_addr); } /* if */ return err; } #define GETPARAM(v) ( v=*(cell *)cip++ ) #define SKIPPARAM(n) ( cip=(cell *)cip+(n) ) #define PUSH(v) ( stk-=sizeof(cell), *(cell *)(data+(int)stk)=v ) #define POP(v) ( v=*(cell *)(data+(int)stk), stk+=sizeof(cell) ) #define ABORT(amx,v) { (amx)->stk=reset_stk; (amx)->hea=reset_hea; return v; } #define CHKMARGIN() if (hea+STKMARGIN>stk) return AMX_ERR_STACKERR #define CHKSTACK() if (stk>amx->stp) return AMX_ERR_STACKLOW #define CHKHEAP() if (heahlw) return AMX_ERR_HEAPLOW #if defined __GNUC__ && !(defined ASM32 || defined JIT) /* GNU C version uses the "labels as values" extension to create * fast "indirect threaded" interpreter. */ #define NEXT(cip) goto *(const void *)*cip++ int AMXAPI amx_Exec(AMX *amx, cell *retval, int index) { static const void * const amx_opcodelist[] = { &&op_none, &&op_load_pri, &&op_load_alt, &&op_load_s_pri, &&op_load_s_alt,&&op_lref_pri, &&op_lref_alt, &&op_lref_s_pri, &&op_lref_s_alt,&&op_load_i, &&op_lodb_i, &&op_const_pri, &&op_const_alt, &&op_addr_pri, &&op_addr_alt, &&op_stor_pri, &&op_stor_alt, &&op_stor_s_pri,&&op_stor_s_alt,&&op_sref_pri, &&op_sref_alt, &&op_sref_s_pri,&&op_sref_s_alt,&&op_stor_i, &&op_strb_i, &&op_lidx, &&op_lidx_b, &&op_idxaddr, &&op_idxaddr_b, &&op_align_pri, &&op_align_alt, &&op_lctrl, &&op_sctrl, &&op_move_pri, &&op_move_alt, &&op_xchg, &&op_push_pri, &&op_push_alt, &&op_push_r, &&op_push_c, &&op_push, &&op_push_s, &&op_pop_pri, &&op_pop_alt, &&op_stack, &&op_heap, &&op_proc, &&op_ret, &&op_retn, &&op_call, &&op_call_pri, &&op_jump, &&op_jrel, &&op_jzer, &&op_jnz, &&op_jeq, &&op_jneq, &&op_jless, &&op_jleq, &&op_jgrtr, &&op_jgeq, &&op_jsless, &&op_jsleq, &&op_jsgrtr, &&op_jsgeq, &&op_shl, &&op_shr, &&op_sshr, &&op_shl_c_pri, &&op_shl_c_alt, &&op_shr_c_pri, &&op_shr_c_alt, &&op_smul, &&op_sdiv, &&op_sdiv_alt, &&op_umul, &&op_udiv, &&op_udiv_alt, &&op_add, &&op_sub, &&op_sub_alt, &&op_and, &&op_or, &&op_xor, &&op_not, &&op_neg, &&op_invert, &&op_add_c, &&op_smul_c, &&op_zero_pri, &&op_zero_alt, &&op_zero, &&op_zero_s, &&op_sign_pri, &&op_sign_alt, &&op_eq, &&op_neq, &&op_less, &&op_leq, &&op_grtr, &&op_geq, &&op_sless, &&op_sleq, &&op_sgrtr, &&op_sgeq, &&op_eq_c_pri, &&op_eq_c_alt, &&op_inc_pri, &&op_inc_alt, &&op_inc, &&op_inc_s, &&op_inc_i, &&op_dec_pri, &&op_dec_alt, &&op_dec, &&op_dec_s, &&op_dec_i, &&op_movs, &&op_cmps, &&op_fill, &&op_halt, &&op_bounds, &&op_sysreq_pri,&&op_sysreq_c, &&op_file, &&op_line, &&op_symbol, &&op_srange, &&op_jump_pri, &&op_switch, &&op_casetbl, &&op_swap_pri, &&op_swap_alt, &&op_pushaddr, &&op_nop, &&op_sysreq_d, &&op_symtag, &&op_break }; AMX_HEADER *hdr; AMX_FUNCSTUB *func; unsigned char *code, *data; cell pri,alt,stk,frm,hea; cell reset_stk, reset_hea, *cip; cell offs; ucell codesize; int num,i; /* HACK: return label table (for amx_BrowseRelocate) if amx structure * has the AMX_FLAG_BROWSE flag set. */ assert(amx!=NULL); if ((amx->flags & AMX_FLAG_BROWSE)==AMX_FLAG_BROWSE) { assert(sizeof(cell)==sizeof(void *)); assert(retval!=NULL); *retval=(cell)amx_opcodelist; return 0; } /* if */ if (amx->callback==NULL) return AMX_ERR_CALLBACK; if ((amx->flags & AMX_FLAG_NTVREG)==0) return AMX_ERR_NOTFOUND; if ((amx->flags & AMX_FLAG_RELOC)==0) return AMX_ERR_INIT; assert((amx->flags & AMX_FLAG_BROWSE)==0); /* set up the registers */ hdr=(AMX_HEADER *)amx->base; assert(hdr->magic==AMX_MAGIC); codesize=(ucell)(hdr->dat-hdr->cod); code=amx->base+(int)hdr->cod; data=(amx->data!=NULL) ? amx->data : amx->base+(int)hdr->dat; hea=amx->hea; stk=amx->stk; reset_stk=stk; reset_hea=hea; alt=frm=0; /* just to avoid compiler warnings */ num=0; /* just to avoid compiler warnings */ /* get the start address */ if (index==AMX_EXEC_MAIN) { if (hdr->cip<0) return AMX_ERR_INDEX; cip=(cell *)(code + (int)hdr->cip); } else if (index==AMX_EXEC_CONT) { /* all registers: pri, alt, frm, cip, hea, stk, reset_stk, reset_hea */ frm=amx->frm; stk=amx->stk; hea=amx->hea; pri=amx->pri; alt=amx->alt; reset_stk=amx->reset_stk; reset_hea=amx->reset_hea; cip=(cell *)(code + (int)amx->cip); } else if (index<0) { return AMX_ERR_INDEX; } else { if (index>=(int)NUMENTRIES(hdr,publics,natives)) return AMX_ERR_INDEX; func=GETENTRY(hdr,publics,index); cip=(cell *)(code + (int)func->address); } /* if */ /* check values just copied */ CHKSTACK(); CHKHEAP(); assert(check_endian()); /* sanity checks */ assert(OP_PUSH_PRI==36); assert(OP_PROC==46); assert(OP_SHL==65); assert(OP_SMUL==72); assert(OP_EQ==95); assert(OP_INC_PRI==107); assert(OP_MOVS==117); assert(OP_SYMBOL==126); #if PAWN_CELL_SIZE==16 assert(sizeof(cell)==2); #elif PAWN_CELL_SIZE==32 assert(sizeof(cell)==4); #elif PAWN_CELL_SIZE==64 assert(sizeof(cell)==8); #else #error Unsupported cell size #endif if (index!=AMX_EXEC_CONT) { reset_stk+=amx->paramcount*sizeof(cell); PUSH(amx->paramcount*sizeof(cell)); amx->paramcount=0; /* push the parameter count to the stack & reset */ PUSH(0); /* zero return address */ } /* if */ /* check stack/heap before starting to run */ CHKMARGIN(); /* start running */ NEXT(cip); op_none: ABORT(amx,AMX_ERR_INVINSTR); op_load_pri: GETPARAM(offs); pri= * (cell *)(data+(int)offs); NEXT(cip); op_load_alt: GETPARAM(offs); alt= * (cell *)(data+(int)offs); NEXT(cip); op_load_s_pri: GETPARAM(offs); pri= * (cell *)(data+(int)frm+(int)offs); NEXT(cip); op_load_s_alt: GETPARAM(offs); alt= * (cell *)(data+(int)frm+(int)offs); NEXT(cip); op_lref_pri: GETPARAM(offs); offs= * (cell *)(data+(int)offs); pri= * (cell *)(data+(int)offs); NEXT(cip); op_lref_alt: GETPARAM(offs); offs= * (cell *)(data+(int)offs); alt= * (cell *)(data+(int)offs); NEXT(cip); op_lref_s_pri: GETPARAM(offs); offs= * (cell *)(data+(int)frm+(int)offs); pri= * (cell *)(data+(int)offs); NEXT(cip); op_lref_s_alt: GETPARAM(offs); offs= * (cell *)(data+(int)frm+(int)offs); alt= * (cell *)(data+(int)offs); NEXT(cip); op_load_i: /* verify address */ if ((pri>=hea && pri=(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); pri= * (cell *)(data+(int)pri); NEXT(cip); op_lodb_i: GETPARAM(offs); /* verify address */ if ((pri>=hea && pri=(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); switch (offs) { case 1: pri= * (data+(int)pri); break; case 2: pri= * (uint16_t *)(data+(int)pri); break; case 4: pri= * (uint32_t *)(data+(int)pri); break; } /* switch */ NEXT(cip); op_const_pri: GETPARAM(pri); NEXT(cip); op_const_alt: GETPARAM(alt); NEXT(cip); op_addr_pri: GETPARAM(pri); pri+=frm; NEXT(cip); op_addr_alt: GETPARAM(alt); alt+=frm; NEXT(cip); op_stor_pri: GETPARAM(offs); *(cell *)(data+(int)offs)=pri; NEXT(cip); op_stor_alt: GETPARAM(offs); *(cell *)(data+(int)offs)=alt; NEXT(cip); op_stor_s_pri: GETPARAM(offs); *(cell *)(data+(int)frm+(int)offs)=pri; NEXT(cip); op_stor_s_alt: GETPARAM(offs); *(cell *)(data+(int)frm+(int)offs)=alt; NEXT(cip); op_sref_pri: GETPARAM(offs); offs= * (cell *)(data+(int)offs); *(cell *)(data+(int)offs)=pri; NEXT(cip); op_sref_alt: GETPARAM(offs); offs= * (cell *)(data+(int)offs); *(cell *)(data+(int)offs)=alt; NEXT(cip); op_sref_s_pri: GETPARAM(offs); offs= * (cell *)(data+(int)frm+(int)offs); *(cell *)(data+(int)offs)=pri; NEXT(cip); op_sref_s_alt: GETPARAM(offs); offs= * (cell *)(data+(int)frm+(int)offs); *(cell *)(data+(int)offs)=alt; NEXT(cip); op_stor_i: /* verify address */ if ((alt>=hea && alt=(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); *(cell *)(data+(int)alt)=pri; NEXT(cip); op_strb_i: GETPARAM(offs); /* verify address */ if ((alt>=hea && alt=(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); switch (offs) { case 1: *(data+(int)alt)=(unsigned char)pri; break; case 2: *(uint16_t *)(data+(int)alt)=(uint16_t)pri; break; case 4: *(uint32_t *)(data+(int)alt)=(uint32_t)pri; break; } /* switch */ NEXT(cip); op_lidx: offs=pri*sizeof(cell)+alt; /* verify address */ if ((offs>=hea && offs=(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); pri= * (cell *)(data+(int)offs); NEXT(cip); op_lidx_b: GETPARAM(offs); offs=(pri << (int)offs)+alt; /* verify address */ if ((offs>=hea && offs=(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); pri= * (cell *)(data+(int)offs); NEXT(cip); op_idxaddr: pri=pri*sizeof(cell)+alt; NEXT(cip); op_idxaddr_b: GETPARAM(offs); pri=(pri << (int)offs)+alt; NEXT(cip); op_align_pri: GETPARAM(offs); #if BYTE_ORDER==LITTLE_ENDIAN if (offs<(int)sizeof(cell)) pri ^= sizeof(cell)-offs; #endif NEXT(cip); op_align_alt: GETPARAM(offs); #if BYTE_ORDER==LITTLE_ENDIAN if (offs<(int)sizeof(cell)) alt ^= sizeof(cell)-offs; #endif NEXT(cip); op_lctrl: GETPARAM(offs); switch (offs) { case 0: pri=hdr->cod; break; case 1: pri=hdr->dat; break; case 2: pri=hea; break; case 3: pri=amx->stp; break; case 4: pri=stk; break; case 5: pri=frm; break; case 6: pri=(cell)((unsigned char *)cip - code); break; } /* switch */ NEXT(cip); op_sctrl: GETPARAM(offs); switch (offs) { case 0: case 1: case 3: /* cannot change these parameters */ break; case 2: hea=pri; break; case 4: stk=pri; break; case 5: frm=pri; break; case 6: cip=(cell *)(code + (int)pri); break; } /* switch */ NEXT(cip); op_move_pri: pri=alt; NEXT(cip); op_move_alt: alt=pri; NEXT(cip); op_xchg: offs=pri; /* offs is a temporary variable */ pri=alt; alt=offs; NEXT(cip); op_push_pri: PUSH(pri); NEXT(cip); op_push_alt: PUSH(alt); NEXT(cip); op_push_c: GETPARAM(offs); PUSH(offs); NEXT(cip); op_push_r: GETPARAM(offs); while (offs--) PUSH(pri); NEXT(cip); op_push: GETPARAM(offs); PUSH(* (cell *)(data+(int)offs)); NEXT(cip); op_push_s: GETPARAM(offs); PUSH(* (cell *)(data+(int)frm+(int)offs)); NEXT(cip); op_pop_pri: POP(pri); NEXT(cip); op_pop_alt: POP(alt); NEXT(cip); op_stack: GETPARAM(offs); alt=stk; stk+=offs; CHKMARGIN(); CHKSTACK(); NEXT(cip); op_heap: GETPARAM(offs); alt=hea; hea+=offs; CHKMARGIN(); CHKHEAP(); NEXT(cip); op_proc: PUSH(frm); frm=stk; CHKMARGIN(); NEXT(cip); op_ret: POP(frm); POP(offs); /* verify the return address */ if ((ucell)offs>=codesize) ABORT(amx,AMX_ERR_MEMACCESS); cip=(cell *)(code+(int)offs); NEXT(cip); op_retn: POP(frm); POP(offs); /* verify the return address */ if ((ucell)offs>=codesize) ABORT(amx,AMX_ERR_MEMACCESS); cip=(cell *)(code+(int)offs); stk+= *(cell *)(data+(int)stk) + sizeof(cell); /* remove parameters from the stack */ NEXT(cip); op_call: PUSH(((unsigned char *)cip-code)+sizeof(cell));/* push address behind instruction */ cip=JUMPABS(code, cip); /* jump to the address */ NEXT(cip); op_call_pri: PUSH((unsigned char *)cip-code); cip=(cell *)(code+(int)pri); NEXT(cip); op_jump: /* since the GETPARAM() macro modifies cip, you cannot * do GETPARAM(cip) directly */ cip=JUMPABS(code, cip); NEXT(cip); op_jrel: offs=*cip; cip=(cell *)((unsigned char *)cip + (int)offs + sizeof(cell)); NEXT(cip); op_jzer: if (pri==0) cip=JUMPABS(code, cip); else cip=(cell *)((unsigned char *)cip+sizeof(cell)); NEXT(cip); op_jnz: if (pri!=0) cip=JUMPABS(code, cip); else cip=(cell *)((unsigned char *)cip+sizeof(cell)); NEXT(cip); op_jeq: if (pri==alt) cip=JUMPABS(code, cip); else cip=(cell *)((unsigned char *)cip+sizeof(cell)); NEXT(cip); op_jneq: if (pri!=alt) cip=JUMPABS(code, cip); else cip=(cell *)((unsigned char *)cip+sizeof(cell)); NEXT(cip); op_jless: if ((ucell)pri < (ucell)alt) cip=JUMPABS(code, cip); else cip=(cell *)((unsigned char *)cip+sizeof(cell)); NEXT(cip); op_jleq: if ((ucell)pri <= (ucell)alt) cip=JUMPABS(code, cip); else cip=(cell *)((unsigned char *)cip+sizeof(cell)); NEXT(cip); op_jgrtr: if ((ucell)pri > (ucell)alt) cip=JUMPABS(code, cip); else cip=(cell *)((unsigned char *)cip+sizeof(cell)); NEXT(cip); op_jgeq: if ((ucell)pri >= (ucell)alt) cip=JUMPABS(code, cip); else cip=(cell *)((unsigned char *)cip+sizeof(cell)); NEXT(cip); op_jsless: if (prialt) cip=JUMPABS(code, cip); else cip=(cell *)((unsigned char *)cip+sizeof(cell)); NEXT(cip); op_jsgeq: if (pri>=alt) cip=JUMPABS(code, cip); else cip=(cell *)((unsigned char *)cip+sizeof(cell)); NEXT(cip); op_shl: pri<<=alt; NEXT(cip); op_shr: pri=(ucell)pri >> (ucell)alt; NEXT(cip); op_sshr: pri>>=alt; NEXT(cip); op_shl_c_pri: GETPARAM(offs); pri<<=offs; NEXT(cip); op_shl_c_alt: GETPARAM(offs); alt<<=offs; NEXT(cip); op_shr_c_pri: GETPARAM(offs); pri=(ucell)pri >> (ucell)offs; NEXT(cip); op_shr_c_alt: GETPARAM(offs); alt=(ucell)alt >> (ucell)offs; NEXT(cip); op_smul: pri*=alt; NEXT(cip); op_sdiv: if (alt==0) ABORT(amx,AMX_ERR_DIVIDE); /* divide must always round down; this is a bit * involved to do in a machine-independent way. */ offs=(pri % alt + alt) % alt; /* true modulus */ pri=(pri - offs) / alt; /* division result */ alt=offs; NEXT(cip); op_sdiv_alt: if (pri==0) ABORT(amx,AMX_ERR_DIVIDE); /* divide must always round down; this is a bit * involved to do in a machine-independent way. */ offs=(alt % pri + pri) % pri; /* true modulus */ pri=(alt - offs) / pri; /* division result */ alt=offs; NEXT(cip); op_umul: pri=(ucell)pri * (ucell)alt; NEXT(cip); op_udiv: if (alt==0) ABORT(amx,AMX_ERR_DIVIDE); offs=(ucell)pri % (ucell)alt; /* temporary storage */ pri=(ucell)pri / (ucell)alt; alt=offs; NEXT(cip); op_udiv_alt: if (pri==0) ABORT(amx,AMX_ERR_DIVIDE); offs=(ucell)alt % (ucell)pri; /* temporary storage */ pri=(ucell)alt / (ucell)pri; alt=offs; NEXT(cip); op_add: pri+=alt; NEXT(cip); op_sub: pri-=alt; NEXT(cip); op_sub_alt: pri=alt-pri; NEXT(cip); op_and: pri&=alt; NEXT(cip); op_or: pri|=alt; NEXT(cip); op_xor: pri^=alt; NEXT(cip); op_not: pri=!pri; NEXT(cip); op_neg: pri=-pri; NEXT(cip); op_invert: pri=~pri; NEXT(cip); op_add_c: GETPARAM(offs); pri+=offs; NEXT(cip); op_smul_c: GETPARAM(offs); pri*=offs; NEXT(cip); op_zero_pri: pri=0; NEXT(cip); op_zero_alt: alt=0; NEXT(cip); op_zero: GETPARAM(offs); *(cell *)(data+(int)offs)=0; NEXT(cip); op_zero_s: GETPARAM(offs); *(cell *)(data+(int)frm+(int)offs)=0; NEXT(cip); op_sign_pri: if ((pri & 0xff)>=0x80) pri|= ~ (ucell)0xff; NEXT(cip); op_sign_alt: if ((alt & 0xff)>=0x80) alt|= ~ (ucell)0xff; NEXT(cip); op_eq: pri= pri==alt ? 1 : 0; NEXT(cip); op_neq: pri= pri!=alt ? 1 : 0; NEXT(cip); op_less: pri= (ucell)pri < (ucell)alt ? 1 : 0; NEXT(cip); op_leq: pri= (ucell)pri <= (ucell)alt ? 1 : 0; NEXT(cip); op_grtr: pri= (ucell)pri > (ucell)alt ? 1 : 0; NEXT(cip); op_geq: pri= (ucell)pri >= (ucell)alt ? 1 : 0; NEXT(cip); op_sless: pri= prialt ? 1 : 0; NEXT(cip); op_sgeq: pri= pri>=alt ? 1 : 0; NEXT(cip); op_eq_c_pri: GETPARAM(offs); pri= pri==offs ? 1 : 0; NEXT(cip); op_eq_c_alt: GETPARAM(offs); pri= alt==offs ? 1 : 0; NEXT(cip); op_inc_pri: pri++; NEXT(cip); op_inc_alt: alt++; NEXT(cip); op_inc: GETPARAM(offs); *(cell *)(data+(int)offs) += 1; NEXT(cip); op_inc_s: GETPARAM(offs); *(cell *)(data+(int)frm+(int)offs) += 1; NEXT(cip); op_inc_i: *(cell *)(data+(int)pri) += 1; NEXT(cip); op_dec_pri: pri--; NEXT(cip); op_dec_alt: alt--; NEXT(cip); op_dec: GETPARAM(offs); *(cell *)(data+(int)offs) -= 1; NEXT(cip); op_dec_s: GETPARAM(offs); *(cell *)(data+(int)frm+(int)offs) -= 1; NEXT(cip); op_dec_i: *(cell *)(data+(int)pri) -= 1; NEXT(cip); op_movs: GETPARAM(offs); /* verify top & bottom memory addresses, for both source and destination * addresses */ if ((pri>=hea && pri=(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); if (((pri+offs)>hea && (pri+offs)(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); if ((alt>=hea && alt=(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); if (((alt+offs)>hea && (alt+offs)(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); memcpy(data+(int)alt, data+(int)pri, (int)offs); NEXT(cip); op_cmps: GETPARAM(offs); /* verify top & bottom memory addresses, for both source and destination * addresses */ if ((pri>=hea && pri=(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); if (((pri+offs)>hea && (pri+offs)(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); if ((alt>=hea && alt=(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); if (((alt+offs)>hea && (alt+offs)(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); pri=memcmp(data+(int)alt, data+(int)pri, (int)offs); NEXT(cip); op_fill: GETPARAM(offs); /* verify top & bottom memory addresses */ if ((alt>=hea && alt=(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); if (((alt+offs)>hea && (alt+offs)(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); for (i=(int)alt; offs>=(int)sizeof(cell); i+=sizeof(cell), offs-=sizeof(cell)) *(cell *)(data+i) = pri; NEXT(cip); op_halt: GETPARAM(offs); if (retval!=NULL) *retval=pri; /* store complete status (stk and hea are already set in the ABORT macro) */ amx->frm=frm; amx->pri=pri; amx->alt=alt; amx->cip=(cell)((unsigned char*)cip-code); if (offs==AMX_ERR_SLEEP) { amx->reset_stk=reset_stk; amx->reset_hea=reset_hea; return (int)offs; } /* if */ ABORT(amx,(int)offs); op_bounds: GETPARAM(offs); if ((ucell)pri>(ucell)offs) ABORT(amx,AMX_ERR_BOUNDS); NEXT(cip); op_sysreq_pri: /* save a few registers */ amx->cip=(cell)((unsigned char *)cip-code); amx->hea=hea; amx->frm=frm; amx->stk=stk; num=amx->callback(amx,pri,&pri,(cell *)(data+(int)stk)); if (num!=AMX_ERR_NONE) { if (num==AMX_ERR_SLEEP) { amx->pri=pri; amx->alt=alt; amx->reset_stk=reset_stk; amx->reset_hea=reset_hea; return num; } /* if */ ABORT(amx,num); } /* if */ NEXT(cip); op_sysreq_c: GETPARAM(offs); /* save a few registers */ amx->cip=(cell)((unsigned char *)cip-code); amx->hea=hea; amx->frm=frm; amx->stk=stk; num=amx->callback(amx,offs,&pri,(cell *)(data+(int)stk)); if (num!=AMX_ERR_NONE) { if (num==AMX_ERR_SLEEP) { amx->pri=pri; amx->alt=alt; amx->reset_stk=reset_stk; amx->reset_hea=reset_hea; return num; } /* if */ ABORT(amx,num); } /* if */ NEXT(cip); op_sysreq_d: GETPARAM(offs); /* save a few registers */ amx->cip=(cell)((unsigned char *)cip-code); amx->hea=hea; amx->frm=frm; amx->stk=stk; pri=((AMX_NATIVE)offs)(amx,(cell *)(data+(int)stk)); if (amx->error!=AMX_ERR_NONE) { if (amx->error==AMX_ERR_SLEEP) { amx->pri=pri; amx->alt=alt; amx->reset_stk=reset_stk; amx->reset_hea=reset_hea; return num; } /* if */ ABORT(amx,amx->error); } /* if */ NEXT(cip); op_file: GETPARAM(offs); cip=(cell *)((unsigned char *)cip + (int)offs); assert(0); /* this code should not occur during execution */ NEXT(cip); op_line: SKIPPARAM(2); NEXT(cip); op_symbol: GETPARAM(offs); cip=(cell *)((unsigned char *)cip + (int)offs); NEXT(cip); op_srange: SKIPPARAM(2); NEXT(cip); op_symtag: SKIPPARAM(1); NEXT(cip); op_jump_pri: cip=(cell *)(code+(int)pri); NEXT(cip); op_switch: { cell *cptr; cptr=JUMPABS(code,cip)+1; /* +1, to skip the "casetbl" opcode */ cip=JUMPABS(code,cptr+1); /* preset to "none-matched" case */ num=(int)*cptr; /* number of records in the case table */ for (cptr+=2; num>0 && *cptr!=pri; num--,cptr+=2) /* nothing */; if (num>0) cip=JUMPABS(code,cptr+1); /* case found */ NEXT(cip); } op_casetbl: assert(0); /* this should not occur during execution */ NEXT(cip); op_swap_pri: offs=*(cell *)(data+(int)stk); *(cell *)(data+(int)stk)=pri; pri=offs; NEXT(cip); op_swap_alt: offs=*(cell *)(data+(int)stk); *(cell *)(data+(int)stk)=alt; alt=offs; NEXT(cip); op_pushaddr: GETPARAM(offs); PUSH(frm+offs); NEXT(cip); op_nop: NEXT(cip); op_break: if (amx->debug!=NULL) { /* store status */ amx->frm=frm; amx->stk=stk; amx->hea=hea; amx->cip=(cell)((unsigned char*)cip-code); num=amx->debug(amx); if (num!=AMX_ERR_NONE) { if (num==AMX_ERR_SLEEP) { amx->pri=pri; amx->alt=alt; amx->reset_stk=reset_stk; amx->reset_hea=reset_hea; return num; } /* if */ ABORT(amx,num); } /* if */ } /* if */ NEXT(cip); } #else /* ANSI C & assembler versions */ #if defined ASM32 || defined JIT /* For Watcom C/C++ use register calling convention (faster); for * Microsoft C/C++ (and most other C compilers) use "cdecl". * The important point is that you assemble AMXEXEC.ASM with the matching * calling convention, or the right JIT, respectively. * AMXJITR.ASM is for Watcom's register calling convention, AMXJITS.ASM and * AMXJITSN.ASM are for "cdecl". */ #if defined __WATCOMC__ #if !defined STACKARGS /* for AMX32.DLL */ extern cell amx_exec_asm(cell *regs,cell *retval,cell stp,cell hea); /* The following pragma tells the compiler into which registers * the parameters have to go. */ #pragma aux amx_exec_asm parm [eax] [edx] [ebx] [ecx]; extern cell amx_exec_jit(cell *regs,cell *retval,cell stp,cell hea); #pragma aux amx_exec_jit parm [eax] [edx] [ebx] [ecx]; #else extern cell __cdecl amx_exec_asm(cell *regs,cell *retval,cell stp,cell hea); extern cell __cdecl amx_exec_jit(cell *regs,cell *retval,cell stp,cell hea); #endif #elif defined __GNUC__ /* force "cdecl" by adding an "attribute" to the declaration */ extern cell amx_exec_asm(cell *regs,cell *retval,cell stp,cell hea) __attribute__((cdecl)); extern cell amx_exec_jit(cell *regs,cell *retval,cell stp,cell hea) __attribute__((cdecl)); #else /* force "cdecl" by specifying it as a "function class" with the "__cdecl" keyword */ extern cell __cdecl amx_exec_asm(cell *regs,cell *retval,cell stp,cell hea); extern cell __cdecl amx_exec_jit(cell *regs,cell *retval,cell stp,cell hea); #endif #endif int AMXAPI amx_Exec(AMX *amx, cell *retval, int index) { AMX_HEADER *hdr; AMX_FUNCSTUB *func; unsigned char *code, *data; cell pri,alt,stk,frm,hea; cell reset_stk, reset_hea, *cip; ucell codesize; int i; #if defined ASM32 || defined JIT cell parms[9]; /* registers and parameters for assembler AMX */ #else OPCODE op; cell offs; int num; #endif #if defined ASM32 extern void const *amx_opcodelist[]; #ifdef __WATCOMC__ #pragma aux amx_opcodelist "_*" #endif #endif #if defined JIT extern void const *amx_opcodelist_jit[]; #ifdef __WATCOMC__ #pragma aux amx_opcodelist_jit "_*" #endif #endif assert(amx!=NULL); #if defined ASM32 || defined JIT /* HACK: return label table (for amx_BrowseRelocate) if amx structure * is not passed. */ if ((amx->flags & AMX_FLAG_BROWSE)==AMX_FLAG_BROWSE) { assert(sizeof(cell)==sizeof(void *)); assert(retval!=NULL); #if defined ASM32 && defined JIT if ((amx->flags & AMX_FLAG_JITC)!=0) *retval=(cell)amx_opcodelist_jit; else *retval=(cell)amx_opcodelist; #elif defined ASM32 *retval=(cell)amx_opcodelist; #else *retval=(cell)amx_opcodelist_jit; #endif return 0; } /* if */ #endif if (amx->callback==NULL) return AMX_ERR_CALLBACK; if ((amx->flags & AMX_FLAG_NTVREG)==0) return AMX_ERR_NOTFOUND; if ((amx->flags & AMX_FLAG_RELOC)==0) return AMX_ERR_INIT; assert((amx->flags & AMX_FLAG_BROWSE)==0); /* set up the registers */ hdr=(AMX_HEADER *)amx->base; assert(hdr->magic==AMX_MAGIC); codesize=(ucell)(hdr->dat-hdr->cod); code=amx->base+(int)hdr->cod; data=(amx->data!=NULL) ? amx->data : amx->base+(int)hdr->dat; hea=amx->hea; stk=amx->stk; reset_stk=stk; reset_hea=hea; frm=alt=pri=0; /* silence up compiler */ /* get the start address */ if (index==AMX_EXEC_MAIN) { if (hdr->cip<0) return AMX_ERR_INDEX; cip=(cell *)(code + (int)hdr->cip); } else if (index==AMX_EXEC_CONT) { /* all registers: pri, alt, frm, cip, hea, stk, reset_stk, reset_hea */ frm=amx->frm; stk=amx->stk; hea=amx->hea; pri=amx->pri; alt=amx->alt; reset_stk=amx->reset_stk; reset_hea=amx->reset_hea; cip=(cell *)(code + (int)amx->cip); } else if (index<0) { return AMX_ERR_INDEX; } else { if (index>=(cell)NUMENTRIES(hdr,publics,natives)) return AMX_ERR_INDEX; func=GETENTRY(hdr,publics,index); cip=(cell *)(code + (int)func->address); } /* if */ /* check values just copied */ CHKSTACK(); CHKHEAP(); assert(check_endian()); /* sanity checks */ assert(OP_PUSH_PRI==36); assert(OP_PROC==46); assert(OP_SHL==65); assert(OP_SMUL==72); assert(OP_EQ==95); assert(OP_INC_PRI==107); assert(OP_MOVS==117); assert(OP_SYMBOL==126); #if PAWN_CELL_SIZE==16 assert(sizeof(cell)==2); #elif PAWN_CELL_SIZE==32 assert(sizeof(cell)==4); #elif PAWN_CELL_SIZE==64 assert(sizeof(cell)==8); #else #error Unsupported cell size #endif if (index!=AMX_EXEC_CONT) { reset_stk+=amx->paramcount*sizeof(cell); PUSH(amx->paramcount*sizeof(cell)); amx->paramcount=0; /* push the parameter count to the stack & reset */ #if defined ASM32 || defined JIT PUSH(RELOC_VALUE(code,0));/* relocated zero return address */ #else PUSH(0); /* zero return address */ #endif } /* if */ /* check stack/heap before starting to run */ CHKMARGIN(); /* start running */ #if defined ASM32 || defined JIT /* either the assembler abstract machine or the JIT; both by Marc Peter */ parms[0] = pri; parms[1] = alt; parms[2] = (cell)cip; parms[3] = (cell)data; parms[4] = stk; parms[5] = frm; parms[6] = (cell)amx; parms[7] = (cell)code; parms[8] = (cell)codesize; #if defined ASM32 && defined JIT if ((amx->flags & AMX_FLAG_JITC)!=0) i = amx_exec_jit(parms,retval,amx->stp,hea); else i = amx_exec_asm(parms,retval,amx->stp,hea); #elif defined ASM32 i = amx_exec_asm(parms,retval,amx->stp,hea); #else i = amx_exec_jit(parms,retval,amx->stp,hea); #endif if (i == AMX_ERR_SLEEP) { amx->reset_stk=reset_stk; amx->reset_hea=reset_hea; } else { /* remove parameters from the stack; do this the "hard" way, because * the assembler version has no internal knowledge of the local * variables, so any "clean" way would be a kludge anyway. */ amx->stk=reset_stk; amx->hea=reset_hea; } /* if */ return i; #else for ( ;; ) { op=(OPCODE) *cip++; switch (op) { case OP_LOAD_PRI: GETPARAM(offs); pri= * (cell *)(data+(int)offs); break; case OP_LOAD_ALT: GETPARAM(offs); alt= * (cell *)(data+(int)offs); break; case OP_LOAD_S_PRI: GETPARAM(offs); pri= * (cell *)(data+(int)frm+(int)offs); break; case OP_LOAD_S_ALT: GETPARAM(offs); alt= * (cell *)(data+(int)frm+(int)offs); break; case OP_LREF_PRI: GETPARAM(offs); offs= * (cell *)(data+(int)offs); pri= * (cell *)(data+(int)offs); break; case OP_LREF_ALT: GETPARAM(offs); offs= * (cell *)(data+(int)offs); alt= * (cell *)(data+(int)offs); break; case OP_LREF_S_PRI: GETPARAM(offs); offs= * (cell *)(data+(int)frm+(int)offs); pri= * (cell *)(data+(int)offs); break; case OP_LREF_S_ALT: GETPARAM(offs); offs= * (cell *)(data+(int)frm+(int)offs); alt= * (cell *)(data+(int)offs); break; case OP_LOAD_I: /* verify address */ if (pri>=hea && pri=(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); pri= * (cell *)(data+(int)pri); break; case OP_LODB_I: GETPARAM(offs); /* verify address */ if (pri>=hea && pri=(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); switch (offs) { case 1: pri= * (data+(int)pri); break; case 2: pri= * (uint16_t *)(data+(int)pri); break; case 4: pri= * (uint32_t *)(data+(int)pri); break; } /* switch */ break; case OP_CONST_PRI: GETPARAM(pri); break; case OP_CONST_ALT: GETPARAM(alt); break; case OP_ADDR_PRI: GETPARAM(pri); pri+=frm; break; case OP_ADDR_ALT: GETPARAM(alt); alt+=frm; break; case OP_STOR_PRI: GETPARAM(offs); *(cell *)(data+(int)offs)=pri; break; case OP_STOR_ALT: GETPARAM(offs); *(cell *)(data+(int)offs)=alt; break; case OP_STOR_S_PRI: GETPARAM(offs); *(cell *)(data+(int)frm+(int)offs)=pri; break; case OP_STOR_S_ALT: GETPARAM(offs); *(cell *)(data+(int)frm+(int)offs)=alt; break; case OP_SREF_PRI: GETPARAM(offs); offs= * (cell *)(data+(int)offs); *(cell *)(data+(int)offs)=pri; break; case OP_SREF_ALT: GETPARAM(offs); offs= * (cell *)(data+(int)offs); *(cell *)(data+(int)offs)=alt; break; case OP_SREF_S_PRI: GETPARAM(offs); offs= * (cell *)(data+(int)frm+(int)offs); *(cell *)(data+(int)offs)=pri; break; case OP_SREF_S_ALT: GETPARAM(offs); offs= * (cell *)(data+(int)frm+(int)offs); *(cell *)(data+(int)offs)=alt; break; case OP_STOR_I: /* verify address */ if (alt>=hea && alt=(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); *(cell *)(data+(int)alt)=pri; break; case OP_STRB_I: GETPARAM(offs); /* verify address */ if (alt>=hea && alt=(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); switch (offs) { case 1: *(data+(int)alt)=(unsigned char)pri; break; case 2: *(uint16_t *)(data+(int)alt)=(uint16_t)pri; break; case 4: *(uint32_t *)(data+(int)alt)=(uint32_t)pri; break; } /* switch */ break; case OP_LIDX: offs=pri*sizeof(cell)+alt; /* verify address */ if (offs>=hea && offs=(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); pri= * (cell *)(data+(int)offs); break; case OP_LIDX_B: GETPARAM(offs); offs=(pri << (int)offs)+alt; /* verify address */ if (offs>=hea && offs=(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); pri= * (cell *)(data+(int)offs); break; case OP_IDXADDR: pri=pri*sizeof(cell)+alt; break; case OP_IDXADDR_B: GETPARAM(offs); pri=(pri << (int)offs)+alt; break; case OP_ALIGN_PRI: GETPARAM(offs); #if BYTE_ORDER==LITTLE_ENDIAN if ((size_t)offscod; break; case 1: pri=hdr->dat; break; case 2: pri=hea; break; case 3: pri=amx->stp; break; case 4: pri=stk; break; case 5: pri=frm; break; case 6: pri=(cell)((unsigned char *)cip - code); break; } /* switch */ break; case OP_SCTRL: GETPARAM(offs); switch (offs) { case 0: case 1: case 3: /* cannot change these parameters */ break; case 2: hea=pri; break; case 4: stk=pri; break; case 5: frm=pri; break; case 6: cip=(cell *)(code + (int)pri); break; } /* switch */ break; case OP_MOVE_PRI: pri=alt; break; case OP_MOVE_ALT: alt=pri; break; case OP_XCHG: offs=pri; /* offs is a temporary variable */ pri=alt; alt=offs; break; case OP_PUSH_PRI: PUSH(pri); break; case OP_PUSH_ALT: PUSH(alt); break; case OP_PUSH_C: GETPARAM(offs); PUSH(offs); break; case OP_PUSH_R: GETPARAM(offs); while (offs--) PUSH(pri); break; case OP_PUSH: GETPARAM(offs); PUSH(* (cell *)(data+(int)offs)); break; case OP_PUSH_S: GETPARAM(offs); PUSH(* (cell *)(data+(int)frm+(int)offs)); break; case OP_POP_PRI: POP(pri); break; case OP_POP_ALT: POP(alt); break; case OP_STACK: GETPARAM(offs); alt=stk; stk+=offs; CHKMARGIN(); CHKSTACK(); break; case OP_HEAP: GETPARAM(offs); alt=hea; hea+=offs; CHKMARGIN(); CHKHEAP(); break; case OP_PROC: PUSH(frm); frm=stk; CHKMARGIN(); break; case OP_RET: POP(frm); POP(offs); /* verify the return address */ if ((ucell)offs>=codesize) ABORT(amx,AMX_ERR_MEMACCESS); cip=(cell *)(code+(int)offs); break; case OP_RETN: POP(frm); POP(offs); /* verify the return address */ if ((ucell)offs>=codesize) ABORT(amx,AMX_ERR_MEMACCESS); cip=(cell *)(code+(int)offs); stk+= *(cell *)(data+(int)stk) + sizeof(cell); /* remove parameters from the stack */ amx->stk=stk; break; case OP_CALL: PUSH(((unsigned char *)cip-code)+sizeof(cell));/* skip address */ cip=JUMPABS(code, cip); /* jump to the address */ break; case OP_CALL_PRI: PUSH((unsigned char *)cip-code); cip=(cell *)(code+(int)pri); break; case OP_JUMP: /* since the GETPARAM() macro modifies cip, you cannot * do GETPARAM(cip) directly */ cip=JUMPABS(code, cip); break; case OP_JREL: offs=*cip; cip=(cell *)((unsigned char *)cip + (int)offs + sizeof(cell)); break; case OP_JZER: if (pri==0) cip=JUMPABS(code, cip); else cip=(cell *)((unsigned char *)cip+sizeof(cell)); break; case OP_JNZ: if (pri!=0) cip=JUMPABS(code, cip); else cip=(cell *)((unsigned char *)cip+sizeof(cell)); break; case OP_JEQ: if (pri==alt) cip=JUMPABS(code, cip); else cip=(cell *)((unsigned char *)cip+sizeof(cell)); break; case OP_JNEQ: if (pri!=alt) cip=JUMPABS(code, cip); else cip=(cell *)((unsigned char *)cip+sizeof(cell)); break; case OP_JLESS: if ((ucell)pri < (ucell)alt) cip=JUMPABS(code, cip); else cip=(cell *)((unsigned char *)cip+sizeof(cell)); break; case OP_JLEQ: if ((ucell)pri <= (ucell)alt) cip=JUMPABS(code, cip); else cip=(cell *)((unsigned char *)cip+sizeof(cell)); break; case OP_JGRTR: if ((ucell)pri > (ucell)alt) cip=JUMPABS(code, cip); else cip=(cell *)((unsigned char *)cip+sizeof(cell)); break; case OP_JGEQ: if ((ucell)pri >= (ucell)alt) cip=JUMPABS(code, cip); else cip=(cell *)((unsigned char *)cip+sizeof(cell)); break; case OP_JSLESS: if (prialt) cip=JUMPABS(code, cip); else cip=(cell *)((unsigned char *)cip+sizeof(cell)); break; case OP_JSGEQ: if (pri>=alt) cip=JUMPABS(code, cip); else cip=(cell *)((unsigned char *)cip+sizeof(cell)); break; case OP_SHL: pri<<=alt; break; case OP_SHR: pri=(ucell)pri >> (int)alt; break; case OP_SSHR: pri>>=alt; break; case OP_SHL_C_PRI: GETPARAM(offs); pri<<=offs; break; case OP_SHL_C_ALT: GETPARAM(offs); alt<<=offs; break; case OP_SHR_C_PRI: GETPARAM(offs); pri=(ucell)pri >> (int)offs; break; case OP_SHR_C_ALT: GETPARAM(offs); alt=(ucell)alt >> (int)offs; break; case OP_SMUL: pri*=alt; break; case OP_SDIV: if (alt==0) ABORT(amx,AMX_ERR_DIVIDE); /* divide must always round down; this is a bit * involved to do in a machine-independent way. */ offs=(pri % alt + alt) % alt; /* true modulus */ pri=(pri - offs) / alt; /* division result */ alt=offs; break; case OP_SDIV_ALT: if (pri==0) ABORT(amx,AMX_ERR_DIVIDE); /* divide must always round down; this is a bit * involved to do in a machine-independent way. */ offs=(alt % pri + pri) % pri; /* true modulus */ pri=(alt - offs) / pri; /* division result */ alt=offs; break; case OP_UMUL: pri=(ucell)pri * (ucell)alt; break; case OP_UDIV: if (alt==0) ABORT(amx,AMX_ERR_DIVIDE); offs=(ucell)pri % (ucell)alt; /* temporary storage */ pri=(ucell)pri / (ucell)alt; alt=offs; break; case OP_UDIV_ALT: if (pri==0) ABORT(amx,AMX_ERR_DIVIDE); offs=(ucell)alt % (ucell)pri; /* temporary storage */ pri=(ucell)alt / (ucell)pri; alt=offs; break; case OP_ADD: pri+=alt; break; case OP_SUB: pri-=alt; break; case OP_SUB_ALT: pri=alt-pri; break; case OP_AND: pri&=alt; break; case OP_OR: pri|=alt; break; case OP_XOR: pri^=alt; break; case OP_NOT: pri=!pri; break; case OP_NEG: pri=-pri; break; case OP_INVERT: pri=~pri; break; case OP_ADD_C: GETPARAM(offs); pri+=offs; break; case OP_SMUL_C: GETPARAM(offs); pri*=offs; break; case OP_ZERO_PRI: pri=0; break; case OP_ZERO_ALT: alt=0; break; case OP_ZERO: GETPARAM(offs); *(cell *)(data+(int)offs)=0; break; case OP_ZERO_S: GETPARAM(offs); *(cell *)(data+(int)frm+(int)offs)=0; break; case OP_SIGN_PRI: if ((pri & 0xff)>=0x80) pri|= ~ (ucell)0xff; break; case OP_SIGN_ALT: if ((alt & 0xff)>=0x80) alt|= ~ (ucell)0xff; break; case OP_EQ: pri= pri==alt ? 1 : 0; break; case OP_NEQ: pri= pri!=alt ? 1 : 0; break; case OP_LESS: pri= (ucell)pri < (ucell)alt ? 1 : 0; break; case OP_LEQ: pri= (ucell)pri <= (ucell)alt ? 1 : 0; break; case OP_GRTR: pri= (ucell)pri > (ucell)alt ? 1 : 0; break; case OP_GEQ: pri= (ucell)pri >= (ucell)alt ? 1 : 0; break; case OP_SLESS: pri= prialt ? 1 : 0; break; case OP_SGEQ: pri= pri>=alt ? 1 : 0; break; case OP_EQ_C_PRI: GETPARAM(offs); pri= pri==offs ? 1 : 0; break; case OP_EQ_C_ALT: GETPARAM(offs); pri= alt==offs ? 1 : 0; break; case OP_INC_PRI: pri++; break; case OP_INC_ALT: alt++; break; case OP_INC: GETPARAM(offs); *(cell *)(data+(int)offs) += 1; break; case OP_INC_S: GETPARAM(offs); *(cell *)(data+(int)frm+(int)offs) += 1; break; case OP_INC_I: *(cell *)(data+(int)pri) += 1; break; case OP_DEC_PRI: pri--; break; case OP_DEC_ALT: alt--; break; case OP_DEC: GETPARAM(offs); *(cell *)(data+(int)offs) -= 1; break; case OP_DEC_S: GETPARAM(offs); *(cell *)(data+(int)frm+(int)offs) -= 1; break; case OP_DEC_I: *(cell *)(data+(int)pri) -= 1; break; case OP_MOVS: GETPARAM(offs); /* verify top & bottom memory addresses, for both source and destination * addresses */ if (pri>=hea && pri=(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); if ((pri+offs)>hea && (pri+offs)(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); if (alt>=hea && alt=(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); if ((alt+offs)>hea && (alt+offs)(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); memcpy(data+(int)alt, data+(int)pri, (int)offs); break; case OP_CMPS: GETPARAM(offs); /* verify top & bottom memory addresses, for both source and destination * addresses */ if (pri>=hea && pri=(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); if ((pri+offs)>hea && (pri+offs)(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); if (alt>=hea && alt=(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); if ((alt+offs)>hea && (alt+offs)(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); pri=memcmp(data+(int)alt, data+(int)pri, (int)offs); break; case OP_FILL: GETPARAM(offs); /* verify top & bottom memory addresses (destination only) */ if (alt>=hea && alt=(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); if ((alt+offs)>hea && (alt+offs)(ucell)amx->stp) ABORT(amx,AMX_ERR_MEMACCESS); for (i=(int)alt; (size_t)offs>=sizeof(cell); i+=sizeof(cell), offs-=sizeof(cell)) *(cell *)(data+i) = pri; break; case OP_HALT: GETPARAM(offs); if (retval!=NULL) *retval=pri; /* store complete status (stk and hea are already set in the ABORT macro) */ amx->frm=frm; amx->pri=pri; amx->alt=alt; amx->cip=(cell)((unsigned char*)cip-code); if (offs==AMX_ERR_SLEEP) { amx->reset_stk=reset_stk; amx->reset_hea=reset_hea; return (int)offs; } /* if */ ABORT(amx,(int)offs); case OP_BOUNDS: GETPARAM(offs); if ((ucell)pri>(ucell)offs) ABORT(amx,AMX_ERR_BOUNDS); break; case OP_SYSREQ_PRI: /* save a few registers */ amx->cip=(cell)((unsigned char *)cip-code); amx->hea=hea; amx->frm=frm; amx->stk=stk; num=amx->callback(amx,pri,&pri,(cell *)(data+(int)stk)); if (num!=AMX_ERR_NONE) { if (num==AMX_ERR_SLEEP) { amx->pri=pri; amx->alt=alt; amx->reset_stk=reset_stk; amx->reset_hea=reset_hea; return num; } /* if */ ABORT(amx,num); } /* if */ break; case OP_SYSREQ_C: GETPARAM(offs); /* save a few registers */ amx->cip=(cell)((unsigned char *)cip-code); amx->hea=hea; amx->frm=frm; amx->stk=stk; num=amx->callback(amx,offs,&pri,(cell *)(data+(int)stk)); if (num!=AMX_ERR_NONE) { if (num==AMX_ERR_SLEEP) { amx->pri=pri; amx->alt=alt; amx->reset_stk=reset_stk; amx->reset_hea=reset_hea; return num; } /* if */ ABORT(amx,num); } /* if */ break; case OP_SYSREQ_D: GETPARAM(offs); /* save a few registers */ amx->cip=(cell)((unsigned char *)cip-code); amx->hea=hea; amx->frm=frm; amx->stk=stk; pri=((AMX_NATIVE)offs)(amx,(cell *)(data+(int)stk)); if (amx->error!=AMX_ERR_NONE) { if (amx->error==AMX_ERR_SLEEP) { amx->pri=pri; amx->alt=alt; amx->reset_stk=reset_stk; amx->reset_hea=reset_hea; return num; } /* if */ ABORT(amx,amx->error); } /* if */ break; case OP_LINE: SKIPPARAM(2); break; case OP_SYMBOL: GETPARAM(offs); cip=(cell *)((unsigned char *)cip + (int)offs); break; case OP_SRANGE: SKIPPARAM(2); break; case OP_SYMTAG: SKIPPARAM(1); break; case OP_JUMP_PRI: cip=(cell *)(code+(int)pri); break; case OP_SWITCH: { cell *cptr; cptr=JUMPABS(code,cip)+1; /* +1, to skip the "casetbl" opcode */ cip=JUMPABS(code,cptr+1); /* preset to "none-matched" case */ num=(int)*cptr; /* number of records in the case table */ for (cptr+=2; num>0 && *cptr!=pri; num--,cptr+=2) /* nothing */; if (num>0) cip=JUMPABS(code,cptr+1); /* case found */ break; } /* case */ case OP_SWAP_PRI: offs=*(cell *)(data+(int)stk); *(cell *)(data+(int)stk)=pri; pri=offs; break; case OP_SWAP_ALT: offs=*(cell *)(data+(int)stk); *(cell *)(data+(int)stk)=alt; alt=offs; break; case OP_PUSHADDR: GETPARAM(offs); PUSH(frm+offs); break; case OP_NOP: break; case OP_BREAK: assert((amx->flags & AMX_FLAG_BROWSE)==0); if (amx->debug!=NULL) { /* store status */ amx->frm=frm; amx->stk=stk; amx->hea=hea; amx->cip=(cell)((unsigned char*)cip-code); num=amx->debug(amx); if (num!=AMX_ERR_NONE) { if (num==AMX_ERR_SLEEP) { amx->pri=pri; amx->alt=alt; amx->reset_stk=reset_stk; amx->reset_hea=reset_hea; return num; } /* if */ ABORT(amx,num); } /* if */ } /* if */ break; default: /* case OP_FILE: should not occur during execution * case OP_CASETBL: should not occur during execution */ assert(0); ABORT(amx,AMX_ERR_INVINSTR); } /* switch */ } /* for */ #endif } #endif /* __GNUC__ */ #endif /* AMX_EXEC || AMX_INIT */ #if defined AMX_SETCALLBACK int AMXAPI amx_SetCallback(AMX *amx,AMX_CALLBACK callback) { assert(amx!=NULL); assert(callback!=NULL); amx->callback=callback; return AMX_ERR_NONE; } #endif /* AMX_SETCALLBACK */ #if defined AMX_SETDEBUGHOOK int AMXAPI amx_SetDebugHook(AMX *amx,AMX_DEBUG debug) { assert(amx!=NULL); amx->debug=debug; return AMX_ERR_NONE; } #endif /* AMX_SETDEBUGHOOK */ #if defined AMX_RAISEERROR int AMXAPI amx_RaiseError(AMX *amx, int error) { assert(error>0); amx->error=error; return AMX_ERR_NONE; } #endif /* AMX_RAISEERROR */ #if defined AMX_GETADDR int AMXAPI amx_GetAddr(AMX *amx,cell amx_addr,cell **phys_addr) { AMX_HEADER *hdr; unsigned char *data; assert(amx!=NULL); hdr=(AMX_HEADER *)amx->base; assert(hdr!=NULL); assert(hdr->magic==AMX_MAGIC); data=(amx->data!=NULL) ? amx->data : amx->base+(int)hdr->dat; assert(phys_addr!=NULL); if ((amx_addr>=amx->hea && amx_addrstk) || amx_addr<0 || amx_addr>=amx->stp) { *phys_addr=NULL; return AMX_ERR_MEMACCESS; } /* if */ *phys_addr=(cell *)(data + (int)amx_addr); return AMX_ERR_NONE; } #endif /* AMX_GETADDR */ #if defined AMX_ALLOT || defined AMX_EXEC int AMXAPI amx_Allot(AMX *amx,int cells,cell *amx_addr,cell **phys_addr) { AMX_HEADER *hdr; unsigned char *data; assert(amx!=NULL); hdr=(AMX_HEADER *)amx->base; assert(hdr!=NULL); assert(hdr->magic==AMX_MAGIC); data=(amx->data!=NULL) ? amx->data : amx->base+(int)hdr->dat; if (amx->stk - amx->hea - cells*sizeof(cell) < STKMARGIN) return AMX_ERR_MEMORY; assert(amx_addr!=NULL); assert(phys_addr!=NULL); *amx_addr=amx->hea; *phys_addr=(cell *)(data + (int)amx->hea); amx->hea += cells*sizeof(cell); return AMX_ERR_NONE; } int AMXAPI amx_Release(AMX *amx,cell amx_addr) { if (amx->hea > amx_addr) amx->hea=amx_addr; return AMX_ERR_NONE; } #endif /* AMX_ALLOT */ #if defined AMX_XXXSTRING || defined AMX_UTF8XXX #define CHARBITS (8*sizeof(char)) #if PAWN_CELL_SIZE==16 #define CHARMASK (0xffffu << 8*(2-sizeof(char))) #elif PAWN_CELL_SIZE==32 #define CHARMASK (0xffffffffuL << 8*(4-sizeof(char))) #elif PAWN_CELL_SIZE==64 #define CHARMASK (0xffffffffffffffffuLL << 8*(8-sizeof(char))) #else #error Unsupported cell size #endif int AMXAPI amx_StrLen(const cell *cstr, int *length) { int len; #if BYTE_ORDER==LITTLE_ENDIAN cell c; #endif assert(length!=NULL); if (cstr==NULL) { *length=0; return AMX_ERR_PARAMS; } /* if */ if ((ucell)*cstr>UNPACKEDMAX) { /* packed string */ assert(sizeof(char)==1); len=strlen((char *)cstr); /* find '\0' */ assert(check_endian()); #if BYTE_ORDER==LITTLE_ENDIAN /* on Little Endian machines, toggle the last bytes */ c=cstr[len/sizeof(cell)]; /* get last cell */ len=len - len % sizeof(cell); /* len = multiple of "cell" bytes */ while ((c & CHARMASK)!=0) { len++; c <<= 8*sizeof(char); } /* if */ #endif } else { for (len=0; cstr[len]!=0; len++) /* nothing */; } /* if */ *length = len; return AMX_ERR_NONE; } #endif #if defined AMX_XXXSTRING || defined AMX_EXEC int AMXAPI amx_SetString(cell *dest,const char *source,int pack,int use_wchar,size_t size) { /* the memory blocks should not overlap */ int len, i; assert(UNLIMITED>0); #if defined AMX_ANSIONLY (void)use_wchar; len=strlen(source); #else len= use_wchar ? wcslen((const wchar_t*)source) : strlen(source); #endif if (pack) { /* create a packed string */ if (size=size*sizeof(cell)) len=size*sizeof(cell)-1; dest[len/sizeof(cell)]=0; /* clear last bytes of last (semi-filled) cell*/ #if defined AMX_ANSIONLY memcpy(dest,source,len); #else if (use_wchar) { for (i=0; i=0) swapcell((ucell *)&dest[len--]); #endif } else { /* create an unpacked string */ if (size=size) len=size-1; #if defined AMX_ANSIONLY for (i=0; iUNPACKEDMAX) { /* source string is packed */ cell c = 0; /* to avoid a compiler warning */ int i=sizeof(cell)-1; while ((size_t)len> i*CHARBITS); #else if (use_wchar) ((wchar_t*)dest)[len++]=(char)(c >> i*CHARBITS); else dest[len++]=(char)(c >> i*CHARBITS); #endif if (dest[len-1]=='\0') break; /* terminating zero character found */ i=(i+sizeof(cell)-1) % sizeof(cell); } /* for */ } else { /* source string is unpacked */ #if defined AMX_ANSIONLY while (*source!=0 && (size_t)len=size) len=size-1; if (len>=0) dest[len]='\0'; /* store terminator */ return AMX_ERR_NONE; } #endif /* AMX_XXXSTRING */ #if defined AMX_UTF8XXX #if defined __BORLANDC__ #pragma warn -amb -8000 /* ambiguous operators need parentheses */ #endif /* amx_UTF8Get() * Extract a single UTF-8 encoded character from a string and return a pointer * to the character just behind that UTF-8 character. The parameters "endptr" * and "value" may be NULL. * If the code is not valid UTF-8, "endptr" has the value of the input * parameter "string" and "value" is zero. */ int AMXAPI amx_UTF8Get(const char *string, const char **endptr, cell *value) { static const char utf8_count[16]={ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 4 }; static const long utf8_lowmark[5] = { 0x80, 0x800, 0x10000L, 0x200000L, 0x4000000L }; unsigned char c; cell result; int followup; assert(string!=NULL); if (value!=NULL) /* preset, in case of an error */ *value=0; if (endptr!=NULL) *endptr=string; c = *(const unsigned char*)string++; if (c<0x80) { /* ASCII */ result=c; } else { if (c<0xc0 || c>=0xfe) return AMX_ERR_PARAMS; /* invalid or "follower" code, quit with error */ /* At this point we know that the two top bits of c are ones. The two * bottom bits are always part of the code. We only need to consider * the 4 remaining bits; i.e., a 16-byte table. This is "utf8_count[]". * (Actually the utf8_count[] table records the number of follow-up * bytes minus 1. This is just for convenience.) */ assert((c & 0xc0)==0xc0); followup=(int)utf8_count[(c >> 2) & 0x0f]; /* The mask depends on the code length; this is just a very simple * relation. */ #define utf8_mask (0x1f >> followup) result= c & utf8_mask; /* Collect the follow-up codes using a drop-through switch statement; * this avoids a loop. In each case, verify the two leading bits. */ assert(followup>=0 && followup<=4); switch (followup) { case 4: if (((c=*string++) & 0xc0) != 0x80) goto error; result = (result << 6) | c & 0x3f; case 3: if (((c=*string++) & 0xc0) != 0x80) goto error; result = (result << 6) | c & 0x3f; case 2: if (((c=*string++) & 0xc0) != 0x80) goto error; result = (result << 6) | c & 0x3f; case 1: if (((c=*string++) & 0xc0) != 0x80) goto error; result = (result << 6) | c & 0x3f; case 0: if (((c=*string++) & 0xc0) != 0x80) goto error; result = (result << 6) | c & 0x3f; } /* switch */ /* Do additional checks: shortest encoding & reserved positions. The * lowmark limits also depends on the code length; it can be read from * a table with 5 elements. This is "utf8_lowmark[]". */ if (result=0xd800 && result<=0xdfff || result==0xfffe || result==0xffff) goto error; } /* if */ if (value!=NULL) *value=result; if (endptr!=NULL) *endptr=string; return AMX_ERR_NONE; error: return AMX_ERR_PARAMS; } /* amx_UTF8Put() * Encode a single character into a byte string. The character may result in * a string of up to 6 bytes. The function returns an error code if "maxchars" * is lower than the required number of characters; in this case nothing is * stored. * The function does not zero-terminate the string. */ int AMXAPI amx_UTF8Put(char *string, char **endptr, int maxchars, cell value) { assert(string!=NULL); if (endptr!=NULL) /* preset, in case of an error */ *endptr=string; if (value<0x80) { /* 0xxxxxxx */ if (maxchars < 1) goto error; *string++ = (char)value; } else if (value<0x800) { /* 110xxxxx 10xxxxxx */ if (maxchars < 2) goto error; *string++ = (char)((value>>6) & 0x1f | 0xc0); *string++ = (char)(value & 0x3f | 0x80); } else if (value<0x10000) { /* 1110xxxx 10xxxxxx 10xxxxxx (16 bits, BMP plane) */ if (maxchars < 3) goto error; if (value>=0xd800 && value<=0xdfff || value==0xfffe || value==0xffff) goto error; /* surrogate pairs and invalid characters */ *string++ = (char)((value>>12) & 0x0f | 0xe0); *string++ = (char)((value>>6) & 0x3f | 0x80); *string++ = (char)(value & 0x3f | 0x80); } else if (value<0x200000) { /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ if (maxchars < 4) goto error; *string++ = (char)((value>>18) & 0x07 | 0xf0); *string++ = (char)((value>>12) & 0x3f | 0x80); *string++ = (char)((value>>6) & 0x3f | 0x80); *string++ = (char)(value & 0x3f | 0x80); } else if (value<0x4000000) { /* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ if (maxchars < 5) goto error; *string++ = (char)((value>>24) & 0x03 | 0xf8); *string++ = (char)((value>>18) & 0x3f | 0x80); *string++ = (char)((value>>12) & 0x3f | 0x80); *string++ = (char)((value>>6) & 0x3f | 0x80); *string++ = (char)(value & 0x3f | 0x80); } else { /* 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx (31 bits) */ if (maxchars < 6) goto error; *string++ = (char)((value>>30) & 0x01 | 0xfc); *string++ = (char)((value>>24) & 0x3f | 0x80); *string++ = (char)((value>>18) & 0x3f | 0x80); *string++ = (char)((value>>12) & 0x3f | 0x80); *string++ = (char)((value>>6) & 0x3f | 0x80); *string++ = (char)(value & 0x3f | 0x80); } /* if */ if (endptr!=NULL) *endptr=string; return AMX_ERR_NONE; error: return AMX_ERR_PARAMS; } /* amx_UTF8Check() * Run through a zero-terminated string and check the validity of the UTF-8 * encoding. The function returns an error code, it is AMX_ERR_NONE if the * string is valid UTF-8 (or valid ASCII for that matter). */ int AMXAPI amx_UTF8Check(const char *string, int *length) { int err=AMX_ERR_NONE; int len=0; while (err==AMX_ERR_NONE && *string!='\0') { err=amx_UTF8Get(string,&string,NULL); len++; } /* while */ if (length!=NULL) *length=len; return err; } /* amx_UTF8Len() * Run through a wide string and return how many 8-bit characters are needed to * store the string in UTF-8 format. The returned cound excludes the terminating * zero byte. The function returns an error code. */ int AMXAPI amx_UTF8Len(const cell *cstr, int *length) { int err; assert(length!=NULL); err=amx_StrLen(cstr, length); if (err==AMX_ERR_NONE && (ucell)*cstr<=UNPACKEDMAX) { char buffer[10]; /* maximum UTF-8 code is 6 characters */ char *endptr; int len=*length, count=0; while (len-->0) { amx_UTF8Put(buffer, &endptr, sizeof buffer, *cstr++); count+=(int)(endptr-buffer); } /* while */ *length=count; } /* while */ return err; } #endif /* AMX_UTF8XXX */