From 6d392852d2b9e0cb2005d828d2c5f54515f6a983 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 5 Oct 2023 22:27:52 -0400 Subject: [PATCH] Hack on through to something that builds. --- .../Implementation/PerformImplementation.hpp | 116 ++++++------ InstructionSets/x86/Perform.hpp | 1 - OSBindings/Mac/Clock SignalTests/8088Tests.mm | 174 +++++++++++++----- 3 files changed, 187 insertions(+), 104 deletions(-) diff --git a/InstructionSets/x86/Implementation/PerformImplementation.hpp b/InstructionSets/x86/Implementation/PerformImplementation.hpp index 15243c06f..2ac3cfabe 100644 --- a/InstructionSets/x86/Implementation/PerformImplementation.hpp +++ b/InstructionSets/x86/Implementation/PerformImplementation.hpp @@ -10,6 +10,7 @@ #define PerformImplementation_h #include "../../../Numeric/Carry.hpp" +#include "../../../Numeric/RegisterSizes.hpp" namespace InstructionSet::x86 { @@ -79,6 +80,7 @@ void aaa(CPU::RegisterPair16 &ax, Status &status) { } else { status.auxiliary_carry = status.carry = 0; } + ax.halves.low &= 0x0f; } void aad(CPU::RegisterPair16 &ax, uint8_t imm, Status &status) { @@ -199,73 +201,63 @@ template < using IntT = typename DataSizeType::type; using AddressT = typename AddressT::type; - // Establish source() and destination() shorthand to fetch data if necessary. - IntT fetched_data = 0, original_data = 0; - Source segment; - AddressT address; - - static constexpr IntT zero = 0; + IntT zero = 0; auto data = [&](DataPointer source) -> IntT& { // Rules: // // * if this is a memory access, set target_address and break; // * otherwise return the appropriate value. + AddressT address; switch(source.source()) { case Source::eAX: - switch(data_size) { - default: return registers.al(); - case DataSize::Word: return registers.ax(); - case DataSize::DWord: return registers.eax(); - } + if constexpr (is_32bit(model) && data_size == DataSize::DWord) { return registers.eax(); } + else if constexpr (data_size == DataSize::DWord) { return zero; } + else if constexpr (data_size == DataSize::Word) { return registers.ax(); } + else { return registers.al(); } case Source::eCX: - switch(data_size) { - default: return registers.cl(); - case DataSize::Word: return registers.cx(); - case DataSize::DWord: return registers.ecx(); - } + if constexpr (is_32bit(model) && data_size == DataSize::DWord) { return registers.ecx(); } + else if constexpr (data_size == DataSize::DWord) { return zero; } + else if constexpr (data_size == DataSize::Word) { return registers.cx(); } + else { return registers.cl(); } case Source::eDX: - switch(data_size) { - default: return registers.dl(); - case DataSize::Word: return registers.dx(); - case DataSize::DWord: return registers.edx(); - } + if constexpr (is_32bit(model) && data_size == DataSize::DWord) { return registers.edx(); } + else if constexpr (data_size == DataSize::DWord) { return zero; } + else if constexpr (data_size == DataSize::Word) { return registers.dx(); } + else { return registers.dl(); } case Source::eBX: - switch(data_size) { - default: return registers.bl(); - case DataSize::Word: return registers.bx(); - case DataSize::DWord: return registers.ebx(); - } + if constexpr (is_32bit(model) && data_size == DataSize::DWord) { return registers.ebx(); } + else if constexpr (data_size == DataSize::DWord) { return zero; } + else if constexpr (data_size == DataSize::Word) { return registers.bx(); } + else { return registers.bl(); } case Source::eSPorAH: - switch(data_size) { - default: return registers.ah(); - case DataSize::Word: return registers.sp(); - case DataSize::DWord: return registers.esp(); - } + if constexpr (is_32bit(model) && data_size == DataSize::DWord) { return registers.esp(); } + else if constexpr (data_size == DataSize::DWord) { return zero; } + else if constexpr (data_size == DataSize::Word) { return registers.sp(); } + else { return registers.ah(); } case Source::eBPorCH: - switch(data_size) { - default: return registers.ch(); - case DataSize::Word: return registers.bp(); - case DataSize::DWord: return registers.ebp(); - } + if constexpr (is_32bit(model) && data_size == DataSize::DWord) { return registers.ebp(); } + else if constexpr (data_size == DataSize::DWord) { return zero; } + else if constexpr (data_size == DataSize::Word) { return registers.bp(); } + else { return registers.ch(); } case Source::eSIorDH: - switch(data_size) { - default: return registers.dh(); - case DataSize::Word: return registers.si(); - case DataSize::DWord: return registers.esi(); - } + if constexpr (is_32bit(model) && data_size == DataSize::DWord) { return registers.esi(); } + else if constexpr (data_size == DataSize::DWord) { return zero; } + else if constexpr (data_size == DataSize::Word) { return registers.si(); } + else { return registers.dh(); } case Source::eDIorBH: - switch(data_size) { - default: return registers.bh(); - case DataSize::Word: return registers.di(); - case DataSize::DWord: return registers.edi(); - } + if constexpr (is_32bit(model) && data_size == DataSize::DWord) { return registers.edi(); } + else if constexpr (data_size == DataSize::DWord) { return zero; } + else if constexpr (data_size == DataSize::Word) { return registers.di(); } + else { return registers.bh(); } - case Source::ES: return registers.es(); - case Source::CS: return registers.cs(); - case Source::SS: return registers.ss(); - case Source::DS: return registers.ds(); - case Source::FS: return registers.fs(); - case Source::GS: return registers.gs(); + // TODO: the below. + default: +// case Source::ES: return registers.es(); +// case Source::CS: return registers.cs(); +// case Source::SS: return registers.ss(); +// case Source::DS: return registers.ds(); +// case Source::FS: return registers.fs(); +// case Source::GS: return registers.gs(); case Source::Immediate: // TODO (here the use of a reference falls down?) @@ -281,11 +273,11 @@ template < // If execution has reached here then a memory fetch is required. // Do it and exit. - segment = source.segment(instruction.segment_override()); - fetched_data = original_data = memory.template read(segment, address); - return fetched_data; + const Source segment = source.segment(instruction.segment_override()); + return memory.template access(segment, address); }; + // Establish source() and destination() shorthand to fetch data if necessary. auto source = [&]() -> IntT& { return data(instruction.source()); }; auto destination = [&]() -> IntT& { return data(instruction.destination()); }; @@ -295,19 +287,21 @@ template < // * return directly if there is definitely no possible write back to RAM; // * otherwise use the source() and destination() lambdas, and break in order to allow a writeback if necessary. switch(instruction.operation) { - case Operation::AAA: Primitive::aaa(registers.ax(), status); return; - case Operation::AAD: Primitive::aad(registers.ax(), instruction.immediate(), status); return; - case Operation::AAM: Primitive::aam(registers.ax(), instruction.immediate(), status); return; - case Operation::AAS: Primitive::aas(registers.ax(), status); return; + default: assert(false); + + case Operation::AAA: Primitive::aaa(registers.axp(), status); return; + case Operation::AAD: Primitive::aad(registers.axp(), instruction.operand(), status); return; + case Operation::AAM: Primitive::aam(registers.axp(), instruction.operand(), status); return; + case Operation::AAS: Primitive::aas(registers.axp(), status); return; case Operation::ADC: Primitive::adc(destination(), source(), status); break; case Operation::ADD: Primitive::add(destination(), source(), status); break; } // Write to memory if required to complete this operation. - if(original_data != fetched_data) { +// if(original_data != fetched_data) { // TODO. - } +// } } template < diff --git a/InstructionSets/x86/Perform.hpp b/InstructionSets/x86/Perform.hpp index 387ccaea5..da77b89b0 100644 --- a/InstructionSets/x86/Perform.hpp +++ b/InstructionSets/x86/Perform.hpp @@ -12,7 +12,6 @@ #include "Instruction.hpp" #include "Model.hpp" #include "Status.hpp" -#include "../../Numeric/RegisterSizes.hpp" namespace InstructionSet::x86 { diff --git a/OSBindings/Mac/Clock SignalTests/8088Tests.mm b/OSBindings/Mac/Clock SignalTests/8088Tests.mm index ea53bf776..8408895dd 100644 --- a/OSBindings/Mac/Clock SignalTests/8088Tests.mm +++ b/OSBindings/Mac/Clock SignalTests/8088Tests.mm @@ -19,6 +19,7 @@ #include "../../../InstructionSets/x86/Decoder.hpp" #include "../../../InstructionSets/x86/Perform.hpp" +#include "../../../Numeric/RegisterSizes.hpp" namespace { @@ -59,22 +60,30 @@ constexpr char TestSuiteHome[] = "/Users/tharte/Projects/ProcessorTests/8088/v1" return [fullPaths sortedArrayUsingSelector:@selector(compare:)]; } +- (NSArray *)testsInFile:(NSString *)file { + NSData *data = [NSData dataWithContentsOfGZippedFile:file]; + return [NSJSONSerialization JSONObjectWithData:data 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]]; } -- (bool)applyDecodingTest:(NSDictionary *)test file:(NSString *)file assert:(BOOL)assert { - using Decoder = InstructionSet::x86::Decoder; - Decoder decoder; - - // Build a vector of the instruction bytes; this makes manual step debugging easier. - NSArray *encoding = test[@"bytes"]; +- (std::vector)bytes:(NSArray *)encoding { std::vector data; data.reserve(encoding.count); for(NSNumber *number in encoding) { data.push_back([number intValue]); } + return data; +} + +- (bool)applyDecodingTest:(NSDictionary *)test file:(NSString *)file assert:(BOOL)assert { + InstructionSet::x86::Decoder decoder; + + // Build a vector of the instruction bytes; this makes manual step debugging easier. + const auto data = [self bytes:test[@"bytes"]]; auto hex_instruction = [&]() -> NSString * { NSMutableString *hexInstruction = [[NSMutableString alloc] init]; for(uint8_t byte: data) { @@ -84,18 +93,19 @@ constexpr char TestSuiteHome[] = "/Users/tharte/Projects/ProcessorTests/8088/v1" }; const auto decoded = decoder.decode(data.data(), data.size()); + const bool sizeMatched = decoded.first == data.size(); if(assert) { XCTAssert( - decoded.first == [encoding count], + sizeMatched, "Wrong length of instruction decoded for %@ — decoded %d rather than %lu from %@; file %@", test[@"name"], decoded.first, - (unsigned long)[encoding count], + (unsigned long)data.size(), hex_instruction(), file ); } - if(decoded.first != [encoding count]) { + if(!sizeMatched) { return false; } @@ -158,54 +168,134 @@ constexpr char TestSuiteHome[] = "/Users/tharte/Projects/ProcessorTests/8088/v1" return isEqual; } -- (void)testDecoding { - NSMutableSet *failures = [[NSMutableSet alloc] init]; - NSArray *testFiles = [self testFiles]; +- (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()); - for(NSString *file in testFiles) { - NSData *data = [NSData dataWithContentsOfGZippedFile:file]; - NSArray *testsInFile = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; - NSUInteger successes = 0; - for(NSDictionary *test in testsInFile) { + 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_; + }; + 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: address = registers_.ds_; break; + case Source::ES: address = registers_.es_; break; + case Source::CS: address = registers_.cs_; break; + case Source::DS: 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; + Memory memory(registers); + IO io; + + InstructionSet::x86::perform( + decoded.second, + status, + flow_controller, + registers, + memory, + io + ); + + return false; +} + +- (void)printFailures:(NSArray *)failures { + NSLog( + @"%ld failures out of %ld tests: %@", + failures.count, + [self testFiles].count, + [failures sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]); +} + +- (void)testDecoding { + NSMutableArray *failures = [[NSMutableArray alloc] init]; + for(NSString *file in [self testFiles]) { + for(NSDictionary *test in [self testsInFile:file]) { // A single failure per instruction is fine. if(![self applyDecodingTest:test file:file assert:YES]) { [failures addObject:file]; // Attempt a second decoding, to provide a debugger hook. [self applyDecodingTest:test file:file assert:NO]; - break; } - ++successes; - } - if(successes != [testsInFile count]) { - NSLog(@"Failed after %ld successes", successes); } } - NSLog(@"%ld failures out of %ld tests: %@", failures.count, testFiles.count, [[failures allObjects] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]); + [self printFailures:failures]; } - (void)testExecution { -// CPU::RegisterPair16 source, dest; -// InstructionSet::x86::Status status; -// struct NoFlow { -// } flow_controller; -// -// dest.full = 0xff; -// source.full = 10; -// -// InstructionSet::x86::perform< -// InstructionSet::x86::Model::i8086, -// InstructionSet::x86::Operation::ADD, -// InstructionSet::x86::DataSize::Byte -// >( -// dest, -// source, -// status, -// flow_controller -// ); -// + NSMutableArray *failures = [[NSMutableArray alloc] init]; + for(NSString *file in [self testFiles]) { + for(NSDictionary *test in [self testsInFile:file]) { + // A single failure per instruction is fine. + if(![self applyExecutionTest:test file:file assert:YES]) { + [failures addObject:file]; + + // Attempt a second decoding, to provide a debugger hook. + [self applyExecutionTest:test file:file assert:NO]; + break; + } + } + } + + [self printFailures:failures]; } @end