OXYGENHUB
# 9120e87480a6 enhanced-x16-emulator / src / cpu / irq_ctx.h
4.2 KiB · Objective-C · last changed chore(): initial commit 26 days ago source blame history raw
1
// Commander X16 Emulator — interrupt context tracking.
2
//
3
// The CPU core has no notion of "currently servicing an interrupt": it vectors
4
// through, and the only trace is a return address on the stack. A debugger
5
// needs more than that. It wants to say whether you are inside a handler and
6
// how deep, to stop at the first instruction of one instead of stepping over
7
// the whole thing invisibly, and to show where the handler will return to.
8
//
9
// interrupt6502() reports an entry and rti() reports a leave; the two are
10
// paired by matching the stack pointer at entry exactly, which keeps nested
11
// handlers straight and unwinds several at once when a handler returns past
12
// its own frame.
13
//
14
// A handler that leaves by some other route -- discarding its frame and exiting
15
// through RTS or JMP, or a BRK that lands in a warm start which resets the
16
// stack -- has no matching RTI. Those entries are retired the next time an
17
// interrupt is taken at or above where they were recorded, since the stack
18
// having risen back past a frame proves it is gone. Until then the depth reads
19
// high rather than low, which is the safe direction: claiming no interrupt is
20
// running while one is would be worse than being stale.
21
//
22
// What this cannot survive is the stack pointer moving for a reason other than
23
// nesting. Both rules -- frames decreasing in sp, and a balanced RTI landing
24
// exactly on the recorded pointer -- read position as evidence of depth. A
25
// guest that pushes past the bottom of page 1 wraps, and a handler that
26
// relocates the stack with TXS or TCS moves it outright; either can make a live
27
// outer frame look like one the stack has passed, and it will be retired.
28
//
29
// No rule based on the stack pointer alone can tell those apart. What decides
30
// it is not which case is commoner but how each fails: retiring wrongly drops
31
// however many outer handlers the bad comparison covered, but only until the
32
// next interrupt taken at a sane stack level, whereas never retiring leaves a
33
// frame open for the rest of the session and adds another every time it
34
// happens. A bounded, self-healing error beats an unbounded permanent one.
35
//
36
// A consumer that acts on cpu_irq_return_pc() -- setting a breakpoint there to
37
// step over a handler, say -- therefore wants an explicit validity signal, so
38
// it can fall back to single-stepping rather than trusting a stale address.
39
// That belongs with the consumer, which does not exist yet.
40
//
41
// Kept apart from fake6502.c so that the CPU core carries only the two calls,
42
// and so the bookkeeping can be tested without standing up a machine.
43
44
#ifndef _CPU_IRQ_CTX_H_
45
#define _CPU_IRQ_CTX_H_
46
47
#include <stdbool.h>
48
#include <stdint.h>
49
50
#ifdef __cplusplus
51
extern "C" {
52
#endif
53
54
// How many nested handlers keep per-frame detail. Deep enough for any plausible
55
// nesting: the X16 has IRQ, NMI and BRK, and a handler that re-enables
56
// interrupts can nest a few more. Past this the depth still counts, but the
57
// per-frame detail stops being recorded. Exposed so tests cannot drift from it.
58
#define CPU_IRQ_CTX_MAX 16
59
60
// ---- Reported by the CPU core ----------------------------------------------
61
// `vector` is the enum InterruptType that got us here, `from_pc` the 24-bit PC
62
// that was interrupted -- the program bank is part of it on the 65816, and is
63
// zeroed on entry, so it cannot be recovered later -- and `sp` the stack
64
// pointer BEFORE the interrupt pushed anything, which is what the matching RTI
65
// will restore it to.
66
void cpu_irq_ctx_enter(int vector, uint32_t from_pc, uint16_t sp);
67
void cpu_irq_ctx_leave(uint16_t sp);
68
void cpu_irq_ctx_reset(void);
69
70
// ---- Asked by the debugger -------------------------------------------------
71
bool     cpu_in_interrupt(void);
72
int      cpu_irq_depth(void);      // nested handlers currently open
73
uint32_t cpu_irq_count(void);      // interrupts taken since reset
74
int      cpu_irq_last_vector(void);
75
uint32_t cpu_irq_return_pc(void);  // where the innermost handler returns to (24-bit)
76
77
// True once per interrupt entry; reading it clears the flag. Lets a debugger
78
// notice "an interrupt was just taken" without polling the depth and having to
79
// work out whether it changed.
80
bool cpu_irq_take_entered_flag(void);
81
82
#ifdef __cplusplus
83
}
84
#endif
85
86
#endif // _CPU_IRQ_CTX_H_