/*
 * RSP Compiler plug in for Project64 (A Nintendo 64 emulator).
 *
 * (c) Copyright 2001 jabo (jabo@emulation64.com) and
 * zilmar (zilmar@emulation64.com)
 *
 * pj64 homepage: www.pj64.net
 * 
 * Permission to use, copy, modify and distribute Project64 in both binary and
 * source form, for non-commercial purposes, is hereby granted without fee,
 * providing that this license information and copyright notice appear with
 * all copies and any derived work.
 *
 * This software is provided 'as-is', without any express or implied
 * warranty. In no event shall the authors be held liable for any damages
 * arising from the use of this software.
 *
 * Project64 is freeware for PERSONAL USE only. Commercial users should
 * seek permission of the copyright holders first. Commercial use includes
 * charging money for Project64 or software derived from Project64.
 *
 * The copyright holders request that bug fixes and improvements to the code
 * should be forwarded to them so if they want them.
 *
 */

#include <windows.h>
#include <stdio.h>
#include <float.h>
#include <stdlib.h>

#include "Rsp.h"
#include "Cpu.h"
#include "Interpreter CPU.h"
#include "Recompiler CPU.h"
#include "Recompiler Ops.h"
#include "RSP registers.h"
#include "RSP Command.h"
#include "memory.h"
#include "opcode.h"
#include "log.h"
#include "Profiling.h"
#include "x86.h"
#include "Types.h"

#pragma warning(disable : 4152) // nonstandard extension, function/data pointer conversion in expression

/* #define REORDER_BLOCK_VERBOSE */
#define LINK_BRANCHES_VERBOSE /* no choice really */
#define X86_RECOMP_VERBOSE
#define BUILD_BRANCHLABELS_VERBOSE

DWORD CompilePC, JumpTableSize, BlockID = 0;
DWORD dwBuffer = MainBuffer;
Boolean ChangedPC;

RSP_BLOCK CurrentBlock;
RSP_CODE RspCode;

BYTE * pLastSecondary = NULL, * pLastPrimary = NULL;

