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 */ /* needed to load some predefinied segment registers */
void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector); 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 /* you can call this signal handler from your SIGBUS and SIGSEGV
signal handlers to inform the virtual CPU of exceptions. non zero signal handlers to inform the virtual CPU of exceptions. non zero
is returned if the signal was handled by the virtual CPU. */ 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; 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 EAX
#undef ECX #undef ECX
#undef EDX #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_err(int exception_index, int error_code);
void raise_exception(int exception_index); void raise_exception(int exception_index);
void cpu_loop_exit(void); 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_eflags_T0(void);
void OPPROTO op_movl_T0_eflags(void); void OPPROTO op_movl_T0_eflags(void);

200
op-i386.c
View file

@ -1073,7 +1073,7 @@ void helper_cpuid(void)
EBX = 0x756e6547; EBX = 0x756e6547;
ECX = 0x6c65746e; ECX = 0x6c65746e;
EDX = 0x49656e69; EDX = 0x49656e69;
} else { } else if (EAX == 1) {
/* EAX = 1 info */ /* EAX = 1 info */
EAX = 0x52b; EAX = 0x52b;
EBX = 0; EBX = 0;
@ -1899,17 +1899,22 @@ void OPPROTO op_fldt_ST0_A0(void)
ST0 = *(long double *)A0; ST0 = *(long double *)A0;
} }
#else #else
void helper_fldt_ST0_A0(void) static inline CPU86_LDouble helper_fldt(uint8_t *ptr)
{ {
CPU86_LDoubleU temp; CPU86_LDoubleU temp;
int upper, e; int upper, e;
/* mantissa */ /* mantissa */
upper = lduw((uint8_t *)A0 + 8); upper = lduw(ptr + 8);
/* XXX: handle overflow ? */ /* XXX: handle overflow ? */
e = (upper & 0x7fff) - 16383 + EXPBIAS; /* exponent */ e = (upper & 0x7fff) - 16383 + EXPBIAS; /* exponent */
e |= (upper >> 4) & 0x800; /* sign */ e |= (upper >> 4) & 0x800; /* sign */
temp.ll = ((ldq((void *)A0) >> 11) & ((1LL << 52) - 1)) | ((uint64_t)e << 52); temp.ll = ((ldq(ptr) >> 11) & ((1LL << 52) - 1)) | ((uint64_t)e << 52);
ST0 = temp.d; return temp.d;
}
void helper_fldt_ST0_A0(void)
{
ST0 = helper_fldt((uint8_t *)A0);
} }
void OPPROTO op_fldt_ST0_A0(void) void OPPROTO op_fldt_ST0_A0(void)
@ -2008,17 +2013,23 @@ void OPPROTO op_fstt_ST0_A0(void)
*(long double *)A0 = ST0; *(long double *)A0 = ST0;
} }
#else #else
void helper_fstt_ST0_A0(void)
static inline void helper_fstt(CPU86_LDouble f, uint8_t *ptr)
{ {
CPU86_LDoubleU temp; CPU86_LDoubleU temp;
int e; int e;
temp.d = ST0; temp.d = f;
/* mantissa */ /* mantissa */
stq((void *)A0, (MANTD(temp) << 11) | (1LL << 63)); stq(ptr, (MANTD(temp) << 11) | (1LL << 63));
/* exponent + sign */ /* exponent + sign */
e = EXPD(temp) - EXPBIAS + 16383; e = EXPD(temp) - EXPBIAS + 16383;
e |= SIGND(temp) >> 16; 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) void OPPROTO op_fstt_ST0_A0(void)
@ -2254,6 +2265,34 @@ void OPPROTO op_fucom_ST0_FT0(void)
FORCE_RET(); 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) void OPPROTO op_fadd_ST0_FT0(void)
{ {
ST0 += FT0; ST0 += FT0;
@ -2750,6 +2789,149 @@ void OPPROTO op_fninit(void)
env->fptags[7] = 1; 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 */ /* threading support */
void OPPROTO op_lock(void) 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->ss32) {
if (s->dflag) if (addend == 2)
gen_op_addl_ESP_4();
else
gen_op_addl_ESP_2(); gen_op_addl_ESP_2();
else if (addend == 4)
gen_op_addl_ESP_4();
else
gen_op_addl_ESP_im(addend);
} else { } else {
if (s->dflag) if (addend == 2)
gen_op_addw_ESP_2();
else if (addend == 4)
gen_op_addw_ESP_4(); gen_op_addw_ESP_4();
else 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 */ /* NOTE: wrap around in 16 bit not fully handled */
static void gen_pusha(DisasContext *s) static void gen_pusha(DisasContext *s)
{ {
@ -1957,7 +1976,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
} }
break; break;
case 0xc9: /* leave */ 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) { if (s->ss32) {
gen_op_mov_TN_reg[OT_LONG][0][R_EBP](); gen_op_mov_TN_reg[OT_LONG][0][R_EBP]();
gen_op_mov_reg_T0[OT_LONG][R_ESP](); gen_op_mov_reg_T0[OT_LONG][R_ESP]();
@ -2453,9 +2472,15 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
break; break;
} }
break; break;
case 0x0c: /* fldenv mem */
gen_op_fldenv_A0(s->dflag);
break;
case 0x0d: /* fldcw mem */ case 0x0d: /* fldcw mem */
gen_op_fldcw_A0(); gen_op_fldcw_A0();
break; break;
case 0x0e: /* fnstenv mem */
gen_op_fnstenv_A0(s->dflag);
break;
case 0x0f: /* fnstcw mem */ case 0x0f: /* fnstcw mem */
gen_op_fnstcw_A0(); gen_op_fnstcw_A0();
break; break;
@ -2467,6 +2492,12 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
gen_op_fstt_ST0_A0(); gen_op_fstt_ST0_A0();
gen_op_fpop(); gen_op_fpop();
break; 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 */ case 0x2f: /* fnstsw mem */
gen_op_fnstsw_A0(); gen_op_fnstsw_A0();
break; break;
@ -2672,6 +2703,20 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
goto illegal_op; goto illegal_op;
} }
break; 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 */ case 0x2a: /* fst sti */
gen_op_fmov_STN_ST0(opreg); gen_op_fmov_STN_ST0(opreg);
break; break;
@ -2709,6 +2754,22 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
goto illegal_op; goto illegal_op;
} }
break; 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: default:
goto illegal_op; goto illegal_op;
} }
@ -2901,10 +2962,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
val = ldsw(s->pc); val = ldsw(s->pc);
s->pc += 2; s->pc += 2;
gen_pop_T0(s); gen_pop_T0(s);
if (s->ss32) gen_stack_update(s, val + (2 << s->dflag));
gen_op_addl_ESP_im(val + (2 << s->dflag));
else
gen_op_addw_ESP_im(val + (2 << s->dflag));
if (s->dflag == 0) if (s->dflag == 0)
gen_op_andl_T0_ffff(); gen_op_andl_T0_ffff();
gen_op_jmp_T0(); gen_op_jmp_T0();
@ -2919,63 +2977,55 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
s->is_jmp = 1; s->is_jmp = 1;
break; break;
case 0xca: /* lret im */ case 0xca: /* lret im */
/* XXX: not restartable */
val = ldsw(s->pc); val = ldsw(s->pc);
s->pc += 2; s->pc += 2;
do_lret:
gen_stack_A0(s);
/* pop offset */ /* pop offset */
gen_pop_T0(s); gen_op_ld_T0_A0[1 + s->dflag]();
if (s->dflag == 0) if (s->dflag == 0)
gen_op_andl_T0_ffff(); gen_op_andl_T0_ffff();
/* NOTE: keeping EIP updated is not a problem in case of
exception */
gen_op_jmp_T0(); gen_op_jmp_T0();
gen_pop_update(s);
/* pop selector */ /* 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_movl_seg_T0(s, R_CS, pc_start - s->cs_base);
gen_pop_update(s);
/* add stack offset */ /* add stack offset */
if (s->ss32) gen_stack_update(s, val + (4 << s->dflag));
gen_op_addl_ESP_im(val);
else
gen_op_addw_ESP_im(val);
s->is_jmp = 1; s->is_jmp = 1;
break; break;
case 0xcb: /* lret */ case 0xcb: /* lret */
/* XXX: not restartable */ val = 0;
/* pop offset */ goto do_lret;
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;
case 0xcf: /* iret */ case 0xcf: /* iret */
if (s->vm86 && s->iopl != 3) { if (s->vm86 && s->iopl != 3) {
gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
} else { } else {
/* XXX: not restartable */ /* XXX: not restartable */
gen_stack_A0(s);
/* pop offset */ /* pop offset */
gen_pop_T0(s); gen_op_ld_T0_A0[1 + s->dflag]();
if (s->dflag == 0) if (s->dflag == 0)
gen_op_andl_T0_ffff(); gen_op_andl_T0_ffff();
gen_op_jmp_T0(); /* NOTE: keeping EIP updated is not a problem in case of
gen_pop_update(s); exception */
gen_op_jmp_T0();
/* pop selector */ /* pop selector */
gen_pop_T0(s); gen_op_addl_A0_im(2 << s->dflag);
gen_movl_seg_T0(s, R_CS, pc_start - s->cs_base); gen_op_ld_T0_A0[1 + s->dflag]();
gen_pop_update(s);
/* pop eflags */ /* 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) { if (s->dflag) {
gen_op_movl_eflags_T0(); gen_op_movl_eflags_T0();
} else { } else {
gen_op_movw_eflags_T0(); gen_op_movw_eflags_T0();
} }
gen_pop_update(s); gen_stack_update(s, (6 << s->dflag));
s->cc_op = CC_OP_EFLAGS; s->cc_op = CC_OP_EFLAGS;
} }
s->is_jmp = 1; s->is_jmp = 1;
@ -2997,6 +3047,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
case 0x9a: /* lcall im */ case 0x9a: /* lcall im */
{ {
unsigned int selector, offset; unsigned int selector, offset;
/* XXX: not restartable */
ot = dflag ? OT_LONG : OT_WORD; ot = dflag ? OT_LONG : OT_WORD;
offset = insn_get(s, ot); offset = insn_get(s, ot);
@ -3613,6 +3664,8 @@ static uint16_t opc_write_flags[NB_OPS] = {
[INDEX_op_cmpxchg8b] = CC_Z, [INDEX_op_cmpxchg8b] = CC_Z,
[INDEX_op_lar] = CC_Z, [INDEX_op_lar] = CC_Z,
[INDEX_op_lsl] = 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 */ /* simpler form of an operation if no flags need to be generated */