SATN fixes (Blue Swirl).
[qemu] / target-i386 / op.c
index 1daa551..a9a8665 100644 (file)
@@ -225,7 +225,7 @@ void OPPROTO op_bswapl_T0(void)
 #ifdef TARGET_X86_64
 void OPPROTO op_bswapq_T0(void)
 {
-    T0 = bswap64(T0);
+    helper_bswapq_T0();
 }
 #endif
 
@@ -286,8 +286,8 @@ void OPPROTO op_imull_EAX_T0(void)
 {
     int64_t res;
     res = (int64_t)((int32_t)EAX) * (int64_t)((int32_t)T0);
-    EAX = res;
-    EDX = res >> 32;
+    EAX = (uint32_t)(res);
+    EDX = (uint32_t)(res >> 32);
     CC_DST = res;
     CC_SRC = (res != (int32_t)res);
 }
@@ -328,7 +328,6 @@ void OPPROTO op_imulq_T0_T1(void)
 #endif
 
 /* division, flags are undefined */
-/* XXX: add exceptions for overflow */
 
 void OPPROTO op_divb_AL_T0(void)
 {
@@ -339,7 +338,10 @@ void OPPROTO op_divb_AL_T0(void)
     if (den == 0) {
         raise_exception(EXCP00_DIVZ);
     }
-    q = (num / den) & 0xff;
+    q = (num / den);
+    if (q > 0xff)
+        raise_exception(EXCP00_DIVZ);
+    q &= 0xff;
     r = (num % den) & 0xff;
     EAX = (EAX & ~0xffff) | (r << 8) | q;
 }
@@ -353,7 +355,10 @@ void OPPROTO op_idivb_AL_T0(void)
     if (den == 0) {
         raise_exception(EXCP00_DIVZ);
     }
-    q = (num / den) & 0xff;
+    q = (num / den);
+    if (q != (int8_t)q)
+        raise_exception(EXCP00_DIVZ);
+    q &= 0xff;
     r = (num % den) & 0xff;
     EAX = (EAX & ~0xffff) | (r << 8) | q;
 }
@@ -367,7 +372,10 @@ void OPPROTO op_divw_AX_T0(void)
     if (den == 0) {
         raise_exception(EXCP00_DIVZ);
     }
-    q = (num / den) & 0xffff;
+    q = (num / den);
+    if (q > 0xffff)
+        raise_exception(EXCP00_DIVZ);
+    q &= 0xffff;
     r = (num % den) & 0xffff;
     EAX = (EAX & ~0xffff) | q;
     EDX = (EDX & ~0xffff) | r;
@@ -382,7 +390,10 @@ void OPPROTO op_idivw_AX_T0(void)
     if (den == 0) {
         raise_exception(EXCP00_DIVZ);
     }
-    q = (num / den) & 0xffff;
+    q = (num / den);
+    if (q != (int16_t)q)
+        raise_exception(EXCP00_DIVZ);
+    q &= 0xffff;
     r = (num % den) & 0xffff;
     EAX = (EAX & ~0xffff) | q;
     EDX = (EDX & ~0xffff) | r;
@@ -517,6 +528,11 @@ void OPPROTO op_movq_T0_im64(void)
     T0 = PARAMQ1;
 }
 
+void OPPROTO op_movq_T1_im64(void)
+{
+    T1 = PARAMQ1;
+}
+
 void OPPROTO op_movq_A0_im(void)
 {
     A0 = (int32_t)PARAM1;
@@ -599,6 +615,7 @@ void OPPROTO op_movq_eip_im64(void)
 void OPPROTO op_hlt(void)
 {
     env->hflags &= ~HF_INHIBIT_IRQ_MASK; /* needed if sti is just before */
+    env->hflags |= HF_HALTED_MASK;
     env->exception_index = EXCP_HLT;
     cpu_loop_exit();
 }
@@ -611,11 +628,10 @@ void OPPROTO op_debug(void)
 
 void OPPROTO op_raise_interrupt(void)
 {
-    int intno;
-    unsigned int next_eip;
+    int intno, next_eip_addend;
     intno = PARAM1;
-    next_eip = PARAM2;
-    raise_interrupt(intno, 1, 0, next_eip);
+    next_eip_addend = PARAM2;
+    raise_interrupt(intno, 1, 0, next_eip_addend);
 }
 
 void OPPROTO op_raise_exception(void)
@@ -753,11 +769,6 @@ void OPPROTO op_movswl_T0_T0(void)
     T0 = (int16_t)T0;
 }
 
