#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <trio/trio.h>

#include "pcfx.h"
#include "debug.h"
#include "v810_cpuD.h"
#include "interrupt.h"
#include "timer.h"
#include "king.h"

static void (*CPUHook)(uint32);
static void (*BPCallB)(uint32 PC) = NULL;

typedef struct __PCFX_BPOINT {
        uint32 A[2];
        int type;
        bool logical;
} PCFX_BPOINT;

static std::vector<PCFX_BPOINT> BreakPointsPC, BreakPointsRead, BreakPointsWrite, BreakPointsIORead, BreakPointsIOWrite;
static std::vector<PCFX_BPOINT> BreakPointsAux0Read, BreakPointsAux0Write;
static bool FoundBPoint = 0;


static int BTIndex = 0;
static uint32 BTEntries[16];

void PCFXDBG_AddBranchTrace(uint32 PC)
{
 if(BTEntries[(BTIndex - 1) & 0xF] == PC) return;

 BTEntries[BTIndex] = PC;
 BTIndex = (BTIndex + 1) & 0xF;
}

std::vector<std::string> PCFXDBG_GetBranchTrace(void)
{
 std::vector<std::string> ret;

 for(int x = 0; x < 16; x++)
 {
  char *tmps = trio_aprintf("%08X", BTEntries[(x + BTIndex) & 0xF]);
  ret.push_back(std::string(tmps));
  free(tmps);
 }
 return(ret);
}

void snortus(void)
{
 FoundBPoint = 1;
}

void PCFXDBG_CheckBP(int type, uint32 address, unsigned int len)
{
 std::vector<PCFX_BPOINT>::iterator bpit, bpit_end;

 if(type == BPOINT_READ)
 {
  bpit = BreakPointsRead.begin();
  bpit_end = BreakPointsRead.end();
 }
 else if(type == BPOINT_WRITE)
 {
  bpit = BreakPointsWrite.begin();
  bpit_end = BreakPointsWrite.end();
 }
 else if(type == BPOINT_IO_READ)
 {
  bpit = BreakPointsIORead.begin();
  bpit_end = BreakPointsIORead.end();
 }
 else if(type == BPOINT_IO_WRITE)
 {
  bpit = BreakPointsIOWrite.begin();
  bpit_end = BreakPointsIOWrite.end();
 }
 else if(type == BPOINT_AUX_READ)
 {
  bpit = BreakPointsAux0Read.begin();
  bpit_end = BreakPointsAux0Read.end();
 }
 else if(type == BPOINT_AUX_WRITE)
 {
  bpit = BreakPointsAux0Write.begin();
  bpit_end = BreakPointsAux0Write.end();
 }
 else
  return;

 for(; bpit != bpit_end; bpit++)
 {
  while(len--)
  {
   if(address >= bpit->A[0] && address <= bpit->A[1])
   {
    //printf("%08x %08x %08x\n", address, bpit->A[0], bpit->A[1]);
    FoundBPoint = TRUE;
    break;
   }
   address++;
  }
 }
}

static void CPUHandler(uint32 PC)
{
 std::vector<PCFX_BPOINT>::iterator bpit;

 for(bpit = BreakPointsPC.begin(); bpit != BreakPointsPC.end(); bpit++)
 {
  if(PC >= bpit->A[0] && PC <= bpit->A[1])
  {
   FoundBPoint = TRUE;
   break;
  }
 }

 if(FoundBPoint)
 {
  BPCallB(PC);
  FoundBPoint = 0;
 }

 if(CPUHook)
  CPUHook(PC);
}

static void RedoCPUHook(void)
{
 v810_setCPUHook((BreakPointsPC.size() || BreakPointsRead.size() || BreakPointsWrite.size() 
	|| BreakPointsIOWrite.size() || BreakPointsIORead.size() || BreakPointsAux0Read.size() || BreakPointsAux0Write.size()) ? CPUHandler : CPUHook);
}

void PCFXDBG_FlushBreakPoints(int type)
{
 std::vector<PCFX_BPOINT>::iterator bpit;

 if(type == BPOINT_READ)
  BreakPointsRead.clear();
 else if(type == BPOINT_WRITE)
  BreakPointsWrite.clear();
 else if(type == BPOINT_IO_READ)
  BreakPointsIORead.clear();
 else if(type == BPOINT_IO_WRITE)
  BreakPointsIOWrite.clear();
 else if(type == BPOINT_AUX_READ)
  BreakPointsAux0Read.clear();
 else if(type == BPOINT_AUX_WRITE)
  BreakPointsAux0Write.clear();
 else if(type == BPOINT_PC)
  BreakPointsPC.clear();

 RedoCPUHook();
 KING_NotifyOfBPE(BreakPointsAux0Read.size(), BreakPointsAux0Write.size());
}

