xref: /openbmc/qemu/target/m68k/op_helper.c (revision 98670d47)
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_ADDRESS) {
364         do_stack_frame(env, &sp, 2, oldsr, 0, retaddr);
365     } else if (cs->exception_index == EXCP_ILLEGAL ||
366                cs->exception_index == EXCP_DIV0 ||
367                cs->exception_index == EXCP_CHK ||
368                cs->exception_index == EXCP_TRAPCC ||
369                cs->exception_index == EXCP_TRACE) {
370         /* FIXME: addr is not only env->pc */
371         do_stack_frame(env, &sp, 2, oldsr, env->pc, retaddr);
372     } else if (is_hw && oldsr & SR_M &&
373                cs->exception_index >= EXCP_SPURIOUS &&
374                cs->exception_index <= EXCP_INT_LEVEL_7) {
375         do_stack_frame(env, &sp, 0, oldsr, 0, retaddr);
376         oldsr = sr;
377         env->aregs[7] = sp;
378         cpu_m68k_set_sr(env, sr &= ~SR_M);
379         sp = env->aregs[7] & ~1;
380         do_stack_frame(env, &sp, 1, oldsr, 0, retaddr);
381     } else {
382         do_stack_frame(env, &sp, 0, oldsr, 0, retaddr);
383     }
384 
385     env->aregs[7] = sp;
386     /* Jump to vector.  */
387     env->pc = cpu_ldl_kernel(env, env->vbr + vector);
388 }
389 
390 static void do_interrupt_all(CPUM68KState *env, int is_hw)
391 {
392     if (m68k_feature(env, M68K_FEATURE_M68000)) {
393         m68k_interrupt_all(env, is_hw);
394         return;
395     }
396     cf_interrupt_all(env, is_hw);
397 }
398 
399 void m68k_cpu_do_interrupt(CPUState *cs)
400 {
401     M68kCPU *cpu = M68K_CPU(cs);
402     CPUM68KState *env = &cpu->env;
403 
404     do_interrupt_all(env, 0);
405 }
406 
407 static inline void do_interrupt_m68k_hardirq(CPUM68KState *env)
408 {
409     do_interrupt_all(env, 1);
410 }
411 #endif
412 
413 bool m68k_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
414 {
415     M68kCPU *cpu = M68K_CPU(cs);
416     CPUM68KState *env = &cpu->env;
417 
418     if (interrupt_request & CPU_INTERRUPT_HARD
419         && ((env->sr & SR_I) >> SR_I_SHIFT) < env->pending_level) {
420         /* Real hardware gets the interrupt vector via an IACK cycle
421            at this point.  Current emulated hardware doesn't rely on
422            this, so we provide/save the vector when the interrupt is
423            first signalled.  */
424         cs->exception_index = env->pending_vector;
425         do_interrupt_m68k_hardirq(env);
426         return true;
427     }
428     return false;
429 }
430 
431 static void raise_exception_ra(CPUM68KState *env, int tt, uintptr_t raddr)
432 {
433     CPUState *cs = CPU(m68k_env_get_cpu(env));
434 
435     cs->exception_index = tt;
436     cpu_loop_exit_restore(cs, raddr);
437 }
438 
439 static void raise_exception(CPUM68KState *env, int tt)
440 {
441     raise_exception_ra(env, tt, 0);
442 }
443 
444 void HELPER(raise_exception)(CPUM68KState *env, uint32_t tt)
445 {
446     raise_exception(env, tt);
447 }
448 
449 void HELPER(divuw)(CPUM68KState *env, int destr, uint32_t den)
450 {
451     uint32_t num = env->dregs[destr];
452     uint32_t quot, rem;
453 
454     if (den == 0) {
455         raise_exception_ra(env, EXCP_DIV0, GETPC());
456     }
457     quot = num / den;
458     rem = num % den;
459 
460     env->cc_c = 0; /* always cleared, even if overflow */
461     if (quot > 0xffff) {
462         env->cc_v = -1;
463         /* real 68040 keeps N and unset Z on overflow,
464          * whereas documentation says "undefined"
465          */
466         env->cc_z = 1;
467         return;
468     }
469     env->dregs[destr] = deposit32(quot, 16, 16, rem);
470     env->cc_z = (int16_t)quot;
471     env->cc_n = (int16_t)quot;
472     env->cc_v = 0;
473 }
474 
475 void HELPER(divsw)(CPUM68KState *env, int destr, int32_t den)
476 {
477     int32_t num = env->dregs[destr];
478     uint32_t quot, rem;
479 
480     if (den == 0) {
481         raise_exception_ra(env, EXCP_DIV0, GETPC());
482     }
483     quot = num / den;
484     rem = num % den;
485 
486     env->cc_c = 0; /* always cleared, even if overflow */
487     if (quot != (int16_t)quot) {
488         env->cc_v = -1;
489         /* nothing else is modified */
490         /* real 68040 keeps N and unset Z on overflow,
491          * whereas documentation says "undefined"
492          */
493         env->cc_z = 1;
494         return;
495     }
496     env->dregs[destr] = deposit32(quot, 16, 16, rem);
497     env->cc_z = (int16_t)quot;
498     env->cc_n = (int16_t)quot;
499     env->cc_v = 0;
500 }
501 
502 void HELPER(divul)(CPUM68KState *env, int numr, int regr, uint32_t den)
503 {
504     uint32_t num = env->dregs[numr];
505     uint32_t quot, rem;
506 
507     if (den == 0) {
508         raise_exception_ra(env, EXCP_DIV0, GETPC());
509     }
510     quot = num / den;
511     rem = num % den;
512 
513     env->cc_c = 0;
514     env->cc_z = quot;
515     env->cc_n = quot;
516     env->cc_v = 0;
517 
518     if (m68k_feature(env, M68K_FEATURE_CF_ISA_A)) {
519         if (numr == regr) {
520             env->dregs[numr] = quot;
521         } else {
522             env->dregs[regr] = rem;
523         }
524     } else {
525         env->dregs[regr] = rem;
526         env->dregs[numr] = quot;
527     }
528 }
529 
530 void HELPER(divsl)(CPUM68KState *env, int numr, int regr, int32_t den)
531 {
532     int32_t num = env->dregs[numr];
533     int32_t quot, rem;
534 
535     if (den == 0) {
536         raise_exception_ra(env, EXCP_DIV0, GETPC());
537     }
538     quot = num / den;
539     rem = num % den;
540 
541     env->cc_c = 0;
542     env->cc_z = quot;
543     env->cc_n = quot;
544     env->cc_v = 0;
545 
546     if (m68k_feature(env, M68K_FEATURE_CF_ISA_A)) {
547         if (numr == regr) {
548             env->dregs[numr] = quot;
549         } else {
550             env->dregs[regr] = rem;
551         }
552     } else {
553         env->dregs[regr] = rem;
554         env->dregs[numr] = quot;
555     }
556 }
557 
558 void HELPER(divull)(CPUM68KState *env, int numr, int regr, uint32_t den)
559 {
560     uint64_t num = deposit64(env->dregs[numr], 32, 32, env->dregs[regr]);
561     uint64_t quot;
562     uint32_t rem;
563 
564     if (den == 0) {
565         raise_exception_ra(env, EXCP_DIV0, GETPC());
566     }
567     quot = num / den;
568     rem = num % den;
569 
570     env->cc_c = 0; /* always cleared, even if overflow */
571     if (quot > 0xffffffffULL) {
572         env->cc_v = -1;
573         /* real 68040 keeps N and unset Z on overflow,
574          * whereas documentation says "undefined"
575          */
576         env->cc_z = 1;
577         return;
578     }
579     env->cc_z = quot;
580     env->cc_n = quot;
581     env->cc_v = 0;
582 
583     /*
584      * If Dq and Dr are the same, the quotient is returned.
585      * therefore we set Dq last.
586      */
587 
588     env->dregs[regr] = rem;
589     env->dregs[numr] = quot;
590 }
591 
592 void HELPER(divsll)(CPUM68KState *env, int numr, int regr, int32_t den)
593 {
594     int64_t num = deposit64(env->dregs[numr], 32, 32, env->dregs[regr]);
595     int64_t quot;
596     int32_t rem;
597 
598     if (den == 0) {
599         raise_exception_ra(env, EXCP_DIV0, GETPC());
600     }
601     quot = num / den;
602     rem = num % den;
603 
604     env->cc_c = 0; /* always cleared, even if overflow */
605     if (quot != (int32_t)quot) {
606         env->cc_v = -1;
607         /* real 68040 keeps N and unset Z on overflow,
608          * whereas documentation says "undefined"
609          */
610         env->cc_z = 1;
611         return;
612     }
613     env->cc_z = quot;
614     env->cc_n = quot;
615     env->cc_v = 0;
616 
617     /*
618      * If Dq and Dr are the same, the quotient is returned.
619      * therefore we set Dq last.
620      */
621 
622     env->dregs[regr] = rem;
623     env->dregs[numr] = quot;
624 }
625 
626 /* We're executing in a serial context -- no need to be atomic.  */
627 void HELPER(cas2w)(CPUM68KState *env, uint32_t regs, uint32_t a1, uint32_t a2)
628 {
629     uint32_t Dc1 = extract32(regs, 9, 3);
630     uint32_t Dc2 = extract32(regs, 6, 3);
631     uint32_t Du1 = extract32(regs, 3, 3);
632     uint32_t Du2 = extract32(regs, 0, 3);
633     int16_t c1 = env->dregs[Dc1];
634     int16_t c2 = env->dregs[Dc2];
635     int16_t u1 = env->dregs[Du1];
636     int16_t u2 = env->dregs[Du2];
637     int16_t l1, l2;
638     uintptr_t ra = GETPC();
639 
640     l1 = cpu_lduw_data_ra(env, a1, ra);
641     l2 = cpu_lduw_data_ra(env, a2, ra);
642     if (l1 == c1 && l2 == c2) {
643         cpu_stw_data_ra(env, a1, u1, ra);
644         cpu_stw_data_ra(env, a2, u2, ra);
645     }
646 
647     if (c1 != l1) {
648         env->cc_n = l1;
649         env->cc_v = c1;
650     } else {
651         env->cc_n = l2;
652         env->cc_v = c2;
653     }
654     env->cc_op = CC_OP_CMPW;
655     env->dregs[Dc1] = deposit32(env->dregs[Dc1], 0, 16, l1);
656     env->dregs[Dc2] = deposit32(env->dregs[Dc2], 0, 16, l2);
657 }
658 
659 static void do_cas2l(CPUM68KState *env, uint32_t regs, uint32_t a1, uint32_t a2,
660                      bool parallel)
661 {
662     uint32_t Dc1 = extract32(regs, 9, 3);
663     uint32_t Dc2 = extract32(regs, 6, 3);
664     uint32_t Du1 = extract32(regs, 3, 3);
665     uint32_t Du2 = extract32(regs, 0, 3);
666     uint32_t c1 = env->dregs[Dc1];
667     uint32_t c2 = env->dregs[Dc2];
668     uint32_t u1 = env->dregs[Du1];
669     uint32_t u2 = env->dregs[Du2];
670     uint32_t l1, l2;
671     uintptr_t ra = GETPC();
672 #if defined(CONFIG_ATOMIC64) && !defined(CONFIG_USER_ONLY)
673     int mmu_idx = cpu_mmu_index(env, 0);
674     TCGMemOpIdx oi;
675 #endif
676 
677     if (parallel) {
678         /* We're executing in a parallel context -- must be atomic.  */
679 #ifdef CONFIG_ATOMIC64
680         uint64_t c, u, l;
681         if ((a1 & 7) == 0 && a2 == a1 + 4) {
682             c = deposit64(c2, 32, 32, c1);
683             u = deposit64(u2, 32, 32, u1);
684 #ifdef CONFIG_USER_ONLY
685             l = helper_atomic_cmpxchgq_be(env, a1, c, u);
686 #else
687             oi = make_memop_idx(MO_BEQ, mmu_idx);
688             l = helper_atomic_cmpxchgq_be_mmu(env, a1, c, u, oi, ra);
689 #endif
690             l1 = l >> 32;
691             l2 = l;
692         } else if ((a2 & 7) == 0 && a1 == a2 + 4) {
693             c = deposit64(c1, 32, 32, c2);
694             u = deposit64(u1, 32, 32, u2);
695 #ifdef CONFIG_USER_ONLY
696             l = helper_atomic_cmpxchgq_be(env, a2, c, u);
697 #else
698             oi = make_memop_idx(MO_BEQ, mmu_idx);
699             l = helper_atomic_cmpxchgq_be_mmu(env, a2, c, u, oi, ra);
700 #endif
701             l2 = l >> 32;
702             l1 = l;
703         } else
704 #endif
705         {
706             /* Tell the main loop we need to serialize this insn.  */
707             cpu_loop_exit_atomic(ENV_GET_CPU(env), ra);
708         }
709     } else {
710         /* We're executing in a serial context -- no need to be atomic.  */
711         l1 = cpu_ldl_data_ra(env, a1, ra);
712         l2 = cpu_ldl_data_ra(env, a2, ra);
713         if (l1 == c1 && l2 == c2) {
714             cpu_stl_data_ra(env, a1, u1, ra);
715             cpu_stl_data_ra(env, a2, u2, ra);
716         }
717     }
718 
719     if (c1 != l1) {
720         env->cc_n = l1;
721         env->cc_v = c1;
722     } else {
723         env->cc_n = l2;
724         env->cc_v = c2;
725     }
726     env->cc_op = CC_OP_CMPL;
727     env->dregs[Dc1] = l1;
728     env->dregs[Dc2] = l2;
729 }
730 
731 void HELPER(cas2l)(CPUM68KState *env, uint32_t regs, uint32_t a1, uint32_t a2)
732 {
733     do_cas2l(env, regs, a1, a2, false);
734 }
735 
736 void HELPER(cas2l_parallel)(CPUM68KState *env, uint32_t regs, uint32_t a1,
737                             uint32_t a2)
738 {
739     do_cas2l(env, regs, a1, a2, true);
740 }
741 
742 struct bf_data {
743     uint32_t addr;
744     uint32_t bofs;
745     uint32_t blen;
746     uint32_t len;
747 };
748 
749 static struct bf_data bf_prep(uint32_t addr, int32_t ofs, uint32_t len)
750 {
751     int bofs, blen;
752 
753     /* Bound length; map 0 to 32.  */
754     len = ((len - 1) & 31) + 1;
755 
756     /* Note that ofs is signed.  */
757     addr += ofs / 8;
758     bofs = ofs % 8;
759     if (bofs < 0) {
760         bofs += 8;
761         addr -= 1;
762     }
763 
764     /* Compute the number of bytes required (minus one) to
765        satisfy the bitfield.  */
766     blen = (bofs + len - 1) / 8;
767 
768     /* Canonicalize the bit offset for data loaded into a 64-bit big-endian
769        word.  For the cases where BLEN is not a power of 2, adjust ADDR so
770        that we can use the next power of two sized load without crossing a
771        page boundary, unless the field itself crosses the boundary.  */
772     switch (blen) {
773     case 0:
774         bofs += 56;
775         break;
776     case 1:
777         bofs += 48;
778         break;
779     case 2:
780         if (addr & 1) {
781             bofs += 8;
782             addr -= 1;
783         }
784         /* fallthru */
785     case 3:
786         bofs += 32;
787         break;
788     case 4:
789         if (addr & 3) {
790             bofs += 8 * (addr & 3);
791             addr &= -4;
792         }
793         break;
794     default:
795         g_assert_not_reached();
796     }
797 
798     return (struct bf_data){
799         .addr = addr,
800         .bofs = bofs,
801         .blen = blen,
802         .len = len,
803     };
804 }
805 
806 static uint64_t bf_load(CPUM68KState *env, uint32_t addr, int blen,
807                         uintptr_t ra)
808 {
809     switch (blen) {
810     case 0:
811         return cpu_ldub_data_ra(env, addr, ra);
812     case 1:
813         return cpu_lduw_data_ra(env, addr, ra);
814     case 2:
815     case 3:
816         return cpu_ldl_data_ra(env, addr, ra);
817     case 4:
818         return cpu_ldq_data_ra(env, addr, ra);
819     default:
820         g_assert_not_reached();
821     }
822 }
823 
824 static void bf_store(CPUM68KState *env, uint32_t addr, int blen,
825                      uint64_t data, uintptr_t ra)
826 {
827     switch (blen) {
828     case 0:
829         cpu_stb_data_ra(env, addr, data, ra);
830         break;
831     case 1:
832         cpu_stw_data_ra(env, addr, data, ra);
833         break;
834     case 2:
835     case 3:
836         cpu_stl_data_ra(env, addr, data, ra);
837         break;
838     case 4:
839         cpu_stq_data_ra(env, addr, data, ra);
840         break;
841     default:
842         g_assert_not_reached();
843     }
844 }
845 
846 uint32_t HELPER(bfexts_mem)(CPUM68KState *env, uint32_t addr,
847                             int32_t ofs, uint32_t len)
848 {
849     uintptr_t ra = GETPC();
850     struct bf_data d = bf_prep(addr, ofs, len);
851     uint64_t data = bf_load(env, d.addr, d.blen, ra);
852 
853     return (int64_t)(data << d.bofs) >> (64 - d.len);
854 }
855 
856 uint64_t HELPER(bfextu_mem)(CPUM68KState *env, uint32_t addr,
857                             int32_t ofs, uint32_t len)
858 {
859     uintptr_t ra = GETPC();
860     struct bf_data d = bf_prep(addr, ofs, len);
861     uint64_t data = bf_load(env, d.addr, d.blen, ra);
862 
863     /* Put CC_N at the top of the high word; put the zero-extended value
864        at the bottom of the low word.  */
865     data <<= d.bofs;
866     data >>= 64 - d.len;
867     data |= data << (64 - d.len);
868 
869     return data;
870 }
871 
872 uint32_t HELPER(bfins_mem)(CPUM68KState *env, uint32_t addr, uint32_t val,
873                            int32_t ofs, uint32_t len)
874 {
875     uintptr_t ra = GETPC();
876     struct bf_data d = bf_prep(addr, ofs, len);
877     uint64_t data = bf_load(env, d.addr, d.blen, ra);
878     uint64_t mask = -1ull << (64 - d.len) >> d.bofs;
879 
880     data = (data & ~mask) | (((uint64_t)val << (64 - d.len)) >> d.bofs);
881 
882     bf_store(env, d.addr, d.blen, data, ra);
883 
884     /* The field at the top of the word is also CC_N for CC_OP_LOGIC.  */
885     return val << (32 - d.len);
886 }
887 
888 uint32_t HELPER(bfchg_mem)(CPUM68KState *env, uint32_t addr,
889                            int32_t ofs, uint32_t len)
890 {
891     uintptr_t ra = GETPC();
892     struct bf_data d = bf_prep(addr, ofs, len);
893     uint64_t data = bf_load(env, d.addr, d.blen, ra);
894     uint64_t mask = -1ull << (64 - d.len) >> d.bofs;
895 
896     bf_store(env, d.addr, d.blen, data ^ mask, ra);
897 
898     return ((data & mask) << d.bofs) >> 32;
899 }
900 
901 uint32_t HELPER(bfclr_mem)(CPUM68KState *env, uint32_t addr,
902                            int32_t ofs, uint32_t len)
903 {
904     uintptr_t ra = GETPC();
905     struct bf_data d = bf_prep(addr, ofs, len);
906     uint64_t data = bf_load(env, d.addr, d.blen, ra);
907     uint64_t mask = -1ull << (64 - d.len) >> d.bofs;
908 
909     bf_store(env, d.addr, d.blen, data & ~mask, ra);
910 
911     return ((data & mask) << d.bofs) >> 32;
912 }
913 
914 uint32_t HELPER(bfset_mem)(CPUM68KState *env, uint32_t addr,
915                            int32_t ofs, uint32_t len)
916 {
917     uintptr_t ra = GETPC();
918     struct bf_data d = bf_prep(addr, ofs, len);
919     uint64_t data = bf_load(env, d.addr, d.blen, ra);
920     uint64_t mask = -1ull << (64 - d.len) >> d.bofs;
921 
922     bf_store(env, d.addr, d.blen, data | mask, ra);
923 
924     return ((data & mask) << d.bofs) >> 32;
925 }
926 
927 uint32_t HELPER(bfffo_reg)(uint32_t n, uint32_t ofs, uint32_t len)
928 {
929     return (n ? clz32(n) : len) + ofs;
930 }
931 
932 uint64_t HELPER(bfffo_mem)(CPUM68KState *env, uint32_t addr,
933                            int32_t ofs, uint32_t len)
934 {
935     uintptr_t ra = GETPC();
936     struct bf_data d = bf_prep(addr, ofs, len);
937     uint64_t data = bf_load(env, d.addr, d.blen, ra);
938     uint64_t mask = -1ull << (64 - d.len) >> d.bofs;
939     uint64_t n = (data & mask) << d.bofs;
940     uint32_t ffo = helper_bfffo_reg(n >> 32, ofs, d.len);
941 
942     /* Return FFO in the low word and N in the high word.
943        Note that because of MASK and the shift, the low word
944        is already zero.  */
945     return n | ffo;
946 }
947 
948 void HELPER(chk)(CPUM68KState *env, int32_t val, int32_t ub)
949 {
950     /* From the specs:
951      *   X: Not affected, C,V,Z: Undefined,
952      *   N: Set if val < 0; cleared if val > ub, undefined otherwise
953      * We implement here values found from a real MC68040:
954      *   X,V,Z: Not affected
955      *   N: Set if val < 0; cleared if val >= 0
956      *   C: if 0 <= ub: set if val < 0 or val > ub, cleared otherwise
957      *      if 0 > ub: set if val > ub and val < 0, cleared otherwise
958      */
959     env->cc_n = val;
960     env->cc_c = 0 <= ub ? val < 0 || val > ub : val > ub && val < 0;
961 
962     if (val < 0 || val > ub) {
963         CPUState *cs = CPU(m68k_env_get_cpu(env));
964 
965         /* Recover PC and CC_OP for the beginning of the insn.  */
966         cpu_restore_state(cs, GETPC());
967 
968         /* flags have been modified by gen_flush_flags() */
969         env->cc_op = CC_OP_FLAGS;
970         /* Adjust PC to end of the insn.  */
971         env->pc += 2;
972 
973         cs->exception_index = EXCP_CHK;
974         cpu_loop_exit(cs);
975     }
976 }
977 
978 void HELPER(chk2)(CPUM68KState *env, int32_t val, int32_t lb, int32_t ub)
979 {
980     /* From the specs:
981      *   X: Not affected, N,V: Undefined,
982      *   Z: Set if val is equal to lb or ub
983      *   C: Set if val < lb or val > ub, cleared otherwise
984      * We implement here values found from a real MC68040:
985      *   X,N,V: Not affected
986      *   Z: Set if val is equal to lb or ub
987      *   C: if lb <= ub: set if val < lb or val > ub, cleared otherwise
988      *      if lb > ub: set if val > ub and val < lb, cleared otherwise
989      */
990     env->cc_z = val != lb && val != ub;
991     env->cc_c = lb <= ub ? val < lb || val > ub : val > ub && val < lb;
992 
993     if (env->cc_c) {
994         CPUState *cs = CPU(m68k_env_get_cpu(env));
995 
996         /* Recover PC and CC_OP for the beginning of the insn.  */
997         cpu_restore_state(cs, GETPC());
998 
999         /* flags have been modified by gen_flush_flags() */
1000         env->cc_op = CC_OP_FLAGS;
1001         /* Adjust PC to end of the insn.  */
1002         env->pc += 4;
1003 
1004         cs->exception_index = EXCP_CHK;
1005         cpu_loop_exit(cs);
1006     }
1007 }
1008