Skip to content

Commit 631764d

Browse files
committed
initial implementation of register watchpoints, cpu.c not yet instrumented
1 parent 0bde2b5 commit 631764d

File tree

3 files changed

+347
-1
lines changed

3 files changed

+347
-1
lines changed

core/debug/debug.c

Lines changed: 130 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ void debug_open(int reason, uint32_t data) {
110110
}
111111
}
112112

113-
if ((debug_get_flags() & DBG_IGNORE) && (reason >= DBG_BREAKPOINT && reason <= DBG_PORT_WRITE)) {
113+
if ((debug_get_flags() & DBG_IGNORE) && (reason >= DBG_BREAKPOINT && reason <= DBG_REG_WRITE)) {
114114
return;
115115
}
116116

@@ -142,6 +142,135 @@ void debug_open(int reason, uint32_t data) {
142142
cpu.flashDelayCycles = debug.flashDelayCycles;
143143
}
144144

145+
static bool reg_bit_get(const uint64_t mask, const unsigned id) {
146+
return (id < 64) && ((mask >> id) & 1u);
147+
}
148+
149+
static void reg_bit_set(uint64_t *mask, const unsigned id, const bool set) {
150+
if (id >= 64) { return; }
151+
if (set) {
152+
*mask |= (1ull << id);
153+
} else {
154+
*mask &= ~(1ull << id);
155+
}
156+
}
157+
158+
#define BIT(x) (1ull << (x))
159+
static const uint64_t dbg_reg_trigger_mask[DBG_REG_COUNT] = {
160+
[DBG_REG_A] = BIT(DBG_REG_A) | BIT(DBG_REG_AF),
161+
[DBG_REG_F] = BIT(DBG_REG_F) | BIT(DBG_REG_AF),
162+
[DBG_REG_AF] = BIT(DBG_REG_AF) | BIT(DBG_REG_A) | BIT(DBG_REG_F),
163+
164+
[DBG_REG_B] = BIT(DBG_REG_B) | BIT(DBG_REG_BC),
165+
[DBG_REG_C] = BIT(DBG_REG_C) | BIT(DBG_REG_BC),
166+
[DBG_REG_BC] = BIT(DBG_REG_BC) | BIT(DBG_REG_B) | BIT(DBG_REG_C),
167+
168+
[DBG_REG_D] = BIT(DBG_REG_D) | BIT(DBG_REG_DE),
169+
[DBG_REG_E] = BIT(DBG_REG_E) | BIT(DBG_REG_DE),
170+
[DBG_REG_DE] = BIT(DBG_REG_DE) | BIT(DBG_REG_D) | BIT(DBG_REG_E),
171+
172+
[DBG_REG_H] = BIT(DBG_REG_H) | BIT(DBG_REG_HL),
173+
[DBG_REG_L] = BIT(DBG_REG_L) | BIT(DBG_REG_HL),
174+
[DBG_REG_HL] = BIT(DBG_REG_HL) | BIT(DBG_REG_H) | BIT(DBG_REG_L),
175+
176+
[DBG_REG_IXH] = BIT(DBG_REG_IXH) | BIT(DBG_REG_IX),
177+
[DBG_REG_IXL] = BIT(DBG_REG_IXL) | BIT(DBG_REG_IX),
178+
[DBG_REG_IX] = BIT(DBG_REG_IX) | BIT(DBG_REG_IXH) | BIT(DBG_REG_IXL),
179+
180+
[DBG_REG_IYH] = BIT(DBG_REG_IYH) | BIT(DBG_REG_IY),
181+
[DBG_REG_IYL] = BIT(DBG_REG_IYL) | BIT(DBG_REG_IY),
182+
[DBG_REG_IY] = BIT(DBG_REG_IY) | BIT(DBG_REG_IYH) | BIT(DBG_REG_IYL),
183+
184+
[DBG_REG_AP] = BIT(DBG_REG_AP) | BIT(DBG_REG_AFP),
185+
[DBG_REG_FP] = BIT(DBG_REG_FP) | BIT(DBG_REG_AFP),
186+
[DBG_REG_AFP] = BIT(DBG_REG_AFP) | BIT(DBG_REG_AP) | BIT(DBG_REG_FP),
187+
188+
[DBG_REG_BP] = BIT(DBG_REG_BP) | BIT(DBG_REG_BCP),
189+
[DBG_REG_CP] = BIT(DBG_REG_CP) | BIT(DBG_REG_BCP),
190+
[DBG_REG_BCP] = BIT(DBG_REG_BCP) | BIT(DBG_REG_BP) | BIT(DBG_REG_CP),
191+
192+
[DBG_REG_DP] = BIT(DBG_REG_DP) | BIT(DBG_REG_DEP),
193+
[DBG_REG_EP] = BIT(DBG_REG_EP) | BIT(DBG_REG_DEP),
194+
[DBG_REG_DEP] = BIT(DBG_REG_DEP) | BIT(DBG_REG_DP) | BIT(DBG_REG_EP),
195+
196+
[DBG_REG_HP] = BIT(DBG_REG_HP) | BIT(DBG_REG_HLP),
197+
[DBG_REG_LP] = BIT(DBG_REG_LP) | BIT(DBG_REG_HLP),
198+
[DBG_REG_HLP] = BIT(DBG_REG_HLP) | BIT(DBG_REG_HP) | BIT(DBG_REG_LP),
199+
200+
[DBG_REG_SPS] = BIT(DBG_REG_SPS),
201+
[DBG_REG_SPL] = BIT(DBG_REG_SPL),
202+
[DBG_REG_PC] = BIT(DBG_REG_PC),
203+
[DBG_REG_I] = BIT(DBG_REG_I),
204+
[DBG_REG_R] = BIT(DBG_REG_R),
205+
[DBG_REG_MBASE] = BIT(DBG_REG_MBASE),
206+
};
207+
208+
uint32_t debug_norm_reg_value(const unsigned regID, const uint32_t value) {
209+
switch (regID) {
210+
// 8 bit regs
211+
case DBG_REG_A: case DBG_REG_F: case DBG_REG_B: case DBG_REG_C:
212+
case DBG_REG_D: case DBG_REG_E: case DBG_REG_H: case DBG_REG_L:
213+
case DBG_REG_IXH: case DBG_REG_IXL: case DBG_REG_IYH: case DBG_REG_IYL:
214+
case DBG_REG_AP: case DBG_REG_FP: case DBG_REG_BP: case DBG_REG_CP:
215+
case DBG_REG_DP: case DBG_REG_EP: case DBG_REG_HP: case DBG_REG_LP:
216+
case DBG_REG_R: case DBG_REG_MBASE:
217+
return value & 0xFFu;
218+
// 16 bit regs
219+
case DBG_REG_AF: case DBG_REG_AFP: case DBG_REG_SPS: case DBG_REG_I:
220+
return value & 0xFFFFu;
221+
// 24 bit regs
222+
case DBG_REG_BC: case DBG_REG_BCP: case DBG_REG_DE: case DBG_REG_DEP:
223+
case DBG_REG_HL: case DBG_REG_HLP: case DBG_REG_IX: case DBG_REG_IY:
224+
case DBG_REG_SPL: case DBG_REG_PC:
225+
return value & 0xFFFFFFu;
226+
default:
227+
return value;
228+
}
229+
}
230+
231+
void debug_reg_watch(const unsigned regID, const int mask, const bool set) {
232+
if (mask & DBG_MASK_READ) {
233+
reg_bit_set(&debug.reg_watch_r, regID, set);
234+
}
235+
236+
if (mask & DBG_MASK_WRITE) {
237+
reg_bit_set(&debug.reg_watch_w, regID, set);
238+
}
239+
}
240+
241+
int debug_reg_get_mask(const unsigned regID) {
242+
int mask = 0;
243+
244+
if (reg_bit_get(debug.reg_watch_r, regID)) {
245+
mask |= DBG_MASK_READ;
246+
}
247+
248+
if (reg_bit_get(debug.reg_watch_w, regID)) {
249+
mask |= DBG_MASK_WRITE;
250+
}
251+
252+
return mask;
253+
}
254+
255+
void debug_touch_reg_read(const unsigned regID) {
256+
if (!(debug.reg_watch_r & dbg_reg_trigger_mask[regID])) { return; }
257+
debug_open(DBG_REG_READ, regID);
258+
}
259+
260+
void debug_touch_reg_write(const unsigned regID, const uint32_t oldValue, const uint32_t new_value) {
261+
if (!(debug.reg_watch_w & dbg_reg_trigger_mask[regID])) {
262+
return;
263+
}
264+
265+
const uint32_t old_v = debug_norm_reg_value(regID, oldValue);
266+
const uint32_t new_v = debug_norm_reg_value(regID, new_value);
267+
if (old_v == new_v) {
268+
return;
269+
}
270+
271+
debug_open(DBG_REG_WRITE, regID);
272+
}
273+
145274
void debug_watch(uint32_t addr, int mask, bool set) {
146275
addr &= 0xFFFFFF;
147276
if (set) {

core/debug/debug.h

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ enum {
2727
DBG_WATCHPOINT_WRITE, /* hit a write watchpoint */
2828
DBG_PORT_READ, /* read a monitored port */
2929
DBG_PORT_WRITE, /* wrote a monitored port */
30+
DBG_REG_READ, /* read of a watched CPU register */
31+
DBG_REG_WRITE, /* write of a watched CPU register */
3032
DBG_NMI_TRIGGERED, /* triggered a non maskable interrupt */
3133
DBG_WATCHDOG_TIMEOUT, /* watchdog timer reset */
3234
DBG_MISC_RESET, /* miscellaneous reset */
@@ -155,6 +157,9 @@ typedef struct {
155157

156158
uint8_t *addr;
157159
uint8_t *port;
160+
161+
uint64_t reg_watch_r; /* bitmask of DBG_MASK_READ for dbg_reg_t ids */
162+
uint64_t reg_watch_w; /* bitmask of DBG_MASK_WRITE for dbg_reg_t ids */
158163
bool basicMode;
159164
bool basicModeLive;
160165
bool basicDeferPC;
@@ -184,6 +189,85 @@ void debug_step_switch(void);
184189
void debug_clear_step(void);
185190
void debug_clear_basic_step(void);
186191

192+
/* register watchpoints */
193+
/* these ids correspond to logical CPU registers shown in the UI */
194+
typedef enum {
195+
DBG_REG_A = 0,
196+
DBG_REG_F,
197+
DBG_REG_B,
198+
DBG_REG_C,
199+
DBG_REG_D,
200+
DBG_REG_E,
201+
DBG_REG_H,
202+
DBG_REG_L,
203+
DBG_REG_IXH,
204+
DBG_REG_IXL,
205+
DBG_REG_IYH,
206+
DBG_REG_IYL,
207+
DBG_REG_AP, /* A' */
208+
DBG_REG_FP, /* F' */
209+
DBG_REG_BP, /* B' */
210+
DBG_REG_CP, /* C' */
211+
DBG_REG_DP, /* D' */
212+
DBG_REG_EP, /* E' */
213+
DBG_REG_HP, /* H' */
214+
DBG_REG_LP, /* L' */
215+
DBG_REG_AF, /* 16-bit */
216+
DBG_REG_BC, /* 24-bit */
217+
DBG_REG_DE, /* 24-bit */
218+
DBG_REG_HL, /* 24-bit */
219+
DBG_REG_IX, /* 24-bit */
220+
DBG_REG_IY, /* 24-bit */
221+
DBG_REG_AFP, /* 16-bit */
222+
DBG_REG_BCP, /* 24-bit */
223+
DBG_REG_DEP, /* 24-bit */
224+
DBG_REG_HLP, /* 24-bit */
225+
DBG_REG_SPS, /* 16-bit */
226+
DBG_REG_SPL, /* 24-bit */
227+
DBG_REG_PC, /* 24-bit */
228+
DBG_REG_I, /* 16-bit */
229+
DBG_REG_R, /* 8-bit */
230+
DBG_REG_MBASE,/* 8-bit */
231+
DBG_REG_COUNT
232+
} dbg_reg_t;
233+
234+
/* enable/disable register watch for a given id and mask (DBG_MASK_READ/WRITE) */
235+
void debug_reg_watch(unsigned regID, int mask, bool set);
236+
/* get current mask (DBG_MASK_READ/WRITE) for a register id */
237+
int debug_reg_get_mask(unsigned regID);
238+
/* helpers to be invoked around register accesses */
239+
void debug_touch_reg_read(unsigned regID);
240+
void debug_touch_reg_write(unsigned regID, uint32_t oldValue, uint32_t new_value);
241+
/* normalize a register value to its natural width (8/16/24) */
242+
uint32_t debug_norm_reg_value(unsigned regID, uint32_t value);
243+
244+
/* trigger helpers to wrap reads/writes
245+
* REG_READ returns the single evaluated value of EXPR
246+
* REG_WRITE evaluates LVAL once to capture OLD, and again to assign
247+
*/
248+
#define REG_READ(ID, EXPR) \
249+
(__extension__({ \
250+
uint32_t __v = (uint32_t)(EXPR); \
251+
debug_touch_reg_read((unsigned)(ID)); \
252+
__v; \
253+
}))
254+
255+
#define REG_WRITE(ID, LVAL, VAL) \
256+
(__extension__({ \
257+
uint32_t __old = (uint32_t)(LVAL); \
258+
uint32_t __new = (uint32_t)(VAL); \
259+
debug_touch_reg_write((unsigned)(ID), __old, __new); \
260+
(LVAL) = (__new); \
261+
}))
262+
263+
/* map CPU context to register IDs */
264+
/* eZ80 PREFIX: 0 = HL, 2 = IX, 3 = IY */
265+
#define DBG_REG_ID_INDEX(PFX) (( (PFX) == 0 ) ? DBG_REG_HL : ( (PFX) == 2 ? DBG_REG_IX : DBG_REG_IY ))
266+
#define DBG_REG_ID_INDEX_H(PFX) (( (PFX) == 0 ) ? DBG_REG_H : ( (PFX) == 2 ? DBG_REG_IXH : DBG_REG_IYH ))
267+
#define DBG_REG_ID_INDEX_L(PFX) (( (PFX) == 0 ) ? DBG_REG_L : ( (PFX) == 2 ? DBG_REG_IXL : DBG_REG_IYL ))
268+
269+
#define DBG_REG_ID_SP(MODE_L) ( (MODE_L) ? DBG_REG_SPL : DBG_REG_SPS )
270+
187271
#ifdef __cplusplus
188272
}
189273
#endif

0 commit comments

Comments
 (0)