5b61a0678e
Signed-off-by: Gwenhael Le Moine <gwenhael.le.moine@gmail.com>
1104 lines
28 KiB
C
1104 lines
28 KiB
C
/*
|
|
* DebugDll.c
|
|
*
|
|
* This file is part of Emu48
|
|
*
|
|
* Copyright (C) 2000 Christoph Gießelink
|
|
*
|
|
*/
|
|
#include "pch.h"
|
|
#include "resource.h"
|
|
#include "Emu48.h"
|
|
#include "Opcodes.h"
|
|
#include "ops.h"
|
|
#include "debugger.h"
|
|
|
|
#include "Emu48Dll.h"
|
|
|
|
#define MAXBREAKPOINTS 256 // max. number of breakpoints
|
|
|
|
typedef struct // type of breakpoint table
|
|
{
|
|
UINT nType; // breakpoint type
|
|
DWORD dwAddr; // breakpoint address
|
|
} BP_T;
|
|
|
|
static DWORD *pdwLinArray; // last instruction linear array
|
|
|
|
static DWORD dwRefRstkp; // reference stack pointer content
|
|
|
|
static WORD wBreakpointCount = 0; // number of breakpoints
|
|
static BP_T sBreakpoint[MAXBREAKPOINTS]; // breakpoint table
|
|
|
|
static BOOL bDbgNotify = FALSE; // debugger notify flag
|
|
static BOOL bDbgRpl; // Flag for ASM or RPL breakpoint
|
|
|
|
// callback function notify debugger breakpoint
|
|
static VOID (CALLBACK *pEmuDbgNotify)(INT nBreaktype) = NULL;
|
|
|
|
// callback function notify hardware stack changed
|
|
static BOOL (CALLBACK *pEmuStackNotify)(VOID) = NULL;
|
|
|
|
|
|
|
|
//################
|
|
//#
|
|
//# Static functions
|
|
//#
|
|
//################
|
|
|
|
//
|
|
// convert nibble register to DWORD
|
|
//
|
|
static DWORD RegToDWORD(BYTE *pReg, WORD wNib)
|
|
{
|
|
WORD i;
|
|
DWORD dwRegister = 0;
|
|
|
|
for (i = 0;i < wNib;++i)
|
|
{
|
|
dwRegister <<= 4;
|
|
dwRegister += pReg[wNib-i-1];
|
|
}
|
|
return dwRegister;
|
|
}
|
|
|
|
//
|
|
// convert DWORD to nibble register
|
|
//
|
|
static VOID DWORDToReg(BYTE *pReg, WORD wNib, DWORD dwReg)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0;i < wNib;++i)
|
|
{
|
|
pReg[i] = (BYTE) (dwReg & 0xF);
|
|
dwReg >>= 4; // next nibble
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
//################
|
|
//#
|
|
//# Public internal functions
|
|
//#
|
|
//################
|
|
|
|
//
|
|
// handle upper 32 bit of cpu cycle counter
|
|
//
|
|
VOID UpdateDbgCycleCounter(VOID)
|
|
{
|
|
return; // not necessary here, cycle counter has 64 bit in DLL version
|
|
}
|
|
|
|
//
|
|
// check for code breakpoints
|
|
//
|
|
BOOL CheckBreakpoint(DWORD dwAddr, DWORD dwRange, UINT nType)
|
|
{
|
|
INT i;
|
|
BOOL bBreak = FALSE; // don't break
|
|
DWORD dwRomAddr = -1; // no valid ROM address
|
|
|
|
// stack changed notify function defined
|
|
if (nType == BP_EXEC && pEmuStackNotify != NULL)
|
|
{
|
|
// hardware stack changed
|
|
if (dwRefRstkp != -1 && dwRefRstkp != Chipset.rstkp)
|
|
bBreak = pEmuStackNotify(); // inform debugger
|
|
|
|
dwRefRstkp = Chipset.rstkp; // save current stack level
|
|
}
|
|
|
|
// absolute ROM adress breakpoints
|
|
if (nType == BP_EXEC) // only on code breakpoints
|
|
{
|
|
LPBYTE I = FASTPTR(dwAddr); // get opcode stream
|
|
|
|
// adress in ROM area
|
|
if (I >= pbyRom && I < pbyRom + dwRomSize)
|
|
dwRomAddr = I - pbyRom; // linear ROM address
|
|
}
|
|
|
|
for (i = 0; i < wBreakpointCount; ++i) // scan all breakpoints
|
|
{
|
|
// check for absolute ROM adress breakpoint
|
|
if ( sBreakpoint[i].dwAddr >= dwRomAddr && sBreakpoint[i].dwAddr < dwRomAddr + dwRange
|
|
&& (sBreakpoint[i].nType & BP_ROM) != 0)
|
|
return TRUE;
|
|
|
|
// check address range and type
|
|
if ( sBreakpoint[i].dwAddr >= dwAddr && sBreakpoint[i].dwAddr < dwAddr + dwRange
|
|
&& (sBreakpoint[i].nType & nType) != 0)
|
|
return TRUE;
|
|
}
|
|
return bBreak;
|
|
}
|
|
|
|
//
|
|
// notify debugger that emulation stopped
|
|
//
|
|
VOID NotifyDebugger(INT nType) // update registers
|
|
{
|
|
nDbgState = DBG_STEPINTO; // state "step into"
|
|
dwDbgStopPC = -1; // disable "cursor stop address"
|
|
|
|
_ASSERT(pEmuDbgNotify); // notify function defined from caller
|
|
pEmuDbgNotify(nType); // notify caller
|
|
bDbgNotify = TRUE; // emulation stopped
|
|
return;
|
|
}
|
|
|
|
//
|
|
// disable debugger
|
|
//
|
|
VOID DisableDebugger(VOID)
|
|
{
|
|
nDbgState = DBG_OFF; // disable debugger
|
|
bInterrupt = TRUE; // exit opcode loop
|
|
SetEvent(hEventDebug);
|
|
return;
|
|
}
|
|
|
|
|
|
//################
|
|
//#
|
|
//# Dummy functions for linking
|
|
//#
|
|
//################
|
|
|
|
//
|
|
// entry from message loop
|
|
//
|
|
LRESULT OnToolDebug(VOID)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// load breakpoint list
|
|
//
|
|
VOID LoadBreakpointList(HANDLE hFile)
|
|
{
|
|
return;
|
|
UNREFERENCED_PARAMETER(hFile);
|
|
}
|
|
|
|
//
|
|
// save breakpoint list
|
|
//
|
|
VOID SaveBreakpointList(HANDLE hFile)
|
|
{
|
|
return;
|
|
UNREFERENCED_PARAMETER(hFile);
|
|
}
|
|
|
|
//
|
|
// create a copy of the breakpoint list
|
|
//
|
|
VOID CreateBackupBreakpointList(VOID)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// restore the breakpoint list from the copy
|
|
//
|
|
VOID RestoreBackupBreakpointList(VOID)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
//################
|
|
//#
|
|
//# Public external functions
|
|
//#
|
|
//################
|
|
|
|
/****************************************************************************
|
|
* EmuInitLastInstr
|
|
*****************************************************************************
|
|
*
|
|
* @func init a circular buffer area for saving the last instruction
|
|
* addresses
|
|
*
|
|
* @xref none
|
|
*
|
|
* @rdesc BOOL: FALSE = OK, TRUE = Error
|
|
*
|
|
****************************************************************************/
|
|
|
|
DECLSPEC BOOL CALLBACK EmuInitLastInstr(
|
|
WORD wNoInstr, // @parm number of saved instructions,
|
|
// 0 = frees the memory buffer
|
|
DWORD *pdwArray) // @parm pointer to linear array
|
|
{
|
|
if (pdwInstrArray) // circular buffer defined
|
|
{
|
|
EnterCriticalSection(&csDbgLock);
|
|
{
|
|
free(pdwInstrArray); // free memory
|
|
pdwInstrArray = NULL;
|
|
}
|
|
LeaveCriticalSection(&csDbgLock);
|
|
}
|
|
|
|
if (wNoInstr) // new size
|
|
{
|
|
pdwLinArray = pdwArray; // save address of linear array
|
|
wInstrSize = wNoInstr + 1; // size of circular buffer
|
|
wInstrWp = wInstrRp = 0; // init write/read pointer
|
|
|
|
pdwInstrArray = malloc(wInstrSize*sizeof(*pdwInstrArray));
|
|
if (pdwInstrArray == NULL) // allocation error
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* EmuGetLastInstr
|
|
*****************************************************************************
|
|
*
|
|
* @func return number of valid entries in the last instruction array,
|
|
* each entry contents a PC address, array[0] contents the oldest,
|
|
* array[*pwNoEntries-1] the last PC address
|
|
*
|
|
* @xref none
|
|
*
|
|
* @rdesc BOOL: FALSE = OK, TRUE = Error
|
|
*
|
|
****************************************************************************/
|
|
|
|
DECLSPEC BOOL CALLBACK EmuGetLastInstr(
|
|
WORD *pwNoEntries) // @parm return number of valid entries in array
|
|
{
|
|
WORD i;
|
|
|
|
if (pdwInstrArray == NULL) return TRUE; // circular buffer not defined
|
|
|
|
// copy data to linear buffer
|
|
*pwNoEntries = 0;
|
|
for (i = wInstrRp; i != wInstrWp; i = (i + 1) % wInstrSize)
|
|
pdwLinArray[(*pwNoEntries)++] = pdwInstrArray[i];
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* EmuRun
|
|
*****************************************************************************
|
|
*
|
|
* @func run emulation
|
|
*
|
|
* @xref none
|
|
*
|
|
* @rdesc BOOL: FALSE = OK
|
|
* TRUE = Error, Emu48 is running
|
|
*
|
|
****************************************************************************/
|
|
|
|
DECLSPEC BOOL CALLBACK EmuRun(VOID)
|
|
{
|
|
BOOL bErr = (nDbgState == DBG_RUN);
|
|
if (nDbgState != DBG_RUN) // emulation stopped
|
|
{
|
|
bDbgNotify = FALSE; // reset debugger notify flag
|
|
nDbgState = DBG_RUN; // state "run"
|
|
SetEvent(hEventDebug); // run emulation
|
|
}
|
|
return bErr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* EmuRunPC
|
|
*****************************************************************************
|
|
*
|
|
* @func run emulation until stop address
|
|
*
|
|
* @xref none
|
|
*
|
|
* @rdesc VOID
|
|
*
|
|
****************************************************************************/
|
|
|
|
DECLSPEC VOID CALLBACK EmuRunPC(
|
|
DWORD dwAddressPC) // @parm breakpoint address
|
|
{
|
|
if (nDbgState != DBG_RUN) // emulation stopped
|
|
{
|
|
dwDbgStopPC = dwAddressPC; // stop address
|
|
EmuRun(); // run emulation
|
|
}
|
|
return;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* EmuStep
|
|
*****************************************************************************
|
|
*
|
|
* @func execute one ASM instruction and return to caller
|
|
*
|
|
* @xref none
|
|
*
|
|
* @rdesc VOID
|
|
*
|
|
****************************************************************************/
|
|
|
|
DECLSPEC VOID CALLBACK EmuStep(VOID)
|
|
{
|
|
if (nDbgState != DBG_RUN) // emulation stopped
|
|
{
|
|
bDbgNotify = FALSE; // reset debugger notify flag
|
|
nDbgState = DBG_STEPINTO; // state "step into"
|
|
SetEvent(hEventDebug); // run emulation
|
|
}
|
|
return;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* EmuStepOver
|
|
*****************************************************************************
|
|
*
|
|
* @func execute one ASM instruction but skip GOSUB, GOSUBL, GOSBVL
|
|
* subroutines
|
|
*
|
|
* @xref none
|
|
*
|
|
* @rdesc VOID
|
|
*
|
|
****************************************************************************/
|
|
|
|
DECLSPEC VOID CALLBACK EmuStepOver(VOID)
|
|
{
|
|
if (nDbgState != DBG_RUN) // emulation stopped
|
|
{
|
|
LPBYTE I;
|
|
|
|
bDbgNotify = FALSE; // reset debugger notify flag
|
|
|
|
I = FASTPTR(Chipset.pc);
|
|
|
|
dwDbgRstkp = Chipset.rstkp; // save stack level
|
|
|
|
nDbgState = DBG_STEPINTO; // state "step into"
|
|
if (I[0] == 0x7) // GOSUB 7aaa
|
|
{
|
|
nDbgState = DBG_STEPOVER; // state "step over"
|
|
}
|
|
if (I[0] == 0x8) // GOSUBL or GOSBVL
|
|
{
|
|
if (I[1] == 0xE) // GOSUBL 8Eaaaa
|
|
{
|
|
nDbgState = DBG_STEPOVER; // state "step over"
|
|
}
|
|
if (I[1] == 0xF) // GOSBVL 8Eaaaaa
|
|
{
|
|
nDbgState = DBG_STEPOVER; // state "step over"
|
|
}
|
|
}
|
|
SetEvent(hEventDebug); // run emulation
|
|
}
|
|
return;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* EmuStepOut
|
|
*****************************************************************************
|
|
*
|
|
* @func run emulation until a RTI, RTN, RTNC, RTNCC, RTNNC, RTNSC, RTNSXN,
|
|
* RTNYES instruction is found above the current stack level
|
|
*
|
|
* @xref none
|
|
*
|
|
* @rdesc VOID
|
|
*
|
|
****************************************************************************/
|
|
|
|
DECLSPEC VOID CALLBACK EmuStepOut(VOID)
|
|
{
|
|
if (nDbgState != DBG_RUN) // emulation stopped
|
|
{
|
|
bDbgNotify = FALSE; // reset debugger notify flag
|
|
dwDbgRstkp = (Chipset.rstkp-1)&7; // save stack data
|
|
dwDbgRstk = Chipset.rstk[dwDbgRstkp];
|
|
nDbgState = DBG_STEPOUT; // state "step out"
|
|
SetEvent(hEventDebug); // run emulation
|
|
}
|
|
return;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* EmuStop
|
|
*****************************************************************************
|
|
*
|
|
* @func break emulation
|
|
*
|
|
* @xref none
|
|
*
|
|
* @rdesc BOOL: FALSE = OK
|
|
* TRUE = Error, no debug notify handler
|
|
*
|
|
****************************************************************************/
|
|
|
|
DECLSPEC BOOL CALLBACK EmuStop(VOID)
|
|
{
|
|
if (pEmuDbgNotify && nDbgState != DBG_STEPINTO) // emulation running
|
|
{
|
|
bDbgNotify = FALSE; // reset debugger notify flag
|
|
nDbgState = DBG_STEPINTO; // state "step into"
|
|
if (Chipset.Shutdn) // cpu thread stopped
|
|
SetEvent(hEventShutdn); // goto debug session
|
|
while (!bDbgNotify) Sleep(0); // wait until emulation stopped
|
|
}
|
|
return nDbgState != DBG_STEPINTO;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* EmuCallBackDebugNotify
|
|
*****************************************************************************
|
|
*
|
|
* @func init CallBack handler to notify caller on debugger breakpoint
|
|
*
|
|
* @xref none
|
|
*
|
|
* @rdesc VOID
|
|
*
|
|
****************************************************************************/
|
|
|
|
DECLSPEC VOID CALLBACK EmuCallBackDebugNotify(
|
|
VOID (CALLBACK *EmuDbgNotify)(INT nBreaktype)) // @parm CallBack function notify Debug breakpoint
|
|
{
|
|
if(EmuDbgNotify != NULL) // debugger enabled
|
|
{
|
|
dwDbgStopPC = -1; // no stop address for goto cursor
|
|
dwDbgRplPC = -1; // no stop address for RPL breakpoint
|
|
pEmuDbgNotify = EmuDbgNotify; // set new handler
|
|
nDbgState = DBG_RUN; // enable debugger
|
|
}
|
|
else
|
|
{
|
|
nDbgState = DBG_OFF; // disable debugger
|
|
pEmuDbgNotify = EmuDbgNotify; // remove handler
|
|
bInterrupt = TRUE; // exit opcode loop
|
|
SetEvent(hEventDebug);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* EmuCallBackStackNotify
|
|
*****************************************************************************
|
|
*
|
|
* @func init CallBack handler to notify caller on hardware stack change;
|
|
* if the CallBack function return TRUE, emulation stops behind the
|
|
* opcode changed hardware stack content, otherwise, if the CallBack
|
|
* function return FALSE, no breakpoint is set
|
|
*
|
|
* @xref none
|
|
*
|
|
* @rdesc VOID
|
|
*
|
|
****************************************************************************/
|
|
|
|
DECLSPEC VOID CALLBACK EmuCallBackStackNotify(
|
|
BOOL (CALLBACK *EmuStackNotify)(VOID)) // @parm CallBack function notify stack changed
|
|
{
|
|
dwRefRstkp = -1; // reference stack pointer not initialized
|
|
pEmuStackNotify = EmuStackNotify; // set new handler
|
|
return;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* EmuGetRegister
|
|
*****************************************************************************
|
|
*
|
|
* @func read a 32 bit register
|
|
*
|
|
* @xref none
|
|
*
|
|
* @rdesc DWORD: 32 bit value of register
|
|
*
|
|
****************************************************************************/
|
|
|
|
DECLSPEC DWORD CALLBACK EmuGetRegister(
|
|
UINT uRegister) // @parm index of register
|
|
{
|
|
DWORD dwResult;
|
|
|
|
switch(uRegister)
|
|
{
|
|
case EMU_REGISTER_PC:
|
|
dwResult = Chipset.pc;
|
|
break;
|
|
case EMU_REGISTER_D0:
|
|
dwResult = Chipset.d0;
|
|
break;
|
|
case EMU_REGISTER_D1:
|
|
dwResult = Chipset.d1;
|
|
break;
|
|
case EMU_REGISTER_DUMMY:
|
|
case EMU_REGISTER_R5L:
|
|
case EMU_REGISTER_R5H:
|
|
case EMU_REGISTER_R6L:
|
|
case EMU_REGISTER_R6H:
|
|
case EMU_REGISTER_R7L:
|
|
case EMU_REGISTER_R7H:
|
|
dwResult = 0; // dummy return
|
|
break;
|
|
case EMU_REGISTER_AL:
|
|
dwResult = RegToDWORD(&Chipset.A[0],8);
|
|
break;
|
|
case EMU_REGISTER_AH:
|
|
dwResult = RegToDWORD(&Chipset.A[8],8);
|
|
break;
|
|
case EMU_REGISTER_BL:
|
|
dwResult = RegToDWORD(&Chipset.B[0],8);
|
|
break;
|
|
case EMU_REGISTER_BH:
|
|
dwResult = RegToDWORD(&Chipset.B[8],8);
|
|
break;
|
|
case EMU_REGISTER_CL:
|
|
dwResult = RegToDWORD(&Chipset.C[0],8);
|
|
break;
|
|
case EMU_REGISTER_CH:
|
|
dwResult = RegToDWORD(&Chipset.C[8],8);
|
|
break;
|
|
case EMU_REGISTER_DL:
|
|
dwResult = RegToDWORD(&Chipset.D[0],8);
|
|
break;
|
|
case EMU_REGISTER_DH:
|
|
dwResult = RegToDWORD(&Chipset.D[8],8);
|
|
break;
|
|
case EMU_REGISTER_R0L:
|
|
dwResult = RegToDWORD(&Chipset.R0[0],8);
|
|
break;
|
|
case EMU_REGISTER_R0H:
|
|
dwResult = RegToDWORD(&Chipset.R0[8],8);
|
|
break;
|
|
case EMU_REGISTER_R1L:
|
|
dwResult = RegToDWORD(&Chipset.R1[0],8);
|
|
break;
|
|
case EMU_REGISTER_R1H:
|
|
dwResult = RegToDWORD(&Chipset.R1[8],8);
|
|
break;
|
|
case EMU_REGISTER_R2L:
|
|
dwResult = RegToDWORD(&Chipset.R2[0],8);
|
|
break;
|
|
case EMU_REGISTER_R2H:
|
|
dwResult = RegToDWORD(&Chipset.R2[8],8);
|
|
break;
|
|
case EMU_REGISTER_R3L:
|
|
dwResult = RegToDWORD(&Chipset.R3[0],8);
|
|
break;
|
|
case EMU_REGISTER_R3H:
|
|
dwResult = RegToDWORD(&Chipset.R3[8],8);
|
|
break;
|
|
case EMU_REGISTER_R4L:
|
|
dwResult = RegToDWORD(&Chipset.R4[0],8);
|
|
break;
|
|
case EMU_REGISTER_R4H:
|
|
dwResult = RegToDWORD(&Chipset.R4[8],8);
|
|
break;
|
|
case EMU_REGISTER_FLAGS:
|
|
/**
|
|
* "FLAGS" register format :
|
|
*
|
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
* | ST |S|x|x|x|K|I|C|M| HST | P |
|
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
* M : Mode (0:Hex, 1:Dec)
|
|
* C : Carry
|
|
* I : Interrupt pending
|
|
* K : KDN Interrupts Enabled
|
|
* S : Shutdn Flag (read only)
|
|
* x : reserved
|
|
*/
|
|
dwResult = RegToDWORD(Chipset.ST,4);
|
|
dwResult <<= 1;
|
|
dwResult |= Chipset.Shutdn ? 1 : 0;
|
|
dwResult <<= (3+1);
|
|
dwResult |= Chipset.intk ? 1 : 0;
|
|
dwResult <<= 1;
|
|
dwResult |= Chipset.inte ? 1 : 0;
|
|
dwResult <<= 1;
|
|
dwResult |= Chipset.carry ? 1 : 0;
|
|
dwResult <<= 1;
|
|
dwResult |= Chipset.mode_dec ? 1 : 0;
|
|
dwResult <<= 4;
|
|
dwResult |= Chipset.HST;
|
|
dwResult <<= 4;
|
|
dwResult |= Chipset.P;
|
|
break;
|
|
case EMU_REGISTER_OUT:
|
|
dwResult = Chipset.out;
|
|
break;
|
|
case EMU_REGISTER_IN:
|
|
dwResult = Chipset.in;
|
|
break;
|
|
case EMU_REGISTER_VIEW1:
|
|
dwResult = ((Chipset.Bank_FF >> 1) & 0x3F) >> 4;
|
|
break;
|
|
case EMU_REGISTER_VIEW2:
|
|
dwResult = ((Chipset.Bank_FF >> 1) & 0x3F) & 0xF;
|
|
break;
|
|
case EMU_REGISTER_RSTKP:
|
|
dwResult = Chipset.rstkp;
|
|
break;
|
|
case EMU_REGISTER_RSTK0:
|
|
dwResult = Chipset.rstk[0];
|
|
break;
|
|
case EMU_REGISTER_RSTK1:
|
|
dwResult = Chipset.rstk[1];
|
|
break;
|
|
case EMU_REGISTER_RSTK2:
|
|
dwResult = Chipset.rstk[2];
|
|
break;
|
|
case EMU_REGISTER_RSTK3:
|
|
dwResult = Chipset.rstk[3];
|
|
break;
|
|
case EMU_REGISTER_RSTK4:
|
|
dwResult = Chipset.rstk[4];
|
|
break;
|
|
case EMU_REGISTER_RSTK5:
|
|
dwResult = Chipset.rstk[5];
|
|
break;
|
|
case EMU_REGISTER_RSTK6:
|
|
dwResult = Chipset.rstk[6];
|
|
break;
|
|
case EMU_REGISTER_RSTK7:
|
|
dwResult = Chipset.rstk[7];
|
|
break;
|
|
case EMU_REGISTER_CLKL:
|
|
dwResult = (DWORD) (Chipset.cycles & 0xFFFFFFFF);
|
|
break;
|
|
case EMU_REGISTER_CLKH:
|
|
dwResult = (DWORD) (Chipset.cycles >> 32);
|
|
break;
|
|
case EMU_REGISTER_CRC:
|
|
dwResult = Chipset.crc;
|
|
break;
|
|
default:
|
|
dwResult = 0; // default return
|
|
_ASSERT(FALSE); // illegal entry
|
|
}
|
|
return dwResult;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* EmuSetRegister
|
|
*****************************************************************************
|
|
*
|
|
* @func write a 32 bit register
|
|
*
|
|
* @xref none
|
|
*
|
|
* @rdesc VOID
|
|
*
|
|
****************************************************************************/
|
|
|
|
DECLSPEC VOID CALLBACK EmuSetRegister(
|
|
UINT uRegister, // @parm index of register
|
|
DWORD dwValue) // @parm new 32 bit value
|
|
{
|
|
switch(uRegister)
|
|
{
|
|
case EMU_REGISTER_PC:
|
|
Chipset.pc = dwValue;
|
|
break;
|
|
case EMU_REGISTER_D0:
|
|
Chipset.d0 = dwValue;
|
|
break;
|
|
case EMU_REGISTER_D1:
|
|
Chipset.d1 = dwValue;
|
|
break;
|
|
case EMU_REGISTER_DUMMY:
|
|
case EMU_REGISTER_R5L:
|
|
case EMU_REGISTER_R5H:
|
|
case EMU_REGISTER_R6L:
|
|
case EMU_REGISTER_R6H:
|
|
case EMU_REGISTER_R7L:
|
|
case EMU_REGISTER_R7H:
|
|
break; // dummy return
|
|
case EMU_REGISTER_AL:
|
|
DWORDToReg(&Chipset.A[0],8,dwValue);
|
|
break;
|
|
case EMU_REGISTER_AH:
|
|
DWORDToReg(&Chipset.A[8],8,dwValue);
|
|
break;
|
|
case EMU_REGISTER_BL:
|
|
DWORDToReg(&Chipset.B[0],8,dwValue);
|
|
break;
|
|
case EMU_REGISTER_BH:
|
|
DWORDToReg(&Chipset.B[8],8,dwValue);
|
|
break;
|
|
case EMU_REGISTER_CL:
|
|
DWORDToReg(&Chipset.C[0],8,dwValue);
|
|
break;
|
|
case EMU_REGISTER_CH:
|
|
DWORDToReg(&Chipset.C[8],8,dwValue);
|
|
break;
|
|
case EMU_REGISTER_DL:
|
|
DWORDToReg(&Chipset.D[0],8,dwValue);
|
|
break;
|
|
case EMU_REGISTER_DH:
|
|
DWORDToReg(&Chipset.D[8],8,dwValue);
|
|
break;
|
|
case EMU_REGISTER_R0L:
|
|
DWORDToReg(&Chipset.R0[0],8,dwValue);
|
|
break;
|
|
case EMU_REGISTER_R0H:
|
|
DWORDToReg(&Chipset.R0[8],8,dwValue);
|
|
break;
|
|
case EMU_REGISTER_R1L:
|
|
DWORDToReg(&Chipset.R1[0],8,dwValue);
|
|
break;
|
|
case EMU_REGISTER_R1H:
|
|
DWORDToReg(&Chipset.R1[8],8,dwValue);
|
|
break;
|
|
case EMU_REGISTER_R2L:
|
|
DWORDToReg(&Chipset.R2[0],8,dwValue);
|
|
break;
|
|
case EMU_REGISTER_R2H:
|
|
DWORDToReg(&Chipset.R2[8],8,dwValue);
|
|
break;
|
|
case EMU_REGISTER_R3L:
|
|
DWORDToReg(&Chipset.R3[0],8,dwValue);
|
|
break;
|
|
case EMU_REGISTER_R3H:
|
|
DWORDToReg(&Chipset.R3[8],8,dwValue);
|
|
break;
|
|
case EMU_REGISTER_R4L:
|
|
DWORDToReg(&Chipset.R4[0],8,dwValue);
|
|
break;
|
|
case EMU_REGISTER_R4H:
|
|
DWORDToReg(&Chipset.R4[8],8,dwValue);
|
|
break;
|
|
case EMU_REGISTER_FLAGS:
|
|
/**
|
|
* "FLAGS" register format :
|
|
*
|
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
* | ST |S|x|x|x|K|I|C|M| HST | P |
|
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
* M : Mode (0:Hex, 1:Dec)
|
|
* C : Carry
|
|
* I : Interrupt pending
|
|
* K : KDN Interrupts Enabled
|
|
* S : Shutdn Flag (read only)
|
|
* x : reserved
|
|
*/
|
|
Chipset.P = (BYTE) (dwValue & 0xF);
|
|
dwValue >>= 4;
|
|
Chipset.HST = (BYTE) (dwValue & 0xF);
|
|
dwValue >>= 4;
|
|
Chipset.mode_dec = (dwValue & 0x1) ? TRUE : FALSE;
|
|
dwValue >>= 1;
|
|
Chipset.carry = (dwValue & 0x1) ? TRUE : FALSE;
|
|
dwValue >>= 1;
|
|
Chipset.inte = (dwValue & 0x1) ? TRUE : FALSE;
|
|
dwValue >>= 1;
|
|
Chipset.intk = (dwValue & 0x1) ? TRUE : FALSE;
|
|
dwValue >>= (1+4);
|
|
DWORDToReg(Chipset.ST,sizeof(Chipset.ST),dwValue);
|
|
PCHANGED;
|
|
break;
|
|
case EMU_REGISTER_OUT:
|
|
Chipset.out = (WORD) (dwValue & 0xFFFF);
|
|
break;
|
|
case EMU_REGISTER_IN:
|
|
Chipset.in = (WORD) (dwValue & 0xFFFF);
|
|
break;
|
|
case EMU_REGISTER_VIEW1:
|
|
Chipset.Bank_FF &= 0x1F;
|
|
Chipset.Bank_FF |= (dwValue & 0x03) << (4+1);
|
|
RomSwitch(Chipset.Bank_FF);
|
|
break;
|
|
case EMU_REGISTER_VIEW2:
|
|
Chipset.Bank_FF &= 0x61;
|
|
Chipset.Bank_FF |= (dwValue & 0x0F) << 1;
|
|
RomSwitch(Chipset.Bank_FF);
|
|
break;
|
|
case EMU_REGISTER_RSTKP:
|
|
Chipset.rstkp = dwValue;
|
|
break;
|
|
case EMU_REGISTER_RSTK0:
|
|
Chipset.rstk[0] = dwValue;
|
|
break;
|
|
case EMU_REGISTER_RSTK1:
|
|
Chipset.rstk[1] = dwValue;
|
|
break;
|
|
case EMU_REGISTER_RSTK2:
|
|
Chipset.rstk[2] = dwValue;
|
|
break;
|
|
case EMU_REGISTER_RSTK3:
|
|
Chipset.rstk[3] = dwValue;
|
|
break;
|
|
case EMU_REGISTER_RSTK4:
|
|
Chipset.rstk[4] = dwValue;
|
|
break;
|
|
case EMU_REGISTER_RSTK5:
|
|
Chipset.rstk[5] = dwValue;
|
|
break;
|
|
case EMU_REGISTER_RSTK6:
|
|
Chipset.rstk[6] = dwValue;
|
|
break;
|
|
case EMU_REGISTER_RSTK7:
|
|
Chipset.rstk[7] = dwValue;
|
|
break;
|
|
case EMU_REGISTER_CLKL:
|
|
case EMU_REGISTER_CLKH:
|
|
break; // not allowed to change
|
|
case EMU_REGISTER_CRC:
|
|
Chipset.crc = (WORD) (dwValue & 0xFFFF);
|
|
break;
|
|
default:
|
|
_ASSERT(FALSE); // illegal entry
|
|
}
|
|
return;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* EmuGetMem
|
|
*****************************************************************************
|
|
*
|
|
* @func read one nibble from the specified mapped address
|
|
*
|
|
* @xref none
|
|
*
|
|
* @rdesc VOID
|
|
*
|
|
****************************************************************************/
|
|
|
|
DECLSPEC VOID CALLBACK EmuGetMem(
|
|
DWORD dwMapAddr, // @parm mapped address of Saturn CPU
|
|
BYTE *pbyValue) // @parm readed nibble
|
|
{
|
|
Npeek(pbyValue,dwMapAddr,1);
|
|
return;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* EmuSetMem
|
|
*****************************************************************************
|
|
*
|
|
* @func write one nibble to the specified mapped address
|
|
*
|
|
* @xref none
|
|
*
|
|
* @rdesc VOID
|
|
*
|
|
****************************************************************************/
|
|
|
|
DECLSPEC VOID CALLBACK EmuSetMem(
|
|
DWORD dwMapAddr, // @parm mapped address of Saturn CPU
|
|
BYTE byValue) // @parm nibble to write
|
|
{
|
|
Nwrite(&byValue,dwMapAddr,1);
|
|
return;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* EmuGetRom
|
|
*****************************************************************************
|
|
*
|
|
* @func return size and base address of mapped ROM
|
|
*
|
|
* @xref none
|
|
*
|
|
* @rdesc LPBYTE: base address of ROM (pointer to original data)
|
|
*
|
|
****************************************************************************/
|
|
|
|
DECLSPEC LPBYTE CALLBACK EmuGetRom(
|
|
DWORD *pdwRomSize) // @parm return size of ROM in nibbles
|
|
{
|
|
*pdwRomSize = dwRomSize;
|
|
return pbyRom;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* EmuSetBreakpoint
|
|
*****************************************************************************
|
|
*
|
|
* @func set ASM code/data breakpoint
|
|
*
|
|
* @xref none
|
|
*
|
|
* @rdesc BOOL: TRUE = Error, Breakpoint table full
|
|
* FALSE = OK, Breakpoint set
|
|
*
|
|
****************************************************************************/
|
|
|
|
DECLSPEC BOOL CALLBACK EmuSetBreakpoint(
|
|
DWORD dwAddress, // @parm breakpoint address to set
|
|
UINT nBreakpointType) // @parm breakpoint type to set
|
|
{
|
|
INT i;
|
|
|
|
_ASSERT( nBreakpointType == BP_EXEC // illegal breakpoint type
|
|
|| nBreakpointType == BP_READ
|
|
|| nBreakpointType == BP_WRITE
|
|
|| nBreakpointType == BP_RPL
|
|
|| nBreakpointType == BP_ACCESS
|
|
|| nBreakpointType == BP_ROM);
|
|
|
|
for (i = 0; i < wBreakpointCount; ++i) // search for breakpoint
|
|
{
|
|
// breakpoint already set
|
|
if ( sBreakpoint[i].dwAddr == dwAddress
|
|
&& sBreakpoint[i].nType == nBreakpointType)
|
|
return FALSE;
|
|
}
|
|
|
|
if (wBreakpointCount >= MAXBREAKPOINTS) // breakpoint buffer full
|
|
return TRUE;
|
|
|
|
sBreakpoint[wBreakpointCount].dwAddr = dwAddress;
|
|
sBreakpoint[wBreakpointCount].nType = nBreakpointType;
|
|
++wBreakpointCount;
|
|
return FALSE;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* EmuClearBreakpoint
|
|
*****************************************************************************
|
|
*
|
|
* @func clear ASM code/data breakpoint
|
|
*
|
|
* @xref none
|
|
*
|
|
* @rdesc BOOL: TRUE = Error, Breakpoint not found
|
|
* FALSE = OK, Breakpoint cleared
|
|
*
|
|
****************************************************************************/
|
|
|
|
DECLSPEC BOOL CALLBACK EmuClearBreakpoint(
|
|
DWORD dwAddress, // @parm breakpoint address to clear
|
|
UINT nBreakpointType) // @parm breakpoint type to clear
|
|
{
|
|
INT i;
|
|
|
|
_ASSERT( nBreakpointType == BP_EXEC // illegal breakpoint type
|
|
|| nBreakpointType == BP_READ
|
|
|| nBreakpointType == BP_WRITE
|
|
|| nBreakpointType == BP_RPL
|
|
|| nBreakpointType == BP_ACCESS
|
|
|| nBreakpointType == BP_ROM);
|
|
|
|
for (i = 0; i < wBreakpointCount; ++i) // search for breakpoint
|
|
{
|
|
// breakpoint found
|
|
if ( sBreakpoint[i].dwAddr == dwAddress
|
|
&& sBreakpoint[i].nType == nBreakpointType)
|
|
{
|
|
// move rest to top
|
|
for (++i; i < wBreakpointCount; ++i)
|
|
sBreakpoint[i-1] = sBreakpoint[i];
|
|
|
|
--wBreakpointCount;
|
|
return FALSE; // breakpoint found
|
|
}
|
|
}
|
|
return TRUE; // breakpoint not found
|
|
}
|
|
|
|
/****************************************************************************
|
|
* EmuClearAllBreakpoints
|
|
*****************************************************************************
|
|
*
|
|
* @func clear all breakpoints
|
|
*
|
|
* @xref none
|
|
*
|
|
* @rdesc VOID
|
|
*
|
|
****************************************************************************/
|
|
|
|
DECLSPEC VOID CALLBACK EmuClearAllBreakpoints(VOID)
|
|
{
|
|
wBreakpointCount = 0;
|
|
return;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* EmuEnableNop3Breakpoint
|
|
*****************************************************************************
|
|
*
|
|
* @func enable/disable NOP3 breakpoint
|
|
* stop emulation at Opcode 420 for GOC + (next line)
|
|
*
|
|
* @xref none
|
|
*
|
|
* @rdesc VOID
|
|
*
|
|
****************************************************************************/
|
|
|
|
DECLSPEC VOID CALLBACK EmuEnableNop3Breakpoint(
|
|
BOOL bEnable) // @parm stop on NOP3 opcode
|
|
{
|
|
bDbgNOP3 = bEnable; // change stop on NOP3 breakpoint flag
|
|
return;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* EmuEnableDocodeBreakpoint
|
|
*****************************************************************************
|
|
*
|
|
* @func enable/disable DOCODE breakpoint
|
|
*
|
|
* @xref none
|
|
*
|
|
* @rdesc VOID
|
|
*
|
|
****************************************************************************/
|
|
|
|
DECLSPEC VOID CALLBACK EmuEnableDoCodeBreakpoint(
|
|
BOOL bEnable) // @parm stop on DOCODE entry
|
|
{
|
|
bDbgCode = bEnable; // change stop on DOCODE breakpoint flag
|
|
return;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* EmuEnableRplBreakpoint
|
|
*****************************************************************************
|
|
*
|
|
* @func enable/disable RPL breakpoint
|
|
*
|
|
* @xref none
|
|
*
|
|
* @rdesc VOID
|
|
*
|
|
****************************************************************************/
|
|
|
|
DECLSPEC VOID CALLBACK EmuEnableRplBreakpoint(
|
|
BOOL bEnable) // @parm stop on RPL exit
|
|
{
|
|
bDbgRPL = bEnable; // change stop on RPL exit flag
|
|
return;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* EmuEnableSkipInterruptCode
|
|
*****************************************************************************
|
|
*
|
|
* @func enable/disable skip single step execution inside the interrupt
|
|
* handler, this option has no effect on code and data breakpoints
|
|
*
|
|
* @xref none
|
|
*
|
|
* @rdesc VOID
|
|
*
|
|
****************************************************************************/
|
|
|
|
DECLSPEC VOID CALLBACK EmuEnableSkipInterruptCode(
|
|
BOOL bEnable) // @parm TRUE = skip code instructions
|
|
// inside interrupt service routine
|
|
{
|
|
bDbgSkipInt = bEnable; // change skip interrupt code flag
|
|
return;
|
|
}
|