More user timer fixes (Robert Reif)
[qemu] / target-m68k / op.c
index 8ea7589..8600f43 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  m68k micro operations
- * 
- *  Copyright (c) 2006 CodeSourcery
+ *
+ *  Copyright (c) 2006-2007 CodeSourcery
  *  Written by Paul Brook
  *
  * This library is free software; you can redistribute it and/or
@@ -48,23 +48,23 @@ static long qreg_offsets[] = {
 uint32_t
 get_op(int qreg)
 {
-    if (qreg == QREG_T0) {
+    if (qreg >= TARGET_NUM_QREGS) {
+        return env->qregs[qreg - TARGET_NUM_QREGS];
+    } else if (qreg == QREG_T0) {
         return T0;
-    } else if (qreg < TARGET_NUM_QREGS) {
-        return *(uint32_t *)(((long)env) + qreg_offsets[qreg]);
     } else {
-        return env->qregs[qreg - TARGET_NUM_QREGS];
+        return *(uint32_t *)(((long)env) + qreg_offsets[qreg]);
     }
 }
 
 void set_op(int qreg, uint32_t val)
 {
-    if (qreg == QREG_T0) {
+    if (qreg >= TARGET_NUM_QREGS) {
+        env->qregs[qreg - TARGET_NUM_QREGS] = val;
+    } else if (qreg == QREG_T0) {
         T0 = val;
-    } else if (qreg < TARGET_NUM_QREGS) {
-        *(uint32_t *)(((long)env) + qreg_offsets[qreg]) = val;
     } else {
-        env->qregs[qreg - TARGET_NUM_QREGS] = val;
+        *(uint32_t *)(((long)env) + qreg_offsets[qreg]) = val;
     }
 }
 
@@ -86,7 +86,7 @@ void set_opf64(int qreg, float64 val)
     }
 }
 
-#define OP(name) void OPPROTO op_##name (void)
+#define OP(name) void OPPROTO glue(op_,name) (void)
 
 OP(mov32)
 {
@@ -170,7 +170,17 @@ OP(btest)
     FORCE_RET();
 }
 
-OP(addx_cc)
+OP(ff1)
+{
+    uint32_t arg = get_op(PARAM2);
+    int n;
+    for (n = 32; arg; n--)
+        arg >>= 1;
+    set_op(PARAM1, n);
+    FORCE_RET();
+}
+
+OP(subx_cc)
 {
     uint32_t op1 = get_op(PARAM1);
     uint32_t op2 = get_op(PARAM2);
@@ -188,7 +198,7 @@ OP(addx_cc)
     FORCE_RET();
 }
 
-OP(subx_cc)
+OP(addx_cc)
 {
     uint32_t op1 = get_op(PARAM1);
     uint32_t op2 = get_op(PARAM2);
@@ -275,6 +285,16 @@ OP(shr_cc)
     FORCE_RET();
 }
 
+OP(sar32)
+{
+    int32_t op2 = get_op(PARAM2);
+    uint32_t op3 = get_op(PARAM3);
+    uint32_t result;
+    result = op2 >> op3;
+    set_op(PARAM1, result);
+    FORCE_RET();
+}
+
 OP(sar_cc)
 {
     int32_t op1 = get_op(PARAM1);
@@ -316,83 +336,9 @@ OP(ext16s32)
     FORCE_RET();
 }
 
-/* Load/store ops.  */
-OP(ld8u32)
-{
-    uint32_t addr = get_op(PARAM2);
-    set_op(PARAM1, ldub(addr));
-    FORCE_RET();
-}
-
-OP(ld8s32)
-{
-    uint32_t addr = get_op(PARAM2);
-    set_op(PARAM1, ldsb(addr));
-    FORCE_RET();
-}
-
-OP(ld16u32)
-{
-    uint32_t addr = get_op(PARAM2);
-    set_op(PARAM1, lduw(addr));
-    FORCE_RET();
-}
-
-OP(ld16s32)
-{
-    uint32_t addr = get_op(PARAM2);
-    set_op(PARAM1, ldsw(addr));
-    FORCE_RET();
-}
-
-OP(ld32)
-{
-    uint32_t addr = get_op(PARAM2);
-    set_op(PARAM1, ldl(addr));
-    FORCE_RET();
-}
-
-OP(st8)
-{
-    uint32_t addr = get_op(PARAM1);
-    stb(addr, get_op(PARAM2));
-    FORCE_RET();
-}
-
-OP(st16)
-{
-    uint32_t addr = get_op(PARAM1);
-    stw(addr, get_op(PARAM2));
-    FORCE_RET();
-}
-
-OP(st32)
-{
-    uint32_t addr = get_op(PARAM1);
-    stl(addr, get_op(PARAM2));
-    FORCE_RET();
-}
-
-OP(ldf64)
-{
-    uint32_t addr = get_op(PARAM2);
-    set_opf64(PARAM1, ldfq(addr));
-    FORCE_RET();
-}
-
-OP(stf64)
-{
-    uint32_t addr = get_op(PARAM1);
-    stfq(addr, get_opf64(PARAM2));
-    FORCE_RET();
-}
-
 OP(flush_flags)
 {
-    int cc_op  = PARAM1;
-    if (cc_op == CC_OP_DYNAMIC)
-        cc_op = env->cc_op;
-    cpu_m68k_flush_flags(env, cc_op);
+    cpu_m68k_flush_flags(env, env->cc_op);
     FORCE_RET();
 }
 
@@ -403,7 +349,7 @@ OP(divu)
     uint32_t quot;
     uint32_t rem;
     uint32_t flags;
-    
+
     num = env->div1;
     den = env->div2;
     /* ??? This needs to make sure the throwing location is accurate.  */
@@ -434,7 +380,7 @@ OP(divs)
     int32_t quot;
     int32_t rem;
     int32_t flags;
-    
+
     num = env->div1;
     den = env->div2;
     if (den == 0)
@@ -454,6 +400,20 @@ OP(divs)
     FORCE_RET();
 }
 
