Debugger:

. Move the BRK and invalid opcodes checks out of main emulation's Fetch() and into DebugContinueStepping()
. Added a new break condition: when PC reads floating bus or I/O memory
. On a break condition, output a 'Stop Reason' message to the console
This commit is contained in:
tomcw 2017-03-07 21:35:38 +00:00
parent adfb48c6ab
commit 3d6c10bad9
8 changed files with 117 additions and 144 deletions

View File

@ -102,7 +102,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "Z80VICE\z80.h" #include "Z80VICE\z80.h"
#include "Z80VICE\z80mem.h" #include "Z80VICE\z80mem.h"
#include "Debugger\Debug.h"
#include "YamlHelper.h" #include "YamlHelper.h"
// 6502 Accumulator Bit Flags // 6502 Accumulator Bit Flags
@ -200,26 +199,9 @@ void SetActiveCpu(eCpuType cpu)
#include "cpu/cpu_instructions.inl" #include "cpu/cpu_instructions.inl"
void RequestDebugger()
{
// BUG: This causes DebugBegin to constantly be called.
// It's as if the WM_KEYUP are auto-repeating?
// FrameWndProc()
// ProcessButtonClick()
// DebugBegin()
// PostMessage( g_hFrameWindow, WM_KEYDOWN, DEBUG_TOGGLE_KEY, 0 );
// PostMessage( g_hFrameWindow, WM_KEYUP , DEBUG_TOGGLE_KEY, 0 );
// Not a valid solution, since hitting F7 (to exit) debugger gets the debugger out of sync
// due to EnterMessageLoop() calling ContinueExecution() after the mode has changed to DEBUG.
// DebugBegin();
FrameWndProc( g_hFrameWindow, WM_KEYDOWN, DEBUG_TOGGLE_KEY, 0 );
FrameWndProc( g_hFrameWindow, WM_KEYUP , DEBUG_TOGGLE_KEY, 0 );
}
// Break into debugger on invalid opcodes // Break into debugger on invalid opcodes
#define INV IsDebugBreakOnInvalid(AM_1); //#define INV IsDebugBreakOnInvalid(AM_1);
#define INV
/**************************************************************************** /****************************************************************************
* *
@ -373,7 +355,7 @@ static void DebugHddEntrypoint(const USHORT PC)
} }
#endif #endif
static __forceinline int Fetch(BYTE& iOpcode, ULONG uExecutedCycles) static __forceinline void Fetch(BYTE& iOpcode, ULONG uExecutedCycles)
{ {
const USHORT PC = regs.pc; const USHORT PC = regs.pc;
@ -385,16 +367,12 @@ static __forceinline int Fetch(BYTE& iOpcode, ULONG uExecutedCycles)
? IORead[(PC>>4) & 0xFF](PC,PC,0,0,uExecutedCycles) // Fetch opcode from I/O memory, but params are still from mem[] ? IORead[(PC>>4) & 0xFF](PC,PC,0,0,uExecutedCycles) // Fetch opcode from I/O memory, but params are still from mem[]
: *(mem+PC); : *(mem+PC);
if (IsDebugBreakOpcode( iOpcode ))
return 0;
#ifdef USE_SPEECH_API #ifdef USE_SPEECH_API
if (PC == COUT && g_Speech.IsEnabled() && !g_bFullSpeed) if (PC == COUT && g_Speech.IsEnabled() && !g_bFullSpeed)
CaptureCOUT(); CaptureCOUT();
#endif #endif
regs.pc++; regs.pc++;
return 1;
} }
//#define ENABLE_NMI_SUPPORT // Not used - so don't enable //#define ENABLE_NMI_SUPPORT // Not used - so don't enable

View File

@ -36,7 +36,6 @@ static DWORD Cpu6502(DWORD uTotalCycles, const bool bVideoUpdate)
AF_TO_EF AF_TO_EF
ULONG uExecutedCycles = 0; ULONG uExecutedCycles = 0;
WORD base; WORD base;
g_bDebugBreakpointHit = 0;
do do
{ {
@ -53,10 +52,10 @@ static DWORD Cpu6502(DWORD uTotalCycles, const bool bVideoUpdate)
} }
else else
{ {
if (!Fetch(iOpcode, uExecutedCycles)) Fetch(iOpcode, uExecutedCycles);
break;
#define $ INV // INV = Invalid -> Debugger Break //#define $ INV // INV = Invalid -> Debugger Break
#define $
switch (iOpcode) switch (iOpcode)
{ {
case 0x00: BRK CYC(7) break; case 0x00: BRK CYC(7) break;
@ -335,10 +334,6 @@ static DWORD Cpu6502(DWORD uTotalCycles, const bool bVideoUpdate)
EF_TO_AF EF_TO_AF
if( g_bDebugBreakpointHit )
if ((g_nAppMode != MODE_DEBUG) && (g_nAppMode != MODE_STEPPING)) // Running at full speed? (debugger not running)
RequestDebugger();
return uExecutedCycles; return uExecutedCycles;
} }

View File

@ -39,7 +39,6 @@ static DWORD Cpu65C02(DWORD uTotalCycles, const bool bVideoUpdate)
AF_TO_EF AF_TO_EF
ULONG uExecutedCycles = 0; ULONG uExecutedCycles = 0;
WORD base; WORD base;
g_bDebugBreakpointHit = 0;
do do
{ {
@ -56,10 +55,10 @@ static DWORD Cpu65C02(DWORD uTotalCycles, const bool bVideoUpdate)
} }
else else
{ {
if (!Fetch(iOpcode, uExecutedCycles)) Fetch(iOpcode, uExecutedCycles);
break;
#define $ INV // INV = Invalid -> Debugger Break //#define $ INV // INV = Invalid -> Debugger Break
#define $
switch (iOpcode) switch (iOpcode)
{ {
case 0x00: BRK CYC(7) break; case 0x00: BRK CYC(7) break;
@ -338,10 +337,6 @@ static DWORD Cpu65C02(DWORD uTotalCycles, const bool bVideoUpdate)
EF_TO_AF // Emulator Flags to Apple Flags EF_TO_AF // Emulator Flags to Apple Flags
if( g_bDebugBreakpointHit )
if ((g_nAppMode != MODE_DEBUG) && (g_nAppMode != MODE_STEPPING)) // Running at full speed? (debugger not running)
RequestDebugger();
return uExecutedCycles; return uExecutedCycles;
} }

View File

@ -113,7 +113,6 @@ static DWORD Cpu65D02(DWORD uTotalCycles, const bool bVideoUpdate)
AF_TO_EF AF_TO_EF
ULONG uExecutedCycles = 0; ULONG uExecutedCycles = 0;
WORD base; WORD base;
g_bDebugBreakpointHit = 0;
do do
{ {
@ -132,15 +131,15 @@ static DWORD Cpu65D02(DWORD uTotalCycles, const bool bVideoUpdate)
HEATMAP_X( regs.pc ); HEATMAP_X( regs.pc );
if (!Fetch(iOpcode, uExecutedCycles)) Fetch(iOpcode, uExecutedCycles);
break;
// INV = Invalid -> Debugger Break // INV = Invalid -> Debugger Break
// MSVC C PreProcessor is BROKEN... #define @ INV // MSVC C PreProcessor is BROKEN... #define @ INV
//#define # INV //#define # INV
//#define @ Read() //#define @ Read()
//#define $ Store() //#define $ Store()
#define $ INV //#define $ INV // INV = Invalid -> Debugger Break
#define $
switch (iOpcode) switch (iOpcode)
{ {
@ -423,9 +422,5 @@ static DWORD Cpu65D02(DWORD uTotalCycles, const bool bVideoUpdate)
EF_TO_AF // Emulator Flags to Apple Flags EF_TO_AF // Emulator Flags to Apple Flags
if( g_bDebugBreakpointHit )
if ((g_nAppMode != MODE_DEBUG) && (g_nAppMode != MODE_STEPPING)) // // Running at full speed? (debugger not running)
RequestDebugger();
return uExecutedCycles; return uExecutedCycles;
} }

View File

@ -67,8 +67,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
int g_nDebugBreakOnInvalid = 0; // Bit Flags of Invalid Opcode to break on: // iOpcodeType = AM_IMPLIED (BRK), AM_1, AM_2, AM_3 int g_nDebugBreakOnInvalid = 0; // Bit Flags of Invalid Opcode to break on: // iOpcodeType = AM_IMPLIED (BRK), AM_1, AM_2, AM_3
int g_iDebugBreakOnOpcode = 0; int g_iDebugBreakOnOpcode = 0;
bool g_bDebugBreakDelayCheck = false; // If exiting the debugger, allow at least one instruction to execute so we don't trigger on the same invalid opcode static int g_bDebugBreakpointHit = 0; // See: BreakpointHit_t
int g_bDebugBreakpointHit = 0; // See: BreakpointHit_t
int g_nBreakpoints = 0; int g_nBreakpoints = 0;
Breakpoint_t g_aBreakpoints[ MAX_BREAKPOINTS ]; Breakpoint_t g_aBreakpoints[ MAX_BREAKPOINTS ];
@ -868,6 +867,24 @@ _Help:
//=========================================================================== //===========================================================================
// iOpcodeType = AM_IMPLIED (BRK), AM_1, AM_2, AM_3
static int IsDebugBreakOnInvalid( int iOpcodeType )
{
g_bDebugBreakpointHit |= ((g_nDebugBreakOnInvalid >> iOpcodeType) & 1) ? BP_HIT_INVALID : 0;
return g_bDebugBreakpointHit;
}
// iOpcodeType = AM_IMPLIED (BRK), AM_1, AM_2, AM_3
static void SetDebugBreakOnInvalid( int iOpcodeType, int nValue )
{
if (iOpcodeType <= AM_3)
{
g_nDebugBreakOnInvalid &= ~ ( 1 << iOpcodeType);
g_nDebugBreakOnInvalid |= ((nValue & 1) << iOpcodeType);
}
}
Update_t CmdBreakInvalid (int nArgs) // Breakpoint IFF Full-speed! Update_t CmdBreakInvalid (int nArgs) // Breakpoint IFF Full-speed!
{ {
if (nArgs > 2) // || (nArgs == 0)) if (nArgs > 2) // || (nArgs == 0))
@ -8469,9 +8486,27 @@ void DebugExitDebugger ()
} }
//=========================================================================== //===========================================================================
static void CheckBreakOpcode( int iOpcode )
{
if (iOpcode == 0x00) // BRK
IsDebugBreakOnInvalid( AM_IMPLIED );
if (g_aOpcodes[iOpcode].sMnemonic[0] >= 'a') // All 6502/65C02 undocumented opcodes mnemonics are lowercase strings!
{
// TODO: Translate g_aOpcodes[iOpcode].nAddressMode into {AM_1, AM_2, AM_3}
IsDebugBreakOnInvalid( AM_1 );
}
// User wants to enter debugger on specific opcode? (NB. Can't be BRK)
if (g_iDebugBreakOnOpcode && g_iDebugBreakOnOpcode == iOpcode)
g_bDebugBreakpointHit |= BP_HIT_OPCODE;
}
void DebugContinueStepping () void DebugContinueStepping ()
{ {
static unsigned nStepsTaken = 0; static unsigned nStepsTaken = 0;
static bool bForceSingleStepNext = false; // Allow at least one instruction to execute so we don't trigger on the same invalid opcode
if (g_nDebugSkipLen > 0) if (g_nDebugSkipLen > 0)
{ {
@ -8491,26 +8526,80 @@ void DebugContinueStepping ()
if (g_nDebugSteps) if (g_nDebugSteps)
{ {
if (g_hTraceFile) bool bDoSingleStep = true;
OutputTraceLine();
// Update profiling stats if (bForceSingleStepNext)
{ {
BYTE nOpcode = *(mem+regs.pc); bForceSingleStepNext = false;
int nOpmode = g_aOpcodes[ nOpcode ].nAddressMode; g_bDebugBreakpointHit = BP_HIT_NONE; // Don't show 'Stop Reason' msg a 2nd time
}
else if (GetActiveCpu() != CPU_Z80)
{
if (g_hTraceFile)
OutputTraceLine();
g_aProfileOpcodes[ nOpcode ].m_nCount++; g_bDebugBreakpointHit = BP_HIT_NONE;
g_aProfileOpmodes[ nOpmode ].m_nCount++;
bool bPCIsFloatBusOrIO = (regs.pc >= 0xC000 && regs.pc <= 0xC0FF); // TODO: Determine $C100..CFFF - assume executable
if (!bPCIsFloatBusOrIO)
{
BYTE nOpcode = *(mem+regs.pc);
// Update profiling stats
int nOpmode = g_aOpcodes[ nOpcode ].nAddressMode;
g_aProfileOpcodes[ nOpcode ].m_nCount++;
g_aProfileOpmodes[ nOpmode ].m_nCount++;
CheckBreakOpcode( nOpcode ); // Can set g_bDebugBreakpointHit
}
else
{
g_bDebugBreakpointHit = BP_HIT_PC_READ_FLOATING_BUS_OR_IO_REG;
}
if (g_bDebugBreakpointHit)
{
bDoSingleStep = false;
bForceSingleStepNext = true; // Allow next single-step (after this) to execute
}
} }
SingleStep(g_bGoCmd_ReinitFlag); if (bDoSingleStep)
g_bGoCmd_ReinitFlag = false; {
SingleStep(g_bGoCmd_ReinitFlag);
g_bGoCmd_ReinitFlag = false;
g_bDebugBreakpointHit |= CheckBreakpointsIO() || CheckBreakpointsReg(); g_bDebugBreakpointHit |= CheckBreakpointsIO() || CheckBreakpointsReg();
}
if (regs.pc == g_nDebugStepUntil || g_bDebugBreakpointHit)
{
TCHAR sText[ CONSOLE_WIDTH ];
char* pszStopReason = NULL;
if (regs.pc == g_nDebugStepUntil)
pszStopReason = TEXT("PC matches 'Go until' address");
else if (g_bDebugBreakpointHit & BP_HIT_INVALID)
pszStopReason = TEXT("Invalid opcode");
else if (g_bDebugBreakpointHit & BP_HIT_OPCODE)
pszStopReason = TEXT("Opcode match");
else if (g_bDebugBreakpointHit & BP_HIT_REG)
pszStopReason = TEXT("Register matches value");
else if (g_bDebugBreakpointHit & BP_HIT_MEM)
pszStopReason = TEXT("Memory accessed");
else if (g_bDebugBreakpointHit & BP_HIT_PC_READ_FLOATING_BUS_OR_IO_REG)
pszStopReason = TEXT("PC reads from floating bus or I/O register");
else
pszStopReason = TEXT("Unknown!");
ConsoleBufferPushFormat( sText, TEXT("Stop reason: %s"), pszStopReason );
ConsoleUpdate();
if ((regs.pc == g_nDebugStepUntil) || g_bDebugBreakpointHit)
g_nDebugSteps = 0; g_nDebugSteps = 0;
else if (g_nDebugSteps > 0) }
if (g_nDebugSteps > 0)
g_nDebugSteps--; g_nDebugSteps--;
} }
@ -8525,7 +8614,7 @@ void DebugContinueStepping ()
DisasmCalcTopBotAddress(); DisasmCalcTopBotAddress();
Update_t bUpdate = UPDATE_ALL; Update_t bUpdate = UPDATE_ALL;
UpdateDisplay( bUpdate ); // nStepsTaken >= 0x10000); UpdateDisplay( bUpdate );
nStepsTaken = 0; nStepsTaken = 0;
} }
} }

View File

@ -34,6 +34,7 @@
,BP_HIT_OPCODE = (1 << 1) ,BP_HIT_OPCODE = (1 << 1)
,BP_HIT_REG = (1 << 2) ,BP_HIT_REG = (1 << 2)
,BP_HIT_MEM = (1 << 3) ,BP_HIT_MEM = (1 << 3)
,BP_HIT_PC_READ_FLOATING_BUS_OR_IO_REG = (1 << 4)
}; };
extern int g_nBreakpoints; extern int g_nBreakpoints;
@ -42,14 +43,9 @@
extern const char *g_aBreakpointSource [ NUM_BREAKPOINT_SOURCES ]; extern const char *g_aBreakpointSource [ NUM_BREAKPOINT_SOURCES ];
extern const TCHAR *g_aBreakpointSymbols[ NUM_BREAKPOINT_OPERATORS ]; extern const TCHAR *g_aBreakpointSymbols[ NUM_BREAKPOINT_OPERATORS ];
// Any Speed Breakpoints
extern int g_nDebugBreakOnInvalid ; extern int g_nDebugBreakOnInvalid ;
extern int g_iDebugBreakOnOpcode ; extern int g_iDebugBreakOnOpcode ;
// Breakpoint Status
extern bool g_bDebugBreakDelayCheck;
extern int g_bDebugBreakpointHit ;
// Commands // Commands
void VerifyDebuggerCommandTable(); void VerifyDebuggerCommandTable();
@ -144,52 +140,6 @@
bool GetBreakpointInfo ( WORD nOffset, bool & bBreakpointActive_, bool & bBreakpointEnable_ ); bool GetBreakpointInfo ( WORD nOffset, bool & bBreakpointActive_, bool & bBreakpointEnable_ );
inline int _IsDebugBreakOnOpcode( int iOpcode )
{
if (g_iDebugBreakOnOpcode == iOpcode)
g_bDebugBreakpointHit |= BP_HIT_OPCODE;
return g_bDebugBreakpointHit;
}
// iOpcodeType = AM_IMPLIED (BRK), AM_1, AM_2, AM_3
inline int IsDebugBreakOnInvalid( int iOpcodeType )
{
g_bDebugBreakpointHit |= ((g_nDebugBreakOnInvalid >> iOpcodeType) & 1) ? BP_HIT_INVALID : 0;
return g_bDebugBreakpointHit;
}
// iOpcodeType = AM_IMPLIED (BRK), AM_1, AM_2, AM_3
inline void SetDebugBreakOnInvalid( int iOpcodeType, int nValue )
{
if (iOpcodeType <= AM_3)
{
g_nDebugBreakOnInvalid &= ~ ( 1 << iOpcodeType);
g_nDebugBreakOnInvalid |= ((nValue & 1) << iOpcodeType);
}
}
//
// CPU checks the Debugger breakpoints
// a) at opcode fetch
// b) after opcode execution
//
inline int IsDebugBreakOpcode( int iOpcode )
{
if (g_bDebugBreakDelayCheck)
{
g_bDebugBreakDelayCheck = false;
return false;
}
if (! iOpcode )
IsDebugBreakOnInvalid( AM_IMPLIED );
if (g_iDebugBreakOnOpcode ) // User wants to enter debugger on specific opcode?
_IsDebugBreakOnOpcode(iOpcode);
return g_bDebugBreakpointHit;
}
// Source Level Debugging // Source Level Debugging
int FindSourceLine( WORD nAddress ); int FindSourceLine( WORD nAddress );
const char* FormatAddress( WORD nAddress, int nBytes ); const char* FormatAddress( WORD nAddress, int nBytes );

View File

@ -1898,7 +1898,6 @@ static void ProcessButtonClick(int button, bool bFromButtonUI /*=false*/)
else else
if (g_nAppMode == MODE_DEBUG) if (g_nAppMode == MODE_DEBUG)
{ {
g_bDebugBreakDelayCheck = true;
ProcessButtonClick(BTN_RUN); // Exit debugger, switch to MODE_RUNNING or MODE_STEPPING ProcessButtonClick(BTN_RUN); // Exit debugger, switch to MODE_RUNNING or MODE_STEPPING
} }
else else

