1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-26 23:52:26 +00:00

Adds byte-by-byte decoder test; corrects divergences.

This commit is contained in:
Thomas Harte 2021-01-13 21:51:18 -05:00
parent 8c0e06e645
commit 2c72a77a25
3 changed files with 62 additions and 53 deletions

View File

@ -23,7 +23,7 @@ namespace {
@end
/*!
Tests PowerPC decoding by throwing a bunch of randomly-generated
Tests 8086 decoding by throwing a bunch of randomly-generated
word streams and checking that the result matches what I got from a
disassembler elsewhere.
*/
@ -98,26 +98,30 @@ namespace {
// MARK: - Decoder
- (void)decode:(const std::initializer_list<uint8_t> &)stream {
// Decode by offering up all data at once.
CPU::Decoder::x86::Decoder decoder(CPU::Decoder::x86::Model::i8086);
// TODO: test that byte-at-a-time decoding gives the same results, as a freebie.
// instructions.clear();
// for(auto item: stream) {
// const auto next = decoder.decode(&item, 1);
// if(next.size() > 0) {
// instructions.push_back(next);
// }
// }
instructions.clear();
const uint8_t *byte = stream.begin();
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;
}
// Grab a byte-at-a-time decoding and check that it matches the previous.
{
CPU::Decoder::x86::Decoder decoder(CPU::Decoder::x86::Model::i8086);
auto previous_instruction = instructions.begin();
for(auto item: stream) {
const auto [size, next] = decoder.decode(&item, 1);
if(size > 0) {
XCTAssert(next == *previous_instruction);
++previous_instruction;
}
}
}
}
// MARK: - Tests
@ -256,7 +260,7 @@ namespace {
// sahf
// fdiv %st(3),%st
// iret
[self assert:instructions[42] operation:Operation::LDS size:4];
[self assert:instructions[42] operation:Operation::LDS size:2];
[self assert:instructions[43] operation:Operation::SAHF];
[self assert:instructions[44] operation:Operation::ESC];
[self assert:instructions[45] operation:Operation::IRET];

View File

@ -238,8 +238,8 @@ std::pair<int, Instruction> Decoder::decode(const uint8_t *source, size_t length
case 0xc2: RegData(RETN, None, 2); break;
case 0xc3: Complete(RETN, None, None, 2); break;
case 0xc4: MemRegReg(LES, Reg_MemReg, 4); break;
case 0xc5: MemRegReg(LDS, Reg_MemReg, 4); 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;
@ -552,7 +552,7 @@ std::pair<int, Instruction> Decoder::decode(const uint8_t *source, size_t length
default: assert(false);
}
phase_ = Phase::AwaitingDisplacementOrOperand;
phase_ = (displacement_size_ + operand_size_) ? Phase::AwaitingDisplacementOrOperand : Phase::ReadyToPost;
}
// MARK: - Displacement and operand.
@ -560,45 +560,41 @@ std::pair<int, Instruction> Decoder::decode(const uint8_t *source, size_t length
if(phase_ == Phase::AwaitingDisplacementOrOperand && source != end) {
const int required_bytes = displacement_size_ + operand_size_;
if(!required_bytes) {
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);
++source;
}
consumed_ += bytes_to_consume;
operand_bytes_ += bytes_to_consume;
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 2: operand_ = inward_data_ >> 48; inward_data_ <<= 16; 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;
}
} else {
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);
++source;
}
consumed_ += bytes_to_consume;
operand_bytes_ += bytes_to_consume;
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) {
operand_ |= (operand_ & 0x80) ? 0xff00 : 0x0000;
}
break;
case 2: operand_ = inward_data_ >> 48; inward_data_ <<= 16; 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;
}
} else {
// Provide a genuine measure of further bytes required.
return std::make_pair(-(outstanding_bytes - bytes_to_consume), Instruction());
}
// Provide a genuine measure of further bytes required.
return std::make_pair(-(outstanding_bytes - bytes_to_consume), Instruction());
}
}

View File

@ -122,6 +122,14 @@ class Instruction {
public:
Operation operation = Operation::Invalid;
bool operator ==(const Instruction &rhs) const {
return
repetition_size_ == rhs.repetition_size_ &&
sources_ == rhs.sources_ &&
displacement_ == rhs.displacement_ &&
operand_ == rhs.operand_;
}
private:
// b0, b1: a Repetition;
// b2+: operation size.
@ -300,6 +308,7 @@ struct Decoder {
segment_override_ = Source::None;
repetition_ = Repetition::None;
phase_ = Phase::Instruction;
source_ = destination_ = Source::None;
}
};