void BuildRecompilerCPU ( void ) {
	RSP_Opcode[ 0] = Compile_SPECIAL;
	RSP_Opcode[ 1] = Compile_REGIMM;
	RSP_Opcode[ 2] = Compile_J;
	RSP_Opcode[ 3] = Compile_JAL;
	RSP_Opcode[ 4] = Compile_BEQ;
	RSP_Opcode[ 5] = Compile_BNE;
	RSP_Opcode[ 6] = Compile_BLEZ;
	RSP_Opcode[ 7] = Compile_BGTZ;
	RSP_Opcode[ 8] = Compile_ADDI;
	RSP_Opcode[ 9] = Compile_ADDIU;
	RSP_Opcode[10] = Compile_SLTI;
	RSP_Opcode[11] = Compile_SLTIU;
	RSP_Opcode[12] = Compile_ANDI;
	RSP_Opcode[13] = Compile_ORI;
	RSP_Opcode[14] = Compile_XORI;
	RSP_Opcode[15] = Compile_LUI;
	RSP_Opcode[16] = Compile_COP0;
	RSP_Opcode[17] = Compile_UnknownOpcode;
	RSP_Opcode[18] = Compile_COP2;
	RSP_Opcode[19] = Compile_UnknownOpcode;
	RSP_Opcode[20] = Compile_UnknownOpcode;
	RSP_Opcode[21] = Compile_UnknownOpcode;
	RSP_Opcode[22] = Compile_UnknownOpcode;
	RSP_Opcode[23] = Compile_UnknownOpcode;
	RSP_Opcode[24] = Compile_UnknownOpcode;
	RSP_Opcode[25] = Compile_UnknownOpcode;
	RSP_Opcode[26] = Compile_UnknownOpcode;
	RSP_Opcode[27] = Compile_UnknownOpcode;
	RSP_Opcode[28] = Compile_UnknownOpcode;
	RSP_Opcode[29] = Compile_UnknownOpcode;
	RSP_Opcode[30] = Compile_UnknownOpcode;
	RSP_Opcode[31] = Compile_UnknownOpcode;
	RSP_Opcode[32] = Compile_LB;
	RSP_Opcode[33] = Compile_LH;
	RSP_Opcode[34] = Compile_UnknownOpcode;
	RSP_Opcode[35] = Compile_LW;
	RSP_Opcode[36] = Compile_LBU;
	RSP_Opcode[37] = Compile_LHU;
	RSP_Opcode[38] = Compile_UnknownOpcode;
	RSP_Opcode[39] = Compile_UnknownOpcode;
	RSP_Opcode[40] = Compile_SB;
	RSP_Opcode[41] = Compile_SH;
	RSP_Opcode[42] = Compile_UnknownOpcode;
	RSP_Opcode[43] = Compile_SW;
	RSP_Opcode[44] = Compile_UnknownOpcode;
	RSP_Opcode[45] = Compile_UnknownOpcode;
	RSP_Opcode[46] = Compile_UnknownOpcode;
	RSP_Opcode[47] = Compile_UnknownOpcode;
	RSP_Opcode[48] = Compile_UnknownOpcode;
	RSP_Opcode[49] = Compile_UnknownOpcode;
	RSP_Opcode[50] = Compile_LC2;
	RSP_Opcode[51] = Compile_UnknownOpcode;
	RSP_Opcode[52] = Compile_UnknownOpcode;
	RSP_Opcode[53] = Compile_UnknownOpcode;
	RSP_Opcode[54] = Compile_UnknownOpcode;
	RSP_Opcode[55] = Compile_UnknownOpcode;
	RSP_Opcode[56] = Compile_UnknownOpcode;
	RSP_Opcode[57] = Compile_UnknownOpcode;
	RSP_Opcode[58] = Compile_SC2;
	RSP_Opcode[59] = Compile_UnknownOpcode;
	RSP_Opcode[60] = Compile_UnknownOpcode;
	RSP_Opcode[61] = Compile_UnknownOpcode;
	RSP_Opcode[62] = Compile_UnknownOpcode;
	RSP_Opcode[63] = Compile_UnknownOpcode;

	RSP_Special[ 0] = Compile_Special_SLL;
	RSP_Special[ 1] = Compile_UnknownOpcode;
	RSP_Special[ 2] = Compile_Special_SRL;
	RSP_Special[ 3] = Compile_Special_SRA;
	RSP_Special[ 4] = Compile_Special_SLLV;
	RSP_Special[ 5] = Compile_UnknownOpcode;
	RSP_Special[ 6] = Compile_Special_SRLV;
	RSP_Special[ 7] = Compile_Special_SRAV;
	RSP_Special[ 8] = Compile_Special_JR;
	RSP_Special[ 9] = Compile_Special_JALR;
	RSP_Special[10] = Compile_UnknownOpcode;
	RSP_Special[11] = Compile_UnknownOpcode;
	RSP_Special[12] = Compile_UnknownOpcode;
	RSP_Special[13] = Compile_Special_BREAK;
	RSP_Special[14] = Compile_UnknownOpcode;
	RSP_Special[15] = Compile_UnknownOpcode;
	RSP_Special[16] = Compile_UnknownOpcode;
	RSP_Special[17] = Compile_UnknownOpcode;
	RSP_Special[18] = Compile_UnknownOpcode;
	RSP_Special[19] = Compile_UnknownOpcode;
	RSP_Special[20] = Compile_UnknownOpcode;
	RSP_Special[21] = Compile_UnknownOpcode;
	RSP_Special[22] = Compile_UnknownOpcode;
	RSP_Special[23] = Compile_UnknownOpcode;
	RSP_Special[24] = Compile_UnknownOpcode;
	RSP_Special[25] = Compile_UnknownOpcode;
	RSP_Special[26] = Compile_UnknownOpcode;
	RSP_Special[27] = Compile_UnknownOpcode;
	RSP_Special[28] = Compile_UnknownOpcode;
	RSP_Special[29] = Compile_UnknownOpcode;
	RSP_Special[30] = Compile_UnknownOpcode;
	RSP_Special[31] = Compile_UnknownOpcode;
	RSP_Special[32] = Compile_Special_ADD;
	RSP_Special[33] = Compile_Special_ADDU;
	RSP_Special[34] = Compile_Special_SUB;
	RSP_Special[35] = Compile_Special_SUBU;
	RSP_Special[36] = Compile_Special_AND;
	RSP_Special[37] = Compile_Special_OR;
	RSP_Special[38] = Compile_Special_XOR;
	RSP_Special[39] = Compile_Special_NOR;
	RSP_Special[40] = Compile_UnknownOpcode;
	RSP_Special[41] = Compile_UnknownOpcode;
	RSP_Special[42] = Compile_Special_SLT;
	RSP_Special[43] = Compile_Special_SLTU;
	RSP_Special[44] = Compile_UnknownOpcode;
	RSP_Special[45] = Compile_UnknownOpcode;
	RSP_Special[46] = Compile_UnknownOpcode;
	RSP_Special[47] = Compile_UnknownOpcode;
	RSP_Special[48] = Compile_UnknownOpcode;
	RSP_Special[49] = Compile_UnknownOpcode;
	RSP_Special[50] = Compile_UnknownOpcode;
	RSP_Special[51] = Compile_UnknownOpcode;
	RSP_Special[52] = Compile_UnknownOpcode;
	RSP_Special[53] = Compile_UnknownOpcode;
	RSP_Special[54] = Compile_UnknownOpcode;
	RSP_Special[55] = Compile_UnknownOpcode;
	RSP_Special[56] = Compile_UnknownOpcode;
	RSP_Special[57] = Compile_UnknownOpcode;
	RSP_Special[58] = Compile_UnknownOpcode;
	RSP_Special[59] = Compile_UnknownOpcode;
	RSP_Special[60] = Compile_UnknownOpcode;
	RSP_Special[61] = Compile_UnknownOpcode;
	RSP_Special[62] = Compile_UnknownOpcode;
	RSP_Special[63] = Compile_UnknownOpcode;

	RSP_RegImm[ 0] = Compile_RegImm_BLTZ;
	RSP_RegImm[ 1] = Compile_RegImm_BGEZ;
	RSP_RegImm[ 2] = Compile_UnknownOpcode;
	RSP_RegImm[ 3] = Compile_UnknownOpcode;
	RSP_RegImm[ 4] = Compile_UnknownOpcode;
	RSP_RegImm[ 5] = Compile_UnknownOpcode;
	RSP_RegImm[ 6] = Compile_UnknownOpcode;
	RSP_RegImm[ 7] = Compile_UnknownOpcode;
	RSP_RegImm[ 8] = Compile_UnknownOpcode;
	RSP_RegImm[ 9] = Compile_UnknownOpcode;
	RSP_RegImm[10] = Compile_UnknownOpcode;
	RSP_RegImm[11] = Compile_UnknownOpcode;
	RSP_RegImm[12] = Compile_UnknownOpcode;
	RSP_RegImm[13] = Compile_UnknownOpcode;
	RSP_RegImm[14] = Compile_UnknownOpcode;
	RSP_RegImm[15] = Compile_UnknownOpcode;
	RSP_RegImm[16] = Compile_RegImm_BLTZAL;
	RSP_RegImm[17] = Compile_RegImm_BGEZAL;
	RSP_RegImm[18] = Compile_UnknownOpcode;
	RSP_RegImm[19] = Compile_UnknownOpcode;
	RSP_RegImm[20] = Compile_UnknownOpcode;
	RSP_RegImm[21] = Compile_UnknownOpcode;
	RSP_RegImm[22] = Compile_UnknownOpcode;
	RSP_RegImm[23] = Compile_UnknownOpcode;
	RSP_RegImm[24] = Compile_UnknownOpcode;
	RSP_RegImm[25] = Compile_UnknownOpcode;
	RSP_RegImm[26] = Compile_UnknownOpcode;
	RSP_RegImm[27] = Compile_UnknownOpcode;
	RSP_RegImm[28] = Compile_UnknownOpcode;
	RSP_RegImm[29] = Compile_UnknownOpcode;
	RSP_RegImm[30] = Compile_UnknownOpcode;
	RSP_RegImm[31] = Compile_UnknownOpcode;

	RSP_Cop0[ 0] = Compile_Cop0_MF;
	RSP_Cop0[ 1] = Compile_UnknownOpcode;
	RSP_Cop0[ 2] = Compile_UnknownOpcode;
	RSP_Cop0[ 3] = Compile_UnknownOpcode;
	RSP_Cop0[ 4] = Compile_Cop0_MT;
	RSP_Cop0[ 5] = Compile_UnknownOpcode;
	RSP_Cop0[ 6] = Compile_UnknownOpcode;
	RSP_Cop0[ 7] = Compile_UnknownOpcode;
	RSP_Cop0[ 8] = Compile_UnknownOpcode;
	RSP_Cop0[ 9] = Compile_UnknownOpcode;
	RSP_Cop0[10] = Compile_UnknownOpcode;
	RSP_Cop0[11] = Compile_UnknownOpcode;
	RSP_Cop0[12] = Compile_UnknownOpcode;
	RSP_Cop0[13] = Compile_UnknownOpcode;
	RSP_Cop0[14] = Compile_UnknownOpcode;
	RSP_Cop0[15] = Compile_UnknownOpcode;
	RSP_Cop0[16] = Compile_UnknownOpcode;
	RSP_Cop0[17] = Compile_UnknownOpcode;
	RSP_Cop0[18] = Compile_UnknownOpcode;
	RSP_Cop0[19] = Compile_UnknownOpcode;
	RSP_Cop0[20] = Compile_UnknownOpcode;
	RSP_Cop0[21] = Compile_UnknownOpcode;
	RSP_Cop0[22] = Compile_UnknownOpcode;
	RSP_Cop0[23] = Compile_UnknownOpcode;
	RSP_Cop0[24] = Compile_UnknownOpcode;
	RSP_Cop0[25] = Compile_UnknownOpcode;
	RSP_Cop0[26] = Compile_UnknownOpcode;
	RSP_Cop0[27] = Compile_UnknownOpcode;
	RSP_Cop0[28] = Compile_UnknownOpcode;
	RSP_Cop0[29] = Compile_UnknownOpcode;
	RSP_Cop0[30] = Compile_UnknownOpcode;
	RSP_Cop0[31] = Compile_UnknownOpcode;
	
	RSP_Cop2[ 0] = Compile_Cop2_MF;
	RSP_Cop2[ 1] = Compile_UnknownOpcode;
	RSP_Cop2[ 2] = Compile_Cop2_CF;
	RSP_Cop2[ 3] = Compile_UnknownOpcode;
	RSP_Cop2[ 4] = Compile_Cop2_MT;
	RSP_Cop2[ 5] = Compile_UnknownOpcode;
	RSP_Cop2[ 6] = Compile_Cop2_CT;
	RSP_Cop2[ 7] = Compile_UnknownOpcode;
	RSP_Cop2[ 8] = Compile_UnknownOpcode;
	RSP_Cop2[ 9] = Compile_UnknownOpcode;
	RSP_Cop2[10] = Compile_UnknownOpcode;
	RSP_Cop2[11] = Compile_UnknownOpcode;
	RSP_Cop2[12] = Compile_UnknownOpcode;
	RSP_Cop2[13] = Compile_UnknownOpcode;
	RSP_Cop2[14] = Compile_UnknownOpcode;
	RSP_Cop2[15] = Compile_UnknownOpcode;
	RSP_Cop2[16] = Compile_COP2_VECTOR;
	RSP_Cop2[17] = Compile_COP2_VECTOR;
	RSP_Cop2[18] = Compile_COP2_VECTOR;
	RSP_Cop2[19] = Compile_COP2_VECTOR;
	RSP_Cop2[20] = Compile_COP2_VECTOR;
	RSP_Cop2[21] = Compile_COP2_VECTOR;
	RSP_Cop2[22] = Compile_COP2_VECTOR;
	RSP_Cop2[23] = Compile_COP2_VECTOR;
	RSP_Cop2[24] = Compile_COP2_VECTOR;
	RSP_Cop2[25] = Compile_COP2_VECTOR;
	RSP_Cop2[26] = Compile_COP2_VECTOR;
	RSP_Cop2[27] = Compile_COP2_VECTOR;
	RSP_Cop2[28] = Compile_COP2_VECTOR;
	RSP_Cop2[29] = Compile_COP2_VECTOR;
	RSP_Cop2[30] = Compile_COP2_VECTOR;
	RSP_Cop2[31] = Compile_COP2_VECTOR;

	RSP_Vector[ 0] = Compile_Vector_VMULF;
	RSP_Vector[ 1] = Compile_Vector_VMULU;
	RSP_Vector[ 2] = Compile_UnknownOpcode;
	RSP_Vector[ 3] = Compile_UnknownOpcode;
	RSP_Vector[ 4] = Compile_Vector_VMUDL;
	RSP_Vector[ 5] = Compile_Vector_VMUDM;
	RSP_Vector[ 6] = Compile_Vector_VMUDN;
	RSP_Vector[ 7] = Compile_Vector_VMUDH;
	RSP_Vector[ 8] = Compile_Vector_VMACF;
	RSP_Vector[ 9] = Compile_Vector_VMACU;
	RSP_Vector[10] = Compile_UnknownOpcode;
	RSP_Vector[11] = Compile_Vector_VMACQ;
	RSP_Vector[12] = Compile_Vector_VMADL;
	RSP_Vector[13] = Compile_Vector_VMADM;
	RSP_Vector[14] = Compile_Vector_VMADN;
	RSP_Vector[15] = Compile_Vector_VMADH;
	RSP_Vector[16] = Compile_Vector_VADD;
	RSP_Vector[17] = Compile_Vector_VSUB;
	RSP_Vector[18] = Compile_UnknownOpcode;
	RSP_Vector[19] = Compile_Vector_VABS;
	RSP_Vector[20] = Compile_Vector_VADDC;
	RSP_Vector[21] = Compile_Vector_VSUBC;
	RSP_Vector[22] = Compile_UnknownOpcode;
	RSP_Vector[23] = Compile_UnknownOpcode;
	RSP_Vector[24] = Compile_UnknownOpcode;
	RSP_Vector[25] = Compile_UnknownOpcode;
	RSP_Vector[26] = Compile_UnknownOpcode;
	RSP_Vector[27] = Compile_UnknownOpcode;
	RSP_Vector[28] = Compile_UnknownOpcode;
	RSP_Vector[29] = Compile_Vector_VSAW;
	RSP_Vector[30] = Compile_UnknownOpcode;
	RSP_Vector[31] = Compile_UnknownOpcode;
	RSP_Vector[32] = Compile_Vector_VLT;
	RSP_Vector[33] = Compile_Vector_VEQ;
	RSP_Vector[34] = Compile_Vector_VNE;
	RSP_Vector[35] = Compile_Vector_VGE;
	RSP_Vector[36] = Compile_Vector_VCL;
	RSP_Vector[37] = Compile_Vector_VCH;
	RSP_Vector[38] = Compile_Vector_VCR;
	RSP_Vector[39] = Compile_Vector_VMRG;
	RSP_Vector[40] = Compile_Vector_VAND;
	RSP_Vector[41] = Compile_Vector_VNAND;
	RSP_Vector[42] = Compile_Vector_VOR;
	RSP_Vector[43] = Compile_Vector_VNOR;
	RSP_Vector[44] = Compile_Vector_VXOR;
	RSP_Vector[45] = Compile_Vector_VNXOR;
	RSP_Vector[46] = Compile_UnknownOpcode;
	RSP_Vector[47] = Compile_UnknownOpcode;
	RSP_Vector[48] = Compile_Vector_VRCP;
	RSP_Vector[49] = Compile_Vector_VRCPL;
	RSP_Vector[50] = Compile_Vector_VRCPH;
	RSP_Vector[51] = Compile_Vector_VMOV;
	RSP_Vector[52] = Compile_Vector_VRSQ;
	RSP_Vector[53] = Compile_Vector_VRSQL;
	RSP_Vector[54] = Compile_Vector_VRSQH;
	RSP_Vector[55] = Compile_Vector_VNOOP;
	RSP_Vector[56] = Compile_UnknownOpcode;
	RSP_Vector[57] = Compile_UnknownOpcode;
	RSP_Vector[58] = Compile_UnknownOpcode;
	RSP_Vector[59] = Compile_UnknownOpcode;
	RSP_Vector[60] = Compile_UnknownOpcode;
	RSP_Vector[61] = Compile_UnknownOpcode;
	RSP_Vector[62] = Compile_UnknownOpcode;
	RSP_Vector[63] = Compile_UnknownOpcode;

	RSP_Lc2[ 0] = Compile_Opcode_LBV;
	RSP_Lc2[ 1] = Compile_Opcode_LSV;
	RSP_Lc2[ 2] = Compile_Opcode_LLV;
	RSP_Lc2[ 3] = Compile_Opcode_LDV;
	RSP_Lc2[ 4] = Compile_Opcode_LQV;
	RSP_Lc2[ 5] = Compile_Opcode_LRV;
	RSP_Lc2[ 6] = Compile_Opcode_LPV;
	RSP_Lc2[ 7] = Compile_Opcode_LUV;
	RSP_Lc2[ 8] = Compile_Opcode_LHV;
	RSP_Lc2[ 9] = Compile_Opcode_LFV;
	RSP_Lc2[10] = Compile_UnknownOpcode;
	RSP_Lc2[11] = Compile_Opcode_LTV;
	RSP_Lc2[12] = Compile_UnknownOpcode;
	RSP_Lc2[13] = Compile_UnknownOpcode;
	RSP_Lc2[14] = Compile_UnknownOpcode;
	RSP_Lc2[15] = Compile_UnknownOpcode;
	RSP_Lc2[16] = Compile_UnknownOpcode;
	RSP_Lc2[17] = Compile_UnknownOpcode;
	RSP_Lc2[18] = Compile_UnknownOpcode;
	RSP_Lc2[19] = Compile_UnknownOpcode;
	RSP_Lc2[20] = Compile_UnknownOpcode;
	RSP_Lc2[21] = Compile_UnknownOpcode;
	RSP_Lc2[22] = Compile_UnknownOpcode;
	RSP_Lc2[23] = Compile_UnknownOpcode;
	RSP_Lc2[24] = Compile_UnknownOpcode;
	RSP_Lc2[25] = Compile_UnknownOpcode;
	RSP_Lc2[26] = Compile_UnknownOpcode;
	RSP_Lc2[27] = Compile_UnknownOpcode;
	RSP_Lc2[28] = Compile_UnknownOpcode;
	RSP_Lc2[29] = Compile_UnknownOpcode;
	RSP_Lc2[30] = Compile_UnknownOpcode;
	RSP_Lc2[31] = Compile_UnknownOpcode;

	RSP_Sc2[ 0] = Compile_Opcode_SBV;
	RSP_Sc2[ 1] = Compile_Opcode_SSV;
	RSP_Sc2[ 2] = Compile_Opcode_SLV;
	RSP_Sc2[ 3] = Compile_Opcode_SDV;
	RSP_Sc2[ 4] = Compile_Opcode_SQV;
	RSP_Sc2[ 5] = Compile_Opcode_SRV;
	RSP_Sc2[ 6] = Compile_Opcode_SPV;
	RSP_Sc2[ 7] = Compile_Opcode_SUV;
	RSP_Sc2[ 8] = Compile_Opcode_SHV;
	RSP_Sc2[ 9] = Compile_Opcode_SFV;
	RSP_Sc2[10] = Compile_Opcode_SWV;
	RSP_Sc2[11] = Compile_Opcode_STV;
	RSP_Sc2[12] = Compile_UnknownOpcode;
	RSP_Sc2[13] = Compile_UnknownOpcode;
	RSP_Sc2[14] = Compile_UnknownOpcode;
	RSP_Sc2[15] = Compile_UnknownOpcode;
	RSP_Sc2[16] = Compile_UnknownOpcode;
	RSP_Sc2[17] = Compile_UnknownOpcode;
	RSP_Sc2[18] = Compile_UnknownOpcode;
	RSP_Sc2[19] = Compile_UnknownOpcode;
	RSP_Sc2[20] = Compile_UnknownOpcode;
	RSP_Sc2[21] = Compile_UnknownOpcode;
	RSP_Sc2[22] = Compile_UnknownOpcode;
	RSP_Sc2[23] = Compile_UnknownOpcode;
	RSP_Sc2[24] = Compile_UnknownOpcode;
	RSP_Sc2[25] = Compile_UnknownOpcode;
	RSP_Sc2[26] = Compile_UnknownOpcode;
	RSP_Sc2[27] = Compile_UnknownOpcode;
	RSP_Sc2[28] = Compile_UnknownOpcode;
	RSP_Sc2[29] = Compile_UnknownOpcode;
	RSP_Sc2[30] = Compile_UnknownOpcode;
	RSP_Sc2[31] = Compile_UnknownOpcode;
	
	BlockID = 0;
	ChangedPC = FALSE;
	#ifdef Log_x86Code
	Start_x86_Log();
	#endif
}

