mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-25 09:00:22 +00:00
UserspaceEmulator: Delegate rounding to the actual hardware
This also makes us a bit more accurate, due to better rounding of intermediate results. This also gives us the flush-to-zero and denormals-are-zero SSE settings for free! (Assuming UE is build with SSE)
This commit is contained in:
parent
56a31ab376
commit
6c41267dcf
Notes:
sideshowbarker
2024-07-17 11:11:41 +09:00
Author: https://github.com/Hendiadyoin1 Commit: https://github.com/SerenityOS/serenity/commit/6c41267dcf Pull-request: https://github.com/SerenityOS/serenity/pull/13573 Reviewed-by: https://github.com/awesomekling Reviewed-by: https://github.com/gmta Reviewed-by: https://github.com/kleinesfilmroellchen ✅ Reviewed-by: https://github.com/timschumi
5 changed files with 91 additions and 126 deletions
|
@ -20,7 +20,7 @@ set(SOURCES
|
||||||
main.cpp
|
main.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_compile_options(-mmmx -Wno-psabi)
|
add_compile_options(-mmmx -Wno-psabi -frounding-math)
|
||||||
|
|
||||||
serenity_bin(UserspaceEmulator)
|
serenity_bin(UserspaceEmulator)
|
||||||
target_link_libraries(UserspaceEmulator LibX86 LibDebug LibCore LibPthread LibLine)
|
target_link_libraries(UserspaceEmulator LibX86 LibDebug LibCore LibPthread LibLine)
|
||||||
|
|
|
@ -123,32 +123,32 @@ ALWAYS_INLINE void SoftFPU::fpu_set_exception(FPU_Exception ex)
|
||||||
break;
|
break;
|
||||||
case FPU_Exception::InvalidOperation:
|
case FPU_Exception::InvalidOperation:
|
||||||
m_fpu_error_invalid = 1;
|
m_fpu_error_invalid = 1;
|
||||||
if (!m_fpu_mask_invalid)
|
if (!m_fpu_cw.mask_invalid)
|
||||||
break;
|
break;
|
||||||
return;
|
return;
|
||||||
case FPU_Exception::DenormalizedOperand:
|
case FPU_Exception::DenormalizedOperand:
|
||||||
m_fpu_error_denorm = 1;
|
m_fpu_error_denorm = 1;
|
||||||
if (!m_fpu_mask_denorm)
|
if (!m_fpu_cw.mask_denorm)
|
||||||
break;
|
break;
|
||||||
return;
|
return;
|
||||||
case FPU_Exception::ZeroDivide:
|
case FPU_Exception::ZeroDivide:
|
||||||
m_fpu_error_zero_div = 1;
|
m_fpu_error_zero_div = 1;
|
||||||
if (!m_fpu_mask_zero_div)
|
if (!m_fpu_cw.mask_zero_div)
|
||||||
break;
|
break;
|
||||||
return;
|
return;
|
||||||
case FPU_Exception::Overflow:
|
case FPU_Exception::Overflow:
|
||||||
m_fpu_error_overflow = 1;
|
m_fpu_error_overflow = 1;
|
||||||
if (!m_fpu_mask_overflow)
|
if (!m_fpu_cw.mask_overflow)
|
||||||
break;
|
break;
|
||||||
return;
|
return;
|
||||||
case FPU_Exception::Underflow:
|
case FPU_Exception::Underflow:
|
||||||
m_fpu_error_underflow = 1;
|
m_fpu_error_underflow = 1;
|
||||||
if (!m_fpu_mask_underflow)
|
if (!m_fpu_cw.mask_underflow)
|
||||||
break;
|
break;
|
||||||
return;
|
return;
|
||||||
case FPU_Exception::Precision:
|
case FPU_Exception::Precision:
|
||||||
m_fpu_error_precision = 1;
|
m_fpu_error_precision = 1;
|
||||||
if (!m_fpu_mask_precision)
|
if (!m_fpu_cw.mask_precision)
|
||||||
break;
|
break;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -168,27 +168,9 @@ ALWAYS_INLINE void SoftFPU::fpu_set_exception(FPU_Exception ex)
|
||||||
}
|
}
|
||||||
|
|
||||||
template<Arithmetic T>
|
template<Arithmetic T>
|
||||||
ALWAYS_INLINE T SoftFPU::fpu_round(long double value) const
|
ALWAYS_INLINE T SoftFPU::round_checked(long double value)
|
||||||
{
|
{
|
||||||
// FIXME: may need to set indefinite values manually
|
T result = static_cast<T>(rintl(value));
|
||||||
switch (fpu_get_round_mode()) {
|
|
||||||
case RoundingMode::NEAREST:
|
|
||||||
return static_cast<T>(roundl(value));
|
|
||||||
case RoundingMode::DOWN:
|
|
||||||
return static_cast<T>(floorl(value));
|
|
||||||
case RoundingMode::UP:
|
|
||||||
return static_cast<T>(ceill(value));
|
|
||||||
case RoundingMode::TRUNC:
|
|
||||||
return static_cast<T>(truncl(value));
|
|
||||||
default:
|
|
||||||
VERIFY_NOT_REACHED();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<Arithmetic T>
|
|
||||||
ALWAYS_INLINE T SoftFPU::fpu_round_checked(long double value)
|
|
||||||
{
|
|
||||||
T result = fpu_round<T>(value);
|
|
||||||
if (result != value)
|
if (result != value)
|
||||||
fpu_set_exception(FPU_Exception::Precision);
|
fpu_set_exception(FPU_Exception::Precision);
|
||||||
if (result > value)
|
if (result > value)
|
||||||
|
@ -199,15 +181,9 @@ ALWAYS_INLINE T SoftFPU::fpu_round_checked(long double value)
|
||||||
}
|
}
|
||||||
|
|
||||||
template<FloatingPoint T>
|
template<FloatingPoint T>
|
||||||
ALWAYS_INLINE T SoftFPU::fpu_convert(long double value) const
|
ALWAYS_INLINE T SoftFPU::convert_checked(long double value)
|
||||||
{
|
{
|
||||||
// FIXME: actually round the right way
|
T result = static_cast<T>(value);
|
||||||
return static_cast<T>(value);
|
|
||||||
}
|
|
||||||
template<FloatingPoint T>
|
|
||||||
ALWAYS_INLINE T SoftFPU::fpu_convert_checked(long double value)
|
|
||||||
{
|
|
||||||
T result = fpu_convert<T>(value);
|
|
||||||
if (auto rnd = value - result) {
|
if (auto rnd = value - result) {
|
||||||
if (rnd > 0)
|
if (rnd > 0)
|
||||||
set_c1(1);
|
set_c1(1);
|
||||||
|
@ -254,7 +230,7 @@ void SoftFPU::FLD_RM80(const X86::Instruction& insn)
|
||||||
void SoftFPU::FST_RM32(const X86::Instruction& insn)
|
void SoftFPU::FST_RM32(const X86::Instruction& insn)
|
||||||
{
|
{
|
||||||
VERIFY(!insn.modrm().is_register());
|
VERIFY(!insn.modrm().is_register());
|
||||||
float f32 = fpu_convert_checked<float>(fpu_get(0));
|
float f32 = convert_checked<float>(fpu_get(0));
|
||||||
|
|
||||||
if (fpu_is_set(0))
|
if (fpu_is_set(0))
|
||||||
insn.modrm().write32(m_cpu, insn, shadow_wrap_as_initialized(bit_cast<u32>(f32)));
|
insn.modrm().write32(m_cpu, insn, shadow_wrap_as_initialized(bit_cast<u32>(f32)));
|
||||||
|
@ -266,7 +242,7 @@ void SoftFPU::FST_RM64(const X86::Instruction& insn)
|
||||||
if (insn.modrm().is_register()) {
|
if (insn.modrm().is_register()) {
|
||||||
fpu_set(insn.modrm().register_index(), fpu_get(0));
|
fpu_set(insn.modrm().register_index(), fpu_get(0));
|
||||||
} else {
|
} else {
|
||||||
double f64 = fpu_convert_checked<double>(fpu_get(0));
|
double f64 = convert_checked<double>(fpu_get(0));
|
||||||
if (fpu_is_set(0))
|
if (fpu_is_set(0))
|
||||||
insn.modrm().write64(m_cpu, insn, shadow_wrap_as_initialized(bit_cast<u64>(f64)));
|
insn.modrm().write64(m_cpu, insn, shadow_wrap_as_initialized(bit_cast<u64>(f64)));
|
||||||
else
|
else
|
||||||
|
@ -335,7 +311,7 @@ void SoftFPU::FIST_RM16(const X86::Instruction& insn)
|
||||||
VERIFY(!insn.modrm().is_register());
|
VERIFY(!insn.modrm().is_register());
|
||||||
auto f = fpu_get(0);
|
auto f = fpu_get(0);
|
||||||
set_c1(0);
|
set_c1(0);
|
||||||
auto int16 = fpu_round_checked<i16>(f);
|
auto int16 = round_checked<i16>(f);
|
||||||
|
|
||||||
// FIXME: Respect shadow values
|
// FIXME: Respect shadow values
|
||||||
insn.modrm().write16(m_cpu, insn, shadow_wrap_as_initialized(bit_cast<u16>(int16)));
|
insn.modrm().write16(m_cpu, insn, shadow_wrap_as_initialized(bit_cast<u16>(int16)));
|
||||||
|
@ -345,7 +321,7 @@ void SoftFPU::FIST_RM32(const X86::Instruction& insn)
|
||||||
VERIFY(!insn.modrm().is_register());
|
VERIFY(!insn.modrm().is_register());
|
||||||
auto f = fpu_get(0);
|
auto f = fpu_get(0);
|
||||||
set_c1(0);
|
set_c1(0);
|
||||||
auto int32 = fpu_round_checked<i32>(f);
|
auto int32 = round_checked<i32>(f);
|
||||||
// FIXME: Respect shadow values
|
// FIXME: Respect shadow values
|
||||||
insn.modrm().write32(m_cpu, insn, shadow_wrap_as_initialized(bit_cast<u32>(int32)));
|
insn.modrm().write32(m_cpu, insn, shadow_wrap_as_initialized(bit_cast<u32>(int32)));
|
||||||
}
|
}
|
||||||
|
@ -365,7 +341,7 @@ void SoftFPU::FISTP_RM64(const X86::Instruction& insn)
|
||||||
VERIFY(!insn.modrm().is_register());
|
VERIFY(!insn.modrm().is_register());
|
||||||
auto f = fpu_pop();
|
auto f = fpu_pop();
|
||||||
set_c1(0);
|
set_c1(0);
|
||||||
auto i64 = fpu_round_checked<int64_t>(f);
|
auto i64 = round_checked<int64_t>(f);
|
||||||
// FIXME: Respect shadow values
|
// FIXME: Respect shadow values
|
||||||
insn.modrm().write64(m_cpu, insn, shadow_wrap_as_initialized(bit_cast<u64>(i64)));
|
insn.modrm().write64(m_cpu, insn, shadow_wrap_as_initialized(bit_cast<u64>(i64)));
|
||||||
}
|
}
|
||||||
|
@ -787,7 +763,7 @@ void SoftFPU::FCHS(const X86::Instruction&)
|
||||||
void SoftFPU::FRNDINT(const X86::Instruction&)
|
void SoftFPU::FRNDINT(const X86::Instruction&)
|
||||||
{
|
{
|
||||||
// FIXME: Raise #IA #D
|
// FIXME: Raise #IA #D
|
||||||
auto res = fpu_round_checked<long double>(fpu_get(0));
|
auto res = round_checked<long double>(fpu_get(0));
|
||||||
fpu_set(0, res);
|
fpu_set(0, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -818,7 +794,7 @@ void SoftFPU::FCOMPP(const X86::Instruction&)
|
||||||
{
|
{
|
||||||
if (fpu_isnan(0) || fpu_isnan(1)) {
|
if (fpu_isnan(0) || fpu_isnan(1)) {
|
||||||
fpu_set_exception(FPU_Exception::InvalidOperation);
|
fpu_set_exception(FPU_Exception::InvalidOperation);
|
||||||
if (m_fpu_mask_invalid)
|
if (m_fpu_cw.mask_invalid)
|
||||||
fpu_set_unordered();
|
fpu_set_unordered();
|
||||||
} else {
|
} else {
|
||||||
set_c2(0);
|
set_c2(0);
|
||||||
|
@ -1146,7 +1122,7 @@ void SoftFPU::FFREEP(const X86::Instruction& insn)
|
||||||
|
|
||||||
void SoftFPU::FNINIT(const X86::Instruction&)
|
void SoftFPU::FNINIT(const X86::Instruction&)
|
||||||
{
|
{
|
||||||
m_fpu_cw = 0x037F;
|
m_fpu_cw.cw = 0x037F;
|
||||||
m_fpu_sw = 0;
|
m_fpu_sw = 0;
|
||||||
m_fpu_tw = 0xFFFF;
|
m_fpu_tw = 0xFFFF;
|
||||||
|
|
||||||
|
@ -1172,11 +1148,23 @@ void SoftFPU::FNCLEX(const X86::Instruction&)
|
||||||
|
|
||||||
void SoftFPU::FNSTCW(const X86::Instruction& insn)
|
void SoftFPU::FNSTCW(const X86::Instruction& insn)
|
||||||
{
|
{
|
||||||
insn.modrm().write16(m_cpu, insn, shadow_wrap_as_initialized(m_fpu_cw));
|
insn.modrm().write16(m_cpu, insn, shadow_wrap_as_initialized(m_fpu_cw.cw));
|
||||||
}
|
}
|
||||||
void SoftFPU::FLDCW(const X86::Instruction& insn)
|
void SoftFPU::FLDCW(const X86::Instruction& insn)
|
||||||
{
|
{
|
||||||
m_fpu_cw = insn.modrm().read16(m_cpu, insn).value();
|
m_fpu_cw.cw = insn.modrm().read16(m_cpu, insn).value();
|
||||||
|
|
||||||
|
// Just let the host's x87 handle the rounding for us
|
||||||
|
// We do not want to accedentally raise an FP-Exception on the host, so we
|
||||||
|
// mask all exceptions
|
||||||
|
AK::X87ControlWord temp = m_fpu_cw;
|
||||||
|
temp.mask_invalid = 1;
|
||||||
|
temp.mask_denorm = 1;
|
||||||
|
temp.mask_zero_div = 1;
|
||||||
|
temp.mask_overflow = 1;
|
||||||
|
temp.mask_underflow = 1;
|
||||||
|
temp.mask_precision = 1;
|
||||||
|
AK::set_cw_x87(temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoftFPU::FNSTENV(const X86::Instruction& insn)
|
void SoftFPU::FNSTENV(const X86::Instruction& insn)
|
||||||
|
@ -1204,7 +1192,7 @@ void SoftFPU::FNSTENV(const X86::Instruction& insn)
|
||||||
|
|
||||||
auto address = insn.modrm().resolve(m_cpu, insn);
|
auto address = insn.modrm().resolve(m_cpu, insn);
|
||||||
|
|
||||||
m_cpu.write_memory16(address, shadow_wrap_as_initialized(m_fpu_cw));
|
m_cpu.write_memory16(address, shadow_wrap_as_initialized(m_fpu_cw.cw));
|
||||||
address.set_offset(address.offset() + 4);
|
address.set_offset(address.offset() + 4);
|
||||||
m_cpu.write_memory16(address, shadow_wrap_as_initialized(m_fpu_sw));
|
m_cpu.write_memory16(address, shadow_wrap_as_initialized(m_fpu_sw));
|
||||||
address.set_offset(address.offset() + 4);
|
address.set_offset(address.offset() + 4);
|
||||||
|
@ -1227,7 +1215,17 @@ void SoftFPU::FLDENV(const X86::Instruction& insn)
|
||||||
auto address = insn.modrm().resolve(m_cpu, insn);
|
auto address = insn.modrm().resolve(m_cpu, insn);
|
||||||
|
|
||||||
// FIXME: Shadow Values
|
// FIXME: Shadow Values
|
||||||
m_fpu_cw = m_cpu.read_memory16(address).value();
|
m_fpu_cw.cw = m_cpu.read_memory16(address).value();
|
||||||
|
// See note in FLDCW
|
||||||
|
AK::X87ControlWord temp = m_fpu_cw;
|
||||||
|
temp.mask_invalid = 1;
|
||||||
|
temp.mask_denorm = 1;
|
||||||
|
temp.mask_zero_div = 1;
|
||||||
|
temp.mask_overflow = 1;
|
||||||
|
temp.mask_underflow = 1;
|
||||||
|
temp.mask_precision = 1;
|
||||||
|
AK::set_cw_x87(temp);
|
||||||
|
|
||||||
address.set_offset(address.offset() + 4);
|
address.set_offset(address.offset() + 4);
|
||||||
m_fpu_sw = m_cpu.read_memory16(address).value();
|
m_fpu_sw = m_cpu.read_memory16(address).value();
|
||||||
address.set_offset(address.offset() + 4);
|
address.set_offset(address.offset() + 4);
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include "Report.h"
|
#include "Report.h"
|
||||||
#include <AK/Concepts.h>
|
#include <AK/Concepts.h>
|
||||||
|
#include <AK/FPControl.h>
|
||||||
#include <AK/SIMD.h>
|
#include <AK/SIMD.h>
|
||||||
#include <LibX86/Instruction.h>
|
#include <LibX86/Instruction.h>
|
||||||
#include <LibX86/Interpreter.h>
|
#include <LibX86/Interpreter.h>
|
||||||
|
@ -17,6 +18,8 @@
|
||||||
|
|
||||||
namespace UserspaceEmulator {
|
namespace UserspaceEmulator {
|
||||||
using namespace AK::SIMD;
|
using namespace AK::SIMD;
|
||||||
|
using AK::RoundingMode;
|
||||||
|
|
||||||
class Emulator;
|
class Emulator;
|
||||||
class SoftCPU;
|
class SoftCPU;
|
||||||
|
|
||||||
|
@ -35,6 +38,7 @@ public:
|
||||||
SoftFPU(Emulator& emulator, SoftCPU& cpu)
|
SoftFPU(Emulator& emulator, SoftCPU& cpu)
|
||||||
: m_emulator(emulator)
|
: m_emulator(emulator)
|
||||||
, m_cpu(cpu)
|
, m_cpu(cpu)
|
||||||
|
, m_fpu_cw { 0x037F }
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,13 +85,6 @@ private:
|
||||||
Empty = 0b11
|
Empty = 0b11
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class RoundingMode : u8 {
|
|
||||||
NEAREST = 0b00,
|
|
||||||
DOWN = 0b01,
|
|
||||||
UP = 0b10,
|
|
||||||
TRUNC = 0b11
|
|
||||||
};
|
|
||||||
|
|
||||||
void fpu_dump_env()
|
void fpu_dump_env()
|
||||||
{
|
{
|
||||||
reportln("Exceptions: #I:{} #D:{} #Z:{} #O:{} #U:{} #P:{} #SF:{} Summary:{}",
|
reportln("Exceptions: #I:{} #D:{} #Z:{} #O:{} #U:{} #P:{} #SF:{} Summary:{}",
|
||||||
|
@ -100,12 +97,12 @@ private:
|
||||||
m_fpu_error_stackfault,
|
m_fpu_error_stackfault,
|
||||||
m_fpu_error_summary);
|
m_fpu_error_summary);
|
||||||
reportln("Masks: #I:{} #D:{} #Z:{} #O:{} #U:{} #P:{}",
|
reportln("Masks: #I:{} #D:{} #Z:{} #O:{} #U:{} #P:{}",
|
||||||
m_fpu_mask_invalid,
|
m_fpu_cw.mask_invalid,
|
||||||
m_fpu_mask_denorm,
|
m_fpu_cw.mask_denorm,
|
||||||
m_fpu_mask_zero_div,
|
m_fpu_cw.mask_zero_div,
|
||||||
m_fpu_mask_overflow,
|
m_fpu_cw.mask_overflow,
|
||||||
m_fpu_mask_underflow,
|
m_fpu_cw.mask_underflow,
|
||||||
m_fpu_mask_precision);
|
m_fpu_cw.mask_precision);
|
||||||
reportln("C0:{} C1:{} C2:{} C3:{}", c0(), c1(), c2(), c3());
|
reportln("C0:{} C1:{} C2:{} C3:{}", c0(), c1(), c2(), c3());
|
||||||
reportln("fpu-stacktop: {}", m_fpu_stack_top);
|
reportln("fpu-stacktop: {}", m_fpu_stack_top);
|
||||||
reportln("fpu-stack /w stacktop (real):");
|
reportln("fpu-stack /w stacktop (real):");
|
||||||
|
@ -261,18 +258,14 @@ private:
|
||||||
|
|
||||||
ALWAYS_INLINE RoundingMode fpu_get_round_mode() const
|
ALWAYS_INLINE RoundingMode fpu_get_round_mode() const
|
||||||
{
|
{
|
||||||
return RoundingMode(m_fpu_round_mode);
|
return m_fpu_cw.rounding_control;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<Arithmetic T>
|
template<Arithmetic T>
|
||||||
T fpu_round(long double) const;
|
T round_checked(long double);
|
||||||
template<Arithmetic T>
|
|
||||||
T fpu_round_checked(long double);
|
|
||||||
|
|
||||||
template<FloatingPoint T>
|
template<FloatingPoint T>
|
||||||
T fpu_convert(long double) const;
|
T convert_checked(long double);
|
||||||
template<FloatingPoint T>
|
|
||||||
T fpu_convert_checked(long double);
|
|
||||||
|
|
||||||
ALWAYS_INLINE void fpu_set_unordered()
|
ALWAYS_INLINE void fpu_set_unordered()
|
||||||
{
|
{
|
||||||
|
@ -295,22 +288,7 @@ private:
|
||||||
};
|
};
|
||||||
} m_storage[8];
|
} m_storage[8];
|
||||||
|
|
||||||
union {
|
AK::X87ControlWord m_fpu_cw;
|
||||||
u16 m_fpu_cw { 0x037F };
|
|
||||||
struct {
|
|
||||||
u16 m_fpu_mask_invalid : 1;
|
|
||||||
u16 m_fpu_mask_denorm : 1;
|
|
||||||
u16 m_fpu_mask_zero_div : 1;
|
|
||||||
u16 m_fpu_mask_overflow : 1;
|
|
||||||
u16 m_fpu_mask_underflow : 1;
|
|
||||||
u16 m_fpu_mask_precision : 1;
|
|
||||||
u16 : 2; // unused
|
|
||||||
u16 m_fpu_precission : 2;
|
|
||||||
u16 m_fpu_round_mode : 2;
|
|
||||||
u16 m_fpu_infinity_control : 1;
|
|
||||||
u16 : 3; // unused
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
union {
|
union {
|
||||||
u16 m_fpu_sw { 0 };
|
u16 m_fpu_sw { 0 };
|
||||||
|
|
|
@ -18,14 +18,35 @@ void SoftVPU::PREFETCHT2(X86::Instruction const&) { TODO(); }
|
||||||
void SoftVPU::LDMXCSR(X86::Instruction const& insn)
|
void SoftVPU::LDMXCSR(X86::Instruction const& insn)
|
||||||
{
|
{
|
||||||
// FIXME: Shadows
|
// FIXME: Shadows
|
||||||
m_mxcsr = insn.modrm().read32(m_cpu, insn).value();
|
m_mxcsr.mxcsr = insn.modrm().read32(m_cpu, insn).value();
|
||||||
|
|
||||||
// #GP - General Protection Fault
|
// #GP - General Protection Fault
|
||||||
VERIFY((m_mxcsr & 0xFFFF'0000) == 0);
|
VERIFY((m_mxcsr.mxcsr & 0xFFFF'0000) == 0);
|
||||||
|
|
||||||
|
// Just let the host's SSE (or if not available x87) handle the rounding for us
|
||||||
|
// We do not want to accedentally raise an FP-Exception on the host, so we
|
||||||
|
// mask all exceptions
|
||||||
|
#ifdef __SSE__
|
||||||
|
AK::MXCSR temp = m_mxcsr;
|
||||||
|
temp.invalid_operation_mask = 1;
|
||||||
|
temp.denormal_operation_mask = 1;
|
||||||
|
temp.divide_by_zero_mask = 1;
|
||||||
|
temp.overflow_mask = 1;
|
||||||
|
temp.underflow_mask = 1;
|
||||||
|
temp.precision_mask = 1;
|
||||||
|
AK::set_mxcsr(temp);
|
||||||
|
#else
|
||||||
|
// FIXME: This will mess with x87-land, because it uses the same trick, and
|
||||||
|
// Does not know of us doing this
|
||||||
|
AK::X87ControlWord cw { 0x037F };
|
||||||
|
cw.rounding_control = m_mxcsr.rounding_control;
|
||||||
|
AK::set_cw_x87(cw);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
void SoftVPU::STMXCSR(X86::Instruction const& insn)
|
void SoftVPU::STMXCSR(X86::Instruction const& insn)
|
||||||
{
|
{
|
||||||
// FIXME: Shadows
|
// FIXME: Shadows
|
||||||
insn.modrm().write32(m_cpu, insn, ValueWithShadow<u32>::create_initialized(m_mxcsr));
|
insn.modrm().write32(m_cpu, insn, ValueWithShadow<u32>::create_initialized(m_mxcsr.mxcsr));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoftVPU::MOVUPS_xmm1_xmm2m128(X86::Instruction const& insn)
|
void SoftVPU::MOVUPS_xmm1_xmm2m128(X86::Instruction const& insn)
|
||||||
|
@ -222,7 +243,7 @@ void SoftVPU::CVTSS2SI_r32_xmm2m32(X86::Instruction const& insn)
|
||||||
{
|
{
|
||||||
// FIXME: Raise Invalid, Precision
|
// FIXME: Raise Invalid, Precision
|
||||||
insn.modrm().write32(m_cpu, insn,
|
insn.modrm().write32(m_cpu, insn,
|
||||||
ValueWithShadow<u32>::create_initialized((u32)lround(m_xmm[insn.modrm().reg()].ps[0])));
|
ValueWithShadow<u32>::create_initialized(static_cast<i32>(m_xmm[insn.modrm().reg()].ps[0])));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoftVPU::UCOMISS_xmm1_xmm2m32(X86::Instruction const& insn)
|
void SoftVPU::UCOMISS_xmm1_xmm2m32(X86::Instruction const& insn)
|
||||||
|
|
|
@ -6,12 +6,14 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/FPControl.h>
|
||||||
#include <AK/SIMD.h>
|
#include <AK/SIMD.h>
|
||||||
#include <AK/Types.h>
|
#include <AK/Types.h>
|
||||||
#include <LibX86/Instruction.h>
|
#include <LibX86/Instruction.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
namespace UserspaceEmulator {
|
namespace UserspaceEmulator {
|
||||||
|
using AK::RoundingMode;
|
||||||
using namespace AK::SIMD;
|
using namespace AK::SIMD;
|
||||||
class Emulator;
|
class Emulator;
|
||||||
class SoftCPU;
|
class SoftCPU;
|
||||||
|
@ -57,51 +59,17 @@ public:
|
||||||
// FIXME: More with VEX prefix
|
// FIXME: More with VEX prefix
|
||||||
};
|
};
|
||||||
|
|
||||||
i32 lround(float value) const
|
|
||||||
{
|
|
||||||
// FIXME: This is not yet 100% correct
|
|
||||||
using enum RoundingMode;
|
|
||||||
switch ((RoundingMode)rounding_control) {
|
|
||||||
case NEAREST:
|
|
||||||
return ::lroundf(value);
|
|
||||||
case DOWN:
|
|
||||||
return floorf(value);
|
|
||||||
case UP:
|
|
||||||
return ceilf(value);
|
|
||||||
case TRUNC:
|
|
||||||
return truncf(value);
|
|
||||||
default:
|
|
||||||
VERIFY_NOT_REACHED();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend SoftCPU;
|
friend SoftCPU;
|
||||||
Emulator& m_emulator;
|
Emulator& m_emulator;
|
||||||
SoftCPU& m_cpu;
|
SoftCPU& m_cpu;
|
||||||
|
|
||||||
XMM m_xmm[8];
|
XMM m_xmm[8];
|
||||||
union {
|
|
||||||
u32 m_mxcsr;
|
// FIXME: Maybe unimplemented features:
|
||||||
struct {
|
// * DAZ
|
||||||
u32 invalid_operation_flag : 1; // IE
|
// * FTZ
|
||||||
u32 denormal_operation_flag : 1; // DE
|
AK::MXCSR m_mxcsr;
|
||||||
u32 divide_by_zero_flag : 1; // ZE
|
|
||||||
u32 overflow_flag : 1; // OE
|
|
||||||
u32 underflow_flag : 1; // UE
|
|
||||||
u32 precision_flag : 1; // PE
|
|
||||||
u32 denormals_are_zero : 1; // FIXME: DAZ
|
|
||||||
u32 invalid_operation_mask : 1; // IM
|
|
||||||
u32 denormal_operation_mask : 1; // DM
|
|
||||||
u32 devide_by_zero_mask : 1; // ZM
|
|
||||||
u32 overflow_mask : 1; // OM
|
|
||||||
u32 underflow_mask : 1; // UM
|
|
||||||
u32 precision_mask : 1; // PM
|
|
||||||
u32 rounding_control : 2; // FIXME: RC
|
|
||||||
u32 flush_to_zero : 1; // FIXME: FTZ
|
|
||||||
u32 __reserved : 16;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
void PREFETCHTNTA(X86::Instruction const&);
|
void PREFETCHTNTA(X86::Instruction const&);
|
||||||
void PREFETCHT0(X86::Instruction const&);
|
void PREFETCHT0(X86::Instruction const&);
|
||||||
|
|
Loading…
Reference in a new issue