/*
	WinSTon

	Disassembly Control

Common Control stuff
http://support.microsoft.com/support/kb/articles/Q186/1/76.ASP
Reported by 'Daniel Gustafsson'


*DONE* 0. Do reset stuff to remove MessageBox crap...
*DONE* 1. 'Reset' short-cut key
*DONE* 1. Add extra line and move up...
*DONE* 2. Get 'address' edit box to work
*DONE* 3. Remove 'pDisassFunctions' and put into element in 'DecodeTable[]'. Nice
*DONE!!!* 4. Need to get scroller to move up/down accordingly to instruction size. Nasty
*DONE* 7. Get to trace and work through as normal...
*DONE* 5. Need 'pop-up' menu to work again
*DONE* 6. Draw > and O icons by instructions. Do as '\tmove.b' and '\1\tmove.b'. Nice!
*DONE* 7. Why so bloody slow???
8. Make sure all menu stuff works, eg 'Skip' instruction(disassemble to find next instruction address!)
1. Don't show code AFTER 0x1000000?
10. Clean up(copy crap to duff file) all debugger files...
11. Get Memory/MFP screens to work at same time! Wooh!

*DONE*  1. Get 'update' to work correctly when set/clear breakpoints etc...
*DONE*  2. Make faster, why so slow?
*DONE*  3. Does all work?
  3. Don't show custom interrupts in 'pending' display
  4. Check short-cut keys/skip etc... all works OK

  5. Clean code and remove crap...
*/

#include "..\includes\winston.h"
#include "..\includes\debug.h"
#include "..\includes\debugger.h"
#include "..\includes\debuggerdisass.h"
#include "..\includes\debuggertab.h"
#include "..\includes\decode.h"
#include "..\includes\dialog.h"
#include "..\includes\disass.h"
#include "..\includes\ikbd.h"
#include "..\includes\int.h"
#include "..\includes\mfp.h"
#include "..\includes\stmemory.h"
#include "..\includes\video.h"

#ifdef USE_DEBUGGER

#define LINES_IN_DISASS_LISTVIEW	12

int DisassListView_Address_Tabs[] = { 0 };
int DisassListView_OpCode_Tabs[] = { 0 };
int DisassListView_Instruction_Tabs[] = { 0,8,20,95 };

int ScrollAddress=0;
int SelMarkAddress=0,NewSelMarkAddress=0;

unsigned long ItemAddresses[LINES_IN_DISASS_LISTVIEW];

//-----------------------------------------------------------------------
/*
	Init Disassembly Control
*/
void Debugger_Disass_Init(HWND hWnd)
{
	char szString[256];

	// Set to select full-row
	ListView_SetExtendedListViewStyle(GetDlgItem(DebughWnd,IDC_DISASS_LISTVIEW),LVS_EX_FULLROWSELECT|LVS_EX_SUBITEMIMAGES);

	// Add Columns
	Dialog_AddListViewColumn(DebughWnd,IDC_DISASS_LISTVIEW,0,"Address",100);
	Dialog_AddListViewColumn(DebughWnd,IDC_DISASS_LISTVIEW,1,"OpCode",150);
	Dialog_AddListViewColumn(DebughWnd,IDC_DISASS_LISTVIEW,2,"Instruction",280);

	// 0...0xFFFFFF
	ListView_SetItemCountEx(GetDlgItem(DebughWnd,IDC_DISASS_LISTVIEW),0x1000000,LVSICF_NOSCROLL);

	// Set default address
	sprintf(szString,"%X",0);
	SetDlgItemText(DebughWnd,IDC_DISASS_EDIT,szString);
}


