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