mirror of
https://github.com/TomHarte/CLK.git
synced 2024-07-29 16:29:08 +00:00
Builds what I think I need to validate Z80 address, MREQ, IOREQ and RFSH.
This commit is contained in:
parent
627b96f73c
commit
67fd6787a6
@ -917,6 +917,7 @@
|
|||||||
4BDA00E022E644AF00AC3CD0 /* CSROMReceiverView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BDA00DF22E644AF00AC3CD0 /* CSROMReceiverView.m */; };
|
4BDA00E022E644AF00AC3CD0 /* CSROMReceiverView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BDA00DF22E644AF00AC3CD0 /* CSROMReceiverView.m */; };
|
||||||
4BDA00E422E663B900AC3CD0 /* NSData+CRC32.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BDA00E222E663B900AC3CD0 /* NSData+CRC32.m */; };
|
4BDA00E422E663B900AC3CD0 /* NSData+CRC32.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BDA00E222E663B900AC3CD0 /* NSData+CRC32.m */; };
|
||||||
4BDA00E622E699B000AC3CD0 /* CSMachine.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B2A53961D117D36003C6002 /* CSMachine.mm */; };
|
4BDA00E622E699B000AC3CD0 /* CSMachine.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B2A53961D117D36003C6002 /* CSMachine.mm */; };
|
||||||
|
4BDA8235261E8E000021AA19 /* Z80ContentionTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BDA8234261E8E000021AA19 /* Z80ContentionTests.mm */; };
|
||||||
4BDACBEC22FFA5D20045EF7E /* ncr5380.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BDACBEA22FFA5D20045EF7E /* ncr5380.cpp */; };
|
4BDACBEC22FFA5D20045EF7E /* ncr5380.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BDACBEA22FFA5D20045EF7E /* ncr5380.cpp */; };
|
||||||
4BDACBED22FFA5D20045EF7E /* ncr5380.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BDACBEA22FFA5D20045EF7E /* ncr5380.cpp */; };
|
4BDACBED22FFA5D20045EF7E /* ncr5380.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BDACBEA22FFA5D20045EF7E /* ncr5380.cpp */; };
|
||||||
4BDB61EB2032806E0048AF91 /* CSAtari2600.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B2A539A1D117D36003C6002 /* CSAtari2600.mm */; };
|
4BDB61EB2032806E0048AF91 /* CSAtari2600.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B2A539A1D117D36003C6002 /* CSAtari2600.mm */; };
|
||||||
@ -1934,6 +1935,7 @@
|
|||||||
4BDA00DF22E644AF00AC3CD0 /* CSROMReceiverView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CSROMReceiverView.m; sourceTree = "<group>"; };
|
4BDA00DF22E644AF00AC3CD0 /* CSROMReceiverView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CSROMReceiverView.m; sourceTree = "<group>"; };
|
||||||
4BDA00E222E663B900AC3CD0 /* NSData+CRC32.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData+CRC32.m"; sourceTree = "<group>"; };
|
4BDA00E222E663B900AC3CD0 /* NSData+CRC32.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData+CRC32.m"; sourceTree = "<group>"; };
|
||||||
4BDA00E322E663B900AC3CD0 /* NSData+CRC32.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+CRC32.h"; sourceTree = "<group>"; };
|
4BDA00E322E663B900AC3CD0 /* NSData+CRC32.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+CRC32.h"; sourceTree = "<group>"; };
|
||||||
|
4BDA8234261E8E000021AA19 /* Z80ContentionTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Z80ContentionTests.mm; sourceTree = "<group>"; };
|
||||||
4BDACBEA22FFA5D20045EF7E /* ncr5380.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ncr5380.cpp; sourceTree = "<group>"; };
|
4BDACBEA22FFA5D20045EF7E /* ncr5380.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ncr5380.cpp; sourceTree = "<group>"; };
|
||||||
4BDACBEB22FFA5D20045EF7E /* ncr5380.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ncr5380.hpp; sourceTree = "<group>"; };
|
4BDACBEB22FFA5D20045EF7E /* ncr5380.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ncr5380.hpp; sourceTree = "<group>"; };
|
||||||
4BDB3D8522833321002D3CEE /* Keyboard.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Keyboard.hpp; sourceTree = "<group>"; };
|
4BDB3D8522833321002D3CEE /* Keyboard.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Keyboard.hpp; sourceTree = "<group>"; };
|
||||||
@ -3964,6 +3966,7 @@
|
|||||||
4B2AF8681E513FC20027EE29 /* TIATests.mm */,
|
4B2AF8681E513FC20027EE29 /* TIATests.mm */,
|
||||||
4B1D08051E0F7A1100763741 /* TimeTests.mm */,
|
4B1D08051E0F7A1100763741 /* TimeTests.mm */,
|
||||||
4BEE4BD325A26E2B00011BD2 /* x86DecoderTests.mm */,
|
4BEE4BD325A26E2B00011BD2 /* x86DecoderTests.mm */,
|
||||||
|
4BDA8234261E8E000021AA19 /* Z80ContentionTests.mm */,
|
||||||
4BB73EB81B587A5100552FC2 /* Info.plist */,
|
4BB73EB81B587A5100552FC2 /* Info.plist */,
|
||||||
4BC9E1ED1D23449A003FCEE4 /* 6502InterruptTests.swift */,
|
4BC9E1ED1D23449A003FCEE4 /* 6502InterruptTests.swift */,
|
||||||
4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */,
|
4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */,
|
||||||
@ -5649,6 +5652,7 @@
|
|||||||
4B9D0C4B22C7D70A00DE1AD3 /* 68000BCDTests.mm in Sources */,
|
4B9D0C4B22C7D70A00DE1AD3 /* 68000BCDTests.mm in Sources */,
|
||||||
4B778F5E23A5F3230000D260 /* Oric.cpp in Sources */,
|
4B778F5E23A5F3230000D260 /* Oric.cpp in Sources */,
|
||||||
4B3BA0C31D318AEC005DD7A7 /* C1540Tests.swift in Sources */,
|
4B3BA0C31D318AEC005DD7A7 /* C1540Tests.swift in Sources */,
|
||||||
|
4BDA8235261E8E000021AA19 /* Z80ContentionTests.mm in Sources */,
|
||||||
4B778F1A23A5ED320000D260 /* Video.cpp in Sources */,
|
4B778F1A23A5ED320000D260 /* Video.cpp in Sources */,
|
||||||
4B778F3B23A5F1650000D260 /* KeyboardMachine.cpp in Sources */,
|
4B778F3B23A5F1650000D260 /* KeyboardMachine.cpp in Sources */,
|
||||||
4B778F2E23A5F09E0000D260 /* IRQDelegatePortHandler.cpp in Sources */,
|
4B778F2E23A5F09E0000D260 /* IRQDelegatePortHandler.cpp in Sources */,
|
||||||
|
194
OSBindings/Mac/Clock SignalTests/Z80ContentionTests.mm
Normal file
194
OSBindings/Mac/Clock SignalTests/Z80ContentionTests.mm
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
//
|
||||||
|
// Z80ContentionTests.cpp
|
||||||
|
// Clock SignalTests
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 7/4/2021.
|
||||||
|
// Copyright © 2021 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <XCTest/XCTest.h>
|
||||||
|
|
||||||
|
#include "../../../Processors/Z80/Z80.hpp"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
static constexpr uint16_t initial_pc = 0;
|
||||||
|
|
||||||
|
struct CapturingZ80: public CPU::Z80::BusHandler {
|
||||||
|
|
||||||
|
CapturingZ80(const std::initializer_list<uint8_t> &code) : z80_(*this) {
|
||||||
|
// Take a copy of the code.
|
||||||
|
std::copy(code.begin(), code.end(), ram_);
|
||||||
|
|
||||||
|
// Skip the three cycles the Z80 spends on a reset, and
|
||||||
|
// purge them from the record.
|
||||||
|
run_for(3);
|
||||||
|
bus_records_.clear();
|
||||||
|
|
||||||
|
// Set the refresh address to the EE page.
|
||||||
|
z80_.set_value_of_register(CPU::Z80::Register::I, 0xe0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void run_for(int cycles) {
|
||||||
|
z80_.run_for(HalfCycles(Cycles(cycles)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A record of the state of the address bus, MREQ, IOREQ and RFSH lines,
|
||||||
|
/// upon every clock transition.
|
||||||
|
struct BusRecord {
|
||||||
|
uint16_t address = 0xffff;
|
||||||
|
bool mreq = false, ioreq = false, refresh = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
HalfCycles perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) {
|
||||||
|
// Log the activity.
|
||||||
|
const uint8_t* const bus_state = cycle.bus_state();
|
||||||
|
for(int c = 0; c < cycle.length.as<int>(); c++) {
|
||||||
|
bus_records_.emplace_back();
|
||||||
|
|
||||||
|
// TODO: I think everything tested here should have an address,
|
||||||
|
// but am currently unsure whether the reset program puts the
|
||||||
|
// address bus in high impedance, as bus req/ack does.
|
||||||
|
if(cycle.address) {
|
||||||
|
bus_records_.back().address = *cycle.address;
|
||||||
|
}
|
||||||
|
bus_records_.back().mreq = bus_state[c] & CPU::Z80::PartialMachineCycle::Line::MREQ;
|
||||||
|
bus_records_.back().ioreq = bus_state[c] & CPU::Z80::PartialMachineCycle::Line::IOREQ;
|
||||||
|
bus_records_.back().refresh = bus_state[c] & CPU::Z80::PartialMachineCycle::Line::RFSH;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provide only reads.
|
||||||
|
if(
|
||||||
|
cycle.operation == CPU::Z80::PartialMachineCycle::Read ||
|
||||||
|
cycle.operation == CPU::Z80::PartialMachineCycle::ReadOpcode
|
||||||
|
) {
|
||||||
|
*cycle.value = ram_[*cycle.address];
|
||||||
|
}
|
||||||
|
|
||||||
|
return HalfCycles(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<BusRecord> &bus_records() const {
|
||||||
|
return bus_records_;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<BusRecord> cycle_records() const {
|
||||||
|
std::vector<BusRecord> cycle_records;
|
||||||
|
for(size_t c = 0; c < bus_records_.size(); c += 2) {
|
||||||
|
cycle_records.push_back(bus_records_[c]);
|
||||||
|
}
|
||||||
|
return cycle_records;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
CPU::Z80::Processor<CapturingZ80, false, false> z80_;
|
||||||
|
uint8_t ram_[65536];
|
||||||
|
|
||||||
|
std::vector<BusRecord> bus_records_;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@interface Z80ContentionTests : XCTestCase
|
||||||
|
@end
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Tests the Z80's MREQ, IOREQ and address outputs for correlation to those
|
||||||
|
observed by ZX Spectrum users in the software-side documentation of
|
||||||
|
contended memory timings.
|
||||||
|
*/
|
||||||
|
@implementation Z80ContentionTests {
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ContentionCheck {
|
||||||
|
uint16_t address;
|
||||||
|
int length;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Checks that the accumulated bus activity in @c z80 matches the expectations given in @c contentions if
|
||||||
|
processed by a Sinclair 48k or 128k ULA.
|
||||||
|
*/
|
||||||
|
- (void)validate48Contention:(const std::initializer_list<ContentionCheck> &)contentions z80:(const CapturingZ80 &)z80 {
|
||||||
|
// 48[/128]k contention logic: triggered on address alone, _unless_
|
||||||
|
// MREQ is also active.
|
||||||
|
//
|
||||||
|
// I think the source I'm using also implicitly assumes that refresh
|
||||||
|
// addresses are outside of the contended area, and doesn't check them.
|
||||||
|
// So unlike the actual ULA I'm also ignoring any address while refresh
|
||||||
|
// is asserted.
|
||||||
|
int count = -1;
|
||||||
|
uint16_t address = 0;
|
||||||
|
auto contention = contentions.begin();
|
||||||
|
|
||||||
|
const auto bus_records = z80.cycle_records();
|
||||||
|
for(const auto &record: bus_records) {
|
||||||
|
++count;
|
||||||
|
|
||||||
|
if(
|
||||||
|
!count || // i.e. is at start.
|
||||||
|
(&record == &bus_records.back()) || // i.e. is at end.
|
||||||
|
!(record.mreq || record.refresh) // i.e. beginning of a new contention.
|
||||||
|
) {
|
||||||
|
if(count) {
|
||||||
|
XCTAssertNotEqual(contention, contentions.end());
|
||||||
|
XCTAssertEqual(contention->address, address);
|
||||||
|
XCTAssertEqual(contention->length, count);
|
||||||
|
++contention;
|
||||||
|
}
|
||||||
|
|
||||||
|
count = 1;
|
||||||
|
address = record.address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertEqual(contention, contentions.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Checks that the accumulated bus activity in @c z80 matches the expectations given in @c contentions if
|
||||||
|
processed by an Amstrad gate array.
|
||||||
|
*/
|
||||||
|
- (void)validatePlus3Contention:(const std::initializer_list<ContentionCheck> &)contentions z80:(const CapturingZ80 &)z80 {
|
||||||
|
// +3 contention logic: triggered by the leading edge of MREQ, sans refresh.
|
||||||
|
int count = -1;
|
||||||
|
uint16_t address = 0;
|
||||||
|
auto contention = contentions.begin();
|
||||||
|
|
||||||
|
const auto bus_records = z80.bus_records();
|
||||||
|
|
||||||
|
for(size_t c = 0; c < bus_records.size(); c += 2) {
|
||||||
|
const bool is_leading_edge = !bus_records[c].mreq && bus_records[c+1].mreq && !bus_records[c].refresh;
|
||||||
|
|
||||||
|
++count;
|
||||||
|
if(
|
||||||
|
!count || // i.e. is at start.
|
||||||
|
(c == bus_records.size() - 2) || // i.e. is at end.
|
||||||
|
is_leading_edge // i.e. beginning of a new contention.
|
||||||
|
) {
|
||||||
|
if(count) {
|
||||||
|
XCTAssertNotEqual(contention, contentions.end());
|
||||||
|
XCTAssertEqual(contention->address, address);
|
||||||
|
XCTAssertEqual(contention->length, count);
|
||||||
|
++contention;
|
||||||
|
}
|
||||||
|
|
||||||
|
count = 1;
|
||||||
|
address = bus_records[c].address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertEqual(contention, contentions.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Opcode tests.
|
||||||
|
|
||||||
|
- (void)testNOP {
|
||||||
|
CapturingZ80 z80({0x00});
|
||||||
|
z80.run_for(4);
|
||||||
|
|
||||||
|
[self validate48Contention:{{initial_pc, 4}} z80:z80];
|
||||||
|
[self validatePlus3Contention:{{initial_pc, 4}} z80:z80];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
Loading…
Reference in New Issue
Block a user