/*  Example implementation of an optimized abstract machine.
 *
 *  This abstract machine uses the "labels as values" feature of the GNU GCC
 *  compiler to create a fast "indirect threaded" interpreter. The Intel C/C++
 *  compiler supports this too.
 *
 *  Copyright (c) ITB CompuPhase, 1998-2013
 *
 *  This software is provided "as-is", without any express or implied warranty.
 *  In no event will the authors be held liable for any damages arising from
 *  the use of this software.
 *
 *  Permission is granted to anyone to use this software for any purpose,
 *  including commercial applications, and to alter it and redistribute it
 *  freely, subject to the following restrictions:
 *
 *  1.  The origin of this software must not be misrepresented; you must not
 *      claim that you wrote the original software. If you use this software in
 *      a product, an acknowledgment in the product documentation would be
 *      appreciated but is not required.
 *  2.  Altered source versions must be plainly marked as such, and must not be
 *      misrepresented as being the original software.
 *  3.  This notice may not be removed or altered from any source distribution.
 *
 *  Version: $Id: amxcons.c 3821 2007-10-15 16:54:20Z thiadmer $
 */
#include <assert.h>
#include <stddef.h>
#include <string.h>
#include "osdefs.h"
#if defined __ECOS__
  /* eCos puts include files in cyg/package_name */
  #include <cyg/pawn/amx.h>
#else
  #include "amx.h"
#endif

#if !(defined __GNUC__ || defined __ICC)
  #error The GNU GCC or the Intel C/C++ compiler is required for this file.
#endif

#if !defined sizearray
  #define sizearray(a)  (sizeof(a)/sizeof((a)[0]))
#endif

/* 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

#if !defined GETPARAM
  #define GETPARAM(v)   ( v=*cip++ )  /* read a parameter from the opcode stream */
#endif
#if !defined GETPARAM_P
  #define GETPARAM_P(v,o) ( v=((cell)(o) >> (int)(sizeof(cell)*4)) )
#endif
#if !defined SKIPPARAM
  #define SKIPPARAM(n)  ( cip=(cell *)cip+(n) ) /* for obsolete opcodes */
#endif

/* 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) )

#define ABORT(amx,v)    { (amx)->stk=reset_stk; (amx)->hea=reset_hea; return v; }

#define STKMARGIN       ((cell)(16*sizeof(cell)))
#define CHKMARGIN()     if (hea+STKMARGIN>stk) return AMX_ERR_STACKERR
#define CHKSTACK()      if (stk>amx->stp) return AMX_ERR_STACKLOW
#define CHKHEAP()       if (hea<amx->hlw) return AMX_ERR_HEAPLOW

#define JUMPREL(ip)     ((cell*)((unsigned long)(ip)+*(cell*)(ip)-sizeof(cell)))


#if !defined AMX_NO_PACKED_OPC && !defined AMX_TOKENTHREADING
  #define AMX_TOKENTHREADING    /* packed opcodes require token threading */
#endif
#if defined AMX_TOKENTHREADING
  #if defined AMX_NO_PACKED_OPC
    #define NEXT(cip,op) goto *amx_opcodelist[*cip++]
  #else
    #define NEXT(cip,op) goto *amx_opcodelist[(op=*cip++) & ((1 << sizeof(cell)*4)-1)]
  #endif
#else
  #if !defined AMX_NO_PACKED_OPC
    #error Packed opcodes support requires token threading
  #endif
  #define NEXT(cip,op)   goto **cip++
#endif

