mirror of
https://github.com/TomHarte/CLK.git
synced 2025-04-09 15:39:08 +00:00
Merge pull request #839 from TomHarte/65816
Adds emulation of the 65816.
This commit is contained in:
commit
69450e27ad
@ -33,8 +33,14 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
|
||||
BD500
|
||||
);
|
||||
|
||||
ReflectableEnum(Processor,
|
||||
MOS6502,
|
||||
WDC65816
|
||||
);
|
||||
|
||||
ROM rom = ROM::BASIC11;
|
||||
DiskInterface disk_interface = DiskInterface::None;
|
||||
Processor processor = Processor::MOS6502;
|
||||
std::string loading_command;
|
||||
bool should_start_jasmin = false;
|
||||
|
||||
@ -42,8 +48,10 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
|
||||
if(needs_declare()) {
|
||||
DeclareField(rom);
|
||||
DeclareField(disk_interface);
|
||||
DeclareField(processor);
|
||||
AnnounceEnum(ROM);
|
||||
AnnounceEnum(DiskInterface);
|
||||
AnnounceEnum(Processor);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -107,7 +107,10 @@ static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform::
|
||||
Format("dmk", result.disks, Disk::DiskImageHolder<Storage::Disk::DMK>, TargetPlatform::MSX) // DMK
|
||||
Format("do", result.disks, Disk::DiskImageHolder<Storage::Disk::AppleDSK>, TargetPlatform::DiskII) // DO
|
||||
Format("dsd", result.disks, Disk::DiskImageHolder<Storage::Disk::SSD>, TargetPlatform::Acorn) // DSD
|
||||
Format("dsk", result.disks, Disk::DiskImageHolder<Storage::Disk::CPCDSK>, TargetPlatform::AmstradCPC) // DSK (Amstrad CPC)
|
||||
Format( "dsk",
|
||||
result.disks,
|
||||
Disk::DiskImageHolder<Storage::Disk::CPCDSK>,
|
||||
TargetPlatform::AmstradCPC | TargetPlatform::Oric) // DSK (Amstrad CPC)
|
||||
Format("dsk", result.disks, Disk::DiskImageHolder<Storage::Disk::AppleDSK>, TargetPlatform::DiskII) // DSK (Apple II)
|
||||
Format("dsk", result.disks, Disk::DiskImageHolder<Storage::Disk::MacintoshIMG>, TargetPlatform::Macintosh) // DSK (Macintosh, floppy disk)
|
||||
Format("dsk", result.mass_storage_devices, MassStorage::HFV, TargetPlatform::Macintosh) // DSK (Macintosh, hard disk)
|
||||
|
@ -75,7 +75,7 @@ template<class T> class Cartridge:
|
||||
cycles_since_6532_update_ += Cycles(cycles_run_for / 3);
|
||||
bus_extender_.advance_cycles(cycles_run_for / 3);
|
||||
|
||||
if(operation != CPU::MOS6502::BusOperation::Ready) {
|
||||
if(isAccessOperation(operation)) {
|
||||
// give the cartridge a chance to respond to the bus access
|
||||
bus_extender_.perform_bus_operation(operation, address, value);
|
||||
|
||||
|
@ -20,7 +20,7 @@
|
||||
#include "../Utility/MemoryFuzzer.hpp"
|
||||
#include "../Utility/StringSerialiser.hpp"
|
||||
|
||||
#include "../../Processors/6502/6502.hpp"
|
||||
#include "../../Processors/6502Esque/6502Selector.hpp"
|
||||
#include "../../Components/6522/6522.hpp"
|
||||
#include "../../Components/AY38910/AY38910.hpp"
|
||||
#include "../../Components/DiskII/DiskII.hpp"
|
||||
@ -41,6 +41,7 @@
|
||||
namespace Oric {
|
||||
|
||||
using DiskInterface = Analyser::Static::Oric::Target::DiskInterface;
|
||||
using Processor = Analyser::Static::Oric::Target::Processor;
|
||||
using AY = GI::AY38910::AY38910<false>;
|
||||
using Speaker = Outputs::Speaker::LowpassSpeaker<AY>;
|
||||
|
||||
@ -205,7 +206,7 @@ class VIAPortHandler: public MOS::MOS6522::IRQDelegatePortHandler {
|
||||
Keyboard &keyboard_;
|
||||
};
|
||||
|
||||
template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class ConcreteMachine:
|
||||
template <Analyser::Static::Oric::Target::DiskInterface disk_interface, CPU::MOS6502Esque::Type processor_type> class ConcreteMachine:
|
||||
public MachineTypes::TimedMachine,
|
||||
public MachineTypes::ScanProducer,
|
||||
public MachineTypes::AudioProducer,
|
||||
@ -424,8 +425,8 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
||||
!tape_player_.get_tape()->is_at_end()) {
|
||||
|
||||
uint8_t next_byte = tape_player_.get_next_byte(!ram_[tape_speed_address_]);
|
||||
m6502_.set_value_of_register(CPU::MOS6502::A, next_byte);
|
||||
m6502_.set_value_of_register(CPU::MOS6502::Flags, next_byte ? 0 : CPU::MOS6502::Flag::Zero);
|
||||
m6502_.set_value_of_register(CPU::MOS6502Esque::A, next_byte);
|
||||
m6502_.set_value_of_register(CPU::MOS6502Esque::Flags, next_byte ? 0 : CPU::MOS6502::Flag::Zero);
|
||||
*value = 0x60; // i.e. RTS
|
||||
}
|
||||
} else {
|
||||
@ -656,7 +657,7 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
||||
const uint16_t basic_invisible_ram_top_ = 0xffff;
|
||||
const uint16_t basic_visible_ram_top_ = 0xbfff;
|
||||
|
||||
CPU::MOS6502::Processor<CPU::MOS6502::Personality::P6502, ConcreteMachine, false> m6502_;
|
||||
CPU::MOS6502Esque::Processor<processor_type, ConcreteMachine, false> m6502_;
|
||||
|
||||
// RAM and ROM
|
||||
std::vector<uint8_t> rom_, disk_rom_;
|
||||
@ -759,13 +760,23 @@ using namespace Oric;
|
||||
|
||||
Machine *Machine::Oric(const Analyser::Static::Target *target_hint, const ROMMachine::ROMFetcher &rom_fetcher) {
|
||||
auto *const oric_target = dynamic_cast<const Analyser::Static::Oric::Target *>(target_hint);
|
||||
switch(oric_target->disk_interface) {
|
||||
default: return new ConcreteMachine<DiskInterface::None>(*oric_target, rom_fetcher);
|
||||
case DiskInterface::Microdisc: return new ConcreteMachine<DiskInterface::Microdisc>(*oric_target, rom_fetcher);
|
||||
case DiskInterface::Pravetz: return new ConcreteMachine<DiskInterface::Pravetz>(*oric_target, rom_fetcher);
|
||||
case DiskInterface::Jasmin: return new ConcreteMachine<DiskInterface::Jasmin>(*oric_target, rom_fetcher);
|
||||
case DiskInterface::BD500: return new ConcreteMachine<DiskInterface::BD500>(*oric_target, rom_fetcher);
|
||||
|
||||
#define DiskInterfaceSwitch(processor) \
|
||||
switch(oric_target->disk_interface) { \
|
||||
default: return new ConcreteMachine<DiskInterface::None, processor>(*oric_target, rom_fetcher); \
|
||||
case DiskInterface::Microdisc: return new ConcreteMachine<DiskInterface::Microdisc, processor>(*oric_target, rom_fetcher); \
|
||||
case DiskInterface::Pravetz: return new ConcreteMachine<DiskInterface::Pravetz, processor>(*oric_target, rom_fetcher); \
|
||||
case DiskInterface::Jasmin: return new ConcreteMachine<DiskInterface::Jasmin, processor>(*oric_target, rom_fetcher); \
|
||||
case DiskInterface::BD500: return new ConcreteMachine<DiskInterface::BD500, processor>(*oric_target, rom_fetcher); \
|
||||
}
|
||||
|
||||
switch(oric_target->processor) {
|
||||
case Processor::WDC65816: DiskInterfaceSwitch(CPU::MOS6502Esque::Type::TWDC65816);
|
||||
case Processor::MOS6502: DiskInterfaceSwitch(CPU::MOS6502Esque::Type::T6502);
|
||||
}
|
||||
|
||||
#undef DiskInterfaceSwitch
|
||||
|
||||
}
|
||||
|
||||
Machine::~Machine() {}
|
||||
|
@ -210,6 +210,12 @@
|
||||
4B4B1A3D200198CA00A0F866 /* KonamiSCC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4B1A3A200198C900A0F866 /* KonamiSCC.cpp */; };
|
||||
4B4DC8211D2C2425003C5BF8 /* Vic20.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DC81F1D2C2425003C5BF8 /* Vic20.cpp */; };
|
||||
4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DC8291D2C27A4003C5BF8 /* SerialBus.cpp */; };
|
||||
4B4DEC06252BFA56004583AC /* 65816Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DEC05252BFA56004583AC /* 65816Base.cpp */; };
|
||||
4B4DEC07252BFA56004583AC /* 65816Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DEC05252BFA56004583AC /* 65816Base.cpp */; };
|
||||
4B4DEC08252BFA56004583AC /* 65816Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DEC05252BFA56004583AC /* 65816Base.cpp */; };
|
||||
4B4F47652533EA64004245B8 /* suite-a.prg in Resources */ = {isa = PBXBuildFile; fileRef = 4B4F475E2533EA64004245B8 /* suite-a.prg */; };
|
||||
4B4F477C253530B7004245B8 /* Jeek816Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4F477B253530B7004245B8 /* Jeek816Tests.swift */; };
|
||||
4B4F478A25367EDC004245B8 /* 65816AddressingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4F478925367EDC004245B8 /* 65816AddressingTests.swift */; };
|
||||
4B50AF80242817F40099BBD7 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B50AF7F242817F40099BBD7 /* QuartzCore.framework */; };
|
||||
4B54C0BC1F8D8E790050900F /* KeyboardMachine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B54C0BB1F8D8E790050900F /* KeyboardMachine.cpp */; };
|
||||
4B54C0BF1F8D8F450050900F /* Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B54C0BD1F8D8F450050900F /* Keyboard.cpp */; };
|
||||
@ -861,6 +867,8 @@
|
||||
4BF437EE209D0F7E008CBD6B /* SegmentParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BF437EC209D0F7E008CBD6B /* SegmentParser.cpp */; };
|
||||
4BF437EF209D0F7E008CBD6B /* SegmentParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BF437EC209D0F7E008CBD6B /* SegmentParser.cpp */; };
|
||||
4BF8D4C82516E27A00BBE21B /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BB8617024E22F4900A00E03 /* Accelerate.framework */; };
|
||||
4BF8D4D5251C11DD00BBE21B /* 65816Storage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BF8D4D4251C11DD00BBE21B /* 65816Storage.cpp */; };
|
||||
4BF8D4D6251C11DD00BBE21B /* 65816Storage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BF8D4D4251C11DD00BBE21B /* 65816Storage.cpp */; };
|
||||
4BFCA1241ECBDCB400AC40C1 /* AllRAMProcessor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BFCA1211ECBDCAF00AC40C1 /* AllRAMProcessor.cpp */; };
|
||||
4BFCA1271ECBE33200AC40C1 /* TestMachineZ80.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BFCA1261ECBE33200AC40C1 /* TestMachineZ80.mm */; };
|
||||
4BFCA1291ECBE7A700AC40C1 /* zexall.com in Resources */ = {isa = PBXBuildFile; fileRef = 4BFCA1281ECBE7A700AC40C1 /* zexall.com */; };
|
||||
@ -1117,7 +1125,15 @@
|
||||
4B4DC8271D2C2470003C5BF8 /* C1540.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = C1540.hpp; sourceTree = "<group>"; };
|
||||
4B4DC8291D2C27A4003C5BF8 /* SerialBus.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SerialBus.cpp; sourceTree = "<group>"; };
|
||||
4B4DC82A1D2C27A4003C5BF8 /* SerialBus.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SerialBus.hpp; sourceTree = "<group>"; };
|
||||
4B4DEC04252BFA56004583AC /* 65816Implementation.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 65816Implementation.hpp; sourceTree = "<group>"; };
|
||||
4B4DEC05252BFA56004583AC /* 65816Base.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = 65816Base.cpp; sourceTree = "<group>"; };
|
||||
4B4DEC16252BFA9C004583AC /* 6502Selector.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 6502Selector.hpp; sourceTree = "<group>"; };
|
||||
4B4DEC18252BFA9C004583AC /* 6502Esque.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 6502Esque.hpp; sourceTree = "<group>"; };
|
||||
4B4DEC19252BFB5A004583AC /* LazyFlags.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = LazyFlags.hpp; sourceTree = "<group>"; };
|
||||
4B4F2B7024DF99D4000DA6B0 /* CSScanTarget+CppScanTarget.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CSScanTarget+CppScanTarget.h"; sourceTree = "<group>"; };
|
||||
4B4F475E2533EA64004245B8 /* suite-a.prg */ = {isa = PBXFileReference; lastKnownFileType = file; path = "suite-a.prg"; sourceTree = "<group>"; };
|
||||
4B4F477B253530B7004245B8 /* Jeek816Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Jeek816Tests.swift; sourceTree = "<group>"; };
|
||||
4B4F478925367EDC004245B8 /* 65816AddressingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 65816AddressingTests.swift; sourceTree = "<group>"; };
|
||||
4B50AF7F242817F40099BBD7 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
|
||||
4B51F70920A521D700AFA2C1 /* Source.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Source.hpp; sourceTree = "<group>"; };
|
||||
4B51F70A20A521D700AFA2C1 /* Observer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Observer.hpp; sourceTree = "<group>"; };
|
||||
@ -1788,6 +1804,9 @@
|
||||
4BF4A2D91F534DB300B171F4 /* TargetPlatforms.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = TargetPlatforms.hpp; sourceTree = "<group>"; };
|
||||
4BF52672218E752E00313227 /* ScanTarget.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ScanTarget.hpp; path = ../../Outputs/ScanTarget.hpp; sourceTree = "<group>"; };
|
||||
4BF6606A1F281573002CB053 /* ClockReceiver.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ClockReceiver.hpp; sourceTree = "<group>"; };
|
||||
4BF8D4CD251C0C9C00BBE21B /* 65816.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 65816.hpp; sourceTree = "<group>"; };
|
||||
4BF8D4D3251C0D9F00BBE21B /* 65816Storage.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 65816Storage.hpp; sourceTree = "<group>"; };
|
||||
4BF8D4D4251C11DD00BBE21B /* 65816Storage.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = 65816Storage.cpp; sourceTree = "<group>"; };
|
||||
4BFCA1211ECBDCAF00AC40C1 /* AllRAMProcessor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AllRAMProcessor.cpp; sourceTree = "<group>"; };
|
||||
4BFCA1221ECBDCAF00AC40C1 /* AllRAMProcessor.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = AllRAMProcessor.hpp; sourceTree = "<group>"; };
|
||||
4BFCA1251ECBE33200AC40C1 /* TestMachineZ80.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestMachineZ80.h; sourceTree = "<group>"; };
|
||||
@ -1980,6 +1999,7 @@
|
||||
4B9F11CB22729B3500701480 /* OPCLOGR2.BIN */,
|
||||
4B2530F2244E6773007980BF /* FM Synthesis */,
|
||||
4BBF49B41ED2881600AB3669 /* FUSE */,
|
||||
4B4F475B2533EA64004245B8 /* jeek816 */,
|
||||
4B670A822401CB8400D4E002 /* Patrik Rak Z80 Tests */,
|
||||
4B9F11C72272375400701480 /* QL Startup */,
|
||||
4B85322B227793CA00F26553 /* TOS Startup */,
|
||||
@ -2451,6 +2471,32 @@
|
||||
path = 1540;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4B4DEC15252BFA9C004583AC /* 6502Esque */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4B4DEC18252BFA9C004583AC /* 6502Esque.hpp */,
|
||||
4B4DEC16252BFA9C004583AC /* 6502Selector.hpp */,
|
||||
4B4DEC17252BFA9C004583AC /* Implementation */,
|
||||
);
|
||||
path = 6502Esque;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4B4DEC17252BFA9C004583AC /* Implementation */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4B4DEC19252BFB5A004583AC /* LazyFlags.hpp */,
|
||||
);
|
||||
path = Implementation;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4B4F475B2533EA64004245B8 /* jeek816 */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4B4F475E2533EA64004245B8 /* suite-a.prg */,
|
||||
);
|
||||
path = jeek816;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4B51F70820A521D700AFA2C1 /* Activity */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -3391,7 +3437,6 @@
|
||||
4BB73EB51B587A5100552FC2 /* Clock SignalTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4BC0CB272446BC7B00A79DBB /* OPLTests.mm */,
|
||||
4B85322922778E4200F26553 /* Comparative68000.hpp */,
|
||||
4B90467222C6FA31000E2074 /* TestRunner68000.hpp */,
|
||||
4B97ADC722C6FD9B00A22A41 /* 68000ArithmeticTests.mm */,
|
||||
@ -3410,6 +3455,7 @@
|
||||
4BE90FFC22D5864800FB464D /* MacintoshVideoTests.mm */,
|
||||
4BA91E1C216D85BA00F79557 /* MasterSystemVDPTests.mm */,
|
||||
4B98A0601FFADCDE00ADF63B /* MSXStaticAnalyserTests.mm */,
|
||||
4BC0CB272446BC7B00A79DBB /* OPLTests.mm */,
|
||||
4B121F9A1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm */,
|
||||
4BD4A8CF1E077FD20020D856 /* PCMTrackTests.mm */,
|
||||
4BE76CF822641ED300ACD6FA /* QLTests.mm */,
|
||||
@ -3420,11 +3466,13 @@
|
||||
4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */,
|
||||
4BC751B11D157E61006C31D9 /* 6522Tests.swift */,
|
||||
4B1E85801D176468001EF87D /* 6532Tests.swift */,
|
||||
4B4F478925367EDC004245B8 /* 65816AddressingTests.swift */,
|
||||
4BB73EB61B587A5100552FC2 /* AllSuiteATests.swift */,
|
||||
4B049CDC1DA3C82F00322067 /* BCDTest.swift */,
|
||||
4B3BA0C21D318AEB005DD7A7 /* C1540Tests.swift */,
|
||||
4BEF6AAB1D35D1C400E73575 /* DPLLTests.swift */,
|
||||
4BBF49AE1ED2880200AB3669 /* FUSETests.swift */,
|
||||
4B4F477B253530B7004245B8 /* Jeek816Tests.swift */,
|
||||
4B1414611B58888700E04248 /* KlausDormannTests.swift */,
|
||||
4BD91D762401C2B8007BDC91 /* PatrikRakTests.swift */,
|
||||
4B14145F1B58885000E04248 /* WolfgangLorenzTests.swift */,
|
||||
@ -3485,6 +3533,8 @@
|
||||
4BFCA1221ECBDCAF00AC40C1 /* AllRAMProcessor.hpp */,
|
||||
4B2C455C1EC9442600FC74DD /* RegisterSizes.hpp */,
|
||||
4B1414561B58879D00E04248 /* 6502 */,
|
||||
4B4DEC15252BFA9C004583AC /* 6502Esque */,
|
||||
4BF8D4CC251C0C9C00BBE21B /* 65816 */,
|
||||
4BFF1D332233778C00838EA1 /* 68000 */,
|
||||
4B77069E1EC9045B0053B588 /* Z80 */,
|
||||
);
|
||||
@ -3892,6 +3942,26 @@
|
||||
path = ../../ClockReceiver;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4BF8D4CC251C0C9C00BBE21B /* 65816 */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4BF8D4CD251C0C9C00BBE21B /* 65816.hpp */,
|
||||
4BF8D4D2251C0D9F00BBE21B /* Implementation */,
|
||||
);
|
||||
path = 65816;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4BF8D4D2251C0D9F00BBE21B /* Implementation */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4B4DEC05252BFA56004583AC /* 65816Base.cpp */,
|
||||
4BF8D4D4251C11DD00BBE21B /* 65816Storage.cpp */,
|
||||
4B4DEC04252BFA56004583AC /* 65816Implementation.hpp */,
|
||||
4BF8D4D3251C0D9F00BBE21B /* 65816Storage.hpp */,
|
||||
);
|
||||
path = Implementation;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4BFDD7891F7F2DB4008579B9 /* Utility */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -4001,7 +4071,7 @@
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 0700;
|
||||
LastUpgradeCheck = 1130;
|
||||
LastUpgradeCheck = 1200;
|
||||
ORGANIZATIONNAME = "Thomas Harte";
|
||||
TargetAttributes = {
|
||||
4B055A691FAE763F0060FFFF = {
|
||||
@ -4239,6 +4309,7 @@
|
||||
4BB299051B587D8400A49093 /* arrb in Resources */,
|
||||
4BB299DC1B587D8400A49093 /* stazx in Resources */,
|
||||
4B670A9D2401CB8400D4E002 /* z80ccf.tap in Resources */,
|
||||
4B4F47652533EA64004245B8 /* suite-a.prg in Resources */,
|
||||
4BB299C41B587D8400A49093 /* sbca in Resources */,
|
||||
4BB298F41B587D8400A49093 /* adcay in Resources */,
|
||||
4B44EBF51DC987AF00A7820C /* AllSuiteA.bin in Resources */,
|
||||
@ -4468,6 +4539,7 @@
|
||||
4BDACBED22FFA5D20045EF7E /* ncr5380.cpp in Sources */,
|
||||
4BC131772346DE9100E4FF3D /* StaticAnalyser.cpp in Sources */,
|
||||
4B055ACF1FAE9B030060FFFF /* SoundGenerator.cpp in Sources */,
|
||||
4B4DEC08252BFA56004583AC /* 65816Base.cpp in Sources */,
|
||||
4B894519201967B4007DE474 /* ConfidenceCounter.cpp in Sources */,
|
||||
4B055AEE1FAE9BBF0060FFFF /* Keyboard.cpp in Sources */,
|
||||
4B055AED1FAE9BA20060FFFF /* Z80Storage.cpp in Sources */,
|
||||
@ -4489,6 +4561,7 @@
|
||||
4B9BE401203A0C0600FFAE60 /* MultiSpeaker.cpp in Sources */,
|
||||
4B055AA61FAE85EF0060FFFF /* Parser.cpp in Sources */,
|
||||
4B055AE91FAE9B990060FFFF /* 6502Base.cpp in Sources */,
|
||||
4BF8D4D6251C11DD00BBE21B /* 65816Storage.cpp in Sources */,
|
||||
4B055AEF1FAE9BF00060FFFF /* Typer.cpp in Sources */,
|
||||
4B89453F201967B4007DE474 /* StaticAnalyser.cpp in Sources */,
|
||||
4B89453D201967B4007DE474 /* StaticAnalyser.cpp in Sources */,
|
||||
@ -4749,6 +4822,7 @@
|
||||
4BEBFB4D2002C4BF000708CC /* MSXDSK.cpp in Sources */,
|
||||
4BBFBB6C1EE8401E00C01E7A /* ZX8081.cpp in Sources */,
|
||||
4B83348A1F5DB94B0097E338 /* IRQDelegatePortHandler.cpp in Sources */,
|
||||
4B4DEC06252BFA56004583AC /* 65816Base.cpp in Sources */,
|
||||
4B894524201967B4007DE474 /* Tape.cpp in Sources */,
|
||||
4B7136891F78725F008B8ED9 /* Shifter.cpp in Sources */,
|
||||
4BDB61EB2032806E0048AF91 /* CSAtari2600.mm in Sources */,
|
||||
@ -4771,6 +4845,7 @@
|
||||
4B55DD8320DF06680043F2E5 /* MachinePicker.swift in Sources */,
|
||||
4B2A539F1D117D36003C6002 /* CSAudioQueue.m in Sources */,
|
||||
4B89453E201967B4007DE474 /* StaticAnalyser.cpp in Sources */,
|
||||
4BF8D4D5251C11DD00BBE21B /* 65816Storage.cpp in Sources */,
|
||||
4B0ACC2823775819008902D0 /* DMAController.cpp in Sources */,
|
||||
4BC131702346DE5000E4FF3D /* StaticAnalyser.cpp in Sources */,
|
||||
4B37EE821D7345A6006A09A4 /* BinaryDump.cpp in Sources */,
|
||||
@ -4839,6 +4914,7 @@
|
||||
4B778F4D23A5F20F0000D260 /* StaticAnalyser.cpp in Sources */,
|
||||
4B778F0423A5EBB00000D260 /* OricMFMDSK.cpp in Sources */,
|
||||
4B3BA0CE1D318B44005DD7A7 /* C1540Bridge.mm in Sources */,
|
||||
4B4F477C253530B7004245B8 /* Jeek816Tests.swift in Sources */,
|
||||
4B778F0F23A5EC560000D260 /* PCMTrack.cpp in Sources */,
|
||||
4B778F1123A5EC650000D260 /* FileHolder.cpp in Sources */,
|
||||
4B778EFC23A5EB8B0000D260 /* AcornADF.cpp in Sources */,
|
||||
@ -4895,6 +4971,7 @@
|
||||
4B778F4123A5F19A0000D260 /* MemoryPacker.cpp in Sources */,
|
||||
4B778F4423A5F1BE0000D260 /* CommodoreGCR.cpp in Sources */,
|
||||
4B778EF923A5EB740000D260 /* MSA.cpp in Sources */,
|
||||
4B4DEC07252BFA56004583AC /* 65816Base.cpp in Sources */,
|
||||
4B778F2323A5EDE40000D260 /* Tape.cpp in Sources */,
|
||||
4B778F4F23A5F21C0000D260 /* StaticAnalyser.cpp in Sources */,
|
||||
4B778EEF23A5D6680000D260 /* AsyncTaskQueue.cpp in Sources */,
|
||||
@ -4935,6 +5012,7 @@
|
||||
4B778F5723A5F2BB0000D260 /* ZX8081.cpp in Sources */,
|
||||
4B778F2F23A5F0B10000D260 /* ScanTarget.cpp in Sources */,
|
||||
4BE90FFD22D5864800FB464D /* MacintoshVideoTests.mm in Sources */,
|
||||
4B4F478A25367EDC004245B8 /* 65816AddressingTests.swift in Sources */,
|
||||
4B778F0B23A5EC150000D260 /* TapeUEF.cpp in Sources */,
|
||||
4B778F0523A5EBB00000D260 /* ST.cpp in Sources */,
|
||||
4B778F0C23A5EC150000D260 /* TZX.cpp in Sources */,
|
||||
@ -5144,6 +5222,7 @@
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
@ -5202,6 +5281,7 @@
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1130"
|
||||
LastUpgradeVersion = "1200"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1130"
|
||||
LastUpgradeVersion = "1200"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@ -48,11 +48,6 @@
|
||||
BlueprintName = "Clock SignalTests"
|
||||
ReferencedContainer = "container:Clock Signal.xcodeproj">
|
||||
</BuildableReference>
|
||||
<SkippedTests>
|
||||
<Test
|
||||
Identifier = "ZexallTests">
|
||||
</Test>
|
||||
</SkippedTests>
|
||||
</TestableReference>
|
||||
<TestableReference
|
||||
skipped = "YES">
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1130"
|
||||
LastUpgradeVersion = "1200"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@ -51,15 +51,6 @@
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4BB73E9D1B587A5100552FC2"
|
||||
BuildableName = "Clock Signal.app"
|
||||
BlueprintName = "Clock Signal"
|
||||
ReferencedContainer = "container:Clock Signal.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
|
@ -14,38 +14,38 @@ class MOS6502InterruptTests: XCTestCase {
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
|
||||
// create a machine full of NOPs
|
||||
machine = CSTestMachine6502(is65C02: false)
|
||||
// Create a machine full of NOPs.
|
||||
machine = CSTestMachine6502(processor: .processor6502)
|
||||
for c in 0...65535 {
|
||||
machine.setValue(0xea, forAddress: UInt16(c))
|
||||
machine.setValue(0xea, forAddress: UInt32(c))
|
||||
}
|
||||
|
||||
// set the IRQ vector to be 0x1234
|
||||
// Set the IRQ vector to 0x1234.
|
||||
machine.setValue(0x34, forAddress: 0xfffe)
|
||||
machine.setValue(0x12, forAddress: 0xffff)
|
||||
|
||||
// add a CLI
|
||||
// Add a CLI.
|
||||
machine.setValue(0x58, forAddress: 0x4000)
|
||||
|
||||
// pick things off at 0x4000
|
||||
machine.setValue(0x4000, for: CSTestMachine6502Register.programCounter)
|
||||
// Begin at 0x4000.
|
||||
machine.setValue(0x4000, for: .programCounter)
|
||||
}
|
||||
|
||||
func testIRQLine() {
|
||||
// run for six cycles; check that no interrupt has occurred
|
||||
// Run for six cycles; check that no interrupt has occurred.
|
||||
machine.runForNumber(ofCycles: 6)
|
||||
XCTAssert(machine.value(for: .programCounter) == 0x4003, "No interrupt should have occurred with line low")
|
||||
XCTAssertEqual(machine.value(for: .programCounter), 0x4003, "No interrupt should have occurred with line low")
|
||||
|
||||
// enable the interrupt line, check that it was too late
|
||||
machine.irqLine = true
|
||||
machine.runForNumber(ofCycles: 2)
|
||||
XCTAssert(machine.value(for: .programCounter) == 0x4004, "No interrupt should have occurred from interrupt raised between instructions")
|
||||
XCTAssertEqual(machine.value(for: .programCounter), 0x4004, "No interrupt should have occurred from interrupt raised between instructions")
|
||||
|
||||
// run for a further 7 cycles, confirm that the IRQ vector was jumped to
|
||||
machine.runForNumber(ofCycles: 6)
|
||||
XCTAssert(machine.value(for: .programCounter) != 0x1234, "Interrupt routine should not yet have begun")
|
||||
XCTAssertNotEqual(machine.value(for: .programCounter), 0x1234, "Interrupt routine should not yet have begun")
|
||||
machine.runForNumber(ofCycles: 1)
|
||||
XCTAssert(machine.value(for: .programCounter) == 0x1234, "Interrupt routine should just have begun")
|
||||
XCTAssertEqual(machine.value(for: .programCounter), 0x1234, "Interrupt routine should just have begun")
|
||||
}
|
||||
|
||||
func testIFlagSet() {
|
||||
@ -53,8 +53,8 @@ class MOS6502InterruptTests: XCTestCase {
|
||||
machine.irqLine = true
|
||||
machine.runForNumber(ofCycles: 11)
|
||||
|
||||
XCTAssert(machine.value(for: .programCounter) == 0x1234, "Interrupt routine should just have begun")
|
||||
XCTAssert(machine.value(for: .flags) & 0x04 == 0x04, "Interrupt status flag should be set")
|
||||
XCTAssertEqual(machine.value(for: .programCounter), 0x1234, "Interrupt routine should just have begun")
|
||||
XCTAssertEqual(machine.value(for: .flags) & 0x04, 0x04, "Interrupt status flag should be set")
|
||||
}
|
||||
|
||||
func testCLISEIFlagClear() {
|
||||
@ -64,12 +64,13 @@ class MOS6502InterruptTests: XCTestCase {
|
||||
|
||||
// run for four cycles; the CLI and SEI should have been performed
|
||||
machine.runForNumber(ofCycles: 4)
|
||||
XCTAssert(machine.value(for: .programCounter) == 0x4002, "CLI/SEI pair should have been performed in their entirety")
|
||||
XCTAssertEqual(machine.value(for: .programCounter), 0x4002, "CLI/SEI pair should have been performed in their entirety")
|
||||
|
||||
// run for seven more cycles
|
||||
machine.runForNumber(ofCycles: 7)
|
||||
|
||||
// interrupt should have taken place despite SEI
|
||||
XCTAssert(machine.value(for: .programCounter) == 0x1234, "Interrupt routine should just have begun")
|
||||
XCTAssertEqual(machine.value(for: .programCounter), 0x1234, "Interrupt routine should just have begun")
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ import XCTest
|
||||
class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
|
||||
private var endTime: UInt32 = 0
|
||||
private let machine = CSTestMachine6502(is65C02: false)
|
||||
private let machine = CSTestMachine6502(processor: .processor6502)
|
||||
|
||||
func testImplied() {
|
||||
let code: [UInt8] = [
|
||||
@ -22,7 +22,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
0x18, // [2] CLC
|
||||
0x2a, // [2] ROL A
|
||||
]
|
||||
self.runTest(code, expectedRunLength: 10)
|
||||
runTest(code, expectedRunLength: 10)
|
||||
}
|
||||
|
||||
func testLDA() {
|
||||
@ -40,7 +40,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
0xb1, 0x00, // [5] LDA ($00), y (no wrap)
|
||||
0xb1, 0x02, // [6] LDA ($01), y (wrap)
|
||||
]
|
||||
self.runTest(code, expectedRunLength: 48)
|
||||
runTest(code, expectedRunLength: 48)
|
||||
}
|
||||
|
||||
func testBIT() {
|
||||
@ -48,7 +48,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
0x24, 0x2a, // [3] BIT $2a
|
||||
0x2c, 0x2a, 0x2b, // [4] BIT $2b2a
|
||||
]
|
||||
self.runTest(code, expectedRunLength: 7)
|
||||
runTest(code, expectedRunLength: 7)
|
||||
}
|
||||
|
||||
func testSTA() {
|
||||
@ -64,7 +64,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
0x91, 0x00, // [6] STA ($00), y (no wrap)
|
||||
0x91, 0x02, // [6] STA ($01), y (wrap)
|
||||
]
|
||||
self.runTest(code, expectedRunLength: 49)
|
||||
runTest(code, expectedRunLength: 49)
|
||||
}
|
||||
|
||||
func testINC() {
|
||||
@ -75,7 +75,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
0xfe, 0x00, 0x00, // [7] INC $0000, x (no wrap)
|
||||
0xfe, 0x02, 0x00, // [7] INC $0002, x (wrap)
|
||||
]
|
||||
self.runTest(code, expectedRunLength: 31)
|
||||
runTest(code, expectedRunLength: 31)
|
||||
}
|
||||
|
||||
func testJSR() {
|
||||
@ -85,7 +85,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
0x60, // [6] RTS
|
||||
]
|
||||
machine.addTrapAddress(0x0203)
|
||||
self.runTest(code, expectedRunLength: 12)
|
||||
runTest(code, expectedRunLength: 12)
|
||||
}
|
||||
|
||||
func testJMP() {
|
||||
@ -94,7 +94,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x4c, 0x0b, 0x02, // [3] JMP 020b
|
||||
]
|
||||
self.runTest(code, expectedRunLength: 8)
|
||||
runTest(code, expectedRunLength: 8)
|
||||
}
|
||||
|
||||
func testPHAPLA() {
|
||||
@ -103,7 +103,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
0x48, // [3] PHA
|
||||
0x68, // [4] PLA
|
||||
]
|
||||
self.runTest(code, expectedRunLength: 10)
|
||||
runTest(code, expectedRunLength: 10)
|
||||
}
|
||||
|
||||
func testBCS() {
|
||||
@ -130,7 +130,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
]
|
||||
self.runTest(code, expectedRunLength: 14)
|
||||
runTest(code, expectedRunLength: 14)
|
||||
}
|
||||
|
||||
func testSnippet1() {
|
||||
@ -138,7 +138,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
0x8d, 0x08, 0x00, // [4] STA $0008
|
||||
0xc6, 0xb4, // [5] DEC $B4
|
||||
]
|
||||
self.runTest(code, expectedRunLength: 9)
|
||||
runTest(code, expectedRunLength: 9)
|
||||
}
|
||||
|
||||
func testSnippet2() {
|
||||
@ -146,7 +146,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
0x16, 0x16, // [6] ASL $16, x
|
||||
0x46, 0x46, // [5] LSR $46
|
||||
]
|
||||
self.runTest(code, expectedRunLength: 11)
|
||||
runTest(code, expectedRunLength: 11)
|
||||
}
|
||||
|
||||
func testSnippet3() {
|
||||
@ -174,7 +174,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
0x60, // [6] RTS
|
||||
]
|
||||
machine.addTrapAddress(0x0203)
|
||||
self.runTest(code, expectedRunLength: 66)
|
||||
runTest(code, expectedRunLength: 66)
|
||||
}
|
||||
|
||||
func testNOP() {
|
||||
@ -194,10 +194,10 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
0xe2, 0x00, // [2] NOP #
|
||||
0xf4, 0x00, // [4] NOP zpg, x
|
||||
]
|
||||
self.runTest(code, expectedRunLength: 43)
|
||||
runTest(code, expectedRunLength: 43)
|
||||
}
|
||||
|
||||
func runTest(_ code: [UInt8], expectedRunLength: UInt32) {
|
||||
private func runTest(_ code: [UInt8], expectedRunLength: UInt32) {
|
||||
machine.trapHandler = self
|
||||
|
||||
let immediateCode = Data(code)
|
||||
@ -213,17 +213,17 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
machine.setValue(0xff, for: CSTestMachine6502Register.X)
|
||||
machine.setValue(0xfe, for: CSTestMachine6502Register.Y)
|
||||
|
||||
self.endTime = 0
|
||||
while self.endTime == 0 {
|
||||
endTime = 0
|
||||
while endTime == 0 {
|
||||
machine.runForNumber(ofCycles: 10)
|
||||
}
|
||||
|
||||
XCTAssert(self.endTime == expectedRunLength, "Took \(self.endTime) cycles to perform rather than \(expectedRunLength)")
|
||||
XCTAssertEqual(endTime, expectedRunLength, "Took \(endTime) cycles to perform rather than \(expectedRunLength)")
|
||||
}
|
||||
|
||||
func testMachine(_ testMachine: CSTestMachine, didTrapAtAddress address: UInt16) {
|
||||
if self.endTime == 0 {
|
||||
self.endTime = (machine.timestamp / 2) - 1
|
||||
if endTime == 0 {
|
||||
endTime = (machine.timestamp / 2) - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ import Foundation
|
||||
|
||||
class MOS6532Tests: XCTestCase {
|
||||
|
||||
fileprivate func with6532(_ action: (MOS6532Bridge) -> ()) {
|
||||
private func with6532(_ action: (MOS6532Bridge) -> ()) {
|
||||
let bridge = MOS6532Bridge()
|
||||
action(bridge)
|
||||
}
|
||||
|
546
OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift
Normal file
546
OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift
Normal file
@ -0,0 +1,546 @@
|
||||
//
|
||||
// WDC65816AddressingTests.swift
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 13/10/2020.
|
||||
// Copyright 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import XCTest
|
||||
|
||||
// This exactly transcribes the examples given in http://6502.org/tutorials/65c816opcodes.html#5
|
||||
// Quoted text is taken verbatim from that document.
|
||||
class WDC65816AddressingTests: XCTestCase {
|
||||
|
||||
// MARK: - Test Machines
|
||||
|
||||
/// Returns a CSTestMachine6502 that is currently configured in native mode with 16-bit memory and index registers.
|
||||
private func machine16() -> CSTestMachine6502 {
|
||||
let machine = CSTestMachine6502(processor: .processor65816)
|
||||
machine.setValue(0, for: .emulationFlag)
|
||||
machine.setValue(0, for: .flags)
|
||||
return machine
|
||||
}
|
||||
|
||||
/// Returns a CSTestMachine6502 that is currently configured in emulation mode.
|
||||
private func machine8() -> CSTestMachine6502 {
|
||||
return CSTestMachine6502(processor: .processor65816)
|
||||
}
|
||||
|
||||
// MARK: - Tests
|
||||
|
||||
func testAbsoluteJMP() {
|
||||
// "If the K register is $12, then JMP $FFFF jumps to $12FFFF"
|
||||
let machine = machine16()
|
||||
|
||||
machine.setValue(0x12, for: .programBank)
|
||||
|
||||
// JMP $ffff
|
||||
machine.setData(Data([0x4c, 0xff, 0xff]), atAddress: 0x120200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 4)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .programCounter), 0) // i.e. 0xffff + 1
|
||||
XCTAssertEqual(machine.value(for: .programBank), 0x12)
|
||||
}
|
||||
|
||||
func testAbsolute() {
|
||||
// "If the DBR is $12 and the m flag is 0, then LDA $FFFF loads the low byte of
|
||||
// the data from address $12FFFF, and the high byte from address $130000"
|
||||
|
||||
let machine = machine16()
|
||||
|
||||
machine.setValue(0x12, for: .dataBank)
|
||||
|
||||
machine.setValue(0xab, forAddress: 0x12ffff)
|
||||
machine.setValue(0xcd, forAddress: 0x130000)
|
||||
|
||||
// LDA $ffff; NOP
|
||||
machine.setData(Data([0xad, 0xff, 0xff, 0xea]), atAddress: 0x200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 6)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .A), 0xcdab)
|
||||
}
|
||||
|
||||
func testAbsoluteX() {
|
||||
// "If the DBR is $12, the X register is $000A, and the m flag is 0, then
|
||||
// LDA $FFFE,X loads the low byte of the data from address $130008, and
|
||||
// the high byte from address $130009"
|
||||
//
|
||||
// "Note that this is one of the rare instances where emulation mode has
|
||||
// different behavior than the 65C02 or NMOS 6502 ..."
|
||||
|
||||
let machine = machine16()
|
||||
|
||||
machine.setValue(0x12, for: .dataBank)
|
||||
machine.setValue(0x0a, for: .X)
|
||||
|
||||
machine.setValue(0xab, forAddress: 0x130008)
|
||||
machine.setValue(0xcd, forAddress: 0x130009)
|
||||
|
||||
// LDA $fffe, x; NOP
|
||||
machine.setData(Data([0xbd, 0xfe, 0xff, 0xea]), atAddress: 0x200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 7)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .A), 0xcdab)
|
||||
}
|
||||
|
||||
func testJMPAbsoluteIndirect() {
|
||||
// "If the K register is $12 and
|
||||
// * $000000 contains $34
|
||||
// * $00FFFF contains $56
|
||||
// then JMP ($FFFF) jumps to $123456"
|
||||
|
||||
let machine = machine16()
|
||||
|
||||
machine.setValue(0x12, for: .programBank)
|
||||
machine.setValue(0x0a, for: .X)
|
||||
|
||||
machine.setValue(0x34, forAddress: 0x0000)
|
||||
machine.setValue(0x56, forAddress: 0xffff)
|
||||
|
||||
// JMP ($ffff); NOP
|
||||
machine.setData(Data([0x6c, 0xff, 0xff, 0xea]), atAddress: 0x120200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 6)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .programCounter), 0x3456 + 1)
|
||||
XCTAssertEqual(machine.value(for: .programBank), 0x12)
|
||||
}
|
||||
|
||||
func testIndirectAbsoluteX() {
|
||||
// "If the K register is $12, the X register is $000A, and
|
||||
// * $120008 contains $56
|
||||
// * $120009 contains $34
|
||||
//then JMP ($FFFE,X) jumps to $123456"
|
||||
|
||||
let machine = machine16()
|
||||
|
||||
machine.setValue(0x12, for: .programBank)
|
||||
machine.setValue(0x0a, for: .X)
|
||||
machine.setValue(0x56, forAddress: 0x120008)
|
||||
machine.setValue(0x34, forAddress: 0x120009)
|
||||
|
||||
// JMP ($fffe, x); NOP
|
||||
machine.setData(Data([0x7c, 0xfe, 0xff, 0xea]), atAddress: 0x120200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 7)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .programCounter), 0x3456 + 1)
|
||||
XCTAssertEqual(machine.value(for: .programBank), 0x12)
|
||||
}
|
||||
|
||||
func testDirect8() {
|
||||
// "If the D register is $FF00 and the e flag is 1 (note that
|
||||
// this means the m flag must be 1), then LDA $FF loads the low
|
||||
// byte of the data from address $00FFFF"
|
||||
|
||||
let machine = machine8()
|
||||
|
||||
machine.setValue(0xff00, for: .direct)
|
||||
|
||||
machine.setValue(0xab, forAddress: 0xffff)
|
||||
|
||||
// LDA $ff; NOP
|
||||
machine.setData(Data([0xa5, 0xff, 0xea]), atAddress: 0x0200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 4)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .A), 0xab)
|
||||
}
|
||||
|
||||
func testDirect16() {
|
||||
// "If the D register is $FF00 and the m flag is 0 (note that
|
||||
// this means the e flag must be 0), then LDA $FF loads the low
|
||||
// byte of the data from address $00FFFF, and the high byte
|
||||
// from address $000000"
|
||||
|
||||
let machine = machine16()
|
||||
|
||||
machine.setValue(0xff00, for: .direct)
|
||||
|
||||
machine.setValue(0xab, forAddress: 0xffff)
|
||||
machine.setValue(0xcd, forAddress: 0x0000)
|
||||
|
||||
// LDA $ff; NOP
|
||||
machine.setData(Data([0xa5, 0xff, 0xea]), atAddress: 0x0200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 5)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .A), 0xcdab)
|
||||
}
|
||||
|
||||
func testDirextX8() {
|
||||
// "If the D register is $FF00, the X register is $000A, and
|
||||
// the e flag is 1 (note that this means the m flag must be 1),
|
||||
// then LDA $FE,X loads the low byte of the data from
|
||||
// address $00FF08"
|
||||
|
||||
let machine = machine8()
|
||||
|
||||
machine.setValue(0xff00, for: .direct)
|
||||
machine.setValue(0x000a, for: .X)
|
||||
|
||||
machine.setValue(0xab, forAddress: 0xff08)
|
||||
|
||||
// LDA $fe, X; NOP
|
||||
machine.setData(Data([0xb5, 0xfe, 0xea]), atAddress: 0x0200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 5)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .A), 0xab)
|
||||
}
|
||||
|
||||
func testDirectX16() {
|
||||
// "If the D register is $FF00, the X register is $000A, and the
|
||||
// m flag is 0 (note that this means the e flag must be 0), then
|
||||
// LDA $FE,X loads the low byte of the data from address $000008,
|
||||
// and the high byte from address $000009"
|
||||
let machine = machine16()
|
||||
|
||||
machine.setValue(0xff00, for: .direct)
|
||||
machine.setValue(0x000a, for: .X)
|
||||
|
||||
machine.setValue(0xab, forAddress: 0x0008)
|
||||
machine.setValue(0xcd, forAddress: 0x0009)
|
||||
|
||||
// LDA $fe, X; NOP
|
||||
machine.setData(Data([0xb5, 0xfe, 0xea]), atAddress: 0x0200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 6)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .A), 0xcdab)
|
||||
}
|
||||
|
||||
func testDirectIndirect8() {
|
||||
// "If the D register is $FF00 and the e flag is 1 (note this means the
|
||||
// m flag must be 1), then for LDA ($FF), the address of the low byte of
|
||||
// the pointer is $00FFFF and the address of the high byte is $00FF00.
|
||||
// Furthermore, if the DBR is $12 and
|
||||
// * $00FF00 contains $FF
|
||||
// * $00FFFF contains $FF
|
||||
// then LDA ($FF) loads the low byte of the data from address $12FFFF."
|
||||
let machine = machine8()
|
||||
|
||||
machine.setValue(0xff00, for: .direct)
|
||||
machine.setValue(0x12, for: .dataBank)
|
||||
|
||||
machine.setValue(0xff, forAddress: 0xff00)
|
||||
machine.setValue(0xff, forAddress: 0xffff)
|
||||
|
||||
machine.setValue(0xab, forAddress: 0x12ffff)
|
||||
|
||||
// LDA ($ff); NOP
|
||||
machine.setData(Data([0xb2, 0xff, 0xea]), atAddress: 0x0200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 6)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .A), 0xab)
|
||||
}
|
||||
|
||||
func testDirectIndirect16() {
|
||||
// "If the D register is $FF00 and the m flag is 0 (note this means the e
|
||||
// flag must be 0), then for LDA ($FF), the address of the low byte of the
|
||||
// pointer is $00FFFF and the address of the high byte is $000000.
|
||||
// Furthermore, if the DBR is $12 and
|
||||
// * $000000 contains $FF
|
||||
// * $00FFFF contains $FF
|
||||
// then LDA ($FF) loads the low byte of the data from address $12FFFF, and
|
||||
// the high byte from $130000."
|
||||
let machine = machine16()
|
||||
|
||||
machine.setValue(0xff00, for: .direct)
|
||||
machine.setValue(0x12, for: .dataBank)
|
||||
|
||||
machine.setValue(0xff, forAddress: 0x0000)
|
||||
machine.setValue(0xff, forAddress: 0xffff)
|
||||
|
||||
machine.setValue(0xab, forAddress: 0x12ffff)
|
||||
machine.setValue(0xcd, forAddress: 0x130000)
|
||||
|
||||
// LDA ($ff); NOP
|
||||
machine.setData(Data([0xb2, 0xff, 0xea]), atAddress: 0x0200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 7)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .A), 0xcdab)
|
||||
}
|
||||
|
||||
func testDirectIndirectLong() {
|
||||
// "If the D register is $FF00 and the m flag is 0, then for LDA [$FE], the
|
||||
// address of the low byte of the pointer is $00FFFE, the address of the middle
|
||||
// byte is $00FFFF, and the address of the high byte is $000000. Furthermore, if
|
||||
// * $000000 contains $12
|
||||
// * $00FFFE contains $FF
|
||||
// * $00FFFF contains $FF
|
||||
// then LDA [$FE] loads the low byte of the data from address $12FFFF, and the
|
||||
// high byte from $130000."
|
||||
let machine = machine16()
|
||||
|
||||
machine.setValue(0xff00, for: .direct)
|
||||
|
||||
machine.setValue(0x12, forAddress: 0x0000)
|
||||
machine.setValue(0xff, forAddress: 0xfffe)
|
||||
machine.setValue(0xff, forAddress: 0xffff)
|
||||
|
||||
machine.setValue(0xab, forAddress: 0x12ffff)
|
||||
machine.setValue(0xcd, forAddress: 0x130000)
|
||||
|
||||
// LDA [$fe]; NOP
|
||||
machine.setData(Data([0xa7, 0xfe, 0xea]), atAddress: 0x0200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 8)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .A), 0xcdab)
|
||||
}
|
||||
|
||||
func testDirectXIndirectX8() {
|
||||
// "If the D register is $FF00, the X register is $000A, and the e flag is 1 (note
|
||||
// that this means the m flag must be 1), then for LDA ($FE,X), the address of the
|
||||
// low byte of the pointer is $00FF08 and the address of the high byte is $00FF09.
|
||||
// Furthermore, if the DBR is $12 and
|
||||
// * $00FF08 contains $FF
|
||||
// * $00FF09 contains $FF
|
||||
// then LDA ($FE,X) loads the low byte of the data from address $12FFFF."
|
||||
let machine = machine8()
|
||||
|
||||
machine.setValue(0xff00, for: .direct)
|
||||
machine.setValue(0x000a, for: .X)
|
||||
machine.setValue(0x12, for: .dataBank)
|
||||
|
||||
machine.setValue(0xff, forAddress: 0xff08)
|
||||
machine.setValue(0xff, forAddress: 0xff09)
|
||||
|
||||
machine.setValue(0xab, forAddress: 0x12ffff)
|
||||
|
||||
// LDA ($fe, x); NOP
|
||||
machine.setData(Data([0xa1, 0xfe, 0xea]), atAddress: 0x0200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 7)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .A), 0xab)
|
||||
}
|
||||
|
||||
func testDirectXIndirect16() {
|
||||
// "If the D register is $FF00, the X register is $000A, and the m flag is 0
|
||||
// (note that this means the e flag must be 0), then for LDA ($FE,X), the address
|
||||
// of the low byte of the pointer is $000008 and the address of the high byte
|
||||
// is $000009. Furthermore, if the DBR is $12 and
|
||||
// * $000008 contains $FF
|
||||
// * $000009 contains $FF
|
||||
// then LDA ($FE,X) loads the low byte of the data from address $12FFFF, and the
|
||||
// high byte from $130000."
|
||||
let machine = machine16()
|
||||
|
||||
machine.setValue(0xff00, for: .direct)
|
||||
machine.setValue(0x000a, for: .X)
|
||||
machine.setValue(0x12, for: .dataBank)
|
||||
|
||||
machine.setValue(0xff, forAddress: 0x0008)
|
||||
machine.setValue(0xff, forAddress: 0x0009)
|
||||
|
||||
machine.setValue(0xab, forAddress: 0x12ffff)
|
||||
machine.setValue(0xcd, forAddress: 0x130000)
|
||||
|
||||
// LDA ($fe, x); NOP
|
||||
machine.setData(Data([0xa1, 0xfe, 0xea]), atAddress: 0x0200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 8)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .A), 0xcdab)
|
||||
}
|
||||
|
||||
func testIndirectY8() {
|
||||
// "If the D register is $FF00 and the e flag is 1 (note that this means the
|
||||
// m flag must be 1), then for LDA ($FF),Y, the address of the low byte of the
|
||||
// pointer is $00FFFF and the address of the high byte is $00FF00.
|
||||
// Furthermore, if the DBR is $12, the Y register is $000A, and
|
||||
// * $00FF00 contains $FF
|
||||
// * $00FFFF contains $FE
|
||||
// then LDA ($FF),Y loads the low byte of the data from address $130008."
|
||||
//
|
||||
// "this is one of the rare instances where emulation mode has
|
||||
// different behavior than the 65C02 or NMOS 6502..."
|
||||
let machine = machine8()
|
||||
|
||||
machine.setValue(0xff00, for: .direct)
|
||||
machine.setValue(0x000a, for: .Y)
|
||||
machine.setValue(0x12, for: .dataBank)
|
||||
|
||||
machine.setValue(0xff, forAddress: 0xff00)
|
||||
machine.setValue(0xfe, forAddress: 0xffff)
|
||||
|
||||
machine.setValue(0xab, forAddress: 0x130008)
|
||||
|
||||
// LDA ($ff), y; NOP
|
||||
machine.setData(Data([0xb1, 0xff, 0xea]), atAddress: 0x0200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 7)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .A), 0xab)
|
||||
}
|
||||
|
||||
func testIndirectY16() {
|
||||
// "If the D register is $FF00 and the m flag is 0 (note that this means the
|
||||
// e flag must be 0), then for LDA ($FF),Y, the address of the low byte of the
|
||||
// pointer is $00FFFF and the address of the high byte is $000000.
|
||||
// Furthermore, if the DBR is $12, the Y register is $000A, and
|
||||
// * $000000 contains $FF
|
||||
// * $00FFFF contains $FE
|
||||
// then LDA ($FF),Y loads the low byte of the data from address $130008, and the
|
||||
// high byte from $130009."
|
||||
let machine = machine16()
|
||||
|
||||
machine.setValue(0xff00, for: .direct)
|
||||
machine.setValue(0x000a, for: .Y)
|
||||
machine.setValue(0x12, for: .dataBank)
|
||||
|
||||
machine.setValue(0xff, forAddress: 0x0000)
|
||||
machine.setValue(0xfe, forAddress: 0xffff)
|
||||
|
||||
machine.setValue(0xab, forAddress: 0x130008)
|
||||
machine.setValue(0xcd, forAddress: 0x130009)
|
||||
|
||||
// LDA ($ff), y; NOP
|
||||
machine.setData(Data([0xb1, 0xff, 0xea]), atAddress: 0x0200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 8)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .A), 0xcdab)
|
||||
}
|
||||
|
||||
func testIndirectYLong() {
|
||||
// "If the D register is $FF00 and the m flag is 0, then for LDA [$FE],Y, the address
|
||||
// of the low byte of the pointer is $00FFFE, the address of the middle byte is $00FFFF,
|
||||
// and the address of the high byte is $000000. Furthermore, if the Y register is $000A, and
|
||||
// * $000000 contains $12
|
||||
// * $00FFFE contains $FC
|
||||
// * $00FFFF contains $FF
|
||||
// then LDA [$FE],Y loads the low byte of the data from address $130006, and the high byte
|
||||
// from $130007."
|
||||
let machine = machine16()
|
||||
|
||||
machine.setValue(0xff00, for: .direct)
|
||||
machine.setValue(0x000a, for: .Y)
|
||||
machine.setValue(0x12, for: .dataBank)
|
||||
|
||||
machine.setValue(0x12, forAddress: 0x0000)
|
||||
machine.setValue(0xfc, forAddress: 0xfffe)
|
||||
machine.setValue(0xff, forAddress: 0xffff)
|
||||
|
||||
machine.setValue(0xab, forAddress: 0x130006)
|
||||
machine.setValue(0xcd, forAddress: 0x130007)
|
||||
|
||||
// LDA [$fe], y; NOP
|
||||
machine.setData(Data([0xb7, 0xfe, 0xea]), atAddress: 0x0200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 8)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .A), 0xcdab)
|
||||
}
|
||||
|
||||
func testAbsoluteLong() {
|
||||
// "If the m flag is 0, then LDA $12FFFF loads the low byte of the data from address $12FFFF,
|
||||
// and the high byte from address $130000."
|
||||
let machine = machine16()
|
||||
|
||||
machine.setValue(0xab, forAddress: 0x12ffff)
|
||||
machine.setValue(0xcd, forAddress: 0x130000)
|
||||
|
||||
// LDA $12ffff; NOP
|
||||
machine.setData(Data([0xaf, 0xff, 0xff, 0x12, 0xea]), atAddress: 0x0200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 7)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .A), 0xcdab)
|
||||
}
|
||||
|
||||
func testAbsoluteLongX() {
|
||||
// "If the X register is $000A and the m flag is 0, then LDA $12FFFE,X loads the low byte of
|
||||
// the data from address $130008, and the high byte from address $130009."
|
||||
let machine = machine16()
|
||||
|
||||
machine.setValue(0x000a, for: .X)
|
||||
|
||||
machine.setValue(0xab, forAddress: 0x130008)
|
||||
machine.setValue(0xcd, forAddress: 0x130009)
|
||||
|
||||
// LDA $12fffe, x; NOP
|
||||
machine.setData(Data([0xbf, 0xfe, 0xff, 0x12, 0xea]), atAddress: 0x0200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 7)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .A), 0xcdab)
|
||||
}
|
||||
|
||||
func testStackS() {
|
||||
// "If the S register is $FF10 and the m flag is 0, then LDA $FA,S loads the low byte
|
||||
// of the data from address $00000A, and the high byte from address $00000B."
|
||||
let machine = machine16()
|
||||
|
||||
machine.setValue(0xff10, for: .stackPointer)
|
||||
|
||||
machine.setValue(0xab, forAddress: 0x000a)
|
||||
machine.setValue(0xcd, forAddress: 0x000b)
|
||||
|
||||
// LDA $fa, s; NOP
|
||||
machine.setData(Data([0xa3, 0xfa, 0xea]), atAddress: 0x0200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 7)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .A), 0xcdab)
|
||||
}
|
||||
|
||||
func testIndirectStackSY() {
|
||||
// "If the S register is $FF10 and the m flag is 0, then for LDA ($FA,S),Y, the address
|
||||
// of the low byte of the pointer is $00000A and the address of the high byte is $00000B.
|
||||
// Furthermore, if the DBR is $12, the Y register is $0050, and
|
||||
// * $00000A contains $F0
|
||||
// * $00000B contains $FF
|
||||
// then LDA ($FA,S),Y loads the low byte of the data from address $130040, and the high
|
||||
// byte from $130041."
|
||||
let machine = machine16()
|
||||
|
||||
machine.setValue(0xff10, for: .stackPointer)
|
||||
machine.setValue(0x0050, for: .Y)
|
||||
machine.setValue(0x12, for: .dataBank)
|
||||
|
||||
machine.setValue(0xf0, forAddress: 0x000a)
|
||||
machine.setValue(0xff, forAddress: 0x000b)
|
||||
|
||||
machine.setValue(0xab, forAddress: 0x130040)
|
||||
machine.setValue(0xcd, forAddress: 0x130041)
|
||||
|
||||
// LDA ($fa, s), y; NOP
|
||||
machine.setData(Data([0xb3, 0xfa, 0xea]), atAddress: 0x0200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 9)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .A), 0xcdab)
|
||||
}
|
||||
|
||||
}
|
@ -91,12 +91,12 @@
|
||||
Test68000() : processor(*this) {
|
||||
}
|
||||
|
||||
void will_perform(uint32_t address, uint16_t opcode) {
|
||||
void will_perform(uint32_t, uint16_t) {
|
||||
--instructions_remaining_;
|
||||
if(!instructions_remaining_) comparitor();
|
||||
}
|
||||
|
||||
HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int is_supervisor) {
|
||||
HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int) {
|
||||
using Microcycle = CPU::MC68000::Microcycle;
|
||||
if(cycle.data_select_active()) {
|
||||
cycle.apply(&ram[cycle.host_endian_byte_address()]);
|
||||
|
@ -13,7 +13,7 @@ class AllSuiteATests: XCTestCase {
|
||||
func testAllSuiteA() {
|
||||
if let filename = Bundle(for: type(of: self)).path(forResource: "AllSuiteA", ofType: "bin") {
|
||||
if let allSuiteA = try? Data(contentsOf: URL(fileURLWithPath: filename)) {
|
||||
let machine = CSTestMachine6502(is65C02: false)
|
||||
let machine = CSTestMachine6502(processor: .processor6502)
|
||||
|
||||
machine.setData(allSuiteA, atAddress: 0x4000)
|
||||
machine.setValue(CSTestMachine6502JamOpcode, forAddress:0x45c0); // end
|
||||
@ -27,4 +27,5 @@ class AllSuiteATests: XCTestCase {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ class BCDTest: XCTestCase, CSTestMachineTrapHandler {
|
||||
func testBCD() {
|
||||
if let filename = Bundle(for: type(of: self)).path(forResource: "BCDTEST_beeb", ofType: nil) {
|
||||
if let bcdTest = try? Data(contentsOf: URL(fileURLWithPath: filename)) {
|
||||
let machine = CSTestMachine6502(is65C02: false)
|
||||
let machine = CSTestMachine6502(processor: .processor6502)
|
||||
machine.trapHandler = self
|
||||
|
||||
machine.setData(bcdTest, atAddress: 0x2900)
|
||||
@ -40,7 +40,7 @@ class BCDTest: XCTestCase, CSTestMachineTrapHandler {
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate var output: String = ""
|
||||
private var output: String = ""
|
||||
func testMachine(_ testMachine: CSTestMachine, didTrapAtAddress address: UInt16) {
|
||||
let machine6502 = testMachine as! CSTestMachine6502
|
||||
|
||||
@ -48,4 +48,5 @@ class BCDTest: XCTestCase, CSTestMachineTrapHandler {
|
||||
let character = machine6502.value(for: .A)
|
||||
output.append(Character(UnicodeScalar(character)!))
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -17,6 +17,16 @@ typedef NS_ENUM(NSInteger, CSTestMachine6502Register) {
|
||||
CSTestMachine6502RegisterA,
|
||||
CSTestMachine6502RegisterX,
|
||||
CSTestMachine6502RegisterY,
|
||||
CSTestMachine6502RegisterEmulationFlag,
|
||||
CSTestMachine6502RegisterDataBank,
|
||||
CSTestMachine6502RegisterProgramBank,
|
||||
CSTestMachine6502RegisterDirect,
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, CSTestMachine6502Processor) {
|
||||
CSTestMachine6502Processor6502,
|
||||
CSTestMachine6502Processor65C02,
|
||||
CSTestMachine6502Processor65816
|
||||
};
|
||||
|
||||
extern const uint8_t CSTestMachine6502JamOpcode;
|
||||
@ -25,13 +35,13 @@ extern const uint8_t CSTestMachine6502JamOpcode;
|
||||
|
||||
- (nonnull instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
- (nonnull instancetype)initIs65C02:(BOOL)is65C02;
|
||||
- (nonnull instancetype)initWithProcessor:(CSTestMachine6502Processor)processor;
|
||||
|
||||
- (void)setData:(nonnull NSData *)data atAddress:(uint16_t)startAddress;
|
||||
- (void)setData:(nonnull NSData *)data atAddress:(uint32_t)startAddress;
|
||||
- (void)runForNumberOfCycles:(int)cycles;
|
||||
|
||||
- (void)setValue:(uint8_t)value forAddress:(uint16_t)address;
|
||||
- (uint8_t)valueForAddress:(uint16_t)address;
|
||||
- (void)setValue:(uint8_t)value forAddress:(uint32_t)address;
|
||||
- (uint8_t)valueForAddress:(uint32_t)address;
|
||||
- (void)setValue:(uint16_t)value forRegister:(CSTestMachine6502Register)reg;
|
||||
- (uint16_t)valueForRegister:(CSTestMachine6502Register)reg;
|
||||
|
||||
|
@ -24,6 +24,10 @@ static CPU::MOS6502::Register registerForRegister(CSTestMachine6502Register reg)
|
||||
case CSTestMachine6502RegisterX: return CPU::MOS6502::Register::X;
|
||||
case CSTestMachine6502RegisterY: return CPU::MOS6502::Register::Y;
|
||||
case CSTestMachine6502RegisterStackPointer: return CPU::MOS6502::Register::StackPointer;
|
||||
case CSTestMachine6502RegisterEmulationFlag: return CPU::MOS6502::Register::EmulationFlag;
|
||||
case CSTestMachine6502RegisterDataBank: return CPU::MOS6502::Register::DataBank;
|
||||
case CSTestMachine6502RegisterProgramBank: return CPU::MOS6502::Register::ProgramBank;
|
||||
case CSTestMachine6502RegisterDirect: return CPU::MOS6502::Register::Direct;
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,12 +39,20 @@ static CPU::MOS6502::Register registerForRegister(CSTestMachine6502Register reg)
|
||||
|
||||
#pragma mark - Lifecycle
|
||||
|
||||
- (instancetype)initIs65C02:(BOOL)is65C02 {
|
||||
- (instancetype)initWithProcessor:(CSTestMachine6502Processor)processor {
|
||||
self = [super init];
|
||||
|
||||
if(self) {
|
||||
_processor = CPU::MOS6502::AllRAMProcessor::Processor(
|
||||
is65C02 ? CPU::MOS6502::Personality::PWDC65C02 : CPU::MOS6502::Personality::P6502);
|
||||
switch(processor) {
|
||||
case CSTestMachine6502Processor6502:
|
||||
_processor = CPU::MOS6502::AllRAMProcessor::Processor(CPU::MOS6502Esque::Type::T6502);
|
||||
break;
|
||||
case CSTestMachine6502Processor65C02:
|
||||
_processor = CPU::MOS6502::AllRAMProcessor::Processor(CPU::MOS6502Esque::Type::TWDC65C02);
|
||||
break;
|
||||
case CSTestMachine6502Processor65816:
|
||||
_processor = CPU::MOS6502::AllRAMProcessor::Processor(CPU::MOS6502Esque::Type::TWDC65816);
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
@ -52,13 +64,13 @@ static CPU::MOS6502::Register registerForRegister(CSTestMachine6502Register reg)
|
||||
|
||||
#pragma mark - Accessors
|
||||
|
||||
- (uint8_t)valueForAddress:(uint16_t)address {
|
||||
- (uint8_t)valueForAddress:(uint32_t)address {
|
||||
uint8_t value;
|
||||
_processor->get_data_at_address(address, 1, &value);
|
||||
return value;
|
||||
}
|
||||
|
||||
- (void)setValue:(uint8_t)value forAddress:(uint16_t)address {
|
||||
- (void)setValue:(uint8_t)value forAddress:(uint32_t)address {
|
||||
_processor->set_data_at_address(address, 1, &value);
|
||||
}
|
||||
|
||||
@ -70,7 +82,7 @@ static CPU::MOS6502::Register registerForRegister(CSTestMachine6502Register reg)
|
||||
return _processor->get_value_of_register(registerForRegister(reg));
|
||||
}
|
||||
|
||||
- (void)setData:(NSData *)data atAddress:(uint16_t)startAddress {
|
||||
- (void)setData:(NSData *)data atAddress:(uint32_t)startAddress {
|
||||
_processor->set_data_at_address(startAddress, data.length, (const uint8_t *)data.bytes);
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ class BusOperationHandler: public CPU::Z80::AllRAMProcessor::MemoryAccessDelegat
|
||||
public:
|
||||
BusOperationHandler(CSTestMachineZ80 *targetMachine) : target_(targetMachine) {}
|
||||
|
||||
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) {
|
||||
void z80_all_ram_processor_did_perform_bus_operation(CPU::Z80::AllRAMProcessor &, CPU::Z80::PartialMachineCycle::Operation operation, uint16_t address, uint8_t value, HalfCycles time_stamp) {
|
||||
[target_ testMachineDidPerformBusOperation:operation address:address value:value timeStamp:time_stamp];
|
||||
}
|
||||
|
||||
@ -77,7 +77,7 @@ struct PortAccessDelegateTopByte: public CPU::Z80::AllRAMProcessor::PortAccessDe
|
||||
};
|
||||
|
||||
struct PortAccessDelegate191: public CPU::Z80::AllRAMProcessor::PortAccessDelegate {
|
||||
uint8_t z80_all_ram_processor_input(uint16_t port) final { return 191; }
|
||||
uint8_t z80_all_ram_processor_input(uint16_t) final { return 191; }
|
||||
};
|
||||
|
||||
#pragma mark - Capture class
|
||||
|
@ -10,21 +10,21 @@ import XCTest
|
||||
|
||||
class C1540Tests: XCTestCase {
|
||||
|
||||
fileprivate func with1540(_ action: (C1540Bridge) -> ()) {
|
||||
private func with1540(_ action: (C1540Bridge) -> ()) {
|
||||
let bridge = C1540Bridge()
|
||||
action(bridge)
|
||||
}
|
||||
|
||||
fileprivate func transmit(_ c1540: C1540Bridge, value: Int) {
|
||||
private func transmit(_ c1540: C1540Bridge, value: Int) {
|
||||
var shiftedValue = value
|
||||
|
||||
c1540.dataLine = true
|
||||
c1540.run(forCycles: 256)
|
||||
XCTAssert(c1540.dataLine == false, "Listener should have taken data line low for start of transmission")
|
||||
XCTAssertFalse(c1540.dataLine, "Listener should have taken data line low for start of transmission")
|
||||
|
||||
c1540.clockLine = true
|
||||
c1540.run(forCycles: 256) // this isn't time limited on real hardware
|
||||
XCTAssert(c1540.dataLine == true, "Listener should have let data line go high again")
|
||||
XCTAssertTrue(c1540.dataLine, "Listener should have let data line go high again")
|
||||
|
||||
// set up for byte transfer
|
||||
c1540.clockLine = false
|
||||
@ -47,7 +47,7 @@ class C1540Tests: XCTestCase {
|
||||
// check for acknowledgment
|
||||
c1540.dataLine = true
|
||||
c1540.run(forCycles: 1000)
|
||||
XCTAssert(c1540.dataLine == false, "Listener should have acknowledged byte")
|
||||
XCTAssertFalse(c1540.dataLine, "Listener should have acknowledged byte")
|
||||
}
|
||||
|
||||
// MARK: EOI
|
||||
@ -64,10 +64,11 @@ class C1540Tests: XCTestCase {
|
||||
|
||||
// proceed 1 ms and check that the 1540 pulled the data line low
|
||||
$0.run(forCycles: 1000)
|
||||
XCTAssert($0.dataLine == false, "Listener should have taken data line low")
|
||||
XCTAssertFalse($0.dataLine, "Listener should have taken data line low")
|
||||
|
||||
// transmit LISTEN #8
|
||||
self.transmit($0, value: 0x28)
|
||||
transmit($0, value: 0x28)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ class ComparativeBusHandler: public CPU::MC68000::BusHandler {
|
||||
gzclose(trace);
|
||||
}
|
||||
|
||||
void will_perform(uint32_t address, uint16_t opcode) {
|
||||
void will_perform(uint32_t address, uint16_t) {
|
||||
// Obtain the next line from the trace file.
|
||||
char correct_state[300] = "\n";
|
||||
gzgets(trace, correct_state, sizeof(correct_state));
|
||||
@ -45,7 +45,7 @@ class ComparativeBusHandler: public CPU::MC68000::BusHandler {
|
||||
fprintf(stderr, "Diverges at line %d\n", line_count);
|
||||
fprintf(stderr, "Good: %s", correct_state);
|
||||
fprintf(stderr, "Bad: %s", local_state);
|
||||
assert(false);
|
||||
throw std::exception();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ class EmuTOS: public ComparativeBusHandler {
|
||||
return m68000_.get_state();
|
||||
}
|
||||
|
||||
HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int is_supervisor) {
|
||||
HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int) {
|
||||
const uint32_t address = cycle.word_address();
|
||||
uint32_t word_address = address;
|
||||
|
||||
|
44
OSBindings/Mac/Clock SignalTests/Jeek816Tests.swift
Normal file
44
OSBindings/Mac/Clock SignalTests/Jeek816Tests.swift
Normal file
@ -0,0 +1,44 @@
|
||||
//
|
||||
// Jeek816Tests.swift
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 12/10/2020.
|
||||
// Copyright 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import Foundation
|
||||
|
||||
class Jeek816Tests: XCTestCase {
|
||||
|
||||
func testJeek816() {
|
||||
var machine: CSTestMachine6502!
|
||||
|
||||
if let filename = Bundle(for: type(of: self)).path(forResource: "suite-a.prg", ofType: nil) {
|
||||
if let testData = try? Data(contentsOf: URL(fileURLWithPath: filename)) {
|
||||
machine = CSTestMachine6502(processor: .processor65816)
|
||||
|
||||
let contents = testData.subdata(in: 0xe ..< testData.count)
|
||||
machine.setData(contents, atAddress: 0x080d)
|
||||
|
||||
machine.setValue(0x080d, for: .programCounter)
|
||||
}
|
||||
}
|
||||
|
||||
if machine == nil {
|
||||
NSException(name: NSExceptionName(rawValue: "Failed Test"), reason: "Couldn't load file \(name)", userInfo: nil).raise()
|
||||
}
|
||||
|
||||
// $874 is the failure stopping point and $85d is success.
|
||||
while machine.value(for: .lastOperationAddress) != 0x0874 && machine.value(for: .lastOperationAddress) != 0x085d {
|
||||
machine.runForNumber(ofCycles: 1000)
|
||||
}
|
||||
|
||||
// The test leaves $ff in $d7ff to indicate failure; $0000 to indicate success.
|
||||
// If the tests failed, it'll leave a bitmap of failures in address $0401.
|
||||
if machine.value(forAddress: 0xd7ff) != 0 {
|
||||
NSException(name: NSExceptionName(rawValue: "Failed Test"), reason: "Failed tests with bitmap: \(String(format:"%02x", machine.value(forAddress: 0x401)))", userInfo: nil).raise()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -11,10 +11,10 @@ import XCTest
|
||||
|
||||
class KlausDormannTests: XCTestCase {
|
||||
|
||||
fileprivate func runTest(resource: String, is65C02: Bool) -> UInt16 {
|
||||
private func runTest(resource: String, processor: CSTestMachine6502Processor) -> UInt16 {
|
||||
if let filename = Bundle(for: type(of: self)).path(forResource: resource, ofType: "bin") {
|
||||
if let functionalTest = try? Data(contentsOf: URL(fileURLWithPath: filename)) {
|
||||
let machine = CSTestMachine6502(is65C02: is65C02)
|
||||
let machine = CSTestMachine6502(processor: processor)
|
||||
|
||||
machine.setData(functionalTest, atAddress: 0)
|
||||
machine.setValue(0x400, for: .programCounter)
|
||||
@ -39,21 +39,25 @@ class KlausDormannTests: XCTestCase {
|
||||
return 0
|
||||
}
|
||||
|
||||
/// Runs Klaus Dorman's 6502 tests.
|
||||
func test6502() {
|
||||
private func runTest6502(processor: CSTestMachine6502Processor) {
|
||||
func errorForTrapAddress(_ address: UInt16) -> String? {
|
||||
switch address {
|
||||
case 0x3399: return nil // success!
|
||||
|
||||
case 0x052a: return "TAX, DEX or LDA did not correctly set flags, or BEQ did not branch correctly"
|
||||
case 0x05db: return "PLP did not affect N flag correctly"
|
||||
case 0x26d2: return "ASL zpg,x produced incorrect flags"
|
||||
case 0x33a7: return "Decimal ADC result has wrong value"
|
||||
case 0x3502: return "Binary SBC result has wrong value"
|
||||
case 0x33b9: return "Decimal SBC result has wrong value"
|
||||
case 0x33c0: return "Decimal SBC wrong carry flag"
|
||||
case 0x36d1: return "BRK: unexpected BRK or IRQ"
|
||||
case 0x3502: return "Binary SBC result has wrong value"
|
||||
case 0x364a: return "JMP (addr) acted as JMP addr"
|
||||
case 0x36ac, 0x36f6: return "Improper JSR return address on stack"
|
||||
case 0x36e5: return "BRK flag not set on stack"
|
||||
case 0x26d2: return "ASL zpg,x produced incorrect flags"
|
||||
case 0x36c6: return "Unexpected RESET"
|
||||
case 0x36d1: return "BRK: unexpected BRK or IRQ"
|
||||
case 0x36e5: return "BRK flag not set on stack following BRK"
|
||||
case 0x36ea: return "BRK did not set the I flag"
|
||||
case 0x36fd: return "Wrong address put on stack by BRK"
|
||||
|
||||
case 0: return "Didn't find tests"
|
||||
|
||||
@ -61,13 +65,12 @@ class KlausDormannTests: XCTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
let destination = runTest(resource: "6502_functional_test", is65C02: false)
|
||||
let destination = runTest(resource: "6502_functional_test", processor: processor)
|
||||
let error = errorForTrapAddress(destination)
|
||||
XCTAssert(error == nil, "Failed with error \(error!)")
|
||||
}
|
||||
|
||||
/// Runs Klaus Dorman's 65C02 tests.
|
||||
func test65C02() {
|
||||
private func runTest65C02(processor: CSTestMachine6502Processor) {
|
||||
func errorForTrapAddress(_ address: UInt16) -> String? {
|
||||
switch address {
|
||||
case 0x24f1: return nil // success!
|
||||
@ -110,8 +113,31 @@ class KlausDormannTests: XCTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
let destination = runTest(resource: "65C02_extended_opcodes_test", is65C02: true)
|
||||
let destination = runTest(resource: "65C02_extended_opcodes_test", processor: processor)
|
||||
let error = errorForTrapAddress(destination)
|
||||
XCTAssert(error == nil, "Failed with error \(error!)")
|
||||
}
|
||||
|
||||
|
||||
/// Runs Klaus Dormann's 6502 tests.
|
||||
func test6502() {
|
||||
runTest6502(processor: .processor6502)
|
||||
}
|
||||
|
||||
/// Runs Klaus Dormann's standard 6502 tests on a 65C02.
|
||||
func test65C02As6502() {
|
||||
runTest6502(processor: .processor65C02)
|
||||
}
|
||||
|
||||
/// Runs Klaus Dormann's standard 6502 tests on a 65816.
|
||||
func test65816As6502() {
|
||||
runTest6502(processor: .processor65816)
|
||||
}
|
||||
|
||||
/// Runs Klaus Dormann's 65C02 tests.
|
||||
func test65C02() {
|
||||
runTest65C02(processor: .processor65C02)
|
||||
}
|
||||
|
||||
/* Dormann's 65C02 tests aren't applicable to the 65816; e.g. they test BBR/BBS, which the 65816 doesn't implement. */
|
||||
}
|
||||
|
@ -11,8 +11,8 @@ import Foundation
|
||||
|
||||
class PatrikRakTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
|
||||
fileprivate var done = false
|
||||
fileprivate var output = ""
|
||||
private var done = false
|
||||
private var output = ""
|
||||
|
||||
private func runTest(_ name: String) {
|
||||
if let filename = Bundle(for: type(of: self)).path(forResource: name, ofType: "tap") {
|
||||
@ -124,4 +124,5 @@ class PatrikRakTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ class QL: public ComparativeBusHandler {
|
||||
return m68000_.get_state();
|
||||
}
|
||||
|
||||
HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int is_supervisor) {
|
||||
HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int) {
|
||||
const uint32_t address = cycle.word_address();
|
||||
uint32_t word_address = address;
|
||||
|
||||
|
@ -48,7 +48,7 @@ class RAM68000: public CPU::MC68000::BusHandler {
|
||||
ram_[1] = sp & 0xffff;
|
||||
}
|
||||
|
||||
void will_perform(uint32_t address, uint16_t opcode) {
|
||||
void will_perform(uint32_t, uint16_t) {
|
||||
--instructions_remaining_;
|
||||
}
|
||||
|
||||
@ -80,7 +80,7 @@ class RAM68000: public CPU::MC68000::BusHandler {
|
||||
return &ram_[(address >> 1) % ram_.size()];
|
||||
}
|
||||
|
||||
HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int is_supervisor) {
|
||||
HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int) {
|
||||
const uint32_t word_address = cycle.word_address();
|
||||
if(instructions_remaining_) duration_ += cycle.length;
|
||||
|
||||
|
@ -11,206 +11,294 @@ import Foundation
|
||||
|
||||
class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
|
||||
func testWolfgangLorenzStart() {
|
||||
self.runWolfgangLorenzTest(" start")
|
||||
// MARK: - 6502 Tests
|
||||
|
||||
func testStart() {
|
||||
runTest(" start", processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzLDA() {
|
||||
self.runWolfgangLorenzTest("lda", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"])
|
||||
func testLDA() {
|
||||
runTest("lda", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzSTA() {
|
||||
self.runWolfgangLorenzTest("sta", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"])
|
||||
func testSTA() {
|
||||
runTest("sta", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzLDX() {
|
||||
self.runWolfgangLorenzTest("ldx", suffixes: ["b", "z", "zy", "a", "ay"])
|
||||
func testLDX() {
|
||||
runTest("ldx", suffixes: ["b", "z", "zy", "a", "ay"], processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzSTX() {
|
||||
self.runWolfgangLorenzTest("stx", suffixes: ["z", "zy", "a"])
|
||||
func testSTX() {
|
||||
runTest("stx", suffixes: ["z", "zy", "a"], processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzLDY() {
|
||||
self.runWolfgangLorenzTest("ldy", suffixes: ["b", "z", "zx", "a", "ax"])
|
||||
func testLDY() {
|
||||
runTest("ldy", suffixes: ["b", "z", "zx", "a", "ax"], processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzSTY() {
|
||||
self.runWolfgangLorenzTest("sty", suffixes: ["z", "zx", "a"])
|
||||
func testSTY() {
|
||||
runTest("sty", suffixes: ["z", "zx", "a"], processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzTransfers() {
|
||||
self.runWolfgangLorenzTest("taxn")
|
||||
self.runWolfgangLorenzTest("tayn")
|
||||
self.runWolfgangLorenzTest("txan")
|
||||
self.runWolfgangLorenzTest("tyan")
|
||||
self.runWolfgangLorenzTest("tsxn")
|
||||
self.runWolfgangLorenzTest("txsn")
|
||||
func testTransfers() {
|
||||
testTransfers(processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzStack() {
|
||||
self.runWolfgangLorenzTest("phan")
|
||||
self.runWolfgangLorenzTest("plan")
|
||||
self.runWolfgangLorenzTest("phpn")
|
||||
self.runWolfgangLorenzTest("plpn")
|
||||
func testStack() {
|
||||
testStack(processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzIncsAndDecs() {
|
||||
self.runWolfgangLorenzTest("inxn")
|
||||
self.runWolfgangLorenzTest("inyn")
|
||||
self.runWolfgangLorenzTest("dexn")
|
||||
self.runWolfgangLorenzTest("deyn")
|
||||
self.runWolfgangLorenzTest("incz")
|
||||
self.runWolfgangLorenzTest("inczx")
|
||||
self.runWolfgangLorenzTest("inca")
|
||||
self.runWolfgangLorenzTest("incax")
|
||||
self.runWolfgangLorenzTest("decz")
|
||||
self.runWolfgangLorenzTest("deczx")
|
||||
self.runWolfgangLorenzTest("deca")
|
||||
self.runWolfgangLorenzTest("decax")
|
||||
func testIncsAndDecs() {
|
||||
testIncsAndDecs(processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzASL() {
|
||||
self.runWolfgangLorenzTest("asl", suffixes: ["n", "z", "zx", "a", "ax"])
|
||||
func testASL() {
|
||||
runTest("asl", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzLSR() {
|
||||
self.runWolfgangLorenzTest("lsr", suffixes: ["n", "z", "zx", "a", "ax"])
|
||||
func testLSR() {
|
||||
runTest("lsr", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzROL() {
|
||||
self.runWolfgangLorenzTest("rol", suffixes: ["n", "z", "zx", "a", "ax"])
|
||||
func testROL() {
|
||||
runTest("rol", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzROR() {
|
||||
self.runWolfgangLorenzTest("ror", suffixes: ["n", "z", "zx", "a", "ax"])
|
||||
func testROR() {
|
||||
runTest("ror", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzAND() {
|
||||
self.runWolfgangLorenzTest("and", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"])
|
||||
func testAND() {
|
||||
runTest("and", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzORA() {
|
||||
self.runWolfgangLorenzTest("ora", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"])
|
||||
func testORA() {
|
||||
runTest("ora", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzEOR() {
|
||||
self.runWolfgangLorenzTest("eor", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"])
|
||||
func testEOR() {
|
||||
runTest("eor", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzFlagManipulation() {
|
||||
self.runWolfgangLorenzTest("clcn")
|
||||
self.runWolfgangLorenzTest("secn")
|
||||
self.runWolfgangLorenzTest("cldn")
|
||||
self.runWolfgangLorenzTest("sedn")
|
||||
self.runWolfgangLorenzTest("clin")
|
||||
self.runWolfgangLorenzTest("sein")
|
||||
self.runWolfgangLorenzTest("clvn")
|
||||
func testFlagManipulation() {
|
||||
testFlagManipulation(processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzADC() {
|
||||
self.runWolfgangLorenzTest("adc", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"])
|
||||
func testADC() {
|
||||
runTest("adc", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzSBC() {
|
||||
self.runWolfgangLorenzTest("sbc", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"])
|
||||
func testSBC() {
|
||||
runTest("sbc", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzCompare() {
|
||||
self.runWolfgangLorenzTest("cmp", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"])
|
||||
self.runWolfgangLorenzTest("cpx", suffixes: ["b", "z", "a"])
|
||||
self.runWolfgangLorenzTest("cpy", suffixes: ["b", "z", "a"])
|
||||
func testCompare() {
|
||||
testCompare(processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzBIT() {
|
||||
self.runWolfgangLorenzTest("bit", suffixes: ["z", "a"])
|
||||
func testBIT() {
|
||||
runTest("bit", suffixes: ["z", "a"], processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzFlow() {
|
||||
self.runWolfgangLorenzTest("brkn")
|
||||
self.runWolfgangLorenzTest("rtin")
|
||||
self.runWolfgangLorenzTest("jsrw")
|
||||
self.runWolfgangLorenzTest("rtsn")
|
||||
self.runWolfgangLorenzTest("jmpw")
|
||||
self.runWolfgangLorenzTest("jmpi")
|
||||
func testFlow() {
|
||||
testFlow(processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzBranch() {
|
||||
self.runWolfgangLorenzTest("beqr")
|
||||
self.runWolfgangLorenzTest("bner")
|
||||
self.runWolfgangLorenzTest("bmir")
|
||||
self.runWolfgangLorenzTest("bplr")
|
||||
self.runWolfgangLorenzTest("bcsr")
|
||||
self.runWolfgangLorenzTest("bccr")
|
||||
self.runWolfgangLorenzTest("bvsr")
|
||||
self.runWolfgangLorenzTest("bvcr")
|
||||
func testBranch() {
|
||||
testBranch(processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzNOP() {
|
||||
self.runWolfgangLorenzTest("nop", suffixes: ["n", "b", "z", "zx", "a", "ax"])
|
||||
func testNOP() {
|
||||
runTest("nop", suffixes: ["n", "b", "z", "zx", "a", "ax"], processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzASO() {
|
||||
self.runWolfgangLorenzTest("aso", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"])
|
||||
func testASO() {
|
||||
runTest("aso", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzRLA() {
|
||||
self.runWolfgangLorenzTest("rla", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"])
|
||||
func testRLA() {
|
||||
runTest("rla", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzLSE() {
|
||||
self.runWolfgangLorenzTest("lse", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"])
|
||||
func testLSE() {
|
||||
runTest("lse", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzRRA() {
|
||||
self.runWolfgangLorenzTest("rra", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"])
|
||||
func testRRA() {
|
||||
runTest("rra", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzDCM() {
|
||||
self.runWolfgangLorenzTest("dcm", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"])
|
||||
func testDCM() {
|
||||
runTest("dcm", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzINS() {
|
||||
self.runWolfgangLorenzTest("ins", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"])
|
||||
func testINS() {
|
||||
runTest("ins", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzLAX() {
|
||||
self.runWolfgangLorenzTest("lax", suffixes: ["z", "zy", "a", "ay", "ix", "iy"])
|
||||
func testLAX() {
|
||||
runTest("lax", suffixes: ["z", "zy", "a", "ay", "ix", "iy"], processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzAXS() {
|
||||
self.runWolfgangLorenzTest("axs", suffixes: ["z", "zy", "a", "ix"])
|
||||
func testAXS() {
|
||||
runTest("axs", suffixes: ["z", "zy", "a", "ix"], processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzALR() {
|
||||
self.runWolfgangLorenzTest("alrb")
|
||||
func testALR() {
|
||||
runTest("alrb", processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzARR() {
|
||||
self.runWolfgangLorenzTest("arrb")
|
||||
func testARR() {
|
||||
runTest("arrb", processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzSBX() {
|
||||
self.runWolfgangLorenzTest("sbxb")
|
||||
func testSBX() {
|
||||
runTest("sbxb", processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzSHA() {
|
||||
self.runWolfgangLorenzTest("sha", suffixes: ["ay", "iy"])
|
||||
func testSHA() {
|
||||
runTest("sha", suffixes: ["ay", "iy"], processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzSHX() {
|
||||
self.runWolfgangLorenzTest("shxay")
|
||||
func testSHX() {
|
||||
runTest("shxay", processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzSHY() {
|
||||
self.runWolfgangLorenzTest("shyax")
|
||||
func testSHY() {
|
||||
runTest("shyax", processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzSHS() {
|
||||
self.runWolfgangLorenzTest("shsay")
|
||||
func testSHS() {
|
||||
runTest("shsay", processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzLXA() {
|
||||
self.runWolfgangLorenzTest("lxab")
|
||||
func testLXA() {
|
||||
runTest("lxab", processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzANE() {
|
||||
self.runWolfgangLorenzTest("aneb")
|
||||
func testANE() {
|
||||
runTest("aneb", processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzANC() {
|
||||
self.runWolfgangLorenzTest("ancb")
|
||||
func testANC() {
|
||||
runTest("ancb", processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzLAS() {
|
||||
self.runWolfgangLorenzTest("lasay")
|
||||
func testLAS() {
|
||||
runTest("lasay", processor: .processor6502)
|
||||
}
|
||||
func testWolfgangLorenzSBCB() {
|
||||
self.runWolfgangLorenzTest("sbcb(eb)")
|
||||
func testSBCB() {
|
||||
runTest("sbcb(eb)", processor: .processor6502)
|
||||
}
|
||||
|
||||
fileprivate func runWolfgangLorenzTest(_ name: String, suffixes: [String]) {
|
||||
|
||||
// MARK: - 65816 Tests
|
||||
|
||||
func testStart65816() {
|
||||
runTest(" start", processor: .processor65816)
|
||||
}
|
||||
func testLDA65816() {
|
||||
runTest("lda", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor65816)
|
||||
}
|
||||
func testSTA65816() {
|
||||
runTest("sta", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor65816)
|
||||
}
|
||||
func testLDX65816() {
|
||||
runTest("ldx", suffixes: ["b", "z", "zy", "a", "ay"], processor: .processor65816)
|
||||
}
|
||||
func testSTX65816() {
|
||||
runTest("stx", suffixes: ["z", "zy", "a"], processor: .processor65816)
|
||||
}
|
||||
func testLDY65816() {
|
||||
runTest("ldy", suffixes: ["b", "z", "zx", "a", "ax"], processor: .processor65816)
|
||||
}
|
||||
func testSTY65816() {
|
||||
runTest("sty", suffixes: ["z", "zx", "a"], processor: .processor65816)
|
||||
}
|
||||
func testTransfers65816() {
|
||||
testTransfers(processor: .processor65816)
|
||||
}
|
||||
func testStack65816() {
|
||||
testStack(processor: .processor65816)
|
||||
}
|
||||
func testIncsAndDecs65816() {
|
||||
testIncsAndDecs(processor: .processor65816)
|
||||
}
|
||||
func testASL65816() {
|
||||
runTest("asl", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor65816)
|
||||
}
|
||||
func testLSR65816() {
|
||||
runTest("lsr", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor65816)
|
||||
}
|
||||
func testROL65816() {
|
||||
runTest("rol", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor65816)
|
||||
}
|
||||
func testROR65816() {
|
||||
runTest("ror", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor65816)
|
||||
}
|
||||
func testAND65816() {
|
||||
runTest("and", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor65816)
|
||||
}
|
||||
func testORA65816() {
|
||||
runTest("ora", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor65816)
|
||||
}
|
||||
func testEOR65816() {
|
||||
runTest("eor", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor65816)
|
||||
}
|
||||
func testFlagManipulation65816() {
|
||||
testFlagManipulation(processor: .processor65816)
|
||||
}
|
||||
/*
|
||||
ADC and SBC tests don't apply, as the 65816 follows the 65C02 in setting flags based on the outcome of decimal
|
||||
results, whereas Lorenzz's tests expect the original 6502 behaviour of setting flags based an intermediate value.
|
||||
*/
|
||||
func testCompare65816() {
|
||||
testCompare(processor: .processor65816)
|
||||
}
|
||||
func testBIT65816() {
|
||||
runTest("bit", suffixes: ["z", "a"], processor: .processor65816)
|
||||
}
|
||||
func testFlow65816() {
|
||||
testFlow(processor: .processor65816)
|
||||
}
|
||||
func testBranch65816() {
|
||||
testBranch(processor: .processor65816)
|
||||
}
|
||||
/* The NOP tests also don't apply; the 65816 has only one, well-defined NOP (well, not counting COP or WDM). */
|
||||
|
||||
|
||||
// MARK: - Collections
|
||||
|
||||
func testTransfers(processor: CSTestMachine6502Processor) {
|
||||
for test in ["taxn", "tayn", "txan", "tyan", "tsxn", "txsn"] {
|
||||
runTest(test, processor: processor)
|
||||
}
|
||||
}
|
||||
func testStack(processor: CSTestMachine6502Processor) {
|
||||
for test in ["phan", "plan", "phpn", "plpn"] {
|
||||
runTest(test, processor: processor)
|
||||
}
|
||||
}
|
||||
func testIncsAndDecs(processor: CSTestMachine6502Processor) {
|
||||
for test in ["inxn", "inyn", "dexn", "deyn", "incz", "inczx", "inca", "incax", "decz", "deczx", "deca", "decax"] {
|
||||
runTest(test, processor: processor)
|
||||
}
|
||||
}
|
||||
func testFlagManipulation(processor: CSTestMachine6502Processor) {
|
||||
for test in ["clcn", "secn", "cldn", "sedn", "clin", "sein", "clvn"] {
|
||||
runTest(test, processor: processor)
|
||||
}
|
||||
}
|
||||
func testCompare(processor: CSTestMachine6502Processor) {
|
||||
runTest("cmp", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: processor)
|
||||
runTest("cpx", suffixes: ["b", "z", "a"], processor: processor)
|
||||
runTest("cpy", suffixes: ["b", "z", "a"], processor: processor)
|
||||
}
|
||||
func testFlow(processor: CSTestMachine6502Processor) {
|
||||
for test in ["brkn", "rtin", "jsrw", "rtsn", "jmpw", "jmpi"] {
|
||||
// JMP indirect is explicitly different on a 65C02 and 65816, not having
|
||||
// the page wraparound bug. So run that test only on a plain 6502.
|
||||
if test == "jmpi" && processor != .processor6502 {
|
||||
continue;
|
||||
}
|
||||
|
||||
runTest(test, processor: processor)
|
||||
}
|
||||
}
|
||||
func testBranch(processor: CSTestMachine6502Processor) {
|
||||
for test in ["beqr", "bner", "bmir", "bplr", "bcsr", "bccr", "bvsr", "bvcr"] {
|
||||
runTest(test, processor: processor)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Test Running
|
||||
|
||||
fileprivate func runTest(_ name: String, suffixes: [String], processor: CSTestMachine6502Processor) {
|
||||
for suffix in suffixes {
|
||||
let testName = name + suffix
|
||||
self.runWolfgangLorenzTest(testName)
|
||||
runTest(testName, processor: processor)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate var output: String = ""
|
||||
fileprivate func runWolfgangLorenzTest(_ name: String) {
|
||||
fileprivate func runTest(_ name: String, processor: CSTestMachine6502Processor) {
|
||||
var machine: CSTestMachine6502!
|
||||
|
||||
if let filename = Bundle(for: type(of: self)).path(forResource: name, ofType: nil) {
|
||||
if let testData = try? Data(contentsOf: URL(fileURLWithPath: filename)) {
|
||||
|
||||
machine = CSTestMachine6502(is65C02: false)
|
||||
machine = CSTestMachine6502(processor: processor)
|
||||
machine.trapHandler = self
|
||||
// machine.logActivity = true
|
||||
output = ""
|
||||
|
||||
let dataPointer = (testData as NSData).bytes.bindMemory(to: UInt8.self, capacity: testData.count)
|
||||
let loadAddress = UInt16(dataPointer[0]) | (UInt16(dataPointer[1]) << 8)
|
||||
let contents = testData.subdata(in: 2..<(testData.count - 2))
|
||||
let loadAddress = UInt32(dataPointer[0]) | (UInt32(dataPointer[1]) << 8)
|
||||
let contents = testData.subdata(in: 2 ..< testData.count)
|
||||
|
||||
machine.setData(contents, atAddress: loadAddress)
|
||||
|
||||
// Cf. http://www.softwolves.com/arkiv/cbm-hackers/7/7114.html for the steps being taken here.
|
||||
|
||||
// Initialise memory locations as instructed.
|
||||
machine.setValue(0x00, forAddress: 0x0002)
|
||||
machine.setValue(0x00, forAddress: 0xa002)
|
||||
machine.setValue(0x80, forAddress: 0xa003)
|
||||
@ -219,28 +307,39 @@ class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
machine.setValue(0x48, forAddress: 0xfffe)
|
||||
machine.setValue(0xff, forAddress: 0xffff)
|
||||
|
||||
let irqHandler = Data(bytes: UnsafePointer<UInt8>([
|
||||
// Place the Commodore's default IRQ handler.
|
||||
let irqHandler: [UInt8] = [
|
||||
0x48, 0x8a, 0x48, 0x98, 0x48, 0xba, 0xbd, 0x04, 0x01,
|
||||
0x29, 0x10, 0xf0, 0x03, 0x6c, 0x16, 0x03, 0x6c, 0x14, 0x03
|
||||
] as [UInt8]), count: 19)
|
||||
machine.setData( irqHandler, atAddress: 0xff48)
|
||||
]
|
||||
machine.setData(Data(irqHandler), atAddress: 0xff48)
|
||||
|
||||
// Set a couple of trap addresses to capture test output.
|
||||
machine.addTrapAddress(0xffd2) // print character
|
||||
machine.addTrapAddress(0xffe4) // scan keyboard
|
||||
|
||||
// Set a couple of test addresses that indicate failure.
|
||||
machine.addTrapAddress(0x8000) // exit
|
||||
machine.addTrapAddress(0xa474) // exit
|
||||
|
||||
// Ensure that any of those addresses return control.
|
||||
machine.setValue(0x60, forAddress:0xffd2) // 0x60 is RTS
|
||||
machine.setValue(0x60, forAddress:0xffe4)
|
||||
machine.setValue(0x60, forAddress:0x8000)
|
||||
machine.setValue(0x60, forAddress:0xa474)
|
||||
|
||||
machine.setValue(CSTestMachine6502JamOpcode, forAddress:0xe16f) // load
|
||||
// Commodore's load routine resides at $e16f; this is used to spot the end of a test.
|
||||
machine.setData(Data([0x4c, 0x6f, 0xe1]), atAddress: 0xe16f)
|
||||
|
||||
machine.setValue(0x0801, for: CSTestMachine6502Register.programCounter)
|
||||
machine.setValue(0xfd, for: CSTestMachine6502Register.stackPointer)
|
||||
machine.setValue(0x04, for: CSTestMachine6502Register.flags)
|
||||
// Seed program entry.
|
||||
machine.setValue(0x0801, for: .programCounter)
|
||||
machine.setValue(0xfd, for: .stackPointer)
|
||||
machine.setValue(0x04, for: .flags)
|
||||
|
||||
// For consistency when debugging; otherwise immaterial.
|
||||
machine.setValue(0x00, for: .A)
|
||||
machine.setValue(0x00, for: .X)
|
||||
machine.setValue(0x00, for: .Y)
|
||||
}
|
||||
}
|
||||
|
||||
@ -248,19 +347,16 @@ class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
NSException(name: NSExceptionName(rawValue: "Failed Test"), reason: "Couldn't load file \(name)", userInfo: nil).raise()
|
||||
}
|
||||
|
||||
while !machine.isJammed {
|
||||
while machine.value(for: .lastOperationAddress) != 0xe16f && !machine.isJammed {
|
||||
machine.runForNumber(ofCycles: 1000)
|
||||
}
|
||||
|
||||
let jammedPC = machine.value(for: CSTestMachine6502Register.lastOperationAddress)
|
||||
if jammedPC != 0xe16f {
|
||||
let hexAddress = String(format:"%04x", jammedPC)
|
||||
if machine.isJammed {
|
||||
let hexAddress = String(format:"%04x", machine.value(for: .lastOperationAddress))
|
||||
NSException(name: NSExceptionName(rawValue: "Failed Test"), reason: "Processor jammed unexpectedly at \(hexAddress)", userInfo: nil).raise()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: MachineJamHandler
|
||||
|
||||
func petsciiToString(_ string: String) -> String {
|
||||
let petsciiToCharCommon: [String] = [
|
||||
"?", "?", "?", "[RUN/STOP]", "?", "[WHT]", "?", "?", "[SHIFT DISABLE]", "[SHIFT ENABLE]", "?", "?", "?", "\r", "[TEXT MODE]", "?",
|
||||
@ -316,7 +412,7 @@ class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
testMachine6502.setValue(0x3, for:CSTestMachine6502Register.A)
|
||||
|
||||
case 0x8000, 0xa474:
|
||||
NSException(name: NSExceptionName(rawValue: "Failed test"), reason: self.petsciiToString(output), userInfo: nil).raise()
|
||||
NSException(name: NSExceptionName(rawValue: "Failed test"), reason: petsciiToString(output), userInfo: nil).raise()
|
||||
|
||||
case 0x0000:
|
||||
NSException(name: NSExceptionName(rawValue: "Failed test"), reason: "Execution hit 0000", userInfo: nil).raise()
|
||||
|
@ -1247,4 +1247,5 @@ class Z80MachineCycleTests: XCTestCase {
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
import XCTest
|
||||
|
||||
class Z80MemptrTester: XCTestCase {
|
||||
|
||||
let machine = CSTestMachineZ80()
|
||||
|
||||
private func test(program : [UInt8], initialValue : UInt16) -> UInt16 {
|
||||
|
@ -11,8 +11,8 @@ import Foundation
|
||||
|
||||
class ZexallTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
|
||||
fileprivate var done = false
|
||||
fileprivate var output = ""
|
||||
private var done = false
|
||||
private var output = ""
|
||||
|
||||
private func runTest(_ name: String) {
|
||||
if let filename = Bundle(for: type(of: self)).path(forResource: name, ofType: "com") {
|
||||
@ -177,4 +177,5 @@ class ZexallTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
11
OSBindings/Mac/Clock SignalTests/jeek816/Makefile
Normal file
11
OSBindings/Mac/Clock SignalTests/jeek816/Makefile
Normal file
@ -0,0 +1,11 @@
|
||||
|
||||
F=suite-a
|
||||
|
||||
all: $(F).prg
|
||||
|
||||
$(F).prg: $(F).asm
|
||||
acme --format plain -o $(F).prg -r $(F).r $(F).asm
|
||||
|
||||
clean:
|
||||
rm -f $(F).prg
|
||||
rm -f *~
|
2
OSBindings/Mac/Clock SignalTests/jeek816/RUN
Normal file
2
OSBindings/Mac/Clock SignalTests/jeek816/RUN
Normal file
@ -0,0 +1,2 @@
|
||||
xscpu64 -scpu64 /usr/local/lib64/vice/SCPU64/scpu64 suite-a.prg
|
||||
#xscpu64.devel -scpu64 /usr/local/lib64/vice/SCPU64/scpu64 suite-a.prg
|
24
OSBindings/Mac/Clock SignalTests/jeek816/license.txt
Normal file
24
OSBindings/Mac/Clock SignalTests/jeek816/license.txt
Normal file
@ -0,0 +1,24 @@
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
23
OSBindings/Mac/Clock SignalTests/jeek816/readme.txt
Normal file
23
OSBindings/Mac/Clock SignalTests/jeek816/readme.txt
Normal file
@ -0,0 +1,23 @@
|
||||
|
||||
65C816 instruction set test
|
||||
|
||||
|
||||
2017-12-13 J.E. Klasek j+816 AT klasek DOT at
|
||||
|
||||
|
||||
ACME syntax, green border shows success. in case of failure red border
|
||||
is shown and $0400 contains number of failed test and $0401 a bitmap
|
||||
showing which tests actually failed.
|
||||
If all tests fail on screen "f?" will be shown (corresponds to 6 failures)
|
||||
and the bitmap %00111111 ($3F = '?')
|
||||
|
||||
There are 6 tests (bit 5 to bit 0):
|
||||
|
||||
STX $FFFF fails in 16 mode for X/Y if wrapping to location 0
|
||||
STY $FFFF fails in 16 mode for X/Y if wrapping to location 0
|
||||
LDX $FFFF,Y fails if wrapping to same bank
|
||||
LDY $FFFF,X fails if wrapping to same bank
|
||||
TRB $FFFF fails in 16 mode for A/M if wrapping to location 0
|
||||
TSB $FFFF fails in 16 mode for A/M if wrapping to location 0
|
||||
|
||||
-------------------------------------------------------------------------------
|
218
OSBindings/Mac/Clock SignalTests/jeek816/suite-a.asm
Normal file
218
OSBindings/Mac/Clock SignalTests/jeek816/suite-a.asm
Normal file
@ -0,0 +1,218 @@
|
||||
!cpu 65816
|
||||
|
||||
; 2017-12-13 J.E. Klasek j+816 AT klasek DOT at
|
||||
|
||||
|
||||
|
||||
videoram = $0400
|
||||
colorram = $d800
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
*=$07ff
|
||||
!word $0801
|
||||
!word bend
|
||||
!word 10
|
||||
!byte $9e
|
||||
!text "2061", 0
|
||||
bend: !word 0
|
||||
;-------------------------------------------------------------------------------
|
||||
|
||||
sei
|
||||
lda #$17
|
||||
sta $d018
|
||||
lda #$35
|
||||
sta $01
|
||||
lda #$7f
|
||||
sta $dc0d
|
||||
sta $dd0d
|
||||
lda $dc0d
|
||||
lda $dd0d
|
||||
ldx #0
|
||||
-
|
||||
lda #$20
|
||||
sta videoram,x
|
||||
sta videoram+$0100,x
|
||||
sta videoram+$0200,x
|
||||
sta videoram+$0300,x
|
||||
lda #1
|
||||
sta colorram,x
|
||||
sta colorram+$0100,x
|
||||
sta colorram+$0200,x
|
||||
sta colorram+$0300,x
|
||||
inx
|
||||
bne -
|
||||
|
||||
jmp start
|
||||
theend:
|
||||
sep #$30 ; 8-bit for X/Y and A/M
|
||||
!as
|
||||
!rs
|
||||
lda $040210
|
||||
cmp #$ff
|
||||
bne error
|
||||
|
||||
lda #5
|
||||
sta $d020
|
||||
ldx #0 ; success
|
||||
stx $d7ff
|
||||
jmp *
|
||||
error
|
||||
sta $0400
|
||||
lda $040211 ; failure map (which test failed)
|
||||
sta $0401
|
||||
lda #10
|
||||
sta $d020
|
||||
ldx #$ff ; failure
|
||||
stx $d7ff
|
||||
jmp *
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
|
||||
* = $1000
|
||||
start:
|
||||
; EXPECTED FINAL RESULTS: $0210 = FF
|
||||
; (any other number will be the
|
||||
; test that failed)
|
||||
|
||||
; initialize:
|
||||
lda #$00
|
||||
sta $040210
|
||||
sta $040211
|
||||
|
||||
|
||||
test00:
|
||||
|
||||
; setup cpu
|
||||
clc
|
||||
xce ; native mode
|
||||
rep #$30 ; 16-bit for X/Y and A/M
|
||||
!al
|
||||
!rl
|
||||
|
||||
; setup registers
|
||||
lda #$0404 ; Data Bank Register register
|
||||
pha ; akku in 16 bit
|
||||
plb ; pull DBR twice
|
||||
plb
|
||||
ldy #$8888 ; change marker
|
||||
tyx
|
||||
tya
|
||||
|
||||
; setup memory
|
||||
lda #$5555 ; wrap marker
|
||||
sta $048887 ; into bank 4, for LDX/LDY
|
||||
lda #$7777 ; no-wrap marker
|
||||
sta $058887 ; into bank 5, for LDX/LDY
|
||||
|
||||
;---------------------------------------------------------------------
|
||||
|
||||
stz $0000 ; init wrap marker
|
||||
lda #$7777 ; no-wrap marker
|
||||
sta $050000 ; to start of bank 5
|
||||
|
||||
sty $ffff ; high byte of Y is where?
|
||||
lda $0000
|
||||
bne +
|
||||
lda $ffff ; fetch, does not wrap
|
||||
cmp #$8888
|
||||
bne +
|
||||
lda $050000
|
||||
cmp #$7788 ; write to bank 5
|
||||
beq ++
|
||||
+ inc $0210 ; fail counter
|
||||
clc
|
||||
++
|
||||
rol $0211 ; update failure map
|
||||
;---------------------------------------------------------------------
|
||||
|
||||
stz $0000 ; init wrap marker
|
||||
lda #$7777 ; no-wrap marker
|
||||
sta $050000 ; to start of bank 5
|
||||
|
||||
tyx ; change marker
|
||||
stx $ffff ; high byte of Y is where?
|
||||
lda $0000
|
||||
bne +
|
||||
lda $ffff ; fetch, does not wrap
|
||||
cmp #$8888
|
||||
bne +
|
||||
lda $050000
|
||||
cmp #$7788 ; write to bank 5
|
||||
beq ++
|
||||
+ inc $0210 ; fail counter
|
||||
clc
|
||||
++
|
||||
rol $0211 ; update failure map
|
||||
;---------------------------------------------------------------------
|
||||
|
||||
ldy $ffff,x ; Y=5555 Y=7777 value for Y comes from which bank?
|
||||
cpy #$7777
|
||||
beq +
|
||||
inc $0210 ; fail counter
|
||||
clc
|
||||
+
|
||||
rol $0211 ; update failure map
|
||||
;---------------------------------------------------------------------
|
||||
|
||||
txy ; reinitialize y
|
||||
ldx $ffff,y ; X=5555 X=7777 value for X comes from which bank?
|
||||
cpx #$7777
|
||||
beq +
|
||||
inc $0210 ; fail counter
|
||||
clc
|
||||
+
|
||||
rol $0211 ; update failure map
|
||||
;---------------------------------------------------------------------
|
||||
|
||||
stz $0000 ; init wrap marker
|
||||
lda #$7777 ; no-wrap marker
|
||||
sta $050000 ; to start of bank 5
|
||||
|
||||
lda #$7788
|
||||
inc $0000 ; $0000 = 1
|
||||
trb $ffff ; 88 77 & ^(88 77) -> 00 00
|
||||
lda $0000
|
||||
cmp #$0001 ; $0000 not reset by trb (does not wrap)
|
||||
bne +
|
||||
lda $050000
|
||||
cmp #$7700 ; $050001 reset by trb
|
||||
beq ++
|
||||
+ inc $0210 ; fail counter
|
||||
clc
|
||||
++
|
||||
rol $0211 ; update failure map
|
||||
;---------------------------------------------------------------------
|
||||
|
||||
lda #$7788
|
||||
sta $050000 ; 00 88 | 88 77 -> 88 ff
|
||||
tsb $ffff ; set bits (which are already cleared)
|
||||
lda $0000
|
||||
cmp #$0001 ; $0000 not set by tsb (does not wrap!)
|
||||
bne +
|
||||
lda $050000
|
||||
cmp #$77ff ; $050001 all bits set by tsb
|
||||
beq ++
|
||||
+ inc $0210 ; fail counter
|
||||
clc
|
||||
++
|
||||
rol $0211 ; update failure map
|
||||
;---------------------------------------------------------------------
|
||||
|
||||
|
||||
test00pass:
|
||||
lda $0210
|
||||
eor #%0011111100000000 ; invert failure map
|
||||
sta $0210
|
||||
bne +
|
||||
dec $0210 ; 0 -> FF
|
||||
+
|
||||
|
||||
lda #$0000
|
||||
pha
|
||||
plb
|
||||
plb ; program bank = 0
|
||||
sec
|
||||
xce ; emulation mode
|
||||
sep #$30 ; a/m, x/y 8 bit
|
||||
|
||||
jmp theend
|
BIN
OSBindings/Mac/Clock SignalTests/jeek816/suite-a.prg
Normal file
BIN
OSBindings/Mac/Clock SignalTests/jeek816/suite-a.prg
Normal file
Binary file not shown.
220
OSBindings/Mac/Clock SignalTests/jeek816/suite-a.r
Normal file
220
OSBindings/Mac/Clock SignalTests/jeek816/suite-a.r
Normal file
@ -0,0 +1,220 @@
|
||||
|
||||
; ******** Source: suite-a.asm
|
||||
1 !cpu 65816
|
||||
2
|
||||
3 ; 2017-12-13 J.E. Klasek j+816 AT klasek DOT at
|
||||
4
|
||||
5
|
||||
6
|
||||
7 videoram = $0400
|
||||
8 colorram = $d800
|
||||
9
|
||||
10 ;-------------------------------------------------------------------------------
|
||||
11 *=$07ff
|
||||
12 07ff 0108 !word $0801
|
||||
13 0801 0b08 !word bend
|
||||
14 0803 0a00 !word 10
|
||||
15 0805 9e !byte $9e
|
||||
16 0806 3230363100 !text "2061", 0
|
||||
17 080b 0000 bend: !word 0
|
||||
18 ;-------------------------------------------------------------------------------
|
||||
19
|
||||
20 080d 78 sei
|
||||
21 080e a917 lda #$17
|
||||
22 0810 8d18d0 sta $d018
|
||||
23 0813 a935 lda #$35
|
||||
24 0815 8501 sta $01
|
||||
25 0817 a97f lda #$7f
|
||||
26 0819 8d0ddc sta $dc0d
|
||||
27 081c 8d0ddd sta $dd0d
|
||||
28 081f ad0ddc lda $dc0d
|
||||
29 0822 ad0ddd lda $dd0d
|
||||
30 0825 a200 ldx #0
|
||||
31 -
|
||||
32 0827 a920 lda #$20
|
||||
33 0829 9d0004 sta videoram,x
|
||||
34 082c 9d0005 sta videoram+$0100,x
|
||||
35 082f 9d0006 sta videoram+$0200,x
|
||||
36 0832 9d0007 sta videoram+$0300,x
|
||||
37 0835 a901 lda #1
|
||||
38 0837 9d00d8 sta colorram,x
|
||||
39 083a 9d00d9 sta colorram+$0100,x
|
||||
40 083d 9d00da sta colorram+$0200,x
|
||||
41 0840 9d00db sta colorram+$0300,x
|
||||
42 0843 e8 inx
|
||||
43 0844 d0e1 bne -
|
||||
44
|
||||
45 0846 4c0010 jmp start
|
||||
46 theend:
|
||||
47 0849 e230 sep #$30 ; 8-bit for X/Y and A/M
|
||||
48 !as
|
||||
49 !rs
|
||||
50 084b af100204 lda $040210
|
||||
51 084f c9ff cmp #$ff
|
||||
52 0851 d00d bne error
|
||||
53
|
||||
54 0853 a905 lda #5
|
||||
55 0855 8d20d0 sta $d020
|
||||
56 0858 a200 ldx #0 ; success
|
||||
57 085a 8effd7 stx $d7ff
|
||||
58 085d 4c5d08 jmp *
|
||||
59 error
|
||||
60 0860 8d0004 sta $0400
|
||||
61 0863 af110204 lda $040211 ; failure map (which test failed)
|
||||
62 0867 8d0104 sta $0401
|
||||
63 086a a90a lda #10
|
||||
64 086c 8d20d0 sta $d020
|
||||
65 086f a2ff ldx #$ff ; failure
|
||||
66 0871 8effd7 stx $d7ff
|
||||
67 0874 4c7408 jmp *
|
||||
68
|
||||
69 ;-------------------------------------------------------------------------------
|
||||
70
|
||||
71 * = $1000
|
||||
72 start:
|
||||
73 ; EXPECTED FINAL RESULTS: $0210 = FF
|
||||
74 ; (any other number will be the
|
||||
75 ; test that failed)
|
||||
76
|
||||
77 ; initialize:
|
||||
78 1000 a900 lda #$00
|
||||
79 1002 8f100204 sta $040210
|
||||
80 1006 8f110204 sta $040211
|
||||
81
|
||||
82
|
||||
83 test00:
|
||||
84
|
||||
85 ; setup cpu
|
||||
86 100a 18 clc
|
||||
87 100b fb xce ; native mode
|
||||
88 100c c230 rep #$30 ; 16-bit for X/Y and A/M
|
||||
89 !al
|
||||
90 !rl
|
||||
91
|
||||
92 ; setup registers
|
||||
93 100e a90404 lda #$0404 ; Data Bank Register register
|
||||
94 1011 48 pha ; akku in 16 bit
|
||||
95 1012 ab plb ; pull DBR twice
|
||||
96 1013 ab plb
|
||||
97 1014 a08888 ldy #$8888 ; change marker
|
||||
98 1017 bb tyx
|
||||
99 1018 98 tya
|
||||
100
|
||||
101 ; setup memory
|
||||
102 1019 a95555 lda #$5555 ; wrap marker
|
||||
103 101c 8f878804 sta $048887 ; into bank 4, for LDX/LDY
|
||||
104 1020 a97777 lda #$7777 ; no-wrap marker
|
||||
105 1023 8f878805 sta $058887 ; into bank 5, for LDX/LDY
|
||||
106
|
||||
107 ;---------------------------------------------------------------------
|
||||
108
|
||||
109 1027 9c0000 stz $0000 ; init wrap marker
|
||||
110 102a a97777 lda #$7777 ; no-wrap marker
|
||||
111 102d 8f000005 sta $050000 ; to start of bank 5
|
||||
112
|
||||
113 1031 8cffff sty $ffff ; high byte of Y is where?
|
||||
114 1034 ad0000 lda $0000
|
||||
115 1037 d011 bne +
|
||||
116 1039 adffff lda $ffff ; fetch, does not wrap
|
||||
117 103c c98888 cmp #$8888
|
||||
118 103f d009 bne +
|
||||
119 1041 af000005 lda $050000
|
||||
120 1045 c98877 cmp #$7788 ; write to bank 5
|
||||
121 1048 f004 beq ++
|
||||
122 104a ee1002 + inc $0210 ; fail counter
|
||||
123 104d 18 clc
|
||||
124 ++
|
||||
125 104e 2e1102 rol $0211 ; update failure map
|
||||
126 ;---------------------------------------------------------------------
|
||||
127
|
||||
128 1051 9c0000 stz $0000 ; init wrap marker
|
||||
129 1054 a97777 lda #$7777 ; no-wrap marker
|
||||
130 1057 8f000005 sta $050000 ; to start of bank 5
|
||||
131
|
||||
132 105b bb tyx ; change marker
|
||||
133 105c 8effff stx $ffff ; high byte of Y is where?
|
||||
134 105f ad0000 lda $0000
|
||||
135 1062 d011 bne +
|
||||
136 1064 adffff lda $ffff ; fetch, does not wrap
|
||||
137 1067 c98888 cmp #$8888
|
||||
138 106a d009 bne +
|
||||
139 106c af000005 lda $050000
|
||||
140 1070 c98877 cmp #$7788 ; write to bank 5
|
||||
141 1073 f004 beq ++
|
||||
142 1075 ee1002 + inc $0210 ; fail counter
|
||||
143 1078 18 clc
|
||||
144 ++
|
||||
145 1079 2e1102 rol $0211 ; update failure map
|
||||
146 ;---------------------------------------------------------------------
|
||||
147
|
||||
148 107c bcffff ldy $ffff,x ; Y=5555 Y=7777 value for Y comes from which bank?
|
||||
149 107f c07777 cpy #$7777
|
||||
150 1082 f004 beq +
|
||||
151 1084 ee1002 inc $0210 ; fail counter
|
||||
152 1087 18 clc
|
||||
153 +
|
||||
154 1088 2e1102 rol $0211 ; update failure map
|
||||
155 ;---------------------------------------------------------------------
|
||||
156
|
||||
157 108b 9b txy ; reinitialize y
|
||||
158 108c beffff ldx $ffff,y ; X=5555 X=7777 value for X comes from which bank?
|
||||
159 108f e07777 cpx #$7777
|
||||
160 1092 f004 beq +
|
||||
161 1094 ee1002 inc $0210 ; fail counter
|
||||
162 1097 18 clc
|
||||
163 +
|
||||
164 1098 2e1102 rol $0211 ; update failure map
|
||||
165 ;---------------------------------------------------------------------
|
||||
166
|
||||
167 109b 9c0000 stz $0000 ; init wrap marker
|
||||
168 109e a97777 lda #$7777 ; no-wrap marker
|
||||
169 10a1 8f000005 sta $050000 ; to start of bank 5
|
||||
170
|
||||
171 10a5 a98877 lda #$7788
|
||||
172 10a8 ee0000 inc $0000 ; $0000 = 1
|
||||
173 10ab 1cffff trb $ffff ; 88 77 & ^(88 77) -> 00 00
|
||||
174 10ae ad0000 lda $0000
|
||||
175 10b1 c90100 cmp #$0001 ; $0000 not reset by trb (does not wrap)
|
||||
176 10b4 d009 bne +
|
||||
177 10b6 af000005 lda $050000
|
||||
178 10ba c90077 cmp #$7700 ; $050001 reset by trb
|
||||
179 10bd f004 beq ++
|
||||
180 10bf ee1002 + inc $0210 ; fail counter
|
||||
181 10c2 18 clc
|
||||
182 ++
|
||||
183 10c3 2e1102 rol $0211 ; update failure map
|
||||
184 ;---------------------------------------------------------------------
|
||||
185
|
||||
186 10c6 a98877 lda #$7788
|
||||
187 10c9 8f000005 sta $050000 ; 00 88 | 88 77 -> 88 ff
|
||||
188 10cd 0cffff tsb $ffff ; set bits (which are already cleared)
|
||||
189 10d0 ad0000 lda $0000
|
||||
190 10d3 c90100 cmp #$0001 ; $0000 not set by tsb (does not wrap!)
|
||||
191 10d6 d009 bne +
|
||||
192 10d8 af000005 lda $050000
|
||||
193 10dc c9ff77 cmp #$77ff ; $050001 all bits set by tsb
|
||||
194 10df f004 beq ++
|
||||
195 10e1 ee1002 + inc $0210 ; fail counter
|
||||
196 10e4 18 clc
|
||||
197 ++
|
||||
198 10e5 2e1102 rol $0211 ; update failure map
|
||||
199 ;---------------------------------------------------------------------
|
||||
200
|
||||
201
|
||||
202 test00pass:
|
||||
203 10e8 ad1002 lda $0210
|
||||
204 10eb 49003f eor #%0011111100000000 ; invert failure map
|
||||
205 10ee 8d1002 sta $0210
|
||||
206 10f1 d003 bne +
|
||||
207 10f3 ce1002 dec $0210 ; 0 -> FF
|
||||
208 +
|
||||
209
|
||||
210 10f6 a90000 lda #$0000
|
||||
211 10f9 48 pha
|
||||
212 10fa ab plb
|
||||
213 10fb ab plb ; program bank = 0
|
||||
214 10fc 38 sec
|
||||
215 10fd fb xce ; emulation mode
|
||||
216 10fe e230 sep #$30 ; a/m, x/y 8 bit
|
||||
217
|
||||
218 1100 4c4908 jmp theend
|
147
OSBindings/Mac/Clock SignalTests/jeek816/test-816.txt
Normal file
147
OSBindings/Mac/Clock SignalTests/jeek816/test-816.txt
Normal file
@ -0,0 +1,147 @@
|
||||
65816 behavior on VICE and true hardware
|
||||
========================================
|
||||
|
||||
Johann Klasek, j AT klasek at
|
||||
2017-12-02
|
||||
|
||||
|
||||
Testenvironment:
|
||||
|
||||
Hardware:
|
||||
Tested on LTC64, a turbo card for the Commodore 64
|
||||
in-place of CPU 6510 (which moves to the card)
|
||||
with a WDC's 65C816, 32 KByte ROM and 256 KByte RAM.
|
||||
based on magazine article c't "C64 aufgemotzt", 1987/06 p. 94
|
||||
http://klasek.at/c64/ltc64 (german)
|
||||
Redesigned and implemented by Christoph Egretzberger
|
||||
and software integration by Johann Klasek
|
||||
|
||||
Software:
|
||||
Jamaica Monitor (Jammon) 4.1, native '816
|
||||
C64 ROMs (Kernal+BASIC)
|
||||
|
||||
---------------------------------------------------------------------
|
||||
Case 1: Wrap-around for addressing mode "absolute"
|
||||
for opcodes TSB, TRB, STX, STY, LDX, LDY
|
||||
|
||||
VICE WDC
|
||||
!e
|
||||
a 1000 clc
|
||||
xce native mode
|
||||
rep #$30 16-bit for X/Y and A/M
|
||||
lda #$0404 Data Bank Register register
|
||||
pha akku in 16 bit
|
||||
plb pull DBR twice
|
||||
plb
|
||||
lda #$7777 no-wrap marker
|
||||
sta $050000 to bank 5
|
||||
sta $058887
|
||||
lda #$5555 unchanged marker
|
||||
sta $048887 into bank 4
|
||||
ldy #$8888 change marker
|
||||
tyx now in X and Y
|
||||
sty $ffff high byte of Y is where?
|
||||
ldy $ffff,x Y=5555 Y=7777 value for Y comes from which bank?
|
||||
sty $0002 save as intermediate result, because we do more
|
||||
lda $ffff A=7788 A=8888 fetch, does no wrap even on VICE
|
||||
ldx $0000 X=2088 X=20b5 see if wrapping occured (from sty above)
|
||||
eor #$ffff A=8877 A=7777 invert akku, 2nd part ...
|
||||
trb $ffff M=0088 M=8888 reset bits (which are already cleared)
|
||||
tsb $ffff M=88ff M=ffff set these bits giving a $ffff value
|
||||
ldy $ffff Y=88ff Y=ffff back to ldy test
|
||||
brk
|
||||
|
||||
!d Jammon alternate display mode
|
||||
d disassemble on current PC
|
||||
m 04ffff show memory including 050000
|
||||
m 040000 start of bank 4 (wrap around area),
|
||||
and intermediate result in 040002
|
||||
m 048887 X indexed wrap area
|
||||
m 058887 X indexed no wrap area
|
||||
z 1000 start single-stepping
|
||||
z single step until BRK or ...
|
||||
g go to BRK
|
||||
|
||||
Results VICE WDC
|
||||
|
||||
$050001 $77 $77 marker, never changed
|
||||
$050000 $77 $ff modified by TRB/TSB
|
||||
$04ffff $ff $ff modified by TRB/TSB
|
||||
$040003 $55 $77 read value (low) from LDY $FFFF,X
|
||||
$040002 $55 $77 read value (high) from LDY $FFFF,X
|
||||
read value
|
||||
$040000 $88 ?? (if no wrapping for STY $FFFF, TRB/TSB $FFFF)
|
||||
only changed if wrapping
|
||||
$058888 $77 $77 marker, never changed
|
||||
$058887 $77 $77 marker, never changed
|
||||
$048888 $55 $55 marker, never changed
|
||||
$048887 $55 $55 marker, never changed
|
||||
|
||||
A: $8877 $7777 value from $04ffff/$050000 after STY $FFFF
|
||||
X: $??88 $???? value from $040000/$040001 with high byte of Y
|
||||
in low byte of X after STY $FFFF
|
||||
Y: $88ff $ffff value from TRB/TSB $FFFF
|
||||
|
||||
|
||||
|
||||
---------------------------------------------------------------------
|
||||
Case 2: Wrap-around for Direct Page Indexed Addressing
|
||||
in Emulation Mode
|
||||
|
||||
### Variant 1
|
||||
|
||||
r
|
||||
# set M X E to "1"
|
||||
e
|
||||
# set DBR to 00
|
||||
|
||||
a 1000 sec
|
||||
xce emulation mode
|
||||
lda #$20 set highbyte
|
||||
xba
|
||||
lda #$00 lowbyte 16-bit akku
|
||||
tcd direct page address to $2000
|
||||
ldx #$20 index
|
||||
lda #$88 marker for wrap
|
||||
sta $2010
|
||||
lda #$77 marker for no-wrap
|
||||
sta $2110
|
||||
lda $f0,x which marker?
|
||||
stz $f0,x write (non-6502)
|
||||
brk
|
||||
|
||||
!d Jammon alternate display mode
|
||||
d disassemble on PC
|
||||
m 2010 show wrap area
|
||||
m 2110 show no-wrap area
|
||||
z 1000 start single-stepping
|
||||
z single step until BRK or ...
|
||||
g go to BRK
|
||||
|
||||
Results VICE WDC
|
||||
|
||||
$2010 00 00 wrap area: =00 from STZ
|
||||
$2110 77 77 no-wrap area
|
||||
|
||||
A: $2088 $2088 A low: 88 = wrap, 77 = no-wrap
|
||||
|
||||
|
||||
### Variant 2
|
||||
|
||||
Based on variant 1:
|
||||
|
||||
a 1005 lda #$01 direct page not aligned!
|
||||
tcd direct page at $2001
|
||||
ldx #$1f correction to hit the
|
||||
same memory locations
|
||||
for monitoring
|
||||
|
||||
g 1000
|
||||
|
||||
Results VICE WDC
|
||||
|
||||
$2011 88 88 wrap area: =00 from STZ
|
||||
$2111 00 00 no-wrap area
|
||||
|
||||
A: $2077 $2077 A low: 88 = wrap, 77 = no-wrap
|
||||
|
@ -91,6 +91,7 @@ SOURCES += \
|
||||
\
|
||||
$$SRC/Processors/6502/Implementation/*.cpp \
|
||||
$$SRC/Processors/6502/State/*.cpp \
|
||||
$$SRC/Processors/65816/Implementation/*.cpp \
|
||||
$$SRC/Processors/68000/Implementation/*.cpp \
|
||||
$$SRC/Processors/68000/State/*.cpp \
|
||||
$$SRC/Processors/Z80/Implementation/*.cpp \
|
||||
@ -214,6 +215,10 @@ HEADERS += \
|
||||
$$SRC/Processors/6502/*.hpp \
|
||||
$$SRC/Processors/6502/Implementation/*.hpp \
|
||||
$$SRC/Processors/6502/State/*.hpp \
|
||||
$$SRC/Processors/6502Esque/*.hpp \
|
||||
$$SRC/Processors/6502Esque/Implementation/*.hpp \
|
||||
$$SRC/Processors/65816/*.hpp \
|
||||
$$SRC/Processors/65816/Implementation/*.hpp \
|
||||
$$SRC/Processors/68000/*.hpp \
|
||||
$$SRC/Processors/68000/Implementation/*.hpp \
|
||||
$$SRC/Processors/68000/State/*.hpp \
|
||||
|
@ -287,7 +287,7 @@ void MainWindow::launchMachine() {
|
||||
// Populate request text.
|
||||
QString requestText = romRequestBaseText;
|
||||
size_t index = 0;
|
||||
for(const auto rom: missingRoms) {
|
||||
for(const auto &rom: missingRoms) {
|
||||
requestText += "• ";
|
||||
requestText += rom.descriptive_name.c_str();
|
||||
|
||||
|
@ -85,6 +85,7 @@ SOURCES += glob.glob('../../Outputs/OpenGL/Primitives/*.cpp')
|
||||
|
||||
SOURCES += glob.glob('../../Processors/6502/Implementation/*.cpp')
|
||||
SOURCES += glob.glob('../../Processors/6502/State/*.cpp')
|
||||
SOURCES += glob.glob('../../Processors/65816/Implementation/*.cpp')
|
||||
SOURCES += glob.glob('../../Processors/68000/Implementation/*.cpp')
|
||||
SOURCES += glob.glob('../../Processors/68000/State/*.cpp')
|
||||
SOURCES += glob.glob('../../Processors/Z80/Implementation/*.cpp')
|
||||
|
@ -13,24 +13,19 @@
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
|
||||
#include "../6502Esque/6502Esque.hpp"
|
||||
#include "../6502Esque/Implementation/LazyFlags.hpp"
|
||||
#include "../RegisterSizes.hpp"
|
||||
#include "../../ClockReceiver/ClockReceiver.hpp"
|
||||
|
||||
namespace CPU {
|
||||
namespace MOS6502 {
|
||||
|
||||
/*
|
||||
The list of registers that can be accessed via @c set_value_of_register and @c set_value_of_register.
|
||||
*/
|
||||
enum Register {
|
||||
LastOperationAddress,
|
||||
ProgramCounter,
|
||||
StackPointer,
|
||||
Flags,
|
||||
A,
|
||||
X,
|
||||
Y
|
||||
};
|
||||
// Adopt a bunch of things from MOS6502Esque.
|
||||
using BusOperation = CPU::MOS6502Esque::BusOperation;
|
||||
using BusHandler = CPU::MOS6502Esque::BusHandler<uint16_t>;
|
||||
using Register = CPU::MOS6502Esque::Register;
|
||||
using Flag = CPU::MOS6502Esque::Flag;
|
||||
|
||||
/*
|
||||
The list of 6502 variants supported by this implementation.
|
||||
@ -48,76 +43,11 @@ enum Personality {
|
||||
#define has_bbrbbsrmbsmb(p) ((p) >= Personality::PRockwell65C02)
|
||||
#define has_stpwai(p) ((p) >= Personality::PWDC65C02)
|
||||
|
||||
/*
|
||||
Flags as defined on the 6502; can be used to decode the result of @c get_value_of_register(Flags) or to form a value for
|
||||
the corresponding set.
|
||||
*/
|
||||
enum Flag: uint8_t {
|
||||
Sign = 0x80,
|
||||
Overflow = 0x40,
|
||||
Always = 0x20,
|
||||
Break = 0x10,
|
||||
Decimal = 0x08,
|
||||
Interrupt = 0x04,
|
||||
Zero = 0x02,
|
||||
Carry = 0x01
|
||||
};
|
||||
|
||||
/*!
|
||||
Subclasses will be given the task of performing bus operations, allowing them to provide whatever interface they like
|
||||
between a 6502 and the rest of the system. @c BusOperation lists the types of bus operation that may be requested.
|
||||
|
||||
@c None is reserved for internal use. It will never be requested from a subclass. It is safe always to use the
|
||||
isReadOperation macro to make a binary choice between reading and writing.
|
||||
*/
|
||||
enum BusOperation {
|
||||
Read, ReadOpcode, Write, Ready, None
|
||||
};
|
||||
|
||||
/*!
|
||||
Evaluates to `true` if the operation is a read; `false` if it is a write.
|
||||
*/
|
||||
#define isReadOperation(v) (v == CPU::MOS6502::BusOperation::Read || v == CPU::MOS6502::BusOperation::ReadOpcode)
|
||||
|
||||
/*!
|
||||
An opcode that is guaranteed to cause the CPU to jam.
|
||||
*/
|
||||
extern const uint8_t JamOpcode;
|
||||
|
||||
/*!
|
||||
A class providing empty implementations of the methods a 6502 uses to access the bus. To wire the 6502 to a bus,
|
||||
machines should subclass BusHandler and then declare a realisation of the 6502 template, suplying their bus
|
||||
handler.
|
||||
*/
|
||||
class BusHandler {
|
||||
public:
|
||||
/*!
|
||||
Announces that the 6502 has performed the cycle defined by operation, address and value. On the 6502,
|
||||
all bus cycles take one clock cycle so the amoutn of time advanced is implicit.
|
||||
|
||||
@param operation The type of bus cycle: read, read opcode (i.e. read, with sync active),
|
||||
write or ready.
|
||||
@param address The value of the address bus during this bus cycle.
|
||||
@param value If this is a cycle that puts a value onto the data bus, *value is that value. If this is
|
||||
a cycle that reads the bus, the bus handler should write a value to *value. Writing to *value during
|
||||
a read cycle will produce undefined behaviour.
|
||||
|
||||
@returns The number of cycles that passed in objective time while this 6502 bus cycle was ongoing.
|
||||
On an archetypal machine this will be Cycles(1) but some architectures may choose not to clock the 6502
|
||||
during some periods; one way to simulate that is to have the bus handler return a number other than
|
||||
Cycles(1) to describe lengthened bus cycles.
|
||||
*/
|
||||
Cycles perform_bus_operation([[maybe_unused]] CPU::MOS6502::BusOperation operation, [[maybe_unused]] uint16_t address, [[maybe_unused]] uint8_t *value) {
|
||||
return Cycles(1);
|
||||
}
|
||||
|
||||
/*!
|
||||
Announces completion of all the cycles supplied to a .run_for request on the 6502. Intended to allow
|
||||
bus handlers to perform any deferred output work.
|
||||
*/
|
||||
void flush() {}
|
||||
};
|
||||
|
||||
#include "Implementation/6502Storage.hpp"
|
||||
|
||||
/*!
|
||||
@ -205,12 +135,12 @@ class ProcessorBase: public ProcessorStorage {
|
||||
can also nominate whether the processor includes support for the ready line. Declining to support the ready line
|
||||
can produce a minor runtime performance improvement.
|
||||
*/
|
||||
template <Personality personality, typename T, bool uses_ready_line> class Processor: public ProcessorBase {
|
||||
template <Personality personality, typename BusHandler, bool uses_ready_line> class Processor: public ProcessorBase {
|
||||
public:
|
||||
/*!
|
||||
Constructs an instance of the 6502 that will use @c bus_handler for all bus communications.
|
||||
*/
|
||||
Processor(T &bus_handler) : ProcessorBase(personality), bus_handler_(bus_handler) {}
|
||||
Processor(BusHandler &bus_handler) : ProcessorBase(personality), bus_handler_(bus_handler) {}
|
||||
|
||||
/*!
|
||||
Runs the 6502 for a supplied number of cycles.
|
||||
@ -227,7 +157,7 @@ template <Personality personality, typename T, bool uses_ready_line> class Proce
|
||||
void set_ready_line(bool active);
|
||||
|
||||
private:
|
||||
T &bus_handler_;
|
||||
BusHandler &bus_handler_;
|
||||
};
|
||||
|
||||
#include "Implementation/6502Implementation.hpp"
|
||||
|
@ -11,28 +11,51 @@
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
//#define BE_NOISY
|
||||
|
||||
using namespace CPU::MOS6502;
|
||||
|
||||
namespace {
|
||||
|
||||
template <Personality personality> class ConcreteAllRAMProcessor: public AllRAMProcessor, public BusHandler {
|
||||
using Type = CPU::MOS6502Esque::Type;
|
||||
|
||||
template <Type type> class ConcreteAllRAMProcessor: public AllRAMProcessor, public BusHandler {
|
||||
public:
|
||||
ConcreteAllRAMProcessor() :
|
||||
ConcreteAllRAMProcessor(size_t memory_size) :
|
||||
AllRAMProcessor(memory_size),
|
||||
mos6502_(*this) {
|
||||
mos6502_.set_power_on(false);
|
||||
}
|
||||
|
||||
inline Cycles perform_bus_operation(BusOperation operation, uint16_t address, uint8_t *value) {
|
||||
inline Cycles perform_bus_operation(BusOperation operation, uint32_t address, uint8_t *value) {
|
||||
timestamp_ += Cycles(1);
|
||||
|
||||
if(operation == BusOperation::ReadOpcode) {
|
||||
#ifdef BE_NOISY
|
||||
printf("[%04x] %02x a:%04x x:%04x y:%04x p:%02x s:%02x\n", address, memory_[address],
|
||||
mos6502_.get_value_of_register(Register::A),
|
||||
mos6502_.get_value_of_register(Register::X),
|
||||
mos6502_.get_value_of_register(Register::Y),
|
||||
mos6502_.get_value_of_register(Register::Flags) & 0xff,
|
||||
mos6502_.get_value_of_register(Register::StackPointer) & 0xff);
|
||||
#endif
|
||||
check_address_for_trap(address);
|
||||
}
|
||||
|
||||
if(isReadOperation(operation)) {
|
||||
*value = memory_[address];
|
||||
#ifdef BE_NOISY
|
||||
// if((address&0xff00) == 0x100) {
|
||||
printf("%04x -> %02x\n", address, *value);
|
||||
// }
|
||||
#endif
|
||||
} else {
|
||||
memory_[address] = *value;
|
||||
#ifdef BE_NOISY
|
||||
// if((address&0xff00) == 0x100) {
|
||||
printf("%04x <- %02x\n", address, *value);
|
||||
// }
|
||||
#endif
|
||||
}
|
||||
|
||||
return Cycles(1);
|
||||
@ -63,20 +86,21 @@ template <Personality personality> class ConcreteAllRAMProcessor: public AllRAMP
|
||||
}
|
||||
|
||||
private:
|
||||
CPU::MOS6502::Processor<personality, ConcreteAllRAMProcessor, false> mos6502_;
|
||||
CPU::MOS6502Esque::Processor<type, ConcreteAllRAMProcessor, false> mos6502_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
AllRAMProcessor *AllRAMProcessor::Processor(Personality personality) {
|
||||
#define Bind(p) case p: return new ConcreteAllRAMProcessor<p>();
|
||||
switch(personality) {
|
||||
AllRAMProcessor *AllRAMProcessor::Processor(Type type) {
|
||||
#define Bind(p) case p: return new ConcreteAllRAMProcessor<p>(type == Type::TWDC65816 ? 16*1024*1024 : 64*1024);
|
||||
switch(type) {
|
||||
default:
|
||||
Bind(Personality::P6502)
|
||||
Bind(Personality::PNES6502)
|
||||
Bind(Personality::PSynertek65C02)
|
||||
Bind(Personality::PWDC65C02)
|
||||
Bind(Personality::PRockwell65C02)
|
||||
Bind(Type::T6502)
|
||||
Bind(Type::TNES6502)
|
||||
Bind(Type::TSynertek65C02)
|
||||
Bind(Type::TWDC65C02)
|
||||
Bind(Type::TRockwell65C02)
|
||||
Bind(Type::TWDC65816)
|
||||
}
|
||||
#undef Bind
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
#ifndef MOS6502AllRAM_cpp
|
||||
#define MOS6502AllRAM_cpp
|
||||
|
||||
#include "../6502.hpp"
|
||||
#include "../../6502Esque/6502Selector.hpp"
|
||||
#include "../../AllRAMProcessor.hpp"
|
||||
|
||||
namespace CPU {
|
||||
@ -17,9 +17,8 @@ namespace MOS6502 {
|
||||
|
||||
class AllRAMProcessor:
|
||||
public ::CPU::AllRAMProcessor {
|
||||
|
||||
public:
|
||||
static AllRAMProcessor *Processor(Personality personality);
|
||||
static AllRAMProcessor *Processor(CPU::MOS6502Esque::Type type);
|
||||
virtual ~AllRAMProcessor() {}
|
||||
|
||||
virtual void run_for(const Cycles cycles) = 0;
|
||||
@ -30,7 +29,7 @@ class AllRAMProcessor:
|
||||
virtual void set_value_of_register(Register r, uint16_t value) = 0;
|
||||
|
||||
protected:
|
||||
AllRAMProcessor() : ::CPU::AllRAMProcessor(65536) {}
|
||||
AllRAMProcessor(size_t memory_size) : ::CPU::AllRAMProcessor(memory_size) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -8,8 +8,6 @@
|
||||
|
||||
#include "../6502.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
using namespace CPU::MOS6502;
|
||||
|
||||
const uint8_t CPU::MOS6502::JamOpcode = 0xf2;
|
||||
|
@ -13,16 +13,6 @@
|
||||
*/
|
||||
|
||||
template <Personality personality, typename T, bool uses_ready_line> void Processor<personality, T, uses_ready_line>::run_for(const Cycles cycles) {
|
||||
static uint8_t throwaway_target;
|
||||
|
||||
// These plus program below act to give the compiler permission to update these values
|
||||
// without touching the class storage (i.e. it explicitly says they need be completely up
|
||||
// to date in this stack frame only); which saves some complicated addressing
|
||||
RegisterPair16 nextAddress = next_address_;
|
||||
BusOperation nextBusOperation = next_bus_operation_;
|
||||
uint16_t busAddress = bus_address_;
|
||||
uint8_t *busValue = bus_value_;
|
||||
|
||||
#define checkSchedule() \
|
||||
if(!scheduled_program_counter_) {\
|
||||
if(interrupt_requests_) {\
|
||||
@ -42,9 +32,9 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
|
||||
#define bus_access() \
|
||||
interrupt_requests_ = (interrupt_requests_ & ~InterruptRequestFlags::IRQ) | irq_request_history_; \
|
||||
irq_request_history_ = irq_line_ & inverse_interrupt_flag_; \
|
||||
number_of_cycles -= bus_handler_.perform_bus_operation(nextBusOperation, busAddress, busValue); \
|
||||
nextBusOperation = BusOperation::None; \
|
||||
irq_request_history_ = irq_line_ & flags_.inverse_interrupt; \
|
||||
number_of_cycles -= bus_handler_.perform_bus_operation(next_bus_operation_, bus_address_, bus_value_); \
|
||||
next_bus_operation_ = BusOperation::None; \
|
||||
if(number_of_cycles <= Cycles(0)) break;
|
||||
|
||||
checkSchedule();
|
||||
@ -54,12 +44,12 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
|
||||
// Deal with a potential RDY state, if this 6502 has anything connected to ready.
|
||||
while(uses_ready_line && ready_is_active_ && number_of_cycles > Cycles(0)) {
|
||||
number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, busAddress, busValue);
|
||||
number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, bus_address_, bus_value_);
|
||||
}
|
||||
|
||||
// Deal with a potential STP state, if this 6502 implements STP.
|
||||
while(has_stpwai(personality) && stop_is_active_ && number_of_cycles > Cycles(0)) {
|
||||
number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, busAddress, busValue);
|
||||
number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, bus_address_, bus_value_);
|
||||
if(interrupt_requests_ & InterruptRequestFlags::Reset) {
|
||||
stop_is_active_ = false;
|
||||
checkSchedule();
|
||||
@ -69,8 +59,8 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
|
||||
// Deal with a potential WAI state, if this 6502 implements WAI.
|
||||
while(has_stpwai(personality) && wait_is_active_ && number_of_cycles > Cycles(0)) {
|
||||
number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, busAddress, busValue);
|
||||
interrupt_requests_ |= (irq_line_ & inverse_interrupt_flag_);
|
||||
number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, bus_address_, bus_value_);
|
||||
interrupt_requests_ |= (irq_line_ & flags_.inverse_interrupt);
|
||||
if(interrupt_requests_ & InterruptRequestFlags::NMI || irq_line_) {
|
||||
wait_is_active_ = false;
|
||||
checkSchedule();
|
||||
@ -79,7 +69,7 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
}
|
||||
|
||||
if((!uses_ready_line || !ready_is_active_) && (!has_stpwai(personality) || (!wait_is_active_ && !stop_is_active_))) {
|
||||
if(nextBusOperation != BusOperation::None) {
|
||||
if(next_bus_operation_ != BusOperation::None) {
|
||||
bus_access();
|
||||
}
|
||||
|
||||
@ -88,10 +78,10 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
const MicroOp cycle = *scheduled_program_counter_;
|
||||
scheduled_program_counter_++;
|
||||
|
||||
#define read_op(val, addr) nextBusOperation = BusOperation::ReadOpcode; busAddress = addr; busValue = &val; val = 0xff
|
||||
#define read_mem(val, addr) nextBusOperation = BusOperation::Read; busAddress = addr; busValue = &val; val = 0xff
|
||||
#define throwaway_read(addr) nextBusOperation = BusOperation::Read; busAddress = addr; busValue = &throwaway_target; throwaway_target = 0xff
|
||||
#define write_mem(val, addr) nextBusOperation = BusOperation::Write; busAddress = addr; busValue = &val
|
||||
#define read_op(val, addr) next_bus_operation_ = BusOperation::ReadOpcode; bus_address_ = addr; bus_value_ = &val; val = 0xff
|
||||
#define read_mem(val, addr) next_bus_operation_ = BusOperation::Read; bus_address_ = addr; bus_value_ = &val; val = 0xff
|
||||
#define throwaway_read(addr) next_bus_operation_ = BusOperation::Read; bus_address_ = addr; bus_value_ = &bus_throwaway_; bus_throwaway_ = 0xff
|
||||
#define write_mem(val, addr) next_bus_operation_ = BusOperation::Write; bus_address_ = addr; bus_value_ = &val
|
||||
|
||||
switch(cycle) {
|
||||
|
||||
@ -155,37 +145,37 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
|
||||
case OperationBRKPickVector:
|
||||
if(is_65c02(personality)) {
|
||||
nextAddress.full = 0xfffe;
|
||||
next_address_.full = 0xfffe;
|
||||
} else {
|
||||
// NMI can usurp BRK-vector operations on the pre-C 6502s.
|
||||
nextAddress.full = (interrupt_requests_ & InterruptRequestFlags::NMI) ? 0xfffa : 0xfffe;
|
||||
next_address_.full = (interrupt_requests_ & InterruptRequestFlags::NMI) ? 0xfffa : 0xfffe;
|
||||
interrupt_requests_ &= ~InterruptRequestFlags::NMI;
|
||||
}
|
||||
continue;
|
||||
case OperationNMIPickVector: nextAddress.full = 0xfffa; continue;
|
||||
case OperationRSTPickVector: nextAddress.full = 0xfffc; continue;
|
||||
case CycleReadVectorLow: read_mem(pc_.halves.low, nextAddress.full); break;
|
||||
case CycleReadVectorHigh: read_mem(pc_.halves.high, nextAddress.full+1); break;
|
||||
case OperationNMIPickVector: next_address_.full = 0xfffa; continue;
|
||||
case OperationRSTPickVector: next_address_.full = 0xfffc; continue;
|
||||
case CycleReadVectorLow: read_mem(pc_.halves.low, next_address_.full); break;
|
||||
case CycleReadVectorHigh: read_mem(pc_.halves.high, next_address_.full+1); break;
|
||||
case OperationSetIRQFlags:
|
||||
inverse_interrupt_flag_ = 0;
|
||||
if(is_65c02(personality)) decimal_flag_ = false;
|
||||
flags_.inverse_interrupt = 0;
|
||||
if(is_65c02(personality)) flags_.decimal = 0;
|
||||
continue;
|
||||
case OperationSetNMIRSTFlags:
|
||||
if(is_65c02(personality)) decimal_flag_ = false;
|
||||
if(is_65c02(personality)) flags_.decimal = 0;
|
||||
continue;
|
||||
|
||||
case CyclePullPCL: s_++; read_mem(pc_.halves.low, s_ | 0x100); break;
|
||||
case CyclePullPCH: s_++; read_mem(pc_.halves.high, s_ | 0x100); break;
|
||||
case CyclePullA: s_++; read_mem(a_, s_ | 0x100); break;
|
||||
case CyclePullX: s_++; read_mem(x_, s_ | 0x100); break;
|
||||
case CyclePullY: s_++; read_mem(y_, s_ | 0x100); break;
|
||||
case CyclePullOperand: s_++; read_mem(operand_, s_ | 0x100); break;
|
||||
case OperationSetFlagsFromOperand: set_flags(operand_); continue;
|
||||
case OperationSetOperandFromFlagsWithBRKSet: operand_ = get_flags() | Flag::Break; continue;
|
||||
case OperationSetOperandFromFlags: operand_ = get_flags(); continue;
|
||||
case OperationSetFlagsFromA: zero_result_ = negative_result_ = a_; continue;
|
||||
case OperationSetFlagsFromX: zero_result_ = negative_result_ = x_; continue;
|
||||
case OperationSetFlagsFromY: zero_result_ = negative_result_ = y_; continue;
|
||||
case CyclePullPCL: s_++; read_mem(pc_.halves.low, s_ | 0x100); break;
|
||||
case CyclePullPCH: s_++; read_mem(pc_.halves.high, s_ | 0x100); break;
|
||||
case CyclePullA: s_++; read_mem(a_, s_ | 0x100); break;
|
||||
case CyclePullX: s_++; read_mem(x_, s_ | 0x100); break;
|
||||
case CyclePullY: s_++; read_mem(y_, s_ | 0x100); break;
|
||||
case CyclePullOperand: s_++; read_mem(operand_, s_ | 0x100); break;
|
||||
case OperationSetFlagsFromOperand: set_flags(operand_); continue;
|
||||
case OperationSetOperandFromFlagsWithBRKSet: operand_ = flags_.get() | Flag::Break; continue;
|
||||
case OperationSetOperandFromFlags: operand_ = flags_.get(); continue;
|
||||
case OperationSetFlagsFromA: flags_.set_nz(a_); continue;
|
||||
case OperationSetFlagsFromX: flags_.set_nz(x_); continue;
|
||||
case OperationSetFlagsFromY: flags_.set_nz(y_); continue;
|
||||
|
||||
case CycleIncrementPCAndReadStack: pc_.full++; throwaway_read(s_ | 0x100); break;
|
||||
case CycleReadPCLFromAddress: read_mem(pc_.halves.low, address_.full); break;
|
||||
@ -216,17 +206,17 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
|
||||
// MARK: - Bitwise
|
||||
|
||||
case OperationORA: a_ |= operand_; negative_result_ = zero_result_ = a_; continue;
|
||||
case OperationAND: a_ &= operand_; negative_result_ = zero_result_ = a_; continue;
|
||||
case OperationEOR: a_ ^= operand_; negative_result_ = zero_result_ = a_; continue;
|
||||
case OperationORA: a_ |= operand_; flags_.set_nz(a_); continue;
|
||||
case OperationAND: a_ &= operand_; flags_.set_nz(a_); continue;
|
||||
case OperationEOR: a_ ^= operand_; flags_.set_nz(a_); continue;
|
||||
|
||||
// MARK: - Load and Store
|
||||
|
||||
case OperationLDA: a_ = negative_result_ = zero_result_ = operand_; continue;
|
||||
case OperationLDX: x_ = negative_result_ = zero_result_ = operand_; continue;
|
||||
case OperationLDY: y_ = negative_result_ = zero_result_ = operand_; continue;
|
||||
case OperationLAX: a_ = x_ = negative_result_ = zero_result_ = operand_; continue;
|
||||
case OperationCopyOperandToA: a_ = operand_; continue;
|
||||
case OperationLDA: flags_.set_nz(a_ = operand_); continue;
|
||||
case OperationLDX: flags_.set_nz(x_ = operand_); continue;
|
||||
case OperationLDY: flags_.set_nz(y_ = operand_); continue;
|
||||
case OperationLAX: flags_.set_nz(a_ = x_ = operand_); continue;
|
||||
case OperationCopyOperandToA: a_ = operand_; continue;
|
||||
|
||||
case OperationSTA: operand_ = a_; continue;
|
||||
case OperationSTX: operand_ = x_; continue;
|
||||
@ -240,43 +230,43 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
|
||||
case OperationLXA:
|
||||
a_ = x_ = (a_ | 0xee) & operand_;
|
||||
negative_result_ = zero_result_ = a_;
|
||||
flags_.set_nz(a_);
|
||||
continue;
|
||||
|
||||
// MARK: - Compare
|
||||
|
||||
case OperationCMP: {
|
||||
const uint16_t temp16 = a_ - operand_;
|
||||
negative_result_ = zero_result_ = uint8_t(temp16);
|
||||
carry_flag_ = ((~temp16) >> 8)&1;
|
||||
flags_.set_nz(uint8_t(temp16));
|
||||
flags_.carry = ((~temp16) >> 8)&1;
|
||||
} continue;
|
||||
case OperationCPX: {
|
||||
const uint16_t temp16 = x_ - operand_;
|
||||
negative_result_ = zero_result_ = uint8_t(temp16);
|
||||
carry_flag_ = ((~temp16) >> 8)&1;
|
||||
flags_.set_nz(uint8_t(temp16));
|
||||
flags_.carry = ((~temp16) >> 8)&1;
|
||||
} continue;
|
||||
case OperationCPY: {
|
||||
const uint16_t temp16 = y_ - operand_;
|
||||
negative_result_ = zero_result_ = uint8_t(temp16);
|
||||
carry_flag_ = ((~temp16) >> 8)&1;
|
||||
flags_.set_nz(uint8_t(temp16));
|
||||
flags_.carry = ((~temp16) >> 8)&1;
|
||||
} continue;
|
||||
|
||||
// MARK: - BIT, TSB, TRB
|
||||
|
||||
case OperationBIT:
|
||||
zero_result_ = operand_ & a_;
|
||||
negative_result_ = operand_;
|
||||
overflow_flag_ = operand_&Flag::Overflow;
|
||||
flags_.zero_result = operand_ & a_;
|
||||
flags_.negative_result = operand_;
|
||||
flags_.overflow = operand_ & Flag::Overflow;
|
||||
continue;
|
||||
case OperationBITNoNV:
|
||||
zero_result_ = operand_ & a_;
|
||||
flags_.zero_result = operand_ & a_;
|
||||
continue;
|
||||
case OperationTRB:
|
||||
zero_result_ = operand_ & a_;
|
||||
flags_.zero_result = operand_ & a_;
|
||||
operand_ &= ~a_;
|
||||
continue;
|
||||
case OperationTSB:
|
||||
zero_result_ = operand_ & a_;
|
||||
flags_.zero_result = operand_ & a_;
|
||||
operand_ |= a_;
|
||||
continue;
|
||||
|
||||
@ -295,8 +285,8 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
operand_++;
|
||||
[[fallthrough]];
|
||||
case OperationSBC:
|
||||
if(decimal_flag_ && has_decimal_mode(personality)) {
|
||||
const uint16_t notCarry = carry_flag_ ^ 0x1;
|
||||
if(flags_.decimal && has_decimal_mode(personality)) {
|
||||
const uint16_t notCarry = flags_.carry ^ 0x1;
|
||||
const uint16_t decimalResult = uint16_t(a_) - uint16_t(operand_) - notCarry;
|
||||
uint16_t temp16;
|
||||
|
||||
@ -305,17 +295,17 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
temp16 = (temp16&0x0f) | ((temp16 > 0x0f) ? 0xfff0 : 0x00);
|
||||
temp16 += (a_&0xf0) - (operand_&0xf0);
|
||||
|
||||
overflow_flag_ = ( ( (decimalResult^a_)&(~decimalResult^operand_) )&0x80) >> 1;
|
||||
negative_result_ = uint8_t(temp16);
|
||||
zero_result_ = uint8_t(decimalResult);
|
||||
flags_.overflow = ( ( (decimalResult^a_)&(~decimalResult^operand_) )&0x80) >> 1;
|
||||
flags_.negative_result = uint8_t(temp16);
|
||||
flags_.zero_result = uint8_t(decimalResult);
|
||||
|
||||
if(temp16 > 0xff) temp16 -= 0x60;
|
||||
|
||||
carry_flag_ = (temp16 > 0xff) ? 0 : Flag::Carry;
|
||||
flags_.carry = (temp16 > 0xff) ? 0 : Flag::Carry;
|
||||
a_ = uint8_t(temp16);
|
||||
|
||||
if(is_65c02(personality)) {
|
||||
negative_result_ = zero_result_ = a_;
|
||||
flags_.set_nz(a_);
|
||||
read_mem(operand_, address_.full);
|
||||
break;
|
||||
}
|
||||
@ -326,30 +316,30 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
[[fallthrough]];
|
||||
|
||||
case OperationADC:
|
||||
if(decimal_flag_ && has_decimal_mode(personality)) {
|
||||
const uint16_t decimalResult = uint16_t(a_) + uint16_t(operand_) + uint16_t(carry_flag_);
|
||||
if(flags_.decimal && has_decimal_mode(personality)) {
|
||||
const uint16_t decimalResult = uint16_t(a_) + uint16_t(operand_) + uint16_t(flags_.carry);
|
||||
|
||||
uint8_t low_nibble = (a_ & 0xf) + (operand_ & 0xf) + carry_flag_;
|
||||
uint8_t low_nibble = (a_ & 0xf) + (operand_ & 0xf) + flags_.carry;
|
||||
if(low_nibble >= 0xa) low_nibble = ((low_nibble + 0x6) & 0xf) + 0x10;
|
||||
uint16_t result = uint16_t(a_ & 0xf0) + uint16_t(operand_ & 0xf0) + uint16_t(low_nibble);
|
||||
negative_result_ = uint8_t(result);
|
||||
overflow_flag_ = (( (result^a_)&(result^operand_) )&0x80) >> 1;
|
||||
flags_.negative_result = uint8_t(result);
|
||||
flags_.overflow = (( (result^a_)&(result^operand_) )&0x80) >> 1;
|
||||
if(result >= 0xa0) result += 0x60;
|
||||
|
||||
carry_flag_ = (result >> 8) ? 1 : 0;
|
||||
flags_.carry = (result >> 8) ? 1 : 0;
|
||||
a_ = uint8_t(result);
|
||||
zero_result_ = uint8_t(decimalResult);
|
||||
flags_.zero_result = uint8_t(decimalResult);
|
||||
|
||||
if(is_65c02(personality)) {
|
||||
negative_result_ = zero_result_ = a_;
|
||||
flags_.set_nz(a_);
|
||||
read_mem(operand_, address_.full);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
const uint16_t result = uint16_t(a_) + uint16_t(operand_) + uint16_t(carry_flag_);
|
||||
overflow_flag_ = (( (result^a_)&(result^operand_) )&0x80) >> 1;
|
||||
negative_result_ = zero_result_ = a_ = uint8_t(result);
|
||||
carry_flag_ = (result >> 8)&1;
|
||||
const uint16_t result = uint16_t(a_) + uint16_t(operand_) + uint16_t(flags_.carry);
|
||||
flags_.overflow = (( (result^a_)&(result^operand_) )&0x80) >> 1;
|
||||
flags_.set_nz(a_ = uint8_t(result));
|
||||
flags_.carry = (result >> 8)&1;
|
||||
}
|
||||
|
||||
// fix up in case this was INS
|
||||
@ -359,99 +349,99 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
// MARK: - Shifts and Rolls
|
||||
|
||||
case OperationASL:
|
||||
carry_flag_ = operand_ >> 7;
|
||||
flags_.carry = operand_ >> 7;
|
||||
operand_ <<= 1;
|
||||
negative_result_ = zero_result_ = operand_;
|
||||
flags_.set_nz(operand_);
|
||||
continue;
|
||||
|
||||
case OperationASO:
|
||||
carry_flag_ = operand_ >> 7;
|
||||
flags_.carry = operand_ >> 7;
|
||||
operand_ <<= 1;
|
||||
a_ |= operand_;
|
||||
negative_result_ = zero_result_ = a_;
|
||||
flags_.set_nz(a_);
|
||||
continue;
|
||||
|
||||
case OperationROL: {
|
||||
const uint8_t temp8 = uint8_t((operand_ << 1) | carry_flag_);
|
||||
carry_flag_ = operand_ >> 7;
|
||||
operand_ = negative_result_ = zero_result_ = temp8;
|
||||
const uint8_t temp8 = uint8_t((operand_ << 1) | flags_.carry);
|
||||
flags_.carry = operand_ >> 7;
|
||||
flags_.set_nz(operand_ = temp8);
|
||||
} continue;
|
||||
|
||||
case OperationRLA: {
|
||||
const uint8_t temp8 = uint8_t((operand_ << 1) | carry_flag_);
|
||||
carry_flag_ = operand_ >> 7;
|
||||
const uint8_t temp8 = uint8_t((operand_ << 1) | flags_.carry);
|
||||
flags_.carry = operand_ >> 7;
|
||||
operand_ = temp8;
|
||||
a_ &= operand_;
|
||||
negative_result_ = zero_result_ = a_;
|
||||
flags_.set_nz(a_);
|
||||
} continue;
|
||||
|
||||
case OperationLSR:
|
||||
carry_flag_ = operand_ & 1;
|
||||
flags_.carry = operand_ & 1;
|
||||
operand_ >>= 1;
|
||||
negative_result_ = zero_result_ = operand_;
|
||||
flags_.set_nz(operand_);
|
||||
continue;
|
||||
|
||||
case OperationLSE:
|
||||
carry_flag_ = operand_ & 1;
|
||||
flags_.carry = operand_ & 1;
|
||||
operand_ >>= 1;
|
||||
a_ ^= operand_;
|
||||
negative_result_ = zero_result_ = a_;
|
||||
flags_.set_nz(a_);
|
||||
continue;
|
||||
|
||||
case OperationASR:
|
||||
a_ &= operand_;
|
||||
carry_flag_ = a_ & 1;
|
||||
flags_.carry = a_ & 1;
|
||||
a_ >>= 1;
|
||||
negative_result_ = zero_result_ = a_;
|
||||
flags_.set_nz(a_);
|
||||
continue;
|
||||
|
||||
case OperationROR: {
|
||||
const uint8_t temp8 = uint8_t((operand_ >> 1) | (carry_flag_ << 7));
|
||||
carry_flag_ = operand_ & 1;
|
||||
operand_ = negative_result_ = zero_result_ = temp8;
|
||||
const uint8_t temp8 = uint8_t((operand_ >> 1) | (flags_.carry << 7));
|
||||
flags_.carry = operand_ & 1;
|
||||
flags_.set_nz(operand_ = temp8);
|
||||
} continue;
|
||||
|
||||
case OperationRRA: {
|
||||
const uint8_t temp8 = uint8_t((operand_ >> 1) | (carry_flag_ << 7));
|
||||
carry_flag_ = operand_ & 1;
|
||||
const uint8_t temp8 = uint8_t((operand_ >> 1) | (flags_.carry << 7));
|
||||
flags_.carry = operand_ & 1;
|
||||
operand_ = temp8;
|
||||
} continue;
|
||||
|
||||
case OperationDecrementOperand: operand_--; continue;
|
||||
case OperationIncrementOperand: operand_++; continue;
|
||||
|
||||
case OperationCLC: carry_flag_ = 0; continue;
|
||||
case OperationCLI: inverse_interrupt_flag_ = Flag::Interrupt; continue;
|
||||
case OperationCLV: overflow_flag_ = 0; continue;
|
||||
case OperationCLD: decimal_flag_ = 0; continue;
|
||||
case OperationCLC: flags_.carry = 0; continue;
|
||||
case OperationCLI: flags_.inverse_interrupt = Flag::Interrupt; continue;
|
||||
case OperationCLV: flags_.overflow = 0; continue;
|
||||
case OperationCLD: flags_.decimal = 0; continue;
|
||||
|
||||
case OperationSEC: carry_flag_ = Flag::Carry; continue;
|
||||
case OperationSEI: inverse_interrupt_flag_ = 0; continue;
|
||||
case OperationSED: decimal_flag_ = Flag::Decimal; continue;
|
||||
case OperationSEC: flags_.carry = Flag::Carry; continue;
|
||||
case OperationSEI: flags_.inverse_interrupt = 0; continue;
|
||||
case OperationSED: flags_.decimal = Flag::Decimal; continue;
|
||||
|
||||
case OperationINC: operand_++; negative_result_ = zero_result_ = operand_; continue;
|
||||
case OperationDEC: operand_--; negative_result_ = zero_result_ = operand_; continue;
|
||||
case OperationINA: a_++; negative_result_ = zero_result_ = a_; continue;
|
||||
case OperationDEA: a_--; negative_result_ = zero_result_ = a_; continue;
|
||||
case OperationINX: x_++; negative_result_ = zero_result_ = x_; continue;
|
||||
case OperationDEX: x_--; negative_result_ = zero_result_ = x_; continue;
|
||||
case OperationINY: y_++; negative_result_ = zero_result_ = y_; continue;
|
||||
case OperationDEY: y_--; negative_result_ = zero_result_ = y_; continue;
|
||||
case OperationINC: operand_++; flags_.set_nz(operand_); continue;
|
||||
case OperationDEC: operand_--; flags_.set_nz(operand_); continue;
|
||||
case OperationINA: a_++; flags_.set_nz(a_); continue;
|
||||
case OperationDEA: a_--; flags_.set_nz(a_); continue;
|
||||
case OperationINX: x_++; flags_.set_nz(x_); continue;
|
||||
case OperationDEX: x_--; flags_.set_nz(x_); continue;
|
||||
case OperationINY: y_++; flags_.set_nz(y_); continue;
|
||||
case OperationDEY: y_--; flags_.set_nz(y_); continue;
|
||||
|
||||
case OperationANE:
|
||||
a_ = (a_ | 0xee) & operand_ & x_;
|
||||
negative_result_ = zero_result_ = a_;
|
||||
flags_.set_nz(a_);
|
||||
continue;
|
||||
|
||||
case OperationANC:
|
||||
a_ &= operand_;
|
||||
negative_result_ = zero_result_ = a_;
|
||||
carry_flag_ = a_ >> 7;
|
||||
flags_.set_nz(a_);
|
||||
flags_.carry = a_ >> 7;
|
||||
continue;
|
||||
|
||||
case OperationLAS:
|
||||
a_ = x_ = s_ = s_ & operand_;
|
||||
negative_result_ = zero_result_ = a_;
|
||||
flags_.set_nz(a_);
|
||||
continue;
|
||||
|
||||
// MARK: - Addressing Mode Work
|
||||
@ -464,36 +454,36 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
}
|
||||
|
||||
case CycleAddXToAddressLow:
|
||||
nextAddress.full = address_.full + x_;
|
||||
address_.halves.low = nextAddress.halves.low;
|
||||
if(address_.halves.high != nextAddress.halves.high) {
|
||||
next_address_.full = address_.full + x_;
|
||||
address_.halves.low = next_address_.halves.low;
|
||||
if(address_.halves.high != next_address_.halves.high) {
|
||||
page_crossing_stall_read();
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case CycleAddXToAddressLowRead:
|
||||
nextAddress.full = address_.full + x_;
|
||||
address_.halves.low = nextAddress.halves.low;
|
||||
next_address_.full = address_.full + x_;
|
||||
address_.halves.low = next_address_.halves.low;
|
||||
page_crossing_stall_read();
|
||||
break;
|
||||
case CycleAddYToAddressLow:
|
||||
nextAddress.full = address_.full + y_;
|
||||
address_.halves.low = nextAddress.halves.low;
|
||||
if(address_.halves.high != nextAddress.halves.high) {
|
||||
next_address_.full = address_.full + y_;
|
||||
address_.halves.low = next_address_.halves.low;
|
||||
if(address_.halves.high != next_address_.halves.high) {
|
||||
page_crossing_stall_read();
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case CycleAddYToAddressLowRead:
|
||||
nextAddress.full = address_.full + y_;
|
||||
address_.halves.low = nextAddress.halves.low;
|
||||
next_address_.full = address_.full + y_;
|
||||
address_.halves.low = next_address_.halves.low;
|
||||
page_crossing_stall_read();
|
||||
break;
|
||||
|
||||
#undef page_crossing_stall_read
|
||||
|
||||
case OperationCorrectAddressHigh:
|
||||
address_.full = nextAddress.full;
|
||||
address_.full = next_address_.full;
|
||||
continue;
|
||||
case CycleIncrementPCFetchAddressLowFromOperand:
|
||||
pc_.full++;
|
||||
@ -548,7 +538,7 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
throwaway_read(operand_);
|
||||
break;
|
||||
|
||||
case OperationIncrementPC: pc_.full++; continue;
|
||||
case OperationIncrementPC: pc_.full++; continue;
|
||||
case CycleFetchOperandFromAddress: read_mem(operand_, address_.full); break;
|
||||
case CycleWriteOperandToAddress: write_mem(operand_, address_.full); break;
|
||||
|
||||
@ -560,25 +550,25 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
scheduled_program_counter_ = operations_[size_t(OperationsSlot::DoBRA)]; \
|
||||
}
|
||||
|
||||
case OperationBPL: BRA(!(negative_result_&0x80)); continue;
|
||||
case OperationBMI: BRA(negative_result_&0x80); continue;
|
||||
case OperationBVC: BRA(!overflow_flag_); continue;
|
||||
case OperationBVS: BRA(overflow_flag_); continue;
|
||||
case OperationBCC: BRA(!carry_flag_); continue;
|
||||
case OperationBCS: BRA(carry_flag_); continue;
|
||||
case OperationBNE: BRA(zero_result_); continue;
|
||||
case OperationBEQ: BRA(!zero_result_); continue;
|
||||
case OperationBPL: BRA(!(flags_.negative_result&0x80)); continue;
|
||||
case OperationBMI: BRA(flags_.negative_result&0x80); continue;
|
||||
case OperationBVC: BRA(!flags_.overflow); continue;
|
||||
case OperationBVS: BRA(flags_.overflow); continue;
|
||||
case OperationBCC: BRA(!flags_.carry); continue;
|
||||
case OperationBCS: BRA(flags_.carry); continue;
|
||||
case OperationBNE: BRA(flags_.zero_result); continue;
|
||||
case OperationBEQ: BRA(!flags_.zero_result); continue;
|
||||
case OperationBRA: BRA(true); continue;
|
||||
|
||||
#undef BRA
|
||||
|
||||
case CycleAddSignedOperandToPC:
|
||||
nextAddress.full = uint16_t(pc_.full + int8_t(operand_));
|
||||
pc_.halves.low = nextAddress.halves.low;
|
||||
if(nextAddress.halves.high != pc_.halves.high) {
|
||||
uint16_t halfUpdatedPc = pc_.full;
|
||||
pc_.full = nextAddress.full;
|
||||
throwaway_read(halfUpdatedPc);
|
||||
next_address_.full = uint16_t(pc_.full + int8_t(operand_));
|
||||
pc_.halves.low = next_address_.halves.low;
|
||||
if(next_address_.halves.high != pc_.halves.high) {
|
||||
const uint16_t half_updated_pc = pc_.full;
|
||||
pc_.full = next_address_.full;
|
||||
throwaway_read(half_updated_pc);
|
||||
break;
|
||||
} else if(is_65c02(personality)) {
|
||||
// 65C02 modification to all branches: a branch that is taken but requires only a single cycle
|
||||
@ -610,31 +600,31 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
|
||||
// MARK: - Transfers
|
||||
|
||||
case OperationTXA: zero_result_ = negative_result_ = a_ = x_; continue;
|
||||
case OperationTYA: zero_result_ = negative_result_ = a_ = y_; continue;
|
||||
case OperationTXS: s_ = x_; continue;
|
||||
case OperationTAY: zero_result_ = negative_result_ = y_ = a_; continue;
|
||||
case OperationTAX: zero_result_ = negative_result_ = x_ = a_; continue;
|
||||
case OperationTSX: zero_result_ = negative_result_ = x_ = s_; continue;
|
||||
case OperationTXA: flags_.set_nz(a_ = x_); continue;
|
||||
case OperationTYA: flags_.set_nz(a_ = y_); continue;
|
||||
case OperationTXS: s_ = x_; continue;
|
||||
case OperationTAY: flags_.set_nz(y_ = a_); continue;
|
||||
case OperationTAX: flags_.set_nz(x_ = a_); continue;
|
||||
case OperationTSX: flags_.set_nz(x_ = s_); continue;
|
||||
|
||||
case OperationARR:
|
||||
if(decimal_flag_) {
|
||||
if(flags_.decimal) {
|
||||
a_ &= operand_;
|
||||
uint8_t unshiftedA = a_;
|
||||
a_ = uint8_t((a_ >> 1) | (carry_flag_ << 7));
|
||||
zero_result_ = negative_result_ = a_;
|
||||
overflow_flag_ = (a_^(a_ << 1))&Flag::Overflow;
|
||||
a_ = uint8_t((a_ >> 1) | (flags_.carry << 7));
|
||||
flags_.set_nz(a_);
|
||||
flags_.overflow = (a_^(a_ << 1))&Flag::Overflow;
|
||||
|
||||
if((unshiftedA&0xf) + (unshiftedA&0x1) > 5) a_ = ((a_ + 6)&0xf) | (a_ & 0xf0);
|
||||
|
||||
carry_flag_ = ((unshiftedA&0xf0) + (unshiftedA&0x10) > 0x50) ? 1 : 0;
|
||||
if(carry_flag_) a_ += 0x60;
|
||||
flags_.carry = ((unshiftedA&0xf0) + (unshiftedA&0x10) > 0x50) ? 1 : 0;
|
||||
if(flags_.carry) a_ += 0x60;
|
||||
} else {
|
||||
a_ &= operand_;
|
||||
a_ = uint8_t((a_ >> 1) | (carry_flag_ << 7));
|
||||
negative_result_ = zero_result_ = a_;
|
||||
carry_flag_ = (a_ >> 6)&1;
|
||||
overflow_flag_ = (a_^(a_ << 1))&Flag::Overflow;
|
||||
a_ = uint8_t((a_ >> 1) | (flags_.carry << 7));
|
||||
flags_.set_nz(a_);
|
||||
flags_.carry = (a_ >> 6)&1;
|
||||
flags_.overflow = (a_^(a_ << 1))&Flag::Overflow;
|
||||
}
|
||||
continue;
|
||||
|
||||
@ -642,15 +632,15 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
x_ &= a_;
|
||||
uint16_t difference = x_ - operand_;
|
||||
x_ = uint8_t(difference);
|
||||
negative_result_ = zero_result_ = x_;
|
||||
carry_flag_ = ((difference >> 8)&1)^1;
|
||||
flags_.set_nz(x_);
|
||||
flags_.carry = ((difference >> 8)&1)^1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(has_stpwai(personality) && (stop_is_active_ || wait_is_active_)) {
|
||||
break;
|
||||
}
|
||||
if(uses_ready_line && ready_line_is_enabled_ && (is_65c02(personality) || isReadOperation(nextBusOperation))) {
|
||||
if(uses_ready_line && ready_line_is_enabled_ && (is_65c02(personality) || isReadOperation(next_bus_operation_))) {
|
||||
ready_is_active_ = true;
|
||||
break;
|
||||
}
|
||||
@ -660,11 +650,6 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
}
|
||||
|
||||
cycles_left_to_run_ = number_of_cycles;
|
||||
next_address_ = nextAddress;
|
||||
next_bus_operation_ = nextBusOperation;
|
||||
bus_address_ = busAddress;
|
||||
bus_value_ = busValue;
|
||||
|
||||
bus_handler_.flush();
|
||||
}
|
||||
|
||||
@ -691,13 +676,13 @@ void ProcessorBase::set_power_on(bool active) {
|
||||
}
|
||||
|
||||
void ProcessorBase::set_irq_line(bool active) {
|
||||
irq_line_ = active ? Flag::Interrupt : 0;
|
||||
irq_line_ = active ? MOS6502Esque::Flag::Interrupt : 0;
|
||||
}
|
||||
|
||||
void ProcessorBase::set_overflow_line(bool active) {
|
||||
// a leading edge will set the overflow flag
|
||||
if(active && !set_overflow_line_is_enabled_)
|
||||
overflow_flag_ = Flag::Overflow;
|
||||
flags_.overflow = MOS6502Esque::Flag::Overflow;
|
||||
set_overflow_line_is_enabled_ = active;
|
||||
}
|
||||
|
||||
@ -709,14 +694,9 @@ void ProcessorBase::set_nmi_line(bool active) {
|
||||
}
|
||||
|
||||
uint8_t ProcessorStorage::get_flags() const {
|
||||
return carry_flag_ | overflow_flag_ | (inverse_interrupt_flag_ ^ Flag::Interrupt) | (negative_result_ & 0x80) | (zero_result_ ? 0 : Flag::Zero) | Flag::Always | decimal_flag_;
|
||||
return flags_.get();
|
||||
}
|
||||
|
||||
void ProcessorStorage::set_flags(uint8_t flags) {
|
||||
carry_flag_ = flags & Flag::Carry;
|
||||
negative_result_ = flags & Flag::Sign;
|
||||
zero_result_ = (~flags) & Flag::Zero;
|
||||
overflow_flag_ = flags & Flag::Overflow;
|
||||
inverse_interrupt_flag_ = (~flags) & Flag::Interrupt;
|
||||
decimal_flag_ = flags & Flag::Decimal;
|
||||
flags_.set(flags);
|
||||
}
|
||||
|
@ -76,12 +76,6 @@ using namespace CPU::MOS6502;
|
||||
#define JAM {CycleFetchOperand, OperationScheduleJam}
|
||||
|
||||
ProcessorStorage::ProcessorStorage(Personality personality) {
|
||||
// only the interrupt flag is defined upon reset but get_flags isn't going to
|
||||
// mask the other flags so we need to do that, at least
|
||||
carry_flag_ &= Flag::Carry;
|
||||
decimal_flag_ &= Flag::Decimal;
|
||||
overflow_flag_ &= Flag::Overflow;
|
||||
|
||||
const InstructionList operations_6502[] = {
|
||||
/* 0x00 BRK */ Program(CycleIncPCPushPCH, CyclePushPCL, OperationBRKPickVector, OperationSetOperandFromFlagsWithBRKSet, CyclePushOperand, OperationSetIRQFlags, CycleReadVectorLow, CycleReadVectorHigh),
|
||||
/* 0x01 ORA x, ind */ IndexedIndirectRead(OperationORA),
|
||||
|
@ -6,9 +6,6 @@
|
||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef MOS6502Storage_h
|
||||
#define MOS6502Storage_h
|
||||
|
||||
/*!
|
||||
A repository for all the internal state of a CPU::MOS6502::Processor; extracted into a separate base
|
||||
class in order to remove it from visibility within the main 6502.hpp.
|
||||
@ -231,7 +228,7 @@ class ProcessorStorage {
|
||||
*/
|
||||
RegisterPair16 pc_, last_operation_pc_;
|
||||
uint8_t a_, x_, y_, s_ = 0;
|
||||
uint8_t carry_flag_, negative_result_, zero_result_, decimal_flag_, overflow_flag_, inverse_interrupt_flag_ = 0;
|
||||
MOS6502Esque::LazyFlags flags_;
|
||||
|
||||
/*
|
||||
Temporary state for the micro programs.
|
||||
@ -246,6 +243,7 @@ class ProcessorStorage {
|
||||
BusOperation next_bus_operation_ = BusOperation::None;
|
||||
uint16_t bus_address_;
|
||||
uint8_t *bus_value_;
|
||||
static inline uint8_t bus_throwaway_;
|
||||
|
||||
/*!
|
||||
Gets the flags register.
|
||||
@ -270,7 +268,7 @@ class ProcessorStorage {
|
||||
|
||||
enum InterruptRequestFlags: uint8_t {
|
||||
Reset = 0x80,
|
||||
IRQ = Flag::Interrupt,
|
||||
IRQ = MOS6502Esque::Flag::Interrupt,
|
||||
NMI = 0x20,
|
||||
|
||||
PowerOn = 0x10,
|
||||
@ -288,5 +286,3 @@ class ProcessorStorage {
|
||||
// Allow state objects to capture and apply state.
|
||||
friend struct State;
|
||||
};
|
||||
|
||||
#endif /* _502Storage_h */
|
||||
|
155
Processors/6502Esque/6502Esque.hpp
Normal file
155
Processors/6502Esque/6502Esque.hpp
Normal file
@ -0,0 +1,155 @@
|
||||
//
|
||||
// 6502Esque.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 28/09/2020.
|
||||
// Copyright © 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef m6502Esque_h
|
||||
#define m6502Esque_h
|
||||
|
||||
#include "../../ClockReceiver/ClockReceiver.hpp"
|
||||
|
||||
/*
|
||||
This file defines how the CPU-controlled part of a bus looks for the 6502 and
|
||||
for other processors with a sufficiently-similar bus.
|
||||
|
||||
I'm not yet a big fan of the name I've used here, and I'm still on the fence
|
||||
about what to do when eventually I get around to the 6800 and/or 6809, which have
|
||||
very similar bus characteristics.
|
||||
|
||||
So: this is _very_ provisional stuff.
|
||||
*/
|
||||
namespace CPU {
|
||||
namespace MOS6502Esque {
|
||||
|
||||
/*
|
||||
The list of registers that can be accessed via @c set_value_of_register and @c set_value_of_register.
|
||||
*/
|
||||
enum Register {
|
||||
LastOperationAddress,
|
||||
ProgramCounter,
|
||||
StackPointer,
|
||||
Flags,
|
||||
A,
|
||||
X,
|
||||
Y,
|
||||
|
||||
// These exist on a 65816 only.
|
||||
EmulationFlag,
|
||||
DataBank,
|
||||
ProgramBank,
|
||||
Direct
|
||||
};
|
||||
|
||||
/*
|
||||
Flags as defined on the 6502; can be used to decode the result of @c get_value_of_register(Flags) or to form a value for
|
||||
the corresponding set.
|
||||
*/
|
||||
enum Flag: uint8_t {
|
||||
Sign = 0x80,
|
||||
Overflow = 0x40,
|
||||
Always = 0x20,
|
||||
Break = 0x10,
|
||||
Decimal = 0x08,
|
||||
Interrupt = 0x04,
|
||||
Zero = 0x02,
|
||||
Carry = 0x01,
|
||||
|
||||
// These are available on a 65816 only.
|
||||
MemorySize = 0x20,
|
||||
IndexSize = 0x10,
|
||||
};
|
||||
|
||||
/*!
|
||||
Bus handlers will be given the task of performing bus operations, allowing them to provide whatever interface they like
|
||||
between a 6502-esque chip and the rest of the system. @c BusOperation lists the types of bus operation that may be requested.
|
||||
*/
|
||||
enum BusOperation {
|
||||
/// 6502: indicates that a read was signalled.
|
||||
/// 65816: indicates that a read was signalled with VDA.
|
||||
Read,
|
||||
/// 6502: indicates that a read was signalled with SYNC.
|
||||
/// 65816: indicates that a read was signalled with VDA and VPA.
|
||||
ReadOpcode,
|
||||
/// 6502: never signalled.
|
||||
/// 65816: indicates that a read was signalled with VPA.
|
||||
ReadProgram,
|
||||
/// 6502: never signalled.
|
||||
/// 65816: indicates that a read was signalled with VPB.
|
||||
ReadVector,
|
||||
/// 6502: never signalled.
|
||||
/// 65816: indicates that a read was signalled, but neither VDA nor VPA were active.
|
||||
InternalOperationRead,
|
||||
|
||||
/// 6502: indicates that a write was signalled.
|
||||
/// 65816: indicates that a write was signalled with VDA.
|
||||
Write,
|
||||
/// 6502: never signalled.
|
||||
/// 65816: indicates that a write was signalled, but neither VDA nor VPA were active.
|
||||
InternalOperationWrite,
|
||||
|
||||
/// All processors: indicates that the processor is paused due to the RDY input.
|
||||
/// 65C02 and 65816: indicates a WAI is ongoing.
|
||||
Ready,
|
||||
|
||||
/// 65C02 and 65816: indicates a STP condition.
|
||||
None,
|
||||
};
|
||||
|
||||
/*!
|
||||
For a machine watching only the RWB line, evaluates to @c true if the operation should be treated as a read; @c false otherwise.
|
||||
*/
|
||||
#define isReadOperation(v) (v <= CPU::MOS6502Esque::InternalOperationRead)
|
||||
|
||||
/*!
|
||||
For a machine watching only the RWB line, evaluates to @c true if the operation is any sort of write; @c false otherwise.
|
||||
*/
|
||||
#define isWriteOperation(v) (v == CPU::MOS6502Esque::Write || v == CPU::MOS6502Esque::InternalOperationWrite)
|
||||
|
||||
/*!
|
||||
Evaluates to @c true if the operation actually expects a response; @c false otherwise.
|
||||
*/
|
||||
#define isAccessOperation(v) ((v < CPU::MOS6502Esque::Ready) && (v != CPU::MOS6502Esque::InternalOperationRead) && (v != CPU::MOS6502Esque::InternalOperationWrite))
|
||||
|
||||
/*!
|
||||
A class providing empty implementations of the methods a 6502 uses to access the bus. To wire the 6502 to a bus,
|
||||
machines should subclass BusHandler and then declare a realisation of the 6502 template, suplying their bus
|
||||
handler.
|
||||
*/
|
||||
template <typename addr_t> class BusHandler {
|
||||
public:
|
||||
using AddressType = addr_t;
|
||||
|
||||
/*!
|
||||
Announces that the 6502 has performed the cycle defined by operation, address and value. On the 6502,
|
||||
all bus cycles take one clock cycle so the amoutn of time advanced is implicit.
|
||||
|
||||
@param operation The type of bus cycle: read, read opcode (i.e. read, with sync active),
|
||||
write or ready.
|
||||
@param address The value of the address bus during this bus cycle.
|
||||
@param value If this is a cycle that puts a value onto the data bus, *value is that value. If this is
|
||||
a cycle that reads the bus, the bus handler should write a value to *value. Writing to *value during
|
||||
a read cycle will produce undefined behaviour.
|
||||
|
||||
@returns The number of cycles that passed in objective time while this 6502 bus cycle was ongoing.
|
||||
On an archetypal machine this will be Cycles(1) but some architectures may choose not to clock the 6502
|
||||
during some periods; one way to simulate that is to have the bus handler return a number other than
|
||||
Cycles(1) to describe lengthened bus cycles.
|
||||
*/
|
||||
Cycles perform_bus_operation([[maybe_unused]] BusOperation operation, [[maybe_unused]] addr_t address, [[maybe_unused]] uint8_t *value) {
|
||||
return Cycles(1);
|
||||
}
|
||||
|
||||
/*!
|
||||
Announces completion of all the cycles supplied to a .run_for request on the 6502. Intended to allow
|
||||
bus handlers to perform any deferred output work.
|
||||
*/
|
||||
void flush() {}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* m6502Esque_h */
|
46
Processors/6502Esque/6502Selector.hpp
Normal file
46
Processors/6502Esque/6502Selector.hpp
Normal file
@ -0,0 +1,46 @@
|
||||
//
|
||||
// 6502Selector.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 28/09/2020.
|
||||
// Copyright © 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef _502Selector_h
|
||||
#define _502Selector_h
|
||||
|
||||
#include "../6502/6502.hpp"
|
||||
#include "../65816/65816.hpp"
|
||||
|
||||
namespace CPU {
|
||||
namespace MOS6502Esque {
|
||||
|
||||
enum class Type {
|
||||
TNES6502, // the NES's 6502, which is like a 6502 but lacks decimal mode (though it retains the decimal flag)
|
||||
T6502, // the original [NMOS] 6502, replete with various undocumented instructions
|
||||
TSynertek65C02, // a 6502 extended with BRA, P[H/L][X/Y], STZ, TRB, TSB and the (zp) addressing mode and a few other additions
|
||||
TRockwell65C02, // like the Synertek, but with BBR, BBS, RMB and SMB
|
||||
TWDC65C02, // like the Rockwell, but with STP and WAI
|
||||
TWDC65816, // the slightly 16-bit follow-up to the 6502
|
||||
};
|
||||
|
||||
/*
|
||||
Machines that can use either a 6502 or a 65816 can use CPU::MOS6502Esque::Processor in order to select the proper
|
||||
class in much the same way that a raw user of CPU::MOS6502::Processor would set the personality. Just provide one
|
||||
of the type enums as above as the appropriate template parameter.
|
||||
*/
|
||||
|
||||
template <Type processor_type, typename BusHandler, bool uses_ready_line> class Processor:
|
||||
public CPU::MOS6502::Processor<CPU::MOS6502::Personality(processor_type), BusHandler, uses_ready_line> {
|
||||
using CPU::MOS6502::Processor<CPU::MOS6502::Personality(processor_type), BusHandler, uses_ready_line>::Processor;
|
||||
};
|
||||
|
||||
template <typename BusHandler, bool uses_ready_line> class Processor<Type::TWDC65816, BusHandler, uses_ready_line>:
|
||||
public CPU::WDC65816::Processor<BusHandler, uses_ready_line> {
|
||||
using CPU::WDC65816::Processor<BusHandler, uses_ready_line>::Processor;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* _502Selector_h */
|
83
Processors/6502Esque/Implementation/LazyFlags.hpp
Normal file
83
Processors/6502Esque/Implementation/LazyFlags.hpp
Normal file
@ -0,0 +1,83 @@
|
||||
//
|
||||
// LazyFlags.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 05/10/2020.
|
||||
// Copyright © 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef LazyFlags_h
|
||||
#define LazyFlags_h
|
||||
|
||||
#include "../6502Esque.hpp"
|
||||
|
||||
namespace CPU {
|
||||
namespace MOS6502Esque {
|
||||
|
||||
struct LazyFlags {
|
||||
/// Bit 7 is set if the negative flag is set; otherwise it is clear.
|
||||
uint8_t negative_result = 0;
|
||||
|
||||
/// Non-zero if the zero flag is clear, zero if it is set.
|
||||
uint8_t zero_result = 0;
|
||||
|
||||
/// Contains Flag::Carry.
|
||||
uint8_t carry = 0;
|
||||
|
||||
/// Contains Flag::Decimal.
|
||||
uint8_t decimal = 0;
|
||||
|
||||
/// Contains Flag::Overflow.
|
||||
uint8_t overflow = 0;
|
||||
|
||||
/// Contains Flag::Interrupt, complemented.
|
||||
uint8_t inverse_interrupt = 0;
|
||||
|
||||
/// Sets N and Z flags per the 8-bit value @c value.
|
||||
void set_nz(uint8_t value) {
|
||||
zero_result = negative_result = value;
|
||||
}
|
||||
|
||||
/// Sets N and Z flags per the 8- or 16-bit value @c value; @c shift should be 0 to indicate an 8-bit value or 8 to indicate a 16-bit value.
|
||||
void set_nz(uint16_t value, int shift) {
|
||||
negative_result = uint8_t(value >> shift);
|
||||
zero_result = uint8_t(value | (value >> shift));
|
||||
}
|
||||
|
||||
/// Sets the Z flag per the 8- or 16-bit value @c value; @c shift should be 0 to indicate an 8-bit value or 8 to indicate a 16-bit value.
|
||||
void set_z(uint16_t value, int shift) {
|
||||
zero_result = uint8_t(value | (value >> shift));
|
||||
}
|
||||
|
||||
/// Sets the N flag per the 8- or 16-bit value @c value; @c shift should be 0 to indicate an 8-bit value or 8 to indicate a 16-bit value.
|
||||
void set_n(uint16_t value, int shift) {
|
||||
negative_result = uint8_t(value >> shift);
|
||||
}
|
||||
|
||||
void set(uint8_t flags) {
|
||||
carry = flags & Flag::Carry;
|
||||
negative_result = flags & Flag::Sign;
|
||||
zero_result = (~flags) & Flag::Zero;
|
||||
overflow = flags & Flag::Overflow;
|
||||
inverse_interrupt = (~flags) & Flag::Interrupt;
|
||||
decimal = flags & Flag::Decimal;
|
||||
}
|
||||
|
||||
uint8_t get() const {
|
||||
return carry | overflow | (inverse_interrupt ^ Flag::Interrupt) | (negative_result & 0x80) | (zero_result ? 0 : Flag::Zero) | Flag::Always | decimal;
|
||||
}
|
||||
|
||||
LazyFlags() {
|
||||
// Only the interrupt flag is defined upon reset but get_flags isn't going to
|
||||
// mask the other flags so we need to do that, at least.
|
||||
carry &= Flag::Carry;
|
||||
decimal &= Flag::Decimal;
|
||||
overflow &= Flag::Overflow;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* LazyFlags_h */
|
5
Processors/6502Esque/README.md
Normal file
5
Processors/6502Esque/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# 6502Esque
|
||||
|
||||
This folder contains common code for CPUs for a 6502-esque bus interface; it also contains a special template, the 6502Selector, which allows a consumer to select between the 6502-esque chips by enum.
|
||||
|
||||
If you know exactly which processor you want, feel free to ignore this folder entirely; just go straight to the 6502, 65816 or whatever.
|
90
Processors/65816/65816.hpp
Normal file
90
Processors/65816/65816.hpp
Normal file
@ -0,0 +1,90 @@
|
||||
//
|
||||
// 65816.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 23/09/2020.
|
||||
// Copyright © 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef WDC65816_hpp
|
||||
#define WDC65816_hpp
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "../RegisterSizes.hpp"
|
||||
#include "../../ClockReceiver/ClockReceiver.hpp"
|
||||
#include "../6502Esque/6502Esque.hpp"
|
||||
#include "../6502Esque/Implementation/LazyFlags.hpp"
|
||||
|
||||
namespace CPU {
|
||||
namespace WDC65816 {
|
||||
|
||||
using BusOperation = CPU::MOS6502Esque::BusOperation;
|
||||
using Register = CPU::MOS6502Esque::Register;
|
||||
using Flag = CPU::MOS6502Esque::Flag;
|
||||
|
||||
enum ExtendedBusOutput {
|
||||
Emulation = (1 << 0),
|
||||
MemorySize = (1 << 1),
|
||||
IndexSize = (1 << 2),
|
||||
MemoryLock = (1 << 3),
|
||||
};
|
||||
|
||||
#include "Implementation/65816Storage.hpp"
|
||||
|
||||
class ProcessorBase: protected ProcessorStorage {
|
||||
public:
|
||||
inline void set_power_on(bool);
|
||||
inline void set_irq_line(bool);
|
||||
inline void set_nmi_line(bool);
|
||||
inline void set_reset_line(bool);
|
||||
inline void set_abort_line(bool);
|
||||
inline bool get_is_resetting() const;
|
||||
|
||||
/*!
|
||||
Returns the current state of all lines not ordinarily pushed to the BusHandler.
|
||||
*/
|
||||
inline int get_extended_bus_output();
|
||||
|
||||
/*!
|
||||
Provided for symmetry with the 6502; a 65816 is never jammed.
|
||||
*/
|
||||
inline bool is_jammed() const;
|
||||
|
||||
void set_value_of_register(Register r, uint16_t value);
|
||||
uint16_t get_value_of_register(Register r) const;
|
||||
};
|
||||
|
||||
template <typename BusHandler, bool uses_ready_line> class Processor: public ProcessorBase {
|
||||
public:
|
||||
/*!
|
||||
Constructs an instance of the 6502 that will use @c bus_handler for all bus communications.
|
||||
*/
|
||||
Processor(BusHandler &bus_handler) : bus_handler_(bus_handler) {}
|
||||
|
||||
/*!
|
||||
Runs the 6502 for a supplied number of cycles.
|
||||
|
||||
@param cycles The number of cycles to run the 6502 for.
|
||||
*/
|
||||
void run_for(const Cycles cycles);
|
||||
|
||||
/*!
|
||||
Sets the current level of the RDY line.
|
||||
|
||||
@param active @c true if the line is logically active; @c false otherwise.
|
||||
*/
|
||||
void set_ready_line(bool active);
|
||||
|
||||
private:
|
||||
BusHandler &bus_handler_;
|
||||
};
|
||||
|
||||
#include "Implementation/65816Implementation.hpp"
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* WDC65816_hpp */
|
44
Processors/65816/Implementation/65816Base.cpp
Normal file
44
Processors/65816/Implementation/65816Base.cpp
Normal file
@ -0,0 +1,44 @@
|
||||
//
|
||||
// 65816Base.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 28/09/2020.
|
||||
// Copyright © 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "../65816.hpp"
|
||||
|
||||
using namespace CPU::WDC65816;
|
||||
|
||||
uint16_t ProcessorBase::get_value_of_register(Register r) const {
|
||||
switch (r) {
|
||||
case Register::ProgramCounter: return registers_.pc;
|
||||
case Register::LastOperationAddress: return last_operation_pc_;
|
||||
case Register::StackPointer: return registers_.s.full;
|
||||
case Register::Flags: return get_flags();
|
||||
case Register::A: return registers_.a.full;
|
||||
case Register::X: return registers_.x.full;
|
||||
case Register::Y: return registers_.y.full;
|
||||
case Register::EmulationFlag: return registers_.emulation_flag;
|
||||
case Register::DataBank: return registers_.data_bank >> 16;
|
||||
case Register::ProgramBank: return registers_.program_bank >> 16;
|
||||
case Register::Direct: return registers_.direct;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessorBase::set_value_of_register(Register r, uint16_t value) {
|
||||
switch (r) {
|
||||
case Register::ProgramCounter: registers_.pc = value; break;
|
||||
case Register::StackPointer: registers_.s.full = value; break;
|
||||
case Register::Flags: set_flags(uint8_t(value)); break;
|
||||
case Register::A: registers_.a.full = value; break;
|
||||
case Register::X: registers_.x.full = value; break;
|
||||
case Register::Y: registers_.y.full = value; break;
|
||||
case Register::EmulationFlag: set_emulation_mode(value); break;
|
||||
case Register::DataBank: registers_.data_bank = uint32_t(value & 0xff) << 16; break;
|
||||
case Register::ProgramBank: registers_.program_bank = uint32_t(value &0xff) << 16; break;
|
||||
case Register::Direct: registers_.direct = value; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
1016
Processors/65816/Implementation/65816Implementation.hpp
Normal file
1016
Processors/65816/Implementation/65816Implementation.hpp
Normal file
File diff suppressed because it is too large
Load Diff
1100
Processors/65816/Implementation/65816Storage.cpp
Normal file
1100
Processors/65816/Implementation/65816Storage.cpp
Normal file
File diff suppressed because it is too large
Load Diff
374
Processors/65816/Implementation/65816Storage.hpp
Normal file
374
Processors/65816/Implementation/65816Storage.hpp
Normal file
@ -0,0 +1,374 @@
|
||||
//
|
||||
// 65816Implementation.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 23/09/2020.
|
||||
// Copyright © 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
enum MicroOp: uint8_t {
|
||||
/// Fetches a byte from the program counter to the instruction buffer and increments the program counter.
|
||||
CycleFetchIncrementPC,
|
||||
/// Fetches a byte from the program counter without incrementing it.
|
||||
CycleFetchPC,
|
||||
/// Fetches a byte from the program counter without incrementing it, and throws it away.
|
||||
CycleFetchPCThrowaway,
|
||||
/// The same as CycleFetchIncrementPC but indicates valid program address rather than valid data address.
|
||||
CycleFetchOpcode,
|
||||
|
||||
/// Fetches a byte from the data address to the data buffer.
|
||||
CycleFetchData,
|
||||
/// Fetches a byte from the data address to the data buffer and increments the data address.
|
||||
CycleFetchIncrementData,
|
||||
/// Fetches from the address formed by the low byte of the data address and the high byte
|
||||
/// of the instruction buffer, throwing the result away.
|
||||
CycleFetchIncorrectDataAddress,
|
||||
/// Fetches a byte from the data address and throws it away.
|
||||
CycleFetchDataThrowaway,
|
||||
/// Fetches a byte from the data address to the data buffer, signalling VPB .
|
||||
CycleFetchVector,
|
||||
/// Fetches a byte from the data address to the data buffer and increments the data address, signalling VPB.
|
||||
CycleFetchIncrementVector,
|
||||
|
||||
// Dedicated block-move cycles; these use the data buffer as an intermediary.
|
||||
CycleFetchBlockX,
|
||||
CycleFetchBlockY,
|
||||
CycleStoreBlockY,
|
||||
|
||||
/// Stores a byte from the data buffer.
|
||||
CycleStoreData,
|
||||
/// Stores the most recent byte placed into the data buffer without removing it.
|
||||
CycleStoreDataThrowaway,
|
||||
/// Stores a byte to the data address from the data buffer and increments the data address.
|
||||
CycleStoreIncrementData,
|
||||
/// Stores a byte to the data address from the data buffer and decrements the data address.
|
||||
CycleStoreDecrementData,
|
||||
|
||||
/// Pushes a single byte from the data buffer to the stack.
|
||||
CyclePush,
|
||||
/// Fetches from the current stack location and throws the result away.
|
||||
CycleAccessStack,
|
||||
/// Pulls a single byte to the data buffer from the stack.
|
||||
CyclePull,
|
||||
/// Performs as CyclePull if the 65816 is not in emulation mode; otherwise skips itself.
|
||||
CyclePullIfNotEmulation,
|
||||
|
||||
/// Issues a BusOperation::None and regresses the micro-op counter until an established
|
||||
/// STP or WAI condition is satisfied.
|
||||
CycleRepeatingNone,
|
||||
|
||||
/// Sets the data address by copying the final two bytes of the instruction buffer and
|
||||
/// using the data register as a high byte.
|
||||
OperationConstructAbsolute,
|
||||
|
||||
/// Constructs a strictly 16-bit address from the instruction buffer.
|
||||
OperationConstructAbsolute16,
|
||||
|
||||
/// Sets the data address by copying the entire instruction buffer.
|
||||
OperationConstructAbsoluteLong,
|
||||
|
||||
/// Sets the data address to the 16-bit result of adding x to the value in the instruction buffer.
|
||||
OperationConstructAbsoluteIndexedIndirect,
|
||||
|
||||
/// Sets the data address to the 24-bit result of adding x to the low 16-bits of the value in the
|
||||
/// instruction buffer and retaining the highest 8-bits as specified.
|
||||
OperationConstructAbsoluteLongX,
|
||||
|
||||
/// Calculates an a, x address; if:
|
||||
/// there was no carry into the top byte of the address; and
|
||||
/// the process or in emulation or 8-bit index mode;
|
||||
/// then it also skips the next micro-op.
|
||||
OperationConstructAbsoluteXRead,
|
||||
|
||||
/// Calculates an a, x address.
|
||||
OperationConstructAbsoluteX,
|
||||
|
||||
// These are analogous to the X versions above.
|
||||
OperationConstructAbsoluteY,
|
||||
OperationConstructAbsoluteYRead,
|
||||
|
||||
/// Constructs the current direct address using the value in the instruction buffer.
|
||||
/// Skips the next micro-op if the low byte of the direct register is 0.
|
||||
OperationConstructDirect,
|
||||
|
||||
/// Exactly like OperationConstructDirect, but doesn't retain any single-byte wrapping
|
||||
/// behaviour in emulation mode.
|
||||
OperationConstructDirectLong,
|
||||
|
||||
/// Constructs the current direct indexed indirect address using the data bank,
|
||||
/// direct and x registers plus the value currently in the instruction buffer.
|
||||
/// Skips the next micro-op if the low byte of the direct register is 0.
|
||||
OperationConstructDirectIndexedIndirect,
|
||||
|
||||
/// Constructs the current direct indexed indirect address using the value
|
||||
/// currently in the data buffer.
|
||||
OperationConstructDirectIndirect,
|
||||
|
||||
/// Adds y to the low 16-bits currently in the instruction buffer and appends a high 8-bits
|
||||
/// also from the instruction buffer.
|
||||
OperationConstructDirectIndirectIndexedLong,
|
||||
|
||||
/// Uses the 24-bit address currently in the instruction buffer.
|
||||
OperationConstructDirectIndirectLong,
|
||||
|
||||
/// Adds the x register to the direct register to produce a 16-bit address;
|
||||
/// skips the next micro-op if the low byte of the direct register is 0.
|
||||
OperationConstructDirectX,
|
||||
|
||||
/// Adds the y register to the direct register to produce a 16-bit address;
|
||||
/// skips the next micro-op if the low byte of the direct register is 0.
|
||||
OperationConstructDirectY,
|
||||
|
||||
/// Adds the instruction buffer to the program counter, making a 16-bit result,
|
||||
/// *and stores it into the data buffer*.
|
||||
OperationConstructPER,
|
||||
|
||||
/// Adds the stack pointer to the instruction buffer to produce a 16-bit address.
|
||||
OperationConstructStackRelative,
|
||||
|
||||
/// Adds y to the value in the instruction buffer to produce a 16-bit result and
|
||||
/// prefixes the current data bank.
|
||||
OperationConstructStackRelativeIndexedIndirect,
|
||||
|
||||
/// Performs whatever operation goes with this program.
|
||||
OperationPerform,
|
||||
|
||||
/// Copies the current program counter to the data buffer.
|
||||
OperationCopyPCToData,
|
||||
OperationCopyDataToPC,
|
||||
OperationCopyInstructionToData,
|
||||
OperationCopyDataToInstruction,
|
||||
|
||||
/// Copies the current PBR to the data buffer.
|
||||
OperationCopyPBRToData,
|
||||
|
||||
/// Copies A to the data buffer.
|
||||
OperationCopyAToData,
|
||||
|
||||
/// Copies the data buffer to A.
|
||||
OperationCopyDataToA,
|
||||
|
||||
/// Fills the data buffer with three or four bytes, depending on emulation mode, containing the program
|
||||
/// counter, flags and possibly the program bank. Also puts the appropriate vector address into the
|
||||
/// address register.
|
||||
OperationPrepareException,
|
||||
|
||||
/// Sets the memory lock output for the rest of this instruction.
|
||||
OperationSetMemoryLock,
|
||||
|
||||
/// Complete this set of micr-ops.
|
||||
OperationMoveToNextProgram,
|
||||
|
||||
/// Inspects the instruction buffer and thereby selects the next set of micro-ops to schedule.
|
||||
OperationDecode,
|
||||
};
|
||||
|
||||
enum Operation: uint8_t {
|
||||
// These perform the named operation using the value in the data buffer;
|
||||
// they are implicitly AccessType::Read.
|
||||
ADC, AND, BIT, CMP, CPX, CPY, EOR, ORA, SBC, BITimm,
|
||||
|
||||
// These load the respective register from the data buffer;
|
||||
// they are implicitly AccessType::Read.
|
||||
LDA, LDX, LDY,
|
||||
PLB, PLD, PLP, // LDA, LDX and LDY can be used for PLA, PLX, PLY.
|
||||
|
||||
// These move the respective register (or value) to the data buffer;
|
||||
// they are implicitly AccessType::Write.
|
||||
STA, STX, STY, STZ,
|
||||
PHB, PHP, PHD, PHK,
|
||||
|
||||
// These modify the value in the data buffer as part of a read-modify-write.
|
||||
INC, DEC, ASL, LSR, ROL, ROR, TRB, TSB,
|
||||
|
||||
// These merely decrement A, increment or decrement X and Y, and regress
|
||||
// the program counter only if appropriate.
|
||||
MVN, MVP,
|
||||
|
||||
// These use a value straight from the instruction buffer.
|
||||
REP, SEP,
|
||||
|
||||
BCC, BCS, BEQ, BMI, BNE, BPL, BRA, BVC, BVS, BRL,
|
||||
|
||||
// These are all implicit.
|
||||
CLC, CLD, CLI, CLV, DEX, DEY, INX, INY, NOP, SEC, SED, SEI,
|
||||
TAX, TAY, TCD, TCS, TDC, TSC, TSX, TXA, TXS, TXY, TYA, TYX,
|
||||
XCE, XBA,
|
||||
|
||||
STP, WAI,
|
||||
|
||||
// These unpack values from the data buffer, which has been filled
|
||||
// from the stack.
|
||||
RTI,
|
||||
|
||||
/// Loads the PC with the contents of the data buffer.
|
||||
JMPind,
|
||||
|
||||
/// Loads the PC with the contents of the instruction bufer.
|
||||
JMP,
|
||||
|
||||
/// Loads the PC and PBR with the operand from the instruction buffer.
|
||||
JML,
|
||||
|
||||
/// Loads the PC with the operand from the instruction buffer, placing
|
||||
/// the current PC into the data buffer.
|
||||
JSR,
|
||||
|
||||
/// Loads the PC and the PBR with the operand from the instruction buffer,
|
||||
/// placing the old PC into the data buffer (and only the PC; PBR not included).
|
||||
JSL,
|
||||
|
||||
/// Loads the PC with the contents of the data buffer + 1.
|
||||
RTS,
|
||||
};
|
||||
|
||||
struct ProcessorStorageConstructor;
|
||||
|
||||
struct ProcessorStorage {
|
||||
ProcessorStorage();
|
||||
|
||||
// Frustratingly, there is not quite enough space in 16 bits to store both
|
||||
// the program offset and the operation as currently defined.
|
||||
struct Instruction {
|
||||
/// Pointers into micro_ops_ for: [0] = 16-bit operation; [1] = 8-bit operation.
|
||||
uint16_t program_offsets[2] = {0xffff, 0xffff};
|
||||
/// The operation to perform upon an OperationPerform.
|
||||
Operation operation = NOP;
|
||||
/// An index into the mx field indicating which of M or X affects whether this is an 8-bit or 16-bit field.
|
||||
/// So the program to perform is that at @c program_offsets[mx_flags[size_field]]
|
||||
uint8_t size_field = 0;
|
||||
};
|
||||
Instruction instructions[256 + 2]; // Arranged as:
|
||||
// 256 entries: instructions;
|
||||
// the entry for 'exceptions' (i.e. reset, irq, nmi); and
|
||||
// the entry for fetch-decode-execute.
|
||||
|
||||
enum class OperationSlot {
|
||||
Exception = 256,
|
||||
FetchDecodeExecute
|
||||
};
|
||||
|
||||
// A helper for testing.
|
||||
uint16_t last_operation_pc_;
|
||||
Instruction *active_instruction_;
|
||||
Cycles cycles_left_to_run_;
|
||||
|
||||
// All registers are boxed up into a struct so that they can be stored and restored in support of abort.
|
||||
struct Registers {
|
||||
// Registers.
|
||||
RegisterPair16 a;
|
||||
RegisterPair16 x, y;
|
||||
RegisterPair16 s;
|
||||
uint16_t pc;
|
||||
|
||||
// Flags aplenty.
|
||||
MOS6502Esque::LazyFlags flags;
|
||||
uint8_t mx_flags[2] = {1, 1}; // [0] = m; [1] = x. In both cases either `0` or `1`; `1` => 8-bit.
|
||||
uint16_t m_masks[2] = {0xff00, 0x00ff}; // [0] = src mask; [1] = dst mask.
|
||||
uint16_t x_masks[2] = {0xff00, 0x00ff}; // [0] = src mask; [1] = dst mask.
|
||||
uint16_t e_masks[2] = {0xff00, 0x00ff};
|
||||
int m_shift = 0;
|
||||
int x_shift = 0;
|
||||
bool emulation_flag = true;
|
||||
|
||||
// I.e. the offset for direct addressing (outside of emulation mode).
|
||||
uint16_t direct = 0;
|
||||
|
||||
// Banking registers are all stored with the relevant byte
|
||||
// shifted up bits 16–23.
|
||||
uint32_t data_bank = 0; // i.e. DBR.
|
||||
uint32_t program_bank = 0; // i.e. PBR.
|
||||
} registers_, abort_registers_copy_;
|
||||
|
||||
// The next bus transaction.
|
||||
uint32_t bus_address_ = 0;
|
||||
uint8_t *bus_value_ = nullptr;
|
||||
static inline uint8_t bus_throwaway_ = 0;
|
||||
BusOperation bus_operation_ = BusOperation::None;
|
||||
|
||||
// A bitfield for various exceptions.
|
||||
static constexpr int PowerOn = 1 << 0;
|
||||
static constexpr int Reset = 1 << 1;
|
||||
static constexpr int IRQ = Flag::Interrupt; // This makes masking a lot easier later on; this is 1 << 2.
|
||||
static constexpr int NMI = 1 << 3;
|
||||
static constexpr int Abort = 1 << 4;
|
||||
|
||||
static constexpr int default_exceptions = PowerOn;
|
||||
int pending_exceptions_ = default_exceptions;
|
||||
int selected_exceptions_ = default_exceptions;
|
||||
|
||||
bool ready_line_ = false;
|
||||
bool memory_lock_ = false;
|
||||
|
||||
// Just to be safe.
|
||||
static_assert(PowerOn != IRQ);
|
||||
static_assert(Reset != IRQ);
|
||||
static_assert(NMI != IRQ);
|
||||
static_assert(Abort != IRQ);
|
||||
|
||||
/// Sets the required exception flags necessary to exit a STP or WAI.
|
||||
int required_exceptions_ = 0;
|
||||
BusOperation stp_wai_bus_operation_ = BusOperation::None;
|
||||
|
||||
/// Defines a four-byte buffer which can be cleared or filled in single-byte increments from least significant byte
|
||||
/// to most significant.
|
||||
struct Buffer {
|
||||
uint32_t value = 0;
|
||||
int size = 0;
|
||||
int read = 0;
|
||||
|
||||
void clear() {
|
||||
value = 0;
|
||||
size = 0;
|
||||
read = 0;
|
||||
}
|
||||
|
||||
uint8_t *next_input() {
|
||||
uint8_t *const next = byte(size);
|
||||
++size;
|
||||
return next;
|
||||
}
|
||||
|
||||
uint8_t *next_output() {
|
||||
uint8_t *const next = byte(read);
|
||||
++read;
|
||||
return next;
|
||||
}
|
||||
|
||||
uint8_t *preview_output() {
|
||||
return byte(read);
|
||||
}
|
||||
|
||||
uint8_t *next_output_descending() {
|
||||
--size;
|
||||
return byte(size);
|
||||
}
|
||||
|
||||
uint8_t *any_byte() {
|
||||
return reinterpret_cast<uint8_t *>(&value);
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t *byte(int pointer) {
|
||||
assert(pointer >= 0 && pointer < 4);
|
||||
#if TARGET_RT_BIG_ENDIAN
|
||||
return reinterpret_cast<uint8_t *>(&value) + (3 ^ pointer);
|
||||
#else
|
||||
return reinterpret_cast<uint8_t *>(&value) + pointer;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
Buffer instruction_buffer_, data_buffer_;
|
||||
uint32_t data_address_;
|
||||
uint32_t data_address_increment_mask_ = 0xffff;
|
||||
uint32_t incorrect_data_address_;
|
||||
|
||||
std::vector<MicroOp> micro_ops_;
|
||||
MicroOp *next_op_ = nullptr;
|
||||
|
||||
void set_reset_state();
|
||||
void set_emulation_mode(bool);
|
||||
void set_m_x_flags(bool m, bool x);
|
||||
uint8_t get_flags() const;
|
||||
void set_flags(uint8_t);
|
||||
};
|
@ -15,14 +15,14 @@ AllRAMProcessor::AllRAMProcessor(std::size_t memory_size) :
|
||||
traps_(memory_size, false),
|
||||
timestamp_(0) {}
|
||||
|
||||
void AllRAMProcessor::set_data_at_address(uint16_t startAddress, std::size_t length, const uint8_t *data) {
|
||||
std::size_t endAddress = std::min(startAddress + length, size_t(65536));
|
||||
std::memcpy(&memory_[startAddress], data, endAddress - startAddress);
|
||||
void AllRAMProcessor::set_data_at_address(size_t start_address, std::size_t length, const uint8_t *data) {
|
||||
const size_t end_address = std::min(start_address + length, memory_.size());
|
||||
memcpy(&memory_[start_address], data, end_address - start_address);
|
||||
}
|
||||
|
||||
void AllRAMProcessor::get_data_at_address(uint16_t startAddress, std::size_t length, uint8_t *data) {
|
||||
std::size_t endAddress = std::min(startAddress + length, size_t(65536));
|
||||
std::memcpy(data, &memory_[startAddress], endAddress - startAddress);
|
||||
void AllRAMProcessor::get_data_at_address(size_t start_address, std::size_t length, uint8_t *data) {
|
||||
const size_t end_address = std::min(start_address + length, memory_.size());
|
||||
memcpy(data, &memory_[start_address], end_address - start_address);
|
||||
}
|
||||
|
||||
HalfCycles AllRAMProcessor::get_timestamp() {
|
||||
|
@ -21,8 +21,8 @@ class AllRAMProcessor {
|
||||
public:
|
||||
AllRAMProcessor(std::size_t memory_size);
|
||||
HalfCycles get_timestamp();
|
||||
void set_data_at_address(uint16_t startAddress, std::size_t length, const uint8_t *data);
|
||||
void get_data_at_address(uint16_t startAddress, std::size_t length, uint8_t *data);
|
||||
void set_data_at_address(size_t startAddress, size_t length, const uint8_t *data);
|
||||
void get_data_at_address(size_t startAddress, size_t length, uint8_t *data);
|
||||
|
||||
class TrapHandler {
|
||||
public:
|
||||
|
@ -29,7 +29,7 @@ class AllRAMProcessor:
|
||||
}
|
||||
|
||||
struct PortAccessDelegate {
|
||||
virtual uint8_t z80_all_ram_processor_input(uint16_t port) { return 0xff; }
|
||||
virtual uint8_t z80_all_ram_processor_input(uint16_t) { return 0xff; }
|
||||
};
|
||||
inline void set_port_access_delegate(PortAccessDelegate *delegate) {
|
||||
port_delegate_ = delegate;
|
||||
|
Loading…
x
Reference in New Issue
Block a user