Bochs/bochs/fpu/fpu.cc
Stanislav Shwartsman afe3ff691d Another fix for FPU tag word restore in FXRESTOR instruction (the tags were assigned to incorrect registers)
Fixed FPU print state status word printing (printed partial status instead of normal status word)
2005-06-18 20:46:08 +00:00

526 lines
18 KiB
C++

/////////////////////////////////////////////////////////////////////////
// $Id: fpu.cc,v 1.14 2005-06-18 20:46:08 sshwarts Exp $
/////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2003 Stanislav Shwartsman
// Written by Stanislav Shwartsman <stl at fidonet.org.il>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
/////////////////////////////////////////////////////////////////////////
#define NEED_CPU_REG_SHORTCUTS 1
#include "bochs.h"
#include "iodev/iodev.h"
#define LOG_THIS BX_CPU_THIS_PTR
#define UPDATE_LAST_OPCODE 1
#define CHECK_PENDING_EXCEPTIONS 1
#if BX_SUPPORT_FPU
void BX_CPU_C::prepareFPU(bxInstruction_c *i,
bx_bool check_pending_exceptions, bx_bool update_last_instruction)
{
if (BX_CPU_THIS_PTR cr0.em || BX_CPU_THIS_PTR cr0.ts)
exception(BX_NM_EXCEPTION, 0, 0);
if (check_pending_exceptions)
BX_CPU_THIS_PTR FPU_check_pending_exceptions();
if (update_last_instruction)
{
BX_CPU_THIS_PTR the_i387.foo = ((Bit32u)(i->b1()) << 8) | (Bit32u)(i->modrm());
BX_CPU_THIS_PTR the_i387.fcs = BX_CPU_THIS_PTR sregs[BX_SEG_REG_CS].selector.value;
BX_CPU_THIS_PTR the_i387.fip = BX_CPU_THIS_PTR prev_eip;
if (! i->modC0()) {
BX_CPU_THIS_PTR the_i387.fds = BX_CPU_THIS_PTR sregs[i->seg()].selector.value;
BX_CPU_THIS_PTR the_i387.fdp = RMAddr(i);
} else {
BX_CPU_THIS_PTR the_i387.fds = BX_CPU_THIS_PTR sregs[BX_SEG_REG_SS].selector.value;
BX_CPU_THIS_PTR the_i387.fdp = 0;
}
}
}
void BX_CPU_C::FPU_check_pending_exceptions(void)
{
if(BX_CPU_THIS_PTR the_i387.get_partial_status() & FPU_SW_Summary)
{
if (BX_CPU_THIS_PTR cr0.ne == 0)
{
// MSDOS compatibility external interrupt (IRQ13)
BX_INFO (("math_abort: MSDOS compatibility FPU exception"));
DEV_pic_raise_irq(13);
}
else
exception(BX_MF_EXCEPTION, 0, 0);
}
}
int BX_CPU_C::fpu_save_environment(bxInstruction_c *i)
{
if (protected_mode()) /* Protected Mode */
{
if (i->os32L() || i->os64L())
{
Bit32u tmp;
tmp = 0xffff0000 | BX_CPU_THIS_PTR the_i387.get_control_word();
write_virtual_dword(i->seg(), RMAddr(i), &tmp);
tmp = 0xffff0000 | BX_CPU_THIS_PTR the_i387.get_status_word();
write_virtual_dword(i->seg(), RMAddr(i) + 0x04, &tmp);
tmp = 0xffff0000 | BX_CPU_THIS_PTR the_i387.get_tag_word();
write_virtual_dword(i->seg(), RMAddr(i) + 0x08, &tmp);
tmp = (BX_CPU_THIS_PTR the_i387.fip) & 0xffffffff;
write_virtual_dword(i->seg(), RMAddr(i) + 0x0c, &tmp);
tmp = (BX_CPU_THIS_PTR the_i387.fcs & 0xffff) |
((Bit32u)(BX_CPU_THIS_PTR the_i387.foo)) << 16;
write_virtual_dword(i->seg(), RMAddr(i) + 0x10, &tmp);
tmp = (BX_CPU_THIS_PTR the_i387.fdp) & 0xffffffff;
write_virtual_dword(i->seg(), RMAddr(i) + 0x14, &tmp);
tmp = 0xffff0000 | (BX_CPU_THIS_PTR the_i387.fds);
write_virtual_dword(i->seg(), RMAddr(i) + 0x18, &tmp);
return 0x1c;
}
else /* Protected Mode - 16 bit */
{
Bit16u tmp;
tmp = BX_CPU_THIS_PTR the_i387.get_control_word();
write_virtual_word(i->seg(), RMAddr(i), &tmp);
tmp = BX_CPU_THIS_PTR the_i387.get_status_word();
write_virtual_word(i->seg(), RMAddr(i) + 0x02, &tmp);
tmp = BX_CPU_THIS_PTR the_i387.get_tag_word();
write_virtual_word(i->seg(), RMAddr(i) + 0x04, &tmp);
tmp = (BX_CPU_THIS_PTR the_i387.fip) & 0xffff;
write_virtual_word(i->seg(), RMAddr(i) + 0x06, &tmp);
tmp = (BX_CPU_THIS_PTR the_i387.fcs);
write_virtual_word(i->seg(), RMAddr(i) + 0x08, &tmp);
tmp = (BX_CPU_THIS_PTR the_i387.fdp) & 0xffff;
write_virtual_word(i->seg(), RMAddr(i) + 0x0a, &tmp);
tmp = (BX_CPU_THIS_PTR the_i387.fds);
write_virtual_word(i->seg(), RMAddr(i) + 0x0c, &tmp);
return 0x0e;
}
}
else /* Real or V86 Mode */
{
Bit32u fp_ip = ((Bit32u)(BX_CPU_THIS_PTR the_i387.fcs) << 4) +
(BX_CPU_THIS_PTR the_i387.fip);
Bit32u fp_dp = ((Bit32u)(BX_CPU_THIS_PTR the_i387.fds) << 4) +
(BX_CPU_THIS_PTR the_i387.fdp);
if (i->os32L() || i->os64L())
{
Bit32u tmp;
tmp = 0xffff0000 | BX_CPU_THIS_PTR the_i387.get_control_word();
write_virtual_dword(i->seg(), RMAddr(i), &tmp);
tmp = 0xffff0000 | BX_CPU_THIS_PTR the_i387.get_status_word();
write_virtual_dword(i->seg(), RMAddr(i) + 0x04, &tmp);
tmp = 0xffff0000 | BX_CPU_THIS_PTR the_i387.get_tag_word();
write_virtual_dword(i->seg(), RMAddr(i) + 0x08, &tmp);
tmp = 0xffff0000 | (fp_ip & 0xffff);
write_virtual_dword(i->seg(), RMAddr(i) + 0x0c, &tmp);
tmp = ((fp_ip & 0xffff0000) >> 4) |
(BX_CPU_THIS_PTR the_i387.foo & 0x7ff);
write_virtual_dword(i->seg(), RMAddr(i) + 0x10, &tmp);
tmp = 0xffff0000 | (fp_dp & 0xffff);
write_virtual_dword(i->seg(), RMAddr(i) + 0x14, &tmp);
tmp = (fp_dp & 0xffff0000) >> 4;
write_virtual_dword(i->seg(), RMAddr(i) + 0x18, &tmp);
return 0x1c;
}
else /* Real or V86 Mode - 16 bit */
{
Bit16u tmp;
tmp = BX_CPU_THIS_PTR the_i387.get_control_word();
write_virtual_word(i->seg(), RMAddr(i), &tmp);
tmp = BX_CPU_THIS_PTR the_i387.get_status_word();
write_virtual_word(i->seg(), RMAddr(i) + 0x02, &tmp);
tmp = BX_CPU_THIS_PTR the_i387.get_tag_word();
write_virtual_word(i->seg(), RMAddr(i) + 0x04, &tmp);
tmp = fp_ip & 0xffff;
write_virtual_word(i->seg(), RMAddr(i) + 0x06, &tmp);
tmp = (Bit16u)(((fp_ip & 0xf0000) >> 4) |
(BX_CPU_THIS_PTR the_i387.foo & 0x7ff));
write_virtual_word(i->seg(), RMAddr(i) + 0x08, &tmp);
tmp = fp_dp & 0xffff;
write_virtual_word(i->seg(), RMAddr(i) + 0x0a, &tmp);
tmp = (Bit16u)((fp_dp & 0xf0000) >> 4);
write_virtual_word(i->seg(), RMAddr(i) + 0x0c, &tmp);
return 0x0e;
}
}
}
int BX_CPU_C::fpu_load_environment(bxInstruction_c *i)
{
int offset;
if (protected_mode()) /* Protected Mode */
{
if (i->os32L() || i->os64L())
{
Bit32u tmp;
read_virtual_dword(i->seg(), RMAddr(i), &tmp);
BX_CPU_THIS_PTR the_i387.cwd = tmp & 0xffff;
read_virtual_dword(i->seg(), RMAddr(i) + 0x04, &tmp);
BX_CPU_THIS_PTR the_i387.swd = tmp & 0xffff;
BX_CPU_THIS_PTR the_i387.tos = (tmp >> 11) & 0x07;
read_virtual_dword(i->seg(), RMAddr(i) + 0x08, &tmp);
BX_CPU_THIS_PTR the_i387.twd = tmp & 0xffff;
read_virtual_dword(i->seg(), RMAddr(i) + 0x0c, &tmp);
BX_CPU_THIS_PTR the_i387.fip = tmp;
read_virtual_dword(i->seg(), RMAddr(i) + 0x10, &tmp);
BX_CPU_THIS_PTR the_i387.fcs = tmp & 0xffff;
BX_CPU_THIS_PTR the_i387.foo = (tmp >> 16) & 0x07ff;
read_virtual_dword(i->seg(), RMAddr(i) + 0x14, &tmp);
BX_CPU_THIS_PTR the_i387.fdp = tmp;
read_virtual_dword(i->seg(), RMAddr(i) + 0x18, &tmp);
BX_CPU_THIS_PTR the_i387.fds = tmp & 0xffff;
offset = 0x1c;
}
else /* Protected Mode - 16 bit */
{
Bit16u tmp;
read_virtual_word(i->seg(), RMAddr(i), &tmp);
BX_CPU_THIS_PTR the_i387.cwd = tmp;
read_virtual_word(i->seg(), RMAddr(i) + 0x2, &tmp);
BX_CPU_THIS_PTR the_i387.swd = tmp;
BX_CPU_THIS_PTR the_i387.tos = (tmp >> 11) & 0x07;
read_virtual_word(i->seg(), RMAddr(i) + 0x04, &tmp);
BX_CPU_THIS_PTR the_i387.twd = tmp;
read_virtual_word(i->seg(), RMAddr(i) + 0x06, &tmp);
BX_CPU_THIS_PTR the_i387.fip = tmp & 0xffff;
read_virtual_word(i->seg(), RMAddr(i) + 0x08, &tmp);
BX_CPU_THIS_PTR the_i387.fcs = tmp;
read_virtual_word(i->seg(), RMAddr(i) + 0x0a, &tmp);
BX_CPU_THIS_PTR the_i387.fdp = tmp & 0xffff;
read_virtual_word(i->seg(), RMAddr(i) + 0x0c, &tmp);
BX_CPU_THIS_PTR the_i387.fds = tmp;
/* opcode is defined to be zero */
BX_CPU_THIS_PTR the_i387.foo = 0;
offset = 0x0e;
}
}
else /* Real or V86 Mode */
{
Bit32u fp_ip = 0, fp_dp = 0;
if (i->os32L() || i->os64L())
{
Bit32u tmp;
read_virtual_dword(i->seg(), RMAddr(i), &tmp);
BX_CPU_THIS_PTR the_i387.cwd = tmp & 0xffff;
read_virtual_dword(i->seg(), RMAddr(i) + 0x04, &tmp);
BX_CPU_THIS_PTR the_i387.swd = tmp & 0xffff;
BX_CPU_THIS_PTR the_i387.tos = (tmp >> 11) & 0x07;
read_virtual_dword(i->seg(), RMAddr(i) + 0x08, &tmp);
BX_CPU_THIS_PTR the_i387.twd = tmp & 0xffff;
read_virtual_dword(i->seg(), RMAddr(i) + 0x0c, &tmp);
fp_ip = tmp & 0xffff;
read_virtual_dword(i->seg(), RMAddr(i) + 0x10, &tmp);
fp_ip = fp_ip | ((tmp & 0x0ffff000) << 4);
BX_CPU_THIS_PTR the_i387.fip = fp_ip;
BX_CPU_THIS_PTR the_i387.foo = tmp & 0x07ff;
BX_CPU_THIS_PTR the_i387.fcs = 0;
read_virtual_dword(i->seg(), RMAddr(i) + 0x14, &tmp);
fp_dp = tmp & 0xffff;
read_virtual_dword(i->seg(), RMAddr(i) + 0x18, &tmp);
fp_dp = fp_dp | ((tmp & 0x0ffff000) << 4);
BX_CPU_THIS_PTR the_i387.fdp = fp_dp;
BX_CPU_THIS_PTR the_i387.fds = 0;
offset = 0x1c;
}
else /* Real or V86 Mode - 16 bit */
{
Bit16u tmp;
read_virtual_word(i->seg(), RMAddr(i), &tmp);
BX_CPU_THIS_PTR the_i387.cwd = tmp;
read_virtual_word(i->seg(), RMAddr(i) + 0x2, &tmp);
BX_CPU_THIS_PTR the_i387.swd = tmp;
BX_CPU_THIS_PTR the_i387.tos = (tmp >> 11) & 0x07;
read_virtual_word(i->seg(), RMAddr(i) + 0x04, &tmp);
BX_CPU_THIS_PTR the_i387.twd = tmp;
read_virtual_word(i->seg(), RMAddr(i) + 0x06, &tmp);
fp_ip = tmp & 0xffff;
read_virtual_word(i->seg(), RMAddr(i) + 0x08, &tmp);
fp_ip = fp_ip | ((tmp & 0xf000) << 4);
BX_CPU_THIS_PTR the_i387.fip = fp_ip;
BX_CPU_THIS_PTR the_i387.foo = tmp & 0x07ff;
BX_CPU_THIS_PTR the_i387.fcs = 0;
read_virtual_word(i->seg(), RMAddr(i) + 0x0a, &tmp);
fp_dp = tmp & 0xffff;
read_virtual_word(i->seg(), RMAddr(i) + 0x0c, &tmp);
fp_dp = fp_dp | ((tmp & 0xf000) << 4);
BX_CPU_THIS_PTR the_i387.fdp = fp_dp;
BX_CPU_THIS_PTR the_i387.fds = 0;
offset = 0x0e;
}
}
/* check for unmasked exceptions */
if (FPU_PARTIAL_STATUS & ~FPU_CONTROL_WORD & FPU_CW_Exceptions_Mask)
{
/* set the B and ES bits in the status-word */
FPU_PARTIAL_STATUS |= FPU_SW_Summary | FPU_SW_Backward;
}
else
{
/* clear the B and ES bits in the status-word */
FPU_PARTIAL_STATUS &= ~(FPU_SW_Summary | FPU_SW_Backward);
}
return offset;
}
#endif
/* D9 /5 */
void BX_CPU_C::FLDCW(bxInstruction_c *i)
{
#if BX_SUPPORT_FPU
BX_CPU_THIS_PTR prepareFPU(i, CHECK_PENDING_EXCEPTIONS, !UPDATE_LAST_OPCODE);
Bit16u cwd;
read_virtual_word(i->seg(), RMAddr(i), &cwd);
FPU_CONTROL_WORD = cwd;
/* check for unmasked exceptions */
if (FPU_PARTIAL_STATUS & ~FPU_CONTROL_WORD & FPU_CW_Exceptions_Mask)
{
/* set the B and ES bits in the status-word */
FPU_PARTIAL_STATUS |= FPU_SW_Summary | FPU_SW_Backward;
}
else
{
/* clear the B and ES bits in the status-word */
FPU_PARTIAL_STATUS &= ~(FPU_SW_Summary | FPU_SW_Backward);
}
#else
BX_INFO(("FLDCW: required FPU, configure --enable-fpu"));
#endif
}
/* D9 /7 */
void BX_CPU_C::FNSTCW(bxInstruction_c *i)
{
#if BX_SUPPORT_FPU
BX_CPU_THIS_PTR prepareFPU(i, !CHECK_PENDING_EXCEPTIONS, !UPDATE_LAST_OPCODE);
Bit16u cwd = BX_CPU_THIS_PTR the_i387.get_control_word();
write_virtual_word(i->seg(), RMAddr(i), &cwd);
#else
BX_INFO(("FNSTCW: required FPU, configure --enable-fpu"));
#endif
}
/* DD /7 */
void BX_CPU_C::FNSTSW(bxInstruction_c *i)
{
#if BX_SUPPORT_FPU
BX_CPU_THIS_PTR prepareFPU(i, !CHECK_PENDING_EXCEPTIONS, !UPDATE_LAST_OPCODE);
Bit16u swd = BX_CPU_THIS_PTR the_i387.get_status_word();
write_virtual_word(i->seg(), RMAddr(i), &swd);
#else
BX_INFO(("FNSTSW: required FPU, configure --enable-fpu"));
#endif
}
/* DF E0 */
void BX_CPU_C::FNSTSW_AX(bxInstruction_c *i)
{
#if BX_SUPPORT_FPU
BX_CPU_THIS_PTR prepareFPU(i, !CHECK_PENDING_EXCEPTIONS, !UPDATE_LAST_OPCODE);
AX = BX_CPU_THIS_PTR the_i387.get_status_word();
#else
BX_INFO(("FNSTSW_AX: required FPU, configure --enable-fpu"));
#endif
}
/* DD /4 */
void BX_CPU_C::FRSTOR(bxInstruction_c *i)
{
#if BX_SUPPORT_FPU
BX_CPU_THIS_PTR prepareFPU(i, CHECK_PENDING_EXCEPTIONS, !UPDATE_LAST_OPCODE);
int offset = fpu_load_environment(i);
/* read all registers in stack order. */
for(int n=0;n<8;n++)
{
floatx80 tmp;
// read register only if its tag is not empty
if (! IS_TAG_EMPTY(n))
{
read_virtual_tword(i->seg(), RMAddr(i) + offset + n*10, &tmp);
BX_WRITE_FPU_REG(tmp, n);
}
}
#else
BX_INFO(("FRSTOR: required FPU, configure --enable-fpu"));
#endif
}
/* DD /6 */
void BX_CPU_C::FNSAVE(bxInstruction_c *i)
{
#if BX_SUPPORT_FPU
BX_CPU_THIS_PTR prepareFPU(i, !CHECK_PENDING_EXCEPTIONS, !UPDATE_LAST_OPCODE);
int offset = fpu_save_environment(i);
/* save all registers in stack order. */
for(int n=0;n<8;n++)
{
floatx80 stn = BX_READ_FPU_REG(n);
write_virtual_tword(i->seg(), RMAddr(i) + offset + n*10, &stn);
}
BX_CPU_THIS_PTR the_i387.init();
#else
BX_INFO(("FNSAVE: required FPU, configure --enable-fpu"));
#endif
}
/* 9B E2 */
void BX_CPU_C::FNCLEX(bxInstruction_c *i)
{
#if BX_SUPPORT_FPU
BX_CPU_THIS_PTR prepareFPU(i, !CHECK_PENDING_EXCEPTIONS, !UPDATE_LAST_OPCODE);
FPU_PARTIAL_STATUS &= ~(FPU_SW_Backward|FPU_SW_Summary|FPU_SW_Stack_Fault|FPU_SW_Precision|
FPU_SW_Underflow|FPU_SW_Overflow|FPU_SW_Zero_Div|FPU_SW_Denormal_Op|
FPU_SW_Invalid);
// do not update last fpu instruction pointer
#else
BX_INFO(("FNCLEX: required FPU, configure --enable-fpu"));
#endif
}
/* DB E3 */
void BX_CPU_C::FNINIT(bxInstruction_c *i)
{
#if BX_SUPPORT_FPU
BX_CPU_THIS_PTR prepareFPU(i, !CHECK_PENDING_EXCEPTIONS, !UPDATE_LAST_OPCODE);
BX_CPU_THIS_PTR the_i387.init();
#else
BX_INFO(("FNINIT: required FPU, configure --enable-fpu"));
#endif
}
/* D9 /4 */
void BX_CPU_C::FLDENV(bxInstruction_c *i)
{
#if BX_SUPPORT_FPU
BX_CPU_THIS_PTR prepareFPU(i, CHECK_PENDING_EXCEPTIONS, !UPDATE_LAST_OPCODE);
fpu_load_environment(i);
#else
BX_INFO(("FLDENV: required FPU, configure --enable-fpu"));
#endif
}
/* D9 /6 */
void BX_CPU_C::FNSTENV(bxInstruction_c *i)
{
#if BX_SUPPORT_FPU
BX_CPU_THIS_PTR prepareFPU(i, !CHECK_PENDING_EXCEPTIONS, !UPDATE_LAST_OPCODE);
fpu_save_environment(i);
#else
BX_INFO(("FNSTENV: required FPU, configure --enable-fpu"));
#endif
}
/* D9 D0 */
void BX_CPU_C::FNOP(bxInstruction_c *i)
{
#if BX_SUPPORT_FPU
BX_CPU_THIS_PTR prepareFPU(i, CHECK_PENDING_EXCEPTIONS, !UPDATE_LAST_OPCODE);
// Perform no FPU operation. This instruction takes up space in the
// instruction stream but does not affect the FPU or machine
// context, except the EIP register.
#else
BX_INFO(("FNOP: required FPU, configure --enable-fpu"));
#endif
}
void BX_CPU_C::FPLEGACY(bxInstruction_c *i)
{
#if BX_SUPPORT_FPU
BX_CPU_THIS_PTR prepareFPU(i, !CHECK_PENDING_EXCEPTIONS, !UPDATE_LAST_OPCODE);
// FPU performs no specific operation and no internal x87 states
// are affected
#else
BX_INFO(("legacy FPU opcodes: required FPU, configure --enable-fpu"));
#endif
}
#if BX_SUPPORT_FPU
#include <math.h>
void BX_CPU_C::print_state_FPU()
{
static double scale_factor = pow(2.0, -63.0);
Bit32u reg;
reg = BX_CPU_THIS_PTR the_i387.get_control_word();
fprintf(stderr, "control word: 0x%04x\n", reg);
reg = BX_CPU_THIS_PTR the_i387.get_status_word();
fprintf(stderr, "status word: 0x%04x\n", reg);
reg = BX_CPU_THIS_PTR the_i387.get_tag_word();
fprintf(stderr, "tag word: 0x%04x\n", reg);
reg = BX_CPU_THIS_PTR the_i387.foo;
fprintf(stderr, "operand: 0x%04x\n", reg);
reg = BX_CPU_THIS_PTR the_i387.fip & 0xffffffff;
fprintf(stderr, "fip: 0x%08x\n", reg);
reg = BX_CPU_THIS_PTR the_i387.fcs;
fprintf(stderr, "fcs: 0x%04x\n", reg);
reg = BX_CPU_THIS_PTR the_i387.fdp & 0xffffffff;
fprintf(stderr, "fdp: 0x%08x\n", reg);
reg = BX_CPU_THIS_PTR the_i387.fds;
fprintf(stderr, "fds: 0x%04x\n", reg);
// print stack too
for (int i=0; i<8; i++) {
const floatx80 &fp = BX_FPU_REG(i);
double f = pow(2.0, ((0x7fff & fp.exp) - 0x3fff));
if (fp.exp & 0x8000) f = -f;
#ifdef _MSC_VER
f *= (double)(signed __int64)(fp.fraction>>1) * scale_factor * 2;
#else
f *= fp.fraction*scale_factor;
#endif
fprintf(stderr, "st(%d): %.10f (raw 0x%04x:%08x%08x)\n", i,
f, fp.exp & 0xffff, fp.fraction >> 32, fp.fraction & 0xffffffff);
}
}
#endif