LibX86: Disassemble most FPU instructions starting with D9

Some of these don't just use the REG bits of the mod/rm byte
as slashes, but also the R/M bits to have up to 9 different
instructions per opcode/slash combination (1 opcode requires
that MOD is != 11, the other 8 have MODE == 11).

This is done by making the slashes table two levels deep for
these cases.

Some of this is cosmetic (e.g "FST st0" has no effect already,
but its bit pattern gets disassembled as "FNOP"), but for
most uses it isn't.

FSTENV and FSTCW have an extraordinary 0x9b prefix. This is
not yet handled in this patch.
This commit is contained in:
Nico Weber 2020-07-28 11:09:54 -04:00 committed by Andreas Kling
parent 6f12ab3ced
commit c99a3efc5b
Notes: sideshowbarker 2024-07-19 04:30:35 +09:00
5 changed files with 209 additions and 13 deletions

View file

@ -1379,6 +1379,42 @@ void SoftCPU::FSUB_RM32(const X86::Instruction&) { TODO(); }
void SoftCPU::FSUBR_RM32(const X86::Instruction&) { TODO(); }
void SoftCPU::FDIV_RM32(const X86::Instruction&) { TODO(); }
void SoftCPU::FDIVR_RM32(const X86::Instruction&) { TODO(); }
void SoftCPU::FLD_RM32(const X86::Instruction&) { TODO(); }
void SoftCPU::FXCH(const X86::Instruction&) { TODO(); }
void SoftCPU::FST_RM32(const X86::Instruction&) { TODO(); }
void SoftCPU::FNOP(const X86::Instruction&) { TODO(); }
void SoftCPU::FSTP_RM32(const X86::Instruction&) { TODO(); }
void SoftCPU::FLDENV(const X86::Instruction&) { TODO(); }
void SoftCPU::FCHS(const X86::Instruction&) { TODO(); }
void SoftCPU::FABS(const X86::Instruction&) { TODO(); }
void SoftCPU::FTST(const X86::Instruction&) { TODO(); }
void SoftCPU::FXAM(const X86::Instruction&) { TODO(); }
void SoftCPU::FLDCW(const X86::Instruction&) { TODO(); }
void SoftCPU::FLD1(const X86::Instruction&) { TODO(); }
void SoftCPU::FLDL2T(const X86::Instruction&) { TODO(); }
void SoftCPU::FLDL2E(const X86::Instruction&) { TODO(); }
void SoftCPU::FLDPI(const X86::Instruction&) { TODO(); }
void SoftCPU::FLDLG2(const X86::Instruction&) { TODO(); }
void SoftCPU::FLDLN2(const X86::Instruction&) { TODO(); }
void SoftCPU::FLDZ(const X86::Instruction&) { TODO(); }
void SoftCPU::FNSTENV(const X86::Instruction&) { TODO(); }
void SoftCPU::F2XM1(const X86::Instruction&) { TODO(); };
void SoftCPU::FYL2X(const X86::Instruction&) { TODO(); };
void SoftCPU::FPTAN(const X86::Instruction&) { TODO(); };
void SoftCPU::FPATAN(const X86::Instruction&) { TODO(); };
void SoftCPU::FXTRACT(const X86::Instruction&) { TODO(); };
void SoftCPU::FPREM1(const X86::Instruction&) { TODO(); };
void SoftCPU::FDECSTP(const X86::Instruction&) { TODO(); };
void SoftCPU::FINCSTP(const X86::Instruction&) { TODO(); };
void SoftCPU::FNSTCW(const X86::Instruction&) { TODO(); };
void SoftCPU::FPREM(const X86::Instruction&) { TODO(); };
void SoftCPU::FYL2XP1(const X86::Instruction&) { TODO(); };
void SoftCPU::FSQRT(const X86::Instruction&) { TODO(); };
void SoftCPU::FSINCOS(const X86::Instruction&) { TODO(); };
void SoftCPU::FRNDINT(const X86::Instruction&) { TODO(); };
void SoftCPU::FSCALE(const X86::Instruction&) { TODO(); };
void SoftCPU::FSIN(const X86::Instruction&) { TODO(); };
void SoftCPU::FCOS(const X86::Instruction&) { TODO(); };
void SoftCPU::FADD_RM64(const X86::Instruction&) { TODO(); }
void SoftCPU::FMUL_RM64(const X86::Instruction&) { TODO(); }
void SoftCPU::FCOM_RM64(const X86::Instruction&) { TODO(); }

