mirror of
https://github.com/TomHarte/CLK.git
synced 2025-08-14 07:26:16 +00:00
Compare commits
28 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
e2414af901 | ||
|
977c961824 | ||
|
2e5636a879 | ||
|
3927ebf763 | ||
|
7a8674f0d7 | ||
|
ef0fb5d16f | ||
|
c50c98ebad | ||
|
34e9870c3c | ||
|
47c1e98e91 | ||
|
b0d0ea9f92 | ||
|
02638b7963 | ||
|
3455f6393a | ||
|
f806eb7ae2 | ||
|
152ffbcbb6 | ||
|
de33ee3e46 | ||
|
42aae39f35 | ||
|
e84c6a4e60 | ||
|
bda2ab47e9 | ||
|
277cdb858b | ||
|
382af4fa3f | ||
|
bf2e879798 | ||
|
9b1d4bcf87 | ||
|
7922920094 | ||
|
a8092c73ac | ||
|
7f6c2e84d3 | ||
|
ec2184894d | ||
|
b67f9d4205 | ||
|
89b5daa160 |
@@ -18,7 +18,7 @@
|
||||
namespace CPU {
|
||||
|
||||
/// Provides access to all intermediate parts of a larger int.
|
||||
template <typename Full, typename Half> union alignas(Full) alignas(Half) RegisterPair {
|
||||
template <typename Full, typename Half> union alignas(alignof(Full)) RegisterPair {
|
||||
RegisterPair(Full v) : full(v) {}
|
||||
RegisterPair() {}
|
||||
|
||||
|
@@ -186,6 +186,7 @@
|
||||
4B0F1C242605996900B85C66 /* ZXSpectrumTAP.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0F1C212605996900B85C66 /* ZXSpectrumTAP.cpp */; };
|
||||
4B0F94FE208C1A1600FE41D9 /* NIB.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0F94FC208C1A1600FE41D9 /* NIB.cpp */; };
|
||||
4B0F94FF208C1A1600FE41D9 /* NIB.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0F94FC208C1A1600FE41D9 /* NIB.cpp */; };
|
||||
4B11D49028D8F60300070EA7 /* Z80JSMooTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B11D48F28D8F60300070EA7 /* Z80JSMooTests.mm */; };
|
||||
4B121F9B1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B121F9A1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm */; };
|
||||
4B12C0ED1FCFA98D005BFD93 /* Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B12C0EB1FCFA98D005BFD93 /* Keyboard.cpp */; };
|
||||
4B12C0EE1FCFAD1A005BFD93 /* Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B12C0EB1FCFA98D005BFD93 /* Keyboard.cpp */; };
|
||||
@@ -247,6 +248,7 @@
|
||||
4B30512D1D989E2200B4FED8 /* Drive.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B30512B1D989E2200B4FED8 /* Drive.cpp */; };
|
||||
4B3051301D98ACC600B4FED8 /* Plus3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B30512E1D98ACC600B4FED8 /* Plus3.cpp */; };
|
||||
4B322E041F5A2E3C004EB04C /* Z80Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B322E031F5A2E3C004EB04C /* Z80Base.cpp */; };
|
||||
4B32DBD128E3DB3200F4A16A /* MacintoshVolume.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4C81C928B56CF800F84AE9 /* MacintoshVolume.cpp */; };
|
||||
4B37EE821D7345A6006A09A4 /* BinaryDump.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B37EE801D7345A6006A09A4 /* BinaryDump.cpp */; };
|
||||
4B38F3481F2EC11D00D9235D /* AmstradCPC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B38F3461F2EC11D00D9235D /* AmstradCPC.cpp */; };
|
||||
4B3BA0C31D318AEC005DD7A7 /* C1540Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B3BA0C21D318AEB005DD7A7 /* C1540Tests.swift */; };
|
||||
@@ -1298,6 +1300,7 @@
|
||||
4B0F94FC208C1A1600FE41D9 /* NIB.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = NIB.cpp; sourceTree = "<group>"; };
|
||||
4B0F94FD208C1A1600FE41D9 /* NIB.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = NIB.hpp; sourceTree = "<group>"; };
|
||||
4B0F9500208C42A300FE41D9 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Target.hpp; path = AppleII/Target.hpp; sourceTree = "<group>"; };
|
||||
4B11D48F28D8F60300070EA7 /* Z80JSMooTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Z80JSMooTests.mm; sourceTree = "<group>"; };
|
||||
4B121F9A1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PCMSegmentEventSourceTests.mm; sourceTree = "<group>"; };
|
||||
4B12C0EB1FCFA98D005BFD93 /* Keyboard.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Keyboard.cpp; sourceTree = "<group>"; };
|
||||
4B12C0EC1FCFA98D005BFD93 /* Keyboard.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Keyboard.hpp; sourceTree = "<group>"; };
|
||||
@@ -4492,6 +4495,7 @@
|
||||
4B1D08051E0F7A1100763741 /* TimeTests.mm */,
|
||||
4BEE4BD325A26E2B00011BD2 /* x86DecoderTests.mm */,
|
||||
4BDA8234261E8E000021AA19 /* Z80ContentionTests.mm */,
|
||||
4B11D48F28D8F60300070EA7 /* Z80JSMooTests.mm */,
|
||||
4BB73EB81B587A5100552FC2 /* Info.plist */,
|
||||
4BC9E1ED1D23449A003FCEE4 /* 6502InterruptTests.swift */,
|
||||
4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */,
|
||||
@@ -6260,6 +6264,7 @@
|
||||
4B778F4223A5F1A70000D260 /* MemoryFuzzer.cpp in Sources */,
|
||||
4B778F0123A5EBA00000D260 /* MacintoshIMG.cpp in Sources */,
|
||||
4B7752AD28217E770073E2C5 /* AmigaADF.cpp in Sources */,
|
||||
4B32DBD128E3DB3200F4A16A /* MacintoshVolume.cpp in Sources */,
|
||||
4BFF1D3D2235C3C100838EA1 /* EmuTOSTests.mm in Sources */,
|
||||
4B3F76B925A1635300178AEC /* PowerPCDecoderTests.mm in Sources */,
|
||||
4B778F0A23A5EC150000D260 /* TapePRG.cpp in Sources */,
|
||||
@@ -6293,6 +6298,7 @@
|
||||
4B924E991E74D22700B76AF1 /* AtariStaticAnalyserTests.mm in Sources */,
|
||||
4B90467622C6FD6E000E2074 /* 68000ArithmeticTests.mm in Sources */,
|
||||
4B778F3423A5F1040000D260 /* DirectAccessDevice.cpp in Sources */,
|
||||
4B11D49028D8F60300070EA7 /* Z80JSMooTests.mm in Sources */,
|
||||
4B7BC7F51F58F27800D1B1B4 /* 6502AllRAM.cpp in Sources */,
|
||||
4BC5C3E022C994CD00795658 /* 68000MoveTests.mm in Sources */,
|
||||
4B778F5923A5F2D00000D260 /* Z80.cpp in Sources */,
|
||||
|
@@ -27,6 +27,8 @@
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
enableASanStackUseAfterReturn = "YES"
|
||||
enableUBSanitizer = "YES"
|
||||
disableMainThreadChecker = "YES"
|
||||
codeCoverageEnabled = "YES">
|
||||
<MacroExpansion>
|
||||
|
@@ -85,7 +85,7 @@ struct CapturingZ80: public CPU::Z80::BusHandler {
|
||||
//
|
||||
// Log the plain bus activity.
|
||||
//
|
||||
const uint8_t *const bus_state = cycle.bus_state();
|
||||
const auto bus_state = cycle.bus_state<CPU::Z80::PartialMachineCycle::SampleType::Period>();
|
||||
for(int c = 0; c < cycle.length.as<int>(); c++) {
|
||||
bus_records_.emplace_back();
|
||||
|
||||
|
381
OSBindings/Mac/Clock SignalTests/Z80JSMooTests.mm
Normal file
381
OSBindings/Mac/Clock SignalTests/Z80JSMooTests.mm
Normal file
@@ -0,0 +1,381 @@
|
||||
//
|
||||
// Z80JSMooTests.cpp
|
||||
// Clock SignalTests
|
||||
//
|
||||
// Created by Thomas Harte on 19/9/2022.
|
||||
// Copyright © 2022 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#include <optional>
|
||||
#include "../../../Processors/Z80/Z80.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
// Tests are not duplicated into this repository due to their size;
|
||||
// put them somewhere on your local system and provide the path here.
|
||||
constexpr const char *TestPath = "/Users/thomasharte/Projects/jsmoo/misc/tests/GeneratedTests/z80/v1";
|
||||
|
||||
#define MapFields() \
|
||||
Map(A, @"a"); Map(Flags, @"f"); Map(AFDash, @"af_"); \
|
||||
Map(B, @"b"); Map(C, @"c"); Map(BCDash, @"bc_"); \
|
||||
Map(D, @"d"); Map(E, @"e"); Map(DEDash, @"de_"); \
|
||||
Map(H, @"h"); Map(L, @"l"); Map(HLDash, @"hl_"); \
|
||||
\
|
||||
Map(IX, @"ix"); Map(IY, @"iy"); \
|
||||
Map(IFF1, @"iff1"); Map(IFF2, @"iff2"); \
|
||||
Map(IM, @"im"); \
|
||||
Map(I, @"i"); Map(R, @"r"); \
|
||||
\
|
||||
Map(ProgramCounter, @"pc"); \
|
||||
Map(StackPointer, @"sp"); \
|
||||
Map(MemPtr, @"wz"); \
|
||||
Map(DidChangeFlags, @"q");
|
||||
|
||||
/*
|
||||
Not used:
|
||||
|
||||
EI (duplicative of IFF1?)
|
||||
p
|
||||
*/
|
||||
|
||||
struct CapturingZ80: public CPU::Z80::BusHandler {
|
||||
|
||||
CapturingZ80(NSDictionary *state, NSArray *port_activity) : z80_(*this) {
|
||||
z80_.reset_power_on();
|
||||
|
||||
// Set registers.
|
||||
#define Map(register, name) z80_.set_value_of(CPU::Z80::Register::register, [state[name] intValue])
|
||||
MapFields();
|
||||
#undef Map
|
||||
|
||||
// Populate RAM.
|
||||
for(NSArray *byte in state[@"ram"]) {
|
||||
const int address = [byte[0] intValue] & 0xffff;
|
||||
const int value = [byte[1] intValue];
|
||||
ram_[address] = value;
|
||||
}
|
||||
|
||||
// Capture expected port activity.
|
||||
for(NSArray *item in port_activity) {
|
||||
expected_port_accesses_.emplace_back([item[0] intValue], [item[1] intValue], [item[2] isEqualToString:@"r"]);
|
||||
}
|
||||
next_port_ = expected_port_accesses_.begin();
|
||||
}
|
||||
|
||||
bool compare_state(NSDictionary *state) {
|
||||
bool failed = false;
|
||||
|
||||
// Compare registers.
|
||||
//
|
||||
// TEMPORARILY: DON'T COMPARE DidChangeFlags OR MemPtr.
|
||||
#define Map(register, name) \
|
||||
if( \
|
||||
CPU::Z80::Register::register != CPU::Z80::Register::DidChangeFlags && \
|
||||
CPU::Z80::Register::register != CPU::Z80::Register::MemPtr && \
|
||||
z80_.value_of(CPU::Z80::Register::register) != [state[name] intValue]) { \
|
||||
NSLog(@"Register %s should be %02x; is %02x", #register, [state[name] intValue], z80_.value_of(CPU::Z80::Register::register)); \
|
||||
failed = true; \
|
||||
}
|
||||
|
||||
MapFields()
|
||||
|
||||
#undef Map
|
||||
|
||||
// Compare RAM.
|
||||
for(NSArray *byte in state[@"ram"]) {
|
||||
const int address = [byte[0] intValue] & 0xffff;
|
||||
const int value = [byte[1] intValue];
|
||||
|
||||
if(ram_[address] != value) {
|
||||
NSLog(@"Value at address %04x should be %02x; is %02x", address, value, ram_[address]);
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check ports.
|
||||
if(!ports_matched()) {
|
||||
NSLog(@"Mismatch in port activity");
|
||||
failed = true;
|
||||
}
|
||||
|
||||
return !failed;
|
||||
}
|
||||
|
||||
bool compare_bus_states([[maybe_unused]] NSArray<NSArray *> *states) {
|
||||
/* auto capture = bus_records_.begin() + 1;
|
||||
|
||||
int cycle = 0;
|
||||
for(NSArray *state in states) {
|
||||
// Extract proper bus state.
|
||||
const std::optional<uint16_t> address =
|
||||
[state[0] isKindOfClass:[NSNumber class]] ? std::optional<uint16_t>([state[0] intValue]) : std::nullopt;
|
||||
const std::optional<uint8_t> data =
|
||||
[state[1] isKindOfClass:[NSNumber class]] ? std::optional<uint8_t>([state[1] intValue]) : std::nullopt;
|
||||
|
||||
NSString *const controls = state[2];
|
||||
const bool read = [controls characterAtIndex:0] != '-';
|
||||
const bool write = [controls characterAtIndex:1] != '-';
|
||||
const bool m1 = [controls characterAtIndex:2] != '-';
|
||||
const bool ioReq = [controls characterAtIndex:2] != '-';
|
||||
|
||||
// Compare to captured state.
|
||||
bool failed = false;
|
||||
if(address != capture->address) {
|
||||
NSLog(@"Address mismatch after %d cycles", cycle);
|
||||
failed = true;
|
||||
}
|
||||
if(data != capture->data) {
|
||||
NSLog(@"Data mismatch after %d cycles", cycle);
|
||||
failed = true;
|
||||
}
|
||||
|
||||
using Line = CPU::Z80::PartialMachineCycle::Line;
|
||||
if(read != bool(capture->lines & Line::RD)) {
|
||||
NSLog(@"Read line mismatch after %d cycles", cycle);
|
||||
failed = true;
|
||||
}
|
||||
if(write != bool(capture->lines & Line::WR)) {
|
||||
NSLog(@"Write line mismatch after %d cycles", cycle);
|
||||
failed = true;
|
||||
}
|
||||
if(m1 != bool(capture->lines & Line::M1)) {
|
||||
NSLog(@"M1 line mismatch after %d cycles", cycle);
|
||||
failed = true;
|
||||
}
|
||||
if(ioReq != bool(capture->lines & Line::IOREQ)) {
|
||||
NSLog(@"IOREQ line mismatch after %d cycles", cycle);
|
||||
failed = true;
|
||||
}
|
||||
|
||||
if(failed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Advance.
|
||||
capture += 2;
|
||||
++cycle;
|
||||
}*/
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void run_for(int cycles) {
|
||||
z80_.run_for(HalfCycles(Cycles(cycles)));
|
||||
XCTAssertEqual(bus_records_.size(), cycles * 2);
|
||||
}
|
||||
|
||||
struct BusRecord {
|
||||
std::optional<uint16_t> address = 0xffff;
|
||||
std::optional<uint8_t> data = 0xff;
|
||||
uint8_t lines = 0xff;
|
||||
|
||||
BusRecord(std::optional<uint16_t> address, std::optional<uint8_t> data, uint8_t lines) :
|
||||
address(address), data(data), lines(lines) {}
|
||||
};
|
||||
|
||||
HalfCycles perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) {
|
||||
|
||||
//
|
||||
// Do the actual action.
|
||||
//
|
||||
switch(cycle.operation) {
|
||||
default: break;
|
||||
|
||||
case CPU::Z80::PartialMachineCycle::Read:
|
||||
case CPU::Z80::PartialMachineCycle::ReadOpcode:
|
||||
*cycle.value = ram_[*cycle.address];
|
||||
break;
|
||||
|
||||
case CPU::Z80::PartialMachineCycle::Write:
|
||||
ram_[*cycle.address] = *cycle.value;
|
||||
break;
|
||||
|
||||
case CPU::Z80::PartialMachineCycle::Input:
|
||||
if(next_port_ != expected_port_accesses_.end() && next_port_->is_read && next_port_->address == *cycle.address) {
|
||||
*cycle.value = next_port_->value;
|
||||
++next_port_;
|
||||
} else {
|
||||
ports_matched_ = false;
|
||||
*cycle.value = 0xff;
|
||||
}
|
||||
break;
|
||||
|
||||
case CPU::Z80::PartialMachineCycle::Output:
|
||||
if(next_port_ != expected_port_accesses_.end() && !next_port_->is_read && next_port_->address == *cycle.address) {
|
||||
ports_matched_ &= *cycle.value == next_port_->value;
|
||||
++next_port_;
|
||||
} else {
|
||||
ports_matched_ = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
// Capture bus activity.
|
||||
//
|
||||
const auto data = cycle.value ? std::optional<uint8_t>(*cycle.value) : std::nullopt;
|
||||
const auto address = cycle.address ? std::optional<uint16_t>(*cycle.address) : std::nullopt;
|
||||
const auto bus = cycle.bus_state<CPU::Z80::PartialMachineCycle::SampleType::Instant>();
|
||||
for(int i = 0; i < cycle.length.as<int>(); i++) {
|
||||
bus_records_.emplace_back(address, data, bus[i]);
|
||||
}
|
||||
|
||||
return HalfCycles(0);
|
||||
}
|
||||
|
||||
const std::vector<BusRecord> &bus_records() const {
|
||||
return bus_records_;
|
||||
}
|
||||
|
||||
bool ports_matched() const {
|
||||
return ports_matched_ && next_port_ == expected_port_accesses_.end();
|
||||
}
|
||||
|
||||
private:
|
||||
CPU::Z80::Processor<CapturingZ80, false, false> z80_;
|
||||
uint8_t ram_[65536];
|
||||
|
||||
struct PortAccess {
|
||||
const uint16_t address = 0;
|
||||
const uint8_t value = 0;
|
||||
const bool is_read = false;
|
||||
|
||||
PortAccess(uint16_t a, uint8_t v, bool r) : address(a), value(v), is_read(r) {}
|
||||
};
|
||||
std::vector<PortAccess> expected_port_accesses_;
|
||||
std::vector<PortAccess>::iterator next_port_;
|
||||
bool ports_matched_ = true;
|
||||
|
||||
std::vector<BusRecord> bus_records_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@interface Z80JSMooTests : XCTestCase
|
||||
@end
|
||||
|
||||
@implementation Z80JSMooTests
|
||||
|
||||
- (BOOL)applyTest:(NSDictionary *)test {
|
||||
// Log something.
|
||||
// NSLog(@"Test %@", test[@"name"]);
|
||||
|
||||
// Seed Z80 and run to conclusion.
|
||||
auto z80 = std::make_unique<CapturingZ80>(test[@"initial"], test[@"ports"]);
|
||||
z80->run_for(int([test[@"cycles"] count]));
|
||||
// z80->run_for(15);
|
||||
|
||||
// Check register and RAM state.
|
||||
return z80->compare_state(test[@"final"]) && z80->compare_bus_states(test[@"cycles"]);
|
||||
}
|
||||
|
||||
- (BOOL)applyTests:(NSString *)path {
|
||||
NSArray<NSDictionary *> *const tests =
|
||||
[NSJSONSerialization JSONObjectWithData:
|
||||
[NSData dataWithContentsOfFile:path]
|
||||
options:0
|
||||
error:nil];
|
||||
|
||||
XCTAssertNotNil(tests);
|
||||
|
||||
BOOL allSucceeded = YES;
|
||||
for(NSDictionary *test in tests) {
|
||||
allSucceeded &= [self applyTest:test];
|
||||
|
||||
if(!allSucceeded) {
|
||||
NSLog(@"Failed at %@", test[@"name"]);
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: switch to the below, or some approximation thereof.
|
||||
// Current issue: Z80 construction assumes something heading towards 500kb
|
||||
// of stack is available, and dispatch_apply seems to create an environment
|
||||
// much tighter than that.
|
||||
|
||||
// __block BOOL allSucceeded = YES;
|
||||
//
|
||||
// const size_t pageSize = 10;
|
||||
// const auto stepsPerBlock = size_t([tests count] / pageSize);
|
||||
// dispatch_apply(pageSize, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t index) {
|
||||
// const size_t start = index * stepsPerBlock;
|
||||
// for(size_t c = start; c < start + stepsPerBlock; c++) {
|
||||
// assert(c < tests.count);
|
||||
// NSLog(@"%d begins %d", int(index), int(c));
|
||||
//
|
||||
// allSucceeded &= [self applyTest:tests[c]];
|
||||
//
|
||||
// if(!allSucceeded) {
|
||||
// NSLog(@"Failed at %@", tests[c][@"name"]);
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
|
||||
return allSucceeded;
|
||||
}
|
||||
|
||||
- (void)testAll {
|
||||
// Get a list of everything in the 'TestPath' directory.
|
||||
NSError *error;
|
||||
NSString *const testPath = @(TestPath);
|
||||
NSArray<NSString *> *const sources =
|
||||
[[NSFileManager defaultManager] contentsOfDirectoryAtPath:testPath error:&error];
|
||||
|
||||
// Optional: a permit list; leave empty to allow all tests.
|
||||
NSSet<NSString *> *permitList = [NSSet setWithArray:@[
|
||||
// @"cb 46.json",
|
||||
// @"cb 4e.json",
|
||||
// @"cb 56.json",
|
||||
// @"cb 5e.json",
|
||||
// @"cb 66.json",
|
||||
// @"cb 6e.json",
|
||||
// @"cb 76.json",
|
||||
// @"cb 7e.json",
|
||||
@"ed a2.json",
|
||||
// @"ed b2.json"
|
||||
]];
|
||||
|
||||
// Treat lack of a local copy of these tests as a non-failing condition.
|
||||
if(error || ![sources count]) {
|
||||
NSLog(@"No tests found at %s; not testing, not failing", TestPath);
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply tests one by one.
|
||||
NSMutableArray *failures = [[NSMutableArray alloc] init];
|
||||
for(NSString *source in sources) {
|
||||
if(![[source pathExtension] isEqualToString:@"json"]) {
|
||||
NSLog(@"Skipping %@", source);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip if: (i) there is a permit list; and (ii) this file isn't on it.
|
||||
if([permitList count] && ![permitList containsObject:source]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NSLog(@"Testing %@", source);
|
||||
if(![self applyTests:[testPath stringByAppendingPathComponent:source]]) {
|
||||
NSLog(@"Failed");
|
||||
[failures addObject:source];
|
||||
}
|
||||
}
|
||||
|
||||
[failures sortUsingComparator:^NSComparisonResult(NSString *obj1, NSString *obj2) {
|
||||
if([obj1 length] < [obj2 length]) {
|
||||
return NSOrderedAscending;
|
||||
}
|
||||
if([obj2 length] < [obj1 length]) {
|
||||
return NSOrderedDescending;
|
||||
}
|
||||
|
||||
return [obj1 compare:obj2];
|
||||
}];
|
||||
NSLog(@"Files with failures were: %@", failures);
|
||||
|
||||
XCTAssertEqual([failures count], 0);
|
||||
}
|
||||
@end
|
@@ -62,6 +62,7 @@ uint16_t ProcessorBase::value_of(Register r) const {
|
||||
case Register::IM: return uint16_t(interrupt_mode_);
|
||||
|
||||
case Register::MemPtr: return memptr_.full;
|
||||
case Register::DidChangeFlags: return flag_adjustment_history_ & 1;
|
||||
|
||||
default: return 0;
|
||||
}
|
||||
@@ -110,11 +111,12 @@ void ProcessorBase::set_value_of(Register r, uint16_t value) {
|
||||
case Register::I: ir_.halves.high = uint8_t(value); break;
|
||||
case Register::Refresh: ir_.full = value; break;
|
||||
|
||||
case Register::IFF1: iff1_ = !!value; break;
|
||||
case Register::IFF2: iff2_ = !!value; break;
|
||||
case Register::IM: interrupt_mode_ = value % 3; break;
|
||||
case Register::IFF1: iff1_ = !!value; break;
|
||||
case Register::IFF2: iff2_ = !!value; break;
|
||||
case Register::IM: interrupt_mode_ = value % 3; break;
|
||||
|
||||
case Register::MemPtr: memptr_.full = value; break;
|
||||
case Register::MemPtr: memptr_.full = value; break;
|
||||
case Register::DidChangeFlags: flag_adjustment_history_ = value ? 1 : 0; break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
@@ -877,6 +877,9 @@ template < class T,
|
||||
case MicroOp::SetInstructionPage:
|
||||
current_instruction_page_ = static_cast<InstructionPage *>(operation->source);
|
||||
scheduled_program_counter_ = current_instruction_page_->fetch_decode_execute_data;
|
||||
|
||||
// Undo spurious history update.
|
||||
flag_adjustment_history_ >>= 1;
|
||||
break;
|
||||
|
||||
case MicroOp::CalculateIndexAddress:
|
||||
@@ -928,7 +931,7 @@ template < class T,
|
||||
template < class T,
|
||||
bool uses_bus_request,
|
||||
bool uses_wait_line> void Processor <T, uses_bus_request, uses_wait_line>
|
||||
::assemble_page(InstructionPage &target, InstructionTable &table, bool add_offsets) {
|
||||
::assemble_page(InstructionPage &target, const InstructionTable &table, bool add_offsets) {
|
||||
std::size_t number_of_micro_ops = 0;
|
||||
std::size_t lengths[256];
|
||||
|
||||
|
@@ -224,7 +224,7 @@ class ProcessorStorage {
|
||||
}
|
||||
|
||||
typedef MicroOp InstructionTable[256][30];
|
||||
virtual void assemble_page(InstructionPage &target, InstructionTable &table, bool add_offsets) = 0;
|
||||
virtual void assemble_page(InstructionPage &target, const InstructionTable &table, bool add_offsets) = 0;
|
||||
virtual void copy_program(const MicroOp *source, std::vector<MicroOp> &destination) = 0;
|
||||
|
||||
void assemble_fetch_decode_execute(InstructionPage &target, int length);
|
||||
|
@@ -10,8 +10,8 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "../../Numeric/RegisterSizes.hpp"
|
||||
#include "../../ClockReceiver/ClockReceiver.hpp"
|
||||
@@ -42,7 +42,12 @@ enum class Register {
|
||||
|
||||
IFF1, IFF2, IM,
|
||||
|
||||
MemPtr
|
||||
MemPtr,
|
||||
|
||||
/// Obscure, and related to status bits 3 & 5 upon an SCF or CCF; this
|
||||
/// is a single bit indicating whether an immediately-following [S/C]CF
|
||||
/// can only set bits 3 & 5 or may also reset them.
|
||||
DidChangeFlags,
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -151,19 +156,27 @@ struct PartialMachineCycle {
|
||||
return operation <= Operation::Write;
|
||||
}
|
||||
|
||||
enum Line {
|
||||
CLK = 1 << 0,
|
||||
using LineStateT = uint16_t;
|
||||
struct Line {
|
||||
static constexpr LineStateT CLK = 1 << 0;
|
||||
|
||||
MREQ = 1 << 1,
|
||||
IOREQ = 1 << 2,
|
||||
static constexpr LineStateT MREQ = 1 << 1;
|
||||
static constexpr LineStateT IOREQ = 1 << 2;
|
||||
|
||||
RD = 1 << 3,
|
||||
WR = 1 << 4,
|
||||
RFSH = 1 << 5,
|
||||
static constexpr LineStateT RD = 1 << 3;
|
||||
static constexpr LineStateT WR = 1 << 4;
|
||||
static constexpr LineStateT RFSH = 1 << 5;
|
||||
|
||||
M1 = 1 << 6,
|
||||
static constexpr LineStateT M1 = 1 << 6;
|
||||
|
||||
BUSACK = 1 << 7,
|
||||
static constexpr LineStateT BUSACK = 1 << 7;
|
||||
|
||||
static constexpr LineStateT DataActive = 1 << 8;
|
||||
static constexpr LineStateT AddressActive = 1 << 9;
|
||||
};
|
||||
|
||||
enum class SampleType {
|
||||
Instant, Period
|
||||
};
|
||||
|
||||
/// @returns A C-style array of the bus state at the beginning of each half cycle in this
|
||||
@@ -171,7 +184,37 @@ struct PartialMachineCycle {
|
||||
/// bit set means line active, bit clear means line inactive. For the CLK line set means high.
|
||||
///
|
||||
/// @discussion This discrete sampling is prone to aliasing errors. Beware.
|
||||
const uint8_t *bus_state() const {
|
||||
///
|
||||
/// @c sample_type indicates whether to describe bus activity:
|
||||
/// (i) as a series of instantaneous samples indicating levels at the
|
||||
/// immediate start of the time period; or
|
||||
/// (ii) as windowed samples, indicating active if the signal is active
|
||||
/// at any time during this time period.
|
||||
///
|
||||
template <SampleType sample_type> const LineStateT *bus_state() const {
|
||||
struct BusStateDecomposer {
|
||||
static constexpr LineStateT state(const char *source) {
|
||||
LineStateT result = 0;
|
||||
while(*source) {
|
||||
switch(*source) {
|
||||
default: break;
|
||||
case 'C': result |= Line::CLK; break;
|
||||
case 'M': result |= Line::MREQ; break;
|
||||
case 'I': result |= Line::IOREQ; break;
|
||||
case 'R': result |= Line::RD; break;
|
||||
case 'W': result |= Line::WR; break;
|
||||
case 'F': result |= Line::RFSH; break;
|
||||
case '1': result |= Line::M1; break;
|
||||
case 'K': result |= Line::BUSACK; break;
|
||||
case 'D': result |= Line::DataActive; break;
|
||||
case 'A': result |= Line::AddressActive; break;
|
||||
}
|
||||
++source;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
switch(operation) {
|
||||
|
||||
//
|
||||
@@ -179,25 +222,30 @@ struct PartialMachineCycle {
|
||||
//
|
||||
|
||||
case Operation::ReadOpcodeStart: {
|
||||
static constexpr uint8_t states[] = {
|
||||
Line::CLK | Line::M1,
|
||||
Line::M1 | Line::MREQ | Line::RD,
|
||||
Line::CLK | Line::M1 | Line::MREQ | Line::RD,
|
||||
static constexpr LineStateT period_states[] = {
|
||||
BusStateDecomposer::state("C 1__ A"),
|
||||
BusStateDecomposer::state("_ 1MR A"),
|
||||
BusStateDecomposer::state("C 1MR A"),
|
||||
};
|
||||
return states;
|
||||
static constexpr LineStateT instant_states[] = {
|
||||
BusStateDecomposer::state("C ___ _"),
|
||||
BusStateDecomposer::state("_ 1M_ A"),
|
||||
BusStateDecomposer::state("C 1MR A"),
|
||||
};
|
||||
return sample_type == SampleType::Period ? period_states : instant_states;
|
||||
}
|
||||
|
||||
case Operation::ReadOpcode:
|
||||
case Operation::ReadOpcodeWait: {
|
||||
static constexpr uint8_t states[] = {
|
||||
Line::M1 | Line::MREQ | Line::RD,
|
||||
Line::CLK | Line::M1 | Line::MREQ | Line::RD,
|
||||
static constexpr LineStateT states[] = {
|
||||
BusStateDecomposer::state("_ 1MR A"),
|
||||
BusStateDecomposer::state("C 1MR A"),
|
||||
};
|
||||
return states;
|
||||
}
|
||||
|
||||
case Operation::Refresh: {
|
||||
static constexpr uint8_t states[] = {
|
||||
static constexpr LineStateT states[] = {
|
||||
Line::CLK | Line::RFSH | Line::MREQ,
|
||||
Line::RFSH,
|
||||
Line::CLK | Line::RFSH | Line::MREQ,
|
||||
@@ -215,7 +263,7 @@ struct PartialMachineCycle {
|
||||
//
|
||||
|
||||
case Operation::ReadStart: {
|
||||
static constexpr uint8_t states[] = {
|
||||
static constexpr LineStateT states[] = {
|
||||
Line::CLK,
|
||||
Line::RD | Line::MREQ,
|
||||
Line::CLK | Line::RD | Line::MREQ,
|
||||
@@ -224,7 +272,7 @@ struct PartialMachineCycle {
|
||||
}
|
||||
|
||||
case Operation::ReadWait: {
|
||||
static constexpr uint8_t states[] = {
|
||||
static constexpr LineStateT states[] = {
|
||||
Line::MREQ | Line::RD,
|
||||
Line::CLK | Line::MREQ | Line::RD,
|
||||
Line::MREQ | Line::RD,
|
||||
@@ -236,7 +284,7 @@ struct PartialMachineCycle {
|
||||
}
|
||||
|
||||
case Operation::Read: {
|
||||
static constexpr uint8_t states[] = {
|
||||
static constexpr LineStateT states[] = {
|
||||
Line::MREQ | Line::RD,
|
||||
Line::CLK | Line::MREQ | Line::RD,
|
||||
0,
|
||||
@@ -249,7 +297,7 @@ struct PartialMachineCycle {
|
||||
//
|
||||
|
||||
case Operation::WriteStart: {
|
||||
static constexpr uint8_t states[] = {
|
||||
static constexpr LineStateT states[] = {
|
||||
Line::CLK,
|
||||
Line::MREQ,
|
||||
Line::CLK | Line::MREQ,
|
||||
@@ -258,7 +306,7 @@ struct PartialMachineCycle {
|
||||
}
|
||||
|
||||
case Operation::WriteWait: {
|
||||
static constexpr uint8_t states[] = {
|
||||
static constexpr LineStateT states[] = {
|
||||
Line::MREQ,
|
||||
Line::CLK | Line::MREQ,
|
||||
Line::MREQ,
|
||||
@@ -270,7 +318,7 @@ struct PartialMachineCycle {
|
||||
}
|
||||
|
||||
case Operation::Write: {
|
||||
static constexpr uint8_t states[] = {
|
||||
static constexpr LineStateT states[] = {
|
||||
Line::MREQ | Line::WR,
|
||||
Line::CLK | Line::MREQ | Line::WR,
|
||||
0,
|
||||
@@ -283,7 +331,7 @@ struct PartialMachineCycle {
|
||||
//
|
||||
|
||||
case Operation::InputStart: {
|
||||
static constexpr uint8_t states[] = {
|
||||
static constexpr LineStateT states[] = {
|
||||
Line::CLK,
|
||||
0,
|
||||
Line::CLK | Line::IOREQ | Line::RD,
|
||||
@@ -292,7 +340,7 @@ struct PartialMachineCycle {
|
||||
}
|
||||
|
||||
case Operation::InputWait: {
|
||||
static constexpr uint8_t states[] = {
|
||||
static constexpr LineStateT states[] = {
|
||||
Line::IOREQ | Line::RD,
|
||||
Line::CLK | Line::IOREQ | Line::RD,
|
||||
};
|
||||
@@ -300,7 +348,7 @@ struct PartialMachineCycle {
|
||||
}
|
||||
|
||||
case Operation::Input: {
|
||||
static constexpr uint8_t states[] = {
|
||||
static constexpr LineStateT states[] = {
|
||||
Line::IOREQ | Line::RD,
|
||||
Line::CLK | Line::IOREQ | Line::RD,
|
||||
0,
|
||||
@@ -313,7 +361,7 @@ struct PartialMachineCycle {
|
||||
//
|
||||
|
||||
case Operation::OutputStart: {
|
||||
static constexpr uint8_t states[] = {
|
||||
static constexpr LineStateT states[] = {
|
||||
Line::CLK,
|
||||
0,
|
||||
Line::CLK | Line::IOREQ | Line::WR,
|
||||
@@ -322,7 +370,7 @@ struct PartialMachineCycle {
|
||||
}
|
||||
|
||||
case Operation::OutputWait: {
|
||||
static constexpr uint8_t states[] = {
|
||||
static constexpr LineStateT states[] = {
|
||||
Line::IOREQ | Line::WR,
|
||||
Line::CLK | Line::IOREQ | Line::WR,
|
||||
};
|
||||
@@ -330,7 +378,7 @@ struct PartialMachineCycle {
|
||||
}
|
||||
|
||||
case Operation::Output: {
|
||||
static constexpr uint8_t states[] = {
|
||||
static constexpr LineStateT states[] = {
|
||||
Line::IOREQ | Line::WR,
|
||||
Line::CLK | Line::IOREQ | Line::WR,
|
||||
0,
|
||||
@@ -347,7 +395,7 @@ struct PartialMachineCycle {
|
||||
//
|
||||
|
||||
case Operation::BusAcknowledge: {
|
||||
static constexpr uint8_t states[] = {
|
||||
static constexpr LineStateT states[] = {
|
||||
Line::CLK | Line::BUSACK,
|
||||
Line::BUSACK,
|
||||
};
|
||||
@@ -359,7 +407,7 @@ struct PartialMachineCycle {
|
||||
//
|
||||
|
||||
case Operation::Internal: {
|
||||
static constexpr uint8_t states[] = {
|
||||
static constexpr LineStateT states[] = {
|
||||
Line::CLK, 0,
|
||||
Line::CLK, 0,
|
||||
Line::CLK, 0,
|
||||
@@ -530,7 +578,7 @@ template <class T, bool uses_bus_request, bool uses_wait_line> class Processor:
|
||||
private:
|
||||
T &bus_handler_;
|
||||
|
||||
void assemble_page(InstructionPage &target, InstructionTable &table, bool add_offsets);
|
||||
void assemble_page(InstructionPage &target, const InstructionTable &table, bool add_offsets);
|
||||
void copy_program(const MicroOp *source, std::vector<MicroOp> &destination);
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user