+/* Halt is special because it may be a semihosting call.  */
+OP(halt)
+{
+    RAISE_EXCEPTION(EXCP_HALT_INSN);
+    FORCE_RET();
+}
+
+OP(stop)
+{
+    env->halted = 1;
+    RAISE_EXCEPTION(EXCP_HLT);
+    FORCE_RET();
+}
+
 OP(raise_exception)
 {
     RAISE_EXCEPTION(PARAM1);
@@ -515,42 +475,50 @@ OP(fp_result)
     FORCE_RET();
 }
 
+OP(set_sr)
+{
+    env->sr = get_op(PARAM1) & 0xffff;
+    m68k_switch_sp(env);
+    FORCE_RET();
+}
+
 OP(jmp)
 {
     GOTO_LABEL_PARAM(1);
 }
 
-/* These ops involve a function call, which probably requires a stack frame
-   and breaks things on some hosts.  */
-OP(jmp_z32)
+OP(set_T0_z32)
 {
     uint32_t arg = get_op(PARAM1);
-    if (arg == 0)
-        GOTO_LABEL_PARAM(2);
+    T0 = (arg == 0);
     FORCE_RET();
 }
 
-OP(jmp_nz32)
+OP(set_T0_nz32)
 {
     uint32_t arg = get_op(PARAM1);
-    if (arg != 0)
-        GOTO_LABEL_PARAM(2);
+    T0 = (arg != 0);
     FORCE_RET();
 }
 
-OP(jmp_s32)
+OP(set_T0_s32)
 {
     int32_t arg = get_op(PARAM1);
-    if (arg < 0)
-        GOTO_LABEL_PARAM(2);
+    T0 = (arg > 0);
     FORCE_RET();
 }
 
-OP(jmp_ns32)
+OP(set_T0_ns32)
 {
     int32_t arg = get_op(PARAM1);
-    if (arg >= 0)
-        GOTO_LABEL_PARAM(2);
+    T0 = (arg >= 0);
+    FORCE_RET();
+}
+
+OP(jmp_T0)
+{
+    if (T0)
+        GOTO_LABEL_PARAM(1);
     FORCE_RET();
 }
 
@@ -679,3 +647,429 @@ OP(compare_quietf64)
     set_op(PARAM1, float64_compare_quiet(op0, op1, &CPU_FP_STATUS));
     FORCE_RET();
 }
