mirror of
https://gitlab.com/qemu-project/qemu
synced 2024-11-05 20:35:44 +00:00
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:
parent
df0f11a03b
commit
d0a1ffc957
5 changed files with 315 additions and 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. */
|
||||
|
|
24
exec-i386.c
24
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
|
||||
|
|
|
@ -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
200
op-i386.c
|
@ -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)
|
||||
{
|
||||
|
|
135
translate-i386.c
135
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 */
|
||||
|
|
Loading…
Reference in a new issue