//-----------------------------------------------------------------------
/*
	Update cycle count display and pending interrupts
*/
void Debugger_Disass_UpdateCycleInterrupts(void)
{
	char TmpStr[256];

	// Cycles, show Frame/Line + HBL
	sprintf(TmpStr,"%d(%d)",Int_FindFrameCycles(),Int_FindFrameCycles()&511);
	SendDlgItemMessage(DebughWnd,IDC_DISASS_CYCLES_EDIT,WM_SETTEXT,0,(LONG)TmpStr);
	sprintf(TmpStr,"%d",nHBL);
	SendDlgItemMessage(DebughWnd,IDC_DISASS_HBL_EDIT,WM_SETTEXT,0,(LONG)TmpStr);
	
	// Show next Pending interrupt
	sprintf(TmpStr,"%d",PendingInterruptCount);
	SendDlgItemMessage(DebughWnd,IDC_DISASS_PENDING_EDIT,WM_SETTEXT,0,(LONG)TmpStr);
	if (PendingInterruptFunction==InterruptHandler_Debugger)
		SendDlgItemMessage(DebughWnd,IDC_DISASS_PENDING_STATIC,WM_SETTEXT,0,(LONG)"Debugger");
	else if (PendingInterruptFunction==IKBD_InterruptHandler_ACIA)
		SendDlgItemMessage(DebughWnd,IDC_DISASS_PENDING_STATIC,WM_SETTEXT,0,(LONG)"IKBD");
	else if (PendingInterruptFunction==MFP_InterruptHandler_TimerC)
		SendDlgItemMessage(DebughWnd,IDC_DISASS_PENDING_STATIC,WM_SETTEXT,0,(LONG)"TimerC");
	else if (PendingInterruptFunction==MFP_InterruptHandler_TimerD)
		SendDlgItemMessage(DebughWnd,IDC_DISASS_PENDING_STATIC,WM_SETTEXT,0,(LONG)"TimerD");
	else if (PendingInterruptFunction==Video_InterruptHandler_VBL)
		SendDlgItemMessage(DebughWnd,IDC_DISASS_PENDING_STATIC,WM_SETTEXT,0,(LONG)"VBL");
	else if (PendingInterruptFunction==Video_InterruptHandler_EndLine)
		SendDlgItemMessage(DebughWnd,IDC_DISASS_PENDING_STATIC,WM_SETTEXT,0,(LONG)"EndLine");
	else if (PendingInterruptFunction==Video_InterruptHandler_HBL)
		SendDlgItemMessage(DebughWnd,IDC_DISASS_PENDING_STATIC,WM_SETTEXT,0,(LONG)"HBL");
	else
		SendDlgItemMessage(DebughWnd,IDC_DISASS_PENDING_STATIC,WM_SETTEXT,0,(LONG)"???");
}

//-----------------------------------------------------------------------
/*
	Build strings for ListView
*/
unsigned long Debugger_Disass_BuildLine(unsigned long Address, char *pszAddressString, char *pszOpCodeString, char *pszInstructionString)
{
	// Disassemble instruction
	DisPC = Address;
	Disass_DiassembleLine();

	// Build strings
	sprintf(pszAddressString,"%8.8X",Address);
	sprintf(pszOpCodeString,"%s",szOpData);
	sprintf(pszInstructionString," \t \t%s",szOpString);
	if (Address==PC)
		pszInstructionString[0] = CHAR_CURSOR;			// PC hi-light
	if (Address==InstructionBreakpoint)
		pszInstructionString[2] = CHAR_BREAKPOINT;		// Breakpoint hi-light

	return(DisPC);
}

//-----------------------------------------------------------------------
/*
	Draw Disassembly Control
*/
void Debugger_Disass_Draw(DRAWITEMSTRUCT *pDrawItem)
{
	char szStrings[3][1024];
	char *pszStrings[3] = { szStrings[0],szStrings[1],szStrings[2] };
	int *pszTabs[3] = { DisassListView_Address_Tabs,DisassListView_OpCode_Tabs,DisassListView_Instruction_Tabs };
	unsigned long Address;
	int i;

	// OK, have we moved from the last time?
	Address = GetScrollPos(pDrawItem->hwndItem,SB_VERT);
	if ((int)Address!=ScrollAddress)						// Yep, set new scroll position to correct instruction
		Debugger_Disass_OnScroll();

	// Draw each line in list view
	ScrollAddress = Address = GetScrollPos(pDrawItem->hwndItem,SB_VERT);
	for(i=0; i<LINES_IN_DISASS_LISTVIEW; i++) {
		ItemAddresses[i] = Address;
		// Draw text
		Address = Debugger_Disass_BuildLine(Address,pszStrings[0],pszStrings[1],pszStrings[2]);
		Debugger_Tab_DrawListViewStrings(pDrawItem,pszStrings,pszTabs,3,i);
	}

	// Reset hi-light
	Debugger_Disass_HighlightAddress(NewSelMarkAddress);
	
	// Show current cycles, and pending interrupts
	Debugger_Disass_UpdateCycleInterrupts();
}