/******************************************************
** ReOrderSubBlock
**
** Desc:
** this can be done, but will be interesting to put
** between branches labels, and actual branches, whichever
** occurs first in code
**
********************************************************/

void ReOrderInstructions(DWORD StartPC, DWORD EndPC) {
	DWORD InstructionCount = EndPC - StartPC;
	DWORD Count, ReorderedOps, CurrentPC;
	OPCODE PreviousOp, CurrentOp, RspOp;

	PreviousOp.Hex = *(DWORD*)(RSPInfo.IMEM + StartPC);

	if (TRUE == IsOpcodeBranch(StartPC, PreviousOp)) {
		/* the sub block ends here anyway */
		return;
	} 
	
	if (IsOpcodeNop(StartPC) && IsOpcodeNop(StartPC + 4) && IsOpcodeNop(StartPC + 8)) {
		/* Dont even bother */
		return;
	}

	CPU_Message("***** Doing reorder (%X to %X) *****", StartPC, EndPC);

	if (InstructionCount < 0x0010) { return; }
	if (InstructionCount > 0x0A00) { return; }
	
	CPU_Message(" Before:");
	for (Count = StartPC; Count < EndPC; Count += 4) {
		RSP_LW_IMEM(Count, &RspOp.Hex);
		CPU_Message("  %X %s",Count,RSPOpcodeName(RspOp.Hex,Count));
	}

	for (Count = 0; Count < InstructionCount; Count += 4) {
		CurrentPC = StartPC;
		PreviousOp.Hex = *(DWORD*)(RSPInfo.IMEM + CurrentPC);
		ReorderedOps = 0;

		for (;;) {
			CurrentPC += 4;
			if (CurrentPC >= EndPC) { break; }
			CurrentOp.Hex = *(DWORD*)(RSPInfo.IMEM + CurrentPC);

			if (TRUE == CompareInstructions(CurrentPC, &PreviousOp, &CurrentOp)) {
				/* Move current opcode up */	
				*(DWORD*)(RSPInfo.IMEM + CurrentPC - 4) = CurrentOp.Hex;
			 	*(DWORD*)(RSPInfo.IMEM + CurrentPC) = PreviousOp.Hex;

				ReorderedOps++;

#ifdef REORDER_BLOCK_VERBOSE
				CPU_Message("Swapped %X and %X", CurrentPC - 4, CurrentPC);
#endif
			}
			PreviousOp.Hex = *(DWORD*)(RSPInfo.IMEM + CurrentPC);

			if (IsOpcodeNop(CurrentPC) && IsOpcodeNop(CurrentPC + 4) && IsOpcodeNop(CurrentPC + 8)) {
				CurrentPC = EndPC;
			}
		}

		if (ReorderedOps == 0) {
			Count = InstructionCount;
		}
	}

	CPU_Message(" After:");
	for (Count = StartPC; Count < EndPC; Count += 4) {
		RSP_LW_IMEM(Count, &RspOp.Hex);
		CPU_Message("  %X %s",Count,RSPOpcodeName(RspOp.Hex,Count));
	}
	CPU_Message("");
}

