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