]> git.proxmox.com Git - qemu.git/blobdiff - op-i386.c
fixed popf TF flag bug (should never hapen in user code except in test-i386!)
[qemu.git] / op-i386.c
index eac73ed93a37b6afbd4b292b8f527beb5f36aaa2..64cbe708a5040d075112e95d137ade4ede9fa8e4 100644 (file)
--- a/op-i386.c
+++ b/op-i386.c
@@ -412,6 +412,22 @@ void OPPROTO op_idivw_AX_T0(void)
     EDX = (EDX & 0xffff0000) | r;
 }
 
+#ifdef BUGGY_GCC_DIV64
+/* gcc 2.95.4 on PowerPC does not seem to like using __udivdi3, so we
+   call it from another function */
+uint32_t div64(uint32_t *q_ptr, uint64_t num, uint32_t den)
+{
+    *q_ptr = num / den;
+    return num % den;
+}
+
+int32_t idiv64(int32_t *q_ptr, int64_t num, int32_t den)
+{
+    *q_ptr = num / den;
+    return num % den;
+}
+#endif
+
 void OPPROTO op_divl_EAX_T0(void)
 {
     unsigned int den, q, r;
@@ -421,8 +437,12 @@ void OPPROTO op_divl_EAX_T0(void)
     den = T0;
     if (den == 0)
         raise_exception(EXCP00_DIVZ);
+#ifdef BUGGY_GCC_DIV64
+    r = div64(&q, num, den);
+#else
     q = (num / den);
     r = (num % den);
+#endif
     EAX = q;
     EDX = r;
 }
@@ -436,8 +456,12 @@ void OPPROTO op_idivl_EAX_T0(void)
     den = T0;
     if (den == 0)
         raise_exception(EXCP00_DIVZ);
+#ifdef BUGGY_GCC_DIV64
+    r = idiv64(&q, num, den);
+#else
     q = (num / den);
     r = (num % den);
+#endif
     EAX = q;
     EDX = r;
 }
@@ -592,14 +616,17 @@ void OPPROTO op_jmp_im(void)
 
 void OPPROTO op_int_im(void)
 {
-    EIP = PARAM1;
-    raise_exception(EXCP0D_GPF);
+    int intno;
+    intno = PARAM1;
+    EIP = PARAM2;
+    raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
 }
 
-void OPPROTO op_int3(void)
+void OPPROTO op_raise_exception(void)
 {
-    EIP = PARAM1;
-    raise_exception(EXCP03_INT3);
+    int exception_index;
+    exception_index = PARAM1;
+    raise_exception(exception_index);
 }
 
 void OPPROTO op_into(void)
@@ -609,9 +636,77 @@ void OPPROTO op_into(void)
     if (eflags & CC_O) {
         EIP = PARAM1;
         raise_exception(EXCP04_INTO);
+    }
+    FORCE_RET();
+}
+
+void OPPROTO op_cli(void)
+{
+    env->eflags &= ~IF_MASK;
+}
+
+void OPPROTO op_sti(void)
+{
+    env->eflags |= IF_MASK;
+}
+
+#if 0
+/* vm86plus instructions */
+void OPPROTO op_cli_vm(void)
+{
+    env->eflags &= ~VIF_MASK;
+}
+
+void OPPROTO op_sti_vm(void)
+{
+    env->eflags |= VIF_MASK;
+    if (env->eflags & VIP_MASK) {
+        EIP = PARAM1;
+        raise_exception(EXCP0D_GPF);
+    }
+    FORCE_RET();
+}
+#endif
+
+void OPPROTO op_boundw(void)
+{
+    int low, high, v;
+    low = ldsw((uint8_t *)A0);
+    high = ldsw((uint8_t *)A0 + 2);
+    v = (int16_t)T0;
+    if (v < low || v > high)
+        raise_exception(EXCP05_BOUND);
+    FORCE_RET();
+}
+
+void OPPROTO op_boundl(void)
+{
+    int low, high, v;
+    low = ldl((uint8_t *)A0);
+    high = ldl((uint8_t *)A0 + 4);
+    v = T0;
+    if (v < low || v > high)
+        raise_exception(EXCP05_BOUND);
+    FORCE_RET();
+}
+
+void OPPROTO op_cmpxchg8b(void)
+{
+    uint64_t d;
+    int eflags;
+
+    eflags = cc_table[CC_OP].compute_all();
+    d = ldq((uint8_t *)A0);
+    if (d == (((uint64_t)EDX << 32) | EAX)) {
+        stq((uint8_t *)A0, ((uint64_t)ECX << 32) | EBX);
+        eflags |= CC_Z;
     } else {
-        EIP = PARAM2;
+        EDX = d >> 32;
+        EAX = d;
+        eflags &= ~CC_Z;
     }
+    CC_SRC = eflags;
+    FORCE_RET();
 }
 
 /* string ops */