//-----------------------------------------------------------------------
/*
	Update 'ItemAddresses' array
*/
void Debugger_Disass_UpdateItemAddresses(unsigned long Address)
{
	char szStrings[3][1024];
	char *pszStrings[3] = { szStrings[0],szStrings[1],szStrings[2] };
	int i;

	// Run through disassembly
	for(i=0; i<LINES_IN_DISASS_LISTVIEW; i++) {
		ItemAddresses[i] = Address;
		Address = Debugger_Disass_BuildLine(Address,pszStrings[0],pszStrings[1],pszStrings[2]);
	}
}

//-----------------------------------------------------------------------
/*
	Set Disassembly view to address
*/
void Debugger_Disass_SetAddress(unsigned long Address)
{
	int OldAddress;

	// Limit to 24-bit
	Address &= 0x00ffffff;

	ScrollAddress = OldAddress = GetScrollPos(GetDlgItem(DebughWnd,IDC_DISASS_LISTVIEW),SB_VERT);
	ListView_Scroll(GetDlgItem(DebughWnd,IDC_DISASS_LISTVIEW),0,-OldAddress*LISTVIEW_ITEMHEIGHT);
	ListView_Scroll(GetDlgItem(DebughWnd,IDC_DISASS_LISTVIEW),0,Address*LISTVIEW_ITEMHEIGHT);

	// And update 'ItemAddresses' array
	Debugger_Disass_UpdateItemAddresses(Address);
}

//-----------------------------------------------------------------------
/*
	Scan list of addresses in list view and return index, or -1 if fails
*/
int Debugger_Disass_IsAddressInView(unsigned long Address)
{
	int i;

	// Scan to see if address is in-view
	for(i=0; i<LINES_IN_DISASS_LISTVIEW; i++) {
		if (ItemAddresses[i]==Address)
			return(i);
	}

	// Not in view!
	return(-1);
}

//-----------------------------------------------------------------------
/*
	Highlight Disassembly address
*/
void Debugger_Disass_HighlightAddress(unsigned long Address)
{
	HWND hItem;
	int ScrollItem;

	// Limit to 24-bit
	Address &= 0x00ffffff;

	if ((int)Address!=SelMarkAddress) {
		SelMarkAddress = Address;

		// Un-select everything
		hItem = GetDlgItem(DebughWnd,IDC_DISASS_LISTVIEW);
		ListView_SetItemState(hItem,-1,0,LVIS_SELECTED|LVIS_FOCUSED);
		// Is in-view?
		ScrollItem = Debugger_Disass_IsAddressInView(Address);
		if (ScrollItem!=-1) {
			// Select the item we need
			Address = GetScrollPos(hItem,SB_VERT);
			ListView_SetItemState(hItem,Address+ScrollItem,LVIS_SELECTED|LVIS_FOCUSED ,LVIS_SELECTED|LVIS_FOCUSED);
		}
	}
}

//-----------------------------------------------------------------------
/*
	Look back 2-bytes at a time until find instruction(if fail just return 'Address-2')
*/
unsigned long Debugger_Disass_StepBackOneInstruction(unsigned long EndAddress)
{
	unsigned long Address;
	int i;

	// Scan back upto 10 bytes
	Address = EndAddress;
	for(i=0; (i<MAX_68000_INSTRUCTION_SIZE/sizeof(short int)) && (Address>2); i++) {
		Address -= 2;
		DisPC = Address;
		// Was this a valid instruction?
		if (Disass_DiassembleLine())
			return(Address);
	}

	// Failed to find valid instruction, just got back two bytes
	Address = ItemAddresses[0]-MIN_68000_INSTRUCTION_SIZE;
	return(Address);
}

