From 5058a8b96a3c88d4407e43843a8bd8b792d41ab2 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 12 Jan 2021 21:49:22 -0500 Subject: [PATCH] Completes the first test stream. ... and improves decoding consistency in conjunction. --- .../Mac/Clock SignalTests/x86DecoderTests.mm | 123 ++++++++++++++++-- Processors/Decoders/x86/x86.cpp | 9 +- Processors/Decoders/x86/x86.hpp | 2 + 3 files changed, 123 insertions(+), 11 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm b/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm index 6d4321818..f2a5711b7 100644 --- a/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm +++ b/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm @@ -37,11 +37,42 @@ namespace { XCTAssertEqual(instruction.operation, operation); } -- (void)assert:(Instruction &)instruction operation:(Operation)operation size:(int)size source:(Source)source destination:(Source)destination { +- (void)assert:(Instruction &)instruction operation:(Operation)operation size:(int)size { + XCTAssertEqual(instruction.operation, operation); + XCTAssertEqual(instruction.operation_size(), CPU::Decoder::x86::Size(size)); +} + +- (void)assert:(Instruction &)instruction operation:(Operation)operation size:(int)size source:(Source)source destination:(Source)destination displacement:(int16_t)displacement { XCTAssertEqual(instruction.operation, operation); XCTAssertEqual(instruction.operation_size(), CPU::Decoder::x86::Size(size)); XCTAssertEqual(instruction.source(), source); XCTAssertEqual(instruction.destination(), destination); + XCTAssertEqual(instruction.displacement(), displacement); +} + +- (void)assert:(Instruction &)instruction operation:(Operation)operation size:(int)size source:(Source)source destination:(Source)destination displacement:(int16_t)displacement operand:(uint16_t)operand { + [self assert:instruction operation:operation size:size source:source destination:destination displacement:displacement]; + XCTAssertEqual(instruction.operand(), operand); +} + +- (void)assert:(Instruction &)instruction operation:(Operation)operation size:(int)size source:(Source)source destination:(Source)destination operand:(uint16_t)operand { + [self assert:instruction operation:operation size:size source:source destination:destination displacement:0 operand:operand]; +} + +- (void)assert:(Instruction &)instruction operation:(Operation)operation size:(int)size source:(Source)source destination:(Source)destination { + [self assert:instruction operation:operation size:size source:source destination:destination displacement:0]; +} + +- (void)assert:(Instruction &)instruction operation:(Operation)operation size:(int)size source:(Source)source { + XCTAssertEqual(instruction.operation, operation); + XCTAssertEqual(instruction.operation_size(), CPU::Decoder::x86::Size(size)); + XCTAssertEqual(instruction.source(), source); +} + +- (void)assert:(Instruction &)instruction operation:(Operation)operation size:(int)size destination:(Source)destination { + XCTAssertEqual(instruction.operation, operation); + XCTAssertEqual(instruction.operation_size(), CPU::Decoder::x86::Size(size)); + XCTAssertEqual(instruction.destination(), destination); } - (void)assert:(Instruction &)instruction operation:(Operation)operation size:(int)size operand:(uint16_t)operand destination:(Source)destination { @@ -50,6 +81,7 @@ namespace { XCTAssertEqual(instruction.destination(), destination); XCTAssertEqual(instruction.source(), Source::Immediate); XCTAssertEqual(instruction.operand(), operand); + XCTAssertEqual(instruction.displacement(), 0); } - (void)assert:(Instruction &)instruction operation:(Operation)operation displacement:(int16_t)displacement { @@ -60,6 +92,7 @@ namespace { - (void)assert:(Instruction &)instruction operation:(Operation)operation operand:(uint16_t)operand { XCTAssertEqual(instruction.operation, operation); XCTAssertEqual(instruction.operand(), operand); + XCTAssertEqual(instruction.displacement(), 0); } // MARK: - Decoder @@ -81,6 +114,7 @@ namespace { while(byte != stream.end()) { const auto [size, next] = decoder.decode(byte, stream.end() - byte); if(size <= 0) break; + NSLog(@"%@ %@", @(instructions.size()), @(size)); instructions.push_back(next); byte += size; } @@ -122,7 +156,7 @@ namespace { // ret // lret $0x4826 - // [[ gs insw (%dx),%es:(%di) ]] + // [[ omitted: gs insw (%dx),%es:(%di) ]] // jnp 0xffffffaf // ret $0x4265 [self assert:instructions[4] operation:Operation::RETN]; @@ -139,63 +173,132 @@ namespace { [self assert:instructions[10] operation:Operation::JO displacement:0x20]; [self assert:instructions[11] operation:Operation::XCHG size:2 source:Source::AX destination:Source::SP]; - // (bad) - // aam $0x93 + // ODA has: + // c4 (bad) + // d4 93 aam $0x93 + // + // That assumes that upon discovering that the d4 doesn't make a valid LES, + // it can become an instruction byte. I'm not persuaded. So I'm taking: + // + // c4 d4 (bad) + // 93 XCHG AX, BX + [self assert:instructions[12] operation:Operation::Invalid]; + [self assert:instructions[13] operation:Operation::XCHG size:2 source:Source::AX destination:Source::BX]; + // inc %bx // cmp $0x8e,%al -// [self assert:instructions[12] operation:Operation::Invalid]; - - // push $0x65 + // [[ omitted: push $0x65 ]] // sbb 0x45(%bx,%si),%bh // adc %bh,0x3c(%bx) + [self assert:instructions[14] operation:Operation::INC size:2 source:Source::BX destination:Source::BX]; + [self assert:instructions[15] operation:Operation::CMP size:1 operand:0x8e destination:Source::AL]; + [self assert:instructions[16] operation:Operation::SBB size:1 source:Source::IndBXPlusSI destination:Source::BH displacement:0x45]; + [self assert:instructions[17] operation:Operation::ADC size:1 source:Source::BH destination:Source::IndBX displacement:0x3c]; + // sbb %bx,0x16(%bp,%si) // xor %sp,0x2c(%si) // out %ax,$0xc6 // jge 0xffffffe0 + [self assert:instructions[18] operation:Operation::SBB size:2 source:Source::BX destination:Source::IndBPPlusSI displacement:0x16]; + [self assert:instructions[19] operation:Operation::XOR size:2 source:Source::SP destination:Source::IndSI displacement:0x2c]; + [self assert:instructions[20] operation:Operation::OUT size:2 source:Source::AX destination:Source::DirectAddress operand:0xc6]; + [self assert:instructions[21] operation:Operation::JNL displacement:0xffb0]; + // mov $0x49,%ch - // addr32 popa + // [[ omitted: addr32 popa ]] // mov $0xcbc0,%dx // adc $0x7e,%al // jno 0x0000000b + [self assert:instructions[22] operation:Operation::MOV size:1 operand:0x49 destination:Source::CH]; + [self assert:instructions[23] operation:Operation::MOV size:2 operand:0xcbc0 destination:Source::DX]; + [self assert:instructions[24] operation:Operation::ADC size:1 operand:0x7e destination:Source::AL]; + [self assert:instructions[25] operation:Operation::JNO displacement:0xffd0]; + // push %ax // js 0x0000007b // add (%di),%bx // in $0xc9,%ax + [self assert:instructions[26] operation:Operation::PUSH size:2 source:Source::AX]; + [self assert:instructions[27] operation:Operation::JS displacement:0x3d]; + [self assert:instructions[28] operation:Operation::ADD size:2 source:Source::IndDI destination:Source::BX]; + [self assert:instructions[29] operation:Operation::IN size:2 source:Source::DirectAddress destination:Source::AX operand:0xc9]; + // xchg %ax,%di // ret // fwait // out %al,$0xd3 - // insb (%dx),%es:(%di) + [self assert:instructions[30] operation:Operation::XCHG size:2 source:Source::AX destination:Source::DI]; + [self assert:instructions[31] operation:Operation::RETN]; + [self assert:instructions[32] operation:Operation::WAIT]; + [self assert:instructions[33] operation:Operation::OUT size:1 source:Source::AL destination:Source::DirectAddress operand:0xd3]; + + // [[ omitted: insb (%dx),%es:(%di) ]] // pop %ax // dec %bp // jbe 0xffffffcc // inc %sp + [self assert:instructions[34] operation:Operation::POP size:2 destination:Source::AX]; + [self assert:instructions[35] operation:Operation::DEC size:2 source:Source::BP destination:Source::BP]; + [self assert:instructions[36] operation:Operation::JBE displacement:0xff80]; + [self assert:instructions[37] operation:Operation::INC size:2 source:Source::SP destination:Source::SP]; + // (bad) // lahf // movsw %ds:(%si),%es:(%di) // mov $0x12a1,%bp + [self assert:instructions[38] operation:Operation::Invalid]; + [self assert:instructions[39] operation:Operation::LAHF]; + [self assert:instructions[40] operation:Operation::MOVS size:2]; + [self assert:instructions[41] operation:Operation::MOV size:2 operand:0x12a1 destination:Source::BP]; + // lds (%bx,%di),%bp - // leave + // [[ omitted: leave ]] // sahf // fdiv %st(3),%st // iret + [self assert:instructions[42] operation:Operation::LDS size:4]; + [self assert:instructions[43] operation:Operation::SAHF]; + [self assert:instructions[44] operation:Operation::ESC]; + [self assert:instructions[45] operation:Operation::IRET]; + // xchg %ax,%dx // cmp %bx,-0x70(%di) // adc $0xb8c3,%ax // lods %ds:(%si),%ax + [self assert:instructions[46] operation:Operation::XCHG size:2 source:Source::AX destination:Source::DX]; + [self assert:instructions[47] operation:Operation::CMP size:2 source:Source::BX destination:Source::IndDI displacement:0xff90]; + [self assert:instructions[48] operation:Operation::ADC size:2 operand:0xb8c3 destination:Source::AX]; + [self assert:instructions[49] operation:Operation::LODS size:2]; + // call 0x0000172d // dec %dx // mov $0x9e,%al // stc + [self assert:instructions[50] operation:Operation::CALLD operand:0x16c8]; + [self assert:instructions[51] operation:Operation::DEC size:2 source:Source::DX destination:Source::DX]; + [self assert:instructions[52] operation:Operation::MOV size:1 operand:0x9e destination:Source::AL]; + [self assert:instructions[53] operation:Operation::STC]; + // mov $0xea56,%di // dec %si // std // in $0x5a,%al + [self assert:instructions[54] operation:Operation::MOV size:2 operand:0xea56 destination:Source::DI]; + [self assert:instructions[55] operation:Operation::DEC size:2 source:Source::SI destination:Source::SI]; + [self assert:instructions[56] operation:Operation::STD]; + [self assert:instructions[57] operation:Operation::IN size:1 source:Source::DirectAddress destination:Source::AL operand:0x5a]; + // and 0x5b2c(%bp,%si),%bp // sub %dl,%dl // negw 0x18(%bx) // xchg %dl,0x6425(%bx,%si) + [self assert:instructions[58] operation:Operation::AND size:2 source:Source::IndBPPlusSI destination:Source::BP displacement:0x5b2c]; + [self assert:instructions[59] operation:Operation::SUB size:1 source:Source::DL destination:Source::DL]; + [self assert:instructions[60] operation:Operation::NEG size:2 source:Source::IndBX destination:Source::IndBX displacement:0x18]; + [self assert:instructions[61] operation:Operation::XCHG size:1 source:Source::IndBXPlusSI destination:Source::DL displacement:0x6425]; + // mov $0xc3,%bh + [self assert:instructions[62] operation:Operation::MOV size:1 operand:0xc3 destination:Source::BH]; } @end diff --git a/Processors/Decoders/x86/x86.cpp b/Processors/Decoders/x86/x86.cpp index 0b63a6399..73b26c5d4 100644 --- a/Processors/Decoders/x86/x86.cpp +++ b/Processors/Decoders/x86/x86.cpp @@ -51,6 +51,7 @@ std::pair Decoder::decode(const uint8_t *source, size_t length #define AddrReg(op, source, op_size, addr_size) \ SetOpSrcDestSize(op, source, DirectAddress, op_size); \ operand_size_ = addr_size; \ + destination_ = Source::DirectAddress; \ phase_ = Phase::AwaitingDisplacementOrOperand /// Covers both `mem/reg, reg` and `reg, mem/reg`. @@ -377,6 +378,13 @@ std::pair Decoder::decode(const uint8_t *source, size_t length // Other operand is just a register. case 3: memreg = reg_table[operation_size_][rm]; + + // LES and LDS accept a real memory argument only. + if(operation_ == Operation::LES || operation_ == Operation::LDS) { + const auto result = std::make_pair(consumed_, Instruction()); + reset_parsing(); + return result; + } break; } @@ -586,7 +594,6 @@ std::pair Decoder::decode(const uint8_t *source, size_t length operand_) ); reset_parsing(); - phase_ = Phase::Instruction; return result; } diff --git a/Processors/Decoders/x86/x86.hpp b/Processors/Decoders/x86/x86.hpp index 0c7894bfe..3e0d9c79d 100644 --- a/Processors/Decoders/x86/x86.hpp +++ b/Processors/Decoders/x86/x86.hpp @@ -289,9 +289,11 @@ struct Decoder { void reset_parsing() { consumed_ = operand_bytes_ = 0; displacement_size_ = operand_size_ = 0; + displacement_ = operand_ = 0; lock_ = false; segment_override_ = Source::None; repetition_ = Repetition::None; + phase_ = Phase::Instruction; } };