+
+OP(movec)
+{
+    int op1 = get_op(PARAM1);
+    uint32_t op2 = get_op(PARAM2);
+    helper_movec(env, op1, op2);
+}
+
+/* Memory access.  */
+
+#define MEMSUFFIX _raw
+#include "op_mem.h"
+
+#if !defined(CONFIG_USER_ONLY)
+#define MEMSUFFIX _user
+#include "op_mem.h"
+#define MEMSUFFIX _kernel
+#include "op_mem.h"
+#endif
+
+/* MAC unit.  */
+/* TODO: The MAC instructions use 64-bit arithmetic fairly extensively.
+   This results in fairly large ops (and sometimes other issues) on 32-bit
+   hosts.  Maybe move most of them into helpers.  */
+OP(macmuls)
+{
+    uint32_t op1 = get_op(PARAM1);
+    uint32_t op2 = get_op(PARAM2);
+    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;
+        }
+    }
+    env->mactmp = res;
+    FORCE_RET();
+}
+
+OP(macmulu)
+{
+    uint32_t op1 = get_op(PARAM1);
+    uint32_t op2 = get_op(PARAM2);
+    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);
+        }
+    }
+    env->mactmp = product;
+    FORCE_RET();
+}
+
+OP(macmulf)
+{
+    int32_t op1 = get_op(PARAM1);
+    int32_t op2 = get_op(PARAM2);
+    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;
+    }
+    env->mactmp = product;
+    FORCE_RET();
+}
+
+OP(macshl)
+{
+    env->mactmp <<= 1;
+}
+
+OP(macshr)
+{
+    env->mactmp >>= 1;
+}
+
+OP(macadd)
+{
+    int acc = PARAM1;
+    env->macc[acc] += env->mactmp;
+    FORCE_RET();
+}
+
+OP(macsub)
+{
+    int acc = PARAM1;
+    env->macc[acc] -= env->mactmp;
+    FORCE_RET();
+}
+
+OP(macsats)
+{
+    int acc = PARAM1;
+    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) {
+            /* The result is saturated to 32 bits, despite overflow occuring
+               at 48 bits.  Seems weird, but that's what the hardware docs
+               say.  */
+            result = (result >> 63) ^ 0x7fffffff;
+        }
+    }
+    env->macc[acc] = result;
+    FORCE_RET();
+}
+
+OP(macsatu)
+{
+    int acc = PARAM1;
+    uint64_t sum;
+
+    sum = env->macc[acc];
+    if (sum & (0xffffull << 48)) {
+        env->macsr |= MACSR_V;
+    }
+    if (env->macsr & MACSR_V) {
+        env->macsr |= MACSR_PAV0 << acc;
+        if (env->macsr & MACSR_OMC) {
+            if (sum > (1ull << 53))
+                sum = 0;
+            else
+                sum = (1ull << 48) - 1;
+        } else {
+            sum &= ((1ull << 48) - 1);
+        }
+    }
+    FORCE_RET();
+}
+
+OP(macsatf)
+{
+    int acc = PARAM1;
+    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;
+    FORCE_RET();
+}
+
+OP(mac_clear_flags)
+{
+    env->macsr &= ~(MACSR_V | MACSR_Z | MACSR_N | MACSR_EV);
+}
+
+OP(mac_set_flags)
+{
+    int acc = PARAM1;
+    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;
+    }
+    FORCE_RET();
+}
+
+OP(get_macf)
+{
+    int acc = PARAM2;
+    int64_t val;
+    int rem;
+    uint32_t result;
+
+    val = env->macc[acc];
+    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;
+        }
+    }
+    set_op(PARAM1, result);
+    FORCE_RET();
+}
+
+OP(get_maci)
+{
+    int acc = PARAM2;
+    set_op(PARAM1, (uint32_t)env->macc[acc]);
+    FORCE_RET();
+}
+
+OP(get_macs)
+{
+    int acc = PARAM2;
+    int64_t val = env->macc[acc];
+    uint32_t result;
+    if (val == (int32_t)val) {
+        result = (int32_t)val;
+    } else {
+        result = (val >> 61) ^ 0x7fffffff;
+    }
+    set_op(PARAM1, result);
+    FORCE_RET();
+}
+
+OP(get_macu)
+{
+    int acc = PARAM2;
+    uint64_t val = env->macc[acc];
+    uint32_t result;
+    if ((val >> 32) == 0) {
+        result = (uint32_t)val;
+    } else {
+        result = 0xffffffffu;
+    }
+    set_op(PARAM1, result);
+    FORCE_RET();
+}
+
+OP(clear_mac)
+{
+    int acc = PARAM1;
+
+    env->macc[acc] = 0;
+    env->macsr &= ~(MACSR_PAV0 << acc);
+    FORCE_RET();
+}
+
+OP(move_mac)
+{
+    int dest = PARAM1;
+    int src = PARAM2;
+    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;
+    FORCE_RET();
+}
+
+OP(get_mac_extf)
+{
+    uint32_t val;
+    int acc = PARAM2;
+    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;
+    set_op(PARAM1, val);
+    FORCE_RET();
+}
+
+OP(get_mac_exti)
+{
+    uint32_t val;
+    int acc = PARAM2;
+    val = (env->macc[acc] >> 32) & 0xffff;
+    val |= (env->macc[acc + 1] >> 16) & 0xffff0000;
+    set_op(PARAM1, val);
+    FORCE_RET();
+}
+
+OP(set_macf)
+{
+    int acc = PARAM2;
+    int32_t val = get_op(PARAM1);
+    env->macc[acc] = ((int64_t)val) << 8;
+    env->macsr &= ~(MACSR_PAV0 << acc);
+    FORCE_RET();
+}
+
+OP(set_macs)
+{
+    int acc = PARAM2;
+    int32_t val = get_op(PARAM1);
+    env->macc[acc] = val;
+    env->macsr &= ~(MACSR_PAV0 << acc);
+    FORCE_RET();
+}
+
+OP(set_macu)
+{
+    int acc = PARAM2;
+    uint32_t val = get_op(PARAM1);
+    env->macc[acc] = val;
+    env->macsr &= ~(MACSR_PAV0 << acc);
+    FORCE_RET();
+}
+
+OP(set_mac_extf)
+{
+    int acc = PARAM2;
+    int32_t val = get_op(PARAM1);
+    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;
+}
+
+OP(set_mac_exts)
+{
+    int acc = PARAM2;
+    int32_t val = get_op(PARAM1);
+    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;
+}
+
+OP(set_mac_extu)
+{
+    int acc = PARAM2;
+    int32_t val = get_op(PARAM1);
+    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;
+}
+
+OP(set_macsr)
+{
+    m68k_set_macsr(env, get_op(PARAM1));
+}