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 | |
6 | the Free Software Foundation; either version 2 of the License, or | |
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 | |
15 | along with this program; if not, write to the Free Software | |
16 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ | |
17 | ||
18 | #include "armdefs.h" | |
19 | #include "armemu.h" | |
6d358e86 | 20 | #include "ansidecl.h" |
c906108c | 21 | |
ff44f8e3 NC |
22 | /* Definitions for the support routines. */ |
23 | ||
24 | static ARMword ModeToBank (ARMword); | |
25 | static void EnvokeList (ARMul_State *, unsigned long, unsigned long); | |
dfcd3bfb JM |
26 | |
27 | struct EventNode | |
ff44f8e3 NC |
28 | { /* An event list node. */ |
29 | unsigned (*func) (ARMul_State *); /* The function to call. */ | |
dfcd3bfb JM |
30 | struct EventNode *next; |
31 | }; | |
c906108c | 32 | |
ff44f8e3 | 33 | /* This routine returns the value of a register from a mode. */ |
c906108c | 34 | |
dfcd3bfb JM |
35 | ARMword |
36 | ARMul_GetReg (ARMul_State * state, unsigned mode, unsigned reg) | |
37 | { | |
38 | mode &= MODEBITS; | |
39 | if (mode != state->Mode) | |
c1a72ffd | 40 | return (state->RegBank[ModeToBank ((ARMword) mode)][reg]); |
dfcd3bfb JM |
41 | else |
42 | return (state->Reg[reg]); | |
c906108c SS |
43 | } |
44 | ||
ff44f8e3 | 45 | /* This routine sets the value of a register for a mode. */ |
c906108c | 46 | |
dfcd3bfb JM |
47 | void |
48 | ARMul_SetReg (ARMul_State * state, unsigned mode, unsigned reg, ARMword value) | |
49 | { | |
50 | mode &= MODEBITS; | |
51 | if (mode != state->Mode) | |
c1a72ffd | 52 | state->RegBank[ModeToBank ((ARMword) mode)][reg] = value; |
dfcd3bfb JM |
53 | else |
54 | state->Reg[reg] = value; | |
c906108c SS |
55 | } |
56 | ||
ff44f8e3 | 57 | /* This routine returns the value of the PC, mode independently. */ |
c906108c | 58 | |
dfcd3bfb JM |
59 | ARMword |
60 | ARMul_GetPC (ARMul_State * state) | |
61 | { | |
62 | if (state->Mode > SVC26MODE) | |
ff44f8e3 | 63 | return state->Reg[15]; |
dfcd3bfb | 64 | else |
ff44f8e3 | 65 | return R15PC; |
c906108c SS |
66 | } |
67 | ||
ff44f8e3 | 68 | /* This routine returns the value of the PC, mode independently. */ |
c906108c | 69 | |
dfcd3bfb JM |
70 | ARMword |
71 | ARMul_GetNextPC (ARMul_State * state) | |
72 | { | |
73 | if (state->Mode > SVC26MODE) | |
ff44f8e3 | 74 | return state->Reg[15] + isize; |
dfcd3bfb | 75 | else |
ff44f8e3 | 76 | return (state->Reg[15] + isize) & R15PCBITS; |
c906108c SS |
77 | } |
78 | ||
ff44f8e3 | 79 | /* This routine sets the value of the PC. */ |
c906108c | 80 | |
dfcd3bfb JM |
81 | void |
82 | ARMul_SetPC (ARMul_State * state, ARMword value) | |
83 | { | |
84 | if (ARMul_MODE32BIT) | |
85 | state->Reg[15] = value & PCBITS; | |
86 | else | |
87 | state->Reg[15] = R15CCINTMODE | (value & R15PCBITS); | |
88 | FLUSHPIPE; | |
c906108c SS |
89 | } |
90 | ||
ff44f8e3 | 91 | /* This routine returns the value of register 15, mode independently. */ |
c906108c | 92 | |
dfcd3bfb JM |
93 | ARMword |
94 | ARMul_GetR15 (ARMul_State * state) | |
95 | { | |
96 | if (state->Mode > SVC26MODE) | |
97 | return (state->Reg[15]); | |
98 | else | |
99 | return (R15PC | ECC | ER15INT | EMODE); | |
c906108c SS |
100 | } |
101 | ||
ff44f8e3 | 102 | /* This routine sets the value of Register 15. */ |
c906108c | 103 | |
dfcd3bfb JM |
104 | void |
105 | ARMul_SetR15 (ARMul_State * state, ARMword value) | |
c906108c | 106 | { |
dfcd3bfb JM |
107 | if (ARMul_MODE32BIT) |
108 | state->Reg[15] = value & PCBITS; | |
109 | else | |
110 | { | |
111 | state->Reg[15] = value; | |
112 | ARMul_R15Altered (state); | |
c906108c | 113 | } |
dfcd3bfb | 114 | FLUSHPIPE; |
c906108c SS |
115 | } |
116 | ||
ff44f8e3 | 117 | /* This routine returns the value of the CPSR. */ |
c906108c | 118 | |
dfcd3bfb JM |
119 | ARMword |
120 | ARMul_GetCPSR (ARMul_State * state) | |
c906108c | 121 | { |
cf52c765 | 122 | return (CPSR | state->Cpsr); |
dfcd3bfb | 123 | } |
c906108c | 124 | |
ff44f8e3 | 125 | /* This routine sets the value of the CPSR. */ |
c906108c | 126 | |
dfcd3bfb JM |
127 | void |
128 | ARMul_SetCPSR (ARMul_State * state, ARMword value) | |
129 | { | |
4ef2594f | 130 | state->Cpsr = value; |
dfcd3bfb JM |
131 | ARMul_CPSRAltered (state); |
132 | } | |
c906108c | 133 | |
ff44f8e3 NC |
134 | /* This routine does all the nasty bits involved in a write to the CPSR, |
135 | including updating the register bank, given a MSR instruction. */ | |
c906108c | 136 | |
dfcd3bfb JM |
137 | void |
138 | ARMul_FixCPSR (ARMul_State * state, ARMword instr, ARMword rhs) | |
139 | { | |
cf52c765 | 140 | state->Cpsr = ARMul_GetCPSR (state); |
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 NC |
571 | INTERNALABORT (address); |
572 | ||
c906108c | 573 | #endif |
dfcd3bfb JM |
574 | BUSUSEDINCPCN; |
575 | if (BIT (21)) | |
576 | LSBase = state->Base; | |
577 | cpab = (state->STC[CPNum]) (state, ARMul_DATA, instr, &data); | |
578 | ARMul_StoreWordN (state, address, data); | |
ff44f8e3 | 579 | |
dfcd3bfb JM |
580 | while (cpab == ARMul_INC) |
581 | { | |
582 | address += 4; | |
583 | cpab = (state->STC[CPNum]) (state, ARMul_DATA, instr, &data); | |
584 | ARMul_StoreWordN (state, address, data); | |
c906108c | 585 | } |
ff44f8e3 | 586 | |
dfcd3bfb | 587 | if (state->abortSig || state->Aborted) |
ff44f8e3 | 588 | TAKEABORT; |
dfcd3bfb | 589 | } |
c906108c | 590 | |
ff44f8e3 | 591 | /* This function does the Busy-Waiting for an MCR instruction. */ |
c906108c | 592 | |
dfcd3bfb JM |
593 | void |
594 | ARMul_MCR (ARMul_State * state, ARMword instr, ARMword source) | |
595 | { | |
596 | unsigned cpab; | |
597 | ||
ff44f8e3 NC |
598 | if (! CP_ACCESS_ALLOWED (state, CPNum)) |
599 | { | |
600 | ARMul_UndefInstr (state, instr); | |
601 | return; | |
602 | } | |
603 | ||
dfcd3bfb | 604 | cpab = (state->MCR[CPNum]) (state, ARMul_FIRST, instr, source); |
c1a72ffd | 605 | |
dfcd3bfb JM |
606 | while (cpab == ARMul_BUSY) |
607 | { | |
608 | ARMul_Icycles (state, 1, 0); | |
c1a72ffd | 609 | |
dfcd3bfb JM |
610 | if (IntPending (state)) |
611 | { | |
612 | cpab = (state->MCR[CPNum]) (state, ARMul_INTERRUPT, instr, 0); | |
613 | return; | |
614 | } | |
615 | else | |
616 | cpab = (state->MCR[CPNum]) (state, ARMul_BUSY, instr, source); | |
c906108c | 617 | } |
c1a72ffd | 618 | |
dfcd3bfb JM |
619 | if (cpab == ARMul_CANT) |
620 | ARMul_Abort (state, ARMul_UndefinedInstrV); | |
621 | else | |
622 | { | |
623 | BUSUSEDINCPCN; | |
624 | ARMul_Ccycles (state, 1, 0); | |
c906108c | 625 | } |
dfcd3bfb | 626 | } |
c906108c | 627 | |
ff44f8e3 | 628 | /* This function does the Busy-Waiting for an MRC instruction. */ |
c906108c | 629 | |
dfcd3bfb JM |
630 | ARMword |
631 | ARMul_MRC (ARMul_State * state, ARMword instr) | |
632 | { | |
633 | unsigned cpab; | |
634 | ARMword result = 0; | |
635 | ||
ff44f8e3 NC |
636 | if (! CP_ACCESS_ALLOWED (state, CPNum)) |
637 | { | |
638 | ARMul_UndefInstr (state, instr); | |
639 | return; | |
640 | } | |
641 | ||
dfcd3bfb JM |
642 | cpab = (state->MRC[CPNum]) (state, ARMul_FIRST, instr, &result); |
643 | while (cpab == ARMul_BUSY) | |
644 | { | |
645 | ARMul_Icycles (state, 1, 0); | |
646 | if (IntPending (state)) | |
647 | { | |
648 | cpab = (state->MRC[CPNum]) (state, ARMul_INTERRUPT, instr, 0); | |
649 | return (0); | |
650 | } | |
651 | else | |
652 | cpab = (state->MRC[CPNum]) (state, ARMul_BUSY, instr, &result); | |
c906108c | 653 | } |
dfcd3bfb JM |
654 | if (cpab == ARMul_CANT) |
655 | { | |
656 | ARMul_Abort (state, ARMul_UndefinedInstrV); | |
ff44f8e3 NC |
657 | /* Parent will destroy the flags otherwise. */ |
658 | result = ECC; | |
c906108c | 659 | } |
dfcd3bfb JM |
660 | else |
661 | { | |
662 | BUSUSEDINCPCN; | |
663 | ARMul_Ccycles (state, 1, 0); | |
664 | ARMul_Icycles (state, 1, 0); | |
c906108c | 665 | } |
ff44f8e3 NC |
666 | |
667 | return result; | |
c906108c SS |
668 | } |
669 | ||
ff44f8e3 | 670 | /* This function does the Busy-Waiting for an CDP instruction. */ |
c906108c | 671 | |
dfcd3bfb JM |
672 | void |
673 | ARMul_CDP (ARMul_State * state, ARMword instr) | |
674 | { | |
675 | unsigned cpab; | |
676 | ||
ff44f8e3 NC |
677 | if (! CP_ACCESS_ALLOWED (state, CPNum)) |
678 | { | |
679 | ARMul_UndefInstr (state, instr); | |
680 | return; | |
681 | } | |
682 | ||
dfcd3bfb JM |
683 | cpab = (state->CDP[CPNum]) (state, ARMul_FIRST, instr); |
684 | while (cpab == ARMul_BUSY) | |
685 | { | |
686 | ARMul_Icycles (state, 1, 0); | |
687 | if (IntPending (state)) | |
688 | { | |
689 | cpab = (state->CDP[CPNum]) (state, ARMul_INTERRUPT, instr); | |
690 | return; | |
691 | } | |
692 | else | |
693 | cpab = (state->CDP[CPNum]) (state, ARMul_BUSY, instr); | |
c906108c | 694 | } |
dfcd3bfb JM |
695 | if (cpab == ARMul_CANT) |
696 | ARMul_Abort (state, ARMul_UndefinedInstrV); | |
697 | else | |
698 | BUSUSEDN; | |
c906108c SS |
699 | } |
700 | ||
ff44f8e3 | 701 | /* This function handles Undefined instructions, as CP isntruction. */ |
c906108c | 702 | |
dfcd3bfb | 703 | void |
6d358e86 | 704 | ARMul_UndefInstr (ARMul_State * state, ARMword instr ATTRIBUTE_UNUSED) |
c906108c | 705 | { |
dfcd3bfb | 706 | ARMul_Abort (state, ARMul_UndefinedInstrV); |
c906108c SS |
707 | } |
708 | ||
ff44f8e3 | 709 | /* Return TRUE if an interrupt is pending, FALSE otherwise. */ |
c906108c | 710 | |
dfcd3bfb JM |
711 | unsigned |
712 | IntPending (ARMul_State * state) | |
713 | { | |
714 | if (state->Exception) | |
ff44f8e3 NC |
715 | { |
716 | /* Any exceptions. */ | |
dfcd3bfb JM |
717 | if (state->NresetSig == LOW) |
718 | { | |
719 | ARMul_Abort (state, ARMul_ResetV); | |
ff44f8e3 | 720 | return TRUE; |
dfcd3bfb JM |
721 | } |
722 | else if (!state->NfiqSig && !FFLAG) | |
723 | { | |
724 | ARMul_Abort (state, ARMul_FIQV); | |
ff44f8e3 | 725 | return TRUE; |
dfcd3bfb JM |
726 | } |
727 | else if (!state->NirqSig && !IFLAG) | |
728 | { | |
729 | ARMul_Abort (state, ARMul_IRQV); | |
ff44f8e3 | 730 | return TRUE; |
dfcd3bfb | 731 | } |
c906108c | 732 | } |
ff44f8e3 NC |
733 | |
734 | return FALSE; | |
dfcd3bfb | 735 | } |
c906108c | 736 | |
ff44f8e3 | 737 | /* Align a word access to a non word boundary. */ |
c906108c | 738 | |
dfcd3bfb | 739 | ARMword |
6d358e86 NC |
740 | ARMul_Align (state, address, data) |
741 | ARMul_State * state ATTRIBUTE_UNUSED; | |
742 | ARMword address; | |
743 | ARMword data; | |
744 | { | |
745 | /* This code assumes the address is really unaligned, | |
746 | as a shift by 32 is undefined in C. */ | |
c906108c | 747 | |
ff44f8e3 | 748 | address = (address & 3) << 3; /* Get the word address. */ |
dfcd3bfb | 749 | return ((data >> address) | (data << (32 - address))); /* rot right */ |
c906108c SS |
750 | } |
751 | ||
ff44f8e3 NC |
752 | /* This routine is used to call another routine after a certain number of |
753 | cycles have been executed. The first parameter is the number of cycles | |
754 | delay before the function is called, the second argument is a pointer | |
755 | to the function. A delay of zero doesn't work, just call the function. */ | |
c906108c | 756 | |
dfcd3bfb JM |
757 | void |
758 | ARMul_ScheduleEvent (ARMul_State * state, unsigned long delay, | |
ff44f8e3 | 759 | unsigned (*what) (ARMul_State *)) |
dfcd3bfb JM |
760 | { |
761 | unsigned long when; | |
762 | struct EventNode *event; | |
763 | ||
764 | if (state->EventSet++ == 0) | |
765 | state->Now = ARMul_Time (state); | |
766 | when = (state->Now + delay) % EVENTLISTSIZE; | |
767 | event = (struct EventNode *) malloc (sizeof (struct EventNode)); | |
768 | event->func = what; | |
769 | event->next = *(state->EventPtr + when); | |
770 | *(state->EventPtr + when) = event; | |
c906108c SS |
771 | } |
772 | ||
ff44f8e3 NC |
773 | /* This routine is called at the beginning of |
774 | every cycle, to envoke scheduled events. */ | |
c906108c | 775 | |
dfcd3bfb JM |
776 | void |
777 | ARMul_EnvokeEvent (ARMul_State * state) | |
778 | { | |
779 | static unsigned long then; | |
780 | ||
781 | then = state->Now; | |
782 | state->Now = ARMul_Time (state) % EVENTLISTSIZE; | |
ff44f8e3 NC |
783 | if (then < state->Now) |
784 | /* Schedule events. */ | |
dfcd3bfb JM |
785 | EnvokeList (state, then, state->Now); |
786 | else if (then > state->Now) | |
ff44f8e3 NC |
787 | { |
788 | /* Need to wrap around the list. */ | |
dfcd3bfb JM |
789 | EnvokeList (state, then, EVENTLISTSIZE - 1L); |
790 | EnvokeList (state, 0L, state->Now); | |
c906108c | 791 | } |
dfcd3bfb | 792 | } |
c906108c | 793 | |
ff44f8e3 NC |
794 | /* Envokes all the entries in a range. */ |
795 | ||
dfcd3bfb JM |
796 | static void |
797 | EnvokeList (ARMul_State * state, unsigned long from, unsigned long to) | |
dfcd3bfb | 798 | { |
dfcd3bfb JM |
799 | for (; from <= to; from++) |
800 | { | |
ff44f8e3 NC |
801 | struct EventNode *anevent; |
802 | ||
dfcd3bfb JM |
803 | anevent = *(state->EventPtr + from); |
804 | while (anevent) | |
805 | { | |
806 | (anevent->func) (state); | |
807 | state->EventSet--; | |
808 | anevent = anevent->next; | |
809 | } | |
810 | *(state->EventPtr + from) = NULL; | |
c906108c | 811 | } |
dfcd3bfb | 812 | } |
c906108c | 813 | |
ff44f8e3 | 814 | /* This routine is returns the number of clock ticks since the last reset. */ |
c906108c | 815 | |
dfcd3bfb JM |
816 | unsigned long |
817 | ARMul_Time (ARMul_State * state) | |
818 | { | |
819 | return (state->NumScycles + state->NumNcycles + | |
820 | state->NumIcycles + state->NumCcycles + state->NumFcycles); | |
c906108c | 821 | } |