diff --git a/amxmodx/amx.cpp b/amxmodx/amx.cpp new file mode 100755 index 00000000..bf989b11 --- /dev/null +++ b/amxmodx/amx.cpp @@ -0,0 +1,4122 @@ +/* Abstract Machine for the Small compiler + * + * Copyright (c) ITB CompuPhase, 1997-2004 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id$ + */ + +// not used +#define AMX_NODYNALOAD + +// bad bad workaround but we have to prevent a compiler crash :/ +#if (BUILD_PLATFORM== WINDOWS) && (BUILD_TYPE== RELEASE) && (BUILD_COMPILER== MSVC) && (SMALL_CELL_SIZE== 64) + #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 + #include + #if !defined AMX_NODYNALOAD + #include + #endif +#endif +#if defined __LCC__ || defined __GNUC__ + #include /* for wcslen() */ +#endif +#if (defined _Windows && !defined AMX_NODYNALOAD) || defined JIT + #include +#endif + +// this file does not include amxmodx.h, so we have to include the memory manager here +#ifdef MEMORY_TEST +#include "mmgr/mmgr.h" +#endif // MEMORY_TEST + +#include "amx.h" + +#ifdef JIT +# ifdef __WIN32__ +# include // DWORD, VirtualProtect, ... +# else + // :TODO: +# endif +#endif // JIT + +/* 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() and amx_Align32() */ + #define AMX_ALLOT /* amx_Allot() and amx_Release() */ + #define AMX_CLEANUP /* amx_Cleanup() */ + #define AMX_CLONE /* amx_Clone() */ + #define AMX_EXEC /* amx_Exec() and amx_Execv() */ + #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_StrLength(), 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 + +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, + 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_NUM_OPCODES +} OPCODE; + +typedef struct tagFUNCSTUBNT { + uint32_t address PACKED; + uint32_t nameofs PACKED; +} FUNCSTUBNT PACKED; + +#define USENAMETABLE(hdr) \ + ((hdr)->defsize==sizeof(FUNCSTUBNT)) +#define NUMENTRIES(hdr,field,nextfield) \ + (int)(((hdr)->nextfield - (hdr)->field) / (hdr)->defsize) +#define GETENTRY(hdr,table,index) \ + (AMX_FUNCSTUB *)((unsigned char*)(hdr) + (int)(hdr)->table + (int)index*(hdr)->defsize) +#define GETENTRYNAME(hdr,entry) \ + ( USENAMETABLE(hdr) \ + ? (char *)((unsigned char*)(hdr) + ((FUNCSTUBNT*)(entry))->nameofs) \ + : ((AMX_FUNCSTUB*)(entry))->name ) + +static int amx_LittleEndian = -1; /* set to TRUE for Little Endian, and + * to FALSE for Big Endian */ + +static void init_little_endian(void) +{ + if (amx_LittleEndian < 0) { /* initialize this variable only once */ + 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); + amx_LittleEndian= *ptr==0xff; + } /* if */ +} + +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; +} + +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; +} + +#if 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 *amx_Align16(uint16_t *v) +{ + assert(sizeof(*v)==2); + init_little_endian(); + if (!amx_LittleEndian) + swap16(v); + return v; +} + +uint32_t *amx_Align32(uint32_t *v) +{ + assert(sizeof(cell)==4); + init_little_endian(); + if (!amx_LittleEndian) + swap32(v); + return v; +} + +#if defined _I64_MAX || defined HAVE_I64 +uint64_t *amx_Align64(uint64_t *v) +{ + assert(sizeof(cell)==8); + init_little_endian(); + if (!amx_LittleEndian) + swap64(v); + return v; +} +#endif /* _I64_MAX || HAVE_I64 */ +#endif /* AMX_ALIGN || AMX_INIT */ + +#if SMALL_CELL_SIZE==16 + #define swapcell swap16 +#elif SMALL_CELL_SIZE==32 + #define swapcell swap32 +#elif SMALL_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); + assert(index>=0 && index<(cell)NUMENTRIES(hdr,natives,libraries)); + func=GETENTRY(hdr,natives,index); + f=(AMX_NATIVE)func->address; + 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) + */ + 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 || defined JIT + if (*(cell*)code==index) { +#else + if (*(cell*)code!=OP_SYSREQ_PRI) { + assert(*(cell*)(code-4)==OP_SYSREQ_C); + assert(*(cell*)code==index); +#endif + *(cell*)(code-4)=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; +} + +#if defined __BORLANDC__ || defined __WATCOMC__ + #pragma argsused +#endif +int AMXAPI amx_Debug(AMX *amx) +{ + return AMX_ERR_DEBUG; +} + +#if defined JIT + extern int AMXAPI getMaxCodeSize(void); + extern int AMXAPI asm_runJIT(void *sourceAMXbase, void *jumparray, void *compiledAMXbase); +#endif + +#if SMALL_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) ) + +static int amx_BrowseRelocate(AMX *amx) +{ + AMX_HEADER *hdr; + unsigned char *code; + cell cip; + long codesize; + OPCODE op; + int debug; + int last_sym_global = 0; + #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; + + /* 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); + + /* check the debug hook */ + amx->dbgcode=DBG_INIT; + assert(amx->flags==0); + amx->flags=AMX_FLAG_BROWSE; + debug= amx->debug(amx)==AMX_ERR_NONE; + if (debug) + amx->flags|=AMX_FLAG_DEBUG; + + #if defined __GNUC__ || defined ASM32 || defined JIT + amx_Exec(amx, (cell*)&opcode_list, 0, 0); + #if !defined JIT + amx->sysreq_d=opcode_list[OP_SYSREQ_D]; + #endif + #else + /* ANSI C + * 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 + */ + amx->sysreq_d= (sizeof(AMX_NATIVE)<=sizeof(cell)) ? OP_SYSREQ_D : 0; + #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 symbol */ + *(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: + 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: { + cell num; + DBGPARAM(num); + DBGPARAM(amx->curfile); + amx->dbgname=(char *)(code+(int)cip); + cip+=num - sizeof(cell); + if (debug) { + assert(amx->flags==(AMX_FLAG_DEBUG | AMX_FLAG_BROWSE)); + amx->dbgcode=DBG_FILE; + amx->debug(amx); + } /* if */ + break; + } /* case */ + case OP_LINE: + DBGPARAM(amx->curline); + DBGPARAM(amx->curfile); + if (debug) { + assert(amx->flags==(AMX_FLAG_DEBUG | AMX_FLAG_BROWSE)); + amx->dbgcode=DBG_LINE; + amx->debug(amx); + } /* if */ + break; + case OP_SYMBOL: { + cell num; + DBGPARAM(num); + DBGPARAM(amx->dbgaddr); + DBGPARAM(amx->dbgparam); + amx->dbgname=(char *)(code+(int)cip); + cip+=num - 2*sizeof(cell); + last_sym_global = (amx->dbgparam >> 8)==0; + if (debug && last_sym_global) { /* do global symbols only */ + assert(amx->flags==(AMX_FLAG_DEBUG | AMX_FLAG_BROWSE)); + amx->dbgcode=DBG_SYMBOL; + amx->debug(amx); + } /* if */ + break; + } /* case */ + case OP_SRANGE: + DBGPARAM(amx->dbgaddr); /* dimension level */ + DBGPARAM(amx->dbgparam); /* length */ + if (debug && last_sym_global) { /* do global symbols only */ + assert(amx->flags==(AMX_FLAG_DEBUG | AMX_FLAG_BROWSE)); + amx->dbgcode=DBG_SRANGE; + amx->debug(amx); + } /* if */ + break; + case OP_SYMTAG: + DBGPARAM(amx->dbgparam); /* tag id */ + if (debug && last_sym_global) { /* do global symbols only */ + assert(amx->flags==(AMX_FLAG_DEBUG | AMX_FLAG_BROWSE)); + amx->dbgcode=DBG_SYMTAG; + amx->debug(amx); + } /* if */ + 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; +} + +static void expand(unsigned char *code, long codesize, long memsize) +{ + ucell c; + struct { + long memloc; + ucell c; + } spare[AMX_EXPANDMARGIN]; + 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(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 < 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_EXPANDMARGIN; + 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 + */ + init_little_endian(); + if (!amx_LittleEndian) { + 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); + } /* if */ + + 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(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 + hdr->nametable); + amx_Align16(namelength); + if (*namelength>sNAMEMAX) + return AMX_ERR_FORMAT; + } /* if */ + if (hdr->stp<=0) + return AMX_ERR_FORMAT; + if (!amx_LittleEndian && (hdr->flags & AMX_FLAG_COMPACT)==0) + return AMX_ERR_FORMAT; /* On Big Endian machines, use compact encoding */ + assert((hdr->flags & AMX_FLAG_COMPACT)!=0 || hdr->hea == hdr->size); + if ((hdr->flags & AMX_FLAG_COMPACT)!=0) + expand((unsigned char *)program+(int)hdr->cod, + hdr->size - hdr->cod, hdr->hea - hdr->cod); + + 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; + if (amx->debug==NULL) + amx->debug=amx_Debug; + amx->curline=0; + amx->curfile=0; + amx->data=NULL; + + /* also align all addresses in the public function, public variable and + * public tag tables --offsets into the name table (if present) must also + * be swapped. + */ + if (!amx_LittleEndian) { + AMX_FUNCSTUB *fs; + int i,num; + + fs=GETENTRY(hdr,publics,0); + assert(hdr->publics<=hdr->natives); + num=NUMENTRIES(hdr,publics,natives); + for (i=0; iaddress); + if (USENAMETABLE(hdr)) + amx_Align32(&((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(&((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(&((FUNCSTUBNT*)fs)->nameofs); + fs=(AMX_FUNCSTUB*)((unsigned char *)fs+hdr->defsize); + } /* for */ + } /* if */ + + /* relocate call and jump instructions, optionally gather debug information */ + amx_BrowseRelocate(amx); + + /* load any extension modules that the AMX refers to */ + #if (defined _Windows || defined LINUX) && !defined AMX_NODYNALOAD + hdr=(AMX_HEADER *)amx->base; + numlibraries=NUMENTRIES(hdr,libraries,pubvars); + for (i=0; iaddress=(uint32_t)hlib; + } /* for */ + #endif + + return AMX_ERR_NONE; +} + +#if defined JIT + + #if defined __WIN32__ /* this also applies to Win32 "console" applications */ + + int memoryFullAccess( void* addr, int len ) + { + DWORD op; + if ( VirtualProtect( addr, len, PAGE_EXECUTE_READWRITE, &op ) ) + return op; + return 0; + } + + int memorySetAccess( void* addr, int len, int access ) + { + DWORD op; + if ( access == 0 ) + return 0; + return VirtualProtect( addr, len, access, &op ); + } + + #else /* #if defined __WIN32 __ */ + + // TODO: Add cases for Linux, Unix, OS/2, ... + + // DOS32 has no imposed limits on its segments. + #if defined __BORLANDC__ || defined __WATCOMC__ + #pragma argsused + #endif + int memoryFullAccess( void* addr, int len ) { return 1; } + + #if defined __BORLANDC__ || defined __WATCOMC__ + #pragma argsused + #endif + int memorySetAccess( void* addr, int len, int access ) { return 1; } + + #endif /* #if defined __WIN32 __ */ + +int AMXAPI amx_InitJIT(AMX *amx, void *reloc_table, void *native_code) +{ + int mac, res; + AMX_HEADER *hdr; + + mac = memoryFullAccess( asm_runJIT, 20000 ); + if ( ! mac ) + return AMX_ERR_INIT_JIT; + + /* copy the prefix */ + memcpy( native_code, amx->base, ((AMX_HEADER *)(amx->base))->cod ); + hdr = (AMX_HEADER*)native_code; + + /* JIT rulz! (TM) */ + /* MP: added check for correct compilation */ + res = asm_runJIT( amx->base, reloc_table, native_code ); + if ( res != 0 ) + { + memorySetAccess( asm_runJIT, 20000, mac ); + return AMX_ERR_INIT_JIT; + } + + /* 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; + + memorySetAccess( asm_runJIT, 20000, mac ); + return AMX_ERR_NONE; +} + +#else /* #if defined JIT */ + +#if defined _MSC_VER + #pragma warning(push) + #pragma warning(disable: 4100) // unreferenced formal parameter +#endif +#if defined __BORLANDC__ || defined __WATCOMC__ + #pragma argsused +#endif +int AMXAPI amx_InitJIT(AMX *amx,void *compiled_program,void *reloc_table) +{ + return AMX_ERR_INIT_JIT; +} + +#if defined _MSC_VER + #pragma warning(pop) +#endif + +#endif /* #if defined JIT */ + +#endif /* AMX_INIT */ + +#if defined AMX_CLEANUP +int AMXAPI amx_Cleanup(AMX *amx) +{ + #if (defined _Windows || defined LINUX) && !defined AMX_NODYNALOAD + #if defined _Windows + typedef int (FAR WINAPI *AMX_ENTRY)(AMX FAR *amx); + #elif defined LINUX + 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 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 + libcleanup=(AMX_ENTRY)dlsym((void*)lib->address,funcname); + #endif + if (libcleanup!=NULL) + libcleanup(amx); + #if defined _Windows + FreeLibrary((HINSTANCE)lib->address); + #elif defined LINUX + dlclose((void*)lib->address); + #endif + } /* if */ + } /* for */ + #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; + amxClone->curline=0; + amxClone->curfile=0; + + /* 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 + hdr->nametable); + *length=*namelength; + assert(hdr->file_version>=7); /* name table exists only for file version 7+ */ + } else { + *length=hdr->defsize - sizeof(uint32_t); + } /* 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, 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=(uint32_t)funcptr; + else + { + no_function = GETENTRYNAME(hdr,func); + err=AMX_ERR_NOTFOUND; + } + } /* if */ + func=(AMX_FUNCSTUB*)((unsigned char*)func+hdr->defsize); + } /* for */ + 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 GETPARAM(v) ( v=*(cell *)cip++ ) +#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 STKMARGIN ((cell)(16*sizeof(cell))) +#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 + /* GNU C version uses the "labels as values" extension to create + * fast "indirect threaded" interpreter. + */ + +#define NEXT(cip) goto **cip++ + +int AMXAPI amx_Exec(AMX *amx, cell *retval, int index, int numparams, ...) +{ +static void *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 }; +static void *amx_opcodelist_nodebug[] = { + &&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_nodebug, &&op_heap, &&op_proc, &&op_ret_nodebug, + &&op_retn_nodebug, &&op_call_nodebug, &&op_call_pri_nodebug, &&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_nodebug, &&op_symbol_nodebug, &&op_srange_nodebug, + &&op_jump_pri, &&op_switch, &&op_casetbl, &&op_swap_pri, + &&op_swap_alt, &&op_pushaddr, &&op_nop, &&op_sysreq_d, + &&op_symtag_nodebug }; + 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; + va_list ap; + int debug; + + /* HACK: return label table (for amx_BrowseRelocate) if amx structure + * has the AMX_FLAG_BROWSE flag set. + */ + if ((amx->flags & AMX_FLAG_BROWSE)==AMX_FLAG_BROWSE) { + assert(sizeof(cell)==sizeof(void *)); + assert(retval!=NULL); + *retval=(cell)((amx->flags & AMX_FLAG_DEBUG)==0 ? amx_opcodelist_nodebug : amx_opcodelist); + return 0; + } /* if */ + + if (amx->callback==NULL) + return AMX_ERR_CALLBACK; + i=amx_Register(amx,NULL,0); /* verify that all natives are registered */ + if (i!=AMX_ERR_NONE) + return i; + + if ((amx->flags & AMX_FLAG_RELOC)==0) + return AMX_ERR_INIT; + assert((amx->flags & AMX_FLAG_BROWSE)==0); + debug= (amx->flags & AMX_FLAG_DEBUG)!=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=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>=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(); + init_little_endian(); + + if (debug && index!=AMX_EXEC_CONT) { + /* set the entry point in the debugger by marking a "call" to the + * exported function + */ + amx->dbgcode=DBG_CALL; + amx->dbgaddr=(ucell)((unsigned char*)cip-code); + amx->debug(amx); + } /* if */ + + /* 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 SMALL_CELL_SIZE==16 + assert(sizeof(cell)==2); + #elif SMALL_CELL_SIZE==32 + assert(sizeof(cell)==4); + #elif SMALL_CELL_SIZE==64 + assert(sizeof(cell)==8); + #else + #error Unsupported cell size + #endif + + if (index!=AMX_EXEC_CONT) { + /* push the parameters to the stack (in reverse order) */ + if (numparams & 0xFFFF0000) { + cell *params; + stk-=(numparams>>=16)*sizeof(cell); + va_start(ap,numparams); + params = va_arg(ap,cell*); + va_end(ap); + for (i=0; i=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 (amx_LittleEndian && 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 */ + 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(); + if (debug && offs>0) { + amx->dbgcode=DBG_CLRSYM; + amx->stk=stk; + amx->hea=hea; + amx->debug(amx); + } /* if */ + NEXT(cip); + op_stack_nodebug: + 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); + if (debug) { + amx->stk=stk; + amx->hea=hea; + amx->dbgcode=DBG_RETURN; + amx->dbgparam=pri; /* store "return value" */ + amx->debug(amx); + } /* if */ + NEXT(cip); + op_ret_nodebug: + 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 */ + if (debug) { + amx->stk=stk; + amx->hea=hea; + amx->dbgcode=DBG_RETURN; + amx->dbgparam=pri; /* store "return value" */ + amx->debug(amx); + amx->dbgcode=DBG_CLRSYM; + amx->debug(amx); + } /* if */ + NEXT(cip); + op_retn_nodebug: + 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 */ + if (debug) { + amx->dbgcode=DBG_CALL; + amx->dbgaddr=(ucell)((unsigned char*)cip-code); + amx->debug(amx); + } /* if */ + NEXT(cip); + op_call_nodebug: + 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); + if (debug) { + amx->dbgcode=DBG_CALL; + amx->dbgaddr=pri; + amx->debug(amx); + } /* if */ + NEXT(cip); + op_call_pri_nodebug: + 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>=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 */ + amx->frm=frm; + amx->stk=stk; + amx->hea=hea; + amx->pri=pri; + amx->alt=alt; + amx->cip=(cell)((unsigned char*)cip-code); + if (debug) { + amx->dbgcode=DBG_TERMINATE; + amx->dbgaddr=(cell)((unsigned char *)cip-code); + amx->dbgparam=offs; + amx->debug(amx); + } /* if */ + 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: + assert((amx->flags & AMX_FLAG_BROWSE)==0); + GETPARAM(amx->curline); + GETPARAM(amx->curfile); + if (debug) { + amx->frm=frm; + amx->stk=stk; + amx->hea=hea; + amx->dbgcode=DBG_LINE; + num=amx->debug(amx); + if (num!=AMX_ERR_NONE) { + if (num==AMX_ERR_SLEEP) { + amx->pri=pri; + amx->alt=alt; + amx->cip=(cell)((unsigned char*)cip-code); + amx->reset_stk=reset_stk; + amx->reset_hea=reset_hea; + } /* if */ + ABORT(amx,num); + } /* if */ + } /* if */ + NEXT(cip); + op_line_nodebug: + assert((amx->flags & AMX_FLAG_BROWSE)==0); + GETPARAM(amx->curline); + GETPARAM(amx->curfile); + NEXT(cip); + op_symbol: + assert((amx->flags & AMX_FLAG_BROWSE)==0); + GETPARAM(offs); + GETPARAM(amx->dbgaddr); + GETPARAM(amx->dbgparam); + amx->dbgname=(char *)cip; + cip=(cell *)((unsigned char *)cip + (int)offs - 2*sizeof(cell)); + amx->dbgcode=DBG_SYMBOL; + assert((amx->dbgparam >> 8)>0); /* local symbols only */ + if (debug) { + amx->frm=frm; /* debugger needs this to relocate the symbols */ + amx->debug(amx); + } /* if */ + NEXT(cip); + op_symbol_nodebug: + assert((amx->flags & AMX_FLAG_BROWSE)==0); + GETPARAM(offs); + cip=(cell *)((unsigned char *)cip + (int)offs); + NEXT(cip); + op_srange: + assert((amx->flags & AMX_FLAG_BROWSE)==0); + GETPARAM(amx->dbgaddr); /* dimension level */ + GETPARAM(amx->dbgparam); /* length */ + amx->dbgcode=DBG_SRANGE; + if (debug) { + amx->frm=frm; /* debugger needs this to relocate the symbols */ + amx->debug(amx); + } /* if */ + NEXT(cip); + op_srange_nodebug: + assert((amx->flags & AMX_FLAG_BROWSE)==0); + cip+=2; + NEXT(cip); + op_symtag: + assert((amx->flags & AMX_FLAG_BROWSE)==0); + GETPARAM(amx->dbgparam); /* tag id */ + amx->dbgcode=DBG_SYMTAG; + if (debug) { + amx->frm=frm; /* debugger needs this to relocate the symbols */ + amx->debug(amx); + } /* if */ + NEXT(cip); + op_symtag_nodebug: + assert((amx->flags & AMX_FLAG_BROWSE)==0); + cip+=1; + NEXT(cip); + op_jump_pri: + cip=(cell *)(code+(int)pri); + NEXT(cip); + op_switch: { + cell *cptr; + cptr=(cell *)*cip + 1; /* +1, to skip the "casetbl" opcode */ + cip=(cell *)*(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=(cell *)*(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); +} + +#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. + * jitr.asm is for Watcom's register calling convention, jits.asm 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]; + #else + extern cell __cdecl amx_exec_asm(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)); + #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); + #endif +#endif + +int AMXAPI amx_Exec(AMX *amx, cell *retval, int index, int numparams, ...) +{ + 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; + va_list ap; + int debug; + #if defined ASM32 || defined JIT + extern void *amx_opcodelist[]; + extern void *amx_opcodelist_nodebug[]; + #ifdef __WATCOMC__ + #pragma aux amx_opcodelist "_*" + #pragma aux amx_opcodelist_nodebug "_*" + #endif + cell parms[9]; /* registers and parameters for assembler AMX */ + #else + OPCODE op; + cell offs; + int num; + #endif + + #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 JIT + /* The JIT does not support a "debug" opcode list; however, its single + * opcode list is called "amx_opcodelist", although it should be called + * amx_opcodelist_nodebug + */ + *retval=(cell)amx_opcodelist; + #else + *retval=(cell)((amx->flags & AMX_FLAG_DEBUG)==0 ? amx_opcodelist_nodebug : amx_opcodelist); + #endif + return 0; + } /* if */ + #endif + + if (amx->callback==NULL) + return AMX_ERR_CALLBACK; + i=amx_Register(amx,NULL,0); /* verify that all natives are registered */ + if (i!=AMX_ERR_NONE) + return i; + + if ((amx->flags & AMX_FLAG_RELOC)==0) + return AMX_ERR_INIT; + assert((amx->flags & AMX_FLAG_BROWSE)==0); + debug= (amx->flags & AMX_FLAG_DEBUG)!=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(); + init_little_endian(); + + if (debug && index!=AMX_EXEC_CONT) { + /* set the entry point in the debugger by marking a "call" to the + * exported function + */ + amx->dbgcode=DBG_CALL; + amx->dbgaddr=(ucell)((unsigned char *)cip-code); + amx->debug(amx); + } /* if */ + + /* 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 SMALL_CELL_SIZE==16 + assert(sizeof(cell)==2); + #elif SMALL_CELL_SIZE==32 + assert(sizeof(cell)==4); + #elif SMALL_CELL_SIZE==64 + assert(sizeof(cell)==8); + #else + #error Unsupported cell size + #endif + + if (index!=AMX_EXEC_CONT) { + /* push the parameters to the stack (in reverse order) */ + if (numparams & 0xFFFF0000L) { + cell *params; + stk-=(numparams>>=16)*sizeof(cell); + va_start(ap,numparams); + params = va_arg(ap,cell*); + va_end(ap); + for (i=0; istp,hea); + 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 (amx_LittleEndian && (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(); + if (debug && offs>0) { + amx->dbgcode=DBG_CLRSYM; + amx->hea=hea; + amx->stk=stk; + amx->debug(amx); + } /* if */ + 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); + if (debug) { + amx->stk=stk; + amx->hea=hea; + amx->dbgcode=DBG_RETURN; + amx->dbgparam=pri; /* store "return value" */ + amx->debug(amx); + } /* if */ + 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; + if (debug) { + amx->stk=stk; + amx->hea=hea; + amx->dbgcode=DBG_RETURN; + amx->dbgparam=pri; /* store "return value" */ + amx->debug(amx); + amx->dbgcode=DBG_CLRSYM; + amx->debug(amx); + } /* if */ + break; + case OP_CALL: + PUSH(((unsigned char *)cip-code)+sizeof(cell));/* skip address */ + cip=JUMPABS(code, cip); /* jump to the address */ + if (debug) { + amx->dbgcode=DBG_CALL; + amx->dbgaddr=(ucell)((unsigned char *)cip-code); + amx->debug(amx); + } /* if */ + break; + case OP_CALL_PRI: + PUSH((unsigned char *)cip-code); + cip=(cell *)(code+(int)pri); + if (debug) { + amx->dbgcode=DBG_CALL; + amx->dbgaddr=pri; + amx->debug(amx); + } /* if */ + 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 */ + amx->frm=frm; + amx->stk=stk; + amx->hea=hea; + amx->pri=pri; + amx->alt=alt; + amx->cip=(cell)((unsigned char*)cip-code); + if (debug) { + amx->dbgcode=DBG_TERMINATE; + amx->dbgaddr=(cell)((unsigned char *)cip-code); + amx->dbgparam=offs; + amx->debug(amx); + } /* if */ + 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_FILE: + GETPARAM(offs); + cip=(cell *)((unsigned char *)cip+(int)offs); + assert(0); /* this code should not occur during execution */ + break; + case OP_LINE: + assert((amx->flags & AMX_FLAG_BROWSE)==0); + GETPARAM(amx->curline); + GETPARAM(amx->curfile); + if (debug) { + amx->frm=frm; + amx->stk=stk; + amx->hea=hea; + amx->dbgcode=DBG_LINE; + num=amx->debug(amx); + if (num!=AMX_ERR_NONE) { + if (num==AMX_ERR_SLEEP) { + amx->pri=pri; + amx->alt=alt; + amx->cip=(cell)((unsigned char*)cip-code); + amx->reset_stk=reset_stk; + amx->reset_hea=reset_hea; + } /* if */ + ABORT(amx,num); + } /* if */ + } /* if */ + break; + case OP_SYMBOL: + assert((amx->flags & AMX_FLAG_BROWSE)==0); + GETPARAM(offs); + GETPARAM(amx->dbgaddr); + GETPARAM(amx->dbgparam); + amx->dbgname=(char *)cip; + cip=(cell *)((unsigned char *)cip + (int)offs - 2*sizeof(cell)); + assert((amx->dbgparam >> 8)>0); /* local symbols only */ + if (debug) { + amx->frm=frm; /* debugger needs this to relocate the symbols */ + amx->dbgcode=DBG_SYMBOL; + amx->debug(amx); + } /* if */ + break; + case OP_SRANGE: + assert((amx->flags & AMX_FLAG_BROWSE)==0); + GETPARAM(amx->dbgaddr); /* dimension level */ + GETPARAM(amx->dbgparam); /* length */ + if (debug) { + amx->frm=frm; /* debugger needs this to relocate the symbols */ + amx->dbgcode=DBG_SRANGE; + amx->debug(amx); + } /* if */ + break; + case OP_SYMTAG: + assert((amx->flags & AMX_FLAG_BROWSE)==0); + GETPARAM(amx->dbgparam); /* tag id */ + if (debug) { + amx->frm=frm; /* debugger needs this to relocate the symbols */ + amx->dbgcode=DBG_SYMTAG; + amx->debug(amx); + } /* if */ + break; + case OP_JUMP_PRI: + cip=(cell *)(code+(int)pri); + break; + case OP_SWITCH: { + cell *cptr; + + cptr=(cell *)*cip + 1; /* +1, to skip the "casetbl" opcode */ + cip=(cell *)*(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=(cell *)*(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_CASETBL: + assert(0); /* should not occur during execution */ + /* drop through to "invalid instruction" */ + default: + ABORT(amx,AMX_ERR_INVINSTR); + } /* switch */ + } /* for */ +#endif +} + +#endif /* __GNUC__ */ + +/* For interfacing applications not written in C/C++, amx_Execv() works like + * amx_Exec(), but has all parameters passed via an array. + */ +int AMXAPI amx_Execv(AMX *amx, cell *retval, int index, int numparams, cell params[]) +{ + return amx_Exec(amx, retval, index, numparams<<16, params); +} + +#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); + assert(debug!=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 +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 + +#define CHARBITS (8*sizeof(char)) +#if SMALL_CELL_SIZE==16 + #define CHARMASK (0xffffu << 8*(2-sizeof(char))) +#elif SMALL_CELL_SIZE==32 + #define CHARMASK (0xffffffffuL << 8*(4-sizeof(char))) +#elif SMALL_CELL_SIZE==64 + #define CHARMASK (0xffffffffffffffffuLL << 8*(8-sizeof(char))) +#else + #error Unsupported cell size +#endif + +int AMXAPI amx_StrLen(cell *cstr, int *length) +{ + int len; + + 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' */ + init_little_endian(); + if (amx_LittleEndian) { + /* on Little Endian machines, toggle the last bytes */ + cell 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 */ + } /* if */ + } else { + for (len=0; cstr[len]!=0; len++) + /* nothing */; + } /* if */ + *length = len; + return AMX_ERR_NONE; +} + +int AMXAPI amx_SetString(cell *dest,const char *source,int pack,int use_wchar) +{ /* the memory blocks should not overlap */ + int len= use_wchar ? wcslen((const wchar_t*)source) : strlen(source); + int i; + if (pack) { + /* create a packed string */ + dest[len/sizeof(cell)]=0; /* clear last bytes of last (semi-filled) cell*/ + if (use_wchar) { + for (i=0; i=0) + swapcell((ucell *)&dest[len--]); + } /* if */ + } else { + /* create an unpacked string */ + if (use_wchar) { + for (i=0; iUNPACKEDMAX) { + /* source string is packed */ + cell c = 0; /* to avoid a compiler warning */ + int i=sizeof(cell)-1; + for ( ;; ) { + if (i==sizeof(cell)-1) + c=*source++; + if (use_wchar) + ((wchar_t*)dest)[len++]=(char)(c >> i*CHARBITS); + else + dest[len++]=(char)(c >> i*CHARBITS); + if (dest[len-1]=='\0') + break; /* terminating zero character found */ + i=(i+sizeof(cell)-1) % sizeof(cell); + } /* for */ + } else { + /* source string is unpacked */ + if (use_wchar) { + while (*source!=0) + ((wchar_t*)dest)[len++]=(wchar_t)*source++; + } else { + while (*source!=0) + dest[len++]=(char)*source++; + } /* if */ + } /* if */ + 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 char utf8_count[16]={ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 4 }; +static long utf8_lowmark[5] = { 0x80, 0x800, 0x10000, 0x200000, 0x4000000 }; + 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 character string. The character may be + * up to 6 bytes long. The function returns an error code if "maxchars" is + * lower than the requried 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 err=AMX_ERR_NONE; + while (err==AMX_ERR_NONE && *string!='\0') + err=amx_UTF8Get(string,&string,NULL); + return err; +} +#endif /* AMX_UTF8XXX */ diff --git a/amxmodx/amxcore.cpp b/amxmodx/amxcore.cpp new file mode 100755 index 00000000..fbbf2cad --- /dev/null +++ b/amxmodx/amxcore.cpp @@ -0,0 +1,625 @@ +/* Core module for the Small AMX + * + * Copyright (c) ITB CompuPhase, 1997-2004 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id$ + */ +#if defined _UNICODE || defined __UNICODE__ || defined UNICODE +# if !defined UNICODE /* for Windows */ +# define UNICODE +# endif +# if !defined _UNICODE /* for C library */ +# define _UNICODE +# endif +#endif + +#include +#include +#include +#include +#include + +// this file does not include amxmodx.h, so we have to include the memory manager here +#ifdef MEMORY_TEST +#include "mmgr/mmgr.h" +#endif // MEMORY_TEST + +#include "amx.h" + +#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined _Windows + #include +#endif + +/* A few compilers do not provide the ANSI C standard "time" functions */ +#if !defined SN_TARGET_PS2 && !defined _WIN32_WCE + #include +#endif + +#if defined _UNICODE +# include +#elif !defined __T + typedef char TCHAR; +# define __T(string) string +# define _tcschr strchr +# define _tcscpy strcpy +# define _tcsdup strdup +# define _tcslen strlen +# define _stprintf sprintf +#endif + + +#define CHARBITS (8*sizeof(char)) +typedef unsigned char uchar; + +#if !defined NOPROPLIST +typedef struct _property_list { + struct _property_list *next; + cell id; + char *name; + cell value; + //??? safe AMX (owner of the property) +} proplist; + +static proplist proproot = { NULL }; + +static proplist *list_additem(proplist *root) +{ + proplist *item; + + assert(root!=NULL); + if ((item=(proplist *)malloc(sizeof(proplist)))==NULL) + return NULL; + item->name=NULL; + item->id=0; + item->value=0; + item->next=root->next; + root->next=item; + return item; +} +static void list_delete(proplist *pred,proplist *item) +{ + assert(pred!=NULL); + assert(item!=NULL); + pred->next=item->next; + assert(item->name!=NULL); + free(item->name); + free(item); +} +static void list_setitem(proplist *item,cell id,char *name,cell value) +{ + char *ptr; + + assert(item!=NULL); + if ((ptr=(char *)malloc(strlen(name)+1))==NULL) + return; + if (item->name!=NULL) + free(item->name); + strcpy(ptr,name); + item->name=ptr; + item->id=id; + item->value=value; +} +static proplist *list_finditem(proplist *root,cell id,char *name,cell value, + proplist **pred) +{ + proplist *item=root->next; + proplist *prev=root; + + /* check whether to find by name or by value */ + assert(name!=NULL); + if (strlen(name)>0) { + /* find by name */ + while (item!=NULL && (item->id!=id || stricmp(item->name,name)!=0)) { + prev=item; + item=item->next; + } /* while */ + } else { + /* find by value */ + while (item!=NULL && (item->id!=id || item->value!=value)) { + prev=item; + item=item->next; + } /* while */ + } /* if */ + if (pred!=NULL) + *pred=prev; + return item; +} +#endif + +#if defined __BORLANDC__ || defined __WATCOMC__ + #pragma argsused +#endif +static cell AMX_NATIVE_CALL numargs(AMX *amx, cell *params) +{ + AMX_HEADER *hdr; + uchar *data; + cell bytes; + + hdr=(AMX_HEADER *)amx->base; + data=amx->data ? amx->data : amx->base+(int)hdr->dat; + /* the number of bytes is on the stack, at "frm + 2*cell" */ + bytes= * (cell *)(data+(int)amx->frm+2*sizeof(cell)); + /* the number of arguments is the number of bytes divided + * by the size of a cell */ + return bytes/sizeof(cell); +} + +static cell AMX_NATIVE_CALL getarg(AMX *amx, cell *params) +{ + AMX_HEADER *hdr; + uchar *data; + cell value; + + hdr=(AMX_HEADER *)amx->base; + data=amx->data ? amx->data : amx->base+(int)hdr->dat; + /* get the base value */ + value= * (cell *)(data+(int)amx->frm+((int)params[1]+3)*sizeof(cell)); + /* adjust the address in "value" in case of an array access */ + value+=params[2]*sizeof(cell); + /* get the value indirectly */ + value= * (cell *)(data+(int)value); + return value; +} + +static cell AMX_NATIVE_CALL setarg(AMX *amx, cell *params) +{ + AMX_HEADER *hdr; + uchar *data; + cell value; + + hdr=(AMX_HEADER *)amx->base; + data=amx->data ? amx->data : amx->base+(int)hdr->dat; + /* get the base value */ + value= * (cell *)(data+(int)amx->frm+((int)params[1]+3)*sizeof(cell)); + /* adjust the address in "value" in case of an array access */ + value+=params[2]*sizeof(cell); + /* verify the address */ + if (value<0 || (value>=amx->hea && valuestk)) + return 0; + /* set the value indirectly */ + * (cell *)(data+(int)value) = params[3]; + return 1; +} + +#if defined __BORLANDC__ || defined __WATCOMC__ + #pragma argsused +#endif +static cell AMX_NATIVE_CALL heapspace(AMX *amx,cell *params) +{ + return amx->stk - amx->hea; +} + +static cell AMX_NATIVE_CALL funcidx(AMX *amx,cell *params) +{ + char name[64]; + cell *cstr; + int index,err,len; + + amx_GetAddr(amx,params[1],&cstr); + + /* verify string length */ + amx_StrLen(cstr,&len); + if (len>=64) { + amx_RaiseError(amx,AMX_ERR_NATIVE); + return 0; + } /* if */ + + amx_GetString(name,cstr,0); + err=amx_FindPublic(amx,name,&index); + if (err!=AMX_ERR_NONE) + index=-1; /* this is not considered a fatal error */ + return index; +} + +int amx_StrPack(cell *dest,cell *source) +{ + int len; + + amx_StrLen(source,&len); + if ((ucell)*source>UNPACKEDMAX) { + /* source string is already packed */ + while (len >= 0) { + *dest++ = *source++; + len-=sizeof(cell); + } /* while */ + } else { + /* pack string, from bottom up */ + cell c; + int i; + for (c=0,i=0; iUNPACKEDMAX) { + /* unpack string, from top down (so string can be unpacked in place) */ + cell c; + int i,len; + amx_StrLen(source,&len); + dest[len]=0; + for (i=len-1; i>=0; i--) { + c=source[i/sizeof(cell)] >> (sizeof(cell)-i%sizeof(cell)-1)*CHARBITS; + dest[i]=c & UCHAR_MAX; + } /* for */ + } else { + /* source string is already unpacked */ + while ((*dest++ = *source++) != 0) + /* nothing */; + } /* if */ + return AMX_ERR_NONE; +} + +static int verify_addr(AMX *amx,cell addr) +{ + int err; + cell *cdest; + + err=amx_GetAddr(amx,addr,&cdest); + if (err!=AMX_ERR_NONE) + amx_RaiseError(amx,err); + return err; +} + +static cell AMX_NATIVE_CALL core_strlen(AMX *amx,cell *params) +{ + cell *cptr; + int len = 0; + + if (amx_GetAddr(amx,params[1],&cptr)==AMX_ERR_NONE) + amx_StrLen(cptr,&len); + return len; +} + +static cell AMX_NATIVE_CALL strpack(AMX *amx,cell *params) +{ + cell *cdest,*csrc; + int len,needed,err; + size_t lastaddr; + + /* calculate number of cells needed for (packed) destination */ + amx_GetAddr(amx,params[2],&csrc); + amx_StrLen(csrc,&len); + needed=(len+sizeof(cell))/sizeof(cell); /* # of cells needed */ + assert(needed>0); + lastaddr=(size_t)(params[1]+sizeof(cell)*needed-1); + if (verify_addr(amx,(cell)lastaddr)!=AMX_ERR_NONE) + return 0; + + amx_GetAddr(amx,params[1],&cdest); + err=amx_StrPack(cdest,csrc); + if (err!=AMX_ERR_NONE) + return amx_RaiseError(amx,err); + + return len; +} + +static cell AMX_NATIVE_CALL strunpack(AMX *amx,cell *params) +{ + cell *cdest,*csrc; + int len,err; + size_t lastaddr; + + /* calculate number of cells needed for (packed) destination */ + amx_GetAddr(amx,params[2],&csrc); + amx_StrLen(csrc,&len); + assert(len>=0); + lastaddr=(size_t)(params[1]+sizeof(cell)*(len+1)-1); + if (verify_addr(amx,(cell)lastaddr)!=AMX_ERR_NONE) + return 0; + + amx_GetAddr(amx,params[1],&cdest); + err=amx_StrUnpack(cdest,csrc); + if (err!=AMX_ERR_NONE) + return amx_RaiseError(amx,err); + + return len; +} + +#if defined __BORLANDC__ || defined __WATCOMC__ + #pragma argsused +#endif +static cell AMX_NATIVE_CALL swapchars(AMX *amx,cell *params) +{ + union { + cell c; + #if SMALL_CELL_SIZE==16 + uchar b[2]; + #elif SMALL_CELL_SIZE==32 + uchar b[4]; + #elif SMALL_CELL_SIZE==64 + uchar b[8]; + #else + #error Unsupported cell size + #endif + } value; + uchar t; + + assert((size_t)params[0]==sizeof(cell)); + value.c = params[1]; + #if SMALL_CELL_SIZE==16 + t = value.b[0]; + value.b[0] = value.b[1]; + value.b[1] = t; + #elif SMALL_CELL_SIZE==32 + t = value.b[0]; + value.b[0] = value.b[3]; + value.b[3] = t; + t = value.b[1]; + value.b[1] = value.b[2]; + value.b[2] = t; + #elif SMALL_CELL_SIZE==64 + t = value.b[0]; + value.b[0] = value.b[7]; + value.b[7] = t; + t = value.b[1]; + value.b[1] = value.b[6]; + value.b[6] = t; + t = value.b[2]; + value.b[2] = value.b[5]; + value.b[5] = t; + t = value.b[3]; + value.b[3] = value.b[4]; + value.b[4] = t; + #else + #error Unsupported cell size + #endif + return value.c; +} + +#if defined __BORLANDC__ || defined __WATCOMC__ + #pragma argsused +#endif +static cell AMX_NATIVE_CALL core_tolower(AMX *amx,cell *params) +{ + #if defined __WIN32__ || defined _WIN32 || defined WIN32 + return (cell)CharLower((LPTSTR)params[1]); + #elif defined _Windows + return (cell)AnsiLower((LPSTR)params[1]); + #else + return tolower((int)params[1]); + #endif +} + +#if defined __BORLANDC__ || defined __WATCOMC__ + #pragma argsused +#endif +static cell AMX_NATIVE_CALL core_toupper(AMX *amx,cell *params) +{ + #if defined __WIN32__ || defined _WIN32 || defined WIN32 + return (cell)CharUpper((LPTSTR)params[1]); + #elif defined _Windows + return (cell)AnsiUpper((LPSTR)params[1]); + #else + return toupper((int)params[1]); + #endif +} + +#if defined __BORLANDC__ || defined __WATCOMC__ + #pragma argsused +#endif +static cell AMX_NATIVE_CALL core_min(AMX *amx,cell *params) +{ + return params[1] <= params[2] ? params[1] : params[2]; +} + +#if defined __BORLANDC__ || defined __WATCOMC__ + #pragma argsused +#endif +static cell AMX_NATIVE_CALL core_max(AMX *amx,cell *params) +{ + return params[1] >= params[2] ? params[1] : params[2]; +} + +static cell AMX_NATIVE_CALL core_clamp(AMX *amx,cell *params) +{ + cell value = params[1]; + if (params[2] > params[3]) /* minimum value > maximum value ! */ + amx_RaiseError(amx,AMX_ERR_NATIVE); + if (value < params[2]) + value = params[2]; + else if (value > params[3]) + value = params[3]; + return value; +} + +#if !defined NOPROPLIST +static char *MakePackedString(cell *cptr) +{ + int len; + char *dest; + + amx_StrLen(cptr,&len); + dest=(char *)malloc(len+sizeof(cell)); + amx_GetString(dest,cptr,0); + return dest; +} + +static cell AMX_NATIVE_CALL getproperty(AMX *amx,cell *params) +{ + cell *cstr; + char *name; + proplist *item; + + amx_GetAddr(amx,params[2],&cstr); + name=MakePackedString(cstr); + item=list_finditem(&proproot,params[1],name,params[3],NULL); + /* if list_finditem() found the value, store the name */ + if (item!=NULL && item->value==params[3] && strlen(name)==0) { + int needed=(strlen(item->name)+sizeof(cell)-1)/sizeof(cell); /* # of cells needed */ + if (verify_addr(amx,(cell)(params[4]+needed))!=AMX_ERR_NONE) { + free(name); + return 0; + } /* if */ + amx_GetAddr(amx,params[4],&cstr); + amx_SetString(cstr,item->name,1,0); + } /* if */ + free(name); + return (item!=NULL) ? item->value : 0; +} + +static cell AMX_NATIVE_CALL setproperty(AMX *amx,cell *params) +{ + cell prev=0; + cell *cstr; + char *name; + proplist *item; + + amx_GetAddr(amx,params[2],&cstr); + name=MakePackedString(cstr); + item=list_finditem(&proproot,params[1],name,params[3],NULL); + if (item==NULL) + item=list_additem(&proproot); + if (item==NULL) { + amx_RaiseError(amx,AMX_ERR_MEMORY); + } else { + prev=item->value; + if (strlen(name)==0) { + free(name); + amx_GetAddr(amx,params[4],&cstr); + name=MakePackedString(cstr); + } /* if */ + list_setitem(item,params[1],name,params[3]); + } /* if */ + free(name); + return prev; +} + +static cell AMX_NATIVE_CALL delproperty(AMX *amx,cell *params) +{ + cell prev=0; + cell *cstr; + char *name; + proplist *item,*pred; + + amx_GetAddr(amx,params[2],&cstr); + name=MakePackedString(cstr); + item=list_finditem(&proproot,params[1],name,params[3],&pred); + if (item!=NULL) { + prev=item->value; + list_delete(pred,item); + } /* if */ + free(name); + return prev; +} + +static cell AMX_NATIVE_CALL existproperty(AMX *amx,cell *params) +{ + cell *cstr; + char *name; + proplist *item; + + amx_GetAddr(amx,params[2],&cstr); + name=MakePackedString(cstr); + item=list_finditem(&proproot,params[1],name,params[3],NULL); + free(name); + return (item!=NULL); +} +#endif + +/* This routine comes from the book "Inner Loops" by Rick Booth, Addison-Wesley + * (ISBN 0-201-47960-5). This is a "multiplicative congruential random number + * generator" that has been extended to 31-bits (the standard C version returns + * only 15-bits). + */ +static unsigned long IL_StandardRandom_seed = 0L; +#define IL_RMULT 1103515245L +#if defined __BORLANDC__ || defined __WATCOMC__ + #pragma argsused +#endif +static cell AMX_NATIVE_CALL core_random(AMX *amx,cell *params) +{ + unsigned long lo, hi, ll, lh, hh, hl; + unsigned long result; + + /* one-time initialization (or, mostly one-time) */ + #if !defined SN_TARGET_PS2 && !defined _WIN32_WCE + if (IL_StandardRandom_seed == 0L) + IL_StandardRandom_seed=(unsigned long)time(NULL); + #endif + + lo = IL_StandardRandom_seed & 0xffff; + hi = IL_StandardRandom_seed >> 16; + IL_StandardRandom_seed = IL_StandardRandom_seed * IL_RMULT + 12345; + ll = lo * (IL_RMULT & 0xffff); + lh = lo * (IL_RMULT >> 16 ); + hl = hi * (IL_RMULT & 0xffff); + hh = hi * (IL_RMULT >> 16 ); + result = ((ll + 12345) >> 16) + lh + hl + (hh << 16); + result &= ~LONG_MIN; /* remove sign bit */ + if (params[1]!=0) + result %= params[1]; + return (cell)result; +} + + +AMX_NATIVE_INFO core_Natives[] = { + { "numargs", numargs }, + { "getarg", getarg }, + { "setarg", setarg }, + { "heapspace", heapspace }, + { "funcidx", funcidx }, + { "strlen", core_strlen }, + { "strpack", strpack }, + { "strunpack", strunpack }, + { "swapchars", swapchars }, + { "tolower", core_tolower }, + { "toupper", core_toupper }, + { "random", core_random }, + { "min", core_min }, + { "max", core_max }, + { "clamp", core_clamp }, +#if !defined NOPROPLIST + { "getproperty", getproperty }, + { "setproperty", setproperty }, + { "deleteproperty",delproperty }, + { "existproperty", existproperty }, +#endif + { NULL, NULL } /* terminator */ +}; + +int AMXEXPORT amx_CoreInit(AMX *amx) +{ + return amx_Register(amx, core_Natives, -1); +} + +#if defined __BORLANDC__ || defined __WATCOMC__ + #pragma argsused +#endif +int AMXEXPORT amx_CoreCleanup(AMX *amx) +{ + #if !defined NOPROPLIST + //??? delete only the properties owned by the AMX + while (proproot.next!=NULL) + list_delete(&proproot,proproot.next); + #endif + return AMX_ERR_NONE; +} diff --git a/amxmodx/amxtime.cpp b/amxmodx/amxtime.cpp new file mode 100755 index 00000000..8c4464e6 --- /dev/null +++ b/amxmodx/amxtime.cpp @@ -0,0 +1,116 @@ +/* Date/time module for the Small AMX + * + * Copyright (c) ITB CompuPhase, 2001-2002 + * This file may be freely used. No warranties of any kind. + * + * Version: $Id$ + */ +#include +#include +#if defined __WIN32__ || defined _WIN32 + #include + #include +#endif + +#ifdef __GNUC__ +# ifndef CLK_TCK +# define CLK_TCK CLOCKS_PER_SEC +# endif +#endif + +// this file does not include amxmodx.h, so we have to include the memory manager here +#ifdef MEMORY_TEST +#include "mmgr/mmgr.h" +#endif // MEMORY_TEST + +#include "amx.h" + +#if defined __BORLANDC__ || defined __WATCOMC__ + #pragma argsused +#endif +static cell AMX_NATIVE_CALL _time(AMX *amx, cell *params) +{ + time_t sec1970; + struct tm gtm; + cell *cptr; + + assert(params[0]==3*sizeof(cell)); + + time(&sec1970); + + /* on DOS/Windows, the timezone is usually not set for the C run-time + * library; in that case gmtime() and localtime() return the same value + */ + gtm=*localtime(&sec1970); + if (amx_GetAddr(amx,params[1],&cptr)==AMX_ERR_NONE) + *cptr=gtm.tm_hour; + if (amx_GetAddr(amx,params[2],&cptr)==AMX_ERR_NONE) + *cptr=gtm.tm_min; + if (amx_GetAddr(amx,params[3],&cptr)==AMX_ERR_NONE) + *cptr=gtm.tm_sec; + + /* the time() function returns the number of seconds since January 1 1970 + * in Universal Coordinated Time (the successor to Greenwich Mean Time) + */ + return sec1970; +} + +#if defined __BORLANDC__ || defined __WATCOMC__ + #pragma argsused +#endif +static cell AMX_NATIVE_CALL _date(AMX *amx, cell *params) +{ + time_t sec1970; + struct tm gtm; + cell *cptr; + + assert(params[0]==3*sizeof(cell)); + + time(&sec1970); + + gtm=*localtime(&sec1970); + if (amx_GetAddr(amx,params[1],&cptr)==AMX_ERR_NONE) + *cptr=gtm.tm_year+1900; + if (amx_GetAddr(amx,params[2],&cptr)==AMX_ERR_NONE) + *cptr=gtm.tm_mon+1; + if (amx_GetAddr(amx,params[3],&cptr)==AMX_ERR_NONE) + *cptr=gtm.tm_mday; + + return 0; +} + +#if defined __BORLANDC__ || defined __WATCOMC__ + #pragma argsused +#endif +static cell AMX_NATIVE_CALL _tickcount(AMX *amx, cell *params) +{ + // #if defined __WIN32__ || defined _WIN32 + // static int timerset = 0; + // #endif + cell value; + + assert(params[0]==sizeof(cell)); + + //#if defined __WIN32__ || defined _WIN32 + // if (!timerset) { + // timeBeginPeriod(1); /* timeGetTime() is more accurate on WindowsNT +// * if timeBeginPeriod(1) is set */ + // timerset=1; + // } /* if */ + // value=timeGetTime(); /* this value is already in milliseconds */ + // #else + value=(cell)clock(); + /* convert to milliseconds */ + value=(cell)((1000L * (value+CLK_TCK/2)) / CLK_TCK); + //#endif + return value; +} + + +AMX_NATIVE_INFO time_Natives[] = { + { "time", _time }, + { "date", _date }, + { "tickcount", _tickcount }, + { NULL, NULL } /* terminator */ +}; + diff --git a/amxmodx/power.cpp b/amxmodx/power.cpp new file mode 100755 index 00000000..7fd68c4e --- /dev/null +++ b/amxmodx/power.cpp @@ -0,0 +1,48 @@ +/* This file implements two native functions. It is provided as + * an example to show how to add native functions. See the manual + * for more information. + * + * Copyright (c) ITB CompuPhase, 1998, 1999 + * This file may be freely used. No warranties of any kind. + */ + +// this file does not include amxmodx.h, so we have to include the memory manager here +#ifdef MEMORY_TEST +#include "mmgr/mmgr.h" +#endif // MEMORY_TEST + +#include "amx.h" + +static cell power(AMX *amx, cell *params) +{ + /* power(value, exponent); + * params[1] = value + * params[2] = exponent + */ + cell result = 1; + while (params[2]-- > 0) + result *= params[1]; + return result; +} + +static cell sqroot(AMX *amx, cell *params) +{ + /* sqroot(value); + * params[1] = value + * This routine uses a simple successice approximation algorithm. + */ + cell div = params[1]; + cell result = 1; + while (div > result) { /* end when div == result, or just below */ + div = (div + result) / 2; /* take mean value as new divisor */ + result = params[1] / div; + } /* while */ + return div; +} + +AMX_NATIVE_INFO power_Natives[] = { + { "power", power }, + { "sqroot", sqroot }, + { 0, 0 } /* terminator */ +}; +