-void OPPROTO op_movslq_T0_T0(void)
-{
-    T0 = (int32_t)T0;
-}
-
 void OPPROTO op_movzwl_T0_T0(void)
 {
     T0 = (uint16_t)T0;
@@ -769,6 +780,11 @@ void OPPROTO op_movswl_EAX_AX(void)
 }
 
 #ifdef TARGET_X86_64
+void OPPROTO op_movslq_T0_T0(void)
+{
+    T0 = (int32_t)T0;
+}
+
 void OPPROTO op_movslq_RAX_EAX(void)
 {
     EAX = (int32_t)EAX;
@@ -850,7 +866,7 @@ void OPPROTO op_decq_ECX(void)
 
 void op_addl_A0_SS(void)
 {
-    A0 += (long)env->segs[R_SS].base;
+    A0 = (uint32_t)(A0 + env->segs[R_SS].base);
 }
 
 void op_subl_A0_2(void)
@@ -894,6 +910,11 @@ void op_addw_ESP_im(void)
 }
 
 #ifdef TARGET_X86_64
+void op_subq_A0_2(void)
+{
+    A0 -= 2;
+}
+
 void op_subq_A0_8(void)
 {
     A0 -= 8;
@@ -925,6 +946,13 @@ void OPPROTO op_enter_level(void)
     helper_enter_level(PARAM1, PARAM2);
 }
 
+#ifdef TARGET_X86_64
+void OPPROTO op_enter64_level(void)
+{
+    helper_enter64_level(PARAM1, PARAM2);
+}
+#endif
+
 void OPPROTO op_sysenter(void)
 {
     helper_sysenter();
@@ -938,7 +966,7 @@ void OPPROTO op_sysexit(void)
 #ifdef TARGET_X86_64
 void OPPROTO op_syscall(void)
 {
-    helper_syscall();
+    helper_syscall(PARAM1);
 }
 
 void OPPROTO op_sysret(void)
@@ -1004,6 +1032,7 @@ void OPPROTO op_aaa(void)
     }
     EAX = (EAX & ~0xffff) | al | (ah << 8);
     CC_SRC = eflags;
+    FORCE_RET();
 }
 
 void OPPROTO op_aas(void)
@@ -1028,6 +1057,7 @@ void OPPROTO op_aas(void)
     }
     EAX = (EAX & ~0xffff) | al | (ah << 8);
     CC_SRC = eflags;
+    FORCE_RET();
 }
 
 void OPPROTO op_daa(void)
@@ -1055,6 +1085,7 @@ void OPPROTO op_daa(void)
     eflags |= parity_table[al]; /* pf */
     eflags |= (al & 0x80); /* sf */
     CC_SRC = eflags;
+    FORCE_RET();
 }
 
 void OPPROTO op_das(void)
@@ -1085,6 +1116,7 @@ void OPPROTO op_das(void)
     eflags |= parity_table[al]; /* pf */
     eflags |= (al & 0x80); /* sf */
     CC_SRC = eflags;
+    FORCE_RET();
 }
 
 /* segment handling */
@@ -1199,6 +1231,13 @@ void OPPROTO op_movl_crN_T0(void)
     helper_movl_crN_T0(PARAM1);
 }
 
