mirror of
https://github.com/TomHarte/CLK.git
synced 2025-09-12 02:24:31 +00:00
Compare commits
28 Commits
Indentatio
...
Z80Tests
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 {
|
namespace CPU {
|
||||||
|
|
||||||
/// Provides access to all intermediate parts of a larger int.
|
/// 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(Full v) : full(v) {}
|
||||||
RegisterPair() {}
|
RegisterPair() {}
|
||||||
|
|
||||||
|
@@ -186,6 +186,7 @@
|
|||||||
4B0F1C242605996900B85C66 /* ZXSpectrumTAP.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0F1C212605996900B85C66 /* ZXSpectrumTAP.cpp */; };
|
4B0F1C242605996900B85C66 /* ZXSpectrumTAP.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0F1C212605996900B85C66 /* ZXSpectrumTAP.cpp */; };
|
||||||
4B0F94FE208C1A1600FE41D9 /* NIB.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0F94FC208C1A1600FE41D9 /* NIB.cpp */; };
|
4B0F94FE208C1A1600FE41D9 /* NIB.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0F94FC208C1A1600FE41D9 /* NIB.cpp */; };
|
||||||
4B0F94FF208C1A1600FE41D9 /* 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 */; };
|
4B121F9B1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B121F9A1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm */; };
|
||||||
4B12C0ED1FCFA98D005BFD93 /* Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B12C0EB1FCFA98D005BFD93 /* Keyboard.cpp */; };
|
4B12C0ED1FCFA98D005BFD93 /* Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B12C0EB1FCFA98D005BFD93 /* Keyboard.cpp */; };
|
||||||
4B12C0EE1FCFAD1A005BFD93 /* 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 */; };
|
4B30512D1D989E2200B4FED8 /* Drive.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B30512B1D989E2200B4FED8 /* Drive.cpp */; };
|
||||||
4B3051301D98ACC600B4FED8 /* Plus3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B30512E1D98ACC600B4FED8 /* Plus3.cpp */; };
|
4B3051301D98ACC600B4FED8 /* Plus3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B30512E1D98ACC600B4FED8 /* Plus3.cpp */; };
|
||||||
4B322E041F5A2E3C004EB04C /* Z80Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B322E031F5A2E3C004EB04C /* Z80Base.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 */; };
|
4B37EE821D7345A6006A09A4 /* BinaryDump.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B37EE801D7345A6006A09A4 /* BinaryDump.cpp */; };
|
||||||
4B38F3481F2EC11D00D9235D /* AmstradCPC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B38F3461F2EC11D00D9235D /* AmstradCPC.cpp */; };
|
4B38F3481F2EC11D00D9235D /* AmstradCPC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B38F3461F2EC11D00D9235D /* AmstradCPC.cpp */; };
|
||||||
4B3BA0C31D318AEC005DD7A7 /* C1540Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B3BA0C21D318AEB005DD7A7 /* C1540Tests.swift */; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
4B12C0EC1FCFA98D005BFD93 /* Keyboard.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Keyboard.hpp; sourceTree = "<group>"; };
|
||||||
@@ -4492,6 +4495,7 @@
|
|||||||
4B1D08051E0F7A1100763741 /* TimeTests.mm */,
|
4B1D08051E0F7A1100763741 /* TimeTests.mm */,
|
||||||
4BEE4BD325A26E2B00011BD2 /* x86DecoderTests.mm */,
|
4BEE4BD325A26E2B00011BD2 /* x86DecoderTests.mm */,
|
||||||
4BDA8234261E8E000021AA19 /* Z80ContentionTests.mm */,
|
4BDA8234261E8E000021AA19 /* Z80ContentionTests.mm */,
|
||||||
|
4B11D48F28D8F60300070EA7 /* Z80JSMooTests.mm */,
|
||||||
4BB73EB81B587A5100552FC2 /* Info.plist */,
|
4BB73EB81B587A5100552FC2 /* Info.plist */,
|
||||||
4BC9E1ED1D23449A003FCEE4 /* 6502InterruptTests.swift */,
|
4BC9E1ED1D23449A003FCEE4 /* 6502InterruptTests.swift */,
|
||||||
4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */,
|
4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */,
|
||||||
@@ -6260,6 +6264,7 @@
|
|||||||
4B778F4223A5F1A70000D260 /* MemoryFuzzer.cpp in Sources */,
|
4B778F4223A5F1A70000D260 /* MemoryFuzzer.cpp in Sources */,
|
||||||
4B778F0123A5EBA00000D260 /* MacintoshIMG.cpp in Sources */,
|
4B778F0123A5EBA00000D260 /* MacintoshIMG.cpp in Sources */,
|
||||||
4B7752AD28217E770073E2C5 /* AmigaADF.cpp in Sources */,
|
4B7752AD28217E770073E2C5 /* AmigaADF.cpp in Sources */,
|
||||||
|
4B32DBD128E3DB3200F4A16A /* MacintoshVolume.cpp in Sources */,
|
||||||
4BFF1D3D2235C3C100838EA1 /* EmuTOSTests.mm in Sources */,
|
4BFF1D3D2235C3C100838EA1 /* EmuTOSTests.mm in Sources */,
|
||||||
4B3F76B925A1635300178AEC /* PowerPCDecoderTests.mm in Sources */,
|
4B3F76B925A1635300178AEC /* PowerPCDecoderTests.mm in Sources */,
|
||||||
4B778F0A23A5EC150000D260 /* TapePRG.cpp in Sources */,
|
4B778F0A23A5EC150000D260 /* TapePRG.cpp in Sources */,
|
||||||
@@ -6293,6 +6298,7 @@
|
|||||||
4B924E991E74D22700B76AF1 /* AtariStaticAnalyserTests.mm in Sources */,
|
4B924E991E74D22700B76AF1 /* AtariStaticAnalyserTests.mm in Sources */,
|
||||||
4B90467622C6FD6E000E2074 /* 68000ArithmeticTests.mm in Sources */,
|
4B90467622C6FD6E000E2074 /* 68000ArithmeticTests.mm in Sources */,
|
||||||
4B778F3423A5F1040000D260 /* DirectAccessDevice.cpp in Sources */,
|
4B778F3423A5F1040000D260 /* DirectAccessDevice.cpp in Sources */,
|
||||||
|
4B11D49028D8F60300070EA7 /* Z80JSMooTests.mm in Sources */,
|
||||||
4B7BC7F51F58F27800D1B1B4 /* 6502AllRAM.cpp in Sources */,
|
4B7BC7F51F58F27800D1B1B4 /* 6502AllRAM.cpp in Sources */,
|
||||||
4BC5C3E022C994CD00795658 /* 68000MoveTests.mm in Sources */,
|
4BC5C3E022C994CD00795658 /* 68000MoveTests.mm in Sources */,
|
||||||
4B778F5923A5F2D00000D260 /* Z80.cpp in Sources */,
|
4B778F5923A5F2D00000D260 /* Z80.cpp in Sources */,
|
||||||
|
@@ -27,6 +27,8 @@
|
|||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
enableASanStackUseAfterReturn = "YES"
|
||||||
|
enableUBSanitizer = "YES"
|
||||||
disableMainThreadChecker = "YES"
|
disableMainThreadChecker = "YES"
|
||||||
codeCoverageEnabled = "YES">
|
codeCoverageEnabled = "YES">
|
||||||
<MacroExpansion>
|
<MacroExpansion>
|
||||||
|
@@ -85,7 +85,7 @@ struct CapturingZ80: public CPU::Z80::BusHandler {
|
|||||||
//
|
//
|
||||||
// Log the plain bus activity.
|
// 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++) {
|
for(int c = 0; c < cycle.length.as<int>(); c++) {
|
||||||
bus_records_.emplace_back();
|
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::IM: return uint16_t(interrupt_mode_);
|
||||||
|
|
||||||
case Register::MemPtr: return memptr_.full;
|
case Register::MemPtr: return memptr_.full;
|
||||||
|
case Register::DidChangeFlags: return flag_adjustment_history_ & 1;
|
||||||
|
|
||||||
default: return 0;
|
default: return 0;
|
||||||
}
|
}
|
||||||
@@ -115,6 +116,7 @@ void ProcessorBase::set_value_of(Register r, uint16_t value) {
|
|||||||
case Register::IM: interrupt_mode_ = value % 3; 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;
|
default: break;
|
||||||
}
|
}
|
||||||
|
@@ -877,6 +877,9 @@ template < class T,
|
|||||||
case MicroOp::SetInstructionPage:
|
case MicroOp::SetInstructionPage:
|
||||||
current_instruction_page_ = static_cast<InstructionPage *>(operation->source);
|
current_instruction_page_ = static_cast<InstructionPage *>(operation->source);
|
||||||
scheduled_program_counter_ = current_instruction_page_->fetch_decode_execute_data;
|
scheduled_program_counter_ = current_instruction_page_->fetch_decode_execute_data;
|
||||||
|
|
||||||
|
// Undo spurious history update.
|
||||||
|
flag_adjustment_history_ >>= 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MicroOp::CalculateIndexAddress:
|
case MicroOp::CalculateIndexAddress:
|
||||||
@@ -928,7 +931,7 @@ template < class T,
|
|||||||
template < class T,
|
template < class T,
|
||||||
bool uses_bus_request,
|
bool uses_bus_request,
|
||||||
bool uses_wait_line> void Processor <T, uses_bus_request, uses_wait_line>
|
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 number_of_micro_ops = 0;
|
||||||
std::size_t lengths[256];
|
std::size_t lengths[256];
|
||||||
|
|
||||||
|
@@ -224,7 +224,7 @@ class ProcessorStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
typedef MicroOp InstructionTable[256][30];
|
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;
|
virtual void copy_program(const MicroOp *source, std::vector<MicroOp> &destination) = 0;
|
||||||
|
|
||||||
void assemble_fetch_decode_execute(InstructionPage &target, int length);
|
void assemble_fetch_decode_execute(InstructionPage &target, int length);
|
||||||
|
@@ -10,8 +10,8 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <vector>
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "../../Numeric/RegisterSizes.hpp"
|
#include "../../Numeric/RegisterSizes.hpp"
|
||||||
#include "../../ClockReceiver/ClockReceiver.hpp"
|
#include "../../ClockReceiver/ClockReceiver.hpp"
|
||||||
@@ -42,7 +42,12 @@ enum class Register {
|
|||||||
|
|
||||||
IFF1, IFF2, IM,
|
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;
|
return operation <= Operation::Write;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Line {
|
using LineStateT = uint16_t;
|
||||||
CLK = 1 << 0,
|
struct Line {
|
||||||
|
static constexpr LineStateT CLK = 1 << 0;
|
||||||
|
|
||||||
MREQ = 1 << 1,
|
static constexpr LineStateT MREQ = 1 << 1;
|
||||||
IOREQ = 1 << 2,
|
static constexpr LineStateT IOREQ = 1 << 2;
|
||||||
|
|
||||||
RD = 1 << 3,
|
static constexpr LineStateT RD = 1 << 3;
|
||||||
WR = 1 << 4,
|
static constexpr LineStateT WR = 1 << 4;
|
||||||
RFSH = 1 << 5,
|
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
|
/// @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.
|
/// 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.
|
/// @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) {
|
switch(operation) {
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -179,25 +222,30 @@ struct PartialMachineCycle {
|
|||||||
//
|
//
|
||||||
|
|
||||||
case Operation::ReadOpcodeStart: {
|
case Operation::ReadOpcodeStart: {
|
||||||
static constexpr uint8_t states[] = {
|
static constexpr LineStateT period_states[] = {
|
||||||
Line::CLK | Line::M1,
|
BusStateDecomposer::state("C 1__ A"),
|
||||||
Line::M1 | Line::MREQ | Line::RD,
|
BusStateDecomposer::state("_ 1MR A"),
|
||||||
Line::CLK | Line::M1 | Line::MREQ | Line::RD,
|
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::ReadOpcode:
|
||||||
case Operation::ReadOpcodeWait: {
|
case Operation::ReadOpcodeWait: {
|
||||||
static constexpr uint8_t states[] = {
|
static constexpr LineStateT states[] = {
|
||||||
Line::M1 | Line::MREQ | Line::RD,
|
BusStateDecomposer::state("_ 1MR A"),
|
||||||
Line::CLK | Line::M1 | Line::MREQ | Line::RD,
|
BusStateDecomposer::state("C 1MR A"),
|
||||||
};
|
};
|
||||||
return states;
|
return states;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Operation::Refresh: {
|
case Operation::Refresh: {
|
||||||
static constexpr uint8_t states[] = {
|
static constexpr LineStateT states[] = {
|
||||||
Line::CLK | Line::RFSH | Line::MREQ,
|
Line::CLK | Line::RFSH | Line::MREQ,
|
||||||
Line::RFSH,
|
Line::RFSH,
|
||||||
Line::CLK | Line::RFSH | Line::MREQ,
|
Line::CLK | Line::RFSH | Line::MREQ,
|
||||||
@@ -215,7 +263,7 @@ struct PartialMachineCycle {
|
|||||||
//
|
//
|
||||||
|
|
||||||
case Operation::ReadStart: {
|
case Operation::ReadStart: {
|
||||||
static constexpr uint8_t states[] = {
|
static constexpr LineStateT states[] = {
|
||||||
Line::CLK,
|
Line::CLK,
|
||||||
Line::RD | Line::MREQ,
|
Line::RD | Line::MREQ,
|
||||||
Line::CLK | Line::RD | Line::MREQ,
|
Line::CLK | Line::RD | Line::MREQ,
|
||||||
@@ -224,7 +272,7 @@ struct PartialMachineCycle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case Operation::ReadWait: {
|
case Operation::ReadWait: {
|
||||||
static constexpr uint8_t states[] = {
|
static constexpr LineStateT states[] = {
|
||||||
Line::MREQ | Line::RD,
|
Line::MREQ | Line::RD,
|
||||||
Line::CLK | Line::MREQ | Line::RD,
|
Line::CLK | Line::MREQ | Line::RD,
|
||||||
Line::MREQ | Line::RD,
|
Line::MREQ | Line::RD,
|
||||||
@@ -236,7 +284,7 @@ struct PartialMachineCycle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case Operation::Read: {
|
case Operation::Read: {
|
||||||
static constexpr uint8_t states[] = {
|
static constexpr LineStateT states[] = {
|
||||||
Line::MREQ | Line::RD,
|
Line::MREQ | Line::RD,
|
||||||
Line::CLK | Line::MREQ | Line::RD,
|
Line::CLK | Line::MREQ | Line::RD,
|
||||||
0,
|
0,
|
||||||
@@ -249,7 +297,7 @@ struct PartialMachineCycle {
|
|||||||
//
|
//
|
||||||
|
|
||||||
case Operation::WriteStart: {
|
case Operation::WriteStart: {
|
||||||
static constexpr uint8_t states[] = {
|
static constexpr LineStateT states[] = {
|
||||||
Line::CLK,
|
Line::CLK,
|
||||||
Line::MREQ,
|
Line::MREQ,
|
||||||
Line::CLK | Line::MREQ,
|
Line::CLK | Line::MREQ,
|
||||||
@@ -258,7 +306,7 @@ struct PartialMachineCycle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case Operation::WriteWait: {
|
case Operation::WriteWait: {
|
||||||
static constexpr uint8_t states[] = {
|
static constexpr LineStateT states[] = {
|
||||||
Line::MREQ,
|
Line::MREQ,
|
||||||
Line::CLK | Line::MREQ,
|
Line::CLK | Line::MREQ,
|
||||||
Line::MREQ,
|
Line::MREQ,
|
||||||
@@ -270,7 +318,7 @@ struct PartialMachineCycle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case Operation::Write: {
|
case Operation::Write: {
|
||||||
static constexpr uint8_t states[] = {
|
static constexpr LineStateT states[] = {
|
||||||
Line::MREQ | Line::WR,
|
Line::MREQ | Line::WR,
|
||||||
Line::CLK | Line::MREQ | Line::WR,
|
Line::CLK | Line::MREQ | Line::WR,
|
||||||
0,
|
0,
|
||||||
@@ -283,7 +331,7 @@ struct PartialMachineCycle {
|
|||||||
//
|
//
|
||||||
|
|
||||||
case Operation::InputStart: {
|
case Operation::InputStart: {
|
||||||
static constexpr uint8_t states[] = {
|
static constexpr LineStateT states[] = {
|
||||||
Line::CLK,
|
Line::CLK,
|
||||||
0,
|
0,
|
||||||
Line::CLK | Line::IOREQ | Line::RD,
|
Line::CLK | Line::IOREQ | Line::RD,
|
||||||
@@ -292,7 +340,7 @@ struct PartialMachineCycle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case Operation::InputWait: {
|
case Operation::InputWait: {
|
||||||
static constexpr uint8_t states[] = {
|
static constexpr LineStateT states[] = {
|
||||||
Line::IOREQ | Line::RD,
|
Line::IOREQ | Line::RD,
|
||||||
Line::CLK | Line::IOREQ | Line::RD,
|
Line::CLK | Line::IOREQ | Line::RD,
|
||||||
};
|
};
|
||||||
@@ -300,7 +348,7 @@ struct PartialMachineCycle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case Operation::Input: {
|
case Operation::Input: {
|
||||||
static constexpr uint8_t states[] = {
|
static constexpr LineStateT states[] = {
|
||||||
Line::IOREQ | Line::RD,
|
Line::IOREQ | Line::RD,
|
||||||
Line::CLK | Line::IOREQ | Line::RD,
|
Line::CLK | Line::IOREQ | Line::RD,
|
||||||
0,
|
0,
|
||||||
@@ -313,7 +361,7 @@ struct PartialMachineCycle {
|
|||||||
//
|
//
|
||||||
|
|
||||||
case Operation::OutputStart: {
|
case Operation::OutputStart: {
|
||||||
static constexpr uint8_t states[] = {
|
static constexpr LineStateT states[] = {
|
||||||
Line::CLK,
|
Line::CLK,
|
||||||
0,
|
0,
|
||||||
Line::CLK | Line::IOREQ | Line::WR,
|
Line::CLK | Line::IOREQ | Line::WR,
|
||||||
@@ -322,7 +370,7 @@ struct PartialMachineCycle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case Operation::OutputWait: {
|
case Operation::OutputWait: {
|
||||||
static constexpr uint8_t states[] = {
|
static constexpr LineStateT states[] = {
|
||||||
Line::IOREQ | Line::WR,
|
Line::IOREQ | Line::WR,
|
||||||
Line::CLK | Line::IOREQ | Line::WR,
|
Line::CLK | Line::IOREQ | Line::WR,
|
||||||
};
|
};
|
||||||
@@ -330,7 +378,7 @@ struct PartialMachineCycle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case Operation::Output: {
|
case Operation::Output: {
|
||||||
static constexpr uint8_t states[] = {
|
static constexpr LineStateT states[] = {
|
||||||
Line::IOREQ | Line::WR,
|
Line::IOREQ | Line::WR,
|
||||||
Line::CLK | Line::IOREQ | Line::WR,
|
Line::CLK | Line::IOREQ | Line::WR,
|
||||||
0,
|
0,
|
||||||
@@ -347,7 +395,7 @@ struct PartialMachineCycle {
|
|||||||
//
|
//
|
||||||
|
|
||||||
case Operation::BusAcknowledge: {
|
case Operation::BusAcknowledge: {
|
||||||
static constexpr uint8_t states[] = {
|
static constexpr LineStateT states[] = {
|
||||||
Line::CLK | Line::BUSACK,
|
Line::CLK | Line::BUSACK,
|
||||||
Line::BUSACK,
|
Line::BUSACK,
|
||||||
};
|
};
|
||||||
@@ -359,7 +407,7 @@ struct PartialMachineCycle {
|
|||||||
//
|
//
|
||||||
|
|
||||||
case Operation::Internal: {
|
case Operation::Internal: {
|
||||||
static constexpr uint8_t states[] = {
|
static constexpr LineStateT states[] = {
|
||||||
Line::CLK, 0,
|
Line::CLK, 0,
|
||||||
Line::CLK, 0,
|
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:
|
private:
|
||||||
T &bus_handler_;
|
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);
|
void copy_program(const MicroOp *source, std::vector<MicroOp> &destination);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user