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); |
10b57fcb | 141 | |
dac07255 NC |
142 | if (state->Mode != USER26MODE |
143 | && state->Mode != USER32MODE) | |
ff44f8e3 NC |
144 | { |
145 | /* In user mode, only write flags. */ | |
4ef2594f AO |
146 | if (BIT (16)) |
147 | SETPSR_C (state->Cpsr, rhs); | |
148 | if (BIT (17)) | |
149 | SETPSR_X (state->Cpsr, rhs); | |
150 | if (BIT (18)) | |
151 | SETPSR_S (state->Cpsr, rhs); | |
c906108c | 152 | } |
4ef2594f AO |
153 | if (BIT (19)) |
154 | SETPSR_F (state->Cpsr, rhs); | |
dfcd3bfb JM |
155 | ARMul_CPSRAltered (state); |
156 | } | |
c906108c | 157 | |
ff44f8e3 | 158 | /* Get an SPSR from the specified mode. */ |
c906108c | 159 | |
dfcd3bfb JM |
160 | ARMword |
161 | ARMul_GetSPSR (ARMul_State * state, ARMword mode) | |
162 | { | |
c1a72ffd NC |
163 | ARMword bank = ModeToBank (mode & MODEBITS); |
164 | ||
165 | if (! BANK_CAN_ACCESS_SPSR (bank)) | |
cf52c765 | 166 | return ARMul_GetCPSR (state); |
c1a72ffd NC |
167 | |
168 | return state->Spsr[bank]; | |
c906108c SS |
169 | } |
170 | ||
ff44f8e3 | 171 | /* This routine does a write to an SPSR. */ |
c906108c | 172 | |
dfcd3bfb JM |
173 | void |
174 | ARMul_SetSPSR (ARMul_State * state, ARMword mode, ARMword value) | |
175 | { | |
c1a72ffd NC |
176 | ARMword bank = ModeToBank (mode & MODEBITS); |
177 | ||
178 | if (BANK_CAN_ACCESS_SPSR (bank)) | |
dfcd3bfb | 179 | state->Spsr[bank] = value; |
c906108c SS |
180 | } |
181 | ||
ff44f8e3 | 182 | /* This routine does a write to the current SPSR, given an MSR instruction. */ |
c906108c | 183 | |
dfcd3bfb JM |
184 | void |
185 | ARMul_FixSPSR (ARMul_State * state, ARMword instr, ARMword rhs) | |
186 | { | |
c1a72ffd | 187 | if (BANK_CAN_ACCESS_SPSR (state->Bank)) |
dfcd3bfb | 188 | { |
4ef2594f AO |
189 | if (BIT (16)) |
190 | SETPSR_C (state->Spsr[state->Bank], rhs); | |
191 | if (BIT (17)) | |
192 | SETPSR_X (state->Spsr[state->Bank], rhs); | |
193 | if (BIT (18)) | |
194 | SETPSR_S (state->Spsr[state->Bank], rhs); | |
195 | if (BIT (19)) | |
196 | SETPSR_F (state->Spsr[state->Bank], rhs); | |
c906108c SS |
197 | } |
198 | } | |
199 | ||
ff44f8e3 NC |
200 | /* This routine updates the state of the emulator after the Cpsr has been |
201 | changed. Both the processor flags and register bank are updated. */ | |
c906108c | 202 | |
dfcd3bfb JM |
203 | void |
204 | ARMul_CPSRAltered (ARMul_State * state) | |
205 | { | |
206 | ARMword oldmode; | |
207 | ||
208 | if (state->prog32Sig == LOW) | |
209 | state->Cpsr &= (CCBITS | INTBITS | R15MODEBITS); | |
c1a72ffd | 210 | |
dfcd3bfb | 211 | oldmode = state->Mode; |
c1a72ffd | 212 | |
dfcd3bfb JM |
213 | if (state->Mode != (state->Cpsr & MODEBITS)) |
214 | { | |
215 | state->Mode = | |
216 | ARMul_SwitchMode (state, state->Mode, state->Cpsr & MODEBITS); | |
c1a72ffd | 217 | |
dfcd3bfb | 218 | state->NtransSig = (state->Mode & 3) ? HIGH : LOW; |
c906108c | 219 | } |
cf52c765 | 220 | state->Cpsr &= ~MODEBITS; |
c906108c | 221 | |
dfcd3bfb | 222 | ASSIGNINT (state->Cpsr & INTBITS); |
cf52c765 | 223 | state->Cpsr &= ~INTBITS; |
dfcd3bfb | 224 | ASSIGNN ((state->Cpsr & NBIT) != 0); |
cf52c765 | 225 | state->Cpsr &= ~NBIT; |
dfcd3bfb | 226 | ASSIGNZ ((state->Cpsr & ZBIT) != 0); |
cf52c765 | 227 | state->Cpsr &= ~ZBIT; |
dfcd3bfb | 228 | ASSIGNC ((state->Cpsr & CBIT) != 0); |
cf52c765 | 229 | state->Cpsr &= ~CBIT; |
dfcd3bfb | 230 | ASSIGNV ((state->Cpsr & VBIT) != 0); |
cf52c765 | 231 | state->Cpsr &= ~VBIT; |
f1129fb8 NC |
232 | ASSIGNS ((state->Cpsr & SBIT) != 0); |
233 | state->Cpsr &= ~SBIT; | |
c906108c | 234 | #ifdef MODET |
dfcd3bfb | 235 | ASSIGNT ((state->Cpsr & TBIT) != 0); |
cf52c765 | 236 | state->Cpsr &= ~TBIT; |
c906108c SS |
237 | #endif |
238 | ||
dfcd3bfb JM |
239 | if (oldmode > SVC26MODE) |
240 | { | |
241 | if (state->Mode <= SVC26MODE) | |
242 | { | |
243 | state->Emulate = CHANGEMODE; | |
244 | state->Reg[15] = ECC | ER15INT | EMODE | R15PC; | |
245 | } | |
c906108c | 246 | } |
dfcd3bfb JM |
247 | else |
248 | { | |
249 | if (state->Mode > SVC26MODE) | |
250 | { | |
251 | state->Emulate = CHANGEMODE; | |
252 | state->Reg[15] = R15PC; | |
253 | } | |
254 | else | |
255 | state->Reg[15] = ECC | ER15INT | EMODE | R15PC; | |
c906108c | 256 | } |
c906108c SS |
257 | } |
258 | ||
ff44f8e3 NC |
259 | /* This routine updates the state of the emulator after register 15 has |
260 | been changed. Both the processor flags and register bank are updated. | |
261 | This routine should only be called from a 26 bit mode. */ | |
c906108c | 262 | |
dfcd3bfb JM |
263 | void |
264 | ARMul_R15Altered (ARMul_State * state) | |
c906108c | 265 | { |
dfcd3bfb JM |
266 | if (state->Mode != R15MODE) |
267 | { | |
268 | state->Mode = ARMul_SwitchMode (state, state->Mode, R15MODE); | |
269 | state->NtransSig = (state->Mode & 3) ? HIGH : LOW; | |
c906108c | 270 | } |
ff44f8e3 | 271 | |
dfcd3bfb JM |
272 | if (state->Mode > SVC26MODE) |
273 | state->Emulate = CHANGEMODE; | |
ff44f8e3 | 274 | |
dfcd3bfb | 275 | ASSIGNR15INT (R15INT); |
ff44f8e3 | 276 | |
dfcd3bfb JM |
277 | ASSIGNN ((state->Reg[15] & NBIT) != 0); |
278 | ASSIGNZ ((state->Reg[15] & ZBIT) != 0); | |
279 | ASSIGNC ((state->Reg[15] & CBIT) != 0); | |
280 | ASSIGNV ((state->Reg[15] & VBIT) != 0); | |
c906108c SS |
281 | } |
282 | ||
ff44f8e3 NC |
283 | /* This routine controls the saving and restoring of registers across mode |
284 | changes. The regbank matrix is largely unused, only rows 13 and 14 are | |
285 | used across all modes, 8 to 14 are used for FIQ, all others use the USER | |
286 | column. It's easier this way. old and new parameter are modes numbers. | |
287 | Notice the side effect of changing the Bank variable. */ | |
c906108c | 288 | |
dfcd3bfb JM |
289 | ARMword |
290 | ARMul_SwitchMode (ARMul_State * state, ARMword oldmode, ARMword newmode) | |
291 | { | |
292 | unsigned i; | |
c1a72ffd NC |
293 | ARMword oldbank; |
294 | ARMword newbank; | |
295 | ||
296 | oldbank = ModeToBank (oldmode); | |
297 | newbank = state->Bank = ModeToBank (newmode); | |
298 | ||
ff44f8e3 | 299 | /* Do we really need to do it? */ |
c1a72ffd | 300 | if (oldbank != newbank) |
ff44f8e3 NC |
301 | { |
302 | /* Save away the old registers. */ | |
c1a72ffd | 303 | switch (oldbank) |
ff44f8e3 | 304 | { |
dfcd3bfb JM |
305 | case USERBANK: |
306 | case IRQBANK: | |
307 | case SVCBANK: | |
308 | case ABORTBANK: | |
309 | case UNDEFBANK: | |
c1a72ffd | 310 | if (newbank == FIQBANK) |
dfcd3bfb JM |
311 | for (i = 8; i < 13; i++) |
312 | state->RegBank[USERBANK][i] = state->Reg[i]; | |
c1a72ffd NC |
313 | state->RegBank[oldbank][13] = state->Reg[13]; |
314 | state->RegBank[oldbank][14] = state->Reg[14]; | |
dfcd3bfb JM |
315 | break; |
316 | case FIQBANK: | |
317 | for (i = 8; i < 15; i++) | |
318 | state->RegBank[FIQBANK][i] = state->Reg[i]; | |
319 | break; | |
320 | case DUMMYBANK: | |
321 | for (i = 8; i < 15; i++) | |
322 | state->RegBank[DUMMYBANK][i] = 0; | |
323 | break; | |
c1a72ffd NC |
324 | default: |
325 | abort (); | |
dfcd3bfb | 326 | } |
c1a72ffd | 327 | |
ff44f8e3 | 328 | /* Restore the new registers. */ |
c1a72ffd | 329 | switch (newbank) |
ff44f8e3 | 330 | { |
dfcd3bfb JM |
331 | case USERBANK: |
332 | case IRQBANK: | |
333 | case SVCBANK: | |
334 | case ABORTBANK: | |
335 | case UNDEFBANK: | |
c1a72ffd | 336 | if (oldbank == FIQBANK) |
dfcd3bfb JM |
337 | for (i = 8; i < 13; i++) |
338 | state->Reg[i] = state->RegBank[USERBANK][i]; | |
c1a72ffd NC |
339 | state->Reg[13] = state->RegBank[newbank][13]; |
340 | state->Reg[14] = state->RegBank[newbank][14]; | |
dfcd3bfb JM |
341 | break; |
342 | case FIQBANK: | |
343 | for (i = 8; i < 15; i++) | |
344 | state->Reg[i] = state->RegBank[FIQBANK][i]; | |
345 | break; | |
346 | case DUMMYBANK: | |
347 | for (i = 8; i < 15; i++) | |
348 | state->Reg[i] = 0; | |
349 | break; | |
c1a72ffd NC |
350 | default: |
351 | abort (); | |
ff44f8e3 NC |
352 | } |
353 | } | |
c1a72ffd NC |
354 | |
355 | return newmode; | |
c906108c SS |
356 | } |
357 | ||
ff44f8e3 NC |
358 | /* Given a processor mode, this routine returns the |
359 | register bank that will be accessed in that mode. */ | |
c906108c | 360 | |
dfcd3bfb | 361 | static ARMword |
c1a72ffd | 362 | ModeToBank (ARMword mode) |
dfcd3bfb | 363 | { |
c1a72ffd NC |
364 | static ARMword bankofmode[] = |
365 | { | |
366 | USERBANK, FIQBANK, IRQBANK, SVCBANK, | |
dfcd3bfb JM |
367 | DUMMYBANK, DUMMYBANK, DUMMYBANK, DUMMYBANK, |
368 | DUMMYBANK, DUMMYBANK, DUMMYBANK, DUMMYBANK, | |
369 | DUMMYBANK, DUMMYBANK, DUMMYBANK, DUMMYBANK, | |
c1a72ffd | 370 | USERBANK, FIQBANK, IRQBANK, SVCBANK, |
dfcd3bfb | 371 | DUMMYBANK, DUMMYBANK, DUMMYBANK, ABORTBANK, |
c1a72ffd NC |
372 | DUMMYBANK, DUMMYBANK, DUMMYBANK, UNDEFBANK, |
373 | DUMMYBANK, DUMMYBANK, DUMMYBANK, SYSTEMBANK | |
dfcd3bfb JM |
374 | }; |
375 | ||
c1a72ffd NC |
376 | if (mode >= (sizeof (bankofmode) / sizeof (bankofmode[0]))) |
377 | return DUMMYBANK; | |
378 | ||
379 | return bankofmode[mode]; | |
dfcd3bfb | 380 | } |
c906108c | 381 | |
ff44f8e3 | 382 | /* Returns the register number of the nth register in a reg list. */ |
c906108c | 383 | |
dfcd3bfb JM |
384 | unsigned |
385 | ARMul_NthReg (ARMword instr, unsigned number) | |
386 | { | |
387 | unsigned bit, upto; | |
c906108c | 388 | |
ff44f8e3 | 389 | for (bit = 0, upto = 0; upto <= number; bit ++) |
dfcd3bfb | 390 | if (BIT (bit)) |
ff44f8e3 NC |
391 | upto ++; |
392 | ||
dfcd3bfb | 393 | return (bit - 1); |
c906108c SS |
394 | } |
395 | ||
ff44f8e3 | 396 | /* Assigns the N and Z flags depending on the value of result. */ |
c906108c | 397 | |
dfcd3bfb JM |
398 | void |
399 | ARMul_NegZero (ARMul_State * state, ARMword result) | |
c906108c | 400 | { |
dfcd3bfb JM |
401 | if (NEG (result)) |
402 | { | |
403 | SETN; | |
404 | CLEARZ; | |
405 | } | |
406 | else if (result == 0) | |
407 | { | |
408 | CLEARN; | |
409 | SETZ; | |
410 | } | |
411 | else | |
412 | { | |
413 | CLEARN; | |
414 | CLEARZ; | |
ff44f8e3 | 415 | } |
dfcd3bfb | 416 | } |
c906108c | 417 | |
f743149e | 418 | /* Compute whether an addition of A and B, giving RESULT, overflowed. */ |
ff44f8e3 | 419 | |
dfcd3bfb JM |
420 | int |
421 | AddOverflow (ARMword a, ARMword b, ARMword result) | |
f743149e JM |
422 | { |
423 | return ((NEG (a) && NEG (b) && POS (result)) | |
424 | || (POS (a) && POS (b) && NEG (result))); | |
425 | } | |
426 | ||
427 | /* Compute whether a subtraction of A and B, giving RESULT, overflowed. */ | |
ff44f8e3 | 428 | |
dfcd3bfb JM |
429 | int |
430 | SubOverflow (ARMword a, ARMword b, ARMword result) | |
f743149e JM |
431 | { |
432 | return ((NEG (a) && POS (b) && POS (result)) | |
433 | || (POS (a) && NEG (b) && NEG (result))); | |
434 | } | |
435 | ||
ff44f8e3 | 436 | /* Assigns the C flag after an addition of a and b to give result. */ |
c906108c | 437 | |
dfcd3bfb JM |
438 | void |
439 | ARMul_AddCarry (ARMul_State * state, ARMword a, ARMword b, ARMword result) | |
c906108c | 440 | { |
dfcd3bfb JM |
441 | ASSIGNC ((NEG (a) && NEG (b)) || |
442 | (NEG (a) && POS (result)) || (NEG (b) && POS (result))); | |
443 | } | |
c906108c | 444 | |
ff44f8e3 | 445 | /* Assigns the V flag after an addition of a and b to give result. */ |
c906108c | 446 | |
dfcd3bfb JM |
447 | void |
448 | ARMul_AddOverflow (ARMul_State * state, ARMword a, ARMword b, ARMword result) | |
c906108c | 449 | { |
f743149e JM |
450 | ASSIGNV (AddOverflow (a, b, result)); |
451 | } | |
c906108c | 452 | |
ff44f8e3 | 453 | /* Assigns the C flag after an subtraction of a and b to give result. */ |
c906108c | 454 | |
dfcd3bfb JM |
455 | void |
456 | ARMul_SubCarry (ARMul_State * state, ARMword a, ARMword b, ARMword result) | |
c906108c | 457 | { |
dfcd3bfb JM |
458 | ASSIGNC ((NEG (a) && POS (b)) || |
459 | (NEG (a) && POS (result)) || (POS (b) && POS (result))); | |
c906108c SS |
460 | } |
461 | ||
ff44f8e3 | 462 | /* Assigns the V flag after an subtraction of a and b to give result. */ |
c906108c | 463 | |
dfcd3bfb JM |
464 | void |
465 | ARMul_SubOverflow (ARMul_State * state, ARMword a, ARMword b, ARMword result) | |
c906108c | 466 | { |
f743149e | 467 | ASSIGNV (SubOverflow (a, b, result)); |
c906108c SS |
468 | } |
469 | ||
ff44f8e3 NC |
470 | /* This function does the work of generating the addresses used in an |
471 | LDC instruction. The code here is always post-indexed, it's up to the | |
472 | caller to get the input address correct and to handle base register | |
473 | modification. It also handles the Busy-Waiting. */ | |
c906108c | 474 | |
dfcd3bfb JM |
475 | void |
476 | ARMul_LDC (ARMul_State * state, ARMword instr, ARMword address) | |
477 | { | |
478 | unsigned cpab; | |
479 | ARMword data; | |
c906108c | 480 | |
dfcd3bfb | 481 | UNDEF_LSCPCBaseWb; |
ff44f8e3 NC |
482 | |
483 | if (! CP_ACCESS_ALLOWED (state, CPNum)) | |
dfcd3bfb | 484 | { |
ff44f8e3 NC |
485 | ARMul_UndefInstr (state, instr); |
486 | return; | |
c906108c | 487 | } |
ff44f8e3 NC |
488 | |
489 | if (ADDREXCEPT (address)) | |
490 | INTERNALABORT (address); | |
491 | ||
dfcd3bfb JM |
492 | cpab = (state->LDC[CPNum]) (state, ARMul_FIRST, instr, 0); |
493 | while (cpab == ARMul_BUSY) | |
494 | { | |
495 | ARMul_Icycles (state, 1, 0); | |
ff44f8e3 | 496 | |
dfcd3bfb JM |
497 | if (IntPending (state)) |
498 | { | |
499 | cpab = (state->LDC[CPNum]) (state, ARMul_INTERRUPT, instr, 0); | |
500 | return; | |
501 | } | |
502 | else | |
503 | cpab = (state->LDC[CPNum]) (state, ARMul_BUSY, instr, 0); | |
c906108c | 504 | } |
dfcd3bfb JM |
505 | if (cpab == ARMul_CANT) |
506 | { | |
507 | CPTAKEABORT; | |
508 | return; | |
c906108c | 509 | } |
ff44f8e3 | 510 | |
dfcd3bfb JM |
511 | cpab = (state->LDC[CPNum]) (state, ARMul_TRANSFER, instr, 0); |
512 | data = ARMul_LoadWordN (state, address); | |
513 | BUSUSEDINCPCN; | |
ff44f8e3 | 514 | |
dfcd3bfb JM |
515 | if (BIT (21)) |
516 | LSBase = state->Base; | |
517 | cpab = (state->LDC[CPNum]) (state, ARMul_DATA, instr, data); | |
ff44f8e3 | 518 | |
dfcd3bfb JM |
519 | while (cpab == ARMul_INC) |
520 | { | |
521 | address += 4; | |
522 | data = ARMul_LoadWordN (state, address); | |
523 | cpab = (state->LDC[CPNum]) (state, ARMul_DATA, instr, data); | |
c906108c | 524 | } |
ff44f8e3 | 525 | |
dfcd3bfb | 526 | if (state->abortSig || state->Aborted) |
ff44f8e3 | 527 | TAKEABORT; |
dfcd3bfb | 528 | } |
c906108c | 529 | |
ff44f8e3 NC |
530 | /* This function does the work of generating the addresses used in an |
531 | STC instruction. The code here is always post-indexed, it's up to the | |
532 | caller to get the input address correct and to handle base register | |
533 | modification. It also handles the Busy-Waiting. */ | |
c906108c | 534 | |
dfcd3bfb JM |
535 | void |
536 | ARMul_STC (ARMul_State * state, ARMword instr, ARMword address) | |
537 | { | |
538 | unsigned cpab; | |
539 | ARMword data; | |
c906108c | 540 | |
dfcd3bfb | 541 | UNDEF_LSCPCBaseWb; |
ff44f8e3 NC |
542 | |
543 | if (! CP_ACCESS_ALLOWED (state, CPNum)) | |
dfcd3bfb | 544 | { |
ff44f8e3 NC |
545 | ARMul_UndefInstr (state, instr); |
546 | return; | |
c906108c | 547 | } |
ff44f8e3 NC |
548 | |
549 | if (ADDREXCEPT (address) || VECTORACCESS (address)) | |
550 | INTERNALABORT (address); | |
551 | ||
dfcd3bfb JM |
552 | cpab = (state->STC[CPNum]) (state, ARMul_FIRST, instr, &data); |
553 | while (cpab == ARMul_BUSY) | |
554 | { | |
555 | ARMul_Icycles (state, 1, 0); | |
556 | if (IntPending (state)) | |
557 | { | |
558 | cpab = (state->STC[CPNum]) (state, ARMul_INTERRUPT, instr, 0); | |
559 | return; | |
560 | } | |
561 | else | |
562 | cpab = (state->STC[CPNum]) (state, ARMul_BUSY, instr, &data); | |
c906108c | 563 | } |
ff44f8e3 | 564 | |
dfcd3bfb JM |
565 | if (cpab == ARMul_CANT) |
566 | { | |
567 | CPTAKEABORT; | |
568 | return; | |
c906108c SS |
569 | } |
570 | #ifndef MODE32 | |
dfcd3bfb | 571 | if (ADDREXCEPT (address) || VECTORACCESS (address)) |
ff44f8e3 | 572 | INTERNALABORT (address); |
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 | } |