mirror of
https://github.com/TomHarte/CLK.git
synced 2024-07-04 18:29:40 +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
|
BD500
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ReflectableEnum(Processor,
|
||||||
|
MOS6502,
|
||||||
|
WDC65816
|
||||||
|
);
|
||||||
|
|
||||||
ROM rom = ROM::BASIC11;
|
ROM rom = ROM::BASIC11;
|
||||||
DiskInterface disk_interface = DiskInterface::None;
|
DiskInterface disk_interface = DiskInterface::None;
|
||||||
|
Processor processor = Processor::MOS6502;
|
||||||
std::string loading_command;
|
std::string loading_command;
|
||||||
bool should_start_jasmin = false;
|
bool should_start_jasmin = false;
|
||||||
|
|
||||||
@ -42,8 +48,10 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
|
|||||||
if(needs_declare()) {
|
if(needs_declare()) {
|
||||||
DeclareField(rom);
|
DeclareField(rom);
|
||||||
DeclareField(disk_interface);
|
DeclareField(disk_interface);
|
||||||
|
DeclareField(processor);
|
||||||
AnnounceEnum(ROM);
|
AnnounceEnum(ROM);
|
||||||
AnnounceEnum(DiskInterface);
|
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("dmk", result.disks, Disk::DiskImageHolder<Storage::Disk::DMK>, TargetPlatform::MSX) // DMK
|
||||||
Format("do", result.disks, Disk::DiskImageHolder<Storage::Disk::AppleDSK>, TargetPlatform::DiskII) // DO
|
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("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::AppleDSK>, TargetPlatform::DiskII) // DSK (Apple II)
|
||||||
Format("dsk", result.disks, Disk::DiskImageHolder<Storage::Disk::MacintoshIMG>, TargetPlatform::Macintosh) // DSK (Macintosh, floppy disk)
|
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)
|
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);
|
cycles_since_6532_update_ += Cycles(cycles_run_for / 3);
|
||||||
bus_extender_.advance_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
|
// give the cartridge a chance to respond to the bus access
|
||||||
bus_extender_.perform_bus_operation(operation, address, value);
|
bus_extender_.perform_bus_operation(operation, address, value);
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
#include "../Utility/MemoryFuzzer.hpp"
|
#include "../Utility/MemoryFuzzer.hpp"
|
||||||
#include "../Utility/StringSerialiser.hpp"
|
#include "../Utility/StringSerialiser.hpp"
|
||||||
|
|
||||||
#include "../../Processors/6502/6502.hpp"
|
#include "../../Processors/6502Esque/6502Selector.hpp"
|
||||||
#include "../../Components/6522/6522.hpp"
|
#include "../../Components/6522/6522.hpp"
|
||||||
#include "../../Components/AY38910/AY38910.hpp"
|
#include "../../Components/AY38910/AY38910.hpp"
|
||||||
#include "../../Components/DiskII/DiskII.hpp"
|
#include "../../Components/DiskII/DiskII.hpp"
|
||||||
@ -41,6 +41,7 @@
|
|||||||
namespace Oric {
|
namespace Oric {
|
||||||
|
|
||||||
using DiskInterface = Analyser::Static::Oric::Target::DiskInterface;
|
using DiskInterface = Analyser::Static::Oric::Target::DiskInterface;
|
||||||
|
using Processor = Analyser::Static::Oric::Target::Processor;
|
||||||
using AY = GI::AY38910::AY38910<false>;
|
using AY = GI::AY38910::AY38910<false>;
|
||||||
using Speaker = Outputs::Speaker::LowpassSpeaker<AY>;
|
using Speaker = Outputs::Speaker::LowpassSpeaker<AY>;
|
||||||
|
|
||||||
@ -205,7 +206,7 @@ class VIAPortHandler: public MOS::MOS6522::IRQDelegatePortHandler {
|
|||||||
Keyboard &keyboard_;
|
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::TimedMachine,
|
||||||
public MachineTypes::ScanProducer,
|
public MachineTypes::ScanProducer,
|
||||||
public MachineTypes::AudioProducer,
|
public MachineTypes::AudioProducer,
|
||||||
@ -424,8 +425,8 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
|||||||
!tape_player_.get_tape()->is_at_end()) {
|
!tape_player_.get_tape()->is_at_end()) {
|
||||||
|
|
||||||
uint8_t next_byte = tape_player_.get_next_byte(!ram_[tape_speed_address_]);
|
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::MOS6502Esque::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::Flags, next_byte ? 0 : CPU::MOS6502::Flag::Zero);
|
||||||
*value = 0x60; // i.e. RTS
|
*value = 0x60; // i.e. RTS
|
||||||
}
|
}
|
||||||
} else {
|
} 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_invisible_ram_top_ = 0xffff;
|
||||||
const uint16_t basic_visible_ram_top_ = 0xbfff;
|
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
|
// RAM and ROM
|
||||||
std::vector<uint8_t> rom_, disk_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) {
|
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);
|
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);
|
#define DiskInterfaceSwitch(processor) \
|
||||||
case DiskInterface::Microdisc: return new ConcreteMachine<DiskInterface::Microdisc>(*oric_target, rom_fetcher);
|
switch(oric_target->disk_interface) { \
|
||||||
case DiskInterface::Pravetz: return new ConcreteMachine<DiskInterface::Pravetz>(*oric_target, rom_fetcher);
|
default: return new ConcreteMachine<DiskInterface::None, processor>(*oric_target, rom_fetcher); \
|
||||||
case DiskInterface::Jasmin: return new ConcreteMachine<DiskInterface::Jasmin>(*oric_target, rom_fetcher);
|
case DiskInterface::Microdisc: return new ConcreteMachine<DiskInterface::Microdisc, processor>(*oric_target, rom_fetcher); \
|
||||||
case DiskInterface::BD500: return new ConcreteMachine<DiskInterface::BD500>(*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() {}
|
Machine::~Machine() {}
|
||||||
|
@ -210,6 +210,12 @@
|
|||||||
4B4B1A3D200198CA00A0F866 /* KonamiSCC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4B1A3A200198C900A0F866 /* KonamiSCC.cpp */; };
|
4B4B1A3D200198CA00A0F866 /* KonamiSCC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4B1A3A200198C900A0F866 /* KonamiSCC.cpp */; };
|
||||||
4B4DC8211D2C2425003C5BF8 /* Vic20.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DC81F1D2C2425003C5BF8 /* Vic20.cpp */; };
|
4B4DC8211D2C2425003C5BF8 /* Vic20.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DC81F1D2C2425003C5BF8 /* Vic20.cpp */; };
|
||||||
4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DC8291D2C27A4003C5BF8 /* SerialBus.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 */; };
|
4B50AF80242817F40099BBD7 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B50AF7F242817F40099BBD7 /* QuartzCore.framework */; };
|
||||||
4B54C0BC1F8D8E790050900F /* KeyboardMachine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B54C0BB1F8D8E790050900F /* KeyboardMachine.cpp */; };
|
4B54C0BC1F8D8E790050900F /* KeyboardMachine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B54C0BB1F8D8E790050900F /* KeyboardMachine.cpp */; };
|
||||||
4B54C0BF1F8D8F450050900F /* Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B54C0BD1F8D8F450050900F /* Keyboard.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 */; };
|
4BF437EE209D0F7E008CBD6B /* SegmentParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BF437EC209D0F7E008CBD6B /* SegmentParser.cpp */; };
|
||||||
4BF437EF209D0F7E008CBD6B /* 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 */; };
|
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 */; };
|
4BFCA1241ECBDCB400AC40C1 /* AllRAMProcessor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BFCA1211ECBDCAF00AC40C1 /* AllRAMProcessor.cpp */; };
|
||||||
4BFCA1271ECBE33200AC40C1 /* TestMachineZ80.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BFCA1261ECBE33200AC40C1 /* TestMachineZ80.mm */; };
|
4BFCA1271ECBE33200AC40C1 /* TestMachineZ80.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BFCA1261ECBE33200AC40C1 /* TestMachineZ80.mm */; };
|
||||||
4BFCA1291ECBE7A700AC40C1 /* zexall.com in Resources */ = {isa = PBXBuildFile; fileRef = 4BFCA1281ECBE7A700AC40C1 /* zexall.com */; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
4BFCA1251ECBE33200AC40C1 /* TestMachineZ80.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestMachineZ80.h; sourceTree = "<group>"; };
|
||||||
@ -1980,6 +1999,7 @@
|
|||||||
4B9F11CB22729B3500701480 /* OPCLOGR2.BIN */,
|
4B9F11CB22729B3500701480 /* OPCLOGR2.BIN */,
|
||||||
4B2530F2244E6773007980BF /* FM Synthesis */,
|
4B2530F2244E6773007980BF /* FM Synthesis */,
|
||||||
4BBF49B41ED2881600AB3669 /* FUSE */,
|
4BBF49B41ED2881600AB3669 /* FUSE */,
|
||||||
|
4B4F475B2533EA64004245B8 /* jeek816 */,
|
||||||
4B670A822401CB8400D4E002 /* Patrik Rak Z80 Tests */,
|
4B670A822401CB8400D4E002 /* Patrik Rak Z80 Tests */,
|
||||||
4B9F11C72272375400701480 /* QL Startup */,
|
4B9F11C72272375400701480 /* QL Startup */,
|
||||||
4B85322B227793CA00F26553 /* TOS Startup */,
|
4B85322B227793CA00F26553 /* TOS Startup */,
|
||||||
@ -2451,6 +2471,32 @@
|
|||||||
path = 1540;
|
path = 1540;
|
||||||
sourceTree = "<group>";
|
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 */ = {
|
4B51F70820A521D700AFA2C1 /* Activity */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -3391,7 +3437,6 @@
|
|||||||
4BB73EB51B587A5100552FC2 /* Clock SignalTests */ = {
|
4BB73EB51B587A5100552FC2 /* Clock SignalTests */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
4BC0CB272446BC7B00A79DBB /* OPLTests.mm */,
|
|
||||||
4B85322922778E4200F26553 /* Comparative68000.hpp */,
|
4B85322922778E4200F26553 /* Comparative68000.hpp */,
|
||||||
4B90467222C6FA31000E2074 /* TestRunner68000.hpp */,
|
4B90467222C6FA31000E2074 /* TestRunner68000.hpp */,
|
||||||
4B97ADC722C6FD9B00A22A41 /* 68000ArithmeticTests.mm */,
|
4B97ADC722C6FD9B00A22A41 /* 68000ArithmeticTests.mm */,
|
||||||
@ -3410,6 +3455,7 @@
|
|||||||
4BE90FFC22D5864800FB464D /* MacintoshVideoTests.mm */,
|
4BE90FFC22D5864800FB464D /* MacintoshVideoTests.mm */,
|
||||||
4BA91E1C216D85BA00F79557 /* MasterSystemVDPTests.mm */,
|
4BA91E1C216D85BA00F79557 /* MasterSystemVDPTests.mm */,
|
||||||
4B98A0601FFADCDE00ADF63B /* MSXStaticAnalyserTests.mm */,
|
4B98A0601FFADCDE00ADF63B /* MSXStaticAnalyserTests.mm */,
|
||||||
|
4BC0CB272446BC7B00A79DBB /* OPLTests.mm */,
|
||||||
4B121F9A1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm */,
|
4B121F9A1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm */,
|
||||||
4BD4A8CF1E077FD20020D856 /* PCMTrackTests.mm */,
|
4BD4A8CF1E077FD20020D856 /* PCMTrackTests.mm */,
|
||||||
4BE76CF822641ED300ACD6FA /* QLTests.mm */,
|
4BE76CF822641ED300ACD6FA /* QLTests.mm */,
|
||||||
@ -3420,11 +3466,13 @@
|
|||||||
4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */,
|
4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */,
|
||||||
4BC751B11D157E61006C31D9 /* 6522Tests.swift */,
|
4BC751B11D157E61006C31D9 /* 6522Tests.swift */,
|
||||||
4B1E85801D176468001EF87D /* 6532Tests.swift */,
|
4B1E85801D176468001EF87D /* 6532Tests.swift */,
|
||||||
|
4B4F478925367EDC004245B8 /* 65816AddressingTests.swift */,
|
||||||
4BB73EB61B587A5100552FC2 /* AllSuiteATests.swift */,
|
4BB73EB61B587A5100552FC2 /* AllSuiteATests.swift */,
|
||||||
4B049CDC1DA3C82F00322067 /* BCDTest.swift */,
|
4B049CDC1DA3C82F00322067 /* BCDTest.swift */,
|
||||||
4B3BA0C21D318AEB005DD7A7 /* C1540Tests.swift */,
|
4B3BA0C21D318AEB005DD7A7 /* C1540Tests.swift */,
|
||||||
4BEF6AAB1D35D1C400E73575 /* DPLLTests.swift */,
|
4BEF6AAB1D35D1C400E73575 /* DPLLTests.swift */,
|
||||||
4BBF49AE1ED2880200AB3669 /* FUSETests.swift */,
|
4BBF49AE1ED2880200AB3669 /* FUSETests.swift */,
|
||||||
|
4B4F477B253530B7004245B8 /* Jeek816Tests.swift */,
|
||||||
4B1414611B58888700E04248 /* KlausDormannTests.swift */,
|
4B1414611B58888700E04248 /* KlausDormannTests.swift */,
|
||||||
4BD91D762401C2B8007BDC91 /* PatrikRakTests.swift */,
|
4BD91D762401C2B8007BDC91 /* PatrikRakTests.swift */,
|
||||||
4B14145F1B58885000E04248 /* WolfgangLorenzTests.swift */,
|
4B14145F1B58885000E04248 /* WolfgangLorenzTests.swift */,
|
||||||
@ -3485,6 +3533,8 @@
|
|||||||
4BFCA1221ECBDCAF00AC40C1 /* AllRAMProcessor.hpp */,
|
4BFCA1221ECBDCAF00AC40C1 /* AllRAMProcessor.hpp */,
|
||||||
4B2C455C1EC9442600FC74DD /* RegisterSizes.hpp */,
|
4B2C455C1EC9442600FC74DD /* RegisterSizes.hpp */,
|
||||||
4B1414561B58879D00E04248 /* 6502 */,
|
4B1414561B58879D00E04248 /* 6502 */,
|
||||||
|
4B4DEC15252BFA9C004583AC /* 6502Esque */,
|
||||||
|
4BF8D4CC251C0C9C00BBE21B /* 65816 */,
|
||||||
4BFF1D332233778C00838EA1 /* 68000 */,
|
4BFF1D332233778C00838EA1 /* 68000 */,
|
||||||
4B77069E1EC9045B0053B588 /* Z80 */,
|
4B77069E1EC9045B0053B588 /* Z80 */,
|
||||||
);
|
);
|
||||||
@ -3892,6 +3942,26 @@
|
|||||||
path = ../../ClockReceiver;
|
path = ../../ClockReceiver;
|
||||||
sourceTree = "<group>";
|
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 */ = {
|
4BFDD7891F7F2DB4008579B9 /* Utility */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -4001,7 +4071,7 @@
|
|||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
LastSwiftUpdateCheck = 0700;
|
LastSwiftUpdateCheck = 0700;
|
||||||
LastUpgradeCheck = 1130;
|
LastUpgradeCheck = 1200;
|
||||||
ORGANIZATIONNAME = "Thomas Harte";
|
ORGANIZATIONNAME = "Thomas Harte";
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
4B055A691FAE763F0060FFFF = {
|
4B055A691FAE763F0060FFFF = {
|
||||||
@ -4239,6 +4309,7 @@
|
|||||||
4BB299051B587D8400A49093 /* arrb in Resources */,
|
4BB299051B587D8400A49093 /* arrb in Resources */,
|
||||||
4BB299DC1B587D8400A49093 /* stazx in Resources */,
|
4BB299DC1B587D8400A49093 /* stazx in Resources */,
|
||||||
4B670A9D2401CB8400D4E002 /* z80ccf.tap in Resources */,
|
4B670A9D2401CB8400D4E002 /* z80ccf.tap in Resources */,
|
||||||
|
4B4F47652533EA64004245B8 /* suite-a.prg in Resources */,
|
||||||
4BB299C41B587D8400A49093 /* sbca in Resources */,
|
4BB299C41B587D8400A49093 /* sbca in Resources */,
|
||||||
4BB298F41B587D8400A49093 /* adcay in Resources */,
|
4BB298F41B587D8400A49093 /* adcay in Resources */,
|
||||||
4B44EBF51DC987AF00A7820C /* AllSuiteA.bin in Resources */,
|
4B44EBF51DC987AF00A7820C /* AllSuiteA.bin in Resources */,
|
||||||
@ -4468,6 +4539,7 @@
|
|||||||
4BDACBED22FFA5D20045EF7E /* ncr5380.cpp in Sources */,
|
4BDACBED22FFA5D20045EF7E /* ncr5380.cpp in Sources */,
|
||||||
4BC131772346DE9100E4FF3D /* StaticAnalyser.cpp in Sources */,
|
4BC131772346DE9100E4FF3D /* StaticAnalyser.cpp in Sources */,
|
||||||
4B055ACF1FAE9B030060FFFF /* SoundGenerator.cpp in Sources */,
|
4B055ACF1FAE9B030060FFFF /* SoundGenerator.cpp in Sources */,
|
||||||
|
4B4DEC08252BFA56004583AC /* 65816Base.cpp in Sources */,
|
||||||
4B894519201967B4007DE474 /* ConfidenceCounter.cpp in Sources */,
|
4B894519201967B4007DE474 /* ConfidenceCounter.cpp in Sources */,
|
||||||
4B055AEE1FAE9BBF0060FFFF /* Keyboard.cpp in Sources */,
|
4B055AEE1FAE9BBF0060FFFF /* Keyboard.cpp in Sources */,
|
||||||
4B055AED1FAE9BA20060FFFF /* Z80Storage.cpp in Sources */,
|
4B055AED1FAE9BA20060FFFF /* Z80Storage.cpp in Sources */,
|
||||||
@ -4489,6 +4561,7 @@
|
|||||||
4B9BE401203A0C0600FFAE60 /* MultiSpeaker.cpp in Sources */,
|
4B9BE401203A0C0600FFAE60 /* MultiSpeaker.cpp in Sources */,
|
||||||
4B055AA61FAE85EF0060FFFF /* Parser.cpp in Sources */,
|
4B055AA61FAE85EF0060FFFF /* Parser.cpp in Sources */,
|
||||||
4B055AE91FAE9B990060FFFF /* 6502Base.cpp in Sources */,
|
4B055AE91FAE9B990060FFFF /* 6502Base.cpp in Sources */,
|
||||||
|
4BF8D4D6251C11DD00BBE21B /* 65816Storage.cpp in Sources */,
|
||||||
4B055AEF1FAE9BF00060FFFF /* Typer.cpp in Sources */,
|
4B055AEF1FAE9BF00060FFFF /* Typer.cpp in Sources */,
|
||||||
4B89453F201967B4007DE474 /* StaticAnalyser.cpp in Sources */,
|
4B89453F201967B4007DE474 /* StaticAnalyser.cpp in Sources */,
|
||||||
4B89453D201967B4007DE474 /* StaticAnalyser.cpp in Sources */,
|
4B89453D201967B4007DE474 /* StaticAnalyser.cpp in Sources */,
|
||||||
@ -4749,6 +4822,7 @@
|
|||||||
4BEBFB4D2002C4BF000708CC /* MSXDSK.cpp in Sources */,
|
4BEBFB4D2002C4BF000708CC /* MSXDSK.cpp in Sources */,
|
||||||
4BBFBB6C1EE8401E00C01E7A /* ZX8081.cpp in Sources */,
|
4BBFBB6C1EE8401E00C01E7A /* ZX8081.cpp in Sources */,
|
||||||
4B83348A1F5DB94B0097E338 /* IRQDelegatePortHandler.cpp in Sources */,
|
4B83348A1F5DB94B0097E338 /* IRQDelegatePortHandler.cpp in Sources */,
|
||||||
|
4B4DEC06252BFA56004583AC /* 65816Base.cpp in Sources */,
|
||||||
4B894524201967B4007DE474 /* Tape.cpp in Sources */,
|
4B894524201967B4007DE474 /* Tape.cpp in Sources */,
|
||||||
4B7136891F78725F008B8ED9 /* Shifter.cpp in Sources */,
|
4B7136891F78725F008B8ED9 /* Shifter.cpp in Sources */,
|
||||||
4BDB61EB2032806E0048AF91 /* CSAtari2600.mm in Sources */,
|
4BDB61EB2032806E0048AF91 /* CSAtari2600.mm in Sources */,
|
||||||
@ -4771,6 +4845,7 @@
|
|||||||
4B55DD8320DF06680043F2E5 /* MachinePicker.swift in Sources */,
|
4B55DD8320DF06680043F2E5 /* MachinePicker.swift in Sources */,
|
||||||
4B2A539F1D117D36003C6002 /* CSAudioQueue.m in Sources */,
|
4B2A539F1D117D36003C6002 /* CSAudioQueue.m in Sources */,
|
||||||
4B89453E201967B4007DE474 /* StaticAnalyser.cpp in Sources */,
|
4B89453E201967B4007DE474 /* StaticAnalyser.cpp in Sources */,
|
||||||
|
4BF8D4D5251C11DD00BBE21B /* 65816Storage.cpp in Sources */,
|
||||||
4B0ACC2823775819008902D0 /* DMAController.cpp in Sources */,
|
4B0ACC2823775819008902D0 /* DMAController.cpp in Sources */,
|
||||||
4BC131702346DE5000E4FF3D /* StaticAnalyser.cpp in Sources */,
|
4BC131702346DE5000E4FF3D /* StaticAnalyser.cpp in Sources */,
|
||||||
4B37EE821D7345A6006A09A4 /* BinaryDump.cpp in Sources */,
|
4B37EE821D7345A6006A09A4 /* BinaryDump.cpp in Sources */,
|
||||||
@ -4839,6 +4914,7 @@
|
|||||||
4B778F4D23A5F20F0000D260 /* StaticAnalyser.cpp in Sources */,
|
4B778F4D23A5F20F0000D260 /* StaticAnalyser.cpp in Sources */,
|
||||||
4B778F0423A5EBB00000D260 /* OricMFMDSK.cpp in Sources */,
|
4B778F0423A5EBB00000D260 /* OricMFMDSK.cpp in Sources */,
|
||||||
4B3BA0CE1D318B44005DD7A7 /* C1540Bridge.mm in Sources */,
|
4B3BA0CE1D318B44005DD7A7 /* C1540Bridge.mm in Sources */,
|
||||||
|
4B4F477C253530B7004245B8 /* Jeek816Tests.swift in Sources */,
|
||||||
4B778F0F23A5EC560000D260 /* PCMTrack.cpp in Sources */,
|
4B778F0F23A5EC560000D260 /* PCMTrack.cpp in Sources */,
|
||||||
4B778F1123A5EC650000D260 /* FileHolder.cpp in Sources */,
|
4B778F1123A5EC650000D260 /* FileHolder.cpp in Sources */,
|
||||||
4B778EFC23A5EB8B0000D260 /* AcornADF.cpp in Sources */,
|
4B778EFC23A5EB8B0000D260 /* AcornADF.cpp in Sources */,
|
||||||
@ -4895,6 +4971,7 @@
|
|||||||
4B778F4123A5F19A0000D260 /* MemoryPacker.cpp in Sources */,
|
4B778F4123A5F19A0000D260 /* MemoryPacker.cpp in Sources */,
|
||||||
4B778F4423A5F1BE0000D260 /* CommodoreGCR.cpp in Sources */,
|
4B778F4423A5F1BE0000D260 /* CommodoreGCR.cpp in Sources */,
|
||||||
4B778EF923A5EB740000D260 /* MSA.cpp in Sources */,
|
4B778EF923A5EB740000D260 /* MSA.cpp in Sources */,
|
||||||
|
4B4DEC07252BFA56004583AC /* 65816Base.cpp in Sources */,
|
||||||
4B778F2323A5EDE40000D260 /* Tape.cpp in Sources */,
|
4B778F2323A5EDE40000D260 /* Tape.cpp in Sources */,
|
||||||
4B778F4F23A5F21C0000D260 /* StaticAnalyser.cpp in Sources */,
|
4B778F4F23A5F21C0000D260 /* StaticAnalyser.cpp in Sources */,
|
||||||
4B778EEF23A5D6680000D260 /* AsyncTaskQueue.cpp in Sources */,
|
4B778EEF23A5D6680000D260 /* AsyncTaskQueue.cpp in Sources */,
|
||||||
@ -4935,6 +5012,7 @@
|
|||||||
4B778F5723A5F2BB0000D260 /* ZX8081.cpp in Sources */,
|
4B778F5723A5F2BB0000D260 /* ZX8081.cpp in Sources */,
|
||||||
4B778F2F23A5F0B10000D260 /* ScanTarget.cpp in Sources */,
|
4B778F2F23A5F0B10000D260 /* ScanTarget.cpp in Sources */,
|
||||||
4BE90FFD22D5864800FB464D /* MacintoshVideoTests.mm in Sources */,
|
4BE90FFD22D5864800FB464D /* MacintoshVideoTests.mm in Sources */,
|
||||||
|
4B4F478A25367EDC004245B8 /* 65816AddressingTests.swift in Sources */,
|
||||||
4B778F0B23A5EC150000D260 /* TapeUEF.cpp in Sources */,
|
4B778F0B23A5EC150000D260 /* TapeUEF.cpp in Sources */,
|
||||||
4B778F0523A5EBB00000D260 /* ST.cpp in Sources */,
|
4B778F0523A5EBB00000D260 /* ST.cpp in Sources */,
|
||||||
4B778F0C23A5EC150000D260 /* TZX.cpp in Sources */,
|
4B778F0C23A5EC150000D260 /* TZX.cpp in Sources */,
|
||||||
@ -5144,6 +5222,7 @@
|
|||||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
@ -5202,6 +5281,7 @@
|
|||||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "1130"
|
LastUpgradeVersion = "1200"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "1130"
|
LastUpgradeVersion = "1200"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
@ -48,11 +48,6 @@
|
|||||||
BlueprintName = "Clock SignalTests"
|
BlueprintName = "Clock SignalTests"
|
||||||
ReferencedContainer = "container:Clock Signal.xcodeproj">
|
ReferencedContainer = "container:Clock Signal.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
<SkippedTests>
|
|
||||||
<Test
|
|
||||||
Identifier = "ZexallTests">
|
|
||||||
</Test>
|
|
||||||
</SkippedTests>
|
|
||||||
</TestableReference>
|
</TestableReference>
|
||||||
<TestableReference
|
<TestableReference
|
||||||
skipped = "YES">
|
skipped = "YES">
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "1130"
|
LastUpgradeVersion = "1200"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
@ -51,15 +51,6 @@
|
|||||||
savedToolIdentifier = ""
|
savedToolIdentifier = ""
|
||||||
useCustomWorkingDirectory = "NO"
|
useCustomWorkingDirectory = "NO"
|
||||||
debugDocumentVersioning = "YES">
|
debugDocumentVersioning = "YES">
|
||||||
<MacroExpansion>
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "4BB73E9D1B587A5100552FC2"
|
|
||||||
BuildableName = "Clock Signal.app"
|
|
||||||
BlueprintName = "Clock Signal"
|
|
||||||
ReferencedContainer = "container:Clock Signal.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</MacroExpansion>
|
|
||||||
</ProfileAction>
|
</ProfileAction>
|
||||||
<AnalyzeAction
|
<AnalyzeAction
|
||||||
buildConfiguration = "Debug">
|
buildConfiguration = "Debug">
|
||||||
|
@ -14,38 +14,38 @@ class MOS6502InterruptTests: XCTestCase {
|
|||||||
override func setUp() {
|
override func setUp() {
|
||||||
super.setUp()
|
super.setUp()
|
||||||
|
|
||||||
// create a machine full of NOPs
|
// Create a machine full of NOPs.
|
||||||
machine = CSTestMachine6502(is65C02: false)
|
machine = CSTestMachine6502(processor: .processor6502)
|
||||||
for c in 0...65535 {
|
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(0x34, forAddress: 0xfffe)
|
||||||
machine.setValue(0x12, forAddress: 0xffff)
|
machine.setValue(0x12, forAddress: 0xffff)
|
||||||
|
|
||||||
// add a CLI
|
// Add a CLI.
|
||||||
machine.setValue(0x58, forAddress: 0x4000)
|
machine.setValue(0x58, forAddress: 0x4000)
|
||||||
|
|
||||||
// pick things off at 0x4000
|
// Begin at 0x4000.
|
||||||
machine.setValue(0x4000, for: CSTestMachine6502Register.programCounter)
|
machine.setValue(0x4000, for: .programCounter)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testIRQLine() {
|
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)
|
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
|
// enable the interrupt line, check that it was too late
|
||||||
machine.irqLine = true
|
machine.irqLine = true
|
||||||
machine.runForNumber(ofCycles: 2)
|
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
|
// run for a further 7 cycles, confirm that the IRQ vector was jumped to
|
||||||
machine.runForNumber(ofCycles: 6)
|
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)
|
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() {
|
func testIFlagSet() {
|
||||||
@ -53,8 +53,8 @@ class MOS6502InterruptTests: XCTestCase {
|
|||||||
machine.irqLine = true
|
machine.irqLine = true
|
||||||
machine.runForNumber(ofCycles: 11)
|
machine.runForNumber(ofCycles: 11)
|
||||||
|
|
||||||
XCTAssert(machine.value(for: .programCounter) == 0x1234, "Interrupt routine should just have begun")
|
XCTAssertEqual(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: .flags) & 0x04, 0x04, "Interrupt status flag should be set")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCLISEIFlagClear() {
|
func testCLISEIFlagClear() {
|
||||||
@ -64,12 +64,13 @@ class MOS6502InterruptTests: XCTestCase {
|
|||||||
|
|
||||||
// run for four cycles; the CLI and SEI should have been performed
|
// run for four cycles; the CLI and SEI should have been performed
|
||||||
machine.runForNumber(ofCycles: 4)
|
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
|
// run for seven more cycles
|
||||||
machine.runForNumber(ofCycles: 7)
|
machine.runForNumber(ofCycles: 7)
|
||||||
|
|
||||||
// interrupt should have taken place despite SEI
|
// 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 {
|
class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
||||||
|
|
||||||
private var endTime: UInt32 = 0
|
private var endTime: UInt32 = 0
|
||||||
private let machine = CSTestMachine6502(is65C02: false)
|
private let machine = CSTestMachine6502(processor: .processor6502)
|
||||||
|
|
||||||
func testImplied() {
|
func testImplied() {
|
||||||
let code: [UInt8] = [
|
let code: [UInt8] = [
|
||||||
@ -22,7 +22,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
|||||||
0x18, // [2] CLC
|
0x18, // [2] CLC
|
||||||
0x2a, // [2] ROL A
|
0x2a, // [2] ROL A
|
||||||
]
|
]
|
||||||
self.runTest(code, expectedRunLength: 10)
|
runTest(code, expectedRunLength: 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testLDA() {
|
func testLDA() {
|
||||||
@ -40,7 +40,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
|||||||
0xb1, 0x00, // [5] LDA ($00), y (no wrap)
|
0xb1, 0x00, // [5] LDA ($00), y (no wrap)
|
||||||
0xb1, 0x02, // [6] LDA ($01), y (wrap)
|
0xb1, 0x02, // [6] LDA ($01), y (wrap)
|
||||||
]
|
]
|
||||||
self.runTest(code, expectedRunLength: 48)
|
runTest(code, expectedRunLength: 48)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testBIT() {
|
func testBIT() {
|
||||||
@ -48,7 +48,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
|||||||
0x24, 0x2a, // [3] BIT $2a
|
0x24, 0x2a, // [3] BIT $2a
|
||||||
0x2c, 0x2a, 0x2b, // [4] BIT $2b2a
|
0x2c, 0x2a, 0x2b, // [4] BIT $2b2a
|
||||||
]
|
]
|
||||||
self.runTest(code, expectedRunLength: 7)
|
runTest(code, expectedRunLength: 7)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSTA() {
|
func testSTA() {
|
||||||
@ -64,7 +64,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
|||||||
0x91, 0x00, // [6] STA ($00), y (no wrap)
|
0x91, 0x00, // [6] STA ($00), y (no wrap)
|
||||||
0x91, 0x02, // [6] STA ($01), y (wrap)
|
0x91, 0x02, // [6] STA ($01), y (wrap)
|
||||||
]
|
]
|
||||||
self.runTest(code, expectedRunLength: 49)
|
runTest(code, expectedRunLength: 49)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testINC() {
|
func testINC() {
|
||||||
@ -75,7 +75,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
|||||||
0xfe, 0x00, 0x00, // [7] INC $0000, x (no wrap)
|
0xfe, 0x00, 0x00, // [7] INC $0000, x (no wrap)
|
||||||
0xfe, 0x02, 0x00, // [7] INC $0002, x (wrap)
|
0xfe, 0x02, 0x00, // [7] INC $0002, x (wrap)
|
||||||
]
|
]
|
||||||
self.runTest(code, expectedRunLength: 31)
|
runTest(code, expectedRunLength: 31)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testJSR() {
|
func testJSR() {
|
||||||
@ -85,7 +85,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
|||||||
0x60, // [6] RTS
|
0x60, // [6] RTS
|
||||||
]
|
]
|
||||||
machine.addTrapAddress(0x0203)
|
machine.addTrapAddress(0x0203)
|
||||||
self.runTest(code, expectedRunLength: 12)
|
runTest(code, expectedRunLength: 12)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testJMP() {
|
func testJMP() {
|
||||||
@ -94,7 +94,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
|||||||
0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x4c, 0x0b, 0x02, // [3] JMP 020b
|
0x4c, 0x0b, 0x02, // [3] JMP 020b
|
||||||
]
|
]
|
||||||
self.runTest(code, expectedRunLength: 8)
|
runTest(code, expectedRunLength: 8)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testPHAPLA() {
|
func testPHAPLA() {
|
||||||
@ -103,7 +103,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
|||||||
0x48, // [3] PHA
|
0x48, // [3] PHA
|
||||||
0x68, // [4] PLA
|
0x68, // [4] PLA
|
||||||
]
|
]
|
||||||
self.runTest(code, expectedRunLength: 10)
|
runTest(code, expectedRunLength: 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testBCS() {
|
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,
|
||||||
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() {
|
func testSnippet1() {
|
||||||
@ -138,7 +138,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
|||||||
0x8d, 0x08, 0x00, // [4] STA $0008
|
0x8d, 0x08, 0x00, // [4] STA $0008
|
||||||
0xc6, 0xb4, // [5] DEC $B4
|
0xc6, 0xb4, // [5] DEC $B4
|
||||||
]
|
]
|
||||||
self.runTest(code, expectedRunLength: 9)
|
runTest(code, expectedRunLength: 9)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSnippet2() {
|
func testSnippet2() {
|
||||||
@ -146,7 +146,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
|||||||
0x16, 0x16, // [6] ASL $16, x
|
0x16, 0x16, // [6] ASL $16, x
|
||||||
0x46, 0x46, // [5] LSR $46
|
0x46, 0x46, // [5] LSR $46
|
||||||
]
|
]
|
||||||
self.runTest(code, expectedRunLength: 11)
|
runTest(code, expectedRunLength: 11)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSnippet3() {
|
func testSnippet3() {
|
||||||
@ -174,7 +174,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
|||||||
0x60, // [6] RTS
|
0x60, // [6] RTS
|
||||||
]
|
]
|
||||||
machine.addTrapAddress(0x0203)
|
machine.addTrapAddress(0x0203)
|
||||||
self.runTest(code, expectedRunLength: 66)
|
runTest(code, expectedRunLength: 66)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testNOP() {
|
func testNOP() {
|
||||||
@ -194,10 +194,10 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
|||||||
0xe2, 0x00, // [2] NOP #
|
0xe2, 0x00, // [2] NOP #
|
||||||
0xf4, 0x00, // [4] NOP zpg, x
|
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
|
machine.trapHandler = self
|
||||||
|
|
||||||
let immediateCode = Data(code)
|
let immediateCode = Data(code)
|
||||||
@ -213,17 +213,17 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
|||||||
machine.setValue(0xff, for: CSTestMachine6502Register.X)
|
machine.setValue(0xff, for: CSTestMachine6502Register.X)
|
||||||
machine.setValue(0xfe, for: CSTestMachine6502Register.Y)
|
machine.setValue(0xfe, for: CSTestMachine6502Register.Y)
|
||||||
|
|
||||||
self.endTime = 0
|
endTime = 0
|
||||||
while self.endTime == 0 {
|
while endTime == 0 {
|
||||||
machine.runForNumber(ofCycles: 10)
|
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) {
|
func testMachine(_ testMachine: CSTestMachine, didTrapAtAddress address: UInt16) {
|
||||||
if self.endTime == 0 {
|
if endTime == 0 {
|
||||||
self.endTime = (machine.timestamp / 2) - 1
|
endTime = (machine.timestamp / 2) - 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ import Foundation
|
|||||||
|
|
||||||
class MOS6532Tests: XCTestCase {
|
class MOS6532Tests: XCTestCase {
|
||||||
|
|
||||||
fileprivate func with6532(_ action: (MOS6532Bridge) -> ()) {
|
private func with6532(_ action: (MOS6532Bridge) -> ()) {
|
||||||
let bridge = MOS6532Bridge()
|
let bridge = MOS6532Bridge()
|
||||||
action(bridge)
|
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) {
|
Test68000() : processor(*this) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void will_perform(uint32_t address, uint16_t opcode) {
|
void will_perform(uint32_t, uint16_t) {
|
||||||
--instructions_remaining_;
|
--instructions_remaining_;
|
||||||
if(!instructions_remaining_) comparitor();
|
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;
|
using Microcycle = CPU::MC68000::Microcycle;
|
||||||
if(cycle.data_select_active()) {
|
if(cycle.data_select_active()) {
|
||||||
cycle.apply(&ram[cycle.host_endian_byte_address()]);
|
cycle.apply(&ram[cycle.host_endian_byte_address()]);
|
||||||
|
@ -13,7 +13,7 @@ class AllSuiteATests: XCTestCase {
|
|||||||
func testAllSuiteA() {
|
func testAllSuiteA() {
|
||||||
if let filename = Bundle(for: type(of: self)).path(forResource: "AllSuiteA", ofType: "bin") {
|
if let filename = Bundle(for: type(of: self)).path(forResource: "AllSuiteA", ofType: "bin") {
|
||||||
if let allSuiteA = try? Data(contentsOf: URL(fileURLWithPath: filename)) {
|
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.setData(allSuiteA, atAddress: 0x4000)
|
||||||
machine.setValue(CSTestMachine6502JamOpcode, forAddress:0x45c0); // end
|
machine.setValue(CSTestMachine6502JamOpcode, forAddress:0x45c0); // end
|
||||||
@ -27,4 +27,5 @@ class AllSuiteATests: XCTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ class BCDTest: XCTestCase, CSTestMachineTrapHandler {
|
|||||||
func testBCD() {
|
func testBCD() {
|
||||||
if let filename = Bundle(for: type(of: self)).path(forResource: "BCDTEST_beeb", ofType: nil) {
|
if let filename = Bundle(for: type(of: self)).path(forResource: "BCDTEST_beeb", ofType: nil) {
|
||||||
if let bcdTest = try? Data(contentsOf: URL(fileURLWithPath: filename)) {
|
if let bcdTest = try? Data(contentsOf: URL(fileURLWithPath: filename)) {
|
||||||
let machine = CSTestMachine6502(is65C02: false)
|
let machine = CSTestMachine6502(processor: .processor6502)
|
||||||
machine.trapHandler = self
|
machine.trapHandler = self
|
||||||
|
|
||||||
machine.setData(bcdTest, atAddress: 0x2900)
|
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) {
|
func testMachine(_ testMachine: CSTestMachine, didTrapAtAddress address: UInt16) {
|
||||||
let machine6502 = testMachine as! CSTestMachine6502
|
let machine6502 = testMachine as! CSTestMachine6502
|
||||||
|
|
||||||
@ -48,4 +48,5 @@ class BCDTest: XCTestCase, CSTestMachineTrapHandler {
|
|||||||
let character = machine6502.value(for: .A)
|
let character = machine6502.value(for: .A)
|
||||||
output.append(Character(UnicodeScalar(character)!))
|
output.append(Character(UnicodeScalar(character)!))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,16 @@ typedef NS_ENUM(NSInteger, CSTestMachine6502Register) {
|
|||||||
CSTestMachine6502RegisterA,
|
CSTestMachine6502RegisterA,
|
||||||
CSTestMachine6502RegisterX,
|
CSTestMachine6502RegisterX,
|
||||||
CSTestMachine6502RegisterY,
|
CSTestMachine6502RegisterY,
|
||||||
|
CSTestMachine6502RegisterEmulationFlag,
|
||||||
|
CSTestMachine6502RegisterDataBank,
|
||||||
|
CSTestMachine6502RegisterProgramBank,
|
||||||
|
CSTestMachine6502RegisterDirect,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef NS_ENUM(NSInteger, CSTestMachine6502Processor) {
|
||||||
|
CSTestMachine6502Processor6502,
|
||||||
|
CSTestMachine6502Processor65C02,
|
||||||
|
CSTestMachine6502Processor65816
|
||||||
};
|
};
|
||||||
|
|
||||||
extern const uint8_t CSTestMachine6502JamOpcode;
|
extern const uint8_t CSTestMachine6502JamOpcode;
|
||||||
@ -25,13 +35,13 @@ extern const uint8_t CSTestMachine6502JamOpcode;
|
|||||||
|
|
||||||
- (nonnull instancetype)init NS_UNAVAILABLE;
|
- (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)runForNumberOfCycles:(int)cycles;
|
||||||
|
|
||||||
- (void)setValue:(uint8_t)value forAddress:(uint16_t)address;
|
- (void)setValue:(uint8_t)value forAddress:(uint32_t)address;
|
||||||
- (uint8_t)valueForAddress:(uint16_t)address;
|
- (uint8_t)valueForAddress:(uint32_t)address;
|
||||||
- (void)setValue:(uint16_t)value forRegister:(CSTestMachine6502Register)reg;
|
- (void)setValue:(uint16_t)value forRegister:(CSTestMachine6502Register)reg;
|
||||||
- (uint16_t)valueForRegister:(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 CSTestMachine6502RegisterX: return CPU::MOS6502::Register::X;
|
||||||
case CSTestMachine6502RegisterY: return CPU::MOS6502::Register::Y;
|
case CSTestMachine6502RegisterY: return CPU::MOS6502::Register::Y;
|
||||||
case CSTestMachine6502RegisterStackPointer: return CPU::MOS6502::Register::StackPointer;
|
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
|
#pragma mark - Lifecycle
|
||||||
|
|
||||||
- (instancetype)initIs65C02:(BOOL)is65C02 {
|
- (instancetype)initWithProcessor:(CSTestMachine6502Processor)processor {
|
||||||
self = [super init];
|
self = [super init];
|
||||||
|
|
||||||
if(self) {
|
if(self) {
|
||||||
_processor = CPU::MOS6502::AllRAMProcessor::Processor(
|
switch(processor) {
|
||||||
is65C02 ? CPU::MOS6502::Personality::PWDC65C02 : CPU::MOS6502::Personality::P6502);
|
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;
|
return self;
|
||||||
@ -52,13 +64,13 @@ static CPU::MOS6502::Register registerForRegister(CSTestMachine6502Register reg)
|
|||||||
|
|
||||||
#pragma mark - Accessors
|
#pragma mark - Accessors
|
||||||
|
|
||||||
- (uint8_t)valueForAddress:(uint16_t)address {
|
- (uint8_t)valueForAddress:(uint32_t)address {
|
||||||
uint8_t value;
|
uint8_t value;
|
||||||
_processor->get_data_at_address(address, 1, &value);
|
_processor->get_data_at_address(address, 1, &value);
|
||||||
return 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);
|
_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));
|
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);
|
_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:
|
public:
|
||||||
BusOperationHandler(CSTestMachineZ80 *targetMachine) : target_(targetMachine) {}
|
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];
|
[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 {
|
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
|
#pragma mark - Capture class
|
||||||
|
@ -10,21 +10,21 @@ import XCTest
|
|||||||
|
|
||||||
class C1540Tests: XCTestCase {
|
class C1540Tests: XCTestCase {
|
||||||
|
|
||||||
fileprivate func with1540(_ action: (C1540Bridge) -> ()) {
|
private func with1540(_ action: (C1540Bridge) -> ()) {
|
||||||
let bridge = C1540Bridge()
|
let bridge = C1540Bridge()
|
||||||
action(bridge)
|
action(bridge)
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func transmit(_ c1540: C1540Bridge, value: Int) {
|
private func transmit(_ c1540: C1540Bridge, value: Int) {
|
||||||
var shiftedValue = value
|
var shiftedValue = value
|
||||||
|
|
||||||
c1540.dataLine = true
|
c1540.dataLine = true
|
||||||
c1540.run(forCycles: 256)
|
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.clockLine = true
|
||||||
c1540.run(forCycles: 256) // this isn't time limited on real hardware
|
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
|
// set up for byte transfer
|
||||||
c1540.clockLine = false
|
c1540.clockLine = false
|
||||||
@ -47,7 +47,7 @@ class C1540Tests: XCTestCase {
|
|||||||
// check for acknowledgment
|
// check for acknowledgment
|
||||||
c1540.dataLine = true
|
c1540.dataLine = true
|
||||||
c1540.run(forCycles: 1000)
|
c1540.run(forCycles: 1000)
|
||||||
XCTAssert(c1540.dataLine == false, "Listener should have acknowledged byte")
|
XCTAssertFalse(c1540.dataLine, "Listener should have acknowledged byte")
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: EOI
|
// MARK: EOI
|
||||||
@ -64,10 +64,11 @@ class C1540Tests: XCTestCase {
|
|||||||
|
|
||||||
// proceed 1 ms and check that the 1540 pulled the data line low
|
// proceed 1 ms and check that the 1540 pulled the data line low
|
||||||
$0.run(forCycles: 1000)
|
$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
|
// transmit LISTEN #8
|
||||||
self.transmit($0, value: 0x28)
|
transmit($0, value: 0x28)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ class ComparativeBusHandler: public CPU::MC68000::BusHandler {
|
|||||||
gzclose(trace);
|
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.
|
// Obtain the next line from the trace file.
|
||||||
char correct_state[300] = "\n";
|
char correct_state[300] = "\n";
|
||||||
gzgets(trace, correct_state, sizeof(correct_state));
|
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, "Diverges at line %d\n", line_count);
|
||||||
fprintf(stderr, "Good: %s", correct_state);
|
fprintf(stderr, "Good: %s", correct_state);
|
||||||
fprintf(stderr, "Bad: %s", local_state);
|
fprintf(stderr, "Bad: %s", local_state);
|
||||||
assert(false);
|
throw std::exception();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ class EmuTOS: public ComparativeBusHandler {
|
|||||||
return m68000_.get_state();
|
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();
|
const uint32_t address = cycle.word_address();
|
||||||
uint32_t word_address = 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 {
|
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 filename = Bundle(for: type(of: self)).path(forResource: resource, ofType: "bin") {
|
||||||
if let functionalTest = try? Data(contentsOf: URL(fileURLWithPath: filename)) {
|
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.setData(functionalTest, atAddress: 0)
|
||||||
machine.setValue(0x400, for: .programCounter)
|
machine.setValue(0x400, for: .programCounter)
|
||||||
@ -39,21 +39,25 @@ class KlausDormannTests: XCTestCase {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs Klaus Dorman's 6502 tests.
|
private func runTest6502(processor: CSTestMachine6502Processor) {
|
||||||
func test6502() {
|
|
||||||
func errorForTrapAddress(_ address: UInt16) -> String? {
|
func errorForTrapAddress(_ address: UInt16) -> String? {
|
||||||
switch address {
|
switch address {
|
||||||
case 0x3399: return nil // success!
|
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 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 0x33b9: return "Decimal SBC result has wrong value"
|
||||||
case 0x33c0: return "Decimal SBC wrong carry flag"
|
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 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 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"
|
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)
|
let error = errorForTrapAddress(destination)
|
||||||
XCTAssert(error == nil, "Failed with error \(error!)")
|
XCTAssert(error == nil, "Failed with error \(error!)")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs Klaus Dorman's 65C02 tests.
|
private func runTest65C02(processor: CSTestMachine6502Processor) {
|
||||||
func test65C02() {
|
|
||||||
func errorForTrapAddress(_ address: UInt16) -> String? {
|
func errorForTrapAddress(_ address: UInt16) -> String? {
|
||||||
switch address {
|
switch address {
|
||||||
case 0x24f1: return nil // success!
|
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)
|
let error = errorForTrapAddress(destination)
|
||||||
XCTAssert(error == nil, "Failed with error \(error!)")
|
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 {
|
class PatrikRakTests: XCTestCase, CSTestMachineTrapHandler {
|
||||||
|
|
||||||
fileprivate var done = false
|
private var done = false
|
||||||
fileprivate var output = ""
|
private var output = ""
|
||||||
|
|
||||||
private func runTest(_ name: String) {
|
private func runTest(_ name: String) {
|
||||||
if let filename = Bundle(for: type(of: self)).path(forResource: name, ofType: "tap") {
|
if let filename = Bundle(for: type(of: self)).path(forResource: name, ofType: "tap") {
|
||||||
@ -124,4 +124,5 @@ class PatrikRakTests: XCTestCase, CSTestMachineTrapHandler {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ class QL: public ComparativeBusHandler {
|
|||||||
return m68000_.get_state();
|
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();
|
const uint32_t address = cycle.word_address();
|
||||||
uint32_t word_address = address;
|
uint32_t word_address = address;
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ class RAM68000: public CPU::MC68000::BusHandler {
|
|||||||
ram_[1] = sp & 0xffff;
|
ram_[1] = sp & 0xffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
void will_perform(uint32_t address, uint16_t opcode) {
|
void will_perform(uint32_t, uint16_t) {
|
||||||
--instructions_remaining_;
|
--instructions_remaining_;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,7 +80,7 @@ class RAM68000: public CPU::MC68000::BusHandler {
|
|||||||
return &ram_[(address >> 1) % ram_.size()];
|
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();
|
const uint32_t word_address = cycle.word_address();
|
||||||
if(instructions_remaining_) duration_ += cycle.length;
|
if(instructions_remaining_) duration_ += cycle.length;
|
||||||
|
|
||||||
|
@ -11,206 +11,294 @@ import Foundation
|
|||||||
|
|
||||||
class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler {
|
class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler {
|
||||||
|
|
||||||
func testWolfgangLorenzStart() {
|
// MARK: - 6502 Tests
|
||||||
self.runWolfgangLorenzTest(" start")
|
|
||||||
|
func testStart() {
|
||||||
|
runTest(" start", processor: .processor6502)
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzLDA() {
|
func testLDA() {
|
||||||
self.runWolfgangLorenzTest("lda", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"])
|
runTest("lda", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzSTA() {
|
func testSTA() {
|
||||||
self.runWolfgangLorenzTest("sta", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"])
|
runTest("sta", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzLDX() {
|
func testLDX() {
|
||||||
self.runWolfgangLorenzTest("ldx", suffixes: ["b", "z", "zy", "a", "ay"])
|
runTest("ldx", suffixes: ["b", "z", "zy", "a", "ay"], processor: .processor6502)
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzSTX() {
|
func testSTX() {
|
||||||
self.runWolfgangLorenzTest("stx", suffixes: ["z", "zy", "a"])
|
runTest("stx", suffixes: ["z", "zy", "a"], processor: .processor6502)
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzLDY() {
|
func testLDY() {
|
||||||
self.runWolfgangLorenzTest("ldy", suffixes: ["b", "z", "zx", "a", "ax"])
|
runTest("ldy", suffixes: ["b", "z", "zx", "a", "ax"], processor: .processor6502)
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzSTY() {
|
func testSTY() {
|
||||||
self.runWolfgangLorenzTest("sty", suffixes: ["z", "zx", "a"])
|
runTest("sty", suffixes: ["z", "zx", "a"], processor: .processor6502)
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzTransfers() {
|
func testTransfers() {
|
||||||
self.runWolfgangLorenzTest("taxn")
|
testTransfers(processor: .processor6502)
|
||||||
self.runWolfgangLorenzTest("tayn")
|
|
||||||
self.runWolfgangLorenzTest("txan")
|
|
||||||
self.runWolfgangLorenzTest("tyan")
|
|
||||||
self.runWolfgangLorenzTest("tsxn")
|
|
||||||
self.runWolfgangLorenzTest("txsn")
|
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzStack() {
|
func testStack() {
|
||||||
self.runWolfgangLorenzTest("phan")
|
testStack(processor: .processor6502)
|
||||||
self.runWolfgangLorenzTest("plan")
|
|
||||||
self.runWolfgangLorenzTest("phpn")
|
|
||||||
self.runWolfgangLorenzTest("plpn")
|
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzIncsAndDecs() {
|
func testIncsAndDecs() {
|
||||||
self.runWolfgangLorenzTest("inxn")
|
testIncsAndDecs(processor: .processor6502)
|
||||||
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 testWolfgangLorenzASL() {
|
func testASL() {
|
||||||
self.runWolfgangLorenzTest("asl", suffixes: ["n", "z", "zx", "a", "ax"])
|
runTest("asl", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor6502)
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzLSR() {
|
func testLSR() {
|
||||||
self.runWolfgangLorenzTest("lsr", suffixes: ["n", "z", "zx", "a", "ax"])
|
runTest("lsr", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor6502)
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzROL() {
|
func testROL() {
|
||||||
self.runWolfgangLorenzTest("rol", suffixes: ["n", "z", "zx", "a", "ax"])
|
runTest("rol", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor6502)
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzROR() {
|
func testROR() {
|
||||||
self.runWolfgangLorenzTest("ror", suffixes: ["n", "z", "zx", "a", "ax"])
|
runTest("ror", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor6502)
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzAND() {
|
func testAND() {
|
||||||
self.runWolfgangLorenzTest("and", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"])
|
runTest("and", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzORA() {
|
func testORA() {
|
||||||
self.runWolfgangLorenzTest("ora", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"])
|
runTest("ora", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzEOR() {
|
func testEOR() {
|
||||||
self.runWolfgangLorenzTest("eor", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"])
|
runTest("eor", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzFlagManipulation() {
|
func testFlagManipulation() {
|
||||||
self.runWolfgangLorenzTest("clcn")
|
testFlagManipulation(processor: .processor6502)
|
||||||
self.runWolfgangLorenzTest("secn")
|
|
||||||
self.runWolfgangLorenzTest("cldn")
|
|
||||||
self.runWolfgangLorenzTest("sedn")
|
|
||||||
self.runWolfgangLorenzTest("clin")
|
|
||||||
self.runWolfgangLorenzTest("sein")
|
|
||||||
self.runWolfgangLorenzTest("clvn")
|
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzADC() {
|
func testADC() {
|
||||||
self.runWolfgangLorenzTest("adc", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"])
|
runTest("adc", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzSBC() {
|
func testSBC() {
|
||||||
self.runWolfgangLorenzTest("sbc", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"])
|
runTest("sbc", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzCompare() {
|
func testCompare() {
|
||||||
self.runWolfgangLorenzTest("cmp", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"])
|
testCompare(processor: .processor6502)
|
||||||
self.runWolfgangLorenzTest("cpx", suffixes: ["b", "z", "a"])
|
|
||||||
self.runWolfgangLorenzTest("cpy", suffixes: ["b", "z", "a"])
|
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzBIT() {
|
func testBIT() {
|
||||||
self.runWolfgangLorenzTest("bit", suffixes: ["z", "a"])
|
runTest("bit", suffixes: ["z", "a"], processor: .processor6502)
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzFlow() {
|
func testFlow() {
|
||||||
self.runWolfgangLorenzTest("brkn")
|
testFlow(processor: .processor6502)
|
||||||
self.runWolfgangLorenzTest("rtin")
|
|
||||||
self.runWolfgangLorenzTest("jsrw")
|
|
||||||
self.runWolfgangLorenzTest("rtsn")
|
|
||||||
self.runWolfgangLorenzTest("jmpw")
|
|
||||||
self.runWolfgangLorenzTest("jmpi")
|
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzBranch() {
|
func testBranch() {
|
||||||
self.runWolfgangLorenzTest("beqr")
|
testBranch(processor: .processor6502)
|
||||||
self.runWolfgangLorenzTest("bner")
|
|
||||||
self.runWolfgangLorenzTest("bmir")
|
|
||||||
self.runWolfgangLorenzTest("bplr")
|
|
||||||
self.runWolfgangLorenzTest("bcsr")
|
|
||||||
self.runWolfgangLorenzTest("bccr")
|
|
||||||
self.runWolfgangLorenzTest("bvsr")
|
|
||||||
self.runWolfgangLorenzTest("bvcr")
|
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzNOP() {
|
func testNOP() {
|
||||||
self.runWolfgangLorenzTest("nop", suffixes: ["n", "b", "z", "zx", "a", "ax"])
|
runTest("nop", suffixes: ["n", "b", "z", "zx", "a", "ax"], processor: .processor6502)
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzASO() {
|
func testASO() {
|
||||||
self.runWolfgangLorenzTest("aso", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"])
|
runTest("aso", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzRLA() {
|
func testRLA() {
|
||||||
self.runWolfgangLorenzTest("rla", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"])
|
runTest("rla", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzLSE() {
|
func testLSE() {
|
||||||
self.runWolfgangLorenzTest("lse", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"])
|
runTest("lse", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzRRA() {
|
func testRRA() {
|
||||||
self.runWolfgangLorenzTest("rra", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"])
|
runTest("rra", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzDCM() {
|
func testDCM() {
|
||||||
self.runWolfgangLorenzTest("dcm", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"])
|
runTest("dcm", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzINS() {
|
func testINS() {
|
||||||
self.runWolfgangLorenzTest("ins", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"])
|
runTest("ins", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzLAX() {
|
func testLAX() {
|
||||||
self.runWolfgangLorenzTest("lax", suffixes: ["z", "zy", "a", "ay", "ix", "iy"])
|
runTest("lax", suffixes: ["z", "zy", "a", "ay", "ix", "iy"], processor: .processor6502)
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzAXS() {
|
func testAXS() {
|
||||||
self.runWolfgangLorenzTest("axs", suffixes: ["z", "zy", "a", "ix"])
|
runTest("axs", suffixes: ["z", "zy", "a", "ix"], processor: .processor6502)
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzALR() {
|
func testALR() {
|
||||||
self.runWolfgangLorenzTest("alrb")
|
runTest("alrb", processor: .processor6502)
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzARR() {
|
func testARR() {
|
||||||
self.runWolfgangLorenzTest("arrb")
|
runTest("arrb", processor: .processor6502)
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzSBX() {
|
func testSBX() {
|
||||||
self.runWolfgangLorenzTest("sbxb")
|
runTest("sbxb", processor: .processor6502)
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzSHA() {
|
func testSHA() {
|
||||||
self.runWolfgangLorenzTest("sha", suffixes: ["ay", "iy"])
|
runTest("sha", suffixes: ["ay", "iy"], processor: .processor6502)
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzSHX() {
|
func testSHX() {
|
||||||
self.runWolfgangLorenzTest("shxay")
|
runTest("shxay", processor: .processor6502)
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzSHY() {
|
func testSHY() {
|
||||||
self.runWolfgangLorenzTest("shyax")
|
runTest("shyax", processor: .processor6502)
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzSHS() {
|
func testSHS() {
|
||||||
self.runWolfgangLorenzTest("shsay")
|
runTest("shsay", processor: .processor6502)
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzLXA() {
|
func testLXA() {
|
||||||
self.runWolfgangLorenzTest("lxab")
|
runTest("lxab", processor: .processor6502)
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzANE() {
|
func testANE() {
|
||||||
self.runWolfgangLorenzTest("aneb")
|
runTest("aneb", processor: .processor6502)
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzANC() {
|
func testANC() {
|
||||||
self.runWolfgangLorenzTest("ancb")
|
runTest("ancb", processor: .processor6502)
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzLAS() {
|
func testLAS() {
|
||||||
self.runWolfgangLorenzTest("lasay")
|
runTest("lasay", processor: .processor6502)
|
||||||
}
|
}
|
||||||
func testWolfgangLorenzSBCB() {
|
func testSBCB() {
|
||||||
self.runWolfgangLorenzTest("sbcb(eb)")
|
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 {
|
for suffix in suffixes {
|
||||||
let testName = name + suffix
|
let testName = name + suffix
|
||||||
self.runWolfgangLorenzTest(testName)
|
runTest(testName, processor: processor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate var output: String = ""
|
fileprivate var output: String = ""
|
||||||
fileprivate func runWolfgangLorenzTest(_ name: String) {
|
fileprivate func runTest(_ name: String, processor: CSTestMachine6502Processor) {
|
||||||
var machine: CSTestMachine6502!
|
var machine: CSTestMachine6502!
|
||||||
|
|
||||||
if let filename = Bundle(for: type(of: self)).path(forResource: name, ofType: nil) {
|
if let filename = Bundle(for: type(of: self)).path(forResource: name, ofType: nil) {
|
||||||
if let testData = try? Data(contentsOf: URL(fileURLWithPath: filename)) {
|
if let testData = try? Data(contentsOf: URL(fileURLWithPath: filename)) {
|
||||||
|
|
||||||
machine = CSTestMachine6502(is65C02: false)
|
machine = CSTestMachine6502(processor: processor)
|
||||||
machine.trapHandler = self
|
machine.trapHandler = self
|
||||||
// machine.logActivity = true
|
|
||||||
output = ""
|
output = ""
|
||||||
|
|
||||||
let dataPointer = (testData as NSData).bytes.bindMemory(to: UInt8.self, capacity: testData.count)
|
let dataPointer = (testData as NSData).bytes.bindMemory(to: UInt8.self, capacity: testData.count)
|
||||||
let loadAddress = UInt16(dataPointer[0]) | (UInt16(dataPointer[1]) << 8)
|
let loadAddress = UInt32(dataPointer[0]) | (UInt32(dataPointer[1]) << 8)
|
||||||
let contents = testData.subdata(in: 2..<(testData.count - 2))
|
let contents = testData.subdata(in: 2 ..< testData.count)
|
||||||
|
|
||||||
machine.setData(contents, atAddress: loadAddress)
|
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: 0x0002)
|
||||||
machine.setValue(0x00, forAddress: 0xa002)
|
machine.setValue(0x00, forAddress: 0xa002)
|
||||||
machine.setValue(0x80, forAddress: 0xa003)
|
machine.setValue(0x80, forAddress: 0xa003)
|
||||||
@ -219,28 +307,39 @@ class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler {
|
|||||||
machine.setValue(0x48, forAddress: 0xfffe)
|
machine.setValue(0x48, forAddress: 0xfffe)
|
||||||
machine.setValue(0xff, forAddress: 0xffff)
|
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,
|
0x48, 0x8a, 0x48, 0x98, 0x48, 0xba, 0xbd, 0x04, 0x01,
|
||||||
0x29, 0x10, 0xf0, 0x03, 0x6c, 0x16, 0x03, 0x6c, 0x14, 0x03
|
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(0xffd2) // print character
|
||||||
machine.addTrapAddress(0xffe4) // scan keyboard
|
machine.addTrapAddress(0xffe4) // scan keyboard
|
||||||
|
|
||||||
|
// Set a couple of test addresses that indicate failure.
|
||||||
machine.addTrapAddress(0x8000) // exit
|
machine.addTrapAddress(0x8000) // exit
|
||||||
machine.addTrapAddress(0xa474) // 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:0xffd2) // 0x60 is RTS
|
||||||
machine.setValue(0x60, forAddress:0xffe4)
|
machine.setValue(0x60, forAddress:0xffe4)
|
||||||
machine.setValue(0x60, forAddress:0x8000)
|
machine.setValue(0x60, forAddress:0x8000)
|
||||||
machine.setValue(0x60, forAddress:0xa474)
|
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)
|
// Seed program entry.
|
||||||
machine.setValue(0xfd, for: CSTestMachine6502Register.stackPointer)
|
machine.setValue(0x0801, for: .programCounter)
|
||||||
machine.setValue(0x04, for: CSTestMachine6502Register.flags)
|
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()
|
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)
|
machine.runForNumber(ofCycles: 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
let jammedPC = machine.value(for: CSTestMachine6502Register.lastOperationAddress)
|
if machine.isJammed {
|
||||||
if jammedPC != 0xe16f {
|
let hexAddress = String(format:"%04x", machine.value(for: .lastOperationAddress))
|
||||||
let hexAddress = String(format:"%04x", jammedPC)
|
|
||||||
NSException(name: NSExceptionName(rawValue: "Failed Test"), reason: "Processor jammed unexpectedly at \(hexAddress)", userInfo: nil).raise()
|
NSException(name: NSExceptionName(rawValue: "Failed Test"), reason: "Processor jammed unexpectedly at \(hexAddress)", userInfo: nil).raise()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: MachineJamHandler
|
|
||||||
|
|
||||||
func petsciiToString(_ string: String) -> String {
|
func petsciiToString(_ string: String) -> String {
|
||||||
let petsciiToCharCommon: [String] = [
|
let petsciiToCharCommon: [String] = [
|
||||||
"?", "?", "?", "[RUN/STOP]", "?", "[WHT]", "?", "?", "[SHIFT DISABLE]", "[SHIFT ENABLE]", "?", "?", "?", "\r", "[TEXT MODE]", "?",
|
"?", "?", "?", "[RUN/STOP]", "?", "[WHT]", "?", "?", "[SHIFT DISABLE]", "[SHIFT ENABLE]", "?", "?", "?", "\r", "[TEXT MODE]", "?",
|
||||||
@ -316,7 +412,7 @@ class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler {
|
|||||||
testMachine6502.setValue(0x3, for:CSTestMachine6502Register.A)
|
testMachine6502.setValue(0x3, for:CSTestMachine6502Register.A)
|
||||||
|
|
||||||
case 0x8000, 0xa474:
|
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:
|
case 0x0000:
|
||||||
NSException(name: NSExceptionName(rawValue: "Failed test"), reason: "Execution hit 0000", userInfo: nil).raise()
|
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
|
import XCTest
|
||||||
|
|
||||||
class Z80MemptrTester: XCTestCase {
|
class Z80MemptrTester: XCTestCase {
|
||||||
|
|
||||||
let machine = CSTestMachineZ80()
|
let machine = CSTestMachineZ80()
|
||||||
|
|
||||||
private func test(program : [UInt8], initialValue : UInt16) -> UInt16 {
|
private func test(program : [UInt8], initialValue : UInt16) -> UInt16 {
|
||||||
|
@ -11,8 +11,8 @@ import Foundation
|
|||||||
|
|
||||||
class ZexallTests: XCTestCase, CSTestMachineTrapHandler {
|
class ZexallTests: XCTestCase, CSTestMachineTrapHandler {
|
||||||
|
|
||||||
fileprivate var done = false
|
private var done = false
|
||||||
fileprivate var output = ""
|
private var output = ""
|
||||||
|
|
||||||
private func runTest(_ name: String) {
|
private func runTest(_ name: String) {
|
||||||
if let filename = Bundle(for: type(of: self)).path(forResource: name, ofType: "com") {
|
if let filename = Bundle(for: type(of: self)).path(forResource: name, ofType: "com") {
|
||||||
@ -177,4 +177,5 @@ class ZexallTests: XCTestCase, CSTestMachineTrapHandler {
|
|||||||
break
|
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/Implementation/*.cpp \
|
||||||
$$SRC/Processors/6502/State/*.cpp \
|
$$SRC/Processors/6502/State/*.cpp \
|
||||||
|
$$SRC/Processors/65816/Implementation/*.cpp \
|
||||||
$$SRC/Processors/68000/Implementation/*.cpp \
|
$$SRC/Processors/68000/Implementation/*.cpp \
|
||||||
$$SRC/Processors/68000/State/*.cpp \
|
$$SRC/Processors/68000/State/*.cpp \
|
||||||
$$SRC/Processors/Z80/Implementation/*.cpp \
|
$$SRC/Processors/Z80/Implementation/*.cpp \
|
||||||
@ -214,6 +215,10 @@ HEADERS += \
|
|||||||
$$SRC/Processors/6502/*.hpp \
|
$$SRC/Processors/6502/*.hpp \
|
||||||
$$SRC/Processors/6502/Implementation/*.hpp \
|
$$SRC/Processors/6502/Implementation/*.hpp \
|
||||||
$$SRC/Processors/6502/State/*.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/*.hpp \
|
||||||
$$SRC/Processors/68000/Implementation/*.hpp \
|
$$SRC/Processors/68000/Implementation/*.hpp \
|
||||||
$$SRC/Processors/68000/State/*.hpp \
|
$$SRC/Processors/68000/State/*.hpp \
|
||||||
|
@ -287,7 +287,7 @@ void MainWindow::launchMachine() {
|
|||||||
// Populate request text.
|
// Populate request text.
|
||||||
QString requestText = romRequestBaseText;
|
QString requestText = romRequestBaseText;
|
||||||
size_t index = 0;
|
size_t index = 0;
|
||||||
for(const auto rom: missingRoms) {
|
for(const auto &rom: missingRoms) {
|
||||||
requestText += "• ";
|
requestText += "• ";
|
||||||
requestText += rom.descriptive_name.c_str();
|
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/Implementation/*.cpp')
|
||||||
SOURCES += glob.glob('../../Processors/6502/State/*.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/Implementation/*.cpp')
|
||||||
SOURCES += glob.glob('../../Processors/68000/State/*.cpp')
|
SOURCES += glob.glob('../../Processors/68000/State/*.cpp')
|
||||||
SOURCES += glob.glob('../../Processors/Z80/Implementation/*.cpp')
|
SOURCES += glob.glob('../../Processors/Z80/Implementation/*.cpp')
|
||||||
|
@ -13,24 +13,19 @@
|
|||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "../6502Esque/6502Esque.hpp"
|
||||||
|
#include "../6502Esque/Implementation/LazyFlags.hpp"
|
||||||
#include "../RegisterSizes.hpp"
|
#include "../RegisterSizes.hpp"
|
||||||
#include "../../ClockReceiver/ClockReceiver.hpp"
|
#include "../../ClockReceiver/ClockReceiver.hpp"
|
||||||
|
|
||||||
namespace CPU {
|
namespace CPU {
|
||||||
namespace MOS6502 {
|
namespace MOS6502 {
|
||||||
|
|
||||||
/*
|
// Adopt a bunch of things from MOS6502Esque.
|
||||||
The list of registers that can be accessed via @c set_value_of_register and @c set_value_of_register.
|
using BusOperation = CPU::MOS6502Esque::BusOperation;
|
||||||
*/
|
using BusHandler = CPU::MOS6502Esque::BusHandler<uint16_t>;
|
||||||
enum Register {
|
using Register = CPU::MOS6502Esque::Register;
|
||||||
LastOperationAddress,
|
using Flag = CPU::MOS6502Esque::Flag;
|
||||||
ProgramCounter,
|
|
||||||
StackPointer,
|
|
||||||
Flags,
|
|
||||||
A,
|
|
||||||
X,
|
|
||||||
Y
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The list of 6502 variants supported by this implementation.
|
The list of 6502 variants supported by this implementation.
|
||||||
@ -48,76 +43,11 @@ enum Personality {
|
|||||||
#define has_bbrbbsrmbsmb(p) ((p) >= Personality::PRockwell65C02)
|
#define has_bbrbbsrmbsmb(p) ((p) >= Personality::PRockwell65C02)
|
||||||
#define has_stpwai(p) ((p) >= Personality::PWDC65C02)
|
#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.
|
An opcode that is guaranteed to cause the CPU to jam.
|
||||||
*/
|
*/
|
||||||
extern const uint8_t JamOpcode;
|
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"
|
#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 also nominate whether the processor includes support for the ready line. Declining to support the ready line
|
||||||
can produce a minor runtime performance improvement.
|
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:
|
public:
|
||||||
/*!
|
/*!
|
||||||
Constructs an instance of the 6502 that will use @c bus_handler for all bus communications.
|
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.
|
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);
|
void set_ready_line(bool active);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
T &bus_handler_;
|
BusHandler &bus_handler_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#include "Implementation/6502Implementation.hpp"
|
#include "Implementation/6502Implementation.hpp"
|
||||||
|
@ -11,28 +11,51 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
//#define BE_NOISY
|
||||||
|
|
||||||
using namespace CPU::MOS6502;
|
using namespace CPU::MOS6502;
|
||||||
|
|
||||||
namespace {
|
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:
|
public:
|
||||||
ConcreteAllRAMProcessor() :
|
ConcreteAllRAMProcessor(size_t memory_size) :
|
||||||
|
AllRAMProcessor(memory_size),
|
||||||
mos6502_(*this) {
|
mos6502_(*this) {
|
||||||
mos6502_.set_power_on(false);
|
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);
|
timestamp_ += Cycles(1);
|
||||||
|
|
||||||
if(operation == BusOperation::ReadOpcode) {
|
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);
|
check_address_for_trap(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isReadOperation(operation)) {
|
if(isReadOperation(operation)) {
|
||||||
*value = memory_[address];
|
*value = memory_[address];
|
||||||
|
#ifdef BE_NOISY
|
||||||
|
// if((address&0xff00) == 0x100) {
|
||||||
|
printf("%04x -> %02x\n", address, *value);
|
||||||
|
// }
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
memory_[address] = *value;
|
memory_[address] = *value;
|
||||||
|
#ifdef BE_NOISY
|
||||||
|
// if((address&0xff00) == 0x100) {
|
||||||
|
printf("%04x <- %02x\n", address, *value);
|
||||||
|
// }
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
return Cycles(1);
|
return Cycles(1);
|
||||||
@ -63,20 +86,21 @@ template <Personality personality> class ConcreteAllRAMProcessor: public AllRAMP
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CPU::MOS6502::Processor<personality, ConcreteAllRAMProcessor, false> mos6502_;
|
CPU::MOS6502Esque::Processor<type, ConcreteAllRAMProcessor, false> mos6502_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AllRAMProcessor *AllRAMProcessor::Processor(Personality personality) {
|
AllRAMProcessor *AllRAMProcessor::Processor(Type type) {
|
||||||
#define Bind(p) case p: return new ConcreteAllRAMProcessor<p>();
|
#define Bind(p) case p: return new ConcreteAllRAMProcessor<p>(type == Type::TWDC65816 ? 16*1024*1024 : 64*1024);
|
||||||
switch(personality) {
|
switch(type) {
|
||||||
default:
|
default:
|
||||||
Bind(Personality::P6502)
|
Bind(Type::T6502)
|
||||||
Bind(Personality::PNES6502)
|
Bind(Type::TNES6502)
|
||||||
Bind(Personality::PSynertek65C02)
|
Bind(Type::TSynertek65C02)
|
||||||
Bind(Personality::PWDC65C02)
|
Bind(Type::TWDC65C02)
|
||||||
Bind(Personality::PRockwell65C02)
|
Bind(Type::TRockwell65C02)
|
||||||
|
Bind(Type::TWDC65816)
|
||||||
}
|
}
|
||||||
#undef Bind
|
#undef Bind
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
#ifndef MOS6502AllRAM_cpp
|
#ifndef MOS6502AllRAM_cpp
|
||||||
#define MOS6502AllRAM_cpp
|
#define MOS6502AllRAM_cpp
|
||||||
|
|
||||||
#include "../6502.hpp"
|
#include "../../6502Esque/6502Selector.hpp"
|
||||||
#include "../../AllRAMProcessor.hpp"
|
#include "../../AllRAMProcessor.hpp"
|
||||||
|
|
||||||
namespace CPU {
|
namespace CPU {
|
||||||
@ -17,9 +17,8 @@ namespace MOS6502 {
|
|||||||
|
|
||||||
class AllRAMProcessor:
|
class AllRAMProcessor:
|
||||||
public ::CPU::AllRAMProcessor {
|
public ::CPU::AllRAMProcessor {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static AllRAMProcessor *Processor(Personality personality);
|
static AllRAMProcessor *Processor(CPU::MOS6502Esque::Type type);
|
||||||
virtual ~AllRAMProcessor() {}
|
virtual ~AllRAMProcessor() {}
|
||||||
|
|
||||||
virtual void run_for(const Cycles cycles) = 0;
|
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;
|
virtual void set_value_of_register(Register r, uint16_t value) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
AllRAMProcessor() : ::CPU::AllRAMProcessor(65536) {}
|
AllRAMProcessor(size_t memory_size) : ::CPU::AllRAMProcessor(memory_size) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -8,8 +8,6 @@
|
|||||||
|
|
||||||
#include "../6502.hpp"
|
#include "../6502.hpp"
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
using namespace CPU::MOS6502;
|
using namespace CPU::MOS6502;
|
||||||
|
|
||||||
const uint8_t CPU::MOS6502::JamOpcode = 0xf2;
|
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) {
|
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() \
|
#define checkSchedule() \
|
||||||
if(!scheduled_program_counter_) {\
|
if(!scheduled_program_counter_) {\
|
||||||
if(interrupt_requests_) {\
|
if(interrupt_requests_) {\
|
||||||
@ -42,9 +32,9 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
|||||||
|
|
||||||
#define bus_access() \
|
#define bus_access() \
|
||||||
interrupt_requests_ = (interrupt_requests_ & ~InterruptRequestFlags::IRQ) | irq_request_history_; \
|
interrupt_requests_ = (interrupt_requests_ & ~InterruptRequestFlags::IRQ) | irq_request_history_; \
|
||||||
irq_request_history_ = irq_line_ & inverse_interrupt_flag_; \
|
irq_request_history_ = irq_line_ & flags_.inverse_interrupt; \
|
||||||
number_of_cycles -= bus_handler_.perform_bus_operation(nextBusOperation, busAddress, busValue); \
|
number_of_cycles -= bus_handler_.perform_bus_operation(next_bus_operation_, bus_address_, bus_value_); \
|
||||||
nextBusOperation = BusOperation::None; \
|
next_bus_operation_ = BusOperation::None; \
|
||||||
if(number_of_cycles <= Cycles(0)) break;
|
if(number_of_cycles <= Cycles(0)) break;
|
||||||
|
|
||||||
checkSchedule();
|
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.
|
// 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)) {
|
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.
|
// Deal with a potential STP state, if this 6502 implements STP.
|
||||||
while(has_stpwai(personality) && stop_is_active_ && number_of_cycles > Cycles(0)) {
|
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) {
|
if(interrupt_requests_ & InterruptRequestFlags::Reset) {
|
||||||
stop_is_active_ = false;
|
stop_is_active_ = false;
|
||||||
checkSchedule();
|
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.
|
// Deal with a potential WAI state, if this 6502 implements WAI.
|
||||||
while(has_stpwai(personality) && wait_is_active_ && number_of_cycles > Cycles(0)) {
|
while(has_stpwai(personality) && wait_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_);
|
||||||
interrupt_requests_ |= (irq_line_ & inverse_interrupt_flag_);
|
interrupt_requests_ |= (irq_line_ & flags_.inverse_interrupt);
|
||||||
if(interrupt_requests_ & InterruptRequestFlags::NMI || irq_line_) {
|
if(interrupt_requests_ & InterruptRequestFlags::NMI || irq_line_) {
|
||||||
wait_is_active_ = false;
|
wait_is_active_ = false;
|
||||||
checkSchedule();
|
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((!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();
|
bus_access();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,10 +78,10 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
|||||||
const MicroOp cycle = *scheduled_program_counter_;
|
const MicroOp cycle = *scheduled_program_counter_;
|
||||||
scheduled_program_counter_++;
|
scheduled_program_counter_++;
|
||||||
|
|
||||||
#define read_op(val, addr) nextBusOperation = BusOperation::ReadOpcode; busAddress = addr; busValue = &val; val = 0xff
|
#define read_op(val, addr) next_bus_operation_ = BusOperation::ReadOpcode; bus_address_ = addr; bus_value_ = &val; val = 0xff
|
||||||
#define read_mem(val, addr) nextBusOperation = BusOperation::Read; busAddress = addr; busValue = &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) nextBusOperation = BusOperation::Read; busAddress = addr; busValue = &throwaway_target; throwaway_target = 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) nextBusOperation = BusOperation::Write; busAddress = addr; busValue = &val
|
#define write_mem(val, addr) next_bus_operation_ = BusOperation::Write; bus_address_ = addr; bus_value_ = &val
|
||||||
|
|
||||||
switch(cycle) {
|
switch(cycle) {
|
||||||
|
|
||||||
@ -155,37 +145,37 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
|||||||
|
|
||||||
case OperationBRKPickVector:
|
case OperationBRKPickVector:
|
||||||
if(is_65c02(personality)) {
|
if(is_65c02(personality)) {
|
||||||
nextAddress.full = 0xfffe;
|
next_address_.full = 0xfffe;
|
||||||
} else {
|
} else {
|
||||||
// NMI can usurp BRK-vector operations on the pre-C 6502s.
|
// 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;
|
interrupt_requests_ &= ~InterruptRequestFlags::NMI;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
case OperationNMIPickVector: nextAddress.full = 0xfffa; continue;
|
case OperationNMIPickVector: next_address_.full = 0xfffa; continue;
|
||||||
case OperationRSTPickVector: nextAddress.full = 0xfffc; continue;
|
case OperationRSTPickVector: next_address_.full = 0xfffc; continue;
|
||||||
case CycleReadVectorLow: read_mem(pc_.halves.low, nextAddress.full); break;
|
case CycleReadVectorLow: read_mem(pc_.halves.low, next_address_.full); break;
|
||||||
case CycleReadVectorHigh: read_mem(pc_.halves.high, nextAddress.full+1); break;
|
case CycleReadVectorHigh: read_mem(pc_.halves.high, next_address_.full+1); break;
|
||||||
case OperationSetIRQFlags:
|
case OperationSetIRQFlags:
|
||||||
inverse_interrupt_flag_ = 0;
|
flags_.inverse_interrupt = 0;
|
||||||
if(is_65c02(personality)) decimal_flag_ = false;
|
if(is_65c02(personality)) flags_.decimal = 0;
|
||||||
continue;
|
continue;
|
||||||
case OperationSetNMIRSTFlags:
|
case OperationSetNMIRSTFlags:
|
||||||
if(is_65c02(personality)) decimal_flag_ = false;
|
if(is_65c02(personality)) flags_.decimal = 0;
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
case CyclePullPCL: s_++; read_mem(pc_.halves.low, s_ | 0x100); break;
|
case CyclePullPCL: s_++; read_mem(pc_.halves.low, s_ | 0x100); break;
|
||||||
case CyclePullPCH: s_++; read_mem(pc_.halves.high, s_ | 0x100); break;
|
case CyclePullPCH: s_++; read_mem(pc_.halves.high, s_ | 0x100); break;
|
||||||
case CyclePullA: s_++; read_mem(a_, s_ | 0x100); break;
|
case CyclePullA: s_++; read_mem(a_, s_ | 0x100); break;
|
||||||
case CyclePullX: s_++; read_mem(x_, s_ | 0x100); break;
|
case CyclePullX: s_++; read_mem(x_, s_ | 0x100); break;
|
||||||
case CyclePullY: s_++; read_mem(y_, s_ | 0x100); break;
|
case CyclePullY: s_++; read_mem(y_, s_ | 0x100); break;
|
||||||
case CyclePullOperand: s_++; read_mem(operand_, s_ | 0x100); break;
|
case CyclePullOperand: s_++; read_mem(operand_, s_ | 0x100); break;
|
||||||
case OperationSetFlagsFromOperand: set_flags(operand_); continue;
|
case OperationSetFlagsFromOperand: set_flags(operand_); continue;
|
||||||
case OperationSetOperandFromFlagsWithBRKSet: operand_ = get_flags() | Flag::Break; continue;
|
case OperationSetOperandFromFlagsWithBRKSet: operand_ = flags_.get() | Flag::Break; continue;
|
||||||
case OperationSetOperandFromFlags: operand_ = get_flags(); continue;
|
case OperationSetOperandFromFlags: operand_ = flags_.get(); continue;
|
||||||
case OperationSetFlagsFromA: zero_result_ = negative_result_ = a_; continue;
|
case OperationSetFlagsFromA: flags_.set_nz(a_); continue;
|
||||||
case OperationSetFlagsFromX: zero_result_ = negative_result_ = x_; continue;
|
case OperationSetFlagsFromX: flags_.set_nz(x_); continue;
|
||||||
case OperationSetFlagsFromY: zero_result_ = negative_result_ = y_; continue;
|
case OperationSetFlagsFromY: flags_.set_nz(y_); continue;
|
||||||
|
|
||||||
case CycleIncrementPCAndReadStack: pc_.full++; throwaway_read(s_ | 0x100); break;
|
case CycleIncrementPCAndReadStack: pc_.full++; throwaway_read(s_ | 0x100); break;
|
||||||
case CycleReadPCLFromAddress: read_mem(pc_.halves.low, address_.full); 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
|
// MARK: - Bitwise
|
||||||
|
|
||||||
case OperationORA: a_ |= operand_; negative_result_ = zero_result_ = a_; continue;
|
case OperationORA: a_ |= operand_; flags_.set_nz(a_); continue;
|
||||||
case OperationAND: a_ &= operand_; negative_result_ = zero_result_ = a_; continue;
|
case OperationAND: a_ &= operand_; flags_.set_nz(a_); continue;
|
||||||
case OperationEOR: a_ ^= operand_; negative_result_ = zero_result_ = a_; continue;
|
case OperationEOR: a_ ^= operand_; flags_.set_nz(a_); continue;
|
||||||
|
|
||||||
// MARK: - Load and Store
|
// MARK: - Load and Store
|
||||||
|
|
||||||
case OperationLDA: a_ = negative_result_ = zero_result_ = operand_; continue;
|
case OperationLDA: flags_.set_nz(a_ = operand_); continue;
|
||||||
case OperationLDX: x_ = negative_result_ = zero_result_ = operand_; continue;
|
case OperationLDX: flags_.set_nz(x_ = operand_); continue;
|
||||||
case OperationLDY: y_ = negative_result_ = zero_result_ = operand_; continue;
|
case OperationLDY: flags_.set_nz(y_ = operand_); continue;
|
||||||
case OperationLAX: a_ = x_ = negative_result_ = zero_result_ = operand_; continue;
|
case OperationLAX: flags_.set_nz(a_ = x_ = operand_); continue;
|
||||||
case OperationCopyOperandToA: a_ = operand_; continue;
|
case OperationCopyOperandToA: a_ = operand_; continue;
|
||||||
|
|
||||||
case OperationSTA: operand_ = a_; continue;
|
case OperationSTA: operand_ = a_; continue;
|
||||||
case OperationSTX: operand_ = x_; continue;
|
case OperationSTX: operand_ = x_; continue;
|
||||||
@ -240,43 +230,43 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
|||||||
|
|
||||||
case OperationLXA:
|
case OperationLXA:
|
||||||
a_ = x_ = (a_ | 0xee) & operand_;
|
a_ = x_ = (a_ | 0xee) & operand_;
|
||||||
negative_result_ = zero_result_ = a_;
|
flags_.set_nz(a_);
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// MARK: - Compare
|
// MARK: - Compare
|
||||||
|
|
||||||
case OperationCMP: {
|
case OperationCMP: {
|
||||||
const uint16_t temp16 = a_ - operand_;
|
const uint16_t temp16 = a_ - operand_;
|
||||||
negative_result_ = zero_result_ = uint8_t(temp16);
|
flags_.set_nz(uint8_t(temp16));
|
||||||
carry_flag_ = ((~temp16) >> 8)&1;
|
flags_.carry = ((~temp16) >> 8)&1;
|
||||||
} continue;
|
} continue;
|
||||||
case OperationCPX: {
|
case OperationCPX: {
|
||||||
const uint16_t temp16 = x_ - operand_;
|
const uint16_t temp16 = x_ - operand_;
|
||||||
negative_result_ = zero_result_ = uint8_t(temp16);
|
flags_.set_nz(uint8_t(temp16));
|
||||||
carry_flag_ = ((~temp16) >> 8)&1;
|
flags_.carry = ((~temp16) >> 8)&1;
|
||||||
} continue;
|
} continue;
|
||||||
case OperationCPY: {
|
case OperationCPY: {
|
||||||
const uint16_t temp16 = y_ - operand_;
|
const uint16_t temp16 = y_ - operand_;
|
||||||
negative_result_ = zero_result_ = uint8_t(temp16);
|
flags_.set_nz(uint8_t(temp16));
|
||||||
carry_flag_ = ((~temp16) >> 8)&1;
|
flags_.carry = ((~temp16) >> 8)&1;
|
||||||
} continue;
|
} continue;
|
||||||
|
|
||||||
// MARK: - BIT, TSB, TRB
|
// MARK: - BIT, TSB, TRB
|
||||||
|
|
||||||
case OperationBIT:
|
case OperationBIT:
|
||||||
zero_result_ = operand_ & a_;
|
flags_.zero_result = operand_ & a_;
|
||||||
negative_result_ = operand_;
|
flags_.negative_result = operand_;
|
||||||
overflow_flag_ = operand_&Flag::Overflow;
|
flags_.overflow = operand_ & Flag::Overflow;
|
||||||
continue;
|
continue;
|
||||||
case OperationBITNoNV:
|
case OperationBITNoNV:
|
||||||
zero_result_ = operand_ & a_;
|
flags_.zero_result = operand_ & a_;
|
||||||
continue;
|
continue;
|
||||||
case OperationTRB:
|
case OperationTRB:
|
||||||
zero_result_ = operand_ & a_;
|
flags_.zero_result = operand_ & a_;
|
||||||
operand_ &= ~a_;
|
operand_ &= ~a_;
|
||||||
continue;
|
continue;
|
||||||
case OperationTSB:
|
case OperationTSB:
|
||||||
zero_result_ = operand_ & a_;
|
flags_.zero_result = operand_ & a_;
|
||||||
operand_ |= a_;
|
operand_ |= a_;
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -295,8 +285,8 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
|||||||
operand_++;
|
operand_++;
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
case OperationSBC:
|
case OperationSBC:
|
||||||
if(decimal_flag_ && has_decimal_mode(personality)) {
|
if(flags_.decimal && has_decimal_mode(personality)) {
|
||||||
const uint16_t notCarry = carry_flag_ ^ 0x1;
|
const uint16_t notCarry = flags_.carry ^ 0x1;
|
||||||
const uint16_t decimalResult = uint16_t(a_) - uint16_t(operand_) - notCarry;
|
const uint16_t decimalResult = uint16_t(a_) - uint16_t(operand_) - notCarry;
|
||||||
uint16_t temp16;
|
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 = (temp16&0x0f) | ((temp16 > 0x0f) ? 0xfff0 : 0x00);
|
||||||
temp16 += (a_&0xf0) - (operand_&0xf0);
|
temp16 += (a_&0xf0) - (operand_&0xf0);
|
||||||
|
|
||||||
overflow_flag_ = ( ( (decimalResult^a_)&(~decimalResult^operand_) )&0x80) >> 1;
|
flags_.overflow = ( ( (decimalResult^a_)&(~decimalResult^operand_) )&0x80) >> 1;
|
||||||
negative_result_ = uint8_t(temp16);
|
flags_.negative_result = uint8_t(temp16);
|
||||||
zero_result_ = uint8_t(decimalResult);
|
flags_.zero_result = uint8_t(decimalResult);
|
||||||
|
|
||||||
if(temp16 > 0xff) temp16 -= 0x60;
|
if(temp16 > 0xff) temp16 -= 0x60;
|
||||||
|
|
||||||
carry_flag_ = (temp16 > 0xff) ? 0 : Flag::Carry;
|
flags_.carry = (temp16 > 0xff) ? 0 : Flag::Carry;
|
||||||
a_ = uint8_t(temp16);
|
a_ = uint8_t(temp16);
|
||||||
|
|
||||||
if(is_65c02(personality)) {
|
if(is_65c02(personality)) {
|
||||||
negative_result_ = zero_result_ = a_;
|
flags_.set_nz(a_);
|
||||||
read_mem(operand_, address_.full);
|
read_mem(operand_, address_.full);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -326,30 +316,30 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
|||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
|
|
||||||
case OperationADC:
|
case OperationADC:
|
||||||
if(decimal_flag_ && has_decimal_mode(personality)) {
|
if(flags_.decimal && has_decimal_mode(personality)) {
|
||||||
const uint16_t decimalResult = uint16_t(a_) + uint16_t(operand_) + uint16_t(carry_flag_);
|
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;
|
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);
|
uint16_t result = uint16_t(a_ & 0xf0) + uint16_t(operand_ & 0xf0) + uint16_t(low_nibble);
|
||||||
negative_result_ = uint8_t(result);
|
flags_.negative_result = uint8_t(result);
|
||||||
overflow_flag_ = (( (result^a_)&(result^operand_) )&0x80) >> 1;
|
flags_.overflow = (( (result^a_)&(result^operand_) )&0x80) >> 1;
|
||||||
if(result >= 0xa0) result += 0x60;
|
if(result >= 0xa0) result += 0x60;
|
||||||
|
|
||||||
carry_flag_ = (result >> 8) ? 1 : 0;
|
flags_.carry = (result >> 8) ? 1 : 0;
|
||||||
a_ = uint8_t(result);
|
a_ = uint8_t(result);
|
||||||
zero_result_ = uint8_t(decimalResult);
|
flags_.zero_result = uint8_t(decimalResult);
|
||||||
|
|
||||||
if(is_65c02(personality)) {
|
if(is_65c02(personality)) {
|
||||||
negative_result_ = zero_result_ = a_;
|
flags_.set_nz(a_);
|
||||||
read_mem(operand_, address_.full);
|
read_mem(operand_, address_.full);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const uint16_t result = uint16_t(a_) + uint16_t(operand_) + uint16_t(carry_flag_);
|
const uint16_t result = uint16_t(a_) + uint16_t(operand_) + uint16_t(flags_.carry);
|
||||||
overflow_flag_ = (( (result^a_)&(result^operand_) )&0x80) >> 1;
|
flags_.overflow = (( (result^a_)&(result^operand_) )&0x80) >> 1;
|
||||||
negative_result_ = zero_result_ = a_ = uint8_t(result);
|
flags_.set_nz(a_ = uint8_t(result));
|
||||||
carry_flag_ = (result >> 8)&1;
|
flags_.carry = (result >> 8)&1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// fix up in case this was INS
|
// 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
|
// MARK: - Shifts and Rolls
|
||||||
|
|
||||||
case OperationASL:
|
case OperationASL:
|
||||||
carry_flag_ = operand_ >> 7;
|
flags_.carry = operand_ >> 7;
|
||||||
operand_ <<= 1;
|
operand_ <<= 1;
|
||||||
negative_result_ = zero_result_ = operand_;
|
flags_.set_nz(operand_);
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
case OperationASO:
|
case OperationASO:
|
||||||
carry_flag_ = operand_ >> 7;
|
flags_.carry = operand_ >> 7;
|
||||||
operand_ <<= 1;
|
operand_ <<= 1;
|
||||||
a_ |= operand_;
|
a_ |= operand_;
|
||||||
negative_result_ = zero_result_ = a_;
|
flags_.set_nz(a_);
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
case OperationROL: {
|
case OperationROL: {
|
||||||
const uint8_t temp8 = uint8_t((operand_ << 1) | carry_flag_);
|
const uint8_t temp8 = uint8_t((operand_ << 1) | flags_.carry);
|
||||||
carry_flag_ = operand_ >> 7;
|
flags_.carry = operand_ >> 7;
|
||||||
operand_ = negative_result_ = zero_result_ = temp8;
|
flags_.set_nz(operand_ = temp8);
|
||||||
} continue;
|
} continue;
|
||||||
|
|
||||||
case OperationRLA: {
|
case OperationRLA: {
|
||||||
const uint8_t temp8 = uint8_t((operand_ << 1) | carry_flag_);
|
const uint8_t temp8 = uint8_t((operand_ << 1) | flags_.carry);
|
||||||
carry_flag_ = operand_ >> 7;
|
flags_.carry = operand_ >> 7;
|
||||||
operand_ = temp8;
|
operand_ = temp8;
|
||||||
a_ &= operand_;
|
a_ &= operand_;
|
||||||
negative_result_ = zero_result_ = a_;
|
flags_.set_nz(a_);
|
||||||
} continue;
|
} continue;
|
||||||
|
|
||||||
case OperationLSR:
|
case OperationLSR:
|
||||||
carry_flag_ = operand_ & 1;
|
flags_.carry = operand_ & 1;
|
||||||
operand_ >>= 1;
|
operand_ >>= 1;
|
||||||
negative_result_ = zero_result_ = operand_;
|
flags_.set_nz(operand_);
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
case OperationLSE:
|
case OperationLSE:
|
||||||
carry_flag_ = operand_ & 1;
|
flags_.carry = operand_ & 1;
|
||||||
operand_ >>= 1;
|
operand_ >>= 1;
|
||||||
a_ ^= operand_;
|
a_ ^= operand_;
|
||||||
negative_result_ = zero_result_ = a_;
|
flags_.set_nz(a_);
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
case OperationASR:
|
case OperationASR:
|
||||||
a_ &= operand_;
|
a_ &= operand_;
|
||||||
carry_flag_ = a_ & 1;
|
flags_.carry = a_ & 1;
|
||||||
a_ >>= 1;
|
a_ >>= 1;
|
||||||
negative_result_ = zero_result_ = a_;
|
flags_.set_nz(a_);
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
case OperationROR: {
|
case OperationROR: {
|
||||||
const uint8_t temp8 = uint8_t((operand_ >> 1) | (carry_flag_ << 7));
|
const uint8_t temp8 = uint8_t((operand_ >> 1) | (flags_.carry << 7));
|
||||||
carry_flag_ = operand_ & 1;
|
flags_.carry = operand_ & 1;
|
||||||
operand_ = negative_result_ = zero_result_ = temp8;
|
flags_.set_nz(operand_ = temp8);
|
||||||
} continue;
|
} continue;
|
||||||
|
|
||||||
case OperationRRA: {
|
case OperationRRA: {
|
||||||
const uint8_t temp8 = uint8_t((operand_ >> 1) | (carry_flag_ << 7));
|
const uint8_t temp8 = uint8_t((operand_ >> 1) | (flags_.carry << 7));
|
||||||
carry_flag_ = operand_ & 1;
|
flags_.carry = operand_ & 1;
|
||||||
operand_ = temp8;
|
operand_ = temp8;
|
||||||
} continue;
|
} continue;
|
||||||
|
|
||||||
case OperationDecrementOperand: operand_--; continue;
|
case OperationDecrementOperand: operand_--; continue;
|
||||||
case OperationIncrementOperand: operand_++; continue;
|
case OperationIncrementOperand: operand_++; continue;
|
||||||
|
|
||||||
case OperationCLC: carry_flag_ = 0; continue;
|
case OperationCLC: flags_.carry = 0; continue;
|
||||||
case OperationCLI: inverse_interrupt_flag_ = Flag::Interrupt; continue;
|
case OperationCLI: flags_.inverse_interrupt = Flag::Interrupt; continue;
|
||||||
case OperationCLV: overflow_flag_ = 0; continue;
|
case OperationCLV: flags_.overflow = 0; continue;
|
||||||
case OperationCLD: decimal_flag_ = 0; continue;
|
case OperationCLD: flags_.decimal = 0; continue;
|
||||||
|
|
||||||
case OperationSEC: carry_flag_ = Flag::Carry; continue;
|
case OperationSEC: flags_.carry = Flag::Carry; continue;
|
||||||
case OperationSEI: inverse_interrupt_flag_ = 0; continue;
|
case OperationSEI: flags_.inverse_interrupt = 0; continue;
|
||||||
case OperationSED: decimal_flag_ = Flag::Decimal; continue;
|
case OperationSED: flags_.decimal = Flag::Decimal; continue;
|
||||||
|
|
||||||
case OperationINC: operand_++; negative_result_ = zero_result_ = operand_; continue;
|
case OperationINC: operand_++; flags_.set_nz(operand_); continue;
|
||||||
case OperationDEC: operand_--; negative_result_ = zero_result_ = operand_; continue;
|
case OperationDEC: operand_--; flags_.set_nz(operand_); continue;
|
||||||
case OperationINA: a_++; negative_result_ = zero_result_ = a_; continue;
|
case OperationINA: a_++; flags_.set_nz(a_); continue;
|
||||||
case OperationDEA: a_--; negative_result_ = zero_result_ = a_; continue;
|
case OperationDEA: a_--; flags_.set_nz(a_); continue;
|
||||||
case OperationINX: x_++; negative_result_ = zero_result_ = x_; continue;
|
case OperationINX: x_++; flags_.set_nz(x_); continue;
|
||||||
case OperationDEX: x_--; negative_result_ = zero_result_ = x_; continue;
|
case OperationDEX: x_--; flags_.set_nz(x_); continue;
|
||||||
case OperationINY: y_++; negative_result_ = zero_result_ = y_; continue;
|
case OperationINY: y_++; flags_.set_nz(y_); continue;
|
||||||
case OperationDEY: y_--; negative_result_ = zero_result_ = y_; continue;
|
case OperationDEY: y_--; flags_.set_nz(y_); continue;
|
||||||
|
|
||||||
case OperationANE:
|
case OperationANE:
|
||||||
a_ = (a_ | 0xee) & operand_ & x_;
|
a_ = (a_ | 0xee) & operand_ & x_;
|
||||||
negative_result_ = zero_result_ = a_;
|
flags_.set_nz(a_);
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
case OperationANC:
|
case OperationANC:
|
||||||
a_ &= operand_;
|
a_ &= operand_;
|
||||||
negative_result_ = zero_result_ = a_;
|
flags_.set_nz(a_);
|
||||||
carry_flag_ = a_ >> 7;
|
flags_.carry = a_ >> 7;
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
case OperationLAS:
|
case OperationLAS:
|
||||||
a_ = x_ = s_ = s_ & operand_;
|
a_ = x_ = s_ = s_ & operand_;
|
||||||
negative_result_ = zero_result_ = a_;
|
flags_.set_nz(a_);
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// MARK: - Addressing Mode Work
|
// MARK: - Addressing Mode Work
|
||||||
@ -464,36 +454,36 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
|||||||
}
|
}
|
||||||
|
|
||||||
case CycleAddXToAddressLow:
|
case CycleAddXToAddressLow:
|
||||||
nextAddress.full = address_.full + x_;
|
next_address_.full = address_.full + x_;
|
||||||
address_.halves.low = nextAddress.halves.low;
|
address_.halves.low = next_address_.halves.low;
|
||||||
if(address_.halves.high != nextAddress.halves.high) {
|
if(address_.halves.high != next_address_.halves.high) {
|
||||||
page_crossing_stall_read();
|
page_crossing_stall_read();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
case CycleAddXToAddressLowRead:
|
case CycleAddXToAddressLowRead:
|
||||||
nextAddress.full = address_.full + x_;
|
next_address_.full = address_.full + x_;
|
||||||
address_.halves.low = nextAddress.halves.low;
|
address_.halves.low = next_address_.halves.low;
|
||||||
page_crossing_stall_read();
|
page_crossing_stall_read();
|
||||||
break;
|
break;
|
||||||
case CycleAddYToAddressLow:
|
case CycleAddYToAddressLow:
|
||||||
nextAddress.full = address_.full + y_;
|
next_address_.full = address_.full + y_;
|
||||||
address_.halves.low = nextAddress.halves.low;
|
address_.halves.low = next_address_.halves.low;
|
||||||
if(address_.halves.high != nextAddress.halves.high) {
|
if(address_.halves.high != next_address_.halves.high) {
|
||||||
page_crossing_stall_read();
|
page_crossing_stall_read();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
case CycleAddYToAddressLowRead:
|
case CycleAddYToAddressLowRead:
|
||||||
nextAddress.full = address_.full + y_;
|
next_address_.full = address_.full + y_;
|
||||||
address_.halves.low = nextAddress.halves.low;
|
address_.halves.low = next_address_.halves.low;
|
||||||
page_crossing_stall_read();
|
page_crossing_stall_read();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
#undef page_crossing_stall_read
|
#undef page_crossing_stall_read
|
||||||
|
|
||||||
case OperationCorrectAddressHigh:
|
case OperationCorrectAddressHigh:
|
||||||
address_.full = nextAddress.full;
|
address_.full = next_address_.full;
|
||||||
continue;
|
continue;
|
||||||
case CycleIncrementPCFetchAddressLowFromOperand:
|
case CycleIncrementPCFetchAddressLowFromOperand:
|
||||||
pc_.full++;
|
pc_.full++;
|
||||||
@ -548,7 +538,7 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
|||||||
throwaway_read(operand_);
|
throwaway_read(operand_);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OperationIncrementPC: pc_.full++; continue;
|
case OperationIncrementPC: pc_.full++; continue;
|
||||||
case CycleFetchOperandFromAddress: read_mem(operand_, address_.full); break;
|
case CycleFetchOperandFromAddress: read_mem(operand_, address_.full); break;
|
||||||
case CycleWriteOperandToAddress: write_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)]; \
|
scheduled_program_counter_ = operations_[size_t(OperationsSlot::DoBRA)]; \
|
||||||
}
|
}
|
||||||
|
|
||||||
case OperationBPL: BRA(!(negative_result_&0x80)); continue;
|
case OperationBPL: BRA(!(flags_.negative_result&0x80)); continue;
|
||||||
case OperationBMI: BRA(negative_result_&0x80); continue;
|
case OperationBMI: BRA(flags_.negative_result&0x80); continue;
|
||||||
case OperationBVC: BRA(!overflow_flag_); continue;
|
case OperationBVC: BRA(!flags_.overflow); continue;
|
||||||
case OperationBVS: BRA(overflow_flag_); continue;
|
case OperationBVS: BRA(flags_.overflow); continue;
|
||||||
case OperationBCC: BRA(!carry_flag_); continue;
|
case OperationBCC: BRA(!flags_.carry); continue;
|
||||||
case OperationBCS: BRA(carry_flag_); continue;
|
case OperationBCS: BRA(flags_.carry); continue;
|
||||||
case OperationBNE: BRA(zero_result_); continue;
|
case OperationBNE: BRA(flags_.zero_result); continue;
|
||||||
case OperationBEQ: BRA(!zero_result_); continue;
|
case OperationBEQ: BRA(!flags_.zero_result); continue;
|
||||||
case OperationBRA: BRA(true); continue;
|
case OperationBRA: BRA(true); continue;
|
||||||
|
|
||||||
#undef BRA
|
#undef BRA
|
||||||
|
|
||||||
case CycleAddSignedOperandToPC:
|
case CycleAddSignedOperandToPC:
|
||||||
nextAddress.full = uint16_t(pc_.full + int8_t(operand_));
|
next_address_.full = uint16_t(pc_.full + int8_t(operand_));
|
||||||
pc_.halves.low = nextAddress.halves.low;
|
pc_.halves.low = next_address_.halves.low;
|
||||||
if(nextAddress.halves.high != pc_.halves.high) {
|
if(next_address_.halves.high != pc_.halves.high) {
|
||||||
uint16_t halfUpdatedPc = pc_.full;
|
const uint16_t half_updated_pc = pc_.full;
|
||||||
pc_.full = nextAddress.full;
|
pc_.full = next_address_.full;
|
||||||
throwaway_read(halfUpdatedPc);
|
throwaway_read(half_updated_pc);
|
||||||
break;
|
break;
|
||||||
} else if(is_65c02(personality)) {
|
} else if(is_65c02(personality)) {
|
||||||
// 65C02 modification to all branches: a branch that is taken but requires only a single cycle
|
// 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
|
// MARK: - Transfers
|
||||||
|
|
||||||
case OperationTXA: zero_result_ = negative_result_ = a_ = x_; continue;
|
case OperationTXA: flags_.set_nz(a_ = x_); continue;
|
||||||
case OperationTYA: zero_result_ = negative_result_ = a_ = y_; continue;
|
case OperationTYA: flags_.set_nz(a_ = y_); continue;
|
||||||
case OperationTXS: s_ = x_; continue;
|
case OperationTXS: s_ = x_; continue;
|
||||||
case OperationTAY: zero_result_ = negative_result_ = y_ = a_; continue;
|
case OperationTAY: flags_.set_nz(y_ = a_); continue;
|
||||||
case OperationTAX: zero_result_ = negative_result_ = x_ = a_; continue;
|
case OperationTAX: flags_.set_nz(x_ = a_); continue;
|
||||||
case OperationTSX: zero_result_ = negative_result_ = x_ = s_; continue;
|
case OperationTSX: flags_.set_nz(x_ = s_); continue;
|
||||||
|
|
||||||
case OperationARR:
|
case OperationARR:
|
||||||
if(decimal_flag_) {
|
if(flags_.decimal) {
|
||||||
a_ &= operand_;
|
a_ &= operand_;
|
||||||
uint8_t unshiftedA = a_;
|
uint8_t unshiftedA = a_;
|
||||||
a_ = uint8_t((a_ >> 1) | (carry_flag_ << 7));
|
a_ = uint8_t((a_ >> 1) | (flags_.carry << 7));
|
||||||
zero_result_ = negative_result_ = a_;
|
flags_.set_nz(a_);
|
||||||
overflow_flag_ = (a_^(a_ << 1))&Flag::Overflow;
|
flags_.overflow = (a_^(a_ << 1))&Flag::Overflow;
|
||||||
|
|
||||||
if((unshiftedA&0xf) + (unshiftedA&0x1) > 5) a_ = ((a_ + 6)&0xf) | (a_ & 0xf0);
|
if((unshiftedA&0xf) + (unshiftedA&0x1) > 5) a_ = ((a_ + 6)&0xf) | (a_ & 0xf0);
|
||||||
|
|
||||||
carry_flag_ = ((unshiftedA&0xf0) + (unshiftedA&0x10) > 0x50) ? 1 : 0;
|
flags_.carry = ((unshiftedA&0xf0) + (unshiftedA&0x10) > 0x50) ? 1 : 0;
|
||||||
if(carry_flag_) a_ += 0x60;
|
if(flags_.carry) a_ += 0x60;
|
||||||
} else {
|
} else {
|
||||||
a_ &= operand_;
|
a_ &= operand_;
|
||||||
a_ = uint8_t((a_ >> 1) | (carry_flag_ << 7));
|
a_ = uint8_t((a_ >> 1) | (flags_.carry << 7));
|
||||||
negative_result_ = zero_result_ = a_;
|
flags_.set_nz(a_);
|
||||||
carry_flag_ = (a_ >> 6)&1;
|
flags_.carry = (a_ >> 6)&1;
|
||||||
overflow_flag_ = (a_^(a_ << 1))&Flag::Overflow;
|
flags_.overflow = (a_^(a_ << 1))&Flag::Overflow;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -642,15 +632,15 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
|||||||
x_ &= a_;
|
x_ &= a_;
|
||||||
uint16_t difference = x_ - operand_;
|
uint16_t difference = x_ - operand_;
|
||||||
x_ = uint8_t(difference);
|
x_ = uint8_t(difference);
|
||||||
negative_result_ = zero_result_ = x_;
|
flags_.set_nz(x_);
|
||||||
carry_flag_ = ((difference >> 8)&1)^1;
|
flags_.carry = ((difference >> 8)&1)^1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(has_stpwai(personality) && (stop_is_active_ || wait_is_active_)) {
|
if(has_stpwai(personality) && (stop_is_active_ || wait_is_active_)) {
|
||||||
break;
|
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;
|
ready_is_active_ = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -660,11 +650,6 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
|||||||
}
|
}
|
||||||
|
|
||||||
cycles_left_to_run_ = number_of_cycles;
|
cycles_left_to_run_ = number_of_cycles;
|
||||||
next_address_ = nextAddress;
|
|
||||||
next_bus_operation_ = nextBusOperation;
|
|
||||||
bus_address_ = busAddress;
|
|
||||||
bus_value_ = busValue;
|
|
||||||
|
|
||||||
bus_handler_.flush();
|
bus_handler_.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -691,13 +676,13 @@ void ProcessorBase::set_power_on(bool active) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ProcessorBase::set_irq_line(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) {
|
void ProcessorBase::set_overflow_line(bool active) {
|
||||||
// a leading edge will set the overflow flag
|
// a leading edge will set the overflow flag
|
||||||
if(active && !set_overflow_line_is_enabled_)
|
if(active && !set_overflow_line_is_enabled_)
|
||||||
overflow_flag_ = Flag::Overflow;
|
flags_.overflow = MOS6502Esque::Flag::Overflow;
|
||||||
set_overflow_line_is_enabled_ = active;
|
set_overflow_line_is_enabled_ = active;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -709,14 +694,9 @@ void ProcessorBase::set_nmi_line(bool active) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint8_t ProcessorStorage::get_flags() const {
|
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) {
|
void ProcessorStorage::set_flags(uint8_t flags) {
|
||||||
carry_flag_ = flags & Flag::Carry;
|
flags_.set(flags);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
@ -76,12 +76,6 @@ using namespace CPU::MOS6502;
|
|||||||
#define JAM {CycleFetchOperand, OperationScheduleJam}
|
#define JAM {CycleFetchOperand, OperationScheduleJam}
|
||||||
|
|
||||||
ProcessorStorage::ProcessorStorage(Personality personality) {
|
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[] = {
|
const InstructionList operations_6502[] = {
|
||||||
/* 0x00 BRK */ Program(CycleIncPCPushPCH, CyclePushPCL, OperationBRKPickVector, OperationSetOperandFromFlagsWithBRKSet, CyclePushOperand, OperationSetIRQFlags, CycleReadVectorLow, CycleReadVectorHigh),
|
/* 0x00 BRK */ Program(CycleIncPCPushPCH, CyclePushPCL, OperationBRKPickVector, OperationSetOperandFromFlagsWithBRKSet, CyclePushOperand, OperationSetIRQFlags, CycleReadVectorLow, CycleReadVectorHigh),
|
||||||
/* 0x01 ORA x, ind */ IndexedIndirectRead(OperationORA),
|
/* 0x01 ORA x, ind */ IndexedIndirectRead(OperationORA),
|
||||||
|
@ -6,9 +6,6 @@
|
|||||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
// 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
|
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.
|
class in order to remove it from visibility within the main 6502.hpp.
|
||||||
@ -231,7 +228,7 @@ class ProcessorStorage {
|
|||||||
*/
|
*/
|
||||||
RegisterPair16 pc_, last_operation_pc_;
|
RegisterPair16 pc_, last_operation_pc_;
|
||||||
uint8_t a_, x_, y_, s_ = 0;
|
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.
|
Temporary state for the micro programs.
|
||||||
@ -246,6 +243,7 @@ class ProcessorStorage {
|
|||||||
BusOperation next_bus_operation_ = BusOperation::None;
|
BusOperation next_bus_operation_ = BusOperation::None;
|
||||||
uint16_t bus_address_;
|
uint16_t bus_address_;
|
||||||
uint8_t *bus_value_;
|
uint8_t *bus_value_;
|
||||||
|
static inline uint8_t bus_throwaway_;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Gets the flags register.
|
Gets the flags register.
|
||||||
@ -270,7 +268,7 @@ class ProcessorStorage {
|
|||||||
|
|
||||||
enum InterruptRequestFlags: uint8_t {
|
enum InterruptRequestFlags: uint8_t {
|
||||||
Reset = 0x80,
|
Reset = 0x80,
|
||||||
IRQ = Flag::Interrupt,
|
IRQ = MOS6502Esque::Flag::Interrupt,
|
||||||
NMI = 0x20,
|
NMI = 0x20,
|
||||||
|
|
||||||
PowerOn = 0x10,
|
PowerOn = 0x10,
|
||||||
@ -288,5 +286,3 @@ class ProcessorStorage {
|
|||||||
// Allow state objects to capture and apply state.
|
// Allow state objects to capture and apply state.
|
||||||
friend struct 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),
|
traps_(memory_size, false),
|
||||||
timestamp_(0) {}
|
timestamp_(0) {}
|
||||||
|
|
||||||
void AllRAMProcessor::set_data_at_address(uint16_t startAddress, std::size_t length, const uint8_t *data) {
|
void AllRAMProcessor::set_data_at_address(size_t start_address, std::size_t length, const uint8_t *data) {
|
||||||
std::size_t endAddress = std::min(startAddress + length, size_t(65536));
|
const size_t end_address = std::min(start_address + length, memory_.size());
|
||||||
std::memcpy(&memory_[startAddress], data, endAddress - startAddress);
|
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) {
|
void AllRAMProcessor::get_data_at_address(size_t start_address, std::size_t length, uint8_t *data) {
|
||||||
std::size_t endAddress = std::min(startAddress + length, size_t(65536));
|
const size_t end_address = std::min(start_address + length, memory_.size());
|
||||||
std::memcpy(data, &memory_[startAddress], endAddress - startAddress);
|
memcpy(data, &memory_[start_address], end_address - start_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
HalfCycles AllRAMProcessor::get_timestamp() {
|
HalfCycles AllRAMProcessor::get_timestamp() {
|
||||||
|
@ -21,8 +21,8 @@ class AllRAMProcessor {
|
|||||||
public:
|
public:
|
||||||
AllRAMProcessor(std::size_t memory_size);
|
AllRAMProcessor(std::size_t memory_size);
|
||||||
HalfCycles get_timestamp();
|
HalfCycles get_timestamp();
|
||||||
void set_data_at_address(uint16_t startAddress, std::size_t length, const uint8_t *data);
|
void set_data_at_address(size_t startAddress, size_t length, const uint8_t *data);
|
||||||
void get_data_at_address(uint16_t startAddress, std::size_t length, uint8_t *data);
|
void get_data_at_address(size_t startAddress, size_t length, uint8_t *data);
|
||||||
|
|
||||||
class TrapHandler {
|
class TrapHandler {
|
||||||
public:
|
public:
|
||||||
|
@ -29,7 +29,7 @@ class AllRAMProcessor:
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct PortAccessDelegate {
|
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) {
|
inline void set_port_access_delegate(PortAccessDelegate *delegate) {
|
||||||
port_delegate_ = delegate;
|
port_delegate_ = delegate;
|
||||||
|
Loading…
Reference in New Issue
Block a user