1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-25 18:30:07 +00:00

Attempt to check defined flags only.

This commit is contained in:
Thomas Harte 2023-10-06 16:32:35 -04:00
parent b6d000ac5e
commit cf4603cb33
2 changed files with 115 additions and 25 deletions

View File

@ -59,6 +59,9 @@ struct Status {
uint32_t auxiliary_carry;
uint32_t sign;
uint32_t overflow;
uint32_t trap;
uint32_t interrupt;
uint32_t direction;
// Zero => set; non-zero => unset.
uint32_t zero;
@ -66,18 +69,48 @@ struct Status {
// Odd number of bits => set; even => unset.
uint32_t parity;
// Convenience getters.
template <typename IntT> IntT carry_bit() { return carry ? 1 : 0; }
// Flag getters.
template <typename IntT> IntT carry_bit() const { return carry ? 1 : 0; }
bool parity_bit() const {
uint32_t result = parity;
result ^= result >> 16;
result ^= result >> 8;
result ^= result >> 4;
result ^= result >> 2;
result ^= result >> 1;
return 1 ^ (result & 1);
}
// Complete value get and set.
void set(uint16_t value) {
carry = value & ConditionCode::Carry;
auxiliary_carry = value & ConditionCode::AuxiliaryCarry;
sign = value & ConditionCode::Sign;
overflow = value & ConditionCode::Overflow;
trap = value & ConditionCode::Trap;
interrupt = value & ConditionCode::Interrupt;
direction = value & ConditionCode::Direction;
// TODO: the rest.
zero = (~value) & ConditionCode::Zero;
parity = (~value) & ConditionCode::Parity;
}
uint16_t get() const {
return
(carry ? ConditionCode::Carry : 0);
0xf002 |
(carry ? ConditionCode::Carry : 0) |
(auxiliary_carry ? ConditionCode::AuxiliaryCarry : 0) |
(sign ? ConditionCode::Sign : 0) |
(overflow ? ConditionCode::Overflow : 0) |
(trap ? ConditionCode::Trap : 0) |
(interrupt ? ConditionCode::Interrupt : 0) |
(direction ? ConditionCode::Direction : 0) |
(zero ? 0 : ConditionCode::Zero) |
(parity_bit() ? ConditionCode::Parity : 0);
}
bool operator ==(const Status &rhs) const {

View File

@ -84,6 +84,13 @@ struct Registers {
}
};
struct Memory {
enum class Tag {
Accessed,
FlagsL,
FlagsH
};
std::unordered_map<uint32_t, Tag> tags;
std::vector<uint8_t> memory;
const Registers &registers_;
@ -91,7 +98,9 @@ struct Memory {
memory.resize(1024*1024);
}
template <typename IntT> IntT &access([[maybe_unused]] InstructionSet::x86::Source segment, uint16_t address) {
// Entry point used by the flow controller so that it can mark up locations at which the flags were written,
// so that defined-flag-only masks can be applied while verifying RAM contents.
template <typename IntT> IntT &access([[maybe_unused]] InstructionSet::x86::Source segment, uint16_t address, Tag tag) {
uint32_t physical_address;
using Source = InstructionSet::x86::Source;
switch(segment) {
@ -101,12 +110,20 @@ struct Memory {
case Source::SS: physical_address = registers_.ss_; break;
}
physical_address = ((physical_address << 4) + address) & 0xf'ffff;
return access<IntT>(physical_address);
return access<IntT>(physical_address, tag);
}
template <typename IntT> IntT &access(uint32_t address) {
// An additional entry point for the flow controller; on the original 8086 interrupt vectors aren't relative
// to a selector, they're just at an absolute location.
template <typename IntT> IntT &access(uint32_t address, Tag tag) {
tags[address] = tag;
return *reinterpret_cast<IntT *>(&memory[address]);
}
// Entry point for the 8086; simply notes that memory was accessed.
template <typename IntT> IntT &access([[maybe_unused]] InstructionSet::x86::Source segment, uint32_t address) {
return access<IntT>(segment, address, Tag::Accessed);
}
};
struct IO {
};
@ -117,13 +134,13 @@ class FlowController {
void interrupt(int index) {
const uint16_t address = static_cast<uint16_t>(index) << 2;
const uint16_t new_ip = memory_.access<uint16_t>(address);
const uint16_t new_cs = memory_.access<uint16_t>(address + 2);
const uint16_t new_ip = memory_.access<uint16_t>(address, Memory::Tag::Accessed);
const uint16_t new_cs = memory_.access<uint16_t>(address + 2, Memory::Tag::Accessed);
push(status_.get());
push(status_.get(), true);
// TODO: set I and TF
// status_.
status_.interrupt = 0;
status_.trap = 0;
// Push CS and IP.
push(registers_.cs_);
@ -136,13 +153,23 @@ class FlowController {
private:
Memory &memory_;
Registers &registers_;
Status status_;
Status &status_;
void push(uint16_t value) {
void push(uint16_t value, bool is_flags = false) {
// Perform the push in two steps because it's possible for SP to underflow, and so that FlagsL and
// FlagsH can be set separately.
--registers_.sp_;
memory_.access<uint8_t>(InstructionSet::x86::Source::SS, registers_.sp_) = value >> 8;
memory_.access<uint8_t>(
InstructionSet::x86::Source::SS,
registers_.sp_,
is_flags ? Memory::Tag::FlagsH : Memory::Tag::Accessed
) = value >> 8;
--registers_.sp_;
memory_.access<uint8_t>(InstructionSet::x86::Source::SS, registers_.sp_) = value & 0xff;
memory_.access<uint8_t>(
InstructionSet::x86::Source::SS,
registers_.sp_,
is_flags ? Memory::Tag::FlagsL : Memory::Tag::Accessed
) = value & 0xff;
}
};
@ -185,6 +212,11 @@ class FlowController {
return [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
}
- (NSDictionary *)metadata {
NSString *path = [[NSString stringWithUTF8String:TestSuiteHome] stringByAppendingPathComponent:@"8088.json"];
return [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfGZippedFile:path] options:0 error:nil];
}
- (NSString *)toString:(const InstructionSet::x86::Instruction<false> &)instruction offsetLength:(int)offsetLength immediateLength:(int)immediateLength {
const auto operation = to_string(instruction, InstructionSet::x86::Model::i8086, offsetLength, immediateLength);
return [[NSString stringWithUTF8String:operation.c_str()] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
@ -307,23 +339,28 @@ class FlowController {
status.set([value[@"flags"] intValue]);
}
- (bool)applyExecutionTest:(NSDictionary *)test file:(NSString *)file assert:(BOOL)assert {
- (bool)applyExecutionTest:(NSDictionary *)test file:(NSString *)file metadata:(NSDictionary *)metadata assert:(BOOL)assert {
InstructionSet::x86::Decoder<InstructionSet::x86::Model::i8086> decoder;
const auto data = [self bytes:test[@"bytes"]];
const auto decoded = decoder.decode(data.data(), data.size());
InstructionSet::x86::Status status;
InstructionSet::x86::Status initial_status, status;
Registers registers;
Memory memory(registers);
FlowController flow_controller(memory, registers, status);
IO io;
const uint16_t flags_mask = metadata[@"flags-mask"] ? [metadata[@"flags-mask"] intValue] : 0xffff;
// Apply initial state.
NSDictionary *const initial_state = test[@"initial"];
for(NSArray<NSNumber *> *ram in initial_state[@"ram"]) {
memory.memory[[ram[0] intValue]] = [ram[1] intValue];
}
[self populate:registers status:status value:initial_state[@"regs"]];
[self populate:registers status:initial_status value:initial_state[@"regs"]];
status = initial_status;
NSLog(@"Initial status: %04x as per %@", status.get(), initial_state[@"regs"][@"flags"]);
// Execute instruction.
registers.ip_ += decoded.first;
@ -343,18 +380,30 @@ class FlowController {
bool ramEqual = true;
for(NSArray<NSNumber *> *ram in final_state[@"ram"]) {
ramEqual &= memory.memory[[ram[0] intValue]] == [ram[1] intValue];
const uint32_t address = [ram[0] intValue];
uint8_t mask = 0xff;
if(const auto tag = memory.tags.find(address); tag != memory.tags.end()) {
switch(tag->second) {
default: break;
case Memory::Tag::FlagsH: mask = flags_mask >> 8; break;
case Memory::Tag::FlagsL: mask = flags_mask & 0xff; break;
}
}
ramEqual &= (memory.memory[address] & mask) == ([ram[1] intValue] & mask);
}
[self populate:intended_registers status:intended_status value:final_state[@"regs"]];
const bool registersEqual = intended_registers == registers;
const bool statusEqual = intended_status == status;
const bool statusEqual = (intended_status.get() & flags_mask) == (status.get() & flags_mask);
if(assert) {
XCTAssert(
statusEqual,
"Status doesn't match — differs in %02x after %@; executing %@",
intended_status.get() ^ status.get(),
"Status doesn't match despite mask %04x — differs in %04x after %@; executing %@",
flags_mask,
(intended_status.get() ^ status.get()) & flags_mask,
test[@"name"],
[self toString:decoded.second offsetLength:4 immediateLength:4]
);
@ -403,15 +452,23 @@ class FlowController {
}
- (void)testExecution {
NSDictionary *metadata = [self metadata];
NSMutableArray<NSString *> *failures = [[NSMutableArray alloc] init];
for(NSString *file in [self testFiles]) {
// Determine what the metadata key.
NSString *const name = [file lastPathComponent];
NSRange first_dot = [name rangeOfString:@"."];
NSString *metadata_key = [name substringToIndex:first_dot.location];
NSDictionary *test_metadata = metadata[metadata_key];
for(NSDictionary *test in [self testsInFile:file]) {
// A single failure per instruction is fine.
if(![self applyExecutionTest:test file:file assert:YES]) {
if(![self applyExecutionTest:test file:file metadata:test_metadata assert:YES]) {
[failures addObject:file];
// Attempt a second decoding, to provide a debugger hook.
[self applyExecutionTest:test file:file assert:NO];
[self applyExecutionTest:test file:file metadata:test_metadata assert:NO];
break;
}
}