emu48plus-mirror/source/DEBUGDLL.C
Gwenhael Le Moine 5b61a0678e
2016-09-11: Updated to version 56
Signed-off-by: Gwenhael Le Moine <gwenhael.le.moine@gmail.com>
2024-03-20 07:46:28 +01:00

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;
}