Commit | Line | Data |
---|---|---|
c906108c SS |
1 | /* armsupp.c -- ARMulator support code: ARM6 Instruction Emulator. |
2 | Copyright (C) 1994 Advanced RISC Machines Ltd. | |
3 | ||
4 | This program is free software; you can redistribute it and/or modify | |
5 | it under the terms of the GNU General Public License as published by | |
3fd725ef | 6 | the Free Software Foundation; either version 3 of the License, or |
c906108c SS |
7 | (at your option) any later version. |
8 | ||
9 | This program is distributed in the hope that it will be useful, | |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | GNU General Public License for more details. | |
13 | ||
14 | You should have received a copy of the GNU General Public License | |
51b318de | 15 | along with this program; if not, see <http://www.gnu.org/licenses/>. */ |
c906108c SS |
16 | |
17 | #include "armdefs.h" | |
18 | #include "armemu.h" | |
6d358e86 | 19 | #include "ansidecl.h" |
c906108c | 20 | |
ff44f8e3 NC |
21 | /* Definitions for the support routines. */ |
22 | ||
23 | static ARMword ModeToBank (ARMword); | |
24 | static void EnvokeList (ARMul_State *, unsigned long, unsigned long); | |
dfcd3bfb JM |
25 | |
26 | struct EventNode | |
ff44f8e3 NC |
27 | { /* An event list node. */ |
28 | unsigned (*func) (ARMul_State *); /* The function to call. */ | |
dfcd3bfb JM |
29 | struct EventNode *next; |
30 | }; | |
c906108c | 31 | |
ff44f8e3 | 32 | /* This routine returns the value of a register from a mode. */ |
c906108c | 33 | |
dfcd3bfb JM |
34 | ARMword |
35 | ARMul_GetReg (ARMul_State * state, unsigned mode, unsigned reg) | |
36 | { | |
37 | mode &= MODEBITS; | |
38 | if (mode != state->Mode) | |
c1a72ffd | 39 | return (state->RegBank[ModeToBank ((ARMword) mode)][reg]); |
dfcd3bfb JM |
40 | else |
41 | return (state->Reg[reg]); | |
c906108c SS |
42 | } |
43 | ||
ff44f8e3 | 44 | /* This routine sets the value of a register for a mode. */ |
c906108c | 45 | |
dfcd3bfb JM |
46 | void |
47 | ARMul_SetReg (ARMul_State * state, unsigned mode, unsigned reg, ARMword value) | |
48 | { | |
49 | mode &= MODEBITS; | |
50 | if (mode != state->Mode) | |
c1a72ffd | 51 | state->RegBank[ModeToBank ((ARMword) mode)][reg] = value; |
dfcd3bfb JM |
52 | else |
53 | state->Reg[reg] = value; | |
c906108c SS |
54 | } |
55 | ||
ff44f8e3 | 56 | /* This routine returns the value of the PC, mode independently. */ |
c906108c | 57 | |
dfcd3bfb JM |
58 | ARMword |
59 | ARMul_GetPC (ARMul_State * state) | |
60 | { | |
61 | if (state->Mode > SVC26MODE) | |
ff44f8e3 | 62 | return state->Reg[15]; |
dfcd3bfb | 63 | else |
ff44f8e3 | 64 | return R15PC; |
c906108c SS |
65 | } |
66 | ||
ff44f8e3 | 67 | /* This routine returns the value of the PC, mode independently. */ |
c906108c | 68 | |
dfcd3bfb JM |
69 | ARMword |
70 | ARMul_GetNextPC (ARMul_State * state) | |
71 | { | |
72 | if (state->Mode > SVC26MODE) | |
ff44f8e3 | 73 | return state->Reg[15] + isize; |
dfcd3bfb | 74 | else |
ff44f8e3 | 75 | return (state->Reg[15] + isize) & R15PCBITS; |
c906108c SS |
76 | } |
77 | ||
ff44f8e3 | 78 | /* This routine sets the value of the PC. */ |
c906108c | 79 | |
dfcd3bfb JM |
80 | void |
81 | ARMul_SetPC (ARMul_State * state, ARMword value) | |
82 | { | |
83 | if (ARMul_MODE32BIT) | |
84 | state->Reg[15] = value & PCBITS; | |
85 | else | |
86 | state->Reg[15] = R15CCINTMODE | (value & R15PCBITS); | |
87 | FLUSHPIPE; | |
c906108c SS |
88 | } |
89 | ||
ff44f8e3 | 90 | /* This routine returns the value of register 15, mode independently. */ |
c906108c | 91 | |
dfcd3bfb JM |
92 | ARMword |
93 | ARMul_GetR15 (ARMul_State * state) | |
94 | { | |
95 | if (state->Mode > SVC26MODE) | |
96 | return (state->Reg[15]); | |
97 | else | |
98 | return (R15PC | ECC | ER15INT | EMODE); | |
c906108c SS |
99 | } |
100 | ||
ff44f8e3 | 101 | /* This routine sets the value of Register 15. */ |
c906108c | 102 | |
dfcd3bfb JM |
103 | void |
104 | ARMul_SetR15 (ARMul_State * state, ARMword value) | |
c906108c | 105 | { |
dfcd3bfb JM |
106 | if (ARMul_MODE32BIT) |
107 | state->Reg[15] = value & PCBITS; | |
108 | else | |
109 | { | |
110 | state->Reg[15] = value; | |
111 | ARMul_R15Altered (state); | |
c906108c | 112 | } |
dfcd3bfb | 113 | FLUSHPIPE; |
c906108c SS |
114 | } |
115 | ||
ff44f8e3 | 116 | /* This routine returns the value of the CPSR. */ |
c906108c | 117 | |
dfcd3bfb JM |
118 | ARMword |
119 | ARMul_GetCPSR (ARMul_State * state) | |
c906108c | 120 | { |
cf52c765 | 121 | return (CPSR | state->Cpsr); |
dfcd3bfb | 122 | } |
c906108c | 123 | |
ff44f8e3 | 124 | /* This routine sets the value of the CPSR. */ |
c906108c | 125 | |
dfcd3bfb JM |
126 | void |
127 | ARMul_SetCPSR (ARMul_State * state, ARMword value) | |
128 | { | |
4ef2594f | 129 | state->Cpsr = value; |
dfcd3bfb JM |
130 | ARMul_CPSRAltered (state); |
131 | } | |
c906108c | 132 | |
ff44f8e3 NC |
133 | /* This routine does all the nasty bits involved in a write to the CPSR, |
134 | including updating the register bank, given a MSR instruction. */ | |
c906108c | 135 | |
dfcd3bfb JM |
136 | void |
137 | ARMul_FixCPSR (ARMul_State * state, ARMword instr, ARMword rhs) | |
138 | { | |
cf52c765 | 139 | state->Cpsr = ARMul_GetCPSR (state); |
10b57fcb | 140 | |
dac07255 NC |
141 | if (state->Mode != USER26MODE |
142 | && state->Mode != USER32MODE) | |
ff44f8e3 NC |
143 | { |
144 | /* In user mode, only write flags. */ | |
4ef2594f AO |
145 | if (BIT (16)) |
146 | SETPSR_C (state->Cpsr, rhs); | |
147 | if (BIT (17)) | |
148 | SETPSR_X (state->Cpsr, rhs); | |
149 | if (BIT (18)) | |
150 | SETPSR_S (state->Cpsr, rhs); | |
c906108c | 151 | } |
4ef2594f AO |
152 | if (BIT (19)) |
153 | SETPSR_F (state->Cpsr, rhs); | |
dfcd3bfb JM |
154 | ARMul_CPSRAltered (state); |
155 | } | |
c906108c | 156 | |
ff44f8e3 | 157 | /* Get an SPSR from the specified mode. */ |
c906108c | 158 | |
dfcd3bfb JM |
159 | ARMword |
160 | ARMul_GetSPSR (ARMul_State * state, ARMword mode) | |
161 | { | |
c1a72ffd NC |
162 | ARMword bank = ModeToBank (mode & MODEBITS); |
163 | ||
164 | if (! BANK_CAN_ACCESS_SPSR (bank)) | |
cf52c765 | 165 | return ARMul_GetCPSR (state); |
c1a72ffd NC |
166 | |
167 | return state->Spsr[bank]; | |
c906108c SS |
168 | } |
169 | ||
ff44f8e3 | 170 | /* This routine does a write to an SPSR. */ |
c906108c | 171 | |
dfcd3bfb JM |
172 | void |
173 | ARMul_SetSPSR (ARMul_State * state, ARMword mode, ARMword value) | |
174 | { | |
c1a72ffd NC |
175 | ARMword bank = ModeToBank (mode & MODEBITS); |
176 | ||
177 | if (BANK_CAN_ACCESS_SPSR (bank)) | |
dfcd3bfb | 178 | state->Spsr[bank] = value; |
c906108c SS |
179 | } |
180 | ||
ff44f8e3 | 181 | /* This routine does a write to the current SPSR, given an MSR instruction. */ |
c906108c | 182 | |
dfcd3bfb JM |
183 | void |
184 | ARMul_FixSPSR (ARMul_State * state, ARMword instr, ARMword rhs) | |
185 | { | |
c1a72ffd | 186 | if (BANK_CAN_ACCESS_SPSR (state->Bank)) |
dfcd3bfb | 187 | { |
4ef2594f AO |
188 | if (BIT (16)) |
189 | SETPSR_C (state->Spsr[state->Bank], rhs); | |
190 | if (BIT (17)) | |
191 | SETPSR_X (state->Spsr[state->Bank], rhs); | |
192 | if (BIT (18)) | |
193 | SETPSR_S (state->Spsr[state->Bank], rhs); | |
194 | if (BIT (19)) | |
195 | SETPSR_F (state->Spsr[state->Bank], rhs); | |
c906108c SS |
196 | } |
197 | } | |
198 | ||
ff44f8e3 NC |
199 | /* This routine updates the state of the emulator after the Cpsr has been |
200 | changed. Both the processor flags and register bank are updated. */ | |
c906108c | 201 | |
dfcd3bfb JM |
202 | void |
203 | ARMul_CPSRAltered (ARMul_State * state) | |
204 | { | |
205 | ARMword oldmode; | |
206 | ||
207 | if (state->prog32Sig == LOW) | |
208 | state->Cpsr &= (CCBITS | INTBITS | R15MODEBITS); | |
c1a72ffd | 209 | |
dfcd3bfb | 210 | oldmode = state->Mode; |
c1a72ffd | 211 | |
dfcd3bfb JM |
212 | if (state->Mode != (state->Cpsr & MODEBITS)) |
213 | { | |
214 | state->Mode = | |
215 | ARMul_SwitchMode (state, state->Mode, state->Cpsr & MODEBITS); | |
c1a72ffd | 216 | |
dfcd3bfb | 217 | state->NtransSig = (state->Mode & 3) ? HIGH : LOW; |
c906108c | 218 | } |
cf52c765 | 219 | state->Cpsr &= ~MODEBITS; |
c906108c | 220 | |
dfcd3bfb | 221 | ASSIGNINT (state->Cpsr & INTBITS); |
cf52c765 | 222 | state->Cpsr &= ~INTBITS; |
dfcd3bfb | 223 | ASSIGNN ((state->Cpsr & NBIT) != 0); |
cf52c765 | 224 | state->Cpsr &= ~NBIT; |
dfcd3bfb | 225 | ASSIGNZ ((state->Cpsr & ZBIT) != 0); |
cf52c765 | 226 | state->Cpsr &= ~ZBIT; |
dfcd3bfb | 227 | ASSIGNC ((state->Cpsr & CBIT) != 0); |
cf52c765 | 228 | state->Cpsr &= ~CBIT; |
dfcd3bfb | 229 | ASSIGNV ((state->Cpsr & VBIT) != 0); |
cf52c765 | 230 | state->Cpsr &= ~VBIT; |
f1129fb8 NC |
231 | ASSIGNS ((state->Cpsr & SBIT) != 0); |
232 | state->Cpsr &= ~SBIT; | |
c906108c | 233 | #ifdef MODET |
dfcd3bfb | 234 | ASSIGNT ((state->Cpsr & TBIT) != 0); |
cf52c765 | 235 | state->Cpsr &= ~TBIT; |
c906108c SS |
236 | #endif |
237 | ||
dfcd3bfb JM |
238 | if (oldmode > SVC26MODE) |
239 | { | |
240 | if (state->Mode <= SVC26MODE) | |
241 | { | |
242 | state->Emulate = CHANGEMODE; | |
243 | state->Reg[15] = ECC | ER15INT | EMODE | R15PC; | |
244 | } | |
c906108c | 245 | } |
dfcd3bfb JM |
246 | else |
247 | { | |
248 | if (state->Mode > SVC26MODE) | |
249 | { | |
250 | state->Emulate = CHANGEMODE; | |
251 | state->Reg[15] = R15PC; | |
252 | } | |
253 | else | |
254 | state->Reg[15] = ECC | ER15INT | EMODE | R15PC; | |
c906108c | 255 | } |
c906108c SS |
256 | } |
257 | ||
ff44f8e3 NC |
258 | /* This routine updates the state of the emulator after register 15 has |
259 | been changed. Both the processor flags and register bank are updated. | |
260 | This routine should only be called from a 26 bit mode. */ | |
c906108c | 261 | |
dfcd3bfb JM |
262 | void |
263 | ARMul_R15Altered (ARMul_State * state) | |
c906108c | 264 | { |
dfcd3bfb JM |
265 | if (state->Mode != R15MODE) |
266 | { | |
267 | state->Mode = ARMul_SwitchMode (state, state->Mode, R15MODE); | |
268 | state->NtransSig = (state->Mode & 3) ? HIGH : LOW; | |
c906108c | 269 | } |
ff44f8e3 | 270 | |
dfcd3bfb JM |
271 | if (state->Mode > SVC26MODE) |
272 | state->Emulate = CHANGEMODE; | |
ff44f8e3 | 273 | |
dfcd3bfb | 274 | ASSIGNR15INT (R15INT); |
ff44f8e3 | 275 | |
dfcd3bfb JM |
276 | ASSIGNN ((state->Reg[15] & NBIT) != 0); |
277 | ASSIGNZ ((state->Reg[15] & ZBIT) != 0); | |
278 | ASSIGNC ((state->Reg[15] & CBIT) != 0); | |
279 | ASSIGNV ((state->Reg[15] & VBIT) != 0); | |
c906108c SS |
280 | } |
281 | ||
ff44f8e3 NC |
282 | /* This routine controls the saving and restoring of registers across mode |
283 | changes. The regbank matrix is largely unused, only rows 13 and 14 are | |
284 | used across all modes, 8 to 14 are used for FIQ, all others use the USER | |
285 | column. It's easier this way. old and new parameter are modes numbers. | |
286 | Notice the side effect of changing the Bank variable. */ | |
c906108c | 287 | |
dfcd3bfb JM |
288 | ARMword |
289 | ARMul_SwitchMode (ARMul_State * state, ARMword oldmode, ARMword newmode) | |
290 | { | |
291 | unsigned i; | |
c1a72ffd NC |
292 | ARMword oldbank; |
293 | ARMword newbank; | |
294 | ||
295 | oldbank = ModeToBank (oldmode); | |
296 | newbank = state->Bank = ModeToBank (newmode); | |
297 | ||
ff44f8e3 | 298 | /* Do we really need to do it? */ |
c1a72ffd | 299 | if (oldbank != newbank) |
ff44f8e3 NC |
300 | { |
301 | /* Save away the old registers. */ | |
c1a72ffd | 302 | switch (oldbank) |
ff44f8e3 | 303 | { |
dfcd3bfb JM |
304 | case USERBANK: |
305 | case IRQBANK: | |
306 | case SVCBANK: | |
307 | case ABORTBANK: | |
308 | case UNDEFBANK: | |
c1a72ffd | 309 | if (newbank == FIQBANK) |
dfcd3bfb JM |
310 | for (i = 8; i < 13; i++) |
311 | state->RegBank[USERBANK][i] = state->Reg[i]; | |
c1a72ffd NC |
312 | state->RegBank[oldbank][13] = state->Reg[13]; |
313 | state->RegBank[oldbank][14] = state->Reg[14]; | |
dfcd3bfb JM |
314 | break; |
315 | case FIQBANK: | |
316 | for (i = 8; i < 15; i++) | |
317 | state->RegBank[FIQBANK][i] = state->Reg[i]; | |
318 | break; | |
319 | case DUMMYBANK: | |
320 | for (i = 8; i < 15; i++) | |
321 | state->RegBank[DUMMYBANK][i] = 0; | |
322 | break; | |
c1a72ffd NC |
323 | default: |
324 | abort (); | |
dfcd3bfb | 325 | } |
c1a72ffd | 326 | |
ff44f8e3 | 327 | /* Restore the new registers. */ |
c1a72ffd | 328 | switch (newbank) |
ff44f8e3 | 329 | { |
dfcd3bfb JM |
330 | case USERBANK: |
331 | case IRQBANK: | |
332 | case SVCBANK: | |
333 | case ABORTBANK: | |
334 | case UNDEFBANK: | |
c1a72ffd | 335 | if (oldbank == FIQBANK) |
dfcd3bfb JM |
336 | for (i = 8; i < 13; i++) |
337 | state->Reg[i] = state->RegBank[USERBANK][i]; | |
c1a72ffd NC |
338 | state->Reg[13] = state->RegBank[newbank][13]; |
339 | state->Reg[14] = state->RegBank[newbank][14]; | |
dfcd3bfb JM |
340 | break; |
341 | case FIQBANK: | |
342 | for (i = 8; i < 15; i++) | |
343 | state->Reg[i] = state->RegBank[FIQBANK][i]; | |
344 | break; | |
345 | case DUMMYBANK: | |
346 | for (i = 8; i < 15; i++) | |
347 | state->Reg[i] = 0; | |
348 | break; | |
c1a72ffd NC |
349 | default: |
350 | abort (); | |
ff44f8e3 NC |
351 | } |
352 | } | |
c1a72ffd NC |
353 | |
354 | return newmode; | |
c906108c SS |
355 | } |
356 | ||
ff44f8e3 NC |
357 | /* Given a processor mode, this routine returns the |
358 | register bank that will be accessed in that mode. */ | |
c906108c | 359 | |
dfcd3bfb | 360 | static ARMword |
c1a72ffd | 361 | ModeToBank (ARMword mode) |
dfcd3bfb | 362 | { |
c1a72ffd NC |
363 | static ARMword bankofmode[] = |
364 | { | |
365 | USERBANK, FIQBANK, IRQBANK, SVCBANK, | |
dfcd3bfb JM |
366 | DUMMYBANK, DUMMYBANK, DUMMYBANK, DUMMYBANK, |
367 | DUMMYBANK, DUMMYBANK, DUMMYBANK, DUMMYBANK, | |
368 | DUMMYBANK, DUMMYBANK, DUMMYBANK, DUMMYBANK, | |
c1a72ffd | 369 | USERBANK, FIQBANK, IRQBANK, SVCBANK, |
dfcd3bfb | 370 | DUMMYBANK, DUMMYBANK, DUMMYBANK, ABORTBANK, |
c1a72ffd NC |
371 | DUMMYBANK, DUMMYBANK, DUMMYBANK, UNDEFBANK, |
372 | DUMMYBANK, DUMMYBANK, DUMMYBANK, SYSTEMBANK | |
dfcd3bfb JM |
373 | }; |
374 | ||
c1a72ffd NC |
375 | if (mode >= (sizeof (bankofmode) / sizeof (bankofmode[0]))) |
376 | return DUMMYBANK; | |
377 | ||
378 | return bankofmode[mode]; | |
dfcd3bfb | 379 | } |
c906108c | 380 | |
ff44f8e3 | 381 | /* Returns the register number of the nth register in a reg list. */ |
c906108c | 382 | |
dfcd3bfb JM |
383 | unsigned |
384 | ARMul_NthReg (ARMword instr, unsigned number) | |
385 | { | |
386 | unsigned bit, upto; | |
c906108c | 387 | |
ff44f8e3 | 388 | for (bit = 0, upto = 0; upto <= number; bit ++) |
dfcd3bfb | 389 | if (BIT (bit)) |
ff44f8e3 NC |
390 | upto ++; |
391 | ||
dfcd3bfb | 392 | return (bit - 1); |
c906108c SS |
393 | } |
394 | ||
ff44f8e3 | 395 | /* Assigns the N and Z flags depending on the value of result. */ |
c906108c | 396 | |
dfcd3bfb JM |
397 | void |
398 | ARMul_NegZero (ARMul_State * state, ARMword result) | |
c906108c | 399 | { |
dfcd3bfb JM |
400 | if (NEG (result)) |
401 | { | |
402 | SETN; | |
403 | CLEARZ; | |
404 | } | |
405 | else if (result == 0) | |
406 | { | |
407 | CLEARN; | |
408 | SETZ; | |
409 | } | |
410 | else | |
411 | { | |
412 | CLEARN; | |
413 | CLEARZ; | |
ff44f8e3 | 414 | } |
dfcd3bfb | 415 | } |
c906108c | 416 | |
f743149e | 417 | /* Compute whether an addition of A and B, giving RESULT, overflowed. */ |
ff44f8e3 | 418 | |
dfcd3bfb JM |
419 | int |
420 | AddOverflow (ARMword a, ARMword b, ARMword result) | |
f743149e JM |
421 | { |
422 | return ((NEG (a) && NEG (b) && POS (result)) | |
423 | || (POS (a) && POS (b) && NEG (result))); | |
424 | } | |
425 | ||
426 | /* Compute whether a subtraction of A and B, giving RESULT, overflowed. */ | |
ff44f8e3 | 427 | |
dfcd3bfb JM |
428 | int |
429 | SubOverflow (ARMword a, ARMword b, ARMword result) | |
f743149e JM |
430 | { |
431 | return ((NEG (a) && POS (b) && POS (result)) | |
432 | || (POS (a) && NEG (b) && NEG (result))); | |
433 | } | |
434 | ||
ff44f8e3 | 435 | /* Assigns the C flag after an addition of a and b to give result. */ |
c906108c | 436 | |
dfcd3bfb JM |
437 | void |
438 | ARMul_AddCarry (ARMul_State * state, ARMword a, ARMword b, ARMword result) | |
c906108c | 439 | { |
dfcd3bfb JM |
440 | ASSIGNC ((NEG (a) && NEG (b)) || |
441 | (NEG (a) && POS (result)) || (NEG (b) && POS (result))); | |
442 | } | |
c906108c | 443 | |
ff44f8e3 | 444 | /* Assigns the V flag after an addition of a and b to give result. */ |
c906108c | 445 | |
dfcd3bfb JM |
446 | void |
447 | ARMul_AddOverflow (ARMul_State * state, ARMword a, ARMword b, ARMword result) | |
c906108c | 448 | { |
f743149e JM |
449 | ASSIGNV (AddOverflow (a, b, result)); |
450 | } | |
c906108c | 451 | |
ff44f8e3 | 452 | /* Assigns the C flag after an subtraction of a and b to give result. */ |
c906108c | 453 | |
dfcd3bfb JM |
454 | void |
455 | ARMul_SubCarry (ARMul_State * state, ARMword a, ARMword b, ARMword result) | |
c906108c | 456 | { |
dfcd3bfb JM |
457 | ASSIGNC ((NEG (a) && POS (b)) || |
458 | (NEG (a) && POS (result)) || (POS (b) && POS (result))); | |
c906108c SS |
459 | } |
460 | ||
ff44f8e3 | 461 | /* Assigns the V flag after an subtraction of a and b to give result. */ |
c906108c | 462 | |
dfcd3bfb JM |
463 | void |
464 | ARMul_SubOverflow (ARMul_State * state, ARMword a, ARMword b, ARMword result) | |
c906108c | 465 | { |
f743149e | 466 | ASSIGNV (SubOverflow (a, b, result)); |
c906108c SS |
467 | } |
468 | ||
ff44f8e3 NC |
469 | /* This function does the work of generating the addresses used in an |
470 | LDC instruction. The code here is always post-indexed, it's up to the | |
471 | caller to get the input address correct and to handle base register | |
472 | modification. It also handles the Busy-Waiting. */ | |
c906108c | 473 | |
dfcd3bfb JM |
474 | void |
475 | ARMul_LDC (ARMul_State * state, ARMword instr, ARMword address) | |
476 | { | |
477 | unsigned cpab; | |
478 | ARMword data; | |
c906108c | 479 | |
dfcd3bfb | 480 | UNDEF_LSCPCBaseWb; |
ff44f8e3 NC |
481 | |
482 | if (! CP_ACCESS_ALLOWED (state, CPNum)) | |
dfcd3bfb | 483 | { |
ff44f8e3 NC |
484 | ARMul_UndefInstr (state, instr); |
485 | return; | |
c906108c | 486 | } |
ff44f8e3 NC |
487 | |
488 | if (ADDREXCEPT (address)) | |
489 | INTERNALABORT (address); | |
490 | ||
dfcd3bfb JM |
491 | cpab = (state->LDC[CPNum]) (state, ARMul_FIRST, instr, 0); |
492 | while (cpab == ARMul_BUSY) | |
493 | { | |
494 | ARMul_Icycles (state, 1, 0); | |
ff44f8e3 | 495 | |
dfcd3bfb JM |
496 | if (IntPending (state)) |
497 | { | |
498 | cpab = (state->LDC[CPNum]) (state, ARMul_INTERRUPT, instr, 0); | |
499 | return; | |
500 | } | |
501 | else | |
502 | cpab = (state->LDC[CPNum]) (state, ARMul_BUSY, instr, 0); | |
c906108c | 503 | } |
dfcd3bfb JM |
504 | if (cpab == ARMul_CANT) |
505 | { | |
506 | CPTAKEABORT; | |
507 | return; | |
c906108c | 508 | } |
ff44f8e3 | 509 | |
dfcd3bfb JM |
510 | cpab = (state->LDC[CPNum]) (state, ARMul_TRANSFER, instr, 0); |
511 | data = ARMul_LoadWordN (state, address); | |
512 | BUSUSEDINCPCN; | |
ff44f8e3 | 513 | |
dfcd3bfb JM |
514 | if (BIT (21)) |
515 | LSBase = state->Base; | |
516 | cpab = (state->LDC[CPNum]) (state, ARMul_DATA, instr, data); | |
ff44f8e3 | 517 | |
dfcd3bfb JM |
518 | while (cpab == ARMul_INC) |
519 | { | |
520 | address += 4; | |
521 | data = ARMul_LoadWordN (state, address); | |
522 | cpab = (state->LDC[CPNum]) (state, ARMul_DATA, instr, data); | |
c906108c | 523 | } |
ff44f8e3 | 524 | |
dfcd3bfb | 525 | if (state->abortSig || state->Aborted) |
ff44f8e3 | 526 | TAKEABORT; |
dfcd3bfb | 527 | } |
c906108c | 528 | |
ff44f8e3 NC |
529 | /* This function does the work of generating the addresses used in an |
530 | STC instruction. The code here is always post-indexed, it's up to the | |
531 | caller to get the input address correct and to handle base register | |
532 | modification. It also handles the Busy-Waiting. */ | |
c906108c | 533 | |
dfcd3bfb JM |
534 | void |
535 | ARMul_STC (ARMul_State * state, ARMword instr, ARMword address) | |
536 | { | |
537 | unsigned cpab; | |
538 | ARMword data; | |
c906108c | 539 | |
dfcd3bfb | 540 | UNDEF_LSCPCBaseWb; |
ff44f8e3 NC |
541 | |
542 | if (! CP_ACCESS_ALLOWED (state, CPNum)) | |
dfcd3bfb | 543 | { |
ff44f8e3 NC |
544 | ARMul_UndefInstr (state, instr); |
545 | return; | |
c906108c | 546 | } |
ff44f8e3 NC |
547 | |
548 | if (ADDREXCEPT (address) || VECTORACCESS (address)) | |
549 | INTERNALABORT (address); | |
550 | ||
dfcd3bfb JM |
551 | cpab = (state->STC[CPNum]) (state, ARMul_FIRST, instr, &data); |
552 | while (cpab == ARMul_BUSY) | |
553 | { | |
554 | ARMul_Icycles (state, 1, 0); | |
555 | if (IntPending (state)) | |
556 | { | |
557 | cpab = (state->STC[CPNum]) (state, ARMul_INTERRUPT, instr, 0); | |
558 | return; | |
559 | } | |
560 | else | |
561 | cpab = (state->STC[CPNum]) (state, ARMul_BUSY, instr, &data); | |
c906108c | 562 | } |
ff44f8e3 | 563 | |
dfcd3bfb JM |
564 | if (cpab == ARMul_CANT) |
565 | { | |
566 | CPTAKEABORT; | |
567 | return; | |
c906108c SS |
568 | } |
569 | #ifndef MODE32 | |
dfcd3bfb | 570 | if (ADDREXCEPT (address) || VECTORACCESS (address)) |
ff44f8e3 | 571 | INTERNALABORT (address); |
c906108c | 572 | #endif |
dfcd3bfb JM |
573 | BUSUSEDINCPCN; |
574 | if (BIT (21)) | |
575 | LSBase = state->Base; | |
576 | cpab = (state->STC[CPNum]) (state, ARMul_DATA, instr, &data); | |
577 | ARMul_StoreWordN (state, address, data); | |
ff44f8e3 | 578 | |
dfcd3bfb JM |
579 | while (cpab == ARMul_INC) |
580 | { | |
581 | address += 4; | |
582 | cpab = (state->STC[CPNum]) (state, ARMul_DATA, instr, &data); | |
583 | ARMul_StoreWordN (state, address, data); | |
c906108c | 584 | } |
ff44f8e3 | 585 | |
dfcd3bfb | 586 | if (state->abortSig || state->Aborted) |
ff44f8e3 | 587 | TAKEABORT; |
dfcd3bfb | 588 | } |
c906108c | 589 | |
ff44f8e3 | 590 | /* This function does the Busy-Waiting for an MCR instruction. */ |
c906108c | 591 | |
dfcd3bfb JM |
592 | void |
593 | ARMul_MCR (ARMul_State * state, ARMword instr, ARMword source) | |
594 | { | |
595 | unsigned cpab; | |
596 | ||
ff44f8e3 NC |
597 | if (! CP_ACCESS_ALLOWED (state, CPNum)) |
598 | { | |
599 | ARMul_UndefInstr (state, instr); | |
600 | return; | |
601 | } | |
602 | ||
dfcd3bfb | 603 | cpab = (state->MCR[CPNum]) (state, ARMul_FIRST, instr, source); |
c1a72ffd | 604 | |
dfcd3bfb JM |
605 | while (cpab == ARMul_BUSY) |
606 | { | |
607 | ARMul_Icycles (state, 1, 0); | |
c1a72ffd | 608 | |
dfcd3bfb JM |
609 | if (IntPending (state)) |
610 | { | |
611 | cpab = (state->MCR[CPNum]) (state, ARMul_INTERRUPT, instr, 0); | |
612 | return; | |
613 | } | |
614 | else | |
615 | cpab = (state->MCR[CPNum]) (state, ARMul_BUSY, instr, source); | |
c906108c | 616 | } |
c1a72ffd | 617 | |
dfcd3bfb JM |
618 | if (cpab == ARMul_CANT) |
619 | ARMul_Abort (state, ARMul_UndefinedInstrV); | |
620 | else | |
621 | { | |
622 | BUSUSEDINCPCN; | |
623 | ARMul_Ccycles (state, 1, 0); | |
c906108c | 624 | } |
dfcd3bfb | 625 | } |
c906108c | 626 | |
ff44f8e3 | 627 | /* This function does the Busy-Waiting for an MRC instruction. */ |
c906108c | 628 | |
dfcd3bfb JM |
629 | ARMword |
630 | ARMul_MRC (ARMul_State * state, ARMword instr) | |
631 | { | |
632 | unsigned cpab; | |
633 | ARMword result = 0; | |
634 | ||
ff44f8e3 NC |
635 | if (! CP_ACCESS_ALLOWED (state, CPNum)) |
636 | { | |
637 | ARMul_UndefInstr (state, instr); | |
f253d86d | 638 | return result; |
ff44f8e3 NC |
639 | } |
640 | ||
dfcd3bfb JM |
641 | cpab = (state->MRC[CPNum]) (state, ARMul_FIRST, instr, &result); |
642 | while (cpab == ARMul_BUSY) | |
643 | { | |
644 | ARMul_Icycles (state, 1, 0); | |
645 | if (IntPending (state)) | |
646 | { | |
647 | cpab = (state->MRC[CPNum]) (state, ARMul_INTERRUPT, instr, 0); | |
648 | return (0); | |
649 | } | |
650 | else | |
651 | cpab = (state->MRC[CPNum]) (state, ARMul_BUSY, instr, &result); | |
c906108c | 652 | } |
dfcd3bfb JM |
653 | if (cpab == ARMul_CANT) |
654 | { | |
655 | ARMul_Abort (state, ARMul_UndefinedInstrV); | |
ff44f8e3 NC |
656 | /* Parent will destroy the flags otherwise. */ |
657 | result = ECC; | |
c906108c | 658 | } |
dfcd3bfb JM |
659 | else |
660 | { | |
661 | BUSUSEDINCPCN; | |
662 | ARMul_Ccycles (state, 1, 0); | |
663 | ARMul_Icycles (state, 1, 0); | |
c906108c | 664 | } |
ff44f8e3 NC |
665 | |
666 | return result; | |
c906108c SS |
667 | } |
668 | ||
ff44f8e3 | 669 | /* This function does the Busy-Waiting for an CDP instruction. */ |
c906108c | 670 | |
dfcd3bfb JM |
671 | void |
672 | ARMul_CDP (ARMul_State * state, ARMword instr) | |
673 | { | |
674 | unsigned cpab; | |
675 | ||
ff44f8e3 NC |
676 | if (! CP_ACCESS_ALLOWED (state, CPNum)) |
677 | { | |
678 | ARMul_UndefInstr (state, instr); | |
679 | return; | |
680 | } | |
681 | ||
dfcd3bfb JM |
682 | cpab = (state->CDP[CPNum]) (state, ARMul_FIRST, instr); |
683 | while (cpab == ARMul_BUSY) | |
684 | { | |
685 | ARMul_Icycles (state, 1, 0); | |
686 | if (IntPending (state)) | |
687 | { | |
688 | cpab = (state->CDP[CPNum]) (state, ARMul_INTERRUPT, instr); | |
689 | return; | |
690 | } | |
691 | else | |
692 | cpab = (state->CDP[CPNum]) (state, ARMul_BUSY, instr); | |
c906108c | 693 | } |
dfcd3bfb JM |
694 | if (cpab == ARMul_CANT) |
695 | ARMul_Abort (state, ARMul_UndefinedInstrV); | |
696 | else | |
697 | BUSUSEDN; | |
c906108c SS |
698 | } |
699 | ||
ff44f8e3 | 700 | /* This function handles Undefined instructions, as CP isntruction. */ |
c906108c | 701 | |
dfcd3bfb | 702 | void |
6d358e86 | 703 | ARMul_UndefInstr (ARMul_State * state, ARMword instr ATTRIBUTE_UNUSED) |
c906108c | 704 | { |
dfcd3bfb | 705 | ARMul_Abort (state, ARMul_UndefinedInstrV); |
c906108c SS |
706 | } |
707 | ||
ff44f8e3 | 708 | /* Return TRUE if an interrupt is pending, FALSE otherwise. */ |
c906108c | 709 | |
dfcd3bfb JM |
710 | unsigned |
711 | IntPending (ARMul_State * state) | |
712 | { | |
713 | if (state->Exception) | |
ff44f8e3 NC |
714 | { |
715 | /* Any exceptions. */ | |
dfcd3bfb JM |
716 | if (state->NresetSig == LOW) |
717 | { | |
718 | ARMul_Abort (state, ARMul_ResetV); | |
ff44f8e3 | 719 | return TRUE; |
dfcd3bfb JM |
720 | } |
721 | else if (!state->NfiqSig && !FFLAG) | |
722 | { | |
723 | ARMul_Abort (state, ARMul_FIQV); | |
ff44f8e3 | 724 | return TRUE; |
dfcd3bfb JM |
725 | } |
726 | else if (!state->NirqSig && !IFLAG) | |
727 | { | |
728 | ARMul_Abort (state, ARMul_IRQV); | |
ff44f8e3 | 729 | return TRUE; |
dfcd3bfb | 730 | } |
c906108c | 731 | } |
ff44f8e3 NC |
732 | |
733 | return FALSE; | |
dfcd3bfb | 734 | } |
c906108c | 735 | |
ff44f8e3 | 736 | /* Align a word access to a non word boundary. */ |
c906108c | 737 | |
dfcd3bfb | 738 | ARMword |
6d358e86 NC |
739 | ARMul_Align (state, address, data) |
740 | ARMul_State * state ATTRIBUTE_UNUSED; | |
741 | ARMword address; | |
742 | ARMword data; | |
743 | { | |
744 | /* This code assumes the address is really unaligned, | |
745 | as a shift by 32 is undefined in C. */ | |
c906108c | 746 | |
ff44f8e3 | 747 | address = (address & 3) << 3; /* Get the word address. */ |
dfcd3bfb | 748 | return ((data >> address) | (data << (32 - address))); /* rot right */ |
c906108c SS |
749 | } |
750 | ||
ff44f8e3 NC |
751 | /* This routine is used to call another routine after a certain number of |
752 | cycles have been executed. The first parameter is the number of cycles | |
753 | delay before the function is called, the second argument is a pointer | |
754 | to the function. A delay of zero doesn't work, just call the function. */ | |
c906108c | 755 | |
dfcd3bfb JM |
756 | void |
757 | ARMul_ScheduleEvent (ARMul_State * state, unsigned long delay, | |
ff44f8e3 | 758 | unsigned (*what) (ARMul_State *)) |
dfcd3bfb JM |
759 | { |
760 | unsigned long when; | |
761 | struct EventNode *event; | |
762 | ||
763 | if (state->EventSet++ == 0) | |
764 | state->Now = ARMul_Time (state); | |
765 | when = (state->Now + delay) % EVENTLISTSIZE; | |
766 | event = (struct EventNode *) malloc (sizeof (struct EventNode)); | |
767 | event->func = what; | |
768 | event->next = *(state->EventPtr + when); | |
769 | *(state->EventPtr + when) = event; | |
c906108c SS |
770 | } |
771 | ||
ff44f8e3 NC |
772 | /* This routine is called at the beginning of |
773 | every cycle, to envoke scheduled events. */ | |
c906108c | 774 | |
dfcd3bfb JM |
775 | void |
776 | ARMul_EnvokeEvent (ARMul_State * state) | |
777 | { | |
778 | static unsigned long then; | |
779 | ||
780 | then = state->Now; | |
781 | state->Now = ARMul_Time (state) % EVENTLISTSIZE; | |
ff44f8e3 NC |
782 | if (then < state->Now) |
783 | /* Schedule events. */ | |
dfcd3bfb JM |
784 | EnvokeList (state, then, state->Now); |
785 | else if (then > state->Now) | |
ff44f8e3 NC |
786 | { |
787 | /* Need to wrap around the list. */ | |
dfcd3bfb JM |
788 | EnvokeList (state, then, EVENTLISTSIZE - 1L); |
789 | EnvokeList (state, 0L, state->Now); | |
c906108c | 790 | } |
dfcd3bfb | 791 | } |
c906108c | 792 | |
ff44f8e3 NC |
793 | /* Envokes all the entries in a range. */ |
794 | ||
dfcd3bfb JM |
795 | static void |
796 | EnvokeList (ARMul_State * state, unsigned long from, unsigned long to) | |
dfcd3bfb | 797 | { |
dfcd3bfb JM |
798 | for (; from <= to; from++) |
799 | { | |
ff44f8e3 NC |
800 | struct EventNode *anevent; |
801 | ||
dfcd3bfb JM |
802 | anevent = *(state->EventPtr + from); |
803 | while (anevent) | |
804 | { | |
805 | (anevent->func) (state); | |
806 | state->EventSet--; | |
807 | anevent = anevent->next; | |
808 | } | |
809 | *(state->EventPtr + from) = NULL; | |
c906108c | 810 | } |
dfcd3bfb | 811 | } |
c906108c | 812 | |
ff44f8e3 | 813 | /* This routine is returns the number of clock ticks since the last reset. */ |
c906108c | 814 | |
dfcd3bfb JM |
815 | unsigned long |
816 | ARMul_Time (ARMul_State * state) | |
817 | { | |
818 | return (state->NumScycles + state->NumNcycles + | |
819 | state->NumIcycles + state->NumCcycles + state->NumFcycles); | |
c906108c | 820 | } |