/***************************************************************************
**
**    $Header:   D:/EPSLDV1/SRC/LOG/SPACODE.CPP   1.8   20 Jun 1996 09:25:28   ZJRD  $
**
**    $Log:   D:/EPSLDV1/SRC/LOG/SPACODE.CPP  $
** 
**    Rev 1.8   20 Jun 1996 09:25:28   ZJRD
** EasyPack/SLD Version 1.20a
** 
**    Rev 1.6   10 Jun 1996 10:20:06   ZJRD
** EasyPack/SLD Version 1.20
** 
**    Rev 1.4   29 May 1996 09:29:32   ZJRD
** EasyPack/SLD Version 1.95
** 
**    Rev 1.3   16 May 1996 09:06:48   ZJRD
** EasyPack/SLD Version 1.94
** 
**    Rev 1.2   10 May 1996 09:09:48   ZJRD
** EasyPack/SLD Version 1.93
** 
**    Rev 1.1   02 May 1996 10:26:26   ZJRD
** EasyPack/SLD Version 1.92
** 
**    Rev 1.1   18 Apr 1996 12:54:18   Shirley
** EasyPack/SLD Version 1.91
** 
**    Rev 1.0   12 Apr 1996 10:36:20   Shirley
** Initial revision.
** 
****************************************************************************/

/////////////////////////////////////////////////////////////////////////////
//
//  File name: SPACODE.CPP
//
//  Description: The implementation file fore class: CSpaCodeCoveragePage.
//
//  Author: Chen Jun
//
//  Date: 03/21/96
//
//  Modification:
//      1. 03/21/96, Initial version of the class: CSpaCodeCoverageDlg.
//      2. 03/29/96, CSpaCodeCoveragePage with CPropertyPage.
//
//  Copyright (C) 1996 Microtek International. All rights reserved.
//
/////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "resource.h"

#include "spagrp.h"
#include "spauser.h"
#include "spabp.h"

#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// External variables.
extern char szAppPath[_MAX_PATH+1];

STATUS AbiAbort();
STATUS AbiGetOneReg(int iRegId, UINT* uRegValue);
STATUS AbiSetQualify(QUALIFY qualifyData);
STATUS AbiCoverageGo(COVERAGEGOINFO coverageGoInfo);
STATUS AbiCoverageGet(COVERAGEGETINFO* coverageGetInfo);
STATUS AbiGetCpuStatus(UINT *uCpuStatus);
int SpaDisAsm(
    unsigned short DAsmStartAddr,	// the start address to be disassembly
    unsigned short DAsmInCodeBlkLen,// the code block length
    unsigned short &usNextAddr,
    CStringList* &pList
);
int SpaListSource(unsigned short uStartAddr, unsigned short uLen, CStringList* &pList);
int SrcIsLoaded(void);

void RestoreAllConfig(void);

/////////////////////////////////////////////////////////////////////////////
// CSpaCodeCoveragePage dialog

CSpaCodeCoveragePage::CSpaCodeCoveragePage()
	: CPropertyPage(CSpaCodeCoveragePage::IDD),
    m_uTimerID(0), m_bStop(TRUE), m_nMode(modeAsm), m_nProgress(0), m_nWhichBtn(btnStart)
{
	//{{AFX_DATA_INIT(CSpaCodeCoveragePage)
	m_bAK = FALSE;
	m_bF = FALSE;
	m_bR = FALSE;
	m_bS = FALSE;
	m_bW = FALSE;
	m_strRange1 = "";
	m_strRange2 = "";
	m_strRange3 = "";
	m_strRange4 = "";
	m_nBlock = -1;
	m_nInst = -1;
	m_bRange1 = FALSE;
	m_bRange2 = FALSE;
	m_bRange3 = FALSE;
	m_bRange4 = FALSE;
	m_strRange0 = _T("");
	//}}AFX_DATA_INIT
}

