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