xref: /openbmc/qemu/target/m68k/helper.c (revision db7a99cd)
1 /*
2  *  m68k op helpers
3  *
4  *  Copyright (c) 2006-2007 CodeSourcery
5  *  Written by Paul Brook
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include "qemu/osdep.h"
22 #include "cpu.h"
23 #include "exec/exec-all.h"
24 #include "exec/gdbstub.h"
25 
26 #include "exec/helper-proto.h"
27 
28 #define SIGNBIT (1u << 31)
29 
30 /* Sort alphabetically, except for "any". */
31 static gint m68k_cpu_list_compare(gconstpointer a, gconstpointer b)
32 {
33     ObjectClass *class_a = (ObjectClass *)a;
34     ObjectClass *class_b = (ObjectClass *)b;
35     const char *name_a, *name_b;
36 
37     name_a = object_class_get_name(class_a);
38     name_b = object_class_get_name(class_b);
39     if (strcmp(name_a, "any-" TYPE_M68K_CPU) == 0) {
40         return 1;
41     } else if (strcmp(name_b, "any-" TYPE_M68K_CPU) == 0) {
42         return -1;
43     } else {
44         return strcasecmp(name_a, name_b);
45     }
46 }
47 
48 static void m68k_cpu_list_entry(gpointer data, gpointer user_data)
49 {
50     ObjectClass *c = data;
51     CPUListState *s = user_data;
52     const char *typename;
53     char *name;
54 
55     typename = object_class_get_name(c);
56     name = g_strndup(typename, strlen(typename) - strlen("-" TYPE_M68K_CPU));
57     (*s->cpu_fprintf)(s->file, "%s\n",
58                       name);
59     g_free(name);
60 }
61 
62 void m68k_cpu_list(FILE *f, fprintf_function cpu_fprintf)
63 {
64     CPUListState s = {
65         .file = f,
66         .cpu_fprintf = cpu_fprintf,
67     };
68     GSList *list;
69 
70     list = object_class_get_list(TYPE_M68K_CPU, false);
71     list = g_slist_sort(list, m68k_cpu_list_compare);
72     g_slist_foreach(list, m68k_cpu_list_entry, &s);
73     g_slist_free(list);
74 }
75 
76 static int fpu_gdb_get_reg(CPUM68KState *env, uint8_t *mem_buf, int n)
77 {
78     if (n < 8) {
79         stfq_p(mem_buf, env->fregs[n]);
80         return 8;
81     }
82     if (n < 11) {
83         /* FP control registers (not implemented)  */
84         memset(mem_buf, 0, 4);
85         return 4;
86     }
87     return 0;
88 }
89 
90 static int fpu_gdb_set_reg(CPUM68KState *env, uint8_t *mem_buf, int n)
91 {
92     if (n < 8) {
93         env->fregs[n] = ldfq_p(mem_buf);
94         return 8;
95     }
96     if (n < 11) {
97         /* FP control registers (not implemented)  */
98         return 4;
99     }
100     return 0;
101 }
102 
103 M68kCPU *cpu_m68k_init(const char *cpu_model)
104 {
105     M68kCPU *cpu;
106     CPUM68KState *env;
107     ObjectClass *oc;
108 
109     oc = cpu_class_by_name(TYPE_M68K_CPU, cpu_model);
110     if (oc == NULL) {
111         return NULL;
112     }
113     cpu = M68K_CPU(object_new(object_class_get_name(oc)));
114     env = &cpu->env;
115 
116     register_m68k_insns(env);
117 
118     object_property_set_bool(OBJECT(cpu), true, "realized", NULL);
119 
120     return cpu;
121 }
122 
123 void m68k_cpu_init_gdb(M68kCPU *cpu)
124 {
125     CPUState *cs = CPU(cpu);
126     CPUM68KState *env = &cpu->env;
127 
128     if (m68k_feature(env, M68K_FEATURE_CF_FPU)) {
129         gdb_register_coprocessor(cs, fpu_gdb_get_reg, fpu_gdb_set_reg,
130                                  11, "cf-fp.xml", 18);
131     }
132     /* TODO: Add [E]MAC registers.  */
133 }
134 
135 void HELPER(movec)(CPUM68KState *env, uint32_t reg, uint32_t val)
136 {
137     M68kCPU *cpu = m68k_env_get_cpu(env);
138 
139     switch (reg) {
140     case 0x02: /* CACR */
141         env->cacr = val;
142         m68k_switch_sp(env);
143         break;
144     case 0x04: case 0x05: case 0x06: case 0x07: /* ACR[0-3] */
145         /* TODO: Implement Access Control Registers.  */
146         break;
147     case 0x801: /* VBR */
148         env->vbr = val;
149         break;
150     /* TODO: Implement control registers.  */
151     default:
152         cpu_abort(CPU(cpu), "Unimplemented control register write 0x%x = 0x%x\n",
153                   reg, val);
154     }
155 }
156 
157 void HELPER(set_macsr)(CPUM68KState *env, uint32_t val)
158 {
159     uint32_t acc;
160     int8_t exthigh;
161     uint8_t extlow;
162     uint64_t regval;
163     int i;
164     if ((env->macsr ^ val) & (MACSR_FI | MACSR_SU)) {
165         for (i = 0; i < 4; i++) {
166             regval = env->macc[i];
167             exthigh = regval >> 40;
168             if (env->macsr & MACSR_FI) {
169                 acc = regval >> 8;
170                 extlow = regval;
171             } else {
172                 acc = regval;
173                 extlow = regval >> 32;
174             }
175             if (env->macsr & MACSR_FI) {
176                 regval = (((uint64_t)acc) << 8) | extlow;
177                 regval |= ((int64_t)exthigh) << 40;
178             } else if (env->macsr & MACSR_SU) {
179                 regval = acc | (((int64_t)extlow) << 32);
180                 regval |= ((int64_t)exthigh) << 40;
181             } else {
182                 regval = acc | (((uint64_t)extlow) << 32);
183                 regval |= ((uint64_t)(uint8_t)exthigh) << 40;
184             }
185             env->macc[i] = regval;
186         }
187     }
188     env->macsr = val;
189 }
190 
191 void m68k_switch_sp(CPUM68KState *env)
192 {
193     int new_sp;
194 
195     env->sp[env->current_sp] = env->aregs[7];
196     new_sp = (env->sr & SR_S && env->cacr & M68K_CACR_EUSP)
197              ? M68K_SSP : M68K_USP;
198     env->aregs[7] = env->sp[new_sp];
199     env->current_sp = new_sp;
200 }
201 
202 #if defined(CONFIG_USER_ONLY)
203 
204 int m68k_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw,
205                               int mmu_idx)
206 {
207     M68kCPU *cpu = M68K_CPU(cs);
208 
209     cs->exception_index = EXCP_ACCESS;
210     cpu->env.mmu.ar = address;
211     return 1;
212 }
213 
214 #else
215 
216 /* MMU */
217 
218 /* TODO: This will need fixing once the MMU is implemented.  */
219 hwaddr m68k_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
220 {
221     return addr;
222 }
223 
224 int m68k_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw,
225                               int mmu_idx)
226 {
227     int prot;
228 
229     address &= TARGET_PAGE_MASK;
230     prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
231     tlb_set_page(cs, address, address, prot, mmu_idx, TARGET_PAGE_SIZE);
232     return 0;
233 }
234 
235 /* Notify CPU of a pending interrupt.  Prioritization and vectoring should
236    be handled by the interrupt controller.  Real hardware only requests
237    the vector when the interrupt is acknowledged by the CPU.  For
238    simplicitly we calculate it when the interrupt is signalled.  */
239 void m68k_set_irq_level(M68kCPU *cpu, int level, uint8_t vector)
240 {
241     CPUState *cs = CPU(cpu);
242     CPUM68KState *env = &cpu->env;
243 
244     env->pending_level = level;
245     env->pending_vector = vector;
246     if (level) {
247         cpu_interrupt(cs, CPU_INTERRUPT_HARD);
248     } else {
249         cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
250     }
251 }
252 
253 #endif
254 
255 uint32_t HELPER(bitrev)(uint32_t x)
256 {
257     x = ((x >> 1) & 0x55555555u) | ((x << 1) & 0xaaaaaaaau);
258     x = ((x >> 2) & 0x33333333u) | ((x << 2) & 0xccccccccu);
259     x = ((x >> 4) & 0x0f0f0f0fu) | ((x << 4) & 0xf0f0f0f0u);
260     return bswap32(x);
261 }
262 
263 uint32_t HELPER(ff1)(uint32_t x)
264 {
265     int n;
266     for (n = 32; x; n--)
267         x >>= 1;
268     return n;
269 }
270 
271 uint32_t HELPER(sats)(uint32_t val, uint32_t v)
272 {
273     /* The result has the opposite sign to the original value.  */
274     if ((int32_t)v < 0) {
275         val = (((int32_t)val) >> 31) ^ SIGNBIT;
276     }
277     return val;
278 }
279 
280 void HELPER(set_sr)(CPUM68KState *env, uint32_t val)
281 {
282     env->sr = val & 0xffe0;
283     cpu_m68k_set_ccr(env, val);
284     m68k_switch_sp(env);
285 }
286 
287 
288 /* MAC unit.  */
289 /* FIXME: The MAC unit implementation is a bit of a mess.  Some helpers
290    take values,  others take register numbers and manipulate the contents
291    in-place.  */
292 void HELPER(mac_move)(CPUM68KState *env, uint32_t dest, uint32_t src)
293 {
294     uint32_t mask;
295     env->macc[dest] = env->macc[src];
296     mask = MACSR_PAV0 << dest;
297     if (env->macsr & (MACSR_PAV0 << src))
298         env->macsr |= mask;
299     else
300         env->macsr &= ~mask;
301 }
302 
303 uint64_t HELPER(macmuls)(CPUM68KState *env, uint32_t op1, uint32_t op2)
304 {
305     int64_t product;
306     int64_t res;
307 
308     product = (uint64_t)op1 * op2;
309     res = (product << 24) >> 24;
310     if (res != product) {
311         env->macsr |= MACSR_V;
312         if (env->macsr & MACSR_OMC) {
313             /* Make sure the accumulate operation overflows.  */
314             if (product < 0)
315                 res = ~(1ll << 50);
316             else
317                 res = 1ll << 50;
318         }
319     }
320     return res;
321 }
322 
323 uint64_t HELPER(macmulu)(CPUM68KState *env, uint32_t op1, uint32_t op2)
324 {
325     uint64_t product;
326 
327     product = (uint64_t)op1 * op2;
328     if (product & (0xffffffull << 40)) {
329         env->macsr |= MACSR_V;
330         if (env->macsr & MACSR_OMC) {
331             /* Make sure the accumulate operation overflows.  */
332             product = 1ll << 50;
333         } else {
334             product &= ((1ull << 40) - 1);
335         }
336     }
337     return product;
338 }
339 
340 uint64_t HELPER(macmulf)(CPUM68KState *env, uint32_t op1, uint32_t op2)
341 {
342     uint64_t product;
343     uint32_t remainder;
344 
345     product = (uint64_t)op1 * op2;
346     if (env->macsr & MACSR_RT) {
347         remainder = product & 0xffffff;
348         product >>= 24;
349         if (remainder > 0x800000)
350             product++;
351         else if (remainder == 0x800000)
352             product += (product & 1);
353     } else {
354         product >>= 24;
355     }
356     return product;
357 }
358 
359 void HELPER(macsats)(CPUM68KState *env, uint32_t acc)
360 {
361     int64_t tmp;
362     int64_t result;
363     tmp = env->macc[acc];
364     result = ((tmp << 16) >> 16);
365     if (result != tmp) {
366         env->macsr |= MACSR_V;
367     }
368     if (env->macsr & MACSR_V) {
369         env->macsr |= MACSR_PAV0 << acc;
370         if (env->macsr & MACSR_OMC) {
371             /* The result is saturated to 32 bits, despite overflow occurring
372                at 48 bits.  Seems weird, but that's what the hardware docs
373                say.  */
374             result = (result >> 63) ^ 0x7fffffff;
375         }
376     }
377     env->macc[acc] = result;
378 }
379 
380 void HELPER(macsatu)(CPUM68KState *env, uint32_t acc)
381 {
382     uint64_t val;
383 
384     val = env->macc[acc];
385     if (val & (0xffffull << 48)) {
386         env->macsr |= MACSR_V;
387     }
388     if (env->macsr & MACSR_V) {
389         env->macsr |= MACSR_PAV0 << acc;
390         if (env->macsr & MACSR_OMC) {
391             if (val > (1ull << 53))
392                 val = 0;
393             else
394                 val = (1ull << 48) - 1;
395         } else {
396             val &= ((1ull << 48) - 1);
397         }
398     }
399     env->macc[acc] = val;
400 }
401 
402 void HELPER(macsatf)(CPUM68KState *env, uint32_t acc)
403 {
404     int64_t sum;
405     int64_t result;
406 
407     sum = env->macc[acc];
408     result = (sum << 16) >> 16;
409     if (result != sum) {
410         env->macsr |= MACSR_V;
411     }
412     if (env->macsr & MACSR_V) {
413         env->macsr |= MACSR_PAV0 << acc;
414         if (env->macsr & MACSR_OMC) {
415             result = (result >> 63) ^ 0x7fffffffffffll;
416         }
417     }
418     env->macc[acc] = result;
419 }
420 
421 void HELPER(mac_set_flags)(CPUM68KState *env, uint32_t acc)
422 {
423     uint64_t val;
424     val = env->macc[acc];
425     if (val == 0) {
426         env->macsr |= MACSR_Z;
427     } else if (val & (1ull << 47)) {
428         env->macsr |= MACSR_N;
429     }
430     if (env->macsr & (MACSR_PAV0 << acc)) {
431         env->macsr |= MACSR_V;
432     }
433     if (env->macsr & MACSR_FI) {
434         val = ((int64_t)val) >> 40;
435         if (val != 0 && val != -1)
436             env->macsr |= MACSR_EV;
437     } else if (env->macsr & MACSR_SU) {
438         val = ((int64_t)val) >> 32;
439         if (val != 0 && val != -1)
440             env->macsr |= MACSR_EV;
441     } else {
442         if ((val >> 32) != 0)
443             env->macsr |= MACSR_EV;
444     }
445 }
446 
447 #define EXTSIGN(val, index) (     \
448     (index == 0) ? (int8_t)(val) : ((index == 1) ? (int16_t)(val) : (val)) \
449 )
450 
451 #define COMPUTE_CCR(op, x, n, z, v, c) {                                   \
452     switch (op) {                                                          \
453     case CC_OP_FLAGS:                                                      \
454         /* Everything in place.  */                                        \
455         break;                                                             \
456     case CC_OP_ADDB:                                                       \
457     case CC_OP_ADDW:                                                       \
458     case CC_OP_ADDL:                                                       \
459         res = n;                                                           \
460         src2 = v;                                                          \
461         src1 = EXTSIGN(res - src2, op - CC_OP_ADDB);                       \
462         c = x;                                                             \
463         z = n;                                                             \
464         v = (res ^ src1) & ~(src1 ^ src2);                                 \
465         break;                                                             \
466     case CC_OP_SUBB:                                                       \
467     case CC_OP_SUBW:                                                       \
468     case CC_OP_SUBL:                                                       \
469         res = n;                                                           \
470         src2 = v;                                                          \
471         src1 = EXTSIGN(res + src2, op - CC_OP_SUBB);                       \
472         c = x;                                                             \
473         z = n;                                                             \
474         v = (res ^ src1) & (src1 ^ src2);                                  \
475         break;                                                             \
476     case CC_OP_CMPB:                                                       \
477     case CC_OP_CMPW:                                                       \
478     case CC_OP_CMPL:                                                       \
479         src1 = n;                                                          \
480         src2 = v;                                                          \
481         res = EXTSIGN(src1 - src2, op - CC_OP_CMPB);                       \
482         n = res;                                                           \
483         z = res;                                                           \
484         c = src1 < src2;                                                   \
485         v = (res ^ src1) & (src1 ^ src2);                                  \
486         break;                                                             \
487     case CC_OP_LOGIC:                                                      \
488         c = v = 0;                                                         \
489         z = n;                                                             \
490         break;                                                             \
491     default:                                                               \
492         cpu_abort(CPU(m68k_env_get_cpu(env)), "Bad CC_OP %d", op);         \
493     }                                                                      \
494 } while (0)
495 
496 uint32_t cpu_m68k_get_ccr(CPUM68KState *env)
497 {
498     uint32_t x, c, n, z, v;
499     uint32_t res, src1, src2;
500 
501     x = env->cc_x;
502     n = env->cc_n;
503     z = env->cc_z;
504     v = env->cc_v;
505     c = env->cc_c;
506 
507     COMPUTE_CCR(env->cc_op, x, n, z, v, c);
508 
509     n = n >> 31;
510     z = (z == 0);
511     v = v >> 31;
512 
513     return x * CCF_X + n * CCF_N + z * CCF_Z + v * CCF_V + c * CCF_C;
514 }
515 
516 uint32_t HELPER(get_ccr)(CPUM68KState *env)
517 {
518     return cpu_m68k_get_ccr(env);
519 }
520 
521 void cpu_m68k_set_ccr(CPUM68KState *env, uint32_t ccr)
522 {
523     env->cc_x = (ccr & CCF_X ? 1 : 0);
524     env->cc_n = (ccr & CCF_N ? -1 : 0);
525     env->cc_z = (ccr & CCF_Z ? 0 : 1);
526     env->cc_v = (ccr & CCF_V ? -1 : 0);
527     env->cc_c = (ccr & CCF_C ? 1 : 0);
528     env->cc_op = CC_OP_FLAGS;
529 }
530 
531 void HELPER(set_ccr)(CPUM68KState *env, uint32_t ccr)
532 {
533     cpu_m68k_set_ccr(env, ccr);
534 }
535 
536 void HELPER(flush_flags)(CPUM68KState *env, uint32_t cc_op)
537 {
538     uint32_t res, src1, src2;
539 
540     COMPUTE_CCR(cc_op, env->cc_x, env->cc_n, env->cc_z, env->cc_v, env->cc_c);
541     env->cc_op = CC_OP_FLAGS;
542 }
543 
544 uint32_t HELPER(get_macf)(CPUM68KState *env, uint64_t val)
545 {
546     int rem;
547     uint32_t result;
548 
549     if (env->macsr & MACSR_SU) {
550         /* 16-bit rounding.  */
551         rem = val & 0xffffff;
552         val = (val >> 24) & 0xffffu;
553         if (rem > 0x800000)
554             val++;
555         else if (rem == 0x800000)
556             val += (val & 1);
557     } else if (env->macsr & MACSR_RT) {
558         /* 32-bit rounding.  */
559         rem = val & 0xff;
560         val >>= 8;
561         if (rem > 0x80)
562             val++;
563         else if (rem == 0x80)
564             val += (val & 1);
565     } else {
566         /* No rounding.  */
567         val >>= 8;
568     }
569     if (env->macsr & MACSR_OMC) {
570         /* Saturate.  */
571         if (env->macsr & MACSR_SU) {
572             if (val != (uint16_t) val) {
573                 result = ((val >> 63) ^ 0x7fff) & 0xffff;
574             } else {
575                 result = val & 0xffff;
576             }
577         } else {
578             if (val != (uint32_t)val) {
579                 result = ((uint32_t)(val >> 63) & 0x7fffffff);
580             } else {
581                 result = (uint32_t)val;
582             }
583         }
584     } else {
585         /* No saturation.  */
586         if (env->macsr & MACSR_SU) {
587             result = val & 0xffff;
588         } else {
589             result = (uint32_t)val;
590         }
591     }
592     return result;
593 }
594 
595 uint32_t HELPER(get_macs)(uint64_t val)
596 {
597     if (val == (int32_t)val) {
598         return (int32_t)val;
599     } else {
600         return (val >> 61) ^ ~SIGNBIT;
601     }
602 }
603 
604 uint32_t HELPER(get_macu)(uint64_t val)
605 {
606     if ((val >> 32) == 0) {
607         return (uint32_t)val;
608     } else {
609         return 0xffffffffu;
610     }
611 }
612 
613 uint32_t HELPER(get_mac_extf)(CPUM68KState *env, uint32_t acc)
614 {
615     uint32_t val;
616     val = env->macc[acc] & 0x00ff;
617     val |= (env->macc[acc] >> 32) & 0xff00;
618     val |= (env->macc[acc + 1] << 16) & 0x00ff0000;
619     val |= (env->macc[acc + 1] >> 16) & 0xff000000;
620     return val;
621 }
622 
623 uint32_t HELPER(get_mac_exti)(CPUM68KState *env, uint32_t acc)
624 {
625     uint32_t val;
626     val = (env->macc[acc] >> 32) & 0xffff;
627     val |= (env->macc[acc + 1] >> 16) & 0xffff0000;
628     return val;
629 }
630 
631 void HELPER(set_mac_extf)(CPUM68KState *env, uint32_t val, uint32_t acc)
632 {
633     int64_t res;
634     int32_t tmp;
635     res = env->macc[acc] & 0xffffffff00ull;
636     tmp = (int16_t)(val & 0xff00);
637     res |= ((int64_t)tmp) << 32;
638     res |= val & 0xff;
639     env->macc[acc] = res;
640     res = env->macc[acc + 1] & 0xffffffff00ull;
641     tmp = (val & 0xff000000);
642     res |= ((int64_t)tmp) << 16;
643     res |= (val >> 16) & 0xff;
644     env->macc[acc + 1] = res;
645 }
646 
647 void HELPER(set_mac_exts)(CPUM68KState *env, uint32_t val, uint32_t acc)
648 {
649     int64_t res;
650     int32_t tmp;
651     res = (uint32_t)env->macc[acc];
652     tmp = (int16_t)val;
653     res |= ((int64_t)tmp) << 32;
654     env->macc[acc] = res;
655     res = (uint32_t)env->macc[acc + 1];
656     tmp = val & 0xffff0000;
657     res |= (int64_t)tmp << 16;
658     env->macc[acc + 1] = res;
659 }
660 
661 void HELPER(set_mac_extu)(CPUM68KState *env, uint32_t val, uint32_t acc)
662 {
663     uint64_t res;
664     res = (uint32_t)env->macc[acc];
665     res |= ((uint64_t)(val & 0xffff)) << 32;
666     env->macc[acc] = res;
667     res = (uint32_t)env->macc[acc + 1];
668     res |= (uint64_t)(val & 0xffff0000) << 16;
669     env->macc[acc + 1] = res;
670 }
671