Skip to content

Commit 1c53b49

Browse files
committed
support JP (HL/IX/IY) tail calls
1 parent 777c296 commit 1c53b49

File tree

3 files changed

+58
-2
lines changed

3 files changed

+58
-2
lines changed

core/cpu.c

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ static void debug_break_before_ret(const uint32_t len) {
6060
if (unlikely(debug.untilRet)) {
6161
const uint32_t curSp = cpu_address_mode(cpu.registers.stack[cpu.L].hl, cpu.L);
6262
if (curSp >= debug.untilRetBase) {
63-
const uint32_t start = cpu_mask_mode(cpu.registers.PC - len, cpu.ADL);
63+
const uint32_t start = cpu_mask_mode(cpu.registers.PC - (len + (cpu.SUFFIX ? 1u : 0u)), cpu.ADL);
6464
cpu.registers.PC = start;
6565
debug_open(DBG_STEP, cpu.registers.PC);
6666
}
@@ -1236,10 +1236,21 @@ void cpu_execute(void) {
12361236
REG_WRITE_EX(HL, r->HL, r->_HL);
12371237
REG_WRITE_EX(HLP, r->_HL, w);
12381238
break;
1239-
case 2: /* JP (rr) */
1239+
case 2: { /* JP (rr) */
1240+
uint32_t target = cpu_read_index();
12401241
cpu_prefetch_discard();
1242+
#ifdef DEBUG_SUPPORT
1243+
if (unlikely(debug.untilRet)) {
1244+
uint32_t curSp = cpu_address_mode(cpu.registers.stack[cpu.L].hl, cpu.L);
1245+
/* if this indirect jp is a logical return for the frame
1246+
* where DBG_UNTIL_RET started, the hook rewinds PC and opens
1247+
* the debugger */
1248+
(void)debug_until_ret_handle_indirect_jump(target, curSp);
1249+
}
1250+
#endif
12411251
cpu_jump(cpu_read_index(), cpu.L);
12421252
break;
1253+
}
12431254
case 3: /* LD SP, HL */
12441255
cpu_write_sp(cpu_read_index());
12451256
break;

core/debug/debug.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,7 @@ void debug_step(int mode, uint32_t addr) {
316316
gui_debug_close();
317317
debug.untilRet = true;
318318
debug.untilRetBase = cpu_address_mode(cpu.registers.stack[cpu.L].hl, cpu.L);
319+
debug.untilRetIndex = debug.stackIndex;
319320
break;
320321
case DBG_BASIC_STEP_IN:
321322
case DBG_BASIC_STEP_NEXT:
@@ -335,6 +336,48 @@ void debug_clear_step(void) {
335336
debug.tempExec = debug.stepOut = ~0u;
336337
debug.untilRet = false;
337338
debug.untilRetBase = 0;
339+
debug.untilRetIndex = 0;
340+
}
341+
342+
bool debug_until_ret_handle_indirect_jump(uint32_t target, uint32_t currentSp) {
343+
/* stop if this JP(rr) is an actual return for a
344+
* recorded call frame, where target matches its retAddr and
345+
* SP has been restored to that frame's precall stack
346+
* value (or frame was detected popped) */
347+
uint32_t idx = debug.stackIndex;
348+
uint32_t sz = debug.stackSize;
349+
debug_stack_entry_t *hit = NULL;
350+
uint32_t hit_idx = ~0u;
351+
352+
while (sz--) {
353+
debug_stack_entry_t *entry = &debug.stack[idx];
354+
const uint32_t this_idx = idx;
355+
idx = (idx - 1) & DBG_STACK_MASK;
356+
if (entry->mode == cpu.L &&
357+
/* only consider frames above current SP baseline. target
358+
* must also match the frame's retAddr window */
359+
entry->stack >= debug.untilRetBase &&
360+
(target - entry->retAddr) <= entry->range) {
361+
hit = entry;
362+
hit_idx = this_idx;
363+
break;
364+
}
365+
}
366+
367+
/* break if we are returning from the same frame
368+
* where DBG_UNTIL_RET started */
369+
if (hit && hit_idx == debug.untilRetIndex &&
370+
(currentSp == hit->stack || hit->popped)) {
371+
const uint32_t len = 1 + (cpu.PREFIX != 0);
372+
const uint32_t start = cpu_mask_mode(
373+
cpu.registers.PC - (len + (cpu.SUFFIX ? 1u : 0u)),
374+
cpu.ADL);
375+
REG_WRITE_EX(PC, cpu.registers.PC, start);
376+
debug_open(DBG_STEP, cpu.registers.PC);
377+
return true;
378+
}
379+
380+
return false;
338381
}
339382

340383
void debug_clear_basic_step(void) {

core/debug/debug.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ typedef struct {
147147
uint32_t tempExec, stepOut;
148148
bool untilRet;
149149
uint32_t untilRetBase; /* normalized 24bit stack pointer baseline */
150+
uint32_t untilRetIndex; /* call-stack index when DBG_UNTIL_RET started */
150151

151152
uint32_t stackIndex, stackSize;
152153
debug_stack_entry_t *stack;
@@ -190,6 +191,7 @@ enum {
190191
void debug_step_switch(void);
191192
void debug_clear_step(void);
192193
void debug_clear_basic_step(void);
194+
bool debug_until_ret_handle_indirect_jump(uint32_t target, uint32_t currentSp);
193195
#endif
194196

195197
/* register watchpoints */

0 commit comments

Comments
 (0)