diff --git a/InstructionSets/x86/Decoder.cpp b/InstructionSets/x86/Decoder.cpp index a3f442ca3..616602e33 100644 --- a/InstructionSets/x86/Decoder.cpp +++ b/InstructionSets/x86/Decoder.cpp @@ -57,27 +57,28 @@ std::pair::InstructionT> Decoder::decode(con operation_ = Operation::op; \ phase_ = Phase::ModRegRM; \ modregrm_format_ = ModRegRMFormat::format; \ - operand_size_ = 0; \ + operand_size_ = DataSize::None; \ operation_size_ = size /// Handles JO, JNO, JB, etc — jumps with a single byte displacement. -#define Jump(op) \ +#define Jump(op, size) \ operation_ = Operation::op; \ phase_ = Phase::DisplacementOrOperand; \ - displacement_size_ = 1 + displacement_size_ = size /// Handles far CALL and far JMP — fixed four byte operand operations. #define Far(op) \ operation_ = Operation::op; \ phase_ = Phase::DisplacementOrOperand; \ - operand_size_ = 4; \ + operand_size_ = data_size_; \ + displacement_size_ = DataSize::Word /// Handles ENTER — a fixed three-byte operation. #define Displacement16Operand8(op) \ operation_ = Operation::op; \ phase_ = Phase::DisplacementOrOperand; \ - displacement_size_ = 2; \ - operand_size_ = 1; \ + displacement_size_ = DataSize::Word; \ + operand_size_ = DataSize::Byte #define undefined() { \ const auto result = std::make_pair(consumed_, InstructionT()); \ @@ -89,29 +90,27 @@ std::pair::InstructionT> Decoder::decode(con #define RequiresMin(x) if constexpr (model < Model::x) undefined(); while(phase_ == Phase::Instruction && source != end) { - // Retain the instruction byte, in case additional decoding is deferred - // to the ModRegRM byte. - instr_ = *source; + const uint8_t instr = *source; ++source; ++consumed_; - switch(instr_) { + switch(instr) { default: undefined(); -#define PartialBlock(start, operation) \ - case start + 0x00: MemRegReg(operation, MemReg_Reg, 1); break; \ - case start + 0x01: MemRegReg(operation, MemReg_Reg, 2); break; \ - case start + 0x02: MemRegReg(operation, Reg_MemReg, 1); break; \ - case start + 0x03: MemRegReg(operation, Reg_MemReg, 2); break; \ - case start + 0x04: RegData(operation, eAX, 1); break; \ - case start + 0x05: RegData(operation, eAX, 2) +#define PartialBlock(start, operation) \ + case start + 0x00: MemRegReg(operation, MemReg_Reg, DataSize::Byte); break; \ + case start + 0x01: MemRegReg(operation, MemReg_Reg, data_size_); break; \ + case start + 0x02: MemRegReg(operation, Reg_MemReg, DataSize::Byte); break; \ + case start + 0x03: MemRegReg(operation, Reg_MemReg, data_size_); break; \ + case start + 0x04: RegData(operation, eAX, DataSize::Byte); break; \ + case start + 0x05: RegData(operation, eAX, data_size_) - PartialBlock(0x00, ADD); break; - case 0x06: Complete(PUSH, ES, None, 2); break; - case 0x07: Complete(POP, None, ES, 2); break; + PartialBlock(0x00, ADD); break; + case 0x06: Complete(PUSH, ES, None, data_size_); break; + case 0x07: Complete(POP, None, ES, data_size_); break; - PartialBlock(0x08, OR); break; - case 0x0e: Complete(PUSH, CS, None, 2); break; + PartialBlock(0x08, OR); break; + case 0x0e: Complete(PUSH, CS, None, data_size_); break; // The 286 onwards have a further set of instructions // prefixed with $0f. @@ -120,41 +119,41 @@ std::pair::InstructionT> Decoder::decode(con phase_ = Phase::InstructionPageF; break; - PartialBlock(0x10, ADC); break; - case 0x16: Complete(PUSH, SS, None, 2); break; - case 0x17: Complete(POP, None, SS, 2); break; + PartialBlock(0x10, ADC); break; + case 0x16: Complete(PUSH, SS, None, DataSize::Word); break; + case 0x17: Complete(POP, None, SS, DataSize::Word); break; - PartialBlock(0x18, SBB); break; - case 0x1e: Complete(PUSH, DS, None, 2); break; - case 0x1f: Complete(POP, None, DS, 2); break; + PartialBlock(0x18, SBB); break; + case 0x1e: Complete(PUSH, DS, None, DataSize::Word); break; + case 0x1f: Complete(POP, None, DS, DataSize::Word); break; - PartialBlock(0x20, AND); break; - case 0x26: segment_override_ = Source::ES; break; - case 0x27: Complete(DAA, eAX, eAX, 1); break; + PartialBlock(0x20, AND); break; + case 0x26: segment_override_ = Source::ES; break; + case 0x27: Complete(DAA, eAX, eAX, DataSize::Byte); break; - PartialBlock(0x28, SUB); break; - case 0x2e: segment_override_ = Source::CS; break; - case 0x2f: Complete(DAS, eAX, eAX, 1); break; + PartialBlock(0x28, SUB); break; + case 0x2e: segment_override_ = Source::CS; break; + case 0x2f: Complete(DAS, eAX, eAX, DataSize::Byte); break; - PartialBlock(0x30, XOR); break; - case 0x36: segment_override_ = Source::SS; break; - case 0x37: Complete(AAA, eAX, eAX, 2); break; + PartialBlock(0x30, XOR); break; + case 0x36: segment_override_ = Source::SS; break; + case 0x37: Complete(AAA, eAX, eAX, DataSize::Word); break; - PartialBlock(0x38, CMP); break; - case 0x3e: segment_override_ = Source::DS; break; - case 0x3f: Complete(AAS, eAX, eAX, 2); break; + PartialBlock(0x38, CMP); break; + case 0x3e: segment_override_ = Source::DS; break; + case 0x3f: Complete(AAS, eAX, eAX, DataSize::Word); break; #undef PartialBlock -#define RegisterBlock(start, operation) \ - case start + 0x00: Complete(operation, eAX, eAX, 2); break; \ - case start + 0x01: Complete(operation, eCX, eCX, 2); break; \ - case start + 0x02: Complete(operation, eDX, eDX, 2); break; \ - case start + 0x03: Complete(operation, eBX, eBX, 2); break; \ - case start + 0x04: Complete(operation, eSP, eSP, 2); break; \ - case start + 0x05: Complete(operation, eBP, eBP, 2); break; \ - case start + 0x06: Complete(operation, eSI, eSI, 2); break; \ - case start + 0x07: Complete(operation, eDI, eDI, 2) +#define RegisterBlock(start, operation) \ + case start + 0x00: Complete(operation, eAX, eAX, data_size_); break; \ + case start + 0x01: Complete(operation, eCX, eCX, data_size_); break; \ + case start + 0x02: Complete(operation, eDX, eDX, data_size_); break; \ + case start + 0x03: Complete(operation, eBX, eBX, data_size_); break; \ + case start + 0x04: Complete(operation, eSP, eSP, data_size_); break; \ + case start + 0x05: Complete(operation, eBP, eBP, data_size_); break; \ + case start + 0x06: Complete(operation, eSI, eSI, data_size_); break; \ + case start + 0x07: Complete(operation, eDI, eDI, data_size_) RegisterBlock(0x40, INC); break; RegisterBlock(0x48, DEC); break; @@ -165,19 +164,19 @@ std::pair::InstructionT> Decoder::decode(con case 0x60: RequiresMin(i80186); - Complete(PUSHA, None, None, 2); + Complete(PUSHA, None, None, data_size_); break; case 0x61: RequiresMin(i80186); - Complete(POPA, None, None, 2); + Complete(POPA, None, None, data_size_); break; case 0x62: RequiresMin(i80186); - MemRegReg(BOUND, Reg_MemReg, 2); + MemRegReg(BOUND, Reg_MemReg, data_size_); break; case 0x63: RequiresMin(i80286); - MemRegReg(ARPL, MemReg_Reg, 2); + MemRegReg(ARPL, MemReg_Reg, DataSize::Word); break; case 0x66: RequiresMin(i80386); @@ -189,115 +188,121 @@ std::pair::InstructionT> Decoder::decode(con break; case 0x6c: // INSB RequiresMin(i80186); - Complete(INS, None, None, 1); + Complete(INS, None, None, DataSize::Byte); break; case 0x6d: // INSW RequiresMin(i80186); - Complete(INS, None, None, 2); + Complete(INS, None, None, data_size_); break; case 0x6e: // OUTSB RequiresMin(i80186); - Complete(OUTS, None, None, 1); + Complete(OUTS, None, None, DataSize::Byte); break; case 0x6f: // OUTSW RequiresMin(i80186); - Complete(OUTS, None, None, 2); + Complete(OUTS, None, None, data_size_); break; - case 0x70: Jump(JO); break; - case 0x71: Jump(JNO); break; - case 0x72: Jump(JB); break; - case 0x73: Jump(JNB); break; - case 0x74: Jump(JE); break; - case 0x75: Jump(JNE); break; - case 0x76: Jump(JBE); break; - case 0x77: Jump(JNBE); break; - case 0x78: Jump(JS); break; - case 0x79: Jump(JNS); break; - case 0x7a: Jump(JP); break; - case 0x7b: Jump(JNP); break; - case 0x7c: Jump(JL); break; - case 0x7d: Jump(JNL); break; - case 0x7e: Jump(JLE); break; - case 0x7f: Jump(JNLE); break; + case 0x70: Jump(JO, DataSize::Byte); break; + case 0x71: Jump(JNO, DataSize::Byte); break; + case 0x72: Jump(JB, DataSize::Byte); break; + case 0x73: Jump(JNB, DataSize::Byte); break; + case 0x74: Jump(JE, DataSize::Byte); break; + case 0x75: Jump(JNE, DataSize::Byte); break; + case 0x76: Jump(JBE, DataSize::Byte); break; + case 0x77: Jump(JNBE, DataSize::Byte); break; + case 0x78: Jump(JS, DataSize::Byte); break; + case 0x79: Jump(JNS, DataSize::Byte); break; + case 0x7a: Jump(JP, DataSize::Byte); break; + case 0x7b: Jump(JNP, DataSize::Byte); break; + case 0x7c: Jump(JL, DataSize::Byte); break; + case 0x7d: Jump(JNL, DataSize::Byte); break; + case 0x7e: Jump(JLE, DataSize::Byte); break; + case 0x7f: Jump(JNLE, DataSize::Byte); break; - case 0x80: MemRegReg(Invalid, MemRegADD_to_CMP, 1); break; - case 0x81: MemRegReg(Invalid, MemRegADD_to_CMP, 2); break; - case 0x82: MemRegReg(Invalid, MemRegADC_to_CMP, 1); break; - case 0x83: MemRegReg(Invalid, MemRegADC_to_CMP, 2); break; + case 0x80: MemRegReg(Invalid, MemRegADD_to_CMP, DataSize::Byte); break; + case 0x81: MemRegReg(Invalid, MemRegADD_to_CMP, data_size_); break; + case 0x82: + MemRegReg(Invalid, MemRegADC_to_CMP, DataSize::Byte); + sign_extend_ = true; + break; + case 0x83: + MemRegReg(Invalid, MemRegADC_to_CMP, data_size_); + sign_extend_ = true; + break; - case 0x84: MemRegReg(TEST, MemReg_Reg, 1); break; - case 0x85: MemRegReg(TEST, MemReg_Reg, 2); break; - case 0x86: MemRegReg(XCHG, Reg_MemReg, 1); break; - case 0x87: MemRegReg(XCHG, Reg_MemReg, 2); break; - case 0x88: MemRegReg(MOV, MemReg_Reg, 1); break; - case 0x89: MemRegReg(MOV, MemReg_Reg, 2); break; - case 0x8a: MemRegReg(MOV, Reg_MemReg, 1); break; - case 0x8b: MemRegReg(MOV, Reg_MemReg, 2); break; + case 0x84: MemRegReg(TEST, MemReg_Reg, DataSize::Byte); break; + case 0x85: MemRegReg(TEST, MemReg_Reg, data_size_); break; + case 0x86: MemRegReg(XCHG, Reg_MemReg, DataSize::Byte); break; + case 0x87: MemRegReg(XCHG, Reg_MemReg, data_size_); break; + case 0x88: MemRegReg(MOV, MemReg_Reg, DataSize::Byte); break; + 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 0x8d: MemRegReg(LEA, Reg_MemReg, 2); break; - case 0x8e: MemRegReg(MOV, SegReg, 2); break; - case 0x8f: MemRegReg(POP, MemRegPOP, 2); break; + case 0x8d: MemRegReg(LEA, Reg_MemReg, data_size_); break; + case 0x8e: MemRegReg(MOV, SegReg, data_size_); break; + case 0x8f: MemRegReg(POP, MemRegPOP, data_size_); break; - case 0x90: Complete(NOP, None, None, 0); break; // Or XCHG AX, AX? - case 0x91: Complete(XCHG, eAX, eCX, 2); break; - case 0x92: Complete(XCHG, eAX, eDX, 2); break; - case 0x93: Complete(XCHG, eAX, eBX, 2); break; - case 0x94: Complete(XCHG, eAX, eSP, 2); break; - case 0x95: Complete(XCHG, eAX, eBP, 2); break; - case 0x96: Complete(XCHG, eAX, eSI, 2); break; - case 0x97: Complete(XCHG, eAX, eDI, 2); break; + case 0x90: Complete(NOP, None, None, DataSize::None); break; // Or XCHG AX, AX? + case 0x91: Complete(XCHG, eAX, eCX, data_size_); break; + case 0x92: Complete(XCHG, eAX, eDX, data_size_); break; + case 0x93: Complete(XCHG, eAX, eBX, data_size_); break; + case 0x94: Complete(XCHG, eAX, eSP, data_size_); break; + case 0x95: Complete(XCHG, eAX, eBP, data_size_); break; + case 0x96: Complete(XCHG, eAX, eSI, data_size_); break; + case 0x97: Complete(XCHG, eAX, eDI, data_size_); break; - case 0x98: Complete(CBW, eAX, AH, 1); break; - case 0x99: Complete(CWD, eAX, eDX, 2); break; - case 0x9a: Far(CALLF); break; - case 0x9b: Complete(WAIT, None, None, 0); break; - case 0x9c: Complete(PUSHF, None, None, 2); break; - case 0x9d: Complete(POPF, None, None, 2); break; - case 0x9e: Complete(SAHF, None, None, 1); break; - case 0x9f: Complete(LAHF, None, None, 1); break; + case 0x98: Complete(CBW, eAX, AH, DataSize::Byte); break; + case 0x99: Complete(CWD, eAX, eDX, data_size_); break; + case 0x9a: Far(CALLF); break; + case 0x9b: Complete(WAIT, None, None, DataSize::None); break; + case 0x9c: Complete(PUSHF, None, None, data_size_); break; + case 0x9d: Complete(POPF, None, None, data_size_); break; + case 0x9e: Complete(SAHF, None, None, DataSize::Byte); break; + case 0x9f: Complete(LAHF, None, None, DataSize::Byte); break; - case 0xa0: RegAddr(MOV, eAX, 1, 1); break; - case 0xa1: RegAddr(MOV, eAX, 2, 2); break; - case 0xa2: AddrReg(MOV, eAX, 1, 1); break; - case 0xa3: AddrReg(MOV, eAX, 2, 2); break; + case 0xa0: RegAddr(MOV, eAX, DataSize::Byte, DataSize::Byte); break; + case 0xa1: RegAddr(MOV, eAX, data_size_, data_size_); break; + case 0xa2: AddrReg(MOV, eAX, DataSize::Byte, DataSize::Byte); break; + case 0xa3: AddrReg(MOV, eAX, data_size_, data_size_); break; - case 0xa4: Complete(MOVS, None, None, 1); break; - case 0xa5: Complete(MOVS, None, None, 2); break; - case 0xa6: Complete(CMPS, None, None, 1); break; - case 0xa7: Complete(CMPS, None, None, 2); break; - case 0xa8: RegData(TEST, eAX, 1); break; - case 0xa9: RegData(TEST, eAX, 2); break; - case 0xaa: Complete(STOS, None, None, 1); break; - case 0xab: Complete(STOS, None, None, 2); break; - case 0xac: Complete(LODS, None, None, 1); break; - case 0xad: Complete(LODS, None, None, 2); break; - case 0xae: Complete(SCAS, None, None, 1); break; - case 0xaf: Complete(SCAS, None, None, 2); break; + case 0xa4: Complete(MOVS, None, None, DataSize::Byte); break; + case 0xa5: Complete(MOVS, None, None, data_size_); break; + case 0xa6: Complete(CMPS, None, None, DataSize::Byte); break; + case 0xa7: Complete(CMPS, None, None, data_size_); break; + case 0xa8: RegData(TEST, eAX, DataSize::Byte); break; + case 0xa9: RegData(TEST, eAX, data_size_); break; + case 0xaa: Complete(STOS, None, None, DataSize::Byte); break; + case 0xab: Complete(STOS, None, None, data_size_); break; + case 0xac: Complete(LODS, None, None, DataSize::Byte); break; + case 0xad: Complete(LODS, None, None, data_size_); break; + case 0xae: Complete(SCAS, None, None, DataSize::Byte); break; + case 0xaf: Complete(SCAS, None, None, data_size_); break; - case 0xb0: RegData(MOV, eAX, 1); break; - case 0xb1: RegData(MOV, eCX, 1); break; - case 0xb2: RegData(MOV, eDX, 1); break; - case 0xb3: RegData(MOV, eBX, 1); break; - case 0xb4: RegData(MOV, AH, 1); break; - case 0xb5: RegData(MOV, CH, 1); break; - case 0xb6: RegData(MOV, DH, 1); break; - case 0xb7: RegData(MOV, BH, 1); break; - case 0xb8: RegData(MOV, eAX, 2); break; - case 0xb9: RegData(MOV, eCX, 2); break; - case 0xba: RegData(MOV, eDX, 2); break; - case 0xbb: RegData(MOV, eBX, 2); break; - case 0xbc: RegData(MOV, eSP, 2); break; - case 0xbd: RegData(MOV, eBP, 2); break; - case 0xbe: RegData(MOV, eSI, 2); break; - case 0xbf: RegData(MOV, eDI, 2); break; + case 0xb0: RegData(MOV, eAX, DataSize::Byte); break; + case 0xb1: RegData(MOV, eCX, DataSize::Byte); break; + case 0xb2: RegData(MOV, eDX, DataSize::Byte); break; + case 0xb3: RegData(MOV, eBX, DataSize::Byte); break; + case 0xb4: RegData(MOV, AH, DataSize::Byte); break; + case 0xb5: RegData(MOV, CH, DataSize::Byte); break; + case 0xb6: RegData(MOV, DH, DataSize::Byte); break; + case 0xb7: RegData(MOV, BH, DataSize::Byte); break; + case 0xb8: RegData(MOV, eAX, data_size_); break; + case 0xb9: RegData(MOV, eCX, data_size_); break; + case 0xba: RegData(MOV, eDX, data_size_); break; + case 0xbb: RegData(MOV, eBX, data_size_); break; + case 0xbc: RegData(MOV, eSP, data_size_); break; + case 0xbd: RegData(MOV, eBP, data_size_); break; + case 0xbe: RegData(MOV, eSI, data_size_); break; + case 0xbf: RegData(MOV, eDI, data_size_); break; - case 0xc2: RegData(RETN, None, 2); break; - case 0xc3: Complete(RETN, None, None, 2); break; - case 0xc4: MemRegReg(LES, Reg_MemReg, 2); break; - case 0xc5: MemRegReg(LDS, Reg_MemReg, 2); break; - case 0xc6: MemRegReg(MOV, MemRegMOV, 1); break; - case 0xc7: MemRegReg(MOV, MemRegMOV, 2); break; + case 0xc2: RegData(RETN, None, data_size_); break; + case 0xc3: Complete(RETN, None, None, DataSize::None); break; + case 0xc4: MemRegReg(LES, Reg_MemReg, data_size_); break; + case 0xc5: MemRegReg(LDS, Reg_MemReg, data_size_); break; + case 0xc6: MemRegReg(MOV, MemRegMOV, DataSize::Byte); break; + case 0xc7: MemRegReg(MOV, MemRegMOV, data_size_); break; case 0xc8: RequiresMin(i80186); @@ -305,106 +310,108 @@ std::pair::InstructionT> Decoder::decode(con break; case 0xc9: RequiresMin(i80186); - Complete(LEAVE, None, None, 0); + Complete(LEAVE, None, None, DataSize::None); break; - case 0xca: RegData(RETF, None, 2); break; - case 0xcb: Complete(RETF, None, None, 4); break; + case 0xca: RegData(RETF, None, data_size_); break; + case 0xcb: Complete(RETF, None, None, DataSize::DWord); break; - case 0xcc: Complete(INT3, None, None, 0); break; - case 0xcd: RegData(INT, None, 1); break; - case 0xce: Complete(INTO, None, None, 0); break; - case 0xcf: Complete(IRET, None, None, 0); break; + case 0xcc: Complete(INT3, None, None, DataSize::None); break; + case 0xcd: RegData(INT, None, DataSize::Byte); break; + case 0xce: Complete(INTO, None, None, DataSize::None); break; + case 0xcf: Complete(IRET, None, None, DataSize::None); break; - case 0xd0: case 0xd1: + case 0xd0: case 0xd1: { + const DataSize sizes[] = {DataSize::Byte, data_size_}; phase_ = Phase::ModRegRM; modregrm_format_ = ModRegRMFormat::MemRegROL_to_SAR; - operation_size_ = 1 + (instr_ & 1); + operation_size_ = sizes[instr & 1]; source_ = Source::Immediate; operand_ = 1; - break; - case 0xd2: case 0xd3: + } break; + case 0xd2: case 0xd3: { + const DataSize sizes[] = {DataSize::Byte, data_size_}; phase_ = Phase::ModRegRM; modregrm_format_ = ModRegRMFormat::MemRegROL_to_SAR; - operation_size_ = 1 + (instr_ & 1); + operation_size_ = sizes[instr & 1]; source_ = Source::eCX; - break; - case 0xd4: RegData(AAM, eAX, 1); break; - case 0xd5: RegData(AAD, eAX, 1); break; + } break; + case 0xd4: RegData(AAM, eAX, DataSize::Byte); break; + case 0xd5: RegData(AAD, eAX, DataSize::Byte); break; - case 0xd7: Complete(XLAT, None, None, 1); break; + case 0xd7: Complete(XLAT, None, None, DataSize::Byte); break; - case 0xd8: MemRegReg(ESC, MemReg_Reg, 0); break; - case 0xd9: MemRegReg(ESC, MemReg_Reg, 0); break; - case 0xda: MemRegReg(ESC, MemReg_Reg, 0); break; - case 0xdb: MemRegReg(ESC, MemReg_Reg, 0); break; - case 0xdc: MemRegReg(ESC, MemReg_Reg, 0); break; - case 0xdd: MemRegReg(ESC, MemReg_Reg, 0); break; - case 0xde: MemRegReg(ESC, MemReg_Reg, 0); break; - case 0xdf: MemRegReg(ESC, MemReg_Reg, 0); break; + case 0xd8: MemRegReg(ESC, MemReg_Reg, DataSize::None); break; + case 0xd9: MemRegReg(ESC, MemReg_Reg, DataSize::None); break; + case 0xda: MemRegReg(ESC, MemReg_Reg, DataSize::None); break; + case 0xdb: MemRegReg(ESC, MemReg_Reg, DataSize::None); break; + case 0xdc: MemRegReg(ESC, MemReg_Reg, DataSize::None); break; + case 0xdd: MemRegReg(ESC, MemReg_Reg, DataSize::None); break; + case 0xde: MemRegReg(ESC, MemReg_Reg, DataSize::None); break; + case 0xdf: MemRegReg(ESC, MemReg_Reg, DataSize::None); break; - case 0xe0: Jump(LOOPNE); break; - case 0xe1: Jump(LOOPE); break; - case 0xe2: Jump(LOOP); break; - case 0xe3: Jump(JPCX); break; + case 0xe0: Jump(LOOPNE, DataSize::Byte); break; + case 0xe1: Jump(LOOPE, DataSize::Byte); break; + case 0xe2: Jump(LOOP, DataSize::Byte); break; + case 0xe3: Jump(JPCX, DataSize::Byte); break; - case 0xe4: RegAddr(IN, eAX, 1, 1); break; - case 0xe5: RegAddr(IN, eAX, 2, 1); break; - case 0xe6: AddrReg(OUT, eAX, 1, 1); break; - case 0xe7: AddrReg(OUT, eAX, 2, 1); break; + case 0xe4: RegAddr(IN, eAX, DataSize::Byte, DataSize::Byte); break; + case 0xe5: RegAddr(IN, eAX, data_size_, DataSize::Byte); break; + case 0xe6: AddrReg(OUT, eAX, DataSize::Byte, DataSize::Byte); break; + case 0xe7: AddrReg(OUT, eAX, data_size_, DataSize::Byte); break; - case 0xe8: RegData(CALLD, None, 2); break; - case 0xe9: RegData(JMPN, None, 2); break; - case 0xea: Far(JMPF); break; - case 0xeb: Jump(JMPN); break; + case 0xe8: RegData(CALLD, None, data_size_); break; + case 0xe9: RegData(JMPN, None, data_size_); break; + case 0xea: Far(JMPF); break; + case 0xeb: Jump(JMPN, DataSize::Byte); break; - case 0xec: Complete(IN, eDX, eAX, 1); break; - case 0xed: Complete(IN, eDX, eAX, 1); break; - case 0xee: Complete(OUT, eAX, eDX, 1); break; - case 0xef: Complete(OUT, eAX, eDX, 2); break; + case 0xec: Complete(IN, eDX, eAX, DataSize::Byte); break; + case 0xed: Complete(IN, eDX, eAX, data_size_); break; + case 0xee: Complete(OUT, eAX, eDX, DataSize::Byte); break; + case 0xef: Complete(OUT, eAX, eDX, data_size_); break; case 0xf0: lock_ = true; break; case 0xf2: repetition_ = Repetition::RepNE; break; case 0xf3: repetition_ = Repetition::RepE; break; - case 0xf4: Complete(HLT, None, None, 1); break; - case 0xf5: Complete(CMC, None, None, 1); break; - case 0xf6: MemRegReg(Invalid, MemRegTEST_to_IDIV, 1); break; - case 0xf7: MemRegReg(Invalid, MemRegTEST_to_IDIV, 2); break; + case 0xf4: Complete(HLT, None, None, DataSize::None); break; + case 0xf5: Complete(CMC, None, None, DataSize::None); break; + case 0xf6: MemRegReg(Invalid, MemRegTEST_to_IDIV, DataSize::Byte); break; + case 0xf7: MemRegReg(Invalid, MemRegTEST_to_IDIV, data_size_); break; - case 0xf8: Complete(CLC, None, None, 1); break; - case 0xf9: Complete(STC, None, None, 1); break; - case 0xfa: Complete(CLI, None, None, 1); break; - case 0xfb: Complete(STI, None, None, 1); break; - case 0xfc: Complete(CLD, None, None, 1); break; - case 0xfd: Complete(STD, None, None, 1); break; + case 0xf8: Complete(CLC, None, None, DataSize::None); break; + case 0xf9: Complete(STC, None, None, DataSize::None); break; + case 0xfa: Complete(CLI, None, None, DataSize::None); break; + case 0xfb: Complete(STI, None, None, DataSize::None); break; + case 0xfc: Complete(CLD, None, None, DataSize::None); break; + case 0xfd: Complete(STD, None, None, DataSize::None); break; - case 0xfe: MemRegReg(Invalid, MemRegINC_DEC, 1); break; - case 0xff: MemRegReg(Invalid, MemRegINC_to_PUSH, 1); break; + case 0xfe: MemRegReg(Invalid, MemRegINC_DEC, DataSize::Byte); break; + case 0xff: MemRegReg(Invalid, MemRegINC_to_PUSH, data_size_); break; } } // MARK: - Additional F page of instructions. if(phase_ == Phase::InstructionPageF && source != end) { // Update the instruction acquired. - instr_ = 0x0f00 | *source; + const uint8_t instr = *source; ++source; ++consumed_; // NB: to reach here, the instruction set must be at least // that of an 80286. - switch(instr_) { + switch(instr) { default: undefined(); - case 0x00: MemRegReg(Invalid, MemRegSLDT_to_VERW, 2); break; - case 0x01: MemRegReg(Invalid, MemRegSGDT_to_LMSW, 2); break; - case 0x02: MemRegReg(LAR, Reg_MemReg, 2); break; - case 0x03: MemRegReg(LSL, Reg_MemReg, 2); break; + case 0x00: MemRegReg(Invalid, MemRegSLDT_to_VERW, data_size_); break; + case 0x01: MemRegReg(Invalid, MemRegSGDT_to_LMSW, data_size_); break; + case 0x02: MemRegReg(LAR, Reg_MemReg, data_size_); break; + case 0x03: MemRegReg(LSL, Reg_MemReg, data_size_); break; case 0x05: Requires(i80286); - Complete(LOADALL, None, None, 0); + Complete(LOADALL, None, None, DataSize::None); break; - case 0x06: Complete(CLTS, None, None, 1); break; + case 0x06: Complete(CLTS, None, None, DataSize::Byte); break; } } @@ -429,19 +436,15 @@ std::pair::InstructionT> Decoder::decode(con ++consumed_; Source memreg; - constexpr Source reg_table[3][8] = { - {}, - { - Source::eAX, Source::eCX, Source::eDX, Source::eBX, - Source::AH, Source::CH, Source::DH, Source::BH, - }, { - Source::eAX, Source::eCX, Source::eDX, Source::eBX, - Source::eSP, Source::eBP, Source::eSI, Source::eDI, - } + constexpr Source reg_table[8] = { + Source::eAX, Source::eCX, Source::eDX, Source::eBX, + Source::eSPorAH, Source::eBPorCH, Source::eSIorDH, Source::eDIorBH, }; switch(mod) { - default: - displacement_size_ = 1 + (mod == 2); + default: { + const DataSize sizes[] = {DataSize::Byte, data_size_}; + displacement_size_ = sizes[mod == 2]; + } [[fallthrough]]; case 0: { constexpr ScaleIndexBase rm_table[8] = { @@ -461,7 +464,7 @@ std::pair::InstructionT> Decoder::decode(con // Other operand is just a register. case 3: - memreg = reg_table[operation_size_][rm]; + memreg = reg_table[rm]; // LES and LDS accept a memory argument only, not a register. if(operation_ == Operation::LES || operation_ == Operation::LDS) { @@ -475,9 +478,9 @@ std::pair::InstructionT> Decoder::decode(con case ModRegRMFormat::MemReg_Reg: { if(modregrm_format_ == ModRegRMFormat::Reg_MemReg) { source_ = memreg; - destination_ = reg_table[operation_size_][reg]; + destination_ = reg_table[reg]; } else { - source_ = reg_table[operation_size_][reg]; + source_ = reg_table[reg]; destination_ = memreg; } } break; @@ -551,13 +554,13 @@ std::pair::InstructionT> Decoder::decode(con case 2: operation_ = Operation::CALLN; break; case 3: operation_ = Operation::CALLF; - operand_size_ = 4; + operand_size_ = DataSize::DWord; source_ = Source::Immediate; break; case 4: operation_ = Operation::JMPN; break; case 5: operation_ = Operation::JMPF; - operand_size_ = 4; + operand_size_ = DataSize::DWord; source_ = Source::Immediate; break; case 6: operation_ = Operation::PUSH; break; @@ -597,8 +600,8 @@ std::pair::InstructionT> Decoder::decode(con case ModRegRMFormat::MemRegADC_to_CMP: destination_ = memreg; source_ = Source::Immediate; - operand_size_ = 1; // ... and always 1; it'll be sign extended if - // the operation requires it. + operand_size_ = DataSize::Byte; // ... and always a byte; it'll be sign extended if + // the operation requires it. switch(reg) { default: undefined(); @@ -642,7 +645,7 @@ std::pair::InstructionT> Decoder::decode(con default: assert(false); } - phase_ = (displacement_size_ + operand_size_) ? Phase::DisplacementOrOperand : Phase::ReadyToPost; + phase_ = (displacement_size_ != DataSize::None || operand_size_ != DataSize::None) ? Phase::DisplacementOrOperand : Phase::ReadyToPost; } #undef undefined @@ -658,15 +661,15 @@ std::pair::InstructionT> Decoder::decode(con // MARK: - Displacement and operand. if(phase_ == Phase::DisplacementOrOperand && source != end) { - const int required_bytes = displacement_size_ + operand_size_; + const auto required_bytes = int(byte_size(displacement_size_) + byte_size(operand_size_)); const int outstanding_bytes = required_bytes - operand_bytes_; const int bytes_to_consume = std::min(int(end - source), outstanding_bytes); - // TODO: I can surely do better than this? for(int c = 0; c < bytes_to_consume; c++) { - inward_data_ = (inward_data_ >> 8) | (uint64_t(source[0]) << 56); + inward_data_ |= decltype(inward_data_)(source[0]) << next_inward_data_shift_; ++source; + next_inward_data_shift_ += 8; } consumed_ += bytes_to_consume; @@ -675,25 +678,28 @@ std::pair::InstructionT> Decoder::decode(con if(bytes_to_consume == outstanding_bytes) { phase_ = Phase::ReadyToPost; - switch(operand_size_) { - default: operand_ = 0; break; - case 1: - operand_ = inward_data_ >> 56; inward_data_ <<= 8; - - // Sign extend if a single byte operand is feeding a two-byte instruction. - if(operation_size_ == 2 && operation_ != Operation::IN && operation_ != Operation::OUT) { - operand_ |= (operand_ & 0x80) ? 0xff00 : 0x0000; - } - break; - case 4: displacement_size_ = 2; [[fallthrough]]; - case 2: operand_ = inward_data_ >> 48; inward_data_ <<= 16; break; - break; - } switch(displacement_size_) { - default: displacement_ = 0; break; - case 1: displacement_ = int8_t(inward_data_ >> 56); break; - case 2: displacement_ = int16_t(inward_data_ >> 48); break; + case DataSize::None: displacement_ = 0; break; + case DataSize::Byte: displacement_ = int8_t(inward_data_); break; + case DataSize::Word: displacement_ = int16_t(inward_data_); break; + case DataSize::DWord: displacement_ = int32_t(inward_data_); break; } + inward_data_ >>= bit_size(displacement_size_); + + // Use inequality of sizes as a test for necessary sign extension. + if(operand_size_ == data_size_ || !sign_extend_) { + operand_ = decltype(operand_)(inward_data_); + } else { + switch(operand_size_) { + case DataSize::None: operand_ = 0; break; + case DataSize::Byte: operand_ = decltype(operand_)(int8_t(inward_data_)); break; + case DataSize::Word: operand_ = decltype(operand_)(int16_t(inward_data_)); break; + case DataSize::DWord: operand_ = decltype(operand_)(int32_t(inward_data_)); break; + } + } + + // TODO: split differently for far jumps/etc. But that information is + // no longer retained now that it's not implied by a DWord-sized operand. } else { // Provide a genuine measure of further bytes required. return std::make_pair(-(outstanding_bytes - bytes_to_consume), InstructionT()); @@ -715,8 +721,8 @@ std::pair::InstructionT> Decoder::decode(con segment_override_, repetition_, DataSize(operation_size_), - displacement_, - operand_) + static_cast(displacement_), + static_cast(operand_)) ); reset_parsing(); return result; diff --git a/InstructionSets/x86/Decoder.hpp b/InstructionSets/x86/Decoder.hpp index 7ce295f93..b50f7acd3 100644 --- a/InstructionSets/x86/Decoder.hpp +++ b/InstructionSets/x86/Decoder.hpp @@ -130,7 +130,6 @@ template class Decoder { // Ephemeral decoding state. Operation operation_ = Operation::Invalid; - uint16_t instr_ = 0x0000; // TODO: is this desired, versus loading more context into ModRegRMFormat? int consumed_ = 0, operand_bytes_ = 0; // Source and destination locations. @@ -138,17 +137,21 @@ template class Decoder { Source destination_ = Source::None; // Immediate fields. - int16_t displacement_ = 0; - uint16_t operand_ = 0; + int32_t displacement_ = 0; + uint32_t operand_ = 0; uint64_t inward_data_ = 0; + int next_inward_data_shift_ = 0; // Indirection style. ScaleIndexBase sib_; // Facts about the instruction. - int displacement_size_ = 0; // i.e. size of in-stream displacement, if any. - int operand_size_ = 0; // i.e. size of in-stream operand, if any. - int operation_size_ = 0; // i.e. size of data manipulated by the operation. + DataSize displacement_size_ = DataSize::None; // i.e. size of in-stream displacement, if any. + DataSize operand_size_ = DataSize::None; // i.e. size of in-stream operand, if any. + DataSize operation_size_ = DataSize::None; // i.e. size of data manipulated by the operation. + + bool sign_extend_ = false; // If set then sign extend the operand up to the operation size; + // otherwise it'll be zero-padded. // Prefix capture fields. Repetition repetition_ = Repetition::None; @@ -164,7 +167,7 @@ template class Decoder { /// Resets size capture and all fields with default values. void reset_parsing() { consumed_ = operand_bytes_ = 0; - displacement_size_ = operand_size_ = 0; + displacement_size_ = operand_size_ = operation_size_ = DataSize::None; displacement_ = operand_ = 0; lock_ = false; address_size_ = default_address_size_; @@ -173,7 +176,10 @@ template class Decoder { repetition_ = Repetition::None; phase_ = Phase::Instruction; source_ = destination_ = Source::None; - sib_ = 0; + sib_ = ScaleIndexBase(); + next_inward_data_shift_ = 0; + inward_data_ = 0; + sign_extend_ = false; } }; diff --git a/InstructionSets/x86/Instruction.hpp b/InstructionSets/x86/Instruction.hpp index 1240756a6..85832be60 100644 --- a/InstructionSets/x86/Instruction.hpp +++ b/InstructionSets/x86/Instruction.hpp @@ -322,17 +322,33 @@ enum class Operation: uint8_t { }; enum class DataSize: uint8_t { - Implied = 0, - Byte = 1, - Word = 2, - DWord = 3, + Byte = 0, + Word = 1, + DWord = 2, + None = 3, }; +constexpr int byte_size(DataSize size) { + return (1 << int(size)) & 7; +} + +constexpr int bit_size(DataSize size) { + return (8 << int(size)) & 0x3f; +} + enum class AddressSize: uint8_t { b16 = 0, b32 = 1, }; +constexpr int byte_size(AddressSize size) { + return 2 << int(size); +} + +constexpr int bit_size(AddressSize size) { + return 16 << int(size); +} + enum class Source: uint8_t { // These are in SIB order; this matters for packing later on. diff --git a/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm b/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm index cc91cb5ed..a9ef904df 100644 --- a/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm +++ b/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm @@ -14,25 +14,20 @@ #include "../../../InstructionSets/x86/Decoder.hpp" #include "../../../InstructionSets/x86/DataPointerResolver.hpp" -namespace { +using namespace InstructionSet::x86; -using Operation = InstructionSet::x86::Operation; -using Instruction = InstructionSet::x86::Instruction; -using Model = InstructionSet::x86::Model; -using Source = InstructionSet::x86::Source; -using Size = InstructionSet::x86::DataSize; -using ScaleIndexBase = InstructionSet::x86::ScaleIndexBase; +namespace { // MARK: - Specific instruction asserts. -template void test(const InstructionT &instruction, int size, Operation operation) { +template void test(const InstructionT &instruction, DataSize size, Operation operation) { XCTAssertEqual(instruction.operation_size(), InstructionSet::x86::DataSize(size)); XCTAssertEqual(instruction.operation, operation); } template void test( const InstructionT &instruction, - int size, + DataSize size, Operation operation, InstructionSet::x86::DataPointer source, std::optional destination = std::nullopt, @@ -134,10 +129,10 @@ template std::vector: // jb 0x00000001 // dec %bx // mov $0x28,%ch - test(instructions[0], 2, Operation::SUB, Source::Immediate, Source::eAX, 0xea77); + test(instructions[0], DataSize::Word, Operation::SUB, Source::Immediate, Source::eAX, 0xea77); test(instructions[1], Operation::JB, std::nullopt, 0xfffc); - test(instructions[2], 2, Operation::DEC, Source::eBX, Source::eBX); - test(instructions[3], 1, Operation::MOV, Source::Immediate, Source::CH, 0x28); + test(instructions[2], DataSize::Word, Operation::DEC, Source::eBX, Source::eBX); + test(instructions[3], DataSize::Byte, Operation::MOV, Source::Immediate, Source::CH, 0x28); // ret // lret $0x4826 @@ -153,10 +148,10 @@ template std::vector: // out %ax,(%dx) // jo 0x00000037 // xchg %ax,%sp - test(instructions[8], 2, Operation::DEC, Source::eSI, Source::eSI); - test(instructions[9], 2, Operation::OUT, Source::eAX, Source::eDX); + test(instructions[8], DataSize::Word, Operation::DEC, Source::eSI, Source::eSI); + test(instructions[9], DataSize::Word, Operation::OUT, Source::eAX, Source::eDX); test(instructions[10], Operation::JO, std::nullopt, 0x20); - test(instructions[11], 2, Operation::XCHG, Source::eAX, Source::eSP); + test(instructions[11], DataSize::Word, Operation::XCHG, Source::eAX, Source::eSP); // ODA has: // c4 (bad) @@ -168,25 +163,25 @@ template std::vector: // c4 d4 (bad) // 93 XCHG AX, BX test(instructions[12], Operation::Invalid); - test(instructions[13], 2, Operation::XCHG, Source::eAX, Source::eBX); + test(instructions[13], DataSize::Word, Operation::XCHG, Source::eAX, Source::eBX); // inc %bx // cmp $0x8e,%al // [[ omitted: push $0x65 ]] // sbb 0x45(%bx,%si),%bh // adc %bh,0x3c(%bx) - test(instructions[14], 2, Operation::INC, Source::eBX, Source::eBX); - test(instructions[15], 1, Operation::CMP, Source::Immediate, Source::eAX, 0x8e); - test(instructions[16], 1, Operation::SBB, ScaleIndexBase(Source::eBX, Source::eSI), Source::BH, std::nullopt, 0x45); - test(instructions[17], 1, Operation::ADC, Source::BH, ScaleIndexBase(Source::eBX), std::nullopt, 0x3c); + test(instructions[14], DataSize::Word, Operation::INC, Source::eBX, Source::eBX); + test(instructions[15], DataSize::Byte, Operation::CMP, Source::Immediate, Source::eAX, 0x8e); + test(instructions[16], DataSize::Byte, Operation::SBB, ScaleIndexBase(Source::eBX, Source::eSI), Source::BH, std::nullopt, 0x45); + test(instructions[17], DataSize::Byte, Operation::ADC, Source::BH, ScaleIndexBase(Source::eBX), std::nullopt, 0x3c); // sbb %bx,0x16(%bp,%si) // xor %sp,0x2c(%si) // out %ax,$0xc6 // jge 0xffffffe0 - test(instructions[18], 2, Operation::SBB, Source::eBX, ScaleIndexBase(Source::eBP, Source::eSI), std::nullopt, 0x16); - test(instructions[19], 2, Operation::XOR, Source::eSP, ScaleIndexBase(Source::eSI), std::nullopt, 0x2c); - test(instructions[20], 2, Operation::OUT, Source::eAX, Source::DirectAddress, 0xc6); + test(instructions[18], DataSize::Word, Operation::SBB, Source::eBX, ScaleIndexBase(Source::eBP, Source::eSI), std::nullopt, 0x16); + test(instructions[19], DataSize::Word, Operation::XOR, Source::eSP, ScaleIndexBase(Source::eSI), std::nullopt, 0x2c); + test(instructions[20], DataSize::Word, Operation::OUT, Source::eAX, Source::DirectAddress, 0xc6); test(instructions[21], Operation::JNL, std::nullopt, 0xffb0); // mov $0x49,%ch @@ -194,38 +189,38 @@ template std::vector: // mov $0xcbc0,%dx // adc $0x7e,%al // jno 0x0000000b - test(instructions[22], 1, Operation::MOV, Source::Immediate, Source::CH, 0x49); - test(instructions[23], 2, Operation::MOV, Source::Immediate, Source::eDX, 0xcbc0); - test(instructions[24], 1, Operation::ADC, Source::Immediate, Source::eAX, 0x7e); + test(instructions[22], DataSize::Byte, Operation::MOV, Source::Immediate, Source::CH, 0x49); + test(instructions[23], DataSize::Word, Operation::MOV, Source::Immediate, Source::eDX, 0xcbc0); + test(instructions[24], DataSize::Byte, Operation::ADC, Source::Immediate, Source::eAX, 0x7e); test(instructions[25], Operation::JNO, std::nullopt, 0xffd0); // push %ax // js 0x0000007b // add (%di),%bx // in $0xc9,%ax - test(instructions[26], 2, Operation::PUSH, Source::eAX); + test(instructions[26], DataSize::Word, Operation::PUSH, Source::eAX); test(instructions[27], Operation::JS, std::nullopt, 0x3d); - test(instructions[28], 2, Operation::ADD, ScaleIndexBase(Source::eDI), Source::eBX); - test(instructions[29], 2, Operation::IN, Source::DirectAddress, Source::eAX, 0xc9); + test(instructions[28], DataSize::Word, Operation::ADD, ScaleIndexBase(Source::eDI), Source::eBX); + test(instructions[29], DataSize::Word, Operation::IN, Source::DirectAddress, Source::eAX, 0xc9); // xchg %ax,%di // ret // fwait // out %al,$0xd3 - test(instructions[30], 2, Operation::XCHG, Source::eAX, Source::eDI); + test(instructions[30], DataSize::Word, Operation::XCHG, Source::eAX, Source::eDI); test(instructions[31], Operation::RETN); test(instructions[32], Operation::WAIT); - test(instructions[33], 1, Operation::OUT, Source::eAX, Source::DirectAddress, 0xd3); + test(instructions[33], DataSize::Byte, Operation::OUT, Source::eAX, Source::DirectAddress, 0xd3); // [[ omitted: insb (%dx),%es:(%di) ]] // pop %ax // dec %bp // jbe 0xffffffcc // inc %sp - test(instructions[34], 2, Operation::POP, Source::eAX); - test(instructions[35], 2, Operation::DEC, Source::eBP, Source::eBP); + test(instructions[34], DataSize::Word, Operation::POP, Source::eAX); + test(instructions[35], DataSize::Word, Operation::DEC, Source::eBP, Source::eBP); test(instructions[36], Operation::JBE, std::nullopt, 0xff80); - test(instructions[37], 2, Operation::INC, Source::eSP, Source::eSP); + test(instructions[37], DataSize::Word, Operation::INC, Source::eSP, Source::eSP); // (bad) // lahf @@ -233,15 +228,15 @@ template std::vector: // mov $0x12a1,%bp test(instructions[38], Operation::Invalid); test(instructions[39], Operation::LAHF); - test(instructions[40], 2, Operation::MOVS); /* Arguments are implicit. */ - test(instructions[41], 2, Operation::MOV, Source::Immediate, Source::eBP, 0x12a1); + test(instructions[40], DataSize::Word, Operation::MOVS); // Arguments are implicit. + test(instructions[41], DataSize::Word, Operation::MOV, Source::Immediate, Source::eBP, 0x12a1); // lds (%bx,%di),%bp // [[ omitted: leave ]] // sahf // fdiv %st(3),%st // iret - test(instructions[42], 2, Operation::LDS); + test(instructions[42], DataSize::Word, Operation::LDS); test(instructions[43], Operation::SAHF); test(instructions[44], Operation::ESC); test(instructions[45], Operation::IRET); @@ -250,40 +245,40 @@ template std::vector: // cmp %bx,-0x70(%di) // adc $0xb8c3,%ax // lods %ds:(%si),%ax - test(instructions[46], 2, Operation::XCHG, Source::eAX, Source::eDX); - test(instructions[47], 2, Operation::CMP, Source::eBX, ScaleIndexBase(Source::eDI), std::nullopt, 0xff90); - test(instructions[48], 2, Operation::ADC, Source::Immediate, Source::eAX, 0xb8c3); - test(instructions[49], 2, Operation::LODS); + test(instructions[46], DataSize::Word, Operation::XCHG, Source::eAX, Source::eDX); + test(instructions[47], DataSize::Word, Operation::CMP, Source::eBX, ScaleIndexBase(Source::eDI), std::nullopt, 0xff90); + test(instructions[48], DataSize::Word, Operation::ADC, Source::Immediate, Source::eAX, 0xb8c3); + test(instructions[49], DataSize::Word, Operation::LODS); // call 0x0000172d // dec %dx // mov $0x9e,%al // stc test(instructions[50], Operation::CALLD, uint16_t(0x16c8)); - test(instructions[51], 2, Operation::DEC, Source::eDX, Source::eDX); - test(instructions[52], 1, Operation::MOV, Source::Immediate, Source::eAX, 0x9e); + test(instructions[51], DataSize::Word, Operation::DEC, Source::eDX, Source::eDX); + test(instructions[52], DataSize::Byte, Operation::MOV, Source::Immediate, Source::eAX, 0x9e); test(instructions[53], Operation::STC); // mov $0xea56,%di // dec %si // std // in $0x5a,%al - test(instructions[54], 2, Operation::MOV, Source::Immediate, Source::eDI, 0xea56); - test(instructions[55], 2, Operation::DEC, Source::eSI, Source::eSI); + test(instructions[54], DataSize::Word, Operation::MOV, Source::Immediate, Source::eDI, 0xea56); + test(instructions[55], DataSize::Word, Operation::DEC, Source::eSI, Source::eSI); test(instructions[56], Operation::STD); - test(instructions[57], 1, Operation::IN, Source::DirectAddress, Source::eAX, 0x5a); + test(instructions[57], DataSize::Byte, Operation::IN, Source::DirectAddress, Source::eAX, 0x5a); // and 0x5b2c(%bp,%si),%bp // sub %dl,%dl // negw 0x18(%bx) // xchg %dl,0x6425(%bx,%si) - test(instructions[58], 2, Operation::AND, ScaleIndexBase(Source::eBP, Source::eSI), Source::eBP, std::nullopt, 0x5b2c); - test(instructions[59], 1, Operation::SUB, Source::eDX, Source::eDX); - test(instructions[60], 2, Operation::NEG, ScaleIndexBase(Source::eBX), ScaleIndexBase(Source::eBX), std::nullopt, 0x18); - test(instructions[61], 1, Operation::XCHG, ScaleIndexBase(Source::eBX, Source::eSI), Source::eDX, std::nullopt, 0x6425); + test(instructions[58], DataSize::Word, Operation::AND, ScaleIndexBase(Source::eBP, Source::eSI), Source::eBP, std::nullopt, 0x5b2c); + test(instructions[59], DataSize::Byte, Operation::SUB, Source::eDX, Source::eDX); + test(instructions[60], DataSize::Word, Operation::NEG, ScaleIndexBase(Source::eBX), ScaleIndexBase(Source::eBX), std::nullopt, 0x18); + test(instructions[61], DataSize::Byte, Operation::XCHG, ScaleIndexBase(Source::eBX, Source::eSI), Source::eDX, std::nullopt, 0x6425); // mov $0xc3,%bh - test(instructions[62], 1, Operation::MOV, Source::Immediate, Source::BH, 0xc3); + test(instructions[62], DataSize::Byte, Operation::MOV, Source::Immediate, Source::BH, 0xc3); } - (void)test83 { @@ -294,9 +289,9 @@ template std::vector: }); XCTAssertEqual(instructions.size(), 3); - test(instructions[0], 2, Operation::ADC, Source::Immediate, ScaleIndexBase(Source::eBX, Source::eSI), 0xff80); - test(instructions[1], 2, Operation::CMP, Source::Immediate, ScaleIndexBase(Source::eBP, Source::eDI), 0x4); - test(instructions[2], 2, Operation::SUB, Source::Immediate, ScaleIndexBase(Source::eBX), 0x9); + test(instructions[0], DataSize::Word, Operation::ADC, Source::Immediate, ScaleIndexBase(Source::eBX, Source::eSI), 0xff80); + test(instructions[1], DataSize::Word, Operation::CMP, Source::Immediate, ScaleIndexBase(Source::eBP, Source::eDI), 0x4); + test(instructions[2], DataSize::Word, Operation::SUB, Source::Immediate, ScaleIndexBase(Source::eBX), 0x9); } - (void)testFar { @@ -308,7 +303,4 @@ template std::vector: test_far(instructions[0], Operation::CALLF, 0x7856, 0x3412); } -- (void)testSequence2 { -} - @end