+#if !defined(CONFIG_USER_ONLY) 
+void OPPROTO op_movtl_T0_cr8(void)
+{
+    T0 = cpu_get_apic_tpr(env);
+}
+#endif
+
 /* DR registers access */
 void OPPROTO op_movl_drN_T0(void)
 {
@@ -1263,12 +1302,12 @@ void OPPROTO op_clts(void)
 
 void OPPROTO op_goto_tb0(void)
 {
-    GOTO_TB(op_goto_tb0, 0);
+    GOTO_TB(op_goto_tb0, PARAM1, 0);
 }
 
 void OPPROTO op_goto_tb1(void)
 {
-    GOTO_TB(op_goto_tb1, 1);
+    GOTO_TB(op_goto_tb1, PARAM1, 1);
 }
 
 void OPPROTO op_jmp_label(void)
@@ -1280,12 +1319,14 @@ void OPPROTO op_jnz_T0_label(void)
 {
     if (T0)
         GOTO_LABEL_PARAM(1);
+    FORCE_RET();
 }
 
 void OPPROTO op_jz_T0_label(void)
 {
     if (!T0)
         GOTO_LABEL_PARAM(1);
+    FORCE_RET();
 }
 
 /* slow set cases (compute x86 flags) */
@@ -1353,6 +1394,11 @@ void OPPROTO op_set_cc_op(void)
     CC_OP = PARAM1;
 }
 
+void OPPROTO op_mov_T0_cc(void)
+{
+    T0 = cc_table[CC_OP].compute_all();
+}
+
 /* XXX: clear VIF/VIP in all ops ? */
 
 void OPPROTO op_movl_eflags_T0(void)
