-
Notifications
You must be signed in to change notification settings - Fork 2
/
irq.c
289 lines (244 loc) · 9.68 KB
/
irq.c
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
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
#include <stdint.h>
#include "irq.h"
#include "console.h"
#include "linker.h"
#include "timer.h"
#include "gba.h"
#include "errlog.h"
#include "peripherals/peripherals.h"
// Loads the stored version of the register
#define SAVED_REGISTER_VALUE(stack, reg) (stack[(reg)])
#define PERIPHERAL_OFFSET(x) ((x) - PERIPHERALS_BEGIN)
#define PERIPHERALS_BEGIN 0x04000000
#define PERIPHERALS_END 0x04000803
#define GBA_IF_ADDRESS 0x04000202
/* Performance related variables */
uint32_t irqBeginTicks;
uint32_t irqEndTicks;
inline void checkRegister(uint32_t sourceRegister)
{
if(sourceRegister > 12)
ErrorDisplayMessage("DataHandler: Unexpected register", 1);
}
static void executeARMStoreInstruction(uint32_t instructionAddress,
uint32_t targetAddress,
uint32_t targetOffset,
uint32_t *registerStack)
{
uint32_t instructionCode = *(uint32_t*)instructionAddress;
uint32_t registerValue;
targetAddress += targetOffset;
// Same assumptions as before
// Additional assumption: source register belongs to r0-r12
// (this assumption was true in thumb mode as registers belong
// to r0-r7, but in ARM mode it might not be the case)
if((instructionCode & 0x0C000000) == 0x04000000) // STR
{
uint32_t reg = (instructionCode >> 12) & 0xF;
checkRegister(reg);
// Check for write-back
if((instructionCode & (1 << 24)) && (instructionCode & (1 << 21)))
ErrorDisplayMessage("DataHandler: unexpected write-back", 1);
registerValue = SAVED_REGISTER_VALUE(registerStack, reg);
*(uint32_t*)targetAddress = registerValue;
}
else if((instructionCode & 0x0E000000) == 0x00000000) // STRH, STRD
{
uint32_t reg = (instructionCode >> 12) & 0xF;
checkRegister(reg);
// Check for write-back
if((instructionCode & (1 << 24)) && (instructionCode & (1 << 21)))
ErrorDisplayMessage("DataHandler: unexpected write-back", 1);
registerValue = SAVED_REGISTER_VALUE(registerStack, reg);
if(instructionCode & (1 << 6)) // STRD
{
volatile uint32_t *ptr = (uint32_t*)targetAddress;
ptr[0] = registerValue;
registerValue = SAVED_REGISTER_VALUE(registerStack, reg + 1);
ptr[1] = registerValue;
}
else // STRH
{
*(uint16_t*)targetAddress = registerValue;
}
}
else if((instructionCode & 0x0E000000) == 0x08000000) // STM
{
// TODO
for(;;);
}
}
static void executeThumbStoreInstruction(uint32_t instructionAddress,
uint32_t targetAddress,
uint32_t targetOffset,
uint32_t *registerStack)
{
uint16_t instructionCode = *(uint16_t*)instructionAddress;
uint16_t instructionPrefix = instructionCode & 0xF000;
uint32_t registerValue;
targetAddress += targetOffset;
// Simple instruction decoder
// The following piece of code makes the assumption that the peripherals
// section is read-only protected, so any LDR instruction would not
// trigger this interrupt as the whole peripheral section is mapped.
// Hence all LDR instructions are ignored which makes the code light
// and fast to execute
switch(instructionPrefix)
{
case 0x5000: // STR, STRB, STRH reg. offset
registerValue = SAVED_REGISTER_VALUE(registerStack, instructionCode & 0x7);
if(instructionCode & (1 << 9)) // STRH
{
//*PERIPH16(PERIPHERAL_OFFSET(faultAddress)) = registerValue;
*(uint16_t*)targetAddress = registerValue;
}
else if(instructionCode & (1 << 10)) // STRB
{
//*PERIPH8(PERIPHERAL_OFFSET(faultAddress)) = registerValue;
*(uint8_t*)targetAddress = registerValue;
}
else // STR
{
//*PERIPH32(PERIPHERAL_OFFSET(faultAddress)) = registerValue;
*(uint32_t*)targetAddress = registerValue;
}
break;
case 0x6000: // STR imm. offset
case 0x7000: // STRB imm. offset
registerValue = SAVED_REGISTER_VALUE(registerStack, instructionCode & 0x7);
if(instructionCode & (1 << 12)) // STRB
{
//*PERIPH8(PERIPHERAL_OFFSET(faultAddress)) = registerValue;
*(uint8_t*)targetAddress = registerValue;
}
else // STR
{
//*PERIPH32(PERIPHERAL_OFFSET(faultAddress)) = registerValue;
*(uint32_t*)targetAddress = registerValue;
}
break;
case 0x8000: // STRH imm. offset
registerValue = SAVED_REGISTER_VALUE(registerStack, instructionCode & 0x7);
//*PERIPH16(PERIPHERAL_OFFSET(faultAddress)) = registerValue;
*(uint16_t*)targetAddress = registerValue;
break;
case 0xA000: // STMxx
// TODO
default:
for(;;);
break;
}
}
void __attribute__((naked)) DataHandler(void)
{
uint32_t instructionAddress;
uint32_t faultAddress;
uint32_t spsr;
uint32_t ticks;
uint32_t *registerStack;
__asm__ volatile("push {r0-r12, lr}\n");
__asm__ volatile("mov %0, sp" : "=r"(registerStack));
__asm__ volatile("sub %0, lr, #8"
: "=r"(instructionAddress));
// Monitoring purpose only
ticks = TimerGetTicks();
__asm__ volatile("mrs %0, spsr" // Get CPSR value at the moment when the interrupt was triggered (SPSR_abt)
: "=r"(spsr));
if((spsr & 0x1f) == 0x12)
{
__asm__ volatile("nop"); // Breakpoint;
}
// Read Fault Address Register
__asm__ volatile("mrc p15, 0, %0, c6, c0, 0" : "=r"(faultAddress));
/*|| (faultAddress >= 0x3007000 && faultAddress < 0x3008000))*/
if(faultAddress == GBA_IF_ADDRESS || faultAddress == 0x3FFFFF8)
{
if(spsr & 0x20) // Thumb mode was enabled
{
// TODO
for(;;);
__asm__ volatile("pop {r0-r12, lr}\n"
"subs pc, lr, #6"); // 8-2
}
else
{
uint32_t instructionCode = *(uint32_t*)instructionAddress;
// /!\ Warning: Assumes that the faulting instruction was STR, STRH or STRD
uint32_t sourceRegister = (instructionCode >> 12) & 0xf;
uint32_t registerValue;
checkRegister(sourceRegister);
// /!\ Warning: Assumes that the register is in the range r0-r12
registerValue = SAVED_REGISTER_VALUE(registerStack, sourceRegister);
if(faultAddress == GBA_IF_ADDRESS)
{
GBAClearInterruptFlags(registerValue);
//*PERIPH32(GBA_IF_ADDRESS - PERIPHERALS_BEGIN) &= ~registerValue;
}
else
GBAClearIF(registerValue);
__asm__ volatile("pop {r0-r12, lr}\n"
"subs pc, lr, #4"); // 8-4
}
}
else if(faultAddress >= PERIPHERALS_BEGIN && faultAddress <= PERIPHERALS_END)
{
// Monitoring is not relevant in the other situations as irq
// handling goes fast.
// Here irq handling is heavier and thus needs to be optimized
irqBeginTicks = ticks;
// Check thumb bit to compute the instruction following the one which
// triggered the interrupt
if(spsr & 0x20) // Thumb mode was enabled
{
executeThumbStoreInstruction(instructionAddress, faultAddress,
PERIPHERAL_OFFSET((uint32_t)periphdata),
registerStack);
PeripheralsRefresh(faultAddress);
// This code leaks support for store SP-relative (Thumb.11)
// and push/pop instructions (Thumb.14)
// SP register value is not supposed to be in peripheral range
irqEndTicks = TimerGetTicks();
__asm__ volatile("pop {r0-r12, lr}");
__asm__ volatile("add lr, lr, #2"); // Next instruction address
__asm__ volatile("subs pc, lr, #8");
}
else
{
executeARMStoreInstruction(instructionAddress,
faultAddress,
PERIPHERAL_OFFSET((uint32_t)periphdata),
registerStack);
PeripheralsRefresh(faultAddress);
irqEndTicks = TimerGetTicks();
__asm__ volatile("pop {r0-r12, lr}");
__asm__ volatile("add lr, lr, #4"); // Next instruction address
__asm__ volatile("subs pc, lr, #8");
}
}
#if 0
else if(faultAddress >= 0x3007000 && faultAddress < 0x3008000)
{
if(spsr & 0x20) // Thumb mode was enabled
{
executeThumbStoreInstruction(instructionAddress, faultAddress, 0,
registerStack);
}
else
{
executeARMStoreInstruction(instructionAddress, faultAddress, 0,
registerStack);
}
}
#endif
else
{
ConsolePrint(31, 1, "DataHandler: ");
ConsolePrintHex(44, 1, faultAddress);
ConsolePrint(31, 2, "@ ");
ConsolePrintHex(33, 2, instructionAddress);
FBCopyDoubleBuffer();
for(;;);
}
__asm__ volatile("pop {r0-r12, lr}\n"
"subs pc, lr, #8");
}