diff options
author | Hendiadyoin1 <leon2002.la@gmail.com> | 2021-07-10 15:00:27 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-07-22 23:33:21 +0200 |
commit | 09a1a0b319a096f70b4c1204e48154323ea1649b (patch) | |
tree | a3644ee1a2d4a07c2d7f8432045ae7593c487897 | |
parent | 1d74742c29a008bc5eb66fd456463ffa4559d31a (diff) | |
download | serenity-09a1a0b319a096f70b4c1204e48154323ea1649b.zip |
UserspaceEmulator: Sketch out a SoftFPU interface
-rw-r--r-- | Userland/DevTools/UserspaceEmulator/SoftFPU.cpp | 248 | ||||
-rw-r--r-- | Userland/DevTools/UserspaceEmulator/SoftFPU.h | 612 |
2 files changed, 860 insertions, 0 deletions
diff --git a/Userland/DevTools/UserspaceEmulator/SoftFPU.cpp b/Userland/DevTools/UserspaceEmulator/SoftFPU.cpp new file mode 100644 index 0000000000..9e1a80c81e --- /dev/null +++ b/Userland/DevTools/UserspaceEmulator/SoftFPU.cpp @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> + * Copyright (c) 2021, Leon Albrecht <leon2002.la@gmail.com> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "SoftFPU.h" +#include "Emulator.h" +#include "SoftCPU.h" +#include "ValueWithShadow.h" + +#include <AK/BitCast.h> +#include <AK/NumericLimits.h> +#include <AK/UFixedBigInt.h> + +#include <unistd.h> + +#if defined(__GNUC__) && !defined(__clang__) +# pragma GCC optimize("O3") +#endif + +#define TODO_INSN() \ + do { \ + reportln("\n=={}== Unimplemented instruction: {}\n", getpid(), __FUNCTION__); \ + m_emulator.dump_backtrace(); \ + _exit(0); \ + } while (0) + +template<typename T> +ALWAYS_INLINE void warn_if_uninitialized(T value_with_shadow, const char* message) +{ + if (value_with_shadow.is_uninitialized()) [[unlikely]] { + reportln("\033[31;1mWarning! Use of uninitialized value: {}\033[0m\n", message); + UserspaceEmulator::Emulator::the().dump_backtrace(); + } +} + +namespace UserspaceEmulator { + +ALWAYS_INLINE void SoftFPU::warn_if_fpu_not_set_absolute(u8 index) const +{ + if (!fpu_is_set(index)) [[unlikely]] { + // FIXME: Are we supposed to set a flag here? + // We might need to raise a stack underflow here + reportln("\033[31;1mWarning! Read of uninitialized value on the FPU Stack ({} abs)\033[0m\n", index); + m_emulator.dump_backtrace(); + } +} +ALWAYS_INLINE void SoftFPU::warn_if_mmx_absolute(u8 index) const +{ + if (m_reg_is_mmx[index]) [[unlikely]] { + reportln("\033[31;1mWarning! Use of an MMX register as an FPU value ({} abs)\033[0m\n", index); + m_emulator.dump_backtrace(); + } +} +ALWAYS_INLINE void SoftFPU::warn_if_fpu_absolute(u8 index) const +{ + if (!m_reg_is_mmx[index]) [[unlikely]] { + reportln("\033[31;1mWarning! Use of an FPU value ({} abs) as an MMX register\033[0m\n", index); + m_emulator.dump_backtrace(); + } +} + +ALWAYS_INLINE long double SoftFPU::fpu_get(u8 index) const +{ + VERIFY(index < 8); + warn_if_fpu_not_set_absolute(index); + warn_if_mmx_absolute(index); + + u8 effective_index = (m_fpu_stack_top + index) % 8; + + return m_storage[effective_index].fp; +} +ALWAYS_INLINE void SoftFPU::fpu_set_absolute(u8 index, long double value) +{ + VERIFY(index < 8); + set_tag_from_value_absolute(index, value); + m_storage[index].fp = value; + m_reg_is_mmx[index] = false; +} +ALWAYS_INLINE void SoftFPU::fpu_set(u8 index, long double value) +{ + VERIFY(index < 8); + fpu_set_absolute((m_fpu_stack_top + index) % 8, value); +} +ALWAYS_INLINE MMX SoftFPU::mmx_get(u8 index) const +{ + VERIFY(index < 8); + warn_if_fpu_absolute(index); + return m_storage[index].mmx; +} +ALWAYS_INLINE void SoftFPU::mmx_set(u8 index, MMX value) +{ + m_storage[index].mmx = value; + // The high bytes are set to 0b11... to make the floatingpoint value NaN. + // This way we are technically able to find out if we are reading the wrong + // type, but this is still difficult, so we use our own lookup for that + // We set the alignment bytes to all 1's, too, just in case + m_storage[index].__high = ~(decltype(m_storage[index].__high))0u; + m_reg_is_mmx[index] = true; +} + +ALWAYS_INLINE void SoftFPU::fpu_push(long double value) +{ + if (fpu_is_set(7)) + fpu_set_stack_overflow(); + m_fpu_stack_top = (m_fpu_stack_top - 1u) % 8; + + fpu_set(0, value); +} + +ALWAYS_INLINE long double SoftFPU::fpu_pop() +{ + warn_if_mmx_absolute(m_fpu_stack_top); + + if (!fpu_is_set(0)) + fpu_set_stack_underflow(); + + auto ret = fpu_get(0); + fpu_set_tag(0, FPU_Tag::Empty); + m_fpu_stack_top = (m_fpu_stack_top + 1u) % 8; + return ret; +} + +ALWAYS_INLINE void SoftFPU::fpu_set_exception(FPU_Exception ex) +{ + switch (ex) { + case FPU_Exception::StackFault: + m_fpu_error_stackfault = 1; + m_fpu_error_invalid = 1; // Implies InvalidOperation + break; + case FPU_Exception::InvalidOperation: + m_fpu_error_invalid = 1; + if (!m_fpu_mask_invalid) + break; + return; + case FPU_Exception::DenormalizedOperand: + m_fpu_error_denorm = 1; + if (!m_fpu_mask_denorm) + break; + return; + case FPU_Exception::ZeroDivide: + m_fpu_error_zero_div = 1; + if (!m_fpu_mask_zero_div) + break; + return; + case FPU_Exception::Overflow: + m_fpu_error_overflow = 1; + if (!m_fpu_mask_overflow) + break; + return; + case FPU_Exception::Underflow: + m_fpu_error_underflow = 1; + if (!m_fpu_mask_underflow) + break; + return; + case FPU_Exception::Precision: + m_fpu_error_precision = 1; + if (!m_fpu_mask_precision) + break; + return; + } + + // set exception bit + m_fpu_error_summary = 1; + + // FIXME: set traceback + // For that we need to get the currently executing instruction and + // the previous eip + + // FIXME: Call FPU Exception handler + reportln("Trying to call Exception handler from {}", fpu_exception_string(ex)); + fpu_dump_env(); + m_emulator.dump_backtrace(); + TODO(); +} + +template<Arithmetic T> +__attribute__((pure)) ALWAYS_INLINE T SoftFPU::fpu_round(long double value) const +{ + // FIXME: may need to set indefinite values manually + 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::TRUNK: + return static_cast<T>(truncl(value)); + default: + VERIFY_NOT_REACHED(); + } +} + +template<Arithmetic T> +__attribute__((pure)) ALWAYS_INLINE T SoftFPU::fpu_round_checked(long double value) +{ + T result = fpu_round<T>(value); + if (auto rnd = value - result) { + if (rnd > 0) + set_c1(1); + else + set_c1(0); + fpu_set_exception(FPU_Exception::Precision); + } + return result; +} + +template<FloatingPoint T> +__attribute__((pure)) ALWAYS_INLINE T SoftFPU::fpu_convert(long double value) const +{ + // FIXME: actually round the right way + return static_cast<T>(value); +} +template<FloatingPoint T> +__attribute__((pure)) ALWAYS_INLINE T SoftFPU::fpu_convert_checked(long double value) +{ + T result = fpu_convert<T>(value); + if (auto rnd = value - result) { + if (rnd > 0) + set_c1(1); + else + set_c1(0); + fpu_set_exception(FPU_Exception::Precision); + } + return result; +} + +template<Signed R, Signed I> +__attribute__((const)) ALWAYS_INLINE R signed_saturate(I input) +{ + if (input > NumericLimits<R>::max()) + return NumericLimits<R>::max(); + if (input < NumericLimits<R>::min()) + return NumericLimits<R>::min(); + return static_cast<R>(input); +} +template<Unsigned R, Unsigned I> +__attribute__((const)) ALWAYS_INLINE R unsigned_saturate(I input) +{ + if (input > NumericLimits<R>::max()) + return NumericLimits<R>::max(); + return static_cast<R>(input); +} + +}
\ No newline at end of file diff --git a/Userland/DevTools/UserspaceEmulator/SoftFPU.h b/Userland/DevTools/UserspaceEmulator/SoftFPU.h new file mode 100644 index 0000000000..ff4cfd40d8 --- /dev/null +++ b/Userland/DevTools/UserspaceEmulator/SoftFPU.h @@ -0,0 +1,612 @@ +/* + * Copyright (c) 2021, Leon Albrecht <leon2002.la@gmail.com> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include "Report.h" +#include <AK/Concepts.h> +#include <AK/SIMD.h> +#include <LibX86/Instruction.h> +#include <LibX86/Interpreter.h> + +#include <math.h> +#include <string.h> + +namespace UserspaceEmulator { +using namespace AK::SIMD; +class Emulator; +class SoftCPU; + +union MMX { + u64 raw; + c8x8 v8; + i16x4 v16; + i32x2 v32; + i16x4 v16u; + i32x2 v32u; +}; +static_assert(sizeof(MMX) == sizeof(u64)); + +class SoftFPU final { +public: + SoftFPU(Emulator& emulator, SoftCPU& cpu) + : m_emulator(emulator) + , m_cpu(cpu) + { + } + + ALWAYS_INLINE bool c0() const { return m_fpu_c0; } + ALWAYS_INLINE bool c1() const { return m_fpu_c1; } + ALWAYS_INLINE bool c2() const { return m_fpu_c2; } + ALWAYS_INLINE bool c3() const { return m_fpu_c3; } + + ALWAYS_INLINE void set_c0(bool val) { m_fpu_c0 = val; } + ALWAYS_INLINE void set_c1(bool val) { m_fpu_c1 = val; } + ALWAYS_INLINE void set_c2(bool val) { m_fpu_c2 = val; } + ALWAYS_INLINE void set_c3(bool val) { m_fpu_c3 = val; } + + long double fpu_get(u8 index) const; + + void fpu_push(long double value); + long double fpu_pop(); + void fpu_set_absolute(u8 index, long double value); + void fpu_set(u8 index, long double value); + + MMX mmx_get(u8 index) const; + void mmx_set(u8 index, MMX value); + +private: + friend class SoftCPU; + + Emulator& m_emulator; + SoftCPU& m_cpu; + + enum class FPU_Exception : u8 { + InvalidOperation, + DenormalizedOperand, + ZeroDivide, + Overflow, + Underflow, + Precision, + StackFault, + }; + + enum class FPU_Tag : u8 { + Valid = 0b00, + Zero = 0b01, + Special = 0b10, + Empty = 0b11 + }; + + enum class RoundingMode : u8 { + NEAREST = 0b00, + DOWN = 0b01, + UP = 0b10, + TRUNK = 0b11 + }; + + void fpu_dump_env() + { + reportln("Exceptions: #I:{} #D:{} #O:{} #D:{} #U:{} #P:{} #SF:{} Summary:{}", + m_fpu_error_invalid, + m_fpu_error_denorm, + m_fpu_error_zero_div, + m_fpu_error_overflow, + m_fpu_error_underflow, + m_fpu_error_precision, + m_fpu_error_stackfault, + m_fpu_error_summary); + reportln("Masks: #I:{} #D:{} #O:{} #D:{} #U:{} #P:{}", + m_fpu_mask_invalid, + m_fpu_mask_denorm, + m_fpu_mask_zero_div, + m_fpu_mask_overflow, + m_fpu_mask_underflow, + m_fpu_mask_precision); + reportln("C0:{} C1:{} C2:{} C3:{}", c0(), c1(), c2(), c3()); + reportln("fpu-stacktop: {}", m_fpu_stack_top); + reportln("fpu-stack /w stacktop (real):"); + for (u8 i = 0; i < 8; ++i) { + reportln("\t{} ({}): fp {} ({}), mmx (:x016)", i, (u8)((m_fpu_stack_top + i) % 8), m_storage[(m_fpu_stack_top + i) % 8].fp, fpu_is_set(i) ? "set" : "free", m_storage[(m_fpu_stack_top + i) % 8].mmx.raw); + } + } + + String fpu_exception_string(FPU_Exception ex) + { + switch (ex) { + case FPU_Exception::StackFault: + return "Stackfault"; + case FPU_Exception::InvalidOperation: + return "Invalid Operation"; + case FPU_Exception::DenormalizedOperand: + return "Denormalized Operant"; + case FPU_Exception::ZeroDivide: + return "Divide by Zero"; + case FPU_Exception::Overflow: + return "Overflow"; + case FPU_Exception::Underflow: + return "Underflow"; + case FPU_Exception::Precision: + return "Precision"; + } + VERIFY_NOT_REACHED(); + } + + // FIXME: Technically we should check for exceptions after each insn, too, + // this might be important for FLDENV, but otherwise it should + // be fine this way + void fpu_set_exception(FPU_Exception ex); + + ALWAYS_INLINE void fpu_set_stack_overflow() + { + reportln("Stack Overflow"); + set_c1(1); + fpu_set_exception(FPU_Exception::StackFault); + } + + ALWAYS_INLINE void fpu_set_stack_underflow() + { + reportln("Stack Underflow"); + set_c1(0); + fpu_set_exception(FPU_Exception::StackFault); + } + + constexpr FPU_Tag fpu_get_tag_absolute(u8 index) const + { + switch (index) { + case 0: + return FPU_Tag(m_fpu_status_0); + case 1: + return FPU_Tag(m_fpu_status_1); + case 2: + return FPU_Tag(m_fpu_status_2); + case 3: + return FPU_Tag(m_fpu_status_3); + case 4: + return FPU_Tag(m_fpu_status_4); + case 5: + return FPU_Tag(m_fpu_status_5); + case 6: + return FPU_Tag(m_fpu_status_6); + case 7: + return FPU_Tag(m_fpu_status_7); + default: + VERIFY_NOT_REACHED(); + } + } + + constexpr FPU_Tag fpu_get_tag(u8 index) const + { + VERIFY(index < 8); + return fpu_get_tag_absolute((m_fpu_stack_top + index) % 8); + } + + ALWAYS_INLINE void fpu_set_tag_absolute(u8 index, FPU_Tag tag) + { + switch (index) { + case 0: + m_fpu_status_0 = (u8)tag; + break; + case 1: + m_fpu_status_1 = (u8)tag; + break; + case 2: + m_fpu_status_2 = (u8)tag; + break; + case 3: + m_fpu_status_3 = (u8)tag; + break; + case 4: + m_fpu_status_4 = (u8)tag; + break; + case 5: + m_fpu_status_5 = (u8)tag; + break; + case 6: + m_fpu_status_6 = (u8)tag; + break; + case 7: + m_fpu_status_7 = (u8)tag; + break; + default: + VERIFY_NOT_REACHED(); + } + } + + ALWAYS_INLINE void fpu_set_tag(u8 index, FPU_Tag tag) + { + VERIFY(index < 8); + fpu_set_tag_absolute((m_fpu_stack_top + index) % 8, tag); + } + + ALWAYS_INLINE void set_tag_from_value_absolute(u8 index, long double val) + { + switch (fpclassify(val)) { + case FP_ZERO: + fpu_set_tag_absolute(index, FPU_Tag::Zero); + break; + case FP_NAN: + case FP_INFINITE: + case FP_SUBNORMAL: + fpu_set_tag_absolute(index, FPU_Tag::Special); + break; + case FP_NORMAL: + fpu_set_tag_absolute(index, FPU_Tag::Valid); + break; + default: + VERIFY_NOT_REACHED(); + } + } + + ALWAYS_INLINE void set_tag_from_value(u8 index, long double val) + { + set_tag_from_value_absolute((m_fpu_stack_top + index) % 8, val); + } + + ALWAYS_INLINE bool fpu_isnan(u8 index) const + { + return isnan(fpu_get(index)); + } + + ALWAYS_INLINE bool fpu_is_set(u8 index) const + { + return fpu_get_tag_absolute((m_fpu_stack_top + index) % 8) != FPU_Tag::Empty; + } + + ALWAYS_INLINE RoundingMode fpu_get_round_mode() const + { + return RoundingMode(m_fpu_round_mode); + } + + template<Arithmetic T> + T fpu_round(long double) const; + template<Arithmetic T> + T fpu_round_checked(long double); + + template<FloatingPoint T> + T fpu_convert(long double) const; + template<FloatingPoint T> + T fpu_convert_checked(long double); + + ALWAYS_INLINE void fpu_set_unordered() + { + set_c0(1); + set_c2(1); + set_c3(1); + } + void warn_if_mmx_absolute(u8 index) const; + void warn_if_fpu_absolute(u8 index) const; + void warn_if_fpu_not_set_absolute(u8 index) const; + + void mmx_common() { m_fpu_tw = 0; } + + bool m_reg_is_mmx[8] { false }; + + union { + long double fp; + struct { + MMX mmx; + Conditional<sizeof(long double) == 16, + u64, + Conditional<sizeof(long double) == 12, + u32, + u16>> + __high; + }; + } m_storage[8]; + + union { + 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 { + u16 m_fpu_sw { 0 }; + struct { + u16 m_fpu_error_invalid : 1; // pre | IE -> #I (#IS, #IA) + u16 m_fpu_error_denorm : 1; // pre | DE -> #D + u16 m_fpu_error_zero_div : 1; // pre | ZE -> #Z + u16 m_fpu_error_overflow : 1; // post| OE -> #O + u16 m_fpu_error_underflow : 1; // post| UE -> #U + u16 m_fpu_error_precision : 1; // post| PE -> #P + u16 m_fpu_error_stackfault : 1; // SF + u16 m_fpu_error_summary : 1; + u16 m_fpu_c0 : 1; + u16 m_fpu_c1 : 1; + u16 m_fpu_c2 : 1; + u16 m_fpu_stack_top : 3; + u16 m_fpu_c3 : 1; + u16 m_fpu_busy : 1; + }; + }; + + union { + u16 m_fpu_tw { 0xFFFF }; + struct { + u16 m_fpu_status_0 : 2; + u16 m_fpu_status_1 : 2; + u16 m_fpu_status_2 : 2; + u16 m_fpu_status_3 : 2; + u16 m_fpu_status_4 : 2; + u16 m_fpu_status_5 : 2; + u16 m_fpu_status_6 : 2; + u16 m_fpu_status_7 : 2; + }; + }; + + u32 m_fpu_ip { 0 }; + u16 m_fpu_cs { 0 }; + + u32 m_fpu_dp { 0 }; + u16 m_fpu_ds { 0 }; + + u16 m_fpu_iop { 0 }; + + // Instructions + + // DATA TRANSFER + void FLD_RM32(const X86::Instruction&); + void FLD_RM64(const X86::Instruction&); + void FLD_RM80(const X86::Instruction&); + + void FST_RM32(const X86::Instruction&); + void FST_RM64(const X86::Instruction&); + void FSTP_RM32(const X86::Instruction&); + void FSTP_RM64(const X86::Instruction&); + void FSTP_RM80(const X86::Instruction&); + + void FILD_RM32(const X86::Instruction&); + void FILD_RM16(const X86::Instruction&); + void FILD_RM64(const X86::Instruction&); + + void FIST_RM16(const X86::Instruction&); + void FIST_RM32(const X86::Instruction&); + void FISTP_RM16(const X86::Instruction&); + void FISTP_RM32(const X86::Instruction&); + void FISTP_RM64(const X86::Instruction&); + void FISTTP_RM16(const X86::Instruction&); + void FISTTP_RM32(const X86::Instruction&); + void FISTTP_RM64(const X86::Instruction&); + + void FBLD_M80(const X86::Instruction&); + void FBSTP_M80(const X86::Instruction&); + + void FXCH(const X86::Instruction&); + + void FCMOVE(const X86::Instruction&); + void FCMOVNE(const X86::Instruction&); + void FCMOVB(const X86::Instruction&); + void FCMOVBE(const X86::Instruction&); + void FCMOVNB(const X86::Instruction&); + void FCMOVNBE(const X86::Instruction&); + void FCMOVU(const X86::Instruction&); + void FCMOVNU(const X86::Instruction&); + + // BASIC ARITHMETIC + void FADD_RM32(const X86::Instruction&); + void FADD_RM64(const X86::Instruction&); + void FADDP(const X86::Instruction&); + + void FIADD_RM16(const X86::Instruction&); + void FIADD_RM32(const X86::Instruction&); + + void FSUB_RM32(const X86::Instruction&); + void FSUB_RM64(const X86::Instruction&); + void FSUBP(const X86::Instruction&); + void FSUBR_RM32(const X86::Instruction&); + void FSUBR_RM64(const X86::Instruction&); + void FSUBRP(const X86::Instruction&); + + void FISUB_RM16(const X86::Instruction&); + void FISUB_RM32(const X86::Instruction&); + void FISUBR_RM16(const X86::Instruction&); + void FISUBR_RM32(const X86::Instruction&); + + void FMUL_RM32(const X86::Instruction&); + void FMUL_RM64(const X86::Instruction&); + void FMULP(const X86::Instruction&); + + void FIMUL_RM16(const X86::Instruction&); + void FIMUL_RM32(const X86::Instruction&); + + void FDIV_RM32(const X86::Instruction&); + void FDIV_RM64(const X86::Instruction&); + void FDIVP(const X86::Instruction&); + void FDIVR_RM32(const X86::Instruction&); + void FDIVR_RM64(const X86::Instruction&); + void FDIVRP(const X86::Instruction&); + + void FIDIV_RM16(const X86::Instruction&); + void FIDIV_RM32(const X86::Instruction&); + void FIDIVR_RM16(const X86::Instruction&); + void FIDIVR_RM32(const X86::Instruction&); + + void FPREM(const X86::Instruction&); + void FPREM1(const X86::Instruction&); + + void FABS(const X86::Instruction&); + void FCHS(const X86::Instruction&); + + void FRNDINT(const X86::Instruction&); + + void FSCALE(const X86::Instruction&); + + void FSQRT(const X86::Instruction&); + + void FXTRACT(const X86::Instruction&); + + // COMPARISON + void FCOM_RM32(const X86::Instruction&); + void FCOM_RM64(const X86::Instruction&); + void FCOMP_RM32(const X86::Instruction&); + void FCOMP_RM64(const X86::Instruction&); + void FCOMPP(const X86::Instruction&); + void FCOMI(const X86::Instruction&); + void FCOMIP(const X86::Instruction&); + + void FUCOM(const X86::Instruction&); + void FUCOMP(const X86::Instruction&); + void FUCOMPP(const X86::Instruction&); + void FUCOMI(const X86::Instruction&); + void FUCOMIP(const X86::Instruction&); + + void FICOM_RM16(const X86::Instruction&); + void FICOM_RM32(const X86::Instruction&); + void FICOMP_RM16(const X86::Instruction&); + void FICOMP_RM32(const X86::Instruction&); + + void FTST(const X86::Instruction&); + void FXAM(const X86::Instruction&); + + // TRANSCENDENTAL + void FSIN(const X86::Instruction&); + void FCOS(const X86::Instruction&); + void FSINCOS(const X86::Instruction&); + void FPTAN(const X86::Instruction&); + void FPATAN(const X86::Instruction&); + + void F2XM1(const X86::Instruction&); + void FYL2X(const X86::Instruction&); + void FYL2XP1(const X86::Instruction&); + + // CONSTANT LOAD + void FLD1(const X86::Instruction&); + void FLDZ(const X86::Instruction&); + void FLDPI(const X86::Instruction&); + void FLDL2E(const X86::Instruction&); + void FLDLN2(const X86::Instruction&); + void FLDL2T(const X86::Instruction&); + void FLDLG2(const X86::Instruction&); + + // CONTROL + void FINCSTP(const X86::Instruction&); + void FDECSTP(const X86::Instruction&); + void FFREE(const X86::Instruction&); + void FFREEP(const X86::Instruction&); // undocumented + + // FIXME: Non N- versions? + void FNINIT(const X86::Instruction&); + void FNCLEX(const X86::Instruction&); + + void FNSTCW(const X86::Instruction&); + void FLDCW(const X86::Instruction&); + + void FNSTENV(const X86::Instruction&); + void FLDENV(const X86::Instruction&); + + void FNSAVE(const X86::Instruction&); + void FRSTOR(const X86::Instruction&); + + void FNSTSW(const X86::Instruction&); + void FNSTSW_AX(const X86::Instruction&); + + // FIXME: WAIT && FWAIT + void FNOP(const X86::Instruction&); + + // FPU & SIMD MANAGEMENT + // FIXME: FXSAVE && FXRSTOR + + // DO NOTHING? + // FIXME: FENI, FDISI, FSETPM + void FNENI(const X86::Instruction&); + void FNDISI(const X86::Instruction&); + void FNSETPM(const X86::Instruction&); + + // MMX + // ARITHMETIC + void PADDB_mm1_mm2m64(const X86::Instruction&); + void PADDW_mm1_mm2m64(const X86::Instruction&); + void PADDD_mm1_mm2m64(const X86::Instruction&); + void PADDSB_mm1_mm2m64(const X86::Instruction&); + void PADDSW_mm1_mm2m64(const X86::Instruction&); + void PADDUSB_mm1_mm2m64(const X86::Instruction&); + void PADDUSW_mm1_mm2m64(const X86::Instruction&); + + void PSUBB_mm1_mm2m64(const X86::Instruction&); + void PSUBW_mm1_mm2m64(const X86::Instruction&); + void PSUBD_mm1_mm2m64(const X86::Instruction&); + void PSUBSB_mm1_mm2m64(const X86::Instruction&); + void PSUBSW_mm1_mm2m64(const X86::Instruction&); + void PSUBUSB_mm1_mm2m64(const X86::Instruction&); + void PSUBUSW_mm1_mm2m64(const X86::Instruction&); + + void PMULHW_mm1_mm2m64(const X86::Instruction&); + void PMULLW_mm1_mm2m64(const X86::Instruction&); + + void PMADDWD_mm1_mm2m64(const X86::Instruction&); + + // COMPARISON + void PCMPEQB_mm1_mm2m64(const X86::Instruction&); + void PCMPEQW_mm1_mm2m64(const X86::Instruction&); + void PCMPEQD_mm1_mm2m64(const X86::Instruction&); + + void PCMPGTB_mm1_mm2m64(const X86::Instruction&); + void PCMPGTW_mm1_mm2m64(const X86::Instruction&); + void PCMPGTD_mm1_mm2m64(const X86::Instruction&); + + // CONVERSION + void PACKSSDW_mm1_mm2m64(const X86::Instruction&); + void PACKSSWB_mm1_mm2m64(const X86::Instruction&); + void PACKUSWB_mm1_mm2m64(const X86::Instruction&); + + // UNPACK + void PUNPCKHBW_mm1_mm2m64(const X86::Instruction&); + void PUNPCKHWD_mm1_mm2m64(const X86::Instruction&); + void PUNPCKHDQ_mm1_mm2m64(const X86::Instruction&); + void PUNPCKLBW_mm1_mm2m32(const X86::Instruction&); + void PUNPCKLWD_mm1_mm2m32(const X86::Instruction&); + void PUNPCKLDQ_mm1_mm2m32(const X86::Instruction&); + + // LOGICAL + void PAND_mm1_mm2m64(const X86::Instruction&); + void PANDN_mm1_mm2m64(const X86::Instruction&); + void POR_mm1_mm2m64(const X86::Instruction&); + void PXOR_mm1_mm2m64(const X86::Instruction&); + + // SHIFT + void PSLLW_mm1_mm2m64(const X86::Instruction&); + void PSLLW_mm1_imm8(const X86::Instruction&); + void PSLLD_mm1_mm2m64(const X86::Instruction&); + void PSLLD_mm1_imm8(const X86::Instruction&); + void PSLLQ_mm1_mm2m64(const X86::Instruction&); + void PSLLQ_mm1_imm8(const X86::Instruction&); + void PSRAW_mm1_mm2m64(const X86::Instruction&); + void PSRAW_mm1_imm8(const X86::Instruction&); + void PSRAD_mm1_mm2m64(const X86::Instruction&); + void PSRAD_mm1_imm8(const X86::Instruction&); + void PSRLW_mm1_mm2m64(const X86::Instruction&); + void PSRLW_mm1_imm8(const X86::Instruction&); + void PSRLD_mm1_mm2m64(const X86::Instruction&); + void PSRLD_mm1_imm8(const X86::Instruction&); + void PSRLQ_mm1_mm2m64(const X86::Instruction&); + void PSRLQ_mm1_imm8(const X86::Instruction&); + + // DATA TRANSFER + void MOVD_mm1_rm32(const X86::Instruction&); + void MOVD_rm32_mm2(const X86::Instruction&); + + void MOVQ_mm1_mm2m64(const X86::Instruction&); + void MOVQ_mm1m64_mm2(const X86::Instruction&); + void MOVQ_mm1_rm64(const X86::Instruction&); // long mode + void MOVQ_rm64_mm2(const X86::Instruction&); // long mode + + // EMPTY MMX STATE + void EMMS(const X86::Instruction&); +}; + +} |