//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "stdio.h"
#include "FormDebug.h"
#include "z80.h"
#include "OSCABoard.h"
#include "FormMain.h"
#include "FormOptions.h"
#include "utils.h"

//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TDebugForm *DebugForm;

int LastMemPos 		= -1;
int LastPCPos 			= -1;
int BreakPt				= 0x10000;
int ThrotMaint			= 0;
int Throttle 			= 0;
unsigned int cycles 	= 0;
unsigned int frames 	= 0;
unsigned int timcnt 	= 0;
DWORD lastTimeUpdate	= 0;

int fps = 0;

//---------------------------------------------------------------------------
__fastcall TDebugForm::TDebugForm(TComponent* Owner)
  : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TDebugForm::UpdateStuff(TObject *)
{
  //
}
//---------------------------------------------------------------------------

void __fastcall TDebugForm::ScrollChange(TObject *)
{
  	Repopulate();
}
//---------------------------------------------------------------------------

void __fastcall TDebugForm::PrepareGrids(TObject *)
{
	// Set initial Column Widths on the Memory Grid
	MemoryGrid->ColWidths[0] = 40;
	MemoryGrid->ColWidths[9] = 80;

	// And set Scrollbar remembered position to -1
	// (to force initial update)
	LastMemPos = -1;
	Repopulate();
}
//---------------------------------------------------------------------------
void __fastcall TDebugForm::Repopulate(void)
{
	Z80_Regs ZR;

	UnicodeString CmpStr, locstr;
	int MemX, MemY, DALoop;
	byte ramval;
	UnicodeString AscDmp;
	char buf[100];

	z80emu->Z80_GetRegs(&ZR);

	// Populate Flag Boxes
	// Primary Flags
	FlagS->Checked = ((ZR.AF.B.l & S_FLAG) > 0);
	FlagZ->Checked = ((ZR.AF.B.l & Z_FLAG) > 0);
	Flag5->Checked = ((ZR.AF.B.l & U1_FLAG) > 0);
	FlagH->Checked = ((ZR.AF.B.l & H_FLAG) > 0);
	Flag3->Checked = ((ZR.AF.B.l & U2_FLAG) > 0);
	FlagP->Checked = ((ZR.AF.B.l & V_FLAG) > 0);
	FlagN->Checked = ((ZR.AF.B.l & N_FLAG) > 0);
	FlagC->Checked = ((ZR.AF.B.l & C_FLAG) > 0);

	// Alternate Flags
	FlagAS->Checked = ((ZR.AF2.B.l & S_FLAG) > 0);
	FlagAZ->Checked = ((ZR.AF2.B.l & Z_FLAG) > 0);
	FlagA5->Checked = ((ZR.AF2.B.l & U1_FLAG) > 0);
	FlagAH->Checked = ((ZR.AF2.B.l & H_FLAG) > 0);
	FlagA3->Checked = ((ZR.AF2.B.l & U2_FLAG) > 0);
	FlagAP->Checked = ((ZR.AF2.B.l & V_FLAG) > 0);
	FlagAN->Checked = ((ZR.AF2.B.l & N_FLAG) > 0);
	FlagAC->Checked = ((ZR.AF2.B.l & C_FLAG) > 0);

	// Draw in Register Values
	sprintf(buf, "%04x", ZR.AF.W.l);
	EditAF->Text = (UnicodeString)buf;
	sprintf(buf, "%04x", ZR.BC.W.l);
	EditBC->Text = (UnicodeString)buf;
	sprintf(buf, "%04x", ZR.DE.W.l);
	EditDE->Text = (UnicodeString)buf;
	sprintf(buf, "%04x", ZR.HL.W.l);
	EditHL->Text = (UnicodeString)buf;
	sprintf(buf, "%04x", ZR.SP.W.l);
	EditSP->Text = (UnicodeString)buf;
	sprintf(buf, "%04x", ZR.IX.W.l);
	EditIX->Text = (UnicodeString)buf;
	sprintf(buf, "%04x", ZR.IY.W.l);
	EditIY->Text = (UnicodeString)buf;
	sprintf(buf, "%04x", ZR.PC.W.l);
	EditPC->Text = (UnicodeString)buf;
	sprintf(buf, "%04x", ZR.AF2.W.l);
	EditAAF->Text = (UnicodeString)buf;
	sprintf(buf, "%04x", ZR.BC2.W.l);
	EditABC->Text = (UnicodeString)buf;
	sprintf(buf, "%04x", ZR.DE2.W.l);
	EditADE->Text = (UnicodeString)buf;
	sprintf(buf, "%04x", ZR.HL2.W.l);
	EditAHL->Text = (UnicodeString)buf;
	sprintf(buf, "%02x", ZR.IM);
	EditIM->Text = (UnicodeString)buf;
	sprintf(buf, "%02x", ZR.I);
	EditI->Text = (UnicodeString)buf;
	sprintf(buf, "%08x", ZR.R);
	EditR->Text = (UnicodeString)buf;

	// Update Memory View Based on the Scrollbar Position
	// (If the scrollbar has been moved)
	if (MemoryScroll->Position != LastMemPos)
	{
		// Remember new Scroll position
		LastMemPos = MemoryScroll->Position;

		// Zero out ASCII Dump
		AscDmp = L"";

		// Loop from Position*8 to Same+63
		for (DALoop = (MemoryScroll->Position << 3); DALoop < ((MemoryScroll->Position << 3) + 64); DALoop++)
		{
			// Work out X co-ord
			MemX = ((DALoop % 8) + 1);

			// && Y co-ord
			MemY = ((DALoop - (MemoryScroll->Position << 3)) / 8);

			// Set Cell to HEX representation of that memory location
			ramval = z80emu->Z80_RDSTACK(DALoop);
			sprintf(buf, "%02x", ramval);
			MemoryGrid->Cells[MemX][MemY] = (UnicodeString)buf;

			// && add to ASCII dump (. if outside 32 -> 127)
			if ((ramval > 0x1f) && (ramval < 0x80))
			{
				AscDmp = AscDmp + (char)ramval;
			}
			else
			{
				AscDmp = AscDmp + L".";
			}

			// If first column on this row
			if (MemX == 1)
			{
				sprintf(buf, "%04x", DALoop);
				MemoryGrid->Cells[0][MemY] = (UnicodeString)buf; // Draw in Address
			}

			// If last column in the row
			if (MemX == 8)
			{
				// Display ASCII Dump
				MemoryGrid->Cells[9][MemY] = AscDmp;

				// && zero for the next line
				AscDmp = L"";
			}
		}

		// Update the memory grid
		MemoryGrid->Refresh();
	}

	// Update Disassembly View
	// Only do this if Trace is checked and the PC has changed
	if ((TagSourceTrace->Checked) && (LastPCPos != ZR.PC.W.l))
	{
		// Reset line count
		MemY=0;

		// Updated last PC
		MemX = LastPCPos = ZR.PC.W.l;

		// Clear Disasm listing
		SourceList->Items->Clear();

		// Disassemble 50 lines
		while (MemY < 50)
		{
			locstr = L"";
			CmpStr = L"      ";

			// Add Addr
			sprintf(buf, "%04x: ", MemX);
			CmpStr = (UnicodeString)buf;

			// Disassemble
			if (MemX < 0x200)
			{
				MemX = MemX + z80emu->Z80_Dasm(&z80emu->BootROM[MemX], &buf[0], MemX);
			}
			else if (MemX < 0x8000)
			{
				MemX = MemX + z80emu->Z80_Dasm(&z80emu->MainRAM[MemX], &buf[0], MemX);
			}
			else
			{
				if ((z80emu->fSysMemSelect & 0x0f) < 0x02)
				{
					MemX = MemX + z80emu->Z80_Dasm(&z80emu->MainRAM[MemX], &buf[0], MemX);
				}
				else
				{
					MemX = MemX + z80emu->Z80_Dasm(
						&z80emu->MainRAM[((MemX & 0x7fff) | ((z80emu->fSysMemSelect & 0x0f) << 15))],
						&buf[0], MemX);
				}
			}

			// Add to line
			locstr = CmpStr + (UnicodeString)buf;

			// Add line to output list
			SourceList->Items->Add(locstr);

			// Move to next line
			MemY++;
		}
	}
}

