From cf4603cb33109b922ddf16f65fcecba6fc00deac Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 6 Oct 2023 16:32:35 -0400 Subject: [PATCH] Attempt to check defined flags only. --- InstructionSets/x86/Status.hpp | 41 +++++++- OSBindings/Mac/Clock SignalTests/8088Tests.mm | 99 +++++++++++++++---- 2 files changed, 115 insertions(+), 25 deletions(-) diff --git a/InstructionSets/x86/Status.hpp b/InstructionSets/x86/Status.hpp index 57cc9157e..9db5c087a 100644 --- a/InstructionSets/x86/Status.hpp +++ b/InstructionSets/x86/Status.hpp @@ -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 IntT carry_bit() { return carry ? 1 : 0; } + // Flag getters. + template 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 { diff --git a/OSBindings/Mac/Clock SignalTests/8088Tests.mm b/OSBindings/Mac/Clock SignalTests/8088Tests.mm index f080c0475..881872d82 100644 --- a/OSBindings/Mac/Clock SignalTests/8088Tests.mm +++ b/OSBindings/Mac/Clock SignalTests/8088Tests.mm @@ -84,6 +84,13 @@ struct Registers { } }; struct Memory { + enum class Tag { + Accessed, + FlagsL, + FlagsH + }; + + std::unordered_map tags; std::vector memory; const Registers ®isters_; @@ -91,7 +98,9 @@ struct Memory { memory.resize(1024*1024); } - template 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 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(physical_address); + return access(physical_address, tag); } - template 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 IntT &access(uint32_t address, Tag tag) { + tags[address] = tag; return *reinterpret_cast(&memory[address]); } + + // Entry point for the 8086; simply notes that memory was accessed. + template IntT &access([[maybe_unused]] InstructionSet::x86::Source segment, uint32_t address) { + return access(segment, address, Tag::Accessed); + } }; struct IO { }; @@ -117,13 +134,13 @@ class FlowController { void interrupt(int index) { const uint16_t address = static_cast(index) << 2; - const uint16_t new_ip = memory_.access(address); - const uint16_t new_cs = memory_.access(address + 2); + const uint16_t new_ip = memory_.access(address, Memory::Tag::Accessed); + const uint16_t new_cs = memory_.access(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 ®isters_; - 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(InstructionSet::x86::Source::SS, registers_.sp_) = value >> 8; + memory_.access( + InstructionSet::x86::Source::SS, + registers_.sp_, + is_flags ? Memory::Tag::FlagsH : Memory::Tag::Accessed + ) = value >> 8; --registers_.sp_; - memory_.access(InstructionSet::x86::Source::SS, registers_.sp_) = value & 0xff; + memory_.access( + 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 &)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 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 *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 *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 *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; } }