diff --git a/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm b/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm index c1508b3e2..b8f947f95 100644 --- a/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm +++ b/OSBindings/Mac/Clock SignalTests/x86DecoderTests.mm @@ -50,10 +50,10 @@ namespace { instructions.clear(); const uint8_t *byte = stream.begin(); while(byte != stream.end()) { - const auto next = decoder.decode(byte, stream.end() - byte); - if(next.size() <= 0) break; + const auto [size, next] = decoder.decode(byte, stream.end() - byte); + if(size <= 0) break; instructions.push_back(next); - byte += next.size(); + byte += size; } } diff --git a/Processors/Decoders/PowerPC/PowerPC.hpp b/Processors/Decoders/PowerPC/PowerPC.hpp index 480c89829..e5f086653 100644 --- a/Processors/Decoders/PowerPC/PowerPC.hpp +++ b/Processors/Decoders/PowerPC/PowerPC.hpp @@ -73,17 +73,15 @@ enum class Operation: uint8_t { Implementation note: because the PowerPC encoding is particularly straightforward, only the operation has been decoded ahead of time; all other fields are decoded on-demand. + + It would be possible to partition the ordering of Operations into user followed by supervisor, + eliminating the storage necessary for a flag, but it wouldn't save anything due to alignment. */ struct Instruction { Operation operation = Operation::Undefined; bool is_supervisor = false; uint32_t opcode = 0; - // PowerPC uses a fixed-size instruction word. - int size() const { - return 4; - } - Instruction() {} Instruction(uint32_t opcode) : opcode(opcode) {} Instruction(Operation operation, uint32_t opcode, bool is_supervisor = false) : operation(operation), is_supervisor(is_supervisor), opcode(opcode) {} diff --git a/Processors/Decoders/x86/x86.cpp b/Processors/Decoders/x86/x86.cpp index 236289bf9..635523ed5 100644 --- a/Processors/Decoders/x86/x86.cpp +++ b/Processors/Decoders/x86/x86.cpp @@ -10,13 +10,14 @@ #include #include +#include using namespace CPU::Decoder::x86; // Only 8086 is suppoted for now. Decoder::Decoder(Model) {} -Instruction Decoder::decode(const uint8_t *source, size_t length) { +std::pair Decoder::decode(const uint8_t *source, size_t length) { const uint8_t *const end = source + length; // MARK: - Prefixes (if present) and the opcode. @@ -36,6 +37,7 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { /// Handles instructions of the form rr, kk and rr, jjkk, i.e. a destination register plus an operand. #define RegData(op, dest, size) \ SetOpSrcDestSize(op, DirectAddress, dest, size); \ + operand_size_ = size; \ phase_ = Phase::AwaitingDisplacementOrOperand /// Handles instructions of the form Ax, jjkk where the latter is implicitly an address. @@ -79,9 +81,9 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { switch(instr_) { default: { - const Instruction instruction(consumed_); + const auto result = std::make_pair(consumed_, Instruction()); reset_parsing(); - return instruction; + return result; } #define PartialBlock(start, operation) \ @@ -390,9 +392,11 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { source_ = destination_ = memreg; switch(reg) { - default: + default: { + const auto result = std::make_pair(consumed_, Instruction()); reset_parsing(); - return Instruction(consumed_); + return result; + } case 0: operation_ = Operation::TEST; break; case 2: operation_ = Operation::NOT; break; @@ -413,8 +417,9 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { }; if(reg & 4) { + const auto result = std::make_pair(consumed_, Instruction()); reset_parsing(); - return Instruction(consumed_); + return result; } destination_ = seg_table[reg]; @@ -424,9 +429,11 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { destination_ = memreg; switch(reg) { - default: + default: { + const auto result = std::make_pair(consumed_, Instruction()); reset_parsing(); - return Instruction(consumed_); + return result; + } case 0: operation_ = Operation::ROL; break; case 2: operation_ = Operation::ROR; break; @@ -442,9 +449,11 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { source_ = destination_ = memreg; switch(reg) { - default: + default: { + const auto result = std::make_pair(consumed_, Instruction()); reset_parsing(); - return Instruction(consumed_); + return result; + } case 0: operation_ = Operation::INC; break; case 1: operation_ = Operation::DEC; break; @@ -455,9 +464,11 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { source_ = destination_ = memreg; switch(reg) { - default: + default: { + const auto result = std::make_pair(consumed_, Instruction()); reset_parsing(); - return Instruction(consumed_); + return result; + } case 0: operation_ = Operation::INC; break; case 1: operation_ = Operation::DEC; break; @@ -482,7 +493,7 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { if(reg != 0) { reset_parsing(); - return Instruction(consumed_); + return std::make_pair(consumed_, Instruction()); } break; @@ -513,19 +524,31 @@ Instruction Decoder::decode(const uint8_t *source, size_t length) { phase_ = Phase::ReadyToPost; } else { // Provide a genuine measure of further bytes required. - return Instruction(-(outstanding_bytes - bytes_to_consume)); + return std::make_pair(-(outstanding_bytes - bytes_to_consume), Instruction()); } } // MARK: - Check for completion. if(phase_ == Phase::ReadyToPost) { - Instruction result(operation_, Size(operation_size_), source_, destination_, consumed_); + const auto result = std::make_pair( + consumed_, + Instruction( + operation_, + source_, + destination_, + lock_, + segment_override_, + repetition_, + Size(operation_size_), + 0, + 0) + ); reset_parsing(); phase_ = Phase::Instruction; return result; } // i.e. not done yet. - return Instruction(); + return std::make_pair(0, Instruction()); } diff --git a/Processors/Decoders/x86/x86.hpp b/Processors/Decoders/x86/x86.hpp index d8891aeaf..5efba12fd 100644 --- a/Processors/Decoders/x86/x86.hpp +++ b/Processors/Decoders/x86/x86.hpp @@ -124,42 +124,58 @@ class Instruction { private: // b0, b1: a Repetition; - // b2+: size. + // b2+: operation size. uint8_t repetition_size_ = 0; // b0–b5: source; - // b6–b11: repetition; + // b6–b11: destination; // b12–b14: segment override; // b15: lock. uint16_t sources_ = 0; // Unpackable fields. - int16_t displacement_ = 0; - int16_t operand_ = 0; // ... or used to store a segment for far operations. + uint16_t displacement_ = 0; + uint16_t operand_ = 0; // ... or used to store a segment for far operations. public: - Source source() const { return Source(sources_ & 0x3f); } - Source destination() const { return Source((sources_ >> 6) & 0x3f); } - bool lock() const { return sources_ & 0x8000; } - Source segment_override() const { return Source((sources_ >> 12) & 7); } - - Size operand_size() const { return Size::Implied; } - Size operation_size() const { return Size::Implied; } - - uint16_t segment() const { return uint16_t(operand_); } + Source source() const { return Source(sources_ & 0x3f); } + Source destination() const { return Source((sources_ >> 6) & 0x3f); } + bool lock() const { return sources_ & 0x8000; } + Source segment_override() const { return Source((sources_ >> 12) & 7); } Repetition repetition() const { return Repetition(repetition_size_ & 3); } - int size() const { return int(repetition_size_ >> 2); } + Size operation_size() const { return Size(repetition_size_ >> 2); } + + uint16_t segment() const { return uint16_t(operand_); } template type displacement(); template type immediate(); - Instruction() {} - Instruction(int) {} - Instruction(Operation, Size, Source, Source, int) {} + Instruction( + Operation operation, + Source source, + Source destination, + bool lock, + Source segment_override, + Repetition repetition, + Size operation_size, + uint16_t displacement, + uint16_t operand) : + operation(operation), + repetition_size_(uint8_t((int(operation_size) << 2) | int(repetition))), + sources_(uint16_t( + int(source) | + (int(destination) << 6) | + (int(segment_override) << 12) | + (int(lock) << 15) + )), + displacement_(displacement), + operand_(operand) {} }; +static_assert(sizeof(Instruction) <= 8); + /*! Implements Intel x86 instruction decoding. @@ -170,13 +186,13 @@ struct Decoder { Decoder(Model model); /*! - @returns an @c Instruction with a positive size to indicate successful decoding; a + @returns an @c Instruction plus a size; a positive size to indicate successful decoding; a negative size specifies the [negatived] number of further bytes the caller should ideally collect before calling again. The caller is free to call with fewer, but may not get a decoded instruction in response, and the decoder may still not be able to complete decoding even if given that number of bytes. */ - Instruction decode(const uint8_t *source, size_t length); + std::pair decode(const uint8_t *source, size_t length); private: enum class Phase {