]>
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 | |
9 | * version 2 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 | |
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" |
cfe67cef | 24 | #include "exec/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 | ||
0633879f PB |
39 | /* Try to fill the TLB and return an exception if error. If retaddr is |
40 | NULL, it means that the function was called in C code (i.e. not | |
41 | from generated code or from helper.c) */ | |
b35399bb SS |
42 | void tlb_fill(CPUState *cs, target_ulong addr, MMUAccessType access_type, |
43 | int mmu_idx, uintptr_t retaddr) | |
0633879f | 44 | { |
0633879f PB |
45 | int ret; |
46 | ||
b35399bb | 47 | ret = m68k_cpu_handle_mmu_fault(cs, addr, access_type, mmu_idx); |
551bd27f | 48 | if (unlikely(ret)) { |
0633879f PB |
49 | if (retaddr) { |
50 | /* now we have a real cpu fault */ | |
3f38f309 | 51 | cpu_restore_state(cs, retaddr); |
0633879f | 52 | } |
5638d180 | 53 | cpu_loop_exit(cs); |
0633879f | 54 | } |
0633879f PB |
55 | } |
56 | ||
31871141 | 57 | static void do_rte(CPUM68KState *env) |
0633879f PB |
58 | { |
59 | uint32_t sp; | |
60 | uint32_t fmt; | |
61 | ||
62 | sp = env->aregs[7]; | |
31871141 BS |
63 | fmt = cpu_ldl_kernel(env, sp); |
64 | env->pc = cpu_ldl_kernel(env, sp + 4); | |
0633879f | 65 | sp |= (fmt >> 28) & 3; |
0633879f | 66 | env->aregs[7] = sp + 8; |
99c51448 RH |
67 | |
68 | helper_set_sr(env, fmt); | |
0633879f PB |
69 | } |
70 | ||
31871141 | 71 | static void do_interrupt_all(CPUM68KState *env, int is_hw) |
0633879f | 72 | { |
27103424 | 73 | CPUState *cs = CPU(m68k_env_get_cpu(env)); |
0633879f PB |
74 | uint32_t sp; |
75 | uint32_t fmt; | |
76 | uint32_t retaddr; | |
77 | uint32_t vector; | |
78 | ||
79 | fmt = 0; | |
80 | retaddr = env->pc; | |
81 | ||
82 | if (!is_hw) { | |
27103424 | 83 | switch (cs->exception_index) { |
0633879f PB |
84 | case EXCP_RTE: |
85 | /* Return from an exception. */ | |
31871141 | 86 | do_rte(env); |
0633879f | 87 | return; |
a87295e8 | 88 | case EXCP_HALT_INSN: |
cfe67cef | 89 | if (semihosting_enabled() |
a87295e8 PB |
90 | && (env->sr & SR_S) != 0 |
91 | && (env->pc & 3) == 0 | |
31871141 BS |
92 | && cpu_lduw_code(env, env->pc - 4) == 0x4e71 |
93 | && cpu_ldl_code(env, env->pc) == 0x4e7bf000) { | |
a87295e8 PB |
94 | env->pc += 4; |
95 | do_m68k_semihosting(env, env->dregs[0]); | |
96 | return; | |
97 | } | |
259186a7 | 98 | cs->halted = 1; |
27103424 | 99 | cs->exception_index = EXCP_HLT; |
5638d180 | 100 | cpu_loop_exit(cs); |
a87295e8 | 101 | return; |
0633879f | 102 | } |
27103424 AF |
103 | if (cs->exception_index >= EXCP_TRAP0 |
104 | && cs->exception_index <= EXCP_TRAP15) { | |
0633879f PB |
105 | /* Move the PC after the trap instruction. */ |
106 | retaddr += 2; | |
107 | } | |
108 | } | |
109 | ||
27103424 | 110 | vector = cs->exception_index << 2; |
0633879f PB |
111 | |
112 | fmt |= 0x40000000; | |
0633879f PB |
113 | fmt |= vector << 16; |
114 | fmt |= env->sr; | |
99c51448 | 115 | fmt |= cpu_m68k_get_ccr(env); |
0633879f | 116 | |
20dcee94 PB |
117 | env->sr |= SR_S; |
118 | if (is_hw) { | |
119 | env->sr = (env->sr & ~SR_I) | (env->pending_level << SR_I_SHIFT); | |
120 | env->sr &= ~SR_M; | |
121 | } | |
122 | m68k_switch_sp(env); | |
0c8ff723 GU |
123 | sp = env->aregs[7]; |
124 | fmt |= (sp & 3) << 28; | |
20dcee94 | 125 | |
0633879f PB |
126 | /* ??? This could cause MMU faults. */ |
127 | sp &= ~3; | |
128 | sp -= 4; | |
31871141 | 129 | cpu_stl_kernel(env, sp, retaddr); |
0633879f | 130 | sp -= 4; |
31871141 | 131 | cpu_stl_kernel(env, sp, fmt); |
0633879f | 132 | env->aregs[7] = sp; |
0633879f | 133 | /* Jump to vector. */ |
31871141 | 134 | env->pc = cpu_ldl_kernel(env, env->vbr + vector); |
0633879f PB |
135 | } |
136 | ||
97a8ea5a | 137 | void m68k_cpu_do_interrupt(CPUState *cs) |
3c688828 | 138 | { |
97a8ea5a AF |
139 | M68kCPU *cpu = M68K_CPU(cs); |
140 | CPUM68KState *env = &cpu->env; | |
141 | ||
31871141 | 142 | do_interrupt_all(env, 0); |
3c688828 BS |
143 | } |
144 | ||
ab409bb3 | 145 | static inline void do_interrupt_m68k_hardirq(CPUM68KState *env) |
3c688828 | 146 | { |
31871141 | 147 | do_interrupt_all(env, 1); |
3c688828 | 148 | } |
0633879f | 149 | #endif |
e1f3808e | 150 | |
ab409bb3 RH |
151 | bool m68k_cpu_exec_interrupt(CPUState *cs, int interrupt_request) |
152 | { | |
153 | M68kCPU *cpu = M68K_CPU(cs); | |
154 | CPUM68KState *env = &cpu->env; | |
155 | ||
156 | if (interrupt_request & CPU_INTERRUPT_HARD | |
157 | && ((env->sr & SR_I) >> SR_I_SHIFT) < env->pending_level) { | |
158 | /* Real hardware gets the interrupt vector via an IACK cycle | |
159 | at this point. Current emulated hardware doesn't rely on | |
160 | this, so we provide/save the vector when the interrupt is | |
161 | first signalled. */ | |
162 | cs->exception_index = env->pending_vector; | |
163 | do_interrupt_m68k_hardirq(env); | |
164 | return true; | |
165 | } | |
166 | return false; | |
167 | } | |
168 | ||
0ccb9c1d | 169 | static void raise_exception_ra(CPUM68KState *env, int tt, uintptr_t raddr) |
e1f3808e | 170 | { |
27103424 AF |
171 | CPUState *cs = CPU(m68k_env_get_cpu(env)); |
172 | ||
173 | cs->exception_index = tt; | |
0ccb9c1d LV |
174 | cpu_loop_exit_restore(cs, raddr); |
175 | } | |
176 | ||
177 | static void raise_exception(CPUM68KState *env, int tt) | |
178 | { | |
179 | raise_exception_ra(env, tt, 0); | |
e1f3808e PB |
180 | } |
181 | ||
31871141 | 182 | void HELPER(raise_exception)(CPUM68KState *env, uint32_t tt) |
e1f3808e | 183 | { |
31871141 | 184 | raise_exception(env, tt); |
e1f3808e PB |
185 | } |
186 | ||
0ccb9c1d | 187 | void HELPER(divuw)(CPUM68KState *env, int destr, uint32_t den) |
e1f3808e | 188 | { |
0ccb9c1d LV |
189 | uint32_t num = env->dregs[destr]; |
190 | uint32_t quot, rem; | |
191 | ||
192 | if (den == 0) { | |
193 | raise_exception_ra(env, EXCP_DIV0, GETPC()); | |
194 | } | |
195 | quot = num / den; | |
196 | rem = num % den; | |
197 | ||
198 | env->cc_c = 0; /* always cleared, even if overflow */ | |
199 | if (quot > 0xffff) { | |
200 | env->cc_v = -1; | |
201 | /* real 68040 keeps N and unset Z on overflow, | |
202 | * whereas documentation says "undefined" | |
203 | */ | |
204 | env->cc_z = 1; | |
205 | return; | |
206 | } | |
207 | env->dregs[destr] = deposit32(quot, 16, 16, rem); | |
208 | env->cc_z = (int16_t)quot; | |
209 | env->cc_n = (int16_t)quot; | |
210 | env->cc_v = 0; | |
211 | } | |
212 | ||
213 | void HELPER(divsw)(CPUM68KState *env, int destr, int32_t den) | |
214 | { | |
215 | int32_t num = env->dregs[destr]; | |
216 | uint32_t quot, rem; | |
e1f3808e | 217 | |
31871141 | 218 | if (den == 0) { |
0ccb9c1d | 219 | raise_exception_ra(env, EXCP_DIV0, GETPC()); |
31871141 | 220 | } |
e1f3808e PB |
221 | quot = num / den; |
222 | rem = num % den; | |
620c6cf6 | 223 | |
0ccb9c1d LV |
224 | env->cc_c = 0; /* always cleared, even if overflow */ |
225 | if (quot != (int16_t)quot) { | |
226 | env->cc_v = -1; | |
227 | /* nothing else is modified */ | |
228 | /* real 68040 keeps N and unset Z on overflow, | |
229 | * whereas documentation says "undefined" | |
230 | */ | |
231 | env->cc_z = 1; | |
232 | return; | |
233 | } | |
234 | env->dregs[destr] = deposit32(quot, 16, 16, rem); | |
235 | env->cc_z = (int16_t)quot; | |
236 | env->cc_n = (int16_t)quot; | |
237 | env->cc_v = 0; | |
238 | } | |
239 | ||
240 | void HELPER(divul)(CPUM68KState *env, int numr, int regr, uint32_t den) | |
241 | { | |
242 | uint32_t num = env->dregs[numr]; | |
243 | uint32_t quot, rem; | |
244 | ||
245 | if (den == 0) { | |
246 | raise_exception_ra(env, EXCP_DIV0, GETPC()); | |
247 | } | |
248 | quot = num / den; | |
249 | rem = num % den; | |
250 | ||
251 | env->cc_c = 0; | |
620c6cf6 RH |
252 | env->cc_z = quot; |
253 | env->cc_n = quot; | |
0ccb9c1d LV |
254 | env->cc_v = 0; |
255 | ||
256 | if (m68k_feature(env, M68K_FEATURE_CF_ISA_A)) { | |
257 | if (numr == regr) { | |
258 | env->dregs[numr] = quot; | |
259 | } else { | |
260 | env->dregs[regr] = rem; | |
261 | } | |
262 | } else { | |
263 | env->dregs[regr] = rem; | |
264 | env->dregs[numr] = quot; | |
265 | } | |
266 | } | |
267 | ||
268 | void HELPER(divsl)(CPUM68KState *env, int numr, int regr, int32_t den) | |
269 | { | |
270 | int32_t num = env->dregs[numr]; | |
271 | int32_t quot, rem; | |
272 | ||
273 | if (den == 0) { | |
274 | raise_exception_ra(env, EXCP_DIV0, GETPC()); | |
275 | } | |
276 | quot = num / den; | |
277 | rem = num % den; | |
278 | ||
620c6cf6 | 279 | env->cc_c = 0; |
0ccb9c1d LV |
280 | env->cc_z = quot; |
281 | env->cc_n = quot; | |
282 | env->cc_v = 0; | |
283 | ||
284 | if (m68k_feature(env, M68K_FEATURE_CF_ISA_A)) { | |
285 | if (numr == regr) { | |
286 | env->dregs[numr] = quot; | |
287 | } else { | |
288 | env->dregs[regr] = rem; | |
289 | } | |
290 | } else { | |
291 | env->dregs[regr] = rem; | |
292 | env->dregs[numr] = quot; | |
293 | } | |
294 | } | |
295 | ||
296 | void HELPER(divull)(CPUM68KState *env, int numr, int regr, uint32_t den) | |
297 | { | |
298 | uint64_t num = deposit64(env->dregs[numr], 32, 32, env->dregs[regr]); | |
299 | uint64_t quot; | |
300 | uint32_t rem; | |
301 | ||
302 | if (den == 0) { | |
303 | raise_exception_ra(env, EXCP_DIV0, GETPC()); | |
304 | } | |
305 | quot = num / den; | |
306 | rem = num % den; | |
620c6cf6 | 307 | |
0ccb9c1d LV |
308 | env->cc_c = 0; /* always cleared, even if overflow */ |
309 | if (quot > 0xffffffffULL) { | |
310 | env->cc_v = -1; | |
311 | /* real 68040 keeps N and unset Z on overflow, | |
312 | * whereas documentation says "undefined" | |
313 | */ | |
314 | env->cc_z = 1; | |
315 | return; | |
316 | } | |
317 | env->cc_z = quot; | |
318 | env->cc_n = quot; | |
319 | env->cc_v = 0; | |
320 | ||
321 | /* | |
322 | * If Dq and Dr are the same, the quotient is returned. | |
323 | * therefore we set Dq last. | |
324 | */ | |
325 | ||
326 | env->dregs[regr] = rem; | |
327 | env->dregs[numr] = quot; | |
e1f3808e PB |
328 | } |
329 | ||
0ccb9c1d | 330 | void HELPER(divsll)(CPUM68KState *env, int numr, int regr, int32_t den) |
e1f3808e | 331 | { |
0ccb9c1d LV |
332 | int64_t num = deposit64(env->dregs[numr], 32, 32, env->dregs[regr]); |
333 | int64_t quot; | |
e1f3808e | 334 | int32_t rem; |
e1f3808e | 335 | |
31871141 | 336 | if (den == 0) { |
0ccb9c1d | 337 | raise_exception_ra(env, EXCP_DIV0, GETPC()); |
31871141 | 338 | } |
e1f3808e PB |
339 | quot = num / den; |
340 | rem = num % den; | |
620c6cf6 | 341 | |
0ccb9c1d LV |
342 | env->cc_c = 0; /* always cleared, even if overflow */ |
343 | if (quot != (int32_t)quot) { | |
344 | env->cc_v = -1; | |
345 | /* real 68040 keeps N and unset Z on overflow, | |
346 | * whereas documentation says "undefined" | |
347 | */ | |
348 | env->cc_z = 1; | |
349 | return; | |
350 | } | |
620c6cf6 RH |
351 | env->cc_z = quot; |
352 | env->cc_n = quot; | |
0ccb9c1d LV |
353 | env->cc_v = 0; |
354 | ||
355 | /* | |
356 | * If Dq and Dr are the same, the quotient is returned. | |
357 | * therefore we set Dq last. | |
358 | */ | |
620c6cf6 | 359 | |
0ccb9c1d LV |
360 | env->dregs[regr] = rem; |
361 | env->dregs[numr] = quot; | |
e1f3808e | 362 | } |
14f94406 | 363 | |
f0ddf11b | 364 | /* We're executing in a serial context -- no need to be atomic. */ |
14f94406 LV |
365 | void HELPER(cas2w)(CPUM68KState *env, uint32_t regs, uint32_t a1, uint32_t a2) |
366 | { | |
367 | uint32_t Dc1 = extract32(regs, 9, 3); | |
368 | uint32_t Dc2 = extract32(regs, 6, 3); | |
369 | uint32_t Du1 = extract32(regs, 3, 3); | |
370 | uint32_t Du2 = extract32(regs, 0, 3); | |
371 | int16_t c1 = env->dregs[Dc1]; | |
372 | int16_t c2 = env->dregs[Dc2]; | |
373 | int16_t u1 = env->dregs[Du1]; | |
374 | int16_t u2 = env->dregs[Du2]; | |
375 | int16_t l1, l2; | |
376 | uintptr_t ra = GETPC(); | |
377 | ||
f0ddf11b EC |
378 | l1 = cpu_lduw_data_ra(env, a1, ra); |
379 | l2 = cpu_lduw_data_ra(env, a2, ra); | |
380 | if (l1 == c1 && l2 == c2) { | |
381 | cpu_stw_data_ra(env, a1, u1, ra); | |
382 | cpu_stw_data_ra(env, a2, u2, ra); | |
14f94406 LV |
383 | } |
384 | ||
385 | if (c1 != l1) { | |
386 | env->cc_n = l1; | |
387 | env->cc_v = c1; | |
388 | } else { | |
389 | env->cc_n = l2; | |
390 | env->cc_v = c2; | |
391 | } | |
392 | env->cc_op = CC_OP_CMPW; | |
393 | env->dregs[Dc1] = deposit32(env->dregs[Dc1], 0, 16, l1); | |
394 | env->dregs[Dc2] = deposit32(env->dregs[Dc2], 0, 16, l2); | |
395 | } | |
396 | ||
f0ddf11b EC |
397 | static void do_cas2l(CPUM68KState *env, uint32_t regs, uint32_t a1, uint32_t a2, |
398 | bool parallel) | |
14f94406 LV |
399 | { |
400 | uint32_t Dc1 = extract32(regs, 9, 3); | |
401 | uint32_t Dc2 = extract32(regs, 6, 3); | |
402 | uint32_t Du1 = extract32(regs, 3, 3); | |
403 | uint32_t Du2 = extract32(regs, 0, 3); | |
404 | uint32_t c1 = env->dregs[Dc1]; | |
405 | uint32_t c2 = env->dregs[Dc2]; | |
406 | uint32_t u1 = env->dregs[Du1]; | |
407 | uint32_t u2 = env->dregs[Du2]; | |
408 | uint32_t l1, l2; | |
409 | uintptr_t ra = GETPC(); | |
410 | #if defined(CONFIG_ATOMIC64) && !defined(CONFIG_USER_ONLY) | |
411 | int mmu_idx = cpu_mmu_index(env, 0); | |
412 | TCGMemOpIdx oi; | |
413 | #endif | |
414 | ||
f0ddf11b | 415 | if (parallel) { |
14f94406 LV |
416 | /* We're executing in a parallel context -- must be atomic. */ |
417 | #ifdef CONFIG_ATOMIC64 | |
418 | uint64_t c, u, l; | |
419 | if ((a1 & 7) == 0 && a2 == a1 + 4) { | |
420 | c = deposit64(c2, 32, 32, c1); | |
421 | u = deposit64(u2, 32, 32, u1); | |
422 | #ifdef CONFIG_USER_ONLY | |
423 | l = helper_atomic_cmpxchgq_be(env, a1, c, u); | |
424 | #else | |
425 | oi = make_memop_idx(MO_BEQ, mmu_idx); | |
426 | l = helper_atomic_cmpxchgq_be_mmu(env, a1, c, u, oi, ra); | |
427 | #endif | |
428 | l1 = l >> 32; | |
429 | l2 = l; | |
430 | } else if ((a2 & 7) == 0 && a1 == a2 + 4) { | |
431 | c = deposit64(c1, 32, 32, c2); | |
432 | u = deposit64(u1, 32, 32, u2); | |
433 | #ifdef CONFIG_USER_ONLY | |
434 | l = helper_atomic_cmpxchgq_be(env, a2, c, u); | |
435 | #else | |
436 | oi = make_memop_idx(MO_BEQ, mmu_idx); | |
437 | l = helper_atomic_cmpxchgq_be_mmu(env, a2, c, u, oi, ra); | |
438 | #endif | |
439 | l2 = l >> 32; | |
440 | l1 = l; | |
441 | } else | |
442 | #endif | |
443 | { | |
444 | /* Tell the main loop we need to serialize this insn. */ | |
445 | cpu_loop_exit_atomic(ENV_GET_CPU(env), ra); | |
446 | } | |
447 | } else { | |
448 | /* We're executing in a serial context -- no need to be atomic. */ | |
449 | l1 = cpu_ldl_data_ra(env, a1, ra); | |
450 | l2 = cpu_ldl_data_ra(env, a2, ra); | |
451 | if (l1 == c1 && l2 == c2) { | |
452 | cpu_stl_data_ra(env, a1, u1, ra); | |
453 | cpu_stl_data_ra(env, a2, u2, ra); | |
454 | } | |
455 | } | |
456 | ||
457 | if (c1 != l1) { | |
458 | env->cc_n = l1; | |
459 | env->cc_v = c1; | |
460 | } else { | |
461 | env->cc_n = l2; | |
462 | env->cc_v = c2; | |
463 | } | |
464 | env->cc_op = CC_OP_CMPL; | |
465 | env->dregs[Dc1] = l1; | |
466 | env->dregs[Dc2] = l2; | |
467 | } | |
f2224f2c | 468 | |
f0ddf11b EC |
469 | void HELPER(cas2l)(CPUM68KState *env, uint32_t regs, uint32_t a1, uint32_t a2) |
470 | { | |
471 | do_cas2l(env, regs, a1, a2, false); | |
472 | } | |
473 | ||
474 | void HELPER(cas2l_parallel)(CPUM68KState *env, uint32_t regs, uint32_t a1, | |
475 | uint32_t a2) | |
476 | { | |
477 | do_cas2l(env, regs, a1, a2, true); | |
478 | } | |
479 | ||
f2224f2c RH |
480 | struct bf_data { |
481 | uint32_t addr; | |
482 | uint32_t bofs; | |
483 | uint32_t blen; | |
484 | uint32_t len; | |
485 | }; | |
486 | ||
487 | static struct bf_data bf_prep(uint32_t addr, int32_t ofs, uint32_t len) | |
488 | { | |
489 | int bofs, blen; | |
490 | ||
491 | /* Bound length; map 0 to 32. */ | |
492 | len = ((len - 1) & 31) + 1; | |
493 | ||
494 | /* Note that ofs is signed. */ | |
495 | addr += ofs / 8; | |
496 | bofs = ofs % 8; | |
497 | if (bofs < 0) { | |
498 | bofs += 8; | |
499 | addr -= 1; | |
500 | } | |
501 | ||
502 | /* Compute the number of bytes required (minus one) to | |
503 | satisfy the bitfield. */ | |
504 | blen = (bofs + len - 1) / 8; | |
505 | ||
506 | /* Canonicalize the bit offset for data loaded into a 64-bit big-endian | |
507 | word. For the cases where BLEN is not a power of 2, adjust ADDR so | |
508 | that we can use the next power of two sized load without crossing a | |
509 | page boundary, unless the field itself crosses the boundary. */ | |
510 | switch (blen) { | |
511 | case 0: | |
512 | bofs += 56; | |
513 | break; | |
514 | case 1: | |
515 | bofs += 48; | |
516 | break; | |
517 | case 2: | |
518 | if (addr & 1) { | |
519 | bofs += 8; | |
520 | addr -= 1; | |
521 | } | |
522 | /* fallthru */ | |
523 | case 3: | |
524 | bofs += 32; | |
525 | break; | |
526 | case 4: | |
527 | if (addr & 3) { | |
528 | bofs += 8 * (addr & 3); | |
529 | addr &= -4; | |
530 | } | |
531 | break; | |
532 | default: | |
533 | g_assert_not_reached(); | |
534 | } | |
535 | ||
536 | return (struct bf_data){ | |
537 | .addr = addr, | |
538 | .bofs = bofs, | |
539 | .blen = blen, | |
540 | .len = len, | |
541 | }; | |
542 | } | |
543 | ||
544 | static uint64_t bf_load(CPUM68KState *env, uint32_t addr, int blen, | |
545 | uintptr_t ra) | |
546 | { | |
547 | switch (blen) { | |
548 | case 0: | |
549 | return cpu_ldub_data_ra(env, addr, ra); | |
550 | case 1: | |
551 | return cpu_lduw_data_ra(env, addr, ra); | |
552 | case 2: | |
553 | case 3: | |
554 | return cpu_ldl_data_ra(env, addr, ra); | |
555 | case 4: | |
556 | return cpu_ldq_data_ra(env, addr, ra); | |
557 | default: | |
558 | g_assert_not_reached(); | |
559 | } | |
560 | } | |
561 | ||
562 | static void bf_store(CPUM68KState *env, uint32_t addr, int blen, | |
563 | uint64_t data, uintptr_t ra) | |
564 | { | |
565 | switch (blen) { | |
566 | case 0: | |
567 | cpu_stb_data_ra(env, addr, data, ra); | |
568 | break; | |
569 | case 1: | |
570 | cpu_stw_data_ra(env, addr, data, ra); | |
571 | break; | |
572 | case 2: | |
573 | case 3: | |
574 | cpu_stl_data_ra(env, addr, data, ra); | |
575 | break; | |
576 | case 4: | |
577 | cpu_stq_data_ra(env, addr, data, ra); | |
578 | break; | |
579 | default: | |
580 | g_assert_not_reached(); | |
581 | } | |
582 | } | |
583 | ||
584 | uint32_t HELPER(bfexts_mem)(CPUM68KState *env, uint32_t addr, | |
585 | int32_t ofs, uint32_t len) | |
586 | { | |
587 | uintptr_t ra = GETPC(); | |
588 | struct bf_data d = bf_prep(addr, ofs, len); | |
589 | uint64_t data = bf_load(env, d.addr, d.blen, ra); | |
590 | ||
591 | return (int64_t)(data << d.bofs) >> (64 - d.len); | |
592 | } | |
593 | ||
594 | uint64_t HELPER(bfextu_mem)(CPUM68KState *env, uint32_t addr, | |
595 | int32_t ofs, uint32_t len) | |
596 | { | |
597 | uintptr_t ra = GETPC(); | |
598 | struct bf_data d = bf_prep(addr, ofs, len); | |
599 | uint64_t data = bf_load(env, d.addr, d.blen, ra); | |
600 | ||
601 | /* Put CC_N at the top of the high word; put the zero-extended value | |
602 | at the bottom of the low word. */ | |
603 | data <<= d.bofs; | |
604 | data >>= 64 - d.len; | |
605 | data |= data << (64 - d.len); | |
606 | ||
607 | return data; | |
608 | } | |
609 | ||
610 | uint32_t HELPER(bfins_mem)(CPUM68KState *env, uint32_t addr, uint32_t val, | |
611 | int32_t ofs, uint32_t len) | |
612 | { | |
613 | uintptr_t ra = GETPC(); | |
614 | struct bf_data d = bf_prep(addr, ofs, len); | |
615 | uint64_t data = bf_load(env, d.addr, d.blen, ra); | |
616 | uint64_t mask = -1ull << (64 - d.len) >> d.bofs; | |
617 | ||
618 | data = (data & ~mask) | (((uint64_t)val << (64 - d.len)) >> d.bofs); | |
619 | ||
620 | bf_store(env, d.addr, d.blen, data, ra); | |
621 | ||
622 | /* The field at the top of the word is also CC_N for CC_OP_LOGIC. */ | |
623 | return val << (32 - d.len); | |
624 | } | |
625 | ||
626 | uint32_t HELPER(bfchg_mem)(CPUM68KState *env, uint32_t addr, | |
627 | int32_t ofs, uint32_t len) | |
628 | { | |
629 | uintptr_t ra = GETPC(); | |
630 | struct bf_data d = bf_prep(addr, ofs, len); | |
631 | uint64_t data = bf_load(env, d.addr, d.blen, ra); | |
632 | uint64_t mask = -1ull << (64 - d.len) >> d.bofs; | |
633 | ||
634 | bf_store(env, d.addr, d.blen, data ^ mask, ra); | |
635 | ||
636 | return ((data & mask) << d.bofs) >> 32; | |
637 | } | |
638 | ||
639 | uint32_t HELPER(bfclr_mem)(CPUM68KState *env, uint32_t addr, | |
640 | int32_t ofs, uint32_t len) | |
641 | { | |
642 | uintptr_t ra = GETPC(); | |
643 | struct bf_data d = bf_prep(addr, ofs, len); | |
644 | uint64_t data = bf_load(env, d.addr, d.blen, ra); | |
645 | uint64_t mask = -1ull << (64 - d.len) >> d.bofs; | |
646 | ||
647 | bf_store(env, d.addr, d.blen, data & ~mask, ra); | |
648 | ||
649 | return ((data & mask) << d.bofs) >> 32; | |
650 | } | |
651 | ||
652 | uint32_t HELPER(bfset_mem)(CPUM68KState *env, uint32_t addr, | |
653 | int32_t ofs, uint32_t len) | |
654 | { | |
655 | uintptr_t ra = GETPC(); | |
656 | struct bf_data d = bf_prep(addr, ofs, len); | |
657 | uint64_t data = bf_load(env, d.addr, d.blen, ra); | |
658 | uint64_t mask = -1ull << (64 - d.len) >> d.bofs; | |
659 | ||
660 | bf_store(env, d.addr, d.blen, data | mask, ra); | |
661 | ||
662 | return ((data & mask) << d.bofs) >> 32; | |
663 | } | |
a45f1763 RH |
664 | |
665 | uint32_t HELPER(bfffo_reg)(uint32_t n, uint32_t ofs, uint32_t len) | |
666 | { | |
667 | return (n ? clz32(n) : len) + ofs; | |
668 | } | |
669 | ||
670 | uint64_t HELPER(bfffo_mem)(CPUM68KState *env, uint32_t addr, | |
671 | int32_t ofs, uint32_t len) | |
672 | { | |
673 | uintptr_t ra = GETPC(); | |
674 | struct bf_data d = bf_prep(addr, ofs, len); | |
675 | uint64_t data = bf_load(env, d.addr, d.blen, ra); | |
676 | uint64_t mask = -1ull << (64 - d.len) >> d.bofs; | |
677 | uint64_t n = (data & mask) << d.bofs; | |
678 | uint32_t ffo = helper_bfffo_reg(n >> 32, ofs, d.len); | |
679 | ||
680 | /* Return FFO in the low word and N in the high word. | |
681 | Note that because of MASK and the shift, the low word | |
682 | is already zero. */ | |
683 | return n | ffo; | |
684 | } |