void CSpaCodeCoveragePage::DoDataExchange(CDataExchange* pDX)
{
	CPropertyPage::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CSpaCodeCoveragePage)
	DDX_Control(pDX, IDC_EDIT_SPA_DEFINE_BP, m_editBp);
	DDX_Control(pDX, IDC_BUTTON_SPA_BP, m_btnBp);
	DDX_Control(pDX, IDC_STATIC_SPA_CODE_PROGRESS, m_staticProgressBar);
	DDX_Control(pDX, IDC_BUTTON_SPA_CODE_MODE, m_btnMode);
	DDX_Control(pDX, IDC_CHECK_SPA_CODE_RANGE1, m_btnRange1);
	DDX_Control(pDX, IDC_CHECK_SPA_CODE_RANGE2, m_btnRange2);
	DDX_Control(pDX, IDC_CHECK_SPA_CODE_RANGE3, m_btnRange3);
	DDX_Control(pDX, IDC_CHECK_SPA_CODE_RANGE4, m_btnRange4);
	DDX_Control(pDX, IDC_BUTTON_SPA_RESTART, m_btnRestart);
	DDX_Control(pDX, IDC_CHECK_SPA_STATUS_AK, m_btnCheckAK);
	DDX_Control(pDX, IDC_CHECK_SPA_STATUS_F, m_btnCheckF);
	DDX_Control(pDX, IDC_CHECK_SPA_STATUS_R, m_btnCheckR);
	DDX_Control(pDX, IDC_CHECK_SPA_STATUS_S, m_btnCheckS);
	DDX_Control(pDX, IDC_CHECK_SPA_STATUS_W, m_btnCheckW);
	DDX_Control(pDX, IDC_LIST_SPA_CODE_INSTRUCTION, m_listInst);
	DDX_Control(pDX, IDC_LIST_SPA_CODE_BLOCK, m_listBlock);
	DDX_Control(pDX, IDC_COMBO_SPA_CODE_RANGE1, m_comboRange1);
	DDX_Control(pDX, IDC_COMBO_SPA_CODE_RANGE2, m_comboRange2);
	DDX_Control(pDX, IDC_COMBO_SPA_CODE_RANGE3, m_comboRange3);
	DDX_Control(pDX, IDC_COMBO_SPA_CODE_RANGE4, m_comboRange4);
	DDX_Control(pDX, IDC_BUTTON_SPA_EDIT, m_btnEdit);
	DDX_Control(pDX, IDC_BUTTON_SPA_STOP, m_btnStop);
	DDX_Control(pDX, IDC_BUTTON_SPA_START, m_btnStart);
	DDX_Check(pDX, IDC_CHECK_SPA_STATUS_AK, m_bAK);
	DDX_Check(pDX, IDC_CHECK_SPA_STATUS_F, m_bF);
	DDX_Check(pDX, IDC_CHECK_SPA_STATUS_R, m_bR);
	DDX_Check(pDX, IDC_CHECK_SPA_STATUS_S, m_bS);
	DDX_Check(pDX, IDC_CHECK_SPA_STATUS_W, m_bW);
	DDX_CBString(pDX, IDC_COMBO_SPA_CODE_RANGE1, m_strRange1);
	DDV_MaxChars(pDX, m_strRange1, 80);
	DDX_CBString(pDX, IDC_COMBO_SPA_CODE_RANGE2, m_strRange2);
	DDV_MaxChars(pDX, m_strRange2, 80);
	DDX_CBString(pDX, IDC_COMBO_SPA_CODE_RANGE3, m_strRange3);
	DDV_MaxChars(pDX, m_strRange3, 80);
	DDX_CBString(pDX, IDC_COMBO_SPA_CODE_RANGE4, m_strRange4);
	DDV_MaxChars(pDX, m_strRange4, 80);
	DDX_LBIndex(pDX, IDC_LIST_SPA_CODE_BLOCK, m_nBlock);
	DDX_LBIndex(pDX, IDC_LIST_SPA_CODE_INSTRUCTION, m_nInst);
	DDX_Check(pDX, IDC_CHECK_SPA_CODE_RANGE1, m_bRange1);
	DDX_Check(pDX, IDC_CHECK_SPA_CODE_RANGE2, m_bRange2);
	DDX_Check(pDX, IDC_CHECK_SPA_CODE_RANGE3, m_bRange3);
	DDX_Check(pDX, IDC_CHECK_SPA_CODE_RANGE4, m_bRange4);
	DDX_Text(pDX, IDC_EDIT_SPA_DEFINE_BP, m_strRange0);
	DDV_MaxChars(pDX, m_strRange0, 80);
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CSpaCodeCoveragePage, CPropertyPage)
	//{{AFX_MSG_MAP(CSpaCodeCoveragePage)
	ON_BN_CLICKED(IDC_BUTTON_SPA_EDIT, OnButtonSpaEdit)
	ON_BN_CLICKED(ID_HELP, OnHelp)
	ON_BN_CLICKED(IDC_BUTTON_SPA_START, OnButtonSpaStart)
	ON_BN_CLICKED(IDC_BUTTON_SPA_STOP, OnButtonSpaStop)
	ON_WM_SHOWWINDOW()
	ON_WM_TIMER()
	ON_LBN_SELCHANGE(IDC_LIST_SPA_CODE_BLOCK, OnSelchangeListSpaCodeBlock)
	ON_BN_CLICKED(IDC_BUTTON_SPA_RESTART, OnButtonSpaRestart)
	ON_BN_CLICKED(IDC_CHECK_SPA_CODE_RANGE1, OnCheckSpaCodeRange1)
	ON_BN_CLICKED(IDC_CHECK_SPA_CODE_RANGE2, OnCheckSpaCodeRange2)
	ON_BN_CLICKED(IDC_CHECK_SPA_CODE_RANGE3, OnCheckSpaCodeRange3)
	ON_BN_CLICKED(IDC_CHECK_SPA_CODE_RANGE4, OnCheckSpaCodeRange4)
	ON_BN_CLICKED(IDC_BUTTON_SPA_CODE_MODE, OnButtonSpaCodeMode)
	ON_BN_CLICKED(IDC_BUTTON_SPA_BP, OnButtonSpaBp)
	ON_CBN_EDITCHANGE(IDC_COMBO_SPA_CODE_RANGE1, OnEditchangeComboSpaCodeRange1)
	ON_CBN_EDITCHANGE(IDC_COMBO_SPA_CODE_RANGE2, OnEditchangeComboSpaCodeRange2)
	ON_CBN_EDITCHANGE(IDC_COMBO_SPA_CODE_RANGE3, OnEditchangeComboSpaCodeRange3)
	ON_CBN_EDITCHANGE(IDC_COMBO_SPA_CODE_RANGE4, OnEditchangeComboSpaCodeRange4)
	ON_CBN_SELCHANGE(IDC_COMBO_SPA_CODE_RANGE1, OnSelchangeComboSpaCodeRange1)
	ON_CBN_SELCHANGE(IDC_COMBO_SPA_CODE_RANGE2, OnSelchangeComboSpaCodeRange2)
	ON_CBN_SELCHANGE(IDC_COMBO_SPA_CODE_RANGE3, OnSelchangeComboSpaCodeRange3)
	ON_CBN_SELCHANGE(IDC_COMBO_SPA_CODE_RANGE4, OnSelchangeComboSpaCodeRange4)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()


/////////////////////////////////////////////////////////////////////////////
// CSpaCodeCoveragePage common routines

void CSpaCodeCoveragePage::CheckInputSyntax(void)
{
	// Check empty.
	CSpaGroupSheet* pSheet = (CSpaGroupSheet*)GetParent();
	if ( m_strRange0.IsEmpty() ) {
		pSheet->m_nErrorID = pSheet->errInput;
		return;
	}
		
	if ( m_strRange1.IsEmpty() && m_strRange2.IsEmpty() && 
		 m_strRange3.IsEmpty() && m_strRange4.IsEmpty() ) {
		pSheet->m_nErrorID = pSheet->errInput;
		return;
	}
}