@@ -1580,26 +1626,6 @@ CCTable cc_table[CC_OP_NB] = {
    functions comes from the LGPL'ed x86 emulator found in the Willows
    TWIN windows emulator. */
 
-#if defined(__powerpc__)
-extern CPU86_LDouble copysign(CPU86_LDouble, CPU86_LDouble);
-
-/* correct (but slow) PowerPC rint() (glibc version is incorrect) */
-double qemu_rint(double x)
-{
-    double y = 4503599627370496.0;
-    if (fabs(x) >= y)
-        return x;
-    if (x < 0) 
-        y = -y;
-    y = (x + y) - y;
-    if (y == 0.0)
-        y = copysign(y, x);
-    return y;
-}
-
-#define rint qemu_rint
-#endif
-
 /* fp load FT0 */
 
 void OPPROTO op_flds_FT0_A0(void)
@@ -1696,9 +1722,9 @@ void OPPROTO op_flds_ST0_A0(void)
     new_fpstt = (env->fpstt - 1) & 7;
 #ifdef USE_FP_CONVERT
     FP_CONVERT.i32 = ldl(A0);
-    env->fpregs[new_fpstt] = FP_CONVERT.f;
+    env->fpregs[new_fpstt].d = FP_CONVERT.f;
 #else
-    env->fpregs[new_fpstt] = ldfl(A0);
+    env->fpregs[new_fpstt].d = ldfl(A0);
 #endif
     env->fpstt = new_fpstt;
     env->fptags[new_fpstt] = 0; /* validate stack entry */
@@ -1710,9 +1736,9 @@ void OPPROTO op_fldl_ST0_A0(void)
     new_fpstt = (env->fpstt - 1) & 7;
 #ifdef USE_FP_CONVERT
     FP_CONVERT.i64 = ldq(A0);
-    env->fpregs[new_fpstt] = FP_CONVERT.d;
+    env->fpregs[new_fpstt].d = FP_CONVERT.d;
 #else
-    env->fpregs[new_fpstt] = ldfq(A0);
+    env->fpregs[new_fpstt].d = ldfq(A0);
 #endif
     env->fpstt = new_fpstt;
     env->fptags[new_fpstt] = 0; /* validate stack entry */
@@ -1730,7 +1756,7 @@ void helper_fild_ST0_A0(void)
 {
     int new_fpstt;
     new_fpstt = (env->fpstt - 1) & 7;
-    env->fpregs[new_fpstt] = (CPU86_LDouble)ldsw(A0);
+    env->fpregs[new_fpstt].d = (CPU86_LDouble)ldsw(A0);
     env->fpstt = new_fpstt;
     env->fptags[new_fpstt] = 0; /* validate stack entry */
 }
@@ -1739,7 +1765,7 @@ void helper_fildl_ST0_A0(void)
 {
     int new_fpstt;
     new_fpstt = (env->fpstt - 1) & 7;
-    env->fpregs[new_fpstt] = (CPU86_LDouble)((int32_t)ldl(A0));
+    env->fpregs[new_fpstt].d = (CPU86_LDouble)((int32_t)ldl(A0));
     env->fpstt = new_fpstt;
     env->fptags[new_fpstt] = 0; /* validate stack entry */
 }
@@ -1748,7 +1774,7 @@ void helper_fildll_ST0_A0(void)
 {
     int new_fpstt;
     new_fpstt = (env->fpstt - 1) & 7;
-    env->fpregs[new_fpstt] = (CPU86_LDouble)((int64_t)ldq(A0));
+    env->fpregs[new_fpstt].d = (CPU86_LDouble)((int64_t)ldq(A0));
     env->fpstt = new_fpstt;
     env->fptags[new_fpstt] = 0; /* validate stack entry */
 }
@@ -1776,9 +1802,9 @@ void OPPROTO op_fild_ST0_A0(void)
     new_fpstt = (env->fpstt - 1) & 7;
 #ifdef USE_FP_CONVERT
     FP_CONVERT.i32 = ldsw(A0);
-    env->fpregs[new_fpstt] = (CPU86_LDouble)FP_CONVERT.i32;
+    env->fpregs[new_fpstt].d = (CPU86_LDouble)FP_CONVERT.i32;
 #else
-    env->fpregs[new_fpstt] = (CPU86_LDouble)ldsw(A0);
+    env->fpregs[new_fpstt].d = (CPU86_LDouble)ldsw(A0);
 #endif
     env->fpstt = new_fpstt;
     env->fptags[new_fpstt] = 0; /* validate stack entry */
@@ -1790,9 +1816,9 @@ void OPPROTO op_fildl_ST0_A0(void)
     new_fpstt = (env->fpstt - 1) & 7;
 #ifdef USE_FP_CONVERT
     FP_CONVERT.i32 = (int32_t) ldl(A0);
-    env->fpregs[new_fpstt] = (CPU86_LDouble)FP_CONVERT.i32;
+    env->fpregs[new_fpstt].d = (CPU86_LDouble)FP_CONVERT.i32;
 #else
-    env->fpregs[new_fpstt] = (CPU86_LDouble)((int32_t)ldl(A0));
+    env->fpregs[new_fpstt].d = (CPU86_LDouble)((int32_t)ldl(A0));
 #endif
     env->fpstt = new_fpstt;
     env->fptags[new_fpstt] = 0; /* validate stack entry */
@@ -1804,9 +1830,9 @@ void OPPROTO op_fildll_ST0_A0(void)
     new_fpstt = (env->fpstt - 1) & 7;
 #ifdef USE_FP_CONVERT
     FP_CONVERT.i64 = (int64_t) ldq(A0);
-    env->fpregs[new_fpstt] = (CPU86_LDouble)FP_CONVERT.i64;
+    env->fpregs[new_fpstt].d = (CPU86_LDouble)FP_CONVERT.i64;
 #else
-    env->fpregs[new_fpstt] = (CPU86_LDouble)((int64_t)ldq(A0));
+    env->fpregs[new_fpstt].d = (CPU86_LDouble)((int64_t)ldq(A0));
 #endif
     env->fpstt = new_fpstt;
     env->fptags[new_fpstt] = 0; /* validate stack entry */
@@ -1824,11 +1850,13 @@ void OPPROTO op_fsts_ST0_A0(void)
 #else
     stfl(A0, (float)ST0);
 #endif
+    FORCE_RET();
 }
 
 void OPPROTO op_fstl_ST0_A0(void)
 {
     stfq(A0, (double)ST0);
+    FORCE_RET();
 }
 
 void OPPROTO op_fstt_ST0_A0(void)
@@ -1846,10 +1874,11 @@ void OPPROTO op_fist_ST0_A0(void)
     int val;
 
     d = ST0;
-    val = lrint(d);
+    val = floatx_to_int32(d, &env->fp_status);
     if (val != (int16_t)val)
         val = -32768;
     stw(A0, val);
+    FORCE_RET();
 }
 
 void OPPROTO op_fistl_ST0_A0(void)
@@ -1862,8 +1891,9 @@ void OPPROTO op_fistl_ST0_A0(void)
     int val;
 
     d = ST0;
-    val = lrint(d);
+    val = floatx_to_int32(d, &env->fp_status);
     stl(A0, val);