@@ -793,7 +888,8 @@ void op_addw_ESP_im(void)
 #ifndef __i386__
 uint64_t emu_time;
 #endif
-void op_rdtsc(void)
+
+void OPPROTO op_rdtsc(void)
 {
     uint64_t val;
 #ifdef __i386__
@@ -806,6 +902,51 @@ void op_rdtsc(void)
     EDX = val >> 32;
 }
 
+/* We simulate a pre-MMX pentium as in valgrind */
+#define CPUID_FP87 (1 << 0)
+#define CPUID_VME  (1 << 1)
+#define CPUID_DE   (1 << 2)
+#define CPUID_PSE  (1 << 3)
+#define CPUID_TSC  (1 << 4)
+#define CPUID_MSR  (1 << 5)
+#define CPUID_PAE  (1 << 6)
+#define CPUID_MCE  (1 << 7)
+#define CPUID_CX8  (1 << 8)
+#define CPUID_APIC (1 << 9)
+#define CPUID_SEP  (1 << 11) /* sysenter/sysexit */
+#define CPUID_MTRR (1 << 12)
+#define CPUID_PGE  (1 << 13)
+#define CPUID_MCA  (1 << 14)
+#define CPUID_CMOV (1 << 15)
+/* ... */
+#define CPUID_MMX  (1 << 23)
+#define CPUID_FXSR (1 << 24)
+#define CPUID_SSE  (1 << 25)
+#define CPUID_SSE2 (1 << 26)
+
+void helper_cpuid(void)
+{
+    if (EAX == 0) {
+        EAX = 1; /* max EAX index supported */
+        EBX = 0x756e6547;
+        ECX = 0x6c65746e;
+        EDX = 0x49656e69;
+    } else {
+        /* EAX = 1 info */
+        EAX = 0x52b;
+        EBX = 0;
+        ECX = 0;
+        EDX = CPUID_FP87 | CPUID_DE | CPUID_PSE |
+            CPUID_TSC | CPUID_MSR | CPUID_MCE |
+            CPUID_CX8;
+    }
+}
+
+void OPPROTO op_cpuid(void)
+{
+    helper_cpuid();
+}
+
 /* bcd */
 
 /* XXX: exception */
@@ -938,6 +1079,7 @@ void OPPROTO op_das(void)
 
 /* segment handling */
 
+/* XXX: use static VM86 information */
 void load_seg(int seg_reg, int selector)
 {
     SegmentCache *sc;
@@ -946,9 +1088,8 @@ void load_seg(int seg_reg, int selector)
     uint32_t e1, e2;
     uint8_t *ptr;
 
-    env->segs[seg_reg] = selector;
     sc = &env->seg_cache[seg_reg];
-    if (env->vm86) {
+    if (env->eflags & VM_MASK) {
         sc->base = (void *)(selector << 4);
         sc->limit = 0xffff;
         sc->seg_32bit = 0;
@@ -959,7 +1100,7 @@ void load_seg(int seg_reg, int selector)
             dt = &env->gdt;
         index = selector & ~7;
         if ((index + 7) > dt->limit)
-            raise_exception(EXCP0D_GPF);
+            raise_exception_err(EXCP0D_GPF, selector);
         ptr = dt->base + index;
         e1 = ldl(ptr);
         e2 = ldl(ptr + 4);
@@ -973,6 +1114,7 @@ void load_seg(int seg_reg, int selector)
                 selector, (unsigned long)sc->base, sc->limit, sc->seg_32bit);
 #endif
     }
+    env->segs[seg_reg] = selector;
 }
 
 void OPPROTO op_movl_seg_T0(void)
@@ -985,11 +1127,76 @@ void OPPROTO op_movl_T0_seg(void)
     T0 = env->segs[PARAM1];
 }
 
+void OPPROTO op_movl_A0_seg(void)
+{
+    A0 = *(unsigned long *)((char *)env + PARAM1);
+}
+
 void OPPROTO op_addl_A0_seg(void)
 {
     A0 += *(unsigned long *)((char *)env + PARAM1);
 }
 
