/* 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 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 this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "main.h"
#include        <trio/trio.h>
#include "gfxdebugger.h"

static bool NeedInit = 1;
static bool NeedHooksInstalled = TRUE;
static bool WatchLogical = 1; // Watch logical memory addresses, not physical
static bool IsActive = 0;
static bool GfxDebugging = 0;

static std::string ReadBreakpoints, IOReadBreakpoints, AuxReadBreakpoints;
static std::string WriteBreakpoints, IOWriteBreakpoints, AuxWriteBreakpoints;

static uint32 ParsePhysAddr(const char *za)
{
 uint32 ret = 0;

 if(strchr(za, ':'))
 {
  unsigned int bank = 0, offset = 0;

  trio_sscanf(za, "%02x:%04x", &bank, &offset);

  ret = (bank * 8192) | (offset & 0x1FFF);
 }
 else
  trio_sscanf(za, "%08x", &ret);

 return(ret);
}

static void ParseBreakpoints(const std::string &Breakpoints, int type)
{
 LockGameMutex(1);

 size_t len = Breakpoints.size();
 const char *zestring = Breakpoints.c_str();
 unsigned int last_x, x;
 bool two_parter = 0;
 bool logical = 1;

 CurGame->Debugger->FlushBreakPoints(type);

 for(last_x = x = 0; x < len; x++)
 {
  if(zestring[x] == '-')
   two_parter = 1;
  else if(zestring[x] == '*')
  {
   logical = 0;
   last_x++;
  }
  else if(zestring[x] == ' ' || x == len - 1)
  {
   uint32 A1, A2;

   if(two_parter)
   {
    char sa1[64], sa2[64];

    if(!logical)
    {
     if(trio_sscanf(zestring + last_x, "%63[^-]%*[-]%63s", sa1, sa2) < 2) continue;

     //printf("%s %s\n", sa1, sa2);
     A1 = ParsePhysAddr(sa1);
     A2 = ParsePhysAddr(sa2);
    }
    else
     if(trio_sscanf(zestring + last_x, "%08x%*[-]%08x", &A1, &A2) < 2) continue;
   }
   else
   {
    if(!logical)
    {
     char sa1[64];

     trio_sscanf(zestring + last_x, "%s", sa1);

     A1 = ParsePhysAddr(sa1);
    }
    else
     if(trio_sscanf(zestring + last_x, "%08x", &A1) != 1) continue;

    A2 = A1;
   }
   //printf("%04x %04x %d\n", A1, A2, logical);
   CurGame->Debugger->AddBreakPoint(type, A1, A2, logical);
   last_x = x + 1;
   logical = 1;
  }
 }
 LockGameMutex(0);
}

static unsigned int RegsPos = 0;
static uint32 InRegs = 0;
static uint32 RegsCols = 0;
static uint32 RegsColsCounts[5];
static uint32 RegsColsPixOffset[5];
static uint32 RegsTotalWidth;

#define MK_COLOR_A(r,g,b,a) ( ((a)<<surface->format->Ashift) | ((r)<<surface->format->Rshift) | ((g) << surface->format->Gshift) | ((b) << surface->format->Bshift))
#define DIS_ENTRIES	28

static std::vector<uint32> PCBreakPoints;
static void RedoPCBreakPoints(void)
{
 LockGameMutex(1);
 CurGame->Debugger->FlushBreakPoints(BPOINT_PC);

 for(unsigned int x = 0; x < PCBreakPoints.size(); x++)
 {
  CurGame->Debugger->AddBreakPoint(BPOINT_PC, PCBreakPoints[x], PCBreakPoints[x], 1);
 }
 LockGameMutex(0);
}

static INLINE bool IsPCBreakPoint(uint32 A)
{
 unsigned int max = PCBreakPoints.size();

 for(unsigned int x = 0; x < max; x++)
  if(PCBreakPoints[x] == A)
   return(1);
 return(0);
}

static void TogglePCBreakPoint(uint32 A)
{
 for(unsigned int x = 0; x < PCBreakPoints.size(); x++)
 {
  if(PCBreakPoints[x] == A)
  {
   PCBreakPoints.erase(PCBreakPoints.begin() + x);
   RedoPCBreakPoints();
   return;
  }
 }
 PCBreakPoints.push_back(A);
 RedoPCBreakPoints();
}


static uint32 WatchAddr = 0x0000, WatchAddrPhys = 0x0000;
static uint32 DisAddr = 0x0000;
static int NeedDisAddrChange = 0;

static bool NeedPCBPToggle = 0;
static volatile int NeedStep = 0;
static volatile int NeedRun = 0;
static bool NeedBreak = 0;
volatile bool InSteppingMode = 0; // R/W in game thread, read in main thread(only when GameMutex is held!)

static std::string CurRegLongName;
static std::string CurRegDetails;

static void DrawRegs(RegType *rec, SDL_Surface *surface, uint32 *pixels, int highlight)
{
  uint32 pitch32 = surface->pitch >> 2;
  uint32 *row = pixels;
  unsigned int meowcow = 0;

  while(rec->bsize)
  {
   char nubuf[256];
   uint32 color = MK_COLOR_A(0xFF, 0xFF, 0xFF, 0xFF);
   uint32 rname_color = MK_COLOR_A(0xE0, 0xFF, 0xFF, 0xFF);

   std::string *details_ptr = NULL;

   if(highlight == meowcow)
   {
    CurRegLongName = rec->long_name;
    rname_color = color = MK_COLOR_A(0xFF, 0x00, 0x00, 0xFF);
    details_ptr = &CurRegDetails;
   }
   int prew = DrawTextTrans(row, surface->pitch, 128, (UTF8*)rec->name.c_str(), rname_color, 0, 1);

   if(rec->bsize == 4)
    snprintf(nubuf, 256, ": %08X", CurGame->Debugger->GetRegister(rec->name, details_ptr));
   else if(rec->bsize == 3)
    snprintf(nubuf, 256, ": %06X", CurGame->Debugger->GetRegister(rec->name, details_ptr));
   else if(rec->bsize == 2)
    snprintf(nubuf, 256, ": %04X", CurGame->Debugger->GetRegister(rec->name, details_ptr));
   else if(rec->bsize == 1)
    snprintf(nubuf, 256, ": %02X", CurGame->Debugger->GetRegister(rec->name, details_ptr));

   DrawTextTrans(row + prew, surface->pitch, 64, (UTF8*)nubuf, color, 0, 1);
   row += 7 * pitch32;
   rec++;
   meowcow++;
  }
}

typedef enum
{
 None = 0,
 DisGoto,
 WatchGoto,
 EditRegs0,
 EditRegs1,
 EditRegs2,
 PokeMe,
 PokeMeHL,
 ReadBPS,
 WriteBPS,
 IOReadBPS,
 IOWriteBPS,
 AuxReadBPS,
 AuxWriteBPS,
 DumpMem,
 DumpGfxMem,
 ForceInt
} PromptType;

static PromptType InPrompt = None;
static std::string PromptText = "";
static std::vector<std::string> kb_buffer;
static int kb_cursor_pos = 0;

static void DrawPrompt(SDL_Surface *surface, const SDL_Rect *rect)
{
 std::string PromptAnswer;

 for(unsigned int i = 0; i < kb_buffer.size(); i++)
  PromptAnswer += kb_buffer[i];

/*
******************************
* PC                         *
* F00F                       *
******************************
*/
 uint32 *pixels = (uint32 *)surface->pixels;
 uint32 pitch32 = surface->pitch >> 2;

 pixels += ((rect->h / 2) - (4 * 7 / 2)) * pitch32;
 pixels += ((rect->w / 2) - (5 * 39 / 2));

 for(unsigned int y = 0; y < 4 * 7; y++)
 {
  uint32 *row = pixels + y * pitch32;
  for(unsigned int x = 0; x < 39 * 5; x++)
   row[x] = MK_COLOR_A(0, 0, 0, 0xFF);
 }

 DrawTextTrans(pixels, surface->pitch, 39 * 5, (UTF8 *)"**************************************", MK_COLOR_A(0xFF,0xFF,0xFF,0xFF), 0, 1);
 DrawTextTrans(pixels + 7 * pitch32, surface->pitch, 39 * 5, (UTF8 *)"*                                    *", MK_COLOR_A(0xFF,0xFF,0xFF,0xFF), 0, 1);
 DrawTextTrans(pixels + 14 * pitch32, surface->pitch, 39 * 5, (UTF8 *)"*                                    *", MK_COLOR_A(0xFF,0xFF,0xFF,0xFF), 0, 1);
 DrawTextTrans(pixels + 21 * pitch32, surface->pitch, 39 * 5, (UTF8 *)"**************************************", MK_COLOR_A(0xFF,0xFF,0xFF,0xFF), 0, 1);

 DrawTextTrans(pixels + 7 * pitch32 + 2 * 5, surface->pitch, (39 - 4) * 5, (UTF8 *)PromptText.c_str(), MK_COLOR_A(0xFF, 0x00, 0xFF, 0xFF), 0, 1);
 DrawTextTrans(pixels + 14 * pitch32 + 2 * 5, surface->pitch, (39 - 4) * 5, (UTF8 *)PromptAnswer.c_str(), MK_COLOR_A(0x00, 0xFF, 0x00, 0xFF), 0, 1);

 if((SDL_GetTicks() & 0x80) && kb_cursor_pos < (39 - 4))
  DrawTextTrans(pixels + 14 * pitch32 + 2 * 5 + kb_cursor_pos * 5, surface->pitch, 5, (UTF8*)"▉", MK_COLOR_A(0x00, 0xFF, 0x00, 0xFF), 0, 1);
}



