// Copyright (C) 2012 Zeex
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

#if defined OPCODE_INC
	#endinput
#endif
#define OPCODE_INC

#if __Pawn >= 0x30A
	// Disable the recursion warning in the Russian compiler.
	#pragma warning push
	#pragma warning disable 207
	#pragma disablerecursion
	#pragma warning pop
	
	// Disable the recursion warning in the fast compiler.
	#pragma warning disable 238
#endif

#define OPCODE_MAX_INSN_NAME 15

enum Opcode {
  OP_NONE,         OP_LOAD_PRI,     OP_LOAD_ALT,
  OP_LOAD_S_PRI,   OP_LOAD_S_ALT,   OP_LREF_PRI,
  OP_LREF_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_PRI,     OP_STOR_ALT,     OP_STOR_S_PRI,
  OP_STOR_S_ALT,   OP_SREF_PRI,     OP_SREF_ALT,
  OP_SREF_S_PRI,   OP_SREF_S_ALT,   OP_STOR_I,
  OP_STRB_I,       OP_LIDX,         OP_LIDX_B,
  OP_IDXADDR,      OP_IDXADDR_B,    OP_ALIGN_PRI,
  OP_ALIGN_ALT,    OP_LCTRL,        OP_SCTRL,
  OP_MOVE_PRI,     OP_MOVE_ALT,     OP_XCHG,
  OP_PUSH_PRI,     OP_PUSH_ALT,     OP_PUSH_R,
  OP_PUSH_C,       OP_PUSH,         OP_PUSH_S,
  OP_POP_PRI,      OP_POP_ALT,      OP_STACK,
  OP_HEAP,         OP_PROC,         OP_RET,
  OP_RETN,         OP_CALL,         OP_CALL_PRI,
  OP_JUMP,         OP_JREL,         OP_JZER,
  OP_JNZ,          OP_JEQ,          OP_JNEQ,
  OP_JLESS,        OP_JLEQ,         OP_JGRTR,
  OP_JGEQ,         OP_JSLESS,       OP_JSLEQ,
  OP_JSGRTR,       OP_JSGEQ,        OP_SHL,
  OP_SHR,          OP_SSHR,         OP_SHL_C_PRI,
  OP_SHL_C_ALT,    OP_SHR_C_PRI,    OP_SHR_C_ALT,
  OP_SMUL,         OP_SDIV,         OP_SDIV_ALT,
  OP_UMUL,         OP_UDIV,         OP_UDIV_ALT,
  OP_ADD,          OP_SUB,          OP_SUB_ALT,
  OP_AND,          OP_OR,           OP_XOR,
  OP_NOT,          OP_NEG,          OP_INVERT,
  OP_ADD_C,        OP_SMUL_C,       OP_ZERO_PRI,
  OP_ZERO_ALT,     OP_ZERO,         OP_ZERO_S,
  OP_SIGN_PRI,     OP_SIGN_ALT,     OP_EQ,
  OP_NEQ,          OP_LESS,         OP_LEQ,
  OP_GRTR,         OP_GEQ,          OP_SLESS,
  OP_SLEQ,         OP_SGRTR,        OP_SGEQ,
  OP_EQ_C_PRI,     OP_EQ_C_ALT,     OP_INC_PRI,
  OP_INC_ALT,      OP_INC,          OP_INC_S,
  OP_INC_I,        OP_DEC_PRI,      OP_DEC_ALT,
  OP_DEC,          OP_DEC_S,        OP_DEC_I,
  OP_MOVS,         OP_CMPS,         OP_FILL,
  OP_HALT,         OP_BOUNDS,       OP_SYSREQ_PRI,
  OP_SYSREQ_C,     OP_FILE,         OP_LINE,
  OP_SYMBOL,       OP_SRANGE,       OP_JUMP_PRI,
  OP_SWITCH,       OP_CASETBL,      OP_SWAP_PRI,
  OP_SWAP_ALT,     OP_PUSH_ADR,     OP_NOP,
  OP_SYSREQ_D,     OP_SYMTAG,       OP_BREAK,
  OP_LAST_
};

stock const gAMXOpcodeParameterCounts[] = {
	0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1,
	1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1,
	0, 1, 1, 0, 0, 1, 0, 1, 1, 0
};