//-----------------------------------------------------------------------
/*
	Look back 'Index' lines
*/
unsigned long Debugger_Disass_StepBackOnePage(unsigned long EndAddress,int Index)
{
	char szStrings[3][1024];
	char *pszStrings[3] = { szStrings[0],szStrings[1],szStrings[2] };
	unsigned long Address;
	int i;

	// Step back one instruction at a time until the one we started on reaches the bottom of the list
	do {
		Address = Debugger_Disass_StepBackOneInstruction(ItemAddresses[0]);
		for(i=0; i<LINES_IN_DISASS_LISTVIEW; i++) {
			ItemAddresses[i] = Address;
			Address = Debugger_Disass_BuildLine(Address,pszStrings[0],pszStrings[1],pszStrings[2]);
		}
	} while(ItemAddresses[Index]>EndAddress);

	// Return new start address
	return(ItemAddresses[0]);
}	

//-----------------------------------------------------------------------
/*
	Update to correct 'Address' when move scroller or press keys
	This routine took the best part of 2 days to get working right!!

	If I had 1p for every lost minute I've had to spend trying to get Windows to work
	in a 'slightly' logical way, I'd be a very rich man. Maybe that's how Bill Gates gets
	his money?
*/
void Debugger_Disass_OnScroll(void)
{
	HWND hItem;
	int Address,ScrollDiff;

	// Read difference in scroller to see how much moved by?
	hItem = GetDlgItem(DebughWnd,IDC_DISASS_LISTVIEW);
	Address = GetScrollPos(hItem,SB_VERT);
	ScrollDiff = Address-ScrollAddress;

	// Amount scrolled by?
	if (ScrollDiff==0)
		return;

	if (ScrollDiff>0) {
		// Down
		if (ScrollDiff>=LINES_IN_DISASS_LISTVIEW) {
			Debugger_Disass_SetAddress(Address);
			NewSelMarkAddress = -1;
		}
		else {
			Debugger_Disass_SetAddress(ItemAddresses[ScrollDiff]);
			NewSelMarkAddress = ItemAddresses[LINES_IN_DISASS_LISTVIEW-2];
		}
	}
	else { // (ScrollDiff<0)
		if (ScrollDiff<=-LINES_IN_DISASS_LISTVIEW) {
			Debugger_Disass_SetAddress(Address);
			NewSelMarkAddress = -1;
		}
		else {
			Debugger_Disass_SetAddress(Debugger_Disass_StepBackOnePage(ItemAddresses[0],-ScrollDiff));
			NewSelMarkAddress = ItemAddresses[0];
		}
	}
}

//-----------------------------------------------------------------------
/*
	Highlight address, move list view to correct place if need to
*/
void Debugger_Disass_SetHighlightAddress(unsigned long Address)
{
	HWND hItem;
	int ScrollItem;

	// Limit to 24-bit
	Address &= 0x00ffffff;

	// Is in-view?
	hItem = GetDlgItem(DebughWnd,IDC_DISASS_LISTVIEW);
	ScrollItem = Debugger_Disass_IsAddressInView(Address);
	if (ScrollItem!=-1) {
		// Select the item we need
		NewSelMarkAddress = Address;
		Address = GetScrollPos(hItem,SB_VERT);
		ListView_RedrawItems(hItem,Address,Address+LINES_IN_DISASS_LISTVIEW);
	}
	else {
		// Item not in view, jump to
		Debugger_Disass_SetAddress(Address);
		NewSelMarkAddress = Address;
		SelMarkAddress = -1;
		Debugger_Disass_HighlightAddress(Address);
		ScrollAddress = Address = GetScrollPos(hItem,SB_VERT);
		ListView_RedrawItems(hItem,Address,Address+LINES_IN_DISASS_LISTVIEW);
	}

	// Update now so can see running through instructions, one at a time
	UpdateWindow(hItem);
}

//-----------------------------------------------------------------------
/*
	Read Highlight address
*/
unsigned long Debugger_Disass_GetHighlightAddress(void)
{
	HWND hItem;
	int Address;

	// Read which item is selected, and offset from top of list view
	hItem = GetDlgItem(DebughWnd,IDC_DISASS_LISTVIEW);
	Address = ListView_GetSelectionMark(hItem);
	Address -= GetScrollPos(hItem,SB_VERT);

	return(ItemAddresses[Address]);
}

#endif	//USE_DEBUGGER
