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/

Dependents:   Pawn4Test

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers amx.c Source File

amx.c

00001 /*  Pawn Abstract Machine (for the Pawn language)
00002  *
00003  *  Copyright (c) ITB CompuPhase, 1997-2012
00004  *
00005  *  Licensed under the Apache License, Version 2.0 (the "License"); you may not
00006  *  use this file except in compliance with the License. You may obtain a copy
00007  *  of the License at
00008  *
00009  *      http://www.apache.org/licenses/LICENSE-2.0
00010  *
00011  *  Unless required by applicable law or agreed to in writing, software
00012  *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
00013  *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
00014  *  License for the specific language governing permissions and limitations
00015  *  under the License.
00016  *
00017  *  Version: $Id: amx.c 4708 2012-05-18 12:52:49Z thiadmer $
00018  */
00019 
00020 #define WIN32_LEAN_AND_MEAN
00021 #if defined _UNICODE || defined __UNICODE__ || defined UNICODE
00022 # if !defined UNICODE   /* for Windows API */
00023 #   define UNICODE
00024 # endif
00025 # if !defined _UNICODE  /* for C library */
00026 #   define _UNICODE
00027 # endif
00028 #endif
00029 
00030 #include <assert.h>
00031 #include <stdarg.h>
00032 #include <stddef.h>     /* for wchar_t */
00033 #include <stdlib.h>     /* for getenv() */
00034 #include <string.h>
00035 #include "osdefs.h"
00036 #if defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__
00037   #include <sclinux.h>
00038   #if !defined AMX_NODYNALOAD
00039     #include <dlfcn.h>
00040   #endif
00041   #if defined AMX_JIT
00042     #include <sys/types.h>
00043     #include <sys/mman.h>
00044   #endif
00045 #endif
00046 #if defined __LCC__ || defined __LINUX__
00047   #include <wchar.h>    /* for wcslen() */
00048 #endif
00049 
00050 #if defined __ECOS__
00051   /* eCos puts include files in cyg/package_name */
00052   #include <cyg/pawn/amx.h>
00053 #else
00054   #include "amx.h"
00055 #endif
00056 
00057 #if (defined _Windows && !defined AMX_NODYNALOAD) || (defined AMX_JIT && __WIN32__)
00058   #include <windows.h>
00059 #endif
00060 
00061 
00062 /* When one or more of the AMX_funcname macros are defined, we want
00063  * to compile only those functions. However, when none of these macros
00064  * is present, we want to compile everything.
00065  */
00066 #if defined AMX_ALIGN       || defined AMX_ALLOT        || defined AMX_CLEANUP
00067   #define AMX_EXPLIT_FUNCTIONS
00068 #endif
00069 #if defined AMX_CLONE       || defined AMX_DEFCALLBACK  || defined AMX_EXEC
00070   #define AMX_EXPLIT_FUNCTIONS
00071 #endif
00072 #if defined AMX_FLAGS       || defined AMX_INIT         || defined AMX_MEMINFO
00073   #define AMX_EXPLIT_FUNCTIONS
00074 #endif
00075 #if defined AMX_NAMELENGTH   || defined AMX_NATIVEINFO  || defined AMX_PUSHXXX
00076   #define AMX_EXPLIT_FUNCTIONS
00077 #endif
00078 #if defined AMX_RAISEERROR   || defined AMX_REGISTER    || defined AMX_SETCALLBACK
00079   #define AMX_EXPLIT_FUNCTIONS
00080 #endif
00081 #if defined AMX_SETDEBUGHOOK || defined AMX_UTF8XXX     || defined AMX_XXXNATIVES
00082   #define AMX_EXPLIT_FUNCTIONS
00083 #endif
00084 #if defined AMX_XXXPUBLICS  || defined AMX_XXXPUBVARS   || defined AMX_XXXSTRING
00085   #define AMX_EXPLIT_FUNCTIONS
00086 #endif
00087 #if defined AMX_XXXTAGS     || defined AMX_XXXUSERDATA
00088   #define AMX_EXPLIT_FUNCTIONS
00089 #endif
00090 #if !defined AMX_EXPLIT_FUNCTIONS
00091   /* no constant set, set them all */
00092   #define AMX_ALIGN             /* amx_Align16(), amx_Align32() and amx_Align64() */
00093   #define AMX_ALLOT             /* amx_Allot() and amx_Release() */
00094   #define AMX_DEFCALLBACK       /* amx_Callback() */
00095   #define AMX_CLEANUP           /* amx_Cleanup() */
00096   #define AMX_CLONE             /* amx_Clone() */
00097   #define AMX_EXEC              /* amx_Exec() */
00098   #define AMX_FLAGS             /* amx_Flags() */
00099   #define AMX_INIT              /* amx_Init() and amx_InitJIT() */
00100   #define AMX_MEMINFO           /* amx_MemInfo() */
00101   #define AMX_NAMELENGTH        /* amx_NameLength() */
00102   #define AMX_NATIVEINFO        /* amx_NativeInfo() */
00103   #define AMX_PUSHXXX           /* amx_Push(), amx_PushAddress(), amx_PushArray() and amx_PushString() */
00104   #define AMX_RAISEERROR        /* amx_RaiseError() */
00105   #define AMX_REGISTER          /* amx_Register() */
00106   #define AMX_SETCALLBACK       /* amx_SetCallback() */
00107   #define AMX_SETDEBUGHOOK      /* amx_SetDebugHook() */
00108   #define AMX_UTF8XXX           /* amx_UTF8Check(), amx_UTF8Get(), amx_UTF8Len() and amx_UTF8Put() */
00109   #define AMX_XXXNATIVES        /* amx_NumNatives(), amx_GetNative() and amx_FindNative() */
00110   #define AMX_XXXPUBLICS        /* amx_NumPublics(), amx_GetPublic() and amx_FindPublic() */
00111   #define AMX_XXXPUBVARS        /* amx_NumPubVars(), amx_GetPubVar() and amx_FindPubVar() */
00112   #define AMX_XXXSTRING         /* amx_StrLen(), amx_GetString() and amx_SetString() */
00113   #define AMX_XXXTAGS           /* amx_NumTags(), amx_GetTag() and amx_FindTagId() */
00114   #define AMX_XXXUSERDATA       /* amx_GetUserData() and amx_SetUserData() */
00115 #endif
00116 #undef AMX_EXPLIT_FUNCTIONS
00117 #if defined AMX_ANSIONLY
00118   #undef AMX_UTF8XXX            /* no UTF-8 support in ANSI/ASCII-only version */
00119 #endif
00120 #if defined AMX_NO_NATIVEINFO
00121   #undef AMX_NATIVEINFO
00122 #endif
00123 #if AMX_USERNUM <= 0
00124   #undef AMX_XXXUSERDATA
00125 #endif
00126 #if defined AMX_JIT
00127   /* JIT is incompatible with macro instructions, packed opcodes and overlays */
00128   #if !defined AMX_NO_MACRO_INSTR
00129     #define AMX_NO_MACRO_INSTR
00130   #endif
00131   #if !defined AMX_NO_PACKED_OPC
00132     #define AMX_NO_PACKED_OPC
00133   #endif
00134   #if !defined AMX_NO_OVERLAY
00135     #define AMX_NO_OVERLAY
00136   #endif
00137 #endif
00138 #if (defined AMX_ASM || defined AMX_JIT) && !defined AMX_ALTCORE
00139   /* do not use the standard ANSI-C amx_Exec() function */
00140   #define AMX_ALTCORE
00141 #endif
00142 #if !defined AMX_NO_PACKED_OPC && !defined AMX_TOKENTHREADING
00143   #define AMX_TOKENTHREADING    /* packed opcodes require token threading */
00144 #endif
00145 
00146 #if defined AMX_ALTCORE
00147   #if defined __WIN32__
00148     /* For Watcom C/C++ use register calling convention (faster); for
00149      * Microsoft C/C++ (and most other C compilers) use "cdecl".
00150      * The important point is that you assemble AMXEXEC.ASM with the matching
00151      * calling convention, or the right JIT, respectively.
00152      * AMXJITR.ASM is for Watcom's register calling convention, AMXJITS.ASM and
00153      * AMXJITSN.ASM are for "cdecl".
00154      */
00155     #if defined __WATCOMC__ && !defined STACKARGS
00156       /* register calling convention; the #pragma tells the compiler into which
00157        * registers the parameters should go
00158        */
00159       extern cell amx_exec_run(AMX *amx,cell *retval,unsigned char *data);
00160           #pragma aux amx_exec_run parm [eax] [edx] [ebx];
00161       extern int amx_exec_list(const AMX *amx,const cell **opcodelist,int *numopcodes);
00162           #pragma aux amx_exec_run parm [eax] [edx] [ebx];
00163       extern cell amx_jit_compile(void *pcode, void *jumparray, void *nativecode);
00164           #pragma aux amx_exec_run parm [eax] [edx] [ebx];
00165       extern cell amx_jit_run(AMX *amx,cell *retval,unsigned char *data);
00166           #pragma aux amx_jit_run parm [eax] [edx] [ebx];
00167       extern int  amx_jit_list(const AMX *amx,const cell **opcodelist,int *numopcodes);
00168           #pragma aux amx_exec_run parm [eax] [edx] [ebx];
00169     #elif defined __GNUC__
00170       /* force "cdecl" by adding an "attribute" to the declaration */
00171       extern cell amx_exec_run(AMX *amx,cell *retval,unsigned char *data) __attribute__((cdecl));
00172       extern int amx_exec_list(const AMX *amx,const cell **opcodelist,int *numopcodes) __attribute__((cdecl));
00173       extern cell amx_jit_compile(void *pcode, void *jumparray, void *nativecode) __attribute__((cdecl));
00174       extern cell amx_jit_run(AMX *amx,cell *retval,unsigned char *data) __attribute__((cdecl));
00175       extern int  amx_jit_list(const AMX *amx,const cell **opcodelist,int *numopcodes) __attribute__((cdecl));
00176     #else
00177       /* force "cdecl" by specifying it as a "function class" with the "__cdecl" keyword */
00178       extern cell __cdecl amx_exec_run(AMX *amx,cell *retval,unsigned char *data);
00179       extern int __cdecl amx_exec_list(const AMX *amx,const cell **opcodelist,int *numopcodes);
00180       extern cell __cdecl amx_jit_compile(void *pcode, void *jumparray, void *nativecode);
00181       extern cell __cdecl amx_jit_run(AMX *amx,cell *retval,unsigned char *data);
00182       extern int  __cdecl amx_jit_list(const AMX *amx,const cell **opcodelist,int *numopcodes);
00183     #endif
00184   #else /* __WIN32__ */
00185     /* assume no specific calling conventions for other platforms than Windows */
00186     extern cell amx_exec_run(AMX *amx,cell *retval,unsigned char *data);
00187     extern int amx_exec_list(const AMX *amx,const cell **opcodelist,int *numopcodes);
00188     extern cell amx_jit_compile(void *pcode, void *jumparray, void *nativecode);
00189     extern cell amx_jit_run(AMX *amx,cell *retval,unsigned char *data);
00190     extern int  amx_jit_list(const AMX *amx,const cell **opcodelist,int *numopcodes);
00191   #endif /* __WIN32__ */
00192 #else
00193   int amx_exec_list(AMX *amx,const cell **opcodelist,int *numopcodes);
00194 #endif /* AMX_ALTCORE */
00195 
00196 typedef enum {
00197   OP_NOP,
00198   OP_LOAD_PRI,
00199   OP_LOAD_ALT,
00200   OP_LOAD_S_PRI,
00201   OP_LOAD_S_ALT,
00202   OP_LREF_S_PRI,
00203   OP_LREF_S_ALT,
00204   OP_LOAD_I,
00205   OP_LODB_I,
00206   OP_CONST_PRI,
00207   OP_CONST_ALT,
00208   OP_ADDR_PRI,
00209   OP_ADDR_ALT,
00210   OP_STOR,
00211   OP_STOR_S,
00212   OP_SREF_S,
00213   OP_STOR_I,
00214   OP_STRB_I,
00215   OP_ALIGN_PRI,
00216   OP_LCTRL,
00217   OP_SCTRL,
00218   OP_XCHG,
00219   OP_PUSH_PRI,
00220   OP_PUSH_ALT,
00221   OP_PUSHR_PRI,
00222   OP_POP_PRI,
00223   OP_POP_ALT,
00224   OP_PICK,
00225   OP_STACK,
00226   OP_HEAP,
00227   OP_PROC,
00228   OP_RET,
00229   OP_RETN,
00230   OP_CALL,
00231   OP_JUMP,
00232   OP_JZER,
00233   OP_JNZ,
00234   OP_SHL,
00235   OP_SHR,
00236   OP_SSHR,
00237   OP_SHL_C_PRI,
00238   OP_SHL_C_ALT,
00239   OP_SMUL,
00240   OP_SDIV,
00241   OP_ADD,
00242   OP_SUB,
00243   OP_AND,
00244   OP_OR,
00245   OP_XOR,
00246   OP_NOT,
00247   OP_NEG,
00248   OP_INVERT,
00249   OP_EQ,
00250   OP_NEQ,
00251   OP_SLESS,
00252   OP_SLEQ,
00253   OP_SGRTR,
00254   OP_SGEQ,
00255   OP_INC_PRI,
00256   OP_INC_ALT,
00257   OP_INC_I,
00258   OP_DEC_PRI,
00259   OP_DEC_ALT,
00260   OP_DEC_I,
00261   OP_MOVS,
00262   OP_CMPS,
00263   OP_FILL,
00264   OP_HALT,
00265   OP_BOUNDS,
00266   OP_SYSREQ,
00267   OP_SWITCH,
00268   OP_SWAP_PRI,
00269   OP_SWAP_ALT,
00270   OP_BREAK,
00271   OP_CASETBL,
00272   /* patched instructions */
00273   OP_SYSREQ_D,
00274   OP_SYSREQ_ND,
00275   /* overlay instructions */
00276   OP_CALL_OVL,
00277   OP_RETN_OVL,
00278   OP_SWITCH_OVL,
00279   OP_CASETBL_OVL,
00280 #if !defined AMX_NO_MACRO_INSTR
00281   /* supplemental & macro instructions */
00282   OP_LIDX,
00283   OP_LIDX_B,
00284   OP_IDXADDR,
00285   OP_IDXADDR_B,
00286   OP_PUSH_C,
00287   OP_PUSH,
00288   OP_PUSH_S,
00289   OP_PUSH_ADR,
00290   OP_PUSHR_C,
00291   OP_PUSHR_S,
00292   OP_PUSHR_ADR,
00293   OP_JEQ,
00294   OP_JNEQ,
00295   OP_JSLESS,
00296   OP_JSLEQ,
00297   OP_JSGRTR,
00298   OP_JSGEQ,
00299   OP_SDIV_INV,
00300   OP_SUB_INV,
00301   OP_ADD_C,
00302   OP_SMUL_C,
00303   OP_ZERO_PRI,
00304   OP_ZERO_ALT,
00305   OP_ZERO,
00306   OP_ZERO_S,
00307   OP_EQ_C_PRI,
00308   OP_EQ_C_ALT,
00309   OP_INC,
00310   OP_INC_S,
00311   OP_DEC,
00312   OP_DEC_S,
00313   /* macro instructions */
00314   OP_SYSREQ_N,
00315   OP_PUSHM_C,
00316   OP_PUSHM,
00317   OP_PUSHM_S,
00318   OP_PUSHM_ADR,
00319   OP_PUSHRM_C,
00320   OP_PUSHRM_S,
00321   OP_PUSHRM_ADR,
00322   OP_LOAD2,
00323   OP_LOAD2_S,
00324   OP_CONST,
00325   OP_CONST_S,
00326 #endif
00327 #if !defined AMX_NO_PACKED_OPC
00328   /* packed instructions */
00329   OP_LOAD_P_PRI,
00330   OP_LOAD_P_ALT,
00331   OP_LOAD_P_S_PRI,
00332   OP_LOAD_P_S_ALT,
00333   OP_LREF_P_S_PRI,
00334   OP_LREF_P_S_ALT,
00335   OP_LODB_P_I,
00336   OP_CONST_P_PRI,
00337   OP_CONST_P_ALT,
00338   OP_ADDR_P_PRI,
00339   OP_ADDR_P_ALT,
00340   OP_STOR_P,
00341   OP_STOR_P_S,
00342   OP_SREF_P_S,
00343   OP_STRB_P_I,
00344   OP_LIDX_P_B,
00345   OP_IDXADDR_P_B,
00346   OP_ALIGN_P_PRI,
00347   OP_PUSH_P_C,
00348   OP_PUSH_P,
00349   OP_PUSH_P_S,
00350   OP_PUSH_P_ADR,
00351   OP_PUSHR_P_C,
00352   OP_PUSHR_P_S,
00353   OP_PUSHR_P_ADR,
00354   OP_PUSHM_P_C,
00355   OP_PUSHM_P,
00356   OP_PUSHM_P_S,
00357   OP_PUSHM_P_ADR,
00358   OP_PUSHRM_P_C,
00359   OP_PUSHRM_P_S,
00360   OP_PUSHRM_P_ADR,
00361   OP_STACK_P,
00362   OP_HEAP_P,
00363   OP_SHL_P_C_PRI,
00364   OP_SHL_P_C_ALT,
00365   OP_ADD_P_C,
00366   OP_SMUL_P_C,
00367   OP_ZERO_P,
00368   OP_ZERO_P_S,
00369   OP_EQ_P_C_PRI,
00370   OP_EQ_P_C_ALT,
00371   OP_INC_P,
00372   OP_INC_P_S,
00373   OP_DEC_P,
00374   OP_DEC_P_S,
00375   OP_MOVS_P,
00376   OP_CMPS_P,
00377   OP_FILL_P,
00378   OP_HALT_P,
00379   OP_BOUNDS_P,
00380 #endif
00381   /* ----- */
00382   OP_NUM_OPCODES
00383 } OPCODE;
00384 
00385 #define NUMENTRIES(hdr,field,nextfield) \
00386                         (unsigned)(((hdr)->nextfield - (hdr)->field) / (hdr)->defsize)
00387 #define GETENTRY(hdr,table,index) \
00388                         (AMX_FUNCSTUB *)((unsigned char*)(hdr) + (unsigned)(hdr)->table + (unsigned)index*(hdr)->defsize)
00389 #define GETENTRYNAME(hdr,entry) \
00390                         (char *)((unsigned char*)(hdr) + (unsigned)((AMX_FUNCSTUB*)(entry))->nameofs)
00391 
00392 #if !defined NDEBUG
00393   static int check_endian(void)
00394   {
00395     uint16_t val=0x00ff;
00396     unsigned char *ptr=(unsigned char *)&val;
00397     /* "ptr" points to the starting address of "val". If that address
00398      * holds the byte "0xff", the computer stored the low byte of "val"
00399      * at the lower address, and so the memory lay out is Little Endian.
00400      */
00401     assert(*ptr==0xff || *ptr==0x00);
00402     #if BYTE_ORDER==BIG_ENDIAN
00403       return *ptr==0x00;  /* return "true" if big endian */
00404     #else
00405       return *ptr==0xff;  /* return "true" if little endian */
00406     #endif
00407   }
00408 #endif
00409 
00410 #if BYTE_ORDER==BIG_ENDIAN || PAWN_CELL_SIZE==16
00411   static void swap16(uint16_t *v)
00412   {
00413     unsigned char *s = (unsigned char *)v;
00414     unsigned char t;
00415 
00416     assert_static(sizeof(*v)==2);
00417     /* swap two bytes */
00418     t=s[0];
00419     s[0]=s[1];
00420     s[1]=t;
00421   }
00422 #endif
00423 
00424 #if BYTE_ORDER==BIG_ENDIAN || PAWN_CELL_SIZE==32
00425   static void swap32(uint32_t *v)
00426   {
00427     unsigned char *s = (unsigned char *)v;
00428     unsigned char t;
00429 
00430     assert_static(sizeof(*v)==4);
00431     /* swap outer two bytes */
00432     t=s[0];
00433     s[0]=s[3];
00434     s[3]=t;
00435     /* swap inner two bytes */
00436     t=s[1];
00437     s[1]=s[2];
00438     s[2]=t;
00439   }
00440 #endif
00441 
00442 #if (BYTE_ORDER==BIG_ENDIAN || PAWN_CELL_SIZE==64) && (defined _I64_MAX || defined HAVE_I64)
00443   static void swap64(uint64_t *v)
00444   {
00445     unsigned char *s = (unsigned char *)v;
00446     unsigned char t;
00447 
00448     assert(sizeof(*v)==8);
00449 
00450     t=s[0];
00451     s[0]=s[7];
00452     s[7]=t;
00453 
00454     t=s[1];
00455     s[1]=s[6];
00456     s[6]=t;
00457 
00458     t=s[2];
00459     s[2]=s[5];
00460     s[5]=t;
00461 
00462     t=s[3];
00463     s[3]=s[4];
00464     s[4]=t;
00465   }
00466 #endif
00467 
00468 #if defined AMX_ALIGN || defined AMX_INIT
00469 uint16_t * AMXAPI amx_Align16(uint16_t *v)
00470 {
00471   assert_static(sizeof(*v)==2);
00472   assert(check_endian());
00473   #if BYTE_ORDER==BIG_ENDIAN
00474     swap16(v);
00475   #endif
00476   return v;
00477 }
00478 
00479 uint32_t * AMXAPI amx_Align32(uint32_t *v)
00480 {
00481   assert_static(sizeof(*v)==4);
00482   assert(check_endian());
00483   #if BYTE_ORDER==BIG_ENDIAN
00484     swap32(v);
00485   #endif
00486   return v;
00487 }
00488 
00489 #if defined _I64_MAX || defined HAVE_I64
00490 uint64_t * AMXAPI amx_Align64(uint64_t *v)
00491 {
00492   assert(sizeof(*v)==8);
00493   assert(check_endian());
00494   #if BYTE_ORDER==BIG_ENDIAN
00495     swap64(v);
00496   #endif
00497   return v;
00498 }
00499 #endif  /* _I64_MAX || HAVE_I64 */
00500 #endif  /* AMX_ALIGN || AMX_INIT */
00501 
00502 #if PAWN_CELL_SIZE==16
00503   #define swapcell  swap16
00504 #elif PAWN_CELL_SIZE==32
00505   #define swapcell  swap32
00506 #elif PAWN_CELL_SIZE==64 && (defined _I64_MAX || defined HAVE_I64)
00507   #define swapcell  swap64
00508 #else
00509   #error Unsupported cell size
00510 #endif
00511 
00512 #if defined AMX_FLAGS
00513 int AMXAPI amx_Flags(AMX *amx,uint16_t *flags)
00514 {
00515   AMX_HEADER *hdr;
00516 
00517   *flags=0;
00518   if (amx==NULL)
00519     return AMX_ERR_FORMAT;
00520   hdr=(AMX_HEADER *)amx->base;
00521   if (hdr->magic!=AMX_MAGIC)
00522     return AMX_ERR_FORMAT;
00523   if (hdr->file_version>CUR_FILE_VERSION || hdr->amx_version<MIN_FILE_VERSION)
00524     return AMX_ERR_VERSION;
00525   *flags=hdr->flags;
00526   return AMX_ERR_NONE;
00527 }
00528 #endif /* AMX_FLAGS */
00529 
00530 #if defined AMX_DEFCALLBACK
00531 int AMXAPI amx_Callback(AMX *amx, cell index, cell *result, const cell *params)
00532 {
00533 #if defined AMX_NATIVETABLE
00534   extern AMX_NATIVE const AMX_NATIVETABLE[];
00535 #endif
00536   AMX_HEADER *hdr;
00537   AMX_FUNCSTUB *func;
00538   AMX_NATIVE f;
00539 
00540   assert(amx!=NULL);
00541   hdr=(AMX_HEADER *)amx->base;
00542   assert(hdr!=NULL);
00543   assert(hdr->magic==AMX_MAGIC);
00544   assert(hdr->natives<=hdr->libraries);
00545 #if defined AMX_NATIVETABLE
00546   if (index<0) {
00547     /* size of AMX_NATIVETABLE is unknown here, so we cannot verify index */
00548     f=(AMX_NATIVETABLE)[-(index+1)];
00549   } else {
00550 #endif
00551     assert(index>=0 && index<(cell)NUMENTRIES(hdr,natives,libraries));
00552     func=GETENTRY(hdr,natives,index);
00553     f=(AMX_NATIVE)func->address;
00554 #if defined AMX_NATIVETABLE
00555   } /* if */
00556 #endif
00557   assert(f!=NULL);
00558 
00559   /* Now that we have found the function, patch the program so that any
00560    * subsequent call will call the function directly (bypassing this
00561    * callback).
00562    * This trick cannot work in the JIT, because the program would need to
00563    * be re-JIT-compiled after patching a P-code instruction.
00564    */
00565   assert((amx->flags & AMX_FLAG_JITC)==0 || amx->sysreq_d==0);
00566   if (amx->sysreq_d!=0) {
00567     /* at the point of the call, the CIP pseudo-register points directly
00568      * behind the SYSREQ(.N) instruction and its parameter(s)
00569      */
00570     unsigned char *code=amx->code+(int)amx->cip-sizeof(cell);
00571     if (amx->flags & AMX_FLAG_SYSREQN)      /* SYSREQ.N has 2 parameters */
00572       code-=sizeof(cell);
00573     assert(amx->code!=NULL);
00574     assert(amx->cip>=4 && amx->cip<(hdr->dat - hdr->cod));
00575     assert_static(sizeof(f)<=sizeof(cell)); /* function pointer must fit in a cell */
00576     assert(*(cell*)code==index);
00577     #if defined AMX_TOKENTHREADING || !(defined __GNUC__ || defined __ICC || defined AMX_ASM || defined AMX_JIT)
00578       assert(!(amx->flags & AMX_FLAG_SYSREQN) && *(cell*)(code-sizeof(cell))==OP_SYSREQ
00579              || (amx->flags & AMX_FLAG_SYSREQN) && *(cell*)(code-sizeof(cell))==OP_SYSREQ_N);
00580     #endif
00581     *(cell*)(code-sizeof(cell))=amx->sysreq_d;
00582     *(cell*)code=(cell)f;
00583   } /* if */
00584 
00585   /* Note:
00586    *   params[0] == number of bytes for the additional parameters passed to the native function
00587    *   params[1] == first argument
00588    *   etc.
00589    */
00590 
00591   amx->error=AMX_ERR_NONE;
00592   *result = f(amx,params);
00593   return amx->error;
00594 }
00595 #endif /* defined AMX_DEFCALLBACK */
00596 
00597 
00598 #if defined AMX_JIT
00599   /* convert from relative addresses to absolute physical addresses */
00600   #define RELOC_ABS(base,off)   (*(ucell *)((base)+(int)(off)) += (ucell)(base)+(int)(off)-sizeof(cell))
00601 #else
00602   #define JUMPREL(ip)           ((cell*)((intptr_t)(ip)+*(cell*)(ip)-sizeof(cell)))
00603 #endif
00604 #if defined AMX_ASM || defined AMX_JIT
00605   #define RELOCATE_ADDR(base,v) ((v)+((ucell)(base)))
00606 #else
00607   #define RELOCATE_ADDR(base,v) (v)
00608 #endif
00609 
00610 #define DBGPARAM(v)     ( (v)=*(cell *)(amx->code+(int)cip), cip+=sizeof(cell) )
00611 
00612 #if !defined GETOPCODE
00613   #if defined AMX_NO_PACKED_OPC
00614     #define GETOPCODE(c)  (OPCODE)(c)
00615   #else
00616     #define GETOPCODE(c)  (OPCODE)((c) & ((1 << sizeof(cell)*4)-1))
00617   #endif
00618 #endif
00619 #if !defined GETPARAM_P
00620   #define GETPARAM_P(v,o) ( v=((cell)(o) >> (int)(sizeof(cell)*4)) )
00621 #endif
00622 
00623 #if defined AMX_INIT
00624 
00625 static int VerifyPcode(AMX *amx)
00626 {
00627   AMX_HEADER *hdr;
00628   cell op,cip,tgt,opmask;
00629   int sysreq_flg,max_opcode;
00630   int datasize,stacksize;
00631   const cell *opcode_list;
00632   #if defined AMX_JIT
00633     int opcode_count=0;
00634     int reloc_count=0;
00635     int jit_codesize=0;
00636   #endif
00637 
00638   assert(amx!=NULL);
00639   hdr=(AMX_HEADER *)amx->base;
00640   assert(hdr!=NULL);
00641   assert(hdr->magic==AMX_MAGIC);
00642   amx->flags|=AMX_FLAG_VERIFY;
00643   datasize=hdr->hea-hdr->dat;
00644   stacksize=hdr->stp-hdr->hea;
00645 
00646   #if defined AMX_ASM && defined AMX_JIT
00647     if ((amx->flags & AMX_FLAG_JITC)!=0)
00648       jit_codesize=amx_jit_list(amx,&opcode_list,&max_opcode);
00649     else
00650       amx_exec_list(amx,&opcode_list,&max_opcode);
00651   #elif defined AMX_JIT
00652     jit_codesize=amx_jit_list(amx,&opcode_list,&max_opcode);
00653   #else
00654     amx_exec_list(amx,&opcode_list,&max_opcode);
00655   #endif
00656   #if defined AMX_TOKENTHREADING
00657     opcode_list=NULL; /* avoid token translation if token threading is in effect */
00658   #endif
00659   #if defined AMX_NO_PACKED_OPC
00660     opmask= ~0;
00661   #else
00662     opmask=(1 << sizeof(cell)*4)-1;
00663   #endif
00664 
00665   /* sanity checks */
00666   assert_static(OP_XCHG==21);
00667   assert_static(OP_SMUL==42);
00668   assert_static(OP_MOVS==64);
00669   #if !defined AMX_NO_MACRO_INSTR
00670     assert_static(OP_LIDX==81);
00671     assert_static(OP_ZERO_PRI==102);
00672     assert_static(OP_LOAD2==120);
00673   #endif
00674   #if !defined AMX_NO_PACKED_OPC
00675     assert_static(OP_LOAD_P_PRI==124);
00676     assert_static(OP_ALIGN_P_PRI==141);
00677     assert_static(OP_BOUNDS_P==174);
00678   #endif
00679 
00680   sysreq_flg=0;
00681   if (opcode_list!=NULL) {
00682     if (amx->sysreq_d==opcode_list[OP_SYSREQ_D])
00683       sysreq_flg=0x01;
00684     else if (amx->sysreq_d==opcode_list[OP_SYSREQ_ND])
00685       sysreq_flg=0x02;
00686   } else {
00687     if (amx->sysreq_d==OP_SYSREQ_D)
00688       sysreq_flg=0x01;
00689     else if (amx->sysreq_d==OP_SYSREQ_ND)
00690       sysreq_flg=0x02;
00691   } /* if */
00692   amx->sysreq_d=0;      /* preset */
00693 
00694   /* start browsing code */
00695   assert(amx->code!=NULL);  /* should already have been set in amx_Init() */
00696   for (cip=0; cip<amx->codesize; ) {
00697     op=*(cell *)(amx->code+(int)cip);
00698     if ((op & opmask)>=max_opcode) {
00699       amx->flags &= ~AMX_FLAG_VERIFY;
00700       return AMX_ERR_INVINSTR;
00701     } /* if */
00702     /* relocate opcode (only works if the size of an opcode is at least
00703      * as big as the size of a pointer (jump address); so basically we
00704      * rely on the opcode and a pointer being 32-bit
00705      */
00706     if (opcode_list!=NULL) {
00707       /* verify that opcode_list[op]!=NULL, if it is, this instruction
00708        * is unsupported
00709        */
00710       if (opcode_list[op & opmask]==0) {
00711         amx->flags &= ~AMX_FLAG_VERIFY;
00712         return AMX_ERR_INVINSTR;
00713       } /* if */
00714       *(cell *)(amx->code+(int)cip)=opcode_list[op & opmask];
00715     } /* if */
00716     #if defined AMX_JIT
00717       opcode_count++;
00718     #endif
00719     cip+=sizeof(cell);
00720     switch (op & opmask) {
00721 #if !defined AMX_NO_MACRO_INSTR
00722     case OP_PUSHM_C:    /* instructions with variable number of parameters */
00723     case OP_PUSHM:
00724     case OP_PUSHM_S:
00725     case OP_PUSHM_ADR:
00726     case OP_PUSHRM_C:
00727     case OP_PUSHRM_S:
00728     case OP_PUSHRM_ADR:
00729       tgt=*(cell*)(amx->code+(int)cip); /* get count */
00730       cip+=sizeof(cell)*(tgt+1);
00731       break;
00732 
00733     case OP_LOAD2:
00734       tgt=*(cell*)(amx->code+(int)cip); /* verify both addresses */
00735       if (tgt<0 || tgt>=datasize) {
00736         amx->flags &= ~AMX_FLAG_VERIFY;
00737         return AMX_ERR_BOUNDS;
00738       } /* if */
00739       tgt=*(cell*)(amx->code+(int)cip+(int)sizeof(cell));
00740       if (tgt<0 || tgt>=datasize) {
00741         amx->flags &= ~AMX_FLAG_VERIFY;
00742         return AMX_ERR_BOUNDS;
00743       } /* if */
00744       cip+=sizeof(cell)*2;
00745       break;
00746 
00747     case OP_LOAD2_S:
00748       tgt=*(cell*)(amx->code+(int)cip); /* verify both addresses */
00749       if (tgt<-stacksize || tgt>stacksize) {
00750         amx->flags &= ~AMX_FLAG_VERIFY;
00751         return AMX_ERR_BOUNDS;
00752       } /* if */
00753       tgt=*(cell*)(amx->code+(int)cip+(int)sizeof(cell));
00754       if (tgt<-stacksize || tgt>stacksize) {
00755         amx->flags &= ~AMX_FLAG_VERIFY;
00756         return AMX_ERR_BOUNDS;
00757       } /* if */
00758       cip+=sizeof(cell)*2;
00759       break;
00760 
00761     case OP_CONST:
00762       tgt=*(cell*)(amx->code+(int)cip); /* verify address */
00763       if (tgt<0 || tgt>=datasize) {
00764         amx->flags &= ~AMX_FLAG_VERIFY;
00765         return AMX_ERR_BOUNDS;
00766       } /* if */
00767       cip+=sizeof(cell)*2;
00768       break;
00769 
00770     case OP_CONST_S:
00771       tgt=*(cell*)(amx->code+(int)cip); /* verify both addresses */
00772       if (tgt<-stacksize || tgt>stacksize) {
00773         amx->flags &= ~AMX_FLAG_VERIFY;
00774         return AMX_ERR_BOUNDS;
00775       } /* if */
00776       cip+=sizeof(cell)*2;
00777       break;
00778 #endif /* !defined AMX_NO_MACRO_INSTR */
00779 
00780 #if !defined AMX_NO_PACKED_OPC
00781     case OP_LODB_P_I:   /* instructions with 1 parameter packed inside the same cell */
00782     case OP_CONST_P_PRI:
00783     case OP_CONST_P_ALT:
00784     case OP_ADDR_P_PRI:
00785     case OP_ADDR_P_ALT:
00786     case OP_STRB_P_I:
00787     case OP_LIDX_P_B:
00788     case OP_IDXADDR_P_B:
00789     case OP_ALIGN_P_PRI:
00790     case OP_PUSH_P_C:
00791     case OP_PUSH_P:
00792     case OP_PUSH_P_S:
00793     case OP_PUSH_P_ADR:
00794     case OP_PUSHR_P_C:
00795     case OP_PUSHR_P_S:
00796     case OP_PUSHR_P_ADR:
00797     case OP_STACK_P:
00798     case OP_HEAP_P:
00799     case OP_SHL_P_C_PRI:
00800     case OP_SHL_P_C_ALT:
00801     case OP_ADD_P_C:
00802     case OP_SMUL_P_C:
00803     case OP_ZERO_P:
00804     case OP_ZERO_P_S:
00805     case OP_EQ_P_C_PRI:
00806     case OP_EQ_P_C_ALT:
00807     case OP_MOVS_P:
00808     case OP_CMPS_P:
00809     case OP_FILL_P:
00810     case OP_HALT_P:
00811     case OP_BOUNDS_P:
00812       break;
00813 
00814     case OP_LOAD_P_PRI: /* data instructions with 1 parameter packed inside the same cell */
00815     case OP_LOAD_P_ALT:
00816     case OP_STOR_P:
00817     case OP_INC_P:
00818     case OP_DEC_P:
00819       GETPARAM_P(tgt,op); /* verify address */
00820       if (tgt<0 || tgt>=datasize) {
00821         amx->flags &= ~AMX_FLAG_VERIFY;
00822         return AMX_ERR_BOUNDS;
00823       } /* if */
00824       break;
00825 
00826     case OP_LOAD_P_S_PRI: /* stack instructions with 1 parameter packed inside the same cell */
00827     case OP_LOAD_P_S_ALT:
00828     case OP_LREF_P_S_PRI:
00829     case OP_LREF_P_S_ALT:
00830     case OP_STOR_P_S:
00831     case OP_SREF_P_S:
00832     case OP_INC_P_S:
00833     case OP_DEC_P_S:
00834       GETPARAM_P(tgt,op); /* verify address */
00835       if (tgt<-stacksize || tgt>stacksize) {
00836         amx->flags &= ~AMX_FLAG_VERIFY;
00837         return AMX_ERR_BOUNDS;
00838       } /* if */
00839       break;
00840 
00841     case OP_PUSHM_P_C:    /* instructions with variable number of parameters */
00842     case OP_PUSHM_P:
00843     case OP_PUSHM_P_S:
00844     case OP_PUSHM_P_ADR:
00845     case OP_PUSHRM_P_C:
00846     case OP_PUSHRM_P_S:
00847     case OP_PUSHRM_P_ADR:
00848       GETPARAM_P(tgt,op); /* verify address */
00849       cip+=sizeof(cell)*tgt;
00850       break;
00851 #endif /* !defined AMX_NO_PACKED_OPC */
00852 
00853     case OP_LODB_I:     /* instructions with 1 parameter (not packed) */
00854     case OP_CONST_PRI:
00855     case OP_CONST_ALT:
00856     case OP_ADDR_PRI:
00857     case OP_ADDR_ALT:
00858     case OP_STRB_I:
00859     case OP_ALIGN_PRI:
00860     case OP_LCTRL:
00861     case OP_SCTRL:
00862     case OP_PICK:
00863     case OP_STACK:
00864     case OP_HEAP:
00865     case OP_SHL_C_PRI:
00866     case OP_SHL_C_ALT:
00867     case OP_MOVS:
00868     case OP_CMPS:
00869     case OP_FILL:
00870     case OP_HALT:
00871     case OP_BOUNDS:
00872 #if !defined AMX_NO_MACRO_INSTR
00873     case OP_LIDX_B:
00874     case OP_IDXADDR_B:
00875     case OP_PUSH_C:
00876     case OP_PUSH_ADR:
00877     case OP_PUSHR_C:
00878     case OP_PUSHR_ADR:
00879     case OP_ADD_C:
00880     case OP_SMUL_C:
00881     case OP_ZERO:
00882     case OP_ZERO_S:
00883     case OP_EQ_C_PRI:
00884     case OP_EQ_C_ALT:
00885 #endif
00886       cip+=sizeof(cell);
00887       break;
00888 
00889     case OP_LOAD_PRI:
00890     case OP_LOAD_ALT:
00891     case OP_STOR:
00892 #if !defined AMX_NO_MACRO_INSTR
00893     case OP_PUSH:
00894     case OP_INC:
00895     case OP_DEC:
00896 #endif
00897       tgt=*(cell*)(amx->code+(int)cip); /* verify address */
00898       if (tgt<0 || tgt>=datasize) {
00899         amx->flags &= ~AMX_FLAG_VERIFY;
00900         return AMX_ERR_BOUNDS;
00901       } /* if */
00902       cip+=sizeof(cell);
00903       break;
00904 
00905     case OP_LOAD_S_PRI:
00906     case OP_LOAD_S_ALT:
00907     case OP_LREF_S_PRI:
00908     case OP_LREF_S_ALT:
00909     case OP_STOR_S:
00910     case OP_SREF_S:
00911 #if !defined AMX_NO_MACRO_INSTR
00912     case OP_PUSH_S:
00913     case OP_PUSHR_S:
00914     case OP_INC_S:
00915     case OP_DEC_S:
00916 #endif
00917       tgt=*(cell*)(amx->code+(int)cip); /* verify address */
00918       if (tgt<-stacksize || tgt>stacksize) {
00919         amx->flags &= ~AMX_FLAG_VERIFY;
00920         return AMX_ERR_BOUNDS;
00921       } /* if */
00922       cip+=sizeof(cell);
00923       break;
00924 
00925     case OP_NOP:        /* instructions without parameters */
00926     case OP_LOAD_I:
00927     case OP_STOR_I:
00928     case OP_XCHG:
00929     case OP_PUSH_PRI:
00930     case OP_PUSH_ALT:
00931     case OP_PUSHR_PRI:
00932     case OP_POP_PRI:
00933     case OP_POP_ALT:
00934     case OP_PROC:
00935     case OP_RET:
00936     case OP_RETN:
00937     case OP_SHL:
00938     case OP_SHR:
00939     case OP_SSHR:
00940     case OP_SMUL:
00941     case OP_SDIV:
00942     case OP_ADD:
00943     case OP_SUB:
00944     case OP_AND:
00945     case OP_OR:
00946     case OP_XOR:
00947     case OP_NOT:
00948     case OP_NEG:
00949     case OP_INVERT:
00950     case OP_EQ:
00951     case OP_NEQ:
00952     case OP_SLESS:
00953     case OP_SLEQ:
00954     case OP_SGRTR:
00955     case OP_SGEQ:
00956     case OP_INC_PRI:
00957     case OP_INC_ALT:
00958     case OP_INC_I:
00959     case OP_DEC_PRI:
00960     case OP_DEC_ALT:
00961     case OP_DEC_I:
00962     case OP_SWAP_PRI:
00963     case OP_SWAP_ALT:
00964     case OP_BREAK:
00965 #if !defined AMX_NO_MACRO_INSTR
00966     case OP_LIDX:
00967     case OP_IDXADDR:
00968     case OP_SDIV_INV:
00969     case OP_SUB_INV:
00970     case OP_ZERO_PRI:
00971     case OP_ZERO_ALT:
00972 #endif
00973       break;
00974 
00975     case OP_CALL:       /* opcodes that need relocation (JIT only), or conversion to position-independent code */
00976     case OP_JUMP:
00977     case OP_JZER:
00978     case OP_JNZ:
00979     case OP_SWITCH:
00980 #if !defined AMX_NO_MACRO_INSTR
00981     case OP_JEQ:
00982     case OP_JNEQ:
00983     case OP_JSLESS:
00984     case OP_JSLEQ:
00985     case OP_JSGRTR:
00986     case OP_JSGEQ:
00987 #endif
00988       tgt=*(cell*)(amx->code+(int)cip)+cip-sizeof(cell);
00989       if (tgt<0 || tgt>amx->codesize) {
00990         amx->flags &= ~AMX_FLAG_VERIFY;
00991         return AMX_ERR_BOUNDS;
00992       } /* if */
00993       #if defined AMX_JIT
00994         reloc_count++;
00995         RELOC_ABS(amx->code, cip);  /* change to absolute physical address */
00996       #endif
00997       cip+=sizeof(cell);
00998       break;
00999 
01000 #if !defined AMX_NO_OVERLAY
01001     /* overlay opcodes (overlays must be enabled) */
01002     case OP_SWITCH_OVL:
01003       assert(hdr->file_version>=10);
01004       tgt=*(cell*)(amx->code+(int)cip)+cip-sizeof(cell);
01005       if (tgt<0 || tgt>amx->codesize) {
01006         amx->flags &= ~AMX_FLAG_VERIFY;
01007         return AMX_ERR_BOUNDS;
01008       } /* if */
01009       /* drop through */
01010     case OP_CALL_OVL:
01011       cip+=sizeof(cell);
01012       /* drop through */
01013     case OP_RETN_OVL:
01014       assert(hdr->overlays!=0 && hdr->overlays!=hdr->nametable);
01015       #if defined AMX_JIT
01016         if ((amx->flags & AMX_FLAG_JITC)!=0)
01017           return AMX_ERR_OVERLAY;     /* JIT does not support overlays */
01018       #endif
01019       if (amx->overlay==NULL)
01020         return AMX_ERR_OVERLAY;       /* no overlay callback */
01021       break;
01022     case OP_CASETBL_OVL: {
01023       cell num;
01024       DBGPARAM(num);    /* number of records follows the opcode */
01025       cip+=(2*num + 1)*sizeof(cell);
01026       if (amx->overlay==NULL)
01027         return AMX_ERR_OVERLAY;       /* no overlay callback */
01028       break;
01029     } /* case */
01030 #endif
01031 
01032     case OP_SYSREQ:
01033       cip+=sizeof(cell);
01034       sysreq_flg|=0x01; /* mark SYSREQ found */
01035       break;
01036 #if !defined AMX_NO_MACRO_INSTR
01037     case OP_SYSREQ_N:
01038       cip+=sizeof(cell)*2;
01039       sysreq_flg|=0x02; /* mark SYSREQ.N found */
01040       break;
01041 #endif
01042 
01043     case OP_CASETBL: {
01044       cell num,offs;
01045       int i;
01046       DBGPARAM(num);    /* number of records follows the opcode */
01047       for (i=0; i<=num; i++) {
01048         offs=cip+2*i*sizeof(cell);
01049         tgt=*(cell*)(amx->code+(int)offs)+offs-sizeof(cell);
01050         if (tgt<0 || tgt>amx->codesize) {
01051           amx->flags &= ~AMX_FLAG_VERIFY;
01052           return AMX_ERR_BOUNDS;
01053         } /* if */
01054         #if defined AMX_JIT
01055           RELOC_ABS(amx->code, cip+2*i*sizeof(cell));
01056           reloc_count++;
01057         #endif
01058       } /* for */
01059       cip+=(2*num + 1)*sizeof(cell);
01060       break;
01061     } /* case */
01062 
01063     default:
01064       amx->flags &= ~AMX_FLAG_VERIFY;
01065       return AMX_ERR_INVINSTR;
01066     } /* switch */
01067   } /* for */
01068 
01069   #if !defined AMX_DONT_RELOCATE
01070     /* only either type of system request opcode should be found (otherwise,
01071      * we probably have a non-conforming compiler
01072      */
01073     if ((sysreq_flg==0x01 || sysreq_flg==0x02) && (amx->flags & AMX_FLAG_JITC)==0) {
01074       /* to use direct system requests, a function pointer must fit in a cell;
01075        * because the native function's address will be stored as the parameter
01076        * of SYSREQ.(N)D
01077        */
01078       if (sizeof(AMX_NATIVE)<=sizeof(cell)) {
01079         if (opcode_list!=NULL)
01080           amx->sysreq_d=(sysreq_flg==0x01) ? opcode_list[OP_SYSREQ_D] : opcode_list[OP_SYSREQ_ND];
01081         else
01082           amx->sysreq_d=(sysreq_flg==0x01) ? OP_SYSREQ_D : OP_SYSREQ_ND;
01083       } /* if */
01084     } /* if */
01085   #endif
01086 
01087   #if defined AMX_JIT
01088     /* adjust the code size to mean: estimated code size of the native code
01089      * (instead of the size of the P-code)
01090      */
01091     amx->codesize=jit_codesize*opcode_count + hdr->cod + (hdr->stp - hdr->dat);
01092     amx->reloc_size=2*sizeof(cell)*reloc_count;
01093   #endif
01094 
01095   amx->flags &= ~AMX_FLAG_VERIFY;
01096   amx->flags |= AMX_FLAG_INIT;
01097   if (sysreq_flg & 0x02)
01098     amx->flags |= AMX_FLAG_SYSREQN;
01099 
01100   return AMX_ERR_NONE;
01101 }
01102 
01103 /* definitions used for amx_Init() and amx_Cleanup() */
01104 #if (defined _Windows || defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__) && !defined AMX_NODYNALOAD
01105   typedef int AMXEXPORT (AMXAPI _FAR *AMX_ENTRY)(AMX _FAR *amx);
01106 #endif
01107 
01108 int AMXAPI amx_Init(AMX *amx,void *program)
01109 {
01110   AMX_HEADER *hdr;
01111   int err;
01112   uint16_t *namelength;
01113   unsigned char *data;
01114   #if (defined _Windows || defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__) && !defined AMX_NODYNALOAD
01115     #if defined _Windows
01116       char libname[sNAMEMAX+8]; /* +1 for '\0', +3 for 'amx' prefix, +4 for extension */
01117       HINSTANCE hlib;
01118     #elif defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__
01119       char libname[_MAX_PATH];
01120       char *root;
01121       void *hlib;
01122       #if !defined AMX_LIBPATH
01123         #define AMX_LIBPATH     "AMXLIB"
01124       #endif
01125     #endif
01126     int numlibraries;
01127     AMX_FUNCSTUB *lib;
01128     AMX_ENTRY libinit;
01129   #endif
01130 
01131   if ((amx->flags & AMX_FLAG_INIT)!=0)
01132     return AMX_ERR_INIT;  /* already initialized (may not do so twice) */
01133 
01134   hdr=(AMX_HEADER *)program;
01135   /* the header is in Little Endian, on a Big Endian machine, swap all
01136    * multi-byte words
01137    */
01138   assert(check_endian());
01139   #if BYTE_ORDER==BIG_ENDIAN
01140     amx_Align32((uint32_t*)&hdr->size);
01141     amx_Align16(&hdr->magic);
01142     amx_Align16((uint16_t*)&hdr->flags);
01143     amx_Align16((uint16_t*)&hdr->defsize);
01144     amx_Align32((uint32_t*)&hdr->cod);
01145     amx_Align32((uint32_t*)&hdr->dat);
01146     amx_Align32((uint32_t*)&hdr->hea);
01147     amx_Align32((uint32_t*)&hdr->stp);
01148     amx_Align32((uint32_t*)&hdr->cip);
01149     amx_Align32((uint32_t*)&hdr->publics);
01150     amx_Align32((uint32_t*)&hdr->natives);
01151     amx_Align32((uint32_t*)&hdr->libraries);
01152     amx_Align32((uint32_t*)&hdr->pubvars);
01153     amx_Align32((uint32_t*)&hdr->tags);
01154     if (hdr->file_version>=10)
01155       amx_Align32((uint32_t*)&hdr->overlays);
01156   #endif
01157 
01158   if (hdr->magic!=AMX_MAGIC)
01159     return AMX_ERR_FORMAT;
01160   if (hdr->file_version>CUR_FILE_VERSION || hdr->amx_version<MIN_FILE_VERSION)
01161     return AMX_ERR_VERSION;
01162   if (hdr->defsize!=sizeof(AMX_FUNCSTUB))
01163     return AMX_ERR_FORMAT;
01164   /* check the maximum name length in the separate name table */
01165   amx_Align32((uint32_t*)&hdr->nametable);
01166   namelength=(uint16_t*)((unsigned char*)program + (unsigned)hdr->nametable);
01167   amx_Align16(namelength);
01168   if (*namelength>sNAMEMAX)
01169     return AMX_ERR_FORMAT;
01170   if (hdr->stp<=0)
01171     return AMX_ERR_FORMAT;
01172   assert(hdr->hea == hdr->size);
01173   #if BYTE_ORDER==BIG_ENDIAN
01174     if ((hdr->flags & AMX_FLAG_COMPACT)==0) {
01175       ucell *code=(ucell *)((unsigned char *)program+(int)hdr->cod);
01176       while (code<(ucell *)((unsigned char *)program+(int)hdr->hea))
01177         swapcell(code++);
01178     } /* if */
01179   #endif
01180 
01181   amx->base=(unsigned char *)program;
01182 
01183   /* set initial values */
01184   amx->hlw=hdr->hea - hdr->dat; /* stack and heap relative to data segment */
01185   amx->stp=hdr->stp - hdr->dat - sizeof(cell);
01186   amx->hea=amx->hlw;
01187   amx->stk=amx->stp;
01188   #if defined AMX_DEFCALLBACK
01189     if (amx->callback==NULL)
01190       amx->callback=amx_Callback;
01191   #endif
01192 
01193   /* when running P-code from ROM (with the header with the native function
01194    * table in RAM), the "code" field must be set to a non-NULL value on
01195    * initialization, before calling amx_Init(); in an overlay scheme, the
01196    * code field is modified dynamically by the overlay callback
01197    */
01198   if (amx->code==NULL)
01199     amx->code=amx->base+(int)hdr->cod;
01200   if (amx->codesize==0)
01201     amx->codesize=hdr->dat-hdr->cod;
01202 
01203   /* to split the data segment off the code segment, the "data" field must
01204    * be set to a non-NULL value on initialization, before calling amx_Init();
01205    * you may also need to explicitly initialize the data section with the
01206    * contents read from the AMX file
01207    */
01208   if (amx->data!=NULL) {
01209     data=amx->data;
01210     if ((amx->flags & AMX_FLAG_DSEG_INIT)==0 && amx->overlay==NULL)
01211       memcpy(data,amx->base+(int)hdr->dat,(size_t)(hdr->hea-hdr->dat));
01212   } else {
01213     data=amx->base+(int)hdr->dat;
01214   } /* if */
01215 
01216   /* Set a zero cell at the top of the stack, which functions
01217    * as a sentinel for strings.
01218    */
01219   * (cell *)(data+(int)(hdr->stp-hdr->dat-sizeof(cell)))=0;
01220 
01221   /* also align all addresses in the public function, public variable,
01222    * public tag and native function tables --offsets into the name table
01223    * (if present) must also be swapped.
01224    */
01225   #if BYTE_ORDER==BIG_ENDIAN
01226   { /* local */
01227     AMX_FUNCSTUB *fs;
01228     int i,num;
01229 
01230     fs=GETENTRY(hdr,natives,0);
01231     num=NUMENTRIES(hdr,natives,libraries);
01232     for (i=0; i<num; i++) {
01233       amx_Align32(&fs->address);  /* redundant, because it should be zero */
01234       amx_Align32(&fs->nameofs);
01235       fs=(AMX_FUNCSTUB*)((unsigned char *)fs+hdr->defsize);
01236     } /* for */
01237 
01238     fs=GETENTRY(hdr,publics,0);
01239     assert(hdr->publics<=hdr->natives);
01240     num=NUMENTRIES(hdr,publics,natives);
01241     for (i=0; i<num; i++) {
01242       amx_Align32(&fs->address);
01243       amx_Align32(&fs->nameofs);
01244       fs=(AMX_FUNCSTUB*)((unsigned char *)fs+hdr->defsize);
01245     } /* for */
01246 
01247     fs=GETENTRY(hdr,pubvars,0);
01248     assert(hdr->pubvars<=hdr->tags);
01249     num=NUMENTRIES(hdr,pubvars,tags);
01250     for (i=0; i<num; i++) {
01251       amx_Align32(&fs->address);
01252       amx_Align32(&fs->nameofs);
01253       fs=(AMX_FUNCSTUB*)((unsigned char *)fs+hdr->defsize);
01254     } /* for */
01255 
01256     fs=GETENTRY(hdr,tags,0);
01257     if (hdr->file_version<7) {  /* file version 7 introduced the name table */
01258       assert(hdr->tags<=hdr->cod);
01259       num=NUMENTRIES(hdr,tags,cod);
01260     } else {
01261       assert(hdr->tags<=hdr->nametable);
01262       num=NUMENTRIES(hdr,tags,nametable);
01263     } /* if */
01264     for (i=0; i<num; i++) {
01265       amx_Align32(&fs->address);
01266       amx_Align32(&fs->nameofs);
01267       fs=(AMX_FUNCSTUB*)((unsigned char *)fs+hdr->defsize);
01268     } /* for */
01269   } /* local */
01270   #endif
01271 
01272   /* verify P-code and relocate address in the case of the JIT */
01273   if ((hdr->flags & AMX_FLAG_OVERLAY)==0) {
01274     err=VerifyPcode(amx);
01275   } else {
01276     int i;
01277     err=(amx->overlay==NULL) ? AMX_ERR_OVERLAY : AMX_ERR_NONE;
01278     /* load every overlay on initialization and verify explicitly; we must
01279      * do this to know whether to use new or old system requests
01280      */
01281     for (i=0; err==AMX_ERR_NONE && i<(int)((hdr->nametable - hdr->overlays)/sizeof(AMX_OVERLAYINFO)); i++) {
01282       err=amx->overlay(amx, i);
01283       if (err==AMX_ERR_NONE)
01284         err=VerifyPcode(amx);
01285     } /* for */
01286   } /* if */
01287   if (err!=AMX_ERR_NONE)
01288     return err;
01289 
01290   /* load any extension modules that the AMX refers to */
01291   #if (defined _Windows || defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__) && !defined AMX_NODYNALOAD
01292   { /* local */
01293     int i;
01294     #if defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__
01295       root=getenv("AMXLIB");
01296     #endif
01297     hdr=(AMX_HEADER *)amx->base;
01298     numlibraries=NUMENTRIES(hdr,libraries,pubvars);
01299     for (i=0; i<numlibraries; i++) {
01300       lib=GETENTRY(hdr,libraries,i);
01301       libname[0]='\0';
01302       #if defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__
01303         if (root!=NULL && *root!='\0') {
01304           strcpy(libname,root);
01305           if (libname[strlen(libname)-1]!='/')
01306             strcat(libname,"/");
01307         } /* if */
01308       #endif
01309       strcat(libname,"amx");
01310       strcat(libname,GETENTRYNAME(hdr,lib));
01311       #if defined _Windows
01312         strcat(libname,".dll");
01313         #if defined __WIN32__
01314           hlib=LoadLibraryA(libname);
01315         #else
01316           hlib=LoadLibrary(libname);
01317           if (hlib<=HINSTANCE_ERROR)
01318             hlib=NULL;
01319         #endif
01320       #elif defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__
01321         strcat(libname,".so");
01322         hlib=dlopen(libname,RTLD_NOW);
01323       #endif
01324       if (hlib!=NULL) {
01325         /* a library that cannot be loaded or that does not have the required
01326          * initialization function is simply ignored
01327          */
01328         char funcname[sNAMEMAX+9]; /* +1 for '\0', +4 for 'amx_', +4 for 'Init' */
01329         strcpy(funcname,"amx_");
01330         strcat(funcname,GETENTRYNAME(hdr,lib));
01331         strcat(funcname,"Init");
01332         #if defined _Windows
01333           libinit=(AMX_ENTRY)GetProcAddress(hlib,funcname);
01334         #elif defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__
01335           libinit=(AMX_ENTRY)dlsym(hlib,funcname);
01336         #endif
01337         if (libinit!=NULL)
01338           libinit(amx);
01339       } /* if */
01340       lib->address=(ucell)hlib;
01341     } /* for */
01342   } /* local */
01343   #endif
01344 
01345   return AMX_ERR_NONE;
01346 }
01347 
01348 #if defined AMX_JIT
01349 
01350   #define CODESIZE_JIT    8192  /* approximate size of the code for the JIT */
01351 
01352   #if defined __WIN32__   /* this also applies to Win32 "console" applications */
01353 
01354     #define ALIGN(addr)     (addr)
01355 
01356     #define PROT_READ       0x1         /* page can be read */
01357     #define PROT_WRITE      0x2         /* page can be written */
01358     #define PROT_EXEC       0x4         /* page can be executed */
01359     #define PROT_NONE       0x0         /* page can not be accessed */
01360 
01361     static int mprotect(void *addr, size_t len, int prot)
01362     {
01363       DWORD prev, p = 0;
01364       if ((prot & PROT_WRITE)!=0)
01365         p = PAGE_EXECUTE_READWRITE;
01366       else
01367         p |= PAGE_EXECUTE_READ;
01368       return !VirtualProtect(addr, len, p, &prev);
01369     }
01370 
01371   #elif defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__
01372 
01373     /* Linux already has mprotect() */
01374     #define ALIGN(addr) (char *)(((long)addr + sysconf(_SC_PAGESIZE)-1) & ~(sysconf(_SC_PAGESIZE)-1))
01375 
01376   #else
01377 
01378     // TODO: Add cases for Mac OS/X and other operating systems
01379 
01380     /* DOS32 has no imposed limits on its segments */
01381     #define ALIGN(addr)     (addr)
01382     #define mprotect(addr, len, prot)   (0)
01383 
01384   #endif /* #if defined __WIN32 __ */
01385 
01386 int AMXAPI amx_InitJIT(AMX *amx, void *reloc_table, void *native_code)
01387 {
01388   int res;
01389   AMX_HEADER *hdr;
01390 
01391   if ((amx->flags & AMX_FLAG_JITC)==0)
01392     return AMX_ERR_INIT_JIT;    /* flag not set, this AMX is not prepared for JIT */
01393   if (hdr->file_version>MAX_FILE_VER_JIT)
01394     return AMX_ERR_VERSION;     /* JIT may not support the newest file version(s) */
01395   /* the JIT does not support overlays, but this is already checked in VerifyPcode()
01396    * the same goes for macro instructions: not supported in the JIT, but already
01397    * checked in VerifyPcode()
01398    */
01399 
01400   /* Patching SYSREQ(.N) opcodes to SYSREQ.(N)D cannot work in the JIT, because
01401    * the program would need to be re-JIT-compiled after patching a P-code
01402    * instruction. If this field is not zero, something went wrong in
01403    * VerifyPcode().
01404    */
01405   assert(amx->sysreq_d==0);
01406 
01407   if (mprotect(ALIGN(amx_jit_compile), CODESIZE_JIT, PROT_READ | PROT_WRITE | PROT_EXEC) != 0)
01408     return AMX_ERR_INIT_JIT;
01409 
01410   /* copy the prefix */
01411   memcpy(native_code, amx->base, ((AMX_HEADER *)(amx->base))->cod);
01412   hdr = native_code;
01413 
01414   /* MP: added check for correct compilation */
01415   if ((res = amx_jit_compile(amx->base, reloc_table, native_code)) == 0) {
01416     /* update the required memory size (the previous value was a
01417      * conservative estimate, now we know the exact size)
01418      */
01419     amx->codesize = (hdr->dat + hdr->stp + sizeof(cell)) & ~(sizeof(cell)-1);
01420     /* The compiled code is relocatable, since only relative jumps are
01421      * used for destinations within the generated code, and absolute
01422      * addresses are only for jumps into the runtime, which is fixed
01423      * in memory.
01424      */
01425     /* set the new pointers */
01426     amx->base = (unsigned char*)native_code;
01427     amx->code = amx->base + (int)hdr->cod;
01428     amx->cip = hdr->cip;
01429   } /* if */
01430 
01431   return (res == 0) ? AMX_ERR_NONE : AMX_ERR_INIT_JIT;
01432 }
01433 
01434 #else /* #if defined AMX_JIT */
01435 
01436 int AMXAPI amx_InitJIT(AMX *amx,void *compiled_program,void *reloc_table)
01437 {
01438   (void)amx;
01439   (void)compiled_program;
01440   (void)reloc_table;
01441   return AMX_ERR_INIT_JIT;
01442 }
01443 
01444 #endif  /* #if defined AMX_JIT */
01445 
01446 #endif  /* AMX_INIT */
01447 
01448 #if defined AMX_CLEANUP
01449 int AMXAPI amx_Cleanup(AMX *amx)
01450 {
01451   #if (defined _Windows || defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__) && !defined AMX_NODYNALOAD
01452     AMX_HEADER *hdr;
01453     int numlibraries,i;
01454     AMX_FUNCSTUB *lib;
01455     AMX_ENTRY libcleanup;
01456   #endif
01457 
01458   /* unload all extension modules */
01459   #if (defined _Windows || defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__) && !defined AMX_NODYNALOAD
01460     hdr=(AMX_HEADER *)amx->base;
01461     assert(hdr->magic==AMX_MAGIC);
01462     numlibraries=NUMENTRIES(hdr,libraries,pubvars);
01463     for (i=0; i<numlibraries; i++) {
01464       lib=GETENTRY(hdr,libraries,i);
01465       if (lib->address!=0) {
01466         char funcname[sNAMEMAX+12]; /* +1 for '\0', +4 for 'amx_', +7 for 'Cleanup' */
01467         strcpy(funcname,"amx_");
01468         strcat(funcname,GETENTRYNAME(hdr,lib));
01469         strcat(funcname,"Cleanup");
01470         #if defined _Windows
01471           libcleanup=(AMX_ENTRY)GetProcAddress((HINSTANCE)lib->address,funcname);
01472         #elif defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__
01473           libcleanup=(AMX_ENTRY)dlsym((void*)lib->address,funcname);
01474         #endif
01475         if (libcleanup!=NULL)
01476           libcleanup(amx);
01477         #if defined _Windows
01478           FreeLibrary((HINSTANCE)lib->address);
01479         #elif defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__
01480           dlclose((void*)lib->address);
01481         #endif
01482       } /* if */
01483     } /* for */
01484   #else
01485     (void)amx;
01486   #endif
01487   return AMX_ERR_NONE;
01488 }
01489 #endif /* AMX_CLEANUP */
01490 
01491 #if defined AMX_CLONE
01492 int AMXAPI amx_Clone(AMX *amxClone, AMX *amxSource, void *data)
01493 {
01494   AMX_HEADER *hdr;
01495   unsigned char _FAR *dataSource;
01496 
01497   if (amxSource==NULL)
01498     return AMX_ERR_FORMAT;
01499   if (amxClone==NULL)
01500     return AMX_ERR_PARAMS;
01501   if ((amxSource->flags & AMX_FLAG_INIT)==0)
01502     return AMX_ERR_INIT;
01503   hdr=(AMX_HEADER *)amxSource->base;
01504   if (hdr->magic!=AMX_MAGIC)
01505     return AMX_ERR_FORMAT;
01506   if (hdr->file_version>CUR_FILE_VERSION || hdr->amx_version<MIN_FILE_VERSION)
01507     return AMX_ERR_VERSION;
01508 
01509   /* set initial values */
01510   amxClone->base=amxSource->base;
01511   amxClone->code=amxSource->code;
01512   amxClone->codesize=amxSource->codesize;
01513   amxClone->hlw=hdr->hea - hdr->dat; /* stack and heap relative to data segment */
01514   amxClone->stp=hdr->stp - hdr->dat - sizeof(cell);
01515   amxClone->hea=amxClone->hlw;
01516   amxClone->stk=amxClone->stp;
01517   if (amxClone->callback==NULL)
01518     amxClone->callback=amxSource->callback;
01519   if (amxClone->debug==NULL)
01520     amxClone->debug=amxSource->debug;
01521   amxClone->flags=amxSource->flags;
01522 
01523   /* copy the data segment; the stack and the heap can be left uninitialized */
01524   assert(data!=NULL);
01525   amxClone->data=(unsigned char _FAR *)data;
01526   dataSource=(amxSource->data!=NULL) ? amxSource->data : amxSource->base+(int)hdr->dat;
01527   memcpy(amxClone->data,dataSource,(size_t)(hdr->hea-hdr->dat));
01528 
01529   /* Set a zero cell at the top of the stack, which functions
01530    * as a sentinel for strings.
01531    */
01532   * (cell *)(amxClone->data+(int)amxClone->stp) = 0;
01533 
01534   return AMX_ERR_NONE;
01535 }
01536 #endif /* AMX_CLONE */
01537 
01538 #if defined AMX_MEMINFO
01539 int AMXAPI amx_MemInfo(AMX *amx, long *codesize, long *datasize, long *stackheap)
01540 {
01541   AMX_HEADER *hdr;
01542 
01543   if (amx==NULL)
01544     return AMX_ERR_FORMAT;
01545   hdr=(AMX_HEADER *)amx->base;
01546   if (hdr->magic!=AMX_MAGIC)
01547     return AMX_ERR_FORMAT;
01548   if (hdr->file_version>CUR_FILE_VERSION || hdr->amx_version<MIN_FILE_VERSION)
01549     return AMX_ERR_VERSION;
01550 
01551   if (codesize!=NULL)
01552     *codesize=amx->codesize;
01553   if (datasize!=NULL)
01554     *datasize=hdr->hea - hdr->dat;
01555   if (stackheap!=NULL)
01556     *stackheap=hdr->stp - hdr->hea;
01557 
01558   return AMX_ERR_NONE;
01559 }
01560 #endif /* AMX_MEMINFO */
01561 
01562 #if defined AMX_NAMELENGTH
01563 int AMXAPI amx_NameLength(AMX *amx, int *length)
01564 {
01565   AMX_HEADER *hdr;
01566   uint16_t *namelength;
01567 
01568   assert(amx!=NULL);
01569   hdr=(AMX_HEADER *)amx->base;
01570   assert(hdr!=NULL);
01571   assert(hdr->magic==AMX_MAGIC);
01572   namelength=(uint16_t*)(amx->base + (unsigned)hdr->nametable);
01573   *length=*namelength;
01574   return AMX_ERR_NONE;
01575 }
01576 #endif /* AMX_NAMELENGTH */
01577 
01578 #if defined AMX_XXXNATIVES
01579 int AMXAPI amx_NumNatives(AMX *amx, int *number)
01580 {
01581   AMX_HEADER *hdr=(AMX_HEADER *)amx->base;
01582   assert(hdr!=NULL);
01583   assert(hdr->magic==AMX_MAGIC);
01584   assert(hdr->natives<=hdr->libraries);
01585   *number=NUMENTRIES(hdr,natives,libraries);
01586   return AMX_ERR_NONE;
01587 }
01588 
01589 int AMXAPI amx_GetNative(AMX *amx, int index, char *name)
01590 {
01591   AMX_HEADER *hdr;
01592   AMX_FUNCSTUB *func;
01593 
01594   hdr=(AMX_HEADER *)amx->base;
01595   assert(hdr!=NULL);
01596   assert(hdr->magic==AMX_MAGIC);
01597   assert(hdr->natives<=hdr->libraries);
01598   if (index>=(cell)NUMENTRIES(hdr,natives,libraries))
01599     return AMX_ERR_INDEX;
01600 
01601   func=GETENTRY(hdr,natives,index);
01602   strcpy(name,GETENTRYNAME(hdr,func));
01603   return AMX_ERR_NONE;
01604 }
01605 
01606 int AMXAPI amx_FindNative(AMX *amx, const char *name, int *index)
01607 {
01608   int idx,last;
01609   char pname[sNAMEMAX+1];
01610 
01611   amx_NumNatives(amx, &last);
01612   /* linear search, the natives table is not sorted alphabetically */
01613   for (idx=0; idx<last; idx++) {
01614     amx_GetNative(amx,idx,pname);
01615     if (strcmp(pname,name)==0) {
01616       *index=idx;
01617       return AMX_ERR_NONE;
01618     } /* if */
01619   } /* for */
01620   *index=INT_MAX;
01621   return AMX_ERR_NOTFOUND;
01622 }
01623 #endif /* AMX_XXXNATIVES */
01624 
01625 #if defined AMX_XXXPUBLICS
01626 int AMXAPI amx_NumPublics(AMX *amx, int *number)
01627 {
01628   AMX_HEADER *hdr=(AMX_HEADER *)amx->base;
01629   assert(hdr!=NULL);
01630   assert(hdr->magic==AMX_MAGIC);
01631   assert(hdr->publics<=hdr->natives);
01632   *number=NUMENTRIES(hdr,publics,natives);
01633   return AMX_ERR_NONE;
01634 }
01635 
01636 int AMXAPI amx_GetPublic(AMX *amx, int index, char *name, ucell *address)
01637 {
01638   AMX_HEADER *hdr;
01639   AMX_FUNCSTUB *func;
01640 
01641   hdr=(AMX_HEADER *)amx->base;
01642   assert(hdr!=NULL);
01643   assert(hdr->magic==AMX_MAGIC);
01644   assert(hdr->publics<=hdr->natives);
01645   if (index>=(cell)NUMENTRIES(hdr,publics,natives))
01646     return AMX_ERR_INDEX;
01647 
01648   func=GETENTRY(hdr,publics,index);
01649   if (name!=NULL)
01650     strcpy(name,GETENTRYNAME(hdr,func));
01651   if (address!=NULL)
01652     *address=func->address;
01653   return AMX_ERR_NONE;
01654 }
01655 
01656 int AMXAPI amx_FindPublic(AMX *amx, const char *name, int *index)
01657 {
01658   int first,last,mid,result;
01659   char pname[sNAMEMAX+1];
01660 
01661   amx_NumPublics(amx, &last);
01662   last--;       /* last valid index is 1 less than the number of functions */
01663   first=0;
01664   /* binary search */
01665   while (first<=last) {
01666     mid=(first+last)/2;
01667     amx_GetPublic(amx,mid,pname,NULL);
01668     result=strcmp(pname,name);
01669     if (result>0) {
01670       last=mid-1;
01671     } else if (result<0) {
01672       first=mid+1;
01673     } else {
01674       *index=mid;
01675       return AMX_ERR_NONE;
01676     } /* if */
01677   } /* while */
01678   /* not found, set to an invalid index, so amx_Exec() on this index will fail
01679    * with an error
01680    */
01681   *index=INT_MAX;
01682   return AMX_ERR_NOTFOUND;
01683 }
01684 #endif /* AMX_XXXPUBLICS */
01685 
01686 #if defined AMX_XXXPUBVARS
01687 int AMXAPI amx_NumPubVars(AMX *amx, int *number)
01688 {
01689   AMX_HEADER *hdr;
01690 
01691   assert(amx!=NULL);
01692   hdr=(AMX_HEADER *)amx->base;
01693   assert(hdr!=NULL);
01694   assert(hdr->magic==AMX_MAGIC);
01695   assert(hdr->pubvars<=hdr->tags);
01696   *number=NUMENTRIES(hdr,pubvars,tags);
01697   return AMX_ERR_NONE;
01698 }
01699 
01700 int AMXAPI amx_GetPubVar(AMX *amx, int index, char *name, cell **address)
01701 {
01702   AMX_HEADER *hdr;
01703   AMX_FUNCSTUB *var;
01704   unsigned char *data;
01705 
01706   assert(amx!=NULL);
01707   hdr=(AMX_HEADER *)amx->base;
01708   assert(hdr!=NULL);
01709   assert(hdr->magic==AMX_MAGIC);
01710   assert(hdr->pubvars<=hdr->tags);
01711   if (index>=(cell)NUMENTRIES(hdr,pubvars,tags))
01712     return AMX_ERR_INDEX;
01713 
01714   var=GETENTRY(hdr,pubvars,index);
01715   strcpy(name,GETENTRYNAME(hdr,var));
01716   data=(amx->data!=NULL) ? amx->data : amx->base+(int)hdr->dat;
01717   assert(address!=NULL);
01718   *address=(cell *)(data+(int)var->address);
01719   return AMX_ERR_NONE;
01720 }
01721 
01722 int AMXAPI amx_FindPubVar(AMX *amx, const char *name, cell **address)
01723 {
01724   int first,last,mid,result;
01725   char pname[sNAMEMAX+1];
01726 
01727   amx_NumPubVars(amx,&last);
01728   last--;       /* last valid index is 1 less than the number of functions */
01729   first=0;
01730   /* binary search */
01731   while (first<=last) {
01732     mid=(first+last)/2;
01733     amx_GetPubVar(amx,mid,pname,address);
01734     result=strcmp(pname,name);
01735     if (result>0)
01736       last=mid-1;
01737     else if (result<0)
01738       first=mid+1;
01739     else
01740       return AMX_ERR_NONE;
01741   } /* while */
01742   /* not found */
01743   assert(address!=NULL);
01744   *address=NULL;
01745   return AMX_ERR_NOTFOUND;
01746 }
01747 #endif /* AMX_XXXPUBVARS */
01748 
01749 #if defined AMX_XXXTAGS
01750 int AMXAPI amx_NumTags(AMX *amx, int *number)
01751 {
01752   AMX_HEADER *hdr=(AMX_HEADER *)amx->base;
01753   assert(hdr!=NULL);
01754   assert(hdr->magic==AMX_MAGIC);
01755   if (hdr->file_version<5) {    /* the tagname table appeared in file format 5 */
01756     *number=0;
01757     return AMX_ERR_VERSION;
01758   } /* if */
01759   if (hdr->file_version<7) {    /* file version 7 introduced the name table */
01760     assert(hdr->tags<=hdr->cod);
01761     *number=NUMENTRIES(hdr,tags,cod);
01762   } else {
01763     assert(hdr->tags<=hdr->nametable);
01764     *number=NUMENTRIES(hdr,tags,nametable);
01765   } /* if */
01766   return AMX_ERR_NONE;
01767 }
01768 
01769 int AMXAPI amx_GetTag(AMX *amx, int index, char *tagname, cell *tag_id)
01770 {
01771   AMX_HEADER *hdr;
01772   AMX_FUNCSTUB *tag;
01773 
01774   hdr=(AMX_HEADER *)amx->base;
01775   assert(hdr!=NULL);
01776   assert(hdr->magic==AMX_MAGIC);
01777   if (hdr->file_version<5) {    /* the tagname table appeared in file format 5 */
01778     *tagname='\0';
01779     *tag_id=0;
01780     return AMX_ERR_VERSION;
01781   } /* if */
01782 
01783   if (hdr->file_version<7) {    /* file version 7 introduced the name table */
01784     assert(hdr->tags<=hdr->cod);
01785     if (index>=(cell)NUMENTRIES(hdr,tags,cod))
01786       return AMX_ERR_INDEX;
01787   } else {
01788     assert(hdr->tags<=hdr->nametable);
01789     if (index>=(cell)NUMENTRIES(hdr,tags,nametable))
01790       return AMX_ERR_INDEX;
01791   } /* if */
01792 
01793   tag=GETENTRY(hdr,tags,index);
01794   strcpy(tagname,GETENTRYNAME(hdr,tag));
01795   *tag_id=tag->address;
01796 
01797   return AMX_ERR_NONE;
01798 }
01799 
01800 int AMXAPI amx_FindTagId(AMX *amx, cell tag_id, char *tagname)
01801 {
01802   int first,last,mid;
01803   cell mid_id;
01804 
01805   #if !defined NDEBUG
01806     /* verify that the tagname table is sorted on the tag_id */
01807     amx_NumTags(amx, &last);
01808     if (last>0) {
01809       cell cur_id;
01810       amx_GetTag(amx,0,tagname,&cur_id);
01811       for (first=1; first<last; first++) {
01812         amx_GetTag(amx,first,tagname,&mid_id);
01813         assert(cur_id<mid_id);
01814         cur_id=mid_id;
01815       } /* for */
01816     } /* if */
01817   #endif
01818 
01819   amx_NumTags(amx, &last);
01820   last--;       /* last valid index is 1 less than the number of functions */
01821   first=0;
01822   /* binary search */
01823   while (first<=last) {
01824     mid=(first+last)/2;
01825     amx_GetTag(amx,mid,tagname,&mid_id);
01826     if (mid_id>tag_id)
01827       last=mid-1;
01828     else if (mid_id<tag_id)
01829       first=mid+1;
01830     else
01831       return AMX_ERR_NONE;
01832   } /* while */
01833   /* not found */
01834   *tagname='\0';
01835   return AMX_ERR_NOTFOUND;
01836 }
01837 #endif /* AMX_XXXTAGS */
01838 
01839 #if defined AMX_XXXUSERDATA
01840 int AMXAPI amx_GetUserData(AMX *amx, long tag, void **ptr)
01841 {
01842   int index;
01843 
01844   assert(amx!=NULL);
01845   assert(tag!=0);
01846   for (index=0; index<AMX_USERNUM && amx->usertags[index]!=tag; index++)
01847     /* nothing */;
01848   if (index>=AMX_USERNUM)
01849     return AMX_ERR_USERDATA;
01850   *ptr=amx->userdata[index];
01851   return AMX_ERR_NONE;
01852 }
01853 
01854 int AMXAPI amx_SetUserData(AMX *amx, long tag, void *ptr)
01855 {
01856   int index;
01857 
01858   assert(amx!=NULL);
01859   assert(tag!=0);
01860   /* try to find existing tag */
01861   for (index=0; index<AMX_USERNUM && amx->usertags[index]!=tag; index++)
01862     /* nothing */;
01863   /* if not found, try to find empty tag */
01864   if (index>=AMX_USERNUM)
01865     for (index=0; index<AMX_USERNUM && amx->usertags[index]!=0; index++)
01866       /* nothing */;
01867   /* if still not found, quit with error */
01868   if (index>=AMX_USERNUM)
01869     return AMX_ERR_INDEX;
01870   /* set the tag and the value */
01871   amx->usertags[index]=tag;
01872   amx->userdata[index]=ptr;
01873   return AMX_ERR_NONE;
01874 }
01875 #endif /* AMX_XXXUSERDATA */
01876 
01877 #if defined AMX_REGISTER
01878 static AMX_NATIVE findfunction(char *name, const AMX_NATIVE_INFO *list, int number)
01879 {
01880   int i;
01881 
01882   assert(list!=NULL);
01883   for (i=0; list[i].name!=NULL && (i<number || number==-1); i++)
01884     if (strcmp(name,list[i].name)==0)
01885       return list[i].func;
01886   return NULL;
01887 }
01888 
01889 int AMXAPI amx_Register(AMX *amx, const AMX_NATIVE_INFO *list, int number)
01890 {
01891   AMX_FUNCSTUB *func;
01892   AMX_HEADER *hdr;
01893   int i,numnatives,err;
01894   AMX_NATIVE funcptr;
01895 
01896   assert(amx!=NULL);
01897   hdr=(AMX_HEADER *)amx->base;
01898   assert(hdr!=NULL);
01899   assert(hdr->magic==AMX_MAGIC);
01900   assert(hdr->natives<=hdr->libraries);
01901   numnatives=NUMENTRIES(hdr,natives,libraries);
01902 
01903   err=AMX_ERR_NONE;
01904   func=GETENTRY(hdr,natives,0);
01905   for (i=0; i<numnatives; i++) {
01906     if (func->address==0) {
01907       /* this function is not yet located */
01908       funcptr=(list!=NULL) ? findfunction(GETENTRYNAME(hdr,func),list,number) : NULL;
01909       if (funcptr!=NULL)
01910         func->address=(ucell)funcptr;
01911       else
01912         err=AMX_ERR_NOTFOUND;
01913     } /* if */
01914     func=(AMX_FUNCSTUB*)((unsigned char*)func+hdr->defsize);
01915   } /* for */
01916   if (err==AMX_ERR_NONE)
01917     amx->flags|=AMX_FLAG_NTVREG;
01918   return err;
01919 }
01920 #endif /* AMX_REGISTER */
01921 
01922 #if defined AMX_NATIVEINFO
01923 AMX_NATIVE_INFO * AMXAPI amx_NativeInfo(const char *name, AMX_NATIVE func)
01924 {
01925   static AMX_NATIVE_INFO n;
01926   n.name=name;
01927   n.func=func;
01928   return &n;
01929 }
01930 #endif /* AMX_NATIVEINFO */
01931 
01932 
01933 #define STKMARGIN       ((cell)(16*sizeof(cell)))
01934 
01935 #if defined AMX_PUSHXXX
01936 
01937 int AMXAPI amx_Push(AMX *amx, cell value)
01938 {
01939   AMX_HEADER *hdr;
01940   unsigned char *data;
01941 
01942   if (amx->hea+STKMARGIN>amx->stk)
01943     return AMX_ERR_STACKERR;
01944   hdr=(AMX_HEADER *)amx->base;
01945   data=(amx->data!=NULL) ? amx->data : amx->base+(int)hdr->dat;
01946   amx->stk-=sizeof(cell);
01947   amx->paramcount+=1;
01948   *(cell *)(data+(int)amx->stk)=value;
01949   return AMX_ERR_NONE;
01950 }
01951 
01952 int AMXAPI amx_PushAddress(AMX *amx, cell *address)
01953 {
01954   AMX_HEADER *hdr;
01955   unsigned char *data;
01956   cell xaddr;
01957 
01958   /* reverse relocate the address */
01959   assert(amx!=NULL);
01960   hdr=(AMX_HEADER *)amx->base;
01961   assert(hdr!=NULL);
01962   assert(hdr->magic==AMX_MAGIC);
01963   data=(amx->data!=NULL) ? amx->data : amx->base+(int)hdr->dat;
01964   xaddr=(cell)((unsigned char*)address-data);
01965   if ((ucell)xaddr>=(ucell)amx->stp)
01966     return AMX_ERR_MEMACCESS;
01967   return amx_Push(amx,xaddr);
01968 }
01969 
01970 int AMXAPI amx_PushArray(AMX *amx, cell **address, const cell array[], int numcells)
01971 {
01972   cell xaddr;
01973   int err;
01974 
01975   assert(amx!=NULL);
01976   assert(array!=NULL);
01977 
01978   xaddr=amx->hea; /* save, before it is modified by amx_Allot() */
01979   err=amx_Allot(amx,numcells,address);
01980   if (err==AMX_ERR_NONE) {
01981     memcpy(*address,array,numcells*sizeof(cell));
01982     err=amx_Push(amx,xaddr);
01983   } /* if */
01984   return err;
01985 }
01986 
01987 int AMXAPI amx_PushString(AMX *amx, cell **address, const char *string, int pack, int use_wchar)
01988 {
01989   cell xaddr;
01990   int numcells,err;
01991 
01992   assert(amx!=NULL);
01993   assert(string!=NULL);
01994 
01995   #if defined AMX_ANSIONLY
01996     numcells=strlen(string) + 1;
01997   #else
01998     numcells= (use_wchar ? wcslen((const wchar_t*)string) : strlen(string)) + 1;
01999   #endif
02000   if (pack)
02001     numcells=(numcells+sizeof(cell)-1)/sizeof(cell);
02002   xaddr=amx->hea; /* save, before it is modified by amx_Allot() */
02003   err=amx_Allot(amx,numcells,address);
02004   if (err==AMX_ERR_NONE) {
02005     amx_SetString(*address,string,pack,use_wchar,numcells);
02006     err=amx_Push(amx,xaddr);
02007   } /* if */
02008   return err;
02009 }
02010 #endif /* AMX_PUSHXXX */
02011 
02012 #if defined AMX_EXEC
02013 
02014 /* It is assumed that the abstract machine can simply access the memory area
02015  * for the global data and the stack. If this is not the case, you need to
02016  * define the macro sets _R() and _W(), for reading and writing to memory.
02017  */
02018 #if !defined _R
02019   #define _R_DEFAULT            /* mark default memory access */
02020   #define _R(base,addr)         (* (cell *)((unsigned char*)(base)+(int)(addr)))
02021   #define _R8(base,addr)        (* (unsigned char *)((unsigned char*)(base)+(int)(addr)))
02022   #define _R16(base,addr)       (* (uint16_t *)((unsigned char*)(base)+(int)(addr)))
02023   #define _R32(base,addr)       (* (uint32_t *)((unsigned char*)(base)+(int)(addr)))
02024 #endif
02025 #if !defined _W
02026   #define _W_DEFAULT            /* mark default memory access */
02027   #define _W(base,addr,value)   ((*(cell *)((unsigned char*)(base)+(int)(addr)))=(cell)(value))
02028   #define _W8(base,addr,value)  ((*(unsigned char *)((unsigned char*)(base)+(int)(addr)))=(unsigned char)(value))
02029   #define _W16(base,addr,value) ((*(uint16_t *)((unsigned char*)(base)+(int)(addr)))=(uint16_t)(value))
02030   #define _W32(base,addr,value) ((*(uint32_t *)((unsigned char*)(base)+(int)(addr)))=(uint32_t)(value))
02031 #endif
02032 
02033 #if -8/3==-2 && 8/-3==-2
02034   #define TRUNC_SDIV    /* signed divisions are truncated on this platform */
02035 #else
02036   #define IABS(a)       ((a)>=0 ? (a) : (-a))
02037 #endif
02038 
02039 /* The pseudo-instructions come from the code stream. Normally, these are just
02040  * accessed from memory. When the instructions must be fetched in some other
02041  * way, the definition below must be pre-defined.
02042  * N.B.:
02043  *   - reading from a code address should increment the instruction pointer
02044  *     (called "cip")
02045  *   - only cell-sized accesses occur in code memory
02046  */
02047 #if !defined _RCODE
02048   #define _RCODE()      ( *cip++ )
02049 #endif
02050 
02051 #if !defined GETPARAM
02052   #define GETPARAM(v)   ( v=_RCODE() )   /* read a parameter from the opcode stream */
02053 #endif
02054 #if !defined SKIPPARAM
02055   #define SKIPPARAM(n)  ( cip=(cell *)cip+(n) ) /* for obsolete opcodes */
02056 #endif
02057 
02058 #define AMXPUSH(v)      ( amx->stk-=sizeof(cell), *(cell*)(data+amx->stk)=(v) )
02059 #define ABORT(amx,v)    { (amx)->stk=reset_stk; (amx)->hea=reset_hea; return v; }
02060 
02061 
02062 #if !defined AMX_ALTCORE
02063 int amx_exec_list(AMX *amx,const cell **opcodelist,int *numopcodes)
02064 {
02065   (void)amx;
02066   assert(opcodelist!=NULL);
02067   *opcodelist=NULL;
02068   assert(numopcodes!=NULL);
02069   *numopcodes=OP_NUM_OPCODES;
02070   return 0;
02071 }
02072 #endif
02073 
02074 int AMXAPI amx_Exec(AMX *amx, cell *retval, int index)
02075 {
02076   AMX_HEADER *hdr;
02077   AMX_FUNCSTUB *func;
02078   unsigned char *data;
02079   cell reset_stk,reset_hea;
02080   int i;
02081 #if !defined AMX_ALTCORE
02082   cell pri,alt,stk,frm,hea;
02083   cell *cip,op,offs,val;
02084 #endif
02085 
02086   assert(amx!=NULL);
02087   if ((amx->flags & AMX_FLAG_INIT)==0)
02088     return AMX_ERR_INIT;
02089   if (amx->callback==NULL)
02090     return AMX_ERR_CALLBACK;
02091 
02092   hdr=(AMX_HEADER *)amx->base;
02093   assert(hdr!=NULL);
02094   assert(hdr->magic==AMX_MAGIC);
02095 
02096   if ((amx->flags & AMX_FLAG_NTVREG)==0) {
02097     /* verify that all native functions have been registered (or do not
02098      * need registering)
02099      */
02100     int numnatives;
02101     assert(hdr->natives<=hdr->libraries);
02102     numnatives=NUMENTRIES(hdr,natives,libraries);
02103     func=GETENTRY(hdr,natives,0);
02104     for (i=0; i<numnatives && func->address!=0; i++)
02105       func=(AMX_FUNCSTUB*)((unsigned char*)func+hdr->defsize);
02106     if (i<numnatives)
02107       return AMX_ERR_NOTFOUND;
02108     amx->flags|=AMX_FLAG_NTVREG;  /* no need to check this again */
02109   } /* if */
02110   assert((amx->flags & AMX_FLAG_VERIFY)==0);
02111 
02112   /* set up the registers */
02113   assert(hdr!=NULL && hdr->magic==AMX_MAGIC);
02114   assert(amx->code!=NULL || hdr->overlays!=hdr->nametable);
02115   data=(amx->data!=NULL) ? amx->data : amx->base+(int)hdr->dat;
02116   reset_stk=amx->stk;
02117   reset_hea=amx->hea;
02118   amx->error=AMX_ERR_NONE;
02119 
02120   /* get the start address */
02121   if (index==AMX_EXEC_MAIN) {
02122     if (hdr->cip<0)
02123       return AMX_ERR_INDEX;
02124     amx->cip=hdr->cip;
02125     if (hdr->overlays!=hdr->nametable) {
02126       assert(hdr->overlays!=0);
02127       assert(amx->overlay!=NULL);
02128       amx->ovl_index=(int)hdr->cip;
02129       if ((i=amx->overlay(amx,amx->ovl_index))!=AMX_ERR_NONE)
02130         return i;
02131       amx->cip=0;
02132     } /* if */
02133   } else if (index==AMX_EXEC_CONT) {
02134     /* restore registers reset_stk & reset_hea */
02135     reset_stk=amx->reset_stk;
02136     reset_hea=amx->reset_hea;
02137     if (hdr->overlays!=hdr->nametable) {
02138       assert(hdr->overlays!=0);
02139       assert(amx->overlay!=NULL);
02140       if ((i=amx->overlay(amx,amx->ovl_index))!=AMX_ERR_NONE)
02141         return i;
02142     } /* if */
02143   } else if (index<0) {
02144     return AMX_ERR_INDEX;
02145   } else {
02146     if (index>=(int)NUMENTRIES(hdr,publics,natives))
02147       return AMX_ERR_INDEX;
02148     func=GETENTRY(hdr,publics,index);
02149     amx->cip=func->address;
02150     if (hdr->overlays!=hdr->nametable) {
02151       assert(hdr->overlays!=0);
02152       assert(amx->overlay!=NULL);
02153       amx->ovl_index=func->address;
02154       if ((i=amx->overlay(amx,amx->ovl_index))!=AMX_ERR_NONE)
02155         return i;
02156       amx->cip=0;
02157     } /* if */
02158   } /* if */
02159   /* check values just copied */
02160   if (amx->stk>amx->stp)
02161     return AMX_ERR_STACKLOW;
02162   if (amx->hea<amx->hlw)
02163     return AMX_ERR_HEAPLOW;
02164   assert(check_endian());
02165 
02166   /* sanity checks */
02167   assert_static(OP_XCHG==21);
02168   assert_static(OP_SMUL==42);
02169   assert_static(OP_MOVS==64);
02170   #if !defined AMX_NO_MACRO_INSTR
02171     assert_static(OP_LIDX==81);
02172     assert_static(OP_ZERO_PRI==102);
02173     assert_static(OP_LOAD2==120);
02174   #endif
02175   #if !defined AMX_NO_PACKED_OPC
02176     assert_static(OP_LOAD_P_PRI==124);
02177     assert_static(OP_ALIGN_P_PRI==141);
02178     assert_static(OP_BOUNDS_P==174);
02179   #endif
02180   #if PAWN_CELL_SIZE==16
02181     assert_static(sizeof(cell)==2);
02182   #elif PAWN_CELL_SIZE==32
02183     assert_static(sizeof(cell)==4);
02184   #elif PAWN_CELL_SIZE==64
02185     assert_static(sizeof(cell)==8);
02186   #else
02187     #error Unsupported cell size
02188   #endif
02189 
02190   if (index!=AMX_EXEC_CONT) {
02191     reset_stk+=amx->paramcount*sizeof(cell);
02192     AMXPUSH(amx->paramcount*sizeof(cell));
02193     amx->paramcount=0;          /* push the parameter count to the stack & reset */
02194     AMXPUSH(0);                 /* return address (for overlays: overlay 0, offset 0) */
02195   } /* if */
02196   /* check stack/heap before starting to run */
02197   if (amx->hea+STKMARGIN>amx->stk)
02198     return AMX_ERR_STACKERR;
02199 
02200 #if defined AMX_ALTCORE
02201 
02202   /* start running either the ARM or 80x86 assembler abstract machine or the JIT */
02203   #if defined AMX_ASM && defined AMX_JIT
02204     if ((amx->flags & AMX_FLAG_JITC)!=0)
02205       i = amx_jit_run(amx,retval,data);
02206     else
02207       i = amx_exec_run(amx,retval,data);
02208   #elif defined AMX_JIT
02209     i = amx_jit_run(amx,retval,data);
02210   #else
02211     /* also for GNU GCC and Intel C/C++ versions */
02212     i = amx_exec_run(amx,retval,data);
02213   #endif
02214   if (i == AMX_ERR_SLEEP) {
02215     amx->reset_stk=reset_stk;
02216     amx->reset_hea=reset_hea;
02217   } else {
02218     /* remove parameters from the stack; do this the "hard" way, because
02219      * the assembler version has no internal knowledge of the local
02220      * variables, so any "clean" way would be a kludge anyway.
02221      */
02222     amx->stk=reset_stk;
02223     amx->hea=reset_hea;
02224   } /* if */
02225   return i;
02226 
02227 #else
02228 
02229   #define CHKMARGIN()   if (hea+STKMARGIN>stk) return AMX_ERR_STACKERR
02230   #define CHKSTACK()    if (stk>amx->stp) return AMX_ERR_STACKLOW
02231   #define CHKHEAP()     if (hea<amx->hlw) return AMX_ERR_HEAPLOW
02232 
02233   /* PUSH() and POP() are defined in terms of the _R() and _W() macros */
02234   #define PUSH(v)       ( stk-=sizeof(cell), _W(data,stk,v) )
02235   #define POP(v)        ( v=_R(data,stk), stk+=sizeof(cell) )
02236 
02237   /* set up registers for ANSI-C core: pri, alt, frm, cip, hea, stk */
02238   pri=amx->pri;
02239   alt=amx->alt;
02240   frm=amx->frm;
02241   cip=(cell *)(amx->code+(int)amx->cip);
02242   hea=amx->hea;
02243   stk=amx->stk;
02244 
02245   /* start running */
02246   for ( ;; ) {
02247     op=_RCODE();
02248     switch (GETOPCODE(op)) {
02249     /* core instruction set */
02250     case OP_NOP:
02251       break;
02252     case OP_LOAD_PRI:
02253       GETPARAM(offs);
02254       pri=_R(data,offs);
02255       break;
02256     case OP_LOAD_ALT:
02257       GETPARAM(offs);
02258       alt=_R(data,offs);
02259       break;
02260     case OP_LOAD_S_PRI:
02261       GETPARAM(offs);
02262       pri=_R(data,frm+offs);
02263       break;
02264     case OP_LOAD_S_ALT:
02265       GETPARAM(offs);
02266       alt=_R(data,frm+offs);
02267       break;
02268     case OP_LREF_S_PRI:
02269       GETPARAM(offs);
02270       offs=_R(data,frm+offs);
02271       pri=_R(data,offs);
02272       break;
02273     case OP_LREF_S_ALT:
02274       GETPARAM(offs);
02275       offs=_R(data,frm+offs);
02276       alt=_R(data,offs);
02277       break;
02278     case OP_LOAD_I:
02279       /* verify address */
02280       if (pri>=hea && pri<stk || (ucell)pri>=(ucell)amx->stp)
02281         ABORT(amx,AMX_ERR_MEMACCESS);
02282       pri=_R(data,pri);
02283       break;
02284     case OP_LODB_I:
02285       GETPARAM(offs);
02286     __lodb_i:
02287       /* verify address */
02288       if (pri>=hea && pri<stk || (ucell)pri>=(ucell)amx->stp)
02289         ABORT(amx,AMX_ERR_MEMACCESS);
02290       switch ((int)offs) {
02291       case 1:
02292         pri=_R8(data,pri);
02293         break;
02294       case 2:
02295         pri=_R16(data,pri);
02296         break;
02297       case 4:
02298         pri=_R32(data,pri);
02299         break;
02300       } /* switch */
02301       break;
02302     case OP_CONST_PRI:
02303       GETPARAM(pri);
02304       break;
02305     case OP_CONST_ALT:
02306       GETPARAM(alt);
02307       break;
02308     case OP_ADDR_PRI:
02309       GETPARAM(pri);
02310       pri+=frm;
02311       break;
02312     case OP_ADDR_ALT:
02313       GETPARAM(alt);
02314       alt+=frm;
02315       break;
02316     case OP_STOR:
02317       GETPARAM(offs);
02318       _W(data,offs,pri);
02319       break;
02320     case OP_STOR_S:
02321       GETPARAM(offs);
02322       _W(data,frm+offs,pri);
02323       break;
02324     case OP_SREF_S:
02325       GETPARAM(offs);
02326       offs=_R(data,frm+offs);
02327       _W(data,offs,pri);
02328       break;
02329     case OP_STOR_I:
02330       /* verify address */
02331       if (alt>=hea && alt<stk || (ucell)alt>=(ucell)amx->stp)
02332         ABORT(amx,AMX_ERR_MEMACCESS);
02333       _W(data,alt,pri);
02334       break;
02335     case OP_STRB_I:
02336       GETPARAM(offs);
02337     __strb_i:
02338       /* verify address */
02339       if (alt>=hea && alt<stk || (ucell)alt>=(ucell)amx->stp)
02340         ABORT(amx,AMX_ERR_MEMACCESS);
02341       switch ((int)offs) {
02342       case 1:
02343         _W8(data,alt,pri);
02344         break;
02345       case 2:
02346         _W16(data,alt,pri);
02347         break;
02348       case 4:
02349         _W32(data,alt,pri);
02350         break;
02351       } /* switch */
02352       break;
02353     case OP_ALIGN_PRI:
02354       GETPARAM(offs);
02355       #if BYTE_ORDER==LITTLE_ENDIAN
02356         if ((size_t)offs<sizeof(cell))
02357           pri ^= sizeof(cell)-offs;
02358       #endif
02359       break;
02360     case OP_LCTRL:
02361       GETPARAM(offs);
02362       switch ((int)offs) {
02363       case 0:
02364         pri=hdr->cod;
02365         break;
02366       case 1:
02367         pri=hdr->dat;
02368         break;
02369       case 2:
02370         pri=hea;
02371         break;
02372       case 3:
02373         pri=amx->stp;
02374         break;
02375       case 4:
02376         pri=stk;
02377         break;
02378       case 5:
02379         pri=frm;
02380         break;
02381       case 6:
02382         pri=(cell)((unsigned char *)cip-amx->code);
02383         break;
02384       } /* switch */
02385       break;
02386     case OP_SCTRL:
02387       GETPARAM(offs);
02388       switch ((int)offs) {
02389       case 0:
02390       case 1:
02391       case 3:
02392         /* cannot change these parameters */
02393         break;
02394       case 2:
02395         hea=pri;
02396         break;
02397       case 4:
02398         stk=pri;
02399         break;
02400       case 5:
02401         frm=pri;
02402         break;
02403       case 6:
02404         cip=(cell *)(amx->code + (int)pri);
02405         break;
02406       } /* switch */
02407       break;
02408     case OP_XCHG:
02409       offs=pri;         /* offs is a temporary variable */
02410       pri=alt;
02411       alt=offs;
02412       break;
02413     case OP_PUSH_PRI:
02414       PUSH(pri);
02415       break;
02416     case OP_PUSH_ALT:
02417       PUSH(alt);
02418       break;
02419     case OP_PUSHR_PRI:
02420       PUSH(data+pri);
02421       break;
02422     case OP_POP_PRI:
02423       POP(pri);
02424       break;
02425     case OP_POP_ALT:
02426       POP(alt);
02427       break;
02428     case OP_PICK:
02429       GETPARAM(offs);
02430       pri=_R(data,stk+offs);
02431       break;
02432     case OP_STACK:
02433       GETPARAM(offs);
02434       alt=stk;
02435       stk+=offs;
02436       CHKMARGIN();
02437       CHKSTACK();
02438       break;
02439     case OP_HEAP:
02440       GETPARAM(offs);
02441       alt=hea;
02442       hea+=offs;
02443       CHKMARGIN();
02444       CHKHEAP();
02445       break;
02446     case OP_PROC:
02447       PUSH(frm);
02448       frm=stk;
02449       CHKMARGIN();
02450       break;
02451     case OP_RET:
02452       POP(frm);
02453       POP(offs);
02454       /* verify the return address */
02455       if ((long)offs>=amx->codesize)
02456         ABORT(amx,AMX_ERR_MEMACCESS);
02457       cip=(cell *)(amx->code+(int)offs);
02458       break;
02459     case OP_RETN:
02460       POP(frm);
02461       POP(offs);
02462       /* verify the return address */
02463       if ((long)offs>=amx->codesize)
02464         ABORT(amx,AMX_ERR_MEMACCESS);
02465       cip=(cell *)(amx->code+(int)offs);
02466       stk+=_R(data,stk)+sizeof(cell);   /* remove parameters from the stack */
02467       break;
02468     case OP_CALL:
02469       PUSH(((unsigned char *)cip-amx->code)+sizeof(cell));/* skip address */
02470       cip=JUMPREL(cip);                 /* jump to the address */
02471       break;
02472     case OP_JUMP:
02473       /* since the GETPARAM() macro modifies cip, you cannot
02474        * do GETPARAM(cip) directly */
02475       cip=JUMPREL(cip);
02476       break;
02477     case OP_JZER:
02478       if (pri==0)
02479         cip=JUMPREL(cip);
02480       else
02481         SKIPPARAM(1);
02482       break;
02483     case OP_JNZ:
02484       if (pri!=0)
02485         cip=JUMPREL(cip);
02486       else
02487         SKIPPARAM(1);
02488       break;
02489     case OP_SHL:
02490       pri<<=alt;
02491       break;
02492     case OP_SHR:
02493       pri=(ucell)pri >> (int)alt;
02494       break;
02495     case OP_SSHR:
02496       pri>>=alt;
02497       break;
02498     case OP_SHL_C_PRI:
02499       GETPARAM(offs);
02500       pri<<=offs;
02501       break;
02502     case OP_SHL_C_ALT:
02503       GETPARAM(offs);
02504       alt<<=offs;
02505       break;
02506     case OP_SMUL:
02507       pri*=alt;
02508       break;
02509     case OP_SDIV:
02510       if (pri==0)
02511         ABORT(amx,AMX_ERR_DIVIDE);
02512       /* use floored division and matching remainder */
02513       offs=pri;
02514       #if defined TRUNC_SDIV
02515         pri=alt/offs;
02516         alt=alt%offs;
02517       #else
02518         val=alt;                /* portable routine for truncated division */
02519         pri=IABS(alt)/IABS(offs);
02520         if ((cell)(val ^ offs)<0)
02521           pri=-pri;
02522         alt=val-pri*offs;       /* calculate the matching remainder */
02523       #endif
02524       /* now "fiddle" with the values to get floored division */
02525       if (alt!=0 && (cell)(alt ^ offs)<0) {
02526         pri--;
02527         alt+=offs;
02528       } /* if */
02529       break;
02530     case OP_ADD:
02531       pri+=alt;
02532       break;
02533     case OP_SUB:
02534       pri=alt-pri;
02535       break;
02536     case OP_AND:
02537       pri&=alt;
02538       break;
02539     case OP_OR:
02540       pri|=alt;
02541       break;
02542     case OP_XOR:
02543       pri^=alt;
02544       break;
02545     case OP_NOT:
02546       pri=!pri;
02547       break;
02548     case OP_NEG:
02549       pri=-pri;
02550       break;
02551     case OP_INVERT:
02552       pri=~pri;
02553       break;
02554     case OP_EQ:
02555       pri= pri==alt ? 1 : 0;
02556       break;
02557     case OP_NEQ:
02558       pri= pri!=alt ? 1 : 0;
02559       break;
02560     case OP_SLESS:
02561       pri= pri<alt ? 1 : 0;
02562       break;
02563     case OP_SLEQ:
02564       pri= pri<=alt ? 1 : 0;
02565       break;
02566     case OP_SGRTR:
02567       pri= pri>alt ? 1 : 0;
02568       break;
02569     case OP_SGEQ:
02570       pri= pri>=alt ? 1 : 0;
02571       break;
02572     case OP_INC_PRI:
02573       pri++;
02574       break;
02575     case OP_INC_ALT:
02576       alt++;
02577       break;
02578     case OP_INC_I:
02579       #if defined _R_DEFAULT
02580         *(cell *)(data+(int)pri) += 1;
02581       #else
02582         val=_R(data,pri);
02583         _W(data,pri,val+1);
02584       #endif
02585       break;
02586     case OP_DEC_PRI:
02587       pri--;
02588       break;
02589     case OP_DEC_ALT:
02590       alt--;
02591       break;
02592     case OP_DEC_I:
02593       #if defined _R_DEFAULT
02594         *(cell *)(data+(int)pri) -= 1;
02595       #else
02596         val=_R(data,pri);
02597         _W(data,pri,val-1);
02598       #endif
02599       break;
02600     case OP_MOVS:
02601       GETPARAM(offs);
02602     __movs:
02603       /* verify top & bottom memory addresses, for both source and destination
02604        * addresses
02605        */
02606       if (pri>=hea && pri<stk || (ucell)pri>=(ucell)amx->stp)
02607         ABORT(amx,AMX_ERR_MEMACCESS);
02608       if ((pri+offs)>hea && (pri+offs)<stk || (ucell)(pri+offs)>(ucell)amx->stp)
02609         ABORT(amx,AMX_ERR_MEMACCESS);
02610       if (alt>=hea && alt<stk || (ucell)alt>=(ucell)amx->stp)
02611         ABORT(amx,AMX_ERR_MEMACCESS);
02612       if ((alt+offs)>hea && (alt+offs)<stk || (ucell)(alt+offs)>(ucell)amx->stp)
02613         ABORT(amx,AMX_ERR_MEMACCESS);
02614       #if defined _R_DEFAULT
02615         memcpy(data+(int)alt, data+(int)pri, (int)offs);
02616       #else
02617         for (i=0; i+4<offs; i+=4) {
02618           val=_R32(data,pri+i);
02619           _W32(data,alt+i,val);
02620         } /* for */
02621         for ( ; i<offs; i++) {
02622           val=_R8(data,pri+i);
02623           _W8(data,alt+i,val);
02624         } /* for */
02625       #endif
02626       break;
02627     case OP_CMPS:
02628       GETPARAM(offs);
02629     __cmps:
02630       /* verify top & bottom memory addresses, for both source and destination
02631        * addresses
02632        */
02633       if (pri>=hea && pri<stk || (ucell)pri>=(ucell)amx->stp)
02634         ABORT(amx,AMX_ERR_MEMACCESS);
02635       if ((pri+offs)>hea && (pri+offs)<stk || (ucell)(pri+offs)>(ucell)amx->stp)
02636         ABORT(amx,AMX_ERR_MEMACCESS);
02637       if (alt>=hea && alt<stk || (ucell)alt>=(ucell)amx->stp)
02638         ABORT(amx,AMX_ERR_MEMACCESS);
02639       if ((alt+offs)>hea && (alt+offs)<stk || (ucell)(alt+offs)>(ucell)amx->stp)
02640         ABORT(amx,AMX_ERR_MEMACCESS);
02641       #if defined _R_DEFAULT
02642         pri=memcmp(data+(int)alt, data+(int)pri, (int)offs);
02643       #else
02644         pri=0;
02645         for (i=0; i+4<offs && pri==0; i+=4)
02646           pri=_R32(data,alt+i)-_R32(data,pri+i);
02647         for ( ; i<offs && pri==0; i++)
02648           pri=_R8(data,alt+i)-_R8(data,pri+i);
02649       #endif
02650       break;
02651     case OP_FILL:
02652       GETPARAM(offs);
02653     __fill:
02654       /* verify top & bottom memory addresses (destination only) */
02655       if (alt>=hea && alt<stk || (ucell)alt>=(ucell)amx->stp)
02656         ABORT(amx,AMX_ERR_MEMACCESS);
02657       if ((alt+offs)>hea && (alt+offs)<stk || (ucell)(alt+offs)>(ucell)amx->stp)
02658         ABORT(amx,AMX_ERR_MEMACCESS);
02659       for (i=(int)alt; (size_t)offs>=sizeof(cell); i+=sizeof(cell), offs-=sizeof(cell))
02660         _W32(data,i,pri);
02661       break;
02662     case OP_HALT:
02663       GETPARAM(offs);
02664     __halt:
02665       if (retval!=NULL)
02666         *retval=pri;
02667       /* store complete status (stk and hea are already set in the ABORT macro) */
02668       amx->frm=frm;
02669       amx->pri=pri;
02670       amx->alt=alt;
02671       amx->cip=(cell)((unsigned char*)cip-amx->code);
02672       if (offs==AMX_ERR_SLEEP) {
02673         amx->stk=stk;
02674         amx->hea=hea;
02675         amx->reset_stk=reset_stk;
02676         amx->reset_hea=reset_hea;
02677         return (int)offs;
02678       } /* if */
02679       ABORT(amx,(int)offs);
02680     case OP_BOUNDS:
02681       GETPARAM(offs);
02682       if ((ucell)pri>(ucell)offs) {
02683         amx->cip=(cell)((unsigned char *)cip-amx->code);
02684         ABORT(amx,AMX_ERR_BOUNDS);
02685       } /* if */
02686       break;
02687     case OP_SYSREQ:
02688       GETPARAM(offs);
02689       /* save a few registers */
02690       amx->cip=(cell)((unsigned char *)cip-amx->code);
02691       amx->hea=hea;
02692       amx->frm=frm;
02693       amx->stk=stk;
02694       i=amx->callback(amx,offs,&pri,(cell *)(data+(int)stk));
02695       if (i!=AMX_ERR_NONE) {
02696         if (i==AMX_ERR_SLEEP) {
02697           amx->pri=pri;
02698           amx->alt=alt;
02699           amx->reset_stk=reset_stk;
02700           amx->reset_hea=reset_hea;
02701           return i;
02702         } /* if */
02703         ABORT(amx,i);
02704       } /* if */
02705       break;
02706     case OP_SWITCH: {
02707       cell *cptr=JUMPREL(cip)+1;/* +1, to skip the "casetbl" opcode */
02708       assert(*JUMPREL(cip)==OP_CASETBL);
02709       cip=JUMPREL(cptr+1);      /* preset to "none-matched" case */
02710       i=(int)*cptr;             /* number of records in the case table */
02711       for (cptr+=2; i>0 && *cptr!=pri; i--,cptr+=2)
02712         /* nothing */;
02713       if (i>0)
02714         cip=JUMPREL(cptr+1);    /* case found */
02715       break;
02716     } /* case */
02717     case OP_SWAP_PRI:
02718       offs=_R(data,stk);
02719       _W32(data,stk,pri);
02720       pri=offs;
02721       break;
02722     case OP_SWAP_ALT:
02723       offs=_R(data,stk);
02724       _W32(data,stk,alt);
02725       alt=offs;
02726       break;
02727     case OP_BREAK:
02728       assert((amx->flags & AMX_FLAG_VERIFY)==0);
02729       if (amx->debug!=NULL) {
02730         /* store status */
02731         amx->frm=frm;
02732         amx->stk=stk;
02733         amx->hea=hea;
02734         amx->cip=(cell)((unsigned char*)cip-amx->code);
02735         i=amx->debug(amx);
02736         if (i!=AMX_ERR_NONE) {
02737           if (i==AMX_ERR_SLEEP) {
02738             amx->pri=pri;
02739             amx->alt=alt;
02740             amx->reset_stk=reset_stk;
02741             amx->reset_hea=reset_hea;
02742             return i;
02743           } /* if */
02744           ABORT(amx,i);
02745         } /* if */
02746       } /* if */
02747       break;
02748 #if !defined AMX_DONT_RELOCATE
02749     case OP_SYSREQ_D:    /* see SYSREQ */
02750       GETPARAM(offs);
02751       /* save a few registers */
02752       amx->cip=(cell)((unsigned char *)cip-amx->code);
02753       amx->hea=hea;
02754       amx->frm=frm;
02755       amx->stk=stk;
02756       pri=((AMX_NATIVE)offs)(amx,(cell *)(data+(int)stk));
02757       if (amx->error!=AMX_ERR_NONE) {
02758         if (amx->error==AMX_ERR_SLEEP) {
02759           amx->pri=pri;
02760           amx->alt=alt;
02761           amx->reset_stk=reset_stk;
02762           amx->reset_hea=reset_hea;
02763           return AMX_ERR_SLEEP;
02764         } /* if */
02765         ABORT(amx,amx->error);
02766       } /* if */
02767       break;
02768 #endif
02769 #if !defined AMX_NO_MACRO_INSTR && !defined AMX_DONT_RELOCATE
02770     case OP_SYSREQ_ND:    /* see SYSREQ_N */
02771       GETPARAM(offs);
02772       GETPARAM(val);
02773       PUSH(val);
02774       /* save a few registers */
02775       amx->cip=(cell)((unsigned char *)cip-amx->code);
02776       amx->hea=hea;
02777       amx->frm=frm;
02778       amx->stk=stk;
02779       pri=((AMX_NATIVE)offs)(amx,(cell *)(data+(int)stk));
02780       stk+=val+4;
02781       if (amx->error!=AMX_ERR_NONE) {
02782         if (amx->error==AMX_ERR_SLEEP) {
02783           amx->pri=pri;
02784           amx->alt=alt;
02785           amx->stk=stk;
02786           amx->reset_stk=reset_stk;
02787           amx->reset_hea=reset_hea;
02788           return AMX_ERR_SLEEP;
02789         } /* if */
02790         ABORT(amx,amx->error);
02791       } /* if */
02792       break;
02793 #endif
02794 
02795     /* overlay instructions */
02796 #if !defined AMX_NO_OVERLAY
02797     case OP_CALL_OVL:
02798       offs=(unsigned char *)cip-amx->code+sizeof(cell); /* skip address */
02799       assert(offs>=0 && offs<(1<<(sizeof(cell)*4)));
02800       PUSH((offs<<(sizeof(cell)*4)) | amx->ovl_index);
02801       amx->ovl_index=(int)*cip;
02802       assert(amx->overlay!=NULL);
02803       if ((i=amx->overlay(amx,amx->ovl_index))!=AMX_ERR_NONE)
02804         ABORT(amx,i);
02805       cip=(cell*)amx->code;
02806       break;
02807     case OP_RETN_OVL:
02808       assert(amx->overlay!=NULL);
02809       POP(frm);
02810       POP(offs);
02811       amx->ovl_index=offs & (((ucell)~0)>>4*sizeof(cell));
02812       offs=(ucell)offs >> (sizeof(cell)*4);
02813       /* verify the index */
02814       stk+=_R(data,stk)+sizeof(cell);   /* remove parameters from the stack */
02815       i=amx->overlay(amx,amx->ovl_index); /* reload overlay */
02816       if (i!=AMX_ERR_NONE || (long)offs>=amx->codesize)
02817         ABORT(amx,AMX_ERR_MEMACCESS);
02818       cip=(cell *)(amx->code+(int)offs);
02819       break;
02820     case OP_SWITCH_OVL: {
02821       cell *cptr=JUMPREL(cip)+1;  /* +1, to skip the "icasetbl" opcode */
02822       assert(*JUMPREL(cip)==OP_CASETBL_OVL);
02823       amx->ovl_index=*(cptr+1);   /* preset to "none-matched" case */
02824       i=(int)*cptr;               /* number of records in the case table */
02825       for (cptr+=2; i>0 && *cptr!=pri; i--,cptr+=2)
02826         /* nothing */;
02827       if (i>0)
02828         amx->ovl_index=*(cptr+1); /* case found */
02829       assert(amx->overlay!=NULL);
02830       if ((i=amx->overlay(amx,amx->ovl_index))!=AMX_ERR_NONE)
02831         ABORT(amx,i);
02832       cip=(cell*)amx->code;
02833       break;
02834     } /* case */
02835 #endif
02836 
02837     /* supplemental and macro instructions */
02838 #if !defined AMX_NO_MACRO_INSTR
02839     case OP_LIDX:
02840       offs=pri*sizeof(cell)+alt;
02841       /* verify address */
02842       if (offs>=hea && offs<stk || (ucell)offs>=(ucell)amx->stp)
02843         ABORT(amx,AMX_ERR_MEMACCESS);
02844       pri=_R(data,offs);
02845       break;
02846     case OP_LIDX_B:
02847       GETPARAM(offs);
02848       offs=(pri << (int)offs)+alt;
02849       /* verify address */
02850       if (offs>=hea && offs<stk || (ucell)offs>=(ucell)amx->stp)
02851         ABORT(amx,AMX_ERR_MEMACCESS);
02852       pri=_R(data,offs);
02853       break;
02854     case OP_IDXADDR:
02855       pri=pri*sizeof(cell)+alt;
02856       break;
02857     case OP_IDXADDR_B:
02858       GETPARAM(offs);
02859       pri=(pri << (int)offs)+alt;
02860       break;
02861     case OP_PUSH_C:
02862       GETPARAM(offs);
02863       PUSH(offs);
02864       break;
02865     case OP_PUSH:
02866       GETPARAM(offs);
02867       PUSH(_R(data,offs));
02868       break;
02869     case OP_PUSH_S:
02870       GETPARAM(offs);
02871       PUSH(_R(data,frm+offs));
02872       break;
02873     case OP_PUSH_ADR:
02874       GETPARAM(offs);
02875       PUSH(frm+offs);
02876       break;
02877     case OP_PUSHR_C:
02878       GETPARAM(offs);
02879       PUSH(data+offs);
02880       break;
02881     case OP_PUSHR_S:
02882       GETPARAM(offs);
02883       PUSH(data+_R(data,frm+offs));
02884       break;
02885     case OP_PUSHR_ADR:
02886       GETPARAM(offs);
02887       PUSH(data+frm+offs);
02888       break;
02889     case OP_JEQ:
02890       if (pri==alt)
02891         cip=JUMPREL(cip);
02892       else
02893         SKIPPARAM(1);
02894       break;
02895     case OP_JNEQ:
02896       if (pri!=alt)
02897         cip=JUMPREL(cip);
02898       else
02899         SKIPPARAM(1);
02900       break;
02901     case OP_JSLESS:
02902       if (pri<alt)
02903         cip=JUMPREL(cip);
02904       else
02905         SKIPPARAM(1);
02906       break;
02907     case OP_JSLEQ:
02908       if (pri<=alt)
02909         cip=JUMPREL(cip);
02910       else
02911         SKIPPARAM(1);
02912       break;
02913     case OP_JSGRTR:
02914       if (pri>alt)
02915         cip=JUMPREL(cip);
02916       else
02917         SKIPPARAM(1);
02918       break;
02919     case OP_JSGEQ:
02920       if (pri>=alt)
02921         cip=JUMPREL(cip);
02922       else
02923         SKIPPARAM(1);
02924       break;
02925     case OP_SDIV_INV:
02926       if (alt==0)
02927         ABORT(amx,AMX_ERR_DIVIDE);
02928       /* use floored division and matching remainder */
02929       offs=alt;
02930       #if defined TRUNC_SDIV
02931         pri=pri/offs;
02932         alt=pri%offs;
02933       #else
02934         val=pri;                /* portable routine for truncated division */
02935         pri=IABS(pri)/IABS(offs);
02936         if ((cell)(val ^ offs)<0)
02937           pri=-pri;
02938         alt=val-pri*offs;       /* calculate the matching remainder */
02939       #endif
02940       /* now "fiddle" with the values to get floored division */
02941       if (alt!=0 && (cell)(alt ^ offs)<0) {
02942         pri--;
02943         alt+=offs;
02944       } /* if */
02945       break;
02946     case OP_SUB_INV:
02947       pri-=alt;
02948       break;
02949     case OP_ADD_C:
02950       GETPARAM(offs);
02951       pri+=offs;
02952       break;
02953     case OP_SMUL_C:
02954       GETPARAM(offs);
02955       pri*=offs;
02956       break;
02957     case OP_ZERO_PRI:
02958       pri=0;
02959       break;
02960     case OP_ZERO_ALT:
02961       alt=0;
02962       break;
02963     case OP_ZERO:
02964       GETPARAM(offs);
02965       _W(data,offs,0);
02966       break;
02967     case OP_ZERO_S:
02968       GETPARAM(offs);
02969       _W(data,frm+offs,0);
02970       break;
02971     case OP_EQ_C_PRI:
02972       GETPARAM(offs);
02973       pri= pri==offs ? 1 : 0;
02974       break;
02975     case OP_EQ_C_ALT:
02976       GETPARAM(offs);
02977       pri= alt==offs ? 1 : 0;
02978       break;
02979     case OP_INC:
02980       GETPARAM(offs);
02981       #if defined _R_DEFAULT
02982         *(cell *)(data+(int)offs) += 1;
02983       #else
02984         val=_R(data,offs);
02985         _W(data,offs,val+1);
02986       #endif
02987       break;
02988     case OP_INC_S:
02989       GETPARAM(offs);
02990       #if defined _R_DEFAULT
02991         *(cell *)(data+(int)(frm+offs)) += 1;
02992       #else
02993         val=_R(data,frm+offs);
02994         _W(data,frm+offs,val+1);
02995       #endif
02996       break;
02997     case OP_DEC:
02998       GETPARAM(offs);
02999       #if defined _R_DEFAULT
03000         *(cell *)(data+(int)offs) -= 1;
03001       #else
03002         val=_R(data,offs);
03003         _W(data,offs,val-1);
03004       #endif
03005       break;
03006     case OP_DEC_S:
03007       GETPARAM(offs);
03008       #if defined _R_DEFAULT
03009         *(cell *)(data+(int)(frm+offs)) -= 1;
03010       #else
03011         val=_R(data,frm+offs);
03012         _W(data,frm+offs,val-1);
03013       #endif
03014       break;
03015     case OP_SYSREQ_N:
03016       GETPARAM(offs);
03017       GETPARAM(val);
03018       PUSH(val);
03019       /* save a few registers */
03020       amx->cip=(cell)((unsigned char *)cip-amx->code);
03021       amx->hea=hea;
03022       amx->frm=frm;
03023       amx->stk=stk;
03024       i=amx->callback(amx,offs,&pri,(cell *)(data+(int)stk));
03025       stk+=val+4;
03026       if (i!=AMX_ERR_NONE) {
03027         if (i==AMX_ERR_SLEEP) {
03028           amx->pri=pri;
03029           amx->alt=alt;
03030           amx->stk=stk;
03031           amx->reset_stk=reset_stk;
03032           amx->reset_hea=reset_hea;
03033           return i;
03034         } /* if */
03035         ABORT(amx,i);
03036       } /* if */
03037       break;
03038     case OP_PUSHM_C:
03039       GETPARAM(val);
03040       while (val--) {
03041         GETPARAM(offs);
03042         PUSH(offs);
03043       } /* while */
03044       break;
03045     case OP_PUSHM:
03046       GETPARAM(val);
03047       while (val--) {
03048         GETPARAM(offs);
03049         PUSH(_R(data,offs));
03050       } /* while */
03051       break;
03052     case OP_PUSHM_S:
03053       GETPARAM(val);
03054       while (val--) {
03055         GETPARAM(offs);
03056         PUSH(_R(data,frm+offs));
03057       } /* while */
03058       break;
03059     case OP_PUSHM_ADR:
03060       GETPARAM(val);
03061       while (val--) {
03062         GETPARAM(offs);
03063         PUSH(frm+offs);
03064       } /* while */
03065       break;
03066     case OP_PUSHRM_C:
03067       GETPARAM(val);
03068       while (val--) {
03069         GETPARAM(offs);
03070         PUSH(data+offs);
03071       } /* while */
03072       break;
03073     case OP_PUSHRM_S:
03074       GETPARAM(val);
03075       while (val--) {
03076         GETPARAM(offs);
03077         PUSH(data+_R(data,frm+offs));
03078       } /* while */
03079       break;
03080     case OP_PUSHRM_ADR:
03081       GETPARAM(val);
03082       while (val--) {
03083         GETPARAM(offs);
03084         PUSH(data+frm+offs);
03085       } /* while */
03086       break;
03087     case OP_LOAD2:
03088       GETPARAM(offs);
03089       pri=_R(data,offs);
03090       GETPARAM(offs);
03091       alt=_R(data,offs);
03092       break;
03093     case OP_LOAD2_S:
03094       GETPARAM(offs);
03095       pri=_R(data,frm+offs);
03096       GETPARAM(offs);
03097       alt=_R(data,frm+offs);
03098       break;
03099     case OP_CONST:
03100       GETPARAM(offs);
03101       GETPARAM(val);
03102       _W32(data,offs,val);
03103       break;
03104     case OP_CONST_S:
03105       GETPARAM(offs);
03106       GETPARAM(val);
03107       _W32(data,frm+offs,val);
03108       break;
03109 #endif  /* AMX_NO_MACRO_INSTR */
03110 
03111 #if !defined AMX_NO_PACKED_OPC
03112     case OP_LOAD_P_PRI:
03113       GETPARAM_P(offs,op);
03114       pri=_R(data,offs);
03115       break;
03116     case OP_LOAD_P_ALT:
03117       GETPARAM_P(offs,op);
03118       alt=_R(data,offs);
03119       break;
03120     case OP_LOAD_P_S_PRI:
03121       GETPARAM_P(offs,op);
03122       pri=_R(data,frm+offs);
03123       break;
03124     case OP_LOAD_P_S_ALT:
03125       GETPARAM_P(offs,op);
03126       alt=_R(data,frm+offs);
03127       break;
03128     case OP_LREF_P_S_PRI:
03129       GETPARAM_P(offs,op);
03130       offs=_R(data,frm+offs);
03131       pri=_R(data,offs);
03132       break;
03133     case OP_LREF_P_S_ALT:
03134       GETPARAM_P(offs,op);
03135       offs=_R(data,frm+offs);
03136       alt=_R(data,offs);
03137       break;
03138     case OP_LODB_P_I:
03139       GETPARAM_P(offs,op);
03140       goto __lodb_i;
03141     case OP_CONST_P_PRI:
03142       GETPARAM_P(pri,op);
03143       break;
03144     case OP_CONST_P_ALT:
03145       GETPARAM_P(alt,op);
03146       break;
03147     case OP_ADDR_P_PRI:
03148       GETPARAM_P(pri,op);
03149       pri+=frm;
03150       break;
03151     case OP_ADDR_P_ALT:
03152       GETPARAM_P(alt,op);
03153       alt+=frm;
03154       break;
03155     case OP_STOR_P:
03156       GETPARAM_P(offs,op);
03157       _W(data,offs,pri);
03158       break;
03159     case OP_STOR_P_S:
03160       GETPARAM_P(offs,op);
03161       _W(data,frm+offs,pri);
03162       break;
03163     case OP_SREF_P_S:
03164       GETPARAM_P(offs,op);
03165       offs=_R(data,frm+offs);
03166       _W(data,offs,pri);
03167       break;
03168     case OP_STRB_P_I:
03169       GETPARAM_P(offs,op);
03170       goto __strb_i;
03171     case OP_LIDX_P_B:
03172       GETPARAM_P(offs,op);
03173       offs=(pri << (int)offs)+alt;
03174       /* verify address */
03175       if (offs>=hea && offs<stk || (ucell)offs>=(ucell)amx->stp)
03176         ABORT(amx,AMX_ERR_MEMACCESS);
03177       pri=_R(data,offs);
03178       break;
03179     case OP_IDXADDR_P_B:
03180       GETPARAM_P(offs,op);
03181       pri=(pri << (int)offs)+alt;
03182       break;
03183     case OP_ALIGN_P_PRI:
03184       GETPARAM_P(offs,op);
03185       #if BYTE_ORDER==LITTLE_ENDIAN
03186         if ((size_t)offs<sizeof(cell))
03187           pri ^= sizeof(cell)-offs;
03188       #endif
03189       break;
03190     case OP_PUSH_P_C:
03191       GETPARAM_P(offs,op);
03192       PUSH(offs);
03193       break;
03194     case OP_PUSH_P:
03195       GETPARAM_P(offs,op);
03196       PUSH(_R(data,offs));
03197       break;
03198     case OP_PUSH_P_S:
03199       GETPARAM_P(offs,op);
03200       PUSH(_R(data,frm+offs));
03201       break;
03202     case OP_PUSH_P_ADR:
03203       GETPARAM_P(offs,op);
03204       PUSH(frm+offs);
03205       break;
03206     case OP_PUSHR_P_C:
03207       GETPARAM_P(offs,op);
03208       PUSH(data+offs);
03209       break;
03210     case OP_PUSHR_P_S:
03211       GETPARAM_P(offs,op);
03212       PUSH(data+_R(data,frm+offs));
03213       break;
03214     case OP_PUSHR_P_ADR:
03215       GETPARAM_P(offs,op);
03216       PUSH(data+frm+offs);
03217       break;
03218     case OP_PUSHM_P:
03219       GETPARAM_P(val,op);
03220       while (val--) {
03221         GETPARAM(offs);
03222         PUSH(_R(data,offs));
03223       } /* while */
03224       break;
03225     case OP_PUSHM_P_S:
03226       GETPARAM_P(val,op);
03227       while (val--) {
03228         GETPARAM(offs);
03229         PUSH(_R(data,frm+offs));
03230       } /* while */
03231       break;
03232     case OP_PUSHM_P_C:
03233       GETPARAM_P(val,op);
03234       while (val--) {
03235         GETPARAM(offs);
03236         PUSH(offs);
03237       } /* while */
03238       break;
03239     case OP_PUSHM_P_ADR:
03240       GETPARAM_P(val,op);
03241       while (val--) {
03242         GETPARAM(offs);
03243         PUSH(frm+offs);
03244       } /* while */
03245       break;
03246     case OP_PUSHRM_P_C:
03247       GETPARAM_P(val,op);
03248       while (val--) {
03249         GETPARAM(offs);
03250         PUSH(data+offs);
03251       } /* while */
03252       break;
03253     case OP_PUSHRM_P_S:
03254       GETPARAM_P(val,op);
03255       while (val--) {
03256         GETPARAM(offs);
03257         PUSH(data+_R(data,frm+offs));
03258       } /* while */
03259       break;
03260     case OP_PUSHRM_P_ADR:
03261       GETPARAM_P(val,op);
03262       while (val--) {
03263         GETPARAM(offs);
03264         PUSH(data+frm+offs);
03265       } /* while */
03266       break;
03267     case OP_STACK_P:
03268       GETPARAM_P(offs,op);
03269       alt=stk;
03270       stk+=offs;
03271       CHKMARGIN();
03272       CHKSTACK();
03273       break;
03274     case OP_HEAP_P:
03275       GETPARAM_P(offs,op);
03276       alt=hea;
03277       hea+=offs;
03278       CHKMARGIN();
03279       CHKHEAP();
03280       break;
03281     case OP_SHL_P_C_PRI:
03282       GETPARAM_P(offs,op);
03283       pri<<=offs;
03284       break;
03285     case OP_SHL_P_C_ALT:
03286       GETPARAM_P(offs,op);
03287       alt<<=offs;
03288       break;
03289     case OP_ADD_P_C:
03290       GETPARAM_P(offs,op);
03291       pri+=offs;
03292       break;
03293     case OP_SMUL_P_C:
03294       GETPARAM_P(offs,op);
03295       pri*=offs;
03296       break;
03297     case OP_ZERO_P:
03298       GETPARAM_P(offs,op);
03299       _W(data,offs,0);
03300       break;
03301     case OP_ZERO_P_S:
03302       GETPARAM_P(offs,op);
03303       _W(data,frm+offs,0);
03304       break;
03305     case OP_EQ_P_C_PRI:
03306       GETPARAM_P(offs,op);
03307       pri= pri==offs ? 1 : 0;
03308       break;
03309     case OP_EQ_P_C_ALT:
03310       GETPARAM_P(offs,op);
03311       pri= alt==offs ? 1 : 0;
03312       break;
03313     case OP_INC_P:
03314       GETPARAM_P(offs,op);
03315       #if defined _R_DEFAULT
03316         *(cell *)(data+(int)offs) += 1;
03317       #else
03318         val=_R(data,offs);
03319         _W(data,offs,val+1);
03320       #endif
03321       break;
03322     case OP_INC_P_S:
03323       GETPARAM_P(offs,op);
03324       #if defined _R_DEFAULT
03325         *(cell *)(data+(int)(frm+offs)) += 1;
03326       #else
03327         val=_R(data,frm+offs);
03328         _W(data,frm+offs,val+1);
03329       #endif
03330       break;
03331     case OP_DEC_P:
03332       GETPARAM_P(offs,op);
03333       #if defined _R_DEFAULT
03334         *(cell *)(data+(int)offs) -= 1;
03335       #else
03336         val=_R(data,offs);
03337         _W(data,offs,val-1);
03338       #endif
03339       break;
03340     case OP_DEC_P_S:
03341       GETPARAM_P(offs,op);
03342       #if defined _R_DEFAULT
03343         *(cell *)(data+(int)(frm+offs)) -= 1;
03344       #else
03345         val=_R(data,frm+offs);
03346         _W(data,frm+offs,val-1);
03347       #endif
03348       break;
03349     case OP_MOVS_P:
03350       GETPARAM_P(offs,op);
03351       goto __movs;
03352     case OP_CMPS_P:
03353       GETPARAM_P(offs,op);
03354       goto __cmps;
03355     case OP_FILL_P:
03356       GETPARAM_P(offs,op);
03357       goto __fill;
03358     case OP_HALT_P:
03359       GETPARAM_P(offs,op);
03360       goto __halt;
03361     case OP_BOUNDS_P:
03362       GETPARAM_P(offs,op);
03363       if ((ucell)pri>(ucell)offs) {
03364         amx->cip=(cell)((unsigned char *)cip-amx->code);
03365         ABORT(amx,AMX_ERR_BOUNDS);
03366       } /* if */
03367       break;
03368 #endif /* AMX_NO_PACKED_OPC */
03369     default:
03370       assert(0);  /* invalid instructions should already have been caught in VerifyPcode() */
03371       ABORT(amx,AMX_ERR_INVINSTR);
03372     } /* switch */
03373   } /* for */
03374 #endif /* AMX_ALTCORE */
03375 }
03376 
03377 #endif /* AMX_EXEC */
03378 
03379 #if defined AMX_SETCALLBACK
03380 int AMXAPI amx_SetCallback(AMX *amx,AMX_CALLBACK callback)
03381 {
03382   assert(amx!=NULL);
03383   assert(callback!=NULL);
03384   amx->callback=callback;
03385   return AMX_ERR_NONE;
03386 }
03387 #endif /* AMX_SETCALLBACK */
03388 
03389 #if defined AMX_SETDEBUGHOOK
03390 int AMXAPI amx_SetDebugHook(AMX *amx,AMX_DEBUG debug)
03391 {
03392   assert(amx!=NULL);
03393   amx->debug=debug;
03394   return AMX_ERR_NONE;
03395 }
03396 #endif /* AMX_SETDEBUGHOOK */
03397 
03398 #if defined AMX_RAISEERROR
03399 int AMXAPI amx_RaiseError(AMX *amx, int error)
03400 {
03401   assert(error>0);
03402   amx->error=error;
03403   return AMX_ERR_NONE;
03404 }
03405 #endif /* AMX_RAISEERROR */
03406 
03407 #if defined AMX_ALLOT || defined AMX_PUSHXXX
03408 int AMXAPI amx_Allot(AMX *amx,int cells,cell **address)
03409 {
03410   AMX_HEADER *hdr;
03411   unsigned char *data;
03412 
03413   assert(amx!=NULL);
03414   hdr=(AMX_HEADER *)amx->base;
03415   assert(hdr!=NULL);
03416   assert(hdr->magic==AMX_MAGIC);
03417   data=(amx->data!=NULL) ? amx->data : amx->base+(int)hdr->dat;
03418 
03419   if (amx->stk - amx->hea - cells*sizeof(cell) < STKMARGIN)
03420     return AMX_ERR_MEMORY;
03421   if (address!=NULL)
03422     *address=(cell *)(data+(int)amx->hea);
03423   amx->hea+=cells*sizeof(cell);
03424   return AMX_ERR_NONE;
03425 }
03426 
03427 int AMXAPI amx_Release(AMX *amx,cell *address)
03428 {
03429   AMX_HEADER *hdr;
03430   unsigned char *data;
03431   cell amx_addr;
03432 
03433   assert(amx!=NULL);
03434   hdr=(AMX_HEADER *)amx->base;
03435   assert(hdr!=NULL);
03436   assert(hdr->magic==AMX_MAGIC);
03437   data=(amx->data!=NULL) ? amx->data : amx->base+(int)hdr->dat;
03438   amx_addr=(cell)((unsigned char*)address-data);
03439   if (amx->hea>amx_addr)
03440     amx->hea=amx_addr;
03441   return AMX_ERR_NONE;
03442 }
03443 #endif /* AMX_ALLOT || AMX_PUSHXXX */
03444 
03445 #if defined AMX_XXXSTRING || defined AMX_UTF8XXX
03446 
03447 #define CHARBITS        (8*sizeof(char))
03448 #if PAWN_CELL_SIZE==16
03449   #define CHARMASK      (0xffffu << 8*(2-sizeof(char)))
03450 #elif PAWN_CELL_SIZE==32
03451   #define CHARMASK      (0xffffffffuL << 8*(4-sizeof(char)))
03452 #elif PAWN_CELL_SIZE==64
03453   #define CHARMASK      (0xffffffffffffffffuLL << 8*(8-sizeof(char)))
03454 #else
03455   #error Unsupported cell size
03456 #endif
03457 
03458 int AMXAPI amx_StrLen(const cell *cstr, int *length)
03459 {
03460   int len;
03461   #if BYTE_ORDER==LITTLE_ENDIAN
03462     cell c;
03463   #endif
03464 
03465   assert(length!=NULL);
03466   if (cstr==NULL) {
03467     *length=0;
03468     return AMX_ERR_PARAMS;
03469   } /* if */
03470 
03471   if ((ucell)*cstr>UNPACKEDMAX) {
03472     /* packed string */
03473     assert_static(sizeof(char)==1);
03474     len=strlen((char *)cstr);           /* find '\0' */
03475     assert(check_endian());
03476     #if BYTE_ORDER==LITTLE_ENDIAN
03477       /* on Little Endian machines, toggle the last bytes */
03478       c=cstr[len/sizeof(cell)];         /* get last cell */
03479       len=len - len % sizeof(cell);     /* len = multiple of "cell" bytes */
03480       while ((c & CHARMASK)!=0) {
03481         len++;
03482         c <<= 8*sizeof(char);
03483       } /* if */
03484     #endif
03485   } else {
03486     for (len=0; cstr[len]!=0; len++)
03487       /* nothing */;
03488   } /* if */
03489   *length = len;
03490   return AMX_ERR_NONE;
03491 }
03492 #endif
03493 
03494 #if defined AMX_XXXSTRING || defined AMX_PUSHXXX
03495 int AMXAPI amx_SetString(cell *dest,const char *source,int pack,int use_wchar,size_t size)
03496 {                 /* the memory blocks should not overlap */
03497   int len, i;
03498 
03499   assert_static(UNLIMITED>0);
03500   #if defined AMX_ANSIONLY
03501     (void)use_wchar;
03502     len=strlen(source);
03503   #else
03504     len= use_wchar ? wcslen((const wchar_t*)source) : strlen(source);
03505   #endif
03506   if (pack) {
03507     /* create a packed string */
03508     if (size<UNLIMITED/sizeof(cell) && (size_t)len>=size*sizeof(cell))
03509       len=size*sizeof(cell)-1;
03510     dest[len/sizeof(cell)]=0;   /* clear last bytes of last (semi-filled) cell*/
03511     #if defined AMX_ANSIONLY
03512       memcpy(dest,source,len);
03513     #else
03514       if (use_wchar) {
03515         for (i=0; i<len; i++)
03516           ((char*)dest)[i]=(char)(((wchar_t*)source)[i]);
03517       } else {
03518         memcpy(dest,source,len);
03519       } /* if */
03520     #endif
03521     /* On Big Endian machines, the characters are well aligned in the
03522      * cells; on Little Endian machines, we must swap all cells.
03523      */
03524     assert(check_endian());
03525     #if BYTE_ORDER==LITTLE_ENDIAN
03526       len /= sizeof(cell);
03527       while (len>=0)
03528         swapcell((ucell *)&dest[len--]);
03529     #endif
03530   } else {
03531     /* create an unpacked string */
03532     if (size<UNLIMITED && (size_t)len>=size)
03533       len=size-1;
03534     #if defined AMX_ANSIONLY
03535       for (i=0; i<len; i++)
03536         dest[i]=(cell)source[i];
03537     #else
03538       if (use_wchar) {
03539         for (i=0; i<len; i++)
03540           dest[i]=(cell)(((wchar_t*)source)[i]);
03541       } else {
03542         for (i=0; i<len; i++)
03543           dest[i]=(cell)source[i];
03544       } /* if */
03545     #endif
03546     dest[len]=0;
03547   } /* if */
03548   return AMX_ERR_NONE;
03549 }
03550 #endif
03551 
03552 #if defined AMX_XXXSTRING
03553 int AMXAPI amx_GetString(char *dest,const cell *source,int use_wchar,size_t size)
03554 {
03555   int len=0;
03556   #if defined AMX_ANSIONLY
03557     (void)use_wchar;    /* unused parameter (if ANSI only) */
03558   #endif
03559   if ((ucell)*source>UNPACKEDMAX) {
03560     /* source string is packed */
03561     cell c=0;           /* initialize to 0 to avoid a compiler warning */
03562     int i=sizeof(cell)-1;
03563     char ch;
03564     while ((size_t)len<size) {
03565       if (i==sizeof(cell)-1)
03566         c=*source++;
03567       ch=(char)(c >> i*CHARBITS);
03568       if (ch=='\0')
03569         break;          /* terminating zero character found */
03570       #if defined AMX_ANSIONLY
03571         dest[len++]=ch;
03572       #else
03573         if (use_wchar)
03574           ((wchar_t*)dest)[len++]=ch;
03575         else
03576           dest[len++]=ch;
03577       #endif
03578       i=(i+sizeof(cell)-1) % sizeof(cell);
03579     } /* while */
03580   } else {
03581     /* source string is unpacked */
03582     #if defined AMX_ANSIONLY
03583       while (*source!=0 && (size_t)len<size)
03584         dest[len++]=(char)*source++;
03585     #else
03586       if (use_wchar) {
03587         while (*source!=0 && (size_t)len<size)
03588           ((wchar_t*)dest)[len++]=(wchar_t)*source++;
03589       } else {
03590         while (*source!=0 && (size_t)len<size)
03591           dest[len++]=(char)*source++;
03592       } /* if */
03593     #endif
03594   } /* if */
03595   /* store terminator */
03596   if ((size_t)len>=size)
03597     len=size-1;
03598   if (len>=0) {
03599     #if defined AMX_ANSIONLY
03600       dest[len]='\0';
03601     #else
03602       if (use_wchar)
03603         ((wchar_t*)dest)[len]=0;
03604       else
03605         dest[len]='\0';
03606     #endif
03607   } /* IF */
03608   return AMX_ERR_NONE;
03609 }
03610 #endif /* AMX_XXXSTRING */
03611 
03612 #if defined AMX_UTF8XXX
03613   #if defined __BORLANDC__
03614     #pragma warn -amb -8000     /* ambiguous operators need parentheses */
03615   #endif
03616 /* amx_UTF8Get()
03617  * Extract a single UTF-8 encoded character from a string and return a pointer
03618  * to the character just behind that UTF-8 character. The parameters "endptr"
03619  * and "value" may be NULL.
03620  * If the code is not valid UTF-8, "endptr" has the value of the input
03621  * parameter "string" and "value" is zero.
03622  */
03623 int AMXAPI amx_UTF8Get(const char *string, const char **endptr, cell *value)
03624 {
03625 static const char utf8_count[16]={ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 4 };
03626 static const long utf8_lowmark[5] = { 0x80, 0x800, 0x10000L, 0x200000L, 0x4000000L };
03627   unsigned char c;
03628   cell result;
03629   int followup;
03630 
03631   assert(string!=NULL);
03632   if (value!=NULL)      /* preset, in case of an error */
03633     *value=0;
03634   if (endptr!=NULL)
03635     *endptr=string;
03636 
03637   c = *(const unsigned char*)string++;
03638   if (c<0x80) {
03639     /* ASCII */
03640     result=c;
03641   } else {
03642     if (c<0xc0 || c>=0xfe)
03643       return AMX_ERR_PARAMS;  /* invalid or "follower" code, quit with error */
03644     /* At this point we know that the two top bits of c are ones. The two
03645      * bottom bits are always part of the code. We only need to consider
03646      * the 4 remaining bits; i.e., a 16-byte table. This is "utf8_count[]".
03647      * (Actually the utf8_count[] table records the number of follow-up
03648      * bytes minus 1. This is just for convenience.)
03649      */
03650     assert((c & 0xc0)==0xc0);
03651     followup=(int)utf8_count[(c >> 2) & 0x0f];
03652     /* The mask depends on the code length; this is just a very simple
03653      * relation.
03654      */
03655     #define utf8_mask   (0x1f >> followup)
03656     result= c & utf8_mask;
03657     /* Collect the follow-up codes using a drop-through switch statement;
03658      * this avoids a loop. In each case, verify the two leading bits.
03659      */
03660     assert(followup>=0 && followup<=4);
03661     switch (followup) {
03662     case 4:
03663       if (((c=*string++) & 0xc0) != 0x80) goto error;
03664       result = (result << 6) | c & 0x3f;
03665     case 3:
03666       if (((c=*string++) & 0xc0) != 0x80) goto error;
03667       result = (result << 6) | c & 0x3f;
03668     case 2:
03669       if (((c=*string++) & 0xc0) != 0x80) goto error;
03670       result = (result << 6) | c & 0x3f;
03671     case 1:
03672       if (((c=*string++) & 0xc0) != 0x80) goto error;
03673       result = (result << 6) | c & 0x3f;
03674     case 0:
03675       if (((c=*string++) & 0xc0) != 0x80) goto error;
03676       result = (result << 6) | c & 0x3f;
03677     } /* switch */
03678     /* Do additional checks: shortest encoding & reserved positions. The
03679      * lowmark limits also depends on the code length; it can be read from
03680      * a table with 5 elements. This is "utf8_lowmark[]".
03681      */
03682     if (result<utf8_lowmark[followup])
03683       goto error;
03684     if (result>=0xd800 && result<=0xdfff || result==0xfffe || result==0xffff)
03685       goto error;
03686   } /* if */
03687 
03688   if (value!=NULL)
03689     *value=result;
03690   if (endptr!=NULL)
03691     *endptr=string;
03692 
03693   return AMX_ERR_NONE;
03694 
03695 error:
03696   return AMX_ERR_PARAMS;
03697 }
03698 
03699 /* amx_UTF8Put()
03700  * Encode a single character into a byte string. The character may result in
03701  * a string of up to 6 bytes. The function returns an error code if "maxchars"
03702  * is lower than the required number of characters; in this case nothing is
03703  * stored.
03704  * The function does not zero-terminate the string.
03705  */
03706 int AMXAPI amx_UTF8Put(char *string, char **endptr, int maxchars, cell value)
03707 {
03708   assert(string!=NULL);
03709   if (endptr!=NULL)     /* preset, in case of an error */
03710     *endptr=string;
03711 
03712   if (value<0x80) {
03713     /* 0xxxxxxx */
03714     if (maxchars < 1) goto error;
03715     *string++ = (char)value;
03716   } else if (value<0x800) {
03717     /* 110xxxxx 10xxxxxx */
03718     if (maxchars < 2) goto error;
03719     *string++ = (char)((value>>6) & 0x1f | 0xc0);
03720     *string++ = (char)(value & 0x3f | 0x80);
03721   } else if (value<0x10000) {
03722     /* 1110xxxx 10xxxxxx 10xxxxxx (16 bits, BMP plane) */
03723     if (maxchars < 3) goto error;
03724     if (value>=0xd800 && value<=0xdfff || value==0xfffe || value==0xffff)
03725       goto error;       /* surrogate pairs and invalid characters */
03726     *string++ = (char)((value>>12) & 0x0f | 0xe0);
03727     *string++ = (char)((value>>6) & 0x3f | 0x80);
03728     *string++ = (char)(value & 0x3f | 0x80);
03729   } else if (value<0x200000) {
03730     /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
03731     if (maxchars < 4) goto error;
03732     *string++ = (char)((value>>18) & 0x07 | 0xf0);
03733     *string++ = (char)((value>>12) & 0x3f | 0x80);
03734     *string++ = (char)((value>>6) & 0x3f | 0x80);
03735     *string++ = (char)(value & 0x3f | 0x80);
03736   } else if (value<0x4000000) {
03737     /* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */
03738     if (maxchars < 5) goto error;
03739     *string++ = (char)((value>>24) & 0x03 | 0xf8);
03740     *string++ = (char)((value>>18) & 0x3f | 0x80);
03741     *string++ = (char)((value>>12) & 0x3f | 0x80);
03742     *string++ = (char)((value>>6) & 0x3f | 0x80);
03743     *string++ = (char)(value & 0x3f | 0x80);
03744   } else {
03745     /* 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx (31 bits) */
03746     if (maxchars < 6) goto error;
03747     *string++ = (char)((value>>30) & 0x01 | 0xfc);
03748     *string++ = (char)((value>>24) & 0x3f | 0x80);
03749     *string++ = (char)((value>>18) & 0x3f | 0x80);
03750     *string++ = (char)((value>>12) & 0x3f | 0x80);
03751     *string++ = (char)((value>>6) & 0x3f | 0x80);
03752     *string++ = (char)(value & 0x3f | 0x80);
03753   } /* if */
03754 
03755   if (endptr!=NULL)
03756     *endptr=string;
03757   return AMX_ERR_NONE;
03758 
03759 error:
03760   return AMX_ERR_PARAMS;
03761 }
03762 
03763 /* amx_UTF8Check()
03764  * Run through a zero-terminated string and check the validity of the UTF-8
03765  * encoding. The function returns an error code, it is AMX_ERR_NONE if the
03766  * string is valid UTF-8 (or valid ASCII for that matter).
03767  */
03768 int AMXAPI amx_UTF8Check(const char *string, int *length)
03769 {
03770   int err=AMX_ERR_NONE;
03771   int len=0;
03772   while (err==AMX_ERR_NONE && *string!='\0') {
03773     err=amx_UTF8Get(string,&string,NULL);
03774     len++;
03775   } /* while */
03776   if (length!=NULL)
03777     *length=len;
03778   return err;
03779 }
03780 
03781 /* amx_UTF8Len()
03782  * Run through a wide string and return how many 8-bit characters are needed to
03783  * store the string in UTF-8 format. The returned cound excludes the terminating
03784  * zero byte. The function returns an error code.
03785  */
03786 int AMXAPI amx_UTF8Len(const cell *cstr, int *length)
03787 {
03788   int err;
03789 
03790   assert(length!=NULL);
03791   err=amx_StrLen(cstr, length);
03792   if (err==AMX_ERR_NONE && (ucell)*cstr<=UNPACKEDMAX) {
03793     char buffer[10];  /* maximum UTF-8 code is 6 characters */
03794     char *endptr;
03795     int len=*length, count=0;
03796     while (len-->0) {
03797       amx_UTF8Put(buffer, &endptr, sizeof buffer, *cstr++);
03798       count+=(int)(endptr-buffer);
03799     } /* while */
03800     *length=count;
03801   } /* while */
03802   return err;
03803 }
03804 #endif /* AMX_UTF8XXX */