/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.

This file is part of Quake III Arena source code.

Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.

Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/
#include "q_shared.h"
#include "qcommon.h"

#define	MAX_OPSTACK_SIZE	512
#define	PROC_OPSTACK_SIZE	30

#define VMMAIN_CALL_ARGS	13

// hardcoded in q3asm and reserved at end of bss
#define	PROGRAM_STACK_SIZE	0x10000

typedef enum {
	OP_UNDEF,

	OP_IGNORE,

	OP_BREAK,

	OP_ENTER,
	OP_LEAVE,
	OP_CALL,
	OP_PUSH,
	OP_POP,

	OP_CONST,
	OP_LOCAL,

	OP_JUMP,

	//-------------------

	OP_EQ,
	OP_NE,

	OP_LTI,
	OP_LEI,
	OP_GTI,
	OP_GEI,

	OP_LTU,
	OP_LEU,
	OP_GTU,
	OP_GEU,

	OP_EQF,
	OP_NEF,

	OP_LTF,
	OP_LEF,
	OP_GTF,
	OP_GEF,

	//-------------------

	OP_LOAD1,
	OP_LOAD2,
	OP_LOAD4,
	OP_STORE1,
	OP_STORE2,
	OP_STORE4,				// *(stack[top-1]) = stack[top]
	OP_ARG,

	OP_BLOCK_COPY,

	//-------------------

	OP_SEX8,
	OP_SEX16,

	OP_NEGI,
	OP_ADD,
	OP_SUB,
	OP_DIVI,
	OP_DIVU,
	OP_MODI,
	OP_MODU,
	OP_MULI,
	OP_MULU,

	OP_BAND,
	OP_BOR,
	OP_BXOR,
	OP_BCOM,

	OP_LSH,
	OP_RSHI,
	OP_RSHU,

	OP_NEGF,
	OP_ADDF,
	OP_SUBF,
	OP_DIVF,
	OP_MULF,

	OP_CVIF,
	OP_CVFI,

	OP_MAX
} opcode_t;

// macro opcode sequences
typedef enum {
	MOP_UNDEF = OP_MAX,
	MOP_IGNORE4,
	MOP_ADD4,
	MOP_SUB4,
	MOP_BAND4,
	MOP_BOR4,
	MOP_CALCF4,
} macro_op_t;

typedef struct {
	int		value;
	byte	op;
	byte	opStack;
	byte	jused;	// boolean
	byte	swtch;	// boolean
} instruction_t;

extern const char *opname[OP_MAX]; 

typedef int	vmptr_t;

typedef struct vmSymbol_s {
	struct vmSymbol_s	*next;
	int		symValue;
	int		profileCount;
	char	symName[1];		// variable sized
} vmSymbol_t;

typedef union vmFunc_u {
	byte		*ptr;
	void (*func)(void);
} vmFunc_t;

#define MAX_VM_CALL_STACK_DEPTH 64

struct vm_s {
	// DO NOT MOVE OR CHANGE THESE WITHOUT CHANGING THE VM_OFFSET_* DEFINES
	// USED BY THE ASM CODE
	int			programStack;		// the vm may be recursively entered
    syscall_t	systemCall;
	byte		*dataBase;
	int			*opStack;			// pointer to local function stack

	int			instructionCount;
	intptr_t	*instructionPointers;

	//------------------------------------

	const char	*name;

	// for dynamic linked modules
	void		*dllHandle;
	dllSyscall_t entryPoint;
	void		(*destroy)(vm_t* self);

	// for interpreted modules
	qboolean	currentlyInterpreting;

	qboolean	compiled;

	vmFunc_t	codeBase;
	int			codeLength;

	int			allocSize;			// total allocation size, in bytes

	int			dataMask;
	int			dataLength;			// exact data segment length

	int			stackBottom;		// if programStack < stackBottom, error
	int			*opStackTop;		

	int			numSymbols;
	vmSymbol_t	*symbols;

	int			callLevel;			// counts recursive VM_Call
	int			breakFunction;		// increment breakCount on function entry to this
	int			breakCount;

	byte		*jumpTableTargets;
	int			numJumpTableTargets;

	vmIndex_t	index;

	int			callStackDepth;
	int			lastCallStackDepth;
	int			callStackDepthTemp; // only for vm_x86.cpp
	int			callStack[MAX_VM_CALL_STACK_DEPTH];
};

extern	vm_t	*currentVM;

#define	VM_MAGIC		0x12721444
typedef struct {
	int		vmMagic;

	int		instructionCount;

	int		codeOffset;
	int		codeLength;

	int		dataOffset;
	int		dataLength;
	int		litLength;			// ( dataLength - litLength ) should be byteswapped on load
	int		bssLength;			// zero filled memory appended to datalength
} vmHeader_t;

qboolean VM_Compile( vm_t *vm, vmHeader_t *header );
int	VM_CallCompiled( vm_t *vm, int *args );

qboolean VM_PrepareInterpreter2( vm_t *vm, vmHeader_t *header );
int	VM_CallInterpreted2( vm_t *vm, int *args );

const char *VM_LoadInstructions( const vmHeader_t *header, instruction_t *buf );
const char *VM_CheckInstructions( instruction_t *buf, int instructionCount, 
								 const byte *jumpTableTargets, 
								 int numJumpTableTargets, 
								 int dataLength );

intptr_t VM_ArgPtr( intptr_t intValue );
intptr_t VM_ExplicitArgPtr( const vm_t* vm, intptr_t intValue );

static ID_INLINE float _vmf(intptr_t x)
{
	union {
		intptr_t l;
		float f;
	} t;
	t.l = x;
	return t.f;
}
#define VMF(x) _vmf(args[x])

#define VM_OF_JUMP	(1<<0)

typedef struct opcode_info_s 
{
	int   size; 
	int	  stack;
	int   nargs;
	int   flags;
} opcode_info_t ;

extern opcode_info_t ops[ OP_MAX ];