void __fastcall TDebugForm::BtnStepOverClick(TObject *)
{
  int pcval, CodeSz;
  char Buf[100];

  // TODO: Fix this to work with new threaded mode
  pcval = z80emu->Z80_GetPC();
//  if ((z80emu->En_EEPROM) && (pcval<0x2000))
//	 CodeSz=z80emu->Z80_Dasm(&EEPROM[pcval], &Buf[0], pcval);
//  else
//	CodeSz=z80emu->Z80_Dasm(&RAM[pcval], &Buf[0], pcval);
  BreakPt = pcval + CodeSz;
  //BtnRun->Click();
}
//---------------------------------------------------------------------------

void __fastcall TDebugForm::DoCPU(TObject *)
{
	char buf[100];
	DWORD currentTime = ::GetTickCount();
	DWORD elapsedTime = currentTime - lastTimeUpdate;

	timcnt++;

	if (elapsedTime > 500)
	{
		//Caption = "CPS = " + IntToStr((__int64)cycles) + " / FPS = " + IntToStr((__int64)frames);
//		double mhz = (double)(cycles / (100000000.0f / (double)elapsedTime)) / 10.0f;
		double mhz = (double)(cycles / (320000000.0f / (double)elapsedTime));// / 15.0f;
		sprintf(&buf[0], "%4.1f MHz", mhz);
		MainForm->StatusBar->Panels->Items[1]->Text = (UnicodeString)buf;

		double framesDrawn = ((double)frames * 500.0f / (double)elapsedTime);
		fps = (int)framesDrawn;
		sprintf(&buf[0], "%3.0f FPS", framesDrawn);	// frames
		MainForm->StatusBar->Panels->Items[2]->Text = (UnicodeString)buf;
		MainForm->StatusBar->Refresh();

		timcnt = 0;
		lastTimeUpdate = currentTime;
		cycles = 0;
		frames = 0;
	}

	if (this->Visible)
	{
		Repopulate();
	}
}
//---------------------------------------------------------------------------

