xref: /openbmc/qemu/target/m68k/op_helper.c (revision f7160f32)
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_mmuidx_ra(env, sp, MMU_KERNEL_IDX, 0);
46     env->pc = cpu_ldl_mmuidx_ra(env, sp + 4, MMU_KERNEL_IDX, 0);
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_mmuidx_ra(env, sp, MMU_KERNEL_IDX, 0);
62     sp += 2;
63     env->pc = cpu_ldl_mmuidx_ra(env, sp, MMU_KERNEL_IDX, 0);
64     sp += 4;
65     if (m68k_feature(env, M68K_FEATURE_QUAD_MULDIV)) {
66         /*  all except 68000 */
67         fmt = cpu_lduw_mmuidx_ra(env, sp, MMU_KERNEL_IDX, 0);
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_mmuidx_ra(env, sp, retaddr, MMU_KERNEL_IDX, 0);
264     sp -= 4;
265     cpu_stl_mmuidx_ra(env, sp, fmt, MMU_KERNEL_IDX, 0);
266     env->aregs[7] = sp;
267     /* Jump to vector.  */
268     env->pc = cpu_ldl_mmuidx_ra(env, env->vbr + vector, MMU_KERNEL_IDX, 0);
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_mmuidx_ra(env, *sp, env->pc, MMU_KERNEL_IDX, 0);
282             *sp -= 4;
283             cpu_stl_mmuidx_ra(env, *sp, addr, MMU_KERNEL_IDX, 0);
284             break;
285         case 3:
286         case 2:
287             *sp -= 4;
288             cpu_stl_mmuidx_ra(env, *sp, addr, MMU_KERNEL_IDX, 0);
289             break;
290         }
291         *sp -= 2;
292         cpu_stw_mmuidx_ra(env, *sp, (format << 12) + (cs->exception_index << 2),
293                           MMU_KERNEL_IDX, 0);
294     }
295     *sp -= 4;
296     cpu_stl_mmuidx_ra(env, *sp, retaddr, MMU_KERNEL_IDX, 0);
297     *sp -= 2;
298     cpu_stw_mmuidx_ra(env, *sp, sr, MMU_KERNEL_IDX, 0);
299 }
300 
301 static void m68k_interrupt_all(CPUM68KState *env, int is_hw)
302 {
303     CPUState *cs = env_cpu(env);
304     uint32_t sp;
305     uint32_t retaddr;
306     uint32_t vector;
307     uint16_t sr, oldsr;
308 
309     retaddr = env->pc;
310 
311     if (!is_hw) {
312         switch (cs->exception_index) {
313         case EXCP_RTE:
314             /* Return from an exception.  */
315             m68k_rte(env);
316             return;
317         case EXCP_TRAP0 ...  EXCP_TRAP15:
318             /* Move the PC after the trap instruction.  */
319             retaddr += 2;
320             break;
321         }
322     }
323 
324     vector = cs->exception_index << 2;
325 
326     sr = env->sr | cpu_m68k_get_ccr(env);
327     if (qemu_loglevel_mask(CPU_LOG_INT)) {
328         static int count;
329         qemu_log("INT %6d: %s(%#x) pc=%08x sp=%08x sr=%04x\n",
330                  ++count, m68k_exception_name(cs->exception_index),
331                  vector, env->pc, env->aregs[7], sr);
332     }
333 
334     /*
335      * MC68040UM/AD,  chapter 9.3.10
336      */
337 
338     /* "the processor first make an internal copy" */
339     oldsr = sr;
340     /* "set the mode to supervisor" */
341     sr |= SR_S;
342     /* "suppress tracing" */
343     sr &= ~SR_T;
344     /* "sets the processor interrupt mask" */
345     if (is_hw) {
346         sr |= (env->sr & ~SR_I) | (env->pending_level << SR_I_SHIFT);
347     }
348     cpu_m68k_set_sr(env, sr);
349     sp = env->aregs[7];
350 
351     sp &= ~1;
352     if (cs->exception_index == EXCP_ACCESS) {
353         if (env->mmu.fault) {
354             cpu_abort(cs, "DOUBLE MMU FAULT\n");
355         }
356         env->mmu.fault = true;
357         /* push data 3 */
358         sp -= 4;
359         cpu_stl_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
360         /* push data 2 */
361         sp -= 4;
362         cpu_stl_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
363         /* push data 1 */
364         sp -= 4;
365         cpu_stl_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
366         /* write back 1 / push data 0 */
367         sp -= 4;
368         cpu_stl_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
369         /* write back 1 address */
370         sp -= 4;
371         cpu_stl_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
372         /* write back 2 data */
373         sp -= 4;
374         cpu_stl_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
375         /* write back 2 address */
376         sp -= 4;
377         cpu_stl_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
378         /* write back 3 data */
379         sp -= 4;
380         cpu_stl_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
381         /* write back 3 address */
382         sp -= 4;
383         cpu_stl_mmuidx_ra(env, sp, env->mmu.ar, MMU_KERNEL_IDX, 0);
384         /* fault address */
385         sp -= 4;
386         cpu_stl_mmuidx_ra(env, sp, env->mmu.ar, MMU_KERNEL_IDX, 0);
387         /* write back 1 status */
388         sp -= 2;
389         cpu_stw_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
390         /* write back 2 status */
391         sp -= 2;
392         cpu_stw_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
393         /* write back 3 status */
394         sp -= 2;
395         cpu_stw_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
396         /* special status word */
397         sp -= 2;
398         cpu_stw_mmuidx_ra(env, sp, env->mmu.ssw, MMU_KERNEL_IDX, 0);
399         /* effective address */
400         sp -= 4;
401         cpu_stl_mmuidx_ra(env, sp, env->mmu.ar, MMU_KERNEL_IDX, 0);
402 
403         do_stack_frame(env, &sp, 7, oldsr, 0, retaddr);
404         env->mmu.fault = false;
405         if (qemu_loglevel_mask(CPU_LOG_INT)) {
406             qemu_log("            "
407                      "ssw:  %08x ea:   %08x sfc:  %d    dfc: %d\n",
408                      env->mmu.ssw, env->mmu.ar, env->sfc, env->dfc);
409         }
410     } else if (cs->exception_index == EXCP_ADDRESS) {
411         do_stack_frame(env, &sp, 2, oldsr, 0, retaddr);
412     } else if (cs->exception_index == EXCP_ILLEGAL ||
413                cs->exception_index == EXCP_DIV0 ||
414                cs->exception_index == EXCP_CHK ||
415                cs->exception_index == EXCP_TRAPCC ||
416                cs->exception_index == EXCP_TRACE) {
417         /* FIXME: addr is not only env->pc */
418         do_stack_frame(env, &sp, 2, oldsr, env->pc, retaddr);
419     } else if (is_hw && oldsr & SR_M &&
420                cs->exception_index >= EXCP_SPURIOUS &&
421                cs->exception_index <= EXCP_INT_LEVEL_7) {
422         do_stack_frame(env, &sp, 0, oldsr, 0, retaddr);
423         oldsr = sr;
424         env->aregs[7] = sp;
425         cpu_m68k_set_sr(env, sr &= ~SR_M);
426         sp = env->aregs[7] & ~1;
427         do_stack_frame(env, &sp, 1, oldsr, 0, retaddr);
428     } else {
429         do_stack_frame(env, &sp, 0, oldsr, 0, retaddr);
430     }
431 
432     env->aregs[7] = sp;
433     /* Jump to vector.  */
434     env->pc = cpu_ldl_mmuidx_ra(env, env->vbr + vector, MMU_KERNEL_IDX, 0);
435 }
436 
437 static void do_interrupt_all(CPUM68KState *env, int is_hw)
438 {
439     if (m68k_feature(env, M68K_FEATURE_M68000)) {
440         m68k_interrupt_all(env, is_hw);
441         return;
442     }
443     cf_interrupt_all(env, is_hw);
444 }
445 
446 void m68k_cpu_do_interrupt(CPUState *cs)
447 {
448     M68kCPU *cpu = M68K_CPU(cs);
449     CPUM68KState *env = &cpu->env;
450 
451     do_interrupt_all(env, 0);
452 }
453 
454 static inline void do_interrupt_m68k_hardirq(CPUM68KState *env)
455 {
456     do_interrupt_all(env, 1);
457 }
458 
459 void m68k_cpu_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr,
460                                  unsigned size, MMUAccessType access_type,
461                                  int mmu_idx, MemTxAttrs attrs,
462                                  MemTxResult response, uintptr_t retaddr)
463 {
464     M68kCPU *cpu = M68K_CPU(cs);
465     CPUM68KState *env = &cpu->env;
466 
467     cpu_restore_state(cs, retaddr, true);
468 
469     if (m68k_feature(env, M68K_FEATURE_M68040)) {
470         env->mmu.mmusr = 0;
471         env->mmu.ssw |= M68K_ATC_040;
472         /* FIXME: manage MMU table access error */
473         env->mmu.ssw &= ~M68K_TM_040;
474         if (env->sr & SR_S) { /* SUPERVISOR */
475             env->mmu.ssw |= M68K_TM_040_SUPER;
476         }
477         if (access_type == MMU_INST_FETCH) { /* instruction or data */
478             env->mmu.ssw |= M68K_TM_040_CODE;
479         } else {
480             env->mmu.ssw |= M68K_TM_040_DATA;
481         }
482         env->mmu.ssw &= ~M68K_BA_SIZE_MASK;
483         switch (size) {
484         case 1:
485             env->mmu.ssw |= M68K_BA_SIZE_BYTE;
486             break;
487         case 2:
488             env->mmu.ssw |= M68K_BA_SIZE_WORD;
489             break;
490         case 4:
491             env->mmu.ssw |= M68K_BA_SIZE_LONG;
492             break;
493         }
494 
495         if (access_type != MMU_DATA_STORE) {
496             env->mmu.ssw |= M68K_RW_040;
497         }
498 
499         env->mmu.ar = addr;
500 
501         cs->exception_index = EXCP_ACCESS;
502         cpu_loop_exit(cs);
503     }
504 }
505 #endif
506 
507 bool m68k_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
508 {
509     M68kCPU *cpu = M68K_CPU(cs);
510     CPUM68KState *env = &cpu->env;
511 
512     if (interrupt_request & CPU_INTERRUPT_HARD
513         && ((env->sr & SR_I) >> SR_I_SHIFT) < env->pending_level) {
514         /*
515          * Real hardware gets the interrupt vector via an IACK cycle
516          * at this point.  Current emulated hardware doesn't rely on
517          * this, so we provide/save the vector when the interrupt is
518          * first signalled.
519          */
520         cs->exception_index = env->pending_vector;
521         do_interrupt_m68k_hardirq(env);
522         return true;
523     }
524     return false;
525 }
526 
527 static void raise_exception_ra(CPUM68KState *env, int tt, uintptr_t raddr)
528 {
529     CPUState *cs = env_cpu(env);
530 
531     cs->exception_index = tt;
532     cpu_loop_exit_restore(cs, raddr);
533 }
534 
535 static void raise_exception(CPUM68KState *env, int tt)
536 {
537     raise_exception_ra(env, tt, 0);
538 }
539 
540 void HELPER(raise_exception)(CPUM68KState *env, uint32_t tt)
541 {
542     raise_exception(env, tt);
543 }
544 
545 void HELPER(divuw)(CPUM68KState *env, int destr, uint32_t den)
546 {
547     uint32_t num = env->dregs[destr];
548     uint32_t quot, rem;
549 
550     if (den == 0) {
551         raise_exception_ra(env, EXCP_DIV0, GETPC());
552     }
553     quot = num / den;
554     rem = num % den;
555 
556     env->cc_c = 0; /* always cleared, even if overflow */
557     if (quot > 0xffff) {
558         env->cc_v = -1;
559         /*
560          * real 68040 keeps N and unset Z on overflow,
561          * whereas documentation says "undefined"
562          */
563         env->cc_z = 1;
564         return;
565     }
566     env->dregs[destr] = deposit32(quot, 16, 16, rem);
567     env->cc_z = (int16_t)quot;
568     env->cc_n = (int16_t)quot;
569     env->cc_v = 0;
570 }
571 
572 void HELPER(divsw)(CPUM68KState *env, int destr, int32_t den)
573 {
574     int32_t num = env->dregs[destr];
575     uint32_t quot, rem;
576 
577     if (den == 0) {
578         raise_exception_ra(env, EXCP_DIV0, GETPC());
579     }
580     quot = num / den;
581     rem = num % den;
582 
583     env->cc_c = 0; /* always cleared, even if overflow */
584     if (quot != (int16_t)quot) {
585         env->cc_v = -1;
586         /* nothing else is modified */
587         /*
588          * real 68040 keeps N and unset Z on overflow,
589          * whereas documentation says "undefined"
590          */
591         env->cc_z = 1;
592         return;
593     }
594     env->dregs[destr] = deposit32(quot, 16, 16, rem);
595     env->cc_z = (int16_t)quot;
596     env->cc_n = (int16_t)quot;
597     env->cc_v = 0;
598 }
599 
600 void HELPER(divul)(CPUM68KState *env, int numr, int regr, uint32_t den)
601 {
602     uint32_t num = env->dregs[numr];
603     uint32_t quot, rem;
604 
605     if (den == 0) {
606         raise_exception_ra(env, EXCP_DIV0, GETPC());
607     }
608     quot = num / den;
609     rem = num % den;
610 
611     env->cc_c = 0;
612     env->cc_z = quot;
613     env->cc_n = quot;
614     env->cc_v = 0;
615 
616     if (m68k_feature(env, M68K_FEATURE_CF_ISA_A)) {
617         if (numr == regr) {
618             env->dregs[numr] = quot;
619         } else {
620             env->dregs[regr] = rem;
621         }
622     } else {
623         env->dregs[regr] = rem;
624         env->dregs[numr] = quot;
625     }
626 }
627 
628 void HELPER(divsl)(CPUM68KState *env, int numr, int regr, int32_t den)
629 {
630     int32_t num = env->dregs[numr];
631     int32_t quot, rem;
632 
633     if (den == 0) {
634         raise_exception_ra(env, EXCP_DIV0, GETPC());
635     }
636     quot = num / den;
637     rem = num % den;
638 
639     env->cc_c = 0;
640     env->cc_z = quot;
641     env->cc_n = quot;
642     env->cc_v = 0;
643 
644     if (m68k_feature(env, M68K_FEATURE_CF_ISA_A)) {
645         if (numr == regr) {
646             env->dregs[numr] = quot;
647         } else {
648             env->dregs[regr] = rem;
649         }
650     } else {
651         env->dregs[regr] = rem;
652         env->dregs[numr] = quot;
653     }
654 }
655 
656 void HELPER(divull)(CPUM68KState *env, int numr, int regr, uint32_t den)
657 {
658     uint64_t num = deposit64(env->dregs[numr], 32, 32, env->dregs[regr]);
659     uint64_t quot;
660     uint32_t rem;
661 
662     if (den == 0) {
663         raise_exception_ra(env, EXCP_DIV0, GETPC());
664     }
665     quot = num / den;
666     rem = num % den;
667 
668     env->cc_c = 0; /* always cleared, even if overflow */
669     if (quot > 0xffffffffULL) {
670         env->cc_v = -1;
671         /*
672          * real 68040 keeps N and unset Z on overflow,
673          * whereas documentation says "undefined"
674          */
675         env->cc_z = 1;
676         return;
677     }
678     env->cc_z = quot;
679     env->cc_n = quot;
680     env->cc_v = 0;
681 
682     /*
683      * If Dq and Dr are the same, the quotient is returned.
684      * therefore we set Dq last.
685      */
686 
687     env->dregs[regr] = rem;
688     env->dregs[numr] = quot;
689 }
690 
691 void HELPER(divsll)(CPUM68KState *env, int numr, int regr, int32_t den)
692 {
693     int64_t num = deposit64(env->dregs[numr], 32, 32, env->dregs[regr]);
694     int64_t quot;
695     int32_t rem;
696 
697     if (den == 0) {
698         raise_exception_ra(env, EXCP_DIV0, GETPC());
699     }
700     quot = num / den;
701     rem = num % den;
702 
703     env->cc_c = 0; /* always cleared, even if overflow */
704     if (quot != (int32_t)quot) {
705         env->cc_v = -1;
706         /*
707          * real 68040 keeps N and unset Z on overflow,
708          * whereas documentation says "undefined"
709          */
710         env->cc_z = 1;
711         return;
712     }
713     env->cc_z = quot;
714     env->cc_n = quot;
715     env->cc_v = 0;
716 
717     /*
718      * If Dq and Dr are the same, the quotient is returned.
719      * therefore we set Dq last.
720      */
721 
722     env->dregs[regr] = rem;
723     env->dregs[numr] = quot;
724 }
725 
726 /* We're executing in a serial context -- no need to be atomic.  */
727 void HELPER(cas2w)(CPUM68KState *env, uint32_t regs, uint32_t a1, uint32_t a2)
728 {
729     uint32_t Dc1 = extract32(regs, 9, 3);
730     uint32_t Dc2 = extract32(regs, 6, 3);
731     uint32_t Du1 = extract32(regs, 3, 3);
732     uint32_t Du2 = extract32(regs, 0, 3);
733     int16_t c1 = env->dregs[Dc1];
734     int16_t c2 = env->dregs[Dc2];
735     int16_t u1 = env->dregs[Du1];
736     int16_t u2 = env->dregs[Du2];
737     int16_t l1, l2;
738     uintptr_t ra = GETPC();
739 
740     l1 = cpu_lduw_data_ra(env, a1, ra);
741     l2 = cpu_lduw_data_ra(env, a2, ra);
742     if (l1 == c1 && l2 == c2) {
743         cpu_stw_data_ra(env, a1, u1, ra);
744         cpu_stw_data_ra(env, a2, u2, ra);
745     }
746 
747     if (c1 != l1) {
748         env->cc_n = l1;
749         env->cc_v = c1;
750     } else {
751         env->cc_n = l2;
752         env->cc_v = c2;
753     }
754     env->cc_op = CC_OP_CMPW;
755     env->dregs[Dc1] = deposit32(env->dregs[Dc1], 0, 16, l1);
756     env->dregs[Dc2] = deposit32(env->dregs[Dc2], 0, 16, l2);
757 }
758 
759 static void do_cas2l(CPUM68KState *env, uint32_t regs, uint32_t a1, uint32_t a2,
760                      bool parallel)
761 {
762     uint32_t Dc1 = extract32(regs, 9, 3);
763     uint32_t Dc2 = extract32(regs, 6, 3);
764     uint32_t Du1 = extract32(regs, 3, 3);
765     uint32_t Du2 = extract32(regs, 0, 3);
766     uint32_t c1 = env->dregs[Dc1];
767     uint32_t c2 = env->dregs[Dc2];
768     uint32_t u1 = env->dregs[Du1];
769     uint32_t u2 = env->dregs[Du2];
770     uint32_t l1, l2;
771     uintptr_t ra = GETPC();
772 #if defined(CONFIG_ATOMIC64) && !defined(CONFIG_USER_ONLY)
773     int mmu_idx = cpu_mmu_index(env, 0);
774     TCGMemOpIdx oi;
775 #endif
776 
777     if (parallel) {
778         /* We're executing in a parallel context -- must be atomic.  */
779 #ifdef CONFIG_ATOMIC64
780         uint64_t c, u, l;
781         if ((a1 & 7) == 0 && a2 == a1 + 4) {
782             c = deposit64(c2, 32, 32, c1);
783             u = deposit64(u2, 32, 32, u1);
784 #ifdef CONFIG_USER_ONLY
785             l = helper_atomic_cmpxchgq_be(env, a1, c, u);
786 #else
787             oi = make_memop_idx(MO_BEQ, mmu_idx);
788             l = helper_atomic_cmpxchgq_be_mmu(env, a1, c, u, oi, ra);
789 #endif
790             l1 = l >> 32;
791             l2 = l;
792         } else if ((a2 & 7) == 0 && a1 == a2 + 4) {
793             c = deposit64(c1, 32, 32, c2);
794             u = deposit64(u1, 32, 32, u2);
795 #ifdef CONFIG_USER_ONLY
796             l = helper_atomic_cmpxchgq_be(env, a2, c, u);
797 #else
798             oi = make_memop_idx(MO_BEQ, mmu_idx);
799             l = helper_atomic_cmpxchgq_be_mmu(env, a2, c, u, oi, ra);
800 #endif
801             l2 = l >> 32;
802             l1 = l;
803         } else
804 #endif
805         {
806             /* Tell the main loop we need to serialize this insn.  */
807             cpu_loop_exit_atomic(env_cpu(env), ra);
808         }
809     } else {
810         /* We're executing in a serial context -- no need to be atomic.  */
811         l1 = cpu_ldl_data_ra(env, a1, ra);
812         l2 = cpu_ldl_data_ra(env, a2, ra);
813         if (l1 == c1 && l2 == c2) {
814             cpu_stl_data_ra(env, a1, u1, ra);
815             cpu_stl_data_ra(env, a2, u2, ra);
816         }
817     }
818 
819     if (c1 != l1) {
820         env->cc_n = l1;
821         env->cc_v = c1;
822     } else {
823         env->cc_n = l2;
824         env->cc_v = c2;
825     }
826     env->cc_op = CC_OP_CMPL;
827     env->dregs[Dc1] = l1;
828     env->dregs[Dc2] = l2;
829 }
830 
831 void HELPER(cas2l)(CPUM68KState *env, uint32_t regs, uint32_t a1, uint32_t a2)
832 {
833     do_cas2l(env, regs, a1, a2, false);
834 }
835 
836 void HELPER(cas2l_parallel)(CPUM68KState *env, uint32_t regs, uint32_t a1,
837                             uint32_t a2)
838 {
839     do_cas2l(env, regs, a1, a2, true);
840 }
841 
842 struct bf_data {
843     uint32_t addr;
844     uint32_t bofs;
845     uint32_t blen;
846     uint32_t len;
847 };
848 
849 static struct bf_data bf_prep(uint32_t addr, int32_t ofs, uint32_t len)
850 {
851     int bofs, blen;
852 
853     /* Bound length; map 0 to 32.  */
854     len = ((len - 1) & 31) + 1;
855 
856     /* Note that ofs is signed.  */
857     addr += ofs / 8;
858     bofs = ofs % 8;
859     if (bofs < 0) {
860         bofs += 8;
861         addr -= 1;
862     }
863 
864     /*
865      * Compute the number of bytes required (minus one) to
866      * satisfy the bitfield.
867      */
868     blen = (bofs + len - 1) / 8;
869 
870     /*
871      * Canonicalize the bit offset for data loaded into a 64-bit big-endian
872      * word.  For the cases where BLEN is not a power of 2, adjust ADDR so
873      * that we can use the next power of two sized load without crossing a
874      * page boundary, unless the field itself crosses the boundary.
875      */
876     switch (blen) {
877     case 0:
878         bofs += 56;
879         break;
880     case 1:
881         bofs += 48;
882         break;
883     case 2:
884         if (addr & 1) {
885             bofs += 8;
886             addr -= 1;
887         }
888         /* fallthru */
889     case 3:
890         bofs += 32;
891         break;
892     case 4:
893         if (addr & 3) {
894             bofs += 8 * (addr & 3);
895             addr &= -4;
896         }
897         break;
898     default:
899         g_assert_not_reached();
900     }
901 
902     return (struct bf_data){
903         .addr = addr,
904         .bofs = bofs,
905         .blen = blen,
906         .len = len,
907     };
908 }
909 
910 static uint64_t bf_load(CPUM68KState *env, uint32_t addr, int blen,
911                         uintptr_t ra)
912 {
913     switch (blen) {
914     case 0:
915         return cpu_ldub_data_ra(env, addr, ra);
916     case 1:
917         return cpu_lduw_data_ra(env, addr, ra);
918     case 2:
919     case 3:
920         return cpu_ldl_data_ra(env, addr, ra);
921     case 4:
922         return cpu_ldq_data_ra(env, addr, ra);
923     default:
924         g_assert_not_reached();
925     }
926 }
927 
928 static void bf_store(CPUM68KState *env, uint32_t addr, int blen,
929                      uint64_t data, uintptr_t ra)
930 {
931     switch (blen) {
932     case 0:
933         cpu_stb_data_ra(env, addr, data, ra);
934         break;
935     case 1:
936         cpu_stw_data_ra(env, addr, data, ra);
937         break;
938     case 2:
939     case 3:
940         cpu_stl_data_ra(env, addr, data, ra);
941         break;
942     case 4:
943         cpu_stq_data_ra(env, addr, data, ra);
944         break;
945     default:
946         g_assert_not_reached();
947     }
948 }
949 
950 uint32_t HELPER(bfexts_mem)(CPUM68KState *env, uint32_t addr,
951                             int32_t ofs, uint32_t len)
952 {
953     uintptr_t ra = GETPC();
954     struct bf_data d = bf_prep(addr, ofs, len);
955     uint64_t data = bf_load(env, d.addr, d.blen, ra);
956 
957     return (int64_t)(data << d.bofs) >> (64 - d.len);
958 }
959 
960 uint64_t HELPER(bfextu_mem)(CPUM68KState *env, uint32_t addr,
961                             int32_t ofs, uint32_t len)
962 {
963     uintptr_t ra = GETPC();
964     struct bf_data d = bf_prep(addr, ofs, len);
965     uint64_t data = bf_load(env, d.addr, d.blen, ra);
966 
967     /*
968      * Put CC_N at the top of the high word; put the zero-extended value
969      * at the bottom of the low word.
970      */
971     data <<= d.bofs;
972     data >>= 64 - d.len;
973     data |= data << (64 - d.len);
974 
975     return data;
976 }
977 
978 uint32_t HELPER(bfins_mem)(CPUM68KState *env, uint32_t addr, uint32_t val,
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     data = (data & ~mask) | (((uint64_t)val << (64 - d.len)) >> d.bofs);
987 
988     bf_store(env, d.addr, d.blen, data, ra);
989 
990     /* The field at the top of the word is also CC_N for CC_OP_LOGIC.  */
991     return val << (32 - d.len);
992 }
993 
994 uint32_t HELPER(bfchg_mem)(CPUM68KState *env, uint32_t addr,
995                            int32_t ofs, uint32_t len)
996 {
997     uintptr_t ra = GETPC();
998     struct bf_data d = bf_prep(addr, ofs, len);
999     uint64_t data = bf_load(env, d.addr, d.blen, ra);
1000     uint64_t mask = -1ull << (64 - d.len) >> d.bofs;
1001 
1002     bf_store(env, d.addr, d.blen, data ^ mask, ra);
1003 
1004     return ((data & mask) << d.bofs) >> 32;
1005 }
1006 
1007 uint32_t HELPER(bfclr_mem)(CPUM68KState *env, uint32_t addr,
1008                            int32_t ofs, uint32_t len)
1009 {
1010     uintptr_t ra = GETPC();
1011     struct bf_data d = bf_prep(addr, ofs, len);
1012     uint64_t data = bf_load(env, d.addr, d.blen, ra);
1013     uint64_t mask = -1ull << (64 - d.len) >> d.bofs;
1014 
1015     bf_store(env, d.addr, d.blen, data & ~mask, ra);
1016 
1017     return ((data & mask) << d.bofs) >> 32;
1018 }
1019 
1020 uint32_t HELPER(bfset_mem)(CPUM68KState *env, uint32_t addr,
1021                            int32_t ofs, uint32_t len)
1022 {
1023     uintptr_t ra = GETPC();
1024     struct bf_data d = bf_prep(addr, ofs, len);
1025     uint64_t data = bf_load(env, d.addr, d.blen, ra);
1026     uint64_t mask = -1ull << (64 - d.len) >> d.bofs;
1027 
1028     bf_store(env, d.addr, d.blen, data | mask, ra);
1029 
1030     return ((data & mask) << d.bofs) >> 32;
1031 }
1032 
1033 uint32_t HELPER(bfffo_reg)(uint32_t n, uint32_t ofs, uint32_t len)
1034 {
1035     return (n ? clz32(n) : len) + ofs;
1036 }
1037 
1038 uint64_t HELPER(bfffo_mem)(CPUM68KState *env, uint32_t addr,
1039                            int32_t ofs, uint32_t len)
1040 {
1041     uintptr_t ra = GETPC();
1042     struct bf_data d = bf_prep(addr, ofs, len);
1043     uint64_t data = bf_load(env, d.addr, d.blen, ra);
1044     uint64_t mask = -1ull << (64 - d.len) >> d.bofs;
1045     uint64_t n = (data & mask) << d.bofs;
1046     uint32_t ffo = helper_bfffo_reg(n >> 32, ofs, d.len);
1047 
1048     /*
1049      * Return FFO in the low word and N in the high word.
1050      * Note that because of MASK and the shift, the low word
1051      * is already zero.
1052      */
1053     return n | ffo;
1054 }
1055 
1056 void HELPER(chk)(CPUM68KState *env, int32_t val, int32_t ub)
1057 {
1058     /*
1059      * From the specs:
1060      *   X: Not affected, C,V,Z: Undefined,
1061      *   N: Set if val < 0; cleared if val > ub, undefined otherwise
1062      * We implement here values found from a real MC68040:
1063      *   X,V,Z: Not affected
1064      *   N: Set if val < 0; cleared if val >= 0
1065      *   C: if 0 <= ub: set if val < 0 or val > ub, cleared otherwise
1066      *      if 0 > ub: set if val > ub and val < 0, cleared otherwise
1067      */
1068     env->cc_n = val;
1069     env->cc_c = 0 <= ub ? val < 0 || val > ub : val > ub && val < 0;
1070 
1071     if (val < 0 || val > ub) {
1072         CPUState *cs = env_cpu(env);
1073 
1074         /* Recover PC and CC_OP for the beginning of the insn.  */
1075         cpu_restore_state(cs, GETPC(), true);
1076 
1077         /* flags have been modified by gen_flush_flags() */
1078         env->cc_op = CC_OP_FLAGS;
1079         /* Adjust PC to end of the insn.  */
1080         env->pc += 2;
1081 
1082         cs->exception_index = EXCP_CHK;
1083         cpu_loop_exit(cs);
1084     }
1085 }
1086 
1087 void HELPER(chk2)(CPUM68KState *env, int32_t val, int32_t lb, int32_t ub)
1088 {
1089     /*
1090      * From the specs:
1091      *   X: Not affected, N,V: Undefined,
1092      *   Z: Set if val is equal to lb or ub
1093      *   C: Set if val < lb or val > ub, cleared otherwise
1094      * We implement here values found from a real MC68040:
1095      *   X,N,V: Not affected
1096      *   Z: Set if val is equal to lb or ub
1097      *   C: if lb <= ub: set if val < lb or val > ub, cleared otherwise
1098      *      if lb > ub: set if val > ub and val < lb, cleared otherwise
1099      */
1100     env->cc_z = val != lb && val != ub;
1101     env->cc_c = lb <= ub ? val < lb || val > ub : val > ub && val < lb;
1102 
1103     if (env->cc_c) {
1104         CPUState *cs = env_cpu(env);
1105 
1106         /* Recover PC and CC_OP for the beginning of the insn.  */
1107         cpu_restore_state(cs, GETPC(), true);
1108 
1109         /* flags have been modified by gen_flush_flags() */
1110         env->cc_op = CC_OP_FLAGS;
1111         /* Adjust PC to end of the insn.  */
1112         env->pc += 4;
1113 
1114         cs->exception_index = EXCP_CHK;
1115         cpu_loop_exit(cs);
1116     }
1117 }
1118