// Call this function from the main thread
void Debugger_Draw(SDL_Surface *surface, SDL_Rect *rect, const SDL_Rect *screen_rect)
{
 if(!IsActive) return;

 if(GfxDebugging)
 {
  rect->w = 384;
  rect->h = 336;
  SDL_FillRect(surface, rect, 0);
  GfxDebugger_Draw(surface, rect, screen_rect);
  return;
 }

 rect->w = 640;
 rect->h = 240;


 uint32 * pixels = (uint32 *)surface->pixels;
 uint32 pitch32 = surface->pitch >> 2;

 for(unsigned int y = 0; y < rect->h; y++)
 {
  uint32 *row = pixels + y * pitch32;
  for(unsigned int x = 0; x < rect->w; x++)
  {
   //printf("%d %d %d\n", y, x, pixels);
   row[x] = MK_COLOR_A(0, 0, 0, 0xc0);
   //row[x] = MK_COLOR_A(0x00, 0x00, 0x00, 0x7F);
  }
 }


 LockGameMutex(1);

 // We need to disassemble (maximum_instruction_size * (DIS_ENTRIES / 2) * 3)
 // bytes to make sure we can center our desired DisAddr, and
 // that we have enough disassembled datums for displayaling and
 // worshipping cactus mules.

 int PreBytes = CurGame->Debugger->MaxInstructionSize * (DIS_ENTRIES / 2) * 2;
 int DisBytes = CurGame->Debugger->MaxInstructionSize * (DIS_ENTRIES / 2) * 3;

 uint32 A = (DisAddr - PreBytes) & ((1ULL << CurGame->Debugger->LogAddrBits) - 1);

 std::vector<std::string> DisBliss;
 std::vector<uint32> DisBlissA;
 int indexcow = 0;

 while(DisBytes > 0)
 {
  uint32 lastA = A;

  if(A == DisAddr)
   indexcow = DisBliss.size();

  DisBlissA.push_back(A);
  DisBliss.push_back(CurGame->Debugger->Disassemble(A, DisAddr)); // A is passed by reference to Disassemble()

  if(A > DisAddr && lastA < DisAddr) // Err, oops, resynch to DisAddr if necessary
  {
   A = DisAddr;
  }

  int64 consumed = (int64)A - lastA;  
  if(consumed < 0)
  {
   // Fixme
   consumed += 1ULL << CurGame->Debugger->LogAddrBits;
  }
  DisBytes -= consumed;
 }

 for(int x = 0; x < DIS_ENTRIES; x++)
 {
  std::string dis_str = DisBliss[indexcow - (DIS_ENTRIES / 2) + x];
  uint32 A = DisBlissA[indexcow - (DIS_ENTRIES / 2) + x];
  char addr_text[64];

  uint32 color = MK_COLOR_A(0xFF, 0xFF, 0xFF, 0xFF);
  uint32 addr_color = MK_COLOR_A(0xa0, 0xa0, 0xFF, 0xFF);

  if(CurGame->Debugger->LogAddrBits == 32)
   snprintf(addr_text, 256, " %08X: ", A);
  else
   snprintf(addr_text, 256, " %04X: ", A);
  
  if(A == DisAddr)
  {
   addr_text[0] = '>';
   if(!InRegs)
    addr_color = color = MK_COLOR_A(0xFF, 0x00, 0x00, 0xFF);

   if(NeedPCBPToggle)
   {
    TogglePCBreakPoint(A);
    NeedPCBPToggle = 0;
   }

  }
  if(IsPCBreakPoint(A))
   addr_text[0] = addr_text[0] == '>' ? '#' : '*';

  DrawTextTrans(pixels + x * 7 * pitch32, surface->pitch, rect->w, (UTF8*)addr_text, addr_color, 0, 1);
  DrawTextTrans(pixels + x * 7 * pitch32 + strlen(addr_text) * 5, surface->pitch, rect->w - strlen(addr_text) * 5, (UTF8*)dis_str.c_str(), color, 0, 1);
 }

 if(NeedDisAddrChange)
 {
  DisAddr = DisBlissA[indexcow + NeedDisAddrChange];
  NeedDisAddrChange = 0;
 }

 CurRegLongName = "";
 CurRegDetails = "";

 for(int rp = 0; rp < 5; rp++)
 {
  if(CurGame->Debugger->Regs[rp])
   DrawRegs(CurGame->Debugger->Regs[rp], surface, pixels + rect->w - RegsTotalWidth + RegsColsPixOffset[rp], (InRegs == rp + 1) ? (int)RegsPos : -1); // 175
 }

 DrawTextTrans(pixels + 28 * 7 * pitch32, surface->pitch, surface->w, (UTF8*)CurRegLongName.c_str(), MK_COLOR_A(0xa0, 0xa0, 0xFF, 0xFF), TRUE, 1);
 DrawTextTrans(pixels + 29 * 7 * pitch32, surface->pitch, surface->w, (UTF8*)CurRegDetails.c_str(), MK_COLOR_A(0x60, 0xb0, 0xFF, 0xFF), TRUE, 1);

 if(!InRegs && CurGame->Debugger->GetBranchTrace)
 {
  std::vector<uint32> btrace = CurGame->Debugger->GetBranchTrace();
  uint32 *btpixels = pixels + (28 * 7) * pitch32;
  char strbuf[256];
  int strbuf_len = 0;

  for(int y = 0; y < btrace.size() / 2; y++)
  {
   if(CurGame->Debugger->LogAddrBits == 32)
    strbuf_len += sprintf(strbuf + strbuf_len, "%08X→", btrace[y]);
   else
    strbuf_len += sprintf(strbuf + strbuf_len, "%04X→", btrace[y]);
  }
  DrawTextTrans(btpixels, surface->pitch, rect->w, (UTF8*)strbuf, MK_COLOR_A(0x60, 0xb0, 0xfF, 0xFF), TRUE, 1);

  strbuf_len = 0;
  for(int y = btrace.size() / 2; y < btrace.size(); y++)
  {
   if(CurGame->Debugger->LogAddrBits == 32)
    strbuf_len += sprintf(strbuf + strbuf_len, "%08X→", btrace[y]);
   else
    strbuf_len += sprintf(strbuf + strbuf_len, "%04X→", btrace[y]);
  }
  strbuf[strbuf_len - 1] = 0; // Get rid of the trailing →
  DrawTextTrans(btpixels + 7 * pitch32, surface->pitch, rect->w, (UTF8*)strbuf, MK_COLOR_A(0x60, 0xb0, 0xfF, 0xFF), TRUE, 1);
 }

 // Draw memory watch section
 {
  uint32 *watchpixels = pixels + ((30 * 7)) * pitch32;
 
  for(int y = 0; y < 4; y++)
  {
   uint32 *row = watchpixels + y * pitch32 * 7;
   char tbuf[256];
   char asciistr[32 + 1];
   uint32 ewa;
   uint32 ewa_bits;
   uint32 ewa_mask;

   asciistr[16] = 0;  

   if(WatchLogical)
   {
    ewa_bits = CurGame->Debugger->LogAddrBits;
    ewa = WatchAddr;
   }
   else
   {
    ewa_bits = CurGame->Debugger->PhysAddrBits;
    ewa = WatchAddrPhys;
   }
   
   ewa_mask = ((uint64)1 << ewa_bits) - 1;

   if(ewa_bits <= 16)
    snprintf(tbuf, 256, "%04X: ", (ewa + y * 32) & ewa_mask);
   else if(ewa_bits <= 24)
    snprintf(tbuf, 256, "%06X: ", (ewa + y * 32) & ewa_mask);
   else
    snprintf(tbuf, 256, "%08X: ", (ewa + y * 32) & ewa_mask);

   row += DrawTextTrans(row, surface->pitch, rect->w, (UTF8 *)tbuf, MK_COLOR_A(0xa0, 0xa0, 0xFF, 0xFF), 0, 1);
   for(int x = 0; x < 32; x++)
   {
    uint8 zebyte = CurGame->Debugger->MemPeek((ewa + y * 32 + x) & ewa_mask, 1, 1, WatchLogical);
    uint32 bcolor = MK_COLOR_A(0xFF, 0xFF, 0xFF, 0xFF);

    if(x & 1)
     bcolor = MK_COLOR_A(0xD0, 0xFF, 0xF0, 0xFF);
    if(!(x & 0x7))
     bcolor = MK_COLOR_A(0xFF, 0x80, 0xFF, 0xFF);
    asciistr[x] = zebyte;
    if(zebyte & 0x80 || !zebyte)
     asciistr[x] = '.';

    if(x == 16) row += 7;

    snprintf(tbuf, 256, "%02X", zebyte);
    row += DrawTextTrans(row, surface->pitch, rect->w, (UTF8*)tbuf, bcolor, 0, 1) + 2;
   }
   DrawTextTrans(row + 5, surface->pitch, rect->w, (UTF8 *)asciistr, MK_COLOR_A(0xFF, 0xFF, 0xFF, 0xFF), 0, 1);
  }
 }  

 if(InPrompt)
  DrawPrompt(surface, rect);

 LockGameMutex(0);
}