void PCFXDBG_AddBreakPoint(int type, unsigned int A1, unsigned int A2, bool logical)
{
 PCFX_BPOINT tmp;

 tmp.A[0] = A1;
 tmp.A[1] = A2;
 tmp.type = type;

 if(type == BPOINT_READ)
  BreakPointsRead.push_back(tmp);
 else if(type == BPOINT_WRITE)
  BreakPointsWrite.push_back(tmp);
 else if(type == BPOINT_IO_READ)
  BreakPointsIORead.push_back(tmp);
 else if(type == BPOINT_IO_WRITE)
  BreakPointsIOWrite.push_back(tmp);
 else if(type == BPOINT_AUX_READ)
  BreakPointsAux0Read.push_back(tmp);
 else if(type == BPOINT_AUX_WRITE)
  BreakPointsAux0Write.push_back(tmp);
 else if(type == BPOINT_PC)
  BreakPointsPC.push_back(tmp);

 RedoCPUHook();
 KING_NotifyOfBPE(BreakPointsAux0Read.size(), BreakPointsAux0Write.size());
}

std::string PCFXDBG_Disassemble(uint32 &a, uint32 SpecialA)
{
 return(v810_dis(a, 1));
}

uint32 PCFXDBG_MemPeek(uint32 A, unsigned int bsize, bool hl, bool logical)
{
 uint32 ret = 0;

 for(unsigned int i = 0; i < bsize; i++)
 {
  A &= 0xFFFFFFFF;
  ret |= mem_rbyte(A) << (i * 8);
  A++;
 }

 return(ret);
}

uint32 PCFXDBG_GetRegister(const std::string &name, std::string *special)
{
 if(name == "PC")
 {
  return(v810_getPC());
 }
 const char *thestring = name.c_str();

 if(!strncmp(thestring, "PR", 2))
 {
  return(P_REG[atoi(thestring + 2)]);
 }
 else if(!strcmp(thestring, "HSP"))
  return(P_REG[2]);
 else if(!strcmp(thestring, "SP"))
  return(P_REG[3]);
 else if(!strcmp(thestring, "GP"))
  return(P_REG[4]);
 else if(!strcmp(thestring, "TP"))
  return(P_REG[5]);
 else if(!strncmp(thestring, "SR", 2))
 {
  int which_one = atoi(thestring + 2);
  uint32 val =  S_REG[which_one];

  if(special && which_one == PSW)
  {
   char buf[256];
   snprintf(buf, 256, "Z: %d, S: %d, OV: %d, CY: %d, ID: %d, AE: %d, EP: %d, NP: %d, IA: %2d",
	(int)(bool)(val & PSW_Z), (int)(bool)(val & PSW_S), (int)(bool)(val & PSW_OV), (int)(bool)(val & PSW_CY),
	(int)(bool)(val & PSW_ID), (int)(bool)(val & PSW_AE), (int)(bool)(val & PSW_EP), (int)(bool)(val & PSW_NP),
	(val & PSW_IA) >> 16);
   *special = std::string(buf);
  }
  return(val);
 }
 uint32 val = 0; 

 if(PCFXIRQ_GetRegister(name, val, special) || FXTIMER_GetRegister(name, val, special) || KING_GetRegister(name, val, special))
  return(val);

 return(val);
}

void PCFXDBG_SetRegister(const std::string &name, uint32 value)
{
 if(name == "PC")
 {
  v810_setPC(value);
  return;
 }

 const char *thestring = name.c_str();

 if(!strncmp(thestring, "PR", 2))
 {
  P_REG[atoi(thestring + 2)] = value;
 }
 else if(!strcmp(thestring, "HSP"))
  P_REG[2] = value;
 else if(!strcmp(thestring, "SP"))
  P_REG[3] = value;
 else if(!strcmp(thestring, "GP"))
  P_REG[4] = value;
 else if(!strcmp(thestring, "TP"))
  P_REG[5] = value;
 else if(!strncmp(thestring, "SR", 2))
 {
  S_REG[atoi(thestring + 2)] = value;
 }
}

void PCFXDBG_SetCPUCallback(void (*callb)(uint32 PC))
{
 CPUHook = callb;
 RedoCPUHook();
}

void PCFXDBG_SetBPCallback(void (*callb)(uint32 PC))
{
 BPCallB = callb;
}