void ReOrderSubBlock(RSP_BLOCK * Block) {
	DWORD end = 0x0ffc;
	DWORD count;

	if (!Compiler.bReOrdering) { 
		return;
	}
	if (Block->CurrPC > 0xFF0) { 
		return;
	}

	/* find the label or jump closest to us */
	if (RspCode.LabelCount) {
		for (count = 0; count < RspCode.LabelCount; count++) {
			if (RspCode.BranchLabels[count] < end && RspCode.BranchLabels[count] > Block->CurrPC) { 
				end = RspCode.BranchLabels[count];
			}
		}
	}
	if (RspCode.BranchCount) {
		for (count = 0; count < RspCode.BranchCount; count++) {
			if (RspCode.BranchLocations[count] < end && RspCode.BranchLocations[count] > Block->CurrPC) { 
				end = RspCode.BranchLocations[count];
			}
		}
	}
	/* it wont actually re-order the op at the end */
	ReOrderInstructions(Block->CurrPC, end);
}

/******************************************************
** DetectGPRConstants
**
** Desc:
**  this needs to be called on a sub-block basis, like
**  after every time we hit a branch and delay slot
**
********************************************************/

void DetectGPRConstants(RSP_CODE * code) {
	DWORD Count, Constant = 0;

	memset(&code->bIsRegConst, 0, sizeof(Boolean) * 0x20);
	memset(&code->MipsRegConst, 0, sizeof(DWORD) * 0x20);
	
	if (!Compiler.bGPRConstants) { 
		return;
	}
	CPU_Message("***** Detecting constants *****");

	/* R0 is constant zero, R31 or RA is not constant */
	code->bIsRegConst[0] = TRUE;
	code->MipsRegConst[0] = 0;

	/* Do your global search for them */
	for (Count = 1; Count < 31; Count++) {
		if (IsRegisterConstant(Count, &Constant) == TRUE) {
			CPU_Message("Global: %s is a constant of: %08X", GPR_Name(Count), Constant);
			code->bIsRegConst[Count] = TRUE;
			code->MipsRegConst[Count] = Constant;
		}
	}
	CPU_Message("");
}