View file

@ -582,6 +582,42 @@ private:
virtual void FSUBR_RM32(const X86::Instruction&) override;
virtual void FDIV_RM32(const X86::Instruction&) override;
virtual void FDIVR_RM32(const X86::Instruction&) override;
virtual void FLD_RM32(const X86::Instruction&) override;
virtual void FXCH(const X86::Instruction&) override;
virtual void FST_RM32(const X86::Instruction&) override;
virtual void FNOP(const X86::Instruction&) override;
virtual void FSTP_RM32(const X86::Instruction&) override;
virtual void FLDENV(const X86::Instruction&) override;
virtual void FCHS(const X86::Instruction&) override;
virtual void FABS(const X86::Instruction&) override;
virtual void FTST(const X86::Instruction&) override;
virtual void FXAM(const X86::Instruction&) override;
virtual void FLDCW(const X86::Instruction&) override;
virtual void FLD1(const X86::Instruction&) override;
virtual void FLDL2T(const X86::Instruction&) override;
virtual void FLDL2E(const X86::Instruction&) override;
virtual void FLDPI(const X86::Instruction&) override;
virtual void FLDLG2(const X86::Instruction&) override;
virtual void FLDLN2(const X86::Instruction&) override;
virtual void FLDZ(const X86::Instruction&) override;
virtual void FNSTENV(const X86::Instruction&) override;
virtual void F2XM1(const X86::Instruction&) override;
virtual void FYL2X(const X86::Instruction&) override;
virtual void FPTAN(const X86::Instruction&) override;
virtual void FPATAN(const X86::Instruction&) override;
virtual void FXTRACT(const X86::Instruction&) override;
virtual void FPREM1(const X86::Instruction&) override;
virtual void FDECSTP(const X86::Instruction&) override;
virtual void FINCSTP(const X86::Instruction&) override;
virtual void FNSTCW(const X86::Instruction&) override;
virtual void FPREM(const X86::Instruction&) override;
virtual void FYL2XP1(const X86::Instruction&) override;
virtual void FSQRT(const X86::Instruction&) override;
virtual void FSINCOS(const X86::Instruction&) override;
virtual void FRNDINT(const X86::Instruction&) override;
virtual void FSCALE(const X86::Instruction&) override;
virtual void FSIN(const X86::Instruction&) override;
virtual void FCOS(const X86::Instruction&) override;
virtual void FADD_RM64(const X86::Instruction&) override;
virtual void FMUL_RM64(const X86::Instruction&) override;
virtual void FCOM_RM64(const X86::Instruction&) override;

View file

