1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
|
/* -*- mode: C++; tab-width: 4 -*- */
/* ================================================================================== */
/* Copyright (c) 1998-1999 3Com Corporation or its subsidiaries. All rights reserved. */
/* ================================================================================== */
#ifndef _DEBUGMGR_H_
#define _DEBUGMGR_H_
#include "CPU_REG.h" // kException_Trace
// Types
#pragma mark Types
class SLP;
class CSocket;
class CTCPSocket;
typedef uae_u32 (*registerFun)(int num);
typedef Bool (*compareFun)(DWord a, DWord b);
// Need to change the packing at this point for the Solaris platform.
// According to Frank Yellin <fy@eng.sun.com>:
//
// This fixes a bug in the compiler.
//
// Without this pack/pop, the code in DebugMgr.cpp, all references to
// gDebuggerGlobals.bp[ii].addr
// are compiled as if this were on a 4-byte boundary. This is in spite of
// the fact that the compiler knows that BreakPoint is packed, and that it
// is 6-bytes long!
//
// Accesses to gDebuggerGlobals.bp[ii].addr sometimes give a bus error.
#include "PalmPack.h"
typedef struct BreakpointCondition
// Breakpoint conditions are of the form
// <register-expr>[<size>] <cond> <value>
// where:
// <register-expr> is either a 68000 register (e.g. "d5") or an indirect reference at a constant
// offset from a 68000 register (e.g. "8(a6)"). An indirect reference reads from memory; when
// you set up a breakpoint condition involving an indirect reference, you must be sure that
// the indirect reference will point to a valid memory address whenever the breakpoint is hit.
//
// <size> is either ".l" (long), ".w" (word) or ".b" (byte). If no size is specified, the
// expression is assumed to be long.
// <cond> is a binary comparison operator: ==, !=, <=, >=, <, or >
// <value> is a 32-bit integer
// All comparisons are unsigned!
{
registerFun regType;
int regNum;
Bool indirect;
uae_u32 indirectOffset;
int size; /* number of bytes to compare: 4, 2, or 1 */
compareFun condition;
uae_u32 value;
// The source text. We keep this around so that, for example, a user can specify
// a condition using either hex or decimal notation and they will see the same
// notation the next time they edit the breakpoint.
char* source;
Bool Evaluate (void);
} BreakpointCondition;
struct DebugGlobalsType
{
// Mode settings
bool inDebugger; // true if in debug mode
bool ignoreDbgBreaks; // if true, ignore DbgBreak's
bool reEntered; // true if we re-entered
bool firstEntrance; // true first time we enter debugger
bool stepSpy; // true if step spying.
bool breakingOnATrap; // true if there are A-Traps to check
bool continueOverATrap; // True if skipping over next system call
bool continueOverBP; // True if skipping over next breakpoint
bool checkTrapWordOnExit;
uae_u16 trapWord;
uae_u16 refNum;
// Breakpoints and saved opcodes behind each one
BreakpointType bp[dbgTotalBreakpoints];
uae_u16 bpOpcode[dbgTotalBreakpoints];
BreakpointCondition *bpCondition[dbgTotalBreakpoints]; // condition, or NULL if none
// Current trap breaks
Word trapBreak[dbgTotalTrapBreaks];
Word trapParam[dbgTotalTrapBreaks];
// Step spy support
uaecptr ssAddr; // address to step spy on
DWord ssValue; // saved value
// Exception type
uae_s32 excType; // why we entered debugger
// (adam) Data breakpoint support. This is similar to the step spy capability, but can monitor
// an arbitrary range of addresses for writes, and doesn't require a saved value.
bool watchEnabled;
uaecptr watchAddr; // address to watch, or 0 if none
DWord watchBytes; // number of bytes to watch
};
#include "PalmPackPop.h"
// class Debug
#pragma mark class Debug
extern DebugGlobalsType gDebuggerGlobals;
extern Bool gFullDebuggerPackets;
class Debug
{
public:
static void Initialize (void);
static void Reset (void);
static void Save (SessionFile&);
static void Load (SessionFile&);
static void Dispose (void);
static void Startup (void);
static void Shutdown (void);
static Bool ConnectedToTCPDebugger (void);
static CTCPSocket* GetTCPDebuggerSocket (void);
static CSocket* GetDebuggerSocket (void);
static ErrCode HandleNewPacket (SLP&);
static Bool BreakIfConnected (uae_s32 exceptionNumber, uaecptr oldpc);
static Bool HandleTrap8 (uae_s32 exceptionNumber, uaecptr oldpc);
static Bool HandleSystemCall (const SystemCallContext& context);
static ErrCode EnterDebugger (uae_s32 reason, uaecptr oldpc, SLP*);
static ErrCode ExitDebugger (void);
static Bool InDebugger (void);
static void CheckStepSpy (uaecptr writeAddress, int writeBytes);
static void HandleCPUBreak (void);
static void InstallCPUBreaks (void);
static void RemoveCPUBreaks (void);
static BreakpointCondition*
NewBreakpointCondition (const char* sourceString);
static void SetBreakpoint (int index, uaecptr addr, BreakpointCondition* c);
static void ClearBreakpoint (int index);
private:
static void ConditionalBreak (uaecptr oldpc);
static Bool MustBreakOnTrapSystemCall
(uae_u16 trapWord, uae_u16 refNum);
static void EventCallback (CSocket* s, int event);
static void CreateListeningSockets (void);
static void DeleteListeningSockets (void);
};
// This function is called from the RAM setter functions. Make it inline so
// that the common case where stepSpy == FALSE executes quickly. Yes, this
// _does_ make a difference.
inline void Debug::CheckStepSpy (uaecptr writeAddress, int writeBytes)
{
if (gDebuggerGlobals.stepSpy)
{
if (get_long (gDebuggerGlobals.ssAddr) != gDebuggerGlobals.ssValue)
{
// !!! Look into adam's comments below.
Emulator::SetBreakReason (kException_Trace);
}
}
if (gDebuggerGlobals.watchEnabled &&
writeAddress < gDebuggerGlobals.watchAddr + gDebuggerGlobals.watchBytes &&
writeAddress + writeBytes > gDebuggerGlobals.watchAddr)
{
// (adam) The step spy code above apparently thinks it can break into
// the debugger by calling Emulator::SetBreakReason, but that won't work
// (I tried it). Software::ProcessException() will do everything we need
// here; it calls Debug::BreakIfConnected(), which in turn calls
// Debug::EnterDebugger().
// We must first check whether we are already in the debugger,
// because it's possible that a single instruction will trigger the
// data breakpoint several times. This can happen, for example, when we hit
// a trap #15 calling the PalmOS system function MemMove. In this case, for
// efficiency, the debugger emulates the entire MemMove call - this happens
// in the function Headpatch::MemMove. A single MemMove can trigger the
// data breakpoint many times, but we must call Debug::EnterDebugger() only once
// or very bad things will happen.
/* (adam) old code - this doesn't work because the PC hasn't been incremented yet
* if (!fgDebuggerGlobals.inDebugger)
* Software::ProcessException (kException_Trace, m68k_getpc (), m68k_getpc ()); */
regs.spcflags |= SPCFLAG_DOTRACE; // break immediately
}
}
#endif /* _DEBUGMGR_H_ */
|