/******************************************************
** CompilerToggleBuffer and ClearX86Code
**
** Desc:
**  1> toggles the compiler buffer, useful for poorly
**  taken branches like alignment
**
**  2> clears all the x86 code, jump tables etc
**
********************************************************/

void CompilerToggleBuffer(void) {
	if (dwBuffer == MainBuffer) {
		dwBuffer = SecondaryBuffer;
		pLastPrimary = RecompPos;

		if (pLastSecondary == NULL) {
			pLastSecondary = RecompCodeSecondary;
		}

		RecompPos = pLastSecondary;
		CPU_Message("   (Secondary Buffer Active 0x%08X)", pLastSecondary);
	} else {
		dwBuffer = MainBuffer;
		pLastSecondary = RecompPos;

		if (pLastPrimary == NULL) {
			pLastPrimary = RecompCode;
		}
	
		RecompPos = pLastPrimary;
		CPU_Message("   (Primary Buffer Active 0x%08X)", pLastPrimary);
	}
}

void ClearAllx86Code (void) {
	extern DWORD NoOfMaps, MapsCRC[32];
	extern BYTE *JumpTables;

	memset(&MapsCRC, 0, sizeof(DWORD) * 0x20);
	NoOfMaps = 0;
	memset(JumpTables,0,0x1000*32);

	RecompPos = RecompCode;

	pLastPrimary = NULL;
	pLastSecondary = NULL;
}