@ -124,7 +124,6 @@ static void build(InstructionDescriptor* table, u8 op, const char* mnemonic, Ins
//default:
case InvalidFormat:
case MultibyteWithSlash:
case MultibyteWithSubopcode:
case InstructionPrefix:
case __BeginFormatsWithRMByte:
case OP_RM16_reg16:
@ -135,6 +134,8 @@ static void build(InstructionDescriptor* table, u8 op, const char* mnemonic, Ins
case OP_RM8:
case OP_RM16:
case OP_RM32:
case OP_FPU:
case OP_FPU_reg:
case OP_FPU_RM32:
case OP_FPU_RM64:
case OP_RM8_reg8:
@ -190,7 +191,7 @@ static void build(InstructionDescriptor* table, u8 op, const char* mnemonic, Ins
static void build_slash(InstructionDescriptor* table, u8 op, u8 slash, const char* mnemonic, InstructionFormat format, InstructionHandler handler, IsLockPrefixAllowed lock_prefix_allowed = LockPrefixNotAllowed)
{
InstructionDescriptor& d = table[op];
d.handler = handler;
ASSERT(d.handler == nullptr);
d.format = MultibyteWithSlash;
d.has_rm = true;
if (!d.slashes)
@ -199,6 +200,27 @@ static void build_slash(InstructionDescriptor* table, u8 op, u8 slash, const cha
build(d.slashes, slash, mnemonic, format, handler, lock_prefix_allowed);
}
static void build_slash_rm(InstructionDescriptor* table, u8 op, u8 slash, u8 rm, const char* mnemonic, InstructionFormat format, InstructionHandler handler, IsLockPrefixAllowed lock_prefix_allowed = LockPrefixNotAllowed)
{
ASSERT((rm & 0xc0) == 0xc0);
ASSERT(((rm >> 3) & 7) == slash);
InstructionDescriptor& d0 = table[op];
ASSERT(d0.format == MultibyteWithSlash);
InstructionDescriptor& d = d0.slashes[slash];
if (!d.slashes) {
// Slash/RM instructions are not always dense, so make them all default to the slash instruction.
d.slashes = new InstructionDescriptor[8];
for (int i = 0; i < 8; ++i) {
d.slashes[i] = d;
d.slashes[i].slashes = nullptr;
}
}
build(d.slashes, rm & 7, mnemonic, format, handler, lock_prefix_allowed);
}
static void build_0f(u8 op, const char* mnemonic, InstructionFormat format, InstructionHandler impl, IsLockPrefixAllowed lock_prefix_allowed = LockPrefixNotAllowed)
{
build(s_0f_table16, op, mnemonic, format, impl, lock_prefix_allowed);
@ -259,6 +281,12 @@ static void build_0f_slash(u8 op, u8 slash, const char* mnemonic, InstructionFor
build_slash(s_0f_table32, op, slash, mnemonic, format, impl, lock_prefix_allowed);
}
static void build_slash_rm(u8 op, u8 slash, u8 rm, const char* mnemonic, InstructionFormat format, InstructionHandler impl, IsLockPrefixAllowed lock_prefix_allowed = LockPrefixNotAllowed)
{
build_slash_rm(s_table16, op, slash, rm, mnemonic, format, impl, lock_prefix_allowed);
build_slash_rm(s_table32, op, slash, rm, mnemonic, format, impl, lock_prefix_allowed);
}
[[gnu::constructor]] static void build_opcode_tables()
{
build(0x00, "ADD", OP_RM8_reg8, &Interpreter::ADD_RM8_reg8, LockPrefixAllowed);
@ -448,9 +476,49 @@ static void build_0f_slash(u8 op, u8 slash, const char* mnemonic, InstructionFor
build_slash(0xD8, 6, "FDIV", OP_FPU_RM32, &Interpreter::FDIV_RM32);
build_slash(0xD8, 7, "FDIVR", OP_FPU_RM32, &Interpreter::FDIVR_RM32);
build_slash(0xD9, 0, "FLD", OP_FPU_RM32, &Interpreter::FLD_RM32);
build_slash(0xD9, 1, "FXCH", OP_FPU_reg, &Interpreter::FXCH);
// FIXME: D9/1 C9 (...but isn't this what D9/1 does naturally, with C9 just being normal R/M?)
build_slash(0xD9, 2, "FST", OP_FPU_RM32, &Interpreter::FST_RM32);
build_slash_rm(0xD9, 2, 0xD0, "FNOP", OP_FPU, &Interpreter::FNOP);
build_slash(0xD9, 3, "FSTP", OP_FPU_RM32, &Interpreter::FSTP_RM32);
build_slash(0xD9, 4, "FLDENV", OP_FPU_RM32, &Interpreter::FLDENV);
build_slash_rm(0xD9, 4, 0xE0, "FCHS", OP_FPU, &Interpreter::FCHS);
build_slash_rm(0xD9, 4, 0xE1, "FABS", OP_FPU, &Interpreter::FABS);
build_slash_rm(0xD9, 4, 0xE2, "FTST", OP_FPU, &Interpreter::FTST);
build_slash_rm(0xD9, 4, 0xE3, "FXAM", OP_FPU, &Interpreter::FXAM);
build_slash(0xD9, 5, "FLDCW", OP_FPU_RM32, &Interpreter::FLDCW);
build_slash_rm(0xD9, 5, 0xE8, "FLD1", OP_FPU, &Interpreter::FLD1);
build_slash_rm(0xD9, 5, 0xE9, "FLDL2T", OP_FPU, &Interpreter::FLDL2T);
build_slash_rm(0xD9, 5, 0xEA, "FLDL2E", OP_FPU, &Interpreter::FLDL2E);
build_slash_rm(0xD9, 5, 0xEB, "FLDPI", OP_FPU, &Interpreter::FLDPI);
build_slash_rm(0xD9, 5, 0xEC, "FLDLG2", OP_FPU, &Interpreter::FLDLG2);
build_slash_rm(0xD9, 5, 0xED, "FLDLN2", OP_FPU, &Interpreter::FLDLN2);
build_slash_rm(0xD9, 5, 0xEE, "FLDZ", OP_FPU, &Interpreter::FLDZ);
build_slash(0xD9, 6, "FNSTENV", OP_FPU_RM32, &Interpreter::FNSTENV);
// FIXME: Extraodinary prefix 0x9B + 0xD9/6: FSTENV
build_slash_rm(0xD9, 6, 0xF0, "F2XM1", OP_FPU, &Interpreter::F2XM1);
build_slash_rm(0xD9, 6, 0xF1, "FYL2X", OP_FPU, &Interpreter::FYL2X);
build_slash_rm(0xD9, 6, 0xF2, "FPTAN", OP_FPU, &Interpreter::FPTAN);
build_slash_rm(0xD9, 6, 0xF3, "FPATAN", OP_FPU, &Interpreter::FPATAN);
build_slash_rm(0xD9, 6, 0xF4, "FXTRACT", OP_FPU, &Interpreter::FXTRACT);
build_slash_rm(0xD9, 6, 0xF5, "FPREM1", OP_FPU, &Interpreter::FPREM1);
build_slash_rm(0xD9, 6, 0xF6, "FDECSTP", OP_FPU, &Interpreter::FDECSTP);
build_slash_rm(0xD9, 6, 0xF7, "FINCSTP", OP_FPU, &Interpreter::FINCSTP);
build_slash(0xD9, 7, "FNSTCW", OP_FPU_RM32, &Interpreter::FNSTCW);
// FIXME: Extraodinary prefix 0x9B + 0xD9/7: FSTCW
build_slash_rm(0xD9, 7, 0xF8, "FPREM", OP_FPU, &Interpreter::FPREM);
build_slash_rm(0xD9, 7, 0xF9, "FYL2XP1", OP_FPU, &Interpreter::FYL2XP1);
build_slash_rm(0xD9, 7, 0xFA, "FSQRT", OP_FPU, &Interpreter::FSQRT);
build_slash_rm(0xD9, 7, 0xFB, "FSINCOS", OP_FPU, &Interpreter::FSINCOS);
build_slash_rm(0xD9, 7, 0xFC, "FRNDINT", OP_FPU, &Interpreter::FRNDINT);
build_slash_rm(0xD9, 7, 0xFD, "FSCALE", OP_FPU, &Interpreter::FSCALE);
build_slash_rm(0xD9, 7, 0xFE, "FSIN", OP_FPU, &Interpreter::FSIN);
build_slash_rm(0xD9, 7, 0xFF, "FCOS", OP_FPU, &Interpreter::FCOS);
// FIXME
for (u8 i = 0; i <= 3; ++i)
build(0xD9 + i, "FPU?", OP_RM8, &Interpreter::ESCAPE);
build(0xDA, "FPU?", OP_RM8, &Interpreter::ESCAPE);
build(0xDB, "FPU?", OP_RM8, &Interpreter::ESCAPE);
build_slash(0xDC, 0, "FADD", OP_FPU_RM64, &Interpreter::FADD_RM64);
build_slash(0xDC, 1, "FMUL", OP_FPU_RM64, &Interpreter::FMUL_RM64);
@ -462,8 +530,9 @@ static void build_0f_slash(u8 op, u8 slash, const char* mnemonic, InstructionFor
build_slash(0xDC, 7, "FDIVR", OP_FPU_RM64, &Interpreter::FDIVR_RM64);
// FIXME
for (u8 i = 0; i <= 2; ++i)
build(0xDD + i, "FPU?", OP_RM8, &Interpreter::ESCAPE);
build(0xDD, "FPU?", OP_RM8, &Interpreter::ESCAPE);
build(0xDE, "FPU?", OP_RM8, &Interpreter::ESCAPE);
build(0xDF, "FPU?", OP_RM8, &Interpreter::ESCAPE);
build(0xE0, "LOOPNZ", OP_imm8, &Interpreter::LOOPNZ_imm8);
build(0xE1, "LOOPZ", OP_imm8, &Interpreter::LOOPZ_imm8);
@ -774,6 +843,12 @@ String MemoryOrRegisterReference::to_string_o32(const Instruction& insn) const
return String::format("[%s]", to_string(insn).characters());
}
String MemoryOrRegisterReference::to_string_fpu_reg() const
{
ASSERT(is_register());
return register_name(reg_fpu());
}
String MemoryOrRegisterReference::to_string_fpu32(const Instruction& insn) const
{
if (is_register())
@ -1077,6 +1152,7 @@ String Instruction::to_string_internal(u32 origin, const SymbolProvider* symbol_
auto append_rm8 = [&] { builder.append(m_modrm.to_string_o8(*this)); };
auto append_rm16 = [&] { builder.append(m_modrm.to_string_o16(*this)); };
auto append_rm32 = [&] { builder.append(m_modrm.to_string_o32(*this)); };
auto append_fpu_reg = [&] { builder.append(m_modrm.to_string_fpu_reg()); };
auto append_fpu_rm32 = [&] { builder.append(m_modrm.to_string_fpu32(*this)); };
auto append_fpu_rm64 = [&] { builder.append(m_modrm.to_string_fpu64(*this)); };
auto append_imm8 = [&] { builder.appendf("%#02x", imm8()); };
@ -1342,6 +1418,11 @@ String Instruction::to_string_internal(u32 origin, const SymbolProvider* symbol_
case OP_RM32:
append_rm32();
break;
case OP_FPU:
break;
case OP_FPU_reg:
append_fpu_reg();
break;
case OP_FPU_RM32:
append_fpu_rm32();
break;
@ -1515,7 +1596,6 @@ String Instruction::to_string_internal(u32 origin, const SymbolProvider* symbol_
return mnemonic;
case InvalidFormat:
case MultibyteWithSlash:
case MultibyteWithSubopcode:
case __BeginFormatsWithRMByte:
case __EndFormatsWithRMByte:
return String::format("(!%s)", mnemonic.characters());

View file

@ -66,7 +66,6 @@ enum IsLockPrefixAllowed {
enum InstructionFormat {
InvalidFormat,
MultibyteWithSlash,
MultibyteWithSubopcode,
InstructionPrefix,
__BeginFormatsWithRMByte,
@ -82,6 +81,8 @@ enum InstructionFormat {
OP_RM8,
OP_RM16,
OP_RM32,
OP_FPU,
OP_FPU_reg,
OP_FPU_RM32,
OP_FPU_RM64,
OP_RM8_reg8,
@ -179,6 +180,11 @@ struct InstructionDescriptor {
bool has_rm { false };
unsigned imm1_bytes { 0 };
unsigned imm2_bytes { 0 };
// Addressed by the 3 REG bits in the MOD-REG-R/M byte.
// Some slash instructions have further subgroups when MOD is 11,
// in that case the InstructionDescriptors in slashes have themselves
// a non-null slashes member that's indexed by the three R/M bits.
InstructionDescriptor* slashes { nullptr };
unsigned imm1_bytes_for_address_size(bool a32)
@ -352,6 +358,7 @@ public:
String to_string_o8(const Instruction&) const;
String to_string_o16(const Instruction&) const;
String to_string_o32(const Instruction&) const;
String to_string_fpu_reg() const;
String to_string_fpu32(const Instruction&) const;
String to_string_fpu64(const Instruction&) const;
String to_string_mm(const Instruction&) const;
@ -864,20 +871,21 @@ ALWAYS_INLINE Instruction::Instruction(InstructionStreamType& stream, bool o32,
m_register_index = m_op & 7;
}
bool hasSlash = m_descriptor->format == MultibyteWithSlash;
if (hasSlash) {
bool has_slash = m_descriptor->format == MultibyteWithSlash;
if (has_slash) {
m_descriptor = &m_descriptor->slashes[slash()];
if ((rm() & 0xc0) == 0xc0 && m_descriptor->slashes)
m_descriptor = &m_descriptor->slashes[rm() & 7];
}
if (!m_descriptor->mnemonic) {
if (has_sub_op()) {
if (hasSlash)
if (has_slash)
fprintf(stderr, "Instruction %02X %02X /%u not understood\n", m_op, m_sub_op, slash());
else
fprintf(stderr, "Instruction %02X %02X not understood\n", m_op, m_sub_op);
} else {
if (hasSlash)
if (has_slash)
fprintf(stderr, "Instruction %02X /%u not understood\n", m_op, slash());
else
fprintf(stderr, "Instruction %02X not understood\n", m_op);

View file

@ -164,6 +164,42 @@ public:
virtual void FSUBR_RM32(const Instruction&) = 0;
virtual void FDIV_RM32(const Instruction&) = 0;
virtual void FDIVR_RM32(const Instruction&) = 0;
virtual void FLD_RM32(const Instruction&) = 0;
virtual void FXCH(const Instruction&) = 0;
virtual void FST_RM32(const Instruction&) = 0;
virtual void FNOP(const Instruction&) = 0;
virtual void FSTP_RM32(const Instruction&) = 0;
virtual void FLDENV(const Instruction&) = 0;
virtual void FCHS(const Instruction&) = 0;
virtual void FABS(const Instruction&) = 0;
virtual void FTST(const Instruction&) = 0;
virtual void FXAM(const Instruction&) = 0;
virtual void FLDCW(const Instruction&) = 0;
virtual void FLD1(const Instruction&) = 0;
virtual void FLDL2T(const Instruction&) = 0;
virtual void FLDL2E(const Instruction&) = 0;
virtual void FLDPI(const Instruction&) = 0;
virtual void FLDLG2(const Instruction&) = 0;
virtual void FLDLN2(const Instruction&) = 0;
virtual void FLDZ(const Instruction&) = 0;
virtual void FNSTENV(const Instruction&) = 0;
virtual void F2XM1(const Instruction&) = 0;
virtual void FYL2X(const Instruction&) = 0;
virtual void FPTAN(const Instruction&) = 0;
virtual void FPATAN(const Instruction&) = 0;
virtual void FXTRACT(const Instruction&) = 0;
virtual void FPREM1(const Instruction&) = 0;
virtual void FDECSTP(const Instruction&) = 0;
virtual void FINCSTP(const Instruction&) = 0;
virtual void FNSTCW(const Instruction&) = 0;
virtual void FPREM(const Instruction&) = 0;
virtual void FYL2XP1(const Instruction&) = 0;
virtual void FSQRT(const Instruction&) = 0;
virtual void FSINCOS(const Instruction&) = 0;
virtual void FRNDINT(const Instruction&) = 0;
virtual void FSCALE(const Instruction&) = 0;
virtual void FSIN(const Instruction&) = 0;
virtual void FCOS(const Instruction&) = 0;
virtual void FADD_RM64(const Instruction&) = 0;
virtual void FMUL_RM64(const Instruction&) = 0;
virtual void FCOM_RM64(const Instruction&) = 0;