MCF5208 emulation.
[qemu] / target-m68k / helper.c
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, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include <stdio.h>
23 #include <string.h>
24
25 #include "config.h"
26 #include "cpu.h"
27 #include "exec-all.h"
28
29 enum m68k_cpuid {
30     M68K_CPUID_M5206,
31     M68K_CPUID_M5208,
32     M68K_CPUID_CFV4E,
33     M68K_CPUID_ANY,
34 };
35
36 struct m68k_def_t {
37     const char * name;
38     enum m68k_cpuid id;
39 };
40
41 static m68k_def_t m68k_cpu_defs[] = {
42     {"m5206", M68K_CPUID_M5206}, 
43     {"m5208", M68K_CPUID_M5208}, 
44     {"cfv4e", M68K_CPUID_CFV4E},
45     {"any", M68K_CPUID_ANY},
46     {NULL, 0}, 
47 };
48
49 static void m68k_set_feature(CPUM68KState *env, int feature)
50 {
51     env->features |= (1u << feature);
52 }
53
54 int cpu_m68k_set_model(CPUM68KState *env, const char * name)
55 {
56     m68k_def_t *def;
57
58     for (def = m68k_cpu_defs; def->name; def++) {
59         if (strcmp(def->name, name) == 0)
60             break;
61     }
62     if (!def->name)
63         return 1;
64
65     switch (def->id) {
66     case M68K_CPUID_M5206:
67         m68k_set_feature(env, M68K_FEATURE_CF_ISA_A);
68         break;
69     case M68K_CPUID_M5208:
70         m68k_set_feature(env, M68K_FEATURE_CF_ISA_A);
71         m68k_set_feature(env, M68K_FEATURE_CF_EMAC);
72         m68k_set_feature(env, M68K_FEATURE_USP);
73         break;
74     case M68K_CPUID_CFV4E:
75         m68k_set_feature(env, M68K_FEATURE_CF_ISA_A);
76         m68k_set_feature(env, M68K_FEATURE_CF_ISA_B);
77         m68k_set_feature(env, M68K_FEATURE_CF_ISA_C);
78         m68k_set_feature(env, M68K_FEATURE_CF_FPU);
79         m68k_set_feature(env, M68K_FEATURE_CF_EMAC);
80         m68k_set_feature(env, M68K_FEATURE_USP);
81         break;
82     case M68K_CPUID_ANY:
83         m68k_set_feature(env, M68K_FEATURE_CF_ISA_A);
84         m68k_set_feature(env, M68K_FEATURE_CF_ISA_B);
85         m68k_set_feature(env, M68K_FEATURE_CF_ISA_C);
86         m68k_set_feature(env, M68K_FEATURE_CF_FPU);
87         /* MAC and EMAC are mututally exclusive, so pick EMAC.
88            It's mostly backwards compatible.  */
89         m68k_set_feature(env, M68K_FEATURE_CF_EMAC);
90         m68k_set_feature(env, M68K_FEATURE_USP);
91         m68k_set_feature(env, M68K_FEATURE_EXT_FULL);
92         break;
93     }
94
95     register_m68k_insns(env);
96
97     return 0;
98 }
99
100 void cpu_m68k_flush_flags(CPUM68KState *env, int cc_op)
101 {
102     int flags;
103     uint32_t src;
104     uint32_t dest;
105     uint32_t tmp;
106
107 #define HIGHBIT 0x80000000u
108
109 #define SET_NZ(x) do { \
110     if ((x) == 0) \
111         flags |= CCF_Z; \
112     else if ((int32_t)(x) < 0) \
113         flags |= CCF_N; \
114     } while (0)
115
116 #define SET_FLAGS_SUB(type, utype) do { \
117     SET_NZ((type)dest); \
118     tmp = dest + src; \
119     if ((utype) tmp < (utype) src) \
120         flags |= CCF_C; \
121     if ((1u << (sizeof(type) * 8 - 1)) & (tmp ^ dest) & (tmp ^ src)) \
122         flags |= CCF_V; \
123     } while (0)
124
125     flags = 0;
126     src = env->cc_src;
127     dest = env->cc_dest;
128     switch (cc_op) {
129     case CC_OP_FLAGS:
130         flags = dest;
131         break;
132     case CC_OP_LOGIC:
133         SET_NZ(dest);
134         break;
135     case CC_OP_ADD:
136         SET_NZ(dest);
137         if (dest < src)
138             flags |= CCF_C;
139         tmp = dest - src;
140         if (HIGHBIT & (src ^ dest) & ~(tmp ^ src))
141             flags |= CCF_V;
142         break;
143     case CC_OP_SUB:
144         SET_FLAGS_SUB(int32_t, uint32_t);
145         break;
146     case CC_OP_CMPB:
147         SET_FLAGS_SUB(int8_t, uint8_t);
148         break;
149     case CC_OP_CMPW:
150         SET_FLAGS_SUB(int16_t, uint16_t);
151         break;
152     case CC_OP_ADDX:
153         SET_NZ(dest);
154         if (dest <= src)
155             flags |= CCF_C;
156         tmp = dest - src - 1;
157         if (HIGHBIT & (src ^ dest) & ~(tmp ^ src))
158             flags |= CCF_V;
159         break;
160     case CC_OP_SUBX:
161         SET_NZ(dest);
162         tmp = dest + src + 1;
163         if (tmp <= src)
164             flags |= CCF_C;
165         if (HIGHBIT & (tmp ^ dest) & (tmp ^ src))
166             flags |= CCF_V;
167         break;
168     case CC_OP_SHL:
169         if (src >= 32) {
170             SET_NZ(0);
171         } else {
172             tmp = dest << src;
173             SET_NZ(tmp);
174         }
175         if (src && src <= 32 && (dest & (1 << (32 - src))))
176             flags |= CCF_C;
177         break;
178     case CC_OP_SHR:
179         if (src >= 32) {
180             SET_NZ(0);
181         } else {
182             tmp = dest >> src;
183             SET_NZ(tmp);
184         }
185         if (src && src <= 32 && ((dest >> (src - 1)) & 1))
186             flags |= CCF_C;
187         break;
188     case CC_OP_SAR:
189         if (src >= 32) {
190             SET_NZ(-1);
191         } else {
192             tmp = (int32_t)dest >> src;
193             SET_NZ(tmp);
194         }
195         if (src && src <= 32 && (((int32_t)dest >> (src - 1)) & 1))
196             flags |= CCF_C;
197         break;
198     default:
199         cpu_abort(env, "Bad CC_OP %d", cc_op);
200     }
201     env->cc_op = CC_OP_FLAGS;
202     env->cc_dest = flags;
203 }
204
205 float64 helper_sub_cmpf64(CPUM68KState *env, float64 src0, float64 src1)
206 {
207     /* ??? This may incorrectly raise exceptions.  */
208     /* ??? Should flush denormals to zero.  */
209     float64 res;
210     res = float64_sub(src0, src1, &env->fp_status);
211     if (float64_is_nan(res)) {
212         /* +/-inf compares equal against itself, but sub returns nan.  */
213         if (!float64_is_nan(src0)
214             && !float64_is_nan(src1)) {
215             res = 0;
216             if (float64_lt_quiet(src0, res, &env->fp_status))
217                 res = float64_chs(res);
218         }
219     }
220     return res;
221 }
222
223 void helper_movec(CPUM68KState *env, int reg, uint32_t val)
224 {
225     switch (reg) {
226     case 0x02: /* CACR */
227         env->cacr = val;
228         m68k_switch_sp(env);
229         break;
230     case 0x04: case 0x05: case 0x06: case 0x07: /* ACR[0-3] */
231         /* TODO: Implement Access Control Registers.  */
232         break;
233     case 0x801: /* VBR */
234         env->vbr = val;
235         break;
236     /* TODO: Implement control registers.  */
237     default:
238         cpu_abort(env, "Unimplemented control register write 0x%x = 0x%x\n",
239                   reg, val);
240     }
241 }
242
243 void m68k_set_macsr(CPUM68KState *env, uint32_t val)
244 {
245     uint32_t acc;
246     int8_t exthigh;
247     uint8_t extlow;
248     uint64_t regval;
249     int i;
250     if ((env->macsr ^ val) & (MACSR_FI | MACSR_SU)) {
251         for (i = 0; i < 4; i++) {
252             regval = env->macc[i];
253             exthigh = regval >> 40;
254             if (env->macsr & MACSR_FI) {
255                 acc = regval >> 8;
256                 extlow = regval;
257             } else {
258                 acc = regval;
259                 extlow = regval >> 32;
260             }
261             if (env->macsr & MACSR_FI) {
262                 regval = (((uint64_t)acc) << 8) | extlow;
263                 regval |= ((int64_t)exthigh) << 40;
264             } else if (env->macsr & MACSR_SU) {
265                 regval = acc | (((int64_t)extlow) << 32);
266                 regval |= ((int64_t)exthigh) << 40;
267             } else {
268                 regval = acc | (((uint64_t)extlow) << 32);
269                 regval |= ((uint64_t)(uint8_t)exthigh) << 40;
270             }
271             env->macc[i] = regval;
272         }
273     }
274     env->macsr = val;
275 }
276
277 void m68k_switch_sp(CPUM68KState *env)
278 {
279     int new_sp;
280
281     env->sp[env->current_sp] = env->aregs[7];
282     new_sp = (env->sr & SR_S && env->cacr & M68K_CACR_EUSP)
283              ? M68K_SSP : M68K_USP;
284     env->aregs[7] = env->sp[new_sp];
285     env->current_sp = new_sp;
286 }
287
288 /* MMU */
289
290 /* TODO: This will need fixing once the MMU is implemented.  */
291 target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
292 {
293     return addr;
294 }
295
296 #if defined(CONFIG_USER_ONLY) 
297
298 int cpu_m68k_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
299                                int is_user, int is_softmmu)
300 {
301     env->exception_index = EXCP_ACCESS;
302     env->mmu.ar = address;
303     return 1;
304 }
305
306 #else
307
308 int cpu_m68k_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
309                                int is_user, int is_softmmu)
310 {
311     int prot;
312
313     address &= TARGET_PAGE_MASK;
314     prot = PAGE_READ | PAGE_WRITE;
315     return tlb_set_page(env, address, address, prot, is_user, is_softmmu);
316 }
317
318 /* Notify CPU of a pending interrupt.  Prioritization and vectoring should
319    be handled by the interrupt controller.  Real hardware only requests
320    the vector when the interrupt is acknowledged by the CPU.  For
321    simplicitly we calculate it when the interrupt is signalled.  */
322 void m68k_set_irq_level(CPUM68KState *env, int level, uint8_t vector)
323 {
324     env->pending_level = level;
325     env->pending_vector = vector;
326     if (level)
327         cpu_interrupt(env, CPU_INTERRUPT_HARD);
328     else
329         cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
330 }
331
332 #endif