1 | /* |
2 | * M68K helper routines |
3 | * |
4 | * Copyright (c) 2007 CodeSourcery |
5 | * |
6 | * This library is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU Lesser General Public |
8 | * License as published by the Free Software Foundation; either |
9 | * version 2.1 of the License, or (at your option) any later version. |
10 | * |
11 | * This library is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | * Lesser General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU Lesser General Public |
17 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
18 | */ |
19 | #include "qemu/osdep.h" |
20 | #include "cpu.h" |
21 | #include "exec/helper-proto.h" |
22 | #include "exec/exec-all.h" |
23 | #include "exec/cpu_ldst.h" |
24 | #include "hw/semihosting/semihost.h" |
25 | |
26 | #if defined(CONFIG_USER_ONLY) |
27 | |
28 | void m68k_cpu_do_interrupt(CPUState *cs) |
29 | { |
30 | cs->exception_index = -1; |
31 | } |
32 | |
33 | static inline void do_interrupt_m68k_hardirq(CPUM68KState *env) |
34 | { |
35 | } |
36 | |
37 | #else |
38 | |
39 | static void cf_rte(CPUM68KState *env) |
40 | { |
41 | uint32_t sp; |
42 | uint32_t fmt; |
43 | |
44 | sp = env->aregs[7]; |
45 | fmt = cpu_ldl_kernel(env, sp); |
46 | env->pc = cpu_ldl_kernel(env, sp + 4); |
47 | sp |= (fmt >> 28) & 3; |
48 | env->aregs[7] = sp + 8; |
49 | |
50 | cpu_m68k_set_sr(env, fmt); |
51 | } |
52 | |
53 | static void m68k_rte(CPUM68KState *env) |
54 | { |
55 | uint32_t sp; |
56 | uint16_t fmt; |
57 | uint16_t sr; |
58 | |
59 | sp = env->aregs[7]; |
60 | throwaway: |
61 | sr = cpu_lduw_kernel(env, sp); |
62 | sp += 2; |
63 | env->pc = cpu_ldl_kernel(env, sp); |
64 | sp += 4; |
65 | if (m68k_feature(env, M68K_FEATURE_QUAD_MULDIV)) { |
66 | /* all except 68000 */ |
67 | fmt = cpu_lduw_kernel(env, sp); |
68 | sp += 2; |
69 | switch (fmt >> 12) { |
70 | case 0: |
71 | break; |
72 | case 1: |
73 | env->aregs[7] = sp; |
74 | cpu_m68k_set_sr(env, sr); |
75 | goto throwaway; |
76 | case 2: |
77 | case 3: |
78 | sp += 4; |
79 | break; |
80 | case 4: |
81 | sp += 8; |
82 | break; |
83 | case 7: |
84 | sp += 52; |
85 | break; |
86 | } |
87 | } |
88 | env->aregs[7] = sp; |
89 | cpu_m68k_set_sr(env, sr); |
90 | } |
91 | |
92 | static const char *m68k_exception_name(int index) |
93 | { |
94 | switch (index) { |
95 | case EXCP_ACCESS: |
96 | return "Access Fault" ; |
97 | case EXCP_ADDRESS: |
98 | return "Address Error" ; |
99 | case EXCP_ILLEGAL: |
100 | return "Illegal Instruction" ; |
101 | case EXCP_DIV0: |
102 | return "Divide by Zero" ; |
103 | case EXCP_CHK: |
104 | return "CHK/CHK2" ; |
105 | case EXCP_TRAPCC: |
106 | return "FTRAPcc, TRAPcc, TRAPV" ; |
107 | case EXCP_PRIVILEGE: |
108 | return "Privilege Violation" ; |
109 | case EXCP_TRACE: |
110 | return "Trace" ; |
111 | case EXCP_LINEA: |
112 | return "A-Line" ; |
113 | case EXCP_LINEF: |
114 | return "F-Line" ; |
115 | case EXCP_DEBEGBP: /* 68020/030 only */ |
116 | return "Copro Protocol Violation" ; |
117 | case EXCP_FORMAT: |
118 | return "Format Error" ; |
119 | case EXCP_UNINITIALIZED: |
120 | return "Unitialized Interruot" ; |
121 | case EXCP_SPURIOUS: |
122 | return "Spurious Interrupt" ; |
123 | case EXCP_INT_LEVEL_1: |
124 | return "Level 1 Interrupt" ; |
125 | case EXCP_INT_LEVEL_1 + 1: |
126 | return "Level 2 Interrupt" ; |
127 | case EXCP_INT_LEVEL_1 + 2: |
128 | return "Level 3 Interrupt" ; |
129 | case EXCP_INT_LEVEL_1 + 3: |
130 | return "Level 4 Interrupt" ; |
131 | case EXCP_INT_LEVEL_1 + 4: |
132 | return "Level 5 Interrupt" ; |
133 | case EXCP_INT_LEVEL_1 + 5: |
134 | return "Level 6 Interrupt" ; |
135 | case EXCP_INT_LEVEL_1 + 6: |
136 | return "Level 7 Interrupt" ; |
137 | case EXCP_TRAP0: |
138 | return "TRAP #0" ; |
139 | case EXCP_TRAP0 + 1: |
140 | return "TRAP #1" ; |
141 | case EXCP_TRAP0 + 2: |
142 | return "TRAP #2" ; |
143 | case EXCP_TRAP0 + 3: |
144 | return "TRAP #3" ; |
145 | case EXCP_TRAP0 + 4: |
146 | return "TRAP #4" ; |
147 | case EXCP_TRAP0 + 5: |
148 | return "TRAP #5" ; |
149 | case EXCP_TRAP0 + 6: |
150 | return "TRAP #6" ; |
151 | case EXCP_TRAP0 + 7: |
152 | return "TRAP #7" ; |
153 | case EXCP_TRAP0 + 8: |
154 | return "TRAP #8" ; |
155 | case EXCP_TRAP0 + 9: |
156 | return "TRAP #9" ; |
157 | case EXCP_TRAP0 + 10: |
158 | return "TRAP #10" ; |
159 | case EXCP_TRAP0 + 11: |
160 | return "TRAP #11" ; |
161 | case EXCP_TRAP0 + 12: |
162 | return "TRAP #12" ; |
163 | case EXCP_TRAP0 + 13: |
164 | return "TRAP #13" ; |
165 | case EXCP_TRAP0 + 14: |
166 | return "TRAP #14" ; |
167 | case EXCP_TRAP0 + 15: |
168 | return "TRAP #15" ; |
169 | case EXCP_FP_BSUN: |
170 | return "FP Branch/Set on unordered condition" ; |
171 | case EXCP_FP_INEX: |
172 | return "FP Inexact Result" ; |
173 | case EXCP_FP_DZ: |
174 | return "FP Divide by Zero" ; |
175 | case EXCP_FP_UNFL: |
176 | return "FP Underflow" ; |
177 | case EXCP_FP_OPERR: |
178 | return "FP Operand Error" ; |
179 | case EXCP_FP_OVFL: |
180 | return "FP Overflow" ; |
181 | case EXCP_FP_SNAN: |
182 | return "FP Signaling NAN" ; |
183 | case EXCP_FP_UNIMP: |
184 | return "FP Unimplemented Data Type" ; |
185 | case EXCP_MMU_CONF: /* 68030/68851 only */ |
186 | return "MMU Configuration Error" ; |
187 | case EXCP_MMU_ILLEGAL: /* 68851 only */ |
188 | return "MMU Illegal Operation" ; |
189 | case EXCP_MMU_ACCESS: /* 68851 only */ |
190 | return "MMU Access Level Violation" ; |
191 | case 64 ... 255: |
192 | return "User Defined Vector" ; |
193 | } |
194 | return "Unassigned" ; |
195 | } |
196 | |
197 | static void cf_interrupt_all(CPUM68KState *env, int is_hw) |
198 | { |
199 | CPUState *cs = env_cpu(env); |
200 | uint32_t sp; |
201 | uint32_t sr; |
202 | uint32_t fmt; |
203 | uint32_t retaddr; |
204 | uint32_t vector; |
205 | |
206 | fmt = 0; |
207 | retaddr = env->pc; |
208 | |
209 | if (!is_hw) { |
210 | switch (cs->exception_index) { |
211 | case EXCP_RTE: |
212 | /* Return from an exception. */ |
213 | cf_rte(env); |
214 | return; |
215 | case EXCP_HALT_INSN: |
216 | if (semihosting_enabled() |
217 | && (env->sr & SR_S) != 0 |
218 | && (env->pc & 3) == 0 |
219 | && cpu_lduw_code(env, env->pc - 4) == 0x4e71 |
220 | && cpu_ldl_code(env, env->pc) == 0x4e7bf000) { |
221 | env->pc += 4; |
222 | do_m68k_semihosting(env, env->dregs[0]); |
223 | return; |
224 | } |
225 | cs->halted = 1; |
226 | cs->exception_index = EXCP_HLT; |
227 | cpu_loop_exit(cs); |
228 | return; |
229 | } |
230 | if (cs->exception_index >= EXCP_TRAP0 |
231 | && cs->exception_index <= EXCP_TRAP15) { |
232 | /* Move the PC after the trap instruction. */ |
233 | retaddr += 2; |
234 | } |
235 | } |
236 | |
237 | vector = cs->exception_index << 2; |
238 | |
239 | sr = env->sr | cpu_m68k_get_ccr(env); |
240 | if (qemu_loglevel_mask(CPU_LOG_INT)) { |
241 | static int count; |
242 | qemu_log("INT %6d: %s(%#x) pc=%08x sp=%08x sr=%04x\n" , |
243 | ++count, m68k_exception_name(cs->exception_index), |
244 | vector, env->pc, env->aregs[7], sr); |
245 | } |
246 | |
247 | fmt |= 0x40000000; |
248 | fmt |= vector << 16; |
249 | fmt |= sr; |
250 | |
251 | env->sr |= SR_S; |
252 | if (is_hw) { |
253 | env->sr = (env->sr & ~SR_I) | (env->pending_level << SR_I_SHIFT); |
254 | env->sr &= ~SR_M; |
255 | } |
256 | m68k_switch_sp(env); |
257 | sp = env->aregs[7]; |
258 | fmt |= (sp & 3) << 28; |
259 | |
260 | /* ??? This could cause MMU faults. */ |
261 | sp &= ~3; |
262 | sp -= 4; |
263 | cpu_stl_kernel(env, sp, retaddr); |
264 | sp -= 4; |
265 | cpu_stl_kernel(env, sp, fmt); |
266 | env->aregs[7] = sp; |
267 | /* Jump to vector. */ |
268 | env->pc = cpu_ldl_kernel(env, env->vbr + vector); |
269 | } |
270 | |
271 | static inline void do_stack_frame(CPUM68KState *env, uint32_t *sp, |
272 | uint16_t format, uint16_t sr, |
273 | uint32_t addr, uint32_t retaddr) |
274 | { |
275 | if (m68k_feature(env, M68K_FEATURE_QUAD_MULDIV)) { |
276 | /* all except 68000 */ |
277 | CPUState *cs = env_cpu(env); |
278 | switch (format) { |
279 | case 4: |
280 | *sp -= 4; |
281 | cpu_stl_kernel(env, *sp, env->pc); |
282 | *sp -= 4; |
283 | cpu_stl_kernel(env, *sp, addr); |
284 | break; |
285 | case 3: |
286 | case 2: |
287 | *sp -= 4; |
288 | cpu_stl_kernel(env, *sp, addr); |
289 | break; |
290 | } |
291 | *sp -= 2; |
292 | cpu_stw_kernel(env, *sp, (format << 12) + (cs->exception_index << 2)); |
293 | } |
294 | *sp -= 4; |
295 | cpu_stl_kernel(env, *sp, retaddr); |
296 | *sp -= 2; |
297 | cpu_stw_kernel(env, *sp, sr); |
298 | } |
299 | |
300 | static void m68k_interrupt_all(CPUM68KState *env, int is_hw) |
301 | { |
302 | CPUState *cs = env_cpu(env); |
303 | uint32_t sp; |
304 | uint32_t retaddr; |
305 | uint32_t vector; |
306 | uint16_t sr, oldsr; |
307 | |
308 | retaddr = env->pc; |
309 | |
310 | if (!is_hw) { |
311 | switch (cs->exception_index) { |
312 | case EXCP_RTE: |
313 | /* Return from an exception. */ |
314 | m68k_rte(env); |
315 | return; |
316 | case EXCP_TRAP0 ... EXCP_TRAP15: |
317 | /* Move the PC after the trap instruction. */ |
318 | retaddr += 2; |
319 | break; |
320 | } |
321 | } |
322 | |
323 | vector = cs->exception_index << 2; |
324 | |
325 | sr = env->sr | cpu_m68k_get_ccr(env); |
326 | if (qemu_loglevel_mask(CPU_LOG_INT)) { |
327 | static int count; |
328 | qemu_log("INT %6d: %s(%#x) pc=%08x sp=%08x sr=%04x\n" , |
329 | ++count, m68k_exception_name(cs->exception_index), |
330 | vector, env->pc, env->aregs[7], sr); |
331 | } |
332 | |
333 | /* |
334 | * MC68040UM/AD, chapter 9.3.10 |
335 | */ |
336 | |
337 | /* "the processor first make an internal copy" */ |
338 | oldsr = sr; |
339 | /* "set the mode to supervisor" */ |
340 | sr |= SR_S; |
341 | /* "suppress tracing" */ |
342 | sr &= ~SR_T; |
343 | /* "sets the processor interrupt mask" */ |
344 | if (is_hw) { |
345 | sr |= (env->sr & ~SR_I) | (env->pending_level << SR_I_SHIFT); |
346 | } |
347 | cpu_m68k_set_sr(env, sr); |
348 | sp = env->aregs[7]; |
349 | |
350 | sp &= ~1; |
351 | if (cs->exception_index == EXCP_ACCESS) { |
352 | if (env->mmu.fault) { |
353 | cpu_abort(cs, "DOUBLE MMU FAULT\n" ); |
354 | } |
355 | env->mmu.fault = true; |
356 | sp -= 4; |
357 | cpu_stl_kernel(env, sp, 0); /* push data 3 */ |
358 | sp -= 4; |
359 | cpu_stl_kernel(env, sp, 0); /* push data 2 */ |
360 | sp -= 4; |
361 | cpu_stl_kernel(env, sp, 0); /* push data 1 */ |
362 | sp -= 4; |
363 | cpu_stl_kernel(env, sp, 0); /* write back 1 / push data 0 */ |
364 | sp -= 4; |
365 | cpu_stl_kernel(env, sp, 0); /* write back 1 address */ |
366 | sp -= 4; |
367 | cpu_stl_kernel(env, sp, 0); /* write back 2 data */ |
368 | sp -= 4; |
369 | cpu_stl_kernel(env, sp, 0); /* write back 2 address */ |
370 | sp -= 4; |
371 | cpu_stl_kernel(env, sp, 0); /* write back 3 data */ |
372 | sp -= 4; |
373 | cpu_stl_kernel(env, sp, env->mmu.ar); /* write back 3 address */ |
374 | sp -= 4; |
375 | cpu_stl_kernel(env, sp, env->mmu.ar); /* fault address */ |
376 | sp -= 2; |
377 | cpu_stw_kernel(env, sp, 0); /* write back 1 status */ |
378 | sp -= 2; |
379 | cpu_stw_kernel(env, sp, 0); /* write back 2 status */ |
380 | sp -= 2; |
381 | cpu_stw_kernel(env, sp, 0); /* write back 3 status */ |
382 | sp -= 2; |
383 | cpu_stw_kernel(env, sp, env->mmu.ssw); /* special status word */ |
384 | sp -= 4; |
385 | cpu_stl_kernel(env, sp, env->mmu.ar); /* effective address */ |
386 | do_stack_frame(env, &sp, 7, oldsr, 0, retaddr); |
387 | env->mmu.fault = false; |
388 | if (qemu_loglevel_mask(CPU_LOG_INT)) { |
389 | qemu_log(" " |
390 | "ssw: %08x ea: %08x sfc: %d dfc: %d\n" , |
391 | env->mmu.ssw, env->mmu.ar, env->sfc, env->dfc); |
392 | } |
393 | } else if (cs->exception_index == EXCP_ADDRESS) { |
394 | do_stack_frame(env, &sp, 2, oldsr, 0, retaddr); |
395 | } else if (cs->exception_index == EXCP_ILLEGAL || |
396 | cs->exception_index == EXCP_DIV0 || |
397 | cs->exception_index == EXCP_CHK || |
398 | cs->exception_index == EXCP_TRAPCC || |
399 | cs->exception_index == EXCP_TRACE) { |
400 | /* FIXME: addr is not only env->pc */ |
401 | do_stack_frame(env, &sp, 2, oldsr, env->pc, retaddr); |
402 | } else if (is_hw && oldsr & SR_M && |
403 | cs->exception_index >= EXCP_SPURIOUS && |
404 | cs->exception_index <= EXCP_INT_LEVEL_7) { |
405 | do_stack_frame(env, &sp, 0, oldsr, 0, retaddr); |
406 | oldsr = sr; |
407 | env->aregs[7] = sp; |
408 | cpu_m68k_set_sr(env, sr &= ~SR_M); |
409 | sp = env->aregs[7] & ~1; |
410 | do_stack_frame(env, &sp, 1, oldsr, 0, retaddr); |
411 | } else { |
412 | do_stack_frame(env, &sp, 0, oldsr, 0, retaddr); |
413 | } |
414 | |
415 | env->aregs[7] = sp; |
416 | /* Jump to vector. */ |
417 | env->pc = cpu_ldl_kernel(env, env->vbr + vector); |
418 | } |
419 | |
420 | static void do_interrupt_all(CPUM68KState *env, int is_hw) |
421 | { |
422 | if (m68k_feature(env, M68K_FEATURE_M68000)) { |
423 | m68k_interrupt_all(env, is_hw); |
424 | return; |
425 | } |
426 | cf_interrupt_all(env, is_hw); |
427 | } |
428 | |
429 | void m68k_cpu_do_interrupt(CPUState *cs) |
430 | { |
431 | M68kCPU *cpu = M68K_CPU(cs); |
432 | CPUM68KState *env = &cpu->env; |
433 | |
434 | do_interrupt_all(env, 0); |
435 | } |
436 | |
437 | static inline void do_interrupt_m68k_hardirq(CPUM68KState *env) |
438 | { |
439 | do_interrupt_all(env, 1); |
440 | } |
441 | |
442 | void m68k_cpu_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr, |
443 | unsigned size, MMUAccessType access_type, |
444 | int mmu_idx, MemTxAttrs attrs, |
445 | MemTxResult response, uintptr_t retaddr) |
446 | { |
447 | M68kCPU *cpu = M68K_CPU(cs); |
448 | CPUM68KState *env = &cpu->env; |
449 | |
450 | cpu_restore_state(cs, retaddr, true); |
451 | |
452 | if (m68k_feature(env, M68K_FEATURE_M68040)) { |
453 | env->mmu.mmusr = 0; |
454 | env->mmu.ssw |= M68K_ATC_040; |
455 | /* FIXME: manage MMU table access error */ |
456 | env->mmu.ssw &= ~M68K_TM_040; |
457 | if (env->sr & SR_S) { /* SUPERVISOR */ |
458 | env->mmu.ssw |= M68K_TM_040_SUPER; |
459 | } |
460 | if (access_type == MMU_INST_FETCH) { /* instruction or data */ |
461 | env->mmu.ssw |= M68K_TM_040_CODE; |
462 | } else { |
463 | env->mmu.ssw |= M68K_TM_040_DATA; |
464 | } |
465 | env->mmu.ssw &= ~M68K_BA_SIZE_MASK; |
466 | switch (size) { |
467 | case 1: |
468 | env->mmu.ssw |= M68K_BA_SIZE_BYTE; |
469 | break; |
470 | case 2: |
471 | env->mmu.ssw |= M68K_BA_SIZE_WORD; |
472 | break; |
473 | case 4: |
474 | env->mmu.ssw |= M68K_BA_SIZE_LONG; |
475 | break; |
476 | } |
477 | |
478 | if (access_type != MMU_DATA_STORE) { |
479 | env->mmu.ssw |= M68K_RW_040; |
480 | } |
481 | |
482 | env->mmu.ar = addr; |
483 | |
484 | cs->exception_index = EXCP_ACCESS; |
485 | cpu_loop_exit(cs); |
486 | } |
487 | } |
488 | #endif |
489 | |
490 | bool m68k_cpu_exec_interrupt(CPUState *cs, int interrupt_request) |
491 | { |
492 | M68kCPU *cpu = M68K_CPU(cs); |
493 | CPUM68KState *env = &cpu->env; |
494 | |
495 | if (interrupt_request & CPU_INTERRUPT_HARD |
496 | && ((env->sr & SR_I) >> SR_I_SHIFT) < env->pending_level) { |
497 | /* |
498 | * Real hardware gets the interrupt vector via an IACK cycle |
499 | * at this point. Current emulated hardware doesn't rely on |
500 | * this, so we provide/save the vector when the interrupt is |
501 | * first signalled. |
502 | */ |
503 | cs->exception_index = env->pending_vector; |
504 | do_interrupt_m68k_hardirq(env); |
505 | return true; |
506 | } |
507 | return false; |
508 | } |
509 | |
510 | static void raise_exception_ra(CPUM68KState *env, int tt, uintptr_t raddr) |
511 | { |
512 | CPUState *cs = env_cpu(env); |
513 | |
514 | cs->exception_index = tt; |
515 | cpu_loop_exit_restore(cs, raddr); |
516 | } |
517 | |
518 | static void raise_exception(CPUM68KState *env, int tt) |
519 | { |
520 | raise_exception_ra(env, tt, 0); |
521 | } |
522 | |
523 | void HELPER(raise_exception)(CPUM68KState *env, uint32_t tt) |
524 | { |
525 | raise_exception(env, tt); |
526 | } |
527 | |
528 | void HELPER(divuw)(CPUM68KState *env, int destr, uint32_t den) |
529 | { |
530 | uint32_t num = env->dregs[destr]; |
531 | uint32_t quot, rem; |
532 | |
533 | if (den == 0) { |
534 | raise_exception_ra(env, EXCP_DIV0, GETPC()); |
535 | } |
536 | quot = num / den; |
537 | rem = num % den; |
538 | |
539 | env->cc_c = 0; /* always cleared, even if overflow */ |
540 | if (quot > 0xffff) { |
541 | env->cc_v = -1; |
542 | /* |
543 | * real 68040 keeps N and unset Z on overflow, |
544 | * whereas documentation says "undefined" |
545 | */ |
546 | env->cc_z = 1; |
547 | return; |
548 | } |
549 | env->dregs[destr] = deposit32(quot, 16, 16, rem); |
550 | env->cc_z = (int16_t)quot; |
551 | env->cc_n = (int16_t)quot; |
552 | env->cc_v = 0; |
553 | } |
554 | |
555 | void HELPER(divsw)(CPUM68KState *env, int destr, int32_t den) |
556 | { |
557 | int32_t num = env->dregs[destr]; |
558 | uint32_t quot, rem; |
559 | |
560 | if (den == 0) { |
561 | raise_exception_ra(env, EXCP_DIV0, GETPC()); |
562 | } |
563 | quot = num / den; |
564 | rem = num % den; |
565 | |
566 | env->cc_c = 0; /* always cleared, even if overflow */ |
567 | if (quot != (int16_t)quot) { |
568 | env->cc_v = -1; |
569 | /* nothing else is modified */ |
570 | /* |
571 | * real 68040 keeps N and unset Z on overflow, |
572 | * whereas documentation says "undefined" |
573 | */ |
574 | env->cc_z = 1; |
575 | return; |
576 | } |
577 | env->dregs[destr] = deposit32(quot, 16, 16, rem); |
578 | env->cc_z = (int16_t)quot; |
579 | env->cc_n = (int16_t)quot; |
580 | env->cc_v = 0; |
581 | } |
582 | |
583 | void HELPER(divul)(CPUM68KState *env, int numr, int regr, uint32_t den) |
584 | { |
585 | uint32_t num = env->dregs[numr]; |
586 | uint32_t quot, rem; |
587 | |
588 | if (den == 0) { |
589 | raise_exception_ra(env, EXCP_DIV0, GETPC()); |
590 | } |
591 | quot = num / den; |
592 | rem = num % den; |
593 | |
594 | env->cc_c = 0; |
595 | env->cc_z = quot; |
596 | env->cc_n = quot; |
597 | env->cc_v = 0; |
598 | |
599 | if (m68k_feature(env, M68K_FEATURE_CF_ISA_A)) { |
600 | if (numr == regr) { |
601 | env->dregs[numr] = quot; |
602 | } else { |
603 | env->dregs[regr] = rem; |
604 | } |
605 | } else { |
606 | env->dregs[regr] = rem; |
607 | env->dregs[numr] = quot; |
608 | } |
609 | } |
610 | |
611 | void HELPER(divsl)(CPUM68KState *env, int numr, int regr, int32_t den) |
612 | { |
613 | int32_t num = env->dregs[numr]; |
614 | int32_t quot, rem; |
615 | |
616 | if (den == 0) { |
617 | raise_exception_ra(env, EXCP_DIV0, GETPC()); |
618 | } |
619 | quot = num / den; |
620 | rem = num % den; |
621 | |
622 | env->cc_c = 0; |
623 | env->cc_z = quot; |
624 | env->cc_n = quot; |
625 | env->cc_v = 0; |
626 | |
627 | if (m68k_feature(env, M68K_FEATURE_CF_ISA_A)) { |
628 | if (numr == regr) { |
629 | env->dregs[numr] = quot; |
630 | } else { |
631 | env->dregs[regr] = rem; |
632 | } |
633 | } else { |
634 | env->dregs[regr] = rem; |
635 | env->dregs[numr] = quot; |
636 | } |
637 | } |
638 | |
639 | void HELPER(divull)(CPUM68KState *env, int numr, int regr, uint32_t den) |
640 | { |
641 | uint64_t num = deposit64(env->dregs[numr], 32, 32, env->dregs[regr]); |
642 | uint64_t quot; |
643 | uint32_t rem; |
644 | |
645 | if (den == 0) { |
646 | raise_exception_ra(env, EXCP_DIV0, GETPC()); |
647 | } |
648 | quot = num / den; |
649 | rem = num % den; |
650 | |
651 | env->cc_c = 0; /* always cleared, even if overflow */ |
652 | if (quot > 0xffffffffULL) { |
653 | env->cc_v = -1; |
654 | /* |
655 | * real 68040 keeps N and unset Z on overflow, |
656 | * whereas documentation says "undefined" |
657 | */ |
658 | env->cc_z = 1; |
659 | return; |
660 | } |
661 | env->cc_z = quot; |
662 | env->cc_n = quot; |
663 | env->cc_v = 0; |
664 | |
665 | /* |
666 | * If Dq and Dr are the same, the quotient is returned. |
667 | * therefore we set Dq last. |
668 | */ |
669 | |
670 | env->dregs[regr] = rem; |
671 | env->dregs[numr] = quot; |
672 | } |
673 | |
674 | void HELPER(divsll)(CPUM68KState *env, int numr, int regr, int32_t den) |
675 | { |
676 | int64_t num = deposit64(env->dregs[numr], 32, 32, env->dregs[regr]); |
677 | int64_t quot; |
678 | int32_t rem; |
679 | |
680 | if (den == 0) { |
681 | raise_exception_ra(env, EXCP_DIV0, GETPC()); |
682 | } |
683 | quot = num / den; |
684 | rem = num % den; |
685 | |
686 | env->cc_c = 0; /* always cleared, even if overflow */ |
687 | if (quot != (int32_t)quot) { |
688 | env->cc_v = -1; |
689 | /* |
690 | * real 68040 keeps N and unset Z on overflow, |
691 | * whereas documentation says "undefined" |
692 | */ |
693 | env->cc_z = 1; |
694 | return; |
695 | } |
696 | env->cc_z = quot; |
697 | env->cc_n = quot; |
698 | env->cc_v = 0; |
699 | |
700 | /* |
701 | * If Dq and Dr are the same, the quotient is returned. |
702 | * therefore we set Dq last. |
703 | */ |
704 | |
705 | env->dregs[regr] = rem; |
706 | env->dregs[numr] = quot; |
707 | } |
708 | |
709 | /* We're executing in a serial context -- no need to be atomic. */ |
710 | void HELPER(cas2w)(CPUM68KState *env, uint32_t regs, uint32_t a1, uint32_t a2) |
711 | { |
712 | uint32_t Dc1 = extract32(regs, 9, 3); |
713 | uint32_t Dc2 = extract32(regs, 6, 3); |
714 | uint32_t Du1 = extract32(regs, 3, 3); |
715 | uint32_t Du2 = extract32(regs, 0, 3); |
716 | int16_t c1 = env->dregs[Dc1]; |
717 | int16_t c2 = env->dregs[Dc2]; |
718 | int16_t u1 = env->dregs[Du1]; |
719 | int16_t u2 = env->dregs[Du2]; |
720 | int16_t l1, l2; |
721 | uintptr_t ra = GETPC(); |
722 | |
723 | l1 = cpu_lduw_data_ra(env, a1, ra); |
724 | l2 = cpu_lduw_data_ra(env, a2, ra); |
725 | if (l1 == c1 && l2 == c2) { |
726 | cpu_stw_data_ra(env, a1, u1, ra); |
727 | cpu_stw_data_ra(env, a2, u2, ra); |
728 | } |
729 | |
730 | if (c1 != l1) { |
731 | env->cc_n = l1; |
732 | env->cc_v = c1; |
733 | } else { |
734 | env->cc_n = l2; |
735 | env->cc_v = c2; |
736 | } |
737 | env->cc_op = CC_OP_CMPW; |
738 | env->dregs[Dc1] = deposit32(env->dregs[Dc1], 0, 16, l1); |
739 | env->dregs[Dc2] = deposit32(env->dregs[Dc2], 0, 16, l2); |
740 | } |
741 | |
742 | static void do_cas2l(CPUM68KState *env, uint32_t regs, uint32_t a1, uint32_t a2, |
743 | bool parallel) |
744 | { |
745 | uint32_t Dc1 = extract32(regs, 9, 3); |
746 | uint32_t Dc2 = extract32(regs, 6, 3); |
747 | uint32_t Du1 = extract32(regs, 3, 3); |
748 | uint32_t Du2 = extract32(regs, 0, 3); |
749 | uint32_t c1 = env->dregs[Dc1]; |
750 | uint32_t c2 = env->dregs[Dc2]; |
751 | uint32_t u1 = env->dregs[Du1]; |
752 | uint32_t u2 = env->dregs[Du2]; |
753 | uint32_t l1, l2; |
754 | uintptr_t ra = GETPC(); |
755 | #if defined(CONFIG_ATOMIC64) && !defined(CONFIG_USER_ONLY) |
756 | int mmu_idx = cpu_mmu_index(env, 0); |
757 | TCGMemOpIdx oi; |
758 | #endif |
759 | |
760 | if (parallel) { |
761 | /* We're executing in a parallel context -- must be atomic. */ |
762 | #ifdef CONFIG_ATOMIC64 |
763 | uint64_t c, u, l; |
764 | if ((a1 & 7) == 0 && a2 == a1 + 4) { |
765 | c = deposit64(c2, 32, 32, c1); |
766 | u = deposit64(u2, 32, 32, u1); |
767 | #ifdef CONFIG_USER_ONLY |
768 | l = helper_atomic_cmpxchgq_be(env, a1, c, u); |
769 | #else |
770 | oi = make_memop_idx(MO_BEQ, mmu_idx); |
771 | l = helper_atomic_cmpxchgq_be_mmu(env, a1, c, u, oi, ra); |
772 | #endif |
773 | l1 = l >> 32; |
774 | l2 = l; |
775 | } else if ((a2 & 7) == 0 && a1 == a2 + 4) { |
776 | c = deposit64(c1, 32, 32, c2); |
777 | u = deposit64(u1, 32, 32, u2); |
778 | #ifdef CONFIG_USER_ONLY |
779 | l = helper_atomic_cmpxchgq_be(env, a2, c, u); |
780 | #else |
781 | oi = make_memop_idx(MO_BEQ, mmu_idx); |
782 | l = helper_atomic_cmpxchgq_be_mmu(env, a2, c, u, oi, ra); |
783 | #endif |
784 | l2 = l >> 32; |
785 | l1 = l; |
786 | } else |
787 | #endif |
788 | { |
789 | /* Tell the main loop we need to serialize this insn. */ |
790 | cpu_loop_exit_atomic(env_cpu(env), ra); |
791 | } |
792 | } else { |
793 | /* We're executing in a serial context -- no need to be atomic. */ |
794 | l1 = cpu_ldl_data_ra(env, a1, ra); |
795 | l2 = cpu_ldl_data_ra(env, a2, ra); |
796 | if (l1 == c1 && l2 == c2) { |
797 | cpu_stl_data_ra(env, a1, u1, ra); |
798 | cpu_stl_data_ra(env, a2, u2, ra); |
799 | } |
800 | } |
801 | |
802 | if (c1 != l1) { |
803 | env->cc_n = l1; |
804 | env->cc_v = c1; |
805 | } else { |
806 | env->cc_n = l2; |
807 | env->cc_v = c2; |
808 | } |
809 | env->cc_op = CC_OP_CMPL; |
810 | env->dregs[Dc1] = l1; |
811 | env->dregs[Dc2] = l2; |
812 | } |
813 | |
814 | void HELPER(cas2l)(CPUM68KState *env, uint32_t regs, uint32_t a1, uint32_t a2) |
815 | { |
816 | do_cas2l(env, regs, a1, a2, false); |
817 | } |
818 | |
819 | void HELPER(cas2l_parallel)(CPUM68KState *env, uint32_t regs, uint32_t a1, |
820 | uint32_t a2) |
821 | { |
822 | do_cas2l(env, regs, a1, a2, true); |
823 | } |
824 | |
825 | struct bf_data { |
826 | uint32_t addr; |
827 | uint32_t bofs; |
828 | uint32_t blen; |
829 | uint32_t len; |
830 | }; |
831 | |
832 | static struct bf_data bf_prep(uint32_t addr, int32_t ofs, uint32_t len) |
833 | { |
834 | int bofs, blen; |
835 | |
836 | /* Bound length; map 0 to 32. */ |
837 | len = ((len - 1) & 31) + 1; |
838 | |
839 | /* Note that ofs is signed. */ |
840 | addr += ofs / 8; |
841 | bofs = ofs % 8; |
842 | if (bofs < 0) { |
843 | bofs += 8; |
844 | addr -= 1; |
845 | } |
846 | |
847 | /* |
848 | * Compute the number of bytes required (minus one) to |
849 | * satisfy the bitfield. |
850 | */ |
851 | blen = (bofs + len - 1) / 8; |
852 | |
853 | /* |
854 | * Canonicalize the bit offset for data loaded into a 64-bit big-endian |
855 | * word. For the cases where BLEN is not a power of 2, adjust ADDR so |
856 | * that we can use the next power of two sized load without crossing a |
857 | * page boundary, unless the field itself crosses the boundary. |
858 | */ |
859 | switch (blen) { |
860 | case 0: |
861 | bofs += 56; |
862 | break; |
863 | case 1: |
864 | bofs += 48; |
865 | break; |
866 | case 2: |
867 | if (addr & 1) { |
868 | bofs += 8; |
869 | addr -= 1; |
870 | } |
871 | /* fallthru */ |
872 | case 3: |
873 | bofs += 32; |
874 | break; |
875 | case 4: |
876 | if (addr & 3) { |
877 | bofs += 8 * (addr & 3); |
878 | addr &= -4; |
879 | } |
880 | break; |
881 | default: |
882 | g_assert_not_reached(); |
883 | } |
884 | |
885 | return (struct bf_data){ |
886 | .addr = addr, |
887 | .bofs = bofs, |
888 | .blen = blen, |
889 | .len = len, |
890 | }; |
891 | } |
892 | |
893 | static uint64_t bf_load(CPUM68KState *env, uint32_t addr, int blen, |
894 | uintptr_t ra) |
895 | { |
896 | switch (blen) { |
897 | case 0: |
898 | return cpu_ldub_data_ra(env, addr, ra); |
899 | case 1: |
900 | return cpu_lduw_data_ra(env, addr, ra); |
901 | case 2: |
902 | case 3: |
903 | return cpu_ldl_data_ra(env, addr, ra); |
904 | case 4: |
905 | return cpu_ldq_data_ra(env, addr, ra); |
906 | default: |
907 | g_assert_not_reached(); |
908 | } |
909 | } |
910 | |
911 | static void bf_store(CPUM68KState *env, uint32_t addr, int blen, |
912 | uint64_t data, uintptr_t ra) |
913 | { |
914 | switch (blen) { |
915 | case 0: |
916 | cpu_stb_data_ra(env, addr, data, ra); |
917 | break; |
918 | case 1: |
919 | cpu_stw_data_ra(env, addr, data, ra); |
920 | break; |
921 | case 2: |
922 | case 3: |
923 | cpu_stl_data_ra(env, addr, data, ra); |
924 | break; |
925 | case 4: |
926 | cpu_stq_data_ra(env, addr, data, ra); |
927 | break; |
928 | default: |
929 | g_assert_not_reached(); |
930 | } |
931 | } |
932 | |
933 | uint32_t HELPER(bfexts_mem)(CPUM68KState *env, uint32_t addr, |
934 | int32_t ofs, uint32_t len) |
935 | { |
936 | uintptr_t ra = GETPC(); |
937 | struct bf_data d = bf_prep(addr, ofs, len); |
938 | uint64_t data = bf_load(env, d.addr, d.blen, ra); |
939 | |
940 | return (int64_t)(data << d.bofs) >> (64 - d.len); |
941 | } |
942 | |
943 | uint64_t HELPER(bfextu_mem)(CPUM68KState *env, uint32_t addr, |
944 | int32_t ofs, uint32_t len) |
945 | { |
946 | uintptr_t ra = GETPC(); |
947 | struct bf_data d = bf_prep(addr, ofs, len); |
948 | uint64_t data = bf_load(env, d.addr, d.blen, ra); |
949 | |
950 | /* |
951 | * Put CC_N at the top of the high word; put the zero-extended value |
952 | * at the bottom of the low word. |
953 | */ |
954 | data <<= d.bofs; |
955 | data >>= 64 - d.len; |
956 | data |= data << (64 - d.len); |
957 | |
958 | return data; |
959 | } |
960 | |
961 | uint32_t HELPER(bfins_mem)(CPUM68KState *env, uint32_t addr, uint32_t val, |
962 | int32_t ofs, uint32_t len) |
963 | { |
964 | uintptr_t ra = GETPC(); |
965 | struct bf_data d = bf_prep(addr, ofs, len); |
966 | uint64_t data = bf_load(env, d.addr, d.blen, ra); |
967 | uint64_t mask = -1ull << (64 - d.len) >> d.bofs; |
968 | |
969 | data = (data & ~mask) | (((uint64_t)val << (64 - d.len)) >> d.bofs); |
970 | |
971 | bf_store(env, d.addr, d.blen, data, ra); |
972 | |
973 | /* The field at the top of the word is also CC_N for CC_OP_LOGIC. */ |
974 | return val << (32 - d.len); |
975 | } |
976 | |
977 | uint32_t HELPER(bfchg_mem)(CPUM68KState *env, uint32_t addr, |
978 | int32_t ofs, uint32_t len) |
979 | { |
980 | uintptr_t ra = GETPC(); |
981 | struct bf_data d = bf_prep(addr, ofs, len); |
982 | uint64_t data = bf_load(env, d.addr, d.blen, ra); |
983 | uint64_t mask = -1ull << (64 - d.len) >> d.bofs; |
984 | |
985 | bf_store(env, d.addr, d.blen, data ^ mask, ra); |
986 | |
987 | return ((data & mask) << d.bofs) >> 32; |
988 | } |
989 | |
990 | uint32_t HELPER(bfclr_mem)(CPUM68KState *env, uint32_t addr, |
991 | int32_t ofs, uint32_t len) |
992 | { |
993 | uintptr_t ra = GETPC(); |
994 | struct bf_data d = bf_prep(addr, ofs, len); |
995 | uint64_t data = bf_load(env, d.addr, d.blen, ra); |
996 | uint64_t mask = -1ull << (64 - d.len) >> d.bofs; |
997 | |
998 | bf_store(env, d.addr, d.blen, data & ~mask, ra); |
999 | |
1000 | return ((data & mask) << d.bofs) >> 32; |
1001 | } |
1002 | |
1003 | uint32_t HELPER(bfset_mem)(CPUM68KState *env, uint32_t addr, |
1004 | int32_t ofs, uint32_t len) |
1005 | { |
1006 | uintptr_t ra = GETPC(); |
1007 | struct bf_data d = bf_prep(addr, ofs, len); |
1008 | uint64_t data = bf_load(env, d.addr, d.blen, ra); |
1009 | uint64_t mask = -1ull << (64 - d.len) >> d.bofs; |
1010 | |
1011 | bf_store(env, d.addr, d.blen, data | mask, ra); |
1012 | |
1013 | return ((data & mask) << d.bofs) >> 32; |
1014 | } |
1015 | |
1016 | uint32_t HELPER(bfffo_reg)(uint32_t n, uint32_t ofs, uint32_t len) |
1017 | { |
1018 | return (n ? clz32(n) : len) + ofs; |
1019 | } |
1020 | |
1021 | uint64_t HELPER(bfffo_mem)(CPUM68KState *env, uint32_t addr, |
1022 | int32_t ofs, uint32_t len) |
1023 | { |
1024 | uintptr_t ra = GETPC(); |
1025 | struct bf_data d = bf_prep(addr, ofs, len); |
1026 | uint64_t data = bf_load(env, d.addr, d.blen, ra); |
1027 | uint64_t mask = -1ull << (64 - d.len) >> d.bofs; |
1028 | uint64_t n = (data & mask) << d.bofs; |
1029 | uint32_t ffo = helper_bfffo_reg(n >> 32, ofs, d.len); |
1030 | |
1031 | /* |
1032 | * Return FFO in the low word and N in the high word. |
1033 | * Note that because of MASK and the shift, the low word |
1034 | * is already zero. |
1035 | */ |
1036 | return n | ffo; |
1037 | } |
1038 | |
1039 | void HELPER(chk)(CPUM68KState *env, int32_t val, int32_t ub) |
1040 | { |
1041 | /* |
1042 | * From the specs: |
1043 | * X: Not affected, C,V,Z: Undefined, |
1044 | * N: Set if val < 0; cleared if val > ub, undefined otherwise |
1045 | * We implement here values found from a real MC68040: |
1046 | * X,V,Z: Not affected |
1047 | * N: Set if val < 0; cleared if val >= 0 |
1048 | * C: if 0 <= ub: set if val < 0 or val > ub, cleared otherwise |
1049 | * if 0 > ub: set if val > ub and val < 0, cleared otherwise |
1050 | */ |
1051 | env->cc_n = val; |
1052 | env->cc_c = 0 <= ub ? val < 0 || val > ub : val > ub && val < 0; |
1053 | |
1054 | if (val < 0 || val > ub) { |
1055 | CPUState *cs = env_cpu(env); |
1056 | |
1057 | /* Recover PC and CC_OP for the beginning of the insn. */ |
1058 | cpu_restore_state(cs, GETPC(), true); |
1059 | |
1060 | /* flags have been modified by gen_flush_flags() */ |
1061 | env->cc_op = CC_OP_FLAGS; |
1062 | /* Adjust PC to end of the insn. */ |
1063 | env->pc += 2; |
1064 | |
1065 | cs->exception_index = EXCP_CHK; |
1066 | cpu_loop_exit(cs); |
1067 | } |
1068 | } |
1069 | |
1070 | void HELPER(chk2)(CPUM68KState *env, int32_t val, int32_t lb, int32_t ub) |
1071 | { |
1072 | /* |
1073 | * From the specs: |
1074 | * X: Not affected, N,V: Undefined, |
1075 | * Z: Set if val is equal to lb or ub |
1076 | * C: Set if val < lb or val > ub, cleared otherwise |
1077 | * We implement here values found from a real MC68040: |
1078 | * X,N,V: Not affected |
1079 | * Z: Set if val is equal to lb or ub |
1080 | * C: if lb <= ub: set if val < lb or val > ub, cleared otherwise |
1081 | * if lb > ub: set if val > ub and val < lb, cleared otherwise |
1082 | */ |
1083 | env->cc_z = val != lb && val != ub; |
1084 | env->cc_c = lb <= ub ? val < lb || val > ub : val > ub && val < lb; |
1085 | |
1086 | if (env->cc_c) { |
1087 | CPUState *cs = env_cpu(env); |
1088 | |
1089 | /* Recover PC and CC_OP for the beginning of the insn. */ |
1090 | cpu_restore_state(cs, GETPC(), true); |
1091 | |
1092 | /* flags have been modified by gen_flush_flags() */ |
1093 | env->cc_op = CC_OP_FLAGS; |
1094 | /* Adjust PC to end of the insn. */ |
1095 | env->pc += 4; |
1096 | |
1097 | cs->exception_index = EXCP_CHK; |
1098 | cpu_loop_exit(cs); |
1099 | } |
1100 | } |
1101 | |