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