void CSpaCodeCoveragePage::StopTimerAction(void)
{
	// Kill timer.
	if ( 0 != m_uTimerID ) {
		KillTimer(m_uTimerID);
		m_uTimerID = 0;
	}

	// Grayed some controls.
	m_bStop = TRUE;
	DisableControl();
}

void CSpaCodeCoveragePage::DisableControl(void)
{
	// Enable / disable buttons.
	m_btnRestart.EnableWindow(m_bStop);
	m_btnStart.EnableWindow(m_bStop);
	m_btnEdit.EnableWindow(m_bStop);
	m_btnMode.EnableWindow(m_bStop);
	m_btnBp.EnableWindow(m_bStop);
	m_editBp.EnableWindow(m_bStop);

	m_btnStop.EnableWindow(!m_bStop);
	
	// Enable combo boxes.
	m_comboRange1.EnableWindow(m_bStop);
	m_comboRange2.EnableWindow(m_bStop);
	m_comboRange3.EnableWindow(m_bStop);
	m_comboRange4.EnableWindow(m_bStop);
	
	// Enable / disable status check boxes.
	m_btnCheckAK.EnableWindow(m_bStop);
	m_btnCheckF.EnableWindow(m_bStop);
	m_btnCheckR.EnableWindow(m_bStop);
	m_btnCheckS.EnableWindow(m_bStop);
	m_btnCheckW.EnableWindow(m_bStop);

	// Enable / disable range check boxes.
	m_btnRange1.EnableWindow(m_bStop);
	m_btnRange2.EnableWindow(m_bStop);
	m_btnRange3.EnableWindow(m_bStop);
	m_btnRange4.EnableWindow(m_bStop);
	
	// Source/Assembly toggle button.
	m_btnMode.EnableWindow(0==::SrcIsLoaded());
}

void CSpaCodeCoveragePage::DetectDisplayMode(void)
{
	// Not loading any source codes.
	if ( 0 != ::SrcIsLoaded() ) {
		m_btnMode.EnableWindow(FALSE);
		return;
	}

	// Get coverage info.
	unsigned short uStart = m_CodeGetData.usAddr[m_nBlock*2];
	unsigned short uEnd = m_CodeGetData.usAddr[m_nBlock*2+1];
	if ( uEnd < uStart ) {
		m_btnMode.EnableWindow(FALSE);
		return;
	}
	unsigned short uLen = uEnd-uStart+1;

	// New a list.
	CStringList* pList = new CStringList;

	// Get C-trace info from Trace server.
	if ( 0 == ::SpaListSource(uStart, uLen, pList) ) {
		m_btnMode.EnableWindow(FALSE);
	}
	else {
		m_btnMode.EnableWindow(TRUE);
	}
	
	delete pList;
}

void CSpaCodeCoveragePage::UpdateCombo(void)
{
	// Remove all from the combo boxes.
	m_comboRange1.ResetContent();
	m_comboRange2.ResetContent();
	m_comboRange3.ResetContent();
	m_comboRange4.ResetContent();
	
	// Read user defined name.
	ReadRecord(entryRange, combo1);
	ReadRecord(entryRange, combo2);
	ReadRecord(entryRange, combo3);
	ReadRecord(entryRange, combo4);

/*
	// Add an empty string to the combo boxes.
	m_comboRange1.AddString("");
	m_comboRange2.AddString("");
	m_comboRange3.AddString("");
	m_comboRange4.AddString("");
*/

	// Select the default setting.
	int nCount = m_comboRange1.GetCount();
	if ( CB_ERR != nCount && nCount > 0 ) {
		m_comboRange1.GetLBText(0, m_strRange1);
		m_bRange1 = TRUE;
	}

	nCount = m_comboRange2.GetCount();
	if ( CB_ERR != nCount && nCount > 1 ) {
		m_comboRange2.GetLBText(1, m_strRange2);
		m_bRange2 = TRUE;
	}
			
	nCount = m_comboRange3.GetCount();
	if ( CB_ERR != nCount && nCount > 2 ) {
		m_comboRange3.GetLBText(2, m_strRange3);
		m_bRange3 = TRUE;
	}
			
	nCount = m_comboRange4.GetCount();
	if ( CB_ERR != nCount && nCount > 3 ) {
		m_comboRange4.GetLBText(3, m_strRange4);
		m_bRange4 = TRUE;
	}
}

void CSpaCodeCoveragePage::ReadRecord(int nWhichFile, int nWhichCombo)
{
	// Read from the record file.
	CString strFile(::szAppPath);
	switch ( nWhichFile ) {
//		case executionRange:
//			strFile += "SPA\\execute.dbf";
//			break;
		case entryRange:
			strFile += "SPA\\entry.dbf";
			break;
		default:
			ASSERT(FALSE);
			return;
	}

	// Assign the open flag.
    CFileStatus status;
	if ( !CFile::GetStatus(strFile, status) ) {
		return;
	}

	// Open the record file.
	UINT nOpenFlag = CFile::modeRead | CFile::typeText;
	CStdioFile* fp = new CStdioFile(strFile, nOpenFlag);
	
	// Read from the record file.
	int nMax = defineLen+startLen+endLen;
	char* pszLine = new char[nMax+4];
	while ( fp->ReadString(pszLine, nMax) ) {
		// Split string.
		char* p = pszLine;
		while ( *p ) {
			if ( '\n' == *p ) {
				*p = NULL;
				break;
			}
			p++;
		}
		CSpaGroupSheet* pSheet = (CSpaGroupSheet*)GetParent();
		pSheet->EmptyBuffer();
		pSheet->SplitRecord(CString(pszLine));
		CString strDefine(pSheet->m_strDefine);
		if ( pSheet->m_strEnd.IsEmpty() ) {
			continue;
		}
		switch ( nWhichCombo ) {
//			case combo0:
//				m_comboRange0.AddString(strDefine);
//				break;
			case combo1:
				m_comboRange1.AddString(strDefine);
				break;
			case combo2:
				m_comboRange2.AddString(strDefine);
				break;
			case combo3:
				m_comboRange3.AddString(strDefine);
				break;
			case combo4:
				m_comboRange4.AddString(strDefine);
				break;
			default:
				ASSERT(FALSE);
				break;
		}
	}
	delete []pszLine;
	
	// Close the record file.
	fp->Close();
	delete fp;
}

