| /* | 
 |  *  m68k op helpers | 
 |  * | 
 |  *  Copyright (c) 2006-2007 CodeSourcery | 
 |  *  Written by Paul Brook | 
 |  * | 
 |  * This library is free software; you can redistribute it and/or | 
 |  * modify it under the terms of the GNU Lesser General Public | 
 |  * License as published by the Free Software Foundation; either | 
 |  * version 2 of the License, or (at your option) any later version. | 
 |  * | 
 |  * This library is distributed in the hope that it will be useful, | 
 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
 |  * General Public License for more details. | 
 |  * | 
 |  * You should have received a copy of the GNU Lesser General Public | 
 |  * License along with this library; if not, see <http://www.gnu.org/licenses/>. | 
 |  */ | 
 |  | 
 | #include <stdio.h> | 
 | #include <string.h> | 
 |  | 
 | #include "config.h" | 
 | #include "cpu.h" | 
 | #include "qemu-common.h" | 
 | #include "gdbstub.h" | 
 |  | 
 | #include "helpers.h" | 
 |  | 
 | #define SIGNBIT (1u << 31) | 
 |  | 
 | enum m68k_cpuid { | 
 |     M68K_CPUID_M5206, | 
 |     M68K_CPUID_M5208, | 
 |     M68K_CPUID_CFV4E, | 
 |     M68K_CPUID_ANY, | 
 | }; | 
 |  | 
 | typedef struct m68k_def_t m68k_def_t; | 
 |  | 
 | struct m68k_def_t { | 
 |     const char * name; | 
 |     enum m68k_cpuid id; | 
 | }; | 
 |  | 
 | static m68k_def_t m68k_cpu_defs[] = { | 
 |     {"m5206", M68K_CPUID_M5206}, | 
 |     {"m5208", M68K_CPUID_M5208}, | 
 |     {"cfv4e", M68K_CPUID_CFV4E}, | 
 |     {"any", M68K_CPUID_ANY}, | 
 |     {NULL, 0}, | 
 | }; | 
 |  | 
 | void m68k_cpu_list(FILE *f, fprintf_function cpu_fprintf) | 
 | { | 
 |     unsigned int i; | 
 |  | 
 |     for (i = 0; m68k_cpu_defs[i].name; i++) { | 
 |         (*cpu_fprintf)(f, "%s\n", m68k_cpu_defs[i].name); | 
 |     } | 
 | } | 
 |  | 
 | static int fpu_gdb_get_reg(CPUState *env, uint8_t *mem_buf, int n) | 
 | { | 
 |     if (n < 8) { | 
 |         stfq_p(mem_buf, env->fregs[n]); | 
 |         return 8; | 
 |     } | 
 |     if (n < 11) { | 
 |         /* FP control registers (not implemented)  */ | 
 |         memset(mem_buf, 0, 4); | 
 |         return 4; | 
 |     } | 
 |     return 0; | 
 | } | 
 |  | 
 | static int fpu_gdb_set_reg(CPUState *env, uint8_t *mem_buf, int n) | 
 | { | 
 |     if (n < 8) { | 
 |         env->fregs[n] = ldfq_p(mem_buf); | 
 |         return 8; | 
 |     } | 
 |     if (n < 11) { | 
 |         /* FP control registers (not implemented)  */ | 
 |         return 4; | 
 |     } | 
 |     return 0; | 
 | } | 
 |  | 
 | static void m68k_set_feature(CPUM68KState *env, int feature) | 
 | { | 
 |     env->features |= (1u << feature); | 
 | } | 
 |  | 
 | static int cpu_m68k_set_model(CPUM68KState *env, const char *name) | 
 | { | 
 |     m68k_def_t *def; | 
 |  | 
 |     for (def = m68k_cpu_defs; def->name; def++) { | 
 |         if (strcmp(def->name, name) == 0) | 
 |             break; | 
 |     } | 
 |     if (!def->name) | 
 |         return -1; | 
 |  | 
 |     switch (def->id) { | 
 |     case M68K_CPUID_M5206: | 
 |         m68k_set_feature(env, M68K_FEATURE_CF_ISA_A); | 
 |         break; | 
 |     case M68K_CPUID_M5208: | 
 |         m68k_set_feature(env, M68K_FEATURE_CF_ISA_A); | 
 |         m68k_set_feature(env, M68K_FEATURE_CF_ISA_APLUSC); | 
 |         m68k_set_feature(env, M68K_FEATURE_BRAL); | 
 |         m68k_set_feature(env, M68K_FEATURE_CF_EMAC); | 
 |         m68k_set_feature(env, M68K_FEATURE_USP); | 
 |         break; | 
 |     case M68K_CPUID_CFV4E: | 
 |         m68k_set_feature(env, M68K_FEATURE_CF_ISA_A); | 
 |         m68k_set_feature(env, M68K_FEATURE_CF_ISA_B); | 
 |         m68k_set_feature(env, M68K_FEATURE_BRAL); | 
 |         m68k_set_feature(env, M68K_FEATURE_CF_FPU); | 
 |         m68k_set_feature(env, M68K_FEATURE_CF_EMAC); | 
 |         m68k_set_feature(env, M68K_FEATURE_USP); | 
 |         break; | 
 |     case M68K_CPUID_ANY: | 
 |         m68k_set_feature(env, M68K_FEATURE_CF_ISA_A); | 
 |         m68k_set_feature(env, M68K_FEATURE_CF_ISA_B); | 
 |         m68k_set_feature(env, M68K_FEATURE_CF_ISA_APLUSC); | 
 |         m68k_set_feature(env, M68K_FEATURE_BRAL); | 
 |         m68k_set_feature(env, M68K_FEATURE_CF_FPU); | 
 |         /* MAC and EMAC are mututally exclusive, so pick EMAC. | 
 |            It's mostly backwards compatible.  */ | 
 |         m68k_set_feature(env, M68K_FEATURE_CF_EMAC); | 
 |         m68k_set_feature(env, M68K_FEATURE_CF_EMAC_B); | 
 |         m68k_set_feature(env, M68K_FEATURE_USP); | 
 |         m68k_set_feature(env, M68K_FEATURE_EXT_FULL); | 
 |         m68k_set_feature(env, M68K_FEATURE_WORD_INDEX); | 
 |         break; | 
 |     } | 
 |  | 
 |     register_m68k_insns(env); | 
 |     if (m68k_feature (env, M68K_FEATURE_CF_FPU)) { | 
 |         gdb_register_coprocessor(env, fpu_gdb_get_reg, fpu_gdb_set_reg, | 
 |                                  11, "cf-fp.xml", 18); | 
 |     } | 
 |     /* TODO: Add [E]MAC registers.  */ | 
 |     return 0; | 
 | } | 
 |  | 
 | void cpu_reset(CPUM68KState *env) | 
 | { | 
 |     if (qemu_loglevel_mask(CPU_LOG_RESET)) { | 
 |         qemu_log("CPU Reset (CPU %d)\n", env->cpu_index); | 
 |         log_cpu_state(env, 0); | 
 |     } | 
 |  | 
 |     memset(env, 0, offsetof(CPUM68KState, breakpoints)); | 
 | #if !defined (CONFIG_USER_ONLY) | 
 |     env->sr = 0x2700; | 
 | #endif | 
 |     m68k_switch_sp(env); | 
 |     /* ??? FP regs should be initialized to NaN.  */ | 
 |     env->cc_op = CC_OP_FLAGS; | 
 |     /* TODO: We should set PC from the interrupt vector.  */ | 
 |     env->pc = 0; | 
 |     tlb_flush(env, 1); | 
 | } | 
 |  | 
 | CPUM68KState *cpu_m68k_init(const char *cpu_model) | 
 | { | 
 |     CPUM68KState *env; | 
 |     static int inited; | 
 |  | 
 |     env = g_malloc0(sizeof(CPUM68KState)); | 
 |     cpu_exec_init(env); | 
 |     if (!inited) { | 
 |         inited = 1; | 
 |         m68k_tcg_init(); | 
 |     } | 
 |  | 
 |     env->cpu_model_str = cpu_model; | 
 |  | 
 |     if (cpu_m68k_set_model(env, cpu_model) < 0) { | 
 |         cpu_m68k_close(env); | 
 |         return NULL; | 
 |     } | 
 |  | 
 |     cpu_reset(env); | 
 |     qemu_init_vcpu(env); | 
 |     return env; | 
 | } | 
 |  | 
 | void cpu_m68k_close(CPUM68KState *env) | 
 | { | 
 |     g_free(env); | 
 | } | 
 |  | 
 | void cpu_m68k_flush_flags(CPUM68KState *env, int cc_op) | 
 | { | 
 |     int flags; | 
 |     uint32_t src; | 
 |     uint32_t dest; | 
 |     uint32_t tmp; | 
 |  | 
 | #define HIGHBIT 0x80000000u | 
 |  | 
 | #define SET_NZ(x) do { \ | 
 |     if ((x) == 0) \ | 
 |         flags |= CCF_Z; \ | 
 |     else if ((int32_t)(x) < 0) \ | 
 |         flags |= CCF_N; \ | 
 |     } while (0) | 
 |  | 
 | #define SET_FLAGS_SUB(type, utype) do { \ | 
 |     SET_NZ((type)dest); \ | 
 |     tmp = dest + src; \ | 
 |     if ((utype) tmp < (utype) src) \ | 
 |         flags |= CCF_C; \ | 
 |     if ((1u << (sizeof(type) * 8 - 1)) & (tmp ^ dest) & (tmp ^ src)) \ | 
 |         flags |= CCF_V; \ | 
 |     } while (0) | 
 |  | 
 |     flags = 0; | 
 |     src = env->cc_src; | 
 |     dest = env->cc_dest; | 
 |     switch (cc_op) { | 
 |     case CC_OP_FLAGS: | 
 |         flags = dest; | 
 |         break; | 
 |     case CC_OP_LOGIC: | 
 |         SET_NZ(dest); | 
 |         break; | 
 |     case CC_OP_ADD: | 
 |         SET_NZ(dest); | 
 |         if (dest < src) | 
 |             flags |= CCF_C; | 
 |         tmp = dest - src; | 
 |         if (HIGHBIT & (src ^ dest) & ~(tmp ^ src)) | 
 |             flags |= CCF_V; | 
 |         break; | 
 |     case CC_OP_SUB: | 
 |         SET_FLAGS_SUB(int32_t, uint32_t); | 
 |         break; | 
 |     case CC_OP_CMPB: | 
 |         SET_FLAGS_SUB(int8_t, uint8_t); | 
 |         break; | 
 |     case CC_OP_CMPW: | 
 |         SET_FLAGS_SUB(int16_t, uint16_t); | 
 |         break; | 
 |     case CC_OP_ADDX: | 
 |         SET_NZ(dest); | 
 |         if (dest <= src) | 
 |             flags |= CCF_C; | 
 |         tmp = dest - src - 1; | 
 |         if (HIGHBIT & (src ^ dest) & ~(tmp ^ src)) | 
 |             flags |= CCF_V; | 
 |         break; | 
 |     case CC_OP_SUBX: | 
 |         SET_NZ(dest); | 
 |         tmp = dest + src + 1; | 
 |         if (tmp <= src) | 
 |             flags |= CCF_C; | 
 |         if (HIGHBIT & (tmp ^ dest) & (tmp ^ src)) | 
 |             flags |= CCF_V; | 
 |         break; | 
 |     case CC_OP_SHIFT: | 
 |         SET_NZ(dest); | 
 |         if (src) | 
 |             flags |= CCF_C; | 
 |         break; | 
 |     default: | 
 |         cpu_abort(env, "Bad CC_OP %d", cc_op); | 
 |     } | 
 |     env->cc_op = CC_OP_FLAGS; | 
 |     env->cc_dest = flags; | 
 | } | 
 |  | 
 | void HELPER(movec)(CPUM68KState *env, uint32_t reg, uint32_t val) | 
 | { | 
 |     switch (reg) { | 
 |     case 0x02: /* CACR */ | 
 |         env->cacr = val; | 
 |         m68k_switch_sp(env); | 
 |         break; | 
 |     case 0x04: case 0x05: case 0x06: case 0x07: /* ACR[0-3] */ | 
 |         /* TODO: Implement Access Control Registers.  */ | 
 |         break; | 
 |     case 0x801: /* VBR */ | 
 |         env->vbr = val; | 
 |         break; | 
 |     /* TODO: Implement control registers.  */ | 
 |     default: | 
 |         cpu_abort(env, "Unimplemented control register write 0x%x = 0x%x\n", | 
 |                   reg, val); | 
 |     } | 
 | } | 
 |  | 
 | void HELPER(set_macsr)(CPUM68KState *env, uint32_t val) | 
 | { | 
 |     uint32_t acc; | 
 |     int8_t exthigh; | 
 |     uint8_t extlow; | 
 |     uint64_t regval; | 
 |     int i; | 
 |     if ((env->macsr ^ val) & (MACSR_FI | MACSR_SU)) { | 
 |         for (i = 0; i < 4; i++) { | 
 |             regval = env->macc[i]; | 
 |             exthigh = regval >> 40; | 
 |             if (env->macsr & MACSR_FI) { | 
 |                 acc = regval >> 8; | 
 |                 extlow = regval; | 
 |             } else { | 
 |                 acc = regval; | 
 |                 extlow = regval >> 32; | 
 |             } | 
 |             if (env->macsr & MACSR_FI) { | 
 |                 regval = (((uint64_t)acc) << 8) | extlow; | 
 |                 regval |= ((int64_t)exthigh) << 40; | 
 |             } else if (env->macsr & MACSR_SU) { | 
 |                 regval = acc | (((int64_t)extlow) << 32); | 
 |                 regval |= ((int64_t)exthigh) << 40; | 
 |             } else { | 
 |                 regval = acc | (((uint64_t)extlow) << 32); | 
 |                 regval |= ((uint64_t)(uint8_t)exthigh) << 40; | 
 |             } | 
 |             env->macc[i] = regval; | 
 |         } | 
 |     } | 
 |     env->macsr = val; | 
 | } | 
 |  | 
 | void m68k_switch_sp(CPUM68KState *env) | 
 | { | 
 |     int new_sp; | 
 |  | 
 |     env->sp[env->current_sp] = env->aregs[7]; | 
 |     new_sp = (env->sr & SR_S && env->cacr & M68K_CACR_EUSP) | 
 |              ? M68K_SSP : M68K_USP; | 
 |     env->aregs[7] = env->sp[new_sp]; | 
 |     env->current_sp = new_sp; | 
 | } | 
 |  | 
 | #if defined(CONFIG_USER_ONLY) | 
 |  | 
 | int cpu_m68k_handle_mmu_fault (CPUState *env, target_ulong address, int rw, | 
 |                                int mmu_idx) | 
 | { | 
 |     env->exception_index = EXCP_ACCESS; | 
 |     env->mmu.ar = address; | 
 |     return 1; | 
 | } | 
 |  | 
 | #else | 
 |  | 
 | /* MMU */ | 
 |  | 
 | /* TODO: This will need fixing once the MMU is implemented.  */ | 
 | target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr) | 
 | { | 
 |     return addr; | 
 | } | 
 |  | 
 | int cpu_m68k_handle_mmu_fault (CPUState *env, target_ulong address, int rw, | 
 |                                int mmu_idx) | 
 | { | 
 |     int prot; | 
 |  | 
 |     address &= TARGET_PAGE_MASK; | 
 |     prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; | 
 |     tlb_set_page(env, address, address, prot, mmu_idx, TARGET_PAGE_SIZE); | 
 |     return 0; | 
 | } | 
 |  | 
 | /* Notify CPU of a pending interrupt.  Prioritization and vectoring should | 
 |    be handled by the interrupt controller.  Real hardware only requests | 
 |    the vector when the interrupt is acknowledged by the CPU.  For | 
 |    simplicitly we calculate it when the interrupt is signalled.  */ | 
 | void m68k_set_irq_level(CPUM68KState *env, int level, uint8_t vector) | 
 | { | 
 |     env->pending_level = level; | 
 |     env->pending_vector = vector; | 
 |     if (level) | 
 |         cpu_interrupt(env, CPU_INTERRUPT_HARD); | 
 |     else | 
 |         cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); | 
 | } | 
 |  | 
 | #endif | 
 |  | 
 | uint32_t HELPER(bitrev)(uint32_t x) | 
 | { | 
 |     x = ((x >> 1) & 0x55555555u) | ((x << 1) & 0xaaaaaaaau); | 
 |     x = ((x >> 2) & 0x33333333u) | ((x << 2) & 0xccccccccu); | 
 |     x = ((x >> 4) & 0x0f0f0f0fu) | ((x << 4) & 0xf0f0f0f0u); | 
 |     return bswap32(x); | 
 | } | 
 |  | 
 | uint32_t HELPER(ff1)(uint32_t x) | 
 | { | 
 |     int n; | 
 |     for (n = 32; x; n--) | 
 |         x >>= 1; | 
 |     return n; | 
 | } | 
 |  | 
 | uint32_t HELPER(sats)(uint32_t val, uint32_t ccr) | 
 | { | 
 |     /* The result has the opposite sign to the original value.  */ | 
 |     if (ccr & CCF_V) | 
 |         val = (((int32_t)val) >> 31) ^ SIGNBIT; | 
 |     return val; | 
 | } | 
 |  | 
 | uint32_t HELPER(subx_cc)(CPUState *env, uint32_t op1, uint32_t op2) | 
 | { | 
 |     uint32_t res; | 
 |     uint32_t old_flags; | 
 |  | 
 |     old_flags = env->cc_dest; | 
 |     if (env->cc_x) { | 
 |         env->cc_x = (op1 <= op2); | 
 |         env->cc_op = CC_OP_SUBX; | 
 |         res = op1 - (op2 + 1); | 
 |     } else { | 
 |         env->cc_x = (op1 < op2); | 
 |         env->cc_op = CC_OP_SUB; | 
 |         res = op1 - op2; | 
 |     } | 
 |     env->cc_dest = res; | 
 |     env->cc_src = op2; | 
 |     cpu_m68k_flush_flags(env, env->cc_op); | 
 |     /* !Z is sticky.  */ | 
 |     env->cc_dest &= (old_flags | ~CCF_Z); | 
 |     return res; | 
 | } | 
 |  | 
 | uint32_t HELPER(addx_cc)(CPUState *env, uint32_t op1, uint32_t op2) | 
 | { | 
 |     uint32_t res; | 
 |     uint32_t old_flags; | 
 |  | 
 |     old_flags = env->cc_dest; | 
 |     if (env->cc_x) { | 
 |         res = op1 + op2 + 1; | 
 |         env->cc_x = (res <= op2); | 
 |         env->cc_op = CC_OP_ADDX; | 
 |     } else { | 
 |         res = op1 + op2; | 
 |         env->cc_x = (res < op2); | 
 |         env->cc_op = CC_OP_ADD; | 
 |     } | 
 |     env->cc_dest = res; | 
 |     env->cc_src = op2; | 
 |     cpu_m68k_flush_flags(env, env->cc_op); | 
 |     /* !Z is sticky.  */ | 
 |     env->cc_dest &= (old_flags | ~CCF_Z); | 
 |     return res; | 
 | } | 
 |  | 
 | uint32_t HELPER(xflag_lt)(uint32_t a, uint32_t b) | 
 | { | 
 |     return a < b; | 
 | } | 
 |  | 
 | void HELPER(set_sr)(CPUState *env, uint32_t val) | 
 | { | 
 |     env->sr = val & 0xffff; | 
 |     m68k_switch_sp(env); | 
 | } | 
 |  | 
 | uint32_t HELPER(shl_cc)(CPUState *env, uint32_t val, uint32_t shift) | 
 | { | 
 |     uint32_t result; | 
 |     uint32_t cf; | 
 |  | 
 |     shift &= 63; | 
 |     if (shift == 0) { | 
 |         result = val; | 
 |         cf = env->cc_src & CCF_C; | 
 |     } else if (shift < 32) { | 
 |         result = val << shift; | 
 |         cf = (val >> (32 - shift)) & 1; | 
 |     } else if (shift == 32) { | 
 |         result = 0; | 
 |         cf = val & 1; | 
 |     } else /* shift > 32 */ { | 
 |         result = 0; | 
 |         cf = 0; | 
 |     } | 
 |     env->cc_src = cf; | 
 |     env->cc_x = (cf != 0); | 
 |     env->cc_dest = result; | 
 |     return result; | 
 | } | 
 |  | 
 | uint32_t HELPER(shr_cc)(CPUState *env, uint32_t val, uint32_t shift) | 
 | { | 
 |     uint32_t result; | 
 |     uint32_t cf; | 
 |  | 
 |     shift &= 63; | 
 |     if (shift == 0) { | 
 |         result = val; | 
 |         cf = env->cc_src & CCF_C; | 
 |     } else if (shift < 32) { | 
 |         result = val >> shift; | 
 |         cf = (val >> (shift - 1)) & 1; | 
 |     } else if (shift == 32) { | 
 |         result = 0; | 
 |         cf = val >> 31; | 
 |     } else /* shift > 32 */ { | 
 |         result = 0; | 
 |         cf = 0; | 
 |     } | 
 |     env->cc_src = cf; | 
 |     env->cc_x = (cf != 0); | 
 |     env->cc_dest = result; | 
 |     return result; | 
 | } | 
 |  | 
 | uint32_t HELPER(sar_cc)(CPUState *env, uint32_t val, uint32_t shift) | 
 | { | 
 |     uint32_t result; | 
 |     uint32_t cf; | 
 |  | 
 |     shift &= 63; | 
 |     if (shift == 0) { | 
 |         result = val; | 
 |         cf = (env->cc_src & CCF_C) != 0; | 
 |     } else if (shift < 32) { | 
 |         result = (int32_t)val >> shift; | 
 |         cf = (val >> (shift - 1)) & 1; | 
 |     } else /* shift >= 32 */ { | 
 |         result = (int32_t)val >> 31; | 
 |         cf = val >> 31; | 
 |     } | 
 |     env->cc_src = cf; | 
 |     env->cc_x = cf; | 
 |     env->cc_dest = result; | 
 |     return result; | 
 | } | 
 |  | 
 | /* FPU helpers.  */ | 
 | uint32_t HELPER(f64_to_i32)(CPUState *env, float64 val) | 
 | { | 
 |     return float64_to_int32(val, &env->fp_status); | 
 | } | 
 |  | 
 | float32 HELPER(f64_to_f32)(CPUState *env, float64 val) | 
 | { | 
 |     return float64_to_float32(val, &env->fp_status); | 
 | } | 
 |  | 
 | float64 HELPER(i32_to_f64)(CPUState *env, uint32_t val) | 
 | { | 
 |     return int32_to_float64(val, &env->fp_status); | 
 | } | 
 |  | 
 | float64 HELPER(f32_to_f64)(CPUState *env, float32 val) | 
 | { | 
 |     return float32_to_float64(val, &env->fp_status); | 
 | } | 
 |  | 
 | float64 HELPER(iround_f64)(CPUState *env, float64 val) | 
 | { | 
 |     return float64_round_to_int(val, &env->fp_status); | 
 | } | 
 |  | 
 | float64 HELPER(itrunc_f64)(CPUState *env, float64 val) | 
 | { | 
 |     return float64_trunc_to_int(val, &env->fp_status); | 
 | } | 
 |  | 
 | float64 HELPER(sqrt_f64)(CPUState *env, float64 val) | 
 | { | 
 |     return float64_sqrt(val, &env->fp_status); | 
 | } | 
 |  | 
 | float64 HELPER(abs_f64)(float64 val) | 
 | { | 
 |     return float64_abs(val); | 
 | } | 
 |  | 
 | float64 HELPER(chs_f64)(float64 val) | 
 | { | 
 |     return float64_chs(val); | 
 | } | 
 |  | 
 | float64 HELPER(add_f64)(CPUState *env, float64 a, float64 b) | 
 | { | 
 |     return float64_add(a, b, &env->fp_status); | 
 | } | 
 |  | 
 | float64 HELPER(sub_f64)(CPUState *env, float64 a, float64 b) | 
 | { | 
 |     return float64_sub(a, b, &env->fp_status); | 
 | } | 
 |  | 
 | float64 HELPER(mul_f64)(CPUState *env, float64 a, float64 b) | 
 | { | 
 |     return float64_mul(a, b, &env->fp_status); | 
 | } | 
 |  | 
 | float64 HELPER(div_f64)(CPUState *env, float64 a, float64 b) | 
 | { | 
 |     return float64_div(a, b, &env->fp_status); | 
 | } | 
 |  | 
 | float64 HELPER(sub_cmp_f64)(CPUState *env, float64 a, float64 b) | 
 | { | 
 |     /* ??? This may incorrectly raise exceptions.  */ | 
 |     /* ??? Should flush denormals to zero.  */ | 
 |     float64 res; | 
 |     res = float64_sub(a, b, &env->fp_status); | 
 |     if (float64_is_quiet_nan(res)) { | 
 |         /* +/-inf compares equal against itself, but sub returns nan.  */ | 
 |         if (!float64_is_quiet_nan(a) | 
 |             && !float64_is_quiet_nan(b)) { | 
 |             res = float64_zero; | 
 |             if (float64_lt_quiet(a, res, &env->fp_status)) | 
 |                 res = float64_chs(res); | 
 |         } | 
 |     } | 
 |     return res; | 
 | } | 
 |  | 
 | uint32_t HELPER(compare_f64)(CPUState *env, float64 val) | 
 | { | 
 |     return float64_compare_quiet(val, float64_zero, &env->fp_status); | 
 | } | 
 |  | 
 | /* MAC unit.  */ | 
 | /* FIXME: The MAC unit implementation is a bit of a mess.  Some helpers | 
 |    take values,  others take register numbers and manipulate the contents | 
 |    in-place.  */ | 
 | void HELPER(mac_move)(CPUState *env, uint32_t dest, uint32_t src) | 
 | { | 
 |     uint32_t mask; | 
 |     env->macc[dest] = env->macc[src]; | 
 |     mask = MACSR_PAV0 << dest; | 
 |     if (env->macsr & (MACSR_PAV0 << src)) | 
 |         env->macsr |= mask; | 
 |     else | 
 |         env->macsr &= ~mask; | 
 | } | 
 |  | 
 | uint64_t HELPER(macmuls)(CPUState *env, uint32_t op1, uint32_t op2) | 
 | { | 
 |     int64_t product; | 
 |     int64_t res; | 
 |  | 
 |     product = (uint64_t)op1 * op2; | 
 |     res = (product << 24) >> 24; | 
 |     if (res != product) { | 
 |         env->macsr |= MACSR_V; | 
 |         if (env->macsr & MACSR_OMC) { | 
 |             /* Make sure the accumulate operation overflows.  */ | 
 |             if (product < 0) | 
 |                 res = ~(1ll << 50); | 
 |             else | 
 |                 res = 1ll << 50; | 
 |         } | 
 |     } | 
 |     return res; | 
 | } | 
 |  | 
 | uint64_t HELPER(macmulu)(CPUState *env, uint32_t op1, uint32_t op2) | 
 | { | 
 |     uint64_t product; | 
 |  | 
 |     product = (uint64_t)op1 * op2; | 
 |     if (product & (0xffffffull << 40)) { | 
 |         env->macsr |= MACSR_V; | 
 |         if (env->macsr & MACSR_OMC) { | 
 |             /* Make sure the accumulate operation overflows.  */ | 
 |             product = 1ll << 50; | 
 |         } else { | 
 |             product &= ((1ull << 40) - 1); | 
 |         } | 
 |     } | 
 |     return product; | 
 | } | 
 |  | 
 | uint64_t HELPER(macmulf)(CPUState *env, uint32_t op1, uint32_t op2) | 
 | { | 
 |     uint64_t product; | 
 |     uint32_t remainder; | 
 |  | 
 |     product = (uint64_t)op1 * op2; | 
 |     if (env->macsr & MACSR_RT) { | 
 |         remainder = product & 0xffffff; | 
 |         product >>= 24; | 
 |         if (remainder > 0x800000) | 
 |             product++; | 
 |         else if (remainder == 0x800000) | 
 |             product += (product & 1); | 
 |     } else { | 
 |         product >>= 24; | 
 |     } | 
 |     return product; | 
 | } | 
 |  | 
 | void HELPER(macsats)(CPUState *env, uint32_t acc) | 
 | { | 
 |     int64_t tmp; | 
 |     int64_t result; | 
 |     tmp = env->macc[acc]; | 
 |     result = ((tmp << 16) >> 16); | 
 |     if (result != tmp) { | 
 |         env->macsr |= MACSR_V; | 
 |     } | 
 |     if (env->macsr & MACSR_V) { | 
 |         env->macsr |= MACSR_PAV0 << acc; | 
 |         if (env->macsr & MACSR_OMC) { | 
 |             /* The result is saturated to 32 bits, despite overflow occurring | 
 |                at 48 bits.  Seems weird, but that's what the hardware docs | 
 |                say.  */ | 
 |             result = (result >> 63) ^ 0x7fffffff; | 
 |         } | 
 |     } | 
 |     env->macc[acc] = result; | 
 | } | 
 |  | 
 | void HELPER(macsatu)(CPUState *env, uint32_t acc) | 
 | { | 
 |     uint64_t val; | 
 |  | 
 |     val = env->macc[acc]; | 
 |     if (val & (0xffffull << 48)) { | 
 |         env->macsr |= MACSR_V; | 
 |     } | 
 |     if (env->macsr & MACSR_V) { | 
 |         env->macsr |= MACSR_PAV0 << acc; | 
 |         if (env->macsr & MACSR_OMC) { | 
 |             if (val > (1ull << 53)) | 
 |                 val = 0; | 
 |             else | 
 |                 val = (1ull << 48) - 1; | 
 |         } else { | 
 |             val &= ((1ull << 48) - 1); | 
 |         } | 
 |     } | 
 |     env->macc[acc] = val; | 
 | } | 
 |  | 
 | void HELPER(macsatf)(CPUState *env, uint32_t acc) | 
 | { | 
 |     int64_t sum; | 
 |     int64_t result; | 
 |  | 
 |     sum = env->macc[acc]; | 
 |     result = (sum << 16) >> 16; | 
 |     if (result != sum) { | 
 |         env->macsr |= MACSR_V; | 
 |     } | 
 |     if (env->macsr & MACSR_V) { | 
 |         env->macsr |= MACSR_PAV0 << acc; | 
 |         if (env->macsr & MACSR_OMC) { | 
 |             result = (result >> 63) ^ 0x7fffffffffffll; | 
 |         } | 
 |     } | 
 |     env->macc[acc] = result; | 
 | } | 
 |  | 
 | void HELPER(mac_set_flags)(CPUState *env, uint32_t acc) | 
 | { | 
 |     uint64_t val; | 
 |     val = env->macc[acc]; | 
 |     if (val == 0) { | 
 |         env->macsr |= MACSR_Z; | 
 |     } else if (val & (1ull << 47)) { | 
 |         env->macsr |= MACSR_N; | 
 |     } | 
 |     if (env->macsr & (MACSR_PAV0 << acc)) { | 
 |         env->macsr |= MACSR_V; | 
 |     } | 
 |     if (env->macsr & MACSR_FI) { | 
 |         val = ((int64_t)val) >> 40; | 
 |         if (val != 0 && val != -1) | 
 |             env->macsr |= MACSR_EV; | 
 |     } else if (env->macsr & MACSR_SU) { | 
 |         val = ((int64_t)val) >> 32; | 
 |         if (val != 0 && val != -1) | 
 |             env->macsr |= MACSR_EV; | 
 |     } else { | 
 |         if ((val >> 32) != 0) | 
 |             env->macsr |= MACSR_EV; | 
 |     } | 
 | } | 
 |  | 
 | void HELPER(flush_flags)(CPUState *env, uint32_t cc_op) | 
 | { | 
 |     cpu_m68k_flush_flags(env, cc_op); | 
 | } | 
 |  | 
 | uint32_t HELPER(get_macf)(CPUState *env, uint64_t val) | 
 | { | 
 |     int rem; | 
 |     uint32_t result; | 
 |  | 
 |     if (env->macsr & MACSR_SU) { | 
 |         /* 16-bit rounding.  */ | 
 |         rem = val & 0xffffff; | 
 |         val = (val >> 24) & 0xffffu; | 
 |         if (rem > 0x800000) | 
 |             val++; | 
 |         else if (rem == 0x800000) | 
 |             val += (val & 1); | 
 |     } else if (env->macsr & MACSR_RT) { | 
 |         /* 32-bit rounding.  */ | 
 |         rem = val & 0xff; | 
 |         val >>= 8; | 
 |         if (rem > 0x80) | 
 |             val++; | 
 |         else if (rem == 0x80) | 
 |             val += (val & 1); | 
 |     } else { | 
 |         /* No rounding.  */ | 
 |         val >>= 8; | 
 |     } | 
 |     if (env->macsr & MACSR_OMC) { | 
 |         /* Saturate.  */ | 
 |         if (env->macsr & MACSR_SU) { | 
 |             if (val != (uint16_t) val) { | 
 |                 result = ((val >> 63) ^ 0x7fff) & 0xffff; | 
 |             } else { | 
 |                 result = val & 0xffff; | 
 |             } | 
 |         } else { | 
 |             if (val != (uint32_t)val) { | 
 |                 result = ((uint32_t)(val >> 63) & 0x7fffffff); | 
 |             } else { | 
 |                 result = (uint32_t)val; | 
 |             } | 
 |         } | 
 |     } else { | 
 |         /* No saturation.  */ | 
 |         if (env->macsr & MACSR_SU) { | 
 |             result = val & 0xffff; | 
 |         } else { | 
 |             result = (uint32_t)val; | 
 |         } | 
 |     } | 
 |     return result; | 
 | } | 
 |  | 
 | uint32_t HELPER(get_macs)(uint64_t val) | 
 | { | 
 |     if (val == (int32_t)val) { | 
 |         return (int32_t)val; | 
 |     } else { | 
 |         return (val >> 61) ^ ~SIGNBIT; | 
 |     } | 
 | } | 
 |  | 
 | uint32_t HELPER(get_macu)(uint64_t val) | 
 | { | 
 |     if ((val >> 32) == 0) { | 
 |         return (uint32_t)val; | 
 |     } else { | 
 |         return 0xffffffffu; | 
 |     } | 
 | } | 
 |  | 
 | uint32_t HELPER(get_mac_extf)(CPUState *env, uint32_t acc) | 
 | { | 
 |     uint32_t val; | 
 |     val = env->macc[acc] & 0x00ff; | 
 |     val = (env->macc[acc] >> 32) & 0xff00; | 
 |     val |= (env->macc[acc + 1] << 16) & 0x00ff0000; | 
 |     val |= (env->macc[acc + 1] >> 16) & 0xff000000; | 
 |     return val; | 
 | } | 
 |  | 
 | uint32_t HELPER(get_mac_exti)(CPUState *env, uint32_t acc) | 
 | { | 
 |     uint32_t val; | 
 |     val = (env->macc[acc] >> 32) & 0xffff; | 
 |     val |= (env->macc[acc + 1] >> 16) & 0xffff0000; | 
 |     return val; | 
 | } | 
 |  | 
 | void HELPER(set_mac_extf)(CPUState *env, uint32_t val, uint32_t acc) | 
 | { | 
 |     int64_t res; | 
 |     int32_t tmp; | 
 |     res = env->macc[acc] & 0xffffffff00ull; | 
 |     tmp = (int16_t)(val & 0xff00); | 
 |     res |= ((int64_t)tmp) << 32; | 
 |     res |= val & 0xff; | 
 |     env->macc[acc] = res; | 
 |     res = env->macc[acc + 1] & 0xffffffff00ull; | 
 |     tmp = (val & 0xff000000); | 
 |     res |= ((int64_t)tmp) << 16; | 
 |     res |= (val >> 16) & 0xff; | 
 |     env->macc[acc + 1] = res; | 
 | } | 
 |  | 
 | void HELPER(set_mac_exts)(CPUState *env, uint32_t val, uint32_t acc) | 
 | { | 
 |     int64_t res; | 
 |     int32_t tmp; | 
 |     res = (uint32_t)env->macc[acc]; | 
 |     tmp = (int16_t)val; | 
 |     res |= ((int64_t)tmp) << 32; | 
 |     env->macc[acc] = res; | 
 |     res = (uint32_t)env->macc[acc + 1]; | 
 |     tmp = val & 0xffff0000; | 
 |     res |= (int64_t)tmp << 16; | 
 |     env->macc[acc + 1] = res; | 
 | } | 
 |  | 
 | void HELPER(set_mac_extu)(CPUState *env, uint32_t val, uint32_t acc) | 
 | { | 
 |     uint64_t res; | 
 |     res = (uint32_t)env->macc[acc]; | 
 |     res |= ((uint64_t)(val & 0xffff)) << 32; | 
 |     env->macc[acc] = res; | 
 |     res = (uint32_t)env->macc[acc + 1]; | 
 |     res |= (uint64_t)(val & 0xffff0000) << 16; | 
 |     env->macc[acc + 1] = res; | 
 | } |