mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-22 12:33:29 +00:00
Merge pull request #761 from TomHarte/Z80Tests
Imports and satisfies additional Z80 unit tests
This commit is contained in:
commit
5f8bb92f36
@ -1012,6 +1012,9 @@ template <bool has_fdc> class ConcreteMachine:
|
||||
default: break;
|
||||
}
|
||||
|
||||
// Check whether the interrupt signal has changed the other way.
|
||||
if(interrupt_timer_.request_has_changed()) z80_.set_interrupt_line(interrupt_timer_.get_request());
|
||||
|
||||
// This implementation doesn't use time-stuffing; once in-phase waits won't be longer
|
||||
// than a single cycle so there's no real performance benefit to trying to find the
|
||||
// next non-wait when a wait cycle comes in, and there'd be no benefit to reproducing
|
||||
@ -1228,7 +1231,7 @@ template <bool has_fdc> class ConcreteMachine:
|
||||
KeyboardState key_state_;
|
||||
AmstradCPC::KeyboardMapper keyboard_mapper_;
|
||||
|
||||
uint8_t ram_[128 * 1024];
|
||||
uint8_t ram_[1024 * 1024];
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -225,6 +225,12 @@
|
||||
4B643F3F1D77B88000D431D6 /* DocumentController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B643F3E1D77B88000D431D6 /* DocumentController.swift */; };
|
||||
4B65086022F4CF8D009C1100 /* Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B65085F22F4CF8D009C1100 /* Keyboard.cpp */; };
|
||||
4B65086122F4CFE0009C1100 /* Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B65085F22F4CF8D009C1100 /* Keyboard.cpp */; };
|
||||
4B670A9B2401CB8400D4E002 /* z80memptr.tap in Resources */ = {isa = PBXBuildFile; fileRef = 4B670A832401CB8400D4E002 /* z80memptr.tap */; };
|
||||
4B670A9D2401CB8400D4E002 /* z80ccf.tap in Resources */ = {isa = PBXBuildFile; fileRef = 4B670A852401CB8400D4E002 /* z80ccf.tap */; };
|
||||
4B670A9F2401CB8400D4E002 /* z80flags.tap in Resources */ = {isa = PBXBuildFile; fileRef = 4B670A872401CB8400D4E002 /* z80flags.tap */; };
|
||||
4B670AA12401CB8400D4E002 /* z80doc.tap in Resources */ = {isa = PBXBuildFile; fileRef = 4B670A892401CB8400D4E002 /* z80doc.tap */; };
|
||||
4B670AB02401CB8400D4E002 /* z80full.tap in Resources */ = {isa = PBXBuildFile; fileRef = 4B670A992401CB8400D4E002 /* z80full.tap */; };
|
||||
4B670AB12401CB8400D4E002 /* z80docflags.tap in Resources */ = {isa = PBXBuildFile; fileRef = 4B670A9A2401CB8400D4E002 /* z80docflags.tap */; };
|
||||
4B680CE223A5553100451D43 /* 68000ComparativeTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B680CE123A5553100451D43 /* 68000ComparativeTests.mm */; };
|
||||
4B680CE423A555CA00451D43 /* 68000 Comparative Tests in Resources */ = {isa = PBXBuildFile; fileRef = 4B680CE323A555CA00451D43 /* 68000 Comparative Tests */; };
|
||||
4B69FB3D1C4D908A00B5F0AA /* Tape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B69FB3B1C4D908A00B5F0AA /* Tape.cpp */; };
|
||||
@ -817,6 +823,8 @@
|
||||
4BD67DCC209BE4D700AB2146 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD67DCA209BE4D600AB2146 /* StaticAnalyser.cpp */; };
|
||||
4BD67DD0209BF27B00AB2146 /* Encoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD67DCE209BF27B00AB2146 /* Encoder.cpp */; };
|
||||
4BD67DD1209BF27B00AB2146 /* Encoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD67DCE209BF27B00AB2146 /* Encoder.cpp */; };
|
||||
4BD91D732401960C007BDC91 /* STX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7BA03323C58B1E00B98D9E /* STX.cpp */; };
|
||||
4BD91D772401C2B8007BDC91 /* PatrikRakTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BD91D762401C2B8007BDC91 /* PatrikRakTests.swift */; };
|
||||
4BDA00DA22E60EE300AC3CD0 /* ROMRequester.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4BDA00D922E60EE300AC3CD0 /* ROMRequester.xib */; };
|
||||
4BDA00DD22E622C200AC3CD0 /* ROMImages in Resources */ = {isa = PBXBuildFile; fileRef = 4BC9DF441D044FCA00F44158 /* ROMImages */; };
|
||||
4BDA00E022E644AF00AC3CD0 /* CSROMReceiverView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BDA00DF22E644AF00AC3CD0 /* CSROMReceiverView.m */; };
|
||||
@ -1131,6 +1139,12 @@
|
||||
4B643F3E1D77B88000D431D6 /* DocumentController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DocumentController.swift; sourceTree = "<group>"; };
|
||||
4B644ED023F0FB55006C0CC5 /* ScanSynchroniser.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ScanSynchroniser.hpp; sourceTree = "<group>"; };
|
||||
4B65085F22F4CF8D009C1100 /* Keyboard.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Keyboard.cpp; sourceTree = "<group>"; };
|
||||
4B670A832401CB8400D4E002 /* z80memptr.tap */ = {isa = PBXFileReference; lastKnownFileType = file; path = z80memptr.tap; sourceTree = "<group>"; };
|
||||
4B670A852401CB8400D4E002 /* z80ccf.tap */ = {isa = PBXFileReference; lastKnownFileType = file; path = z80ccf.tap; sourceTree = "<group>"; };
|
||||
4B670A872401CB8400D4E002 /* z80flags.tap */ = {isa = PBXFileReference; lastKnownFileType = file; path = z80flags.tap; sourceTree = "<group>"; };
|
||||
4B670A892401CB8400D4E002 /* z80doc.tap */ = {isa = PBXFileReference; lastKnownFileType = file; path = z80doc.tap; sourceTree = "<group>"; };
|
||||
4B670A992401CB8400D4E002 /* z80full.tap */ = {isa = PBXFileReference; lastKnownFileType = file; path = z80full.tap; sourceTree = "<group>"; };
|
||||
4B670A9A2401CB8400D4E002 /* z80docflags.tap */ = {isa = PBXFileReference; lastKnownFileType = file; path = z80docflags.tap; sourceTree = "<group>"; };
|
||||
4B680CE123A5553100451D43 /* 68000ComparativeTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = 68000ComparativeTests.mm; sourceTree = "<group>"; };
|
||||
4B680CE323A555CA00451D43 /* 68000 Comparative Tests */ = {isa = PBXFileReference; lastKnownFileType = folder; path = "68000 Comparative Tests"; sourceTree = "<group>"; };
|
||||
4B698D1A1FE768A100696C91 /* SampleSource.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = SampleSource.hpp; sourceTree = "<group>"; };
|
||||
@ -1696,6 +1710,7 @@
|
||||
4BD67DCE209BF27B00AB2146 /* Encoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Encoder.cpp; sourceTree = "<group>"; };
|
||||
4BD67DCF209BF27B00AB2146 /* Encoder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Encoder.hpp; sourceTree = "<group>"; };
|
||||
4BD9137D1F311BC5009BCF85 /* i8255.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = i8255.hpp; path = 8255/i8255.hpp; sourceTree = "<group>"; };
|
||||
4BD91D762401C2B8007BDC91 /* PatrikRakTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PatrikRakTests.swift; sourceTree = "<group>"; };
|
||||
4BDA00D922E60EE300AC3CD0 /* ROMRequester.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ROMRequester.xib; sourceTree = "<group>"; };
|
||||
4BDA00DE22E644AF00AC3CD0 /* CSROMReceiverView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CSROMReceiverView.h; sourceTree = "<group>"; };
|
||||
4BDA00DF22E644AF00AC3CD0 /* CSROMReceiverView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CSROMReceiverView.m; sourceTree = "<group>"; };
|
||||
@ -1923,6 +1938,7 @@
|
||||
4B1414631B588A1100E04248 /* Test Binaries */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4B670A822401CB8400D4E002 /* Patrik Rak Z80 Tests */,
|
||||
4B680CE323A555CA00451D43 /* 68000 Comparative Tests */,
|
||||
4B85322B227793CA00F26553 /* TOS Startup */,
|
||||
4B9252CD1E74D28200B76AF1 /* Atari ROMs */,
|
||||
@ -2432,6 +2448,19 @@
|
||||
path = "Document Controller";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4B670A822401CB8400D4E002 /* Patrik Rak Z80 Tests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4B670A852401CB8400D4E002 /* z80ccf.tap */,
|
||||
4B670A892401CB8400D4E002 /* z80doc.tap */,
|
||||
4B670A9A2401CB8400D4E002 /* z80docflags.tap */,
|
||||
4B670A872401CB8400D4E002 /* z80flags.tap */,
|
||||
4B670A992401CB8400D4E002 /* z80full.tap */,
|
||||
4B670A832401CB8400D4E002 /* z80memptr.tap */,
|
||||
);
|
||||
path = "Patrik Rak Z80 Tests";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4B69FB391C4D908A00B5F0AA /* Storage */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -3316,6 +3345,7 @@
|
||||
4BEF6AAB1D35D1C400E73575 /* DPLLTests.swift */,
|
||||
4BBF49AE1ED2880200AB3669 /* FUSETests.swift */,
|
||||
4B1414611B58888700E04248 /* KlausDormannTests.swift */,
|
||||
4BD91D762401C2B8007BDC91 /* PatrikRakTests.swift */,
|
||||
4B14145F1B58885000E04248 /* WolfgangLorenzTests.swift */,
|
||||
4B08A2741EE35D56008B7065 /* Z80InterruptTests.swift */,
|
||||
4BDDBA981EF3451200347E61 /* Z80MachineCycleTests.swift */,
|
||||
@ -3953,6 +3983,7 @@
|
||||
4BB299551B587D8400A49093 /* eoriy in Resources */,
|
||||
4BB298F91B587D8400A49093 /* adczx in Resources */,
|
||||
4BB299F21B587D8400A49093 /* trap6 in Resources */,
|
||||
4B670A9B2401CB8400D4E002 /* z80memptr.tap in Resources */,
|
||||
4BB299A91B587D8400A49093 /* plpn in Resources */,
|
||||
4BB299671B587D8400A49093 /* inxn in Resources */,
|
||||
4BB2998C1B587D8400A49093 /* lsez in Resources */,
|
||||
@ -3994,6 +4025,7 @@
|
||||
4BB299B01B587D8400A49093 /* rlazx in Resources */,
|
||||
4BB2999F1B587D8400A49093 /* oraax in Resources */,
|
||||
4BB299B71B587D8400A49093 /* rorax in Resources */,
|
||||
4B670AB02401CB8400D4E002 /* z80full.tap in Resources */,
|
||||
4BB299DB1B587D8400A49093 /* staz in Resources */,
|
||||
4BB299961B587D8400A49093 /* nmi in Resources */,
|
||||
4BB299241B587D8400A49093 /* cia1ta in Resources */,
|
||||
@ -4007,6 +4039,7 @@
|
||||
4BB299B11B587D8400A49093 /* rola in Resources */,
|
||||
4BB299CE1B587D8400A49093 /* secn in Resources */,
|
||||
4BB298F31B587D8400A49093 /* adcax in Resources */,
|
||||
4B670A9F2401CB8400D4E002 /* z80flags.tap in Resources */,
|
||||
4BB299641B587D8400A49093 /* insiy in Resources */,
|
||||
4BB299E61B587D8400A49093 /* trap10 in Resources */,
|
||||
4BB299651B587D8400A49093 /* insz in Resources */,
|
||||
@ -4047,6 +4080,7 @@
|
||||
4BB299251B587D8400A49093 /* cia1tab in Resources */,
|
||||
4BB299C21B587D8400A49093 /* rtin in Resources */,
|
||||
4BB299071B587D8400A49093 /* aslax in Resources */,
|
||||
4B670AB12401CB8400D4E002 /* z80docflags.tap in Resources */,
|
||||
4BB299D51B587D8400A49093 /* shyax in Resources */,
|
||||
4BB2992F1B587D8400A49093 /* clin in Resources */,
|
||||
4BB299D21B587D8400A49093 /* shaiy in Resources */,
|
||||
@ -4074,6 +4108,7 @@
|
||||
4BB299DD1B587D8400A49093 /* stxa in Resources */,
|
||||
4BB299051B587D8400A49093 /* arrb in Resources */,
|
||||
4BB299DC1B587D8400A49093 /* stazx in Resources */,
|
||||
4B670A9D2401CB8400D4E002 /* z80ccf.tap in Resources */,
|
||||
4BB299C41B587D8400A49093 /* sbca in Resources */,
|
||||
4BB298F41B587D8400A49093 /* adcay in Resources */,
|
||||
4B44EBF51DC987AF00A7820C /* AllSuiteA.bin in Resources */,
|
||||
@ -4104,6 +4139,7 @@
|
||||
4BB2991E1B587D8400A49093 /* branchwrap in Resources */,
|
||||
4BB299121B587D8400A49093 /* axsa in Resources */,
|
||||
4BB299561B587D8400A49093 /* eorz in Resources */,
|
||||
4B670AA12401CB8400D4E002 /* z80doc.tap in Resources */,
|
||||
4BB299941B587D8400A49093 /* mmu in Resources */,
|
||||
4BB299E11B587D8400A49093 /* styz in Resources */,
|
||||
4BB299BA1B587D8400A49093 /* rorzx in Resources */,
|
||||
@ -4643,6 +4679,7 @@
|
||||
4BEF6AAA1D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm in Sources */,
|
||||
4B778F3123A5F0CB0000D260 /* Keyboard.cpp in Sources */,
|
||||
4B778F4A23A5F1FB0000D260 /* StaticAnalyser.cpp in Sources */,
|
||||
4BD91D772401C2B8007BDC91 /* PatrikRakTests.swift in Sources */,
|
||||
4B680CE223A5553100451D43 /* 68000ComparativeTests.mm in Sources */,
|
||||
4B778F3723A5F11C0000D260 /* Parser.cpp in Sources */,
|
||||
4B778F4523A5F1CD0000D260 /* SegmentParser.cpp in Sources */,
|
||||
@ -4780,6 +4817,7 @@
|
||||
4B778F0223A5EBA40000D260 /* MFMSectorDump.cpp in Sources */,
|
||||
4BFCA1271ECBE33200AC40C1 /* TestMachineZ80.mm in Sources */,
|
||||
4B778F3E23A5F17C0000D260 /* IWM.cpp in Sources */,
|
||||
4BD91D732401960C007BDC91 /* STX.cpp in Sources */,
|
||||
4B778F1023A5EC5D0000D260 /* Drive.cpp in Sources */,
|
||||
4B9D0C4F22C7E0CF00DE1AD3 /* 68000RollShiftTests.mm in Sources */,
|
||||
);
|
||||
|
@ -23,9 +23,9 @@
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
buildConfiguration = "Release"
|
||||
selectedDebuggerIdentifier = ""
|
||||
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
disableMainThreadChecker = "YES"
|
||||
codeCoverageEnabled = "YES">
|
||||
@ -67,7 +67,7 @@
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Release"
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
enableASanStackUseAfterReturn = "YES"
|
||||
|
@ -149,7 +149,7 @@
|
||||
}
|
||||
|
||||
// Run the thing.
|
||||
const auto comparitor = [] {
|
||||
const auto comparitor = [=] {
|
||||
// Test the end state.
|
||||
NSDictionary *const finalState = test[@"final state"];
|
||||
const auto state = test68000->processor.get_state();
|
||||
|
@ -46,6 +46,11 @@ typedef NS_ENUM(NSInteger, CSTestMachineZ80Register) {
|
||||
CSTestMachineZ80RegisterMemPtr
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, CSTestMachinePortLogic) {
|
||||
CSTestMachinePortLogicReturnUpperByte,
|
||||
CSTestMachinePortLogicReturn191
|
||||
};
|
||||
|
||||
@interface CSTestMachineZ80 : CSTestMachine
|
||||
|
||||
- (void)setData:(nonnull NSData *)data atAddress:(uint16_t)startAddress;
|
||||
@ -53,6 +58,7 @@ typedef NS_ENUM(NSInteger, CSTestMachineZ80Register) {
|
||||
- (uint8_t)valueAtAddress:(uint16_t)address;
|
||||
|
||||
- (void)runForNumberOfCycles:(int)cycles;
|
||||
- (void)runForInstruction;
|
||||
|
||||
- (void)setValue:(uint16_t)value forRegister:(CSTestMachineZ80Register)reg;
|
||||
- (uint16_t)valueForRegister:(CSTestMachineZ80Register)reg;
|
||||
@ -67,4 +73,6 @@ typedef NS_ENUM(NSInteger, CSTestMachineZ80Register) {
|
||||
@property(nonatomic) BOOL irqLine;
|
||||
@property(nonatomic) BOOL waitLine;
|
||||
|
||||
@property(nonatomic) CSTestMachinePortLogic portLogic;
|
||||
|
||||
@end
|
||||
|
@ -70,6 +70,16 @@ static CPU::Z80::Register registerForRegister(CSTestMachineZ80Register reg) {
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Port Logic
|
||||
|
||||
struct PortAccessDelegateTopByte: public CPU::Z80::AllRAMProcessor::PortAccessDelegate {
|
||||
uint8_t z80_all_ram_processor_input(uint16_t port) final { return port >> 8; }
|
||||
};
|
||||
|
||||
struct PortAccessDelegate191: public CPU::Z80::AllRAMProcessor::PortAccessDelegate {
|
||||
uint8_t z80_all_ram_processor_input(uint16_t port) final { return 191; }
|
||||
};
|
||||
|
||||
#pragma mark - Capture class
|
||||
|
||||
@interface CSTestMachineZ80BusOperationCapture()
|
||||
@ -104,6 +114,9 @@ static CPU::Z80::Register registerForRegister(CSTestMachineZ80Register reg) {
|
||||
|
||||
NSMutableArray<CSTestMachineZ80BusOperationCapture *> *_busOperationCaptures;
|
||||
int _timeSeekingReadOpcode;
|
||||
|
||||
PortAccessDelegateTopByte _topBytePortDelegate;
|
||||
PortAccessDelegate191 _value191PortDelegate;
|
||||
}
|
||||
|
||||
#pragma mark - Lifecycle
|
||||
@ -114,6 +127,7 @@ static CPU::Z80::Register registerForRegister(CSTestMachineZ80Register reg) {
|
||||
_processor->reset_power_on();
|
||||
_busOperationHandler = new BusOperationHandler(self);
|
||||
_busOperationCaptures = [[NSMutableArray alloc] init];
|
||||
self.portLogic = CSTestMachinePortLogicReturnUpperByte;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@ -132,6 +146,10 @@ static CPU::Z80::Register registerForRegister(CSTestMachineZ80Register reg) {
|
||||
_processor->run_for(Cycles(cycles));
|
||||
}
|
||||
|
||||
- (void)runForInstruction {
|
||||
_processor->run_for_instruction();
|
||||
}
|
||||
|
||||
- (void)setValue:(uint16_t)value forRegister:(CSTestMachineZ80Register)reg {
|
||||
_processor->set_value_of_register(registerForRegister(reg), value);
|
||||
}
|
||||
@ -173,6 +191,16 @@ static CPU::Z80::Register registerForRegister(CSTestMachineZ80Register reg) {
|
||||
_processor->set_wait_line(waitLine ? true : false);
|
||||
}
|
||||
|
||||
- (void)setPortLogic:(CSTestMachinePortLogic)portLogic {
|
||||
_portLogic = portLogic;
|
||||
|
||||
if(_portLogic == CSTestMachinePortLogicReturn191) {
|
||||
_processor->set_port_access_delegate(&_value191PortDelegate);
|
||||
} else {
|
||||
_processor->set_port_access_delegate(&_topBytePortDelegate);
|
||||
}
|
||||
}
|
||||
|
||||
- (CPU::AllRAMProcessor *)processor {
|
||||
return _processor;
|
||||
}
|
||||
|
@ -0,0 +1,144 @@
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
/*
|
||||
Converter for FUSE-style tests.expected that writes JSON out. Hacky, barely tested,
|
||||
not reliable, but seemed to work long enough to produce a JSON object, for which
|
||||
robust parsers are widely available.
|
||||
|
||||
Intended usage: expectedconverter < tests.in > tests.in.json
|
||||
*/
|
||||
|
||||
int main(void) {
|
||||
std::cout << '[';
|
||||
|
||||
bool isFirstObject = true;
|
||||
while(true) {
|
||||
// Name is always present.
|
||||
std::string name;
|
||||
std::cin >> name;
|
||||
|
||||
// Exit if a whole test wasn't found.
|
||||
// SUPER HACK HERE. I can't be bothered working out why
|
||||
if(std::cin.eof() || name == "5505") break;
|
||||
|
||||
// Close previous object, if there was one and output the name.
|
||||
if(!isFirstObject) std::cout << "}," << std::endl;
|
||||
isFirstObject = false;
|
||||
std::cout << "{" << std::endl;
|
||||
std::cout << "\t\"name\" : \"" << name << "\"," << std::endl;
|
||||
|
||||
// There are now arbitrarily many events, and at least one.
|
||||
//
|
||||
// I suspect you're supposed to distinguish the end of events by indentation,
|
||||
// but I'm going to do it by string length. Hack attack!
|
||||
std::cout << "\t\"busActivity\" : [" << std::endl;
|
||||
std::string time;
|
||||
bool isFirstEvent = true;
|
||||
while(true) {
|
||||
std::cin >> time;
|
||||
if(time.size() == 4) break;
|
||||
|
||||
std::string type, address, data;
|
||||
std::cin >> type >> address;
|
||||
|
||||
if(!isFirstEvent) std::cout << "," << std::endl;
|
||||
isFirstEvent = false;
|
||||
|
||||
std::cout << "\t\t{ \"time\" : " << time << ", "; // Arbitrarily, FUSE switches to base 10 for these numbers.
|
||||
std::cout << "\"type\" : \"" << type << "\", ";
|
||||
std::cout << "\"address\" : " << strtol(address.c_str(), nullptr, 16);
|
||||
|
||||
// Memory type can be used to determine whether there's a value at the end.
|
||||
if(type == "MR" || type == "MW" || type == "PR" || type == "PW") {
|
||||
std::string value;
|
||||
std::cin >> value;
|
||||
std::cout << ", \"value\" : " << strtol(value.c_str(), nullptr, 16);
|
||||
}
|
||||
std::cout << " }";
|
||||
}
|
||||
std::cout << std::endl << "\t]," << std::endl;
|
||||
|
||||
// Okay, now for the closing machine state.
|
||||
std::string af, bc, de, hl, afDash, bcDash, deDash, hlDash, ix, iy, sp, pc, memptr;
|
||||
std::string i, r, iff1, iff2, im, halted, tStates;
|
||||
|
||||
af = time;
|
||||
std::cin >> bc >> de >> hl >> afDash >> bcDash >> deDash >> hlDash >> ix >> iy >> sp >> pc >> memptr;
|
||||
std::cin >> i >> r >> iff1 >> iff2 >> im >> halted >> tStates;
|
||||
|
||||
// Output the state
|
||||
std::cout << "\t\"state\" : {" << std::endl;
|
||||
|
||||
#define OUTPUTnbr(name) std::cout << "\t\t\"" << #name << "\" : " << strtol(name.c_str(), nullptr, 16)
|
||||
#define OUTPUT10(name) std::cout << "\t\t\"" << #name << "\" : " << name
|
||||
#define OUTPUT(name) OUTPUTnbr(name) << "," << std::endl
|
||||
#define OUTPUTb(name) std::cout << "\t\t\"" << #name << "\" : " << ((name == "0") ? "false" : "true") << "," << std::endl
|
||||
|
||||
OUTPUT(af);
|
||||
OUTPUT(bc);
|
||||
OUTPUT(de);
|
||||
OUTPUT(hl);
|
||||
OUTPUT(afDash);
|
||||
OUTPUT(bcDash);
|
||||
OUTPUT(deDash);
|
||||
OUTPUT(hlDash);
|
||||
OUTPUT(ix);
|
||||
OUTPUT(iy);
|
||||
OUTPUT(sp);
|
||||
OUTPUT(pc);
|
||||
OUTPUT(memptr);
|
||||
OUTPUT(i);
|
||||
OUTPUT(r);
|
||||
OUTPUTb(iff1);
|
||||
OUTPUTb(iff2);
|
||||
OUTPUT(im);
|
||||
OUTPUTb(halted);
|
||||
OUTPUT10(tStates) << std::endl;
|
||||
|
||||
#undef OUTPUTb
|
||||
#undef OUTPUT
|
||||
#undef OUTPUTnbr
|
||||
std::cout << "\t}";
|
||||
|
||||
|
||||
// A memory list may or may not follow. If it does it'll be terminated
|
||||
// in the usual way. If not, it just won't be there. Hassle!
|
||||
char nextChar;
|
||||
std::cin.rdbuf()->sbumpc();
|
||||
nextChar = std::cin.rdbuf()->sgetc();
|
||||
if(nextChar == '\n') {
|
||||
std::cout << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse and transcode the memory list.
|
||||
// There's only ever one memory block.
|
||||
std::cout << "," << std::endl;
|
||||
std::cout << "\t\"memory\" : [" << std::endl;
|
||||
|
||||
std::string address;
|
||||
std::cin >> address;
|
||||
|
||||
std::cout << "\t\t{ \"address\" : " << strtol(address.c_str(), nullptr, 16) << ", \"data\" : [";
|
||||
bool isFirstValue = true;
|
||||
while(true) {
|
||||
std::string value;
|
||||
std::cin >> value;
|
||||
if(value == "-1") break;
|
||||
|
||||
if(!isFirstValue) std::cout << ", ";
|
||||
isFirstValue = false;
|
||||
|
||||
std::cout << strtol(value.c_str(), nullptr, 16);
|
||||
}
|
||||
std::cout << "] }";
|
||||
|
||||
// Close the object.
|
||||
std::cout << std::endl << "\t]" << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "}]" << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
103
OSBindings/Mac/Clock SignalTests/FUSE/converters/inconverter.cpp
Normal file
103
OSBindings/Mac/Clock SignalTests/FUSE/converters/inconverter.cpp
Normal file
@ -0,0 +1,103 @@
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
/*
|
||||
Converter for FUSE-style tests.in that writes JSON out. Hacky, barely tested, not reliable,
|
||||
but seemed to work long enough to produce a JSON object, for which robust parsers are
|
||||
widely available.
|
||||
|
||||
Intended usage: inconverter < tests.in > tests.in.json
|
||||
*/
|
||||
|
||||
int main(void) {
|
||||
std::cout << '[';
|
||||
|
||||
bool isFirstObject = true;
|
||||
while(true) {
|
||||
std::string name;
|
||||
std::string af, bc, de, hl, afDash, bcDash, deDash, hlDash, ix, iy, sp, pc, memptr;
|
||||
std::string i, r, iff1, iff2, im, halted, tStates;
|
||||
|
||||
// Read the fixed part of this test.
|
||||
std::cin >> name;
|
||||
std::cin >> af >> bc >> de >> hl >> afDash >> bcDash >> deDash >> hlDash >> ix >> iy >> sp >> pc >> memptr;
|
||||
std::cin >> i >> r >> iff1 >> iff2 >> im >> halted >> tStates;
|
||||
|
||||
// Exit if a whole test wasn't found.
|
||||
if(std::cin.eof()) break;
|
||||
|
||||
if(!isFirstObject) std::cout << "}," << std::endl;
|
||||
isFirstObject = false;
|
||||
|
||||
// Output that much.
|
||||
std::cout << "{" << std::endl;
|
||||
std::cout << "\t\"name\" : \"" << name << "\"," << std::endl;
|
||||
std::cout << "\t\"state\" : {" << std::endl;
|
||||
|
||||
#define OUTPUTnbr(name) std::cout << "\t\t\"" << #name << "\" : " << strtol(name.c_str(), nullptr, 16)
|
||||
#define OUTPUT10(name) std::cout << "\t\t\"" << #name << "\" : " << name
|
||||
#define OUTPUT(name) OUTPUTnbr(name) << "," << std::endl
|
||||
#define OUTPUTb(name) std::cout << "\t\t\"" << #name << "\" : " << ((name == "0") ? "false" : "true") << "," << std::endl
|
||||
|
||||
OUTPUT(af);
|
||||
OUTPUT(bc);
|
||||
OUTPUT(de);
|
||||
OUTPUT(hl);
|
||||
OUTPUT(afDash);
|
||||
OUTPUT(bcDash);
|
||||
OUTPUT(deDash);
|
||||
OUTPUT(hlDash);
|
||||
OUTPUT(ix);
|
||||
OUTPUT(iy);
|
||||
OUTPUT(sp);
|
||||
OUTPUT(pc);
|
||||
OUTPUT(memptr);
|
||||
OUTPUT(i);
|
||||
OUTPUT(r);
|
||||
OUTPUTb(iff1);
|
||||
OUTPUTb(iff2);
|
||||
OUTPUT(im);
|
||||
OUTPUTb(halted);
|
||||
OUTPUT10(tStates) << std::endl;
|
||||
|
||||
#undef OUTPUTb
|
||||
#undef OUTPUT
|
||||
#undef OUTPUTnbr
|
||||
|
||||
std::cout << "\t}," << std::endl;
|
||||
|
||||
// Parse and transcode the memory list.
|
||||
std::cout << "\t\"memory\" : [" << std::endl;
|
||||
|
||||
bool isFirstBlock = true;
|
||||
while(true) {
|
||||
std::string address;
|
||||
std::cin >> address;
|
||||
if(address == "-1") break;
|
||||
|
||||
if(!isFirstBlock) std::cout << "," << std::endl;
|
||||
isFirstBlock = false;
|
||||
|
||||
std::cout << "\t\t{ \"address\" : " << strtol(address.c_str(), nullptr, 16) << ", \"data\" : [";
|
||||
bool isFirstValue = true;
|
||||
while(true) {
|
||||
std::string value;
|
||||
std::cin >> value;
|
||||
if(value == "-1") break;
|
||||
|
||||
if(!isFirstValue) std::cout << ", ";
|
||||
isFirstValue = false;
|
||||
|
||||
std::cout << strtol(value.c_str(), nullptr, 16);
|
||||
}
|
||||
std::cout << "] }";
|
||||
}
|
||||
|
||||
// Close the object.
|
||||
std::cout << std::endl << "\t]" << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "}]" << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
tests.expected and tests.in are sourced from FUSE, the For UNIX Spectrum Emulator. FUSE is GPL software, and can be found at:
|
||||
|
||||
https://github.com/tom-seddon/fuse-emulator-code/
|
||||
https://sourceforge.net/projects/fuse-emulator/
|
||||
|
||||
tests.exepected.json and tests.in.json are direct derivatives of those files and therefore are also offered under the GPL.
|
||||
tests.exepected.json and tests.in.json are direct derivatives of those files and therefore are also offered under the GPL. See the converters directory for the hacky means I used to generate those from the originals.
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -28,6 +28,7 @@ fileprivate struct RegisterState {
|
||||
let i: UInt8, r: UInt8
|
||||
let iff1: Bool, iff2: Bool, interruptMode: Int
|
||||
let isHalted: Bool
|
||||
let memptr: UInt16
|
||||
let tStates: Int
|
||||
|
||||
func set(onMachine machine: CSTestMachineZ80) {
|
||||
@ -48,6 +49,7 @@ fileprivate struct RegisterState {
|
||||
machine.setValue(iff1 ? 1 : 0, for: .IFF1)
|
||||
machine.setValue(iff2 ? 1 : 0, for: .IFF2)
|
||||
machine.setValue(UInt16(interruptMode), for: .IM)
|
||||
machine.setValue(memptr, for: .memPtr)
|
||||
// TODO: isHalted
|
||||
}
|
||||
|
||||
@ -83,6 +85,7 @@ fileprivate struct RegisterState {
|
||||
|
||||
interruptMode = (dictionary["im"] as! NSNumber).intValue
|
||||
isHalted = (dictionary["halted"] as! NSNumber).boolValue
|
||||
memptr = UInt16(truncating: dictionary["memptr"] as! NSNumber)
|
||||
|
||||
tStates = (dictionary["tStates"] as! NSNumber).intValue
|
||||
}
|
||||
@ -115,6 +118,7 @@ fileprivate struct RegisterState {
|
||||
|
||||
isHalted = machine.isHalted
|
||||
tStates = 0 // TODO (?)
|
||||
memptr = machine.value(for: .memPtr)
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,7 +142,8 @@ fileprivate func ==(lhs: RegisterState, rhs: RegisterState) -> Bool {
|
||||
lhs.iff1 == rhs.iff1 &&
|
||||
lhs.iff2 == rhs.iff2 &&
|
||||
lhs.interruptMode == rhs.interruptMode &&
|
||||
lhs.isHalted == rhs.isHalted
|
||||
lhs.isHalted == rhs.isHalted &&
|
||||
lhs.memptr == rhs.memptr
|
||||
}
|
||||
|
||||
class FUSETests: XCTestCase {
|
||||
@ -167,7 +172,14 @@ class FUSETests: XCTestCase {
|
||||
|
||||
let name = itemDictionary["name"] as! String
|
||||
|
||||
// if name != "02" {
|
||||
// Provisionally skip the FUSE HALT test. It tests PC during a HALT; this emulator advances
|
||||
// it only upon interrupt, FUSE seems to increment it and then stay still. I need to find
|
||||
// out which of those is correct.
|
||||
if name == "76" {
|
||||
continue
|
||||
}
|
||||
|
||||
// if name != "10" {
|
||||
// continue;
|
||||
// }
|
||||
// print("\(name)")
|
||||
@ -176,6 +188,7 @@ class FUSETests: XCTestCase {
|
||||
let targetState = RegisterState(dictionary: outputDictionary["state"] as! [String: Any])
|
||||
|
||||
let machine = CSTestMachineZ80()
|
||||
machine.portLogic = .returnUpperByte
|
||||
machine.captureBusActivity = true
|
||||
initialState.set(onMachine: machine)
|
||||
|
||||
|
@ -0,0 +1,3 @@
|
||||
1.0 (7.12.2012)
|
||||
|
||||
+ First release.
|
@ -0,0 +1,19 @@
|
||||
Copyright (c) 2012 Patrik Rak (patrik@raxoft.cz)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
@ -0,0 +1,28 @@
|
||||
Welcome to the Zilog Z80 CPU test suite.
|
||||
|
||||
This set of programs is intended to help the emulator authors to reach the
|
||||
desired level of the CPU emulation authenticity. Each of the included programs
|
||||
performs an exhaustive computation using each of the tested Z80 instructions,
|
||||
compares the results with values obtained from a real 48K Spectrum with Zilog Z80 CPU,
|
||||
and reports any deviations detected.
|
||||
|
||||
The following variants are available:
|
||||
|
||||
- z80full - tests all flags and registers.
|
||||
- z80doc - tests all registers, but only officially documented flags.
|
||||
- z80flags - tests all flags, ignores registers.
|
||||
- z80docflags - tests documented flags only, ignores registers.
|
||||
- z80ccf - tests all flags after executing CCF after each instruction tested.
|
||||
- z80memptr - tests all flags after executing BIT N,(HL) after each instruction tested.
|
||||
|
||||
The first four are the standard tests for CPU emulation. The CCF variant is
|
||||
used to thoroughly test the authentic SCF/CCF behavior after each Z80
|
||||
instruction. Finally the MEMPTR variant can be used to discover problems in
|
||||
the MEMPTR emulation - however note that the current set of test was not
|
||||
specifically designed to stress test MEMPTR, so many of the possible
|
||||
problems are very likely left undetected. I may eventually add specific
|
||||
MEMPTR tests in later releases.
|
||||
|
||||
Enjoy!
|
||||
|
||||
Patrik Rak
|
@ -0,0 +1,34 @@
|
||||
# gmake
|
||||
|
||||
NAME = z80test
|
||||
VERSION = 1.0
|
||||
PKG := $(NAME)-$(VERSION)
|
||||
|
||||
PROGS := z80full z80flags z80doc z80docflags z80ccf z80memptr
|
||||
SRCS := main idea crctab tests testmacros print
|
||||
|
||||
all: $(addsuffix .tap,$(PROGS))
|
||||
|
||||
.DELETE_ON_ERROR: %.out
|
||||
|
||||
%.out : %.asm $(addsuffix .asm,$(SRCS))
|
||||
sjasm $<
|
||||
|
||||
%.tap : loader.bas %.out
|
||||
mktap -b $(basename $(word 2,$^)) 10 <$(word 1,$^) >$@
|
||||
mktap $(basename $(word 2,$^)) 32768 <$(word 2,$^) >>$@
|
||||
|
||||
FILES := Makefile loader.bas $(addsuffix .asm,$(PROGS)) $(addsuffix .asm, $(SRCS))
|
||||
|
||||
dist: all
|
||||
ln -s .. $(PKG)
|
||||
cp *.tap $(PKG)
|
||||
zip ../$(PKG).zip $(addprefix $(PKG)/src/, $(FILES)) $(PKG)/*.txt $(PKG)/*.tap
|
||||
rm $(PKG)/*.tap
|
||||
rm $(PKG)
|
||||
|
||||
clean:
|
||||
rm -rf *.out *.lst *.tap
|
||||
|
||||
tidy: clean
|
||||
rm -rf $(PROGS)
|
1037
OSBindings/Mac/Clock SignalTests/Patrik Rak Z80 Tests/src/crctab.asm
Normal file
1037
OSBindings/Mac/Clock SignalTests/Patrik Rak Z80 Tests/src/crctab.asm
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,291 @@
|
||||
; The Z80 tester.
|
||||
;
|
||||
; Copyright (C) 2012 Patrik Rak (patrik@raxoft.cz)
|
||||
;
|
||||
; This source code is released under the MIT license, see included license.txt.
|
||||
|
||||
opsize equ 4+postccf ; Size of the tested instruction sequence.
|
||||
datasize equ 16 ; Size of the tested registers and data.
|
||||
vecsize equ opsize+datasize ; Size of entire test vector.
|
||||
|
||||
test: ld (.spptr+1),sp
|
||||
|
||||
if maskflags ; Keep mask for official flags.
|
||||
ld a,(hl)
|
||||
ld (.flagptr+1),a
|
||||
endif
|
||||
|
||||
inc hl
|
||||
|
||||
ld de,vector ; Init the test vector, counter and shifter.
|
||||
ld bc,vecsize
|
||||
call .copy
|
||||
|
||||
add hl,bc
|
||||
|
||||
call .copy
|
||||
|
||||
call .copy
|
||||
|
||||
add hl,bc
|
||||
|
||||
ld (.valptr+1),de
|
||||
|
||||
inc de
|
||||
|
||||
call .clear
|
||||
|
||||
ld (.maskptr+1),de
|
||||
|
||||
xor a
|
||||
ld (de),a
|
||||
inc de
|
||||
|
||||
call .copy
|
||||
|
||||
ld a,0x07 ; Make sure we get 0
|
||||
out (0xfe),a ; on MIC bit when doing IN.
|
||||
|
||||
ld a,0xa9 ; Set I,R,AF' to known values.
|
||||
ld i,a
|
||||
ld r,a
|
||||
or a
|
||||
ex af,af
|
||||
|
||||
ld bc,65535 ; Init CRC.
|
||||
ld d,b
|
||||
ld e,c
|
||||
exx
|
||||
|
||||
ld sp,data.regs
|
||||
|
||||
; Test vector sequence combinator.
|
||||
|
||||
.loop ld hl,counter
|
||||
ld de,shifter+1
|
||||
ld bc,vector
|
||||
|
||||
macro combine base,count,offset:0,last:1
|
||||
repeat count
|
||||
ld a,(bc)
|
||||
xor (hl)
|
||||
ex de,hl
|
||||
xor (hl)
|
||||
ld (base+offset+@#),a
|
||||
if ( @# < count-1 ) | ! last
|
||||
inc c
|
||||
inc e
|
||||
inc l
|
||||
endif
|
||||
endrepeat
|
||||
endm
|
||||
|
||||
ld a,(bc)
|
||||
xor (hl)
|
||||
ex de,hl
|
||||
xor (hl)
|
||||
cp 0x76 ; Skip halt.
|
||||
jp z,.next
|
||||
ld (.opcode),a
|
||||
inc c
|
||||
inc e
|
||||
inc l
|
||||
|
||||
ld a,(bc)
|
||||
xor (hl)
|
||||
ex de,hl
|
||||
xor (hl)
|
||||
ld (.opcode+1),a
|
||||
cp 0x76 ; Skip halt...
|
||||
jp nz,.ok
|
||||
ld a,(.opcode)
|
||||
and 0xdf ; ... with IX/IY prefix.
|
||||
cp 0xdd
|
||||
jp z,.next
|
||||
.ok inc c
|
||||
inc e
|
||||
inc l
|
||||
|
||||
combine .opcode,opsize-2,2,0
|
||||
combine data,datasize
|
||||
|
||||
; The test itself.
|
||||
|
||||
pop af
|
||||
pop bc
|
||||
pop de
|
||||
pop hl
|
||||
pop ix
|
||||
pop iy
|
||||
ld sp,(data.sp)
|
||||
|
||||
.opcode ds opsize
|
||||
.continue
|
||||
if memptr
|
||||
ld hl,data
|
||||
bit 0,(hl)
|
||||
endif
|
||||
|
||||
ld (data.sp),sp
|
||||
ld sp,data.regstop
|
||||
push iy
|
||||
push ix
|
||||
push hl
|
||||
push de
|
||||
push bc
|
||||
push af
|
||||
|
||||
ld hl,data
|
||||
|
||||
if maskflags
|
||||
ld a,(hl)
|
||||
.flagptr and 0xff
|
||||
|
||||
if ! onlyflags
|
||||
ld (hl),a
|
||||
endif
|
||||
|
||||
endif
|
||||
|
||||
; CRC update.
|
||||
|
||||
if ! onlyflags
|
||||
ld b,datasize
|
||||
endif
|
||||
|
||||
if ! ( onlyflags & maskflags )
|
||||
.crcloop ld a,(hl)
|
||||
endif
|
||||
|
||||
exx
|
||||
xor e
|
||||
|
||||
ld l,a
|
||||
ld h,crctable/256
|
||||
|
||||
ld a,(hl)
|
||||
xor d
|
||||
ld e,a
|
||||
inc h
|
||||
|
||||
ld a,(hl)
|
||||
xor c
|
||||
ld d,a
|
||||
inc h
|
||||
|
||||
ld a,(hl)
|
||||
xor b
|
||||
ld c,a
|
||||
inc h
|
||||
|
||||
ld b,(hl)
|
||||
|
||||
exx
|
||||
|
||||
if ! onlyflags
|
||||
inc hl
|
||||
djnz .crcloop
|
||||
endif
|
||||
|
||||
; Multibyte counter with arbitrary bit mask.
|
||||
|
||||
.next ld hl,countmask
|
||||
ld de,counter
|
||||
ld b,vecsize
|
||||
.countloop ld a,(de)
|
||||
or a
|
||||
jr z,.countnext
|
||||
dec a
|
||||
and (hl)
|
||||
ld (de),a
|
||||
jp .loop
|
||||
.countnext ld a,(hl)
|
||||
ld (de),a
|
||||
inc l
|
||||
inc e
|
||||
djnz .countloop
|
||||
|
||||
; Multibyte shifter with arbitrary bit mask.
|
||||
|
||||
.maskptr ld hl,shiftmask
|
||||
.valptr ld de,shifter
|
||||
ld a,(de)
|
||||
add a,a
|
||||
neg
|
||||
add (hl)
|
||||
xor (hl)
|
||||
and (hl)
|
||||
ld (de),a
|
||||
jp nz,.loop
|
||||
.shiftloop inc l
|
||||
inc e
|
||||
ld a,e
|
||||
cp shiftend % 256
|
||||
jr z,.exit
|
||||
ld a,(hl)
|
||||
dec a
|
||||
xor (hl)
|
||||
and (hl)
|
||||
jr z,.shiftloop
|
||||
ld (de),a
|
||||
ld (.maskptr+1),hl
|
||||
ld (.valptr+1),de
|
||||
jp .loop
|
||||
|
||||
.exit exx
|
||||
.spptr ld sp,0
|
||||
ret
|
||||
|
||||
; Misc helper routines.
|
||||
|
||||
.copy push hl
|
||||
push bc
|
||||
ldir
|
||||
pop bc
|
||||
pop hl
|
||||
ret
|
||||
|
||||
.clear push hl
|
||||
push bc
|
||||
ld h,d
|
||||
ld l,e
|
||||
ld (hl),0
|
||||
inc de
|
||||
dec bc
|
||||
ldir
|
||||
pop bc
|
||||
pop hl
|
||||
ret
|
||||
|
||||
align 256
|
||||
|
||||
include crctab.asm
|
||||
|
||||
; If this moves from 0x8800, all tests which use this address
|
||||
; will need to have their CRCs updated, so don't move it.
|
||||
|
||||
align 256
|
||||
data
|
||||
.regs ds datasize-4
|
||||
.regstop
|
||||
.mem ds 2
|
||||
.sp ds 2
|
||||
|
||||
.jump
|
||||
if postccf
|
||||
ccf
|
||||
else
|
||||
inc bc
|
||||
endif
|
||||
jp test.continue
|
||||
|
||||
; This entire workspace must be kept within single 256 byte page.
|
||||
|
||||
vector ds vecsize
|
||||
counter ds vecsize
|
||||
countmask ds vecsize
|
||||
shifter ds 1+vecsize
|
||||
shiftend
|
||||
shiftmask ds 1+vecsize
|
||||
|
||||
; EOF ;
|
@ -0,0 +1,2 @@
|
||||
10 CLEAR 32767:LOAD "" CODE:CLS
|
||||
20 RANDOMIZE USR 32768
|
@ -0,0 +1,149 @@
|
||||
; Main driver for the Z80 tester.
|
||||
;
|
||||
; Copyright (C) 2012 Patrik Rak (patrik@raxoft.cz)
|
||||
;
|
||||
; This source code is released under the MIT license, see included license.txt.
|
||||
|
||||
org 0x8000
|
||||
|
||||
main: di
|
||||
push iy
|
||||
exx
|
||||
push hl
|
||||
|
||||
call printinit
|
||||
|
||||
call print
|
||||
db "Z80 "
|
||||
testname
|
||||
db " test"
|
||||
db 23,32-13,1,127," 2012 RAXOFT",13,13,0
|
||||
|
||||
ld bc,0
|
||||
ld hl,testtable
|
||||
jr .entry
|
||||
|
||||
.loop push hl
|
||||
push bc
|
||||
call .test
|
||||
pop bc
|
||||
pop hl
|
||||
|
||||
add a,b
|
||||
ld b,a
|
||||
|
||||
inc c
|
||||
|
||||
.entry ld e,(hl)
|
||||
inc hl
|
||||
ld d,(hl)
|
||||
inc hl
|
||||
|
||||
ld a,d
|
||||
or e
|
||||
jr nz,.loop
|
||||
|
||||
call print
|
||||
db 13,"Result: ",0
|
||||
|
||||
ld a,b
|
||||
or a
|
||||
jr z,.ok
|
||||
|
||||
call printdeca
|
||||
|
||||
call print
|
||||
db " of ",0
|
||||
|
||||
ld a,c
|
||||
call printdeca
|
||||
|
||||
call print
|
||||
db " tests failed.",13,0
|
||||
jr .done
|
||||
|
||||
.ok call print
|
||||
db "all tests passed.",13,0
|
||||
|
||||
.done pop hl
|
||||
exx
|
||||
pop iy
|
||||
ei
|
||||
ret
|
||||
|
||||
.test ld hl,1+3*vecsize
|
||||
add hl,de
|
||||
push hl
|
||||
|
||||
ld a,c
|
||||
call printdeca
|
||||
|
||||
ld a,' '
|
||||
call printchr
|
||||
|
||||
ld hl,1+3*vecsize+4
|
||||
add hl,de
|
||||
|
||||
call printhl
|
||||
|
||||
ex de,hl
|
||||
|
||||
call test
|
||||
|
||||
ld hl,data+3
|
||||
|
||||
ld (hl),e
|
||||
dec hl
|
||||
ld (hl),d
|
||||
dec hl
|
||||
ld (hl),c
|
||||
dec hl
|
||||
ld (hl),b
|
||||
|
||||
pop de
|
||||
|
||||
ld b,4
|
||||
call .cmp
|
||||
|
||||
jr nz,.mismatch
|
||||
|
||||
call print
|
||||
db 23,32-2,1,"OK",13,0
|
||||
|
||||
ret
|
||||
|
||||
.mismatch call print
|
||||
db 23,32-6,1,"FAILED",13
|
||||
db "CRC:",0
|
||||
|
||||
call printcrc
|
||||
|
||||
call print
|
||||
db " Expected:",0
|
||||
|
||||
ex de,hl
|
||||
call printcrc
|
||||
|
||||
ld a,13
|
||||
call printchr
|
||||
|
||||
ld a,1
|
||||
ret
|
||||
|
||||
.cmp push hl
|
||||
push de
|
||||
.cmploop ld a,(de)
|
||||
xor (hl)
|
||||
jr nz,.exit
|
||||
inc de
|
||||
inc hl
|
||||
djnz .cmploop
|
||||
.exit pop de
|
||||
pop hl
|
||||
ret
|
||||
|
||||
include print.asm
|
||||
include idea.asm
|
||||
include tests.asm
|
||||
|
||||
; EOF ;
|
@ -0,0 +1,82 @@
|
||||
; Simple printing module.
|
||||
;
|
||||
; Copyright (C) 2012 Patrik Rak (patrik@raxoft.cz)
|
||||
;
|
||||
; This source code is released under the MIT license, see included license.txt.
|
||||
|
||||
|
||||
printinit: ld a,2
|
||||
jp 0x1601 ; CHAN-OPEN
|
||||
|
||||
|
||||
print: ex (sp),hl
|
||||
call printhl
|
||||
ex (sp),hl
|
||||
ret
|
||||
|
||||
printhl:
|
||||
.loop ld a,(hl)
|
||||
inc hl
|
||||
or a
|
||||
ret z
|
||||
call printchr
|
||||
jr .loop
|
||||
|
||||
|
||||
printdeca: ld h,a
|
||||
ld b,-100
|
||||
call .digit
|
||||
ld b,-10
|
||||
call .digit
|
||||
ld b,-1
|
||||
|
||||
.digit ld a,h
|
||||
ld l,'0'-1
|
||||
.loop inc l
|
||||
add a,b
|
||||
jr c,.loop
|
||||
sub b
|
||||
ld h,a
|
||||
ld a,l
|
||||
jr printchr
|
||||
|
||||
|
||||
printcrc: ld b,4
|
||||
|
||||
printhexs:
|
||||
.loop ld a,(hl)
|
||||
inc hl
|
||||
call printhexa
|
||||
djnz .loop
|
||||
ret
|
||||
|
||||
|
||||
printhexa: push af
|
||||
rrca
|
||||
rrca
|
||||
rrca
|
||||
rrca
|
||||
call .nibble
|
||||
pop af
|
||||
|
||||
.nibble or 0xf0
|
||||
daa
|
||||
add a,0xa0
|
||||
adc a,0x40
|
||||
|
||||
printchr: push iy
|
||||
ld iy,0x5c3a ; ERR-NR
|
||||
push de
|
||||
push bc
|
||||
exx
|
||||
ei
|
||||
; out (0xff),a
|
||||
rst 0x10
|
||||
di
|
||||
exx
|
||||
pop bc
|
||||
pop de
|
||||
pop iy
|
||||
ret
|
||||
|
||||
; EOF ;
|
@ -0,0 +1,103 @@
|
||||
; Macros for defining the test vectors.
|
||||
;
|
||||
; Copyright (C) 2012 Patrik Rak (patrik@raxoft.cz)
|
||||
;
|
||||
; This source code is released under the MIT license, see included license.txt.
|
||||
|
||||
macro db8 b7,b6,b5,b4,b3,b2,b1,b0
|
||||
db (b7<<7)|(b6<<6)|(b5<<5)|(b4<<4)|(b3<<3)|(b2<<2)|(b1<<1)|b0
|
||||
endm
|
||||
|
||||
macro ddbe n
|
||||
db (n>>24)&0xff
|
||||
db (n>>16)&0xff
|
||||
db (n>>8)&0xff
|
||||
db n&0xff
|
||||
endm
|
||||
|
||||
macro inst op1,op2,op3,op4,tail
|
||||
; Unfortunately, elseifidn doesn't seem to work properly.
|
||||
ifidn op4,stop
|
||||
db op1,op2,op3,tail,0
|
||||
else
|
||||
ifidn op3,stop
|
||||
db op1,op2,tail,op4,0
|
||||
else
|
||||
ifidn op2,stop
|
||||
db op1,tail,op3,op4,0
|
||||
else
|
||||
db op1,op2,op3,op4,tail
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endm
|
||||
|
||||
macro flags sn,s,zn,z,f5n,f5,hcn,hc,f3n,f3,pvn,pv,nn,n,cn,c
|
||||
if maskflags
|
||||
db8 s,z,f5,hc,f3,pv,n,c
|
||||
else
|
||||
db 0xff
|
||||
endif
|
||||
endm
|
||||
|
||||
.veccount := 0
|
||||
|
||||
macro vec op1,op2,op3,op4,memn,mem,an,a,fn,f,bcn,bc,den,de,hln,hl,ixn,ix,iyn,iy,spn,sp
|
||||
|
||||
if postccf
|
||||
|
||||
if ( .@veccount % 3 ) == 0
|
||||
inst op1,op2,op3,op4,tail
|
||||
.@areg := 0
|
||||
else
|
||||
db op1,op2,op3,op4,0
|
||||
.@areg := .@areg | a
|
||||
endif
|
||||
|
||||
else
|
||||
db op1,op2,op3,op4
|
||||
endif
|
||||
|
||||
db f
|
||||
|
||||
if postccf & ( ( .veccount % 3 ) == 2 )
|
||||
db a | ( ( ~ .@areg ) & 0x28 )
|
||||
else
|
||||
db a
|
||||
endif
|
||||
|
||||
dw bc,de,hl,ix,iy
|
||||
dw mem
|
||||
dw sp
|
||||
|
||||
.@veccount := .@veccount+1
|
||||
|
||||
endm
|
||||
|
||||
macro crcs allflagsn,allflags,alln,all,docflagsn,docflags,docn,doc,ccfn,ccf,mptrn,mptr
|
||||
if postccf
|
||||
ddbe ccf
|
||||
elseif memptr
|
||||
ddbe mptr
|
||||
else
|
||||
if maskflags
|
||||
if onlyflags
|
||||
ddbe docflags
|
||||
else
|
||||
ddbe doc
|
||||
endif
|
||||
else
|
||||
if onlyflags
|
||||
ddbe allflags
|
||||
else
|
||||
ddbe all
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endm
|
||||
|
||||
macro name n
|
||||
dz n
|
||||
endm
|
||||
|
||||
; EOF ;
|
1379
OSBindings/Mac/Clock SignalTests/Patrik Rak Z80 Tests/src/tests.asm
Normal file
1379
OSBindings/Mac/Clock SignalTests/Patrik Rak Z80 Tests/src/tests.asm
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,18 @@
|
||||
; Z80 test - post CCF version.
|
||||
;
|
||||
; Copyright (C) 2012 Patrik Rak (patrik@raxoft.cz)
|
||||
;
|
||||
; This source code is released under the MIT license, see included license.txt.
|
||||
|
||||
macro testname
|
||||
db "CCF"
|
||||
endm
|
||||
|
||||
maskflags equ 0
|
||||
onlyflags equ 1
|
||||
postccf equ 1
|
||||
memptr equ 0
|
||||
|
||||
include main.asm
|
||||
|
||||
; EOF ;
|
@ -0,0 +1,18 @@
|
||||
; Z80 test - officially documented flags version.
|
||||
;
|
||||
; Copyright (C) 2012 Patrik Rak (patrik@raxoft.cz)
|
||||
;
|
||||
; This source code is released under the MIT license, see included license.txt.
|
||||
|
||||
macro testname
|
||||
db "doc"
|
||||
endm
|
||||
|
||||
maskflags equ 1
|
||||
onlyflags equ 0
|
||||
postccf equ 0
|
||||
memptr equ 0
|
||||
|
||||
include main.asm
|
||||
|
||||
; EOF ;
|
@ -0,0 +1,18 @@
|
||||
; Z80 test - officially documented flags, flags only version.
|
||||
;
|
||||
; Copyright (C) 2012 Patrik Rak (patrik@raxoft.cz)
|
||||
;
|
||||
; This source code is released under the MIT license, see included license.txt.
|
||||
|
||||
macro testname
|
||||
db "doc flags"
|
||||
endm
|
||||
|
||||
maskflags equ 1
|
||||
onlyflags equ 1
|
||||
postccf equ 0
|
||||
memptr equ 0
|
||||
|
||||
include main.asm
|
||||
|
||||
; EOF ;
|
@ -0,0 +1,18 @@
|
||||
; Z80 test - flags only version.
|
||||
;
|
||||
; Copyright (C) 2012 Patrik Rak (patrik@raxoft.cz)
|
||||
;
|
||||
; This source code is released under the MIT license, see included license.txt.
|
||||
|
||||
macro testname
|
||||
db "flags"
|
||||
endm
|
||||
|
||||
maskflags equ 0
|
||||
onlyflags equ 1
|
||||
postccf equ 0
|
||||
memptr equ 0
|
||||
|
||||
include main.asm
|
||||
|
||||
; EOF ;
|
@ -0,0 +1,18 @@
|
||||
; Z80 test - the full version.
|
||||
;
|
||||
; Copyright (C) 2012 Patrik Rak (patrik@raxoft.cz)
|
||||
;
|
||||
; This source code is released under the MIT license, see included license.txt.
|
||||
|
||||
macro testname
|
||||
db "full"
|
||||
endm
|
||||
|
||||
maskflags equ 0
|
||||
onlyflags equ 0
|
||||
postccf equ 0
|
||||
memptr equ 0
|
||||
|
||||
include main.asm
|
||||
|
||||
; EOF ;
|
@ -0,0 +1,21 @@
|
||||
; Z80 test - MEMPTR version.
|
||||
;
|
||||
; However note that the current set of tests was not designed to stress test MEMPTR
|
||||
; particularly, so it doesn't detect much - I may eventually add such specific tests later.
|
||||
;
|
||||
; Copyright (C) 2012 Patrik Rak (patrik@raxoft.cz)
|
||||
;
|
||||
; This source code is released under the MIT license, see included license.txt.
|
||||
|
||||
macro testname
|
||||
db "MEMPTR"
|
||||
endm
|
||||
|
||||
maskflags equ 0
|
||||
onlyflags equ 1
|
||||
postccf equ 0
|
||||
memptr equ 1
|
||||
|
||||
include main.asm
|
||||
|
||||
; EOF ;
|
BIN
OSBindings/Mac/Clock SignalTests/Patrik Rak Z80 Tests/z80ccf.tap
Normal file
BIN
OSBindings/Mac/Clock SignalTests/Patrik Rak Z80 Tests/z80ccf.tap
Normal file
Binary file not shown.
BIN
OSBindings/Mac/Clock SignalTests/Patrik Rak Z80 Tests/z80doc.tap
Normal file
BIN
OSBindings/Mac/Clock SignalTests/Patrik Rak Z80 Tests/z80doc.tap
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
127
OSBindings/Mac/Clock SignalTests/PatrikRakTests.swift
Normal file
127
OSBindings/Mac/Clock SignalTests/PatrikRakTests.swift
Normal file
@ -0,0 +1,127 @@
|
||||
//
|
||||
// PatrikRakTests.swift
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 22/02/2020.
|
||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import Foundation
|
||||
|
||||
class PatrikRakTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
|
||||
fileprivate var done = false
|
||||
fileprivate var output = ""
|
||||
|
||||
private func runTest(_ name: String) {
|
||||
if let filename = Bundle(for: type(of: self)).path(forResource: name, ofType: "tap") {
|
||||
if let testData = try? Data(contentsOf: URL(fileURLWithPath: filename)) {
|
||||
|
||||
// Do a minor parsing of the TAP file to find the final file.
|
||||
var dataPointer = 0
|
||||
var finalBlock = 0
|
||||
while dataPointer < testData.count {
|
||||
let blockSize = Int(testData[dataPointer]) + Int(testData[dataPointer+1]) << 8
|
||||
finalBlock = dataPointer + 2
|
||||
dataPointer += 2 + blockSize
|
||||
}
|
||||
|
||||
assert(dataPointer == testData.count)
|
||||
|
||||
// Create a machine.
|
||||
let machine = CSTestMachineZ80()
|
||||
machine.portLogic = .return191
|
||||
|
||||
// Copy everything from finalBlock+1 to the end of the file to $8000.
|
||||
let fileContents = testData.subdata(in: finalBlock+1 ..< testData.count)
|
||||
machine.setData(fileContents, atAddress: 0x8000)
|
||||
|
||||
// Add a RET and a trap at 10h, this is the Spectrum's system call for outputting text.
|
||||
machine.setValue(0xc9, atAddress: 0x0010)
|
||||
machine.addTrapAddress(0x0010);
|
||||
machine.trapHandler = self
|
||||
|
||||
// Also add a RET at $1601, which is where the Spectrum puts 'channel open'.
|
||||
machine.setValue(0xc9, atAddress: 0x1601)
|
||||
|
||||
// Add a call to $8000 and then an infinite loop; these tests load at $8000 and RET when done.
|
||||
machine.setValue(0xcd, atAddress: 0x7000)
|
||||
machine.setValue(0x00, atAddress: 0x7001)
|
||||
machine.setValue(0x80, atAddress: 0x7002)
|
||||
machine.setValue(0xc3, atAddress: 0x7003)
|
||||
machine.setValue(0x03, atAddress: 0x7004)
|
||||
machine.setValue(0x70, atAddress: 0x7005)
|
||||
machine.addTrapAddress(0x7003);
|
||||
|
||||
// seed execution at 0x7000
|
||||
machine.setValue(0x7000, for: .programCounter)
|
||||
|
||||
// run!
|
||||
let cyclesPerIteration: Int32 = 400_000_000
|
||||
while !done {
|
||||
machine.runForNumber(ofCycles: cyclesPerIteration)
|
||||
}
|
||||
|
||||
let successRange = output.range(of: "Result: all tests passed.")
|
||||
XCTAssertNotEqual(successRange, nil)
|
||||
|
||||
if successRange == nil {
|
||||
print("Output was: \(output)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testCCF() {
|
||||
runTest("z80ccf")
|
||||
}
|
||||
|
||||
func testDoc() {
|
||||
runTest("z80doc")
|
||||
}
|
||||
|
||||
func testDocFlags() {
|
||||
runTest("z80docflags")
|
||||
}
|
||||
|
||||
func testFlags() {
|
||||
runTest("z80flags")
|
||||
}
|
||||
|
||||
func testFull() {
|
||||
runTest("z80full")
|
||||
}
|
||||
|
||||
func testMemptr() {
|
||||
runTest("z80memptr")
|
||||
}
|
||||
|
||||
func testMachine(_ testMachine: CSTestMachine, didTrapAtAddress address: UInt16) {
|
||||
let testMachineZ80 = testMachine as! CSTestMachineZ80
|
||||
switch address {
|
||||
case 0x0010:
|
||||
var characterCode = testMachineZ80.value(for: .A)
|
||||
|
||||
// Of the control codes, retain only new line. Map the rest to space.
|
||||
if characterCode < 32 && characterCode != 13 {
|
||||
characterCode = 32
|
||||
}
|
||||
|
||||
// Similarly, map down unprintables.
|
||||
if characterCode >= 127 {
|
||||
characterCode = 32
|
||||
}
|
||||
|
||||
let textToAppend = UnicodeScalar(characterCode)!
|
||||
output += String(textToAppend)
|
||||
// print(textToAppend, terminator:"")
|
||||
|
||||
case 0x7003:
|
||||
done = true
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
@ -8,10 +8,10 @@
|
||||
|
||||
import XCTest
|
||||
|
||||
class Z80MemptrTests: XCTestCase {
|
||||
private let machine = CSTestMachineZ80()
|
||||
class Z80MemptrTester: XCTestCase {
|
||||
let machine = CSTestMachineZ80()
|
||||
|
||||
private func test(program : [UInt8], length : Int32, initialValue : UInt16) -> UInt16 {
|
||||
private func test(program : [UInt8], initialValue : UInt16) -> UInt16 {
|
||||
// Create a machine and install the supplied program at address 0, setting the PC to run from there
|
||||
machine.setValue(0x0000, for: .programCounter)
|
||||
machine.setData(Data(_: program), atAddress: 0x0000)
|
||||
@ -19,15 +19,137 @@ class Z80MemptrTests: XCTestCase {
|
||||
// Set the initial value of memptr, run for the requested number of cycles,
|
||||
// return the new value
|
||||
machine.setValue(initialValue, for: .memPtr)
|
||||
machine.runForNumber(ofCycles: length)
|
||||
machine.runForInstruction()
|
||||
return machine.value(for: .memPtr)
|
||||
}
|
||||
|
||||
fileprivate func insert16(program: inout [UInt8], address: Int, offset: size_t) {
|
||||
private func testPage(_ prefix: UInt8, exclusions: [Int]) {
|
||||
for opcode in 0 ..< 256 {
|
||||
if exclusions.contains(opcode) {
|
||||
continue
|
||||
}
|
||||
|
||||
var program: [UInt8] = [
|
||||
(prefix != 0) ? prefix : UInt8(opcode), UInt8(opcode), 0, 0
|
||||
]
|
||||
|
||||
let argumentPosition = (prefix != 0) ? 2 : 1
|
||||
for _ in 0 ..< 10 {
|
||||
let random = arc4random_uniform(65536)
|
||||
program[argumentPosition + 0] = UInt8(random & 0x00ff)
|
||||
program[argumentPosition + 1] = UInt8(random >> 8)
|
||||
|
||||
let expectedResult = UInt16(arc4random_uniform(65536))
|
||||
|
||||
let result = test(program: program, initialValue: expectedResult)
|
||||
XCTAssertEqual(result, expectedResult, "Failed for opcode \(String(opcode, radix:16))")
|
||||
|
||||
// One failure per opcode will do.
|
||||
if result != expectedResult {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func insert16(program: inout [UInt8], address: Int, offset: size_t) {
|
||||
program[offset] = UInt8(address & 0x00ff)
|
||||
program[offset + 1] = UInt8(address >> 8)
|
||||
}
|
||||
|
||||
/// Tests that everything not listed in the documentation has no effect upon MEMPTR.
|
||||
func testStandardPageOthers() {
|
||||
testPage(0, exclusions: [
|
||||
0x02, // LD (BC), A
|
||||
0x09, // ADD HL, BC
|
||||
0x0a, // LD A, (BC)
|
||||
0x10, // DJNZ
|
||||
0x12, // LD (DE), A
|
||||
0x18, // JR
|
||||
0x19, // ADD HL, DE
|
||||
0x1a, // LD A, (DE)
|
||||
0x20, // JR NZ
|
||||
0x22, // LD (nn), HL
|
||||
0x28, // JR Z
|
||||
0x29, // ADD HL, HL
|
||||
0x2a, // LD HL, (nn)
|
||||
0x30, // JR NC
|
||||
0x32, // LD (nn), A
|
||||
0x38, // JR C
|
||||
0x39, // ADD HL, SP
|
||||
0x3a, // LD A, (nn)
|
||||
|
||||
0xcb, // CB page
|
||||
0xdd, // DD page
|
||||
0xed, // ED page
|
||||
0xfd, // FD page
|
||||
])
|
||||
}
|
||||
|
||||
func testEDPageOthers() {
|
||||
testPage(0xed, exclusions: [
|
||||
0x40, // IN B, (C)
|
||||
0x41, // OUT (C), B
|
||||
0x42, // SBC HL, BC
|
||||
0x43, // LD (nn), HL
|
||||
0x45, // RETN (??)
|
||||
0x48, // IN C, (C)
|
||||
0x49, // OUT (C), C
|
||||
0x4a, // ADC HL, BC
|
||||
0x4b, // LD BC, (nn)
|
||||
0x4d, // RETI
|
||||
0x50, // IN D, (C)
|
||||
0x51, // OUT (C), D
|
||||
0x52, // SBC HL, DE
|
||||
0x53, // LD (nn), DE
|
||||
0x55, // RETN (??)
|
||||
0x58, // IN E, (C)
|
||||
0x59, // OUT (C), E
|
||||
0x5a, // ADC HL, DE
|
||||
0x5b, // LD DE, (nn)
|
||||
0x5d, // RETN (??)
|
||||
0x60, // IN H, (C)
|
||||
0x61, // OUT (C), H
|
||||
0x62, // SBC HL, HL
|
||||
0x63, // LD (nn), HL
|
||||
0x65, // RETN (??)
|
||||
0x67, // RRD
|
||||
0x68, // IN L, (C)
|
||||
0x69, // OUT (C), L
|
||||
0x6a, // ADC HL, HL
|
||||
0x6b, // LD HL, (nn)
|
||||
0x6d, // RETN (??)
|
||||
0x6f, // RLD
|
||||
0x70, // IN (C)
|
||||
0x71, // OUT (C), 0
|
||||
0x72, // SBC HL, SP
|
||||
0x73, // LD (nn), SP
|
||||
0x75, // RETN (??)
|
||||
0x78, // IN A, (C)
|
||||
0x79, // OUT (C), A
|
||||
0x7a, // ADC HL, SP
|
||||
0x7b, // LD SP, (nn)
|
||||
0x7d, // RETN (??)
|
||||
0xa1, // CPI
|
||||
0xa2, // INI
|
||||
0xa3, // OUTI
|
||||
0xa9, // CPD
|
||||
0xaa, // IND
|
||||
0xab, // OUTD
|
||||
0xb0, // LDIR
|
||||
0xb1, // CPIR
|
||||
0xb2, // INIR
|
||||
0xb3, // OUIR
|
||||
0xb8, // LDDR
|
||||
0xb9, // CPDR
|
||||
0xba, // INDR
|
||||
0xbb, // OTDR
|
||||
])
|
||||
// testPage(0xcb, exclusions: [])
|
||||
// testPage(0xdd, exclusions: [])
|
||||
// testPage(0xfd, exclusions: [])
|
||||
}
|
||||
|
||||
/*
|
||||
Re: comments below:
|
||||
All the CPU chips tested give the same results except KP1858BM1 and T34BM1 slices noted as "BM1".
|
||||
@ -44,7 +166,7 @@ class Z80MemptrTests: XCTestCase {
|
||||
program[2] = UInt8(addr >> 8)
|
||||
let expectedResult = UInt16((addr + 1) & 0xffff)
|
||||
|
||||
let result = test(program: program, length: 13, initialValue: 0xffff)
|
||||
let result = test(program: program, initialValue: 0xffff)
|
||||
XCTAssertEqual(result, expectedResult)
|
||||
}
|
||||
}
|
||||
@ -73,9 +195,9 @@ class Z80MemptrTests: XCTestCase {
|
||||
|
||||
let expectedResult = UInt16(((addr + 1) & 0xff) + (a << 8))
|
||||
|
||||
let bcResult = test(program: bcProgram, length: 7, initialValue: 0xffff)
|
||||
let deResult = test(program: deProgram, length: 7, initialValue: 0xffff)
|
||||
let nnResult = test(program: nnProgram, length: 13, initialValue: 0xffff)
|
||||
let bcResult = test(program: bcProgram, initialValue: 0xffff)
|
||||
let deResult = test(program: deProgram, initialValue: 0xffff)
|
||||
let nnResult = test(program: nnProgram, initialValue: 0xffff)
|
||||
|
||||
XCTAssertEqual(bcResult, expectedResult)
|
||||
XCTAssertEqual(deResult, expectedResult)
|
||||
@ -99,8 +221,8 @@ class Z80MemptrTests: XCTestCase {
|
||||
|
||||
let expectedResult = UInt16((addr + 1) & 0xffff)
|
||||
|
||||
let bcResult = test(program: bcProgram, length: 7, initialValue: 0xffff)
|
||||
let deResult = test(program: deProgram, length: 7, initialValue: 0xffff)
|
||||
let bcResult = test(program: bcProgram, initialValue: 0xffff)
|
||||
let deResult = test(program: deProgram, initialValue: 0xffff)
|
||||
|
||||
XCTAssertEqual(bcResult, expectedResult)
|
||||
XCTAssertEqual(deResult, expectedResult)
|
||||
@ -135,11 +257,11 @@ class Z80MemptrTests: XCTestCase {
|
||||
|
||||
let expectedResult = UInt16((addr + 1) & 0xffff)
|
||||
|
||||
XCTAssertEqual(test(program: ldnnhlBaseProgram, length: 16, initialValue: expectedResult ^ 1), expectedResult)
|
||||
XCTAssertEqual(test(program: ldnnbcEDProgram, length: 20, initialValue: expectedResult ^ 1), expectedResult)
|
||||
XCTAssertEqual(test(program: ldnndeEDProgram, length: 20, initialValue: expectedResult ^ 1), expectedResult)
|
||||
XCTAssertEqual(test(program: ldnnhlEDProgram, length: 20, initialValue: expectedResult ^ 1), expectedResult)
|
||||
XCTAssertEqual(test(program: ldnnspEDProgram, length: 20, initialValue: expectedResult ^ 1), expectedResult)
|
||||
XCTAssertEqual(test(program: ldnnhlBaseProgram, initialValue: expectedResult ^ 1), expectedResult)
|
||||
XCTAssertEqual(test(program: ldnnbcEDProgram, initialValue: expectedResult ^ 1), expectedResult)
|
||||
XCTAssertEqual(test(program: ldnndeEDProgram, initialValue: expectedResult ^ 1), expectedResult)
|
||||
XCTAssertEqual(test(program: ldnnhlEDProgram, initialValue: expectedResult ^ 1), expectedResult)
|
||||
XCTAssertEqual(test(program: ldnnspEDProgram, initialValue: expectedResult ^ 1), expectedResult)
|
||||
}
|
||||
}
|
||||
|
||||
@ -190,15 +312,15 @@ class Z80MemptrTests: XCTestCase {
|
||||
|
||||
let expectedResult = UInt16((addr + 1) & 0xffff)
|
||||
|
||||
XCTAssertEqual(test(program: hlBaseProgram, length: 16, initialValue: 0xffff), expectedResult)
|
||||
XCTAssertEqual(test(program: hlBaseProgram, initialValue: 0xffff), expectedResult)
|
||||
|
||||
XCTAssertEqual(test(program: bcEDProgram, length: 20, initialValue: 0xffff), expectedResult)
|
||||
XCTAssertEqual(test(program: deEDProgram, length: 20, initialValue: 0xffff), expectedResult)
|
||||
XCTAssertEqual(test(program: hlEDProgram, length: 20, initialValue: 0xffff), expectedResult)
|
||||
XCTAssertEqual(test(program: spEDProgram, length: 20, initialValue: 0xffff), expectedResult)
|
||||
XCTAssertEqual(test(program: bcEDProgram, initialValue: 0xffff), expectedResult)
|
||||
XCTAssertEqual(test(program: deEDProgram, initialValue: 0xffff), expectedResult)
|
||||
XCTAssertEqual(test(program: hlEDProgram, initialValue: 0xffff), expectedResult)
|
||||
XCTAssertEqual(test(program: spEDProgram, initialValue: 0xffff), expectedResult)
|
||||
|
||||
XCTAssertEqual(test(program: ixProgram, length: 20, initialValue: 0xffff), expectedResult)
|
||||
XCTAssertEqual(test(program: iyProgram, length: 20, initialValue: 0xffff), expectedResult)
|
||||
XCTAssertEqual(test(program: ixProgram, initialValue: 0xffff), expectedResult)
|
||||
XCTAssertEqual(test(program: iyProgram, initialValue: 0xffff), expectedResult)
|
||||
}
|
||||
}
|
||||
|
||||
@ -225,9 +347,9 @@ class Z80MemptrTests: XCTestCase {
|
||||
iyProgram[2] = UInt8(addr & 0x00ff)
|
||||
iyProgram[3] = UInt8(addr >> 8)
|
||||
|
||||
XCTAssertEqual(test(program: hlProgram, length: 19, initialValue: 0xffff), UInt16(addr))
|
||||
XCTAssertEqual(test(program: ixProgram, length: 23, initialValue: 0xffff), UInt16(addr))
|
||||
XCTAssertEqual(test(program: iyProgram, length: 23, initialValue: 0xffff), UInt16(addr))
|
||||
XCTAssertEqual(test(program: hlProgram, initialValue: 0xffff), UInt16(addr))
|
||||
XCTAssertEqual(test(program: ixProgram, initialValue: 0xffff), UInt16(addr))
|
||||
XCTAssertEqual(test(program: iyProgram, initialValue: 0xffff), UInt16(addr))
|
||||
}
|
||||
}
|
||||
|
||||
@ -248,9 +370,9 @@ class Z80MemptrTests: XCTestCase {
|
||||
let expectedResult = UInt16((addr + 1) & 0xffff)
|
||||
machine.setValue(UInt16(addr), for: .HL)
|
||||
|
||||
XCTAssertEqual(test(program: addProgram, length: 11, initialValue: 0xffff), expectedResult)
|
||||
XCTAssertEqual(test(program: adcProgram, length: 15, initialValue: 0xffff), expectedResult)
|
||||
XCTAssertEqual(test(program: sbcProgram, length: 15, initialValue: 0xffff), expectedResult)
|
||||
XCTAssertEqual(test(program: addProgram, initialValue: 0xffff), expectedResult)
|
||||
XCTAssertEqual(test(program: adcProgram, initialValue: 0xffff), expectedResult)
|
||||
XCTAssertEqual(test(program: sbcProgram, initialValue: 0xffff), expectedResult)
|
||||
}
|
||||
}
|
||||
|
||||
@ -268,29 +390,145 @@ class Z80MemptrTests: XCTestCase {
|
||||
let expectedResult = UInt16((addr + 1) & 0xffff)
|
||||
machine.setValue(UInt16(addr), for: .HL)
|
||||
|
||||
XCTAssertEqual(test(program: rldProgram, length: 18, initialValue: 0xffff), expectedResult)
|
||||
XCTAssertEqual(test(program: rrdProgram, length: 18, initialValue: 0xffff), expectedResult)
|
||||
XCTAssertEqual(test(program: rldProgram, initialValue: 0xffff), expectedResult)
|
||||
XCTAssertEqual(test(program: rrdProgram, initialValue: 0xffff), expectedResult)
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO:
|
||||
JR/DJNZ/RET/RETI/RST (jumping to addr)
|
||||
MEMPTR = addr
|
||||
|
||||
(implemented in principle)
|
||||
*/
|
||||
func testJR() {
|
||||
var jrProgram: [UInt8] = [
|
||||
0x18, 0x00
|
||||
]
|
||||
|
||||
for offset in 0 ..< 256 {
|
||||
jrProgram[1] = UInt8(offset)
|
||||
let result = test(program: jrProgram, initialValue: 0xffff)
|
||||
XCTAssertEqual(result, machine.value(for: .programCounter))
|
||||
}
|
||||
}
|
||||
|
||||
func testJRcc() {
|
||||
func testJR(instruction: UInt8, shouldPass: Bool) {
|
||||
var program: [UInt8] = [
|
||||
instruction, 0x00
|
||||
]
|
||||
|
||||
for offset in 0 ..< 256 {
|
||||
program[1] = UInt8(offset)
|
||||
let result = test(program: program, initialValue: 0xffff)
|
||||
XCTAssertEqual(result, shouldPass ? machine.value(for: .programCounter) : 0xffff)
|
||||
}
|
||||
}
|
||||
|
||||
// JR NZ.
|
||||
machine.setValue(0x00, for: .AF)
|
||||
testJR(instruction: 0x20, shouldPass: true)
|
||||
|
||||
machine.setValue(0xff, for: .AF)
|
||||
testJR(instruction: 0x20, shouldPass: false)
|
||||
|
||||
// JR NC
|
||||
machine.setValue(0x00, for: .AF)
|
||||
testJR(instruction: 0x30, shouldPass: true)
|
||||
|
||||
machine.setValue(0xff, for: .AF)
|
||||
testJR(instruction: 0x30, shouldPass: false)
|
||||
|
||||
// JR Z
|
||||
machine.setValue(0x00, for: .AF)
|
||||
testJR(instruction: 0x28, shouldPass: false)
|
||||
|
||||
machine.setValue(0xff, for: .AF)
|
||||
testJR(instruction: 0x28, shouldPass: true)
|
||||
|
||||
// JR C
|
||||
machine.setValue(0x00, for: .AF)
|
||||
testJR(instruction: 0x38, shouldPass: false)
|
||||
|
||||
machine.setValue(0xff, for: .AF)
|
||||
testJR(instruction: 0x38, shouldPass: true)
|
||||
}
|
||||
|
||||
func testRST() {
|
||||
var rstProgram: [UInt8] = [
|
||||
0x00
|
||||
]
|
||||
|
||||
for offset in 0 ..< 8 {
|
||||
rstProgram[0] = UInt8(offset << 3) + 0xc7
|
||||
let result = test(program: rstProgram, initialValue: 0xffff)
|
||||
XCTAssertEqual(result, machine.value(for: .programCounter))
|
||||
}
|
||||
}
|
||||
|
||||
func testRET() {
|
||||
let retProgram: [UInt8] = [
|
||||
0xc9
|
||||
]
|
||||
|
||||
for addr in 0 ..< 65536 {
|
||||
let stackContents: [UInt8] = [
|
||||
UInt8(addr & 0xff), UInt8(addr >> 8)
|
||||
]
|
||||
|
||||
machine.setData(Data(stackContents), atAddress: 0xf001)
|
||||
machine.setValue(0xf000, for: .stackPointer)
|
||||
let result = test(program: retProgram, initialValue: 0xffff)
|
||||
XCTAssertEqual(result, machine.value(for: .programCounter))
|
||||
}
|
||||
}
|
||||
func testRETI() {
|
||||
let retiProgram: [UInt8] = [
|
||||
0xed, 0x4d
|
||||
]
|
||||
|
||||
for addr in 0 ..< 65536 {
|
||||
let stackContents: [UInt8] = [
|
||||
UInt8(addr & 0xff), UInt8(addr >> 8)
|
||||
]
|
||||
|
||||
machine.setData(Data(stackContents), atAddress: 0xf001)
|
||||
machine.setValue(0xf000, for: .stackPointer)
|
||||
let result = test(program: retiProgram, initialValue: 0xffff)
|
||||
XCTAssertEqual(result, machine.value(for: .programCounter))
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO:
|
||||
JP(except JP rp)/CALL addr (even in case of conditional call/jp, independantly on condition satisfied or not)
|
||||
JP(except JP rp)/CALL addr (even in case of conditional call/jp, independently on condition satisfied or not)
|
||||
MEMPTR = addr
|
||||
*/
|
||||
func testCALL() {
|
||||
var callProgram: [UInt8] = [
|
||||
0xcd, 0x00, 0x00
|
||||
]
|
||||
|
||||
for offset in 0 ..< 65536 {
|
||||
callProgram[1] = UInt8(offset & 0xff)
|
||||
callProgram[2] = UInt8(offset >> 8)
|
||||
let result = test(program: callProgram, initialValue: 0xffff)
|
||||
XCTAssertEqual(result, machine.value(for: .programCounter))
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO:
|
||||
IN A,(port)
|
||||
MEMPTR = (A_before_operation << 8) + port + 1
|
||||
|
||||
(implemented, not tested)
|
||||
*/
|
||||
|
||||
/* TODO:
|
||||
IN A,(C)
|
||||
MEMPTR = BC + 1
|
||||
|
||||
(implemented, not tested)
|
||||
*/
|
||||
|
||||
/* TODO:
|
||||
|
@ -14,50 +14,51 @@ class ZexallTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
fileprivate var done = false
|
||||
fileprivate var output = ""
|
||||
|
||||
func testZexall() {
|
||||
if let filename = Bundle(for: type(of: self)).path(forResource: "zexdoc", ofType: "com") {
|
||||
private func runTest(_ name: String) {
|
||||
if let filename = Bundle(for: type(of: self)).path(forResource: name, ofType: "com") {
|
||||
if let testData = try? Data(contentsOf: URL(fileURLWithPath: filename)) {
|
||||
|
||||
// install test program, at the usual CP/M place
|
||||
// Install test program, at the usual CP/M place.
|
||||
let machine = CSTestMachineZ80()
|
||||
machine.setData(testData, atAddress: 0x0100)
|
||||
|
||||
// add a RET at the CP/M entry location, and establish it as a trap location
|
||||
// Add a RET at the CP/M entry location, set a high memtop, and
|
||||
// and establish the entry location as a trap location.
|
||||
machine.setValue(0xc9, atAddress: 0x0005)
|
||||
machine.setValue(0xff, atAddress: 0x0006)
|
||||
machine.setValue(0xff, atAddress: 0x0007)
|
||||
machine.addTrapAddress(0x0005);
|
||||
machine.trapHandler = self
|
||||
|
||||
// establish 0 as another trap location, as RST 0h is one of the ways that
|
||||
// CP/M programs can exit
|
||||
// Establish 0 as another trap location, as RST 0h is one of the ways that
|
||||
// CP/M programs can exit.
|
||||
machine.addTrapAddress(0);
|
||||
|
||||
// ensure that if the CPU hits zero, it stays there until the end of the
|
||||
// sampling window
|
||||
// Ensure that if the CPU hits zero, it stays there until the end of the
|
||||
// sampling window.
|
||||
machine.setValue(0xc3, atAddress: 0x0000)
|
||||
machine.setValue(0x00, atAddress: 0x0001)
|
||||
machine.setValue(0x00, atAddress: 0x0002)
|
||||
|
||||
// seed execution at 0x0100
|
||||
// Seed execution at 0x0100.
|
||||
machine.setValue(0x0100, for: .programCounter)
|
||||
|
||||
// run!
|
||||
// Run!
|
||||
let cyclesPerIteration: Int32 = 400_000_000
|
||||
var cyclesToDate: TimeInterval = 0
|
||||
let startDate = Date()
|
||||
var printDate = Date()
|
||||
let printMhz = false
|
||||
while !done {
|
||||
machine.runForNumber(ofCycles: cyclesPerIteration)
|
||||
cyclesToDate += TimeInterval(cyclesPerIteration)
|
||||
if printDate.timeIntervalSinceNow < -5.0 {
|
||||
if printMhz && printDate.timeIntervalSinceNow < -5.0 {
|
||||
print("\(cyclesToDate / -startDate.timeIntervalSinceNow) Mhz")
|
||||
printDate = Date()
|
||||
}
|
||||
}
|
||||
|
||||
let targetOutput =
|
||||
"Z80doc instruction exerciser\n\r" +
|
||||
"<adc,sbc> hl,<bc,de,hl,sp>.... OK\n\r" +
|
||||
"add hl,<bc,de,hl,sp>.......... OK\n\r" +
|
||||
"add ix,<bc,de,ix,sp>.......... OK\n\r" +
|
||||
@ -126,15 +127,25 @@ class ZexallTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
"ld (<ix,iy>+1),a.............. OK\n\r" +
|
||||
"ld (<bc,de>),a................ OK\n\r" +
|
||||
"Tests complete\n\r"
|
||||
XCTAssertEqual(targetOutput, output);
|
||||
let successRange = output.range(of: targetOutput)
|
||||
XCTAssertNotEqual(successRange, nil, output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testZexAll() {
|
||||
runTest("zexall")
|
||||
}
|
||||
|
||||
func testZexDoc() {
|
||||
runTest("zexdoc")
|
||||
}
|
||||
|
||||
func testMachine(_ testMachine: CSTestMachine, didTrapAtAddress address: UInt16) {
|
||||
let testMachineZ80 = testMachine as! CSTestMachineZ80
|
||||
switch address {
|
||||
case 0x0005:
|
||||
// Only the output text CP/M calls are implemented.
|
||||
let cRegister = testMachineZ80.value(for: .C)
|
||||
var textToAppend = ""
|
||||
switch cRegister {
|
||||
|
@ -36,9 +36,7 @@ class ConcreteAllRAMProcessor: public AllRAMProcessor, public BusHandler {
|
||||
case PartialMachineCycle::Output:
|
||||
break;
|
||||
case PartialMachineCycle::Input:
|
||||
// This logic is selected specifically because it seems to match
|
||||
// the FUSE unit tests. It might need factoring out.
|
||||
*cycle.value = address >> 8;
|
||||
*cycle.value = port_delegate_ ? port_delegate_->z80_all_ram_processor_input(address) : 0xff;
|
||||
break;
|
||||
|
||||
case PartialMachineCycle::Internal:
|
||||
@ -55,47 +53,66 @@ class ConcreteAllRAMProcessor: public AllRAMProcessor, public BusHandler {
|
||||
break;
|
||||
}
|
||||
|
||||
if(delegate_ != nullptr) {
|
||||
delegate_->z80_all_ram_processor_did_perform_bus_operation(*this, cycle.operation, address, cycle.value ? *cycle.value : 0x00, timestamp_);
|
||||
if(memory_delegate_ != nullptr) {
|
||||
memory_delegate_->z80_all_ram_processor_did_perform_bus_operation(*this, cycle.operation, address, cycle.value ? *cycle.value : 0x00, timestamp_);
|
||||
}
|
||||
|
||||
return HalfCycles(0);
|
||||
}
|
||||
|
||||
void run_for(const Cycles cycles) {
|
||||
void run_for(const Cycles cycles) final {
|
||||
z80_.run_for(cycles);
|
||||
}
|
||||
|
||||
uint16_t get_value_of_register(Register r) {
|
||||
void run_for_instruction() final {
|
||||
int toggles = 0;
|
||||
int cycles = 0;
|
||||
|
||||
// Run:
|
||||
// (1) until is_starting_new_instruction is true;
|
||||
// (2) until it is false again; and
|
||||
// (3) until it is true again.
|
||||
while(true) {
|
||||
if(z80_.is_starting_new_instruction() != (toggles&1)) {
|
||||
++toggles;
|
||||
if(toggles == 3) break;
|
||||
}
|
||||
z80_.run_for(Cycles(1));
|
||||
++cycles;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t get_value_of_register(Register r) final {
|
||||
return z80_.get_value_of_register(r);
|
||||
}
|
||||
|
||||
void set_value_of_register(Register r, uint16_t value) {
|
||||
void set_value_of_register(Register r, uint16_t value) final {
|
||||
z80_.set_value_of_register(r, value);
|
||||
}
|
||||
|
||||
bool get_halt_line() {
|
||||
bool get_halt_line() final {
|
||||
return z80_.get_halt_line();
|
||||
}
|
||||
|
||||
void reset_power_on() {
|
||||
void reset_power_on() final {
|
||||
return z80_.reset_power_on();
|
||||
}
|
||||
|
||||
void set_interrupt_line(bool value) {
|
||||
void set_interrupt_line(bool value) final {
|
||||
z80_.set_interrupt_line(value);
|
||||
}
|
||||
|
||||
void set_non_maskable_interrupt_line(bool value) {
|
||||
void set_non_maskable_interrupt_line(bool value) final {
|
||||
z80_.set_non_maskable_interrupt_line(value);
|
||||
}
|
||||
|
||||
void set_wait_line(bool value) {
|
||||
void set_wait_line(bool value) final {
|
||||
z80_.set_wait_line(value);
|
||||
}
|
||||
|
||||
private:
|
||||
CPU::Z80::Processor<ConcreteAllRAMProcessor, false, true> z80_;
|
||||
bool was_m1_ = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -25,10 +25,18 @@ class AllRAMProcessor:
|
||||
virtual void z80_all_ram_processor_did_perform_bus_operation(CPU::Z80::AllRAMProcessor &processor, CPU::Z80::PartialMachineCycle::Operation operation, uint16_t address, uint8_t value, HalfCycles time_stamp) = 0;
|
||||
};
|
||||
inline void set_memory_access_delegate(MemoryAccessDelegate *delegate) {
|
||||
delegate_ = delegate;
|
||||
memory_delegate_ = delegate;
|
||||
}
|
||||
|
||||
struct PortAccessDelegate {
|
||||
virtual uint8_t z80_all_ram_processor_input(uint16_t port) { return 0xff; }
|
||||
};
|
||||
inline void set_port_access_delegate(PortAccessDelegate *delegate) {
|
||||
port_delegate_ = delegate;
|
||||
}
|
||||
|
||||
virtual void run_for(const Cycles cycles) = 0;
|
||||
virtual void run_for_instruction() = 0;
|
||||
virtual uint16_t get_value_of_register(Register r) = 0;
|
||||
virtual void set_value_of_register(Register r, uint16_t value) = 0;
|
||||
virtual bool get_halt_line() = 0;
|
||||
@ -39,8 +47,9 @@ class AllRAMProcessor:
|
||||
virtual void set_wait_line(bool value) = 0;
|
||||
|
||||
protected:
|
||||
MemoryAccessDelegate *delegate_;
|
||||
AllRAMProcessor() : ::CPU::AllRAMProcessor(65536), delegate_(nullptr) {}
|
||||
MemoryAccessDelegate *memory_delegate_ = nullptr;
|
||||
PortAccessDelegate *port_delegate_ = nullptr;
|
||||
AllRAMProcessor() : ::CPU::AllRAMProcessor(65536) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -101,9 +101,10 @@ template < class T,
|
||||
scheduled_program_counter_ = current_instruction_page_->instructions[operation_ & halt_mask_];
|
||||
break;
|
||||
|
||||
case MicroOp::Increment16: (*static_cast<uint16_t *>(operation->source))++; break;
|
||||
case MicroOp::Increment8NoFlags: ++ *static_cast<uint8_t *>(operation->source); break;
|
||||
case MicroOp::Increment16: ++ *static_cast<uint16_t *>(operation->source); break;
|
||||
case MicroOp::IncrementPC: pc_.full += pc_increment_; break;
|
||||
case MicroOp::Decrement16: (*static_cast<uint16_t *>(operation->source))--; break;
|
||||
case MicroOp::Decrement16: -- *static_cast<uint16_t *>(operation->source); break;
|
||||
case MicroOp::Move8: *static_cast<uint8_t *>(operation->destination) = *static_cast<uint8_t *>(operation->source); break;
|
||||
case MicroOp::Move16: *static_cast<uint16_t *>(operation->destination) = *static_cast<uint16_t *>(operation->source); break;
|
||||
|
||||
@ -479,8 +480,9 @@ template < class T,
|
||||
#define REPEAT(test) \
|
||||
if(test) { \
|
||||
pc_.full -= 2; \
|
||||
memptr_.full = pc_.full + 1; \
|
||||
} else { \
|
||||
advance_operation(); \
|
||||
advance_operation(); \
|
||||
}
|
||||
|
||||
#define LDxR_STEP(dir) \
|
||||
@ -514,9 +516,10 @@ template < class T,
|
||||
|
||||
#undef LDxR_STEP
|
||||
|
||||
#define CPxR_STEP(dir) \
|
||||
hl_.full += dir; \
|
||||
bc_.full--; \
|
||||
#define CPxR_STEP(dir) \
|
||||
hl_.full += dir; \
|
||||
memptr_.full += dir; \
|
||||
bc_.full--; \
|
||||
\
|
||||
uint8_t result = a_ - temp8_; \
|
||||
const uint8_t halfResult = (a_&0xf) - (temp8_&0xf); \
|
||||
@ -541,18 +544,25 @@ template < class T,
|
||||
} break;
|
||||
|
||||
case MicroOp::CPD: {
|
||||
memptr_.full--;
|
||||
CPxR_STEP(-1);
|
||||
} break;
|
||||
|
||||
case MicroOp::CPI: {
|
||||
memptr_.full++;
|
||||
CPxR_STEP(1);
|
||||
} break;
|
||||
|
||||
#undef CPxR_STEP
|
||||
|
||||
#undef REPEAT
|
||||
#define REPEAT(test) \
|
||||
if(test) { \
|
||||
pc_.full -= 2; \
|
||||
} else { \
|
||||
advance_operation(); \
|
||||
}
|
||||
|
||||
#define INxR_STEP(dir) \
|
||||
memptr_.full = uint16_t(bc_.full + dir); \
|
||||
bc_.halves.high--; \
|
||||
hl_.full += dir; \
|
||||
\
|
||||
@ -585,12 +595,10 @@ template < class T,
|
||||
} break;
|
||||
|
||||
case MicroOp::IND: {
|
||||
memptr_.full = bc_.full - 1;
|
||||
INxR_STEP(-1);
|
||||
} break;
|
||||
|
||||
case MicroOp::INI: {
|
||||
memptr_.full = bc_.full + 1;
|
||||
INxR_STEP(1);
|
||||
} break;
|
||||
|
||||
@ -598,6 +606,7 @@ template < class T,
|
||||
|
||||
#define OUTxR_STEP(dir) \
|
||||
bc_.halves.high--; \
|
||||
memptr_.full = uint16_t(bc_.full + dir); \
|
||||
hl_.full += dir; \
|
||||
\
|
||||
sign_result_ = zero_result_ = bit53_result_ = bc_.halves.high; \
|
||||
@ -621,12 +630,10 @@ template < class T,
|
||||
|
||||
case MicroOp::OUTD: {
|
||||
OUTxR_STEP(-1);
|
||||
memptr_.full = bc_.full - 1;
|
||||
} break;
|
||||
|
||||
case MicroOp::OUTI: {
|
||||
OUTxR_STEP(1);
|
||||
memptr_.full = bc_.full + 1;
|
||||
} break;
|
||||
|
||||
#undef OUTxR_STEP
|
||||
@ -636,6 +643,7 @@ template < class T,
|
||||
case MicroOp::BIT: {
|
||||
const uint8_t result = *static_cast<uint8_t *>(operation->source) & (1 << ((operation_ >> 3)&7));
|
||||
|
||||
// Leak MEMPTR into bits 5 and 3 if this is either BIT n,(HL) or BIT n,(IX/IY+d).
|
||||
if(current_instruction_page_->is_indexed || ((operation_&0x07) == 6)) {
|
||||
bit53_result_ = memptr_.halves.high;
|
||||
} else {
|
||||
@ -797,13 +805,18 @@ template < class T,
|
||||
}
|
||||
break;
|
||||
|
||||
// MARK: - Input
|
||||
// MARK: - Input and Output
|
||||
|
||||
case MicroOp::SetInFlags:
|
||||
subtract_flag_ = half_carry_result_ = 0;
|
||||
sign_result_ = zero_result_ = bit53_result_ = *static_cast<uint8_t *>(operation->source);
|
||||
set_parity(sign_result_);
|
||||
set_did_compute_flags();
|
||||
++memptr_.full;
|
||||
break;
|
||||
|
||||
case MicroOp::SetOutFlags:
|
||||
memptr_.full = bc_.full + 1;
|
||||
break;
|
||||
|
||||
case MicroOp::SetAFlags:
|
||||
@ -840,6 +853,7 @@ template < class T,
|
||||
case MicroOp::RETN:
|
||||
iff1_ = iff2_;
|
||||
if(irq_line_ && iff1_) request_status_ |= Interrupt::IRQ;
|
||||
memptr_ = pc_;
|
||||
break;
|
||||
|
||||
case MicroOp::HALT:
|
||||
@ -866,7 +880,7 @@ template < class T,
|
||||
break;
|
||||
|
||||
case MicroOp::CalculateIndexAddress:
|
||||
memptr_.full = static_cast<uint16_t>(*static_cast<uint16_t *>(operation->source) + (int8_t)temp8_);
|
||||
memptr_.full = static_cast<uint16_t>(*static_cast<uint16_t *>(operation->source) + int8_t(temp8_));
|
||||
break;
|
||||
|
||||
case MicroOp::SetAddrAMemptr:
|
||||
|
@ -80,6 +80,7 @@ ProcessorStorage::ProcessorStorage() {
|
||||
|
||||
/* The following are helper macros that define common parts of instructions */
|
||||
#define Inc16(r) {(&r == &pc_) ? MicroOp::IncrementPC : MicroOp::Increment16, &r.full}
|
||||
#define Inc8NoFlags(r) {MicroOp::Increment8NoFlags, &r}
|
||||
|
||||
#define ReadInc(addr, val) Read3(addr, val), Inc16(addr)
|
||||
#define Read4Inc(addr, val) Read4(addr, val), Inc16(addr)
|
||||
@ -104,8 +105,8 @@ ProcessorStorage::ProcessorStorage() {
|
||||
/* The following are actual instructions */
|
||||
#define NOP Sequence(BusOp(Refresh(4)))
|
||||
|
||||
#define JP(cc) StdInstr(Read16Inc(pc_, temp16_), {MicroOp::cc, nullptr}, {MicroOp::Move16, &temp16_.full, &pc_.full})
|
||||
#define CALL(cc) StdInstr(ReadInc(pc_, temp16_.halves.low), {MicroOp::cc, conditional_call_untaken_program_.data()}, Read4Inc(pc_, temp16_.halves.high), Push(pc_), {MicroOp::Move16, &temp16_.full, &pc_.full})
|
||||
#define JP(cc) StdInstr(Read16Inc(pc_, memptr_), {MicroOp::cc, nullptr}, {MicroOp::Move16, &memptr_.full, &pc_.full})
|
||||
#define CALL(cc) StdInstr(ReadInc(pc_, memptr_.halves.low), {MicroOp::cc, conditional_call_untaken_program_.data()}, Read4Inc(pc_, memptr_.halves.high), Push(pc_), {MicroOp::Move16, &memptr_.full, &pc_.full})
|
||||
#define RET(cc) Instr(6, {MicroOp::cc, nullptr}, Pop(memptr_), {MicroOp::Move16, &memptr_.full, &pc_.full})
|
||||
#define JR(cc) StdInstr(ReadInc(pc_, temp8_), {MicroOp::cc, nullptr}, InternalOperation(10), {MicroOp::CalculateIndexAddress, &pc_.full}, {MicroOp::Move16, &memptr_.full, &pc_.full})
|
||||
#define RST() Instr(6, {MicroOp::CalculateRSTDestination}, Push(pc_), {MicroOp::Move16, &memptr_.full, &pc_.full})
|
||||
@ -166,7 +167,7 @@ ProcessorStorage::ProcessorStorage() {
|
||||
#define SBC16(d, s) StdInstr(InternalOperation(8), InternalOperation(6), {MicroOp::SBC16, &s.full, &d.full})
|
||||
|
||||
void ProcessorStorage::install_default_instruction_set() {
|
||||
MicroOp conditional_call_untaken_program[] = Sequence(ReadInc(pc_, temp16_.halves.high));
|
||||
MicroOp conditional_call_untaken_program[] = Sequence(ReadInc(pc_, memptr_.halves.high));
|
||||
copy_program(conditional_call_untaken_program, conditional_call_untaken_program_);
|
||||
|
||||
assemble_base_page(base_page_, hl_, false, cb_page_);
|
||||
@ -243,8 +244,8 @@ void ProcessorStorage::install_default_instruction_set() {
|
||||
}
|
||||
|
||||
void ProcessorStorage::assemble_ed_page(InstructionPage &target) {
|
||||
#define IN_C(r) StdInstr(Input(bc_, r), {MicroOp::SetInFlags, &r})
|
||||
#define OUT_C(r) StdInstr(Output(bc_, r))
|
||||
#define IN_C(r) StdInstr({MicroOp::Move16, &bc_.full, &memptr_.full}, Input(bc_, r), {MicroOp::SetInFlags, &r})
|
||||
#define OUT_C(r) StdInstr(Output(bc_, r), {MicroOp::SetOutFlags})
|
||||
#define IN_OUT(r) IN_C(r), OUT_C(r)
|
||||
|
||||
#define NOP_ROW() NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP
|
||||
@ -258,7 +259,7 @@ void ProcessorStorage::assemble_ed_page(InstructionPage &target) {
|
||||
/* 0x44 NEG */ StdInstr({MicroOp::NEG}), /* 0x45 RETN */ StdInstr(Pop(pc_), {MicroOp::RETN}),
|
||||
/* 0x46 IM 0 */ StdInstr({MicroOp::IM}), /* 0x47 LD I, A */ Instr(6, {MicroOp::Move8, &a_, &ir_.halves.high}),
|
||||
/* 0x48 IN C, (C); 0x49 OUT (C), C */ IN_OUT(bc_.halves.low),
|
||||
/* 0x4a ADC HL, BC */ ADC16(hl_, bc_), /* 0x4b LD BC, (nn) */ StdInstr(Read16Inc(pc_, temp16_), Read16(temp16_, bc_)),
|
||||
/* 0x4a ADC HL, BC */ ADC16(hl_, bc_), /* 0x4b LD BC, (nn) */ StdInstr(Read16Inc(pc_, memptr_), Read16(memptr_, bc_)),
|
||||
/* 0x4c NEG */ StdInstr({MicroOp::NEG}), /* 0x4d RETI */ StdInstr(Pop(pc_), {MicroOp::RETN}),
|
||||
/* 0x4e IM 0/1 */ StdInstr({MicroOp::IM}), /* 0x4f LD R, A */ Instr(6, {MicroOp::Move8, &a_, &ir_.halves.low}),
|
||||
/* 0x50 IN D, (C); 0x51 OUT (C), D */ IN_OUT(de_.halves.high),
|
||||
@ -266,7 +267,7 @@ void ProcessorStorage::assemble_ed_page(InstructionPage &target) {
|
||||
/* 0x54 NEG */ StdInstr({MicroOp::NEG}), /* 0x55 RETN */ StdInstr(Pop(pc_), {MicroOp::RETN}),
|
||||
/* 0x56 IM 1 */ StdInstr({MicroOp::IM}), /* 0x57 LD A, I */ Instr(6, {MicroOp::Move8, &ir_.halves.high, &a_}, {MicroOp::SetAFlags}),
|
||||
/* 0x58 IN E, (C); 0x59 OUT (C), E */ IN_OUT(de_.halves.low),
|
||||
/* 0x5a ADC HL, DE */ ADC16(hl_, de_), /* 0x5b LD DE, (nn) */ StdInstr(Read16Inc(pc_, temp16_), Read16(temp16_, de_)),
|
||||
/* 0x5a ADC HL, DE */ ADC16(hl_, de_), /* 0x5b LD DE, (nn) */ StdInstr(Read16Inc(pc_, memptr_), Read16(memptr_, de_)),
|
||||
/* 0x5c NEG */ StdInstr({MicroOp::NEG}), /* 0x5d RETN */ StdInstr(Pop(pc_), {MicroOp::RETN}),
|
||||
/* 0x5e IM 2 */ StdInstr({MicroOp::IM}), /* 0x5f LD A, R */ Instr(6, {MicroOp::Move8, &ir_.halves.low, &a_}, {MicroOp::SetAFlags}),
|
||||
/* 0x60 IN H, (C); 0x61 OUT (C), H */ IN_OUT(hl_.halves.high),
|
||||
@ -274,15 +275,15 @@ void ProcessorStorage::assemble_ed_page(InstructionPage &target) {
|
||||
/* 0x64 NEG */ StdInstr({MicroOp::NEG}), /* 0x65 RETN */ StdInstr(Pop(pc_), {MicroOp::RETN}),
|
||||
/* 0x66 IM 0 */ StdInstr({MicroOp::IM}), /* 0x67 RRD */ StdInstr(Read3(hl_, temp8_), InternalOperation(8), {MicroOp::RRD}, Write3(hl_, temp8_)),
|
||||
/* 0x68 IN L, (C); 0x69 OUT (C), L */ IN_OUT(hl_.halves.low),
|
||||
/* 0x6a ADC HL, HL */ ADC16(hl_, hl_), /* 0x6b LD HL, (nn) */ StdInstr(Read16Inc(pc_, temp16_), Read16(temp16_, hl_)),
|
||||
/* 0x6a ADC HL, HL */ ADC16(hl_, hl_), /* 0x6b LD HL, (nn) */ StdInstr(Read16Inc(pc_, memptr_), Read16(memptr_, hl_)),
|
||||
/* 0x6c NEG */ StdInstr({MicroOp::NEG}), /* 0x6d RETN */ StdInstr(Pop(pc_), {MicroOp::RETN}),
|
||||
/* 0x6e IM 0/1 */ StdInstr({MicroOp::IM}), /* 0x6f RLD */ StdInstr(Read3(hl_, temp8_), InternalOperation(8), {MicroOp::RLD}, Write3(hl_, temp8_)),
|
||||
/* 0x70 IN (C) */ IN_C(temp8_), /* 0x71 OUT (C), 0 */ StdInstr({MicroOp::SetZero}, Output(bc_, temp8_)),
|
||||
/* 0x70 IN (C) */ IN_C(temp8_), /* 0x71 OUT (C), 0 */ StdInstr({MicroOp::SetZero}, Output(bc_, temp8_), {MicroOp::SetOutFlags}),
|
||||
/* 0x72 SBC HL, SP */ SBC16(hl_, sp_), /* 0x73 LD (nn), SP */ StdInstr(Read16Inc(pc_, memptr_), Write16(memptr_, sp_)),
|
||||
/* 0x74 NEG */ StdInstr({MicroOp::NEG}), /* 0x75 RETN */ StdInstr(Pop(pc_), {MicroOp::RETN}),
|
||||
/* 0x76 IM 1 */ StdInstr({MicroOp::IM}), /* 0x77 XX */ NOP,
|
||||
/* 0x78 IN A, (C); 0x79 OUT (C), A */ IN_OUT(a_),
|
||||
/* 0x7a ADC HL, SP */ ADC16(hl_, sp_), /* 0x7b LD SP, (nn) */ StdInstr(Read16Inc(pc_, temp16_), Read16(temp16_, sp_)),
|
||||
/* 0x7a ADC HL, SP */ ADC16(hl_, sp_), /* 0x7b LD SP, (nn) */ StdInstr(Read16Inc(pc_, memptr_), Read16(memptr_, sp_)),
|
||||
/* 0x7c NEG */ StdInstr({MicroOp::NEG}), /* 0x7d RETN */ StdInstr(Pop(pc_), {MicroOp::RETN}),
|
||||
/* 0x7e IM 2 */ StdInstr({MicroOp::IM}), /* 0x7f XX */ NOP,
|
||||
NOP_ROW(), /* 0x80 ... 0x8f */
|
||||
@ -394,7 +395,7 @@ void ProcessorStorage::assemble_base_page(InstructionPage &target, RegisterPair1
|
||||
|
||||
/* 0x27 DAA */ StdInstr({MicroOp::DAA}),
|
||||
/* 0x28 JR Z */ JR(TestZ), /* 0x29 ADD HL, HL */ ADD16(index, index),
|
||||
/* 0x2a LD HL, (nn) */ StdInstr(Read16Inc(pc_, temp16_), Read16(temp16_, index)),
|
||||
/* 0x2a LD HL, (nn) */ StdInstr(Read16Inc(pc_, memptr_), Read16(memptr_, index)),
|
||||
|
||||
/* 0x2b DEC HL; 0x2c INC L; 0x2d DEC L; 0x2e LD L, n */
|
||||
DEC_INC_DEC_LD(index, index.halves.low),
|
||||
@ -472,22 +473,22 @@ void ProcessorStorage::assemble_base_page(InstructionPage &target, RegisterPair1
|
||||
READ_OP_GROUP(CP8),
|
||||
|
||||
/* 0xc0 RET NZ */ RET(TestNZ), /* 0xc1 POP BC */ StdInstr(Pop(bc_)),
|
||||
/* 0xc2 JP NZ */ JP(TestNZ), /* 0xc3 JP nn */ StdInstr(Read16(pc_, temp16_), {MicroOp::Move16, &temp16_.full, &pc_.full}),
|
||||
/* 0xc2 JP NZ */ JP(TestNZ), /* 0xc3 JP nn */ StdInstr(Read16(pc_, memptr_), {MicroOp::Move16, &memptr_.full, &pc_.full}),
|
||||
/* 0xc4 CALL NZ */ CALL(TestNZ), /* 0xc5 PUSH BC */ Instr(6, Push(bc_)),
|
||||
/* 0xc6 ADD A, n */ StdInstr(ReadInc(pc_, temp8_), {MicroOp::ADD8, &temp8_}),
|
||||
/* 0xc7 RST 00h */ RST(),
|
||||
/* 0xc8 RET Z */ RET(TestZ), /* 0xc9 RET */ StdInstr(Pop(pc_)),
|
||||
/* 0xc8 RET Z */ RET(TestZ), /* 0xc9 RET */ StdInstr(Pop(memptr_), {MicroOp::Move16, &memptr_.full, &pc_.full}),
|
||||
/* 0xca JP Z */ JP(TestZ), /* 0xcb [CB page] */StdInstr(FINDEX(), {MicroOp::SetInstructionPage, &cb_page}),
|
||||
/* 0xcc CALL Z */ CALL(TestZ), /* 0xcd CALL */ StdInstr(ReadInc(pc_, temp16_.halves.low), Read4Inc(pc_, temp16_.halves.high), Push(pc_), {MicroOp::Move16, &temp16_.full, &pc_.full}),
|
||||
/* 0xcc CALL Z */ CALL(TestZ), /* 0xcd CALL */ StdInstr(ReadInc(pc_, memptr_.halves.low), Read4Inc(pc_, memptr_.halves.high), Push(pc_), {MicroOp::Move16, &memptr_.full, &pc_.full}),
|
||||
/* 0xce ADC A, n */ StdInstr(ReadInc(pc_, temp8_), {MicroOp::ADC8, &temp8_}),
|
||||
/* 0xcf RST 08h */ RST(),
|
||||
/* 0xd0 RET NC */ RET(TestNC), /* 0xd1 POP DE */ StdInstr(Pop(de_)),
|
||||
/* 0xd2 JP NC */ JP(TestNC), /* 0xd3 OUT (n), A */StdInstr(ReadInc(pc_, temp16_.halves.low), {MicroOp::Move8, &a_, &temp16_.halves.high}, Output(temp16_, a_)),
|
||||
/* 0xd2 JP NC */ JP(TestNC), /* 0xd3 OUT (n), A */StdInstr(ReadInc(pc_, memptr_.halves.low), {MicroOp::Move8, &a_, &memptr_.halves.high}, Output(memptr_, a_), Inc8NoFlags(memptr_.halves.low)),
|
||||
/* 0xd4 CALL NC */ CALL(TestNC), /* 0xd5 PUSH DE */ Instr(6, Push(de_)),
|
||||
/* 0xd6 SUB n */ StdInstr(ReadInc(pc_, temp8_), {MicroOp::SUB8, &temp8_}),
|
||||
/* 0xd7 RST 10h */ RST(),
|
||||
/* 0xd8 RET C */ RET(TestC), /* 0xd9 EXX */ StdInstr({MicroOp::EXX}),
|
||||
/* 0xda JP C */ JP(TestC), /* 0xdb IN A, (n) */StdInstr(ReadInc(pc_, temp16_.halves.low), {MicroOp::Move8, &a_, &temp16_.halves.high}, Input(temp16_, a_)),
|
||||
/* 0xda JP C */ JP(TestC), /* 0xdb IN A, (n) */StdInstr(ReadInc(pc_, memptr_.halves.low), {MicroOp::Move8, &a_, &memptr_.halves.high}, Input(memptr_, a_), Inc16(memptr_)),
|
||||
/* 0xdc CALL C */ CALL(TestC), /* 0xdd [DD page] */StdInstr({MicroOp::SetInstructionPage, &dd_page_}),
|
||||
/* 0xde SBC A, n */ StdInstr(ReadInc(pc_, temp8_), {MicroOp::SBC8, &temp8_}),
|
||||
/* 0xdf RST 18h */ RST(),
|
||||
@ -543,3 +544,9 @@ void ProcessorStorage::assemble_fetch_decode_execute(InstructionPage &target, in
|
||||
copy_program((length == 4) ? normal_fetch_decode_execute : short_fetch_decode_execute, target.fetch_decode_execute);
|
||||
target.fetch_decode_execute_data = target.fetch_decode_execute.data();
|
||||
}
|
||||
|
||||
bool ProcessorBase::is_starting_new_instruction() {
|
||||
return
|
||||
current_instruction_page_ == &base_page_ &&
|
||||
scheduled_program_counter_ == &base_page_.fetch_decode_execute[0];
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ class ProcessorStorage {
|
||||
DecodeOperationNoRChange,
|
||||
MoveToNextProgram,
|
||||
|
||||
Increment8NoFlags,
|
||||
Increment8,
|
||||
Increment16,
|
||||
Decrement8,
|
||||
@ -73,31 +74,45 @@ class ProcessorStorage {
|
||||
JumpTo66,
|
||||
HALT,
|
||||
|
||||
/// Decrements BC; if BC is 0 then moves to the next instruction. Otherwise allows this instruction to finish.
|
||||
DJNZ,
|
||||
|
||||
DAA,
|
||||
CPL,
|
||||
SCF,
|
||||
CCF,
|
||||
|
||||
/// Resets the bit in @c source implied by @c operation_ .
|
||||
RES,
|
||||
/// Tests the bit in @c source implied by @c operation_ .
|
||||
BIT,
|
||||
/// Sets the bit in @c source implied by @c operation_ .
|
||||
SET,
|
||||
|
||||
/// Sets @c memptr_ to the target address implied by @c operation_ .
|
||||
CalculateRSTDestination,
|
||||
|
||||
/// Resets subtract and carry, sets sign, zero, five and three according to the value of @c a_ and sets parity to the value of @c IFF2 .
|
||||
SetAFlags,
|
||||
/// Resets subtract and carry, sets sign, zero, five and three according to the value of @c operation and sets parity the same as sign.
|
||||
SetInFlags,
|
||||
/// Sets @c memptr_ to @c bc_.full+1 .
|
||||
SetOutFlags,
|
||||
/// Sets @c temp8_ to 0.
|
||||
SetZero,
|
||||
|
||||
/// A no-op; used in instruction lists to indicate where an index calculation should go if this is an I[X/Y]+d operation.
|
||||
IndexedPlaceHolder,
|
||||
|
||||
/// Sets @c memptr_ to (a_ << 8) + ((source_ + 1) & 0xff)
|
||||
SetAddrAMemptr,
|
||||
|
||||
/// Resets: IFF1, IFF2, interrupt mode, the PC, I and R; sets all flags, the SP to 0xffff and a_ to 0xff.
|
||||
Reset
|
||||
};
|
||||
Type type;
|
||||
void *source;
|
||||
void *destination;
|
||||
void *source = nullptr;
|
||||
void *destination = nullptr;
|
||||
PartialMachineCycle machine_cycle;
|
||||
};
|
||||
|
||||
@ -105,7 +120,7 @@ class ProcessorStorage {
|
||||
std::vector<MicroOp *> instructions;
|
||||
std::vector<MicroOp> all_operations;
|
||||
std::vector<MicroOp> fetch_decode_execute;
|
||||
MicroOp *fetch_decode_execute_data;
|
||||
MicroOp *fetch_decode_execute_data = nullptr;
|
||||
uint8_t r_step;
|
||||
bool is_indexed;
|
||||
|
||||
|
@ -224,6 +224,13 @@ class ProcessorBase: public ProcessorStorage {
|
||||
reset at the first opportunity. Use @c reset_power_on to disable that behaviour.
|
||||
*/
|
||||
void reset_power_on();
|
||||
|
||||
/*!
|
||||
@returns @c true if the Z80 is currently beginning to fetch a new instruction; @c false otherwise.
|
||||
|
||||
This is not a speedy operation.
|
||||
*/
|
||||
bool is_starting_new_instruction();
|
||||
};
|
||||
|
||||
/*!
|
||||
|
Loading…
Reference in New Issue
Block a user