You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openmw-tes3mp/apps/openmw-mp/amx/amxexec_gcc.c

1362 lines
36 KiB
C

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