/* Mednafen - Multi-system Emulator
 *
 * This program 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.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITPCES 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 this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

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

#include "pce.h"
#include "huc6280.h"
#include "debug.h"
#include "vdc.h"
#include "../dis6280.h"

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

void PCEDBG_AddBranchTrace(uint32 PC)
{
 PC &= 0xFFFF;

 if(BTEntries[(BTIndex - 1) & 0x7] == PC) return;

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

std::vector<uint32> PCEDBG_GetBranchTrace(void)
{
 std::vector<uint32> ret;

 for(int x = 0; x < 8; x++)
  ret.push_back(BTEntries[(x + BTIndex) & 0x7]);

 return(ret);
}

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

 PCE_InDebug = 1;

 for(unsigned int i = 0; i < bsize; i++)
 {
  if(logical)
  {
   A &= 0xFFFF;
   wmpr = HuCPU.MPR[A >> 13];
   ret |= PCERead[wmpr]((wmpr << 13) | (A & 0x1FFF)) << (i * 8);
  }
  else
  {
   A &= (1 << 21) - 1;
   ret |= PCERead[A >> 13](A) << (i * 8);
  }

  A++;
 }

 PCE_InDebug = 0;

 return(ret);
}

void PCEDBG_MemPoke(uint32 A, uint32 V, unsigned int bsize, bool hl, bool logical)
{
 uint8 wmpr;

 for(unsigned int i = 0; i < bsize; i++)
 {
  if(logical)
  {
   A &= 0xFFFF;
   wmpr = HuCPU.MPR[A >> 13];
   if(hl)
   {
    if(HuCPUFastMap[wmpr])
     HuCPUFastMap[wmpr][(wmpr << 13) + (A & 0x1FFF)] = V & 0xFF;
   }
   else
    PCEWrite[wmpr](A, V & 0xFF);
  }
  else
  {
   A &= (1 << 21) - 1;

   if(hl)
   {
    if(HuCPUFastMap[A >> 13])
     HuCPUFastMap[A >> 13][A] = V & 0xFF;
   }  
   else
    PCEWrite[A >> 13](A, V & 0xFF);
  }
  V >>= 8;
  A++;
 }


}

void PCEDBG_IRQ(int level)
{


}

uint32 PCEDBG_GetVector(int level)
{


}

class DisPCE : public Dis6280
{
	public:
	DisPCE(void)
	{

	}

	uint8 GetX(void)
	{
	 return(HuCPU.X);
	}

	uint8 GetY(void)
	{
	 return(HuCPU.Y);
	}

	uint8 Read(uint16 A)
	{
	 uint8 ret;
	 uint8 wmpr = HuCPU.MPR[A >> 13];

	 PCE_InDebug = 1;
	 ret = PCERead[wmpr]((wmpr << 13) | (A & 0x1FFF));
	 PCE_InDebug = 0;

	 return(ret);
	}
};

static DisPCE DisObj;

std::string PCEDBG_Disassemble(uint32 &a, uint32 SpecialA)
{
	uint16 tmpa = a;
	std::string ret;

	ret = DisObj.Disassemble(tmpa, SpecialA);

	a = tmpa;

	return(ret);
}

typedef struct __PCE_BPOINT {
	unsigned int A[2];
	int type;
	bool logical;
} PCE_BPOINT;

static std::vector<PCE_BPOINT> BreakPointsPC, BreakPointsRead, BreakPointsWrite;

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

void PCEDBG_TestFoundBPoint(void)
{
 if(FoundBPoint)
 {
  BPCallB(HuCPU.PC);
 }
 FoundBPoint = 0;
}

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

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

 if(CPUHook)
  CPUHook(PC);
}

static uint8 ReadHandler(HuC6280 *X, unsigned int A)
{
 std::vector<PCE_BPOINT>::iterator bpit;

 for(bpit = BreakPointsRead.begin(); bpit != BreakPointsRead.end(); bpit++)
 {
  unsigned int testA;

  if(!bpit->logical)
   testA = (X->MPR[A >> 13] << 13) | (A & 0x1FFF);
  else
   testA = A;

  if(testA >= bpit->A[0] && testA <= bpit->A[1])
  {
   FoundBPoint = 1;
   break;
  }
 }

 uint8 wmpr = HuCPU.MPR[A >> 13];
 return(PCERead[wmpr]((wmpr << 13) | (A & 0x1FFF)));
}

static void WriteHandler(HuC6280 *X, unsigned int A, uint8 V)
{
 std::vector<PCE_BPOINT>::iterator bpit;

 for(bpit = BreakPointsWrite.begin(); bpit != BreakPointsWrite.end(); bpit++)
 {
  unsigned int testA;

  if(!bpit->logical)
   testA = (X->MPR[A >> 13] << 13) | (A & 0x1FFF);
  else
   testA = A;

  if(testA >= bpit->A[0] && testA <= bpit->A[1])
  {
   FoundBPoint = 1;
   break;
  }
 }
}

void PCEDBG_AddBreakPoint(int type, unsigned int A1, unsigned int A2, bool logical)
{
 PCE_BPOINT tmp;

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

 if(type == BPOINT_READ)
  BreakPointsRead.push_back(tmp);
 else if(type == BPOINT_WRITE)
  BreakPointsWrite.push_back(tmp);
 else if(type == BPOINT_PC)
  BreakPointsPC.push_back(tmp);

 HuC6280_Debug(BreakPointsPC.size() ? CPUHandler : CPUHook, BreakPointsRead.size() ? ReadHandler : NULL, BreakPointsWrite.size() ? WriteHandler : 0);
}

void PCEDBG_FlushBreakPoints(int type)
{
 std::vector<PCE_BPOINT>::iterator bpit;

 if(type == BPOINT_READ)
  BreakPointsRead.clear();
 else if(type == BPOINT_WRITE)
  BreakPointsWrite.clear();
 else if(type == BPOINT_PC)
  BreakPointsPC.clear();

 HuC6280_Debug(BreakPointsPC.size() ? CPUHandler : CPUHook, BreakPointsRead.size() ? ReadHandler : NULL, BreakPointsWrite.size() ? WriteHandler : 0);
}

void PCEDBG_SetCPUCallback(void (*callb)(uint32 PC))
{
 CPUHook = callb;
 HuC6280_Debug(BreakPointsPC.size() ? CPUHandler : CPUHook, BreakPointsRead.size() ? ReadHandler : NULL, BreakPointsWrite.size() ? WriteHandler : 0);
}

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

uint32 PCEDBG_GetRegister(std::string name)
{
 if(name == "PC")
  return(HuCPU.PC & 0xFFFF);
 else if(name == "A")
  return(HuCPU.A);
 else if(name == "X")
  return(HuCPU.X);
 else if(name == "Y")
  return(HuCPU.Y);
 else if(name == "SP")
  return(HuCPU.S);
 else if(name == "P")
  return(HuCPU.P);
 else if(name == "SPD")
  return(HuCPU.speed);
 else if(name == "MPR0")
  return(HuCPU.MPR[0]);
 else if(name == "MPR1")
  return(HuCPU.MPR[1]);
 else if(name == "MPR2")
  return(HuCPU.MPR[2]);
 else if(name == "MPR3")
  return(HuCPU.MPR[3]);
 else if(name == "MPR4")
  return(HuCPU.MPR[4]);
 else if(name == "MPR5")
  return(HuCPU.MPR[5]);
 else if(name == "MPR6")
  return(HuCPU.MPR[6]);
 else if(name == "MPR7")
  return(HuCPU.MPR[7]);
 else if(name == "IRQM")
  return(HuCPU.IRQMask ^ 0x7);
 else if(name == "TIMS")
  return(HuCPU.timer_status);
 else if(name == "TIMV")
  return(HuCPU.timer_value);
 else if(name == "TIML")
  return(HuCPU.timer_load);
 else if(name == "TIMD")
  return(HuCPU.timer_div);
 else 
  return(VDC_GetRegister(name));
}

void PCEDBG_SetRegister(std::string name, uint32 value)
{
 if(name == "PC")
  HuCPU.PC = value & 0xFFFF;
 else if(name == "A")
  HuCPU.A = value & 0xFF;
 else if(name == "X")
  HuCPU.X = value & 0xFF;
 else if(name == "Y")
  HuCPU.Y = value & 0xFF;
 else if(name == "SP")
  HuCPU.S = value & 0xFF;
 else if(name == "P")
  HuCPU.P = value & 0xFF;
 else if(name == "SPD")
 {
  // FIXME later, duplicate macro
  #define REDOSPEEDCACHE() { HuCPU.speed_shift_cache = ((HuCPU.speed ^ 1) << 1); }
  HuCPU.speed = value & 0x01;
  REDOSPEEDCACHE();
 }
 else if(name == "MPR0")
 {
  HuCPU.MPR[0] = value & 0xFF;
  HuC6280_FlushMPRCache();
 }
 else if(name == "MPR1")
 {
  HuCPU.MPR[1] = value & 0xFF;
  HuC6280_FlushMPRCache();
 }
 else if(name == "MPR2")
 {
  HuCPU.MPR[2] = value & 0xFF;
  HuC6280_FlushMPRCache();
 }
 else if(name == "MPR3")
 {
  HuCPU.MPR[3] = value & 0xFF;
  HuC6280_FlushMPRCache();
 }
 else if(name == "MPR4")
 {
  HuCPU.MPR[4] = value & 0xFF;
  HuC6280_FlushMPRCache();
 }
 else if(name == "MPR5")
 {
  HuCPU.MPR[5] = value & 0xFF;
  HuC6280_FlushMPRCache();
 }
 else if(name == "MPR6")
 {
  HuCPU.MPR[6] = value & 0xFF;
  HuC6280_FlushMPRCache();
 }
 else if(name == "MPR7")
 {
  HuCPU.MPR[7] = value & 0xFF;
  HuC6280_FlushMPRCache();
 }
 else if(name == "IRQM")
  HuCPU.IRQMask = (value & 0x7) ^ 0x7;
 else if(name == "TIMS")
  HuCPU.timer_status = value & 0x1;
 else if(name == "TIMV")
  HuCPU.timer_value = value & 0x7F;
 else if(name == "TIML")
  HuCPU.timer_load = value & 0x7F;
 else if(name == "TIMD")
  HuCPU.timer_div = value & 1023;
 else
  VDC_SetRegister(name, value);
}