/******************************************************
** Link Branches
**
** Desc:
**  resolves all the collected branches, x86 style
**
********************************************************/

void LinkBranches(RSP_BLOCK * Block) {
	DWORD OrigPrgCount = *PrgCount;
	DWORD Count, Target;
	DWORD * JumpWord;
	BYTE * X86Code;
	RSP_BLOCK Save;

	if (!CurrentBlock.ResolveCount) { 
		return;
	}
	CPU_Message("***** Linking branches (%i) *****", CurrentBlock.ResolveCount);

	for (Count = 0; Count < CurrentBlock.ResolveCount; Count++) {
		Target = CurrentBlock.BranchesToResolve[Count].TargetPC;
		X86Code = *(JumpTable + (Target >> 2));

		if (!X86Code) {
			*PrgCount = Target;
			CPU_Message("");
			CPU_Message("===== (Generate Code: %04X) =====", Target);
			Save = *Block;

			/* compile this block and link */
			CompilerRSPBlock();
			LinkBranches(Block);

			*Block = Save;
			CPU_Message("===== (End Generate Code: %04X) =====", Target);
			CPU_Message("");
			X86Code = *(JumpTable + (Target >> 2));
		}

		JumpWord = CurrentBlock.BranchesToResolve[Count].X86JumpLoc;
		x86_SetBranch32b(JumpWord, (DWORD*)X86Code);

		CPU_Message("Linked RSP branch from x86: %08X, to RSP: %X / x86: %08X",
			JumpWord, Target, X86Code);
	}
	*PrgCount = OrigPrgCount;
	CPU_Message("***** Done Linking Branches *****");
	CPU_Message("");
}