+void helper_lsl(void)
+{
+    unsigned int selector, limit;
+    SegmentDescriptorTable *dt;
+    int index;
+    uint32_t e1, e2;
+    uint8_t *ptr;
+
+    CC_SRC = cc_table[CC_OP].compute_all() & ~CC_Z;
+    selector = T0 & 0xffff;
+    if (selector & 0x4)
+        dt = &env->ldt;
+    else
+        dt = &env->gdt;
+    index = selector & ~7;
+    if ((index + 7) > dt->limit)
+        return;
+    ptr = dt->base + index;
+    e1 = ldl(ptr);
+    e2 = ldl(ptr + 4);
+    limit = (e1 & 0xffff) | (e2 & 0x000f0000);
+    if (e2 & (1 << 23))
+        limit = (limit << 12) | 0xfff;
+    T1 = limit;
+    CC_SRC |= CC_Z;
+}
+
+void OPPROTO op_lsl(void)
+{
+    helper_lsl();
+}
+
+void helper_lar(void)
+{
+    unsigned int selector;
+    SegmentDescriptorTable *dt;
+    int index;
+    uint32_t e2;
+    uint8_t *ptr;
+
+    CC_SRC = cc_table[CC_OP].compute_all() & ~CC_Z;
+    selector = T0 & 0xffff;
+    if (selector & 0x4)
+        dt = &env->ldt;
+    else
+        dt = &env->gdt;
+    index = selector & ~7;
+    if ((index + 7) > dt->limit)
+        return;
+    ptr = dt->base + index;
+    e2 = ldl(ptr + 4);
+    T1 = e2 & 0x00f0ff00;
+    CC_SRC |= CC_Z;
+}
+
+void OPPROTO op_lar(void)
+{
+    helper_lar();
+}
+
 /* flags handling */
 
 /* slow jumps cases (compute x86 flags) */
@@ -1144,26 +1351,101 @@ void OPPROTO op_set_cc_op(void)
     CC_OP = PARAM1;
 }
 
+#define FL_UPDATE_MASK32 (TF_MASK | AC_MASK | ID_MASK)
+#define FL_UPDATE_MASK16 (TF_MASK)
+
 void OPPROTO op_movl_eflags_T0(void)
 {
-    CC_SRC = T0;
-    DF = 1 - (2 * ((T0 >> 10) & 1));
+    int eflags;
+    eflags = T0;
+    CC_SRC = eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
+    DF = 1 - (2 * ((eflags >> 10) & 1));
+    /* we also update some system flags as in user mode */
+    env->eflags = (env->eflags & ~FL_UPDATE_MASK32) | (eflags & FL_UPDATE_MASK32);
+}
+
+void OPPROTO op_movw_eflags_T0(void)
+{
+    int eflags;
+    eflags = T0;
+    CC_SRC = eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
+    DF = 1 - (2 * ((eflags >> 10) & 1));
+    /* we also update some system flags as in user mode */
+    env->eflags = (env->eflags & ~FL_UPDATE_MASK16) | (eflags & FL_UPDATE_MASK16);
+}
+
+#if 0
+/* vm86plus version */
+void OPPROTO op_movw_eflags_T0_vm(void)
+{
+    int eflags;
+    eflags = T0;
+    CC_SRC = eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
+    DF = 1 - (2 * ((eflags >> 10) & 1));
+    /* we also update some system flags as in user mode */
+    env->eflags = (env->eflags & ~(FL_UPDATE_MASK16 | VIF_MASK)) |
+        (eflags & FL_UPDATE_MASK16);
+    if (eflags & IF_MASK) {
+        env->eflags |= VIF_MASK;
+        if (env->eflags & VIP_MASK) {
+            EIP = PARAM1;
+            raise_exception(EXCP0D_GPF);
+        }
+    }
+    FORCE_RET();
+}
+
+void OPPROTO op_movl_eflags_T0_vm(void)
+{
+    int eflags;
+    eflags = T0;
+    CC_SRC = eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
+    DF = 1 - (2 * ((eflags >> 10) & 1));
+    /* we also update some system flags as in user mode */
+    env->eflags = (env->eflags & ~(FL_UPDATE_MASK32 | VIF_MASK)) |
+        (eflags & FL_UPDATE_MASK32);
+    if (eflags & IF_MASK) {
+        env->eflags |= VIF_MASK;
+        if (env->eflags & VIP_MASK) {
+            EIP = PARAM1;
+            raise_exception(EXCP0D_GPF);
+        }
+    }
+    FORCE_RET();
 }
