added fsave/frstor/fstenv/fldenv/fcomi - fixed cpuid - make lret/iret restartable

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@198 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
bellard 2003-05-29 20:04:28 +00:00
parent df0f11a03b
commit d0a1ffc957
5 changed files with 315 additions and 50 deletions

View file

@ -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. */

View file

@ -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

View file

@ -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);

200
op-i386.c
View file

@ -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)
{

View file

@ -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 */