From 2d17d9d316572ab9843afaf09cfdabc79716f23e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 6 Oct 2023 11:31:45 -0400 Subject: [PATCH] Execute some tests at some facile level. --- .../Implementation/PerformImplementation.hpp | 2 +- InstructionSets/x86/Status.hpp | 9 + OSBindings/Mac/Clock SignalTests/8088Tests.mm | 225 +++++++++++------- 3 files changed, 151 insertions(+), 85 deletions(-) diff --git a/InstructionSets/x86/Implementation/PerformImplementation.hpp b/InstructionSets/x86/Implementation/PerformImplementation.hpp index 3893a22f0..072d59e53 100644 --- a/InstructionSets/x86/Implementation/PerformImplementation.hpp +++ b/InstructionSets/x86/Implementation/PerformImplementation.hpp @@ -63,7 +63,7 @@ IntT overflow(IntT lhs, IntT rhs, IntT result) { // Order Number 243191; e.g. https://www.ardent-tool.com/CPU/docs/Intel/IA/243191-002.pdf // -inline void aaa(CPU::RegisterPair16 &ax, Status &status) { +inline void aaa(CPU::RegisterPair16 &ax, Status &status) { // P. 313 /* IF ((AL AND 0FH) > 9) OR (AF = 1) THEN diff --git a/InstructionSets/x86/Status.hpp b/InstructionSets/x86/Status.hpp index b1c4b2e1e..57cc9157e 100644 --- a/InstructionSets/x86/Status.hpp +++ b/InstructionSets/x86/Status.hpp @@ -74,6 +74,15 @@ struct Status { // TODO: the rest. } + + uint16_t get() const { + return + (carry ? ConditionCode::Carry : 0); + } + + bool operator ==(const Status &rhs) const { + return get() == rhs.get(); + } }; } diff --git a/OSBindings/Mac/Clock SignalTests/8088Tests.mm b/OSBindings/Mac/Clock SignalTests/8088Tests.mm index 7c5f53033..25748663a 100644 --- a/OSBindings/Mac/Clock SignalTests/8088Tests.mm +++ b/OSBindings/Mac/Clock SignalTests/8088Tests.mm @@ -27,6 +27,87 @@ namespace { // provide their real path here. constexpr char TestSuiteHome[] = "/Users/tharte/Projects/ProcessorTests/8088/v1"; +struct Registers { + CPU::RegisterPair16 ax_; + uint8_t &al() { return ax_.halves.low; } + uint8_t &ah() { return ax_.halves.high; } + uint16_t &ax() { return ax_.full; } + + CPU::RegisterPair16 &axp() { return ax_; } + + CPU::RegisterPair16 cx_; + uint8_t &cl() { return cx_.halves.low; } + uint8_t &ch() { return cx_.halves.high; } + uint16_t &cx() { return cx_.full; } + + CPU::RegisterPair16 dx_; + uint8_t &dl() { return dx_.halves.low; } + uint8_t &dh() { return dx_.halves.high; } + uint16_t &dx() { return dx_.full; } + + CPU::RegisterPair16 bx_; + uint8_t &bl() { return bx_.halves.low; } + uint8_t &bh() { return bx_.halves.high; } + uint16_t &bx() { return bx_.full; } + + uint16_t sp_; + uint16_t &sp() { return sp_; } + + uint16_t bp_; + uint16_t &bp() { return bp_; } + + uint16_t si_; + uint16_t &si() { return si_; } + + uint16_t di_; + uint16_t &di() { return di_; } + + uint16_t es_, cs_, ds_, ss_; + uint16_t ip_; + + bool operator ==(const Registers &rhs) const { + return + ax_.full == rhs.ax_.full && + cx_.full == rhs.cx_.full && + dx_.full == rhs.dx_.full && + bx_.full == rhs.bx_.full && + sp_ == rhs.sp_ && + bp_ == rhs.bp_ && + si_ == rhs.si_ && + di_ == rhs.di_ && + es_ == rhs.es_ && + cs_ == rhs.cs_ && + ds_ == rhs.ds_ && + si_ == rhs.si_ && + ip_ == rhs.ip_; + } +}; +struct Memory { + std::vector memory; + const Registers ®isters_; + + Memory(Registers ®isters) : registers_(registers) { + memory.resize(1024*1024); + } + + template IntT &access([[maybe_unused]] InstructionSet::x86::Source segment, uint16_t address) { + uint32_t physical_address; + using Source = InstructionSet::x86::Source; + switch(segment) { + default: physical_address = registers_.ds_; break; + case Source::ES: physical_address = registers_.es_; break; + case Source::CS: physical_address = registers_.cs_; break; + case Source::DS: physical_address = registers_.ds_; break; + } + physical_address = ((physical_address << 4) + address) & 0xf'ffff; + return *reinterpret_cast(&memory[physical_address]); + } +}; +struct IO { +}; +struct FlowController { +}; + } @interface i8088Tests : XCTestCase @@ -37,6 +118,7 @@ constexpr char TestSuiteHome[] = "/Users/tharte/Projects/ProcessorTests/8088/v1" - (NSArray *)testFiles { NSString *path = [NSString stringWithUTF8String:TestSuiteHome]; NSSet *allowList = [NSSet setWithArray:@[ + @"D4.json.gz" ]]; NSSet *ignoreList = nil; @@ -168,75 +250,30 @@ constexpr char TestSuiteHome[] = "/Users/tharte/Projects/ProcessorTests/8088/v1" return isEqual; } +- (void)populate:(Registers &)registers status:(InstructionSet::x86::Status &)status value:(NSDictionary *)value { + registers.ax_.full = [value[@"ax"] intValue]; + registers.bx_.full = [value[@"bx"] intValue]; + registers.cx_.full = [value[@"cx"] intValue]; + registers.dx_.full = [value[@"dx"] intValue]; + + registers.bp_ = [value[@"bp"] intValue]; + registers.cs_ = [value[@"cs"] intValue]; + registers.di_ = [value[@"di"] intValue]; + registers.ds_ = [value[@"ds"] intValue]; + registers.es_ = [value[@"es"] intValue]; + registers.si_ = [value[@"si"] intValue]; + registers.sp_ = [value[@"sp"] intValue]; + registers.ss_ = [value[@"ss"] intValue]; + registers.ip_ = [value[@"ip"] intValue]; + + status.set([value[@"flags"] intValue]); +} + - (bool)applyExecutionTest:(NSDictionary *)test file:(NSString *)file assert:(BOOL)assert { InstructionSet::x86::Decoder decoder; const auto data = [self bytes:test[@"bytes"]]; const auto decoded = decoder.decode(data.data(), data.size()); - struct Registers { - CPU::RegisterPair16 ax_; - uint8_t &al() { return ax_.halves.low; } - uint8_t &ah() { return ax_.halves.high; } - uint16_t &ax() { return ax_.full; } - - CPU::RegisterPair16 &axp() { return ax_; } - - CPU::RegisterPair16 cx_; - uint8_t &cl() { return cx_.halves.low; } - uint8_t &ch() { return cx_.halves.high; } - uint16_t &cx() { return cx_.full; } - - CPU::RegisterPair16 dx_; - uint8_t &dl() { return dx_.halves.low; } - uint8_t &dh() { return dx_.halves.high; } - uint16_t &dx() { return dx_.full; } - - CPU::RegisterPair16 bx_; - uint8_t &bl() { return bx_.halves.low; } - uint8_t &bh() { return bx_.halves.high; } - uint16_t &bx() { return bx_.full; } - - uint16_t sp_; - uint16_t &sp() { return sp_; } - - uint16_t bp_; - uint16_t &bp() { return bp_; } - - uint16_t si_; - uint16_t &si() { return si_; } - - uint16_t di_; - uint16_t &di() { return di_; } - - uint16_t es_, cs_, ds_, ss_; - uint16_t ip_; - }; - struct Memory { - std::vector memory; - const Registers ®isters_; - - Memory(Registers ®isters) : registers_(registers) { - memory.resize(1024*1024); - } - - template IntT &access([[maybe_unused]] InstructionSet::x86::Source segment, uint16_t address) { - uint32_t physical_address; - using Source = InstructionSet::x86::Source; - switch(segment) { - default: physical_address = registers_.ds_; break; - case Source::ES: physical_address = registers_.es_; break; - case Source::CS: physical_address = registers_.cs_; break; - case Source::DS: physical_address = registers_.ds_; break; - } - physical_address = ((physical_address << 4) + address) & 0xf'ffff; - return *reinterpret_cast(&memory[physical_address]); - } - }; - struct IO { - }; - struct FlowController { - }; - InstructionSet::x86::Status status; FlowController flow_controller; Registers registers; @@ -244,28 +281,14 @@ constexpr char TestSuiteHome[] = "/Users/tharte/Projects/ProcessorTests/8088/v1" IO io; // Apply initial state. - NSDictionary *const initial = test[@"initial"]; - for(NSArray *ram in initial[@"ram"]) { + NSDictionary *const initial_state = test[@"initial"]; + for(NSArray *ram in initial_state[@"ram"]) { memory.memory[[ram[0] intValue]] = [ram[1] intValue]; } - NSDictionary *const initial_registers = initial[@"regs"]; - registers.ax_.full = [initial_registers[@"ax"] intValue]; - registers.bx_.full = [initial_registers[@"bx"] intValue]; - registers.cx_.full = [initial_registers[@"cx"] intValue]; - registers.dx_.full = [initial_registers[@"dx"] intValue]; - - registers.bp_ = [initial_registers[@"bp"] intValue]; - registers.cs_ = [initial_registers[@"cs"] intValue]; - registers.di_ = [initial_registers[@"di"] intValue]; - registers.ds_ = [initial_registers[@"ds"] intValue]; - registers.es_ = [initial_registers[@"es"] intValue]; - registers.si_ = [initial_registers[@"si"] intValue]; - registers.sp_ = [initial_registers[@"sp"] intValue]; - registers.ss_ = [initial_registers[@"ss"] intValue]; - registers.ip_ = [initial_registers[@"ip"] intValue]; - - status.set([initial_registers[@"flags"] intValue]); + [self populate:registers status:status value:initial_state[@"regs"]]; + // Execute instruction. + registers.ip_ += decoded.first; InstructionSet::x86::perform( decoded.second, status, @@ -275,7 +298,41 @@ constexpr char TestSuiteHome[] = "/Users/tharte/Projects/ProcessorTests/8088/v1" io ); - return false; + // Compare final state. + NSDictionary *const final_state = test[@"final"]; + Registers intended_registers; + InstructionSet::x86::Status intended_status; + + bool ramEqual = true; + for(NSArray *ram in final_state[@"ram"]) { + ramEqual &= memory.memory[[ram[0] intValue]] == [ram[1] intValue]; + } + + [self populate:intended_registers status:intended_status value:final_state[@"regs"]]; + const bool registersEqual = intended_registers == registers; + const bool statusEqual = intended_status == status; + + if(assert) { + XCTAssert( + statusEqual, + "Status doesn't match — differs in %02x after %@", + intended_status.get() ^ status.get(), + test[@"name"] + ); + // TODO: should probably say more about the following two. + XCTAssert( + registersEqual, + "Register mismatch after %@", + test[@"name"] + ); + XCTAssert( + ramEqual, + "Memory contents mismatch after %@", + test[@"name"] + ); + } + + return statusEqual && registersEqual && ramEqual; } - (void)printFailures:(NSArray *)failures {