1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-08-14 07:26:16 +00:00

Compare commits

...

28 Commits

Author SHA1 Message Date
Thomas Harte
e2414af901 Test with debug builds. 2024-01-21 22:08:48 -05:00
Thomas Harte
977c961824 Update Z80 access interface. 2024-01-21 21:24:58 -05:00
Thomas Harte
2e5636a879 Merge branch 'master' into Z80Tests 2024-01-21 21:17:39 -05:00
Thomas Harte
3927ebf763 Merge branch 'master' into Z80Tests 2022-10-20 10:33:33 -04:00
Thomas Harte
7a8674f0d7 Start being more granular on data/address timing. 2022-10-10 10:43:18 -04:00
Thomas Harte
ef0fb5d16f Prepare provisionally to offer instantaneous Z80 bus sampling. 2022-09-29 22:04:16 -04:00
Thomas Harte
c50c98ebad Fix parsing of recorded address and data. 2022-09-27 16:10:11 -04:00
Thomas Harte
34e9870c3c Switch away from enum. 2022-09-27 16:09:48 -04:00
Thomas Harte
47c1e98e91 Make a first pass at bus state comparison. 2022-09-26 22:10:42 -04:00
Thomas Harte
b0d0ea9f92 Take a const ref. 2022-09-26 21:57:42 -04:00
Thomas Harte
02638b7963 Improve alignment for all RegisterPairs. 2022-09-26 13:28:41 -04:00
Thomas Harte
3455f6393a Add but disable multithreaded runner. 2022-09-26 13:18:17 -04:00
Thomas Harte
f806eb7ae2 Capture bus activity; install I and R correctly. 2022-09-22 15:34:46 -04:00
Thomas Harte
152ffbcbb6 Update permit[/current failure] list. 2022-09-20 20:04:47 -04:00
Thomas Harte
de33ee3e46 Log all digressions. 2022-09-20 15:08:55 -04:00
Thomas Harte
42aae39f35 Fix effect on flags of DD- and FD-page SCF and CCF. 2022-09-20 14:43:21 -04:00
Thomas Harte
e84c6a4e60 Apply and verify 'Q' status. 2022-09-20 14:42:52 -04:00
Thomas Harte
bda2ab47e9 Expose flag adjustment history. 2022-09-20 14:37:47 -04:00
Thomas Harte
277cdb858b Extend notes. 2022-09-20 12:20:28 -04:00
Thomas Harte
382af4fa3f Use std::swap rather than reimplementing a subset of it. 2022-09-20 12:20:10 -04:00
Thomas Harte
bf2e879798 Attempt proper IO replay, correcting 26 tests. 2022-09-20 11:55:29 -04:00
Thomas Harte
9b1d4bcf87 Sort permit list, ensure future outputs are sorted. 2022-09-20 11:20:07 -04:00
Thomas Harte
7922920094 Add initial 200 failures to the permit list. 2022-09-20 11:16:10 -04:00
Thomas Harte
a8092c73ac Fully revoke use of exceptions. 2022-09-19 22:05:53 -04:00
Thomas Harte
7f6c2e84d3 Stop a file upon any failure. 2022-09-19 22:03:55 -04:00
Thomas Harte
ec2184894d Test final register state; carve out option for permit list. 2022-09-19 22:01:49 -04:00
Thomas Harte
b67f9d4205 Mask addresses, prepare for a compact summary. 2022-09-19 16:08:16 -04:00
Thomas Harte
89b5daa160 Start attempting to apply JSMoo tests. 2022-09-19 15:44:35 -04:00
9 changed files with 486 additions and 44 deletions

View File

@@ -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() {}

View File

@@ -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 */,

View File

@@ -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>

View File

@@ -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();

View 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

View File

@@ -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;
}

View File

@@ -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];

View File

@@ -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);

View File

@@ -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);
};