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

Some instructions:

  • Put the attached include folder next to your source, so when you compile you get all the proper definitions
  • Use the attached main.p as a starting point if you wish
  • Compile your main.p into main.amx - Put your main.amx on the mbed 'drive'
  • Reset and be amazed.

Important Compile Notes:

  • You should use the -S# option to define a smaller default stack size. Start with -S64 and go up from there if needed.
  • To use on the Cortex-M0 version of the mbed (LPC11U24), you MUST include the TARGET=3 command-line option as well, so the pin names are properly defined. In the future this may be handled on the native code side.

Known Issues:

  • At the moment it appears the kbhit() function is not working right - at least on my mac. Will continue testing on Windows. Working fine.

Todo:

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