cell amx_exec_run(AMX *amx,cell *retval,unsigned char *data)
{
static const void * const amx_opcodelist[] = {
        /* core set */
        &&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 */
        /* if op_sysreq_d and/or op_sysreq_nd are not implemented, their entries
         * in this table must be NULL
         */
        &&op_sysreq_d,    &&op_sysreq_nd,
        /* overlay instructions */
        &&op_call_ovl,    &&op_retn_ovl,    &&op_switch_ovl,  &&op_casetbl_ovl,
        /* supplemental and macro instructions */
#if !defined AMX_NO_MACRO_INSTR
        &&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,       &&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
        &&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
};
  AMX_HEADER *hdr;
  cell pri,alt,stk,frm,hea;
  cell reset_stk, reset_hea, *cip;
  cell offs,val;
  int num,i;
  #if !defined AMX_NO_PACKED_OPC
    int op;
  #endif

  assert(amx!=NULL);
  /* HACK: return label table and opcode count (for VerifyPcode()) if amx
   * structure has the flags set to all ones (see amx_exec_list() above)
   */
  if (amx->flags==~0) {
    assert(sizeof(cell)==sizeof(void *));
    assert(data==NULL);
    assert(retval!=NULL);
    *retval=(cell)amx_opcodelist;
    return sizearray(amx_opcodelist);
  } /* if */

  /* set up the registers */
  hdr=(AMX_HEADER *)amx->base;
  assert(hdr->magic==AMX_MAGIC);
  assert(hdr->file_version>=11);
  cip=(cell*)amx->cip;
  hea=amx->hea;
  stk=amx->stk;
  reset_stk=stk;
  reset_hea=hea;
  alt=frm=pri=0;/* just to avoid compiler warnings */
  num=0;        /* just to avoid compiler warnings */

  /* start running */
  assert(amx->code!=NULL);
  assert(data!=NULL);
  cip=(cell *)(amx->code+(int)amx->cip);
  NEXT(cip,op);

  op_nop:
    NEXT(cip,op);
  op_load_pri:
    GETPARAM(offs);
    pri=_R(data,offs);
    NEXT(cip,op);
  op_load_alt:
    GETPARAM(offs);
    alt=_R(data,offs);
    NEXT(cip,op);
  op_load_s_pri:
    GETPARAM(offs);
    pri=_R(data,frm+offs);
    NEXT(cip,op);
  op_load_s_alt:
    GETPARAM(offs);
    alt=_R(data,frm+offs);
    NEXT(cip,op);
  op_lref_s_pri:
    GETPARAM(offs);
    offs=_R(data,frm+offs);
    pri=_R(data,offs);
    NEXT(cip,op);
  op_lref_s_alt:
    GETPARAM(offs);
    offs=_R(data,frm+offs);
    alt=_R(data,offs);
    NEXT(cip,op);
  op_load_i:
    /* verify address */
    if (pri>=hea && pri<stk || (ucell)pri>=(ucell)amx->stp)
      ABORT(amx,AMX_ERR_MEMACCESS);
    pri=_R(data,pri);
    NEXT(cip,op);
  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 (offs) {
    case 1:
      pri=_R8(data,pri);
      break;
    case 2:
      pri=_R16(data,pri);
      break;
    case 4:
      pri=_R32(data,pri);
      break;
    } /* switch */
    NEXT(cip,op);
  op_const_pri:
    GETPARAM(pri);
    NEXT(cip,op);
  op_const_alt:
    GETPARAM(alt);
    NEXT(cip,op);
  op_addr_pri:
    GETPARAM(pri);
    pri+=frm;
    NEXT(cip,op);
  op_addr_alt:
    GETPARAM(alt);
    alt+=frm;
    NEXT(cip,op);
  op_stor:
    GETPARAM(offs);
    _W(data,offs,pri);
    NEXT(cip,op);
  op_stor_s:
    GETPARAM(offs);
    _W(data,frm+offs,pri);
    NEXT(cip,op);
  op_sref_s:
    GETPARAM(offs);
    offs=_R(data,frm+offs);
    _W(data,offs,pri);
    NEXT(cip,op);
  op_stor_i:
    /* verify address */
    if (alt>=hea && alt<stk || (ucell)alt>=(ucell)amx->stp)
      ABORT(amx,AMX_ERR_MEMACCESS);
    _W(data,alt,pri);
    NEXT(cip,op);
  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 (offs) {
    case 1:
      _W8(data,alt,pri);
      break;
    case 2:
      _W16(data,alt,pri);
      break;
    case 4:
      _W32(data,alt,pri);
      break;
    } /* switch */
    NEXT(cip,op);
  op_align_pri:
    GETPARAM(offs);
    #if BYTE_ORDER==LITTLE_ENDIAN
      if (offs<(int)sizeof(cell))
        pri ^= sizeof(cell)-offs;
    #endif
    NEXT(cip,op);
  op_lctrl:
    GETPARAM(offs);
    switch (offs) {
    case 0:
      pri=hdr->cod;
      break;
    case 1:
      pri=hdr->dat;
      break;
    case 2:
      pri=hea;
      break;
    case 3:
      pri=amx->stp;
      break;
    case 4:
      pri=stk;
      break;
    case 5:
      pri=frm;
      break;
    case 6:
      pri=(cell)((unsigned char *)cip - amx->code);
      break;
    } /* switch */
    NEXT(cip,op);
  op_sctrl:
    GETPARAM(offs);
    switch (offs) {
    case 0:
    case 1:
    case 3:
      /* cannot change these parameters */
      break;
    case 2:
      hea=pri;
      break;
    case 4:
      stk=pri;
      break;
    case 5:
      frm=pri;
      break;
    case 6:
      cip=(cell *)(amx->code + (int)pri);
      break;
    } /* switch */
    NEXT(cip,op);
  op_xchg:
    offs=pri;         /* offs is a temporary variable */
    pri=alt;
    alt=offs;
    NEXT(cip,op);
  op_push_pri:
    PUSH(pri);
    NEXT(cip,op);
  op_push_alt:
    PUSH(alt);
    NEXT(cip,op);
  op_pushr_pri:
    PUSH(data+pri);
    NEXT(cip,op);
  op_pop_pri:
    POP(pri);
    NEXT(cip,op);
  op_pop_alt:
    POP(alt);
    NEXT(cip,op);
  op_pick:
    GETPARAM(offs);
    pri=_R(data,stk+offs);
    NEXT(cip,op);
  op_stack:
    GETPARAM(offs);
    alt=stk;
    stk+=offs;
    CHKMARGIN();
    CHKSTACK();
    NEXT(cip,op);
  op_heap:
    GETPARAM(offs);
    alt=hea;
    hea+=offs;
    CHKMARGIN();
    CHKHEAP();
    NEXT(cip,op);
  op_proc:
    PUSH(frm);
    frm=stk;
    CHKMARGIN();
    NEXT(cip,op);
  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);
    NEXT(cip,op);
  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 */
    NEXT(cip,op);
  op_call:
    PUSH(((unsigned char *)cip-amx->code)+sizeof(cell));/* push address behind instruction */
    cip=JUMPREL(cip);                   /* jump to the address */
    NEXT(cip,op);
  op_jump:
    /* since the GETPARAM() macro modifies cip, you cannot
     * do GETPARAM(cip) directly */
    cip=JUMPREL(cip);
    NEXT(cip,op);
  op_jzer:
    if (pri==0)
      cip=JUMPREL(cip);
    else
      SKIPPARAM(1);
    NEXT(cip,op);
  op_jnz:
    if (pri!=0)
      cip=JUMPREL(cip);
    else
      SKIPPARAM(1);
    NEXT(cip,op);
  op_shl:
    pri<<=alt;
    NEXT(cip,op);
  op_shr:
    pri=(ucell)pri >> (ucell)alt;
    NEXT(cip,op);
  op_sshr:
    pri>>=alt;
    NEXT(cip,op);
  op_shl_c_pri:
    GETPARAM(offs);
    pri<<=offs;
    NEXT(cip,op);
  op_shl_c_alt:
    GETPARAM(offs);
    alt<<=offs;
    NEXT(cip,op);
  op_smul:
    pri*=alt;
    NEXT(cip,op);
  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 */
    NEXT(cip,op);
  op_add:
    pri+=alt;
    NEXT(cip,op);
  op_sub:
    pri=alt-pri;
    NEXT(cip,op);
  op_and:
    pri&=alt;
    NEXT(cip,op);
  op_or:
    pri|=alt;
    NEXT(cip,op);
  op_xor:
    pri^=alt;
    NEXT(cip,op);
  op_not:
    pri=!pri;
    NEXT(cip,op);
  op_neg:
    pri=-pri;
    NEXT(cip,op);
  op_invert:
    pri=~pri;
    NEXT(cip,op);
  op_eq:
    pri= pri==alt ? 1 : 0;
    NEXT(cip,op);
  op_neq:
    pri= pri!=alt ? 1 : 0;
    NEXT(cip,op);
  op_sless:
    pri= pri<alt ? 1 : 0;
    NEXT(cip,op);
  op_sleq:
    pri= pri<=alt ? 1 : 0;
    NEXT(cip,op);
  op_sgrtr:
    pri= pri>alt ? 1 : 0;
    NEXT(cip,op);
  op_sgeq:
    pri= pri>=alt ? 1 : 0;
    NEXT(cip,op);
  op_inc_pri:
    pri++;
    NEXT(cip,op);
  op_inc_alt:
    alt++;
    NEXT(cip,op);
  op_inc_i:
    #if defined _R_DEFAULT
      *(cell *)(data+(int)pri) += 1;
    #else
      val=_R(data,pri);
      _W(data,pri,val+1);
    #endif
    NEXT(cip,op);
  op_dec_pri:
    pri--;
    NEXT(cip,op);
  op_dec_alt:
    alt--;
    NEXT(cip,op);
  op_dec_i:
    #if defined _R_DEFAULT
      *(cell *)(data+(int)pri) -= 1;
    #else
      val=_R(data,pri);
      _W(data,pri,val-1);
    #endif
    NEXT(cip,op);
  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
    NEXT(cip,op);
  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
    NEXT(cip,op);
  op_fill:
    GETPARAM(offs);
  __fill:
    /* verify top & bottom memory addresses */
    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; offs>=(int)sizeof(cell); i+=sizeof(cell), offs-=sizeof(cell))
      _W32(data,i,pri);
    NEXT(cip,op);
  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);
  op_bounds:
    GETPARAM(offs);
    if ((ucell)pri>(ucell)offs) {
      amx->cip=(cell)((unsigned char *)cip-amx->code);
      ABORT(amx,AMX_ERR_BOUNDS);
    } /* if */
    NEXT(cip,op);
  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;
    num=amx->callback(amx,offs,&pri,(cell *)(data+(int)stk));
    if (num!=AMX_ERR_NONE) {
      if (num==AMX_ERR_SLEEP) {
        amx->pri=pri;
        amx->alt=alt;
        amx->reset_stk=reset_stk;
        amx->reset_hea=reset_hea;
        return num;
      } /* if */
      ABORT(amx,num);
    } /* if */
    NEXT(cip,op);
  op_switch: {
    cell *cptr=JUMPREL(cip)+1;  /* +1, to skip the "casetbl" opcode */
    cip=JUMPREL(cptr+1);        /* preset to "none-matched" case */
    num=(int)*cptr;             /* number of records in the case table */
    for (cptr+=2; num>0 && *cptr!=pri; num--,cptr+=2)
      /* nothing */;
    if (num>0)
      cip=JUMPREL(cptr+1);      /* case found */
    NEXT(cip,op);
    }
  op_swap_pri:
    offs=_R(data,stk);
    _W(data,stk,pri);
    pri=offs;
    NEXT(cip,op);
  op_swap_alt:
    offs=_R(data,stk);
    _W(data,stk,alt);
    alt=offs;
    NEXT(cip,op);
  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);
      num=amx->debug(amx);
      if (num!=AMX_ERR_NONE) {
        if (num==AMX_ERR_SLEEP) {
          amx->pri=pri;
          amx->alt=alt;
          amx->reset_stk=reset_stk;
          amx->reset_hea=reset_hea;
          return num;
        } /* if */
        ABORT(amx,num);
      } /* if */
    } /* if */
    NEXT(cip,op);
  op_casetbl:
    assert(0);                  /* this should not occur during execution */
    ABORT(amx,AMX_ERR_INVINSTR);
  op_sysreq_d:          /* see op_sysreq */
    #if !defined AMX_DONT_RELOCATE
      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 */
      NEXT(cip,op);
    #else
      ABORT(amx,AMX_ERR_INVINSTR);
    #endif
  op_sysreq_nd:    /* see op_sysreq_n */
    #if !defined AMX_NO_MACRO_INSTR && !defined AMX_DONT_RELOCATE
      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 */
      NEXT(cip,op);
    #else
      ABORT(amx,AMX_ERR_INVINSTR);
    #endif

    /* overlay instructions */