+    FORCE_RET();
 }
 
 void OPPROTO op_fistll_ST0_A0(void)
@@ -1876,8 +1906,56 @@ void OPPROTO op_fistll_ST0_A0(void)
     int64_t val;
 
     d = ST0;
-    val = llrint(d);
+    val = floatx_to_int64(d, &env->fp_status);
+    stq(A0, val);
+    FORCE_RET();
+}
+
+void OPPROTO op_fistt_ST0_A0(void)
+{
+#if defined(__sparc__) && !defined(__sparc_v9__)
+    register CPU86_LDouble d asm("o0");
+#else
+    CPU86_LDouble d;
+#endif
+    int val;
+
+    d = ST0;
+    val = floatx_to_int32_round_to_zero(d, &env->fp_status);
+    if (val != (int16_t)val)
+        val = -32768;
+    stw(A0, val);
+    FORCE_RET();
+}
+
+void OPPROTO op_fisttl_ST0_A0(void)
+{
+#if defined(__sparc__) && !defined(__sparc_v9__)
+    register CPU86_LDouble d asm("o0");
+#else
+    CPU86_LDouble d;
+#endif
+    int val;
+
+    d = ST0;
+    val = floatx_to_int32_round_to_zero(d, &env->fp_status);
+    stl(A0, val);
+    FORCE_RET();
+}
+
+void OPPROTO op_fisttll_ST0_A0(void)
+{
+#if defined(__sparc__) && !defined(__sparc_v9__)
+    register CPU86_LDouble d asm("o0");
+#else
+    CPU86_LDouble d;
+#endif
+    int64_t val;
+
+    d = ST0;
+    val = floatx_to_int64_round_to_zero(d, &env->fp_status);
     stq(A0, val);
+    FORCE_RET();
 }
 
 void OPPROTO op_fbld_ST0_A0(void)
@@ -1949,52 +2027,48 @@ void OPPROTO op_fxchg_ST0_STN(void)
 
 /* FPU operations */
 
-/* XXX: handle nans */
+const int fcom_ccval[4] = {0x0100, 0x4000, 0x0000, 0x4500};
+
 void OPPROTO op_fcom_ST0_FT0(void)
 {
-    env->fpus &= (~0x4500);    /* (C3,C2,C0) <-- 000 */
-    if (ST0 < FT0)
-        env->fpus |= 0x100;    /* (C3,C2,C0) <-- 001 */
-    else if (ST0 == FT0)
-        env->fpus |= 0x4000; /* (C3,C2,C0) <-- 100 */
+    int ret;
+
+    ret = floatx_compare(ST0, FT0, &env->fp_status);
+    env->fpus = (env->fpus & ~0x4500) | fcom_ccval[ret + 1];
     FORCE_RET();
 }
 
-/* XXX: handle nans */
 void OPPROTO op_fucom_ST0_FT0(void)
 {
-    env->fpus &= (~0x4500);    /* (C3,C2,C0) <-- 000 */
-    if (ST0 < FT0)
-        env->fpus |= 0x100;    /* (C3,C2,C0) <-- 001 */
-    else if (ST0 == FT0)
-        env->fpus |= 0x4000; /* (C3,C2,C0) <-- 100 */
+    int ret;
+
+    ret = floatx_compare_quiet(ST0, FT0, &env->fp_status);
+    env->fpus = (env->fpus & ~0x4500) | fcom_ccval[ret+ 1];
     FORCE_RET();
 }
 
-/* XXX: handle nans */
+const int fcomi_ccval[4] = {CC_C, CC_Z, 0, CC_Z | CC_P | CC_C};
+
 void OPPROTO op_fcomi_ST0_FT0(void)
 {
     int eflags;
+    int ret;
+
+    ret = floatx_compare(ST0, FT0, &env->fp_status);
     eflags = cc_table[CC_OP].compute_all();
-    eflags &= ~(CC_Z | CC_P | CC_C);
-    if (ST0 < FT0)
-        eflags |= CC_C;
-    else if (ST0 == FT0)
-        eflags |= CC_Z;
+    eflags = (eflags & ~(CC_Z | CC_P | CC_C)) | fcomi_ccval[ret + 1];
     CC_SRC = eflags;
     FORCE_RET();
 }
 