// Function called from game thread
static void CPUCallback(uint32 PC)
{
 if((NeedStep == 2 && !InSteppingMode) || NeedBreak)
 {
  DisAddr = PC;
  NeedStep = 0;
  InSteppingMode = 1;
  NeedBreak = 0;
 }

 if(NeedStep == 1)
 {
  DisAddr = PC;
  NeedStep = 0;
 }

 while(InSteppingMode && GameThreadRun)
 {
  DebuggerFudge();
  if(NeedStep == 2)
  {
   NeedStep--;
   break;
  }
  if(NeedRun)
  {
   NeedStep = 0;
   NeedRun = 0;
   InSteppingMode = 0;
  }
 }
 if(NeedRun) NeedRun = 0;
}

// Function called from game thread
static void BPCallback(uint32 PC)
{
 NeedBreak = 1;
 IsActive = 1;
}

// Function called from game thread, input driver code.
void Debugger_ForceStepIfStepping(void)
{
 if(InSteppingMode)
  NeedStep = 2;
}

// Call this function from any thread:
bool Debugger_IsActive(void)
{
 return(IsActive);
}


// Call this function from the game thread:
bool Debugger_Toggle(void)
{
 if(CurGame->Debugger)
 {
  IsActive = !IsActive;

  if(IsActive)
  {
   if(NeedInit)
   {
    NeedInit = FALSE;
    WatchAddr = CurGame->Debugger->DefaultWatchAddr;
    DisAddr = CurGame->Debugger->GetRegister("PC", NULL);

    RegsCols = 0;
    RegsTotalWidth = 0;

    memset(RegsColsCounts, 0, sizeof(RegsColsCounts));

    int pw_offset = 0;
    for(int r = 0; r < 5; r++)
    {
     uint32 pw = 0;

     if(CurGame->Debugger->Regs[r])
     {
      for(int x = 0; CurGame->Debugger->Regs[r][x].bsize; x++)
      {
       int tmp_pw = 5 * (strlen(CurGame->Debugger->Regs[r][x].name.c_str()) + CurGame->Debugger->Regs[r][x].bsize * 2 + 2 + 2);
       if(tmp_pw > pw)
 	 pw = tmp_pw;

       RegsColsCounts[r]++;
      }
      RegsCols++;
     }
     RegsColsPixOffset[r] = pw_offset;
     pw_offset += pw;
    }
    RegsTotalWidth = pw_offset;
    NeedHooksInstalled = TRUE;
   }

   if(NeedHooksInstalled)
   {
    NeedHooksInstalled = FALSE;
    CurGame->Debugger->SetCPUCallback(CPUCallback);
    CurGame->Debugger->SetBPCallback(BPCallback);
    ParseBreakpoints(ReadBreakpoints, BPOINT_READ);
    ParseBreakpoints(WriteBreakpoints, BPOINT_WRITE);
    ParseBreakpoints(IOReadBreakpoints, BPOINT_IO_READ);
    ParseBreakpoints(IOWriteBreakpoints, BPOINT_IO_WRITE);
    ParseBreakpoints(AuxReadBreakpoints, BPOINT_AUX_READ);
    ParseBreakpoints(AuxReadBreakpoints, BPOINT_AUX_READ);

    if(GfxDebugging)
     GfxDebugger_SetActive(TRUE);
   }
  }
  else // Disabled, yahyahyah
  {
   // Only uninstall our hooks if we don't have any active breakpoints.
   if(!ReadBreakpoints.size() && !WriteBreakpoints.size() && !IOReadBreakpoints.size() && 
	!IOWriteBreakpoints.size() && !AuxReadBreakpoints.size() && !AuxWriteBreakpoints.size())
   {
    NeedHooksInstalled = TRUE;
    CurGame->Debugger->SetCPUCallback(FALSE);
    CurGame->Debugger->SetBPCallback(FALSE);
    GfxDebugger_SetActive(FALSE);
   }
  }
 }
 return(IsActive);
}


