xref: /openbmc/qemu/target/m68k/op_helper.c (revision 3109cd98)
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         /* Real hardware gets the interrupt vector via an IACK cycle
498            at this point.  Current emulated hardware doesn't rely on
499            this, so we provide/save the vector when the interrupt is
500            first signalled.  */
501         cs->exception_index = env->pending_vector;
502         do_interrupt_m68k_hardirq(env);
503         return true;
504     }
505     return false;
506 }
507 
508 static void raise_exception_ra(CPUM68KState *env, int tt, uintptr_t raddr)
509 {
510     CPUState *cs = env_cpu(env);
511 
512     cs->exception_index = tt;
513     cpu_loop_exit_restore(cs, raddr);
514 }
515 
516 static void raise_exception(CPUM68KState *env, int tt)
517 {
518     raise_exception_ra(env, tt, 0);
519 }
520 
521 void HELPER(raise_exception)(CPUM68KState *env, uint32_t tt)
522 {
523     raise_exception(env, tt);
524 }
525 
526 void HELPER(divuw)(CPUM68KState *env, int destr, uint32_t den)
527 {
528     uint32_t num = env->dregs[destr];
529     uint32_t quot, rem;
530 
531     if (den == 0) {
532         raise_exception_ra(env, EXCP_DIV0, GETPC());
533     }
534     quot = num / den;
535     rem = num % den;
536 
537     env->cc_c = 0; /* always cleared, even if overflow */
538     if (quot > 0xffff) {
539         env->cc_v = -1;
540         /* real 68040 keeps N and unset Z on overflow,
541          * whereas documentation says "undefined"
542          */
543         env->cc_z = 1;
544         return;
545     }
546     env->dregs[destr] = deposit32(quot, 16, 16, rem);
547     env->cc_z = (int16_t)quot;
548     env->cc_n = (int16_t)quot;
549     env->cc_v = 0;
550 }
551 
552 void HELPER(divsw)(CPUM68KState *env, int destr, int32_t den)
553 {
554     int32_t num = env->dregs[destr];
555     uint32_t quot, rem;
556 
557     if (den == 0) {
558         raise_exception_ra(env, EXCP_DIV0, GETPC());
559     }
560     quot = num / den;
561     rem = num % den;
562 
563     env->cc_c = 0; /* always cleared, even if overflow */
564     if (quot != (int16_t)quot) {
565         env->cc_v = -1;
566         /* nothing else is modified */
567         /* real 68040 keeps N and unset Z on overflow,
568          * whereas documentation says "undefined"
569          */
570         env->cc_z = 1;
571         return;
572     }
573     env->dregs[destr] = deposit32(quot, 16, 16, rem);
574     env->cc_z = (int16_t)quot;
575     env->cc_n = (int16_t)quot;
576     env->cc_v = 0;
577 }
578 
579 void HELPER(divul)(CPUM68KState *env, int numr, int regr, uint32_t den)
580 {
581     uint32_t num = env->dregs[numr];
582     uint32_t quot, rem;
583 
584     if (den == 0) {
585         raise_exception_ra(env, EXCP_DIV0, GETPC());
586     }
587     quot = num / den;
588     rem = num % den;
589 
590     env->cc_c = 0;
591     env->cc_z = quot;
592     env->cc_n = quot;
593     env->cc_v = 0;
594 
595     if (m68k_feature(env, M68K_FEATURE_CF_ISA_A)) {
596         if (numr == regr) {
597             env->dregs[numr] = quot;
598         } else {
599             env->dregs[regr] = rem;
600         }
601     } else {
602         env->dregs[regr] = rem;
603         env->dregs[numr] = quot;
604     }
605 }
606 
607 void HELPER(divsl)(CPUM68KState *env, int numr, int regr, int32_t den)
608 {
609     int32_t num = env->dregs[numr];
610     int32_t quot, rem;
611 
612     if (den == 0) {
613         raise_exception_ra(env, EXCP_DIV0, GETPC());
614     }
615     quot = num / den;
616     rem = num % den;
617 
618     env->cc_c = 0;
619     env->cc_z = quot;
620     env->cc_n = quot;
621     env->cc_v = 0;
622 
623     if (m68k_feature(env, M68K_FEATURE_CF_ISA_A)) {
624         if (numr == regr) {
625             env->dregs[numr] = quot;
626         } else {
627             env->dregs[regr] = rem;
628         }
629     } else {
630         env->dregs[regr] = rem;
631         env->dregs[numr] = quot;
632     }
633 }
634 
635 void HELPER(divull)(CPUM68KState *env, int numr, int regr, uint32_t den)
636 {
637     uint64_t num = deposit64(env->dregs[numr], 32, 32, env->dregs[regr]);
638     uint64_t quot;
639     uint32_t rem;
640 
641     if (den == 0) {
642         raise_exception_ra(env, EXCP_DIV0, GETPC());
643     }
644     quot = num / den;
645     rem = num % den;
646 
647     env->cc_c = 0; /* always cleared, even if overflow */
648     if (quot > 0xffffffffULL) {
649         env->cc_v = -1;
650         /* real 68040 keeps N and unset Z on overflow,
651          * whereas documentation says "undefined"
652          */
653         env->cc_z = 1;
654         return;
655     }
656     env->cc_z = quot;
657     env->cc_n = quot;
658     env->cc_v = 0;
659 
660     /*
661      * If Dq and Dr are the same, the quotient is returned.
662      * therefore we set Dq last.
663      */
664 
665     env->dregs[regr] = rem;
666     env->dregs[numr] = quot;
667 }
668 
669 void HELPER(divsll)(CPUM68KState *env, int numr, int regr, int32_t den)
670 {
671     int64_t num = deposit64(env->dregs[numr], 32, 32, env->dregs[regr]);
672     int64_t quot;
673     int32_t rem;
674 
675     if (den == 0) {
676         raise_exception_ra(env, EXCP_DIV0, GETPC());
677     }
678     quot = num / den;
679     rem = num % den;
680 
681     env->cc_c = 0; /* always cleared, even if overflow */
682     if (quot != (int32_t)quot) {
683         env->cc_v = -1;
684         /* real 68040 keeps N and unset Z on overflow,
685          * whereas documentation says "undefined"
686          */
687         env->cc_z = 1;
688         return;
689     }
690     env->cc_z = quot;
691     env->cc_n = quot;
692     env->cc_v = 0;
693 
694     /*
695      * If Dq and Dr are the same, the quotient is returned.
696      * therefore we set Dq last.
697      */
698 
699     env->dregs[regr] = rem;
700     env->dregs[numr] = quot;
701 }
702 
703 /* We're executing in a serial context -- no need to be atomic.  */
704 void HELPER(cas2w)(CPUM68KState *env, uint32_t regs, uint32_t a1, uint32_t a2)
705 {
706     uint32_t Dc1 = extract32(regs, 9, 3);
707     uint32_t Dc2 = extract32(regs, 6, 3);
708     uint32_t Du1 = extract32(regs, 3, 3);
709     uint32_t Du2 = extract32(regs, 0, 3);
710     int16_t c1 = env->dregs[Dc1];
711     int16_t c2 = env->dregs[Dc2];
712     int16_t u1 = env->dregs[Du1];
713     int16_t u2 = env->dregs[Du2];
714     int16_t l1, l2;
715     uintptr_t ra = GETPC();
716 
717     l1 = cpu_lduw_data_ra(env, a1, ra);
718     l2 = cpu_lduw_data_ra(env, a2, ra);
719     if (l1 == c1 && l2 == c2) {
720         cpu_stw_data_ra(env, a1, u1, ra);
721         cpu_stw_data_ra(env, a2, u2, ra);
722     }
723 
724     if (c1 != l1) {
725         env->cc_n = l1;
726         env->cc_v = c1;
727     } else {
728         env->cc_n = l2;
729         env->cc_v = c2;
730     }
731     env->cc_op = CC_OP_CMPW;
732     env->dregs[Dc1] = deposit32(env->dregs[Dc1], 0, 16, l1);
733     env->dregs[Dc2] = deposit32(env->dregs[Dc2], 0, 16, l2);
734 }
735 
736 static void do_cas2l(CPUM68KState *env, uint32_t regs, uint32_t a1, uint32_t a2,
737                      bool parallel)
738 {
739     uint32_t Dc1 = extract32(regs, 9, 3);
740     uint32_t Dc2 = extract32(regs, 6, 3);
741     uint32_t Du1 = extract32(regs, 3, 3);
742     uint32_t Du2 = extract32(regs, 0, 3);
743     uint32_t c1 = env->dregs[Dc1];
744     uint32_t c2 = env->dregs[Dc2];
745     uint32_t u1 = env->dregs[Du1];
746     uint32_t u2 = env->dregs[Du2];
747     uint32_t l1, l2;
748     uintptr_t ra = GETPC();
749 #if defined(CONFIG_ATOMIC64) && !defined(CONFIG_USER_ONLY)
750     int mmu_idx = cpu_mmu_index(env, 0);
751     TCGMemOpIdx oi;
752 #endif
753 
754     if (parallel) {
755         /* We're executing in a parallel context -- must be atomic.  */
756 #ifdef CONFIG_ATOMIC64
757         uint64_t c, u, l;
758         if ((a1 & 7) == 0 && a2 == a1 + 4) {
759             c = deposit64(c2, 32, 32, c1);
760             u = deposit64(u2, 32, 32, u1);
761 #ifdef CONFIG_USER_ONLY
762             l = helper_atomic_cmpxchgq_be(env, a1, c, u);
763 #else
764             oi = make_memop_idx(MO_BEQ, mmu_idx);
765             l = helper_atomic_cmpxchgq_be_mmu(env, a1, c, u, oi, ra);
766 #endif
767             l1 = l >> 32;
768             l2 = l;
769         } else if ((a2 & 7) == 0 && a1 == a2 + 4) {
770             c = deposit64(c1, 32, 32, c2);
771             u = deposit64(u1, 32, 32, u2);
772 #ifdef CONFIG_USER_ONLY
773             l = helper_atomic_cmpxchgq_be(env, a2, c, u);
774 #else
775             oi = make_memop_idx(MO_BEQ, mmu_idx);
776             l = helper_atomic_cmpxchgq_be_mmu(env, a2, c, u, oi, ra);
777 #endif
778             l2 = l >> 32;
779             l1 = l;
780         } else
781 #endif
782         {
783             /* Tell the main loop we need to serialize this insn.  */
784             cpu_loop_exit_atomic(env_cpu(env), ra);
785         }
786     } else {
787         /* We're executing in a serial context -- no need to be atomic.  */
788         l1 = cpu_ldl_data_ra(env, a1, ra);
789         l2 = cpu_ldl_data_ra(env, a2, ra);
790         if (l1 == c1 && l2 == c2) {
791             cpu_stl_data_ra(env, a1, u1, ra);
792             cpu_stl_data_ra(env, a2, u2, ra);
793         }
794     }
795 
796     if (c1 != l1) {
797         env->cc_n = l1;
798         env->cc_v = c1;
799     } else {
800         env->cc_n = l2;
801         env->cc_v = c2;
802     }
803     env->cc_op = CC_OP_CMPL;
804     env->dregs[Dc1] = l1;
805     env->dregs[Dc2] = l2;
806 }
807 
808 void HELPER(cas2l)(CPUM68KState *env, uint32_t regs, uint32_t a1, uint32_t a2)
809 {
810     do_cas2l(env, regs, a1, a2, false);
811 }
812 
813 void HELPER(cas2l_parallel)(CPUM68KState *env, uint32_t regs, uint32_t a1,
814                             uint32_t a2)
815 {
816     do_cas2l(env, regs, a1, a2, true);
817 }
818 
819 struct bf_data {
820     uint32_t addr;
821     uint32_t bofs;
822     uint32_t blen;
823     uint32_t len;
824 };
825 
826 static struct bf_data bf_prep(uint32_t addr, int32_t ofs, uint32_t len)
827 {
828     int bofs, blen;
829 
830     /* Bound length; map 0 to 32.  */
831     len = ((len - 1) & 31) + 1;
832 
833     /* Note that ofs is signed.  */
834     addr += ofs / 8;
835     bofs = ofs % 8;
836     if (bofs < 0) {
837         bofs += 8;
838         addr -= 1;
839     }
840 
841     /* Compute the number of bytes required (minus one) to
842        satisfy the bitfield.  */
843     blen = (bofs + len - 1) / 8;
844 
845     /* Canonicalize the bit offset for data loaded into a 64-bit big-endian
846        word.  For the cases where BLEN is not a power of 2, adjust ADDR so
847        that we can use the next power of two sized load without crossing a
848        page boundary, unless the field itself crosses the boundary.  */
849     switch (blen) {
850     case 0:
851         bofs += 56;
852         break;
853     case 1:
854         bofs += 48;
855         break;
856     case 2:
857         if (addr & 1) {
858             bofs += 8;
859             addr -= 1;
860         }
861         /* fallthru */
862     case 3:
863         bofs += 32;
864         break;
865     case 4:
866         if (addr & 3) {
867             bofs += 8 * (addr & 3);
868             addr &= -4;
869         }
870         break;
871     default:
872         g_assert_not_reached();
873     }
874 
875     return (struct bf_data){
876         .addr = addr,
877         .bofs = bofs,
878         .blen = blen,
879         .len = len,
880     };
881 }
882 
883 static uint64_t bf_load(CPUM68KState *env, uint32_t addr, int blen,
884                         uintptr_t ra)
885 {
886     switch (blen) {
887     case 0:
888         return cpu_ldub_data_ra(env, addr, ra);
889     case 1:
890         return cpu_lduw_data_ra(env, addr, ra);
891     case 2:
892     case 3:
893         return cpu_ldl_data_ra(env, addr, ra);
894     case 4:
895         return cpu_ldq_data_ra(env, addr, ra);
896     default:
897         g_assert_not_reached();
898     }
899 }
900 
901 static void bf_store(CPUM68KState *env, uint32_t addr, int blen,
902                      uint64_t data, uintptr_t ra)
903 {
904     switch (blen) {
905     case 0:
906         cpu_stb_data_ra(env, addr, data, ra);
907         break;
908     case 1:
909         cpu_stw_data_ra(env, addr, data, ra);
910         break;
911     case 2:
912     case 3:
913         cpu_stl_data_ra(env, addr, data, ra);
914         break;
915     case 4:
916         cpu_stq_data_ra(env, addr, data, ra);
917         break;
918     default:
919         g_assert_not_reached();
920     }
921 }
922 
923 uint32_t HELPER(bfexts_mem)(CPUM68KState *env, uint32_t addr,
924                             int32_t ofs, uint32_t len)
925 {
926     uintptr_t ra = GETPC();
927     struct bf_data d = bf_prep(addr, ofs, len);
928     uint64_t data = bf_load(env, d.addr, d.blen, ra);
929 
930     return (int64_t)(data << d.bofs) >> (64 - d.len);
931 }
932 
933 uint64_t HELPER(bfextu_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     /* Put CC_N at the top of the high word; put the zero-extended value
941        at the bottom of the low word.  */
942     data <<= d.bofs;
943     data >>= 64 - d.len;
944     data |= data << (64 - d.len);
945 
946     return data;
947 }
948 
949 uint32_t HELPER(bfins_mem)(CPUM68KState *env, uint32_t addr, uint32_t val,
950                            int32_t ofs, uint32_t len)
951 {
952     uintptr_t ra = GETPC();
953     struct bf_data d = bf_prep(addr, ofs, len);
954     uint64_t data = bf_load(env, d.addr, d.blen, ra);
955     uint64_t mask = -1ull << (64 - d.len) >> d.bofs;
956 
957     data = (data & ~mask) | (((uint64_t)val << (64 - d.len)) >> d.bofs);
958 
959     bf_store(env, d.addr, d.blen, data, ra);
960 
961     /* The field at the top of the word is also CC_N for CC_OP_LOGIC.  */
962     return val << (32 - d.len);
963 }
964 
965 uint32_t HELPER(bfchg_mem)(CPUM68KState *env, uint32_t addr,
966                            int32_t ofs, uint32_t len)
967 {
968     uintptr_t ra = GETPC();
969     struct bf_data d = bf_prep(addr, ofs, len);
970     uint64_t data = bf_load(env, d.addr, d.blen, ra);
971     uint64_t mask = -1ull << (64 - d.len) >> d.bofs;
972 
973     bf_store(env, d.addr, d.blen, data ^ mask, ra);
974 
975     return ((data & mask) << d.bofs) >> 32;
976 }
977 
978 uint32_t HELPER(bfclr_mem)(CPUM68KState *env, uint32_t addr,
979                            int32_t ofs, uint32_t len)
980 {
981     uintptr_t ra = GETPC();
982     struct bf_data d = bf_prep(addr, ofs, len);
983     uint64_t data = bf_load(env, d.addr, d.blen, ra);
984     uint64_t mask = -1ull << (64 - d.len) >> d.bofs;
985 
986     bf_store(env, d.addr, d.blen, data & ~mask, ra);
987 
988     return ((data & mask) << d.bofs) >> 32;
989 }
990 
991 uint32_t HELPER(bfset_mem)(CPUM68KState *env, uint32_t addr,
992                            int32_t ofs, uint32_t len)
993 {
994     uintptr_t ra = GETPC();
995     struct bf_data d = bf_prep(addr, ofs, len);
996     uint64_t data = bf_load(env, d.addr, d.blen, ra);
997     uint64_t mask = -1ull << (64 - d.len) >> d.bofs;
998 
999     bf_store(env, d.addr, d.blen, data | mask, ra);
1000 
1001     return ((data & mask) << d.bofs) >> 32;
1002 }
1003 
1004 uint32_t HELPER(bfffo_reg)(uint32_t n, uint32_t ofs, uint32_t len)
1005 {
1006     return (n ? clz32(n) : len) + ofs;
1007 }
1008 
1009 uint64_t HELPER(bfffo_mem)(CPUM68KState *env, uint32_t addr,
1010                            int32_t ofs, uint32_t len)
1011 {
1012     uintptr_t ra = GETPC();
1013     struct bf_data d = bf_prep(addr, ofs, len);
1014     uint64_t data = bf_load(env, d.addr, d.blen, ra);
1015     uint64_t mask = -1ull << (64 - d.len) >> d.bofs;
1016     uint64_t n = (data & mask) << d.bofs;
1017     uint32_t ffo = helper_bfffo_reg(n >> 32, ofs, d.len);
1018 
1019     /* Return FFO in the low word and N in the high word.
1020        Note that because of MASK and the shift, the low word
1021        is already zero.  */
1022     return n | ffo;
1023 }
1024 
1025 void HELPER(chk)(CPUM68KState *env, int32_t val, int32_t ub)
1026 {
1027     /* From the specs:
1028      *   X: Not affected, C,V,Z: Undefined,
1029      *   N: Set if val < 0; cleared if val > ub, undefined otherwise
1030      * We implement here values found from a real MC68040:
1031      *   X,V,Z: Not affected
1032      *   N: Set if val < 0; cleared if val >= 0
1033      *   C: if 0 <= ub: set if val < 0 or val > ub, cleared otherwise
1034      *      if 0 > ub: set if val > ub and val < 0, cleared otherwise
1035      */
1036     env->cc_n = val;
1037     env->cc_c = 0 <= ub ? val < 0 || val > ub : val > ub && val < 0;
1038 
1039     if (val < 0 || val > ub) {
1040         CPUState *cs = env_cpu(env);
1041 
1042         /* Recover PC and CC_OP for the beginning of the insn.  */
1043         cpu_restore_state(cs, GETPC(), true);
1044 
1045         /* flags have been modified by gen_flush_flags() */
1046         env->cc_op = CC_OP_FLAGS;
1047         /* Adjust PC to end of the insn.  */
1048         env->pc += 2;
1049 
1050         cs->exception_index = EXCP_CHK;
1051         cpu_loop_exit(cs);
1052     }
1053 }
1054 
1055 void HELPER(chk2)(CPUM68KState *env, int32_t val, int32_t lb, int32_t ub)
1056 {
1057     /* From the specs:
1058      *   X: Not affected, N,V: Undefined,
1059      *   Z: Set if val is equal to lb or ub
1060      *   C: Set if val < lb or val > ub, cleared otherwise
1061      * We implement here values found from a real MC68040:
1062      *   X,N,V: Not affected
1063      *   Z: Set if val is equal to lb or ub
1064      *   C: if lb <= ub: set if val < lb or val > ub, cleared otherwise
1065      *      if lb > ub: set if val > ub and val < lb, cleared otherwise
1066      */
1067     env->cc_z = val != lb && val != ub;
1068     env->cc_c = lb <= ub ? val < lb || val > ub : val > ub && val < lb;
1069 
1070     if (env->cc_c) {
1071         CPUState *cs = env_cpu(env);
1072 
1073         /* Recover PC and CC_OP for the beginning of the insn.  */
1074         cpu_restore_state(cs, GETPC(), true);
1075 
1076         /* flags have been modified by gen_flush_flags() */
1077         env->cc_op = CC_OP_FLAGS;
1078         /* Adjust PC to end of the insn.  */
1079         env->pc += 4;
1080 
1081         cs->exception_index = EXCP_CHK;
1082         cpu_loop_exit(cs);
1083     }
1084 }
1085