From 3983f8303fbd055faf3194bdf2d24e67459bc6dd Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 25 Apr 2019 22:06:05 -0400 Subject: [PATCH] Introduces failing test of 68000 opcode coverage. --- .../Clock Signal.xcodeproj/project.pbxproj | 4 + .../Mac/Clock SignalTests/68000Tests.mm | 89 +++++++++++++++++++ .../68000/Implementation/68000Storage.hpp | 1 + 3 files changed, 94 insertions(+) diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index fb752c11b..d4c4332ce 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -308,6 +308,7 @@ 4B9BE401203A0C0600FFAE60 /* MultiSpeaker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B9BE3FE203A0C0600FFAE60 /* MultiSpeaker.cpp */; }; 4B9F11C92272375400701480 /* qltrace.txt.gz in Resources */ = {isa = PBXBuildFile; fileRef = 4B9F11C82272375400701480 /* qltrace.txt.gz */; }; 4B9F11CA2272433900701480 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B69FB451C4D950F00B5F0AA /* libz.tbd */; }; + 4B9F11CC22729B3600701480 /* OPCLOGR2.BIN in Resources */ = {isa = PBXBuildFile; fileRef = 4B9F11CB22729B3500701480 /* OPCLOGR2.BIN */; }; 4BA0F68E1EEA0E8400E9489E /* ZX8081.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BA0F68C1EEA0E8400E9489E /* ZX8081.cpp */; }; 4BA61EB01D91515900B3C876 /* NSData+StdVector.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BA61EAF1D91515900B3C876 /* NSData+StdVector.mm */; }; 4BA91E1D216D85BA00F79557 /* MasterSystemVDPTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BA91E1C216D85BA00F79557 /* MasterSystemVDPTests.mm */; }; @@ -1040,6 +1041,7 @@ 4B9BE3FE203A0C0600FFAE60 /* MultiSpeaker.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MultiSpeaker.cpp; sourceTree = ""; }; 4B9BE3FF203A0C0600FFAE60 /* MultiSpeaker.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MultiSpeaker.hpp; sourceTree = ""; }; 4B9F11C82272375400701480 /* qltrace.txt.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; path = qltrace.txt.gz; sourceTree = ""; }; + 4B9F11CB22729B3500701480 /* OPCLOGR2.BIN */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; name = OPCLOGR2.BIN; path = "68000 Coverage/OPCLOGR2.BIN"; sourceTree = ""; }; 4BA0F68C1EEA0E8400E9489E /* ZX8081.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ZX8081.cpp; path = Data/ZX8081.cpp; sourceTree = ""; }; 4BA0F68D1EEA0E8400E9489E /* ZX8081.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ZX8081.hpp; path = Data/ZX8081.hpp; sourceTree = ""; }; 4BA141C12073100800A31EC9 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = ""; }; @@ -1570,6 +1572,7 @@ 4B018B88211930DE002A3937 /* 65C02_extended_opcodes_test.bin */, 4B44EBF61DC9883B00A7820C /* 6502_functional_test.bin */, 4B44EBF41DC987AE00A7820C /* AllSuiteA.bin */, + 4B9F11CB22729B3500701480 /* OPCLOGR2.BIN */, 4BBF49B41ED2881600AB3669 /* FUSE */, 4B9F11C72272375400701480 /* QL Startup */, 4BB297E41B587D8300A49093 /* Wolfgang Lorenz 6502 test suite */, @@ -3528,6 +3531,7 @@ 4BB2997D1B587D8400A49093 /* ldxay in Resources */, 4BB299D71B587D8400A49093 /* staax in Resources */, 4B98A1CE1FFADEC500ADF63B /* MSX ROMs in Resources */, + 4B9F11CC22729B3600701480 /* OPCLOGR2.BIN in Resources */, 4BB2990C1B587D8400A49093 /* asoax in Resources */, 4BB299191B587D8400A49093 /* bita in Resources */, 4BB2992A1B587D8400A49093 /* cia2ta in Resources */, diff --git a/OSBindings/Mac/Clock SignalTests/68000Tests.mm b/OSBindings/Mac/Clock SignalTests/68000Tests.mm index 6ca099aea..85bfa39b0 100644 --- a/OSBindings/Mac/Clock SignalTests/68000Tests.mm +++ b/OSBindings/Mac/Clock SignalTests/68000Tests.mm @@ -10,6 +10,7 @@ #include #include +#include #include "68000.hpp" @@ -72,11 +73,88 @@ class RAM68000: public CPU::MC68000::BusHandler { m68000_.set_state(state); } + const CPU::MC68000::Processor &processor() { + return m68000_; + } + private: CPU::MC68000::Processor m68000_; std::vector ram_; }; +class CPU::MC68000::ProcessorStorageTests { + public: + ProcessorStorageTests(const CPU::MC68000::ProcessorStorage &storage, const char *coverage_file_name) { + FILE *source = fopen(coverage_file_name, "rb"); + + // The file format here is [2 bytes opcode][2 ASCII characters:VA for valid, IN for invalid]... + // The file terminates with four additional bytes that begin with two zero bytes. + // + // The version of the file I grabbed seems to cover all opcodes, making their enumeration + // arguably redundant; the code below nevertheless uses the codes from the file. + // + // Similarly, I'm testing for exactly the strings VA or IN to ensure no further + // types creep into any updated version of the table that I then deal with incorrectly. + uint16_t last_observed = 0; + while(true) { + // Fetch opcode number. + uint16_t next_opcode = fgetc(source) << 8; + next_opcode |= fgetc(source); + if(next_opcode < last_observed) break; + last_observed = next_opcode; + + // Determine whether it's meant to be valid. + char type[3]; + type[0] = fgetc(source); + type[1] = fgetc(source); + type[2] = '\0'; + + if(!strcmp(type, "VA")) { + // Test for validity. + if(!storage.instructions[next_opcode].micro_operations) { + false_invalids_.insert(next_opcode); + } + continue; + } + + if(!strcmp(type, "IN")) { + // Test for invalidity. + if(!storage.instructions[next_opcode].micro_operations) { + false_valids_.insert(next_opcode); + } + continue; + } + + assert(false); + } + + fclose(source); + } + + NSString *false_valids() const { + return contents_of(false_valids_); + } + + NSString *false_invalids() const { + return contents_of(false_invalids_); + } + + private: + std::set false_invalids_; + std::set false_valids_; + + static NSString *contents_of(const std::set &set) { + NSMutableString *result = [[NSMutableString alloc] init]; + + for(auto value: set) { + [result appendFormat:@"%04x ", value]; + } + + return result; + } +}; + + @interface M68000Tests : XCTestCase @end @@ -164,4 +242,15 @@ class RAM68000: public CPU::MC68000::BusHandler { XCTAssert(state.data[2] == 0x303cfb2e, "D2 was %08x instead of 0x303cfb2e", state.data[2]); } +- (void)testOpcodeCoverage { + // Perform an audit of implemented instructions. + CPU::MC68000::ProcessorStorageTests storage_tests( + _machine->processor(), + [[NSBundle bundleForClass:[self class]] pathForResource:@"OPCLOGR2" ofType:@"BIN"].UTF8String + ); + + XCTAssert(!storage_tests.false_valids().length, "Opcodes should be invalid but aren't: %@", storage_tests.false_valids()); + XCTAssert(!storage_tests.false_invalids().length, "Opcodes should be valid but aren't: %@", storage_tests.false_invalids()); +} + @end diff --git a/Processors/68000/Implementation/68000Storage.hpp b/Processors/68000/Implementation/68000Storage.hpp index 99be37fc2..cb1ccb3ab 100644 --- a/Processors/68000/Implementation/68000Storage.hpp +++ b/Processors/68000/Implementation/68000Storage.hpp @@ -387,6 +387,7 @@ class ProcessorStorage { private: friend class ProcessorStorageConstructor; + friend class ProcessorStorageTests; }; #endif /* MC68000Storage_h */