#if !defined AMX_NO_OVERLAY
  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 ((num=amx->overlay(amx,amx->ovl_index))!=AMX_ERR_NONE)
      ABORT(amx,num);
    cip=(cell*)amx->code;
    NEXT(cip,op);
  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 */
    num=amx->overlay(amx,amx->ovl_index); /* reload overlay */
    if (num!=AMX_ERR_NONE || (long)offs>=amx->codesize)
      ABORT(amx,AMX_ERR_MEMACCESS);
    cip=(cell *)(amx->code+(int)offs);
    NEXT(cip,op);
  op_switch_ovl: {
    cell *cptr=JUMPREL(cip)+1;  /* +1, to skip the "icasetbl" opcode */
    amx->ovl_index=*(cptr+1);   /* preset to "none-matched" case */
    num=(int)*cptr;             /* number of records in the case table */
    for (cptr+=2; num>0 && *cptr!=pri; num--,cptr+=2)
      /* nothing */;
    if (num>0)
      amx->ovl_index=*(cptr+1); /* case found */
    assert(amx->overlay!=NULL);
    if ((num=amx->overlay(amx,amx->ovl_index))!=AMX_ERR_NONE)
      ABORT(amx,num);
    cip=(cell*)amx->code;
    NEXT(cip,op);
    }