/******************************************************
** BuildBranchLabels
**
** Desc:
**   Branch labels are used to start and stop re-ordering 
**   sections as well as set the jump table to points 
**   within a block that are safe
**
********************************************************/

void BuildBranchLabels(void) {
	OPCODE RspOp;
	DWORD i, Dest;

#ifdef BUILD_BRANCHLABELS_VERBOSE
	CPU_Message("***** Building branch labels *****");
#endif

	for (i = 0; i < 0x1000; i += 4) {
		RspOp.Hex = *(DWORD*)(RSPInfo.IMEM + i);

		if (TRUE == IsOpcodeBranch(i, RspOp)) {
			if (RspCode.LabelCount >= (sizeof(RspCode.BranchLabels) / sizeof(RspCode.BranchLabels[0])) - 1) {
				CompilerWarning("Out of space for Branch Labels");
				return;
			}

			if (RspCode.BranchCount >= (sizeof(RspCode.BranchLocations) / sizeof(RspCode.BranchLocations[0])) - 1) {
				CompilerWarning("Out of space for Branch Locations");
				return;
			}
			RspCode.BranchLocations[RspCode.BranchCount++] = i;
			if (RspOp.op == RSP_SPECIAL) {
				/* register jump not predictable */
			} else if (RspOp.op == RSP_J || RspOp.op == RSP_JAL) {
				/* for JAL its a sub-block for returns */
				Dest = (RspOp.target << 2) & 0xFFC;
				RspCode.BranchLabels[RspCode.LabelCount] = Dest;
				RspCode.LabelCount += 1;
#ifdef BUILD_BRANCHLABELS_VERBOSE
				CPU_Message("[%02i] Added branch at %X to %X", RspCode.LabelCount, i, Dest);
#endif
			} else {
				Dest = (i + ((short)RspOp.offset << 2) + 4) & 0xFFC;
				RspCode.BranchLabels[RspCode.LabelCount] = Dest;
				RspCode.LabelCount += 1;
#ifdef BUILD_BRANCHLABELS_VERBOSE
				CPU_Message("[%02i] Added branch at %X to %X", RspCode.LabelCount, i, Dest);
#endif
			}
		}
	}

#ifdef BUILD_BRANCHLABELS_VERBOSE
	CPU_Message("***** End branch labels *****");
	CPU_Message("");
#endif
}

Boolean IsJumpLabel(DWORD PC)
{
	DWORD Count;
	
	if (!RspCode.LabelCount) {
		return FALSE;
	}

	for (Count = 0; Count < RspCode.LabelCount; Count++) {
		if (PC == RspCode.BranchLabels[Count]) {
			return TRUE;
		}
	}
	return FALSE;
}

void CompilerLinkBlocks(void) {
	BYTE * KnownCode = *(JumpTable + (CompilePC >> 2));

	CPU_Message("***** Linking block to X86: %08X *****", KnownCode);
	NextInstruction = FINISH_BLOCK;

	/* block linking scenario */				
	JmpLabel32("Linked block", 0);
	x86_SetBranch32b(RecompPos - 4, KnownCode);
}