stock const gAMXOpcodeNeedsReloc[] = {
	false, false, false, false, false, false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false, false, false, true , false, true ,
	false, true , true , true , true , true , true , true , true , true , true , true , true ,
	false, false, false, false, false, false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false, false, false, false, false, true ,
	true , false, false, false, false, false, false, false
};

stock const gAMXOpcodeNames[][OPCODE_MAX_INSN_NAME] = {
	"none", "load.pri", "load.alt", "load.s.pri", "load.s.alt", "lref.pri", "lref.alt",
	"lref.s.pri", "lref.s.alt", "load.i", "lodb.i", "const.pri", "const.alt", "addr.pri",
	"addr.alt", "stor.pri", "stor.alt", "stor.s.pri", "stor.s.alt", "sref.pri", "sref.alt",
	"sref.s.pri", "sref.s.alt", "stor.i", "strb.i", "lidx", "lidx.b", "idxaddr", "idxaddr.b",
	"align.pri", "align.alt", "lctrl", "sctrl", "move.pri", "move.alt", "xchg", "push.pri",
	"push.alt", "push.r", "push.c", "push", "push.s", "pop.pri", "pop.alt", "stack", "heap", "proc",
	"ret", "retn", "call", "call.pri", "jump", "jrel", "jzer", "jnz", "jeq", "jneq", "jless",
	"jleq", "jgrtr", "jgeq", "jsless", "jsleq", "jsgrtr", "jsgeq", "shl", "shr", "sshr",
	"shl.c.pri", "shl.c.alt", "shr.c.pri", "shr.c.alt", "smul", "sdiv", "sdiv.alt", "umul", "udiv",
	"udiv.alt", "add", "sub", "sub.alt", "and", "or", "xort", "not", "neg", "invert", "add.c",
	"smul.c", "zero.pri", "zero.alt", "zero", "zero.s", "sign.pri", "sign.alt", "eq", "neq", "less",
	"leq", "grtr", "geq", "sless", "sleq", "sgrtr", "sgeq", "eq.c.pri", "eq.c.alt", "inc.pri",
	"inc.alt", "inc", "inc.s", "inc.i", "dec.pri", "dec.alt", "dec", "dec.s", "dec.i", "movs",
	"cmps", "fill", "halt", "bounds", "sysreq.pri", "sysreq.c", "file", "line", "symbol", "srange",
	"jump.pri", "switch", "casetbl", "swap.pri", "swap.alt", "push.adr", "nop", "sysreq.d",
	"symtag", "break"
};

stock const gAMXOpcodeBaseSizes[] = {
	4, 8, 8, 8, 8, 8, 8, 8, 8, 4, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 4, 8, 4, 8, 4, 8, 8, 8, 8,
	8, 4, 4, 4, 4, 4, 8, 8, 8, 8, 4, 4, 8, 8, 4, 4, 4, 8, 4, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
	8, 4, 4, 4, 8, 8, 8, 8, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 4, 4, 8, 8, 4, 4, 4,
	4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 4, 4, 8, 8, 4, 4, 4, 8, 8, 4, 8, 8, 8, 8, 8, 4, 8, 8, 8, 8, 8,
	4, 8, 8, 4, 4, 8, 4, 8, 8, 4
};

const NUM_OPCODES = _:OP_LAST_;

static stock Opcode:ReadOpcodeNearThis(offset = 0) {
	new ret_addr = 0;

	// Get return address + COD - DAT + offset.
	#emit load.s.alt 4
	#emit lctrl 0
	#emit add
	#emit move.alt
	#emit lctrl 1
	#emit xchg
	#emit sub
	#emit load.s.alt offset
	#emit add

	#emit stor.s.pri ret_addr
	#emit lref.s.pri ret_addr

	#emit stack 4
	#emit retn

	return OP_NONE; // make compiler happy
}

static stock bool:HaveToRelocateOpcodes() {
	return ReadOpcodeNearThis(-8) != OP_CALL;
}

