ppc/pnv: set phb4 properties in stk_realize()
[qemu/rayw.git] / target / m68k / op_helper.c
blobacbd4735154e316c359efcb077847787cb73b423
1 /*
2 * M68K helper routines
4 * Copyright (c) 2007 CodeSourcery
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.
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.
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/>.
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 "semihosting/semihost.h"
26 #if !defined(CONFIG_USER_ONLY)
28 static void cf_rte(CPUM68KState *env)
30 uint32_t sp;
31 uint32_t fmt;
33 sp = env->aregs[7];
34 fmt = cpu_ldl_mmuidx_ra(env, sp, MMU_KERNEL_IDX, 0);
35 env->pc = cpu_ldl_mmuidx_ra(env, sp + 4, MMU_KERNEL_IDX, 0);
36 sp |= (fmt >> 28) & 3;
37 env->aregs[7] = sp + 8;
39 cpu_m68k_set_sr(env, fmt);
42 static void m68k_rte(CPUM68KState *env)
44 uint32_t sp;
45 uint16_t fmt;
46 uint16_t sr;
48 sp = env->aregs[7];
49 throwaway:
50 sr = cpu_lduw_mmuidx_ra(env, sp, MMU_KERNEL_IDX, 0);
51 sp += 2;
52 env->pc = cpu_ldl_mmuidx_ra(env, sp, MMU_KERNEL_IDX, 0);
53 sp += 4;
54 if (m68k_feature(env, M68K_FEATURE_QUAD_MULDIV)) {
55 /* all except 68000 */
56 fmt = cpu_lduw_mmuidx_ra(env, sp, MMU_KERNEL_IDX, 0);
57 sp += 2;
58 switch (fmt >> 12) {
59 case 0:
60 break;
61 case 1:
62 env->aregs[7] = sp;
63 cpu_m68k_set_sr(env, sr);
64 goto throwaway;
65 case 2:
66 case 3:
67 sp += 4;
68 break;
69 case 4:
70 sp += 8;
71 break;
72 case 7:
73 sp += 52;
74 break;
77 env->aregs[7] = sp;
78 cpu_m68k_set_sr(env, sr);
81 static const char *m68k_exception_name(int index)
83 switch (index) {
84 case EXCP_ACCESS:
85 return "Access Fault";
86 case EXCP_ADDRESS:
87 return "Address Error";
88 case EXCP_ILLEGAL:
89 return "Illegal Instruction";
90 case EXCP_DIV0:
91 return "Divide by Zero";
92 case EXCP_CHK:
93 return "CHK/CHK2";
94 case EXCP_TRAPCC:
95 return "FTRAPcc, TRAPcc, TRAPV";
96 case EXCP_PRIVILEGE:
97 return "Privilege Violation";
98 case EXCP_TRACE:
99 return "Trace";
100 case EXCP_LINEA:
101 return "A-Line";
102 case EXCP_LINEF:
103 return "F-Line";
104 case EXCP_DEBEGBP: /* 68020/030 only */
105 return "Copro Protocol Violation";
106 case EXCP_FORMAT:
107 return "Format Error";
108 case EXCP_UNINITIALIZED:
109 return "Uninitialized Interrupt";
110 case EXCP_SPURIOUS:
111 return "Spurious Interrupt";
112 case EXCP_INT_LEVEL_1:
113 return "Level 1 Interrupt";
114 case EXCP_INT_LEVEL_1 + 1:
115 return "Level 2 Interrupt";
116 case EXCP_INT_LEVEL_1 + 2:
117 return "Level 3 Interrupt";
118 case EXCP_INT_LEVEL_1 + 3:
119 return "Level 4 Interrupt";
120 case EXCP_INT_LEVEL_1 + 4:
121 return "Level 5 Interrupt";
122 case EXCP_INT_LEVEL_1 + 5:
123 return "Level 6 Interrupt";
124 case EXCP_INT_LEVEL_1 + 6:
125 return "Level 7 Interrupt";
126 case EXCP_TRAP0:
127 return "TRAP #0";
128 case EXCP_TRAP0 + 1:
129 return "TRAP #1";
130 case EXCP_TRAP0 + 2:
131 return "TRAP #2";
132 case EXCP_TRAP0 + 3:
133 return "TRAP #3";
134 case EXCP_TRAP0 + 4:
135 return "TRAP #4";
136 case EXCP_TRAP0 + 5:
137 return "TRAP #5";
138 case EXCP_TRAP0 + 6:
139 return "TRAP #6";
140 case EXCP_TRAP0 + 7:
141 return "TRAP #7";
142 case EXCP_TRAP0 + 8:
143 return "TRAP #8";
144 case EXCP_TRAP0 + 9:
145 return "TRAP #9";
146 case EXCP_TRAP0 + 10:
147 return "TRAP #10";
148 case EXCP_TRAP0 + 11:
149 return "TRAP #11";
150 case EXCP_TRAP0 + 12:
151 return "TRAP #12";
152 case EXCP_TRAP0 + 13:
153 return "TRAP #13";
154 case EXCP_TRAP0 + 14:
155 return "TRAP #14";
156 case EXCP_TRAP0 + 15:
157 return "TRAP #15";
158 case EXCP_FP_BSUN:
159 return "FP Branch/Set on unordered condition";
160 case EXCP_FP_INEX:
161 return "FP Inexact Result";
162 case EXCP_FP_DZ:
163 return "FP Divide by Zero";
164 case EXCP_FP_UNFL:
165 return "FP Underflow";
166 case EXCP_FP_OPERR:
167 return "FP Operand Error";
168 case EXCP_FP_OVFL:
169 return "FP Overflow";
170 case EXCP_FP_SNAN:
171 return "FP Signaling NAN";
172 case EXCP_FP_UNIMP:
173 return "FP Unimplemented Data Type";
174 case EXCP_MMU_CONF: /* 68030/68851 only */
175 return "MMU Configuration Error";
176 case EXCP_MMU_ILLEGAL: /* 68851 only */
177 return "MMU Illegal Operation";
178 case EXCP_MMU_ACCESS: /* 68851 only */
179 return "MMU Access Level Violation";
180 case 64 ... 255:
181 return "User Defined Vector";
183 return "Unassigned";
186 static void cf_interrupt_all(CPUM68KState *env, int is_hw)
188 CPUState *cs = env_cpu(env);
189 uint32_t sp;
190 uint32_t sr;
191 uint32_t fmt;
192 uint32_t retaddr;
193 uint32_t vector;
195 fmt = 0;
196 retaddr = env->pc;
198 if (!is_hw) {
199 switch (cs->exception_index) {
200 case EXCP_RTE:
201 /* Return from an exception. */
202 cf_rte(env);
203 return;
204 case EXCP_HALT_INSN:
205 if (semihosting_enabled()
206 && (env->sr & SR_S) != 0
207 && (env->pc & 3) == 0
208 && cpu_lduw_code(env, env->pc - 4) == 0x4e71
209 && cpu_ldl_code(env, env->pc) == 0x4e7bf000) {
210 env->pc += 4;
211 do_m68k_semihosting(env, env->dregs[0]);
212 return;
214 cs->halted = 1;
215 cs->exception_index = EXCP_HLT;
216 cpu_loop_exit(cs);
217 return;
219 if (cs->exception_index >= EXCP_TRAP0
220 && cs->exception_index <= EXCP_TRAP15) {
221 /* Move the PC after the trap instruction. */
222 retaddr += 2;
226 vector = cs->exception_index << 2;
228 sr = env->sr | cpu_m68k_get_ccr(env);
229 if (qemu_loglevel_mask(CPU_LOG_INT)) {
230 static int count;
231 qemu_log("INT %6d: %s(%#x) pc=%08x sp=%08x sr=%04x\n",
232 ++count, m68k_exception_name(cs->exception_index),
233 vector, env->pc, env->aregs[7], sr);
236 fmt |= 0x40000000;
237 fmt |= vector << 16;
238 fmt |= sr;
240 env->sr |= SR_S;
241 if (is_hw) {
242 env->sr = (env->sr & ~SR_I) | (env->pending_level << SR_I_SHIFT);
243 env->sr &= ~SR_M;
245 m68k_switch_sp(env);
246 sp = env->aregs[7];
247 fmt |= (sp & 3) << 28;
249 /* ??? This could cause MMU faults. */
250 sp &= ~3;
251 sp -= 4;
252 cpu_stl_mmuidx_ra(env, sp, retaddr, MMU_KERNEL_IDX, 0);
253 sp -= 4;
254 cpu_stl_mmuidx_ra(env, sp, fmt, MMU_KERNEL_IDX, 0);
255 env->aregs[7] = sp;
256 /* Jump to vector. */
257 env->pc = cpu_ldl_mmuidx_ra(env, env->vbr + vector, MMU_KERNEL_IDX, 0);
260 static inline void do_stack_frame(CPUM68KState *env, uint32_t *sp,
261 uint16_t format, uint16_t sr,
262 uint32_t addr, uint32_t retaddr)
264 if (m68k_feature(env, M68K_FEATURE_QUAD_MULDIV)) {
265 /* all except 68000 */
266 CPUState *cs = env_cpu(env);
267 switch (format) {
268 case 4:
269 *sp -= 4;
270 cpu_stl_mmuidx_ra(env, *sp, env->pc, MMU_KERNEL_IDX, 0);
271 *sp -= 4;
272 cpu_stl_mmuidx_ra(env, *sp, addr, MMU_KERNEL_IDX, 0);
273 break;
274 case 3:
275 case 2:
276 *sp -= 4;
277 cpu_stl_mmuidx_ra(env, *sp, addr, MMU_KERNEL_IDX, 0);
278 break;
280 *sp -= 2;
281 cpu_stw_mmuidx_ra(env, *sp, (format << 12) + (cs->exception_index << 2),
282 MMU_KERNEL_IDX, 0);
284 *sp -= 4;
285 cpu_stl_mmuidx_ra(env, *sp, retaddr, MMU_KERNEL_IDX, 0);
286 *sp -= 2;
287 cpu_stw_mmuidx_ra(env, *sp, sr, MMU_KERNEL_IDX, 0);
290 static void m68k_interrupt_all(CPUM68KState *env, int is_hw)
292 CPUState *cs = env_cpu(env);
293 uint32_t sp;
294 uint32_t retaddr;
295 uint32_t vector;
296 uint16_t sr, oldsr;
298 retaddr = env->pc;
300 if (!is_hw) {
301 switch (cs->exception_index) {
302 case EXCP_RTE:
303 /* Return from an exception. */
304 m68k_rte(env);
305 return;
306 case EXCP_TRAP0 ... EXCP_TRAP15:
307 /* Move the PC after the trap instruction. */
308 retaddr += 2;
309 break;
313 vector = cs->exception_index << 2;
315 sr = env->sr | cpu_m68k_get_ccr(env);
316 if (qemu_loglevel_mask(CPU_LOG_INT)) {
317 static int count;
318 qemu_log("INT %6d: %s(%#x) pc=%08x sp=%08x sr=%04x\n",
319 ++count, m68k_exception_name(cs->exception_index),
320 vector, env->pc, env->aregs[7], sr);
324 * MC68040UM/AD, chapter 9.3.10
327 /* "the processor first make an internal copy" */
328 oldsr = sr;
329 /* "set the mode to supervisor" */
330 sr |= SR_S;
331 /* "suppress tracing" */
332 sr &= ~SR_T;
333 /* "sets the processor interrupt mask" */
334 if (is_hw) {
335 sr |= (env->sr & ~SR_I) | (env->pending_level << SR_I_SHIFT);
337 cpu_m68k_set_sr(env, sr);
338 sp = env->aregs[7];
340 if (!m68k_feature(env, M68K_FEATURE_UNALIGNED_DATA)) {
341 sp &= ~1;
344 if (cs->exception_index == EXCP_ACCESS) {
345 if (env->mmu.fault) {
346 cpu_abort(cs, "DOUBLE MMU FAULT\n");
348 env->mmu.fault = true;
349 /* push data 3 */
350 sp -= 4;
351 cpu_stl_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
352 /* push data 2 */
353 sp -= 4;
354 cpu_stl_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
355 /* push data 1 */
356 sp -= 4;
357 cpu_stl_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
358 /* write back 1 / push data 0 */
359 sp -= 4;
360 cpu_stl_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
361 /* write back 1 address */
362 sp -= 4;
363 cpu_stl_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
364 /* write back 2 data */
365 sp -= 4;
366 cpu_stl_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
367 /* write back 2 address */
368 sp -= 4;
369 cpu_stl_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
370 /* write back 3 data */
371 sp -= 4;
372 cpu_stl_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
373 /* write back 3 address */
374 sp -= 4;
375 cpu_stl_mmuidx_ra(env, sp, env->mmu.ar, MMU_KERNEL_IDX, 0);
376 /* fault address */
377 sp -= 4;
378 cpu_stl_mmuidx_ra(env, sp, env->mmu.ar, MMU_KERNEL_IDX, 0);
379 /* write back 1 status */
380 sp -= 2;
381 cpu_stw_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
382 /* write back 2 status */
383 sp -= 2;
384 cpu_stw_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
385 /* write back 3 status */
386 sp -= 2;
387 cpu_stw_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
388 /* special status word */
389 sp -= 2;
390 cpu_stw_mmuidx_ra(env, sp, env->mmu.ssw, MMU_KERNEL_IDX, 0);
391 /* effective address */
392 sp -= 4;
393 cpu_stl_mmuidx_ra(env, sp, env->mmu.ar, MMU_KERNEL_IDX, 0);
395 do_stack_frame(env, &sp, 7, oldsr, 0, retaddr);
396 env->mmu.fault = false;
397 if (qemu_loglevel_mask(CPU_LOG_INT)) {
398 qemu_log(" "
399 "ssw: %08x ea: %08x sfc: %d dfc: %d\n",
400 env->mmu.ssw, env->mmu.ar, env->sfc, env->dfc);
402 } else if (cs->exception_index == EXCP_ADDRESS) {
403 do_stack_frame(env, &sp, 2, oldsr, 0, retaddr);
404 } else if (cs->exception_index == EXCP_ILLEGAL ||
405 cs->exception_index == EXCP_DIV0 ||
406 cs->exception_index == EXCP_CHK ||
407 cs->exception_index == EXCP_TRAPCC ||
408 cs->exception_index == EXCP_TRACE) {
409 /* FIXME: addr is not only env->pc */
410 do_stack_frame(env, &sp, 2, oldsr, env->pc, retaddr);
411 } else if (is_hw && oldsr & SR_M &&
412 cs->exception_index >= EXCP_SPURIOUS &&
413 cs->exception_index <= EXCP_INT_LEVEL_7) {
414 do_stack_frame(env, &sp, 0, oldsr, 0, retaddr);
415 oldsr = sr;
416 env->aregs[7] = sp;
417 cpu_m68k_set_sr(env, sr &= ~SR_M);
418 sp = env->aregs[7];
419 if (!m68k_feature(env, M68K_FEATURE_UNALIGNED_DATA)) {
420 sp &= ~1;
422 do_stack_frame(env, &sp, 1, oldsr, 0, retaddr);
423 } else {
424 do_stack_frame(env, &sp, 0, oldsr, 0, retaddr);
427 env->aregs[7] = sp;
428 /* Jump to vector. */
429 env->pc = cpu_ldl_mmuidx_ra(env, env->vbr + vector, MMU_KERNEL_IDX, 0);
432 static void do_interrupt_all(CPUM68KState *env, int is_hw)
434 if (m68k_feature(env, M68K_FEATURE_M68000)) {
435 m68k_interrupt_all(env, is_hw);
436 return;
438 cf_interrupt_all(env, is_hw);
441 void m68k_cpu_do_interrupt(CPUState *cs)
443 M68kCPU *cpu = M68K_CPU(cs);
444 CPUM68KState *env = &cpu->env;
446 do_interrupt_all(env, 0);
449 static inline void do_interrupt_m68k_hardirq(CPUM68KState *env)
451 do_interrupt_all(env, 1);
454 void m68k_cpu_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr,
455 unsigned size, MMUAccessType access_type,
456 int mmu_idx, MemTxAttrs attrs,
457 MemTxResult response, uintptr_t retaddr)
459 M68kCPU *cpu = M68K_CPU(cs);
460 CPUM68KState *env = &cpu->env;
462 cpu_restore_state(cs, retaddr, true);
464 if (m68k_feature(env, M68K_FEATURE_M68040)) {
465 env->mmu.mmusr = 0;
468 * According to the MC68040 users manual the ATC bit of the SSW is
469 * used to distinguish between ATC faults and physical bus errors.
470 * In the case of a bus error e.g. during nubus read from an empty
471 * slot this bit should not be set
473 if (response != MEMTX_DECODE_ERROR) {
474 env->mmu.ssw |= M68K_ATC_040;
477 /* FIXME: manage MMU table access error */
478 env->mmu.ssw &= ~M68K_TM_040;
479 if (env->sr & SR_S) { /* SUPERVISOR */
480 env->mmu.ssw |= M68K_TM_040_SUPER;
482 if (access_type == MMU_INST_FETCH) { /* instruction or data */
483 env->mmu.ssw |= M68K_TM_040_CODE;
484 } else {
485 env->mmu.ssw |= M68K_TM_040_DATA;
487 env->mmu.ssw &= ~M68K_BA_SIZE_MASK;
488 switch (size) {
489 case 1:
490 env->mmu.ssw |= M68K_BA_SIZE_BYTE;
491 break;
492 case 2:
493 env->mmu.ssw |= M68K_BA_SIZE_WORD;
494 break;
495 case 4:
496 env->mmu.ssw |= M68K_BA_SIZE_LONG;
497 break;
500 if (access_type != MMU_DATA_STORE) {
501 env->mmu.ssw |= M68K_RW_040;
504 env->mmu.ar = addr;
506 cs->exception_index = EXCP_ACCESS;
507 cpu_loop_exit(cs);
511 bool m68k_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
513 M68kCPU *cpu = M68K_CPU(cs);
514 CPUM68KState *env = &cpu->env;
516 if (interrupt_request & CPU_INTERRUPT_HARD
517 && ((env->sr & SR_I) >> SR_I_SHIFT) < env->pending_level) {
519 * Real hardware gets the interrupt vector via an IACK cycle
520 * at this point. Current emulated hardware doesn't rely on
521 * this, so we provide/save the vector when the interrupt is
522 * first signalled.
524 cs->exception_index = env->pending_vector;
525 do_interrupt_m68k_hardirq(env);
526 return true;
528 return false;
531 #endif /* !CONFIG_USER_ONLY */
533 static void raise_exception_ra(CPUM68KState *env, int tt, uintptr_t raddr)
535 CPUState *cs = env_cpu(env);
537 cs->exception_index = tt;
538 cpu_loop_exit_restore(cs, raddr);
541 static void raise_exception(CPUM68KState *env, int tt)
543 raise_exception_ra(env, tt, 0);
546 void HELPER(raise_exception)(CPUM68KState *env, uint32_t tt)
548 raise_exception(env, tt);
551 void HELPER(divuw)(CPUM68KState *env, int destr, uint32_t den)
553 uint32_t num = env->dregs[destr];
554 uint32_t quot, rem;
556 if (den == 0) {
557 raise_exception_ra(env, EXCP_DIV0, GETPC());
559 quot = num / den;
560 rem = num % den;
562 env->cc_c = 0; /* always cleared, even if overflow */
563 if (quot > 0xffff) {
564 env->cc_v = -1;
566 * real 68040 keeps N and unset Z on overflow,
567 * whereas documentation says "undefined"
569 env->cc_z = 1;
570 return;
572 env->dregs[destr] = deposit32(quot, 16, 16, rem);
573 env->cc_z = (int16_t)quot;
574 env->cc_n = (int16_t)quot;
575 env->cc_v = 0;
578 void HELPER(divsw)(CPUM68KState *env, int destr, int32_t den)
580 int32_t num = env->dregs[destr];
581 uint32_t quot, rem;
583 if (den == 0) {
584 raise_exception_ra(env, EXCP_DIV0, GETPC());
586 quot = num / den;
587 rem = num % den;
589 env->cc_c = 0; /* always cleared, even if overflow */
590 if (quot != (int16_t)quot) {
591 env->cc_v = -1;
592 /* nothing else is modified */
594 * real 68040 keeps N and unset Z on overflow,
595 * whereas documentation says "undefined"
597 env->cc_z = 1;
598 return;
600 env->dregs[destr] = deposit32(quot, 16, 16, rem);
601 env->cc_z = (int16_t)quot;
602 env->cc_n = (int16_t)quot;
603 env->cc_v = 0;
606 void HELPER(divul)(CPUM68KState *env, int numr, int regr, uint32_t den)
608 uint32_t num = env->dregs[numr];
609 uint32_t quot, rem;
611 if (den == 0) {
612 raise_exception_ra(env, EXCP_DIV0, GETPC());
614 quot = num / den;
615 rem = num % den;
617 env->cc_c = 0;
618 env->cc_z = quot;
619 env->cc_n = quot;
620 env->cc_v = 0;
622 if (m68k_feature(env, M68K_FEATURE_CF_ISA_A)) {
623 if (numr == regr) {
624 env->dregs[numr] = quot;
625 } else {
626 env->dregs[regr] = rem;
628 } else {
629 env->dregs[regr] = rem;
630 env->dregs[numr] = quot;
634 void HELPER(divsl)(CPUM68KState *env, int numr, int regr, int32_t den)
636 int32_t num = env->dregs[numr];
637 int32_t quot, rem;
639 if (den == 0) {
640 raise_exception_ra(env, EXCP_DIV0, GETPC());
642 quot = num / den;
643 rem = num % den;
645 env->cc_c = 0;
646 env->cc_z = quot;
647 env->cc_n = quot;
648 env->cc_v = 0;
650 if (m68k_feature(env, M68K_FEATURE_CF_ISA_A)) {
651 if (numr == regr) {
652 env->dregs[numr] = quot;
653 } else {
654 env->dregs[regr] = rem;
656 } else {
657 env->dregs[regr] = rem;
658 env->dregs[numr] = quot;
662 void HELPER(divull)(CPUM68KState *env, int numr, int regr, uint32_t den)
664 uint64_t num = deposit64(env->dregs[numr], 32, 32, env->dregs[regr]);
665 uint64_t quot;
666 uint32_t rem;
668 if (den == 0) {
669 raise_exception_ra(env, EXCP_DIV0, GETPC());
671 quot = num / den;
672 rem = num % den;
674 env->cc_c = 0; /* always cleared, even if overflow */
675 if (quot > 0xffffffffULL) {
676 env->cc_v = -1;
678 * real 68040 keeps N and unset Z on overflow,
679 * whereas documentation says "undefined"
681 env->cc_z = 1;
682 return;
684 env->cc_z = quot;
685 env->cc_n = quot;
686 env->cc_v = 0;
689 * If Dq and Dr are the same, the quotient is returned.
690 * therefore we set Dq last.
693 env->dregs[regr] = rem;
694 env->dregs[numr] = quot;
697 void HELPER(divsll)(CPUM68KState *env, int numr, int regr, int32_t den)
699 int64_t num = deposit64(env->dregs[numr], 32, 32, env->dregs[regr]);
700 int64_t quot;
701 int32_t rem;
703 if (den == 0) {
704 raise_exception_ra(env, EXCP_DIV0, GETPC());
706 quot = num / den;
707 rem = num % den;
709 env->cc_c = 0; /* always cleared, even if overflow */
710 if (quot != (int32_t)quot) {
711 env->cc_v = -1;
713 * real 68040 keeps N and unset Z on overflow,
714 * whereas documentation says "undefined"
716 env->cc_z = 1;
717 return;
719 env->cc_z = quot;
720 env->cc_n = quot;
721 env->cc_v = 0;
724 * If Dq and Dr are the same, the quotient is returned.
725 * therefore we set Dq last.
728 env->dregs[regr] = rem;
729 env->dregs[numr] = quot;
732 /* We're executing in a serial context -- no need to be atomic. */
733 void HELPER(cas2w)(CPUM68KState *env, uint32_t regs, uint32_t a1, uint32_t a2)
735 uint32_t Dc1 = extract32(regs, 9, 3);
736 uint32_t Dc2 = extract32(regs, 6, 3);
737 uint32_t Du1 = extract32(regs, 3, 3);
738 uint32_t Du2 = extract32(regs, 0, 3);
739 int16_t c1 = env->dregs[Dc1];
740 int16_t c2 = env->dregs[Dc2];
741 int16_t u1 = env->dregs[Du1];
742 int16_t u2 = env->dregs[Du2];
743 int16_t l1, l2;
744 uintptr_t ra = GETPC();
746 l1 = cpu_lduw_data_ra(env, a1, ra);
747 l2 = cpu_lduw_data_ra(env, a2, ra);
748 if (l1 == c1 && l2 == c2) {
749 cpu_stw_data_ra(env, a1, u1, ra);
750 cpu_stw_data_ra(env, a2, u2, ra);
753 if (c1 != l1) {
754 env->cc_n = l1;
755 env->cc_v = c1;
756 } else {
757 env->cc_n = l2;
758 env->cc_v = c2;
760 env->cc_op = CC_OP_CMPW;
761 env->dregs[Dc1] = deposit32(env->dregs[Dc1], 0, 16, l1);
762 env->dregs[Dc2] = deposit32(env->dregs[Dc2], 0, 16, l2);
765 static void do_cas2l(CPUM68KState *env, uint32_t regs, uint32_t a1, uint32_t a2,
766 bool parallel)
768 uint32_t Dc1 = extract32(regs, 9, 3);
769 uint32_t Dc2 = extract32(regs, 6, 3);
770 uint32_t Du1 = extract32(regs, 3, 3);
771 uint32_t Du2 = extract32(regs, 0, 3);
772 uint32_t c1 = env->dregs[Dc1];
773 uint32_t c2 = env->dregs[Dc2];
774 uint32_t u1 = env->dregs[Du1];
775 uint32_t u2 = env->dregs[Du2];
776 uint32_t l1, l2;
777 uintptr_t ra = GETPC();
778 #if defined(CONFIG_ATOMIC64)
779 int mmu_idx = cpu_mmu_index(env, 0);
780 MemOpIdx oi = make_memop_idx(MO_BEUQ, mmu_idx);
781 #endif
783 if (parallel) {
784 /* We're executing in a parallel context -- must be atomic. */
785 #ifdef CONFIG_ATOMIC64
786 uint64_t c, u, l;
787 if ((a1 & 7) == 0 && a2 == a1 + 4) {
788 c = deposit64(c2, 32, 32, c1);
789 u = deposit64(u2, 32, 32, u1);
790 l = cpu_atomic_cmpxchgq_be_mmu(env, a1, c, u, oi, ra);
791 l1 = l >> 32;
792 l2 = l;
793 } else if ((a2 & 7) == 0 && a1 == a2 + 4) {
794 c = deposit64(c1, 32, 32, c2);
795 u = deposit64(u1, 32, 32, u2);
796 l = cpu_atomic_cmpxchgq_be_mmu(env, a2, c, u, oi, ra);
797 l2 = l >> 32;
798 l1 = l;
799 } else
800 #endif
802 /* Tell the main loop we need to serialize this insn. */
803 cpu_loop_exit_atomic(env_cpu(env), ra);
805 } else {
806 /* We're executing in a serial context -- no need to be atomic. */
807 l1 = cpu_ldl_data_ra(env, a1, ra);
808 l2 = cpu_ldl_data_ra(env, a2, ra);
809 if (l1 == c1 && l2 == c2) {
810 cpu_stl_data_ra(env, a1, u1, ra);
811 cpu_stl_data_ra(env, a2, u2, ra);
815 if (c1 != l1) {
816 env->cc_n = l1;
817 env->cc_v = c1;
818 } else {
819 env->cc_n = l2;
820 env->cc_v = c2;
822 env->cc_op = CC_OP_CMPL;
823 env->dregs[Dc1] = l1;
824 env->dregs[Dc2] = l2;
827 void HELPER(cas2l)(CPUM68KState *env, uint32_t regs, uint32_t a1, uint32_t a2)
829 do_cas2l(env, regs, a1, a2, false);
832 void HELPER(cas2l_parallel)(CPUM68KState *env, uint32_t regs, uint32_t a1,
833 uint32_t a2)
835 do_cas2l(env, regs, a1, a2, true);
838 struct bf_data {
839 uint32_t addr;
840 uint32_t bofs;
841 uint32_t blen;
842 uint32_t len;
845 static struct bf_data bf_prep(uint32_t addr, int32_t ofs, uint32_t len)
847 int bofs, blen;
849 /* Bound length; map 0 to 32. */
850 len = ((len - 1) & 31) + 1;
852 /* Note that ofs is signed. */
853 addr += ofs / 8;
854 bofs = ofs % 8;
855 if (bofs < 0) {
856 bofs += 8;
857 addr -= 1;
861 * Compute the number of bytes required (minus one) to
862 * satisfy the bitfield.
864 blen = (bofs + len - 1) / 8;
867 * Canonicalize the bit offset for data loaded into a 64-bit big-endian
868 * word. For the cases where BLEN is not a power of 2, adjust ADDR so
869 * that we can use the next power of two sized load without crossing a
870 * page boundary, unless the field itself crosses the boundary.
872 switch (blen) {
873 case 0:
874 bofs += 56;
875 break;
876 case 1:
877 bofs += 48;
878 break;
879 case 2:
880 if (addr & 1) {
881 bofs += 8;
882 addr -= 1;
884 /* fallthru */
885 case 3:
886 bofs += 32;
887 break;
888 case 4:
889 if (addr & 3) {
890 bofs += 8 * (addr & 3);
891 addr &= -4;
893 break;
894 default:
895 g_assert_not_reached();
898 return (struct bf_data){
899 .addr = addr,
900 .bofs = bofs,
901 .blen = blen,
902 .len = len,
906 static uint64_t bf_load(CPUM68KState *env, uint32_t addr, int blen,
907 uintptr_t ra)
909 switch (blen) {
910 case 0:
911 return cpu_ldub_data_ra(env, addr, ra);
912 case 1:
913 return cpu_lduw_data_ra(env, addr, ra);
914 case 2:
915 case 3:
916 return cpu_ldl_data_ra(env, addr, ra);
917 case 4:
918 return cpu_ldq_data_ra(env, addr, ra);
919 default:
920 g_assert_not_reached();
924 static void bf_store(CPUM68KState *env, uint32_t addr, int blen,
925 uint64_t data, uintptr_t ra)
927 switch (blen) {
928 case 0:
929 cpu_stb_data_ra(env, addr, data, ra);
930 break;
931 case 1:
932 cpu_stw_data_ra(env, addr, data, ra);
933 break;
934 case 2:
935 case 3:
936 cpu_stl_data_ra(env, addr, data, ra);
937 break;
938 case 4:
939 cpu_stq_data_ra(env, addr, data, ra);
940 break;
941 default:
942 g_assert_not_reached();
946 uint32_t HELPER(bfexts_mem)(CPUM68KState *env, uint32_t addr,
947 int32_t ofs, uint32_t len)
949 uintptr_t ra = GETPC();
950 struct bf_data d = bf_prep(addr, ofs, len);
951 uint64_t data = bf_load(env, d.addr, d.blen, ra);
953 return (int64_t)(data << d.bofs) >> (64 - d.len);
956 uint64_t HELPER(bfextu_mem)(CPUM68KState *env, uint32_t addr,
957 int32_t ofs, uint32_t len)
959 uintptr_t ra = GETPC();
960 struct bf_data d = bf_prep(addr, ofs, len);
961 uint64_t data = bf_load(env, d.addr, d.blen, ra);
964 * Put CC_N at the top of the high word; put the zero-extended value
965 * at the bottom of the low word.
967 data <<= d.bofs;
968 data >>= 64 - d.len;
969 data |= data << (64 - d.len);
971 return data;
974 uint32_t HELPER(bfins_mem)(CPUM68KState *env, uint32_t addr, uint32_t val,
975 int32_t ofs, uint32_t len)
977 uintptr_t ra = GETPC();
978 struct bf_data d = bf_prep(addr, ofs, len);
979 uint64_t data = bf_load(env, d.addr, d.blen, ra);
980 uint64_t mask = -1ull << (64 - d.len) >> d.bofs;
982 data = (data & ~mask) | (((uint64_t)val << (64 - d.len)) >> d.bofs);
984 bf_store(env, d.addr, d.blen, data, ra);
986 /* The field at the top of the word is also CC_N for CC_OP_LOGIC. */
987 return val << (32 - d.len);
990 uint32_t HELPER(bfchg_mem)(CPUM68KState *env, uint32_t addr,
991 int32_t ofs, uint32_t len)
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;
998 bf_store(env, d.addr, d.blen, data ^ mask, ra);
1000 return ((data & mask) << d.bofs) >> 32;
1003 uint32_t HELPER(bfclr_mem)(CPUM68KState *env, uint32_t addr,
1004 int32_t ofs, uint32_t len)
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;
1011 bf_store(env, d.addr, d.blen, data & ~mask, ra);
1013 return ((data & mask) << d.bofs) >> 32;
1016 uint32_t HELPER(bfset_mem)(CPUM68KState *env, uint32_t addr,
1017 int32_t ofs, uint32_t len)
1019 uintptr_t ra = GETPC();
1020 struct bf_data d = bf_prep(addr, ofs, len);
1021 uint64_t data = bf_load(env, d.addr, d.blen, ra);
1022 uint64_t mask = -1ull << (64 - d.len) >> d.bofs;
1024 bf_store(env, d.addr, d.blen, data | mask, ra);
1026 return ((data & mask) << d.bofs) >> 32;
1029 uint32_t HELPER(bfffo_reg)(uint32_t n, uint32_t ofs, uint32_t len)
1031 return (n ? clz32(n) : len) + ofs;
1034 uint64_t HELPER(bfffo_mem)(CPUM68KState *env, uint32_t addr,
1035 int32_t ofs, uint32_t len)
1037 uintptr_t ra = GETPC();
1038 struct bf_data d = bf_prep(addr, ofs, len);
1039 uint64_t data = bf_load(env, d.addr, d.blen, ra);
1040 uint64_t mask = -1ull << (64 - d.len) >> d.bofs;
1041 uint64_t n = (data & mask) << d.bofs;
1042 uint32_t ffo = helper_bfffo_reg(n >> 32, ofs, d.len);
1045 * Return FFO in the low word and N in the high word.
1046 * Note that because of MASK and the shift, the low word
1047 * is already zero.
1049 return n | ffo;
1052 void HELPER(chk)(CPUM68KState *env, int32_t val, int32_t ub)
1055 * From the specs:
1056 * X: Not affected, C,V,Z: Undefined,
1057 * N: Set if val < 0; cleared if val > ub, undefined otherwise
1058 * We implement here values found from a real MC68040:
1059 * X,V,Z: Not affected
1060 * N: Set if val < 0; cleared if val >= 0
1061 * C: if 0 <= ub: set if val < 0 or val > ub, cleared otherwise
1062 * if 0 > ub: set if val > ub and val < 0, cleared otherwise
1064 env->cc_n = val;
1065 env->cc_c = 0 <= ub ? val < 0 || val > ub : val > ub && val < 0;
1067 if (val < 0 || val > ub) {
1068 CPUState *cs = env_cpu(env);
1070 /* Recover PC and CC_OP for the beginning of the insn. */
1071 cpu_restore_state(cs, GETPC(), true);
1073 /* flags have been modified by gen_flush_flags() */
1074 env->cc_op = CC_OP_FLAGS;
1075 /* Adjust PC to end of the insn. */
1076 env->pc += 2;
1078 cs->exception_index = EXCP_CHK;
1079 cpu_loop_exit(cs);
1083 void HELPER(chk2)(CPUM68KState *env, int32_t val, int32_t lb, int32_t ub)
1086 * From the specs:
1087 * X: Not affected, N,V: Undefined,
1088 * Z: Set if val is equal to lb or ub
1089 * C: Set if val < lb or val > ub, cleared otherwise
1090 * We implement here values found from a real MC68040:
1091 * X,N,V: Not affected
1092 * Z: Set if val is equal to lb or ub
1093 * C: if lb <= ub: set if val < lb or val > ub, cleared otherwise
1094 * if lb > ub: set if val > ub and val < lb, cleared otherwise
1096 env->cc_z = val != lb && val != ub;
1097 env->cc_c = lb <= ub ? val < lb || val > ub : val > ub && val < lb;
1099 if (env->cc_c) {
1100 CPUState *cs = env_cpu(env);
1102 /* Recover PC and CC_OP for the beginning of the insn. */
1103 cpu_restore_state(cs, GETPC(), true);
1105 /* flags have been modified by gen_flush_flags() */
1106 env->cc_op = CC_OP_FLAGS;
1107 /* Adjust PC to end of the insn. */
1108 env->pc += 4;
1110 cs->exception_index = EXCP_CHK;
1111 cpu_loop_exit(cs);