123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609 |
- /*
- * 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;
- u16x4 v16u;
- u32x2 v32u;
- };
- static_assert(AssertSize<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);
- 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,
- TRUNC = 0b11
- };
- void fpu_dump_env()
- {
- reportln("Exceptions: #I:{} #D:{} #Z:{} #O:{} #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:{} #Z:{} #O:{} #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 {:016x}",
- 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 Operand";
- 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)
- {
- 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 mmx_common() { m_fpu_tw = 0; }
- bool m_reg_is_mmx[8] { false };
- union {
- long double fp;
- struct {
- MMX mmx;
- 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&);
- };
- }
|