void CSpaCodeCoveragePage::ReadRecord(int nWhichFile, const CString& strDefine)
{
	// Read from the record file.
	CString strFile(::szAppPath);
	switch ( nWhichFile ) {
//		case executionRange:
//			strFile += "SPA\\execute.dbf";
//			break;
		case entryRange:
			strFile += "SPA\\entry.dbf";
			break;
		default:
			ASSERT(FALSE);
			return;
	}

	// Assign the open flag.
    CFileStatus status;
	if ( !CFile::GetStatus(strFile, status) ) {
		return;
	}

	// Open the record file.
	UINT nOpenFlag = CFile::modeRead | CFile::typeText;
	CStdioFile* fp = new CStdioFile(strFile, nOpenFlag);
	
	// Read from the record file.
	int nMax = defineLen+startLen+endLen;
	char* pszLine = new char[nMax+4];
	while ( fp->ReadString(pszLine, nMax) ) {
		// Split string.
		char* p = pszLine;
		while ( *p ) {
			if ( '\n' == *p ) {
				*p = NULL;
				break;
			}
			p++;
		}
		CSpaGroupSheet* pSheet = (CSpaGroupSheet*)GetParent();
		pSheet->EmptyBuffer();
		pSheet->SplitRecord(CString(pszLine));
		if ( !pSheet->m_strEnd.IsEmpty() && 
			 strDefine == pSheet->m_strDefine ) {
			break;
		}
		pSheet->EmptyBuffer();
	}
	delete []pszLine;
		
	// Close the record file.
	fp->Close();
	delete fp;
}

void CSpaCodeCoveragePage::ClearResult(void)
{
	// Initial result variables.
	m_nMemory = 0;
	
	// Reset list box.
	m_listBlock.ResetContent();
	m_listInst.ResetContent();
}

void CSpaCodeCoveragePage::GetExecuteRange()
{
	// Get start address.
	char szPC[33+1];
	UINT uPC;
	CSpaGroupSheet* pSheet = (CSpaGroupSheet*)GetParent();

	switch ( m_nWhichBtn ) {
		case btnStart:
			// Get the current PC.
			if ( ::ICE_OK != ::AbiGetOneReg(0, &uPC) ) {
				ultoa(pSheet->m_uPC, szPC, 16);
			}
			else {
				ultoa(uPC, szPC, 16);
			}
			break;
		case btnRestart:
			// Get the old PC.
			ultoa(pSheet->m_uPC, szPC, 16);
			break;
		default:
			ASSERT(FALSE);
			return;
	}
	
	// Get the end BP.
	pSheet->m_strStart = szPC;
	pSheet->m_strEnd = m_strRange0;
}

