summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2020-07-10 16:23:07 +0200
committerAndreas Kling <kling@serenityos.org>2020-07-10 20:20:27 +0200
commit45bfdd0063ebbd72a39df4e756147056002f5568 (patch)
tree8f05000374dd28791f98cd2b99b78ffb6f23c13d
parent3a1cf9505d3fd4d36f8666dab3734535538f6e2e (diff)
downloadserenity-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.h221
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);
+}
+
}