1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-02 20:30:00 +00:00

Merge pull request #839 from TomHarte/65816

Adds emulation of the 65816.
This commit is contained in:
Thomas Harte 2020-10-18 21:49:46 -04:00 committed by GitHub
commit 69450e27ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
60 changed files with 4883 additions and 568 deletions

View File

@ -33,8 +33,14 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
BD500
);
ReflectableEnum(Processor,
MOS6502,
WDC65816
);
ROM rom = ROM::BASIC11;
DiskInterface disk_interface = DiskInterface::None;
Processor processor = Processor::MOS6502;
std::string loading_command;
bool should_start_jasmin = false;
@ -42,8 +48,10 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
if(needs_declare()) {
DeclareField(rom);
DeclareField(disk_interface);
DeclareField(processor);
AnnounceEnum(ROM);
AnnounceEnum(DiskInterface);
AnnounceEnum(Processor);
}
}
};

View File

@ -107,7 +107,10 @@ static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform::
Format("dmk", result.disks, Disk::DiskImageHolder<Storage::Disk::DMK>, TargetPlatform::MSX) // DMK
Format("do", result.disks, Disk::DiskImageHolder<Storage::Disk::AppleDSK>, TargetPlatform::DiskII) // DO
Format("dsd", result.disks, Disk::DiskImageHolder<Storage::Disk::SSD>, TargetPlatform::Acorn) // DSD
Format("dsk", result.disks, Disk::DiskImageHolder<Storage::Disk::CPCDSK>, TargetPlatform::AmstradCPC) // DSK (Amstrad CPC)
Format( "dsk",
result.disks,
Disk::DiskImageHolder<Storage::Disk::CPCDSK>,
TargetPlatform::AmstradCPC | TargetPlatform::Oric) // DSK (Amstrad CPC)
Format("dsk", result.disks, Disk::DiskImageHolder<Storage::Disk::AppleDSK>, TargetPlatform::DiskII) // DSK (Apple II)
Format("dsk", result.disks, Disk::DiskImageHolder<Storage::Disk::MacintoshIMG>, TargetPlatform::Macintosh) // DSK (Macintosh, floppy disk)
Format("dsk", result.mass_storage_devices, MassStorage::HFV, TargetPlatform::Macintosh) // DSK (Macintosh, hard disk)

View File