BOOL CSpaCodeCoveragePage::SetCoverageData(void)
{
	// Initial when restart.
	memset(&m_QualifyData, 0, sizeof(m_QualifyData));
	memset(&m_CodeSetData, 0, sizeof(m_CodeSetData));
	memset(&m_CodeGetData, 0, sizeof(m_CodeGetData));
	
	// Reset progress bar.
	m_nProgress = 0;
	UpdateBar(FALSE);
	
	// Convert input data.
	::ADDRESS Addr;
	CSpaGroupSheet* pSheet = (CSpaGroupSheet*)GetParent();
	pSheet->EmptyBuffer();

	// Select no modules.
	if ( !m_bRange1 && !m_bRange2 && !m_bRange3 && !m_bRange4 ) {
		pSheet->m_nErrorID = pSheet->errInput;
		pSheet->DisplayErrorMessage();
		return FALSE;
	}
	
	// Set qualify.
	GetExecuteRange();
	if ( pSheet->m_strStart.IsEmpty() ) {
		pSheet->m_nErrorID = pSheet->errInput;
		pSheet->DisplayErrorMessage();
		return FALSE;
	}
	else {
		if ( !pSheet->IsValidAddr(pSheet->m_strStart) ||
			 !pSheet->IsValidAddr(pSheet->m_strEnd) ) {
			pSheet->m_nErrorID = pSheet->errInput;
			pSheet->DisplayErrorMessage();
			return FALSE;
		}
		else {
			m_QualifyData.qlfyType[4] = 0x10;
			if ( !m_bS && !m_bF && !m_bR && !m_bW && !m_bAK ) {
				m_QualifyData.qlfyType[8] = 0;
			}
			else {
				m_QualifyData.qlfyType[8] = 1;
			}
            
			m_QualifyData.status = 0;
            if ( m_bS ) {
				m_QualifyData.status |= 0x1;
			}
            if ( m_bF ) {
				m_QualifyData.status |= 0x2;
			}
            if ( m_bR ) {
				m_QualifyData.status |= 0x4;
			}
            if ( m_bW ) {
				m_QualifyData.status |= 0x8;
			}
            if ( m_bAK ) {
				m_QualifyData.status |= 0x10;
			}

			m_QualifyData.addrCount = 2;

			::AdrTextToAddr(pSheet->m_strStart, Addr);
			m_QualifyData.addr[0][0] = (WORD&)(Addr.adrAddress);

			::AdrTextToAddr(pSheet->m_strEnd, Addr);
			m_QualifyData.addr[1][0] = (WORD&)(Addr.adrAddress);
		}
	}
	
	if ( ::ICE_OK != ::AbiSetQualify(m_QualifyData) ) {
		pSheet->m_nErrorID = pSheet->errAbi;
		pSheet->DisplayErrorMessage();
		return FALSE;
	}
		
	// Convert execution range.
	::AdrTextToAddr(pSheet->m_strStart, Addr);
	m_CodeSetData.goArgument.addrFrom.addr = (WORD&)(Addr.adrAddress);
	m_CodeSetData.goArgument.addrFrom.addrType = (FLAG&)(Addr.adrSpace);
	::AdrTextToAddr(pSheet->m_strEnd, Addr);
	m_CodeSetData.goArgument.addrTo.addr = (WORD&)(Addr.adrAddress);
	m_CodeSetData.goArgument.addrTo.addrType = (FLAG&)(Addr.adrSpace);

	// Convert Module 1 range.
	if ( m_bRange1 ) {
		ReadRecord(entryRange, m_strRange1);
		if ( !pSheet->m_strStart.IsEmpty() ) {
			if ( !pSheet->IsValidAddr(pSheet->m_strStart) ||
				 !pSheet->IsValidAddr(pSheet->m_strEnd) ) {
				pSheet->m_nErrorID = pSheet->errInput;
				pSheet->DisplayErrorMessage();
				return FALSE;
			}
			else {
				::AdrTextToAddr(pSheet->m_strStart, Addr);
				m_CodeSetData.usAddr[m_CodeSetData.uchNumber++] = (unsigned short&)(Addr.adrAddress);
				::AdrTextToAddr(pSheet->m_strEnd, Addr);
				m_CodeSetData.usAddr[m_CodeSetData.uchNumber++] = (unsigned short&)(Addr.adrAddress);
			}
		}
	}

	// Convert Module 2 range.
	if ( m_bRange2 ) {
		ReadRecord(entryRange, m_strRange2);
		if ( !pSheet->m_strStart.IsEmpty() ) {
			if ( !pSheet->IsValidAddr(pSheet->m_strStart) ||
				 !pSheet->IsValidAddr(pSheet->m_strEnd) ) {
				pSheet->m_nErrorID = pSheet->errInput;
				pSheet->DisplayErrorMessage();
				return FALSE;
			}
			else {
				::AdrTextToAddr(pSheet->m_strStart, Addr);
				m_CodeSetData.usAddr[m_CodeSetData.uchNumber++] = (unsigned short&)(Addr.adrAddress);
				::AdrTextToAddr(pSheet->m_strEnd, Addr);
				m_CodeSetData.usAddr[m_CodeSetData.uchNumber++] = (unsigned short&)(Addr.adrAddress);
			}
		}
	}

	// Convert Module 3 range.
	if ( m_bRange3 ) {
		ReadRecord(entryRange, m_strRange3);
		if ( !pSheet->m_strStart.IsEmpty() ) {
			if ( !pSheet->IsValidAddr(pSheet->m_strStart) ||
				 !pSheet->IsValidAddr(pSheet->m_strEnd) ) {
				pSheet->m_nErrorID = pSheet->errInput;
				pSheet->DisplayErrorMessage();
				return FALSE;
			}
			else {
				::AdrTextToAddr(pSheet->m_strStart, Addr);
				m_CodeSetData.usAddr[m_CodeSetData.uchNumber++] = (unsigned short&)(Addr.adrAddress);
				::AdrTextToAddr(pSheet->m_strEnd, Addr);
				m_CodeSetData.usAddr[m_CodeSetData.uchNumber++] = (unsigned short&)(Addr.adrAddress);
			}
		}
	}

	// Convert Module 4 range.
	if ( m_bRange4 ) {
		ReadRecord(entryRange, m_strRange4);
		if ( !pSheet->m_strStart.IsEmpty() ) {
			if ( !pSheet->IsValidAddr(pSheet->m_strStart) ||
				 !pSheet->IsValidAddr(pSheet->m_strEnd) ) {
				pSheet->m_nErrorID = pSheet->errInput;
				pSheet->DisplayErrorMessage();
				return FALSE;
			}
			else {
				::AdrTextToAddr(pSheet->m_strStart, Addr);
				m_CodeSetData.usAddr[m_CodeSetData.uchNumber++] = (unsigned short&)(Addr.adrAddress);
				::AdrTextToAddr(pSheet->m_strEnd, Addr);
				m_CodeSetData.usAddr[m_CodeSetData.uchNumber++] = (unsigned short&)(Addr.adrAddress);
			}
		}
	}

	// Set coverage data.
	m_CodeSetData.uchNumber /= 2;
	if ( ::ICE_OK != ::AbiCoverageGo(m_CodeSetData) ) {
		pSheet->m_nErrorID = pSheet->errAbi;
		pSheet->DisplayErrorMessage();
		return FALSE;
	}

	return TRUE;
}

void CSpaCodeCoveragePage::UpdateCoverage(void)
{
	// Detect EP status.
	CSpaGroupSheet* pSheet = (CSpaGroupSheet*)GetParent();
	UINT uStatus;
	if ( ::ICE_OK != ::AbiGetCpuStatus(&uStatus) ) {
		StopTimerAction();
		pSheet->m_nErrorID = pSheet->errAbi;
		pSheet->DisplayErrorMessage();
		return;
	}

	// EP running.
	if ( 0x40 == (uStatus & 0x70) ) {
		// Idle action.
		IdleAction();
		return;
	}
		
	// Get coverage data.
	::STATUS nStatus = ::AbiCoverageGet(&m_CodeGetData);
	StopTimerAction();
	if ( ::ICE_OK != nStatus ) {
		pSheet->m_nErrorID = pSheet->errAbi;
		pSheet->DisplayErrorMessage();
		return;
	}
	
	// Update the progress bar.
	m_nProgress = 100;
	UpdateBar();

	// Not any data coveraged.
	ASSERT(m_CodeGetData.uchNumber >= 0 && m_CodeGetData.uchNumber <= 16);
	ClearResult();
	if ( 0 == m_CodeGetData.uchNumber ) {
		pSheet->m_nErrorID = pSheet->errNoCoverage;
		pSheet->DisplayErrorMessage();
		UpdateData(FALSE);
		return;
	}
	
	// Add to Block list box.
	for ( int i(0); i < m_CodeGetData.uchNumber*2; i++ ) {
		CString strPercent, strLeft, strRight;
		CalcMemoryPercent(i);
		strPercent.Format("%2d%%  ", m_nMemory);
		strLeft.Format("P:%04X", m_CodeGetData.usAddr[i++]);
		strRight.Format("P:%04X", m_CodeGetData.usAddr[i]);
		m_listBlock.AddString(strPercent + strLeft + " ~ " + strRight);
	}
	
	// Select the first item.
	if ( m_listBlock.GetCount() > 0 ) {
		m_nBlock = 0;
		m_listBlock.SetCurSel(m_nBlock);
		DetectDisplayMode();
	}

	// Update Instruction list.
	UpdateInst();
    
    UpdateData(FALSE);
}