// Based on this idea: http://forum.sa-mp.com/showthread.php?t=358084
stock Opcode:RelocateOpcodeNow(Opcode:opcode) {
	if (!HaveToRelocateOpcodes()) {
		return opcode;
	}
	switch (opcode) {
		case OP_LOAD_PRI: {
			return ReadOpcodeNearThis(4);
			#emit load.pri 0
		}
		case OP_LOAD_ALT: {
			return ReadOpcodeNearThis(4);
			#emit load.alt 0
		}
		case OP_LOAD_S_PRI: {
			return ReadOpcodeNearThis(4);
			#emit load.s.pri 0
		}
		case OP_LOAD_S_ALT: {
			return ReadOpcodeNearThis(4);
			#emit load.s.alt 0
		}
		case OP_LREF_PRI: {
			return ReadOpcodeNearThis(4);
			#emit lref.pri 0
		}
		case OP_LREF_ALT: {
			return ReadOpcodeNearThis(4);
			#emit lref.alt 0
		}
		case OP_LREF_S_PRI: {
			return ReadOpcodeNearThis(4);
			#emit lref.s.pri 0
		}
		case OP_LREF_S_ALT: {
			return ReadOpcodeNearThis(4);
			#emit lref.s.alt 0
		}
		case OP_LOAD_I: {
			return ReadOpcodeNearThis(4);
			#emit load.i
		}
		case OP_LODB_I: {
			return ReadOpcodeNearThis(4);
			#emit lodb.i 1
		}
		case OP_CONST_PRI: {
			return ReadOpcodeNearThis(4);
			#emit const.pri 0
		}
		case OP_CONST_ALT: {
			return ReadOpcodeNearThis(4);
			#emit const.alt 0
		}
		case OP_ADDR_PRI: {
			return ReadOpcodeNearThis(4);
			#emit addr.pri 0
		}
		case OP_ADDR_ALT: {
			return ReadOpcodeNearThis(4);
			#emit addr.alt 0
		}
		case OP_STOR_PRI: {
			return ReadOpcodeNearThis(4);
			#emit stor.pri 0
		}
		case OP_STOR_ALT: {
			return ReadOpcodeNearThis(4);
			#emit stor.alt 0
		}
		case OP_STOR_S_PRI: {
			return ReadOpcodeNearThis(4);
			#emit stor.s.pri 0
		}
		case OP_STOR_S_ALT: {
			return ReadOpcodeNearThis(4);
			#emit stor.s.alt 0
		}
		case OP_SREF_PRI: {
			return ReadOpcodeNearThis(4);
			#emit sref.pri 0
		}
		case OP_SREF_ALT: {
			return ReadOpcodeNearThis(4);
			#emit sref.alt 0
		}
		case OP_SREF_S_PRI: {
			return ReadOpcodeNearThis(4);
			#emit sref.s.pri 0
		}
		case OP_SREF_S_ALT: {
			return ReadOpcodeNearThis(4);
			#emit sref.s.alt 0
		}
		case OP_STOR_I: {
			return ReadOpcodeNearThis(4);
			#emit stor.i
		}
		case OP_STRB_I: {
			return ReadOpcodeNearThis(4);
			#emit strb.i 1
		}
		case OP_LIDX: {
			return ReadOpcodeNearThis(4);
			#emit lidx
		}
		case OP_LIDX_B: {
			return ReadOpcodeNearThis(4);
			#emit lidx.b 0
		}
		case OP_IDXADDR: {
			return ReadOpcodeNearThis(4);
			#emit idxaddr
		}
		case OP_IDXADDR_B: {
			return ReadOpcodeNearThis(4);
			#emit idxaddr.b 0
		}
		case OP_ALIGN_PRI: {
			return ReadOpcodeNearThis(4);
			#emit align.pri 0
		}
		case OP_ALIGN_ALT: {
			return ReadOpcodeNearThis(4);
			#emit align.alt 0
		}
		case OP_LCTRL: {
			return ReadOpcodeNearThis(4);
			#emit lctrl 0
		}
		case OP_SCTRL: {
			return ReadOpcodeNearThis(4);
			#emit sctrl 0
		}
		case OP_MOVE_PRI: {
			return ReadOpcodeNearThis(4);
			#emit move.pri
		}
		case OP_MOVE_ALT: {
			return ReadOpcodeNearThis(4);
			#emit move.alt
		}
		case OP_XCHG: {
			return ReadOpcodeNearThis(4);
			#emit xchg
		}
		case OP_PUSH_PRI: {
			return ReadOpcodeNearThis(4);
			#emit push.pri
		}
		case OP_PUSH_ALT: {
			return ReadOpcodeNearThis(4);
			#emit push.alt
		}
		case OP_PUSH_C: {
			return ReadOpcodeNearThis(4);
			#emit push.c 0
		}
		case OP_PUSH: {
			return ReadOpcodeNearThis(4);
			#emit push 0
		}
		case OP_PUSH_S: {
			return ReadOpcodeNearThis(4);
			#emit push.s 0
		}
		case OP_POP_PRI: {
			return ReadOpcodeNearThis(4);
			#emit pop.pri
		}
		case OP_POP_ALT: {
			return ReadOpcodeNearThis(4);
			#emit pop.alt
		}
		case OP_STACK: {
			return ReadOpcodeNearThis(4);
			#emit stack 0
		}
		case OP_HEAP: {
			return ReadOpcodeNearThis(4);
			#emit heap 0
		}
		case OP_PROC: {
			return ReadOpcodeNearThis(4);
			#emit proc
		}
		case OP_RET: {
			return ReadOpcodeNearThis(4);
			#emit ret
		}
		case OP_RETN: {
			return ReadOpcodeNearThis(4);
			#emit retn
		}
		case OP_CALL: {
			// We can't do just "#emit call 0" - this will crash compiler for some reason (bug?).
			return ReadOpcodeNearThis(-8);
		}
		case OP_JUMP: {
			return ReadOpcodeNearThis(4);
			#emit jump 0
		}
		case OP_JZER: {
			return ReadOpcodeNearThis(4);
			#emit jzer 0
		}
		case OP_JNZ: {
			return ReadOpcodeNearThis(4);
			#emit jnz 0
		}
		case OP_JEQ: {
			return ReadOpcodeNearThis(4);
			#emit jeq 0
		}
		case OP_JNEQ: {
			return ReadOpcodeNearThis(4);
			#emit jneq 0
		}
		case OP_JLESS: {
			return ReadOpcodeNearThis(4);
			#emit jless 0
		}
		case OP_JLEQ: {
			return ReadOpcodeNearThis(4);
			#emit jleq 0
		}
		case OP_JGRTR: {
			return ReadOpcodeNearThis(4);
			#emit jgrtr 0
		}
		case OP_JGEQ: {
			return ReadOpcodeNearThis(4);
			#emit jgeq 0
		}
		case OP_JSLESS: {
			return ReadOpcodeNearThis(4);
			#emit jsless 0
		}
		case OP_JSLEQ: {
			return ReadOpcodeNearThis(4);
			#emit jsleq 0
		}
		case OP_JSGRTR: {
			return ReadOpcodeNearThis(4);
			#emit jsgrtr 0
		}
		case OP_JSGEQ: {
			return ReadOpcodeNearThis(4);
			#emit jsgeq 0
		}
		case OP_SHL: {
			return ReadOpcodeNearThis(4);
			#emit shl
		}
		case OP_SHR: {
			return ReadOpcodeNearThis(4);
			#emit shr
		}
		case OP_SSHR: {
			return ReadOpcodeNearThis(4);
			#emit sshr
		}
		case OP_SHL_C_PRI: {
			return ReadOpcodeNearThis(4);
			#emit shl.c.pri 0
		}
		case OP_SHL_C_ALT: {
			return ReadOpcodeNearThis(4);
			#emit shl.c.alt 0
		}
		case OP_SHR_C_PRI: {
			return ReadOpcodeNearThis(4);
			#emit shr.c.pri 0
		}
		case OP_SHR_C_ALT: {
			return ReadOpcodeNearThis(4);
			#emit shr.c.alt 0
		}
		case OP_SMUL: {
			return ReadOpcodeNearThis(4);
			#emit smul
		}
		case OP_SDIV: {
			return ReadOpcodeNearThis(4);
			#emit sdiv
		}
		case OP_SDIV_ALT: {
			return ReadOpcodeNearThis(4);
			#emit sdiv.alt
		}
		case OP_UMUL: {
			return ReadOpcodeNearThis(4);
			#emit umul
		}
		case OP_UDIV: {
			return ReadOpcodeNearThis(4);
			#emit udiv
		}
		case OP_UDIV_ALT: {
			return ReadOpcodeNearThis(4);
			#emit udiv.alt
		}
		case OP_ADD: {
			return ReadOpcodeNearThis(4);
			#emit add
		}
		case OP_SUB: {
			return ReadOpcodeNearThis(4);
			#emit sub
		}
		case OP_SUB_ALT: {
			return ReadOpcodeNearThis(4);
			#emit sub.alt
		}
		case OP_AND: {
			return ReadOpcodeNearThis(4);
			#emit and
		}
		case OP_OR: {
			return ReadOpcodeNearThis(4);
			#emit or
		}
		case OP_XOR: {
			return ReadOpcodeNearThis(4);
			#emit xor
		}
		case OP_NOT: {
			return ReadOpcodeNearThis(4);
			#emit not
		}
		case OP_NEG: {
			return ReadOpcodeNearThis(4);
			#emit neg
		}
		case OP_INVERT: {
			return ReadOpcodeNearThis(4);
			#emit invert
		}
		case OP_ADD_C: {
			return ReadOpcodeNearThis(4);
			#emit add.c 0
		}
		case OP_SMUL_C: {
			return ReadOpcodeNearThis(4);
			#emit smul.c 0
		}
		case OP_ZERO_PRI: {
			return ReadOpcodeNearThis(4);
			#emit zero.pri
		}
		case OP_ZERO_ALT: {
			return ReadOpcodeNearThis(4);
			#emit zero.alt
		}
		case OP_ZERO: {
			return ReadOpcodeNearThis(4);
			#emit zero 0
		}
		case OP_ZERO_S: {
			return ReadOpcodeNearThis(4);
			#emit zero.s 0
		}
		case OP_SIGN_PRI: {
			return ReadOpcodeNearThis(4);
			#emit sign.pri
		}
		case OP_SIGN_ALT: {
			return ReadOpcodeNearThis(4);
			#emit sign.alt
		}
		case OP_EQ: {
			return ReadOpcodeNearThis(4);
			#emit eq
		}
		case OP_NEQ: {
			return ReadOpcodeNearThis(4);
			#emit neq
		}
		case OP_LESS: {
			return ReadOpcodeNearThis(4);
			#emit less
		}
		case OP_LEQ: {
			return ReadOpcodeNearThis(4);
			#emit leq
		}
		case OP_GRTR: {
			return ReadOpcodeNearThis(4);
			#emit grtr
		}
		case OP_GEQ: {
			return ReadOpcodeNearThis(4);
			#emit geq
		}
		case OP_SLESS: {
			return ReadOpcodeNearThis(4);
			#emit sless
		}
		case OP_SLEQ: {
			return ReadOpcodeNearThis(4);
			#emit sleq
		}
		case OP_SGRTR: {
			return ReadOpcodeNearThis(4);
			#emit sgrtr
		}
		case OP_SGEQ: {
			return ReadOpcodeNearThis(4);
			#emit sgeq
		}
		case OP_EQ_C_PRI: {
			return ReadOpcodeNearThis(4);
			#emit eq.c.pri 0
		}
		case OP_EQ_C_ALT: {
			return ReadOpcodeNearThis(4);
			#emit eq.c.alt 0
		}
		case OP_INC_PRI: {
			return ReadOpcodeNearThis(4);
			#emit inc.pri
		}
		case OP_INC_ALT: {
			return ReadOpcodeNearThis(4);
			#emit inc.alt
		}
		case OP_INC: {
			return ReadOpcodeNearThis(4);
			#emit inc 0
		}
		case OP_INC_S: {
			return ReadOpcodeNearThis(4);
			#emit inc.s 0
		}
		case OP_INC_I: {
			return ReadOpcodeNearThis(4);
			#emit inc.i
		}
		case OP_DEC_PRI: {
			return ReadOpcodeNearThis(4);
			#emit dec.pri
		}
		case OP_DEC_ALT: {
			return ReadOpcodeNearThis(4);
			#emit dec.alt
		}
		case OP_DEC: {
			return ReadOpcodeNearThis(4);
			#emit dec 0
		}
		case OP_DEC_S: {
			return ReadOpcodeNearThis(4);
			#emit dec.s 0
		}
		case OP_DEC_I: {
			return ReadOpcodeNearThis(4);
			#emit dec.i
		}
		case OP_MOVS: {
			return ReadOpcodeNearThis(4);
			#emit movs 0
		}
		case OP_CMPS: {
			return ReadOpcodeNearThis(4);
			#emit cmps 0
		}
		case OP_FILL: {
			return ReadOpcodeNearThis(4);
			#emit fill 0
		}
		case OP_HALT: {
			return ReadOpcodeNearThis(4);
			#emit halt 0
		}
		case OP_BOUNDS: {
			return ReadOpcodeNearThis(4);
			#emit bounds 0
		}
		case OP_SYSREQ_C: {
			return ReadOpcodeNearThis(4);
			#emit sysreq.c 0
		}
		case OP_SWITCH: {
			static T = 1;
			#if debug > 0
				if (T) return ReadOpcodeNearThis(12);
			#else
				if (T) return ReadOpcodeNearThis(8);
			#endif
			#if ((__Pawn & 0x0F) >= 0x0A) || ((__Pawn & 0xF0) >= 0xA0)
				// Disable `switch control expression is constant`.
				#pragma warning push
				#pragma warning disable 243
			#endif
			switch (0) {
				case 0: {}
			}
			#if ((__Pawn & 0x0F) >= 0x0A) || ((__Pawn & 0xF0) >= 0xA0)
				#pragma warning pop
			#endif
		}
		case OP_CASETBL: {
			new x = 0;
			switch (x) { case 0: return ReadOpcodeNearThis(20); }
		}
		case OP_SWAP_PRI: {
			return ReadOpcodeNearThis(4);
			#emit swap.pri
		}
		case OP_SWAP_ALT: {
			return ReadOpcodeNearThis(4);
			#emit swap.alt
		}
		case OP_PUSH_ADR: {
			return ReadOpcodeNearThis(4);
			#emit push.adr 0
		}
		case OP_NOP: {
			return ReadOpcodeNearThis(4);
			#emit nop
		}
		case OP_SYSREQ_D: {
			// Since there's no way to #emit sysreq.d we have to compute its
			// address using a relative offset to another opcode, e.g. nop.
			new bool:is_crashdetect = false;
			#emit zero.pri
			#emit lctrl 0xFF
			#emit stor.s.pri is_crashdetect
			if (is_crashdetect) {
				// The offset is different when running under crashdetect as
				// it uses its own (modified) version of the VM. This value
				// value works only with crashdetect 4.13.
				return RelocateOpcodeNow(OP_NOP) - Opcode:0x1EA;
			} else {
				return RelocateOpcodeNow(OP_NOP) - Opcode:0x4F;
			}
		}
		case OP_BREAK: {
			return ReadOpcodeNearThis(4);
			#emit break
		}
	}
	return opcode;
}