View File

@ -14,15 +14,6 @@ LPBYTE memdirty = NULL; // TODO: Init
iofunction IORead[256] = {0}; // TODO: Init iofunction IORead[256] = {0}; // TODO: Init
iofunction IOWrite[256] = {0}; // TODO: Init iofunction IOWrite[256] = {0}; // TODO: Init
// From Debugger_Types.h
enum AddressingMode_e // ADDRESSING_MODES_e
{
AM_IMPLIED // Note: SetDebugBreakOnInvalid() assumes this order of first 4 entries
, AM_1 // Invalid 1 Byte
, AM_2 // Invalid 2 Bytes
, AM_3 // Invalid 3 Bytes
};
// From CPU.cpp // From CPU.cpp
#define AF_SIGN 0x80 #define AF_SIGN 0x80
#define AF_OVERFLOW 0x40 #define AF_OVERFLOW 0x40
@ -58,12 +49,6 @@ static __forceinline int Fetch(BYTE& iOpcode, ULONG uExecutedCycles)
return 1; return 1;
} }
#define INV IsDebugBreakOnInvalid(AM_1);
inline int IsDebugBreakOnInvalid( int iOpcodeType )
{
return 0;
}
static __forceinline void DoIrqProfiling(DWORD uCycles) static __forceinline void DoIrqProfiling(DWORD uCycles)
{ {
} }
@ -80,19 +65,6 @@ static __forceinline void IRQ(ULONG& uExecutedCycles, BOOL& flagc, BOOL& flagn,
{ {
} }
void RequestDebugger()
{
}
// From Debug.h
inline int IsDebugBreakpointHit()
{
return 0;
}
// From Debug.cpp
int g_bDebugBreakpointHit = 0;
// From z80.cpp // From z80.cpp
DWORD z80_mainloop(ULONG uTotalCycles, ULONG uExecutedCycles) DWORD z80_mainloop(ULONG uTotalCycles, ULONG uExecutedCycles)
{ {