]>
Commit | Line | Data |
---|---|---|
e8af50a3 FB |
1 | #include "exec.h" |
2 | ||
83469015 | 3 | //#define DEBUG_PCALL |
e80cfcfc FB |
4 | //#define DEBUG_MMU |
5 | ||
9d893301 FB |
6 | void raise_exception(int tt) |
7 | { | |
8 | env->exception_index = tt; | |
9 | cpu_loop_exit(); | |
10 | } | |
11 | ||
a0c4cb4a FB |
12 | #ifdef USE_INT_TO_FLOAT_HELPERS |
13 | void do_fitos(void) | |
14 | { | |
65ce8c2f | 15 | FT0 = int32_to_float32(*((int32_t *)&FT1)); |
a0c4cb4a FB |
16 | } |
17 | ||
18 | void do_fitod(void) | |
19 | { | |
65ce8c2f | 20 | DT0 = int32_to_float64(*((int32_t *)&FT1)); |
a0c4cb4a FB |
21 | } |
22 | #endif | |
23 | ||
24 | void do_fabss(void) | |
e8af50a3 | 25 | { |
7a0e1f41 | 26 | FT0 = float32_abs(FT1); |
e8af50a3 FB |
27 | } |
28 | ||
3475187d FB |
29 | #ifdef TARGET_SPARC64 |
30 | void do_fabsd(void) | |
31 | { | |
32 | DT0 = float64_abs(DT1); | |
33 | } | |
34 | #endif | |
35 | ||
a0c4cb4a | 36 | void do_fsqrts(void) |
e8af50a3 | 37 | { |
7a0e1f41 | 38 | FT0 = float32_sqrt(FT1, &env->fp_status); |
e8af50a3 FB |
39 | } |
40 | ||
a0c4cb4a | 41 | void do_fsqrtd(void) |
e8af50a3 | 42 | { |
7a0e1f41 | 43 | DT0 = float64_sqrt(DT1, &env->fp_status); |
e8af50a3 FB |
44 | } |
45 | ||
65ce8c2f FB |
46 | #define GEN_FCMP(name, size, reg1, reg2, FS) \ |
47 | void glue(do_, name) (void) \ | |
48 | { \ | |
49 | env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); \ | |
50 | switch (glue(size, _compare) (reg1, reg2, &env->fp_status)) { \ | |
51 | case float_relation_unordered: \ | |
52 | T0 = (FSR_FCC1 | FSR_FCC0) << FS; \ | |
53 | if (env->fsr & FSR_NVM) { \ | |
54 | env->fsr |= T0; \ | |
55 | raise_exception(TT_FP_EXCP); \ | |
56 | } else { \ | |
57 | env->fsr |= FSR_NVA; \ | |
58 | } \ | |
59 | break; \ | |
60 | case float_relation_less: \ | |
61 | T0 = FSR_FCC0 << FS; \ | |
62 | break; \ | |
63 | case float_relation_greater: \ | |
64 | T0 = FSR_FCC1 << FS; \ | |
65 | break; \ | |
66 | default: \ | |
67 | T0 = 0; \ | |
68 | break; \ | |
69 | } \ | |
70 | env->fsr |= T0; \ | |
e8af50a3 | 71 | } |
e8af50a3 | 72 | |
65ce8c2f FB |
73 | GEN_FCMP(fcmps, float32, FT0, FT1, 0); |
74 | GEN_FCMP(fcmpd, float64, DT0, DT1, 0); | |
3475187d FB |
75 | |
76 | #ifdef TARGET_SPARC64 | |
65ce8c2f FB |
77 | GEN_FCMP(fcmps_fcc1, float32, FT0, FT1, 22); |
78 | GEN_FCMP(fcmpd_fcc1, float64, DT0, DT1, 22); | |
3475187d | 79 | |
65ce8c2f FB |
80 | GEN_FCMP(fcmps_fcc2, float32, FT0, FT1, 24); |
81 | GEN_FCMP(fcmpd_fcc2, float64, DT0, DT1, 24); | |
3475187d | 82 | |
65ce8c2f FB |
83 | GEN_FCMP(fcmps_fcc3, float32, FT0, FT1, 26); |
84 | GEN_FCMP(fcmpd_fcc3, float64, DT0, DT1, 26); | |
3475187d FB |
85 | #endif |
86 | ||
24741ef3 FB |
87 | #if defined(CONFIG_USER_ONLY) |
88 | void helper_ld_asi(int asi, int size, int sign) | |
89 | { | |
90 | } | |
91 | ||
92 | void helper_st_asi(int asi, int size, int sign) | |
93 | { | |
94 | } | |
95 | #else | |
3475187d | 96 | #ifndef TARGET_SPARC64 |
a0c4cb4a | 97 | void helper_ld_asi(int asi, int size, int sign) |
e8af50a3 | 98 | { |
83469015 | 99 | uint32_t ret = 0; |
e80cfcfc FB |
100 | |
101 | switch (asi) { | |
e8af50a3 | 102 | case 3: /* MMU probe */ |
e80cfcfc FB |
103 | { |
104 | int mmulev; | |
105 | ||
106 | mmulev = (T0 >> 8) & 15; | |
107 | if (mmulev > 4) | |
108 | ret = 0; | |
109 | else { | |
ee5bbe38 | 110 | ret = mmu_probe(env, T0, mmulev); |
e80cfcfc FB |
111 | //bswap32s(&ret); |
112 | } | |
113 | #ifdef DEBUG_MMU | |
114 | printf("mmu_probe: 0x%08x (lev %d) -> 0x%08x\n", T0, mmulev, ret); | |
115 | #endif | |
116 | } | |
117 | break; | |
e8af50a3 FB |
118 | case 4: /* read MMU regs */ |
119 | { | |
e80cfcfc | 120 | int reg = (T0 >> 8) & 0xf; |
e8af50a3 | 121 | |
e80cfcfc | 122 | ret = env->mmuregs[reg]; |
55754d9e FB |
123 | if (reg == 3) /* Fault status cleared on read */ |
124 | env->mmuregs[reg] = 0; | |
125 | #ifdef DEBUG_MMU | |
126 | printf("mmu_read: reg[%d] = 0x%08x\n", reg, ret); | |
127 | #endif | |
e8af50a3 | 128 | } |
e80cfcfc | 129 | break; |
e8af50a3 | 130 | case 0x20 ... 0x2f: /* MMU passthrough */ |
02aab46a FB |
131 | switch(size) { |
132 | case 1: | |
133 | ret = ldub_phys(T0); | |
134 | break; | |
135 | case 2: | |
136 | ret = lduw_phys(T0 & ~1); | |
137 | break; | |
138 | default: | |
139 | case 4: | |
140 | ret = ldl_phys(T0 & ~3); | |
141 | break; | |
9e61bde5 FB |
142 | case 8: |
143 | ret = ldl_phys(T0 & ~3); | |
144 | T0 = ldl_phys((T0 + 4) & ~3); | |
145 | break; | |
02aab46a | 146 | } |
e80cfcfc | 147 | break; |
e8af50a3 | 148 | default: |
e80cfcfc FB |
149 | ret = 0; |
150 | break; | |
e8af50a3 | 151 | } |
e80cfcfc | 152 | T1 = ret; |
e8af50a3 FB |
153 | } |
154 | ||
a0c4cb4a | 155 | void helper_st_asi(int asi, int size, int sign) |
e8af50a3 FB |
156 | { |
157 | switch(asi) { | |
158 | case 3: /* MMU flush */ | |
e80cfcfc FB |
159 | { |
160 | int mmulev; | |
161 | ||
162 | mmulev = (T0 >> 8) & 15; | |
55754d9e FB |
163 | #ifdef DEBUG_MMU |
164 | printf("mmu flush level %d\n", mmulev); | |
165 | #endif | |
e80cfcfc FB |
166 | switch (mmulev) { |
167 | case 0: // flush page | |
55754d9e | 168 | tlb_flush_page(env, T0 & 0xfffff000); |
e80cfcfc FB |
169 | break; |
170 | case 1: // flush segment (256k) | |
171 | case 2: // flush region (16M) | |
172 | case 3: // flush context (4G) | |
173 | case 4: // flush entire | |
55754d9e | 174 | tlb_flush(env, 1); |
e80cfcfc FB |
175 | break; |
176 | default: | |
177 | break; | |
178 | } | |
55754d9e | 179 | #ifdef DEBUG_MMU |
ee5bbe38 | 180 | dump_mmu(env); |
55754d9e | 181 | #endif |
e80cfcfc FB |
182 | return; |
183 | } | |
e8af50a3 FB |
184 | case 4: /* write MMU regs */ |
185 | { | |
83469015 FB |
186 | int reg = (T0 >> 8) & 0xf; |
187 | uint32_t oldreg; | |
e80cfcfc FB |
188 | |
189 | oldreg = env->mmuregs[reg]; | |
55754d9e FB |
190 | switch(reg) { |
191 | case 0: | |
e8af50a3 FB |
192 | env->mmuregs[reg] &= ~(MMU_E | MMU_NF); |
193 | env->mmuregs[reg] |= T1 & (MMU_E | MMU_NF); | |
6f7e9aec FB |
194 | // Mappings generated during no-fault mode or MMU |
195 | // disabled mode are invalid in normal mode | |
196 | if (oldreg != env->mmuregs[reg]) | |
55754d9e FB |
197 | tlb_flush(env, 1); |
198 | break; | |
199 | case 2: | |
e8af50a3 | 200 | env->mmuregs[reg] = T1; |
55754d9e FB |
201 | if (oldreg != env->mmuregs[reg]) { |
202 | /* we flush when the MMU context changes because | |
203 | QEMU has no MMU context support */ | |
204 | tlb_flush(env, 1); | |
205 | } | |
206 | break; | |
207 | case 3: | |
208 | case 4: | |
209 | break; | |
210 | default: | |
211 | env->mmuregs[reg] = T1; | |
212 | break; | |
213 | } | |
214 | #ifdef DEBUG_MMU | |
215 | if (oldreg != env->mmuregs[reg]) { | |
216 | printf("mmu change reg[%d]: 0x%08x -> 0x%08x\n", reg, oldreg, env->mmuregs[reg]); | |
217 | } | |
ee5bbe38 | 218 | dump_mmu(env); |
55754d9e | 219 | #endif |
e8af50a3 FB |
220 | return; |
221 | } | |
e80cfcfc FB |
222 | case 0x17: /* Block copy, sta access */ |
223 | { | |
224 | // value (T1) = src | |
225 | // address (T0) = dst | |
226 | // copy 32 bytes | |
83469015 | 227 | uint32_t src = T1, dst = T0; |
e80cfcfc FB |
228 | uint8_t temp[32]; |
229 | ||
49be8030 | 230 | tswap32s(&src); |
e80cfcfc FB |
231 | |
232 | cpu_physical_memory_read(src, (void *) &temp, 32); | |
233 | cpu_physical_memory_write(dst, (void *) &temp, 32); | |
234 | } | |
235 | return; | |
236 | case 0x1f: /* Block fill, stda access */ | |
237 | { | |
238 | // value (T1, T2) | |
239 | // address (T0) = dst | |
240 | // fill 32 bytes | |
83469015 FB |
241 | int i; |
242 | uint32_t dst = T0; | |
e80cfcfc FB |
243 | uint64_t val; |
244 | ||
245 | val = (((uint64_t)T1) << 32) | T2; | |
49be8030 | 246 | tswap64s(&val); |
e80cfcfc FB |
247 | |
248 | for (i = 0; i < 32; i += 8, dst += 8) { | |
249 | cpu_physical_memory_write(dst, (void *) &val, 8); | |
250 | } | |
251 | } | |
252 | return; | |
e8af50a3 FB |
253 | case 0x20 ... 0x2f: /* MMU passthrough */ |
254 | { | |
02aab46a FB |
255 | switch(size) { |
256 | case 1: | |
257 | stb_phys(T0, T1); | |
258 | break; | |
259 | case 2: | |
260 | stw_phys(T0 & ~1, T1); | |
261 | break; | |
262 | case 4: | |
263 | default: | |
264 | stl_phys(T0 & ~3, T1); | |
265 | break; | |
9e61bde5 FB |
266 | case 8: |
267 | stl_phys(T0 & ~3, T1); | |
268 | stl_phys((T0 + 4) & ~3, T2); | |
269 | break; | |
02aab46a | 270 | } |
e8af50a3 FB |
271 | } |
272 | return; | |
273 | default: | |
274 | return; | |
275 | } | |
276 | } | |
277 | ||
3475187d FB |
278 | #else |
279 | ||
280 | void helper_ld_asi(int asi, int size, int sign) | |
281 | { | |
83469015 | 282 | uint64_t ret = 0; |
3475187d FB |
283 | |
284 | if (asi < 0x80 && (env->pstate & PS_PRIV) == 0) | |
83469015 | 285 | raise_exception(TT_PRIV_ACT); |
3475187d FB |
286 | |
287 | switch (asi) { | |
288 | case 0x14: // Bypass | |
289 | case 0x15: // Bypass, non-cacheable | |
290 | { | |
02aab46a FB |
291 | switch(size) { |
292 | case 1: | |
293 | ret = ldub_phys(T0); | |
294 | break; | |
295 | case 2: | |
296 | ret = lduw_phys(T0 & ~1); | |
297 | break; | |
298 | case 4: | |
299 | ret = ldl_phys(T0 & ~3); | |
300 | break; | |
301 | default: | |
302 | case 8: | |
303 | ret = ldq_phys(T0 & ~7); | |
304 | break; | |
305 | } | |
3475187d FB |
306 | break; |
307 | } | |
83469015 FB |
308 | case 0x04: // Nucleus |
309 | case 0x0c: // Nucleus Little Endian (LE) | |
310 | case 0x10: // As if user primary | |
311 | case 0x11: // As if user secondary | |
312 | case 0x18: // As if user primary LE | |
313 | case 0x19: // As if user secondary LE | |
3475187d FB |
314 | case 0x1c: // Bypass LE |
315 | case 0x1d: // Bypass, non-cacheable LE | |
83469015 FB |
316 | case 0x24: // Nucleus quad LDD 128 bit atomic |
317 | case 0x2c: // Nucleus quad LDD 128 bit atomic | |
318 | case 0x4a: // UPA config | |
319 | case 0x82: // Primary no-fault | |
320 | case 0x83: // Secondary no-fault | |
321 | case 0x88: // Primary LE | |
322 | case 0x89: // Secondary LE | |
323 | case 0x8a: // Primary no-fault LE | |
324 | case 0x8b: // Secondary no-fault LE | |
3475187d FB |
325 | // XXX |
326 | break; | |
327 | case 0x45: // LSU | |
328 | ret = env->lsu; | |
329 | break; | |
330 | case 0x50: // I-MMU regs | |
331 | { | |
332 | int reg = (T0 >> 3) & 0xf; | |
333 | ||
334 | ret = env->immuregs[reg]; | |
335 | break; | |
336 | } | |
337 | case 0x51: // I-MMU 8k TSB pointer | |
338 | case 0x52: // I-MMU 64k TSB pointer | |
339 | case 0x55: // I-MMU data access | |
83469015 | 340 | // XXX |
3475187d | 341 | break; |
83469015 FB |
342 | case 0x56: // I-MMU tag read |
343 | { | |
344 | unsigned int i; | |
345 | ||
346 | for (i = 0; i < 64; i++) { | |
347 | // Valid, ctx match, vaddr match | |
348 | if ((env->itlb_tte[i] & 0x8000000000000000ULL) != 0 && | |
349 | env->itlb_tag[i] == T0) { | |
350 | ret = env->itlb_tag[i]; | |
351 | break; | |
352 | } | |
353 | } | |
354 | break; | |
355 | } | |
3475187d FB |
356 | case 0x58: // D-MMU regs |
357 | { | |
358 | int reg = (T0 >> 3) & 0xf; | |
359 | ||
360 | ret = env->dmmuregs[reg]; | |
361 | break; | |
362 | } | |
83469015 FB |
363 | case 0x5e: // D-MMU tag read |
364 | { | |
365 | unsigned int i; | |
366 | ||
367 | for (i = 0; i < 64; i++) { | |
368 | // Valid, ctx match, vaddr match | |
369 | if ((env->dtlb_tte[i] & 0x8000000000000000ULL) != 0 && | |
370 | env->dtlb_tag[i] == T0) { | |
371 | ret = env->dtlb_tag[i]; | |
372 | break; | |
373 | } | |
374 | } | |
375 | break; | |
376 | } | |
3475187d FB |
377 | case 0x59: // D-MMU 8k TSB pointer |
378 | case 0x5a: // D-MMU 64k TSB pointer | |
379 | case 0x5b: // D-MMU data pointer | |
380 | case 0x5d: // D-MMU data access | |
83469015 FB |
381 | case 0x48: // Interrupt dispatch, RO |
382 | case 0x49: // Interrupt data receive | |
383 | case 0x7f: // Incoming interrupt vector, RO | |
384 | // XXX | |
3475187d FB |
385 | break; |
386 | case 0x54: // I-MMU data in, WO | |
387 | case 0x57: // I-MMU demap, WO | |
388 | case 0x5c: // D-MMU data in, WO | |
389 | case 0x5f: // D-MMU demap, WO | |
83469015 | 390 | case 0x77: // Interrupt vector, WO |
3475187d FB |
391 | default: |
392 | ret = 0; | |
393 | break; | |
394 | } | |
395 | T1 = ret; | |
396 | } | |
397 | ||
398 | void helper_st_asi(int asi, int size, int sign) | |
399 | { | |
400 | if (asi < 0x80 && (env->pstate & PS_PRIV) == 0) | |
83469015 | 401 | raise_exception(TT_PRIV_ACT); |
3475187d FB |
402 | |
403 | switch(asi) { | |
404 | case 0x14: // Bypass | |
405 | case 0x15: // Bypass, non-cacheable | |
406 | { | |
02aab46a FB |
407 | switch(size) { |
408 | case 1: | |
409 | stb_phys(T0, T1); | |
410 | break; | |
411 | case 2: | |
412 | stw_phys(T0 & ~1, T1); | |
413 | break; | |
414 | case 4: | |
415 | stl_phys(T0 & ~3, T1); | |
416 | break; | |
417 | case 8: | |
418 | default: | |
419 | stq_phys(T0 & ~7, T1); | |
420 | break; | |
421 | } | |
3475187d FB |
422 | } |
423 | return; | |
83469015 FB |
424 | case 0x04: // Nucleus |
425 | case 0x0c: // Nucleus Little Endian (LE) | |
426 | case 0x10: // As if user primary | |
427 | case 0x11: // As if user secondary | |
428 | case 0x18: // As if user primary LE | |
429 | case 0x19: // As if user secondary LE | |
3475187d FB |
430 | case 0x1c: // Bypass LE |
431 | case 0x1d: // Bypass, non-cacheable LE | |
83469015 FB |
432 | case 0x24: // Nucleus quad LDD 128 bit atomic |
433 | case 0x2c: // Nucleus quad LDD 128 bit atomic | |
434 | case 0x4a: // UPA config | |
435 | case 0x88: // Primary LE | |
436 | case 0x89: // Secondary LE | |
3475187d FB |
437 | // XXX |
438 | return; | |
439 | case 0x45: // LSU | |
440 | { | |
441 | uint64_t oldreg; | |
442 | ||
443 | oldreg = env->lsu; | |
444 | env->lsu = T1 & (DMMU_E | IMMU_E); | |
445 | // Mappings generated during D/I MMU disabled mode are | |
446 | // invalid in normal mode | |
83469015 FB |
447 | if (oldreg != env->lsu) { |
448 | #ifdef DEBUG_MMU | |
26a76461 | 449 | printf("LSU change: 0x%" PRIx64 " -> 0x%" PRIx64 "\n", oldreg, env->lsu); |
83469015 FB |
450 | dump_mmu(env); |
451 | #endif | |
3475187d | 452 | tlb_flush(env, 1); |
83469015 | 453 | } |
3475187d FB |
454 | return; |
455 | } | |
456 | case 0x50: // I-MMU regs | |
457 | { | |
458 | int reg = (T0 >> 3) & 0xf; | |
459 | uint64_t oldreg; | |
460 | ||
461 | oldreg = env->immuregs[reg]; | |
462 | switch(reg) { | |
463 | case 0: // RO | |
464 | case 4: | |
465 | return; | |
466 | case 1: // Not in I-MMU | |
467 | case 2: | |
468 | case 7: | |
469 | case 8: | |
470 | return; | |
471 | case 3: // SFSR | |
472 | if ((T1 & 1) == 0) | |
473 | T1 = 0; // Clear SFSR | |
474 | break; | |
475 | case 5: // TSB access | |
476 | case 6: // Tag access | |
477 | default: | |
478 | break; | |
479 | } | |
480 | env->immuregs[reg] = T1; | |
481 | #ifdef DEBUG_MMU | |
482 | if (oldreg != env->immuregs[reg]) { | |
26a76461 | 483 | printf("mmu change reg[%d]: 0x%08" PRIx64 " -> 0x%08" PRIx64 "\n", reg, oldreg, env->immuregs[reg]); |
3475187d | 484 | } |
ee5bbe38 | 485 | dump_mmu(env); |
3475187d FB |
486 | #endif |
487 | return; | |
488 | } | |
489 | case 0x54: // I-MMU data in | |
490 | { | |
491 | unsigned int i; | |
492 | ||
493 | // Try finding an invalid entry | |
494 | for (i = 0; i < 64; i++) { | |
495 | if ((env->itlb_tte[i] & 0x8000000000000000ULL) == 0) { | |
496 | env->itlb_tag[i] = env->immuregs[6]; | |
497 | env->itlb_tte[i] = T1; | |
498 | return; | |
499 | } | |
500 | } | |
501 | // Try finding an unlocked entry | |
502 | for (i = 0; i < 64; i++) { | |
503 | if ((env->itlb_tte[i] & 0x40) == 0) { | |
504 | env->itlb_tag[i] = env->immuregs[6]; | |
505 | env->itlb_tte[i] = T1; | |
506 | return; | |
507 | } | |
508 | } | |
509 | // error state? | |
510 | return; | |
511 | } | |
512 | case 0x55: // I-MMU data access | |
513 | { | |
514 | unsigned int i = (T0 >> 3) & 0x3f; | |
515 | ||
516 | env->itlb_tag[i] = env->immuregs[6]; | |
517 | env->itlb_tte[i] = T1; | |
518 | return; | |
519 | } | |
520 | case 0x57: // I-MMU demap | |
83469015 | 521 | // XXX |
3475187d FB |
522 | return; |
523 | case 0x58: // D-MMU regs | |
524 | { | |
525 | int reg = (T0 >> 3) & 0xf; | |
526 | uint64_t oldreg; | |
527 | ||
528 | oldreg = env->dmmuregs[reg]; | |
529 | switch(reg) { | |
530 | case 0: // RO | |
531 | case 4: | |
532 | return; | |
533 | case 3: // SFSR | |
534 | if ((T1 & 1) == 0) { | |
535 | T1 = 0; // Clear SFSR, Fault address | |
536 | env->dmmuregs[4] = 0; | |
537 | } | |
538 | env->dmmuregs[reg] = T1; | |
539 | break; | |
540 | case 1: // Primary context | |
541 | case 2: // Secondary context | |
542 | case 5: // TSB access | |
543 | case 6: // Tag access | |
544 | case 7: // Virtual Watchpoint | |
545 | case 8: // Physical Watchpoint | |
546 | default: | |
547 | break; | |
548 | } | |
549 | env->dmmuregs[reg] = T1; | |
550 | #ifdef DEBUG_MMU | |
551 | if (oldreg != env->dmmuregs[reg]) { | |
26a76461 | 552 | printf("mmu change reg[%d]: 0x%08" PRIx64 " -> 0x%08" PRIx64 "\n", reg, oldreg, env->dmmuregs[reg]); |
3475187d | 553 | } |
ee5bbe38 | 554 | dump_mmu(env); |
3475187d FB |
555 | #endif |
556 | return; | |
557 | } | |
558 | case 0x5c: // D-MMU data in | |
559 | { | |
560 | unsigned int i; | |
561 | ||
562 | // Try finding an invalid entry | |
563 | for (i = 0; i < 64; i++) { | |
564 | if ((env->dtlb_tte[i] & 0x8000000000000000ULL) == 0) { | |
565 | env->dtlb_tag[i] = env->dmmuregs[6]; | |
566 | env->dtlb_tte[i] = T1; | |
567 | return; | |
568 | } | |
569 | } | |
570 | // Try finding an unlocked entry | |
571 | for (i = 0; i < 64; i++) { | |
572 | if ((env->dtlb_tte[i] & 0x40) == 0) { | |
573 | env->dtlb_tag[i] = env->dmmuregs[6]; | |
574 | env->dtlb_tte[i] = T1; | |
575 | return; | |
576 | } | |
577 | } | |
578 | // error state? | |
579 | return; | |
580 | } | |
581 | case 0x5d: // D-MMU data access | |
582 | { | |
583 | unsigned int i = (T0 >> 3) & 0x3f; | |
584 | ||
585 | env->dtlb_tag[i] = env->dmmuregs[6]; | |
586 | env->dtlb_tte[i] = T1; | |
587 | return; | |
588 | } | |
589 | case 0x5f: // D-MMU demap | |
83469015 FB |
590 | case 0x49: // Interrupt data receive |
591 | // XXX | |
3475187d FB |
592 | return; |
593 | case 0x51: // I-MMU 8k TSB pointer, RO | |
594 | case 0x52: // I-MMU 64k TSB pointer, RO | |
595 | case 0x56: // I-MMU tag read, RO | |
596 | case 0x59: // D-MMU 8k TSB pointer, RO | |
597 | case 0x5a: // D-MMU 64k TSB pointer, RO | |
598 | case 0x5b: // D-MMU data pointer, RO | |
599 | case 0x5e: // D-MMU tag read, RO | |
83469015 FB |
600 | case 0x48: // Interrupt dispatch, RO |
601 | case 0x7f: // Incoming interrupt vector, RO | |
602 | case 0x82: // Primary no-fault, RO | |
603 | case 0x83: // Secondary no-fault, RO | |
604 | case 0x8a: // Primary no-fault LE, RO | |
605 | case 0x8b: // Secondary no-fault LE, RO | |
3475187d FB |
606 | default: |
607 | return; | |
608 | } | |
609 | } | |
3475187d | 610 | #endif |
24741ef3 | 611 | #endif /* !CONFIG_USER_ONLY */ |
3475187d FB |
612 | |
613 | #ifndef TARGET_SPARC64 | |
a0c4cb4a | 614 | void helper_rett() |
e8af50a3 | 615 | { |
af7bf89b FB |
616 | unsigned int cwp; |
617 | ||
e8af50a3 FB |
618 | env->psret = 1; |
619 | cwp = (env->cwp + 1) & (NWINDOWS - 1); | |
620 | if (env->wim & (1 << cwp)) { | |
621 | raise_exception(TT_WIN_UNF); | |
622 | } | |
623 | set_cwp(cwp); | |
624 | env->psrs = env->psrps; | |
625 | } | |
3475187d | 626 | #endif |
e8af50a3 | 627 | |
8d5f07fa | 628 | void helper_ldfsr(void) |
e8af50a3 | 629 | { |
7a0e1f41 | 630 | int rnd_mode; |
e8af50a3 FB |
631 | switch (env->fsr & FSR_RD_MASK) { |
632 | case FSR_RD_NEAREST: | |
7a0e1f41 | 633 | rnd_mode = float_round_nearest_even; |
e8af50a3 | 634 | break; |
ed910241 | 635 | default: |
e8af50a3 | 636 | case FSR_RD_ZERO: |
7a0e1f41 | 637 | rnd_mode = float_round_to_zero; |
e8af50a3 FB |
638 | break; |
639 | case FSR_RD_POS: | |
7a0e1f41 | 640 | rnd_mode = float_round_up; |
e8af50a3 FB |
641 | break; |
642 | case FSR_RD_NEG: | |
7a0e1f41 | 643 | rnd_mode = float_round_down; |
e8af50a3 FB |
644 | break; |
645 | } | |
7a0e1f41 | 646 | set_float_rounding_mode(rnd_mode, &env->fp_status); |
e8af50a3 | 647 | } |
e80cfcfc | 648 | |
e80cfcfc FB |
649 | void helper_debug() |
650 | { | |
651 | env->exception_index = EXCP_DEBUG; | |
652 | cpu_loop_exit(); | |
653 | } | |
af7bf89b | 654 | |
3475187d | 655 | #ifndef TARGET_SPARC64 |
af7bf89b FB |
656 | void do_wrpsr() |
657 | { | |
658 | PUT_PSR(env, T0); | |
659 | } | |
660 | ||
661 | void do_rdpsr() | |
662 | { | |
663 | T0 = GET_PSR(env); | |
664 | } | |
3475187d FB |
665 | |
666 | #else | |
667 | ||
668 | void do_popc() | |
669 | { | |
670 | T0 = (T1 & 0x5555555555555555ULL) + ((T1 >> 1) & 0x5555555555555555ULL); | |
671 | T0 = (T0 & 0x3333333333333333ULL) + ((T0 >> 2) & 0x3333333333333333ULL); | |
672 | T0 = (T0 & 0x0f0f0f0f0f0f0f0fULL) + ((T0 >> 4) & 0x0f0f0f0f0f0f0f0fULL); | |
673 | T0 = (T0 & 0x00ff00ff00ff00ffULL) + ((T0 >> 8) & 0x00ff00ff00ff00ffULL); | |
674 | T0 = (T0 & 0x0000ffff0000ffffULL) + ((T0 >> 16) & 0x0000ffff0000ffffULL); | |
675 | T0 = (T0 & 0x00000000ffffffffULL) + ((T0 >> 32) & 0x00000000ffffffffULL); | |
676 | } | |
83469015 FB |
677 | |
678 | static inline uint64_t *get_gregset(uint64_t pstate) | |
679 | { | |
680 | switch (pstate) { | |
681 | default: | |
682 | case 0: | |
683 | return env->bgregs; | |
684 | case PS_AG: | |
685 | return env->agregs; | |
686 | case PS_MG: | |
687 | return env->mgregs; | |
688 | case PS_IG: | |
689 | return env->igregs; | |
690 | } | |
691 | } | |
692 | ||
693 | void do_wrpstate() | |
694 | { | |
695 | uint64_t new_pstate, pstate_regs, new_pstate_regs; | |
696 | uint64_t *src, *dst; | |
697 | ||
698 | new_pstate = T0 & 0xf3f; | |
699 | pstate_regs = env->pstate & 0xc01; | |
700 | new_pstate_regs = new_pstate & 0xc01; | |
701 | if (new_pstate_regs != pstate_regs) { | |
702 | // Switch global register bank | |
703 | src = get_gregset(new_pstate_regs); | |
704 | dst = get_gregset(pstate_regs); | |
705 | memcpy32(dst, env->gregs); | |
706 | memcpy32(env->gregs, src); | |
707 | } | |
708 | env->pstate = new_pstate; | |
709 | } | |
710 | ||
711 | void do_done(void) | |
712 | { | |
713 | env->tl--; | |
714 | env->pc = env->tnpc[env->tl]; | |
715 | env->npc = env->tnpc[env->tl] + 4; | |
716 | PUT_CCR(env, env->tstate[env->tl] >> 32); | |
717 | env->asi = (env->tstate[env->tl] >> 24) & 0xff; | |
718 | env->pstate = (env->tstate[env->tl] >> 8) & 0xfff; | |
719 | set_cwp(env->tstate[env->tl] & 0xff); | |
720 | } | |
721 | ||
722 | void do_retry(void) | |
723 | { | |
724 | env->tl--; | |
725 | env->pc = env->tpc[env->tl]; | |
726 | env->npc = env->tnpc[env->tl]; | |
727 | PUT_CCR(env, env->tstate[env->tl] >> 32); | |
728 | env->asi = (env->tstate[env->tl] >> 24) & 0xff; | |
729 | env->pstate = (env->tstate[env->tl] >> 8) & 0xfff; | |
730 | set_cwp(env->tstate[env->tl] & 0xff); | |
731 | } | |
3475187d | 732 | #endif |
ee5bbe38 FB |
733 | |
734 | void set_cwp(int new_cwp) | |
735 | { | |
736 | /* put the modified wrap registers at their proper location */ | |
737 | if (env->cwp == (NWINDOWS - 1)) | |
738 | memcpy32(env->regbase, env->regbase + NWINDOWS * 16); | |
739 | env->cwp = new_cwp; | |
740 | /* put the wrap registers at their temporary location */ | |
741 | if (new_cwp == (NWINDOWS - 1)) | |
742 | memcpy32(env->regbase + NWINDOWS * 16, env->regbase); | |
743 | env->regwptr = env->regbase + (new_cwp * 16); | |
744 | REGWPTR = env->regwptr; | |
745 | } | |
746 | ||
747 | void cpu_set_cwp(CPUState *env1, int new_cwp) | |
748 | { | |
749 | CPUState *saved_env; | |
750 | #ifdef reg_REGWPTR | |
751 | target_ulong *saved_regwptr; | |
752 | #endif | |
753 | ||
754 | saved_env = env; | |
755 | #ifdef reg_REGWPTR | |
756 | saved_regwptr = REGWPTR; | |
757 | #endif | |
758 | env = env1; | |
759 | set_cwp(new_cwp); | |
760 | env = saved_env; | |
761 | #ifdef reg_REGWPTR | |
762 | REGWPTR = saved_regwptr; | |
763 | #endif | |
764 | } | |
765 | ||
766 | #ifdef TARGET_SPARC64 | |
767 | void do_interrupt(int intno) | |
768 | { | |
769 | #ifdef DEBUG_PCALL | |
770 | if (loglevel & CPU_LOG_INT) { | |
771 | static int count; | |
26a76461 | 772 | fprintf(logfile, "%6d: v=%04x pc=%016" PRIx64 " npc=%016" PRIx64 " SP=%016" PRIx64 "\n", |
ee5bbe38 FB |
773 | count, intno, |
774 | env->pc, | |
775 | env->npc, env->regwptr[6]); | |
776 | cpu_dump_state(env, logfile, fprintf, 0); | |
777 | #if 0 | |
778 | { | |
779 | int i; | |
780 | uint8_t *ptr; | |
781 | ||
782 | fprintf(logfile, " code="); | |
783 | ptr = (uint8_t *)env->pc; | |
784 | for(i = 0; i < 16; i++) { | |
785 | fprintf(logfile, " %02x", ldub(ptr + i)); | |
786 | } | |
787 | fprintf(logfile, "\n"); | |
788 | } | |
789 | #endif | |
790 | count++; | |
791 | } | |
792 | #endif | |
793 | #if !defined(CONFIG_USER_ONLY) | |
83469015 | 794 | if (env->tl == MAXTL) { |
c68ea704 | 795 | cpu_abort(env, "Trap 0x%04x while trap level is MAXTL, Error state", env->exception_index); |
ee5bbe38 FB |
796 | return; |
797 | } | |
798 | #endif | |
799 | env->tstate[env->tl] = ((uint64_t)GET_CCR(env) << 32) | ((env->asi & 0xff) << 24) | | |
800 | ((env->pstate & 0xfff) << 8) | (env->cwp & 0xff); | |
801 | env->tpc[env->tl] = env->pc; | |
802 | env->tnpc[env->tl] = env->npc; | |
803 | env->tt[env->tl] = intno; | |
83469015 FB |
804 | env->pstate = PS_PEF | PS_PRIV | PS_AG; |
805 | env->tbr &= ~0x7fffULL; | |
806 | env->tbr |= ((env->tl > 1) ? 1 << 14 : 0) | (intno << 5); | |
807 | if (env->tl < MAXTL - 1) { | |
808 | env->tl++; | |
809 | } else { | |
810 | env->pstate |= PS_RED; | |
811 | if (env->tl != MAXTL) | |
812 | env->tl++; | |
813 | } | |
ee5bbe38 FB |
814 | env->pc = env->tbr; |
815 | env->npc = env->pc + 4; | |
816 | env->exception_index = 0; | |
817 | } | |
818 | #else | |
819 | void do_interrupt(int intno) | |
820 | { | |
821 | int cwp; | |
822 | ||
823 | #ifdef DEBUG_PCALL | |
824 | if (loglevel & CPU_LOG_INT) { | |
825 | static int count; | |
826 | fprintf(logfile, "%6d: v=%02x pc=%08x npc=%08x SP=%08x\n", | |
827 | count, intno, | |
828 | env->pc, | |
829 | env->npc, env->regwptr[6]); | |
830 | cpu_dump_state(env, logfile, fprintf, 0); | |
831 | #if 0 | |
832 | { | |
833 | int i; | |
834 | uint8_t *ptr; | |
835 | ||
836 | fprintf(logfile, " code="); | |
837 | ptr = (uint8_t *)env->pc; | |
838 | for(i = 0; i < 16; i++) { | |
839 | fprintf(logfile, " %02x", ldub(ptr + i)); | |
840 | } | |
841 | fprintf(logfile, "\n"); | |
842 | } | |
843 | #endif | |
844 | count++; | |
845 | } | |
846 | #endif | |
847 | #if !defined(CONFIG_USER_ONLY) | |
848 | if (env->psret == 0) { | |
c68ea704 | 849 | cpu_abort(env, "Trap 0x%02x while interrupts disabled, Error state", env->exception_index); |
ee5bbe38 FB |
850 | return; |
851 | } | |
852 | #endif | |
853 | env->psret = 0; | |
854 | cwp = (env->cwp - 1) & (NWINDOWS - 1); | |
855 | set_cwp(cwp); | |
856 | env->regwptr[9] = env->pc; | |
857 | env->regwptr[10] = env->npc; | |
858 | env->psrps = env->psrs; | |
859 | env->psrs = 1; | |
860 | env->tbr = (env->tbr & TBR_BASE_MASK) | (intno << 4); | |
861 | env->pc = env->tbr; | |
862 | env->npc = env->pc + 4; | |
863 | env->exception_index = 0; | |
864 | } | |
865 | #endif | |
866 | ||
867 | #if !defined(CONFIG_USER_ONLY) | |
868 | ||
869 | #define MMUSUFFIX _mmu | |
870 | #define GETPC() (__builtin_return_address(0)) | |
871 | ||
872 | #define SHIFT 0 | |
873 | #include "softmmu_template.h" | |
874 | ||
875 | #define SHIFT 1 | |
876 | #include "softmmu_template.h" | |
877 | ||
878 | #define SHIFT 2 | |
879 | #include "softmmu_template.h" | |
880 | ||
881 | #define SHIFT 3 | |
882 | #include "softmmu_template.h" | |
883 | ||
884 | ||
885 | /* try to fill the TLB and return an exception if error. If retaddr is | |
886 | NULL, it means that the function was called in C code (i.e. not | |
887 | from generated code or from helper.c) */ | |
888 | /* XXX: fix it to restore all registers */ | |
889 | void tlb_fill(target_ulong addr, int is_write, int is_user, void *retaddr) | |
890 | { | |
891 | TranslationBlock *tb; | |
892 | int ret; | |
893 | unsigned long pc; | |
894 | CPUState *saved_env; | |
895 | ||
896 | /* XXX: hack to restore env in all cases, even if not called from | |
897 | generated code */ | |
898 | saved_env = env; | |
899 | env = cpu_single_env; | |
900 | ||
901 | ret = cpu_sparc_handle_mmu_fault(env, addr, is_write, is_user, 1); | |
902 | if (ret) { | |
903 | if (retaddr) { | |
904 | /* now we have a real cpu fault */ | |
905 | pc = (unsigned long)retaddr; | |
906 | tb = tb_find_pc(pc); | |
907 | if (tb) { | |
908 | /* the PC is inside the translated code. It means that we have | |
909 | a virtual CPU fault */ | |
910 | cpu_restore_state(tb, env, pc, (void *)T2); | |
911 | } | |
912 | } | |
913 | cpu_loop_exit(); | |
914 | } | |
915 | env = saved_env; | |
916 | } | |
917 | ||
918 | #endif |