void __fastcall TDebugForm::BtnStepClick(TObject *)
{
	z80emu->SwitchRunState(RS_STEP);
	Repopulate();
}
//---------------------------------------------------------------------------

void __fastcall TDebugForm::BtnRunClick(TObject *)
{
	// If the CPU is not currently running
	if (CPUTimer->Enabled == false)
	{
		// Reset the CPU & Start Execution
		MainForm->StatusBar->Panels->Items[0]->Text = "RUN";
		BtnStep->Enabled		= false;
		BtnStepOver->Enabled	= false;
		BtnReset->Enabled		= false;
		BtnRun->Caption		= L"Stop";
		z80emu->SwitchRunState(RS_RUN);
		CPUTimer->Enabled		= true;
	}
	else
	{
		// Otherwise, halt the cpu
		MainForm->StatusBar->Panels->Items[0]->Text = "IDLE";
		CPUTimer->Enabled		= false;
		BtnStep->Enabled		= true;
		BtnStepOver->Enabled	= true;
		BtnReset->Enabled		= true;
		BtnRun->Caption		= L"Run";
		z80emu->SwitchRunState(RS_STOPPED);

  }

}

//---------------------------------------------------------------------------

void __fastcall TDebugForm::BtnResetClick(TObject *)
{
	// TODO: Check that this works with new threaded mode
	z80emu->PowerOnReset();
	Repopulate();
}

//---------------------------------------------------------------------------

void __fastcall TDebugForm::BPChange(TObject *)
{
	ChkBP->Checked = false;
}
//---------------------------------------------------------------------------

void __fastcall TDebugForm::ChkBPClick(TObject *)
{
	// TODO: Fix this to work with new threaded mode
	if (ChkBP->Checked)
	{
		BreakPt = HToI(EditBP->Text);
	}
	else
	{
		BreakPt = -1;
	}
}

//---------------------------------------------------------------------------