static stock Opcode:opcode_table[NUM_OPCODES];
static stock bool:opcode_table_is_ready = false;

stock bool:IsOpcodeValid(Opcode:opcode) {
	return (OP_NONE <= opcode < Opcode:NUM_OPCODES);
}

static stock InitOpcodeTable() {
	for (new i = 0; i < NUM_OPCODES; i++) {
		opcode_table[i] = RelocateOpcodeNow(Opcode:i);
	}
	opcode_table_is_ready = true;
}

stock Opcode:RelocateOpcode(Opcode:opcode) {
	if (!opcode_table_is_ready) {
		InitOpcodeTable();
	}
	return opcode_table[_:opcode];
}

stock bool:IsOpcodeRelocationRequired() {
	return RelocateOpcode(OP_LOAD_PRI) != OP_LOAD_PRI;
}

stock Opcode:UnrelocateOpcode(Opcode:opcode) {
	if (!opcode_table_is_ready) {
		InitOpcodeTable();
	}
	if (OP_NONE <= opcode < Opcode:NUM_OPCODES) {
		return opcode;
	}
	for (new i = 0; i < NUM_OPCODES; i++) {
		if (opcode_table[i] == opcode) {
			return Opcode:i;
		}
	}
	return opcode;
}

stock GetOpcodeInstructionName(Opcode:opcode) {
	static const ret[OPCODE_MAX_INSN_NAME] = "none";
	if (OP_NONE < opcode < Opcode:NUM_OPCODES) {
		return gAMXOpcodeNames[_:opcode];
	}
	return ret;
}

stock GetOpcodeInstructionParameters(Opcode:opcode) {
	if (OP_NONE < opcode < Opcode:NUM_OPCODES) {
		return gAMXOpcodeParameterCounts[_:opcode];
	}
	return 0;
}

stock bool:GetOpcodeInstructionRelocatable(Opcode:opcode) {
	if (OP_NONE < opcode < Opcode:NUM_OPCODES) {
		return gAMXOpcodeNeedsReloc[_:opcode];
	}
	return false;
}