void CSpaCodeCoveragePage::UpdateInst(void)
{
	// If running, return.
	if ( !m_bStop ) {
		return;
	}
	
	// Update right list box.
	switch ( m_nMode ) {
		case modeAsm:
			UpdateInstAsm();
			break;
		case modeSrc:
			UpdateInstSrc();
			break;
		default:
			ASSERT(FALSE);
			return;
	}
}

void CSpaCodeCoveragePage::UpdateInstAsm(void)
{
	// Get coverage info.
	unsigned short uStart = m_CodeGetData.usAddr[m_nBlock*2];
	unsigned short uEnd = m_CodeGetData.usAddr[m_nBlock*2+1];
	if ( uEnd < uStart ) {
		return;
	}
	unsigned short uLen = min(uEnd-uStart+1, maxMemBlock);
	unsigned short uNext;

	// New a list.
	CStringList* pList = new CStringList;
	pList->AddTail("Address  Object  Instruction");

	// Get info from DAD server.
	while ( uStart + uLen <= uEnd + 1 ) {
		int nRet = ::SpaDisAsm(uStart, uLen, uNext, pList);
		if ( 0 == nRet ) {
			uStart += uLen;
			uLen = min(uEnd-uStart+1, maxMemBlock);
			if ( 0 == uLen ) {
				break;
			}
			else {
				continue;
			}
		}
		else if ( -4 == nRet ) {
			if ( uStart + uLen < uEnd + 1 ) {
				uStart = uNext;
				uLen = min(uEnd-uStart+1, maxMemBlock);
				if ( 0 == uLen ) {
					break;
				}
				else {
					continue;
				}
			}
			else {
				break;
			}
		}
		else {
			CSpaGroupSheet* pSheet = (CSpaGroupSheet*)GetParent();
			pSheet->m_nErrorID = pSheet->errAbi;
			pSheet->DisplayErrorMessage();
			delete pList;
			return;
		}
	}
	
	// Add to Instruction list box.
    m_listInst.ResetContent();
    if ( !pList->IsEmpty() ) {
        POSITION posHead = pList->GetHeadPosition();
        while ( posHead ) {
            m_listInst.AddString(pList->GetNext(posHead));
        }
    }
	
	if ( m_listInst.GetCount() > 0 ) {
		m_nInst = 0;
		m_listInst.SetCurSel(m_nInst);
	}
	
	delete pList;
}

void CSpaCodeCoveragePage::UpdateInstSrc(void)
{
	// Get coverage info.
	unsigned short uStart = m_CodeGetData.usAddr[m_nBlock*2];
	unsigned short uEnd = m_CodeGetData.usAddr[m_nBlock*2+1];
	if ( uEnd < uStart ) {
		return;
	}
	unsigned short uLen = uEnd-uStart+1;

	// New a list.
	CStringList* pList = new CStringList;
	pList->AddTail("Module   Line   Source");

	// Get C-trace info from Trace server.
	if ( 0 == ::SpaListSource(uStart, uLen, pList) ) {
		pList->AddTail("............................");
	}

	// Add to Instruction list box.
    m_listInst.ResetContent();
    if ( !pList->IsEmpty() ) {
        POSITION posHead = pList->GetHeadPosition();
        while ( posHead ) {
            m_listInst.AddString(pList->GetNext(posHead));
        }
    }
	
	if ( m_listInst.GetCount() > 0 ) {
		m_nInst = 0;
		m_listInst.SetCurSel(m_nInst);
	}
	
	delete pList;
}

void CSpaCodeCoveragePage::CalcMemoryPercent(void)
{
	// Calclate the memory coverage percent.
	m_nMemory = 0;
	long lWhole = m_CodeSetData.goArgument.addrTo.addr - 
				 			m_CodeSetData.goArgument.addrFrom.addr + 1;
	
	long lSelect = m_CodeGetData.usAddr[m_nBlock*2+1] - 
				  			 m_CodeGetData.usAddr[m_nBlock*2] + 1;
	
	if ( lWhole <= 0 || lSelect <= 0 || lWhole < lSelect ) {
		return;
	}

	m_nMemory = int(lSelect * 100 / lWhole);
	m_nMemory = min(max(1, m_nMemory), 100);
}

void CSpaCodeCoveragePage::CalcMemoryPercent(int nWhichRange)
{
	// Calclate the memory coverage percent.
	m_nMemory = 0;
	long lWhole = m_CodeSetData.goArgument.addrTo.addr - 
				 			m_CodeSetData.goArgument.addrFrom.addr + 1;
	
	long lSelect = m_CodeGetData.usAddr[nWhichRange*2+1] - 
				  			 m_CodeGetData.usAddr[nWhichRange*2] + 1;
	
	if ( lWhole <= 0 || lSelect <= 0 || lWhole < lSelect ) {
		return;
	}

	m_nMemory = int(lSelect * 100 / lWhole);
	m_nMemory = min(max(1, m_nMemory), 99);
}

