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