-/* XXX: handle nans */
 void OPPROTO op_fucomi_ST0_FT0(void)
 {
     int eflags;
+    int ret;
+
+    ret = floatx_compare_quiet(ST0, FT0, &env->fp_status);
     eflags = cc_table[CC_OP].compute_all();
-    eflags &= ~(CC_Z | CC_P | CC_C);
-    if (ST0 < FT0)
-        eflags |= CC_C;
-    else if (ST0 == FT0)
-        eflags |= CC_Z;
+    eflags = (eflags & ~(CC_Z | CC_P | CC_C)) | fcomi_ccval[ret + 1];
     CC_SRC = eflags;
     FORCE_RET();
 }
@@ -2078,12 +2152,12 @@ void OPPROTO op_fdivr_STN_ST0(void)
 /* misc FPU operations */
 void OPPROTO op_fchs_ST0(void)
 {
-    ST0 = -ST0;
+    ST0 = floatx_chs(ST0);
 }
 
 void OPPROTO op_fabs_ST0(void)
 {
-    ST0 = fabs(ST0);
+    ST0 = floatx_abs(ST0);
 }
 
 void OPPROTO op_fxam_ST0(void)
@@ -2210,6 +2284,7 @@ void OPPROTO op_fnstsw_A0(void)
     int fpus;
     fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
     stw(A0, fpus);
+    FORCE_RET();
 }
 
 void OPPROTO op_fnstsw_EAX(void)
@@ -2222,29 +2297,13 @@ void OPPROTO op_fnstsw_EAX(void)
 void OPPROTO op_fnstcw_A0(void)
 {
     stw(A0, env->fpuc);
+    FORCE_RET();
 }
 
 void OPPROTO op_fldcw_A0(void)
 {
-    int rnd_type;
     env->fpuc = lduw(A0);
-    /* set rounding mode */
-    switch(env->fpuc & RC_MASK) {
-    default:
-    case RC_NEAR:
-        rnd_type = FE_TONEAREST;
-        break;
-    case RC_DOWN:
-        rnd_type = FE_DOWNWARD;
-        break;
-    case RC_UP:
-        rnd_type = FE_UPWARD;
-        break;
-    case RC_CHOP:
-        rnd_type = FE_TOWARDZERO;
-        break;
-    }
-    fesetround(rnd_type);
+    update_fp_status();
 }
 
 void OPPROTO op_fclex(void)
@@ -2323,6 +2382,29 @@ void OPPROTO op_movo(void)
     memcpy16(d, s);
 }
 
+void OPPROTO op_movq(void)
+{
+    uint64_t *d, *s;
+    d = (uint64_t *)((char *)env + PARAM1);
+    s = (uint64_t *)((char *)env + PARAM2);
+    *d = *s;
+}
+
+void OPPROTO op_movl(void)
+{
+    uint32_t *d, *s;
+    d = (uint32_t *)((char *)env + PARAM1);
+    s = (uint32_t *)((char *)env + PARAM2);
+    *d = *s;
+}
+
+void OPPROTO op_movq_env_0(void)
+{
+    uint64_t *d;
+    d = (uint64_t *)((char *)env + PARAM1);
+    *d = 0;
+}
+
 void OPPROTO op_fxsave_A0(void)
 {
     helper_fxsave(A0, PARAM1);
@@ -2332,3 +2414,24 @@ void OPPROTO op_fxrstor_A0(void)
 {
     helper_fxrstor(A0, PARAM1);
 }
+
+/* XXX: optimize by storing fptt and fptags in the static cpu state */
+void OPPROTO op_enter_mmx(void)
+{
+    env->fpstt = 0;
+    *(uint32_t *)(env->fptags) = 0;
+    *(uint32_t *)(env->fptags + 4) = 0;
+}
+
+void OPPROTO op_emms(void)
+{
+    /* set to empty state */
+    *(uint32_t *)(env->fptags) = 0x01010101;
+    *(uint32_t *)(env->fptags + 4) = 0x01010101;
+}
+
+#define SHIFT 0
+#include "ops_sse.h"
+
+#define SHIFT 1
+#include "ops_sse.h"