diff options
-rw-r--r-- | cpu-i386.h | 4 | ||||
-rw-r--r-- | exec-i386.c | 24 | ||||
-rw-r--r-- | exec-i386.h | 2 | ||||
-rw-r--r-- | op-i386.c | 200 | ||||
-rw-r--r-- | translate-i386.c | 135 |
5 files changed, 315 insertions, 50 deletions
@@ -436,6 +436,10 @@ void cpu_x86_close(CPUX86State *s); /* needed to load some predefinied segment registers */ void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector); +/* simulate fsave/frstor */ +void cpu_x86_fsave(CPUX86State *s, uint8_t *ptr, int data32); +void cpu_x86_frstor(CPUX86State *s, uint8_t *ptr, int data32); + /* you can call this signal handler from your SIGBUS and SIGSEGV signal handlers to inform the virtual CPU of exceptions. non zero is returned if the signal was handled by the virtual CPU. */ diff --git a/exec-i386.c b/exec-i386.c index 002f00b..2114d65 100644 --- a/exec-i386.c +++ b/exec-i386.c @@ -327,6 +327,30 @@ void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector) env = saved_env; } +void cpu_x86_fsave(CPUX86State *s, uint8_t *ptr, int data32) +{ + CPUX86State *saved_env; + + saved_env = env; + env = s; + + helper_fsave(ptr, data32); + + env = saved_env; +} + +void cpu_x86_frstor(CPUX86State *s, uint8_t *ptr, int data32) +{ + CPUX86State *saved_env; + + saved_env = env; + env = s; + + helper_frstor(ptr, data32); + + env = saved_env; +} + #undef EAX #undef ECX #undef EDX diff --git a/exec-i386.h b/exec-i386.h index ba5ea1a..00da9b8 100644 --- a/exec-i386.h +++ b/exec-i386.h @@ -225,6 +225,8 @@ void raise_interrupt(int intno, int is_int, int error_code, void raise_exception_err(int exception_index, int error_code); void raise_exception(int exception_index); void cpu_loop_exit(void); +void helper_fsave(uint8_t *ptr, int data32); +void helper_frstor(uint8_t *ptr, int data32); void OPPROTO op_movl_eflags_T0(void); void OPPROTO op_movl_T0_eflags(void); @@ -1073,7 +1073,7 @@ void helper_cpuid(void) EBX = 0x756e6547; ECX = 0x6c65746e; EDX = 0x49656e69; - } else { + } else if (EAX == 1) { /* EAX = 1 info */ EAX = 0x52b; EBX = 0; @@ -1899,17 +1899,22 @@ void OPPROTO op_fldt_ST0_A0(void) ST0 = *(long double *)A0; } #else -void helper_fldt_ST0_A0(void) +static inline CPU86_LDouble helper_fldt(uint8_t *ptr) { CPU86_LDoubleU temp; int upper, e; /* mantissa */ - upper = lduw((uint8_t *)A0 + 8); + upper = lduw(ptr + 8); /* XXX: handle overflow ? */ e = (upper & 0x7fff) - 16383 + EXPBIAS; /* exponent */ e |= (upper >> 4) & 0x800; /* sign */ - temp.ll = ((ldq((void *)A0) >> 11) & ((1LL << 52) - 1)) | ((uint64_t)e << 52); - ST0 = temp.d; + temp.ll = ((ldq(ptr) >> 11) & ((1LL << 52) - 1)) | ((uint64_t)e << 52); + return temp.d; +} + +void helper_fldt_ST0_A0(void) +{ + ST0 = helper_fldt((uint8_t *)A0); } void OPPROTO op_fldt_ST0_A0(void) @@ -2008,17 +2013,23 @@ void OPPROTO op_fstt_ST0_A0(void) *(long double *)A0 = ST0; } #else -void helper_fstt_ST0_A0(void) + +static inline void helper_fstt(CPU86_LDouble f, uint8_t *ptr) { CPU86_LDoubleU temp; int e; - temp.d = ST0; + temp.d = f; /* mantissa */ - stq((void *)A0, (MANTD(temp) << 11) | (1LL << 63)); + stq(ptr, (MANTD(temp) << 11) | (1LL << 63)); /* exponent + sign */ e = EXPD(temp) - EXPBIAS + 16383; e |= SIGND(temp) >> 16; - stw((uint8_t *)A0 + 8, e); + stw(ptr + 8, e); +} + +void helper_fstt_ST0_A0(void) +{ + helper_fstt(ST0, (uint8_t *)A0); } void OPPROTO op_fstt_ST0_A0(void) @@ -2254,6 +2265,34 @@ void OPPROTO op_fucom_ST0_FT0(void) FORCE_RET(); } +/* XXX: handle nans */ +void OPPROTO op_fcomi_ST0_FT0(void) +{ + int eflags; + 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; + CC_SRC = eflags; + FORCE_RET(); +} + +/* XXX: handle nans */ +void OPPROTO op_fucomi_ST0_FT0(void) +{ + int eflags; + 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; + CC_SRC = eflags; + FORCE_RET(); +} + void OPPROTO op_fadd_ST0_FT0(void) { ST0 += FT0; @@ -2750,6 +2789,149 @@ void OPPROTO op_fninit(void) env->fptags[7] = 1; } +void helper_fstenv(uint8_t *ptr, int data32) +{ + int fpus, fptag, exp, i; + uint64_t mant; + CPU86_LDoubleU tmp; + + fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11; + fptag = 0; + for (i=7; i>=0; i--) { + fptag <<= 2; + if (env->fptags[i]) { + fptag |= 3; + } else { + tmp.d = env->fpregs[i]; + exp = EXPD(tmp); + mant = MANTD(tmp); + if (exp == 0 && mant == 0) { + /* zero */ + fptag |= 1; + } else if (exp == 0 || exp == MAXEXPD +#ifdef USE_X86LDOUBLE + || (mant & (1LL << 63)) == 0 +#endif + ) { + /* NaNs, infinity, denormal */ + fptag |= 2; + } + } + } + if (data32) { + /* 32 bit */ + stl(ptr, env->fpuc); + stl(ptr + 4, fpus); + stl(ptr + 8, fptag); + stl(ptr + 12, 0); + stl(ptr + 16, 0); + stl(ptr + 20, 0); + stl(ptr + 24, 0); + } else { + /* 16 bit */ + stw(ptr, env->fpuc); + stw(ptr + 2, fpus); + stw(ptr + 4, fptag); + stw(ptr + 6, 0); + stw(ptr + 8, 0); + stw(ptr + 10, 0); + stw(ptr + 12, 0); + } +} + +void helper_fldenv(uint8_t *ptr, int data32) +{ + int i, fpus, fptag; + + if (data32) { + env->fpuc = lduw(ptr); + fpus = lduw(ptr + 4); + fptag = lduw(ptr + 8); + } + else { + env->fpuc = lduw(ptr); + fpus = lduw(ptr + 2); + fptag = lduw(ptr + 4); + } + env->fpstt = (fpus >> 11) & 7; + env->fpus = fpus & ~0x3800; + for(i = 0;i < 7; i++) { + env->fptags[i] = ((fptag & 3) == 3); + fptag >>= 2; + } +} + +void helper_fsave(uint8_t *ptr, int data32) +{ + CPU86_LDouble tmp; + int i; + + helper_fstenv(ptr, data32); + + ptr += (14 << data32); + for(i = 0;i < 8; i++) { + tmp = ST(i); +#ifdef USE_X86LDOUBLE + *(long double *)ptr = tmp; +#else + helper_fstt(tmp, ptr); +#endif + ptr += 10; + } + + /* fninit */ + env->fpus = 0; + env->fpstt = 0; + env->fpuc = 0x37f; + env->fptags[0] = 1; + env->fptags[1] = 1; + env->fptags[2] = 1; + env->fptags[3] = 1; + env->fptags[4] = 1; + env->fptags[5] = 1; + env->fptags[6] = 1; + env->fptags[7] = 1; +} + +void helper_frstor(uint8_t *ptr, int data32) +{ + CPU86_LDouble tmp; + int i; + + helper_fldenv(ptr, data32); + ptr += (14 << data32); + + for(i = 0;i < 8; i++) { +#ifdef USE_X86LDOUBLE + tmp = *(long double *)ptr; +#else + tmp = helper_fldt(ptr); +#endif + ST(i) = tmp; + ptr += 10; + } +} + +void OPPROTO op_fnstenv_A0(void) +{ + helper_fstenv((uint8_t *)A0, PARAM1); +} + +void OPPROTO op_fldenv_A0(void) +{ + helper_fldenv((uint8_t *)A0, PARAM1); +} + +void OPPROTO op_fnsave_A0(void) +{ + helper_fsave((uint8_t *)A0, PARAM1); +} + +void OPPROTO op_frstor_A0(void) +{ + helper_frstor((uint8_t *)A0, PARAM1); +} + /* threading support */ void OPPROTO op_lock(void) { diff --git a/translate-i386.c b/translate-i386.c index e682d81..3802c5e 100644 --- a/translate-i386.c +++ b/translate-i386.c @@ -1273,21 +1273,40 @@ static void gen_pop_T0(DisasContext *s) } } -static void gen_pop_update(DisasContext *s) +static inline void gen_stack_update(DisasContext *s, int addend) { if (s->ss32) { - if (s->dflag) - gen_op_addl_ESP_4(); - else + if (addend == 2) gen_op_addl_ESP_2(); + else if (addend == 4) + gen_op_addl_ESP_4(); + else + gen_op_addl_ESP_im(addend); } else { - if (s->dflag) + if (addend == 2) + gen_op_addw_ESP_2(); + else if (addend == 4) gen_op_addw_ESP_4(); else - gen_op_addw_ESP_2(); + gen_op_addw_ESP_im(addend); } } +static void gen_pop_update(DisasContext *s) +{ + gen_stack_update(s, 2 << s->dflag); +} + +static void gen_stack_A0(DisasContext *s) +{ + gen_op_movl_A0_ESP(); + if (!s->ss32) + gen_op_andl_A0_ffff(); + gen_op_movl_T1_A0(); + if (s->addseg) + gen_op_addl_A0_seg(offsetof(CPUX86State,seg_cache[R_SS].base)); +} + /* NOTE: wrap around in 16 bit not fully handled */ static void gen_pusha(DisasContext *s) { @@ -1957,7 +1976,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) } break; case 0xc9: /* leave */ - /* XXX: exception not precise (ESP is update before potential exception) */ + /* XXX: exception not precise (ESP is updated before potential exception) */ if (s->ss32) { gen_op_mov_TN_reg[OT_LONG][0][R_EBP](); gen_op_mov_reg_T0[OT_LONG][R_ESP](); @@ -2453,9 +2472,15 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) break; } break; + case 0x0c: /* fldenv mem */ + gen_op_fldenv_A0(s->dflag); + break; case 0x0d: /* fldcw mem */ gen_op_fldcw_A0(); break; + case 0x0e: /* fnstenv mem */ + gen_op_fnstenv_A0(s->dflag); + break; case 0x0f: /* fnstcw mem */ gen_op_fnstcw_A0(); break; @@ -2467,6 +2492,12 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) gen_op_fstt_ST0_A0(); gen_op_fpop(); break; + case 0x2c: /* frstor mem */ + gen_op_frstor_A0(s->dflag); + break; + case 0x2e: /* fnsave mem */ + gen_op_fnsave_A0(s->dflag); + break; case 0x2f: /* fnstsw mem */ gen_op_fnstsw_A0(); break; @@ -2672,6 +2703,20 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) goto illegal_op; } break; + case 0x1d: /* fucomi */ + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + gen_op_fmov_FT0_STN(opreg); + gen_op_fucomi_ST0_FT0(); + s->cc_op = CC_OP_EFLAGS; + break; + case 0x1e: /* fcomi */ + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + gen_op_fmov_FT0_STN(opreg); + gen_op_fcomi_ST0_FT0(); + s->cc_op = CC_OP_EFLAGS; + break; case 0x2a: /* fst sti */ gen_op_fmov_STN_ST0(opreg); break; @@ -2709,6 +2754,22 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) goto illegal_op; } break; + case 0x3d: /* fucomip */ + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + gen_op_fmov_FT0_STN(opreg); + gen_op_fucomi_ST0_FT0(); + gen_op_fpop(); + s->cc_op = CC_OP_EFLAGS; + break; + case 0x3e: /* fcomip */ + if (s->cc_op != CC_OP_DYNAMIC) + gen_op_set_cc_op(s->cc_op); + gen_op_fmov_FT0_STN(opreg); + gen_op_fcomi_ST0_FT0(); + gen_op_fpop(); + s->cc_op = CC_OP_EFLAGS; + break; default: goto illegal_op; } @@ -2901,10 +2962,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) val = ldsw(s->pc); s->pc += 2; gen_pop_T0(s); - if (s->ss32) - gen_op_addl_ESP_im(val + (2 << s->dflag)); - else - gen_op_addw_ESP_im(val + (2 << s->dflag)); + gen_stack_update(s, val + (2 << s->dflag)); if (s->dflag == 0) gen_op_andl_T0_ffff(); gen_op_jmp_T0(); @@ -2919,63 +2977,55 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) s->is_jmp = 1; break; case 0xca: /* lret im */ - /* XXX: not restartable */ val = ldsw(s->pc); s->pc += 2; + do_lret: + gen_stack_A0(s); /* pop offset */ - gen_pop_T0(s); + gen_op_ld_T0_A0[1 + s->dflag](); if (s->dflag == 0) gen_op_andl_T0_ffff(); + /* NOTE: keeping EIP updated is not a problem in case of + exception */ gen_op_jmp_T0(); - gen_pop_update(s); /* pop selector */ - gen_pop_T0(s); + gen_op_addl_A0_im(2 << s->dflag); + gen_op_ld_T0_A0[1 + s->dflag](); gen_movl_seg_T0(s, R_CS, pc_start - s->cs_base); - gen_pop_update(s); /* add stack offset */ - if (s->ss32) - gen_op_addl_ESP_im(val); - else - gen_op_addw_ESP_im(val); + gen_stack_update(s, val + (4 << s->dflag)); s->is_jmp = 1; break; case 0xcb: /* lret */ - /* XXX: not restartable */ - /* pop offset */ - gen_pop_T0(s); - if (s->dflag == 0) - gen_op_andl_T0_ffff(); - gen_op_jmp_T0(); - gen_pop_update(s); - /* pop selector */ - gen_pop_T0(s); - gen_movl_seg_T0(s, R_CS, pc_start - s->cs_base); - gen_pop_update(s); - s->is_jmp = 1; - break; + val = 0; + goto do_lret; case 0xcf: /* iret */ if (s->vm86 && s->iopl != 3) { gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); } else { /* XXX: not restartable */ + gen_stack_A0(s); /* pop offset */ - gen_pop_T0(s); + gen_op_ld_T0_A0[1 + s->dflag](); if (s->dflag == 0) gen_op_andl_T0_ffff(); - gen_op_jmp_T0(); - gen_pop_update(s); + /* NOTE: keeping EIP updated is not a problem in case of + exception */ + gen_op_jmp_T0(); /* pop selector */ - gen_pop_T0(s); - gen_movl_seg_T0(s, R_CS, pc_start - s->cs_base); - gen_pop_update(s); + gen_op_addl_A0_im(2 << s->dflag); + gen_op_ld_T0_A0[1 + s->dflag](); /* pop eflags */ - gen_pop_T0(s); + gen_op_addl_A0_im(2 << s->dflag); + gen_op_ld_T1_A0[1 + s->dflag](); + gen_movl_seg_T0(s, R_CS, pc_start - s->cs_base); + gen_op_movl_T0_T1(); if (s->dflag) { gen_op_movl_eflags_T0(); } else { gen_op_movw_eflags_T0(); } - gen_pop_update(s); + gen_stack_update(s, (6 << s->dflag)); s->cc_op = CC_OP_EFLAGS; } s->is_jmp = 1; @@ -2997,6 +3047,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) case 0x9a: /* lcall im */ { unsigned int selector, offset; + /* XXX: not restartable */ ot = dflag ? OT_LONG : OT_WORD; offset = insn_get(s, ot); @@ -3613,6 +3664,8 @@ static uint16_t opc_write_flags[NB_OPS] = { [INDEX_op_cmpxchg8b] = CC_Z, [INDEX_op_lar] = CC_Z, [INDEX_op_lsl] = CC_Z, + [INDEX_op_fcomi_ST0_FT0] = CC_Z | CC_P | CC_C, + [INDEX_op_fucomi_ST0_FT0] = CC_Z | CC_P | CC_C, }; /* simpler form of an operation if no flags need to be generated */ |