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
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
|
/*
* PSn00bSDK interrupt management library
* (C) 2022 spicyjpeg - MPL licensed
*/
#include <stdint.h>
#include <psxapi.h>
#include <psxetc.h>
#include <hwregs_c.h>
#define NUM_IRQ_CHANNELS 11
#define NUM_DMA_CHANNELS 7
#define ISR_STACK_SIZE 0x1000
/* Internal globals */
static void (*_irq_handlers[NUM_IRQ_CHANNELS])(void);
static void (*_dma_handlers[NUM_DMA_CHANNELS])(void);
static int _num_dma_handlers = 0;
static uint16_t _saved_irq_mask;
static uint32_t _saved_dma_dpcr, _saved_dma_dicr;
static int _isr_installed = 0;
/* Custom ISR jmp_buf */
// The ISR and all functions called by it (thus, all callbacks registered using
// InterruptCallback() and DMACallback()) use an independent stack, isolated
// from the main thread's stack. As the size of this stack is limited, custom
// callbacks shall keep the number of nested subroutine calls to a minimum and
// avoid allocating large buffers (e.g. for receiving a sector from the CD
// drive) on the stack.
static uint8_t _isr_stack[ISR_STACK_SIZE];
extern uint8_t _gp[];
static void _global_isr(void);
static const struct JMP_BUF _isr_jmp_buf = {
.ra = (uint32_t) &_global_isr,
.sp = (uint32_t) &_isr_stack[ISR_STACK_SIZE],
.fp = 0,
.s0 = 0,
.s1 = 0,
.s2 = 0,
.s3 = 0,
.s4 = 0,
.s5 = 0,
.s6 = 0,
.s7 = 0,
.gp = (uint32_t) _gp
};
/* Internal IRQ and DMA handlers */
static void _global_isr(void) {
uint16_t stat = IRQ_STAT & IRQ_MASK;
for (; stat; stat = IRQ_STAT & IRQ_MASK) {
//for (int i = 0; i < NUM_IRQ_CHANNELS; i++) {
for (int i = 0, mask = 1; stat; i++, stat >>= 1, mask <<= 1) {
if (!(stat & 1))
continue;
// Acknowledge the current IRQ. Note that clearing all IRQ flags in one
// shot would result in hard-to-debug race conditions (been there, done
// that).
IRQ_STAT = (uint16_t) (mask ^ 0xffff);
if (_irq_handlers[i])
_irq_handlers[i]();
}
}
ReturnFromException();
}
static void _global_dma_handler(void) {
uint32_t dicr = DMA_DICR;
uint32_t stat = (dicr >> 24) & 0x7f;
for (; stat; dicr = DMA_DICR, stat = (dicr >> 24) & 0x7f) {
uint32_t base = dicr & 0x00ffffff;
//for (int i = 0; i < NUM_DMA_CHANNELS; i++) {
for (int i = 0, mask = (1 << 24); stat; i++, stat >>= 1, mask <<= 1) {
if (!(stat & 1))
continue;
// Acknowledge the current DMA channel's IRQ. For whatever reason
// DMA IRQ flags are cleared by writing 1 to them rather than 0.
DMA_DICR = base | mask;
if (_dma_handlers[i])
_dma_handlers[i]();
}
}
}
/* IRQ and DMA handler API */
void *InterruptCallback(IRQ_Channel irq, void (*func)(void)) {
if ((irq < 0) || (irq >= NUM_IRQ_CHANNELS))
return 0;
void *old_callback = _irq_handlers[irq];
_irq_handlers[irq] = func;
// Enable or disable the IRQ in the IRQ_MASK register depending on whether
// the callback is being registered or removed.
if (func)
IRQ_MASK |= 1 << irq;
else
IRQ_MASK &= ~(1 << irq);
return old_callback;
}
void *GetInterruptCallback(IRQ_Channel irq) {
if ((irq < 0) || (irq >= NUM_IRQ_CHANNELS))
return 0;
return _irq_handlers[irq];
}
void *DMACallback(DMA_Channel dma, void (*func)(void)) {
if ((dma < 0) || (dma >= NUM_DMA_CHANNELS))
return 0;
void *old_callback = _dma_handlers[dma];
_dma_handlers[dma] = func;
// Enable or disable the IRQ in the DMA_DICR register depending on whether
// the callback is being registered or removed. The main DMA IRQ dispatcher
// is also registered if this is the first DMA callback being configured,
// or disabled if it's the last one being removed.
if (!old_callback && func) {
DMA_DICR |= (0x10000 << dma) | (1 << 23);
if (!(_num_dma_handlers++))
InterruptCallback(IRQ_DMA, &_global_dma_handler);
} else if (old_callback && !func) {
if (--_num_dma_handlers) {
DMA_DICR &= ~(0x10000 << dma);
} else {
DMA_DICR = 0;
InterruptCallback(IRQ_DMA, 0);
}
}
return old_callback;
}
void *GetDMACallback(DMA_Channel dma) {
if ((dma < 0) || (dma >= NUM_DMA_CHANNELS))
return 0;
return _dma_handlers[dma];
}
/* Hook installation/removal API */
int ResetCallback(void) {
if (_isr_installed)
return -1;
EnterCriticalSection();
_saved_irq_mask = 0;
_saved_dma_dpcr = 0x03333333;
_saved_dma_dicr = 0;
for (int i = 0; i < NUM_IRQ_CHANNELS; i++)
_irq_handlers[i] = (void *) 0;
for (int i = 0; i < NUM_DMA_CHANNELS; i++)
_dma_handlers[i] = (void *) 0;
BUS_COM_DELAY = 0x00001325;
_96_remove();
RestartCallback();
return 0;
}
void RestartCallback(void) {
if (_isr_installed)
return;
IRQ_STAT = 0;
IRQ_MASK = _saved_irq_mask;
DMA_DPCR = _saved_dma_dpcr;
DMA_DICR = _saved_dma_dicr;
// Install the ISR hook and prevent the kernel's internal handlers from
// automatically acknowledging SPI and timer IRQs.
SetCustomExitFromException(&_isr_jmp_buf);
ChangeClearPAD(0);
ChangeClearRCnt(0, 0);
ChangeClearRCnt(1, 0);
ChangeClearRCnt(2, 0);
ChangeClearRCnt(3, 0);
_isr_installed = 1;
ExitCriticalSection();
}
void StopCallback(void) {
if (!_isr_installed)
return;
// Save the state of IRQ and DMA registers, then reset them and undo the
// changes that were made to the kernel's state.
EnterCriticalSection();
_saved_irq_mask = IRQ_MASK;
_saved_dma_dpcr = DMA_DPCR;
_saved_dma_dicr = DMA_DICR;
IRQ_STAT = 0;
IRQ_MASK = 0;
DMA_DPCR = _saved_dma_dpcr & 0x07777777;
DMA_DICR = 0;
SetDefaultExitFromException();
ChangeClearPAD(1);
ChangeClearRCnt(0, 1);
ChangeClearRCnt(1, 1);
ChangeClearRCnt(2, 1);
ChangeClearRCnt(3, 1);
_isr_installed = 0;
}
|