void CSpaCodeCoveragePage::UpdateBar(BOOL bRedraw)
{
	// Assign a static control.
	CWnd* pWnd = &m_staticProgressBar;
	int nPercent = m_nProgress;
	
	// No change.
	if ( 0 == nPercent && bRedraw ) {
		return;
	}

	// Update timer bar.
	CDC* pDC = pWnd->GetDC();

	CBrush brushGray(RGB(192,192,192));
	CBrush* pBrushOld = pDC->SelectObject(&brushGray);
	CRect rect;
	pWnd->GetClientRect(rect);
	rect.left += 1;
	rect.top += 1;
	rect.right -= 1;
	rect.bottom -= 1;
	pDC->FillRect(rect, &brushGray);
	pDC->SelectObject(pBrushOld);

	if ( bRedraw ) {
		CBrush brushRed(RGB(255,0,0));
		pBrushOld = pDC->SelectObject(&brushRed);
		rect.right = rect.right * nPercent / 100;
		rect.right =  rect.right <= rect.left ? rect.left + 1 : rect.right;
		pDC->FillRect(rect, &brushRed);
		pDC->SelectObject(pBrushOld);
		
		// Set percentage.
		CString strPer;
		strPer.Format("%d %%", nPercent);
		pDC->SetTextColor(RGB(0,0,0));
		pDC->SetBkMode(TRANSPARENT);
		pWnd->GetClientRect(rect);
		pDC->TextOut(rect.Width()/2-12, 2, strPer);
	}

	pWnd->ReleaseDC(pDC);
}

void CSpaCodeCoveragePage::IdleAction(void)
{
	// Increase the progress percentage.
	m_nProgress = ++m_nProgress > 100 ? --m_nProgress : m_nProgress;

	// Update the progress bar.
	UpdateBar();
}


/////////////////////////////////////////////////////////////////////////////
// CSpaCodeCoveragePage message handlers

BOOL CSpaCodeCoveragePage::OnKillActive()
{
	// TODO: Add your message handler code here

	// Disabled when running.
	if ( !m_bStop ) {
		return FALSE;
	}

	return CPropertyPage::OnKillActive();
}

BOOL CSpaCodeCoveragePage::OnSetActive()
{
	// TODO: Add your message handler code here

	// Set timer to update the graph bar.
 	m_uTimerID = SetTimer(tmEvent, tmElapse, NULL);

	return CPropertyPage::OnSetActive();
}

void CSpaCodeCoveragePage::OnCancel()
{
	// TODO: Add extra cleanup here
	
	// Kill one-shot timer.
	if ( 0 != m_uTimerID ) {
		KillTimer(m_uTimerID);
	}

	// Abort EP.
	AbiAbort();

	// Restore HW configuration.
	BeginWaitCursor();
	RestoreAllConfig();
	EndWaitCursor();

	// Close the whole group.
	CSpaGroupSheet* pSheet = (CSpaGroupSheet*)GetParent();
	if ( pSheet->IsKindOf(RUNTIME_CLASS(CSpaGroupSheet)) ) {
		pSheet->EndDialog(IDCANCEL);
	}
	else {
		CPropertyPage::OnCancel();
	}
}

void CSpaCodeCoveragePage::OnHelp()
{
	// TODO: Add your control notification handler code here
	
	// Active Windows Help system
	AfxGetApp()->WinHelp(IDD_SPA_CODE);
}

void CSpaCodeCoveragePage::OnButtonSpaEdit()
{
	// TODO: Add your control notification handler code here
	
	// Open the user defined dialog.
	CSpaGroupSheet* pSheet = (CSpaGroupSheet*)GetParent();
	CSpaUserDefineDlg* pDlg = new CSpaUserDefineDlg(pSheet);
	pDlg->DoModal();
	delete pDlg;

	// Update current selection.
	BeginWaitCursor();

	UpdateData();
              
	UpdateCombo();

	UpdateData(FALSE);

	EndWaitCursor();
}

void CSpaCodeCoveragePage::OnButtonSpaStart()
{
	// TODO: Add your control notification handler code here
	
	// Clear current result.
	UpdateData();

    // Check the control syntax.
	CSpaGroupSheet* pSheet = (CSpaGroupSheet*)GetParent();
    CheckInputSyntax();
    if ( pSheet->errOK != pSheet->m_nErrorID ) {
		pSheet->DisplayErrorMessage();
    	return;
    }

	// Set coverage data.
	BeginWaitCursor();
	BOOL bOK = SetCoverageData();
    EndWaitCursor();
    if ( !bOK ) {
    	return;
    }

	// Grayed the some controls.
	m_bStop = FALSE;
	DisableControl();
	
	// Enable ESC to cancel this page.
	SetFocus();

	// Set timer.
	if ( 0 != m_uTimerID ) {
		// If outstanding timer, kill it.
		KillTimer(m_uTimerID);
	}

 	// Set a timer for 200 milliseconds.
 	m_uTimerID = SetTimer(tmEvent, tmElapse, NULL);
	if ( 0 == m_uTimerID ) {
		pSheet->m_nErrorID = pSheet->errTimer;
		pSheet->DisplayErrorMessage();
		return;
	}

    UpdateData(FALSE);
}

void CSpaCodeCoveragePage::OnButtonSpaStop()
{
	// TODO: Add your control notification handler code here
	
	// Kill one-shot timer.
	if ( 0 != m_uTimerID ) {
		KillTimer(m_uTimerID);
		m_uTimerID = 0;
		
		// Abort EP.
		AbiAbort();

		// Update coverage info.
		UpdateCoverage();
	}
    
	// Grayed some controls.
	m_bStop = TRUE;
	DisableControl();
}

void CSpaCodeCoveragePage::OnShowWindow(BOOL bShow, UINT nStatus)
{
	CPropertyPage::OnShowWindow(bShow, nStatus);
	
	// TODO: Add your message handler code here
	
	// The window is being shown.
	if ( TRUE == bShow ) {
		// Grayed some controls.
		m_bStop = TRUE;
		DisableControl();
		
		// Update current selection.
		BeginWaitCursor();
	
		UpdateData();
        
	    HFONT hFont = (HFONT)::GetStockObject(ANSI_FIXED_FONT);
	    CFont font;
		m_listBlock.SetFont(font.FromHandle(hFont));
		m_listInst.SetFont(font.FromHandle(hFont));

		m_listBlock.SetHorizontalExtent(300);
		m_listInst.SetHorizontalExtent(500);

		UpdateCombo();

		UpdateData(FALSE);
	
		EndWaitCursor();
	}
}

