correct eflags evaluation order for all operations - fixed important CPU state restor...
[qemu] / op-i386.c
index 4c7b380..a7e057b 100644 (file)
--- a/op-i386.c
+++ b/op-i386.c
  */
 #include "exec-i386.h"
 
-/* NOTE: data are not static to force relocation generation by GCC */
-
-uint8_t parity_table[256] = {
-    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
-    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
-    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
-    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
-    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
-    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
-    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
-    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
-    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
-    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
-    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
-    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
-    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
-    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
-    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
-    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
-    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
-    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
-    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
-    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
-    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
-    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
-    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
-    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
-    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
-    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
-    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
-    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
-    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
-    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
-    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
-    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
-};
-
-/* modulo 17 table */
-const uint8_t rclw_table[32] = {
-    0, 1, 2, 3, 4, 5, 6, 7, 
-    8, 9,10,11,12,13,14,15,
-   16, 0, 1, 2, 3, 4, 5, 6,
-    7, 8, 9,10,11,12,13,14,
-};
-
-/* modulo 9 table */
-const uint8_t rclb_table[32] = {
-    0, 1, 2, 3, 4, 5, 6, 7, 
-    8, 0, 1, 2, 3, 4, 5, 6,
-    7, 8, 0, 1, 2, 3, 4, 5, 
-    6, 7, 8, 0, 1, 2, 3, 4,
-};
-
-#ifdef USE_X86LDOUBLE
-/* an array of Intel 80-bit FP constants, to be loaded via integer ops */
-typedef unsigned short f15ld[5];
-const f15ld f15rk[] =
-{
-/*0*/  {0x0000,0x0000,0x0000,0x0000,0x0000},
-/*1*/  {0x0000,0x0000,0x0000,0x8000,0x3fff},
-/*pi*/ {0xc235,0x2168,0xdaa2,0xc90f,0x4000},
-/*lg2*/        {0xf799,0xfbcf,0x9a84,0x9a20,0x3ffd},
-/*ln2*/        {0x79ac,0xd1cf,0x17f7,0xb172,0x3ffe},
-/*l2e*/        {0xf0bc,0x5c17,0x3b29,0xb8aa,0x3fff},
-/*l2t*/        {0x8afe,0xcd1b,0x784b,0xd49a,0x4000}
-};
-#else
-/* the same, 64-bit version */
-typedef unsigned short f15ld[4];
-const f15ld f15rk[] =
-{
-#ifndef WORDS_BIGENDIAN
-/*0*/  {0x0000,0x0000,0x0000,0x0000},
-/*1*/  {0x0000,0x0000,0x0000,0x3ff0},
-/*pi*/ {0x2d18,0x5444,0x21fb,0x4009},
-/*lg2*/        {0x79ff,0x509f,0x4413,0x3fd3},
-/*ln2*/        {0x39ef,0xfefa,0x2e42,0x3fe6},
-/*l2e*/        {0x82fe,0x652b,0x1547,0x3ff7},
-/*l2t*/        {0xa371,0x0979,0x934f,0x400a}
-#else
-/*0*/   {0x0000,0x0000,0x0000,0x0000},
-/*1*/   {0x3ff0,0x0000,0x0000,0x0000},
-/*pi*/  {0x4009,0x21fb,0x5444,0x2d18},
-/*lg2*/        {0x3fd3,0x4413,0x509f,0x79ff},
-/*ln2*/        {0x3fe6,0x2e42,0xfefa,0x39ef},
-/*l2e*/        {0x3ff7,0x1547,0x652b,0x82fe},
-/*l2t*/        {0x400a,0x934f,0x0979,0xa371}
-#endif
-};
-#endif
-    
 /* n must be a constant to be efficient */
 static inline int lshift(int x, int n)
 {
@@ -171,62 +80,34 @@ static inline int lshift(int x, int n)
 
 /* operations with flags */
 
-void OPPROTO op_addl_T0_T1_cc(void)
+/* update flags with T0 and T1 (add/sub case) */
+void OPPROTO op_update2_cc(void)
 {
-    CC_SRC = T0;
-    T0 += T1;
+    CC_SRC = T1;
     CC_DST = T0;
 }
 
-void OPPROTO op_orl_T0_T1_cc(void)
+/* update flags with T0 (logic operation case) */
+void OPPROTO op_update1_cc(void)
 {
-    T0 |= T1;
     CC_DST = T0;
 }
 
-void OPPROTO op_andl_T0_T1_cc(void)
+void OPPROTO op_update_neg_cc(void)
 {
-    T0 &= T1;
-    CC_DST = T0;
-}
-
-void OPPROTO op_subl_T0_T1_cc(void)
-{
-    CC_SRC = T0;
-    T0 -= T1;
-    CC_DST = T0;
-}
-
-void OPPROTO op_xorl_T0_T1_cc(void)
-{
-    T0 ^= T1;
+    CC_SRC = -T0;
     CC_DST = T0;
 }
 
 void OPPROTO op_cmpl_T0_T1_cc(void)
 {
-    CC_SRC = T0;
+    CC_SRC = T1;
     CC_DST = T0 - T1;
 }
 
-void OPPROTO op_negl_T0_cc(void)
-{
-    CC_SRC = 0;
-    T0 = -T0;
-    CC_DST = T0;
-}
-
-void OPPROTO op_incl_T0_cc(void)
-{
-    CC_SRC = cc_table[CC_OP].compute_c();
-    T0++;
-    CC_DST = T0;
-}
-
-void OPPROTO op_decl_T0_cc(void)
+void OPPROTO op_update_inc_cc(void)
 {
     CC_SRC = cc_table[CC_OP].compute_c();
-    T0--;
     CC_DST = T0;
 }
 
@@ -358,6 +239,7 @@ void OPPROTO op_imull_T0_T1(void)
 
 /* division, flags are undefined */
 /* XXX: add exceptions for overflow */
+
 void OPPROTO op_divb_AL_T0(void)
 {
     unsigned int num, den, q, r;
@@ -420,62 +302,14 @@ 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;
-    uint64_t num;
-    
-    num = EAX | ((uint64_t)EDX << 32);
-    den = T0;
-    if (den == 0) {
-        EIP = PARAM1;
-        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;
+    helper_divl_EAX_T0(PARAM1);
 }
 
 void OPPROTO op_idivl_EAX_T0(void)
 {
-    int den, q, r;
-    int64_t num;
-    
-    num = EAX | ((uint64_t)EDX << 32);
-    den = T0;
-    if (den == 0) {
-        EIP = PARAM1;
-        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;
+    helper_idivl_EAX_T0(PARAM1);
 }
 
 /* constant load & misc op */
@@ -495,6 +329,11 @@ void OPPROTO op_andl_T0_ffff(void)
     T0 = T0 & 0xffff;
 }
 
+void OPPROTO op_andl_T0_im(void)
+{
+    T0 = T0 & PARAM1;
+}
+
 void OPPROTO op_movl_T0_T1(void)
 {
     T0 = T1;
@@ -626,6 +465,12 @@ void OPPROTO op_jmp_im(void)
     EIP = PARAM1;
 }
 
+void OPPROTO op_hlt(void)
+{
+    env->exception_index = EXCP_HLT;
+    cpu_loop_exit();
+}
+
 void OPPROTO op_raise_interrupt(void)
 {
     int intno;
@@ -708,50 +553,9 @@ void OPPROTO op_boundl(void)
 
 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 {
-        EDX = d >> 32;
-        EAX = d;
-        eflags &= ~CC_Z;
-    }
-    CC_SRC = eflags;
-    FORCE_RET();
+    helper_cmpxchg8b();
 }
 
-#if defined(__powerpc__)
-
-/* on PowerPC we patch the jump instruction directly */
-#define JUMP_TB(tbparam, n, eip)\
-do {\
-    static void __attribute__((unused)) *__op_label ## n = &&label ## n;\
-    asm volatile ("b %0" : : "i" (&__op_jmp ## n));\
-label ## n:\
-    T0 = (long)(tbparam) + (n);\
-    EIP = eip;\
-} while (0)
-
-#else
-
-/* jump to next block operations (more portable code, does not need
-   cache flushing, but slower because of indirect jump) */
-#define JUMP_TB(tbparam, n, eip)\
-do {\
-    static void __attribute__((unused)) *__op_label ## n = &&label ## n;\
-    goto *(void *)(((TranslationBlock *)tbparam)->tb_next[n]);\
-label ## n:\
-    T0 = (long)(tbparam) + (n);\
-    EIP = eip;\
-} while (0)
-
-#endif
-
 void OPPROTO op_jmp_tb_next(void)
 {
     JUMP_TB(PARAM1, 0, PARAM2);
@@ -844,7 +648,7 @@ void op_pushl_ss32_T0(void)
 {
     uint32_t offset;
     offset = ESP - 4;
-    stl(env->seg_cache[R_SS].base + offset, T0);
+    stl(env->segs[R_SS].base + offset, T0);
     /* modify ESP after to handle exceptions correctly */
     ESP = offset;
 }
@@ -853,7 +657,7 @@ void op_pushw_ss32_T0(void)
 {
     uint32_t offset;
     offset = ESP - 2;
-    stw(env->seg_cache[R_SS].base + offset, T0);
+    stw(env->segs[R_SS].base + offset, T0);
     /* modify ESP after to handle exceptions correctly */
     ESP = offset;
 }
@@ -862,7 +666,7 @@ void op_pushl_ss16_T0(void)
 {
     uint32_t offset;
     offset = (ESP - 4) & 0xffff;
-    stl(env->seg_cache[R_SS].base + offset, T0);
+    stl(env->segs[R_SS].base + offset, T0);
     /* modify ESP after to handle exceptions correctly */
     ESP = (ESP & ~0xffff) | offset;
 }
@@ -871,7 +675,7 @@ void op_pushw_ss16_T0(void)
 {
     uint32_t offset;
     offset = (ESP - 2) & 0xffff;
-    stw(env->seg_cache[R_SS].base + offset, T0);
+    stw(env->segs[R_SS].base + offset, T0);
     /* modify ESP after to handle exceptions correctly */
     ESP = (ESP & ~0xffff) | offset;
 }
@@ -889,22 +693,22 @@ void op_popw_T0(void)
 
 void op_popl_ss32_T0(void)
 {
-    T0 = ldl(env->seg_cache[R_SS].base + ESP);
+    T0 = ldl(env->segs[R_SS].base + ESP);
 }
 
 void op_popw_ss32_T0(void)
 {
-    T0 = lduw(env->seg_cache[R_SS].base + ESP);
+    T0 = lduw(env->segs[R_SS].base + ESP);
 }
 
 void op_popl_ss16_T0(void)
 {
-    T0 = ldl(env->seg_cache[R_SS].base + (ESP & 0xffff));
+    T0 = ldl(env->segs[R_SS].base + (ESP & 0xffff));
 }
 
 void op_popw_ss16_T0(void)
 {
-    T0 = lduw(env->seg_cache[R_SS].base + (ESP & 0xffff));
+    T0 = lduw(env->segs[R_SS].base + (ESP & 0xffff));
 }
 
 void op_addl_ESP_4(void)
@@ -937,22 +741,9 @@ void op_addw_ESP_im(void)
     ESP = (ESP & ~0xffff) | ((ESP + PARAM1) & 0xffff);
 }
 
-/* rdtsc */
-#ifndef __i386__
-uint64_t emu_time;
-#endif
-
 void OPPROTO op_rdtsc(void)
 {
-    uint64_t val;
-#ifdef __i386__
-    asm("rdtsc" : "=A" (val));
-#else
-    /* better than nothing: the time increases */
-    val = emu_time++;
-#endif
-    EAX = val;
-    EDX = val >> 32;
+    helper_rdtsc();
 }
 
 void OPPROTO op_cpuid(void)
@@ -1101,17 +892,18 @@ void OPPROTO op_movl_seg_T0(void)
 void OPPROTO op_movl_seg_T0_vm(void)
 {
     int selector;
+    SegmentCache *sc;
     
     selector = T0 & 0xffff;
     /* env->segs[] access */
-    *(uint32_t *)((char *)env + PARAM1) = selector;
-    /* env->seg_cache[] access */
-    ((SegmentCache *)((char *)env + PARAM2))->base = (void *)(selector << 4);
+    sc = (SegmentCache *)((char *)env + PARAM1);
+    sc->selector = selector;
+    sc->base = (void *)(selector << 4);
 }
 
 void OPPROTO op_movl_T0_seg(void)
 {
-    T0 = env->segs[PARAM1];
+    T0 = env->segs[PARAM1].selector;
 }
 
 void OPPROTO op_movl_A0_seg(void)
@@ -1134,6 +926,71 @@ void OPPROTO op_lar(void)
     helper_lar();
 }
 
+/* T0: segment, T1:eip */
+void OPPROTO op_ljmp_T0_T1(void)
+{
+    jmp_seg(T0 & 0xffff, T1);
+}
+
+void OPPROTO op_iret_protected(void)
+{
+    helper_iret_protected(PARAM1);
+}
+
+void OPPROTO op_lldt_T0(void)
+{
+    helper_lldt_T0();
+}
+
+void OPPROTO op_ltr_T0(void)
+{
+    helper_ltr_T0();
+}
+
+/* CR registers access */
+void OPPROTO op_movl_crN_T0(void)
+{
+    helper_movl_crN_T0(PARAM1);
+}
+
+/* DR registers access */
+void OPPROTO op_movl_drN_T0(void)
+{
+    helper_movl_drN_T0(PARAM1);
+}
+
+void OPPROTO op_lmsw_T0(void)
+{
+    /* only 4 lower bits of CR0 are modified */
+    T0 = (env->cr[0] & ~0xf) | (T0 & 0xf);
+    helper_movl_crN_T0(0);
+}
+
+void OPPROTO op_invlpg_A0(void)
+{
+    helper_invlpg(A0);
+}
+
+void OPPROTO op_movl_T0_env(void)
+{
+    T0 = *(uint32_t *)((char *)env + PARAM1);
+}
+
+void OPPROTO op_movl_env_T0(void)
+{
+    *(uint32_t *)((char *)env + PARAM1) = T0;
+}
+
+void OPPROTO op_movl_env_T1(void)
+{
+    *(uint32_t *)((char *)env + PARAM1) = T1;
+}
+
+void OPPROTO op_clts(void)
+{
+    env->cr[0] &= ~CR0_TS_MASK;
+}
+
 /* flags handling */
 
 /* slow jumps cases : in order to avoid calling a function with a
@@ -1213,8 +1070,7 @@ 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)
+#define FL_UPDATE_MASK16 (FL_UPDATE_MASK32 & 0xffff)
 
 void OPPROTO op_movl_eflags_T0(void)
 {
@@ -1223,7 +1079,8 @@ void OPPROTO op_movl_eflags_T0(void)
     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);
+    env->eflags = (env->eflags & ~FL_UPDATE_MASK32) | 
+        (eflags & FL_UPDATE_MASK32);
 }
 
 void OPPROTO op_movw_eflags_T0(void)
@@ -1233,7 +1090,18 @@ void OPPROTO op_movw_eflags_T0(void)
     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);
+    env->eflags = (env->eflags & ~FL_UPDATE_MASK16) | 
+        (eflags & FL_UPDATE_MASK16);
+}
+
+void OPPROTO op_movl_eflags_T0_cpl0(void)
+{
+    load_eflags(T0, FL_UPDATE_CPL0_MASK);
+}
+
+void OPPROTO op_movw_eflags_T0_cpl0(void)
+{
+    load_eflags(T0, FL_UPDATE_CPL0_MASK & 0xffff);
 }
 
 #if 0
@@ -1640,7 +1508,7 @@ void OPPROTO op_fildll_ST0_A0(void)
 void OPPROTO op_fsts_ST0_A0(void)
 {
 #ifdef USE_FP_CONVERT
-    FP_CONVERT.d = ST0;
+    FP_CONVERT.f = (float)ST0;
     stfl((void *)A0, FP_CONVERT.f);
 #else
     stfl((void *)A0, (float)ST0);
@@ -1675,6 +1543,8 @@ void OPPROTO op_fist_ST0_A0(void)
 
     d = ST0;
     val = lrint(d);
+    if (val != (int16_t)val)
+        val = -32768;
     stw((void *)A0, val);
 }
 
@@ -1904,42 +1774,42 @@ void OPPROTO op_fxam_ST0(void)
 
 void OPPROTO op_fld1_ST0(void)
 {
-    ST0 = *(CPU86_LDouble *)&f15rk[1];
+    ST0 = f15rk[1];
 }
 
 void OPPROTO op_fldl2t_ST0(void)
 {
-    ST0 = *(CPU86_LDouble *)&f15rk[6];
+    ST0 = f15rk[6];
 }
 
 void OPPROTO op_fldl2e_ST0(void)
 {
-    ST0 = *(CPU86_LDouble *)&f15rk[5];
+    ST0 = f15rk[5];
 }
 
 void OPPROTO op_fldpi_ST0(void)
 {
-    ST0 = *(CPU86_LDouble *)&f15rk[2];
+    ST0 = f15rk[2];
 }
 
 void OPPROTO op_fldlg2_ST0(void)
 {
-    ST0 = *(CPU86_LDouble *)&f15rk[3];
+    ST0 = f15rk[3];
 }
 
 void OPPROTO op_fldln2_ST0(void)
 {
-    ST0 = *(CPU86_LDouble *)&f15rk[4];
+    ST0 = f15rk[4];
 }
 
 void OPPROTO op_fldz_ST0(void)
 {
-    ST0 = *(CPU86_LDouble *)&f15rk[0];
+    ST0 = f15rk[0];
 }
 
 void OPPROTO op_fldz_FT0(void)
 {
-    ST0 = *(CPU86_LDouble *)&f15rk[0];
+    ST0 = f15rk[0];
 }
 
 /* associated heplers to reduce generated code length and to simplify