void CompilerRSPBlock(void)
{
	BYTE * IMEM_SAVE = (BYTE *)malloc(0x1000);
	const size_t X86BaseAddress = (size_t)RecompPos;

	NextInstruction = NORMAL;
	CompilePC = *PrgCount;
	
	memset(&CurrentBlock, 0, sizeof(CurrentBlock));
	CurrentBlock.StartPC = CompilePC;
	CurrentBlock.CurrPC = CompilePC;

	/* Align the block to a boundary */	
	if (X86BaseAddress & 7)
	{
		register size_t Count;
		const size_t Padding = (8 - (X86BaseAddress & 7)) & 7;

		for (Count = 0; Count < Padding; Count++) {
			CPU_Message("%08X: nop", RecompPos);
			*(RecompPos++) = 0x90;
		}
	}

	CPU_Message("====== block %d ======", BlockID++);
	CPU_Message("x86 code at: %X",RecompPos);
	CPU_Message("Jumpt Table: %X",Table );
	CPU_Message("Start of Block: %X",CurrentBlock.StartPC );
	CPU_Message("====== recompiled code ======");

	if (Compiler.bReOrdering == TRUE) {
		memcpy(IMEM_SAVE, RSPInfo.IMEM, 0x1000);
		ReOrderSubBlock(&CurrentBlock);
	}

	/* this is for the block about to be compiled */
	*(JumpTable + (CompilePC >> 2)) = RecompPos;

	do {
		/*
		 * Re-Ordering is setup to allow us to have loop labels
		 * so here we see if this is one and put it in the jump table
		 */
		if (NextInstruction == NORMAL && IsJumpLabel(CompilePC)) {
			/* jumps come around twice */
			if (NULL == *(JumpTable + (CompilePC >> 2))) {
				CPU_Message("***** Adding Jump Table Entry for PC: %04X at X86: %08X *****", CompilePC, RecompPos);
				CPU_Message("");
				*(JumpTable + (CompilePC >> 2)) = RecompPos;

				/* reorder from here to next label or branch */
				CurrentBlock.CurrPC = CompilePC;
				ReOrderSubBlock(&CurrentBlock);
			} else if (NextInstruction != DELAY_SLOT_DONE) {
				/*
				 * we could link the blocks here, but performance
				 * wise it might be better to just let it run
				 */
			}
		}

		if (Compiler.bSections == TRUE) {
			if (TRUE == RSP_DoSections()) {
				continue;
			}
		}

#ifdef X86_RECOMP_VERBOSE
		if (FALSE == IsOpcodeNop(CompilePC)) {
			CPU_Message("X86 Address: %08X", RecompPos);
		}
#endif

		RSP_LW_IMEM(CompilePC, &RSPOpC.Hex);

		if (LogRDP && NextInstruction != DELAY_SLOT_DONE){
			char str[40];
			sprintf(str,"%X",CompilePC);
			PushImm32(str,CompilePC);
			Call_Direct(RDP_LogLoc,"RDP_LogLoc");
			AddConstToX86Reg(x86_ESP, 4);
		}

		if (RSPOpC.Hex == 0xFFFFFFFF) {
			/* i think this pops up an unknown op dialog */
			/* NextInstruction = FINISH_BLOCK; */
		} else {
			RSP_Opcode[ RSPOpC.op ]();
		}

		switch (NextInstruction) {
		case NORMAL: 
			CompilePC += 4;
			break;
		case DO_DELAY_SLOT:
			NextInstruction = DELAY_SLOT;
			CompilePC += 4;
			break;
		case DELAY_SLOT:
			NextInstruction = DELAY_SLOT_DONE;
			CompilePC -= 4;
			break;
		case DELAY_SLOT_EXIT:
			NextInstruction = DELAY_SLOT_EXIT_DONE;
			CompilePC -= 4;
			break;
		case FINISH_SUB_BLOCK:
			NextInstruction = NORMAL;
			CompilePC += 8;
			if (CompilePC >= 0x1000) {
				NextInstruction = FINISH_BLOCK;
			} else if (NULL == *(JumpTable + (CompilePC >> 2))) {
				/* this is for the new block being compiled now */
				CPU_Message("**** Continuing static SubBlock (jump table entry added for PC: %04X at X86: %08X) *****", CompilePC, RecompPos);
				*(JumpTable + (CompilePC >> 2)) = RecompPos;

				CurrentBlock.CurrPC = CompilePC;
				/* reorder from after delay to next label or branch */
				ReOrderSubBlock(&CurrentBlock);
			} else {
				CompilerLinkBlocks();
			}
			break;

		case FINISH_BLOCK: break;
		default:
			DisplayError("Rsp Main loop\n\nWTF NextInstruction = %d",NextInstruction);
			CompilePC += 4;
			break;
		}
	} while (NextInstruction != FINISH_BLOCK && (CompilePC < 0x1000 || NextInstruction == DELAY_SLOT));
	CPU_Message("==== end of recompiled code ====");

	if (Compiler.bReOrdering == TRUE) {
		memcpy(RSPInfo.IMEM, IMEM_SAVE, 0x1000);
	}
	free(IMEM_SAVE);
}

DWORD RunRecompilerCPU ( DWORD Cycles ) {
	BYTE * Block;

	RSP_Running = TRUE;
	SetJumpTable(JumpTableSize);

	while (RSP_Running) 
	{
		Block = *(JumpTable + (*PrgCount >> 2));

		if (Block == NULL) {
			if (Profiling && !IndvidualBlock) {
				StartTimer((DWORD)Timer_Compiling);
			}

			memset(&RspCode, 0, sizeof(RspCode));
#if defined(_MSC_VER)
			__try {
				BuildBranchLabels();
				DetectGPRConstants(&RspCode);
				CompilerRSPBlock();
			} __except(EXCEPTION_EXECUTE_HANDLER) {
				DisplayError("Error CompilePC = %08X", CompilePC);
				ClearAllx86Code();
				continue;
			}
#else
			BuildBranchLabels();
			DetectGPRConstants(&RspCode);
			CompilerRSPBlock();
#endif
			
			Block = *(JumpTable + (*PrgCount >> 2));

			/*
			** we are done compiling, but we may have references
			** to fill in still either from this block, or jumps
			** that go out of it, let's rock
			**/

			LinkBranches(&CurrentBlock);
			if (Profiling && !IndvidualBlock) {
				StopTimer();
			}
		}

		if (Profiling && IndvidualBlock) {
			StartTimer(*PrgCount);
		}

#if defined(_M_IX86) && defined(_MSC_VER)
		_asm {
			pushad
			call Block
			popad
		}		
#else
		DebugBreak();
#endif
		if (Profiling && IndvidualBlock) {
			StopTimer();
		}
		if (RSP_NextInstruction == SINGLE_STEP)
		{
			RSP_Running = FALSE;
		}
	}

	if (IsMmxEnabled == TRUE) {
#if defined(_M_IX86) && defined(_MSC_VER)
		_asm emms
#else
		DebugBreak();
#endif
	}
	return Cycles;
}