void CSpaCodeCoveragePage::OnTimer(UINT nIDEvent)
{
	// TODO: Add your message handler code here and/or call default
	
	// Not our timer.
	if ( m_uTimerID != nIDEvent ) {
		CPropertyPage::OnTimer(nIDEvent);
		return;
	}
    
	// Update coverage info.
	if ( !m_bStop ) {
		UpdateCoverage();
	}
	else {
		// Kill the timer.
		KillTimer(m_uTimerID);
		m_uTimerID = 0;
		
		// Update the progress bar.
		UpdateBar();
	}
}

void CSpaCodeCoveragePage::OnSelchangeListSpaCodeBlock()
{
	// TODO: Add your control notification handler code here

	// Update Instruction list box.
	UpdateData();
	DetectDisplayMode();
	UpdateInst();
	UpdateData(FALSE);
}

void CSpaCodeCoveragePage::OnButtonSpaRestart() 
{
	// TODO: Add your control notification handler code here

	// It's as same as OnStart().
    m_nWhichBtn = btnRestart;
	OnButtonSpaStart();
	m_nWhichBtn = btnStart;
}

void CSpaCodeCoveragePage::OnCheckSpaCodeRange1() 
{
	// TODO: Add your control notification handler code here

	// Update check box.
	UpdateData();
	UpdateData(FALSE);
}

void CSpaCodeCoveragePage::OnCheckSpaCodeRange2() 
{
	// TODO: Add your control notification handler code here
	
	// Update check box.
	UpdateData();
	UpdateData(FALSE);
}

void CSpaCodeCoveragePage::OnCheckSpaCodeRange3() 
{
	// TODO: Add your control notification handler code here
	
	// Update check box.
	UpdateData();
	UpdateData(FALSE);
}

void CSpaCodeCoveragePage::OnCheckSpaCodeRange4() 
{
	// TODO: Add your control notification handler code here
	
	// Update check box.
	UpdateData();
	UpdateData(FALSE);
}

void CSpaCodeCoveragePage::OnButtonSpaCodeMode() 
{
	// TODO: Add your control notification handler code here

	// Toggle Source/Assembly mode.
	UpdateData();
	CString strCaption;
	switch ( m_nMode ) {
		case modeAsm:
			m_nMode = modeSrc;
			strCaption = "=> Assembly Mode";
			break;
		case modeSrc:
			m_nMode = modeAsm;
			strCaption = "=> Source Mode";
			break;
		default:
			ASSERT(FALSE);
			return;
	}
	m_btnMode.SetWindowText(strCaption);
	
	// Update list box.
	if ( m_listBlock.GetCount() > 0 ) {
		UpdateInst();
	}
}

void CSpaCodeCoveragePage::OnButtonSpaBp() 
{
	// TODO: Add your control notification handler code here
	
	// Open the BP set dialog.
	UpdateData();

	CSpaGroupSheet* pSheet = (CSpaGroupSheet*)GetParent();
	CSpaBpSetDlg* pDlg = new CSpaBpSetDlg(pSheet);
	if ( IDOK == pDlg->DoModal() ) {
		m_strRange0 = pDlg->m_strBp;
	}
	delete pDlg;

	UpdateData(FALSE);
}

void CSpaCodeCoveragePage::OnEditchangeComboSpaCodeRange1() 
{
	// TODO: Add your control notification handler code here

	// Uncheck the check-box if erasing the input.
	UpdateData();

	m_bRange1 = m_strRange1.IsEmpty() ? FALSE : TRUE;

	UpdateData(FALSE);
}

void CSpaCodeCoveragePage::OnEditchangeComboSpaCodeRange2() 
{
	// TODO: Add your control notification handler code here
	
	// Uncheck the check-box if erasing the input.
	UpdateData();
	
	m_bRange2 = m_strRange2.IsEmpty() ? FALSE : TRUE;

	UpdateData(FALSE);
}

void CSpaCodeCoveragePage::OnEditchangeComboSpaCodeRange3() 
{
	// TODO: Add your control notification handler code here
	
	// Uncheck the check-box if erasing the input.
	UpdateData();
	
	m_bRange3 = m_strRange3.IsEmpty() ? FALSE : TRUE;

	UpdateData(FALSE);
}

void CSpaCodeCoveragePage::OnEditchangeComboSpaCodeRange4() 
{
	// TODO: Add your control notification handler code here
	
	// Uncheck the check-box if erasing the input.
	UpdateData();
	
	m_bRange4 = m_strRange4.IsEmpty() ? FALSE : TRUE;

	UpdateData(FALSE);
}

void CSpaCodeCoveragePage::OnSelchangeComboSpaCodeRange1() 
{
	// TODO: Add your control notification handler code here
	
	// Check the check-box if erasing the input.
	m_bRange1 = TRUE;
	m_btnRange1.SetCheck(1);
}

void CSpaCodeCoveragePage::OnSelchangeComboSpaCodeRange2() 
{
	// TODO: Add your control notification handler code here
	
	// Check the check-box if erasing the input.
	m_bRange2 = TRUE;
	m_btnRange2.SetCheck(1);
}

void CSpaCodeCoveragePage::OnSelchangeComboSpaCodeRange3() 
{
	// TODO: Add your control notification handler code here
	
	// Check the check-box if erasing the input.
	m_bRange3 = TRUE;
	m_btnRange3.SetCheck(1);
}

void CSpaCodeCoveragePage::OnSelchangeComboSpaCodeRange4() 
{
	// TODO: Add your control notification handler code here
	
	// Check the check-box if erasing the input.
	m_bRange4 = TRUE;
	m_btnRange4.SetCheck(1);
}
