mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-30 04:50:08 +00:00
Adds byte-by-byte decoder test; corrects divergences.
This commit is contained in:
parent
8c0e06e645
commit
2c72a77a25
@ -23,7 +23,7 @@ namespace {
|
|||||||
@end
|
@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
|
word streams and checking that the result matches what I got from a
|
||||||
disassembler elsewhere.
|
disassembler elsewhere.
|
||||||
*/
|
*/
|
||||||
@ -98,26 +98,30 @@ namespace {
|
|||||||
// MARK: - Decoder
|
// MARK: - Decoder
|
||||||
|
|
||||||
- (void)decode:(const std::initializer_list<uint8_t> &)stream {
|
- (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);
|
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();
|
instructions.clear();
|
||||||
const uint8_t *byte = stream.begin();
|
const uint8_t *byte = stream.begin();
|
||||||
while(byte != stream.end()) {
|
while(byte != stream.end()) {
|
||||||
const auto [size, next] = decoder.decode(byte, stream.end() - byte);
|
const auto [size, next] = decoder.decode(byte, stream.end() - byte);
|
||||||
if(size <= 0) break;
|
if(size <= 0) break;
|
||||||
NSLog(@"%@ %@", @(instructions.size()), @(size));
|
|
||||||
instructions.push_back(next);
|
instructions.push_back(next);
|
||||||
byte += size;
|
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
|
// MARK: - Tests
|
||||||
@ -256,7 +260,7 @@ namespace {
|
|||||||
// sahf
|
// sahf
|
||||||
// fdiv %st(3),%st
|
// fdiv %st(3),%st
|
||||||
// iret
|
// 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[43] operation:Operation::SAHF];
|
||||||
[self assert:instructions[44] operation:Operation::ESC];
|
[self assert:instructions[44] operation:Operation::ESC];
|
||||||
[self assert:instructions[45] operation:Operation::IRET];
|
[self assert:instructions[45] operation:Operation::IRET];
|
||||||
|
@ -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 0xc2: RegData(RETN, None, 2); break;
|
||||||
case 0xc3: Complete(RETN, None, None, 2); break;
|
case 0xc3: Complete(RETN, None, None, 2); break;
|
||||||
case 0xc4: MemRegReg(LES, Reg_MemReg, 4); break;
|
case 0xc4: MemRegReg(LES, Reg_MemReg, 2); break;
|
||||||
case 0xc5: MemRegReg(LDS, Reg_MemReg, 4); break;
|
case 0xc5: MemRegReg(LDS, Reg_MemReg, 2); break;
|
||||||
case 0xc6: MemRegReg(MOV, MemRegMOV, 1); break;
|
case 0xc6: MemRegReg(MOV, MemRegMOV, 1); break;
|
||||||
case 0xc7: MemRegReg(MOV, MemRegMOV, 2); 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);
|
default: assert(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
phase_ = Phase::AwaitingDisplacementOrOperand;
|
phase_ = (displacement_size_ + operand_size_) ? Phase::AwaitingDisplacementOrOperand : Phase::ReadyToPost;
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Displacement and operand.
|
// MARK: - Displacement and operand.
|
||||||
@ -560,9 +560,6 @@ std::pair<int, Instruction> Decoder::decode(const uint8_t *source, size_t length
|
|||||||
if(phase_ == Phase::AwaitingDisplacementOrOperand && source != end) {
|
if(phase_ == Phase::AwaitingDisplacementOrOperand && source != end) {
|
||||||
const int required_bytes = displacement_size_ + operand_size_;
|
const int required_bytes = displacement_size_ + operand_size_;
|
||||||
|
|
||||||
if(!required_bytes) {
|
|
||||||
phase_ = Phase::ReadyToPost;
|
|
||||||
} else {
|
|
||||||
const int outstanding_bytes = required_bytes - operand_bytes_;
|
const int outstanding_bytes = required_bytes - operand_bytes_;
|
||||||
const int bytes_to_consume = std::min(int(end - source), outstanding_bytes);
|
const int bytes_to_consume = std::min(int(end - source), outstanding_bytes);
|
||||||
|
|
||||||
@ -584,7 +581,7 @@ std::pair<int, Instruction> Decoder::decode(const uint8_t *source, size_t length
|
|||||||
operand_ = inward_data_ >> 56; inward_data_ <<= 8;
|
operand_ = inward_data_ >> 56; inward_data_ <<= 8;
|
||||||
|
|
||||||
// Sign extend if a single byte operand is feeding a two-byte instruction.
|
// Sign extend if a single byte operand is feeding a two-byte instruction.
|
||||||
if(operation_size_ == 2) {
|
if(operation_size_ == 2 && operation_ != Operation::IN && operation_ != Operation::OUT) {
|
||||||
operand_ |= (operand_ & 0x80) ? 0xff00 : 0x0000;
|
operand_ |= (operand_ & 0x80) ? 0xff00 : 0x0000;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -600,7 +597,6 @@ std::pair<int, Instruction> Decoder::decode(const uint8_t *source, size_t length
|
|||||||
return std::make_pair(-(outstanding_bytes - bytes_to_consume), Instruction());
|
return std::make_pair(-(outstanding_bytes - bytes_to_consume), Instruction());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Check for completion.
|
// MARK: - Check for completion.
|
||||||
|
|
||||||
|
@ -122,6 +122,14 @@ class Instruction {
|
|||||||
public:
|
public:
|
||||||
Operation operation = Operation::Invalid;
|
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:
|
private:
|
||||||
// b0, b1: a Repetition;
|
// b0, b1: a Repetition;
|
||||||
// b2+: operation size.
|
// b2+: operation size.
|
||||||
@ -300,6 +308,7 @@ struct Decoder {
|
|||||||
segment_override_ = Source::None;
|
segment_override_ = Source::None;
|
||||||
repetition_ = Repetition::None;
|
repetition_ = Repetition::None;
|
||||||
phase_ = Phase::Instruction;
|
phase_ = Phase::Instruction;
|
||||||
|
source_ = destination_ = Source::None;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user