#else
  op_call_ovl:
  op_retn_ovl:
  op_switch_ovl:
    ABORT(amx,AMX_ERR_INVINSTR);
#endif
  op_casetbl_ovl:
    assert(0);                  /* this should not occur during execution */
    ABORT(amx,AMX_ERR_INVINSTR);

    /* supplemental and macro instructions */
#if !defined AMX_NO_MACRO_INSTR
  op_lidx:
    offs=pri*sizeof(cell)+alt;  /* implicit shift value for a cell */
    /* verify address */
    if (offs>=hea && offs<stk || (ucell)offs>=(ucell)amx->stp)
      ABORT(amx,AMX_ERR_MEMACCESS);
    pri=_R(data,offs);
    NEXT(cip,op);
  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);
    NEXT(cip,op);
  op_idxaddr:
    pri=pri*sizeof(cell)+alt;
    NEXT(cip,op);
  op_idxaddr_b:
    GETPARAM(offs);
    pri=(pri << (int)offs)+alt;
    NEXT(cip,op);
  op_push_c:
    GETPARAM(offs);
    PUSH(offs);
    NEXT(cip,op);
  op_push:
    GETPARAM(offs);
    PUSH(_R(data,offs));
    NEXT(cip,op);
  op_push_s:
    GETPARAM(offs);
    PUSH(_R(data,frm+offs));
    NEXT(cip,op);
  op_push_adr:
    GETPARAM(offs);
    PUSH(frm+offs);
    NEXT(cip,op);
  op_pushr_c:
    GETPARAM(offs);
    PUSH(data+offs);
    NEXT(cip,op);
  op_pushr_s:
    GETPARAM(offs);
    PUSH(data+_R(data,frm+offs));
    NEXT(cip,op);
  op_pushr_adr:
    GETPARAM(offs);
    PUSH(data+frm+offs);
    NEXT(cip,op);
  op_jeq:
    if (pri==alt)
      cip=JUMPREL(cip);
    else
      SKIPPARAM(1);
    NEXT(cip,op);
  op_jneq:
    if (pri!=alt)
      cip=JUMPREL(cip);
    else
      SKIPPARAM(1);
    NEXT(cip,op);
  op_jsless:
    if (pri<alt)
      cip=JUMPREL(cip);
    else
      SKIPPARAM(1);
    NEXT(cip,op);
  op_jsleq:
    if (pri<=alt)
      cip=JUMPREL(cip);
    else
      SKIPPARAM(1);
    NEXT(cip,op);
  op_jsgrtr:
    if (pri>alt)
      cip=JUMPREL(cip);
    else
      SKIPPARAM(1);
    NEXT(cip,op);
  op_jsgeq:
    if (pri>=alt)
      cip=JUMPREL(cip);
    else
      SKIPPARAM(1);
    NEXT(cip,op);
  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 */
    NEXT(cip,op);
  op_sub_inv:
    pri-=alt;
    NEXT(cip,op);
  op_add_c:
    GETPARAM(offs);
    pri+=offs;
    NEXT(cip,op);
  op_smul_c:
    GETPARAM(offs);
    pri*=offs;
    NEXT(cip,op);
  op_zero_pri:
    pri=0;
    NEXT(cip,op);
  op_zero_alt:
    alt=0;
    NEXT(cip,op);
  op_zero:
    GETPARAM(offs);
    _W(data,offs,0);
    NEXT(cip,op);
  op_zero_s:
    GETPARAM(offs);
    _W(data,frm+offs,0);
    NEXT(cip,op);
  op_eq_c_pri:
    GETPARAM(offs);
    pri= pri==offs ? 1 : 0;
    NEXT(cip,op);
  op_eq_c_alt:
    GETPARAM(offs);
    pri= alt==offs ? 1 : 0;
    NEXT(cip,op);
  op_inc:
    GETPARAM(offs);
    #if defined _R_DEFAULT
      *(cell *)(data+(int)offs) += 1;
    #else
      val=_R(data,offs);
      _W(data,offs,val+1);
    #endif
    NEXT(cip,op);
  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
    NEXT(cip,op);
  op_dec:
    GETPARAM(offs);
    #if defined _R_DEFAULT
      *(cell *)(data+(int)offs) -= 1;
    #else
      val=_R(data,offs);
      _W(data,offs,val-1);
    #endif
    NEXT(cip,op);
  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
    NEXT(cip,op);
  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;
    num=amx->callback(amx,offs,&pri,(cell *)(data+(int)stk));
    stk+=val+4;
    if (num!=AMX_ERR_NONE) {
      if (num==AMX_ERR_SLEEP) {
        amx->pri=pri;
        amx->alt=alt;
        amx->stk=stk;
        amx->reset_stk=reset_stk;
        amx->reset_hea=reset_hea;
        return num;
      } /* if */
      ABORT(amx,num);
    } /* if */
    NEXT(cip,op);
  op_pushm_c:
    GETPARAM(val);
    while (val--) {
      GETPARAM(offs);
      PUSH(offs);
    } /* while */
    NEXT(cip,op);
  op_pushm:
    GETPARAM(val);
    while (val--) {
      GETPARAM(offs);
      PUSH(_R(data,offs));
    } /* while */
    NEXT(cip,op);
  op_pushm_s:
    GETPARAM(val);
    while (val--) {
      GETPARAM(offs);
      PUSH(_R(data,frm+offs));
    } /* while */
    NEXT(cip,op);
  op_pushm_adr:
    GETPARAM(val);
    while (val--) {
      GETPARAM(offs);
      PUSH(frm+offs);
    } /* while */
    NEXT(cip,op);
  op_pushrm_c:
    GETPARAM(val);
    while (val--) {
      GETPARAM(offs);
      PUSH(data+offs);
    } /* while */
    NEXT(cip,op);
  op_pushrm_s:
    GETPARAM(val);
    while (val--) {
      GETPARAM(offs);
      PUSH(data+_R(data,frm+offs));
    } /* while */
    NEXT(cip,op);
  op_pushrm_adr:
    GETPARAM(val);
    while (val--) {
      GETPARAM(offs);
      PUSH(data+frm+offs);
    } /* while */
    NEXT(cip,op);
  op_load2:
    GETPARAM(offs);
    pri=_R(data,offs);
    GETPARAM(offs);
    alt=_R(data,offs);
    NEXT(cip,op);
  op_load2_s:
    GETPARAM(offs);
    pri=_R(data,frm+offs);
    GETPARAM(offs);
    alt=_R(data,frm+offs);
    NEXT(cip,op);
  op_const:
    GETPARAM(offs);
    GETPARAM(val);
    _W(data,offs,val);
    NEXT(cip,op);
  op_const_s:
    GETPARAM(offs);
    GETPARAM(val);
    _W(data,frm+offs,val);
    NEXT(cip,op);
