/*****************************************************************************
 * $Id: gfx_machine_defs.hh,v 1.3 2005/09/24 17:16:10 devel Exp $
 * @file
 * @author	Jim E. Brooks  http://www.jimbrooks.org
 * @brief	State-machine definitions.
 *//*
 * LEGAL:	COPYRIGHT (C) 2005 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE (GPL).
 *****************************************************************************/

#ifndef GFX_MACHINE_DEFS_HH
#define GFX_MACHINE_DEFS_HH 1

namespace gfx {

// Machine instructions enums for Machine::Opcode arrays.
enum
{
	OPC_0,			// 0,1 reserved in case compiler's scan encounters these as compileFlag=false,true
	OPC_1,
	OPC_INVALID = OPC_0,

	// User instructions:
	//
	// T/F means to execute instruction if FLAG is true/false.
	// Making instructions conditional can reduce the amount of instruction and increase speed.
	// The clever notion originated from the ARM architecture.
	OPC_FUNC,
	OPC_FUNC_T,
	OPC_FUNC_F,
	OPC_JUMP,		// -- OPC_JUMP* order must correlate to MNEM_JUMP* which Compile() depends --
	OPC_JUMP_T,
	OPC_JUMP_F,
	OPC_CALL,
	OPC_CALL_T,
	OPC_CALL_F,
	OPC_RETURN,
	OPC_RETURN_T,
	OPC_RETURN_F,
	OPC_SUSPEND,
	OPC_QUIT,

	// Mnemonics, placeholders, reserved instructions:
	OPC_NOP,
	OPC_NOP2,
	OPC_PROC_BEGIN,
	OPC_PROC_END,
	MNEM_LABEL,
	MNEM_RERUN,

	MNEM_JUMP,		// -- OPC_JUMP* order must correlate to MNEM_JUMP* which Compile() depends --
	MNEM_JUMP_T,
	MNEM_JUMP_F,
#define IF_A_MNEM_JUMP( OPC ) ( ((OPC) >= MNEM_JUMP) && ((OPC) <= MNEM_JUMP_F) )

	OPC_MAX_LEGAL,	// end of legal opcodes
};

// Opcode lengths (in elements).
enum
{
	OPC_FUNC_LEN		= 2,
	OPC_JUMP_LEN		= 2,
	OPC_CALL_LEN		= 2,
	OPC_RETURN_LEN		= 1,
	OPC_SUSPEND_LEN		= 1,
	OPC_QUIT_LEN		= 1,
	OPC_NOP_LEN			= 1,
	OPC_NOP2_LEN		= 2,	// NOP2 means two NOPs
	OPC_PROC_BEGIN_LEN	= 3,
	OPC_PROC_END_LEN	= 1,
	MNEM_LABEL_LEN		= 2,
	MNEM_JUMP_LEN		= OPC_JUMP_LEN,
	MNEM_RERUN_LEN		= 2,
};

// Index of opcode operands.
enum
{
	// { OPC_PROC_BEGIN, "procedureName", compileFlag }
	OPC_PROC_BEGIN_PROC_NAME_IDX		= 1,
	OPC_PROC_BEGIN_COMPILE_FLAG_IDX		= 2,
};

// Helpers to define instructions.
// Be careful with commas -- DON'T WRITE A TRAILING COMMA HERE!!
#define DEF_PROC_BEGIN(procNameStr)	OPC_PROC_BEGIN, ((Machine::Opcode)procNameStr), false
#define DEF_PROC_END()				OPC_PROC_END,
#define DEF_LABEL(labelStr)			MNEM_LABEL,		((Machine::Opcode)labelStr)
// Call a C function.
#define DEF_FUNC(func)				OPC_FUNC,   ((Machine::Opcode)func)
#define DEF_FUNC_T(func)			OPC_FUNC_T,	((Machine::Opcode)func)
#define DEF_FUNC_F(func)			OPC_FUNC_F,	((Machine::Opcode)func)
// Jump inside same procedure.
#define DEF_JUMP(labelStr)			MNEM_JUMP,   ((Machine::Opcode)labelStr)
#define DEF_JUMP_T(labelStr)		MNEM_JUMP_T, ((Machine::Opcode)labelStr)
#define DEF_JUMP_F(labelStr)		MNEM_JUMP_F, ((Machine::Opcode)labelStr)
// Jump to different procedure.
#define DEF_JUMP_PROC(smproc)		OPC_JUMP,   ((Machine::Opcode)smproc)
#define DEF_JUMP_T_PROC(smproc)		OPC_JUMP_T, ((Machine::Opcode)smproc)
#define DEF_JUMP_F_PROC(smproc)		OPC_JUMP_F, ((Machine::Opcode)smproc)
// Call a procedure.
#define DEF_CALL(smproc)			OPC_CALL,   ((Machine::Opcode)smproc)
#define DEF_CALL_T(smproc)			OPC_CALL_T, ((Machine::Opcode)smproc)
#define DEF_CALL_F(smproc)			OPC_CALL_F, ((Machine::Opcode)smproc)
// Rerun the same procedure.
#define DEF_RERUN()					MNEM_RERUN, 0

// Helper macro.
#define MACHINE_FUNC_PROTO(func)	extern bool func( Machine* machine );

// Helper macro to be expanded inside C functions which are called by state-machine.
// Such C functions typically get the user-data object (hence this macro).
// Example:
// bool DoAction( Machince* machine )
// {
//     MACHINE_FUNC( MyClass, MyMethod )
// }
#define MACHINE_C_FUNC( CLASS, METHOD )											\
{																				\
	CLASS* obj = reinterpret_cast<CLASS*>( machine->GetUserData() );			\
	return obj->METHOD();														\
}
#define MACHINE_C_FUNC_TRUE( CLASS, METHOD )									\
{																				\
	CLASS* obj = reinterpret_cast<CLASS*>( machine->GetUserData() );			\
	obj->METHOD();																\
	return true;																\
}

} // namespace gfx

#endif // GFX_MACHINE_DEFS_HH
