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