+#endif
 
 /* XXX: compute only O flag */
 void OPPROTO op_movb_eflags_T0(void)
 {
     int of;
     of = cc_table[CC_OP].compute_all() & CC_O;
-    CC_SRC = T0 | of;
+    CC_SRC = (T0 & (CC_S | CC_Z | CC_A | CC_P | CC_C)) | of;
 }
 
 void OPPROTO op_movl_T0_eflags(void)
 {
-    T0 = cc_table[CC_OP].compute_all();
-    T0 |= (DF & DIRECTION_FLAG);
+    int eflags;
+    eflags = cc_table[CC_OP].compute_all();
+    eflags |= (DF & DF_MASK);
+    eflags |= env->eflags & ~(VM_MASK | RF_MASK);
+    T0 = eflags;
 }
 
+/* vm86plus version */
+#if 0
+void OPPROTO op_movl_T0_eflags_vm(void)
+{
+    int eflags;
+    eflags = cc_table[CC_OP].compute_all();
+    eflags |= (DF & DF_MASK);
+    eflags |= env->eflags & ~(VM_MASK | RF_MASK | IF_MASK);
+    if (env->eflags & VIF_MASK)
+        eflags |= IF_MASK;
+    T0 = eflags;
+}
+#endif
+
 void OPPROTO op_cld(void)
 {
     DF = 1;
@@ -1269,16 +1551,18 @@ CCTable cc_table[CC_OP_NB] = {
     [CC_OP_DECW] = { compute_all_decw, compute_c_incl },
     [CC_OP_DECL] = { compute_all_decl, compute_c_incl },
     
-    [CC_OP_SHLB] = { compute_all_shlb, compute_c_shll },
-    [CC_OP_SHLW] = { compute_all_shlw, compute_c_shll },
+    [CC_OP_SHLB] = { compute_all_shlb, compute_c_shlb },
+    [CC_OP_SHLW] = { compute_all_shlw, compute_c_shlw },
     [CC_OP_SHLL] = { compute_all_shll, compute_c_shll },
 
-    [CC_OP_SARB] = { compute_all_sarb, compute_c_shll },
-    [CC_OP_SARW] = { compute_all_sarw, compute_c_shll },
-    [CC_OP_SARL] = { compute_all_sarl, compute_c_shll },
+    [CC_OP_SARB] = { compute_all_sarb, compute_c_sarl },
+    [CC_OP_SARW] = { compute_all_sarw, compute_c_sarl },
+    [CC_OP_SARL] = { compute_all_sarl, compute_c_sarl },
 };
 
-/* floating point support */
+/* floating point support. Some of the code for complicated x87
+   functions comes from the LGPL'ed x86 emulator found in the Willows
+   TWIN windows emulator. */
 
 #ifdef USE_X86LDOUBLE
 /* use long double functions */
@@ -1311,6 +1595,26 @@ extern CPU86_LDouble floor(CPU86_LDouble x);
 extern CPU86_LDouble ceil(CPU86_LDouble x);
 extern CPU86_LDouble rint(CPU86_LDouble x);
 
+#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
+
 #define RC_MASK         0xc00
 #define RC_NEAR                0x000
 #define RC_DOWN                0x400
@@ -1369,12 +1673,22 @@ typedef union {
 
 void OPPROTO op_flds_FT0_A0(void)
 {
+#ifdef USE_FP_CONVERT
+    FP_CONVERT.i32 = ldl((void *)A0);
+    FT0 = FP_CONVERT.f;
+#else
     FT0 = ldfl((void *)A0);
+#endif
 }
 
 void OPPROTO op_fldl_FT0_A0(void)
 {
+#ifdef USE_FP_CONVERT
+    FP_CONVERT.i64 = ldq((void *)A0);
+    FT0 = FP_CONVERT.d;
+#else
     FT0 = ldfq((void *)A0);
+#endif
 }
 
 /* helpers are needed to avoid static constant reference. XXX: find a better way */
@@ -1414,17 +1728,32 @@ void OPPROTO op_fildll_FT0_A0(void)
 
 void OPPROTO op_fild_FT0_A0(void)
 {
+#ifdef USE_FP_CONVERT
+    FP_CONVERT.i32 = ldsw((void *)A0);
+    FT0 = (CPU86_LDouble)FP_CONVERT.i32;
+#else
     FT0 = (CPU86_LDouble)ldsw((void *)A0);
+#endif
 }
 
 void OPPROTO op_fildl_FT0_A0(void)
 {
+#ifdef USE_FP_CONVERT
+    FP_CONVERT.i32 = (int32_t) ldl((void *)A0);
+    FT0 = (CPU86_LDouble)FP_CONVERT.i32;
+#else
     FT0 = (CPU86_LDouble)((int32_t)ldl((void *)A0));
+#endif
 }
 
 void OPPROTO op_fildll_FT0_A0(void)
 {
+#ifdef USE_FP_CONVERT
+    FP_CONVERT.i64 = (int64_t) ldq((void *)A0);
+    FT0 = (CPU86_LDouble)FP_CONVERT.i64;
+#else
     FT0 = (CPU86_LDouble)((int64_t)ldq((void *)A0));
+#endif
 }
 #endif
 
@@ -1432,12 +1761,22 @@ void OPPROTO op_fildll_FT0_A0(void)
 
 void OPPROTO op_flds_ST0_A0(void)
 {
+#ifdef USE_FP_CONVERT
+    FP_CONVERT.i32 = ldl((void *)A0);
+    ST0 = FP_CONVERT.f;
+#else
     ST0 = ldfl((void *)A0);
+#endif
 }
 
 void OPPROTO op_fldl_ST0_A0(void)
 {
+#ifdef USE_FP_CONVERT
+    FP_CONVERT.i64 = ldq((void *)A0);
+    ST0 = FP_CONVERT.d;
+#else
     ST0 = ldfq((void *)A0);
+#endif
 }
 
 #ifdef USE_X86LDOUBLE
@@ -1502,17 +1841,32 @@ void OPPROTO op_fildll_ST0_A0(void)
 
 void OPPROTO op_fild_ST0_A0(void)
 {
+#ifdef USE_FP_CONVERT
+    FP_CONVERT.i32 = ldsw((void *)A0);
+    ST0 = (CPU86_LDouble)FP_CONVERT.i32;
+#else
     ST0 = (CPU86_LDouble)ldsw((void *)A0);
+#endif
 }
 
 void OPPROTO op_fildl_ST0_A0(void)
 {
+#ifdef USE_FP_CONVERT
+    FP_CONVERT.i32 = (int32_t) ldl((void *)A0);
+    ST0 = (CPU86_LDouble)FP_CONVERT.i32;
+#else
     ST0 = (CPU86_LDouble)((int32_t)ldl((void *)A0));
+#endif
 }
 
 void OPPROTO op_fildll_ST0_A0(void)
 {
+#ifdef USE_FP_CONVERT
+    FP_CONVERT.i64 = (int64_t) ldq((void *)A0);
+    ST0 = (CPU86_LDouble)FP_CONVERT.i64;
+#else
     ST0 = (CPU86_LDouble)((int64_t)ldq((void *)A0));
+#endif
 }
 
 #endif
@@ -1521,7 +1875,12 @@ void OPPROTO op_fildll_ST0_A0(void)
 
 void OPPROTO op_fsts_ST0_A0(void)
 {
+#ifdef USE_FP_CONVERT
+    FP_CONVERT.d = ST0;
+    stfl((void *)A0, FP_CONVERT.f);
+#else
     stfl((void *)A0, (float)ST0);
+#endif
 }
 
 void OPPROTO op_fstl_ST0_A0(void)
@@ -1556,22 +1915,43 @@ void OPPROTO op_fstt_ST0_A0(void)
 
 void OPPROTO op_fist_ST0_A0(void)
 {
+#if defined(__sparc__) && !defined(__sparc_v9__)
+    register CPU86_LDouble d asm("o0");
+#else
+    CPU86_LDouble d;
+#endif
     int val;
-    val = lrint(ST0);
+
+    d = ST0;
+    val = lrint(d);
     stw((void *)A0, val);
 }
 
 void OPPROTO op_fistl_ST0_A0(void)
 {
+#if defined(__sparc__) && !defined(__sparc_v9__)
+    register CPU86_LDouble d asm("o0");
+#else
+    CPU86_LDouble d;
+#endif
     int val;
-    val = lrint(ST0);
+
+    d = ST0;
+    val = lrint(d);
     stl((void *)A0, val);
 }
 
 void OPPROTO op_fistll_ST0_A0(void)
 {
+#if defined(__sparc__) && !defined(__sparc_v9__)
+    register CPU86_LDouble d asm("o0");
+#else
+    CPU86_LDouble d;
+#endif
     int64_t val;
-    val = llrint(ST0);
+
+    d = ST0;
+    val = llrint(d);
     stq((void *)A0, val);
 }