diff options
author | Andreas Kling <kling@serenityos.org> | 2020-07-10 16:23:07 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-07-10 20:20:27 +0200 |
commit | 45bfdd0063ebbd72a39df4e756147056002f5568 (patch) | |
tree | 8f05000374dd28791f98cd2b99b78ffb6f23c13d | |
parent | 3a1cf9505d3fd4d36f8666dab3734535538f6e2e (diff) | |
download | serenity-45bfdd0063ebbd72a39df4e756147056002f5568.zip |
LibX86: Add a templatized way to resolve ModR/M memory references
Hopefully this will be flexible enough for our SoftCPU. :^)
-rw-r--r-- | Libraries/LibX86/Instruction.h | 221 |
1 files changed, 215 insertions, 6 deletions
diff --git a/Libraries/LibX86/Instruction.h b/Libraries/LibX86/Instruction.h index 24818ddbf5..ea928ee3eb 100644 --- a/Libraries/LibX86/Instruction.h +++ b/Libraries/LibX86/Instruction.h @@ -204,12 +204,11 @@ public: RegisterIndex16 reg16() const { return static_cast<RegisterIndex16>(register_index()); } RegisterIndex8 reg8() const { return static_cast<RegisterIndex8>(register_index()); } - SegmentRegister segment() const - { - ASSERT(!is_register()); - return m_segment; - } - u32 offset(); + template<typename CPU> + void write32(CPU&, const Instruction&, u32); + + template<typename CPU> + u32 read32(CPU&, const Instruction&); private: MemoryOrRegisterReference() { } @@ -222,6 +221,14 @@ private: void decode16(InstructionStream&); void decode32(InstructionStream&); + template<typename CPU> + LogicalAddress resolve16(const CPU&, Optional<SegmentRegister>); + template<typename CPU> + LogicalAddress resolve32(const CPU&, Optional<SegmentRegister>); + + template<typename CPU> + u32 evaluate_sib(const CPU&, SegmentRegister& default_segment) const; + unsigned m_register_index { 0xffffffff }; SegmentRegister m_segment; union { @@ -372,4 +379,206 @@ private: InstructionDescriptor* m_descriptor { nullptr }; }; +template<typename CPU> +LogicalAddress MemoryOrRegisterReference::resolve16(const CPU& cpu, Optional<SegmentRegister> segment_prefix) +{ + ASSERT(!m_a32); + + auto default_segment = SegmentRegister::DS; + u16 offset = 0; + + switch (m_rm & 7) { + case 0: + offset = cpu.bx() + cpu.si() + m_displacement16; + break; + case 1: + offset = cpu.bx() + cpu.di() + m_displacement16; + break; + case 2: + default_segment = SegmentRegister::SS; + offset = cpu.bp() + cpu.si() + m_displacement16; + break; + case 3: + default_segment = SegmentRegister::SS; + offset = cpu.bp() + cpu.di() + m_displacement16; + break; + case 4: + offset = cpu.si() + m_displacement16; + break; + case 5: + offset = cpu.di() + m_displacement16; + break; + case 6: + if ((m_rm & 0xc0) == 0) + offset = m_displacement16; + else { + default_segment = SegmentRegister::SS; + offset = cpu.bp() + m_displacement16; + } + break; + default: + offset = cpu.bx() + m_displacement16; + break; + } + + u16 segment = cpu.segment(segment_prefix.value_or(default_segment)); + return { segment, offset }; +} + +template<typename CPU> +inline LogicalAddress MemoryOrRegisterReference::resolve32(const CPU& cpu, Optional<SegmentRegister> segment_prefix) +{ + ASSERT(m_a32); + + auto default_segment = SegmentRegister::DS; + u32 offset = 0; + + switch (m_rm & 0x07) { + case 0: + offset = cpu.eax() + m_displacement32; + break; + case 1: + offset = cpu.ecx() + m_displacement32; + break; + case 2: + offset = cpu.edx() + m_displacement32; + break; + case 3: + offset = cpu.ebx() + m_displacement32; + break; + case 4: + offset = evaluate_sib(cpu, default_segment); + break; + case 6: + offset = cpu.esi() + m_displacement32; + break; + case 7: + offset = cpu.edi() + m_displacement32; + break; + default: // 5 + if ((m_rm & 0xc0) == 0x00) { + offset = m_displacement32; + break; + } else { + default_segment = SegmentRegister::SS; + offset = cpu.ebp() + m_displacement32; + break; + } + break; + } + u16 segment = cpu.segment(segment_prefix.value_or(default_segment)); + return { segment, offset }; +} + +template<typename CPU> +inline u32 MemoryOrRegisterReference::evaluate_sib(const CPU& cpu, SegmentRegister& default_segment) const +{ + u32 scale = 0; + switch (m_sib & 0xc0) { + case 0x00: + scale = 1; + break; + case 0x40: + scale = 2; + break; + case 0x80: + scale = 4; + break; + case 0xc0: + scale = 8; + break; + } + u32 index = 0; + switch ((m_sib >> 3) & 0x07) { + case 0: + index = cpu.eax(); + break; + case 1: + index = cpu.ecx(); + break; + case 2: + index = cpu.edx(); + break; + case 3: + index = cpu.ebx(); + break; + case 4: + index = 0; + break; + case 5: + index = cpu.ebp(); + break; + case 6: + index = cpu.esi(); + break; + case 7: + index = cpu.edi(); + break; + } + + u32 base = m_displacement32; + switch (m_sib & 0x07) { + case 0: + base += cpu.eax(); + break; + case 1: + base += cpu.ecx(); + break; + case 2: + base += cpu.edx(); + break; + case 3: + base += cpu.ebx(); + break; + case 4: + default_segment = SegmentRegister::SS; + base += cpu.esp(); + break; + case 6: + base += cpu.esi(); + break; + case 7: + base += cpu.edi(); + break; + default: // 5 + switch ((m_rm >> 6) & 3) { + case 0: + break; + case 1: + case 2: + default_segment = SegmentRegister::SS; + base += cpu.ebp(); + break; + default: + ASSERT_NOT_REACHED(); + break; + } + break; + } + + return (scale * index) + base; +} + +template<typename CPU> +inline void MemoryOrRegisterReference::write32(CPU& cpu, const Instruction& insn, u32 value) +{ + if (is_register()) { + cpu.gpr32(reg32()) = value; + return; + } + + auto address = resolve32(cpu, insn.segment_prefix()); + cpu.write_memory32(address, value); +} + +template<typename CPU> +inline u32 MemoryOrRegisterReference::read32(CPU& cpu, const Instruction& insn) +{ + if (is_register()) + return cpu.gpr32(reg32()); + + auto address = resolve32(cpu, insn.segment_prefix()); + return cpu.read_memory32(address); +} + } |