#include "../ConvertUTF.h"

static void Stuff_KB_Buffer(const std::string &zestring)
{
 for(int x = 0; x < zestring.size(); x++)
 {
  char short_buf[2];

  short_buf[0] = zestring[x];
  short_buf[1] = 0;

  kb_buffer.push_back(std::string(short_buf));
  kb_cursor_pos++;
 }
}

// Called from the main thread
void Debugger_Event(const SDL_Event *event)
{
  if(event->type == SDL_KEYDOWN && event->key.keysym.mod & KMOD_ALT)
  {
   switch(event->key.keysym.sym)
   {
    case SDLK_1: GfxDebugging = FALSE; GfxDebugger_SetActive(FALSE); break;
    case SDLK_2: GfxDebugging = TRUE; 
		 GfxDebugger_SetActive(TRUE); 
		 break;
    default: break;
   }
  }
  if(GfxDebugging)
  {
   GfxDebugger_Event(event);
   return;
  }
  switch(event->type)
  {
   case SDL_KEYDOWN:
        if(event->key.keysym.mod & KMOD_ALT)
         break;

        if(InPrompt)
        {
         switch(event->key.keysym.sym)
         {
          case SDLK_HOME:
                kb_cursor_pos = 0;
                break;
          case SDLK_END:
                kb_cursor_pos = kb_buffer.size();
                break;
	  case SDLK_LEFT:
		if(kb_cursor_pos)
		 kb_cursor_pos--;
		break;
	  case SDLK_RIGHT:
		if(kb_cursor_pos < kb_buffer.size())
		 kb_cursor_pos++;
		break;
          case SDLK_RETURN:
                 {
                  std::string concat_str;
                  for(unsigned int i = 0; i < kb_buffer.size(); i++)
                   concat_str += kb_buffer[i];

                  char *tmp_c_str = strdup(concat_str.c_str());
                  kb_buffer.clear();
		  kb_cursor_pos = 0;

                  if(InPrompt == DisGoto)
                  {
                   trio_sscanf(tmp_c_str, "%08X", &DisAddr);
                   DisAddr &= ((1ULL << CurGame->Debugger->LogAddrBits) - 1);
                  }
		  else if(InPrompt == ReadBPS)
		  {
                   ReadBreakpoints = std::string(tmp_c_str);
                   ParseBreakpoints(ReadBreakpoints, BPOINT_READ);
		  }
		  else if(InPrompt == WriteBPS)
		  {
		   WriteBreakpoints = std::string(tmp_c_str);
		   ParseBreakpoints(WriteBreakpoints, BPOINT_WRITE);
		  }
                  else if(InPrompt == IOReadBPS)
                  {
                   IOReadBreakpoints = std::string(tmp_c_str);
                   ParseBreakpoints(IOReadBreakpoints, BPOINT_IO_READ);
                  }
                  else if(InPrompt == IOWriteBPS)
                  {
                   IOWriteBreakpoints = std::string(tmp_c_str);
                   ParseBreakpoints(IOWriteBreakpoints, BPOINT_IO_WRITE);
                  }
                  else if(InPrompt == AuxReadBPS)
                  {
                   AuxReadBreakpoints = std::string(tmp_c_str);
                   ParseBreakpoints(AuxReadBreakpoints, BPOINT_AUX_READ);
                  }
                  else if(InPrompt == AuxWriteBPS)
                  {
                   AuxWriteBreakpoints = std::string(tmp_c_str);
                   ParseBreakpoints(AuxWriteBreakpoints, BPOINT_AUX_WRITE);
                  }
		  else if(InPrompt == ForceInt)
		  {
		   CurGame->Debugger->IRQ(atoi(tmp_c_str));
		  }
                  else if(InPrompt == PokeMe)
                  {
                   uint32 A = 0,V = 0,S = 1;
		   bool logical = 1;

		   char *meow_str = tmp_c_str;

		   if(meow_str[0] == '*')
		   {
		    meow_str++;
		    logical = 0;
		   }

		   int ssf_ret;

		   if(logical)
                    ssf_ret = trio_sscanf(tmp_c_str, "%08X %08X %d", &A, &V, &S);
		   else
		   {
		    char sa[64];

		    ssf_ret = trio_sscanf(tmp_c_str, "%63s %08X %d", sa, &V, &S);

		    A = ParsePhysAddr(sa);
		   }

		   if(ssf_ret >= 2) // Allow size to be omitted, implicit as '1'
		   {
                    A &= ((1ULL << CurGame->Debugger->LogAddrBits) - 1);

                    if(S < 1) S = 1;
                    if(S > 4) S = 4;

		    LockGameMutex(1);
                    CurGame->Debugger->MemPoke(A, V, S, 0, logical);
 		    LockGameMutex(0);
		   }
                  }
                  else if(InPrompt == PokeMeHL)
                  {
                   uint32 A = 0,V = 0,S = 1;
                   bool logical = 1;
 
                   char *meow_str = tmp_c_str;

                   if(meow_str[0] == '*')
                   {
                    meow_str++;
                    logical = 0;
                   }

                   if(trio_sscanf(meow_str, "%08X %08X %d", &A, &V, &S) >= 2) // Allow size to be omitted, implicit as '1'
                   {
                    A &= ((1ULL << CurGame->Debugger->LogAddrBits) - 1);

                    if(S < 1) S = 1;
                    if(S > 4) S = 4;

                    LockGameMutex(1);
                    CurGame->Debugger->MemPoke(A, V, S, 1, logical);
                    LockGameMutex(0);
                   }
                  }
		  else if(InPrompt == DumpMem)
		  {
		   uint32 A1, A2;
		   char fname[64];
		   char *meow_str = tmp_c_str;
		   bool logical = 1;

		   if(meow_str[0] == '*')
		   {
		    meow_str++;
		    logical = 0;
		   }

		   if(trio_sscanf(meow_str, "%08x %08x %63[^\r\n]", &A1, &A2, fname) == 3)
		   {
		    FILE *fp = fopen(fname, "wb");
		    if(fp)
		    {
		     LockGameMutex(1);
		     for(uint64 A = A1; A <= A2; A++)
		     {
		      fputc(CurGame->Debugger->MemPeek(A, 1, 1, logical), fp);
		     }
		     LockGameMutex(0);
		     fclose(fp);
		    }
		   }
		  }
                  else if(InPrompt == DumpGfxMem)
                  {
                   uint32 A1, A2;
                   char fname[64];
                   char *meow_str = tmp_c_str;
                   bool logical = 1;

                   if(meow_str[0] == '*')
                   {
                    meow_str++;
                    logical = 0;
                   }

                   if(trio_sscanf(meow_str, "%08x %08x %63[^\r\n]", &A1, &A2, fname) == 3)
                   {
                    FILE *fp = fopen(fname, "wb");
                    if(fp)
                    {
                     LockGameMutex(1);
                     for(uint64 A = A1; A <= A2; A++)
                     {
                      fputc(CurGame->Debugger->GfxMemPeek(A, 1, 1), fp);
                     }
                     LockGameMutex(0);
                     fclose(fp);
                    }
                   }
                  }
                  else if(InPrompt == EditRegs0 || InPrompt == EditRegs1 || InPrompt == EditRegs2)
                  {
                   uint32 RegValue = 0;

                   trio_sscanf(tmp_c_str, "%08X", &RegValue);
		   LockGameMutex(1);
                   CurGame->Debugger->SetRegister(PromptText, RegValue);
		   LockGameMutex(0);
                  }
                  else if(InPrompt == WatchGoto)
                  {
                   if(WatchLogical)
                   {
                    trio_sscanf(tmp_c_str, "%08X", &WatchAddr);
                    WatchAddr &= 0xFFF0;
                   }
                   else
                   {
                    trio_sscanf(tmp_c_str, "%08X", &WatchAddrPhys);
                    WatchAddrPhys &= (((uint64)1 << CurGame->Debugger->PhysAddrBits) - 1);
                    WatchAddrPhys &= ~0xF;
                   }
                  }
                  free(tmp_c_str);
                  InPrompt = None;
                 }
		 break;
	 case SDLK_ESCAPE:
		InPrompt = None;
		kb_buffer.clear();
		kb_cursor_pos = 0;

		break;
         case SDLK_BACKSPACE:
                if(kb_buffer.size() && kb_cursor_pos)
                {
                  kb_buffer.erase(kb_buffer.begin() + kb_cursor_pos - 1, kb_buffer.begin() + kb_cursor_pos);
		  kb_cursor_pos--;
                }
                break;
	  case SDLK_DELETE:
		if(kb_buffer.size() && kb_cursor_pos < kb_buffer.size())
		{
		 kb_buffer.erase(kb_buffer.begin() + kb_cursor_pos, kb_buffer.begin() + kb_cursor_pos + 1);
		}
		break;
	  default:
	   if(event->key.keysym.unicode)
	   {
            uint8 utf8_buffer[8];
            UTF8 *dest_ptr = utf8_buffer;
            memset(utf8_buffer, 0, sizeof(utf8_buffer));
            const UTF16 *start_utf16 = &event->key.keysym.unicode;
            ConvertUTF16toUTF8(&start_utf16, (UTF16 *)&event->key.keysym.unicode + 1, &dest_ptr, &utf8_buffer[8], lenientConversion);
            kb_buffer.insert(kb_buffer.begin() + kb_cursor_pos, std::string((char *)utf8_buffer));
	    kb_cursor_pos++;
	   }
	   break;
         }
	}
        else switch(event->key.keysym.sym)
        {
	 default: break;
	 case SDLK_HOME:
		if(event->key.keysym.mod & KMOD_SHIFT)
		{
		 if(WatchLogical)
		  WatchAddr = 0;
		 else
		  WatchAddrPhys = 0;
		}
		else
		 DisAddr = 0x0000;
		break;
	 case SDLK_END:
		if(event->key.keysym.mod & KMOD_SHIFT)
		{
                 if(WatchLogical)
                  WatchAddr = ((1ULL << CurGame->Debugger->LogAddrBits) - 1) & ~0x7F;
                 else
                  WatchAddrPhys = (((uint64)1 << CurGame->Debugger->PhysAddrBits) - 1) & ~0x7F;
		}
		else
		 DisAddr = ((1ULL << CurGame->Debugger->LogAddrBits) - 1);
		break;

         case SDLK_PAGEUP:
          if(event->key.keysym.mod & KMOD_SHIFT)
	  {
	   if(WatchLogical)
            WatchAddr = (WatchAddr - 0x80) & ((1ULL << CurGame->Debugger->LogAddrBits) - 1);
	   else
	    WatchAddrPhys = (WatchAddrPhys - 0x80) & (((uint64)1 << CurGame->Debugger->PhysAddrBits) - 1);
	  }
          else
	   NeedDisAddrChange = -11;
          break;
	 case SDLK_PAGEDOWN: 
          if(event->key.keysym.mod & KMOD_SHIFT) 
	  {
	   if(WatchLogical)
            WatchAddr = (WatchAddr + 0x80) & ((1ULL << CurGame->Debugger->LogAddrBits) - 1);
	   else
	    WatchAddrPhys = (WatchAddrPhys + 0x80) & (((uint64)1 << CurGame->Debugger->PhysAddrBits) - 1);
	  }
          else  
	   NeedDisAddrChange = 11;
          break;

	case SDLK_m:
	   WatchLogical = !WatchLogical;
	   break;

	case SDLK_LEFT:
		if(!InRegs)
		 InRegs = RegsCols;		
		else
		 InRegs = InRegs - 1;

		if(InRegs && RegsPos >= RegsColsCounts[InRegs - 1])
 		 RegsPos = RegsColsCounts[InRegs - 1] - 1;

		break;
	case SDLK_RIGHT:
		InRegs = (InRegs + 1) % (RegsCols + 1);
                if(InRegs && RegsPos >= RegsColsCounts[InRegs - 1])
                  RegsPos = RegsColsCounts[InRegs - 1] - 1;
		break;
        case SDLK_UP:
	  if(InRegs)
	  {
		if(RegsPos)
		 RegsPos--;
	  }
	  else
	  {
           if(event->key.keysym.mod & KMOD_SHIFT)
	   {
	    if(WatchLogical)
             WatchAddr = (WatchAddr - 0x10) & ((1ULL << CurGame->Debugger->LogAddrBits) - 1);
	    else
	     WatchAddrPhys = (WatchAddrPhys - 0x10) & (((uint64)1 << CurGame->Debugger->PhysAddrBits) - 1);
	   }
           else
	   {
            NeedDisAddrChange = -1;
	   }
	  }
          break;
         case SDLK_DOWN:
	  if(InRegs)
	  {
                if(RegsPos < (RegsColsCounts[InRegs - 1] - 1))
		 RegsPos++;
	  }
	  else
	  {
           if(event->key.keysym.mod & KMOD_SHIFT)
	   {
	    if(WatchLogical)
             WatchAddr = (WatchAddr + 0x10) & ((1ULL << CurGame->Debugger->LogAddrBits) - 1);
	    else
	     WatchAddrPhys = (WatchAddrPhys + 0x10) & (((uint64)1 << CurGame->Debugger->PhysAddrBits) - 1);
	   }
           else
	   {
	    NeedDisAddrChange = 1;
	   }
	  }
          break;

         case SDLK_SPACE:
		NeedPCBPToggle = 1;
		break;
	 case SDLK_s:
		LockGameMutex(1);
		NeedStep = 2;
		LockGameMutex(0);
		break;
	 case SDLK_d:
		if(event->key.keysym.mod & KMOD_SHIFT)
		{
		 InPrompt = DumpGfxMem;
		 PromptText = "Dump Gfx Memory(start end filename)";
		}
		else
		{
		 InPrompt = DumpMem;
		 PromptText = "Dump Memory(start end filename)";
		}
		break;
	 case SDLK_w:
                if(event->key.keysym.mod & KMOD_SHIFT)
                {
		 if(event->key.keysym.mod & KMOD_CTRL)
		 {
		  InPrompt = IOWriteBPS;
		  PromptText = "I/O Write Breakpoints";
		  Stuff_KB_Buffer(IOWriteBreakpoints);
		 }
		 else
		 {
                  InPrompt = WriteBPS;
                  PromptText = "Write Breakpoints";
		  Stuff_KB_Buffer(WriteBreakpoints);
                 }
                }
                else if(event->key.keysym.mod & KMOD_CTRL)
                {
                 InPrompt = AuxWriteBPS;
                 PromptText = "Aux Write Breakpoints";
                 Stuff_KB_Buffer(AuxWriteBreakpoints);
                }
		break;

	 case SDLK_r:
		if(event->key.keysym.mod & KMOD_SHIFT)
		{
		 if(event->key.keysym.mod & KMOD_CTRL)
		 {
		  InPrompt = IOReadBPS;
		  PromptText = "I/O Read Breakpoints";
		  Stuff_KB_Buffer(IOReadBreakpoints);
		 }
		 else
		 {
		  InPrompt = ReadBPS;
		  PromptText = "Read Breakpoints";
		  Stuff_KB_Buffer(ReadBreakpoints);
		 }
		}
                else if(event->key.keysym.mod & KMOD_CTRL)
                {
                 InPrompt = AuxReadBPS;
                 PromptText = "Aux Read Breakpoints";
                 Stuff_KB_Buffer(AuxReadBreakpoints);
                }
		else
		 NeedRun = 1;
		break;

	 case SDLK_i:
	 	if(!InPrompt)
		{
		 InPrompt = ForceInt;
		 PromptText = "Force Interrupt";
		}
		break;
	 case SDLK_p:
		if(!InPrompt)
		{
		 if(event->key.keysym.mod & KMOD_SHIFT)
		 {
		  InPrompt = PokeMeHL;
		  PromptText = "HL Poke(address value size)";
		 }
		 else
		 {
		  InPrompt = PokeMe;
		  PromptText = "Poke(address value size)";
		 }
		}
		break;

	  case SDLK_RETURN:
		 {
		  char buf[64];

		  if(event->key.keysym.mod & KMOD_SHIFT)
		  {
		   InPrompt = WatchGoto;
		   PromptText = "Watch Address";
                   snprintf(buf, 64, "%08X", WatchLogical ? WatchAddr : WatchAddrPhys);
	 	  }
		  else
		  {
		   if(InRegs)
		   {
		    InPrompt = (PromptType)(EditRegs0 + InRegs - 1);
		    PromptText = CurGame->Debugger->Regs[InRegs - 1][RegsPos].name;
		    int len = CurGame->Debugger->Regs[InRegs - 1][RegsPos].bsize;

		    uint32 RegValue = CurGame->Debugger->GetRegister(PromptText, NULL);

		    if(len == 1)
		     snprintf(buf, 64, "%02X", RegValue);
		    else if(len == 2)
		     snprintf(buf, 64, "%04X", RegValue);
		    else
		     snprintf(buf, 64, "%08X", RegValue);
		   }
		   else
		   {
		    InPrompt = DisGoto;
		    PromptText = "Disassembly Address";
                    snprintf(buf, 64, "%04X", DisAddr);
		   }
		  }

		  for(size_t x = 0; x < strlen(buf); x++)
		  {
		   char short_buf[2];

		   short_buf[0] = buf[x];
		   short_buf[1] = 0;

		   kb_buffer.push_back(std::string(short_buf));
		   kb_cursor_pos++;
		  }

		 }
	         break;
         }
         break;
  }
}

