This is the open source Pawn interpreter ported to mbed. See here: http://www.compuphase.com/pawn/pawn.htm and here: http://code.google.com/p/pawnscript/
Some instructions:
- Put the attached include folder next to your source, so when you compile you get all the proper definitions
- Use the attached main.p as a starting point if you wish
- Compile your main.p into main.amx - Put your main.amx on the mbed 'drive'
- Reset and be amazed.
Important Compile Notes:
- You should use the -S# option to define a smaller default stack size. Start with -S64 and go up from there if needed.
- To use on the Cortex-M0 version of the mbed (LPC11U24), you MUST include the TARGET=3 command-line option as well, so the pin names are properly defined. In the future this may be handled on the native code side.
Known Issues:
At the moment it appears the kbhit() function is not working right - at least on my mac. Will continue testing on Windows.Working fine.
Todo:
- Add more wrappers for the mbed peripherals
- Add Pawn overlay support, to allow much larger scripts to run (even on the LPC11U24)
Diff: amx.c
- Revision:
- 0:3ab1d2d14eb3
diff -r 000000000000 -r 3ab1d2d14eb3 amx.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/amx.c Thu Nov 15 17:41:21 2012 +0000 @@ -0,0 +1,3804 @@ +/* Pawn Abstract Machine (for the Pawn language) + * + * Copyright (c) ITB CompuPhase, 1997-2012 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Version: $Id: amx.c 4708 2012-05-18 12:52:49Z thiadmer $ + */ + +#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 <assert.h> +#include <stdarg.h> +#include <stddef.h> /* for wchar_t */ +#include <stdlib.h> /* for getenv() */ +#include <string.h> +#include "osdefs.h" +#if defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ + #include <sclinux.h> + #if !defined AMX_NODYNALOAD + #include <dlfcn.h> + #endif + #if defined AMX_JIT + #include <sys/types.h> + #include <sys/mman.h> + #endif +#endif +#if defined __LCC__ || defined __LINUX__ + #include <wchar.h> /* for wcslen() */ +#endif + +#if defined __ECOS__ + /* eCos puts include files in cyg/package_name */ + #include <cyg/pawn/amx.h> +#else + #include "amx.h" +#endif + +#if (defined _Windows && !defined AMX_NODYNALOAD) || (defined AMX_JIT && __WIN32__) + #include <windows.h> +#endif + + +/* When one or more of the AMX_funcname macros 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_DEFCALLBACK || defined AMX_EXEC + #define AMX_EXPLIT_FUNCTIONS +#endif +#if defined AMX_FLAGS || defined AMX_INIT || defined AMX_MEMINFO + #define AMX_EXPLIT_FUNCTIONS +#endif +#if defined AMX_NAMELENGTH || defined AMX_NATIVEINFO || defined AMX_PUSHXXX + #define AMX_EXPLIT_FUNCTIONS +#endif +#if defined AMX_RAISEERROR || defined AMX_REGISTER || defined AMX_SETCALLBACK + #define AMX_EXPLIT_FUNCTIONS +#endif +#if defined AMX_SETDEBUGHOOK || defined AMX_UTF8XXX || defined AMX_XXXNATIVES + #define AMX_EXPLIT_FUNCTIONS +#endif +#if defined AMX_XXXPUBLICS || defined AMX_XXXPUBVARS || defined AMX_XXXSTRING + #define AMX_EXPLIT_FUNCTIONS +#endif +#if defined AMX_XXXTAGS || defined AMX_XXXUSERDATA + #define AMX_EXPLIT_FUNCTIONS +#endif +#if !defined AMX_EXPLIT_FUNCTIONS + /* no constant set, set them all */ + #define AMX_ALIGN /* amx_Align16(), amx_Align32() and amx_Align64() */ + #define AMX_ALLOT /* amx_Allot() and amx_Release() */ + #define AMX_DEFCALLBACK /* amx_Callback() */ + #define AMX_CLEANUP /* amx_Cleanup() */ + #define AMX_CLONE /* amx_Clone() */ + #define AMX_EXEC /* amx_Exec() */ + #define AMX_FLAGS /* amx_Flags() */ + #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_PUSHXXX /* amx_Push(), amx_PushAddress(), amx_PushArray() and amx_PushString() */ + #define AMX_RAISEERROR /* amx_RaiseError() */ + #define AMX_REGISTER /* amx_Register() */ + #define AMX_SETCALLBACK /* amx_SetCallback() */ + #define AMX_SETDEBUGHOOK /* amx_SetDebugHook() */ + #define AMX_UTF8XXX /* amx_UTF8Check(), amx_UTF8Get(), amx_UTF8Len() and amx_UTF8Put() */ + #define AMX_XXXNATIVES /* amx_NumNatives(), amx_GetNative() and amx_FindNative() */ + #define AMX_XXXPUBLICS /* amx_NumPublics(), amx_GetPublic() and amx_FindPublic() */ + #define AMX_XXXPUBVARS /* amx_NumPubVars(), amx_GetPubVar() and amx_FindPubVar() */ + #define AMX_XXXSTRING /* amx_StrLen(), amx_GetString() and amx_SetString() */ + #define AMX_XXXTAGS /* amx_NumTags(), amx_GetTag() and amx_FindTagId() */ + #define AMX_XXXUSERDATA /* amx_GetUserData() and amx_SetUserData() */ +#endif +#undef AMX_EXPLIT_FUNCTIONS +#if defined AMX_ANSIONLY + #undef AMX_UTF8XXX /* no UTF-8 support in ANSI/ASCII-only version */ +#endif +#if defined AMX_NO_NATIVEINFO + #undef AMX_NATIVEINFO +#endif +#if AMX_USERNUM <= 0 + #undef AMX_XXXUSERDATA +#endif +#if defined AMX_JIT + /* JIT is incompatible with macro instructions, packed opcodes and overlays */ + #if !defined AMX_NO_MACRO_INSTR + #define AMX_NO_MACRO_INSTR + #endif + #if !defined AMX_NO_PACKED_OPC + #define AMX_NO_PACKED_OPC + #endif + #if !defined AMX_NO_OVERLAY + #define AMX_NO_OVERLAY + #endif +#endif +#if (defined AMX_ASM || defined AMX_JIT) && !defined AMX_ALTCORE + /* do not use the standard ANSI-C amx_Exec() function */ + #define AMX_ALTCORE +#endif +#if !defined AMX_NO_PACKED_OPC && !defined AMX_TOKENTHREADING + #define AMX_TOKENTHREADING /* packed opcodes require token threading */ +#endif + +#if defined AMX_ALTCORE + #if defined __WIN32__ + /* For Watcom C/C++ use register calling convention (faster); for + * Microsoft C/C++ (and most other C compilers) use "cdecl". + * The important point is that you assemble AMXEXEC.ASM with the matching + * calling convention, or the right JIT, respectively. + * AMXJITR.ASM is for Watcom's register calling convention, AMXJITS.ASM and + * AMXJITSN.ASM are for "cdecl". + */ + #if defined __WATCOMC__ && !defined STACKARGS + /* register calling convention; the #pragma tells the compiler into which + * registers the parameters should go + */ + extern cell amx_exec_run(AMX *amx,cell *retval,unsigned char *data); + #pragma aux amx_exec_run parm [eax] [edx] [ebx]; + extern int amx_exec_list(const AMX *amx,const cell **opcodelist,int *numopcodes); + #pragma aux amx_exec_run parm [eax] [edx] [ebx]; + extern cell amx_jit_compile(void *pcode, void *jumparray, void *nativecode); + #pragma aux amx_exec_run parm [eax] [edx] [ebx]; + extern cell amx_jit_run(AMX *amx,cell *retval,unsigned char *data); + #pragma aux amx_jit_run parm [eax] [edx] [ebx]; + extern int amx_jit_list(const AMX *amx,const cell **opcodelist,int *numopcodes); + #pragma aux amx_exec_run parm [eax] [edx] [ebx]; + #elif defined __GNUC__ + /* force "cdecl" by adding an "attribute" to the declaration */ + extern cell amx_exec_run(AMX *amx,cell *retval,unsigned char *data) __attribute__((cdecl)); + extern int amx_exec_list(const AMX *amx,const cell **opcodelist,int *numopcodes) __attribute__((cdecl)); + extern cell amx_jit_compile(void *pcode, void *jumparray, void *nativecode) __attribute__((cdecl)); + extern cell amx_jit_run(AMX *amx,cell *retval,unsigned char *data) __attribute__((cdecl)); + extern int amx_jit_list(const AMX *amx,const cell **opcodelist,int *numopcodes) __attribute__((cdecl)); + #else + /* force "cdecl" by specifying it as a "function class" with the "__cdecl" keyword */ + extern cell __cdecl amx_exec_run(AMX *amx,cell *retval,unsigned char *data); + extern int __cdecl amx_exec_list(const AMX *amx,const cell **opcodelist,int *numopcodes); + extern cell __cdecl amx_jit_compile(void *pcode, void *jumparray, void *nativecode); + extern cell __cdecl amx_jit_run(AMX *amx,cell *retval,unsigned char *data); + extern int __cdecl amx_jit_list(const AMX *amx,const cell **opcodelist,int *numopcodes); + #endif + #else /* __WIN32__ */ + /* assume no specific calling conventions for other platforms than Windows */ + extern cell amx_exec_run(AMX *amx,cell *retval,unsigned char *data); + extern int amx_exec_list(const AMX *amx,const cell **opcodelist,int *numopcodes); + extern cell amx_jit_compile(void *pcode, void *jumparray, void *nativecode); + extern cell amx_jit_run(AMX *amx,cell *retval,unsigned char *data); + extern int amx_jit_list(const AMX *amx,const cell **opcodelist,int *numopcodes); + #endif /* __WIN32__ */ +#else + int amx_exec_list(AMX *amx,const cell **opcodelist,int *numopcodes); +#endif /* AMX_ALTCORE */ + +typedef enum { + OP_NOP, + OP_LOAD_PRI, + OP_LOAD_ALT, + OP_LOAD_S_PRI, + OP_LOAD_S_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, + OP_STOR_S, + OP_SREF_S, + OP_STOR_I, + OP_STRB_I, + OP_ALIGN_PRI, + OP_LCTRL, + OP_SCTRL, + OP_XCHG, + OP_PUSH_PRI, + OP_PUSH_ALT, + OP_PUSHR_PRI, + OP_POP_PRI, + OP_POP_ALT, + OP_PICK, + OP_STACK, + OP_HEAP, + OP_PROC, + OP_RET, + OP_RETN, + OP_CALL, + OP_JUMP, + OP_JZER, + OP_JNZ, + OP_SHL, + OP_SHR, + OP_SSHR, + OP_SHL_C_PRI, + OP_SHL_C_ALT, + OP_SMUL, + OP_SDIV, + OP_ADD, + OP_SUB, + OP_AND, + OP_OR, + OP_XOR, + OP_NOT, + OP_NEG, + OP_INVERT, + OP_EQ, + OP_NEQ, + OP_SLESS, + OP_SLEQ, + OP_SGRTR, + OP_SGEQ, + OP_INC_PRI, + OP_INC_ALT, + OP_INC_I, + OP_DEC_PRI, + OP_DEC_ALT, + OP_DEC_I, + OP_MOVS, + OP_CMPS, + OP_FILL, + OP_HALT, + OP_BOUNDS, + OP_SYSREQ, + OP_SWITCH, + OP_SWAP_PRI, + OP_SWAP_ALT, + OP_BREAK, + OP_CASETBL, + /* patched instructions */ + OP_SYSREQ_D, + OP_SYSREQ_ND, + /* overlay instructions */ + OP_CALL_OVL, + OP_RETN_OVL, + OP_SWITCH_OVL, + OP_CASETBL_OVL, +#if !defined AMX_NO_MACRO_INSTR + /* supplemental & macro instructions */ + OP_LIDX, + OP_LIDX_B, + OP_IDXADDR, + OP_IDXADDR_B, + OP_PUSH_C, + OP_PUSH, + OP_PUSH_S, + OP_PUSH_ADR, + OP_PUSHR_C, + OP_PUSHR_S, + OP_PUSHR_ADR, + OP_JEQ, + OP_JNEQ, + OP_JSLESS, + OP_JSLEQ, + OP_JSGRTR, + OP_JSGEQ, + OP_SDIV_INV, + OP_SUB_INV, + OP_ADD_C, + OP_SMUL_C, + OP_ZERO_PRI, + OP_ZERO_ALT, + OP_ZERO, + OP_ZERO_S, + OP_EQ_C_PRI, + OP_EQ_C_ALT, + OP_INC, + OP_INC_S, + OP_DEC, + OP_DEC_S, + /* macro instructions */ + OP_SYSREQ_N, + OP_PUSHM_C, + OP_PUSHM, + OP_PUSHM_S, + OP_PUSHM_ADR, + OP_PUSHRM_C, + OP_PUSHRM_S, + OP_PUSHRM_ADR, + OP_LOAD2, + OP_LOAD2_S, + OP_CONST, + OP_CONST_S, +#endif +#if !defined AMX_NO_PACKED_OPC + /* packed instructions */ + OP_LOAD_P_PRI, + OP_LOAD_P_ALT, + OP_LOAD_P_S_PRI, + OP_LOAD_P_S_ALT, + OP_LREF_P_S_PRI, + OP_LREF_P_S_ALT, + OP_LODB_P_I, + OP_CONST_P_PRI, + OP_CONST_P_ALT, + OP_ADDR_P_PRI, + OP_ADDR_P_ALT, + OP_STOR_P, + OP_STOR_P_S, + OP_SREF_P_S, + OP_STRB_P_I, + OP_LIDX_P_B, + OP_IDXADDR_P_B, + OP_ALIGN_P_PRI, + OP_PUSH_P_C, + OP_PUSH_P, + OP_PUSH_P_S, + OP_PUSH_P_ADR, + OP_PUSHR_P_C, + OP_PUSHR_P_S, + OP_PUSHR_P_ADR, + OP_PUSHM_P_C, + OP_PUSHM_P, + OP_PUSHM_P_S, + OP_PUSHM_P_ADR, + OP_PUSHRM_P_C, + OP_PUSHRM_P_S, + OP_PUSHRM_P_ADR, + OP_STACK_P, + OP_HEAP_P, + OP_SHL_P_C_PRI, + OP_SHL_P_C_ALT, + OP_ADD_P_C, + OP_SMUL_P_C, + OP_ZERO_P, + OP_ZERO_P_S, + OP_EQ_P_C_PRI, + OP_EQ_P_C_ALT, + OP_INC_P, + OP_INC_P_S, + OP_DEC_P, + OP_DEC_P_S, + OP_MOVS_P, + OP_CMPS_P, + OP_FILL_P, + OP_HALT_P, + OP_BOUNDS_P, +#endif + /* ----- */ + OP_NUM_OPCODES +} OPCODE; + +#define NUMENTRIES(hdr,field,nextfield) \ + (unsigned)(((hdr)->nextfield - (hdr)->field) / (hdr)->defsize) +#define GETENTRY(hdr,table,index) \ + (AMX_FUNCSTUB *)((unsigned char*)(hdr) + (unsigned)(hdr)->table + (unsigned)index*(hdr)->defsize) +#define GETENTRYNAME(hdr,entry) \ + (char *)((unsigned char*)(hdr) + (unsigned)((AMX_FUNCSTUB*)(entry))->nameofs) + +#if !defined NDEBUG + static int check_endian(void) + { + uint16_t val=0x00ff; + unsigned char *ptr=(unsigned char *)&val; + /* "ptr" points to the starting address of "val". If that address + * holds the byte "0xff", the computer stored the low byte of "val" + * at the lower address, and so the memory lay out is Little Endian. + */ + assert(*ptr==0xff || *ptr==0x00); + #if BYTE_ORDER==BIG_ENDIAN + return *ptr==0x00; /* return "true" if big endian */ + #else + return *ptr==0xff; /* return "true" if little endian */ + #endif + } +#endif + +#if BYTE_ORDER==BIG_ENDIAN || PAWN_CELL_SIZE==16 + static void swap16(uint16_t *v) + { + unsigned char *s = (unsigned char *)v; + unsigned char t; + + assert_static(sizeof(*v)==2); + /* swap two bytes */ + t=s[0]; + s[0]=s[1]; + s[1]=t; + } +#endif + +#if BYTE_ORDER==BIG_ENDIAN || PAWN_CELL_SIZE==32 + static void swap32(uint32_t *v) + { + unsigned char *s = (unsigned char *)v; + unsigned char t; + + assert_static(sizeof(*v)==4); + /* swap outer two bytes */ + t=s[0]; + s[0]=s[3]; + s[3]=t; + /* swap inner two bytes */ + t=s[1]; + s[1]=s[2]; + s[2]=t; + } +#endif + +#if (BYTE_ORDER==BIG_ENDIAN || PAWN_CELL_SIZE==64) && (defined _I64_MAX || defined HAVE_I64) + static void swap64(uint64_t *v) + { + unsigned char *s = (unsigned char *)v; + unsigned char t; + + assert(sizeof(*v)==8); + + t=s[0]; + s[0]=s[7]; + s[7]=t; + + t=s[1]; + s[1]=s[6]; + s[6]=t; + + t=s[2]; + s[2]=s[5]; + s[5]=t; + + t=s[3]; + s[3]=s[4]; + s[4]=t; + } +#endif + +#if defined AMX_ALIGN || defined AMX_INIT +uint16_t * AMXAPI amx_Align16(uint16_t *v) +{ + assert_static(sizeof(*v)==2); + assert(check_endian()); + #if BYTE_ORDER==BIG_ENDIAN + swap16(v); + #endif + return v; +} + +uint32_t * AMXAPI amx_Align32(uint32_t *v) +{ + assert_static(sizeof(*v)==4); + assert(check_endian()); + #if BYTE_ORDER==BIG_ENDIAN + swap32(v); + #endif + return v; +} + +#if defined _I64_MAX || defined HAVE_I64 +uint64_t * AMXAPI amx_Align64(uint64_t *v) +{ + assert(sizeof(*v)==8); + assert(check_endian()); + #if BYTE_ORDER==BIG_ENDIAN + swap64(v); + #endif + return v; +} +#endif /* _I64_MAX || HAVE_I64 */ +#endif /* AMX_ALIGN || AMX_INIT */ + +#if PAWN_CELL_SIZE==16 + #define swapcell swap16 +#elif PAWN_CELL_SIZE==32 + #define swapcell swap32 +#elif PAWN_CELL_SIZE==64 && (defined _I64_MAX || defined HAVE_I64) + #define swapcell swap64 +#else + #error Unsupported cell size +#endif + +#if defined AMX_FLAGS +int AMXAPI amx_Flags(AMX *amx,uint16_t *flags) +{ + AMX_HEADER *hdr; + + *flags=0; + if (amx==NULL) + return AMX_ERR_FORMAT; + hdr=(AMX_HEADER *)amx->base; + if (hdr->magic!=AMX_MAGIC) + return AMX_ERR_FORMAT; + if (hdr->file_version>CUR_FILE_VERSION || hdr->amx_version<MIN_FILE_VERSION) + return AMX_ERR_VERSION; + *flags=hdr->flags; + return AMX_ERR_NONE; +} +#endif /* AMX_FLAGS */ + +#if defined AMX_DEFCALLBACK +int AMXAPI amx_Callback(AMX *amx, cell index, cell *result, const cell *params) +{ +#if defined AMX_NATIVETABLE + extern AMX_NATIVE const AMX_NATIVETABLE[]; +#endif + AMX_HEADER *hdr; + AMX_FUNCSTUB *func; + AMX_NATIVE f; + + assert(amx!=NULL); + hdr=(AMX_HEADER *)amx->base; + assert(hdr!=NULL); + assert(hdr->magic==AMX_MAGIC); + assert(hdr->natives<=hdr->libraries); +#if defined AMX_NATIVETABLE + if (index<0) { + /* size of AMX_NATIVETABLE is unknown here, so we cannot verify index */ + f=(AMX_NATIVETABLE)[-(index+1)]; + } else { +#endif + assert(index>=0 && index<(cell)NUMENTRIES(hdr,natives,libraries)); + func=GETENTRY(hdr,natives,index); + f=(AMX_NATIVE)func->address; +#if defined AMX_NATIVETABLE + } /* if */ +#endif + assert(f!=NULL); + + /* Now that we have found the function, patch the program so that any + * subsequent call will call the function directly (bypassing this + * callback). + * This trick cannot work in the JIT, because the program would need to + * be re-JIT-compiled after patching a P-code instruction. + */ + assert((amx->flags & AMX_FLAG_JITC)==0 || amx->sysreq_d==0); + if (amx->sysreq_d!=0) { + /* at the point of the call, the CIP pseudo-register points directly + * behind the SYSREQ(.N) instruction and its parameter(s) + */ + unsigned char *code=amx->code+(int)amx->cip-sizeof(cell); + if (amx->flags & AMX_FLAG_SYSREQN) /* SYSREQ.N has 2 parameters */ + code-=sizeof(cell); + assert(amx->code!=NULL); + assert(amx->cip>=4 && amx->cip<(hdr->dat - hdr->cod)); + assert_static(sizeof(f)<=sizeof(cell)); /* function pointer must fit in a cell */ + assert(*(cell*)code==index); + #if defined AMX_TOKENTHREADING || !(defined __GNUC__ || defined __ICC || defined AMX_ASM || defined AMX_JIT) + assert(!(amx->flags & AMX_FLAG_SYSREQN) && *(cell*)(code-sizeof(cell))==OP_SYSREQ + || (amx->flags & AMX_FLAG_SYSREQN) && *(cell*)(code-sizeof(cell))==OP_SYSREQ_N); + #endif + *(cell*)(code-sizeof(cell))=amx->sysreq_d; + *(cell*)code=(cell)f; + } /* if */ + + /* Note: + * params[0] == number of bytes for the additional parameters passed to the native function + * params[1] == first argument + * etc. + */ + + amx->error=AMX_ERR_NONE; + *result = f(amx,params); + return amx->error; +} +#endif /* defined AMX_DEFCALLBACK */ + + +#if defined AMX_JIT + /* convert from relative addresses to absolute physical addresses */ + #define RELOC_ABS(base,off) (*(ucell *)((base)+(int)(off)) += (ucell)(base)+(int)(off)-sizeof(cell)) +#else + #define JUMPREL(ip) ((cell*)((intptr_t)(ip)+*(cell*)(ip)-sizeof(cell))) +#endif +#if defined AMX_ASM || defined AMX_JIT + #define RELOCATE_ADDR(base,v) ((v)+((ucell)(base))) +#else + #define RELOCATE_ADDR(base,v) (v) +#endif + +#define DBGPARAM(v) ( (v)=*(cell *)(amx->code+(int)cip), cip+=sizeof(cell) ) + +#if !defined GETOPCODE + #if defined AMX_NO_PACKED_OPC + #define GETOPCODE(c) (OPCODE)(c) + #else + #define GETOPCODE(c) (OPCODE)((c) & ((1 << sizeof(cell)*4)-1)) + #endif +#endif +#if !defined GETPARAM_P + #define GETPARAM_P(v,o) ( v=((cell)(o) >> (int)(sizeof(cell)*4)) ) +#endif + +#if defined AMX_INIT + +static int VerifyPcode(AMX *amx) +{ + AMX_HEADER *hdr; + cell op,cip,tgt,opmask; + int sysreq_flg,max_opcode; + int datasize,stacksize; + const cell *opcode_list; + #if defined AMX_JIT + int opcode_count=0; + int reloc_count=0; + int jit_codesize=0; + #endif + + assert(amx!=NULL); + hdr=(AMX_HEADER *)amx->base; + assert(hdr!=NULL); + assert(hdr->magic==AMX_MAGIC); + amx->flags|=AMX_FLAG_VERIFY; + datasize=hdr->hea-hdr->dat; + stacksize=hdr->stp-hdr->hea; + + #if defined AMX_ASM && defined AMX_JIT + if ((amx->flags & AMX_FLAG_JITC)!=0) + jit_codesize=amx_jit_list(amx,&opcode_list,&max_opcode); + else + amx_exec_list(amx,&opcode_list,&max_opcode); + #elif defined AMX_JIT + jit_codesize=amx_jit_list(amx,&opcode_list,&max_opcode); + #else + amx_exec_list(amx,&opcode_list,&max_opcode); + #endif + #if defined AMX_TOKENTHREADING + opcode_list=NULL; /* avoid token translation if token threading is in effect */ + #endif + #if defined AMX_NO_PACKED_OPC + opmask= ~0; + #else + opmask=(1 << sizeof(cell)*4)-1; + #endif + + /* sanity checks */ + assert_static(OP_XCHG==21); + assert_static(OP_SMUL==42); + assert_static(OP_MOVS==64); + #if !defined AMX_NO_MACRO_INSTR + assert_static(OP_LIDX==81); + assert_static(OP_ZERO_PRI==102); + assert_static(OP_LOAD2==120); + #endif + #if !defined AMX_NO_PACKED_OPC + assert_static(OP_LOAD_P_PRI==124); + assert_static(OP_ALIGN_P_PRI==141); + assert_static(OP_BOUNDS_P==174); + #endif + + sysreq_flg=0; + if (opcode_list!=NULL) { + if (amx->sysreq_d==opcode_list[OP_SYSREQ_D]) + sysreq_flg=0x01; + else if (amx->sysreq_d==opcode_list[OP_SYSREQ_ND]) + sysreq_flg=0x02; + } else { + if (amx->sysreq_d==OP_SYSREQ_D) + sysreq_flg=0x01; + else if (amx->sysreq_d==OP_SYSREQ_ND) + sysreq_flg=0x02; + } /* if */ + amx->sysreq_d=0; /* preset */ + + /* start browsing code */ + assert(amx->code!=NULL); /* should already have been set in amx_Init() */ + for (cip=0; cip<amx->codesize; ) { + op=*(cell *)(amx->code+(int)cip); + if ((op & opmask)>=max_opcode) { + amx->flags &= ~AMX_FLAG_VERIFY; + return AMX_ERR_INVINSTR; + } /* if */ + /* relocate opcode (only works if the size of an opcode is at least + * as big as the size of a pointer (jump address); so basically we + * rely on the opcode and a pointer being 32-bit + */ + if (opcode_list!=NULL) { + /* verify that opcode_list[op]!=NULL, if it is, this instruction + * is unsupported + */ + if (opcode_list[op & opmask]==0) { + amx->flags &= ~AMX_FLAG_VERIFY; + return AMX_ERR_INVINSTR; + } /* if */ + *(cell *)(amx->code+(int)cip)=opcode_list[op & opmask]; + } /* if */ + #if defined AMX_JIT + opcode_count++; + #endif + cip+=sizeof(cell); + switch (op & opmask) { +#if !defined AMX_NO_MACRO_INSTR + case OP_PUSHM_C: /* instructions with variable number of parameters */ + case OP_PUSHM: + case OP_PUSHM_S: + case OP_PUSHM_ADR: + case OP_PUSHRM_C: + case OP_PUSHRM_S: + case OP_PUSHRM_ADR: + tgt=*(cell*)(amx->code+(int)cip); /* get count */ + cip+=sizeof(cell)*(tgt+1); + break; + + case OP_LOAD2: + tgt=*(cell*)(amx->code+(int)cip); /* verify both addresses */ + if (tgt<0 || tgt>=datasize) { + amx->flags &= ~AMX_FLAG_VERIFY; + return AMX_ERR_BOUNDS; + } /* if */ + tgt=*(cell*)(amx->code+(int)cip+(int)sizeof(cell)); + if (tgt<0 || tgt>=datasize) { + amx->flags &= ~AMX_FLAG_VERIFY; + return AMX_ERR_BOUNDS; + } /* if */ + cip+=sizeof(cell)*2; + break; + + case OP_LOAD2_S: + tgt=*(cell*)(amx->code+(int)cip); /* verify both addresses */ + if (tgt<-stacksize || tgt>stacksize) { + amx->flags &= ~AMX_FLAG_VERIFY; + return AMX_ERR_BOUNDS; + } /* if */ + tgt=*(cell*)(amx->code+(int)cip+(int)sizeof(cell)); + if (tgt<-stacksize || tgt>stacksize) { + amx->flags &= ~AMX_FLAG_VERIFY; + return AMX_ERR_BOUNDS; + } /* if */ + cip+=sizeof(cell)*2; + break; + + case OP_CONST: + tgt=*(cell*)(amx->code+(int)cip); /* verify address */ + if (tgt<0 || tgt>=datasize) { + amx->flags &= ~AMX_FLAG_VERIFY; + return AMX_ERR_BOUNDS; + } /* if */ + cip+=sizeof(cell)*2; + break; + + case OP_CONST_S: + tgt=*(cell*)(amx->code+(int)cip); /* verify both addresses */ + if (tgt<-stacksize || tgt>stacksize) { + amx->flags &= ~AMX_FLAG_VERIFY; + return AMX_ERR_BOUNDS; + } /* if */ + cip+=sizeof(cell)*2; + break; +#endif /* !defined AMX_NO_MACRO_INSTR */ + +#if !defined AMX_NO_PACKED_OPC + case OP_LODB_P_I: /* instructions with 1 parameter packed inside the same cell */ + case OP_CONST_P_PRI: + case OP_CONST_P_ALT: + case OP_ADDR_P_PRI: + case OP_ADDR_P_ALT: + case OP_STRB_P_I: + case OP_LIDX_P_B: + case OP_IDXADDR_P_B: + case OP_ALIGN_P_PRI: + case OP_PUSH_P_C: + case OP_PUSH_P: + case OP_PUSH_P_S: + case OP_PUSH_P_ADR: + case OP_PUSHR_P_C: + case OP_PUSHR_P_S: + case OP_PUSHR_P_ADR: + case OP_STACK_P: + case OP_HEAP_P: + case OP_SHL_P_C_PRI: + case OP_SHL_P_C_ALT: + case OP_ADD_P_C: + case OP_SMUL_P_C: + case OP_ZERO_P: + case OP_ZERO_P_S: + case OP_EQ_P_C_PRI: + case OP_EQ_P_C_ALT: + case OP_MOVS_P: + case OP_CMPS_P: + case OP_FILL_P: + case OP_HALT_P: + case OP_BOUNDS_P: + break; + + case OP_LOAD_P_PRI: /* data instructions with 1 parameter packed inside the same cell */ + case OP_LOAD_P_ALT: + case OP_STOR_P: + case OP_INC_P: + case OP_DEC_P: + GETPARAM_P(tgt,op); /* verify address */ + if (tgt<0 || tgt>=datasize) { + amx->flags &= ~AMX_FLAG_VERIFY; + return AMX_ERR_BOUNDS; + } /* if */ + break; + + case OP_LOAD_P_S_PRI: /* stack instructions with 1 parameter packed inside the same cell */ + case OP_LOAD_P_S_ALT: + case OP_LREF_P_S_PRI: + case OP_LREF_P_S_ALT: + case OP_STOR_P_S: + case OP_SREF_P_S: + case OP_INC_P_S: + case OP_DEC_P_S: + GETPARAM_P(tgt,op); /* verify address */ + if (tgt<-stacksize || tgt>stacksize) { + amx->flags &= ~AMX_FLAG_VERIFY; + return AMX_ERR_BOUNDS; + } /* if */ + break; + + case OP_PUSHM_P_C: /* instructions with variable number of parameters */ + case OP_PUSHM_P: + case OP_PUSHM_P_S: + case OP_PUSHM_P_ADR: + case OP_PUSHRM_P_C: + case OP_PUSHRM_P_S: + case OP_PUSHRM_P_ADR: + GETPARAM_P(tgt,op); /* verify address */ + cip+=sizeof(cell)*tgt; + break; +#endif /* !defined AMX_NO_PACKED_OPC */ + + case OP_LODB_I: /* instructions with 1 parameter (not packed) */ + case OP_CONST_PRI: + case OP_CONST_ALT: + case OP_ADDR_PRI: + case OP_ADDR_ALT: + case OP_STRB_I: + case OP_ALIGN_PRI: + case OP_LCTRL: + case OP_SCTRL: + case OP_PICK: + case OP_STACK: + case OP_HEAP: + case OP_SHL_C_PRI: + case OP_SHL_C_ALT: + case OP_MOVS: + case OP_CMPS: + case OP_FILL: + case OP_HALT: + case OP_BOUNDS: +#if !defined AMX_NO_MACRO_INSTR + case OP_LIDX_B: + case OP_IDXADDR_B: + case OP_PUSH_C: + case OP_PUSH_ADR: + case OP_PUSHR_C: + case OP_PUSHR_ADR: + 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: +#endif + cip+=sizeof(cell); + break; + + case OP_LOAD_PRI: + case OP_LOAD_ALT: + case OP_STOR: +#if !defined AMX_NO_MACRO_INSTR + case OP_PUSH: + case OP_INC: + case OP_DEC: +#endif + tgt=*(cell*)(amx->code+(int)cip); /* verify address */ + if (tgt<0 || tgt>=datasize) { + amx->flags &= ~AMX_FLAG_VERIFY; + return AMX_ERR_BOUNDS; + } /* if */ + cip+=sizeof(cell); + break; + + case OP_LOAD_S_PRI: + case OP_LOAD_S_ALT: + case OP_LREF_S_PRI: + case OP_LREF_S_ALT: + case OP_STOR_S: + case OP_SREF_S: +#if !defined AMX_NO_MACRO_INSTR + case OP_PUSH_S: + case OP_PUSHR_S: + case OP_INC_S: + case OP_DEC_S: +#endif + tgt=*(cell*)(amx->code+(int)cip); /* verify address */ + if (tgt<-stacksize || tgt>stacksize) { + amx->flags &= ~AMX_FLAG_VERIFY; + return AMX_ERR_BOUNDS; + } /* if */ + cip+=sizeof(cell); + break; + + case OP_NOP: /* instructions without parameters */ + case OP_LOAD_I: + case OP_STOR_I: + case OP_XCHG: + case OP_PUSH_PRI: + case OP_PUSH_ALT: + case OP_PUSHR_PRI: + case OP_POP_PRI: + case OP_POP_ALT: + case OP_PROC: + case OP_RET: + case OP_RETN: + case OP_SHL: + case OP_SHR: + case OP_SSHR: + case OP_SMUL: + case OP_SDIV: + case OP_ADD: + case OP_SUB: + case OP_AND: + case OP_OR: + case OP_XOR: + case OP_NOT: + case OP_NEG: + case OP_INVERT: + case OP_EQ: + case OP_NEQ: + 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_SWAP_PRI: + case OP_SWAP_ALT: + case OP_BREAK: +#if !defined AMX_NO_MACRO_INSTR + case OP_LIDX: + case OP_IDXADDR: + case OP_SDIV_INV: + case OP_SUB_INV: + case OP_ZERO_PRI: + case OP_ZERO_ALT: +#endif + break; + + case OP_CALL: /* opcodes that need relocation (JIT only), or conversion to position-independent code */ + case OP_JUMP: + case OP_JZER: + case OP_JNZ: + case OP_SWITCH: +#if !defined AMX_NO_MACRO_INSTR + case OP_JEQ: + case OP_JNEQ: + case OP_JSLESS: + case OP_JSLEQ: + case OP_JSGRTR: + case OP_JSGEQ: +#endif + tgt=*(cell*)(amx->code+(int)cip)+cip-sizeof(cell); + if (tgt<0 || tgt>amx->codesize) { + amx->flags &= ~AMX_FLAG_VERIFY; + return AMX_ERR_BOUNDS; + } /* if */ + #if defined AMX_JIT + reloc_count++; + RELOC_ABS(amx->code, cip); /* change to absolute physical address */ + #endif + cip+=sizeof(cell); + break; + +#if !defined AMX_NO_OVERLAY + /* overlay opcodes (overlays must be enabled) */ + case OP_SWITCH_OVL: + assert(hdr->file_version>=10); + tgt=*(cell*)(amx->code+(int)cip)+cip-sizeof(cell); + if (tgt<0 || tgt>amx->codesize) { + amx->flags &= ~AMX_FLAG_VERIFY; + return AMX_ERR_BOUNDS; + } /* if */ + /* drop through */ + case OP_CALL_OVL: + cip+=sizeof(cell); + /* drop through */ + case OP_RETN_OVL: + assert(hdr->overlays!=0 && hdr->overlays!=hdr->nametable); + #if defined AMX_JIT + if ((amx->flags & AMX_FLAG_JITC)!=0) + return AMX_ERR_OVERLAY; /* JIT does not support overlays */ + #endif + if (amx->overlay==NULL) + return AMX_ERR_OVERLAY; /* no overlay callback */ + break; + case OP_CASETBL_OVL: { + cell num; + DBGPARAM(num); /* number of records follows the opcode */ + cip+=(2*num + 1)*sizeof(cell); + if (amx->overlay==NULL) + return AMX_ERR_OVERLAY; /* no overlay callback */ + break; + } /* case */ +#endif + + case OP_SYSREQ: + cip+=sizeof(cell); + sysreq_flg|=0x01; /* mark SYSREQ found */ + break; +#if !defined AMX_NO_MACRO_INSTR + case OP_SYSREQ_N: + cip+=sizeof(cell)*2; + sysreq_flg|=0x02; /* mark SYSREQ.N found */ + break; +#endif + + case OP_CASETBL: { + cell num,offs; + int i; + DBGPARAM(num); /* number of records follows the opcode */ + for (i=0; i<=num; i++) { + offs=cip+2*i*sizeof(cell); + tgt=*(cell*)(amx->code+(int)offs)+offs-sizeof(cell); + if (tgt<0 || tgt>amx->codesize) { + amx->flags &= ~AMX_FLAG_VERIFY; + return AMX_ERR_BOUNDS; + } /* if */ + #if defined AMX_JIT + RELOC_ABS(amx->code, cip+2*i*sizeof(cell)); + reloc_count++; + #endif + } /* for */ + cip+=(2*num + 1)*sizeof(cell); + break; + } /* case */ + + default: + amx->flags &= ~AMX_FLAG_VERIFY; + return AMX_ERR_INVINSTR; + } /* switch */ + } /* for */ + + #if !defined AMX_DONT_RELOCATE + /* only either type of system request opcode should be found (otherwise, + * we probably have a non-conforming compiler + */ + if ((sysreq_flg==0x01 || sysreq_flg==0x02) && (amx->flags & AMX_FLAG_JITC)==0) { + /* to use direct system requests, a function pointer must fit in a cell; + * because the native function's address will be stored as the parameter + * of SYSREQ.(N)D + */ + if (sizeof(AMX_NATIVE)<=sizeof(cell)) { + if (opcode_list!=NULL) + amx->sysreq_d=(sysreq_flg==0x01) ? opcode_list[OP_SYSREQ_D] : opcode_list[OP_SYSREQ_ND]; + else + amx->sysreq_d=(sysreq_flg==0x01) ? OP_SYSREQ_D : OP_SYSREQ_ND; + } /* if */ + } /* if */ + #endif + + #if defined AMX_JIT + /* adjust the code size to mean: estimated code size of the native code + * (instead of the size of the P-code) + */ + amx->codesize=jit_codesize*opcode_count + hdr->cod + (hdr->stp - hdr->dat); + amx->reloc_size=2*sizeof(cell)*reloc_count; + #endif + + amx->flags &= ~AMX_FLAG_VERIFY; + amx->flags |= AMX_FLAG_INIT; + if (sysreq_flg & 0x02) + amx->flags |= AMX_FLAG_SYSREQN; + + return AMX_ERR_NONE; +} + +/* definitions used for amx_Init() and amx_Cleanup() */ +#if (defined _Windows || defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__) && !defined AMX_NODYNALOAD + typedef int AMXEXPORT (AMXAPI _FAR *AMX_ENTRY)(AMX _FAR *amx); +#endif + +int AMXAPI amx_Init(AMX *amx,void *program) +{ + AMX_HEADER *hdr; + int err; + uint16_t *namelength; + unsigned char *data; + #if (defined _Windows || defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__) && !defined AMX_NODYNALOAD + #if defined _Windows + char libname[sNAMEMAX+8]; /* +1 for '\0', +3 for 'amx' prefix, +4 for extension */ + HINSTANCE hlib; + #elif defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ + char libname[_MAX_PATH]; + char *root; + void *hlib; + #if !defined AMX_LIBPATH + #define AMX_LIBPATH "AMXLIB" + #endif + #endif + int numlibraries; + AMX_FUNCSTUB *lib; + AMX_ENTRY libinit; + #endif + + if ((amx->flags & AMX_FLAG_INIT)!=0) + return AMX_ERR_INIT; /* already initialized (may not do so twice) */ + + hdr=(AMX_HEADER *)program; + /* the header is in Little Endian, on a Big Endian machine, swap all + * multi-byte words + */ + assert(check_endian()); + #if BYTE_ORDER==BIG_ENDIAN + amx_Align32((uint32_t*)&hdr->size); + amx_Align16(&hdr->magic); + amx_Align16((uint16_t*)&hdr->flags); + amx_Align16((uint16_t*)&hdr->defsize); + amx_Align32((uint32_t*)&hdr->cod); + amx_Align32((uint32_t*)&hdr->dat); + amx_Align32((uint32_t*)&hdr->hea); + amx_Align32((uint32_t*)&hdr->stp); + amx_Align32((uint32_t*)&hdr->cip); + amx_Align32((uint32_t*)&hdr->publics); + amx_Align32((uint32_t*)&hdr->natives); + amx_Align32((uint32_t*)&hdr->libraries); + amx_Align32((uint32_t*)&hdr->pubvars); + amx_Align32((uint32_t*)&hdr->tags); + if (hdr->file_version>=10) + amx_Align32((uint32_t*)&hdr->overlays); + #endif + + if (hdr->magic!=AMX_MAGIC) + return AMX_ERR_FORMAT; + if (hdr->file_version>CUR_FILE_VERSION || hdr->amx_version<MIN_FILE_VERSION) + return AMX_ERR_VERSION; + if (hdr->defsize!=sizeof(AMX_FUNCSTUB)) + return AMX_ERR_FORMAT; + /* check the maximum name length in the separate name table */ + amx_Align32((uint32_t*)&hdr->nametable); + namelength=(uint16_t*)((unsigned char*)program + (unsigned)hdr->nametable); + amx_Align16(namelength); + if (*namelength>sNAMEMAX) + return AMX_ERR_FORMAT; + if (hdr->stp<=0) + return AMX_ERR_FORMAT; + assert(hdr->hea == hdr->size); + #if BYTE_ORDER==BIG_ENDIAN + if ((hdr->flags & AMX_FLAG_COMPACT)==0) { + ucell *code=(ucell *)((unsigned char *)program+(int)hdr->cod); + while (code<(ucell *)((unsigned char *)program+(int)hdr->hea)) + swapcell(code++); + } /* if */ + #endif + + amx->base=(unsigned char *)program; + + /* 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 defined AMX_DEFCALLBACK + if (amx->callback==NULL) + amx->callback=amx_Callback; + #endif + + /* when running P-code from ROM (with the header with the native function + * table in RAM), the "code" field must be set to a non-NULL value on + * initialization, before calling amx_Init(); in an overlay scheme, the + * code field is modified dynamically by the overlay callback + */ + if (amx->code==NULL) + amx->code=amx->base+(int)hdr->cod; + if (amx->codesize==0) + amx->codesize=hdr->dat-hdr->cod; + + /* to split the data segment off the code segment, the "data" field must + * be set to a non-NULL value on initialization, before calling amx_Init(); + * you may also need to explicitly initialize the data section with the + * contents read from the AMX file + */ + if (amx->data!=NULL) { + data=amx->data; + if ((amx->flags & AMX_FLAG_DSEG_INIT)==0 && amx->overlay==NULL) + memcpy(data,amx->base+(int)hdr->dat,(size_t)(hdr->hea-hdr->dat)); + } else { + data=amx->base+(int)hdr->dat; + } /* if */ + + /* Set a zero cell at the top of the stack, which functions + * as a sentinel for strings. + */ + * (cell *)(data+(int)(hdr->stp-hdr->dat-sizeof(cell)))=0; + + /* also align all addresses in the public function, public variable, + * public tag and native function tables --offsets into the name table + * (if present) must also be swapped. + */ + #if BYTE_ORDER==BIG_ENDIAN + { /* local */ + AMX_FUNCSTUB *fs; + int i,num; + + fs=GETENTRY(hdr,natives,0); + num=NUMENTRIES(hdr,natives,libraries); + for (i=0; i<num; i++) { + amx_Align32(&fs->address); /* redundant, because it should be zero */ + amx_Align32(&fs->nameofs); + fs=(AMX_FUNCSTUB*)((unsigned char *)fs+hdr->defsize); + } /* for */ + + fs=GETENTRY(hdr,publics,0); + assert(hdr->publics<=hdr->natives); + num=NUMENTRIES(hdr,publics,natives); + for (i=0; i<num; i++) { + amx_Align32(&fs->address); + amx_Align32(&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; i<num; i++) { + amx_Align32(&fs->address); + amx_Align32(&fs->nameofs); + fs=(AMX_FUNCSTUB*)((unsigned char *)fs+hdr->defsize); + } /* for */ + + fs=GETENTRY(hdr,tags,0); + if (hdr->file_version<7) { /* file version 7 introduced the name table */ + 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; i<num; i++) { + amx_Align32(&fs->address); + amx_Align32(&fs->nameofs); + fs=(AMX_FUNCSTUB*)((unsigned char *)fs+hdr->defsize); + } /* for */ + } /* local */ + #endif + + /* verify P-code and relocate address in the case of the JIT */ + if ((hdr->flags & AMX_FLAG_OVERLAY)==0) { + err=VerifyPcode(amx); + } else { + int i; + err=(amx->overlay==NULL) ? AMX_ERR_OVERLAY : AMX_ERR_NONE; + /* load every overlay on initialization and verify explicitly; we must + * do this to know whether to use new or old system requests + */ + for (i=0; err==AMX_ERR_NONE && i<(int)((hdr->nametable - hdr->overlays)/sizeof(AMX_OVERLAYINFO)); i++) { + err=amx->overlay(amx, i); + if (err==AMX_ERR_NONE) + err=VerifyPcode(amx); + } /* for */ + } /* if */ + if (err!=AMX_ERR_NONE) + return err; + + /* load any extension modules that the AMX refers to */ + #if (defined _Windows || defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__) && !defined AMX_NODYNALOAD + { /* local */ + int i; + #if defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ + root=getenv("AMXLIB"); + #endif + hdr=(AMX_HEADER *)amx->base; + numlibraries=NUMENTRIES(hdr,libraries,pubvars); + for (i=0; i<numlibraries; i++) { + lib=GETENTRY(hdr,libraries,i); + libname[0]='\0'; + #if defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ + if (root!=NULL && *root!='\0') { + strcpy(libname,root); + if (libname[strlen(libname)-1]!='/') + strcat(libname,"/"); + } /* if */ + #endif + strcat(libname,"amx"); + strcat(libname,GETENTRYNAME(hdr,lib)); + #if defined _Windows + strcat(libname,".dll"); + #if defined __WIN32__ + hlib=LoadLibraryA(libname); + #else + hlib=LoadLibrary(libname); + if (hlib<=HINSTANCE_ERROR) + hlib=NULL; + #endif + #elif defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ + strcat(libname,".so"); + hlib=dlopen(libname,RTLD_NOW); + #endif + if (hlib!=NULL) { + /* a library that cannot be loaded or that does not have the required + * initialization function is simply ignored + */ + char funcname[sNAMEMAX+9]; /* +1 for '\0', +4 for 'amx_', +4 for 'Init' */ + strcpy(funcname,"amx_"); + strcat(funcname,GETENTRYNAME(hdr,lib)); + strcat(funcname,"Init"); + #if defined _Windows + libinit=(AMX_ENTRY)GetProcAddress(hlib,funcname); + #elif defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ + libinit=(AMX_ENTRY)dlsym(hlib,funcname); + #endif + if (libinit!=NULL) + libinit(amx); + } /* if */ + lib->address=(ucell)hlib; + } /* for */ + } /* local */ + #endif + + return AMX_ERR_NONE; +} + +#if defined AMX_JIT + + #define CODESIZE_JIT 8192 /* approximate size of the code for the JIT */ + + #if defined __WIN32__ /* this also applies to Win32 "console" applications */ + + #define ALIGN(addr) (addr) + + #define PROT_READ 0x1 /* page can be read */ + #define PROT_WRITE 0x2 /* page can be written */ + #define PROT_EXEC 0x4 /* page can be executed */ + #define PROT_NONE 0x0 /* page can not be accessed */ + + static int mprotect(void *addr, size_t len, int prot) + { + DWORD prev, p = 0; + if ((prot & PROT_WRITE)!=0) + p = PAGE_EXECUTE_READWRITE; + else + p |= PAGE_EXECUTE_READ; + return !VirtualProtect(addr, len, p, &prev); + } + + #elif defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ + + /* Linux already has mprotect() */ + #define ALIGN(addr) (char *)(((long)addr + sysconf(_SC_PAGESIZE)-1) & ~(sysconf(_SC_PAGESIZE)-1)) + + #else + + // TODO: Add cases for Mac OS/X and other operating systems + + /* DOS32 has no imposed limits on its segments */ + #define ALIGN(addr) (addr) + #define mprotect(addr, len, prot) (0) + + #endif /* #if defined __WIN32 __ */ + +int AMXAPI amx_InitJIT(AMX *amx, void *reloc_table, void *native_code) +{ + int res; + AMX_HEADER *hdr; + + if ((amx->flags & AMX_FLAG_JITC)==0) + return AMX_ERR_INIT_JIT; /* flag not set, this AMX is not prepared for JIT */ + if (hdr->file_version>MAX_FILE_VER_JIT) + return AMX_ERR_VERSION; /* JIT may not support the newest file version(s) */ + /* the JIT does not support overlays, but this is already checked in VerifyPcode() + * the same goes for macro instructions: not supported in the JIT, but already + * checked in VerifyPcode() + */ + + /* Patching SYSREQ(.N) opcodes to SYSREQ.(N)D cannot work in the JIT, because + * the program would need to be re-JIT-compiled after patching a P-code + * instruction. If this field is not zero, something went wrong in + * VerifyPcode(). + */ + assert(amx->sysreq_d==0); + + if (mprotect(ALIGN(amx_jit_compile), CODESIZE_JIT, PROT_READ | PROT_WRITE | PROT_EXEC) != 0) + return AMX_ERR_INIT_JIT; + + /* copy the prefix */ + memcpy(native_code, amx->base, ((AMX_HEADER *)(amx->base))->cod); + hdr = native_code; + + /* MP: added check for correct compilation */ + if ((res = amx_jit_compile(amx->base, reloc_table, native_code)) == 0) { + /* update the required memory size (the previous value was a + * conservative estimate, now we know the exact size) + */ + amx->codesize = (hdr->dat + hdr->stp + sizeof(cell)) & ~(sizeof(cell)-1); + /* The compiled code is relocatable, since only relative jumps are + * used for destinations within the generated code, and absolute + * addresses are only for jumps into the runtime, which is fixed + * in memory. + */ + /* set the new pointers */ + amx->base = (unsigned char*)native_code; + amx->code = amx->base + (int)hdr->cod; + amx->cip = hdr->cip; + } /* if */ + + return (res == 0) ? AMX_ERR_NONE : AMX_ERR_INIT_JIT; +} + +#else /* #if defined AMX_JIT */ + +int AMXAPI amx_InitJIT(AMX *amx,void *compiled_program,void *reloc_table) +{ + (void)amx; + (void)compiled_program; + (void)reloc_table; + return AMX_ERR_INIT_JIT; +} + +#endif /* #if defined AMX_JIT */ + +#endif /* AMX_INIT */ + +#if defined AMX_CLEANUP +int AMXAPI amx_Cleanup(AMX *amx) +{ + #if (defined _Windows || defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__) && !defined AMX_NODYNALOAD + AMX_HEADER *hdr; + int numlibraries,i; + AMX_FUNCSTUB *lib; + AMX_ENTRY libcleanup; + #endif + + /* unload all extension modules */ + #if (defined _Windows || defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__) && !defined AMX_NODYNALOAD + hdr=(AMX_HEADER *)amx->base; + assert(hdr->magic==AMX_MAGIC); + numlibraries=NUMENTRIES(hdr,libraries,pubvars); + for (i=0; i<numlibraries; i++) { + lib=GETENTRY(hdr,libraries,i); + if (lib->address!=0) { + char funcname[sNAMEMAX+12]; /* +1 for '\0', +4 for 'amx_', +7 for 'Cleanup' */ + strcpy(funcname,"amx_"); + strcat(funcname,GETENTRYNAME(hdr,lib)); + strcat(funcname,"Cleanup"); + #if defined _Windows + libcleanup=(AMX_ENTRY)GetProcAddress((HINSTANCE)lib->address,funcname); + #elif defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ + libcleanup=(AMX_ENTRY)dlsym((void*)lib->address,funcname); + #endif + if (libcleanup!=NULL) + libcleanup(amx); + #if defined _Windows + FreeLibrary((HINSTANCE)lib->address); + #elif defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ + dlclose((void*)lib->address); + #endif + } /* if */ + } /* for */ + #else + (void)amx; + #endif + return AMX_ERR_NONE; +} +#endif /* AMX_CLEANUP */ + +#if defined AMX_CLONE +int AMXAPI amx_Clone(AMX *amxClone, AMX *amxSource, void *data) +{ + AMX_HEADER *hdr; + unsigned char _FAR *dataSource; + + if (amxSource==NULL) + return AMX_ERR_FORMAT; + if (amxClone==NULL) + return AMX_ERR_PARAMS; + if ((amxSource->flags & AMX_FLAG_INIT)==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_version<MIN_FILE_VERSION) + return AMX_ERR_VERSION; + + /* set initial values */ + amxClone->base=amxSource->base; + amxClone->code=amxSource->code; + amxClone->codesize=amxSource->codesize; + amxClone->hlw=hdr->hea - hdr->dat; /* stack and heap relative to data segment */ + amxClone->stp=hdr->stp - hdr->dat - sizeof(cell); + amxClone->hea=amxClone->hlw; + amxClone->stk=amxClone->stp; + if (amxClone->callback==NULL) + amxClone->callback=amxSource->callback; + if (amxClone->debug==NULL) + amxClone->debug=amxSource->debug; + amxClone->flags=amxSource->flags; + + /* copy the data segment; the stack and the heap can be left uninitialized */ + assert(data!=NULL); + amxClone->data=(unsigned char _FAR *)data; + dataSource=(amxSource->data!=NULL) ? amxSource->data : amxSource->base+(int)hdr->dat; + memcpy(amxClone->data,dataSource,(size_t)(hdr->hea-hdr->dat)); + + /* Set a zero cell at the top of the stack, which functions + * as a sentinel for strings. + */ + * (cell *)(amxClone->data+(int)amxClone->stp) = 0; + + return AMX_ERR_NONE; +} +#endif /* AMX_CLONE */ + +#if defined AMX_MEMINFO +int AMXAPI amx_MemInfo(AMX *amx, long *codesize, long *datasize, long *stackheap) +{ + AMX_HEADER *hdr; + + if (amx==NULL) + return AMX_ERR_FORMAT; + hdr=(AMX_HEADER *)amx->base; + if (hdr->magic!=AMX_MAGIC) + return AMX_ERR_FORMAT; + if (hdr->file_version>CUR_FILE_VERSION || hdr->amx_version<MIN_FILE_VERSION) + return AMX_ERR_VERSION; + + if (codesize!=NULL) + *codesize=amx->codesize; + 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; + uint16_t *namelength; + + assert(amx!=NULL); + hdr=(AMX_HEADER *)amx->base; + assert(hdr!=NULL); + assert(hdr->magic==AMX_MAGIC); + namelength=(uint16_t*)(amx->base + (unsigned)hdr->nametable); + *length=*namelength; + 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 *name) +{ + 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(name,GETENTRYNAME(hdr,func)); + return AMX_ERR_NONE; +} + +int AMXAPI amx_FindNative(AMX *amx, const char *name, int *index) +{ + int idx,last; + char pname[sNAMEMAX+1]; + + amx_NumNatives(amx, &last); + /* linear search, the natives table is not sorted alphabetically */ + for (idx=0; idx<last; idx++) { + amx_GetNative(amx,idx,pname); + if (strcmp(pname,name)==0) { + *index=idx; + return AMX_ERR_NONE; + } /* if */ + } /* for */ + *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 *name, ucell *address) +{ + 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); + if (name!=NULL) + strcpy(name,GETENTRYNAME(hdr,func)); + if (address!=NULL) + *address=func->address; + 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,NULL); + 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() on this index will fail + * with an error + */ + *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; + + assert(amx!=NULL); + 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 *name, cell **address) +{ + AMX_HEADER *hdr; + AMX_FUNCSTUB *var; + unsigned char *data; + + assert(amx!=NULL); + 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(name,GETENTRYNAME(hdr,var)); + data=(amx->data!=NULL) ? amx->data : amx->base+(int)hdr->dat; + assert(address!=NULL); + *address=(cell *)(data+(int)var->address); + return AMX_ERR_NONE; +} + +int AMXAPI amx_FindPubVar(AMX *amx, const char *name, cell **address) +{ + int first,last,mid,result; + char pname[sNAMEMAX+1]; + + 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,address); + result=strcmp(pname,name); + if (result>0) + last=mid-1; + else if (result<0) + first=mid+1; + else + return AMX_ERR_NONE; + } /* while */ + /* not found */ + assert(address!=NULL); + *address=NULL; + 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) { /* file version 7 introduced the name table */ + 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) { /* file version 7 introduced the name table */ + 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; first<last; first++) { + amx_GetTag(amx,first,tagname,&mid_id); + assert(cur_id<mid_id); + cur_id=mid_id; + } /* for */ + } /* if */ + #endif + + amx_NumTags(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_GetTag(amx,mid,tagname,&mid_id); + if (mid_id>tag_id) + last=mid-1; + else if (mid_id<tag_id) + first=mid+1; + else + return AMX_ERR_NONE; + } /* while */ + /* not found */ + *tagname='\0'; + return AMX_ERR_NOTFOUND; +} +#endif /* AMX_XXXTAGS */ + +#if defined AMX_XXXUSERDATA +int AMXAPI amx_GetUserData(AMX *amx, long tag, void **ptr) +{ + int index; + + assert(amx!=NULL); + assert(tag!=0); + for (index=0; index<AMX_USERNUM && amx->usertags[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; index<AMX_USERNUM && amx->usertags[index]!=tag; index++) + /* nothing */; + /* if not found, try to find empty tag */ + if (index>=AMX_USERNUM) + for (index=0; index<AMX_USERNUM && amx->usertags[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 +static AMX_NATIVE findfunction(char *name, const AMX_NATIVE_INFO *list, int number) +{ + int i; + + assert(list!=NULL); + for (i=0; list[i].name!=NULL && (i<number || number==-1); i++) + if (strcmp(name,list[i].name)==0) + return list[i].func; + return NULL; +} + +int AMXAPI amx_Register(AMX *amx, const AMX_NATIVE_INFO *list, int number) +{ + AMX_FUNCSTUB *func; + AMX_HEADER *hdr; + int i,numnatives,err; + AMX_NATIVE funcptr; + + assert(amx!=NULL); + hdr=(AMX_HEADER *)amx->base; + 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; i<numnatives; i++) { + if (func->address==0) { + /* this function is not yet located */ + funcptr=(list!=NULL) ? findfunction(GETENTRYNAME(hdr,func),list,number) : NULL; + if (funcptr!=NULL) + func->address=(ucell)funcptr; + else + err=AMX_ERR_NOTFOUND; + } /* if */ + func=(AMX_FUNCSTUB*)((unsigned char*)func+hdr->defsize); + } /* for */ + if (err==AMX_ERR_NONE) + amx->flags|=AMX_FLAG_NTVREG; + return err; +} +#endif /* AMX_REGISTER */ + +#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 */ + + +#define STKMARGIN ((cell)(16*sizeof(cell))) + +#if defined AMX_PUSHXXX + +int AMXAPI amx_Push(AMX *amx, cell value) +{ + AMX_HEADER *hdr; + unsigned char *data; + + if (amx->hea+STKMARGIN>amx->stk) + return AMX_ERR_STACKERR; + hdr=(AMX_HEADER *)amx->base; + data=(amx->data!=NULL) ? amx->data : amx->base+(int)hdr->dat; + amx->stk-=sizeof(cell); + amx->paramcount+=1; + *(cell *)(data+(int)amx->stk)=value; + return AMX_ERR_NONE; +} + +int AMXAPI amx_PushAddress(AMX *amx, cell *address) +{ + AMX_HEADER *hdr; + unsigned char *data; + cell xaddr; + + /* reverse relocate the address */ + 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; + xaddr=(cell)((unsigned char*)address-data); + if ((ucell)xaddr>=(ucell)amx->stp) + return AMX_ERR_MEMACCESS; + return amx_Push(amx,xaddr); +} + +int AMXAPI amx_PushArray(AMX *amx, cell **address, const cell array[], int numcells) +{ + cell xaddr; + int err; + + assert(amx!=NULL); + assert(array!=NULL); + + xaddr=amx->hea; /* save, before it is modified by amx_Allot() */ + err=amx_Allot(amx,numcells,address); + if (err==AMX_ERR_NONE) { + memcpy(*address,array,numcells*sizeof(cell)); + err=amx_Push(amx,xaddr); + } /* if */ + return err; +} + +int AMXAPI amx_PushString(AMX *amx, cell **address, const char *string, int pack, int use_wchar) +{ + cell xaddr; + int numcells,err; + + assert(amx!=NULL); + assert(string!=NULL); + + #if defined AMX_ANSIONLY + numcells=strlen(string) + 1; + #else + numcells= (use_wchar ? wcslen((const wchar_t*)string) : strlen(string)) + 1; + #endif + if (pack) + numcells=(numcells+sizeof(cell)-1)/sizeof(cell); + xaddr=amx->hea; /* save, before it is modified by amx_Allot() */ + err=amx_Allot(amx,numcells,address); + if (err==AMX_ERR_NONE) { + amx_SetString(*address,string,pack,use_wchar,numcells); + err=amx_Push(amx,xaddr); + } /* if */ + return err; +} +#endif /* AMX_PUSHXXX */ + +#if defined AMX_EXEC + +/* It is assumed that the abstract machine can simply access the memory area + * for the global data and the stack. If this is not the case, you need to + * define the macro sets _R() and _W(), for reading and writing to memory. + */ +#if !defined _R + #define _R_DEFAULT /* mark default memory access */ + #define _R(base,addr) (* (cell *)((unsigned char*)(base)+(int)(addr))) + #define _R8(base,addr) (* (unsigned char *)((unsigned char*)(base)+(int)(addr))) + #define _R16(base,addr) (* (uint16_t *)((unsigned char*)(base)+(int)(addr))) + #define _R32(base,addr) (* (uint32_t *)((unsigned char*)(base)+(int)(addr))) +#endif +#if !defined _W + #define _W_DEFAULT /* mark default memory access */ + #define _W(base,addr,value) ((*(cell *)((unsigned char*)(base)+(int)(addr)))=(cell)(value)) + #define _W8(base,addr,value) ((*(unsigned char *)((unsigned char*)(base)+(int)(addr)))=(unsigned char)(value)) + #define _W16(base,addr,value) ((*(uint16_t *)((unsigned char*)(base)+(int)(addr)))=(uint16_t)(value)) + #define _W32(base,addr,value) ((*(uint32_t *)((unsigned char*)(base)+(int)(addr)))=(uint32_t)(value)) +#endif + +#if -8/3==-2 && 8/-3==-2 + #define TRUNC_SDIV /* signed divisions are truncated on this platform */ +#else + #define IABS(a) ((a)>=0 ? (a) : (-a)) +#endif + +/* The pseudo-instructions come from the code stream. Normally, these are just + * accessed from memory. When the instructions must be fetched in some other + * way, the definition below must be pre-defined. + * N.B.: + * - reading from a code address should increment the instruction pointer + * (called "cip") + * - only cell-sized accesses occur in code memory + */ +#if !defined _RCODE + #define _RCODE() ( *cip++ ) +#endif + +#if !defined GETPARAM + #define GETPARAM(v) ( v=_RCODE() ) /* read a parameter from the opcode stream */ +#endif +#if !defined SKIPPARAM + #define SKIPPARAM(n) ( cip=(cell *)cip+(n) ) /* for obsolete opcodes */ +#endif + +#define AMXPUSH(v) ( amx->stk-=sizeof(cell), *(cell*)(data+amx->stk)=(v) ) +#define ABORT(amx,v) { (amx)->stk=reset_stk; (amx)->hea=reset_hea; return v; } + + +#if !defined AMX_ALTCORE +int amx_exec_list(AMX *amx,const cell **opcodelist,int *numopcodes) +{ + (void)amx; + assert(opcodelist!=NULL); + *opcodelist=NULL; + assert(numopcodes!=NULL); + *numopcodes=OP_NUM_OPCODES; + return 0; +} +#endif + +int AMXAPI amx_Exec(AMX *amx, cell *retval, int index) +{ + AMX_HEADER *hdr; + AMX_FUNCSTUB *func; + unsigned char *data; + cell reset_stk,reset_hea; + int i; +#if !defined AMX_ALTCORE + cell pri,alt,stk,frm,hea; + cell *cip,op,offs,val; +#endif + + assert(amx!=NULL); + if ((amx->flags & AMX_FLAG_INIT)==0) + return AMX_ERR_INIT; + if (amx->callback==NULL) + return AMX_ERR_CALLBACK; + + hdr=(AMX_HEADER *)amx->base; + assert(hdr!=NULL); + assert(hdr->magic==AMX_MAGIC); + + if ((amx->flags & AMX_FLAG_NTVREG)==0) { + /* verify that all native functions have been registered (or do not + * need registering) + */ + int numnatives; + assert(hdr->natives<=hdr->libraries); + numnatives=NUMENTRIES(hdr,natives,libraries); + func=GETENTRY(hdr,natives,0); + for (i=0; i<numnatives && func->address!=0; i++) + func=(AMX_FUNCSTUB*)((unsigned char*)func+hdr->defsize); + if (i<numnatives) + return AMX_ERR_NOTFOUND; + amx->flags|=AMX_FLAG_NTVREG; /* no need to check this again */ + } /* if */ + assert((amx->flags & AMX_FLAG_VERIFY)==0); + + /* set up the registers */ + assert(hdr!=NULL && hdr->magic==AMX_MAGIC); + assert(amx->code!=NULL || hdr->overlays!=hdr->nametable); + data=(amx->data!=NULL) ? amx->data : amx->base+(int)hdr->dat; + reset_stk=amx->stk; + reset_hea=amx->hea; + amx->error=AMX_ERR_NONE; + + /* get the start address */ + if (index==AMX_EXEC_MAIN) { + if (hdr->cip<0) + return AMX_ERR_INDEX; + amx->cip=hdr->cip; + if (hdr->overlays!=hdr->nametable) { + assert(hdr->overlays!=0); + assert(amx->overlay!=NULL); + amx->ovl_index=(int)hdr->cip; + if ((i=amx->overlay(amx,amx->ovl_index))!=AMX_ERR_NONE) + return i; + amx->cip=0; + } /* if */ + } else if (index==AMX_EXEC_CONT) { + /* restore registers reset_stk & reset_hea */ + reset_stk=amx->reset_stk; + reset_hea=amx->reset_hea; + if (hdr->overlays!=hdr->nametable) { + assert(hdr->overlays!=0); + assert(amx->overlay!=NULL); + if ((i=amx->overlay(amx,amx->ovl_index))!=AMX_ERR_NONE) + return i; + } /* if */ + } else if (index<0) { + return AMX_ERR_INDEX; + } else { + if (index>=(int)NUMENTRIES(hdr,publics,natives)) + return AMX_ERR_INDEX; + func=GETENTRY(hdr,publics,index); + amx->cip=func->address; + if (hdr->overlays!=hdr->nametable) { + assert(hdr->overlays!=0); + assert(amx->overlay!=NULL); + amx->ovl_index=func->address; + if ((i=amx->overlay(amx,amx->ovl_index))!=AMX_ERR_NONE) + return i; + amx->cip=0; + } /* if */ + } /* if */ + /* check values just copied */ + if (amx->stk>amx->stp) + return AMX_ERR_STACKLOW; + if (amx->hea<amx->hlw) + return AMX_ERR_HEAPLOW; + assert(check_endian()); + + /* sanity checks */ + assert_static(OP_XCHG==21); + assert_static(OP_SMUL==42); + assert_static(OP_MOVS==64); + #if !defined AMX_NO_MACRO_INSTR + assert_static(OP_LIDX==81); + assert_static(OP_ZERO_PRI==102); + assert_static(OP_LOAD2==120); + #endif + #if !defined AMX_NO_PACKED_OPC + assert_static(OP_LOAD_P_PRI==124); + assert_static(OP_ALIGN_P_PRI==141); + assert_static(OP_BOUNDS_P==174); + #endif + #if PAWN_CELL_SIZE==16 + assert_static(sizeof(cell)==2); + #elif PAWN_CELL_SIZE==32 + assert_static(sizeof(cell)==4); + #elif PAWN_CELL_SIZE==64 + assert_static(sizeof(cell)==8); + #else + #error Unsupported cell size + #endif + + if (index!=AMX_EXEC_CONT) { + reset_stk+=amx->paramcount*sizeof(cell); + AMXPUSH(amx->paramcount*sizeof(cell)); + amx->paramcount=0; /* push the parameter count to the stack & reset */ + AMXPUSH(0); /* return address (for overlays: overlay 0, offset 0) */ + } /* if */ + /* check stack/heap before starting to run */ + if (amx->hea+STKMARGIN>amx->stk) + return AMX_ERR_STACKERR; + +#if defined AMX_ALTCORE + + /* start running either the ARM or 80x86 assembler abstract machine or the JIT */ + #if defined AMX_ASM && defined AMX_JIT + if ((amx->flags & AMX_FLAG_JITC)!=0) + i = amx_jit_run(amx,retval,data); + else + i = amx_exec_run(amx,retval,data); + #elif defined AMX_JIT + i = amx_jit_run(amx,retval,data); + #else + /* also for GNU GCC and Intel C/C++ versions */ + i = amx_exec_run(amx,retval,data); + #endif + if (i == AMX_ERR_SLEEP) { + amx->reset_stk=reset_stk; + amx->reset_hea=reset_hea; + } else { + /* remove parameters from the stack; do this the "hard" way, because + * the assembler version has no internal knowledge of the local + * variables, so any "clean" way would be a kludge anyway. + */ + amx->stk=reset_stk; + amx->hea=reset_hea; + } /* if */ + return i; + +#else + + #define CHKMARGIN() if (hea+STKMARGIN>stk) return AMX_ERR_STACKERR + #define CHKSTACK() if (stk>amx->stp) return AMX_ERR_STACKLOW + #define CHKHEAP() if (hea<amx->hlw) return AMX_ERR_HEAPLOW + + /* PUSH() and POP() are defined in terms of the _R() and _W() macros */ + #define PUSH(v) ( stk-=sizeof(cell), _W(data,stk,v) ) + #define POP(v) ( v=_R(data,stk), stk+=sizeof(cell) ) + + /* set up registers for ANSI-C core: pri, alt, frm, cip, hea, stk */ + pri=amx->pri; + alt=amx->alt; + frm=amx->frm; + cip=(cell *)(amx->code+(int)amx->cip); + hea=amx->hea; + stk=amx->stk; + + /* start running */ + for ( ;; ) { + op=_RCODE(); + switch (GETOPCODE(op)) { + /* core instruction set */ + case OP_NOP: + break; + case OP_LOAD_PRI: + GETPARAM(offs); + pri=_R(data,offs); + break; + case OP_LOAD_ALT: + GETPARAM(offs); + alt=_R(data,offs); + break; + case OP_LOAD_S_PRI: + GETPARAM(offs); + pri=_R(data,frm+offs); + break; + case OP_LOAD_S_ALT: + GETPARAM(offs); + alt=_R(data,frm+offs); + break; + case OP_LREF_S_PRI: + GETPARAM(offs); + offs=_R(data,frm+offs); + pri=_R(data,offs); + break; + case OP_LREF_S_ALT: + GETPARAM(offs); + offs=_R(data,frm+offs); + alt=_R(data,offs); + break; + case OP_LOAD_I: + /* verify address */ + if (pri>=hea && pri<stk || (ucell)pri>=(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + pri=_R(data,pri); + break; + case OP_LODB_I: + GETPARAM(offs); + __lodb_i: + /* verify address */ + if (pri>=hea && pri<stk || (ucell)pri>=(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + switch ((int)offs) { + case 1: + pri=_R8(data,pri); + break; + case 2: + pri=_R16(data,pri); + break; + case 4: + pri=_R32(data,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: + GETPARAM(offs); + _W(data,offs,pri); + break; + case OP_STOR_S: + GETPARAM(offs); + _W(data,frm+offs,pri); + break; + case OP_SREF_S: + GETPARAM(offs); + offs=_R(data,frm+offs); + _W(data,offs,pri); + break; + case OP_STOR_I: + /* verify address */ + if (alt>=hea && alt<stk || (ucell)alt>=(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + _W(data,alt,pri); + break; + case OP_STRB_I: + GETPARAM(offs); + __strb_i: + /* verify address */ + if (alt>=hea && alt<stk || (ucell)alt>=(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + switch ((int)offs) { + case 1: + _W8(data,alt,pri); + break; + case 2: + _W16(data,alt,pri); + break; + case 4: + _W32(data,alt,pri); + break; + } /* switch */ + break; + case OP_ALIGN_PRI: + GETPARAM(offs); + #if BYTE_ORDER==LITTLE_ENDIAN + if ((size_t)offs<sizeof(cell)) + pri ^= sizeof(cell)-offs; + #endif + break; + case OP_LCTRL: + GETPARAM(offs); + switch ((int)offs) { + case 0: + pri=hdr->cod; + break; + case 1: + pri=hdr->dat; + break; + case 2: + pri=hea; + break; + case 3: + pri=amx->stp; + break; + case 4: + pri=stk; + break; + case 5: + pri=frm; + break; + case 6: + pri=(cell)((unsigned char *)cip-amx->code); + break; + } /* switch */ + break; + case OP_SCTRL: + GETPARAM(offs); + switch ((int)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 *)(amx->code + (int)pri); + break; + } /* switch */ + 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_PUSHR_PRI: + PUSH(data+pri); + break; + case OP_POP_PRI: + POP(pri); + break; + case OP_POP_ALT: + POP(alt); + break; + case OP_PICK: + GETPARAM(offs); + pri=_R(data,stk+offs); + break; + case OP_STACK: + GETPARAM(offs); + alt=stk; + stk+=offs; + CHKMARGIN(); + CHKSTACK(); + break; + case OP_HEAP: + GETPARAM(offs); + alt=hea; + hea+=offs; + CHKMARGIN(); + CHKHEAP(); + break; + case OP_PROC: + PUSH(frm); + frm=stk; + CHKMARGIN(); + break; + case OP_RET: + POP(frm); + POP(offs); + /* verify the return address */ + if ((long)offs>=amx->codesize) + ABORT(amx,AMX_ERR_MEMACCESS); + cip=(cell *)(amx->code+(int)offs); + break; + case OP_RETN: + POP(frm); + POP(offs); + /* verify the return address */ + if ((long)offs>=amx->codesize) + ABORT(amx,AMX_ERR_MEMACCESS); + cip=(cell *)(amx->code+(int)offs); + stk+=_R(data,stk)+sizeof(cell); /* remove parameters from the stack */ + break; + case OP_CALL: + PUSH(((unsigned char *)cip-amx->code)+sizeof(cell));/* skip address */ + cip=JUMPREL(cip); /* jump to the address */ + break; + case OP_JUMP: + /* since the GETPARAM() macro modifies cip, you cannot + * do GETPARAM(cip) directly */ + cip=JUMPREL(cip); + break; + case OP_JZER: + if (pri==0) + cip=JUMPREL(cip); + else + SKIPPARAM(1); + break; + case OP_JNZ: + if (pri!=0) + cip=JUMPREL(cip); + else + SKIPPARAM(1); + 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_SMUL: + pri*=alt; + break; + case OP_SDIV: + if (pri==0) + ABORT(amx,AMX_ERR_DIVIDE); + /* use floored division and matching remainder */ + offs=pri; + #if defined TRUNC_SDIV + pri=alt/offs; + alt=alt%offs; + #else + val=alt; /* portable routine for truncated division */ + pri=IABS(alt)/IABS(offs); + if ((cell)(val ^ offs)<0) + pri=-pri; + alt=val-pri*offs; /* calculate the matching remainder */ + #endif + /* now "fiddle" with the values to get floored division */ + if (alt!=0 && (cell)(alt ^ offs)<0) { + pri--; + alt+=offs; + } /* if */ + break; + case OP_ADD: + pri+=alt; + break; + case OP_SUB: + 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_EQ: + pri= pri==alt ? 1 : 0; + break; + case OP_NEQ: + pri= pri!=alt ? 1 : 0; + break; + case OP_SLESS: + pri= pri<alt ? 1 : 0; + break; + case OP_SLEQ: + pri= pri<=alt ? 1 : 0; + break; + case OP_SGRTR: + pri= pri>alt ? 1 : 0; + break; + case OP_SGEQ: + pri= pri>=alt ? 1 : 0; + break; + case OP_INC_PRI: + pri++; + break; + case OP_INC_ALT: + alt++; + break; + case OP_INC_I: + #if defined _R_DEFAULT + *(cell *)(data+(int)pri) += 1; + #else + val=_R(data,pri); + _W(data,pri,val+1); + #endif + break; + case OP_DEC_PRI: + pri--; + break; + case OP_DEC_ALT: + alt--; + break; + case OP_DEC_I: + #if defined _R_DEFAULT + *(cell *)(data+(int)pri) -= 1; + #else + val=_R(data,pri); + _W(data,pri,val-1); + #endif + break; + case OP_MOVS: + GETPARAM(offs); + __movs: + /* verify top & bottom memory addresses, for both source and destination + * addresses + */ + if (pri>=hea && pri<stk || (ucell)pri>=(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + if ((pri+offs)>hea && (pri+offs)<stk || (ucell)(pri+offs)>(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + if (alt>=hea && alt<stk || (ucell)alt>=(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + if ((alt+offs)>hea && (alt+offs)<stk || (ucell)(alt+offs)>(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + #if defined _R_DEFAULT + memcpy(data+(int)alt, data+(int)pri, (int)offs); + #else + for (i=0; i+4<offs; i+=4) { + val=_R32(data,pri+i); + _W32(data,alt+i,val); + } /* for */ + for ( ; i<offs; i++) { + val=_R8(data,pri+i); + _W8(data,alt+i,val); + } /* for */ + #endif + break; + case OP_CMPS: + GETPARAM(offs); + __cmps: + /* verify top & bottom memory addresses, for both source and destination + * addresses + */ + if (pri>=hea && pri<stk || (ucell)pri>=(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + if ((pri+offs)>hea && (pri+offs)<stk || (ucell)(pri+offs)>(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + if (alt>=hea && alt<stk || (ucell)alt>=(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + if ((alt+offs)>hea && (alt+offs)<stk || (ucell)(alt+offs)>(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + #if defined _R_DEFAULT + pri=memcmp(data+(int)alt, data+(int)pri, (int)offs); + #else + pri=0; + for (i=0; i+4<offs && pri==0; i+=4) + pri=_R32(data,alt+i)-_R32(data,pri+i); + for ( ; i<offs && pri==0; i++) + pri=_R8(data,alt+i)-_R8(data,pri+i); + #endif + break; + case OP_FILL: + GETPARAM(offs); + __fill: + /* verify top & bottom memory addresses (destination only) */ + if (alt>=hea && alt<stk || (ucell)alt>=(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + if ((alt+offs)>hea && (alt+offs)<stk || (ucell)(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)) + _W32(data,i,pri); + break; + case OP_HALT: + GETPARAM(offs); + __halt: + if (retval!=NULL) + *retval=pri; + /* store complete status (stk and hea are already set in the ABORT macro) */ + amx->frm=frm; + amx->pri=pri; + amx->alt=alt; + amx->cip=(cell)((unsigned char*)cip-amx->code); + if (offs==AMX_ERR_SLEEP) { + amx->stk=stk; + amx->hea=hea; + 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) { + amx->cip=(cell)((unsigned char *)cip-amx->code); + ABORT(amx,AMX_ERR_BOUNDS); + } /* if */ + break; + case OP_SYSREQ: + GETPARAM(offs); + /* save a few registers */ + amx->cip=(cell)((unsigned char *)cip-amx->code); + amx->hea=hea; + amx->frm=frm; + amx->stk=stk; + i=amx->callback(amx,offs,&pri,(cell *)(data+(int)stk)); + if (i!=AMX_ERR_NONE) { + if (i==AMX_ERR_SLEEP) { + amx->pri=pri; + amx->alt=alt; + amx->reset_stk=reset_stk; + amx->reset_hea=reset_hea; + return i; + } /* if */ + ABORT(amx,i); + } /* if */ + break; + case OP_SWITCH: { + cell *cptr=JUMPREL(cip)+1;/* +1, to skip the "casetbl" opcode */ + assert(*JUMPREL(cip)==OP_CASETBL); + cip=JUMPREL(cptr+1); /* preset to "none-matched" case */ + i=(int)*cptr; /* number of records in the case table */ + for (cptr+=2; i>0 && *cptr!=pri; i--,cptr+=2) + /* nothing */; + if (i>0) + cip=JUMPREL(cptr+1); /* case found */ + break; + } /* case */ + case OP_SWAP_PRI: + offs=_R(data,stk); + _W32(data,stk,pri); + pri=offs; + break; + case OP_SWAP_ALT: + offs=_R(data,stk); + _W32(data,stk,alt); + alt=offs; + break; + case OP_BREAK: + assert((amx->flags & AMX_FLAG_VERIFY)==0); + if (amx->debug!=NULL) { + /* store status */ + amx->frm=frm; + amx->stk=stk; + amx->hea=hea; + amx->cip=(cell)((unsigned char*)cip-amx->code); + i=amx->debug(amx); + if (i!=AMX_ERR_NONE) { + if (i==AMX_ERR_SLEEP) { + amx->pri=pri; + amx->alt=alt; + amx->reset_stk=reset_stk; + amx->reset_hea=reset_hea; + return i; + } /* if */ + ABORT(amx,i); + } /* if */ + } /* if */ + break; +#if !defined AMX_DONT_RELOCATE + case OP_SYSREQ_D: /* see SYSREQ */ + GETPARAM(offs); + /* save a few registers */ + amx->cip=(cell)((unsigned char *)cip-amx->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 AMX_ERR_SLEEP; + } /* if */ + ABORT(amx,amx->error); + } /* if */ + break; +#endif +#if !defined AMX_NO_MACRO_INSTR && !defined AMX_DONT_RELOCATE + case OP_SYSREQ_ND: /* see SYSREQ_N */ + GETPARAM(offs); + GETPARAM(val); + PUSH(val); + /* save a few registers */ + amx->cip=(cell)((unsigned char *)cip-amx->code); + amx->hea=hea; + amx->frm=frm; + amx->stk=stk; + pri=((AMX_NATIVE)offs)(amx,(cell *)(data+(int)stk)); + stk+=val+4; + if (amx->error!=AMX_ERR_NONE) { + if (amx->error==AMX_ERR_SLEEP) { + amx->pri=pri; + amx->alt=alt; + amx->stk=stk; + amx->reset_stk=reset_stk; + amx->reset_hea=reset_hea; + return AMX_ERR_SLEEP; + } /* if */ + ABORT(amx,amx->error); + } /* if */ + break; +#endif + + /* overlay instructions */ +#if !defined AMX_NO_OVERLAY + case OP_CALL_OVL: + offs=(unsigned char *)cip-amx->code+sizeof(cell); /* skip address */ + assert(offs>=0 && offs<(1<<(sizeof(cell)*4))); + PUSH((offs<<(sizeof(cell)*4)) | amx->ovl_index); + amx->ovl_index=(int)*cip; + assert(amx->overlay!=NULL); + if ((i=amx->overlay(amx,amx->ovl_index))!=AMX_ERR_NONE) + ABORT(amx,i); + cip=(cell*)amx->code; + break; + case OP_RETN_OVL: + assert(amx->overlay!=NULL); + POP(frm); + POP(offs); + amx->ovl_index=offs & (((ucell)~0)>>4*sizeof(cell)); + offs=(ucell)offs >> (sizeof(cell)*4); + /* verify the index */ + stk+=_R(data,stk)+sizeof(cell); /* remove parameters from the stack */ + i=amx->overlay(amx,amx->ovl_index); /* reload overlay */ + if (i!=AMX_ERR_NONE || (long)offs>=amx->codesize) + ABORT(amx,AMX_ERR_MEMACCESS); + cip=(cell *)(amx->code+(int)offs); + break; + case OP_SWITCH_OVL: { + cell *cptr=JUMPREL(cip)+1; /* +1, to skip the "icasetbl" opcode */ + assert(*JUMPREL(cip)==OP_CASETBL_OVL); + amx->ovl_index=*(cptr+1); /* preset to "none-matched" case */ + i=(int)*cptr; /* number of records in the case table */ + for (cptr+=2; i>0 && *cptr!=pri; i--,cptr+=2) + /* nothing */; + if (i>0) + amx->ovl_index=*(cptr+1); /* case found */ + assert(amx->overlay!=NULL); + if ((i=amx->overlay(amx,amx->ovl_index))!=AMX_ERR_NONE) + ABORT(amx,i); + cip=(cell*)amx->code; + break; + } /* case */ +#endif + + /* supplemental and macro instructions */ +#if !defined AMX_NO_MACRO_INSTR + case OP_LIDX: + offs=pri*sizeof(cell)+alt; + /* verify address */ + if (offs>=hea && offs<stk || (ucell)offs>=(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + pri=_R(data,offs); + break; + case OP_LIDX_B: + GETPARAM(offs); + offs=(pri << (int)offs)+alt; + /* verify address */ + if (offs>=hea && offs<stk || (ucell)offs>=(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + pri=_R(data,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_PUSH_C: + GETPARAM(offs); + PUSH(offs); + break; + case OP_PUSH: + GETPARAM(offs); + PUSH(_R(data,offs)); + break; + case OP_PUSH_S: + GETPARAM(offs); + PUSH(_R(data,frm+offs)); + break; + case OP_PUSH_ADR: + GETPARAM(offs); + PUSH(frm+offs); + break; + case OP_PUSHR_C: + GETPARAM(offs); + PUSH(data+offs); + break; + case OP_PUSHR_S: + GETPARAM(offs); + PUSH(data+_R(data,frm+offs)); + break; + case OP_PUSHR_ADR: + GETPARAM(offs); + PUSH(data+frm+offs); + break; + case OP_JEQ: + if (pri==alt) + cip=JUMPREL(cip); + else + SKIPPARAM(1); + break; + case OP_JNEQ: + if (pri!=alt) + cip=JUMPREL(cip); + else + SKIPPARAM(1); + break; + case OP_JSLESS: + if (pri<alt) + cip=JUMPREL(cip); + else + SKIPPARAM(1); + break; + case OP_JSLEQ: + if (pri<=alt) + cip=JUMPREL(cip); + else + SKIPPARAM(1); + break; + case OP_JSGRTR: + if (pri>alt) + cip=JUMPREL(cip); + else + SKIPPARAM(1); + break; + case OP_JSGEQ: + if (pri>=alt) + cip=JUMPREL(cip); + else + SKIPPARAM(1); + break; + case OP_SDIV_INV: + if (alt==0) + ABORT(amx,AMX_ERR_DIVIDE); + /* use floored division and matching remainder */ + offs=alt; + #if defined TRUNC_SDIV + pri=pri/offs; + alt=pri%offs; + #else + val=pri; /* portable routine for truncated division */ + pri=IABS(pri)/IABS(offs); + if ((cell)(val ^ offs)<0) + pri=-pri; + alt=val-pri*offs; /* calculate the matching remainder */ + #endif + /* now "fiddle" with the values to get floored division */ + if (alt!=0 && (cell)(alt ^ offs)<0) { + pri--; + alt+=offs; + } /* if */ + break; + case OP_SUB_INV: + pri-=alt; + 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); + _W(data,offs,0); + break; + case OP_ZERO_S: + GETPARAM(offs); + _W(data,frm+offs,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: + GETPARAM(offs); + #if defined _R_DEFAULT + *(cell *)(data+(int)offs) += 1; + #else + val=_R(data,offs); + _W(data,offs,val+1); + #endif + break; + case OP_INC_S: + GETPARAM(offs); + #if defined _R_DEFAULT + *(cell *)(data+(int)(frm+offs)) += 1; + #else + val=_R(data,frm+offs); + _W(data,frm+offs,val+1); + #endif + break; + case OP_DEC: + GETPARAM(offs); + #if defined _R_DEFAULT + *(cell *)(data+(int)offs) -= 1; + #else + val=_R(data,offs); + _W(data,offs,val-1); + #endif + break; + case OP_DEC_S: + GETPARAM(offs); + #if defined _R_DEFAULT + *(cell *)(data+(int)(frm+offs)) -= 1; + #else + val=_R(data,frm+offs); + _W(data,frm+offs,val-1); + #endif + break; + case OP_SYSREQ_N: + GETPARAM(offs); + GETPARAM(val); + PUSH(val); + /* save a few registers */ + amx->cip=(cell)((unsigned char *)cip-amx->code); + amx->hea=hea; + amx->frm=frm; + amx->stk=stk; + i=amx->callback(amx,offs,&pri,(cell *)(data+(int)stk)); + stk+=val+4; + if (i!=AMX_ERR_NONE) { + if (i==AMX_ERR_SLEEP) { + amx->pri=pri; + amx->alt=alt; + amx->stk=stk; + amx->reset_stk=reset_stk; + amx->reset_hea=reset_hea; + return i; + } /* if */ + ABORT(amx,i); + } /* if */ + break; + case OP_PUSHM_C: + GETPARAM(val); + while (val--) { + GETPARAM(offs); + PUSH(offs); + } /* while */ + break; + case OP_PUSHM: + GETPARAM(val); + while (val--) { + GETPARAM(offs); + PUSH(_R(data,offs)); + } /* while */ + break; + case OP_PUSHM_S: + GETPARAM(val); + while (val--) { + GETPARAM(offs); + PUSH(_R(data,frm+offs)); + } /* while */ + break; + case OP_PUSHM_ADR: + GETPARAM(val); + while (val--) { + GETPARAM(offs); + PUSH(frm+offs); + } /* while */ + break; + case OP_PUSHRM_C: + GETPARAM(val); + while (val--) { + GETPARAM(offs); + PUSH(data+offs); + } /* while */ + break; + case OP_PUSHRM_S: + GETPARAM(val); + while (val--) { + GETPARAM(offs); + PUSH(data+_R(data,frm+offs)); + } /* while */ + break; + case OP_PUSHRM_ADR: + GETPARAM(val); + while (val--) { + GETPARAM(offs); + PUSH(data+frm+offs); + } /* while */ + break; + case OP_LOAD2: + GETPARAM(offs); + pri=_R(data,offs); + GETPARAM(offs); + alt=_R(data,offs); + break; + case OP_LOAD2_S: + GETPARAM(offs); + pri=_R(data,frm+offs); + GETPARAM(offs); + alt=_R(data,frm+offs); + break; + case OP_CONST: + GETPARAM(offs); + GETPARAM(val); + _W32(data,offs,val); + break; + case OP_CONST_S: + GETPARAM(offs); + GETPARAM(val); + _W32(data,frm+offs,val); + break; +#endif /* AMX_NO_MACRO_INSTR */ + +#if !defined AMX_NO_PACKED_OPC + case OP_LOAD_P_PRI: + GETPARAM_P(offs,op); + pri=_R(data,offs); + break; + case OP_LOAD_P_ALT: + GETPARAM_P(offs,op); + alt=_R(data,offs); + break; + case OP_LOAD_P_S_PRI: + GETPARAM_P(offs,op); + pri=_R(data,frm+offs); + break; + case OP_LOAD_P_S_ALT: + GETPARAM_P(offs,op); + alt=_R(data,frm+offs); + break; + case OP_LREF_P_S_PRI: + GETPARAM_P(offs,op); + offs=_R(data,frm+offs); + pri=_R(data,offs); + break; + case OP_LREF_P_S_ALT: + GETPARAM_P(offs,op); + offs=_R(data,frm+offs); + alt=_R(data,offs); + break; + case OP_LODB_P_I: + GETPARAM_P(offs,op); + goto __lodb_i; + case OP_CONST_P_PRI: + GETPARAM_P(pri,op); + break; + case OP_CONST_P_ALT: + GETPARAM_P(alt,op); + break; + case OP_ADDR_P_PRI: + GETPARAM_P(pri,op); + pri+=frm; + break; + case OP_ADDR_P_ALT: + GETPARAM_P(alt,op); + alt+=frm; + break; + case OP_STOR_P: + GETPARAM_P(offs,op); + _W(data,offs,pri); + break; + case OP_STOR_P_S: + GETPARAM_P(offs,op); + _W(data,frm+offs,pri); + break; + case OP_SREF_P_S: + GETPARAM_P(offs,op); + offs=_R(data,frm+offs); + _W(data,offs,pri); + break; + case OP_STRB_P_I: + GETPARAM_P(offs,op); + goto __strb_i; + case OP_LIDX_P_B: + GETPARAM_P(offs,op); + offs=(pri << (int)offs)+alt; + /* verify address */ + if (offs>=hea && offs<stk || (ucell)offs>=(ucell)amx->stp) + ABORT(amx,AMX_ERR_MEMACCESS); + pri=_R(data,offs); + break; + case OP_IDXADDR_P_B: + GETPARAM_P(offs,op); + pri=(pri << (int)offs)+alt; + break; + case OP_ALIGN_P_PRI: + GETPARAM_P(offs,op); + #if BYTE_ORDER==LITTLE_ENDIAN + if ((size_t)offs<sizeof(cell)) + pri ^= sizeof(cell)-offs; + #endif + break; + case OP_PUSH_P_C: + GETPARAM_P(offs,op); + PUSH(offs); + break; + case OP_PUSH_P: + GETPARAM_P(offs,op); + PUSH(_R(data,offs)); + break; + case OP_PUSH_P_S: + GETPARAM_P(offs,op); + PUSH(_R(data,frm+offs)); + break; + case OP_PUSH_P_ADR: + GETPARAM_P(offs,op); + PUSH(frm+offs); + break; + case OP_PUSHR_P_C: + GETPARAM_P(offs,op); + PUSH(data+offs); + break; + case OP_PUSHR_P_S: + GETPARAM_P(offs,op); + PUSH(data+_R(data,frm+offs)); + break; + case OP_PUSHR_P_ADR: + GETPARAM_P(offs,op); + PUSH(data+frm+offs); + break; + case OP_PUSHM_P: + GETPARAM_P(val,op); + while (val--) { + GETPARAM(offs); + PUSH(_R(data,offs)); + } /* while */ + break; + case OP_PUSHM_P_S: + GETPARAM_P(val,op); + while (val--) { + GETPARAM(offs); + PUSH(_R(data,frm+offs)); + } /* while */ + break; + case OP_PUSHM_P_C: + GETPARAM_P(val,op); + while (val--) { + GETPARAM(offs); + PUSH(offs); + } /* while */ + break; + case OP_PUSHM_P_ADR: + GETPARAM_P(val,op); + while (val--) { + GETPARAM(offs); + PUSH(frm+offs); + } /* while */ + break; + case OP_PUSHRM_P_C: + GETPARAM_P(val,op); + while (val--) { + GETPARAM(offs); + PUSH(data+offs); + } /* while */ + break; + case OP_PUSHRM_P_S: + GETPARAM_P(val,op); + while (val--) { + GETPARAM(offs); + PUSH(data+_R(data,frm+offs)); + } /* while */ + break; + case OP_PUSHRM_P_ADR: + GETPARAM_P(val,op); + while (val--) { + GETPARAM(offs); + PUSH(data+frm+offs); + } /* while */ + break; + case OP_STACK_P: + GETPARAM_P(offs,op); + alt=stk; + stk+=offs; + CHKMARGIN(); + CHKSTACK(); + break; + case OP_HEAP_P: + GETPARAM_P(offs,op); + alt=hea; + hea+=offs; + CHKMARGIN(); + CHKHEAP(); + break; + case OP_SHL_P_C_PRI: + GETPARAM_P(offs,op); + pri<<=offs; + break; + case OP_SHL_P_C_ALT: + GETPARAM_P(offs,op); + alt<<=offs; + break; + case OP_ADD_P_C: + GETPARAM_P(offs,op); + pri+=offs; + break; + case OP_SMUL_P_C: + GETPARAM_P(offs,op); + pri*=offs; + break; + case OP_ZERO_P: + GETPARAM_P(offs,op); + _W(data,offs,0); + break; + case OP_ZERO_P_S: + GETPARAM_P(offs,op); + _W(data,frm+offs,0); + break; + case OP_EQ_P_C_PRI: + GETPARAM_P(offs,op); + pri= pri==offs ? 1 : 0; + break; + case OP_EQ_P_C_ALT: + GETPARAM_P(offs,op); + pri= alt==offs ? 1 : 0; + break; + case OP_INC_P: + GETPARAM_P(offs,op); + #if defined _R_DEFAULT + *(cell *)(data+(int)offs) += 1; + #else + val=_R(data,offs); + _W(data,offs,val+1); + #endif + break; + case OP_INC_P_S: + GETPARAM_P(offs,op); + #if defined _R_DEFAULT + *(cell *)(data+(int)(frm+offs)) += 1; + #else + val=_R(data,frm+offs); + _W(data,frm+offs,val+1); + #endif + break; + case OP_DEC_P: + GETPARAM_P(offs,op); + #if defined _R_DEFAULT + *(cell *)(data+(int)offs) -= 1; + #else + val=_R(data,offs); + _W(data,offs,val-1); + #endif + break; + case OP_DEC_P_S: + GETPARAM_P(offs,op); + #if defined _R_DEFAULT + *(cell *)(data+(int)(frm+offs)) -= 1; + #else + val=_R(data,frm+offs); + _W(data,frm+offs,val-1); + #endif + break; + case OP_MOVS_P: + GETPARAM_P(offs,op); + goto __movs; + case OP_CMPS_P: + GETPARAM_P(offs,op); + goto __cmps; + case OP_FILL_P: + GETPARAM_P(offs,op); + goto __fill; + case OP_HALT_P: + GETPARAM_P(offs,op); + goto __halt; + case OP_BOUNDS_P: + GETPARAM_P(offs,op); + if ((ucell)pri>(ucell)offs) { + amx->cip=(cell)((unsigned char *)cip-amx->code); + ABORT(amx,AMX_ERR_BOUNDS); + } /* if */ + break; +#endif /* AMX_NO_PACKED_OPC */ + default: + assert(0); /* invalid instructions should already have been caught in VerifyPcode() */ + ABORT(amx,AMX_ERR_INVINSTR); + } /* switch */ + } /* for */ +#endif /* AMX_ALTCORE */ +} + +#endif /* AMX_EXEC */ + +#if defined AMX_SETCALLBACK +int AMXAPI amx_SetCallback(AMX *amx,AMX_CALLBACK callback) +{ + assert(amx!=NULL); + assert(callback!=NULL); + amx->callback=callback; + return AMX_ERR_NONE; +} +#endif /* AMX_SETCALLBACK */ + +#if defined AMX_SETDEBUGHOOK +int AMXAPI amx_SetDebugHook(AMX *amx,AMX_DEBUG debug) +{ + assert(amx!=NULL); + amx->debug=debug; + return AMX_ERR_NONE; +} +#endif /* AMX_SETDEBUGHOOK */ + +#if defined AMX_RAISEERROR +int AMXAPI amx_RaiseError(AMX *amx, int error) +{ + assert(error>0); + amx->error=error; + return AMX_ERR_NONE; +} +#endif /* AMX_RAISEERROR */ + +#if defined AMX_ALLOT || defined AMX_PUSHXXX +int AMXAPI amx_Allot(AMX *amx,int cells,cell **address) +{ + 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; + if (address!=NULL) + *address=(cell *)(data+(int)amx->hea); + amx->hea+=cells*sizeof(cell); + return AMX_ERR_NONE; +} + +int AMXAPI amx_Release(AMX *amx,cell *address) +{ + AMX_HEADER *hdr; + unsigned char *data; + cell amx_addr; + + 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; + amx_addr=(cell)((unsigned char*)address-data); + if (amx->hea>amx_addr) + amx->hea=amx_addr; + return AMX_ERR_NONE; +} +#endif /* AMX_ALLOT || AMX_PUSHXXX */ + +#if defined AMX_XXXSTRING || defined AMX_UTF8XXX + +#define CHARBITS (8*sizeof(char)) +#if PAWN_CELL_SIZE==16 + #define CHARMASK (0xffffu << 8*(2-sizeof(char))) +#elif PAWN_CELL_SIZE==32 + #define CHARMASK (0xffffffffuL << 8*(4-sizeof(char))) +#elif PAWN_CELL_SIZE==64 + #define CHARMASK (0xffffffffffffffffuLL << 8*(8-sizeof(char))) +#else + #error Unsupported cell size +#endif + +int AMXAPI amx_StrLen(const cell *cstr, int *length) +{ + int len; + #if BYTE_ORDER==LITTLE_ENDIAN + cell c; + #endif + + assert(length!=NULL); + if (cstr==NULL) { + *length=0; + return AMX_ERR_PARAMS; + } /* if */ + + if ((ucell)*cstr>UNPACKEDMAX) { + /* packed string */ + assert_static(sizeof(char)==1); + len=strlen((char *)cstr); /* find '\0' */ + assert(check_endian()); + #if BYTE_ORDER==LITTLE_ENDIAN + /* on Little Endian machines, toggle the last bytes */ + c=cstr[len/sizeof(cell)]; /* get last cell */ + len=len - len % sizeof(cell); /* len = multiple of "cell" bytes */ + while ((c & CHARMASK)!=0) { + len++; + c <<= 8*sizeof(char); + } /* if */ + #endif + } else { + for (len=0; cstr[len]!=0; len++) + /* nothing */; + } /* if */ + *length = len; + return AMX_ERR_NONE; +} +#endif + +#if defined AMX_XXXSTRING || defined AMX_PUSHXXX +int AMXAPI amx_SetString(cell *dest,const char *source,int pack,int use_wchar,size_t size) +{ /* the memory blocks should not overlap */ + int len, i; + + assert_static(UNLIMITED>0); + #if defined AMX_ANSIONLY + (void)use_wchar; + len=strlen(source); + #else + len= use_wchar ? wcslen((const wchar_t*)source) : strlen(source); + #endif + if (pack) { + /* create a packed string */ + if (size<UNLIMITED/sizeof(cell) && (size_t)len>=size*sizeof(cell)) + len=size*sizeof(cell)-1; + dest[len/sizeof(cell)]=0; /* clear last bytes of last (semi-filled) cell*/ + #if defined AMX_ANSIONLY + memcpy(dest,source,len); + #else + if (use_wchar) { + for (i=0; i<len; i++) + ((char*)dest)[i]=(char)(((wchar_t*)source)[i]); + } else { + memcpy(dest,source,len); + } /* if */ + #endif + /* On Big Endian machines, the characters are well aligned in the + * cells; on Little Endian machines, we must swap all cells. + */ + assert(check_endian()); + #if BYTE_ORDER==LITTLE_ENDIAN + len /= sizeof(cell); + while (len>=0) + swapcell((ucell *)&dest[len--]); + #endif + } else { + /* create an unpacked string */ + if (size<UNLIMITED && (size_t)len>=size) + len=size-1; + #if defined AMX_ANSIONLY + for (i=0; i<len; i++) + dest[i]=(cell)source[i]; + #else + if (use_wchar) { + for (i=0; i<len; i++) + dest[i]=(cell)(((wchar_t*)source)[i]); + } else { + for (i=0; i<len; i++) + dest[i]=(cell)source[i]; + } /* if */ + #endif + dest[len]=0; + } /* if */ + return AMX_ERR_NONE; +} +#endif + +#if defined AMX_XXXSTRING +int AMXAPI amx_GetString(char *dest,const cell *source,int use_wchar,size_t size) +{ + int len=0; + #if defined AMX_ANSIONLY + (void)use_wchar; /* unused parameter (if ANSI only) */ + #endif + if ((ucell)*source>UNPACKEDMAX) { + /* source string is packed */ + cell c=0; /* initialize to 0 to avoid a compiler warning */ + int i=sizeof(cell)-1; + char ch; + while ((size_t)len<size) { + if (i==sizeof(cell)-1) + c=*source++; + ch=(char)(c >> i*CHARBITS); + if (ch=='\0') + break; /* terminating zero character found */ + #if defined AMX_ANSIONLY + dest[len++]=ch; + #else + if (use_wchar) + ((wchar_t*)dest)[len++]=ch; + else + dest[len++]=ch; + #endif + i=(i+sizeof(cell)-1) % sizeof(cell); + } /* while */ + } else { + /* source string is unpacked */ + #if defined AMX_ANSIONLY + while (*source!=0 && (size_t)len<size) + dest[len++]=(char)*source++; + #else + if (use_wchar) { + while (*source!=0 && (size_t)len<size) + ((wchar_t*)dest)[len++]=(wchar_t)*source++; + } else { + while (*source!=0 && (size_t)len<size) + dest[len++]=(char)*source++; + } /* if */ + #endif + } /* if */ + /* store terminator */ + if ((size_t)len>=size) + len=size-1; + if (len>=0) { + #if defined AMX_ANSIONLY + dest[len]='\0'; + #else + if (use_wchar) + ((wchar_t*)dest)[len]=0; + else + dest[len]='\0'; + #endif + } /* IF */ + return AMX_ERR_NONE; +} +#endif /* AMX_XXXSTRING */ + +#if defined AMX_UTF8XXX + #if defined __BORLANDC__ + #pragma warn -amb -8000 /* ambiguous operators need parentheses */ + #endif +/* amx_UTF8Get() + * Extract a single UTF-8 encoded character from a string and return a pointer + * to the character just behind that UTF-8 character. The parameters "endptr" + * and "value" may be NULL. + * If the code is not valid UTF-8, "endptr" has the value of the input + * parameter "string" and "value" is zero. + */ +int AMXAPI amx_UTF8Get(const char *string, const char **endptr, cell *value) +{ +static const char utf8_count[16]={ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 4 }; +static const long utf8_lowmark[5] = { 0x80, 0x800, 0x10000L, 0x200000L, 0x4000000L }; + unsigned char c; + cell result; + int followup; + + assert(string!=NULL); + if (value!=NULL) /* preset, in case of an error */ + *value=0; + if (endptr!=NULL) + *endptr=string; + + c = *(const unsigned char*)string++; + if (c<0x80) { + /* ASCII */ + result=c; + } else { + if (c<0xc0 || c>=0xfe) + return AMX_ERR_PARAMS; /* invalid or "follower" code, quit with error */ + /* At this point we know that the two top bits of c are ones. The two + * bottom bits are always part of the code. We only need to consider + * the 4 remaining bits; i.e., a 16-byte table. This is "utf8_count[]". + * (Actually the utf8_count[] table records the number of follow-up + * bytes minus 1. This is just for convenience.) + */ + assert((c & 0xc0)==0xc0); + followup=(int)utf8_count[(c >> 2) & 0x0f]; + /* The mask depends on the code length; this is just a very simple + * relation. + */ + #define utf8_mask (0x1f >> followup) + result= c & utf8_mask; + /* Collect the follow-up codes using a drop-through switch statement; + * this avoids a loop. In each case, verify the two leading bits. + */ + assert(followup>=0 && followup<=4); + switch (followup) { + case 4: + if (((c=*string++) & 0xc0) != 0x80) goto error; + result = (result << 6) | c & 0x3f; + case 3: + if (((c=*string++) & 0xc0) != 0x80) goto error; + result = (result << 6) | c & 0x3f; + case 2: + if (((c=*string++) & 0xc0) != 0x80) goto error; + result = (result << 6) | c & 0x3f; + case 1: + if (((c=*string++) & 0xc0) != 0x80) goto error; + result = (result << 6) | c & 0x3f; + case 0: + if (((c=*string++) & 0xc0) != 0x80) goto error; + result = (result << 6) | c & 0x3f; + } /* switch */ + /* Do additional checks: shortest encoding & reserved positions. The + * lowmark limits also depends on the code length; it can be read from + * a table with 5 elements. This is "utf8_lowmark[]". + */ + if (result<utf8_lowmark[followup]) + goto error; + if (result>=0xd800 && result<=0xdfff || result==0xfffe || result==0xffff) + goto error; + } /* if */ + + if (value!=NULL) + *value=result; + if (endptr!=NULL) + *endptr=string; + + return AMX_ERR_NONE; + +error: + return AMX_ERR_PARAMS; +} + +/* amx_UTF8Put() + * Encode a single character into a byte string. The character may result in + * a string of up to 6 bytes. The function returns an error code if "maxchars" + * is lower than the required number of characters; in this case nothing is + * stored. + * The function does not zero-terminate the string. + */ +int AMXAPI amx_UTF8Put(char *string, char **endptr, int maxchars, cell value) +{ + assert(string!=NULL); + if (endptr!=NULL) /* preset, in case of an error */ + *endptr=string; + + if (value<0x80) { + /* 0xxxxxxx */ + if (maxchars < 1) goto error; + *string++ = (char)value; + } else if (value<0x800) { + /* 110xxxxx 10xxxxxx */ + if (maxchars < 2) goto error; + *string++ = (char)((value>>6) & 0x1f | 0xc0); + *string++ = (char)(value & 0x3f | 0x80); + } else if (value<0x10000) { + /* 1110xxxx 10xxxxxx 10xxxxxx (16 bits, BMP plane) */ + if (maxchars < 3) goto error; + if (value>=0xd800 && value<=0xdfff || value==0xfffe || value==0xffff) + goto error; /* surrogate pairs and invalid characters */ + *string++ = (char)((value>>12) & 0x0f | 0xe0); + *string++ = (char)((value>>6) & 0x3f | 0x80); + *string++ = (char)(value & 0x3f | 0x80); + } else if (value<0x200000) { + /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (maxchars < 4) goto error; + *string++ = (char)((value>>18) & 0x07 | 0xf0); + *string++ = (char)((value>>12) & 0x3f | 0x80); + *string++ = (char)((value>>6) & 0x3f | 0x80); + *string++ = (char)(value & 0x3f | 0x80); + } else if (value<0x4000000) { + /* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (maxchars < 5) goto error; + *string++ = (char)((value>>24) & 0x03 | 0xf8); + *string++ = (char)((value>>18) & 0x3f | 0x80); + *string++ = (char)((value>>12) & 0x3f | 0x80); + *string++ = (char)((value>>6) & 0x3f | 0x80); + *string++ = (char)(value & 0x3f | 0x80); + } else { + /* 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx (31 bits) */ + if (maxchars < 6) goto error; + *string++ = (char)((value>>30) & 0x01 | 0xfc); + *string++ = (char)((value>>24) & 0x3f | 0x80); + *string++ = (char)((value>>18) & 0x3f | 0x80); + *string++ = (char)((value>>12) & 0x3f | 0x80); + *string++ = (char)((value>>6) & 0x3f | 0x80); + *string++ = (char)(value & 0x3f | 0x80); + } /* if */ + + if (endptr!=NULL) + *endptr=string; + return AMX_ERR_NONE; + +error: + return AMX_ERR_PARAMS; +} + +/* amx_UTF8Check() + * Run through a zero-terminated string and check the validity of the UTF-8 + * encoding. The function returns an error code, it is AMX_ERR_NONE if the + * string is valid UTF-8 (or valid ASCII for that matter). + */ +int AMXAPI amx_UTF8Check(const char *string, int *length) +{ + int err=AMX_ERR_NONE; + int len=0; + while (err==AMX_ERR_NONE && *string!='\0') { + err=amx_UTF8Get(string,&string,NULL); + len++; + } /* while */ + if (length!=NULL) + *length=len; + return err; +} + +/* amx_UTF8Len() + * Run through a wide string and return how many 8-bit characters are needed to + * store the string in UTF-8 format. The returned cound excludes the terminating + * zero byte. The function returns an error code. + */ +int AMXAPI amx_UTF8Len(const cell *cstr, int *length) +{ + int err; + + assert(length!=NULL); + err=amx_StrLen(cstr, length); + if (err==AMX_ERR_NONE && (ucell)*cstr<=UNPACKEDMAX) { + char buffer[10]; /* maximum UTF-8 code is 6 characters */ + char *endptr; + int len=*length, count=0; + while (len-->0) { + amx_UTF8Put(buffer, &endptr, sizeof buffer, *cstr++); + count+=(int)(endptr-buffer); + } /* while */ + *length=count; + } /* while */ + return err; +} +#endif /* AMX_UTF8XXX */