@ -75,7 +75,7 @@ template<class T> class Cartridge:
cycles_since_6532_update_ += Cycles(cycles_run_for / 3);
bus_extender_.advance_cycles(cycles_run_for / 3);
if(operation != CPU::MOS6502::BusOperation::Ready) {
if(isAccessOperation(operation)) {
// give the cartridge a chance to respond to the bus access
bus_extender_.perform_bus_operation(operation, address, value);

View File

@ -20,7 +20,7 @@
#include "../Utility/MemoryFuzzer.hpp"
#include "../Utility/StringSerialiser.hpp"
#include "../../Processors/6502/6502.hpp"
#include "../../Processors/6502Esque/6502Selector.hpp"
#include "../../Components/6522/6522.hpp"
#include "../../Components/AY38910/AY38910.hpp"
#include "../../Components/DiskII/DiskII.hpp"
@ -41,6 +41,7 @@
namespace Oric {
using DiskInterface = Analyser::Static::Oric::Target::DiskInterface;
using Processor = Analyser::Static::Oric::Target::Processor;
using AY = GI::AY38910::AY38910<false>;
using Speaker = Outputs::Speaker::LowpassSpeaker<AY>;
@ -205,7 +206,7 @@ class VIAPortHandler: public MOS::MOS6522::IRQDelegatePortHandler {
Keyboard &keyboard_;
};
template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class ConcreteMachine:
template <Analyser::Static::Oric::Target::DiskInterface disk_interface, CPU::MOS6502Esque::Type processor_type> class ConcreteMachine:
public MachineTypes::TimedMachine,
public MachineTypes::ScanProducer,
public MachineTypes::AudioProducer,
@ -424,8 +425,8 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
!tape_player_.get_tape()->is_at_end()) {
uint8_t next_byte = tape_player_.get_next_byte(!ram_[tape_speed_address_]);
m6502_.set_value_of_register(CPU::MOS6502::A, next_byte);
m6502_.set_value_of_register(CPU::MOS6502::Flags, next_byte ? 0 : CPU::MOS6502::Flag::Zero);
m6502_.set_value_of_register(CPU::MOS6502Esque::A, next_byte);
m6502_.set_value_of_register(CPU::MOS6502Esque::Flags, next_byte ? 0 : CPU::MOS6502::Flag::Zero);
*value = 0x60; // i.e. RTS
}
} else {
@ -656,7 +657,7 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
const uint16_t basic_invisible_ram_top_ = 0xffff;
const uint16_t basic_visible_ram_top_ = 0xbfff;
CPU::MOS6502::Processor<CPU::MOS6502::Personality::P6502, ConcreteMachine, false> m6502_;
CPU::MOS6502Esque::Processor<processor_type, ConcreteMachine, false> m6502_;
// RAM and ROM
std::vector<uint8_t> rom_, disk_rom_;
@ -759,13 +760,23 @@ using namespace Oric;
Machine *Machine::Oric(const Analyser::Static::Target *target_hint, const ROMMachine::ROMFetcher &rom_fetcher) {
auto *const oric_target = dynamic_cast<const Analyser::Static::Oric::Target *>(target_hint);
switch(oric_target->disk_interface) {
default: return new ConcreteMachine<DiskInterface::None>(*oric_target, rom_fetcher);
case DiskInterface::Microdisc: return new ConcreteMachine<DiskInterface::Microdisc>(*oric_target, rom_fetcher);
case DiskInterface::Pravetz: return new ConcreteMachine<DiskInterface::Pravetz>(*oric_target, rom_fetcher);
case DiskInterface::Jasmin: return new ConcreteMachine<DiskInterface::Jasmin>(*oric_target, rom_fetcher);
case DiskInterface::BD500: return new ConcreteMachine<DiskInterface::BD500>(*oric_target, rom_fetcher);
#define DiskInterfaceSwitch(processor) \
switch(oric_target->disk_interface) { \
default: return new ConcreteMachine<DiskInterface::None, processor>(*oric_target, rom_fetcher); \
case DiskInterface::Microdisc: return new ConcreteMachine<DiskInterface::Microdisc, processor>(*oric_target, rom_fetcher); \
case DiskInterface::Pravetz: return new ConcreteMachine<DiskInterface::Pravetz, processor>(*oric_target, rom_fetcher); \
case DiskInterface::Jasmin: return new ConcreteMachine<DiskInterface::Jasmin, processor>(*oric_target, rom_fetcher); \
case DiskInterface::BD500: return new ConcreteMachine<DiskInterface::BD500, processor>(*oric_target, rom_fetcher); \
}
switch(oric_target->processor) {
case Processor::WDC65816: DiskInterfaceSwitch(CPU::MOS6502Esque::Type::TWDC65816);
case Processor::MOS6502: DiskInterfaceSwitch(CPU::MOS6502Esque::Type::T6502);
}
#undef DiskInterfaceSwitch
}
Machine::~Machine() {}

View File

@ -210,6 +210,12 @@
4B4B1A3D200198CA00A0F866 /* KonamiSCC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4B1A3A200198C900A0F866 /* KonamiSCC.cpp */; };
4B4DC8211D2C2425003C5BF8 /* Vic20.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DC81F1D2C2425003C5BF8 /* Vic20.cpp */; };
4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DC8291D2C27A4003C5BF8 /* SerialBus.cpp */; };
4B4DEC06252BFA56004583AC /* 65816Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DEC05252BFA56004583AC /* 65816Base.cpp */; };
4B4DEC07252BFA56004583AC /* 65816Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DEC05252BFA56004583AC /* 65816Base.cpp */; };
4B4DEC08252BFA56004583AC /* 65816Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DEC05252BFA56004583AC /* 65816Base.cpp */; };
4B4F47652533EA64004245B8 /* suite-a.prg in Resources */ = {isa = PBXBuildFile; fileRef = 4B4F475E2533EA64004245B8 /* suite-a.prg */; };
4B4F477C253530B7004245B8 /* Jeek816Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4F477B253530B7004245B8 /* Jeek816Tests.swift */; };
4B4F478A25367EDC004245B8 /* 65816AddressingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4F478925367EDC004245B8 /* 65816AddressingTests.swift */; };
4B50AF80242817F40099BBD7 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B50AF7F242817F40099BBD7 /* QuartzCore.framework */; };
4B54C0BC1F8D8E790050900F /* KeyboardMachine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B54C0BB1F8D8E790050900F /* KeyboardMachine.cpp */; };
4B54C0BF1F8D8F450050900F /* Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B54C0BD1F8D8F450050900F /* Keyboard.cpp */; };
@ -861,6 +867,8 @@
4BF437EE209D0F7E008CBD6B /* SegmentParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BF437EC209D0F7E008CBD6B /* SegmentParser.cpp */; };
4BF437EF209D0F7E008CBD6B /* SegmentParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BF437EC209D0F7E008CBD6B /* SegmentParser.cpp */; };
4BF8D4C82516E27A00BBE21B /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BB8617024E22F4900A00E03 /* Accelerate.framework */; };
4BF8D4D5251C11DD00BBE21B /* 65816Storage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BF8D4D4251C11DD00BBE21B /* 65816Storage.cpp */; };
4BF8D4D6251C11DD00BBE21B /* 65816Storage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BF8D4D4251C11DD00BBE21B /* 65816Storage.cpp */; };
4BFCA1241ECBDCB400AC40C1 /* AllRAMProcessor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BFCA1211ECBDCAF00AC40C1 /* AllRAMProcessor.cpp */; };
4BFCA1271ECBE33200AC40C1 /* TestMachineZ80.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BFCA1261ECBE33200AC40C1 /* TestMachineZ80.mm */; };
4BFCA1291ECBE7A700AC40C1 /* zexall.com in Resources */ = {isa = PBXBuildFile; fileRef = 4BFCA1281ECBE7A700AC40C1 /* zexall.com */; };
@ -1117,7 +1125,15 @@
4B4DC8271D2C2470003C5BF8 /* C1540.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = C1540.hpp; sourceTree = "<group>"; };
4B4DC8291D2C27A4003C5BF8 /* SerialBus.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SerialBus.cpp; sourceTree = "<group>"; };
4B4DC82A1D2C27A4003C5BF8 /* SerialBus.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SerialBus.hpp; sourceTree = "<group>"; };
4B4DEC04252BFA56004583AC /* 65816Implementation.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 65816Implementation.hpp; sourceTree = "<group>"; };
4B4DEC05252BFA56004583AC /* 65816Base.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = 65816Base.cpp; sourceTree = "<group>"; };
4B4DEC16252BFA9C004583AC /* 6502Selector.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 6502Selector.hpp; sourceTree = "<group>"; };
4B4DEC18252BFA9C004583AC /* 6502Esque.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 6502Esque.hpp; sourceTree = "<group>"; };
4B4DEC19252BFB5A004583AC /* LazyFlags.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = LazyFlags.hpp; sourceTree = "<group>"; };
4B4F2B7024DF99D4000DA6B0 /* CSScanTarget+CppScanTarget.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CSScanTarget+CppScanTarget.h"; sourceTree = "<group>"; };
4B4F475E2533EA64004245B8 /* suite-a.prg */ = {isa = PBXFileReference; lastKnownFileType = file; path = "suite-a.prg"; sourceTree = "<group>"; };
4B4F477B253530B7004245B8 /* Jeek816Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Jeek816Tests.swift; sourceTree = "<group>"; };
4B4F478925367EDC004245B8 /* 65816AddressingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 65816AddressingTests.swift; sourceTree = "<group>"; };
4B50AF7F242817F40099BBD7 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
4B51F70920A521D700AFA2C1 /* Source.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Source.hpp; sourceTree = "<group>"; };
4B51F70A20A521D700AFA2C1 /* Observer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Observer.hpp; sourceTree = "<group>"; };
@ -1788,6 +1804,9 @@
4BF4A2D91F534DB300B171F4 /* TargetPlatforms.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = TargetPlatforms.hpp; sourceTree = "<group>"; };
4BF52672218E752E00313227 /* ScanTarget.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ScanTarget.hpp; path = ../../Outputs/ScanTarget.hpp; sourceTree = "<group>"; };
4BF6606A1F281573002CB053 /* ClockReceiver.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ClockReceiver.hpp; sourceTree = "<group>"; };
4BF8D4CD251C0C9C00BBE21B /* 65816.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 65816.hpp; sourceTree = "<group>"; };
4BF8D4D3251C0D9F00BBE21B /* 65816Storage.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 65816Storage.hpp; sourceTree = "<group>"; };
4BF8D4D4251C11DD00BBE21B /* 65816Storage.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = 65816Storage.cpp; sourceTree = "<group>"; };
4BFCA1211ECBDCAF00AC40C1 /* AllRAMProcessor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AllRAMProcessor.cpp; sourceTree = "<group>"; };
4BFCA1221ECBDCAF00AC40C1 /* AllRAMProcessor.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = AllRAMProcessor.hpp; sourceTree = "<group>"; };
4BFCA1251ECBE33200AC40C1 /* TestMachineZ80.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestMachineZ80.h; sourceTree = "<group>"; };
@ -1980,6 +1999,7 @@
4B9F11CB22729B3500701480 /* OPCLOGR2.BIN */,
4B2530F2244E6773007980BF /* FM Synthesis */,
4BBF49B41ED2881600AB3669 /* FUSE */,
4B4F475B2533EA64004245B8 /* jeek816 */,
4B670A822401CB8400D4E002 /* Patrik Rak Z80 Tests */,
4B9F11C72272375400701480 /* QL Startup */,
4B85322B227793CA00F26553 /* TOS Startup */,
@ -2451,6 +2471,32 @@
path = 1540;
sourceTree = "<group>";
};
4B4DEC15252BFA9C004583AC /* 6502Esque */ = {
isa = PBXGroup;
children = (
4B4DEC18252BFA9C004583AC /* 6502Esque.hpp */,
4B4DEC16252BFA9C004583AC /* 6502Selector.hpp */,
4B4DEC17252BFA9C004583AC /* Implementation */,
);
path = 6502Esque;
sourceTree = "<group>";
};
4B4DEC17252BFA9C004583AC /* Implementation */ = {
isa = PBXGroup;
children = (
4B4DEC19252BFB5A004583AC /* LazyFlags.hpp */,
);
path = Implementation;
sourceTree = "<group>";
};
4B4F475B2533EA64004245B8 /* jeek816 */ = {
isa = PBXGroup;
children = (
4B4F475E2533EA64004245B8 /* suite-a.prg */,
);
path = jeek816;
sourceTree = "<group>";
};
4B51F70820A521D700AFA2C1 /* Activity */ = {
isa = PBXGroup;
children = (
@ -3391,7 +3437,6 @@
4BB73EB51B587A5100552FC2 /* Clock SignalTests */ = {
isa = PBXGroup;
children = (
4BC0CB272446BC7B00A79DBB /* OPLTests.mm */,
4B85322922778E4200F26553 /* Comparative68000.hpp */,
4B90467222C6FA31000E2074 /* TestRunner68000.hpp */,
4B97ADC722C6FD9B00A22A41 /* 68000ArithmeticTests.mm */,
@ -3410,6 +3455,7 @@
4BE90FFC22D5864800FB464D /* MacintoshVideoTests.mm */,
4BA91E1C216D85BA00F79557 /* MasterSystemVDPTests.mm */,
4B98A0601FFADCDE00ADF63B /* MSXStaticAnalyserTests.mm */,
4BC0CB272446BC7B00A79DBB /* OPLTests.mm */,
4B121F9A1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm */,
4BD4A8CF1E077FD20020D856 /* PCMTrackTests.mm */,
4BE76CF822641ED300ACD6FA /* QLTests.mm */,
@ -3420,11 +3466,13 @@
4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */,
4BC751B11D157E61006C31D9 /* 6522Tests.swift */,
4B1E85801D176468001EF87D /* 6532Tests.swift */,
4B4F478925367EDC004245B8 /* 65816AddressingTests.swift */,
4BB73EB61B587A5100552FC2 /* AllSuiteATests.swift */,
4B049CDC1DA3C82F00322067 /* BCDTest.swift */,
4B3BA0C21D318AEB005DD7A7 /* C1540Tests.swift */,
4BEF6AAB1D35D1C400E73575 /* DPLLTests.swift */,
4BBF49AE1ED2880200AB3669 /* FUSETests.swift */,
4B4F477B253530B7004245B8 /* Jeek816Tests.swift */,
4B1414611B58888700E04248 /* KlausDormannTests.swift */,
4BD91D762401C2B8007BDC91 /* PatrikRakTests.swift */,
4B14145F1B58885000E04248 /* WolfgangLorenzTests.swift */,
@ -3485,6 +3533,8 @@
4BFCA1221ECBDCAF00AC40C1 /* AllRAMProcessor.hpp */,
4B2C455C1EC9442600FC74DD /* RegisterSizes.hpp */,
4B1414561B58879D00E04248 /* 6502 */,
4B4DEC15252BFA9C004583AC /* 6502Esque */,
4BF8D4CC251C0C9C00BBE21B /* 65816 */,
4BFF1D332233778C00838EA1 /* 68000 */,
4B77069E1EC9045B0053B588 /* Z80 */,
);
@ -3892,6 +3942,26 @@
path = ../../ClockReceiver;
sourceTree = "<group>";
};
4BF8D4CC251C0C9C00BBE21B /* 65816 */ = {
isa = PBXGroup;
children = (
4BF8D4CD251C0C9C00BBE21B /* 65816.hpp */,
4BF8D4D2251C0D9F00BBE21B /* Implementation */,
);
path = 65816;
sourceTree = "<group>";
};
4BF8D4D2251C0D9F00BBE21B /* Implementation */ = {
isa = PBXGroup;
children = (
4B4DEC05252BFA56004583AC /* 65816Base.cpp */,
4BF8D4D4251C11DD00BBE21B /* 65816Storage.cpp */,
4B4DEC04252BFA56004583AC /* 65816Implementation.hpp */,
4BF8D4D3251C0D9F00BBE21B /* 65816Storage.hpp */,
);
path = Implementation;
sourceTree = "<group>";
};
4BFDD7891F7F2DB4008579B9 /* Utility */ = {
isa = PBXGroup;
children = (
@ -4001,7 +4071,7 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0700;
LastUpgradeCheck = 1130;
LastUpgradeCheck = 1200;
ORGANIZATIONNAME = "Thomas Harte";
TargetAttributes = {
4B055A691FAE763F0060FFFF = {
@ -4239,6 +4309,7 @@
4BB299051B587D8400A49093 /* arrb in Resources */,
4BB299DC1B587D8400A49093 /* stazx in Resources */,
4B670A9D2401CB8400D4E002 /* z80ccf.tap in Resources */,
4B4F47652533EA64004245B8 /* suite-a.prg in Resources */,
4BB299C41B587D8400A49093 /* sbca in Resources */,
4BB298F41B587D8400A49093 /* adcay in Resources */,
4B44EBF51DC987AF00A7820C /* AllSuiteA.bin in Resources */,
@ -4468,6 +4539,7 @@
4BDACBED22FFA5D20045EF7E /* ncr5380.cpp in Sources */,
4BC131772346DE9100E4FF3D /* StaticAnalyser.cpp in Sources */,
4B055ACF1FAE9B030060FFFF /* SoundGenerator.cpp in Sources */,
4B4DEC08252BFA56004583AC /* 65816Base.cpp in Sources */,
4B894519201967B4007DE474 /* ConfidenceCounter.cpp in Sources */,
4B055AEE1FAE9BBF0060FFFF /* Keyboard.cpp in Sources */,
4B055AED1FAE9BA20060FFFF /* Z80Storage.cpp in Sources */,
@ -4489,6 +4561,7 @@
4B9BE401203A0C0600FFAE60 /* MultiSpeaker.cpp in Sources */,
4B055AA61FAE85EF0060FFFF /* Parser.cpp in Sources */,
4B055AE91FAE9B990060FFFF /* 6502Base.cpp in Sources */,
4BF8D4D6251C11DD00BBE21B /* 65816Storage.cpp in Sources */,
4B055AEF1FAE9BF00060FFFF /* Typer.cpp in Sources */,
4B89453F201967B4007DE474 /* StaticAnalyser.cpp in Sources */,
4B89453D201967B4007DE474 /* StaticAnalyser.cpp in Sources */,
@ -4749,6 +4822,7 @@
4BEBFB4D2002C4BF000708CC /* MSXDSK.cpp in Sources */,
4BBFBB6C1EE8401E00C01E7A /* ZX8081.cpp in Sources */,
4B83348A1F5DB94B0097E338 /* IRQDelegatePortHandler.cpp in Sources */,
4B4DEC06252BFA56004583AC /* 65816Base.cpp in Sources */,
4B894524201967B4007DE474 /* Tape.cpp in Sources */,
4B7136891F78725F008B8ED9 /* Shifter.cpp in Sources */,
4BDB61EB2032806E0048AF91 /* CSAtari2600.mm in Sources */,
@ -4771,6 +4845,7 @@
4B55DD8320DF06680043F2E5 /* MachinePicker.swift in Sources */,
4B2A539F1D117D36003C6002 /* CSAudioQueue.m in Sources */,
4B89453E201967B4007DE474 /* StaticAnalyser.cpp in Sources */,
4BF8D4D5251C11DD00BBE21B /* 65816Storage.cpp in Sources */,
4B0ACC2823775819008902D0 /* DMAController.cpp in Sources */,
4BC131702346DE5000E4FF3D /* StaticAnalyser.cpp in Sources */,
4B37EE821D7345A6006A09A4 /* BinaryDump.cpp in Sources */,
@ -4839,6 +4914,7 @@
4B778F4D23A5F20F0000D260 /* StaticAnalyser.cpp in Sources */,
4B778F0423A5EBB00000D260 /* OricMFMDSK.cpp in Sources */,
4B3BA0CE1D318B44005DD7A7 /* C1540Bridge.mm in Sources */,
4B4F477C253530B7004245B8 /* Jeek816Tests.swift in Sources */,
4B778F0F23A5EC560000D260 /* PCMTrack.cpp in Sources */,
4B778F1123A5EC650000D260 /* FileHolder.cpp in Sources */,
4B778EFC23A5EB8B0000D260 /* AcornADF.cpp in Sources */,
@ -4895,6 +4971,7 @@
4B778F4123A5F19A0000D260 /* MemoryPacker.cpp in Sources */,
4B778F4423A5F1BE0000D260 /* CommodoreGCR.cpp in Sources */,
4B778EF923A5EB740000D260 /* MSA.cpp in Sources */,
4B4DEC07252BFA56004583AC /* 65816Base.cpp in Sources */,
4B778F2323A5EDE40000D260 /* Tape.cpp in Sources */,
4B778F4F23A5F21C0000D260 /* StaticAnalyser.cpp in Sources */,
4B778EEF23A5D6680000D260 /* AsyncTaskQueue.cpp in Sources */,
@ -4935,6 +5012,7 @@
4B778F5723A5F2BB0000D260 /* ZX8081.cpp in Sources */,
4B778F2F23A5F0B10000D260 /* ScanTarget.cpp in Sources */,
4BE90FFD22D5864800FB464D /* MacintoshVideoTests.mm in Sources */,
4B4F478A25367EDC004245B8 /* 65816AddressingTests.swift in Sources */,
4B778F0B23A5EC150000D260 /* TapeUEF.cpp in Sources */,
4B778F0523A5EBB00000D260 /* ST.cpp in Sources */,
4B778F0C23A5EC150000D260 /* TZX.cpp in Sources */,
@ -5144,6 +5222,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@ -5202,6 +5281,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1130"
LastUpgradeVersion = "1200"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1130"
LastUpgradeVersion = "1200"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@ -48,11 +48,6 @@
BlueprintName = "Clock SignalTests"
ReferencedContainer = "container:Clock Signal.xcodeproj">
</BuildableReference>
<SkippedTests>
<Test
Identifier = "ZexallTests">
</Test>
</SkippedTests>
</TestableReference>
<TestableReference
skipped = "YES">

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1130"
LastUpgradeVersion = "1200"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@ -51,15 +51,6 @@
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "4BB73E9D1B587A5100552FC2"
BuildableName = "Clock Signal.app"
BlueprintName = "Clock Signal"
ReferencedContainer = "container:Clock Signal.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">

View File

@ -14,38 +14,38 @@ class MOS6502InterruptTests: XCTestCase {
override func setUp() {
super.setUp()
// create a machine full of NOPs
machine = CSTestMachine6502(is65C02: false)
// Create a machine full of NOPs.
machine = CSTestMachine6502(processor: .processor6502)
for c in 0...65535 {
machine.setValue(0xea, forAddress: UInt16(c))
machine.setValue(0xea, forAddress: UInt32(c))
}
// set the IRQ vector to be 0x1234
// Set the IRQ vector to 0x1234.
machine.setValue(0x34, forAddress: 0xfffe)
machine.setValue(0x12, forAddress: 0xffff)
// add a CLI
// Add a CLI.
machine.setValue(0x58, forAddress: 0x4000)
// pick things off at 0x4000
machine.setValue(0x4000, for: CSTestMachine6502Register.programCounter)
// Begin at 0x4000.
machine.setValue(0x4000, for: .programCounter)
}
func testIRQLine() {
// run for six cycles; check that no interrupt has occurred
// Run for six cycles; check that no interrupt has occurred.
machine.runForNumber(ofCycles: 6)
XCTAssert(machine.value(for: .programCounter) == 0x4003, "No interrupt should have occurred with line low")
XCTAssertEqual(machine.value(for: .programCounter), 0x4003, "No interrupt should have occurred with line low")
// enable the interrupt line, check that it was too late
machine.irqLine = true
machine.runForNumber(ofCycles: 2)
XCTAssert(machine.value(for: .programCounter) == 0x4004, "No interrupt should have occurred from interrupt raised between instructions")
XCTAssertEqual(machine.value(for: .programCounter), 0x4004, "No interrupt should have occurred from interrupt raised between instructions")
// run for a further 7 cycles, confirm that the IRQ vector was jumped to
machine.runForNumber(ofCycles: 6)
XCTAssert(machine.value(for: .programCounter) != 0x1234, "Interrupt routine should not yet have begun")
XCTAssertNotEqual(machine.value(for: .programCounter), 0x1234, "Interrupt routine should not yet have begun")
machine.runForNumber(ofCycles: 1)
XCTAssert(machine.value(for: .programCounter) == 0x1234, "Interrupt routine should just have begun")
XCTAssertEqual(machine.value(for: .programCounter), 0x1234, "Interrupt routine should just have begun")
}
func testIFlagSet() {
@ -53,8 +53,8 @@ class MOS6502InterruptTests: XCTestCase {
machine.irqLine = true
machine.runForNumber(ofCycles: 11)
XCTAssert(machine.value(for: .programCounter) == 0x1234, "Interrupt routine should just have begun")
XCTAssert(machine.value(for: .flags) & 0x04 == 0x04, "Interrupt status flag should be set")
XCTAssertEqual(machine.value(for: .programCounter), 0x1234, "Interrupt routine should just have begun")
XCTAssertEqual(machine.value(for: .flags) & 0x04, 0x04, "Interrupt status flag should be set")
}
func testCLISEIFlagClear() {
@ -64,12 +64,13 @@ class MOS6502InterruptTests: XCTestCase {
// run for four cycles; the CLI and SEI should have been performed
machine.runForNumber(ofCycles: 4)
XCTAssert(machine.value(for: .programCounter) == 0x4002, "CLI/SEI pair should have been performed in their entirety")
XCTAssertEqual(machine.value(for: .programCounter), 0x4002, "CLI/SEI pair should have been performed in their entirety")
// run for seven more cycles
machine.runForNumber(ofCycles: 7)
// interrupt should have taken place despite SEI
XCTAssert(machine.value(for: .programCounter) == 0x1234, "Interrupt routine should just have begun")
XCTAssertEqual(machine.value(for: .programCounter), 0x1234, "Interrupt routine should just have begun")
}
}

View File

@ -12,7 +12,7 @@ import XCTest
class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
private var endTime: UInt32 = 0
private let machine = CSTestMachine6502(is65C02: false)
private let machine = CSTestMachine6502(processor: .processor6502)
func testImplied() {
let code: [UInt8] = [
@ -22,7 +22,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
0x18, // [2] CLC
0x2a, // [2] ROL A
]
self.runTest(code, expectedRunLength: 10)
runTest(code, expectedRunLength: 10)
}
func testLDA() {
@ -40,7 +40,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
0xb1, 0x00, // [5] LDA ($00), y (no wrap)
0xb1, 0x02, // [6] LDA ($01), y (wrap)
]
self.runTest(code, expectedRunLength: 48)
runTest(code, expectedRunLength: 48)
}
func testBIT() {
@ -48,7 +48,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
0x24, 0x2a, // [3] BIT $2a
0x2c, 0x2a, 0x2b, // [4] BIT $2b2a
]
self.runTest(code, expectedRunLength: 7)
runTest(code, expectedRunLength: 7)
}
func testSTA() {
@ -64,7 +64,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
0x91, 0x00, // [6] STA ($00), y (no wrap)
0x91, 0x02, // [6] STA ($01), y (wrap)
]
self.runTest(code, expectedRunLength: 49)
runTest(code, expectedRunLength: 49)
}
func testINC() {
@ -75,7 +75,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
0xfe, 0x00, 0x00, // [7] INC $0000, x (no wrap)
0xfe, 0x02, 0x00, // [7] INC $0002, x (wrap)
]
self.runTest(code, expectedRunLength: 31)
runTest(code, expectedRunLength: 31)
}
func testJSR() {
@ -85,7 +85,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
0x60, // [6] RTS
]
machine.addTrapAddress(0x0203)
self.runTest(code, expectedRunLength: 12)
runTest(code, expectedRunLength: 12)
}
func testJMP() {
@ -94,7 +94,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
0x00, 0x00, 0x00, 0x00, 0x00,
0x4c, 0x0b, 0x02, // [3] JMP 020b
]
self.runTest(code, expectedRunLength: 8)
runTest(code, expectedRunLength: 8)
}
func testPHAPLA() {
@ -103,7 +103,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
0x48, // [3] PHA
0x68, // [4] PLA
]
self.runTest(code, expectedRunLength: 10)
runTest(code, expectedRunLength: 10)
}
func testBCS() {
@ -130,7 +130,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]
self.runTest(code, expectedRunLength: 14)
runTest(code, expectedRunLength: 14)
}
func testSnippet1() {
@ -138,7 +138,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
0x8d, 0x08, 0x00, // [4] STA $0008
0xc6, 0xb4, // [5] DEC $B4
]
self.runTest(code, expectedRunLength: 9)
runTest(code, expectedRunLength: 9)
}
func testSnippet2() {
@ -146,7 +146,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
0x16, 0x16, // [6] ASL $16, x
0x46, 0x46, // [5] LSR $46
]
self.runTest(code, expectedRunLength: 11)
runTest(code, expectedRunLength: 11)
}
func testSnippet3() {
@ -174,7 +174,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
0x60, // [6] RTS
]
machine.addTrapAddress(0x0203)
self.runTest(code, expectedRunLength: 66)
runTest(code, expectedRunLength: 66)
}
func testNOP() {
@ -194,10 +194,10 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
0xe2, 0x00, // [2] NOP #
0xf4, 0x00, // [4] NOP zpg, x
]
self.runTest(code, expectedRunLength: 43)
runTest(code, expectedRunLength: 43)
}
func runTest(_ code: [UInt8], expectedRunLength: UInt32) {
private func runTest(_ code: [UInt8], expectedRunLength: UInt32) {
machine.trapHandler = self
let immediateCode = Data(code)
@ -213,17 +213,17 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
machine.setValue(0xff, for: CSTestMachine6502Register.X)
machine.setValue(0xfe, for: CSTestMachine6502Register.Y)
self.endTime = 0
while self.endTime == 0 {
endTime = 0
while endTime == 0 {
machine.runForNumber(ofCycles: 10)
}
XCTAssert(self.endTime == expectedRunLength, "Took \(self.endTime) cycles to perform rather than \(expectedRunLength)")
XCTAssertEqual(endTime, expectedRunLength, "Took \(endTime) cycles to perform rather than \(expectedRunLength)")
}
func testMachine(_ testMachine: CSTestMachine, didTrapAtAddress address: UInt16) {
if self.endTime == 0 {
self.endTime = (machine.timestamp / 2) - 1
if endTime == 0 {
endTime = (machine.timestamp / 2) - 1
}
}
}

View File

@ -11,7 +11,7 @@ import Foundation
class MOS6532Tests: XCTestCase {
fileprivate func with6532(_ action: (MOS6532Bridge) -> ()) {
private func with6532(_ action: (MOS6532Bridge) -> ()) {
let bridge = MOS6532Bridge()
action(bridge)
}

View 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)
}
}

View File

@ -91,12 +91,12 @@
Test68000() : processor(*this) {
}
void will_perform(uint32_t address, uint16_t opcode) {
void will_perform(uint32_t, uint16_t) {
--instructions_remaining_;
if(!instructions_remaining_) comparitor();
}
HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int is_supervisor) {
HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int) {
using Microcycle = CPU::MC68000::Microcycle;
if(cycle.data_select_active()) {
cycle.apply(&ram[cycle.host_endian_byte_address()]);

View File

@ -13,7 +13,7 @@ class AllSuiteATests: XCTestCase {
func testAllSuiteA() {
if let filename = Bundle(for: type(of: self)).path(forResource: "AllSuiteA", ofType: "bin") {
if let allSuiteA = try? Data(contentsOf: URL(fileURLWithPath: filename)) {
let machine = CSTestMachine6502(is65C02: false)
let machine = CSTestMachine6502(processor: .processor6502)
machine.setData(allSuiteA, atAddress: 0x4000)
machine.setValue(CSTestMachine6502JamOpcode, forAddress:0x45c0); // end
@ -27,4 +27,5 @@ class AllSuiteATests: XCTestCase {
}
}
}
}

View File

@ -14,7 +14,7 @@ class BCDTest: XCTestCase, CSTestMachineTrapHandler {
func testBCD() {
if let filename = Bundle(for: type(of: self)).path(forResource: "BCDTEST_beeb", ofType: nil) {
if let bcdTest = try? Data(contentsOf: URL(fileURLWithPath: filename)) {
let machine = CSTestMachine6502(is65C02: false)
let machine = CSTestMachine6502(processor: .processor6502)
machine.trapHandler = self
machine.setData(bcdTest, atAddress: 0x2900)
@ -40,7 +40,7 @@ class BCDTest: XCTestCase, CSTestMachineTrapHandler {
}
}
fileprivate var output: String = ""
private var output: String = ""
func testMachine(_ testMachine: CSTestMachine, didTrapAtAddress address: UInt16) {
let machine6502 = testMachine as! CSTestMachine6502
@ -48,4 +48,5 @@ class BCDTest: XCTestCase, CSTestMachineTrapHandler {
let character = machine6502.value(for: .A)
output.append(Character(UnicodeScalar(character)!))
}
}

View File

@ -17,6 +17,16 @@ typedef NS_ENUM(NSInteger, CSTestMachine6502Register) {
CSTestMachine6502RegisterA,
CSTestMachine6502RegisterX,
CSTestMachine6502RegisterY,
CSTestMachine6502RegisterEmulationFlag,
CSTestMachine6502RegisterDataBank,
CSTestMachine6502RegisterProgramBank,
CSTestMachine6502RegisterDirect,
};
typedef NS_ENUM(NSInteger, CSTestMachine6502Processor) {
CSTestMachine6502Processor6502,
CSTestMachine6502Processor65C02,
CSTestMachine6502Processor65816
};
extern const uint8_t CSTestMachine6502JamOpcode;
@ -25,13 +35,13 @@ extern const uint8_t CSTestMachine6502JamOpcode;
- (nonnull instancetype)init NS_UNAVAILABLE;
- (nonnull instancetype)initIs65C02:(BOOL)is65C02;
- (nonnull instancetype)initWithProcessor:(CSTestMachine6502Processor)processor;
- (void)setData:(nonnull NSData *)data atAddress:(uint16_t)startAddress;
- (void)setData:(nonnull NSData *)data atAddress:(uint32_t)startAddress;
- (void)runForNumberOfCycles:(int)cycles;
- (void)setValue:(uint8_t)value forAddress:(uint16_t)address;
- (uint8_t)valueForAddress:(uint16_t)address;
- (void)setValue:(uint8_t)value forAddress:(uint32_t)address;
- (uint8_t)valueForAddress:(uint32_t)address;
- (void)setValue:(uint16_t)value forRegister:(CSTestMachine6502Register)reg;
- (uint16_t)valueForRegister:(CSTestMachine6502Register)reg;

View File

@ -24,6 +24,10 @@ static CPU::MOS6502::Register registerForRegister(CSTestMachine6502Register reg)
case CSTestMachine6502RegisterX: return CPU::MOS6502::Register::X;
case CSTestMachine6502RegisterY: return CPU::MOS6502::Register::Y;
case CSTestMachine6502RegisterStackPointer: return CPU::MOS6502::Register::StackPointer;
case CSTestMachine6502RegisterEmulationFlag: return CPU::MOS6502::Register::EmulationFlag;
case CSTestMachine6502RegisterDataBank: return CPU::MOS6502::Register::DataBank;
case CSTestMachine6502RegisterProgramBank: return CPU::MOS6502::Register::ProgramBank;
case CSTestMachine6502RegisterDirect: return CPU::MOS6502::Register::Direct;
}
}
@ -35,12 +39,20 @@ static CPU::MOS6502::Register registerForRegister(CSTestMachine6502Register reg)
#pragma mark - Lifecycle
- (instancetype)initIs65C02:(BOOL)is65C02 {
- (instancetype)initWithProcessor:(CSTestMachine6502Processor)processor {
self = [super init];
if(self) {
_processor = CPU::MOS6502::AllRAMProcessor::Processor(
is65C02 ? CPU::MOS6502::Personality::PWDC65C02 : CPU::MOS6502::Personality::P6502);
switch(processor) {
case CSTestMachine6502Processor6502:
_processor = CPU::MOS6502::AllRAMProcessor::Processor(CPU::MOS6502Esque::Type::T6502);
break;
case CSTestMachine6502Processor65C02:
_processor = CPU::MOS6502::AllRAMProcessor::Processor(CPU::MOS6502Esque::Type::TWDC65C02);
break;
case CSTestMachine6502Processor65816:
_processor = CPU::MOS6502::AllRAMProcessor::Processor(CPU::MOS6502Esque::Type::TWDC65816);
}
}
return self;
@ -52,13 +64,13 @@ static CPU::MOS6502::Register registerForRegister(CSTestMachine6502Register reg)
#pragma mark - Accessors
- (uint8_t)valueForAddress:(uint16_t)address {
- (uint8_t)valueForAddress:(uint32_t)address {
uint8_t value;
_processor->get_data_at_address(address, 1, &value);
return value;
}
- (void)setValue:(uint8_t)value forAddress:(uint16_t)address {
- (void)setValue:(uint8_t)value forAddress:(uint32_t)address {
_processor->set_data_at_address(address, 1, &value);
}
@ -70,7 +82,7 @@ static CPU::MOS6502::Register registerForRegister(CSTestMachine6502Register reg)
return _processor->get_value_of_register(registerForRegister(reg));
}
- (void)setData:(NSData *)data atAddress:(uint16_t)startAddress {
- (void)setData:(NSData *)data atAddress:(uint32_t)startAddress {
_processor->set_data_at_address(startAddress, data.length, (const uint8_t *)data.bytes);
}

View File

@ -23,7 +23,7 @@ class BusOperationHandler: public CPU::Z80::AllRAMProcessor::MemoryAccessDelegat
public:
BusOperationHandler(CSTestMachineZ80 *targetMachine) : target_(targetMachine) {}
void z80_all_ram_processor_did_perform_bus_operation(CPU::Z80::AllRAMProcessor &processor, CPU::Z80::PartialMachineCycle::Operation operation, uint16_t address, uint8_t value, HalfCycles time_stamp) {
void z80_all_ram_processor_did_perform_bus_operation(CPU::Z80::AllRAMProcessor &, CPU::Z80::PartialMachineCycle::Operation operation, uint16_t address, uint8_t value, HalfCycles time_stamp) {
[target_ testMachineDidPerformBusOperation:operation address:address value:value timeStamp:time_stamp];
}
@ -77,7 +77,7 @@ struct PortAccessDelegateTopByte: public CPU::Z80::AllRAMProcessor::PortAccessDe
};
struct PortAccessDelegate191: public CPU::Z80::AllRAMProcessor::PortAccessDelegate {
uint8_t z80_all_ram_processor_input(uint16_t port) final { return 191; }
uint8_t z80_all_ram_processor_input(uint16_t) final { return 191; }
};
#pragma mark - Capture class

View File

@ -10,21 +10,21 @@ import XCTest
class C1540Tests: XCTestCase {
fileprivate func with1540(_ action: (C1540Bridge) -> ()) {
private func with1540(_ action: (C1540Bridge) -> ()) {
let bridge = C1540Bridge()
action(bridge)
}
fileprivate func transmit(_ c1540: C1540Bridge, value: Int) {
private func transmit(_ c1540: C1540Bridge, value: Int) {
var shiftedValue = value
c1540.dataLine = true
c1540.run(forCycles: 256)
XCTAssert(c1540.dataLine == false, "Listener should have taken data line low for start of transmission")
XCTAssertFalse(c1540.dataLine, "Listener should have taken data line low for start of transmission")
c1540.clockLine = true
c1540.run(forCycles: 256) // this isn't time limited on real hardware
XCTAssert(c1540.dataLine == true, "Listener should have let data line go high again")
XCTAssertTrue(c1540.dataLine, "Listener should have let data line go high again")
// set up for byte transfer
c1540.clockLine = false
@ -47,7 +47,7 @@ class C1540Tests: XCTestCase {
// check for acknowledgment
c1540.dataLine = true
c1540.run(forCycles: 1000)
XCTAssert(c1540.dataLine == false, "Listener should have acknowledged byte")
XCTAssertFalse(c1540.dataLine, "Listener should have acknowledged byte")
}
// MARK: EOI
@ -64,10 +64,11 @@ class C1540Tests: XCTestCase {
// proceed 1 ms and check that the 1540 pulled the data line low
$0.run(forCycles: 1000)
XCTAssert($0.dataLine == false, "Listener should have taken data line low")
XCTAssertFalse($0.dataLine, "Listener should have taken data line low")
// transmit LISTEN #8
self.transmit($0, value: 0x28)
transmit($0, value: 0x28)
}
}
}

View File

@ -23,7 +23,7 @@ class ComparativeBusHandler: public CPU::MC68000::BusHandler {
gzclose(trace);
}
void will_perform(uint32_t address, uint16_t opcode) {
void will_perform(uint32_t address, uint16_t) {
// Obtain the next line from the trace file.
char correct_state[300] = "\n";
gzgets(trace, correct_state, sizeof(correct_state));
@ -45,7 +45,7 @@ class ComparativeBusHandler: public CPU::MC68000::BusHandler {
fprintf(stderr, "Diverges at line %d\n", line_count);
fprintf(stderr, "Good: %s", correct_state);
fprintf(stderr, "Bad: %s", local_state);
assert(false);
throw std::exception();
}
}

View File

@ -36,7 +36,7 @@ class EmuTOS: public ComparativeBusHandler {
return m68000_.get_state();
}
HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int is_supervisor) {
HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int) {
const uint32_t address = cycle.word_address();
uint32_t word_address = address;

View 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()
}
}
}

View File

@ -11,10 +11,10 @@ import XCTest
class KlausDormannTests: XCTestCase {
fileprivate func runTest(resource: String, is65C02: Bool) -> UInt16 {
private func runTest(resource: String, processor: CSTestMachine6502Processor) -> UInt16 {
if let filename = Bundle(for: type(of: self)).path(forResource: resource, ofType: "bin") {
if let functionalTest = try? Data(contentsOf: URL(fileURLWithPath: filename)) {
let machine = CSTestMachine6502(is65C02: is65C02)
let machine = CSTestMachine6502(processor: processor)
machine.setData(functionalTest, atAddress: 0)
machine.setValue(0x400, for: .programCounter)
@ -39,21 +39,25 @@ class KlausDormannTests: XCTestCase {
return 0
}
/// Runs Klaus Dorman's 6502 tests.
func test6502() {
private func runTest6502(processor: CSTestMachine6502Processor) {
func errorForTrapAddress(_ address: UInt16) -> String? {
switch address {
case 0x3399: return nil // success!
case 0x052a: return "TAX, DEX or LDA did not correctly set flags, or BEQ did not branch correctly"
case 0x05db: return "PLP did not affect N flag correctly"
case 0x26d2: return "ASL zpg,x produced incorrect flags"
case 0x33a7: return "Decimal ADC result has wrong value"
case 0x3502: return "Binary SBC result has wrong value"
case 0x33b9: return "Decimal SBC result has wrong value"
case 0x33c0: return "Decimal SBC wrong carry flag"
case 0x36d1: return "BRK: unexpected BRK or IRQ"
case 0x3502: return "Binary SBC result has wrong value"
case 0x364a: return "JMP (addr) acted as JMP addr"
case 0x36ac, 0x36f6: return "Improper JSR return address on stack"
case 0x36e5: return "BRK flag not set on stack"
case 0x26d2: return "ASL zpg,x produced incorrect flags"
case 0x36c6: return "Unexpected RESET"
case 0x36d1: return "BRK: unexpected BRK or IRQ"
case 0x36e5: return "BRK flag not set on stack following BRK"
case 0x36ea: return "BRK did not set the I flag"
case 0x36fd: return "Wrong address put on stack by BRK"
case 0: return "Didn't find tests"
@ -61,13 +65,12 @@ class KlausDormannTests: XCTestCase {
}
}
let destination = runTest(resource: "6502_functional_test", is65C02: false)
let destination = runTest(resource: "6502_functional_test", processor: processor)
let error = errorForTrapAddress(destination)
XCTAssert(error == nil, "Failed with error \(error!)")
}
/// Runs Klaus Dorman's 65C02 tests.
func test65C02() {
private func runTest65C02(processor: CSTestMachine6502Processor) {
func errorForTrapAddress(_ address: UInt16) -> String? {
switch address {
case 0x24f1: return nil // success!
@ -110,8 +113,31 @@ class KlausDormannTests: XCTestCase {
}
}
let destination = runTest(resource: "65C02_extended_opcodes_test", is65C02: true)
let destination = runTest(resource: "65C02_extended_opcodes_test", processor: processor)
let error = errorForTrapAddress(destination)
XCTAssert(error == nil, "Failed with error \(error!)")
}
/// Runs Klaus Dormann's 6502 tests.
func test6502() {
runTest6502(processor: .processor6502)
}
/// Runs Klaus Dormann's standard 6502 tests on a 65C02.
func test65C02As6502() {
runTest6502(processor: .processor65C02)
}
/// Runs Klaus Dormann's standard 6502 tests on a 65816.
func test65816As6502() {
runTest6502(processor: .processor65816)
}
/// Runs Klaus Dormann's 65C02 tests.
func test65C02() {
runTest65C02(processor: .processor65C02)
}
/* Dormann's 65C02 tests aren't applicable to the 65816; e.g. they test BBR/BBS, which the 65816 doesn't implement. */
}

View File

@ -11,8 +11,8 @@ import Foundation
class PatrikRakTests: XCTestCase, CSTestMachineTrapHandler {
fileprivate var done = false
fileprivate var output = ""
private var done = false
private var output = ""
private func runTest(_ name: String) {
if let filename = Bundle(for: type(of: self)).path(forResource: name, ofType: "tap") {
@ -124,4 +124,5 @@ class PatrikRakTests: XCTestCase, CSTestMachineTrapHandler {
break
}
}
}

View File

@ -39,7 +39,7 @@ class QL: public ComparativeBusHandler {
return m68000_.get_state();
}
HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int is_supervisor) {
HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int) {
const uint32_t address = cycle.word_address();
uint32_t word_address = address;

View File

@ -48,7 +48,7 @@ class RAM68000: public CPU::MC68000::BusHandler {
ram_[1] = sp & 0xffff;
}
void will_perform(uint32_t address, uint16_t opcode) {
void will_perform(uint32_t, uint16_t) {
--instructions_remaining_;
}
@ -80,7 +80,7 @@ class RAM68000: public CPU::MC68000::BusHandler {
return &ram_[(address >> 1) % ram_.size()];
}
HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int is_supervisor) {
HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int) {
const uint32_t word_address = cycle.word_address();
if(instructions_remaining_) duration_ += cycle.length;

View File

@ -11,206 +11,294 @@ import Foundation
class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler {
func testWolfgangLorenzStart() {
self.runWolfgangLorenzTest(" start")
// MARK: - 6502 Tests
func testStart() {
runTest(" start", processor: .processor6502)
}
func testWolfgangLorenzLDA() {
self.runWolfgangLorenzTest("lda", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"])
func testLDA() {
runTest("lda", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
}
func testWolfgangLorenzSTA() {
self.runWolfgangLorenzTest("sta", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"])
func testSTA() {
runTest("sta", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
}
func testWolfgangLorenzLDX() {
self.runWolfgangLorenzTest("ldx", suffixes: ["b", "z", "zy", "a", "ay"])
func testLDX() {
runTest("ldx", suffixes: ["b", "z", "zy", "a", "ay"], processor: .processor6502)
}
func testWolfgangLorenzSTX() {
self.runWolfgangLorenzTest("stx", suffixes: ["z", "zy", "a"])
func testSTX() {
runTest("stx", suffixes: ["z", "zy", "a"], processor: .processor6502)
}
func testWolfgangLorenzLDY() {
self.runWolfgangLorenzTest("ldy", suffixes: ["b", "z", "zx", "a", "ax"])
func testLDY() {
runTest("ldy", suffixes: ["b", "z", "zx", "a", "ax"], processor: .processor6502)
}
func testWolfgangLorenzSTY() {
self.runWolfgangLorenzTest("sty", suffixes: ["z", "zx", "a"])
func testSTY() {
runTest("sty", suffixes: ["z", "zx", "a"], processor: .processor6502)
}
func testWolfgangLorenzTransfers() {
self.runWolfgangLorenzTest("taxn")
self.runWolfgangLorenzTest("tayn")
self.runWolfgangLorenzTest("txan")
self.runWolfgangLorenzTest("tyan")
self.runWolfgangLorenzTest("tsxn")
self.runWolfgangLorenzTest("txsn")
func testTransfers() {
testTransfers(processor: .processor6502)
}
func testWolfgangLorenzStack() {
self.runWolfgangLorenzTest("phan")
self.runWolfgangLorenzTest("plan")
self.runWolfgangLorenzTest("phpn")
self.runWolfgangLorenzTest("plpn")
func testStack() {
testStack(processor: .processor6502)
}
func testWolfgangLorenzIncsAndDecs() {
self.runWolfgangLorenzTest("inxn")
self.runWolfgangLorenzTest("inyn")
self.runWolfgangLorenzTest("dexn")
self.runWolfgangLorenzTest("deyn")
self.runWolfgangLorenzTest("incz")
self.runWolfgangLorenzTest("inczx")
self.runWolfgangLorenzTest("inca")
self.runWolfgangLorenzTest("incax")
self.runWolfgangLorenzTest("decz")
self.runWolfgangLorenzTest("deczx")
self.runWolfgangLorenzTest("deca")
self.runWolfgangLorenzTest("decax")
func testIncsAndDecs() {
testIncsAndDecs(processor: .processor6502)
}
func testWolfgangLorenzASL() {
self.runWolfgangLorenzTest("asl", suffixes: ["n", "z", "zx", "a", "ax"])
func testASL() {
runTest("asl", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor6502)
}
func testWolfgangLorenzLSR() {
self.runWolfgangLorenzTest("lsr", suffixes: ["n", "z", "zx", "a", "ax"])
func testLSR() {
runTest("lsr", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor6502)
}
func testWolfgangLorenzROL() {
self.runWolfgangLorenzTest("rol", suffixes: ["n", "z", "zx", "a", "ax"])
func testROL() {
runTest("rol", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor6502)
}
func testWolfgangLorenzROR() {
self.runWolfgangLorenzTest("ror", suffixes: ["n", "z", "zx", "a", "ax"])
func testROR() {
runTest("ror", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor6502)
}
func testWolfgangLorenzAND() {
self.runWolfgangLorenzTest("and", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"])
func testAND() {
runTest("and", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
}
func testWolfgangLorenzORA() {
self.runWolfgangLorenzTest("ora", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"])
func testORA() {
runTest("ora", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
}
func testWolfgangLorenzEOR() {
self.runWolfgangLorenzTest("eor", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"])
func testEOR() {
runTest("eor", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
}
func testWolfgangLorenzFlagManipulation() {
self.runWolfgangLorenzTest("clcn")
self.runWolfgangLorenzTest("secn")
self.runWolfgangLorenzTest("cldn")
self.runWolfgangLorenzTest("sedn")
self.runWolfgangLorenzTest("clin")
self.runWolfgangLorenzTest("sein")
self.runWolfgangLorenzTest("clvn")
func testFlagManipulation() {
testFlagManipulation(processor: .processor6502)
}
func testWolfgangLorenzADC() {
self.runWolfgangLorenzTest("adc", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"])
func testADC() {
runTest("adc", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
}
func testWolfgangLorenzSBC() {
self.runWolfgangLorenzTest("sbc", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"])
func testSBC() {
runTest("sbc", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
}
func testWolfgangLorenzCompare() {
self.runWolfgangLorenzTest("cmp", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"])
self.runWolfgangLorenzTest("cpx", suffixes: ["b", "z", "a"])
self.runWolfgangLorenzTest("cpy", suffixes: ["b", "z", "a"])
func testCompare() {
testCompare(processor: .processor6502)
}
func testWolfgangLorenzBIT() {
self.runWolfgangLorenzTest("bit", suffixes: ["z", "a"])
func testBIT() {
runTest("bit", suffixes: ["z", "a"], processor: .processor6502)
}
func testWolfgangLorenzFlow() {
self.runWolfgangLorenzTest("brkn")
self.runWolfgangLorenzTest("rtin")
self.runWolfgangLorenzTest("jsrw")
self.runWolfgangLorenzTest("rtsn")
self.runWolfgangLorenzTest("jmpw")
self.runWolfgangLorenzTest("jmpi")
func testFlow() {
testFlow(processor: .processor6502)
}
func testWolfgangLorenzBranch() {
self.runWolfgangLorenzTest("beqr")
self.runWolfgangLorenzTest("bner")
self.runWolfgangLorenzTest("bmir")
self.runWolfgangLorenzTest("bplr")
self.runWolfgangLorenzTest("bcsr")
self.runWolfgangLorenzTest("bccr")
self.runWolfgangLorenzTest("bvsr")
self.runWolfgangLorenzTest("bvcr")
func testBranch() {
testBranch(processor: .processor6502)
}
func testWolfgangLorenzNOP() {
self.runWolfgangLorenzTest("nop", suffixes: ["n", "b", "z", "zx", "a", "ax"])
func testNOP() {
runTest("nop", suffixes: ["n", "b", "z", "zx", "a", "ax"], processor: .processor6502)
}
func testWolfgangLorenzASO() {
self.runWolfgangLorenzTest("aso", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"])
func testASO() {
runTest("aso", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
}
func testWolfgangLorenzRLA() {
self.runWolfgangLorenzTest("rla", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"])
func testRLA() {
runTest("rla", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
}
func testWolfgangLorenzLSE() {
self.runWolfgangLorenzTest("lse", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"])
func testLSE() {
runTest("lse", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
}
func testWolfgangLorenzRRA() {
self.runWolfgangLorenzTest("rra", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"])
func testRRA() {
runTest("rra", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
}
func testWolfgangLorenzDCM() {
self.runWolfgangLorenzTest("dcm", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"])
func testDCM() {
runTest("dcm", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
}
func testWolfgangLorenzINS() {
self.runWolfgangLorenzTest("ins", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"])
func testINS() {
runTest("ins", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor6502)
}
func testWolfgangLorenzLAX() {
self.runWolfgangLorenzTest("lax", suffixes: ["z", "zy", "a", "ay", "ix", "iy"])
func testLAX() {
runTest("lax", suffixes: ["z", "zy", "a", "ay", "ix", "iy"], processor: .processor6502)
}
func testWolfgangLorenzAXS() {
self.runWolfgangLorenzTest("axs", suffixes: ["z", "zy", "a", "ix"])
func testAXS() {
runTest("axs", suffixes: ["z", "zy", "a", "ix"], processor: .processor6502)
}
func testWolfgangLorenzALR() {
self.runWolfgangLorenzTest("alrb")
func testALR() {
runTest("alrb", processor: .processor6502)
}
func testWolfgangLorenzARR() {
self.runWolfgangLorenzTest("arrb")
func testARR() {
runTest("arrb", processor: .processor6502)
}
func testWolfgangLorenzSBX() {
self.runWolfgangLorenzTest("sbxb")
func testSBX() {
runTest("sbxb", processor: .processor6502)
}
func testWolfgangLorenzSHA() {
self.runWolfgangLorenzTest("sha", suffixes: ["ay", "iy"])
func testSHA() {
runTest("sha", suffixes: ["ay", "iy"], processor: .processor6502)
}
func testWolfgangLorenzSHX() {
self.runWolfgangLorenzTest("shxay")
func testSHX() {
runTest("shxay", processor: .processor6502)
}
func testWolfgangLorenzSHY() {
self.runWolfgangLorenzTest("shyax")
func testSHY() {
runTest("shyax", processor: .processor6502)
}
func testWolfgangLorenzSHS() {
self.runWolfgangLorenzTest("shsay")
func testSHS() {
runTest("shsay", processor: .processor6502)
}
func testWolfgangLorenzLXA() {
self.runWolfgangLorenzTest("lxab")
func testLXA() {
runTest("lxab", processor: .processor6502)
}
func testWolfgangLorenzANE() {
self.runWolfgangLorenzTest("aneb")
func testANE() {
runTest("aneb", processor: .processor6502)
}
func testWolfgangLorenzANC() {
self.runWolfgangLorenzTest("ancb")
func testANC() {
runTest("ancb", processor: .processor6502)
}
func testWolfgangLorenzLAS() {
self.runWolfgangLorenzTest("lasay")
func testLAS() {
runTest("lasay", processor: .processor6502)
}
func testWolfgangLorenzSBCB() {
self.runWolfgangLorenzTest("sbcb(eb)")
func testSBCB() {
runTest("sbcb(eb)", processor: .processor6502)
}
fileprivate func runWolfgangLorenzTest(_ name: String, suffixes: [String]) {
// MARK: - 65816 Tests
func testStart65816() {
runTest(" start", processor: .processor65816)
}
func testLDA65816() {
runTest("lda", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor65816)
}
func testSTA65816() {
runTest("sta", suffixes: ["z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor65816)
}
func testLDX65816() {
runTest("ldx", suffixes: ["b", "z", "zy", "a", "ay"], processor: .processor65816)
}
func testSTX65816() {
runTest("stx", suffixes: ["z", "zy", "a"], processor: .processor65816)
}
func testLDY65816() {
runTest("ldy", suffixes: ["b", "z", "zx", "a", "ax"], processor: .processor65816)
}
func testSTY65816() {
runTest("sty", suffixes: ["z", "zx", "a"], processor: .processor65816)
}
func testTransfers65816() {
testTransfers(processor: .processor65816)
}
func testStack65816() {
testStack(processor: .processor65816)
}
func testIncsAndDecs65816() {
testIncsAndDecs(processor: .processor65816)
}
func testASL65816() {
runTest("asl", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor65816)
}
func testLSR65816() {
runTest("lsr", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor65816)
}
func testROL65816() {
runTest("rol", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor65816)
}
func testROR65816() {
runTest("ror", suffixes: ["n", "z", "zx", "a", "ax"], processor: .processor65816)
}
func testAND65816() {
runTest("and", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor65816)
}
func testORA65816() {
runTest("ora", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor65816)
}
func testEOR65816() {
runTest("eor", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: .processor65816)
}
func testFlagManipulation65816() {
testFlagManipulation(processor: .processor65816)
}
/*
ADC and SBC tests don't apply, as the 65816 follows the 65C02 in setting flags based on the outcome of decimal
results, whereas Lorenzz's tests expect the original 6502 behaviour of setting flags based an intermediate value.
*/
func testCompare65816() {
testCompare(processor: .processor65816)
}
func testBIT65816() {
runTest("bit", suffixes: ["z", "a"], processor: .processor65816)
}
func testFlow65816() {
testFlow(processor: .processor65816)
}
func testBranch65816() {
testBranch(processor: .processor65816)
}
/* The NOP tests also don't apply; the 65816 has only one, well-defined NOP (well, not counting COP or WDM). */
// MARK: - Collections
func testTransfers(processor: CSTestMachine6502Processor) {
for test in ["taxn", "tayn", "txan", "tyan", "tsxn", "txsn"] {
runTest(test, processor: processor)
}
}
func testStack(processor: CSTestMachine6502Processor) {
for test in ["phan", "plan", "phpn", "plpn"] {
runTest(test, processor: processor)
}
}
func testIncsAndDecs(processor: CSTestMachine6502Processor) {
for test in ["inxn", "inyn", "dexn", "deyn", "incz", "inczx", "inca", "incax", "decz", "deczx", "deca", "decax"] {
runTest(test, processor: processor)
}
}
func testFlagManipulation(processor: CSTestMachine6502Processor) {
for test in ["clcn", "secn", "cldn", "sedn", "clin", "sein", "clvn"] {
runTest(test, processor: processor)
}
}
func testCompare(processor: CSTestMachine6502Processor) {
runTest("cmp", suffixes: ["b", "z", "zx", "a", "ax", "ay", "ix", "iy"], processor: processor)
runTest("cpx", suffixes: ["b", "z", "a"], processor: processor)
runTest("cpy", suffixes: ["b", "z", "a"], processor: processor)
}
func testFlow(processor: CSTestMachine6502Processor) {
for test in ["brkn", "rtin", "jsrw", "rtsn", "jmpw", "jmpi"] {
// JMP indirect is explicitly different on a 65C02 and 65816, not having
// the page wraparound bug. So run that test only on a plain 6502.
if test == "jmpi" && processor != .processor6502 {
continue;
}
runTest(test, processor: processor)
}
}
func testBranch(processor: CSTestMachine6502Processor) {
for test in ["beqr", "bner", "bmir", "bplr", "bcsr", "bccr", "bvsr", "bvcr"] {
runTest(test, processor: processor)
}
}
// MARK: - Test Running
fileprivate func runTest(_ name: String, suffixes: [String], processor: CSTestMachine6502Processor) {
for suffix in suffixes {
let testName = name + suffix
self.runWolfgangLorenzTest(testName)
runTest(testName, processor: processor)
}
}
fileprivate var output: String = ""
fileprivate func runWolfgangLorenzTest(_ name: String) {
fileprivate func runTest(_ name: String, processor: CSTestMachine6502Processor) {
var machine: CSTestMachine6502!
if let filename = Bundle(for: type(of: self)).path(forResource: name, ofType: nil) {
if let testData = try? Data(contentsOf: URL(fileURLWithPath: filename)) {
machine = CSTestMachine6502(is65C02: false)
machine = CSTestMachine6502(processor: processor)
machine.trapHandler = self
// machine.logActivity = true
output = ""
let dataPointer = (testData as NSData).bytes.bindMemory(to: UInt8.self, capacity: testData.count)
let loadAddress = UInt16(dataPointer[0]) | (UInt16(dataPointer[1]) << 8)
let contents = testData.subdata(in: 2..<(testData.count - 2))
let loadAddress = UInt32(dataPointer[0]) | (UInt32(dataPointer[1]) << 8)
let contents = testData.subdata(in: 2 ..< testData.count)
machine.setData(contents, atAddress: loadAddress)
// Cf. http://www.softwolves.com/arkiv/cbm-hackers/7/7114.html for the steps being taken here.
// Initialise memory locations as instructed.
machine.setValue(0x00, forAddress: 0x0002)
machine.setValue(0x00, forAddress: 0xa002)
machine.setValue(0x80, forAddress: 0xa003)
@ -219,28 +307,39 @@ class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler {
machine.setValue(0x48, forAddress: 0xfffe)
machine.setValue(0xff, forAddress: 0xffff)
let irqHandler = Data(bytes: UnsafePointer<UInt8>([
// Place the Commodore's default IRQ handler.
let irqHandler: [UInt8] = [
0x48, 0x8a, 0x48, 0x98, 0x48, 0xba, 0xbd, 0x04, 0x01,
0x29, 0x10, 0xf0, 0x03, 0x6c, 0x16, 0x03, 0x6c, 0x14, 0x03
] as [UInt8]), count: 19)
machine.setData( irqHandler, atAddress: 0xff48)
]
machine.setData(Data(irqHandler), atAddress: 0xff48)
// Set a couple of trap addresses to capture test output.
machine.addTrapAddress(0xffd2) // print character
machine.addTrapAddress(0xffe4) // scan keyboard
// Set a couple of test addresses that indicate failure.
machine.addTrapAddress(0x8000) // exit
machine.addTrapAddress(0xa474) // exit
// Ensure that any of those addresses return control.
machine.setValue(0x60, forAddress:0xffd2) // 0x60 is RTS
machine.setValue(0x60, forAddress:0xffe4)
machine.setValue(0x60, forAddress:0x8000)
machine.setValue(0x60, forAddress:0xa474)
machine.setValue(CSTestMachine6502JamOpcode, forAddress:0xe16f) // load
// Commodore's load routine resides at $e16f; this is used to spot the end of a test.
machine.setData(Data([0x4c, 0x6f, 0xe1]), atAddress: 0xe16f)
machine.setValue(0x0801, for: CSTestMachine6502Register.programCounter)
machine.setValue(0xfd, for: CSTestMachine6502Register.stackPointer)
machine.setValue(0x04, for: CSTestMachine6502Register.flags)
// Seed program entry.
machine.setValue(0x0801, for: .programCounter)
machine.setValue(0xfd, for: .stackPointer)
machine.setValue(0x04, for: .flags)
// For consistency when debugging; otherwise immaterial.
machine.setValue(0x00, for: .A)
machine.setValue(0x00, for: .X)
machine.setValue(0x00, for: .Y)
}
}
@ -248,19 +347,16 @@ class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler {
NSException(name: NSExceptionName(rawValue: "Failed Test"), reason: "Couldn't load file \(name)", userInfo: nil).raise()
}
while !machine.isJammed {
while machine.value(for: .lastOperationAddress) != 0xe16f && !machine.isJammed {
machine.runForNumber(ofCycles: 1000)
}
let jammedPC = machine.value(for: CSTestMachine6502Register.lastOperationAddress)
if jammedPC != 0xe16f {
let hexAddress = String(format:"%04x", jammedPC)
if machine.isJammed {
let hexAddress = String(format:"%04x", machine.value(for: .lastOperationAddress))
NSException(name: NSExceptionName(rawValue: "Failed Test"), reason: "Processor jammed unexpectedly at \(hexAddress)", userInfo: nil).raise()
}
}
// MARK: MachineJamHandler
func petsciiToString(_ string: String) -> String {
let petsciiToCharCommon: [String] = [
"?", "?", "?", "[RUN/STOP]", "?", "[WHT]", "?", "?", "[SHIFT DISABLE]", "[SHIFT ENABLE]", "?", "?", "?", "\r", "[TEXT MODE]", "?",
@ -316,7 +412,7 @@ class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler {
testMachine6502.setValue(0x3, for:CSTestMachine6502Register.A)
case 0x8000, 0xa474:
NSException(name: NSExceptionName(rawValue: "Failed test"), reason: self.petsciiToString(output), userInfo: nil).raise()
NSException(name: NSExceptionName(rawValue: "Failed test"), reason: petsciiToString(output), userInfo: nil).raise()
case 0x0000:
NSException(name: NSExceptionName(rawValue: "Failed test"), reason: "Execution hit 0000", userInfo: nil).raise()

View File

@ -1247,4 +1247,5 @@ class Z80MachineCycleTests: XCTestCase {
]
)
}
}

View File

@ -9,6 +9,7 @@
import XCTest
class Z80MemptrTester: XCTestCase {
let machine = CSTestMachineZ80()
private func test(program : [UInt8], initialValue : UInt16) -> UInt16 {

View File

@ -11,8 +11,8 @@ import Foundation
class ZexallTests: XCTestCase, CSTestMachineTrapHandler {
fileprivate var done = false
fileprivate var output = ""
private var done = false
private var output = ""
private func runTest(_ name: String) {
if let filename = Bundle(for: type(of: self)).path(forResource: name, ofType: "com") {
@ -177,4 +177,5 @@ class ZexallTests: XCTestCase, CSTestMachineTrapHandler {
break
}
}
}

View 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 *~

View 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

View 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.

View 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
-------------------------------------------------------------------------------

View 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

Binary file not shown.

View 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

View 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

View File

@ -91,6 +91,7 @@ SOURCES += \
\
$$SRC/Processors/6502/Implementation/*.cpp \
$$SRC/Processors/6502/State/*.cpp \
$$SRC/Processors/65816/Implementation/*.cpp \
$$SRC/Processors/68000/Implementation/*.cpp \
$$SRC/Processors/68000/State/*.cpp \
$$SRC/Processors/Z80/Implementation/*.cpp \
@ -214,6 +215,10 @@ HEADERS += \
$$SRC/Processors/6502/*.hpp \
$$SRC/Processors/6502/Implementation/*.hpp \
$$SRC/Processors/6502/State/*.hpp \
$$SRC/Processors/6502Esque/*.hpp \
$$SRC/Processors/6502Esque/Implementation/*.hpp \
$$SRC/Processors/65816/*.hpp \
$$SRC/Processors/65816/Implementation/*.hpp \
$$SRC/Processors/68000/*.hpp \
$$SRC/Processors/68000/Implementation/*.hpp \
$$SRC/Processors/68000/State/*.hpp \

View File

@ -287,7 +287,7 @@ void MainWindow::launchMachine() {
// Populate request text.
QString requestText = romRequestBaseText;
size_t index = 0;
for(const auto rom: missingRoms) {
for(const auto &rom: missingRoms) {
requestText += "";
requestText += rom.descriptive_name.c_str();

View File

@ -85,6 +85,7 @@ SOURCES += glob.glob('../../Outputs/OpenGL/Primitives/*.cpp')
SOURCES += glob.glob('../../Processors/6502/Implementation/*.cpp')
SOURCES += glob.glob('../../Processors/6502/State/*.cpp')
SOURCES += glob.glob('../../Processors/65816/Implementation/*.cpp')
SOURCES += glob.glob('../../Processors/68000/Implementation/*.cpp')
SOURCES += glob.glob('../../Processors/68000/State/*.cpp')
SOURCES += glob.glob('../../Processors/Z80/Implementation/*.cpp')

View File

@ -13,24 +13,19 @@
#include <cstdio>
#include <cstdint>
#include "../6502Esque/6502Esque.hpp"
#include "../6502Esque/Implementation/LazyFlags.hpp"
#include "../RegisterSizes.hpp"
#include "../../ClockReceiver/ClockReceiver.hpp"
namespace CPU {
namespace MOS6502 {
/*
The list of registers that can be accessed via @c set_value_of_register and @c set_value_of_register.
*/
enum Register {
LastOperationAddress,
ProgramCounter,
StackPointer,
Flags,
A,
X,
Y
};
// Adopt a bunch of things from MOS6502Esque.
using BusOperation = CPU::MOS6502Esque::BusOperation;
using BusHandler = CPU::MOS6502Esque::BusHandler<uint16_t>;
using Register = CPU::MOS6502Esque::Register;
using Flag = CPU::MOS6502Esque::Flag;
/*
The list of 6502 variants supported by this implementation.
@ -48,76 +43,11 @@ enum Personality {
#define has_bbrbbsrmbsmb(p) ((p) >= Personality::PRockwell65C02)
#define has_stpwai(p) ((p) >= Personality::PWDC65C02)
/*
Flags as defined on the 6502; can be used to decode the result of @c get_value_of_register(Flags) or to form a value for
the corresponding set.
*/
enum Flag: uint8_t {
Sign = 0x80,
Overflow = 0x40,
Always = 0x20,
Break = 0x10,
Decimal = 0x08,
Interrupt = 0x04,
Zero = 0x02,
Carry = 0x01
};
/*!
Subclasses will be given the task of performing bus operations, allowing them to provide whatever interface they like
between a 6502 and the rest of the system. @c BusOperation lists the types of bus operation that may be requested.
@c None is reserved for internal use. It will never be requested from a subclass. It is safe always to use the
isReadOperation macro to make a binary choice between reading and writing.
*/
enum BusOperation {
Read, ReadOpcode, Write, Ready, None
};
/*!
Evaluates to `true` if the operation is a read; `false` if it is a write.
*/
#define isReadOperation(v) (v == CPU::MOS6502::BusOperation::Read || v == CPU::MOS6502::BusOperation::ReadOpcode)
/*!
An opcode that is guaranteed to cause the CPU to jam.
*/
extern const uint8_t JamOpcode;
/*!
A class providing empty implementations of the methods a 6502 uses to access the bus. To wire the 6502 to a bus,
machines should subclass BusHandler and then declare a realisation of the 6502 template, suplying their bus
handler.
*/
class BusHandler {
public:
/*!
Announces that the 6502 has performed the cycle defined by operation, address and value. On the 6502,
all bus cycles take one clock cycle so the amoutn of time advanced is implicit.
@param operation The type of bus cycle: read, read opcode (i.e. read, with sync active),
write or ready.
@param address The value of the address bus during this bus cycle.
@param value If this is a cycle that puts a value onto the data bus, *value is that value. If this is
a cycle that reads the bus, the bus handler should write a value to *value. Writing to *value during
a read cycle will produce undefined behaviour.
@returns The number of cycles that passed in objective time while this 6502 bus cycle was ongoing.
On an archetypal machine this will be Cycles(1) but some architectures may choose not to clock the 6502
during some periods; one way to simulate that is to have the bus handler return a number other than
Cycles(1) to describe lengthened bus cycles.
*/
Cycles perform_bus_operation([[maybe_unused]] CPU::MOS6502::BusOperation operation, [[maybe_unused]] uint16_t address, [[maybe_unused]] uint8_t *value) {
return Cycles(1);
}
/*!
Announces completion of all the cycles supplied to a .run_for request on the 6502. Intended to allow
bus handlers to perform any deferred output work.
*/
void flush() {}
};
#include "Implementation/6502Storage.hpp"
/*!
@ -205,12 +135,12 @@ class ProcessorBase: public ProcessorStorage {
can also nominate whether the processor includes support for the ready line. Declining to support the ready line
can produce a minor runtime performance improvement.
*/
template <Personality personality, typename T, bool uses_ready_line> class Processor: public ProcessorBase {
template <Personality personality, typename BusHandler, bool uses_ready_line> class Processor: public ProcessorBase {
public:
/*!
Constructs an instance of the 6502 that will use @c bus_handler for all bus communications.
*/
Processor(T &bus_handler) : ProcessorBase(personality), bus_handler_(bus_handler) {}
Processor(BusHandler &bus_handler) : ProcessorBase(personality), bus_handler_(bus_handler) {}
/*!
Runs the 6502 for a supplied number of cycles.
@ -227,7 +157,7 @@ template <Personality personality, typename T, bool uses_ready_line> class Proce
void set_ready_line(bool active);
private:
T &bus_handler_;
BusHandler &bus_handler_;
};
#include "Implementation/6502Implementation.hpp"

View File

@ -11,28 +11,51 @@
#include <algorithm>
#include <cstring>
//#define BE_NOISY
using namespace CPU::MOS6502;
namespace {
template <Personality personality> class ConcreteAllRAMProcessor: public AllRAMProcessor, public BusHandler {
using Type = CPU::MOS6502Esque::Type;
template <Type type> class ConcreteAllRAMProcessor: public AllRAMProcessor, public BusHandler {
public:
ConcreteAllRAMProcessor() :
ConcreteAllRAMProcessor(size_t memory_size) :
AllRAMProcessor(memory_size),
mos6502_(*this) {
mos6502_.set_power_on(false);
}
inline Cycles perform_bus_operation(BusOperation operation, uint16_t address, uint8_t *value) {
inline Cycles perform_bus_operation(BusOperation operation, uint32_t address, uint8_t *value) {
timestamp_ += Cycles(1);
if(operation == BusOperation::ReadOpcode) {
#ifdef BE_NOISY
printf("[%04x] %02x a:%04x x:%04x y:%04x p:%02x s:%02x\n", address, memory_[address],
mos6502_.get_value_of_register(Register::A),
mos6502_.get_value_of_register(Register::X),
mos6502_.get_value_of_register(Register::Y),
mos6502_.get_value_of_register(Register::Flags) & 0xff,
mos6502_.get_value_of_register(Register::StackPointer) & 0xff);
#endif
check_address_for_trap(address);
}
if(isReadOperation(operation)) {
*value = memory_[address];
#ifdef BE_NOISY
// if((address&0xff00) == 0x100) {
printf("%04x -> %02x\n", address, *value);
// }
#endif
} else {
memory_[address] = *value;
#ifdef BE_NOISY
// if((address&0xff00) == 0x100) {
printf("%04x <- %02x\n", address, *value);
// }
#endif
}
return Cycles(1);
@ -63,20 +86,21 @@ template <Personality personality> class ConcreteAllRAMProcessor: public AllRAMP
}
private:
CPU::MOS6502::Processor<personality, ConcreteAllRAMProcessor, false> mos6502_;
CPU::MOS6502Esque::Processor<type, ConcreteAllRAMProcessor, false> mos6502_;
};
}
AllRAMProcessor *AllRAMProcessor::Processor(Personality personality) {
#define Bind(p) case p: return new ConcreteAllRAMProcessor<p>();
switch(personality) {
AllRAMProcessor *AllRAMProcessor::Processor(Type type) {
#define Bind(p) case p: return new ConcreteAllRAMProcessor<p>(type == Type::TWDC65816 ? 16*1024*1024 : 64*1024);
switch(type) {
default:
Bind(Personality::P6502)
Bind(Personality::PNES6502)
Bind(Personality::PSynertek65C02)
Bind(Personality::PWDC65C02)
Bind(Personality::PRockwell65C02)
Bind(Type::T6502)
Bind(Type::TNES6502)
Bind(Type::TSynertek65C02)
Bind(Type::TWDC65C02)
Bind(Type::TRockwell65C02)
Bind(Type::TWDC65816)
}
#undef Bind
}

View File

@ -9,7 +9,7 @@
#ifndef MOS6502AllRAM_cpp
#define MOS6502AllRAM_cpp
#include "../6502.hpp"
#include "../../6502Esque/6502Selector.hpp"
#include "../../AllRAMProcessor.hpp"
namespace CPU {
@ -17,9 +17,8 @@ namespace MOS6502 {
class AllRAMProcessor:
public ::CPU::AllRAMProcessor {
public:
static AllRAMProcessor *Processor(Personality personality);
static AllRAMProcessor *Processor(CPU::MOS6502Esque::Type type);
virtual ~AllRAMProcessor() {}
virtual void run_for(const Cycles cycles) = 0;
@ -30,7 +29,7 @@ class AllRAMProcessor:
virtual void set_value_of_register(Register r, uint16_t value) = 0;
protected:
AllRAMProcessor() : ::CPU::AllRAMProcessor(65536) {}
AllRAMProcessor(size_t memory_size) : ::CPU::AllRAMProcessor(memory_size) {}
};
}

View File

@ -8,8 +8,6 @@
#include "../6502.hpp"
#include <cassert>
using namespace CPU::MOS6502;
const uint8_t CPU::MOS6502::JamOpcode = 0xf2;

View File

@ -13,16 +13,6 @@
*/
template <Personality personality, typename T, bool uses_ready_line> void Processor<personality, T, uses_ready_line>::run_for(const Cycles cycles) {
static uint8_t throwaway_target;
// These plus program below act to give the compiler permission to update these values
// without touching the class storage (i.e. it explicitly says they need be completely up
// to date in this stack frame only); which saves some complicated addressing
RegisterPair16 nextAddress = next_address_;
BusOperation nextBusOperation = next_bus_operation_;
uint16_t busAddress = bus_address_;
uint8_t *busValue = bus_value_;
#define checkSchedule() \
if(!scheduled_program_counter_) {\
if(interrupt_requests_) {\
@ -42,9 +32,9 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
#define bus_access() \
interrupt_requests_ = (interrupt_requests_ & ~InterruptRequestFlags::IRQ) | irq_request_history_; \
irq_request_history_ = irq_line_ & inverse_interrupt_flag_; \
number_of_cycles -= bus_handler_.perform_bus_operation(nextBusOperation, busAddress, busValue); \
nextBusOperation = BusOperation::None; \
irq_request_history_ = irq_line_ & flags_.inverse_interrupt; \
number_of_cycles -= bus_handler_.perform_bus_operation(next_bus_operation_, bus_address_, bus_value_); \
next_bus_operation_ = BusOperation::None; \
if(number_of_cycles <= Cycles(0)) break;
checkSchedule();
@ -54,12 +44,12 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
// Deal with a potential RDY state, if this 6502 has anything connected to ready.
while(uses_ready_line && ready_is_active_ && number_of_cycles > Cycles(0)) {
number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, busAddress, busValue);
number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, bus_address_, bus_value_);
}
// Deal with a potential STP state, if this 6502 implements STP.
while(has_stpwai(personality) && stop_is_active_ && number_of_cycles > Cycles(0)) {
number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, busAddress, busValue);
number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, bus_address_, bus_value_);
if(interrupt_requests_ & InterruptRequestFlags::Reset) {
stop_is_active_ = false;
checkSchedule();
@ -69,8 +59,8 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
// Deal with a potential WAI state, if this 6502 implements WAI.
while(has_stpwai(personality) && wait_is_active_ && number_of_cycles > Cycles(0)) {
number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, busAddress, busValue);
interrupt_requests_ |= (irq_line_ & inverse_interrupt_flag_);
number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, bus_address_, bus_value_);
interrupt_requests_ |= (irq_line_ & flags_.inverse_interrupt);
if(interrupt_requests_ & InterruptRequestFlags::NMI || irq_line_) {
wait_is_active_ = false;
checkSchedule();
@ -79,7 +69,7 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
}
if((!uses_ready_line || !ready_is_active_) && (!has_stpwai(personality) || (!wait_is_active_ && !stop_is_active_))) {
if(nextBusOperation != BusOperation::None) {
if(next_bus_operation_ != BusOperation::None) {
bus_access();
}
@ -88,10 +78,10 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
const MicroOp cycle = *scheduled_program_counter_;
scheduled_program_counter_++;
#define read_op(val, addr) nextBusOperation = BusOperation::ReadOpcode; busAddress = addr; busValue = &val; val = 0xff
#define read_mem(val, addr) nextBusOperation = BusOperation::Read; busAddress = addr; busValue = &val; val = 0xff
#define throwaway_read(addr) nextBusOperation = BusOperation::Read; busAddress = addr; busValue = &throwaway_target; throwaway_target = 0xff
#define write_mem(val, addr) nextBusOperation = BusOperation::Write; busAddress = addr; busValue = &val
#define read_op(val, addr) next_bus_operation_ = BusOperation::ReadOpcode; bus_address_ = addr; bus_value_ = &val; val = 0xff
#define read_mem(val, addr) next_bus_operation_ = BusOperation::Read; bus_address_ = addr; bus_value_ = &val; val = 0xff
#define throwaway_read(addr) next_bus_operation_ = BusOperation::Read; bus_address_ = addr; bus_value_ = &bus_throwaway_; bus_throwaway_ = 0xff
#define write_mem(val, addr) next_bus_operation_ = BusOperation::Write; bus_address_ = addr; bus_value_ = &val
switch(cycle) {
@ -155,37 +145,37 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
case OperationBRKPickVector:
if(is_65c02(personality)) {
nextAddress.full = 0xfffe;
next_address_.full = 0xfffe;
} else {
// NMI can usurp BRK-vector operations on the pre-C 6502s.
nextAddress.full = (interrupt_requests_ & InterruptRequestFlags::NMI) ? 0xfffa : 0xfffe;
next_address_.full = (interrupt_requests_ & InterruptRequestFlags::NMI) ? 0xfffa : 0xfffe;
interrupt_requests_ &= ~InterruptRequestFlags::NMI;
}
continue;
case OperationNMIPickVector: nextAddress.full = 0xfffa; continue;
case OperationRSTPickVector: nextAddress.full = 0xfffc; continue;
case CycleReadVectorLow: read_mem(pc_.halves.low, nextAddress.full); break;
case CycleReadVectorHigh: read_mem(pc_.halves.high, nextAddress.full+1); break;
case OperationNMIPickVector: next_address_.full = 0xfffa; continue;
case OperationRSTPickVector: next_address_.full = 0xfffc; continue;
case CycleReadVectorLow: read_mem(pc_.halves.low, next_address_.full); break;
case CycleReadVectorHigh: read_mem(pc_.halves.high, next_address_.full+1); break;
case OperationSetIRQFlags:
inverse_interrupt_flag_ = 0;
if(is_65c02(personality)) decimal_flag_ = false;
flags_.inverse_interrupt = 0;
if(is_65c02(personality)) flags_.decimal = 0;
continue;
case OperationSetNMIRSTFlags:
if(is_65c02(personality)) decimal_flag_ = false;
if(is_65c02(personality)) flags_.decimal = 0;
continue;
case CyclePullPCL: s_++; read_mem(pc_.halves.low, s_ | 0x100); break;
case CyclePullPCH: s_++; read_mem(pc_.halves.high, s_ | 0x100); break;
case CyclePullA: s_++; read_mem(a_, s_ | 0x100); break;
case CyclePullX: s_++; read_mem(x_, s_ | 0x100); break;
case CyclePullY: s_++; read_mem(y_, s_ | 0x100); break;
case CyclePullOperand: s_++; read_mem(operand_, s_ | 0x100); break;
case OperationSetFlagsFromOperand: set_flags(operand_); continue;
case OperationSetOperandFromFlagsWithBRKSet: operand_ = get_flags() | Flag::Break; continue;
case OperationSetOperandFromFlags: operand_ = get_flags(); continue;
case OperationSetFlagsFromA: zero_result_ = negative_result_ = a_; continue;
case OperationSetFlagsFromX: zero_result_ = negative_result_ = x_; continue;
case OperationSetFlagsFromY: zero_result_ = negative_result_ = y_; continue;
case CyclePullPCL: s_++; read_mem(pc_.halves.low, s_ | 0x100); break;
case CyclePullPCH: s_++; read_mem(pc_.halves.high, s_ | 0x100); break;
case CyclePullA: s_++; read_mem(a_, s_ | 0x100); break;
case CyclePullX: s_++; read_mem(x_, s_ | 0x100); break;
case CyclePullY: s_++; read_mem(y_, s_ | 0x100); break;
case CyclePullOperand: s_++; read_mem(operand_, s_ | 0x100); break;
case OperationSetFlagsFromOperand: set_flags(operand_); continue;
case OperationSetOperandFromFlagsWithBRKSet: operand_ = flags_.get() | Flag::Break; continue;
case OperationSetOperandFromFlags: operand_ = flags_.get(); continue;
case OperationSetFlagsFromA: flags_.set_nz(a_); continue;
case OperationSetFlagsFromX: flags_.set_nz(x_); continue;
case OperationSetFlagsFromY: flags_.set_nz(y_); continue;
case CycleIncrementPCAndReadStack: pc_.full++; throwaway_read(s_ | 0x100); break;
case CycleReadPCLFromAddress: read_mem(pc_.halves.low, address_.full); break;
@ -216,17 +206,17 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
// MARK: - Bitwise
case OperationORA: a_ |= operand_; negative_result_ = zero_result_ = a_; continue;
case OperationAND: a_ &= operand_; negative_result_ = zero_result_ = a_; continue;
case OperationEOR: a_ ^= operand_; negative_result_ = zero_result_ = a_; continue;
case OperationORA: a_ |= operand_; flags_.set_nz(a_); continue;
case OperationAND: a_ &= operand_; flags_.set_nz(a_); continue;
case OperationEOR: a_ ^= operand_; flags_.set_nz(a_); continue;
// MARK: - Load and Store
case OperationLDA: a_ = negative_result_ = zero_result_ = operand_; continue;
case OperationLDX: x_ = negative_result_ = zero_result_ = operand_; continue;
case OperationLDY: y_ = negative_result_ = zero_result_ = operand_; continue;
case OperationLAX: a_ = x_ = negative_result_ = zero_result_ = operand_; continue;
case OperationCopyOperandToA: a_ = operand_; continue;
case OperationLDA: flags_.set_nz(a_ = operand_); continue;
case OperationLDX: flags_.set_nz(x_ = operand_); continue;
case OperationLDY: flags_.set_nz(y_ = operand_); continue;
case OperationLAX: flags_.set_nz(a_ = x_ = operand_); continue;
case OperationCopyOperandToA: a_ = operand_; continue;
case OperationSTA: operand_ = a_; continue;
case OperationSTX: operand_ = x_; continue;
@ -240,43 +230,43 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
case OperationLXA:
a_ = x_ = (a_ | 0xee) & operand_;
negative_result_ = zero_result_ = a_;
flags_.set_nz(a_);
continue;
// MARK: - Compare
case OperationCMP: {
const uint16_t temp16 = a_ - operand_;
negative_result_ = zero_result_ = uint8_t(temp16);
carry_flag_ = ((~temp16) >> 8)&1;
flags_.set_nz(uint8_t(temp16));
flags_.carry = ((~temp16) >> 8)&1;
} continue;
case OperationCPX: {
const uint16_t temp16 = x_ - operand_;
negative_result_ = zero_result_ = uint8_t(temp16);
carry_flag_ = ((~temp16) >> 8)&1;
flags_.set_nz(uint8_t(temp16));
flags_.carry = ((~temp16) >> 8)&1;
} continue;
case OperationCPY: {
const uint16_t temp16 = y_ - operand_;
negative_result_ = zero_result_ = uint8_t(temp16);
carry_flag_ = ((~temp16) >> 8)&1;
flags_.set_nz(uint8_t(temp16));
flags_.carry = ((~temp16) >> 8)&1;
} continue;
// MARK: - BIT, TSB, TRB
case OperationBIT:
zero_result_ = operand_ & a_;
negative_result_ = operand_;
overflow_flag_ = operand_&Flag::Overflow;
flags_.zero_result = operand_ & a_;
flags_.negative_result = operand_;
flags_.overflow = operand_ & Flag::Overflow;
continue;
case OperationBITNoNV:
zero_result_ = operand_ & a_;
flags_.zero_result = operand_ & a_;
continue;
case OperationTRB:
zero_result_ = operand_ & a_;
flags_.zero_result = operand_ & a_;
operand_ &= ~a_;
continue;
case OperationTSB:
zero_result_ = operand_ & a_;
flags_.zero_result = operand_ & a_;
operand_ |= a_;
continue;
@ -295,8 +285,8 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
operand_++;
[[fallthrough]];
case OperationSBC:
if(decimal_flag_ && has_decimal_mode(personality)) {
const uint16_t notCarry = carry_flag_ ^ 0x1;
if(flags_.decimal && has_decimal_mode(personality)) {
const uint16_t notCarry = flags_.carry ^ 0x1;
const uint16_t decimalResult = uint16_t(a_) - uint16_t(operand_) - notCarry;
uint16_t temp16;
@ -305,17 +295,17 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
temp16 = (temp16&0x0f) | ((temp16 > 0x0f) ? 0xfff0 : 0x00);
temp16 += (a_&0xf0) - (operand_&0xf0);
overflow_flag_ = ( ( (decimalResult^a_)&(~decimalResult^operand_) )&0x80) >> 1;
negative_result_ = uint8_t(temp16);
zero_result_ = uint8_t(decimalResult);
flags_.overflow = ( ( (decimalResult^a_)&(~decimalResult^operand_) )&0x80) >> 1;
flags_.negative_result = uint8_t(temp16);
flags_.zero_result = uint8_t(decimalResult);
if(temp16 > 0xff) temp16 -= 0x60;
carry_flag_ = (temp16 > 0xff) ? 0 : Flag::Carry;
flags_.carry = (temp16 > 0xff) ? 0 : Flag::Carry;
a_ = uint8_t(temp16);
if(is_65c02(personality)) {
negative_result_ = zero_result_ = a_;
flags_.set_nz(a_);
read_mem(operand_, address_.full);
break;
}
@ -326,30 +316,30 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
[[fallthrough]];
case OperationADC:
if(decimal_flag_ && has_decimal_mode(personality)) {
const uint16_t decimalResult = uint16_t(a_) + uint16_t(operand_) + uint16_t(carry_flag_);
if(flags_.decimal && has_decimal_mode(personality)) {
const uint16_t decimalResult = uint16_t(a_) + uint16_t(operand_) + uint16_t(flags_.carry);
uint8_t low_nibble = (a_ & 0xf) + (operand_ & 0xf) + carry_flag_;
uint8_t low_nibble = (a_ & 0xf) + (operand_ & 0xf) + flags_.carry;
if(low_nibble >= 0xa) low_nibble = ((low_nibble + 0x6) & 0xf) + 0x10;
uint16_t result = uint16_t(a_ & 0xf0) + uint16_t(operand_ & 0xf0) + uint16_t(low_nibble);
negative_result_ = uint8_t(result);
overflow_flag_ = (( (result^a_)&(result^operand_) )&0x80) >> 1;
flags_.negative_result = uint8_t(result);
flags_.overflow = (( (result^a_)&(result^operand_) )&0x80) >> 1;
if(result >= 0xa0) result += 0x60;
carry_flag_ = (result >> 8) ? 1 : 0;
flags_.carry = (result >> 8) ? 1 : 0;
a_ = uint8_t(result);
zero_result_ = uint8_t(decimalResult);
flags_.zero_result = uint8_t(decimalResult);
if(is_65c02(personality)) {
negative_result_ = zero_result_ = a_;
flags_.set_nz(a_);
read_mem(operand_, address_.full);
break;
}
} else {
const uint16_t result = uint16_t(a_) + uint16_t(operand_) + uint16_t(carry_flag_);
overflow_flag_ = (( (result^a_)&(result^operand_) )&0x80) >> 1;
negative_result_ = zero_result_ = a_ = uint8_t(result);
carry_flag_ = (result >> 8)&1;
const uint16_t result = uint16_t(a_) + uint16_t(operand_) + uint16_t(flags_.carry);
flags_.overflow = (( (result^a_)&(result^operand_) )&0x80) >> 1;
flags_.set_nz(a_ = uint8_t(result));
flags_.carry = (result >> 8)&1;
}
// fix up in case this was INS
@ -359,99 +349,99 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
// MARK: - Shifts and Rolls
case OperationASL:
carry_flag_ = operand_ >> 7;
flags_.carry = operand_ >> 7;
operand_ <<= 1;
negative_result_ = zero_result_ = operand_;
flags_.set_nz(operand_);
continue;
case OperationASO:
carry_flag_ = operand_ >> 7;
flags_.carry = operand_ >> 7;
operand_ <<= 1;
a_ |= operand_;
negative_result_ = zero_result_ = a_;
flags_.set_nz(a_);
continue;
case OperationROL: {
const uint8_t temp8 = uint8_t((operand_ << 1) | carry_flag_);
carry_flag_ = operand_ >> 7;
operand_ = negative_result_ = zero_result_ = temp8;
const uint8_t temp8 = uint8_t((operand_ << 1) | flags_.carry);
flags_.carry = operand_ >> 7;
flags_.set_nz(operand_ = temp8);
} continue;
case OperationRLA: {
const uint8_t temp8 = uint8_t((operand_ << 1) | carry_flag_);
carry_flag_ = operand_ >> 7;
const uint8_t temp8 = uint8_t((operand_ << 1) | flags_.carry);
flags_.carry = operand_ >> 7;
operand_ = temp8;
a_ &= operand_;
negative_result_ = zero_result_ = a_;
flags_.set_nz(a_);
} continue;
case OperationLSR:
carry_flag_ = operand_ & 1;
flags_.carry = operand_ & 1;
operand_ >>= 1;
negative_result_ = zero_result_ = operand_;
flags_.set_nz(operand_);
continue;
case OperationLSE:
carry_flag_ = operand_ & 1;
flags_.carry = operand_ & 1;
operand_ >>= 1;
a_ ^= operand_;
negative_result_ = zero_result_ = a_;
flags_.set_nz(a_);
continue;
case OperationASR:
a_ &= operand_;
carry_flag_ = a_ & 1;
flags_.carry = a_ & 1;
a_ >>= 1;
negative_result_ = zero_result_ = a_;
flags_.set_nz(a_);
continue;
case OperationROR: {
const uint8_t temp8 = uint8_t((operand_ >> 1) | (carry_flag_ << 7));
carry_flag_ = operand_ & 1;
operand_ = negative_result_ = zero_result_ = temp8;
const uint8_t temp8 = uint8_t((operand_ >> 1) | (flags_.carry << 7));
flags_.carry = operand_ & 1;
flags_.set_nz(operand_ = temp8);
} continue;
case OperationRRA: {
const uint8_t temp8 = uint8_t((operand_ >> 1) | (carry_flag_ << 7));
carry_flag_ = operand_ & 1;
const uint8_t temp8 = uint8_t((operand_ >> 1) | (flags_.carry << 7));
flags_.carry = operand_ & 1;
operand_ = temp8;
} continue;
case OperationDecrementOperand: operand_--; continue;
case OperationIncrementOperand: operand_++; continue;
case OperationCLC: carry_flag_ = 0; continue;
case OperationCLI: inverse_interrupt_flag_ = Flag::Interrupt; continue;
case OperationCLV: overflow_flag_ = 0; continue;
case OperationCLD: decimal_flag_ = 0; continue;
case OperationCLC: flags_.carry = 0; continue;
case OperationCLI: flags_.inverse_interrupt = Flag::Interrupt; continue;
case OperationCLV: flags_.overflow = 0; continue;
case OperationCLD: flags_.decimal = 0; continue;
case OperationSEC: carry_flag_ = Flag::Carry; continue;
case OperationSEI: inverse_interrupt_flag_ = 0; continue;
case OperationSED: decimal_flag_ = Flag::Decimal; continue;
case OperationSEC: flags_.carry = Flag::Carry; continue;
case OperationSEI: flags_.inverse_interrupt = 0; continue;
case OperationSED: flags_.decimal = Flag::Decimal; continue;
case OperationINC: operand_++; negative_result_ = zero_result_ = operand_; continue;
case OperationDEC: operand_--; negative_result_ = zero_result_ = operand_; continue;
case OperationINA: a_++; negative_result_ = zero_result_ = a_; continue;
case OperationDEA: a_--; negative_result_ = zero_result_ = a_; continue;
case OperationINX: x_++; negative_result_ = zero_result_ = x_; continue;
case OperationDEX: x_--; negative_result_ = zero_result_ = x_; continue;
case OperationINY: y_++; negative_result_ = zero_result_ = y_; continue;
case OperationDEY: y_--; negative_result_ = zero_result_ = y_; continue;
case OperationINC: operand_++; flags_.set_nz(operand_); continue;
case OperationDEC: operand_--; flags_.set_nz(operand_); continue;
case OperationINA: a_++; flags_.set_nz(a_); continue;
case OperationDEA: a_--; flags_.set_nz(a_); continue;
case OperationINX: x_++; flags_.set_nz(x_); continue;
case OperationDEX: x_--; flags_.set_nz(x_); continue;
case OperationINY: y_++; flags_.set_nz(y_); continue;
case OperationDEY: y_--; flags_.set_nz(y_); continue;
case OperationANE:
a_ = (a_ | 0xee) & operand_ & x_;
negative_result_ = zero_result_ = a_;
flags_.set_nz(a_);
continue;
case OperationANC:
a_ &= operand_;
negative_result_ = zero_result_ = a_;
carry_flag_ = a_ >> 7;
flags_.set_nz(a_);
flags_.carry = a_ >> 7;
continue;
case OperationLAS:
a_ = x_ = s_ = s_ & operand_;
negative_result_ = zero_result_ = a_;
flags_.set_nz(a_);
continue;
// MARK: - Addressing Mode Work
@ -464,36 +454,36 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
}
case CycleAddXToAddressLow:
nextAddress.full = address_.full + x_;
address_.halves.low = nextAddress.halves.low;
if(address_.halves.high != nextAddress.halves.high) {
next_address_.full = address_.full + x_;
address_.halves.low = next_address_.halves.low;
if(address_.halves.high != next_address_.halves.high) {
page_crossing_stall_read();
break;
}
continue;
case CycleAddXToAddressLowRead:
nextAddress.full = address_.full + x_;
address_.halves.low = nextAddress.halves.low;
next_address_.full = address_.full + x_;
address_.halves.low = next_address_.halves.low;
page_crossing_stall_read();
break;
case CycleAddYToAddressLow:
nextAddress.full = address_.full + y_;
address_.halves.low = nextAddress.halves.low;
if(address_.halves.high != nextAddress.halves.high) {
next_address_.full = address_.full + y_;
address_.halves.low = next_address_.halves.low;
if(address_.halves.high != next_address_.halves.high) {
page_crossing_stall_read();
break;
}
continue;
case CycleAddYToAddressLowRead:
nextAddress.full = address_.full + y_;
address_.halves.low = nextAddress.halves.low;
next_address_.full = address_.full + y_;
address_.halves.low = next_address_.halves.low;
page_crossing_stall_read();
break;
#undef page_crossing_stall_read
case OperationCorrectAddressHigh:
address_.full = nextAddress.full;
address_.full = next_address_.full;
continue;
case CycleIncrementPCFetchAddressLowFromOperand:
pc_.full++;
@ -548,7 +538,7 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
throwaway_read(operand_);
break;
case OperationIncrementPC: pc_.full++; continue;
case OperationIncrementPC: pc_.full++; continue;
case CycleFetchOperandFromAddress: read_mem(operand_, address_.full); break;
case CycleWriteOperandToAddress: write_mem(operand_, address_.full); break;
@ -560,25 +550,25 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
scheduled_program_counter_ = operations_[size_t(OperationsSlot::DoBRA)]; \
}
case OperationBPL: BRA(!(negative_result_&0x80)); continue;
case OperationBMI: BRA(negative_result_&0x80); continue;
case OperationBVC: BRA(!overflow_flag_); continue;
case OperationBVS: BRA(overflow_flag_); continue;
case OperationBCC: BRA(!carry_flag_); continue;
case OperationBCS: BRA(carry_flag_); continue;
case OperationBNE: BRA(zero_result_); continue;
case OperationBEQ: BRA(!zero_result_); continue;
case OperationBPL: BRA(!(flags_.negative_result&0x80)); continue;
case OperationBMI: BRA(flags_.negative_result&0x80); continue;
case OperationBVC: BRA(!flags_.overflow); continue;
case OperationBVS: BRA(flags_.overflow); continue;
case OperationBCC: BRA(!flags_.carry); continue;
case OperationBCS: BRA(flags_.carry); continue;
case OperationBNE: BRA(flags_.zero_result); continue;
case OperationBEQ: BRA(!flags_.zero_result); continue;
case OperationBRA: BRA(true); continue;
#undef BRA
case CycleAddSignedOperandToPC:
nextAddress.full = uint16_t(pc_.full + int8_t(operand_));
pc_.halves.low = nextAddress.halves.low;
if(nextAddress.halves.high != pc_.halves.high) {
uint16_t halfUpdatedPc = pc_.full;
pc_.full = nextAddress.full;
throwaway_read(halfUpdatedPc);
next_address_.full = uint16_t(pc_.full + int8_t(operand_));
pc_.halves.low = next_address_.halves.low;
if(next_address_.halves.high != pc_.halves.high) {
const uint16_t half_updated_pc = pc_.full;
pc_.full = next_address_.full;
throwaway_read(half_updated_pc);
break;
} else if(is_65c02(personality)) {
// 65C02 modification to all branches: a branch that is taken but requires only a single cycle
@ -610,31 +600,31 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
// MARK: - Transfers
case OperationTXA: zero_result_ = negative_result_ = a_ = x_; continue;
case OperationTYA: zero_result_ = negative_result_ = a_ = y_; continue;
case OperationTXS: s_ = x_; continue;
case OperationTAY: zero_result_ = negative_result_ = y_ = a_; continue;
case OperationTAX: zero_result_ = negative_result_ = x_ = a_; continue;
case OperationTSX: zero_result_ = negative_result_ = x_ = s_; continue;
case OperationTXA: flags_.set_nz(a_ = x_); continue;
case OperationTYA: flags_.set_nz(a_ = y_); continue;
case OperationTXS: s_ = x_; continue;
case OperationTAY: flags_.set_nz(y_ = a_); continue;
case OperationTAX: flags_.set_nz(x_ = a_); continue;
case OperationTSX: flags_.set_nz(x_ = s_); continue;
case OperationARR:
if(decimal_flag_) {
if(flags_.decimal) {
a_ &= operand_;
uint8_t unshiftedA = a_;
a_ = uint8_t((a_ >> 1) | (carry_flag_ << 7));
zero_result_ = negative_result_ = a_;
overflow_flag_ = (a_^(a_ << 1))&Flag::Overflow;
a_ = uint8_t((a_ >> 1) | (flags_.carry << 7));
flags_.set_nz(a_);
flags_.overflow = (a_^(a_ << 1))&Flag::Overflow;
if((unshiftedA&0xf) + (unshiftedA&0x1) > 5) a_ = ((a_ + 6)&0xf) | (a_ & 0xf0);
carry_flag_ = ((unshiftedA&0xf0) + (unshiftedA&0x10) > 0x50) ? 1 : 0;
if(carry_flag_) a_ += 0x60;
flags_.carry = ((unshiftedA&0xf0) + (unshiftedA&0x10) > 0x50) ? 1 : 0;
if(flags_.carry) a_ += 0x60;
} else {
a_ &= operand_;
a_ = uint8_t((a_ >> 1) | (carry_flag_ << 7));
negative_result_ = zero_result_ = a_;
carry_flag_ = (a_ >> 6)&1;
overflow_flag_ = (a_^(a_ << 1))&Flag::Overflow;
a_ = uint8_t((a_ >> 1) | (flags_.carry << 7));
flags_.set_nz(a_);
flags_.carry = (a_ >> 6)&1;
flags_.overflow = (a_^(a_ << 1))&Flag::Overflow;
}
continue;
@ -642,15 +632,15 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
x_ &= a_;
uint16_t difference = x_ - operand_;
x_ = uint8_t(difference);
negative_result_ = zero_result_ = x_;
carry_flag_ = ((difference >> 8)&1)^1;
flags_.set_nz(x_);
flags_.carry = ((difference >> 8)&1)^1;
continue;
}
if(has_stpwai(personality) && (stop_is_active_ || wait_is_active_)) {
break;
}
if(uses_ready_line && ready_line_is_enabled_ && (is_65c02(personality) || isReadOperation(nextBusOperation))) {
if(uses_ready_line && ready_line_is_enabled_ && (is_65c02(personality) || isReadOperation(next_bus_operation_))) {
ready_is_active_ = true;
break;
}
@ -660,11 +650,6 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
}
cycles_left_to_run_ = number_of_cycles;
next_address_ = nextAddress;
next_bus_operation_ = nextBusOperation;
bus_address_ = busAddress;
bus_value_ = busValue;
bus_handler_.flush();
}
@ -691,13 +676,13 @@ void ProcessorBase::set_power_on(bool active) {
}
void ProcessorBase::set_irq_line(bool active) {
irq_line_ = active ? Flag::Interrupt : 0;
irq_line_ = active ? MOS6502Esque::Flag::Interrupt : 0;
}
void ProcessorBase::set_overflow_line(bool active) {
// a leading edge will set the overflow flag
if(active && !set_overflow_line_is_enabled_)
overflow_flag_ = Flag::Overflow;
flags_.overflow = MOS6502Esque::Flag::Overflow;
set_overflow_line_is_enabled_ = active;
}
@ -709,14 +694,9 @@ void ProcessorBase::set_nmi_line(bool active) {
}
uint8_t ProcessorStorage::get_flags() const {
return carry_flag_ | overflow_flag_ | (inverse_interrupt_flag_ ^ Flag::Interrupt) | (negative_result_ & 0x80) | (zero_result_ ? 0 : Flag::Zero) | Flag::Always | decimal_flag_;
return flags_.get();
}
void ProcessorStorage::set_flags(uint8_t flags) {
carry_flag_ = flags & Flag::Carry;
negative_result_ = flags & Flag::Sign;
zero_result_ = (~flags) & Flag::Zero;
overflow_flag_ = flags & Flag::Overflow;
inverse_interrupt_flag_ = (~flags) & Flag::Interrupt;
decimal_flag_ = flags & Flag::Decimal;
flags_.set(flags);
}

View File

@ -76,12 +76,6 @@ using namespace CPU::MOS6502;
#define JAM {CycleFetchOperand, OperationScheduleJam}
ProcessorStorage::ProcessorStorage(Personality personality) {
// only the interrupt flag is defined upon reset but get_flags isn't going to
// mask the other flags so we need to do that, at least
carry_flag_ &= Flag::Carry;
decimal_flag_ &= Flag::Decimal;
overflow_flag_ &= Flag::Overflow;
const InstructionList operations_6502[] = {
/* 0x00 BRK */ Program(CycleIncPCPushPCH, CyclePushPCL, OperationBRKPickVector, OperationSetOperandFromFlagsWithBRKSet, CyclePushOperand, OperationSetIRQFlags, CycleReadVectorLow, CycleReadVectorHigh),
/* 0x01 ORA x, ind */ IndexedIndirectRead(OperationORA),

View File

@ -6,9 +6,6 @@
// Copyright 2017 Thomas Harte. All rights reserved.
//
#ifndef MOS6502Storage_h
#define MOS6502Storage_h
/*!
A repository for all the internal state of a CPU::MOS6502::Processor; extracted into a separate base
class in order to remove it from visibility within the main 6502.hpp.
@ -231,7 +228,7 @@ class ProcessorStorage {
*/
RegisterPair16 pc_, last_operation_pc_;
uint8_t a_, x_, y_, s_ = 0;
uint8_t carry_flag_, negative_result_, zero_result_, decimal_flag_, overflow_flag_, inverse_interrupt_flag_ = 0;
MOS6502Esque::LazyFlags flags_;
/*
Temporary state for the micro programs.
@ -246,6 +243,7 @@ class ProcessorStorage {
BusOperation next_bus_operation_ = BusOperation::None;
uint16_t bus_address_;
uint8_t *bus_value_;
static inline uint8_t bus_throwaway_;
/*!
Gets the flags register.
@ -270,7 +268,7 @@ class ProcessorStorage {
enum InterruptRequestFlags: uint8_t {
Reset = 0x80,
IRQ = Flag::Interrupt,
IRQ = MOS6502Esque::Flag::Interrupt,
NMI = 0x20,
PowerOn = 0x10,
@ -288,5 +286,3 @@ class ProcessorStorage {
// Allow state objects to capture and apply state.
friend struct State;
};
#endif /* _502Storage_h */

View 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 */

View 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 */

View 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 */

View 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.

View 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 */

View 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;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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 1623.
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);
};

View File

@ -15,14 +15,14 @@ AllRAMProcessor::AllRAMProcessor(std::size_t memory_size) :
traps_(memory_size, false),
timestamp_(0) {}
void AllRAMProcessor::set_data_at_address(uint16_t startAddress, std::size_t length, const uint8_t *data) {
std::size_t endAddress = std::min(startAddress + length, size_t(65536));
std::memcpy(&memory_[startAddress], data, endAddress - startAddress);
void AllRAMProcessor::set_data_at_address(size_t start_address, std::size_t length, const uint8_t *data) {
const size_t end_address = std::min(start_address + length, memory_.size());
memcpy(&memory_[start_address], data, end_address - start_address);
}
void AllRAMProcessor::get_data_at_address(uint16_t startAddress, std::size_t length, uint8_t *data) {
std::size_t endAddress = std::min(startAddress + length, size_t(65536));
std::memcpy(data, &memory_[startAddress], endAddress - startAddress);
void AllRAMProcessor::get_data_at_address(size_t start_address, std::size_t length, uint8_t *data) {
const size_t end_address = std::min(start_address + length, memory_.size());
memcpy(data, &memory_[start_address], end_address - start_address);
}
HalfCycles AllRAMProcessor::get_timestamp() {

View File

@ -21,8 +21,8 @@ class AllRAMProcessor {
public:
AllRAMProcessor(std::size_t memory_size);
HalfCycles get_timestamp();
void set_data_at_address(uint16_t startAddress, std::size_t length, const uint8_t *data);
void get_data_at_address(uint16_t startAddress, std::size_t length, uint8_t *data);
void set_data_at_address(size_t startAddress, size_t length, const uint8_t *data);
void get_data_at_address(size_t startAddress, size_t length, uint8_t *data);
class TrapHandler {
public:

View File

@ -29,7 +29,7 @@ class AllRAMProcessor:
}
struct PortAccessDelegate {
virtual uint8_t z80_all_ram_processor_input(uint16_t port) { return 0xff; }
virtual uint8_t z80_all_ram_processor_input(uint16_t) { return 0xff; }
};
inline void set_port_access_delegate(PortAccessDelegate *delegate) {
port_delegate_ = delegate;