diff --git a/InstructionSets/x86/Decoder.cpp b/InstructionSets/x86/Decoder.cpp index b641a4edc..5f1913e8d 100644 --- a/InstructionSets/x86/Decoder.cpp +++ b/InstructionSets/x86/Decoder.cpp @@ -233,9 +233,12 @@ std::pair::InstructionT> Decoder::decode(con case 0x89: MemRegReg(MOV, MemReg_Reg, data_size_); break; case 0x8a: MemRegReg(MOV, Reg_MemReg, DataSize::Byte); break; case 0x8b: MemRegReg(MOV, Reg_MemReg, data_size_); break; - // 0x8c: not used. + case 0x8c: + RequiresMin(i80286); // TODO: or is this 80386? + MemRegReg(MOV, MemReg_Seg, DataSize::Word); + break; case 0x8d: MemRegReg(LEA, Reg_MemReg, data_size_); break; - case 0x8e: MemRegReg(MOV, SegReg, data_size_); break; + case 0x8e: MemRegReg(MOV, Seg_MemReg, DataSize::Word); break; case 0x8f: MemRegReg(POP, MemRegPOP, data_size_); break; case 0x90: Complete(NOP, None, None, DataSize::None); break; // Or XCHG AX, AX? @@ -430,10 +433,15 @@ std::pair::InstructionT> Decoder::decode(con ++consumed_; Source memreg; + + // TODO: can I just eliminate these lookup tables given the deliberate ordering within Source? constexpr Source reg_table[8] = { Source::eAX, Source::eCX, Source::eDX, Source::eBX, Source::eSPorAH, Source::eBPorCH, Source::eSIorDH, Source::eDIorBH, }; + constexpr Source seg_table[6] = { + Source::ES, Source::CS, Source::SS, Source::DS, Source::FS, Source::GS + }; switch(mod) { default: { const DataSize sizes[] = {DataSize::Byte, data_size_}; @@ -495,20 +503,29 @@ std::pair::InstructionT> Decoder::decode(con } break; - case ModRegRMFormat::SegReg: { - source_ = memreg; - - constexpr Source seg_table[4] = { - Source::ES, Source::CS, - Source::SS, Source::DS, - }; - - if(reg & 4) { + case ModRegRMFormat::Seg_MemReg: + case ModRegRMFormat::MemReg_Seg: + // The 16-bit chips have four segment registers; + // the 80386 onwards has six. + if(!is_32bit(model) && reg > 3) { + undefined(); + } else if(reg > 5) { undefined(); } - destination_ = seg_table[reg]; - } break; + if(modregrm_format_ == ModRegRMFormat::Seg_MemReg) { + source_ = memreg; + destination_ = seg_table[reg]; + + // 80286 and later disallow MOV to CS. + if(model >= Model::i80286 && destination_ == Source::CS) { + undefined(); + } + } else { + source_ = seg_table[reg]; + destination_ = memreg; + } + break; case ModRegRMFormat::MemRegROL_to_SAR: destination_ = memreg; diff --git a/InstructionSets/x86/Decoder.hpp b/InstructionSets/x86/Decoder.hpp index 387bb3e05..041937fb6 100644 --- a/InstructionSets/x86/Decoder.hpp +++ b/InstructionSets/x86/Decoder.hpp @@ -65,7 +65,7 @@ template class Decoder { /// are packaged into an Instruction. enum class ModRegRMFormat: uint8_t { // Parse the ModRegRM for mode, register and register/memory fields - // and populate the source_ and destination_ fields appropriate. + // and populate the source_ and destination_ fields appropriately. MemReg_Reg, Reg_MemReg, @@ -82,7 +82,8 @@ template class Decoder { // Parse for mode and register/memory fields, populating the // source_ field with the result. Fills destination_ with a segment // register based on the reg field. - SegReg, + Seg_MemReg, + MemReg_Seg, // // 'Group 1'