#endif

#if !defined AMX_NO_PACKED_OPC
  op_load_p_pri:
    GETPARAM_P(offs,op);
    pri=_R(data,offs);
    NEXT(cip,op);
  op_load_p_alt:
    GETPARAM_P(offs,op);
    alt=_R(data,offs);
    NEXT(cip,op);
  op_load_p_s_pri:
    GETPARAM_P(offs,op);
    pri=_R(data,frm+offs);
    NEXT(cip,op);
  op_load_p_s_alt:
    GETPARAM_P(offs,op);
    alt=_R(data,frm+offs);
    NEXT(cip,op);
  op_lref_p_s_pri:
    GETPARAM_P(offs,op);
    offs=_R(data,frm+offs);
    pri=_R(data,offs);
    NEXT(cip,op);
  op_lref_p_s_alt:
    GETPARAM_P(offs,op);
    offs=_R(data,frm+offs);
    alt=_R(data,offs);
    NEXT(cip,op);
  op_lodb_p_i:
    GETPARAM_P(offs,op);
    goto __lodb_i;
  op_const_p_pri:
    GETPARAM_P(pri,op);
    NEXT(cip,op);
  op_const_p_alt:
    GETPARAM_P(alt,op);
    NEXT(cip,op);
  op_addr_p_pri:
    GETPARAM_P(pri,op);
    pri+=frm;
    NEXT(cip,op);
  op_addr_p_alt:
    GETPARAM_P(alt,op);
    alt+=frm;
    NEXT(cip,op);
  op_stor_p:
    GETPARAM_P(offs,op);
    _W(data,offs,pri);
    NEXT(cip,op);
  op_stor_p_s:
    GETPARAM_P(offs,op);
    _W(data,frm+offs,pri);
    NEXT(cip,op);
  op_sref_p_s:
    GETPARAM_P(offs,op);
    offs=_R(data,frm+offs);
    _W(data,offs,pri);
    NEXT(cip,op);
  op_strb_p_i:
    GETPARAM_P(offs,op);
    goto __strb_i;
  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);
    NEXT(cip,op);
  op_idxaddr_p_b:
    GETPARAM_P(offs,op);
    pri=(pri << (int)offs)+alt;
    NEXT(cip,op);
  op_align_p_pri:
    GETPARAM_P(offs,op);
    #if BYTE_ORDER==LITTLE_ENDIAN
      if ((size_t)offs<sizeof(cell))
        pri ^= sizeof(cell)-offs;
    #endif
    NEXT(cip,op);
  op_push_p_c:
    GETPARAM_P(offs,op);
    PUSH(offs);
    NEXT(cip,op);
  op_push_p:
    GETPARAM_P(offs,op);
    PUSH(_R(data,offs));
    NEXT(cip,op);
  op_push_p_s:
    GETPARAM_P(offs,op);
    PUSH(_R(data,frm+offs));
    NEXT(cip,op);
  op_push_p_adr:
    GETPARAM_P(offs,op);
    PUSH(frm+offs);
    NEXT(cip,op);
  op_pushr_p_c:
    GETPARAM_P(offs,op);
    PUSH(data+offs);
    NEXT(cip,op);
  op_pushr_p_s:
    GETPARAM_P(offs,op);
    PUSH(data+_R(data,frm+offs));
    NEXT(cip,op);
  op_pushr_p_adr:
    GETPARAM_P(offs,op);
    PUSH(data+frm+offs);
    NEXT(cip,op);
  op_pushm_p_c:
    GETPARAM_P(val,op);
    while (val--) {
      GETPARAM(offs);
      PUSH(offs);
    } /* while */
    NEXT(cip,op);
  op_pushm_p:
    GETPARAM_P(val,op);
    while (val--) {
      GETPARAM(offs);
      PUSH(_R(data,offs));
    } /* while */
    NEXT(cip,op);
  op_pushm_p_s:
    GETPARAM_P(val,op);
    while (val--) {
      GETPARAM(offs);
      PUSH(_R(data,frm+offs));
    } /* while */
    NEXT(cip,op);
  op_pushm_p_adr:
    GETPARAM_P(val,op);
    while (val--) {
      GETPARAM(offs);
      PUSH(frm+offs);
    } /* while */
    NEXT(cip,op);
  op_pushrm_p_c:
    GETPARAM_P(val,op);
    while (val--) {
      GETPARAM(offs);
      PUSH(data+offs);
    } /* while */
    NEXT(cip,op);
  op_pushrm_p_s:
    GETPARAM_P(val,op);
    while (val--) {
      GETPARAM(offs);
      PUSH(data+_R(data,frm+offs));
    } /* while */
    NEXT(cip,op);
  op_pushrm_p_adr:
    GETPARAM_P(val,op);
    while (val--) {
      GETPARAM(offs);
      PUSH(data+frm+offs);
    } /* while */
    NEXT(cip,op);
  op_stack_p:
    GETPARAM_P(offs,op);
    alt=stk;
    stk+=offs;
    CHKMARGIN();
    CHKSTACK();
    NEXT(cip,op);
  op_heap_p:
    GETPARAM_P(offs,op);
    alt=hea;
    hea+=offs;
    CHKMARGIN();
    CHKHEAP();
    NEXT(cip,op);
  op_shl_p_c_pri:
    GETPARAM_P(offs,op);
    pri<<=offs;
    NEXT(cip,op);
  op_shl_p_c_alt:
    GETPARAM_P(offs,op);
    alt<<=offs;
    NEXT(cip,op);
  op_add_p_c:
    GETPARAM_P(offs,op);
    pri+=offs;
    NEXT(cip,op);
  op_smul_p_c:
    GETPARAM_P(offs,op);
    pri*=offs;
    NEXT(cip,op);
  op_zero_p:
    GETPARAM_P(offs,op);
    _W(data,offs,0);
    NEXT(cip,op);
  op_zero_p_s:
    GETPARAM_P(offs,op);
    _W(data,frm+offs,0);
    NEXT(cip,op);
  op_eq_p_c_pri:
    GETPARAM_P(offs,op);
    pri= pri==offs ? 1 : 0;
    NEXT(cip,op);
  op_eq_p_c_alt:
    GETPARAM_P(offs,op);
    pri= alt==offs ? 1 : 0;
    NEXT(cip,op);
  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
    NEXT(cip,op);
  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
    NEXT(cip,op);
  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
    NEXT(cip,op);
  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
    NEXT(cip,op);
  op_movs_p:
    GETPARAM_P(offs,op);
    goto __movs;
  op_cmps_p:
    GETPARAM_P(offs,op);
    goto __cmps;
  op_fill_p:
    GETPARAM_P(offs,op);
    goto __fill;
  op_halt_p:
    GETPARAM_P(offs,op);
    goto __halt;
  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 */
    NEXT(cip,op);
#endif
}

void amx_exec_list(const AMX *amx,const cell **opcodelist,int *numopcodes)
{
  /* since the opcode list of the GNU GCC version of the abstract machine core
   * must be a local variable (as it references code labels, which are local
   * too), we use a trick to get the opcode list: call amx_Exec() while a
   * special flags value is set in the AMX header.
   */
   int orgflags;
   AMX *amxptr=(AMX*)amx;

   assert(amx!=NULL);
   assert(opcodelist!=NULL);
   assert(numopcodes!=NULL);
   orgflags=amx->flags;
   amxptr->flags=~0;
   *numopcodes=amx_exec_run(amxptr, (cell*)opcodelist, NULL);
   amxptr->flags=orgflags;
}
