mirror of
https://github.com/TomHarte/CLK.git
synced 2025-10-25 09:27:01 +00:00
Compare commits
188 Commits
2020-10-02
...
2020-12-09
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8560b38ffa | ||
|
|
94eb17db0c | ||
|
|
9577c8e27f | ||
|
|
32ccce3040 | ||
|
|
ab3fcb3ea0 | ||
|
|
9610672615 | ||
|
|
5ee9630624 | ||
|
|
1b3836eb1c | ||
|
|
1302a046e9 | ||
|
|
33dec3c220 | ||
|
|
7c29c3a944 | ||
|
|
c9ca1fc7a0 | ||
|
|
a965c8de9f | ||
|
|
0b4b271e3d | ||
|
|
5fc6dd1a4d | ||
|
|
79ef026b93 | ||
|
|
a4ab5b0b49 | ||
|
|
3207183f05 | ||
|
|
e803f993b7 | ||
|
|
5dbc87caf0 | ||
|
|
4862ccc947 | ||
|
|
e1ecf66485 | ||
|
|
2c71ba0744 | ||
|
|
a7aeb779e9 | ||
|
|
e72cfbf447 | ||
|
|
5d154e3d0c | ||
|
|
86a24cc928 | ||
|
|
e8b52d20e9 | ||
|
|
b5fa574686 | ||
|
|
7aea3dc124 | ||
|
|
3aa47f9c68 | ||
|
|
ab07814614 | ||
|
|
1653abdf88 | ||
|
|
b3ab9fff9b | ||
|
|
14718b93a4 | ||
|
|
69450e27ad | ||
|
|
0cd08aa79d | ||
|
|
1fa94e1b08 | ||
|
|
76d9893866 | ||
|
|
c3f8982c62 | ||
|
|
99eba2f8ba | ||
|
|
69509f6502 | ||
|
|
c3187fdbe1 | ||
|
|
42228ea955 | ||
|
|
e5f57ea743 | ||
|
|
3b398f7a9a | ||
|
|
096add7551 | ||
|
|
334e0666b7 | ||
|
|
98c81749c8 | ||
|
|
5dcf720bb5 | ||
|
|
9c0c0255f6 | ||
|
|
68c15bd605 | ||
|
|
9a2f32795f | ||
|
|
7aa6cf4c6b | ||
|
|
dfda2adf0d | ||
|
|
c0a1c34012 | ||
|
|
3c6adc1ff4 | ||
|
|
e511d33a7c | ||
|
|
c35969d677 | ||
|
|
27afb8f0a7 | ||
|
|
327ab81436 | ||
|
|
db7178495f | ||
|
|
979186e71d | ||
|
|
f05e0d956b | ||
|
|
b22aa5d699 | ||
|
|
3e6a2adaaf | ||
|
|
8f5537aaaa | ||
|
|
a15d4a156b | ||
|
|
6a47571d17 | ||
|
|
7479dc74ed | ||
|
|
28da1a724a | ||
|
|
f529eadbec | ||
|
|
5dc3cd3a2f | ||
|
|
3039a445f0 | ||
|
|
82797fd395 | ||
|
|
a0885ab7d0 | ||
|
|
8eaf1303a3 | ||
|
|
20cbe72985 | ||
|
|
071ad6b767 | ||
|
|
0619e49eac | ||
|
|
b8848d8580 | ||
|
|
aface1f8be | ||
|
|
ae87728770 | ||
|
|
28c8ba70c1 | ||
|
|
486324ecab | ||
|
|
6892ac13e8 | ||
|
|
340ad093a6 | ||
|
|
0fe09cd1e4 | ||
|
|
da4702851f | ||
|
|
09fba72d58 | ||
|
|
d17c90edf7 | ||
|
|
7966592fae | ||
|
|
6efe4e1753 | ||
|
|
536c4d45c1 | ||
|
|
a02f88fe7c | ||
|
|
d9be6ab806 | ||
|
|
290598429a | ||
|
|
92e72959c3 | ||
|
|
776f014dbe | ||
|
|
c01bc784b9 | ||
|
|
abcd86a294 | ||
|
|
451f83ba51 | ||
|
|
b439f40fe2 | ||
|
|
968166b06d | ||
|
|
88293909f4 | ||
|
|
9b6c48631d | ||
|
|
0ed98cbfac | ||
|
|
7dde7cc743 | ||
|
|
755627f12d | ||
|
|
f8004d7096 | ||
|
|
0418f51ef2 | ||
|
|
054e0af071 | ||
|
|
907c3374c3 | ||
|
|
b578240993 | ||
|
|
f83ee97439 | ||
|
|
19aea85184 | ||
|
|
1ba0a117e7 | ||
|
|
b510b9d337 | ||
|
|
b608e11965 | ||
|
|
e68b3a2f32 | ||
|
|
f7b119ffe1 | ||
|
|
a4cec95db1 | ||
|
|
84c4fa197b | ||
|
|
eac722cf59 | ||
|
|
7439a326a6 | ||
|
|
5ca1c0747f | ||
|
|
466ca38dfa | ||
|
|
93b0839036 | ||
|
|
e068cbc103 | ||
|
|
5c809e5fbf | ||
|
|
3933bf49cf | ||
|
|
7065ba4857 | ||
|
|
ebff83018e | ||
|
|
9ce9167e3c | ||
|
|
993eff1d3d | ||
|
|
7be983ec00 | ||
|
|
18e8d6ce06 | ||
|
|
b7ba0d4327 | ||
|
|
825201f4f2 | ||
|
|
9a05c68ce7 | ||
|
|
d8dccf2500 | ||
|
|
b416aa640f | ||
|
|
4ebf594b3b | ||
|
|
8a83024962 | ||
|
|
bdc1136b96 | ||
|
|
da78dea98f | ||
|
|
b83d93abc2 | ||
|
|
36f843bc6e | ||
|
|
15c87e02e9 | ||
|
|
00923eac7c | ||
|
|
a72ac8294c | ||
|
|
4f03bf754d | ||
|
|
78b3ec4b10 | ||
|
|
ef1a514785 | ||
|
|
6635876e7e | ||
|
|
5645f90abe | ||
|
|
b96cd4d18b | ||
|
|
ad8a2e2cb9 | ||
|
|
fa438e5113 | ||
|
|
8641494809 | ||
|
|
5449e90b34 | ||
|
|
1cd664ad85 | ||
|
|
e680022b1f | ||
|
|
67c2ce2174 | ||
|
|
596e700b60 | ||
|
|
4a53b6e538 | ||
|
|
687f4bb3bb | ||
|
|
473799cb62 | ||
|
|
3dc22a9fd5 | ||
|
|
f54b655606 | ||
|
|
d2e868ea2b | ||
|
|
3fc649359a | ||
|
|
1512ac11da | ||
|
|
5039cc7bb2 | ||
|
|
5360a7b4ce | ||
|
|
2957a31f40 | ||
|
|
8c11df52bf | ||
|
|
2b7ffcd48f | ||
|
|
7980a9033e | ||
|
|
125ddfa513 | ||
|
|
636e929607 | ||
|
|
22c792dc46 | ||
|
|
95af1815c8 | ||
|
|
d707c5ac95 | ||
|
|
5c9192e5e6 | ||
|
|
72b5584042 | ||
|
|
f9045b5352 | ||
|
|
f87fe92bc8 |
13
.github/FUNDING.yml
vendored
Normal file
13
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: ['https://www.amazon.com/hz/wishlist/ls/8WPVFLQQDPTA']
|
||||
# Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
@@ -33,8 +33,14 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
|
||||
BD500
|
||||
);
|
||||
|
||||
ReflectableEnum(Processor,
|
||||
MOS6502,
|
||||
WDC65816
|
||||
);
|
||||
|
||||
ROM rom = ROM::BASIC11;
|
||||
DiskInterface disk_interface = DiskInterface::None;
|
||||
Processor processor = Processor::MOS6502;
|
||||
std::string loading_command;
|
||||
bool should_start_jasmin = false;
|
||||
|
||||
@@ -42,8 +48,10 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
|
||||
if(needs_declare()) {
|
||||
DeclareField(rom);
|
||||
DeclareField(disk_interface);
|
||||
DeclareField(processor);
|
||||
AnnounceEnum(ROM);
|
||||
AnnounceEnum(DiskInterface);
|
||||
AnnounceEnum(Processor);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -107,7 +107,10 @@ static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform::
|
||||
Format("dmk", result.disks, Disk::DiskImageHolder<Storage::Disk::DMK>, TargetPlatform::MSX) // DMK
|
||||
Format("do", result.disks, Disk::DiskImageHolder<Storage::Disk::AppleDSK>, TargetPlatform::DiskII) // DO
|
||||
Format("dsd", result.disks, Disk::DiskImageHolder<Storage::Disk::SSD>, TargetPlatform::Acorn) // DSD
|
||||
Format("dsk", result.disks, Disk::DiskImageHolder<Storage::Disk::CPCDSK>, TargetPlatform::AmstradCPC) // DSK (Amstrad CPC)
|
||||
Format( "dsk",
|
||||
result.disks,
|
||||
Disk::DiskImageHolder<Storage::Disk::CPCDSK>,
|
||||
TargetPlatform::AmstradCPC | TargetPlatform::Oric) // DSK (Amstrad CPC)
|
||||
Format("dsk", result.disks, Disk::DiskImageHolder<Storage::Disk::AppleDSK>, TargetPlatform::DiskII) // DSK (Apple II)
|
||||
Format("dsk", result.disks, Disk::DiskImageHolder<Storage::Disk::MacintoshIMG>, TargetPlatform::Macintosh) // DSK (Macintosh, floppy disk)
|
||||
Format("dsk", result.mass_storage_devices, MassStorage::HFV, TargetPlatform::Macintosh) // DSK (Macintosh, hard disk)
|
||||
|
||||
@@ -804,38 +804,62 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||
m6502_.run_for(cycles);
|
||||
}
|
||||
|
||||
void reset_all_keys(Inputs::Keyboard *) final {
|
||||
void reset_all_keys() final {
|
||||
open_apple_is_pressed_ = closed_apple_is_pressed_ = key_is_down_ = false;
|
||||
}
|
||||
|
||||
bool prefers_logical_input() final {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool set_key_pressed(Key key, char value, bool is_pressed) final {
|
||||
// If no ASCII value is supplied, look for a few special cases.
|
||||
switch(key) {
|
||||
default: break;
|
||||
case Key::F12:
|
||||
m6502_.set_reset_line(is_pressed);
|
||||
return true;
|
||||
case Key::Left: value = 0x08; break;
|
||||
case Key::Right: value = 0x15; break;
|
||||
case Key::Down: value = 0x0a; break;
|
||||
case Key::Up: value = 0x0b; break;
|
||||
case Key::Backspace: value = 0x7f; break;
|
||||
case Key::Enter: value = 0x0d; break;
|
||||
case Key::Tab: value = '\t'; break;
|
||||
case Key::Escape: value = 0x1b; break;
|
||||
|
||||
case Key::LeftOption:
|
||||
case Key::RightMeta:
|
||||
open_apple_is_pressed_ = is_pressed;
|
||||
return true;
|
||||
|
||||
case Key::RightOption:
|
||||
case Key::LeftMeta:
|
||||
closed_apple_is_pressed_ = is_pressed;
|
||||
return true;
|
||||
}
|
||||
|
||||
// If no ASCII value is supplied, look for a few special cases.
|
||||
if(!value) {
|
||||
switch(key) {
|
||||
case Key::Left: value = 0x08; break;
|
||||
case Key::Right: value = 0x15; break;
|
||||
case Key::Down: value = 0x0a; break;
|
||||
case Key::Up: value = 0x0b; break;
|
||||
case Key::Backspace: value = 0x7f; break;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
case Key::F1: case Key::F2: case Key::F3: case Key::F4:
|
||||
case Key::F5: case Key::F6: case Key::F7: case Key::F8:
|
||||
case Key::F9: case Key::F10: case Key::F11: case Key::F12:
|
||||
case Key::PrintScreen:
|
||||
case Key::ScrollLock:
|
||||
case Key::Pause:
|
||||
case Key::Insert:
|
||||
case Key::Home:
|
||||
case Key::PageUp:
|
||||
case Key::PageDown:
|
||||
case Key::End:
|
||||
// Accept a bunch non-symbolic other keys, as
|
||||
// reset, in the hope that the user can find
|
||||
// at least one usable key.
|
||||
m6502_.set_reset_line(is_pressed);
|
||||
return true;
|
||||
|
||||
// Prior to the IIe, the keyboard could produce uppercase only.
|
||||
if(!is_iie()) value = char(toupper(value));
|
||||
default:
|
||||
if(!value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prior to the IIe, the keyboard could produce uppercase only.
|
||||
if(!is_iie()) value = char(toupper(value));
|
||||
break;
|
||||
}
|
||||
|
||||
if(is_pressed) {
|
||||
keyboard_input_ = uint8_t(value | 0x80);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -32,6 +32,11 @@ struct KeyActions {
|
||||
Instructs that all keys should now be treated as released.
|
||||
*/
|
||||
virtual void clear_all_keys() {}
|
||||
|
||||
/*!
|
||||
Indicates whether a machine most naturally accepts logical rather than physical input.
|
||||
*/
|
||||
virtual bool prefers_logical_input() { return false; }
|
||||
};
|
||||
|
||||
/*!
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#include "../Utility/MemoryFuzzer.hpp"
|
||||
#include "../Utility/StringSerialiser.hpp"
|
||||
|
||||
#include "../../Processors/6502/6502.hpp"
|
||||
#include "../../Processors/6502Esque/6502Selector.hpp"
|
||||
#include "../../Components/6522/6522.hpp"
|
||||
#include "../../Components/AY38910/AY38910.hpp"
|
||||
#include "../../Components/DiskII/DiskII.hpp"
|
||||
@@ -41,6 +41,7 @@
|
||||
namespace Oric {
|
||||
|
||||
using DiskInterface = Analyser::Static::Oric::Target::DiskInterface;
|
||||
using Processor = Analyser::Static::Oric::Target::Processor;
|
||||
using AY = GI::AY38910::AY38910<false>;
|
||||
using Speaker = Outputs::Speaker::LowpassSpeaker<AY>;
|
||||
|
||||
@@ -205,7 +206,7 @@ class VIAPortHandler: public MOS::MOS6522::IRQDelegatePortHandler {
|
||||
Keyboard &keyboard_;
|
||||
};
|
||||
|
||||
template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class ConcreteMachine:
|
||||
template <Analyser::Static::Oric::Target::DiskInterface disk_interface, CPU::MOS6502Esque::Type processor_type> class ConcreteMachine:
|
||||
public MachineTypes::TimedMachine,
|
||||
public MachineTypes::ScanProducer,
|
||||
public MachineTypes::AudioProducer,
|
||||
@@ -424,8 +425,8 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
||||
!tape_player_.get_tape()->is_at_end()) {
|
||||
|
||||
uint8_t next_byte = tape_player_.get_next_byte(!ram_[tape_speed_address_]);
|
||||
m6502_.set_value_of_register(CPU::MOS6502::A, next_byte);
|
||||
m6502_.set_value_of_register(CPU::MOS6502::Flags, next_byte ? 0 : CPU::MOS6502::Flag::Zero);
|
||||
m6502_.set_value_of_register(CPU::MOS6502Esque::A, next_byte);
|
||||
m6502_.set_value_of_register(CPU::MOS6502Esque::Flags, next_byte ? 0 : CPU::MOS6502::Flag::Zero);
|
||||
*value = 0x60; // i.e. RTS
|
||||
}
|
||||
} else {
|
||||
@@ -656,7 +657,7 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
||||
const uint16_t basic_invisible_ram_top_ = 0xffff;
|
||||
const uint16_t basic_visible_ram_top_ = 0xbfff;
|
||||
|
||||
CPU::MOS6502::Processor<CPU::MOS6502::Personality::P6502, ConcreteMachine, false> m6502_;
|
||||
CPU::MOS6502Esque::Processor<processor_type, ConcreteMachine, false> m6502_;
|
||||
|
||||
// RAM and ROM
|
||||
std::vector<uint8_t> rom_, disk_rom_;
|
||||
@@ -759,13 +760,23 @@ using namespace Oric;
|
||||
|
||||
Machine *Machine::Oric(const Analyser::Static::Target *target_hint, const ROMMachine::ROMFetcher &rom_fetcher) {
|
||||
auto *const oric_target = dynamic_cast<const Analyser::Static::Oric::Target *>(target_hint);
|
||||
switch(oric_target->disk_interface) {
|
||||
default: return new ConcreteMachine<DiskInterface::None>(*oric_target, rom_fetcher);
|
||||
case DiskInterface::Microdisc: return new ConcreteMachine<DiskInterface::Microdisc>(*oric_target, rom_fetcher);
|
||||
case DiskInterface::Pravetz: return new ConcreteMachine<DiskInterface::Pravetz>(*oric_target, rom_fetcher);
|
||||
case DiskInterface::Jasmin: return new ConcreteMachine<DiskInterface::Jasmin>(*oric_target, rom_fetcher);
|
||||
case DiskInterface::BD500: return new ConcreteMachine<DiskInterface::BD500>(*oric_target, rom_fetcher);
|
||||
|
||||
#define DiskInterfaceSwitch(processor) \
|
||||
switch(oric_target->disk_interface) { \
|
||||
default: return new ConcreteMachine<DiskInterface::None, processor>(*oric_target, rom_fetcher); \
|
||||
case DiskInterface::Microdisc: return new ConcreteMachine<DiskInterface::Microdisc, processor>(*oric_target, rom_fetcher); \
|
||||
case DiskInterface::Pravetz: return new ConcreteMachine<DiskInterface::Pravetz, processor>(*oric_target, rom_fetcher); \
|
||||
case DiskInterface::Jasmin: return new ConcreteMachine<DiskInterface::Jasmin, processor>(*oric_target, rom_fetcher); \
|
||||
case DiskInterface::BD500: return new ConcreteMachine<DiskInterface::BD500, processor>(*oric_target, rom_fetcher); \
|
||||
}
|
||||
|
||||
switch(oric_target->processor) {
|
||||
case Processor::WDC65816: DiskInterfaceSwitch(CPU::MOS6502Esque::Type::TWDC65816);
|
||||
case Processor::MOS6502: DiskInterfaceSwitch(CPU::MOS6502Esque::Type::T6502);
|
||||
}
|
||||
|
||||
#undef DiskInterfaceSwitch
|
||||
|
||||
}
|
||||
|
||||
Machine::~Machine() {}
|
||||
|
||||
@@ -210,6 +210,12 @@
|
||||
4B4B1A3D200198CA00A0F866 /* KonamiSCC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4B1A3A200198C900A0F866 /* KonamiSCC.cpp */; };
|
||||
4B4DC8211D2C2425003C5BF8 /* Vic20.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DC81F1D2C2425003C5BF8 /* Vic20.cpp */; };
|
||||
4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DC8291D2C27A4003C5BF8 /* SerialBus.cpp */; };
|
||||
4B4DEC06252BFA56004583AC /* 65816Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DEC05252BFA56004583AC /* 65816Base.cpp */; };
|
||||
4B4DEC07252BFA56004583AC /* 65816Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DEC05252BFA56004583AC /* 65816Base.cpp */; };
|
||||
4B4DEC08252BFA56004583AC /* 65816Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DEC05252BFA56004583AC /* 65816Base.cpp */; };
|
||||
4B4F47652533EA64004245B8 /* suite-a.prg in Resources */ = {isa = PBXBuildFile; fileRef = 4B4F475E2533EA64004245B8 /* suite-a.prg */; };
|
||||
4B4F477C253530B7004245B8 /* Jeek816Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4F477B253530B7004245B8 /* Jeek816Tests.swift */; };
|
||||
4B4F478A25367EDC004245B8 /* 65816AddressingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4F478925367EDC004245B8 /* 65816AddressingTests.swift */; };
|
||||
4B50AF80242817F40099BBD7 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B50AF7F242817F40099BBD7 /* QuartzCore.framework */; };
|
||||
4B54C0BC1F8D8E790050900F /* KeyboardMachine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B54C0BB1F8D8E790050900F /* KeyboardMachine.cpp */; };
|
||||
4B54C0BF1F8D8F450050900F /* Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B54C0BD1F8D8F450050900F /* Keyboard.cpp */; };
|
||||
@@ -840,6 +846,7 @@
|
||||
4BDDBA991EF3451200347E61 /* Z80MachineCycleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BDDBA981EF3451200347E61 /* Z80MachineCycleTests.swift */; };
|
||||
4BE0A3EE237BB170002AB46F /* ST.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE0A3EC237BB170002AB46F /* ST.cpp */; };
|
||||
4BE0A3EF237BB170002AB46F /* ST.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE0A3EC237BB170002AB46F /* ST.cpp */; };
|
||||
4BE211DE253E4E4800435408 /* 65C02_no_Rockwell_test.bin in Resources */ = {isa = PBXBuildFile; fileRef = 4BE211DD253E4E4800435408 /* 65C02_no_Rockwell_test.bin */; };
|
||||
4BE34438238389E10058E78F /* AtariSTVideoTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BE34437238389E10058E78F /* AtariSTVideoTests.mm */; };
|
||||
4BE76CF922641ED400ACD6FA /* QLTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BE76CF822641ED300ACD6FA /* QLTests.mm */; };
|
||||
4BE90FFD22D5864800FB464D /* MacintoshVideoTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BE90FFC22D5864800FB464D /* MacintoshVideoTests.mm */; };
|
||||
@@ -861,6 +868,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 +1126,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>"; };
|
||||
@@ -1752,6 +1769,7 @@
|
||||
4BDDBA981EF3451200347E61 /* Z80MachineCycleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Z80MachineCycleTests.swift; sourceTree = "<group>"; };
|
||||
4BE0A3EC237BB170002AB46F /* ST.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ST.cpp; sourceTree = "<group>"; };
|
||||
4BE0A3ED237BB170002AB46F /* ST.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ST.hpp; sourceTree = "<group>"; };
|
||||
4BE211DD253E4E4800435408 /* 65C02_no_Rockwell_test.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; name = 65C02_no_Rockwell_test.bin; path = "Klaus Dormann/65C02_no_Rockwell_test.bin"; sourceTree = "<group>"; };
|
||||
4BE3231220532443006EF799 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = "<group>"; };
|
||||
4BE32313205327D7006EF799 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = "<group>"; };
|
||||
4BE32314205328FF006EF799 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = "<group>"; };
|
||||
@@ -1788,6 +1806,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>"; };
|
||||
@@ -1975,11 +1996,13 @@
|
||||
4B44EBF81DC9898E00A7820C /* BCDTEST_beeb */,
|
||||
4B98A1CD1FFADEC400ADF63B /* MSX ROMs */,
|
||||
4B018B88211930DE002A3937 /* 65C02_extended_opcodes_test.bin */,
|
||||
4BE211DD253E4E4800435408 /* 65C02_no_Rockwell_test.bin */,
|
||||
4B44EBF61DC9883B00A7820C /* 6502_functional_test.bin */,
|
||||
4B44EBF41DC987AE00A7820C /* AllSuiteA.bin */,
|
||||
4B9F11CB22729B3500701480 /* OPCLOGR2.BIN */,
|
||||
4B2530F2244E6773007980BF /* FM Synthesis */,
|
||||
4BBF49B41ED2881600AB3669 /* FUSE */,
|
||||
4B4F475B2533EA64004245B8 /* jeek816 */,
|
||||
4B670A822401CB8400D4E002 /* Patrik Rak Z80 Tests */,
|
||||
4B9F11C72272375400701480 /* QL Startup */,
|
||||
4B85322B227793CA00F26553 /* TOS Startup */,
|
||||
@@ -2451,6 +2474,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 +3440,6 @@
|
||||
4BB73EB51B587A5100552FC2 /* Clock SignalTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4BC0CB272446BC7B00A79DBB /* OPLTests.mm */,
|
||||
4B85322922778E4200F26553 /* Comparative68000.hpp */,
|
||||
4B90467222C6FA31000E2074 /* TestRunner68000.hpp */,
|
||||
4B97ADC722C6FD9B00A22A41 /* 68000ArithmeticTests.mm */,
|
||||
@@ -3410,6 +3458,7 @@
|
||||
4BE90FFC22D5864800FB464D /* MacintoshVideoTests.mm */,
|
||||
4BA91E1C216D85BA00F79557 /* MasterSystemVDPTests.mm */,
|
||||
4B98A0601FFADCDE00ADF63B /* MSXStaticAnalyserTests.mm */,
|
||||
4BC0CB272446BC7B00A79DBB /* OPLTests.mm */,
|
||||
4B121F9A1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm */,
|
||||
4BD4A8CF1E077FD20020D856 /* PCMTrackTests.mm */,
|
||||
4BE76CF822641ED300ACD6FA /* QLTests.mm */,
|
||||
@@ -3420,11 +3469,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 +3536,8 @@
|
||||
4BFCA1221ECBDCAF00AC40C1 /* AllRAMProcessor.hpp */,
|
||||
4B2C455C1EC9442600FC74DD /* RegisterSizes.hpp */,
|
||||
4B1414561B58879D00E04248 /* 6502 */,
|
||||
4B4DEC15252BFA9C004583AC /* 6502Esque */,
|
||||
4BF8D4CC251C0C9C00BBE21B /* 65816 */,
|
||||
4BFF1D332233778C00838EA1 /* 68000 */,
|
||||
4B77069E1EC9045B0053B588 /* Z80 */,
|
||||
);
|
||||
@@ -3892,6 +3945,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 +4074,7 @@
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 0700;
|
||||
LastUpgradeCheck = 1130;
|
||||
LastUpgradeCheck = 1200;
|
||||
ORGANIZATIONNAME = "Thomas Harte";
|
||||
TargetAttributes = {
|
||||
4B055A691FAE763F0060FFFF = {
|
||||
@@ -4011,7 +4084,7 @@
|
||||
};
|
||||
4BB73E9D1B587A5100552FC2 = {
|
||||
CreatedOnToolsVersion = 7.0;
|
||||
DevelopmentTeam = CP2SKEB3XT;
|
||||
DevelopmentTeam = DV3346VVUN;
|
||||
LastSwiftMigration = 1020;
|
||||
ProvisioningStyle = Automatic;
|
||||
SystemCapabilities = {
|
||||
@@ -4160,6 +4233,7 @@
|
||||
4BB299961B587D8400A49093 /* nmi in Resources */,
|
||||
4BB299241B587D8400A49093 /* cia1ta in Resources */,
|
||||
4BB2990E1B587D8400A49093 /* asoix in Resources */,
|
||||
4BE211DE253E4E4800435408 /* 65C02_no_Rockwell_test.bin in Resources */,
|
||||
4BB299F51B587D8400A49093 /* trap9 in Resources */,
|
||||
4BB299C81B587D8400A49093 /* sbcb(eb) in Resources */,
|
||||
4BB299CF1B587D8400A49093 /* sedn in Resources */,
|
||||
@@ -4239,6 +4313,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 +4543,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 +4565,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 +4826,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 +4849,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 +4918,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 +4975,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 +5016,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 */,
|
||||
@@ -5093,7 +5175,7 @@
|
||||
"$(USER_LIBRARY_DIR)/Frameworks",
|
||||
);
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Debug;
|
||||
@@ -5116,7 +5198,8 @@
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_OPTIMIZATION_LEVEL = 2;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = NDEBUG;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Release;
|
||||
@@ -5144,6 +5227,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;
|
||||
@@ -5169,7 +5253,7 @@
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_PARAMETER = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
@@ -5202,6 +5286,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;
|
||||
@@ -5221,7 +5306,7 @@
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_PARAMETER = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SDKROOT = macosx;
|
||||
@@ -5243,8 +5328,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "Clock Signal/Clock Signal.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
DEVELOPMENT_TEAM = DV3346VVUN;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
@@ -5289,8 +5373,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "Clock Signal/Clock Signal.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
DEVELOPMENT_TEAM = DV3346VVUN;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
@@ -5311,6 +5394,7 @@
|
||||
INFOPLIST_FILE = "Clock Signal/Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
|
||||
MTL_TREAT_WARNINGS_AS_ERRORS = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_CPLUSPLUSFLAGS = (
|
||||
"$(OTHER_CFLAGS)",
|
||||
"-Wreorder",
|
||||
@@ -5332,7 +5416,6 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "Clock Signal/Clock Signal.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_HARDENED_RUNTIME = NO;
|
||||
INFOPLIST_FILE = "Clock SignalTests/Info.plist";
|
||||
@@ -5356,7 +5439,6 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "Clock Signal/Clock Signal.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_HARDENED_RUNTIME = NO;
|
||||
GCC_OPTIMIZATION_LEVEL = 2;
|
||||
@@ -5374,7 +5456,6 @@
|
||||
4BB73ECD1B587A5100552FC2 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = CP2SKEB3XT;
|
||||
INFOPLIST_FILE = "Clock SignalUITests/Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
|
||||
@@ -5389,7 +5470,6 @@
|
||||
4BB73ECE1B587A5100552FC2 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = CP2SKEB3XT;
|
||||
INFOPLIST_FILE = "Clock SignalUITests/Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1130"
|
||||
LastUpgradeVersion = "1200"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -31,7 +31,7 @@
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Release"
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
disableMainThreadChecker = "YES"
|
||||
@@ -58,8 +58,16 @@
|
||||
</CommandLineArgument>
|
||||
<CommandLineArgument
|
||||
argument = ""/Users/thomasharte/Library/Mobile Documents/com~apple~CloudDocs/Desktop/Soft/Macintosh/MusicWorks 0.42.image""
|
||||
isEnabled = "NO">
|
||||
</CommandLineArgument>
|
||||
<CommandLineArgument
|
||||
argument = "/Users/thomasharte/Library/Mobile\ Documents/com\~apple\~CloudDocs/Desktop/Soft/Apple\ II/Keplermatik.dsk"
|
||||
isEnabled = "YES">
|
||||
</CommandLineArgument>
|
||||
<CommandLineArgument
|
||||
argument = "/Users/thomasharte/Library/Mobile\ Documents/com\~apple\~CloudDocs/Desktop/Soft/Apple\ II/WOZs/Prince\ of\ Persia\ side\ A.woz"
|
||||
isEnabled = "NO">
|
||||
</CommandLineArgument>
|
||||
<CommandLineArgument
|
||||
argument = "--volume=0.001"
|
||||
isEnabled = "NO">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1130"
|
||||
LastUpgradeVersion = "1200"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -48,11 +48,6 @@
|
||||
BlueprintName = "Clock SignalTests"
|
||||
ReferencedContainer = "container:Clock Signal.xcodeproj">
|
||||
</BuildableReference>
|
||||
<SkippedTests>
|
||||
<Test
|
||||
Identifier = "ZexallTests">
|
||||
</Test>
|
||||
</SkippedTests>
|
||||
</TestableReference>
|
||||
<TestableReference
|
||||
skipped = "YES">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1130"
|
||||
LastUpgradeVersion = "1200"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -51,15 +51,6 @@
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4BB73E9D1B587A5100552FC2"
|
||||
BuildableName = "Clock Signal.app"
|
||||
BlueprintName = "Clock Signal"
|
||||
ReferencedContainer = "container:Clock Signal.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
|
||||
@@ -88,14 +88,30 @@ class MachineDocument:
|
||||
}
|
||||
|
||||
override func close() {
|
||||
// Close any dangling sheets.
|
||||
//
|
||||
// Be warned: in 11.0 at least, if there are any panels then posting the endSheet request
|
||||
// will defer the close(), and close() will be called again at the end of that animation.
|
||||
//
|
||||
// So: MAKE SURE IT'S SAFE TO ENTER THIS FUNCTION TWICE. Hence the non-assumption here about
|
||||
// any windows still existing.
|
||||
if self.windowControllers.count > 0, let window = self.windowControllers[0].window {
|
||||
for sheet in window.sheets {
|
||||
window.endSheet(sheet)
|
||||
}
|
||||
}
|
||||
|
||||
// Stop the machine, if any.
|
||||
machine?.stop()
|
||||
|
||||
// Dismiss panels.
|
||||
activityPanel?.setIsVisible(false)
|
||||
activityPanel = nil
|
||||
|
||||
optionsPanel?.setIsVisible(false)
|
||||
optionsPanel = nil
|
||||
|
||||
// End the update cycle.
|
||||
actionLock.lock()
|
||||
drawLock.lock()
|
||||
machine = nil
|
||||
@@ -103,6 +119,7 @@ class MachineDocument:
|
||||
actionLock.unlock()
|
||||
drawLock.unlock()
|
||||
|
||||
// Let the document controller do its thing.
|
||||
super.close()
|
||||
}
|
||||
|
||||
@@ -144,35 +161,37 @@ class MachineDocument:
|
||||
private var interactionMode: InteractionMode = .notStarted
|
||||
|
||||
// Attempting to show a sheet before the window is visible (such as when the NIB is loaded) results in
|
||||
// a sheet mysteriously floating on its own. For now, use windowDidUpdate as a proxy to know that the window
|
||||
// is visible, though it's a little premature.
|
||||
// a sheet mysteriously floating on its own. For now, use windowDidUpdate as a proxy to check whether
|
||||
// the window is visible.
|
||||
func windowDidUpdate(_ notification: Notification) {
|
||||
// Grab the regular window title, if it's not already stored.
|
||||
if self.unadornedWindowTitle.count == 0 {
|
||||
self.unadornedWindowTitle = self.windowControllers[0].window!.title
|
||||
}
|
||||
|
||||
// If an interaction mode is not yet in effect, pick the proper one and display the relevant thing.
|
||||
if self.interactionMode == .notStarted {
|
||||
// If a full machine exists, just continue showing it.
|
||||
if self.machine != nil {
|
||||
self.interactionMode = .showingMachine
|
||||
setupMachineOutput()
|
||||
return
|
||||
if self.windowControllers.count > 0, let window = self.windowControllers[0].window, window.isVisible {
|
||||
// Grab the regular window title, if it's not already stored.
|
||||
if self.unadornedWindowTitle.count == 0 {
|
||||
self.unadornedWindowTitle = window.title
|
||||
}
|
||||
|
||||
// If a machine has been picked but is not showing, there must be ROMs missing.
|
||||
if self.machineDescription != nil {
|
||||
self.interactionMode = .showingROMRequester
|
||||
requestRoms()
|
||||
return
|
||||
}
|
||||
// If an interaction mode is not yet in effect, pick the proper one and display the relevant thing.
|
||||
if self.interactionMode == .notStarted {
|
||||
// If a full machine exists, just continue showing it.
|
||||
if self.machine != nil {
|
||||
self.interactionMode = .showingMachine
|
||||
setupMachineOutput()
|
||||
return
|
||||
}
|
||||
|
||||
// If a machine hasn't even been picked yet, show the machine picker.
|
||||
self.interactionMode = .showingMachinePicker
|
||||
Bundle.main.loadNibNamed("MachinePicker", owner: self, topLevelObjects: nil)
|
||||
self.machinePicker?.establishStoredOptions()
|
||||
self.windowControllers[0].window?.beginSheet(self.machinePickerPanel!, completionHandler: nil)
|
||||
// If a machine has been picked but is not showing, there must be ROMs missing.
|
||||
if self.machineDescription != nil {
|
||||
self.interactionMode = .showingROMRequester
|
||||
requestRoms()
|
||||
return
|
||||
}
|
||||
|
||||
// If a machine hasn't even been picked yet, show the machine picker.
|
||||
self.interactionMode = .showingMachinePicker
|
||||
Bundle.main.loadNibNamed("MachinePicker", owner: self, topLevelObjects: nil)
|
||||
self.machinePicker?.establishStoredOptions()
|
||||
window.beginSheet(self.machinePickerPanel!, completionHandler: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="16097" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="17506" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="16097"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17506"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
@@ -17,14 +17,14 @@
|
||||
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" titleVisibility="hidden" id="QvC-M9-y7g">
|
||||
<windowStyleMask key="styleMask" titled="YES" documentModal="YES"/>
|
||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||
<rect key="contentRect" x="196" y="240" width="720" height="205"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="3840" height="2137"/>
|
||||
<rect key="contentRect" x="196" y="240" width="725" height="205"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="900"/>
|
||||
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="720" height="205"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="725" height="205"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="hKn-1l-OSN">
|
||||
<rect key="frame" x="619" y="13" width="87" height="32"/>
|
||||
<rect key="frame" x="631" y="13" width="81" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="Choose" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="MnM-xo-4Qa">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
@@ -37,7 +37,7 @@ DQ
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="JQy-Cj-AOK">
|
||||
<rect key="frame" x="538" y="13" width="82" height="32"/>
|
||||
<rect key="frame" x="558" y="13" width="76" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="sub-rB-Req">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
@@ -50,7 +50,7 @@ Gw
|
||||
</connections>
|
||||
</button>
|
||||
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="9YM-5x-pc0">
|
||||
<rect key="frame" x="20" y="15" width="398" height="32"/>
|
||||
<rect key="frame" x="20" y="14" width="398" height="32"/>
|
||||
<textFieldCell key="cell" allowsUndo="NO" sendsActionOnEndEditing="YES" id="xTm-Oy-oz5">
|
||||
<font key="font" metaFont="system"/>
|
||||
<string key="title">If you use File -> Open... to select a disk, tape or cartridge directly, the emulator will select and configure a machine for you.</string>
|
||||
@@ -59,16 +59,16 @@ Gw
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<tabView translatesAutoresizingMaskIntoConstraints="NO" id="VUb-QG-x7c">
|
||||
<rect key="frame" x="13" y="51" width="694" height="140"/>
|
||||
<rect key="frame" x="13" y="50" width="699" height="141"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<tabViewItems>
|
||||
<tabViewItem label="Apple II" identifier="appleii" id="P59-QG-LOa">
|
||||
<view key="view" id="dHz-Yv-GNq">
|
||||
<rect key="frame" x="10" y="33" width="674" height="94"/>
|
||||
<rect key="frame" x="10" y="33" width="679" height="95"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="V5Z-dX-Ns4">
|
||||
<rect key="frame" x="15" y="73" width="46" height="16"/>
|
||||
<rect key="frame" x="15" y="74" width="46" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Model:" id="qV3-2P-3JW">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -76,7 +76,7 @@ Gw
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="WnO-ef-IC6">
|
||||
<rect key="frame" x="15" y="42" width="96" height="16"/>
|
||||
<rect key="frame" x="15" y="44" width="96" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Disk controller:" id="kbf-rc-Y4M">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -84,10 +84,10 @@ Gw
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="jli-ac-Sij">
|
||||
<rect key="frame" x="65" y="67" width="116" height="25"/>
|
||||
<rect key="frame" x="64" y="68" width="117" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Apple II" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="VBQ-JG-AeM" id="U6V-us-O2F">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<menu key="menu" id="esp-ir-7iH">
|
||||
<items>
|
||||
<menuItem title="Apple II" state="on" id="VBQ-JG-AeM"/>
|
||||
@@ -99,10 +99,10 @@ Gw
|
||||
</popUpButtonCell>
|
||||
</popUpButton>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="LSB-WP-FMi">
|
||||
<rect key="frame" x="115" y="36" width="133" height="25"/>
|
||||
<rect key="frame" x="114" y="38" width="134" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Sixteen Sector" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="16" imageScaling="proportionallyDown" inset="2" selectedItem="QaV-Yr-k9o" id="8BT-RV-2Nm">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<menu key="menu" id="udQ-NK-FG8">
|
||||
<items>
|
||||
<menuItem title="Sixteen Sector" state="on" tag="16" id="QaV-Yr-k9o"/>
|
||||
@@ -130,14 +130,14 @@ Gw
|
||||
</tabViewItem>
|
||||
<tabViewItem label="Amstrad CPC" identifier="cpc" id="JmB-OF-xcM">
|
||||
<view key="view" id="5zS-Nj-Ynx">
|
||||
<rect key="frame" x="10" y="33" width="674" height="94"/>
|
||||
<rect key="frame" x="10" y="33" width="679" height="95"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="00d-sg-Krh">
|
||||
<rect key="frame" x="65" y="67" width="95" height="25"/>
|
||||
<rect key="frame" x="64" y="68" width="96" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="CPC6128" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="6128" imageScaling="proportionallyDown" inset="2" selectedItem="klh-ZE-Agp" id="hVJ-h6-iea">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<menu key="menu" id="r3D-C2-Ruq">
|
||||
<items>
|
||||
<menuItem title="CPC464" tag="464" id="5kZ-XF-RFl"/>
|
||||
@@ -148,7 +148,7 @@ Gw
|
||||
</popUpButtonCell>
|
||||
</popUpButton>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="q9q-sl-J0q">
|
||||
<rect key="frame" x="15" y="73" width="46" height="16"/>
|
||||
<rect key="frame" x="15" y="74" width="46" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Model:" id="Cw3-q5-1bC">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -235,7 +235,7 @@ Gw
|
||||
<rect key="frame" x="65" y="67" width="74" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Plus" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="3" imageScaling="proportionallyDown" inset="2" selectedItem="R6T-hg-rOF" id="1Kb-Q2-BGM">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<menu key="menu" id="ofy-j9-YnU">
|
||||
<items>
|
||||
<menuItem title="512ke" tag="2" id="WCG-6u-ANQ"/>
|
||||
@@ -271,7 +271,7 @@ Gw
|
||||
<rect key="frame" x="69" y="67" width="145" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="European (PAL)" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="xAh-Ch-tby" id="yR4-yv-Lvu">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<menu key="menu" id="BCR-rp-Kpc">
|
||||
<items>
|
||||
<menuItem title="European (PAL)" state="on" id="xAh-Ch-tby"/>
|
||||
@@ -320,7 +320,7 @@ Gw
|
||||
<rect key="frame" x="65" y="67" width="106" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Oric-1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="jGN-1a-biF" id="Jll-EJ-cMr">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<menu key="menu" id="E9d-fH-Eak">
|
||||
<items>
|
||||
<menuItem title="Oric-1" state="on" id="jGN-1a-biF"/>
|
||||
@@ -334,7 +334,7 @@ Gw
|
||||
<rect key="frame" x="111" y="36" width="129" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="None" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="XhK-Jh-oTW" id="aYb-m1-H9X">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<menu key="menu" id="hy3-Li-nlW">
|
||||
<items>
|
||||
<menuItem title="None" state="on" id="XhK-Jh-oTW"/>
|
||||
@@ -379,7 +379,7 @@ Gw
|
||||
<rect key="frame" x="69" y="67" width="145" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="European (PAL)" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="45i-0n-gau" id="yi7-eo-I0q">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<menu key="menu" id="uCQ-9l-eBb">
|
||||
<items>
|
||||
<menuItem title="European (PAL)" state="on" id="45i-0n-gau"/>
|
||||
@@ -395,7 +395,7 @@ Gw
|
||||
<rect key="frame" x="106" y="36" width="115" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Unexpanded" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="fOl-8Q-fsA" id="rH0-7T-pJE">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<menu key="menu" id="e9J-Ie-PjH">
|
||||
<items>
|
||||
<menuItem title="Unexpanded" state="on" id="fOl-8Q-fsA"/>
|
||||
@@ -456,7 +456,7 @@ Gw
|
||||
<rect key="frame" x="106" y="67" width="115" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Unexpanded" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="4Sa-jR-xOd" id="B8M-do-Yod">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<menu key="menu" id="Of4-DY-mE5">
|
||||
<items>
|
||||
<menuItem title="Unexpanded" state="on" id="4Sa-jR-xOd"/>
|
||||
@@ -504,7 +504,7 @@ Gw
|
||||
<rect key="frame" x="106" y="67" width="115" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Unexpanded" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="7QC-Ij-hES" id="d3W-Gl-3Mf">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<menu key="menu" id="mua-Lp-9wl">
|
||||
<items>
|
||||
<menuItem title="Unexpanded" state="on" id="7QC-Ij-hES"/>
|
||||
|
||||
@@ -51,7 +51,11 @@ class MachinePicker: NSObject {
|
||||
|
||||
// Machine type
|
||||
if let machineIdentifier = standardUserDefaults.string(forKey: "new.machine") {
|
||||
machineSelector?.selectTabViewItem(withIdentifier: machineIdentifier as Any)
|
||||
// If I've changed my mind about visible tabs between versions, there may not be one that corresponds
|
||||
// to the stored identifier. Make sure not to raise an NSRangeException in that scenario.
|
||||
if let index = machineSelector?.indexOfTabViewItem(withIdentifier: machineIdentifier as Any), index != NSNotFound {
|
||||
machineSelector?.selectTabViewItem(at: index)
|
||||
}
|
||||
}
|
||||
|
||||
// Apple II settings
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14868" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="17506" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14868"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17506"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
@@ -37,7 +37,7 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="nvl-dm-8bK">
|
||||
<rect key="frame" x="384" y="13" width="82" height="32"/>
|
||||
<rect key="frame" x="391" y="13" width="76" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="oS1-Pk-LsO">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
|
||||
@@ -332,12 +332,12 @@ half3 convertRed8Green8Blue8(SourceInterpolator vert, texture2d<half> texture) {
|
||||
|
||||
half3 convertRed4Green4Blue4(SourceInterpolator vert, texture2d<ushort> texture) {
|
||||
const auto sample = texture.sample(standardSampler, vert.textureCoordinates).rg;
|
||||
return clamp(half3(sample.r&15, (sample.g >> 4)&15, sample.g&15), half(0.0f), half(1.0f));
|
||||
return half3(sample.r&15, (sample.g >> 4)&15, sample.g&15) / 15.0f;
|
||||
}
|
||||
|
||||
half3 convertRed2Green2Blue2(SourceInterpolator vert, texture2d<ushort> texture) {
|
||||
const auto sample = texture.sample(standardSampler, vert.textureCoordinates).r;
|
||||
return clamp(half3((sample >> 4)&3, (sample >> 2)&3, sample&3), half(0.0f), half(1.0f));
|
||||
return half3((sample >> 4)&3, (sample >> 2)&3, sample&3) / 3.0f;
|
||||
}
|
||||
|
||||
half3 convertRed1Green1Blue1(SourceInterpolator vert, texture2d<ushort> texture) {
|
||||
|
||||
@@ -14,38 +14,38 @@ class MOS6502InterruptTests: XCTestCase {
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
|
||||
// create a machine full of NOPs
|
||||
machine = CSTestMachine6502(is65C02: false)
|
||||
// Create a machine full of NOPs.
|
||||
machine = CSTestMachine6502(processor: .processor6502)
|
||||
for c in 0...65535 {
|
||||
machine.setValue(0xea, forAddress: UInt16(c))
|
||||
machine.setValue(0xea, forAddress: UInt32(c))
|
||||
}
|
||||
|
||||
// set the IRQ vector to be 0x1234
|
||||
// Set the IRQ vector to 0x1234.
|
||||
machine.setValue(0x34, forAddress: 0xfffe)
|
||||
machine.setValue(0x12, forAddress: 0xffff)
|
||||
|
||||
// add a CLI
|
||||
// Add a CLI.
|
||||
machine.setValue(0x58, forAddress: 0x4000)
|
||||
|
||||
// pick things off at 0x4000
|
||||
machine.setValue(0x4000, for: CSTestMachine6502Register.programCounter)
|
||||
// Begin at 0x4000.
|
||||
machine.setValue(0x4000, for: .programCounter)
|
||||
}
|
||||
|
||||
func testIRQLine() {
|
||||
// run for six cycles; check that no interrupt has occurred
|
||||
// Run for six cycles; check that no interrupt has occurred.
|
||||
machine.runForNumber(ofCycles: 6)
|
||||
XCTAssert(machine.value(for: .programCounter) == 0x4003, "No interrupt should have occurred with line low")
|
||||
XCTAssertEqual(machine.value(for: .programCounter), 0x4003, "No interrupt should have occurred with line low")
|
||||
|
||||
// enable the interrupt line, check that it was too late
|
||||
machine.irqLine = true
|
||||
machine.runForNumber(ofCycles: 2)
|
||||
XCTAssert(machine.value(for: .programCounter) == 0x4004, "No interrupt should have occurred from interrupt raised between instructions")
|
||||
XCTAssertEqual(machine.value(for: .programCounter), 0x4004, "No interrupt should have occurred from interrupt raised between instructions")
|
||||
|
||||
// run for a further 7 cycles, confirm that the IRQ vector was jumped to
|
||||
machine.runForNumber(ofCycles: 6)
|
||||
XCTAssert(machine.value(for: .programCounter) != 0x1234, "Interrupt routine should not yet have begun")
|
||||
XCTAssertNotEqual(machine.value(for: .programCounter), 0x1234, "Interrupt routine should not yet have begun")
|
||||
machine.runForNumber(ofCycles: 1)
|
||||
XCTAssert(machine.value(for: .programCounter) == 0x1234, "Interrupt routine should just have begun")
|
||||
XCTAssertEqual(machine.value(for: .programCounter), 0x1234, "Interrupt routine should just have begun")
|
||||
}
|
||||
|
||||
func testIFlagSet() {
|
||||
@@ -53,8 +53,8 @@ class MOS6502InterruptTests: XCTestCase {
|
||||
machine.irqLine = true
|
||||
machine.runForNumber(ofCycles: 11)
|
||||
|
||||
XCTAssert(machine.value(for: .programCounter) == 0x1234, "Interrupt routine should just have begun")
|
||||
XCTAssert(machine.value(for: .flags) & 0x04 == 0x04, "Interrupt status flag should be set")
|
||||
XCTAssertEqual(machine.value(for: .programCounter), 0x1234, "Interrupt routine should just have begun")
|
||||
XCTAssertEqual(machine.value(for: .flags) & 0x04, 0x04, "Interrupt status flag should be set")
|
||||
}
|
||||
|
||||
func testCLISEIFlagClear() {
|
||||
@@ -64,12 +64,13 @@ class MOS6502InterruptTests: XCTestCase {
|
||||
|
||||
// run for four cycles; the CLI and SEI should have been performed
|
||||
machine.runForNumber(ofCycles: 4)
|
||||
XCTAssert(machine.value(for: .programCounter) == 0x4002, "CLI/SEI pair should have been performed in their entirety")
|
||||
XCTAssertEqual(machine.value(for: .programCounter), 0x4002, "CLI/SEI pair should have been performed in their entirety")
|
||||
|
||||
// run for seven more cycles
|
||||
machine.runForNumber(ofCycles: 7)
|
||||
|
||||
// interrupt should have taken place despite SEI
|
||||
XCTAssert(machine.value(for: .programCounter) == 0x1234, "Interrupt routine should just have begun")
|
||||
XCTAssertEqual(machine.value(for: .programCounter), 0x1234, "Interrupt routine should just have begun")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import XCTest
|
||||
class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
|
||||
private var endTime: UInt32 = 0
|
||||
private let machine = CSTestMachine6502(is65C02: false)
|
||||
private let machine = CSTestMachine6502(processor: .processor6502)
|
||||
|
||||
func testImplied() {
|
||||
let code: [UInt8] = [
|
||||
@@ -22,7 +22,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
0x18, // [2] CLC
|
||||
0x2a, // [2] ROL A
|
||||
]
|
||||
self.runTest(code, expectedRunLength: 10)
|
||||
runTest(code, expectedRunLength: 10)
|
||||
}
|
||||
|
||||
func testLDA() {
|
||||
@@ -40,7 +40,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
0xb1, 0x00, // [5] LDA ($00), y (no wrap)
|
||||
0xb1, 0x02, // [6] LDA ($01), y (wrap)
|
||||
]
|
||||
self.runTest(code, expectedRunLength: 48)
|
||||
runTest(code, expectedRunLength: 48)
|
||||
}
|
||||
|
||||
func testBIT() {
|
||||
@@ -48,7 +48,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
0x24, 0x2a, // [3] BIT $2a
|
||||
0x2c, 0x2a, 0x2b, // [4] BIT $2b2a
|
||||
]
|
||||
self.runTest(code, expectedRunLength: 7)
|
||||
runTest(code, expectedRunLength: 7)
|
||||
}
|
||||
|
||||
func testSTA() {
|
||||
@@ -64,7 +64,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
0x91, 0x00, // [6] STA ($00), y (no wrap)
|
||||
0x91, 0x02, // [6] STA ($01), y (wrap)
|
||||
]
|
||||
self.runTest(code, expectedRunLength: 49)
|
||||
runTest(code, expectedRunLength: 49)
|
||||
}
|
||||
|
||||
func testINC() {
|
||||
@@ -75,7 +75,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
0xfe, 0x00, 0x00, // [7] INC $0000, x (no wrap)
|
||||
0xfe, 0x02, 0x00, // [7] INC $0002, x (wrap)
|
||||
]
|
||||
self.runTest(code, expectedRunLength: 31)
|
||||
runTest(code, expectedRunLength: 31)
|
||||
}
|
||||
|
||||
func testJSR() {
|
||||
@@ -85,7 +85,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
0x60, // [6] RTS
|
||||
]
|
||||
machine.addTrapAddress(0x0203)
|
||||
self.runTest(code, expectedRunLength: 12)
|
||||
runTest(code, expectedRunLength: 12)
|
||||
}
|
||||
|
||||
func testJMP() {
|
||||
@@ -94,7 +94,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x4c, 0x0b, 0x02, // [3] JMP 020b
|
||||
]
|
||||
self.runTest(code, expectedRunLength: 8)
|
||||
runTest(code, expectedRunLength: 8)
|
||||
}
|
||||
|
||||
func testPHAPLA() {
|
||||
@@ -103,7 +103,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
0x48, // [3] PHA
|
||||
0x68, // [4] PLA
|
||||
]
|
||||
self.runTest(code, expectedRunLength: 10)
|
||||
runTest(code, expectedRunLength: 10)
|
||||
}
|
||||
|
||||
func testBCS() {
|
||||
@@ -130,7 +130,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
]
|
||||
self.runTest(code, expectedRunLength: 14)
|
||||
runTest(code, expectedRunLength: 14)
|
||||
}
|
||||
|
||||
func testSnippet1() {
|
||||
@@ -138,7 +138,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
0x8d, 0x08, 0x00, // [4] STA $0008
|
||||
0xc6, 0xb4, // [5] DEC $B4
|
||||
]
|
||||
self.runTest(code, expectedRunLength: 9)
|
||||
runTest(code, expectedRunLength: 9)
|
||||
}
|
||||
|
||||
func testSnippet2() {
|
||||
@@ -146,7 +146,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
0x16, 0x16, // [6] ASL $16, x
|
||||
0x46, 0x46, // [5] LSR $46
|
||||
]
|
||||
self.runTest(code, expectedRunLength: 11)
|
||||
runTest(code, expectedRunLength: 11)
|
||||
}
|
||||
|
||||
func testSnippet3() {
|
||||
@@ -174,7 +174,7 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
0x60, // [6] RTS
|
||||
]
|
||||
machine.addTrapAddress(0x0203)
|
||||
self.runTest(code, expectedRunLength: 66)
|
||||
runTest(code, expectedRunLength: 66)
|
||||
}
|
||||
|
||||
func testNOP() {
|
||||
@@ -194,10 +194,10 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
0xe2, 0x00, // [2] NOP #
|
||||
0xf4, 0x00, // [4] NOP zpg, x
|
||||
]
|
||||
self.runTest(code, expectedRunLength: 43)
|
||||
runTest(code, expectedRunLength: 43)
|
||||
}
|
||||
|
||||
func runTest(_ code: [UInt8], expectedRunLength: UInt32) {
|
||||
private func runTest(_ code: [UInt8], expectedRunLength: UInt32) {
|
||||
machine.trapHandler = self
|
||||
|
||||
let immediateCode = Data(code)
|
||||
@@ -213,17 +213,17 @@ class MOS6502TimingTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
machine.setValue(0xff, for: CSTestMachine6502Register.X)
|
||||
machine.setValue(0xfe, for: CSTestMachine6502Register.Y)
|
||||
|
||||
self.endTime = 0
|
||||
while self.endTime == 0 {
|
||||
endTime = 0
|
||||
while endTime == 0 {
|
||||
machine.runForNumber(ofCycles: 10)
|
||||
}
|
||||
|
||||
XCTAssert(self.endTime == expectedRunLength, "Took \(self.endTime) cycles to perform rather than \(expectedRunLength)")
|
||||
XCTAssertEqual(endTime, expectedRunLength, "Took \(endTime) cycles to perform rather than \(expectedRunLength)")
|
||||
}
|
||||
|
||||
func testMachine(_ testMachine: CSTestMachine, didTrapAtAddress address: UInt16) {
|
||||
if self.endTime == 0 {
|
||||
self.endTime = (machine.timestamp / 2) - 1
|
||||
if endTime == 0 {
|
||||
endTime = (machine.timestamp / 2) - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import Foundation
|
||||
|
||||
class MOS6532Tests: XCTestCase {
|
||||
|
||||
fileprivate func with6532(_ action: (MOS6532Bridge) -> ()) {
|
||||
private func with6532(_ action: (MOS6532Bridge) -> ()) {
|
||||
let bridge = MOS6532Bridge()
|
||||
action(bridge)
|
||||
}
|
||||
|
||||
546
OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift
Normal file
546
OSBindings/Mac/Clock SignalTests/65816AddressingTests.swift
Normal file
@@ -0,0 +1,546 @@
|
||||
//
|
||||
// WDC65816AddressingTests.swift
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 13/10/2020.
|
||||
// Copyright 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import XCTest
|
||||
|
||||
// This exactly transcribes the examples given in http://6502.org/tutorials/65c816opcodes.html#5
|
||||
// Quoted text is taken verbatim from that document.
|
||||
class WDC65816AddressingTests: XCTestCase {
|
||||
|
||||
// MARK: - Test Machines
|
||||
|
||||
/// Returns a CSTestMachine6502 that is currently configured in native mode with 16-bit memory and index registers.
|
||||
private func machine16() -> CSTestMachine6502 {
|
||||
let machine = CSTestMachine6502(processor: .processor65816)
|
||||
machine.setValue(0, for: .emulationFlag)
|
||||
machine.setValue(0, for: .flags)
|
||||
return machine
|
||||
}
|
||||
|
||||
/// Returns a CSTestMachine6502 that is currently configured in emulation mode.
|
||||
private func machine8() -> CSTestMachine6502 {
|
||||
return CSTestMachine6502(processor: .processor65816)
|
||||
}
|
||||
|
||||
// MARK: - Tests
|
||||
|
||||
func testAbsoluteJMP() {
|
||||
// "If the K register is $12, then JMP $FFFF jumps to $12FFFF"
|
||||
let machine = machine16()
|
||||
|
||||
machine.setValue(0x12, for: .programBank)
|
||||
|
||||
// JMP $ffff
|
||||
machine.setData(Data([0x4c, 0xff, 0xff]), atAddress: 0x120200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 4)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .programCounter), 0) // i.e. 0xffff + 1
|
||||
XCTAssertEqual(machine.value(for: .programBank), 0x12)
|
||||
}
|
||||
|
||||
func testAbsolute() {
|
||||
// "If the DBR is $12 and the m flag is 0, then LDA $FFFF loads the low byte of
|
||||
// the data from address $12FFFF, and the high byte from address $130000"
|
||||
|
||||
let machine = machine16()
|
||||
|
||||
machine.setValue(0x12, for: .dataBank)
|
||||
|
||||
machine.setValue(0xab, forAddress: 0x12ffff)
|
||||
machine.setValue(0xcd, forAddress: 0x130000)
|
||||
|
||||
// LDA $ffff; NOP
|
||||
machine.setData(Data([0xad, 0xff, 0xff, 0xea]), atAddress: 0x200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 6)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .A), 0xcdab)
|
||||
}
|
||||
|
||||
func testAbsoluteX() {
|
||||
// "If the DBR is $12, the X register is $000A, and the m flag is 0, then
|
||||
// LDA $FFFE,X loads the low byte of the data from address $130008, and
|
||||
// the high byte from address $130009"
|
||||
//
|
||||
// "Note that this is one of the rare instances where emulation mode has
|
||||
// different behavior than the 65C02 or NMOS 6502 ..."
|
||||
|
||||
let machine = machine16()
|
||||
|
||||
machine.setValue(0x12, for: .dataBank)
|
||||
machine.setValue(0x0a, for: .X)
|
||||
|
||||
machine.setValue(0xab, forAddress: 0x130008)
|
||||
machine.setValue(0xcd, forAddress: 0x130009)
|
||||
|
||||
// LDA $fffe, x; NOP
|
||||
machine.setData(Data([0xbd, 0xfe, 0xff, 0xea]), atAddress: 0x200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 7)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .A), 0xcdab)
|
||||
}
|
||||
|
||||
func testJMPAbsoluteIndirect() {
|
||||
// "If the K register is $12 and
|
||||
// * $000000 contains $34
|
||||
// * $00FFFF contains $56
|
||||
// then JMP ($FFFF) jumps to $123456"
|
||||
|
||||
let machine = machine16()
|
||||
|
||||
machine.setValue(0x12, for: .programBank)
|
||||
machine.setValue(0x0a, for: .X)
|
||||
|
||||
machine.setValue(0x34, forAddress: 0x0000)
|
||||
machine.setValue(0x56, forAddress: 0xffff)
|
||||
|
||||
// JMP ($ffff); NOP
|
||||
machine.setData(Data([0x6c, 0xff, 0xff, 0xea]), atAddress: 0x120200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 6)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .programCounter), 0x3456 + 1)
|
||||
XCTAssertEqual(machine.value(for: .programBank), 0x12)
|
||||
}
|
||||
|
||||
func testIndirectAbsoluteX() {
|
||||
// "If the K register is $12, the X register is $000A, and
|
||||
// * $120008 contains $56
|
||||
// * $120009 contains $34
|
||||
//then JMP ($FFFE,X) jumps to $123456"
|
||||
|
||||
let machine = machine16()
|
||||
|
||||
machine.setValue(0x12, for: .programBank)
|
||||
machine.setValue(0x0a, for: .X)
|
||||
machine.setValue(0x56, forAddress: 0x120008)
|
||||
machine.setValue(0x34, forAddress: 0x120009)
|
||||
|
||||
// JMP ($fffe, x); NOP
|
||||
machine.setData(Data([0x7c, 0xfe, 0xff, 0xea]), atAddress: 0x120200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 7)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .programCounter), 0x3456 + 1)
|
||||
XCTAssertEqual(machine.value(for: .programBank), 0x12)
|
||||
}
|
||||
|
||||
func testDirect8() {
|
||||
// "If the D register is $FF00 and the e flag is 1 (note that
|
||||
// this means the m flag must be 1), then LDA $FF loads the low
|
||||
// byte of the data from address $00FFFF"
|
||||
|
||||
let machine = machine8()
|
||||
|
||||
machine.setValue(0xff00, for: .direct)
|
||||
|
||||
machine.setValue(0xab, forAddress: 0xffff)
|
||||
|
||||
// LDA $ff; NOP
|
||||
machine.setData(Data([0xa5, 0xff, 0xea]), atAddress: 0x0200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 4)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .A), 0xab)
|
||||
}
|
||||
|
||||
func testDirect16() {
|
||||
// "If the D register is $FF00 and the m flag is 0 (note that
|
||||
// this means the e flag must be 0), then LDA $FF loads the low
|
||||
// byte of the data from address $00FFFF, and the high byte
|
||||
// from address $000000"
|
||||
|
||||
let machine = machine16()
|
||||
|
||||
machine.setValue(0xff00, for: .direct)
|
||||
|
||||
machine.setValue(0xab, forAddress: 0xffff)
|
||||
machine.setValue(0xcd, forAddress: 0x0000)
|
||||
|
||||
// LDA $ff; NOP
|
||||
machine.setData(Data([0xa5, 0xff, 0xea]), atAddress: 0x0200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 5)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .A), 0xcdab)
|
||||
}
|
||||
|
||||
func testDirextX8() {
|
||||
// "If the D register is $FF00, the X register is $000A, and
|
||||
// the e flag is 1 (note that this means the m flag must be 1),
|
||||
// then LDA $FE,X loads the low byte of the data from
|
||||
// address $00FF08"
|
||||
|
||||
let machine = machine8()
|
||||
|
||||
machine.setValue(0xff00, for: .direct)
|
||||
machine.setValue(0x000a, for: .X)
|
||||
|
||||
machine.setValue(0xab, forAddress: 0xff08)
|
||||
|
||||
// LDA $fe, X; NOP
|
||||
machine.setData(Data([0xb5, 0xfe, 0xea]), atAddress: 0x0200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 5)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .A), 0xab)
|
||||
}
|
||||
|
||||
func testDirectX16() {
|
||||
// "If the D register is $FF00, the X register is $000A, and the
|
||||
// m flag is 0 (note that this means the e flag must be 0), then
|
||||
// LDA $FE,X loads the low byte of the data from address $000008,
|
||||
// and the high byte from address $000009"
|
||||
let machine = machine16()
|
||||
|
||||
machine.setValue(0xff00, for: .direct)
|
||||
machine.setValue(0x000a, for: .X)
|
||||
|
||||
machine.setValue(0xab, forAddress: 0x0008)
|
||||
machine.setValue(0xcd, forAddress: 0x0009)
|
||||
|
||||
// LDA $fe, X; NOP
|
||||
machine.setData(Data([0xb5, 0xfe, 0xea]), atAddress: 0x0200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 6)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .A), 0xcdab)
|
||||
}
|
||||
|
||||
func testDirectIndirect8() {
|
||||
// "If the D register is $FF00 and the e flag is 1 (note this means the
|
||||
// m flag must be 1), then for LDA ($FF), the address of the low byte of
|
||||
// the pointer is $00FFFF and the address of the high byte is $00FF00.
|
||||
// Furthermore, if the DBR is $12 and
|
||||
// * $00FF00 contains $FF
|
||||
// * $00FFFF contains $FF
|
||||
// then LDA ($FF) loads the low byte of the data from address $12FFFF."
|
||||
let machine = machine8()
|
||||
|
||||
machine.setValue(0xff00, for: .direct)
|
||||
machine.setValue(0x12, for: .dataBank)
|
||||
|
||||
machine.setValue(0xff, forAddress: 0xff00)
|
||||
machine.setValue(0xff, forAddress: 0xffff)
|
||||
|
||||
machine.setValue(0xab, forAddress: 0x12ffff)
|
||||
|
||||
// LDA ($ff); NOP
|
||||
machine.setData(Data([0xb2, 0xff, 0xea]), atAddress: 0x0200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 6)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .A), 0xab)
|
||||
}
|
||||
|
||||
func testDirectIndirect16() {
|
||||
// "If the D register is $FF00 and the m flag is 0 (note this means the e
|
||||
// flag must be 0), then for LDA ($FF), the address of the low byte of the
|
||||
// pointer is $00FFFF and the address of the high byte is $000000.
|
||||
// Furthermore, if the DBR is $12 and
|
||||
// * $000000 contains $FF
|
||||
// * $00FFFF contains $FF
|
||||
// then LDA ($FF) loads the low byte of the data from address $12FFFF, and
|
||||
// the high byte from $130000."
|
||||
let machine = machine16()
|
||||
|
||||
machine.setValue(0xff00, for: .direct)
|
||||
machine.setValue(0x12, for: .dataBank)
|
||||
|
||||
machine.setValue(0xff, forAddress: 0x0000)
|
||||
machine.setValue(0xff, forAddress: 0xffff)
|
||||
|
||||
machine.setValue(0xab, forAddress: 0x12ffff)
|
||||
machine.setValue(0xcd, forAddress: 0x130000)
|
||||
|
||||
// LDA ($ff); NOP
|
||||
machine.setData(Data([0xb2, 0xff, 0xea]), atAddress: 0x0200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 7)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .A), 0xcdab)
|
||||
}
|
||||
|
||||
func testDirectIndirectLong() {
|
||||
// "If the D register is $FF00 and the m flag is 0, then for LDA [$FE], the
|
||||
// address of the low byte of the pointer is $00FFFE, the address of the middle
|
||||
// byte is $00FFFF, and the address of the high byte is $000000. Furthermore, if
|
||||
// * $000000 contains $12
|
||||
// * $00FFFE contains $FF
|
||||
// * $00FFFF contains $FF
|
||||
// then LDA [$FE] loads the low byte of the data from address $12FFFF, and the
|
||||
// high byte from $130000."
|
||||
let machine = machine16()
|
||||
|
||||
machine.setValue(0xff00, for: .direct)
|
||||
|
||||
machine.setValue(0x12, forAddress: 0x0000)
|
||||
machine.setValue(0xff, forAddress: 0xfffe)
|
||||
machine.setValue(0xff, forAddress: 0xffff)
|
||||
|
||||
machine.setValue(0xab, forAddress: 0x12ffff)
|
||||
machine.setValue(0xcd, forAddress: 0x130000)
|
||||
|
||||
// LDA [$fe]; NOP
|
||||
machine.setData(Data([0xa7, 0xfe, 0xea]), atAddress: 0x0200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 8)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .A), 0xcdab)
|
||||
}
|
||||
|
||||
func testDirectXIndirectX8() {
|
||||
// "If the D register is $FF00, the X register is $000A, and the e flag is 1 (note
|
||||
// that this means the m flag must be 1), then for LDA ($FE,X), the address of the
|
||||
// low byte of the pointer is $00FF08 and the address of the high byte is $00FF09.
|
||||
// Furthermore, if the DBR is $12 and
|
||||
// * $00FF08 contains $FF
|
||||
// * $00FF09 contains $FF
|
||||
// then LDA ($FE,X) loads the low byte of the data from address $12FFFF."
|
||||
let machine = machine8()
|
||||
|
||||
machine.setValue(0xff00, for: .direct)
|
||||
machine.setValue(0x000a, for: .X)
|
||||
machine.setValue(0x12, for: .dataBank)
|
||||
|
||||
machine.setValue(0xff, forAddress: 0xff08)
|
||||
machine.setValue(0xff, forAddress: 0xff09)
|
||||
|
||||
machine.setValue(0xab, forAddress: 0x12ffff)
|
||||
|
||||
// LDA ($fe, x); NOP
|
||||
machine.setData(Data([0xa1, 0xfe, 0xea]), atAddress: 0x0200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 7)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .A), 0xab)
|
||||
}
|
||||
|
||||
func testDirectXIndirect16() {
|
||||
// "If the D register is $FF00, the X register is $000A, and the m flag is 0
|
||||
// (note that this means the e flag must be 0), then for LDA ($FE,X), the address
|
||||
// of the low byte of the pointer is $000008 and the address of the high byte
|
||||
// is $000009. Furthermore, if the DBR is $12 and
|
||||
// * $000008 contains $FF
|
||||
// * $000009 contains $FF
|
||||
// then LDA ($FE,X) loads the low byte of the data from address $12FFFF, and the
|
||||
// high byte from $130000."
|
||||
let machine = machine16()
|
||||
|
||||
machine.setValue(0xff00, for: .direct)
|
||||
machine.setValue(0x000a, for: .X)
|
||||
machine.setValue(0x12, for: .dataBank)
|
||||
|
||||
machine.setValue(0xff, forAddress: 0x0008)
|
||||
machine.setValue(0xff, forAddress: 0x0009)
|
||||
|
||||
machine.setValue(0xab, forAddress: 0x12ffff)
|
||||
machine.setValue(0xcd, forAddress: 0x130000)
|
||||
|
||||
// LDA ($fe, x); NOP
|
||||
machine.setData(Data([0xa1, 0xfe, 0xea]), atAddress: 0x0200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 8)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .A), 0xcdab)
|
||||
}
|
||||
|
||||
func testIndirectY8() {
|
||||
// "If the D register is $FF00 and the e flag is 1 (note that this means the
|
||||
// m flag must be 1), then for LDA ($FF),Y, the address of the low byte of the
|
||||
// pointer is $00FFFF and the address of the high byte is $00FF00.
|
||||
// Furthermore, if the DBR is $12, the Y register is $000A, and
|
||||
// * $00FF00 contains $FF
|
||||
// * $00FFFF contains $FE
|
||||
// then LDA ($FF),Y loads the low byte of the data from address $130008."
|
||||
//
|
||||
// "this is one of the rare instances where emulation mode has
|
||||
// different behavior than the 65C02 or NMOS 6502..."
|
||||
let machine = machine8()
|
||||
|
||||
machine.setValue(0xff00, for: .direct)
|
||||
machine.setValue(0x000a, for: .Y)
|
||||
machine.setValue(0x12, for: .dataBank)
|
||||
|
||||
machine.setValue(0xff, forAddress: 0xff00)
|
||||
machine.setValue(0xfe, forAddress: 0xffff)
|
||||
|
||||
machine.setValue(0xab, forAddress: 0x130008)
|
||||
|
||||
// LDA ($ff), y; NOP
|
||||
machine.setData(Data([0xb1, 0xff, 0xea]), atAddress: 0x0200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 7)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .A), 0xab)
|
||||
}
|
||||
|
||||
func testIndirectY16() {
|
||||
// "If the D register is $FF00 and the m flag is 0 (note that this means the
|
||||
// e flag must be 0), then for LDA ($FF),Y, the address of the low byte of the
|
||||
// pointer is $00FFFF and the address of the high byte is $000000.
|
||||
// Furthermore, if the DBR is $12, the Y register is $000A, and
|
||||
// * $000000 contains $FF
|
||||
// * $00FFFF contains $FE
|
||||
// then LDA ($FF),Y loads the low byte of the data from address $130008, and the
|
||||
// high byte from $130009."
|
||||
let machine = machine16()
|
||||
|
||||
machine.setValue(0xff00, for: .direct)
|
||||
machine.setValue(0x000a, for: .Y)
|
||||
machine.setValue(0x12, for: .dataBank)
|
||||
|
||||
machine.setValue(0xff, forAddress: 0x0000)
|
||||
machine.setValue(0xfe, forAddress: 0xffff)
|
||||
|
||||
machine.setValue(0xab, forAddress: 0x130008)
|
||||
machine.setValue(0xcd, forAddress: 0x130009)
|
||||
|
||||
// LDA ($ff), y; NOP
|
||||
machine.setData(Data([0xb1, 0xff, 0xea]), atAddress: 0x0200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 8)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .A), 0xcdab)
|
||||
}
|
||||
|
||||
func testIndirectYLong() {
|
||||
// "If the D register is $FF00 and the m flag is 0, then for LDA [$FE],Y, the address
|
||||
// of the low byte of the pointer is $00FFFE, the address of the middle byte is $00FFFF,
|
||||
// and the address of the high byte is $000000. Furthermore, if the Y register is $000A, and
|
||||
// * $000000 contains $12
|
||||
// * $00FFFE contains $FC
|
||||
// * $00FFFF contains $FF
|
||||
// then LDA [$FE],Y loads the low byte of the data from address $130006, and the high byte
|
||||
// from $130007."
|
||||
let machine = machine16()
|
||||
|
||||
machine.setValue(0xff00, for: .direct)
|
||||
machine.setValue(0x000a, for: .Y)
|
||||
machine.setValue(0x12, for: .dataBank)
|
||||
|
||||
machine.setValue(0x12, forAddress: 0x0000)
|
||||
machine.setValue(0xfc, forAddress: 0xfffe)
|
||||
machine.setValue(0xff, forAddress: 0xffff)
|
||||
|
||||
machine.setValue(0xab, forAddress: 0x130006)
|
||||
machine.setValue(0xcd, forAddress: 0x130007)
|
||||
|
||||
// LDA [$fe], y; NOP
|
||||
machine.setData(Data([0xb7, 0xfe, 0xea]), atAddress: 0x0200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 8)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .A), 0xcdab)
|
||||
}
|
||||
|
||||
func testAbsoluteLong() {
|
||||
// "If the m flag is 0, then LDA $12FFFF loads the low byte of the data from address $12FFFF,
|
||||
// and the high byte from address $130000."
|
||||
let machine = machine16()
|
||||
|
||||
machine.setValue(0xab, forAddress: 0x12ffff)
|
||||
machine.setValue(0xcd, forAddress: 0x130000)
|
||||
|
||||
// LDA $12ffff; NOP
|
||||
machine.setData(Data([0xaf, 0xff, 0xff, 0x12, 0xea]), atAddress: 0x0200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 7)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .A), 0xcdab)
|
||||
}
|
||||
|
||||
func testAbsoluteLongX() {
|
||||
// "If the X register is $000A and the m flag is 0, then LDA $12FFFE,X loads the low byte of
|
||||
// the data from address $130008, and the high byte from address $130009."
|
||||
let machine = machine16()
|
||||
|
||||
machine.setValue(0x000a, for: .X)
|
||||
|
||||
machine.setValue(0xab, forAddress: 0x130008)
|
||||
machine.setValue(0xcd, forAddress: 0x130009)
|
||||
|
||||
// LDA $12fffe, x; NOP
|
||||
machine.setData(Data([0xbf, 0xfe, 0xff, 0x12, 0xea]), atAddress: 0x0200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 7)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .A), 0xcdab)
|
||||
}
|
||||
|
||||
func testStackS() {
|
||||
// "If the S register is $FF10 and the m flag is 0, then LDA $FA,S loads the low byte
|
||||
// of the data from address $00000A, and the high byte from address $00000B."
|
||||
let machine = machine16()
|
||||
|
||||
machine.setValue(0xff10, for: .stackPointer)
|
||||
|
||||
machine.setValue(0xab, forAddress: 0x000a)
|
||||
machine.setValue(0xcd, forAddress: 0x000b)
|
||||
|
||||
// LDA $fa, s; NOP
|
||||
machine.setData(Data([0xa3, 0xfa, 0xea]), atAddress: 0x0200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 7)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .A), 0xcdab)
|
||||
}
|
||||
|
||||
func testIndirectStackSY() {
|
||||
// "If the S register is $FF10 and the m flag is 0, then for LDA ($FA,S),Y, the address
|
||||
// of the low byte of the pointer is $00000A and the address of the high byte is $00000B.
|
||||
// Furthermore, if the DBR is $12, the Y register is $0050, and
|
||||
// * $00000A contains $F0
|
||||
// * $00000B contains $FF
|
||||
// then LDA ($FA,S),Y loads the low byte of the data from address $130040, and the high
|
||||
// byte from $130041."
|
||||
let machine = machine16()
|
||||
|
||||
machine.setValue(0xff10, for: .stackPointer)
|
||||
machine.setValue(0x0050, for: .Y)
|
||||
machine.setValue(0x12, for: .dataBank)
|
||||
|
||||
machine.setValue(0xf0, forAddress: 0x000a)
|
||||
machine.setValue(0xff, forAddress: 0x000b)
|
||||
|
||||
machine.setValue(0xab, forAddress: 0x130040)
|
||||
machine.setValue(0xcd, forAddress: 0x130041)
|
||||
|
||||
// LDA ($fa, s), y; NOP
|
||||
machine.setData(Data([0xb3, 0xfa, 0xea]), atAddress: 0x0200)
|
||||
|
||||
machine.setValue(0x200, for: .programCounter)
|
||||
machine.runForNumber(ofCycles: 9)
|
||||
|
||||
XCTAssertEqual(machine.value(for: .A), 0xcdab)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -91,12 +91,12 @@
|
||||
Test68000() : processor(*this) {
|
||||
}
|
||||
|
||||
void will_perform(uint32_t address, uint16_t opcode) {
|
||||
void will_perform(uint32_t, uint16_t) {
|
||||
--instructions_remaining_;
|
||||
if(!instructions_remaining_) comparitor();
|
||||
}
|
||||
|
||||
HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int is_supervisor) {
|
||||
HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int) {
|
||||
using Microcycle = CPU::MC68000::Microcycle;
|
||||
if(cycle.data_select_active()) {
|
||||
cycle.apply(&ram[cycle.host_endian_byte_address()]);
|
||||
|
||||
@@ -13,7 +13,7 @@ class AllSuiteATests: XCTestCase {
|
||||
func testAllSuiteA() {
|
||||
if let filename = Bundle(for: type(of: self)).path(forResource: "AllSuiteA", ofType: "bin") {
|
||||
if let allSuiteA = try? Data(contentsOf: URL(fileURLWithPath: filename)) {
|
||||
let machine = CSTestMachine6502(is65C02: false)
|
||||
let machine = CSTestMachine6502(processor: .processor6502)
|
||||
|
||||
machine.setData(allSuiteA, atAddress: 0x4000)
|
||||
machine.setValue(CSTestMachine6502JamOpcode, forAddress:0x45c0); // end
|
||||
@@ -27,4 +27,5 @@ class AllSuiteATests: XCTestCase {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ class BCDTest: XCTestCase, CSTestMachineTrapHandler {
|
||||
func testBCD() {
|
||||
if let filename = Bundle(for: type(of: self)).path(forResource: "BCDTEST_beeb", ofType: nil) {
|
||||
if let bcdTest = try? Data(contentsOf: URL(fileURLWithPath: filename)) {
|
||||
let machine = CSTestMachine6502(is65C02: false)
|
||||
let machine = CSTestMachine6502(processor: .processor6502)
|
||||
machine.trapHandler = self
|
||||
|
||||
machine.setData(bcdTest, atAddress: 0x2900)
|
||||
@@ -40,7 +40,7 @@ class BCDTest: XCTestCase, CSTestMachineTrapHandler {
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate var output: String = ""
|
||||
private var output: String = ""
|
||||
func testMachine(_ testMachine: CSTestMachine, didTrapAtAddress address: UInt16) {
|
||||
let machine6502 = testMachine as! CSTestMachine6502
|
||||
|
||||
@@ -48,4 +48,5 @@ class BCDTest: XCTestCase, CSTestMachineTrapHandler {
|
||||
let character = machine6502.value(for: .A)
|
||||
output.append(Character(UnicodeScalar(character)!))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,6 +17,16 @@ typedef NS_ENUM(NSInteger, CSTestMachine6502Register) {
|
||||
CSTestMachine6502RegisterA,
|
||||
CSTestMachine6502RegisterX,
|
||||
CSTestMachine6502RegisterY,
|
||||
CSTestMachine6502RegisterEmulationFlag,
|
||||
CSTestMachine6502RegisterDataBank,
|
||||
CSTestMachine6502RegisterProgramBank,
|
||||
CSTestMachine6502RegisterDirect,
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, CSTestMachine6502Processor) {
|
||||
CSTestMachine6502Processor6502,
|
||||
CSTestMachine6502Processor65C02,
|
||||
CSTestMachine6502Processor65816
|
||||
};
|
||||
|
||||
extern const uint8_t CSTestMachine6502JamOpcode;
|
||||
@@ -25,13 +35,13 @@ extern const uint8_t CSTestMachine6502JamOpcode;
|
||||
|
||||
- (nonnull instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
- (nonnull instancetype)initIs65C02:(BOOL)is65C02;
|
||||
- (nonnull instancetype)initWithProcessor:(CSTestMachine6502Processor)processor;
|
||||
|
||||
- (void)setData:(nonnull NSData *)data atAddress:(uint16_t)startAddress;
|
||||
- (void)setData:(nonnull NSData *)data atAddress:(uint32_t)startAddress;
|
||||
- (void)runForNumberOfCycles:(int)cycles;
|
||||
|
||||
- (void)setValue:(uint8_t)value forAddress:(uint16_t)address;
|
||||
- (uint8_t)valueForAddress:(uint16_t)address;
|
||||
- (void)setValue:(uint8_t)value forAddress:(uint32_t)address;
|
||||
- (uint8_t)valueForAddress:(uint32_t)address;
|
||||
- (void)setValue:(uint16_t)value forRegister:(CSTestMachine6502Register)reg;
|
||||
- (uint16_t)valueForRegister:(CSTestMachine6502Register)reg;
|
||||
|
||||
|
||||
@@ -24,6 +24,10 @@ static CPU::MOS6502::Register registerForRegister(CSTestMachine6502Register reg)
|
||||
case CSTestMachine6502RegisterX: return CPU::MOS6502::Register::X;
|
||||
case CSTestMachine6502RegisterY: return CPU::MOS6502::Register::Y;
|
||||
case CSTestMachine6502RegisterStackPointer: return CPU::MOS6502::Register::StackPointer;
|
||||
case CSTestMachine6502RegisterEmulationFlag: return CPU::MOS6502::Register::EmulationFlag;
|
||||
case CSTestMachine6502RegisterDataBank: return CPU::MOS6502::Register::DataBank;
|
||||
case CSTestMachine6502RegisterProgramBank: return CPU::MOS6502::Register::ProgramBank;
|
||||
case CSTestMachine6502RegisterDirect: return CPU::MOS6502::Register::Direct;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,12 +39,20 @@ static CPU::MOS6502::Register registerForRegister(CSTestMachine6502Register reg)
|
||||
|
||||
#pragma mark - Lifecycle
|
||||
|
||||
- (instancetype)initIs65C02:(BOOL)is65C02 {
|
||||
- (instancetype)initWithProcessor:(CSTestMachine6502Processor)processor {
|
||||
self = [super init];
|
||||
|
||||
if(self) {
|
||||
_processor = CPU::MOS6502::AllRAMProcessor::Processor(
|
||||
is65C02 ? CPU::MOS6502::Personality::PWDC65C02 : CPU::MOS6502::Personality::P6502);
|
||||
switch(processor) {
|
||||
case CSTestMachine6502Processor6502:
|
||||
_processor = CPU::MOS6502::AllRAMProcessor::Processor(CPU::MOS6502Esque::Type::T6502);
|
||||
break;
|
||||
case CSTestMachine6502Processor65C02:
|
||||
_processor = CPU::MOS6502::AllRAMProcessor::Processor(CPU::MOS6502Esque::Type::TWDC65C02);
|
||||
break;
|
||||
case CSTestMachine6502Processor65816:
|
||||
_processor = CPU::MOS6502::AllRAMProcessor::Processor(CPU::MOS6502Esque::Type::TWDC65816);
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -52,13 +64,13 @@ static CPU::MOS6502::Register registerForRegister(CSTestMachine6502Register reg)
|
||||
|
||||
#pragma mark - Accessors
|
||||
|
||||
- (uint8_t)valueForAddress:(uint16_t)address {
|
||||
- (uint8_t)valueForAddress:(uint32_t)address {
|
||||
uint8_t value;
|
||||
_processor->get_data_at_address(address, 1, &value);
|
||||
return value;
|
||||
}
|
||||
|
||||
- (void)setValue:(uint8_t)value forAddress:(uint16_t)address {
|
||||
- (void)setValue:(uint8_t)value forAddress:(uint32_t)address {
|
||||
_processor->set_data_at_address(address, 1, &value);
|
||||
}
|
||||
|
||||
@@ -70,7 +82,7 @@ static CPU::MOS6502::Register registerForRegister(CSTestMachine6502Register reg)
|
||||
return _processor->get_value_of_register(registerForRegister(reg));
|
||||
}
|
||||
|
||||
- (void)setData:(NSData *)data atAddress:(uint16_t)startAddress {
|
||||
- (void)setData:(NSData *)data atAddress:(uint32_t)startAddress {
|
||||
_processor->set_data_at_address(startAddress, data.length, (const uint8_t *)data.bytes);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ class BusOperationHandler: public CPU::Z80::AllRAMProcessor::MemoryAccessDelegat
|
||||
public:
|
||||
BusOperationHandler(CSTestMachineZ80 *targetMachine) : target_(targetMachine) {}
|
||||
|
||||
void z80_all_ram_processor_did_perform_bus_operation(CPU::Z80::AllRAMProcessor &processor, CPU::Z80::PartialMachineCycle::Operation operation, uint16_t address, uint8_t value, HalfCycles time_stamp) {
|
||||
void z80_all_ram_processor_did_perform_bus_operation(CPU::Z80::AllRAMProcessor &, CPU::Z80::PartialMachineCycle::Operation operation, uint16_t address, uint8_t value, HalfCycles time_stamp) {
|
||||
[target_ testMachineDidPerformBusOperation:operation address:address value:value timeStamp:time_stamp];
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ struct PortAccessDelegateTopByte: public CPU::Z80::AllRAMProcessor::PortAccessDe
|
||||
};
|
||||
|
||||
struct PortAccessDelegate191: public CPU::Z80::AllRAMProcessor::PortAccessDelegate {
|
||||
uint8_t z80_all_ram_processor_input(uint16_t port) final { return 191; }
|
||||
uint8_t z80_all_ram_processor_input(uint16_t) final { return 191; }
|
||||
};
|
||||
|
||||
#pragma mark - Capture class
|
||||
|
||||
@@ -10,21 +10,21 @@ import XCTest
|
||||
|
||||
class C1540Tests: XCTestCase {
|
||||
|
||||
fileprivate func with1540(_ action: (C1540Bridge) -> ()) {
|
||||
private func with1540(_ action: (C1540Bridge) -> ()) {
|
||||
let bridge = C1540Bridge()
|
||||
action(bridge)
|
||||
}
|
||||
|
||||
fileprivate func transmit(_ c1540: C1540Bridge, value: Int) {
|
||||
private func transmit(_ c1540: C1540Bridge, value: Int) {
|
||||
var shiftedValue = value
|
||||
|
||||
c1540.dataLine = true
|
||||
c1540.run(forCycles: 256)
|
||||
XCTAssert(c1540.dataLine == false, "Listener should have taken data line low for start of transmission")
|
||||
XCTAssertFalse(c1540.dataLine, "Listener should have taken data line low for start of transmission")
|
||||
|
||||
c1540.clockLine = true
|
||||
c1540.run(forCycles: 256) // this isn't time limited on real hardware
|
||||
XCTAssert(c1540.dataLine == true, "Listener should have let data line go high again")
|
||||
XCTAssertTrue(c1540.dataLine, "Listener should have let data line go high again")
|
||||
|
||||
// set up for byte transfer
|
||||
c1540.clockLine = false
|
||||
@@ -47,7 +47,7 @@ class C1540Tests: XCTestCase {
|
||||
// check for acknowledgment
|
||||
c1540.dataLine = true
|
||||
c1540.run(forCycles: 1000)
|
||||
XCTAssert(c1540.dataLine == false, "Listener should have acknowledged byte")
|
||||
XCTAssertFalse(c1540.dataLine, "Listener should have acknowledged byte")
|
||||
}
|
||||
|
||||
// MARK: EOI
|
||||
@@ -64,10 +64,11 @@ class C1540Tests: XCTestCase {
|
||||
|
||||
// proceed 1 ms and check that the 1540 pulled the data line low
|
||||
$0.run(forCycles: 1000)
|
||||
XCTAssert($0.dataLine == false, "Listener should have taken data line low")
|
||||
XCTAssertFalse($0.dataLine, "Listener should have taken data line low")
|
||||
|
||||
// transmit LISTEN #8
|
||||
self.transmit($0, value: 0x28)
|
||||
transmit($0, value: 0x28)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ class ComparativeBusHandler: public CPU::MC68000::BusHandler {
|
||||
gzclose(trace);
|
||||
}
|
||||
|
||||
void will_perform(uint32_t address, uint16_t opcode) {
|
||||
void will_perform(uint32_t address, uint16_t) {
|
||||
// Obtain the next line from the trace file.
|
||||
char correct_state[300] = "\n";
|
||||
gzgets(trace, correct_state, sizeof(correct_state));
|
||||
@@ -45,7 +45,7 @@ class ComparativeBusHandler: public CPU::MC68000::BusHandler {
|
||||
fprintf(stderr, "Diverges at line %d\n", line_count);
|
||||
fprintf(stderr, "Good: %s", correct_state);
|
||||
fprintf(stderr, "Bad: %s", local_state);
|
||||
assert(false);
|
||||
throw std::exception();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ class EmuTOS: public ComparativeBusHandler {
|
||||
return m68000_.get_state();
|
||||
}
|
||||
|
||||
HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int is_supervisor) {
|
||||
HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int) {
|
||||
const uint32_t address = cycle.word_address();
|
||||
uint32_t word_address = address;
|
||||
|
||||
|
||||
44
OSBindings/Mac/Clock SignalTests/Jeek816Tests.swift
Normal file
44
OSBindings/Mac/Clock SignalTests/Jeek816Tests.swift
Normal file
@@ -0,0 +1,44 @@
|
||||
//
|
||||
// Jeek816Tests.swift
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 12/10/2020.
|
||||
// Copyright 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import Foundation
|
||||
|
||||
class Jeek816Tests: XCTestCase {
|
||||
|
||||
func testJeek816() {
|
||||
var machine: CSTestMachine6502!
|
||||
|
||||
if let filename = Bundle(for: type(of: self)).path(forResource: "suite-a.prg", ofType: nil) {
|
||||
if let testData = try? Data(contentsOf: URL(fileURLWithPath: filename)) {
|
||||
machine = CSTestMachine6502(processor: .processor65816)
|
||||
|
||||
let contents = testData.subdata(in: 0xe ..< testData.count)
|
||||
machine.setData(contents, atAddress: 0x080d)
|
||||
|
||||
machine.setValue(0x080d, for: .programCounter)
|
||||
}
|
||||
}
|
||||
|
||||
if machine == nil {
|
||||
NSException(name: NSExceptionName(rawValue: "Failed Test"), reason: "Couldn't load file \(name)", userInfo: nil).raise()
|
||||
}
|
||||
|
||||
// $874 is the failure stopping point and $85d is success.
|
||||
while machine.value(for: .lastOperationAddress) != 0x0874 && machine.value(for: .lastOperationAddress) != 0x085d {
|
||||
machine.runForNumber(ofCycles: 1000)
|
||||
}
|
||||
|
||||
// The test leaves $ff in $d7ff to indicate failure; $0000 to indicate success.
|
||||
// If the tests failed, it'll leave a bitmap of failures in address $0401.
|
||||
if machine.value(forAddress: 0xd7ff) != 0 {
|
||||
NSException(name: NSExceptionName(rawValue: "Failed Test"), reason: "Failed tests with bitmap: \(String(format:"%02x", machine.value(forAddress: 0x401)))", userInfo: nil).raise()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -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,54 @@ 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!)")
|
||||
}
|
||||
|
||||
private func runTest65C02NoRockwell(processor: CSTestMachine6502Processor) {
|
||||
func errorForTrapAddress(_ address: UInt16) -> String? {
|
||||
switch address {
|
||||
case 0x11e0: return nil // success!
|
||||
|
||||
case 0x1474: return "BRK didn't clear the decimal flag"
|
||||
case 0x0e3d: return "TRB set flags incorrectly"
|
||||
|
||||
case 0: return "Didn't find tests"
|
||||
default: return "Unknown error at \(String(format:"%04x", address))"
|
||||
}
|
||||
}
|
||||
|
||||
let destination = runTest(resource: "65C02_no_Rockwell_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 standard 6502 tests on a 65816.
|
||||
func test65816AsNonRockwell65C02() {
|
||||
runTest65C02NoRockwell(processor: .processor65816)
|
||||
}
|
||||
|
||||
/// Runs Klaus Dormann's 65C02 tests.
|
||||
func test65C02() {
|
||||
runTest65C02(processor: .processor65C02)
|
||||
}
|
||||
|
||||
/* Dormann's 65C02 tests aren't applicable to the 65816; e.g. they test BBR/BBS, which the 65816 doesn't implement. */
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@ import Foundation
|
||||
|
||||
class PatrikRakTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
|
||||
fileprivate var done = false
|
||||
fileprivate var output = ""
|
||||
private var done = false
|
||||
private var output = ""
|
||||
|
||||
private func runTest(_ name: String) {
|
||||
if let filename = Bundle(for: type(of: self)).path(forResource: name, ofType: "tap") {
|
||||
@@ -124,4 +124,5 @@ class PatrikRakTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ class QL: public ComparativeBusHandler {
|
||||
return m68000_.get_state();
|
||||
}
|
||||
|
||||
HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int is_supervisor) {
|
||||
HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int) {
|
||||
const uint32_t address = cycle.word_address();
|
||||
uint32_t word_address = address;
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ class RAM68000: public CPU::MC68000::BusHandler {
|
||||
ram_[1] = sp & 0xffff;
|
||||
}
|
||||
|
||||
void will_perform(uint32_t address, uint16_t opcode) {
|
||||
void will_perform(uint32_t, uint16_t) {
|
||||
--instructions_remaining_;
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ class RAM68000: public CPU::MC68000::BusHandler {
|
||||
return &ram_[(address >> 1) % ram_.size()];
|
||||
}
|
||||
|
||||
HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int is_supervisor) {
|
||||
HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int) {
|
||||
const uint32_t word_address = cycle.word_address();
|
||||
if(instructions_remaining_) duration_ += cycle.length;
|
||||
|
||||
|
||||
@@ -11,206 +11,295 @@ 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)
|
||||
}
|
||||
/*
|
||||
The flow tests don't apply; the 65816 [and 65C02] reset the decimal flag upon a BRK, but the 6502 that Lorenz
|
||||
tests doesn't do so.
|
||||
*/
|
||||
func testBranch65816() {
|
||||
testBranch(processor: .processor65816)
|
||||
}
|
||||
/* The NOP tests also don't apply; the 65816 has only one, well-defined NOP. */
|
||||
|
||||
|
||||
// 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 +308,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 +348,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 +413,7 @@ class WolfgangLorenzTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
testMachine6502.setValue(0x3, for:CSTestMachine6502Register.A)
|
||||
|
||||
case 0x8000, 0xa474:
|
||||
NSException(name: NSExceptionName(rawValue: "Failed test"), reason: self.petsciiToString(output), userInfo: nil).raise()
|
||||
NSException(name: NSExceptionName(rawValue: "Failed test"), reason: petsciiToString(output), userInfo: nil).raise()
|
||||
|
||||
case 0x0000:
|
||||
NSException(name: NSExceptionName(rawValue: "Failed test"), reason: "Execution hit 0000", userInfo: nil).raise()
|
||||
|
||||
@@ -1247,4 +1247,5 @@ class Z80MachineCycleTests: XCTestCase {
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
import XCTest
|
||||
|
||||
class Z80MemptrTester: XCTestCase {
|
||||
|
||||
let machine = CSTestMachineZ80()
|
||||
|
||||
private func test(program : [UInt8], initialValue : UInt16) -> UInt16 {
|
||||
|
||||
@@ -11,8 +11,8 @@ import Foundation
|
||||
|
||||
class ZexallTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
|
||||
fileprivate var done = false
|
||||
fileprivate var output = ""
|
||||
private var done = false
|
||||
private var output = ""
|
||||
|
||||
private func runTest(_ name: String) {
|
||||
if let filename = Bundle(for: type(of: self)).path(forResource: name, ofType: "com") {
|
||||
@@ -177,4 +177,5 @@ class ZexallTests: XCTestCase, CSTestMachineTrapHandler {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
11
OSBindings/Mac/Clock SignalTests/jeek816/Makefile
Normal file
11
OSBindings/Mac/Clock SignalTests/jeek816/Makefile
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
F=suite-a
|
||||
|
||||
all: $(F).prg
|
||||
|
||||
$(F).prg: $(F).asm
|
||||
acme --format plain -o $(F).prg -r $(F).r $(F).asm
|
||||
|
||||
clean:
|
||||
rm -f $(F).prg
|
||||
rm -f *~
|
||||
2
OSBindings/Mac/Clock SignalTests/jeek816/RUN
Normal file
2
OSBindings/Mac/Clock SignalTests/jeek816/RUN
Normal file
@@ -0,0 +1,2 @@
|
||||
xscpu64 -scpu64 /usr/local/lib64/vice/SCPU64/scpu64 suite-a.prg
|
||||
#xscpu64.devel -scpu64 /usr/local/lib64/vice/SCPU64/scpu64 suite-a.prg
|
||||
24
OSBindings/Mac/Clock SignalTests/jeek816/license.txt
Normal file
24
OSBindings/Mac/Clock SignalTests/jeek816/license.txt
Normal file
@@ -0,0 +1,24 @@
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
23
OSBindings/Mac/Clock SignalTests/jeek816/readme.txt
Normal file
23
OSBindings/Mac/Clock SignalTests/jeek816/readme.txt
Normal file
@@ -0,0 +1,23 @@
|
||||
|
||||
65C816 instruction set test
|
||||
|
||||
|
||||
2017-12-13 J.E. Klasek j+816 AT klasek DOT at
|
||||
|
||||
|
||||
ACME syntax, green border shows success. in case of failure red border
|
||||
is shown and $0400 contains number of failed test and $0401 a bitmap
|
||||
showing which tests actually failed.
|
||||
If all tests fail on screen "f?" will be shown (corresponds to 6 failures)
|
||||
and the bitmap %00111111 ($3F = '?')
|
||||
|
||||
There are 6 tests (bit 5 to bit 0):
|
||||
|
||||
STX $FFFF fails in 16 mode for X/Y if wrapping to location 0
|
||||
STY $FFFF fails in 16 mode for X/Y if wrapping to location 0
|
||||
LDX $FFFF,Y fails if wrapping to same bank
|
||||
LDY $FFFF,X fails if wrapping to same bank
|
||||
TRB $FFFF fails in 16 mode for A/M if wrapping to location 0
|
||||
TSB $FFFF fails in 16 mode for A/M if wrapping to location 0
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
218
OSBindings/Mac/Clock SignalTests/jeek816/suite-a.asm
Normal file
218
OSBindings/Mac/Clock SignalTests/jeek816/suite-a.asm
Normal file
@@ -0,0 +1,218 @@
|
||||
!cpu 65816
|
||||
|
||||
; 2017-12-13 J.E. Klasek j+816 AT klasek DOT at
|
||||
|
||||
|
||||
|
||||
videoram = $0400
|
||||
colorram = $d800
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
*=$07ff
|
||||
!word $0801
|
||||
!word bend
|
||||
!word 10
|
||||
!byte $9e
|
||||
!text "2061", 0
|
||||
bend: !word 0
|
||||
;-------------------------------------------------------------------------------
|
||||
|
||||
sei
|
||||
lda #$17
|
||||
sta $d018
|
||||
lda #$35
|
||||
sta $01
|
||||
lda #$7f
|
||||
sta $dc0d
|
||||
sta $dd0d
|
||||
lda $dc0d
|
||||
lda $dd0d
|
||||
ldx #0
|
||||
-
|
||||
lda #$20
|
||||
sta videoram,x
|
||||
sta videoram+$0100,x
|
||||
sta videoram+$0200,x
|
||||
sta videoram+$0300,x
|
||||
lda #1
|
||||
sta colorram,x
|
||||
sta colorram+$0100,x
|
||||
sta colorram+$0200,x
|
||||
sta colorram+$0300,x
|
||||
inx
|
||||
bne -
|
||||
|
||||
jmp start
|
||||
theend:
|
||||
sep #$30 ; 8-bit for X/Y and A/M
|
||||
!as
|
||||
!rs
|
||||
lda $040210
|
||||
cmp #$ff
|
||||
bne error
|
||||
|
||||
lda #5
|
||||
sta $d020
|
||||
ldx #0 ; success
|
||||
stx $d7ff
|
||||
jmp *
|
||||
error
|
||||
sta $0400
|
||||
lda $040211 ; failure map (which test failed)
|
||||
sta $0401
|
||||
lda #10
|
||||
sta $d020
|
||||
ldx #$ff ; failure
|
||||
stx $d7ff
|
||||
jmp *
|
||||
|
||||
;-------------------------------------------------------------------------------
|
||||
|
||||
* = $1000
|
||||
start:
|
||||
; EXPECTED FINAL RESULTS: $0210 = FF
|
||||
; (any other number will be the
|
||||
; test that failed)
|
||||
|
||||
; initialize:
|
||||
lda #$00
|
||||
sta $040210
|
||||
sta $040211
|
||||
|
||||
|
||||
test00:
|
||||
|
||||
; setup cpu
|
||||
clc
|
||||
xce ; native mode
|
||||
rep #$30 ; 16-bit for X/Y and A/M
|
||||
!al
|
||||
!rl
|
||||
|
||||
; setup registers
|
||||
lda #$0404 ; Data Bank Register register
|
||||
pha ; akku in 16 bit
|
||||
plb ; pull DBR twice
|
||||
plb
|
||||
ldy #$8888 ; change marker
|
||||
tyx
|
||||
tya
|
||||
|
||||
; setup memory
|
||||
lda #$5555 ; wrap marker
|
||||
sta $048887 ; into bank 4, for LDX/LDY
|
||||
lda #$7777 ; no-wrap marker
|
||||
sta $058887 ; into bank 5, for LDX/LDY
|
||||
|
||||
;---------------------------------------------------------------------
|
||||
|
||||
stz $0000 ; init wrap marker
|
||||
lda #$7777 ; no-wrap marker
|
||||
sta $050000 ; to start of bank 5
|
||||
|
||||
sty $ffff ; high byte of Y is where?
|
||||
lda $0000
|
||||
bne +
|
||||
lda $ffff ; fetch, does not wrap
|
||||
cmp #$8888
|
||||
bne +
|
||||
lda $050000
|
||||
cmp #$7788 ; write to bank 5
|
||||
beq ++
|
||||
+ inc $0210 ; fail counter
|
||||
clc
|
||||
++
|
||||
rol $0211 ; update failure map
|
||||
;---------------------------------------------------------------------
|
||||
|
||||
stz $0000 ; init wrap marker
|
||||
lda #$7777 ; no-wrap marker
|
||||
sta $050000 ; to start of bank 5
|
||||
|
||||
tyx ; change marker
|
||||
stx $ffff ; high byte of Y is where?
|
||||
lda $0000
|
||||
bne +
|
||||
lda $ffff ; fetch, does not wrap
|
||||
cmp #$8888
|
||||
bne +
|
||||
lda $050000
|
||||
cmp #$7788 ; write to bank 5
|
||||
beq ++
|
||||
+ inc $0210 ; fail counter
|
||||
clc
|
||||
++
|
||||
rol $0211 ; update failure map
|
||||
;---------------------------------------------------------------------
|
||||
|
||||
ldy $ffff,x ; Y=5555 Y=7777 value for Y comes from which bank?
|
||||
cpy #$7777
|
||||
beq +
|
||||
inc $0210 ; fail counter
|
||||
clc
|
||||
+
|
||||
rol $0211 ; update failure map
|
||||
;---------------------------------------------------------------------
|
||||
|
||||
txy ; reinitialize y
|
||||
ldx $ffff,y ; X=5555 X=7777 value for X comes from which bank?
|
||||
cpx #$7777
|
||||
beq +
|
||||
inc $0210 ; fail counter
|
||||
clc
|
||||
+
|
||||
rol $0211 ; update failure map
|
||||
;---------------------------------------------------------------------
|
||||
|
||||
stz $0000 ; init wrap marker
|
||||
lda #$7777 ; no-wrap marker
|
||||
sta $050000 ; to start of bank 5
|
||||
|
||||
lda #$7788
|
||||
inc $0000 ; $0000 = 1
|
||||
trb $ffff ; 88 77 & ^(88 77) -> 00 00
|
||||
lda $0000
|
||||
cmp #$0001 ; $0000 not reset by trb (does not wrap)
|
||||
bne +
|
||||
lda $050000
|
||||
cmp #$7700 ; $050001 reset by trb
|
||||
beq ++
|
||||
+ inc $0210 ; fail counter
|
||||
clc
|
||||
++
|
||||
rol $0211 ; update failure map
|
||||
;---------------------------------------------------------------------
|
||||
|
||||
lda #$7788
|
||||
sta $050000 ; 00 88 | 88 77 -> 88 ff
|
||||
tsb $ffff ; set bits (which are already cleared)
|
||||
lda $0000
|
||||
cmp #$0001 ; $0000 not set by tsb (does not wrap!)
|
||||
bne +
|
||||
lda $050000
|
||||
cmp #$77ff ; $050001 all bits set by tsb
|
||||
beq ++
|
||||
+ inc $0210 ; fail counter
|
||||
clc
|
||||
++
|
||||
rol $0211 ; update failure map
|
||||
;---------------------------------------------------------------------
|
||||
|
||||
|
||||
test00pass:
|
||||
lda $0210
|
||||
eor #%0011111100000000 ; invert failure map
|
||||
sta $0210
|
||||
bne +
|
||||
dec $0210 ; 0 -> FF
|
||||
+
|
||||
|
||||
lda #$0000
|
||||
pha
|
||||
plb
|
||||
plb ; program bank = 0
|
||||
sec
|
||||
xce ; emulation mode
|
||||
sep #$30 ; a/m, x/y 8 bit
|
||||
|
||||
jmp theend
|
||||
BIN
OSBindings/Mac/Clock SignalTests/jeek816/suite-a.prg
Normal file
BIN
OSBindings/Mac/Clock SignalTests/jeek816/suite-a.prg
Normal file
Binary file not shown.
220
OSBindings/Mac/Clock SignalTests/jeek816/suite-a.r
Normal file
220
OSBindings/Mac/Clock SignalTests/jeek816/suite-a.r
Normal file
@@ -0,0 +1,220 @@
|
||||
|
||||
; ******** Source: suite-a.asm
|
||||
1 !cpu 65816
|
||||
2
|
||||
3 ; 2017-12-13 J.E. Klasek j+816 AT klasek DOT at
|
||||
4
|
||||
5
|
||||
6
|
||||
7 videoram = $0400
|
||||
8 colorram = $d800
|
||||
9
|
||||
10 ;-------------------------------------------------------------------------------
|
||||
11 *=$07ff
|
||||
12 07ff 0108 !word $0801
|
||||
13 0801 0b08 !word bend
|
||||
14 0803 0a00 !word 10
|
||||
15 0805 9e !byte $9e
|
||||
16 0806 3230363100 !text "2061", 0
|
||||
17 080b 0000 bend: !word 0
|
||||
18 ;-------------------------------------------------------------------------------
|
||||
19
|
||||
20 080d 78 sei
|
||||
21 080e a917 lda #$17
|
||||
22 0810 8d18d0 sta $d018
|
||||
23 0813 a935 lda #$35
|
||||
24 0815 8501 sta $01
|
||||
25 0817 a97f lda #$7f
|
||||
26 0819 8d0ddc sta $dc0d
|
||||
27 081c 8d0ddd sta $dd0d
|
||||
28 081f ad0ddc lda $dc0d
|
||||
29 0822 ad0ddd lda $dd0d
|
||||
30 0825 a200 ldx #0
|
||||
31 -
|
||||
32 0827 a920 lda #$20
|
||||
33 0829 9d0004 sta videoram,x
|
||||
34 082c 9d0005 sta videoram+$0100,x
|
||||
35 082f 9d0006 sta videoram+$0200,x
|
||||
36 0832 9d0007 sta videoram+$0300,x
|
||||
37 0835 a901 lda #1
|
||||
38 0837 9d00d8 sta colorram,x
|
||||
39 083a 9d00d9 sta colorram+$0100,x
|
||||
40 083d 9d00da sta colorram+$0200,x
|
||||
41 0840 9d00db sta colorram+$0300,x
|
||||
42 0843 e8 inx
|
||||
43 0844 d0e1 bne -
|
||||
44
|
||||
45 0846 4c0010 jmp start
|
||||
46 theend:
|
||||
47 0849 e230 sep #$30 ; 8-bit for X/Y and A/M
|
||||
48 !as
|
||||
49 !rs
|
||||
50 084b af100204 lda $040210
|
||||
51 084f c9ff cmp #$ff
|
||||
52 0851 d00d bne error
|
||||
53
|
||||
54 0853 a905 lda #5
|
||||
55 0855 8d20d0 sta $d020
|
||||
56 0858 a200 ldx #0 ; success
|
||||
57 085a 8effd7 stx $d7ff
|
||||
58 085d 4c5d08 jmp *
|
||||
59 error
|
||||
60 0860 8d0004 sta $0400
|
||||
61 0863 af110204 lda $040211 ; failure map (which test failed)
|
||||
62 0867 8d0104 sta $0401
|
||||
63 086a a90a lda #10
|
||||
64 086c 8d20d0 sta $d020
|
||||
65 086f a2ff ldx #$ff ; failure
|
||||
66 0871 8effd7 stx $d7ff
|
||||
67 0874 4c7408 jmp *
|
||||
68
|
||||
69 ;-------------------------------------------------------------------------------
|
||||
70
|
||||
71 * = $1000
|
||||
72 start:
|
||||
73 ; EXPECTED FINAL RESULTS: $0210 = FF
|
||||
74 ; (any other number will be the
|
||||
75 ; test that failed)
|
||||
76
|
||||
77 ; initialize:
|
||||
78 1000 a900 lda #$00
|
||||
79 1002 8f100204 sta $040210
|
||||
80 1006 8f110204 sta $040211
|
||||
81
|
||||
82
|
||||
83 test00:
|
||||
84
|
||||
85 ; setup cpu
|
||||
86 100a 18 clc
|
||||
87 100b fb xce ; native mode
|
||||
88 100c c230 rep #$30 ; 16-bit for X/Y and A/M
|
||||
89 !al
|
||||
90 !rl
|
||||
91
|
||||
92 ; setup registers
|
||||
93 100e a90404 lda #$0404 ; Data Bank Register register
|
||||
94 1011 48 pha ; akku in 16 bit
|
||||
95 1012 ab plb ; pull DBR twice
|
||||
96 1013 ab plb
|
||||
97 1014 a08888 ldy #$8888 ; change marker
|
||||
98 1017 bb tyx
|
||||
99 1018 98 tya
|
||||
100
|
||||
101 ; setup memory
|
||||
102 1019 a95555 lda #$5555 ; wrap marker
|
||||
103 101c 8f878804 sta $048887 ; into bank 4, for LDX/LDY
|
||||
104 1020 a97777 lda #$7777 ; no-wrap marker
|
||||
105 1023 8f878805 sta $058887 ; into bank 5, for LDX/LDY
|
||||
106
|
||||
107 ;---------------------------------------------------------------------
|
||||
108
|
||||
109 1027 9c0000 stz $0000 ; init wrap marker
|
||||
110 102a a97777 lda #$7777 ; no-wrap marker
|
||||
111 102d 8f000005 sta $050000 ; to start of bank 5
|
||||
112
|
||||
113 1031 8cffff sty $ffff ; high byte of Y is where?
|
||||
114 1034 ad0000 lda $0000
|
||||
115 1037 d011 bne +
|
||||
116 1039 adffff lda $ffff ; fetch, does not wrap
|
||||
117 103c c98888 cmp #$8888
|
||||
118 103f d009 bne +
|
||||
119 1041 af000005 lda $050000
|
||||
120 1045 c98877 cmp #$7788 ; write to bank 5
|
||||
121 1048 f004 beq ++
|
||||
122 104a ee1002 + inc $0210 ; fail counter
|
||||
123 104d 18 clc
|
||||
124 ++
|
||||
125 104e 2e1102 rol $0211 ; update failure map
|
||||
126 ;---------------------------------------------------------------------
|
||||
127
|
||||
128 1051 9c0000 stz $0000 ; init wrap marker
|
||||
129 1054 a97777 lda #$7777 ; no-wrap marker
|
||||
130 1057 8f000005 sta $050000 ; to start of bank 5
|
||||
131
|
||||
132 105b bb tyx ; change marker
|
||||
133 105c 8effff stx $ffff ; high byte of Y is where?
|
||||
134 105f ad0000 lda $0000
|
||||
135 1062 d011 bne +
|
||||
136 1064 adffff lda $ffff ; fetch, does not wrap
|
||||
137 1067 c98888 cmp #$8888
|
||||
138 106a d009 bne +
|
||||
139 106c af000005 lda $050000
|
||||
140 1070 c98877 cmp #$7788 ; write to bank 5
|
||||
141 1073 f004 beq ++
|
||||
142 1075 ee1002 + inc $0210 ; fail counter
|
||||
143 1078 18 clc
|
||||
144 ++
|
||||
145 1079 2e1102 rol $0211 ; update failure map
|
||||
146 ;---------------------------------------------------------------------
|
||||
147
|
||||
148 107c bcffff ldy $ffff,x ; Y=5555 Y=7777 value for Y comes from which bank?
|
||||
149 107f c07777 cpy #$7777
|
||||
150 1082 f004 beq +
|
||||
151 1084 ee1002 inc $0210 ; fail counter
|
||||
152 1087 18 clc
|
||||
153 +
|
||||
154 1088 2e1102 rol $0211 ; update failure map
|
||||
155 ;---------------------------------------------------------------------
|
||||
156
|
||||
157 108b 9b txy ; reinitialize y
|
||||
158 108c beffff ldx $ffff,y ; X=5555 X=7777 value for X comes from which bank?
|
||||
159 108f e07777 cpx #$7777
|
||||
160 1092 f004 beq +
|
||||
161 1094 ee1002 inc $0210 ; fail counter
|
||||
162 1097 18 clc
|
||||
163 +
|
||||
164 1098 2e1102 rol $0211 ; update failure map
|
||||
165 ;---------------------------------------------------------------------
|
||||
166
|
||||
167 109b 9c0000 stz $0000 ; init wrap marker
|
||||
168 109e a97777 lda #$7777 ; no-wrap marker
|
||||
169 10a1 8f000005 sta $050000 ; to start of bank 5
|
||||
170
|
||||
171 10a5 a98877 lda #$7788
|
||||
172 10a8 ee0000 inc $0000 ; $0000 = 1
|
||||
173 10ab 1cffff trb $ffff ; 88 77 & ^(88 77) -> 00 00
|
||||
174 10ae ad0000 lda $0000
|
||||
175 10b1 c90100 cmp #$0001 ; $0000 not reset by trb (does not wrap)
|
||||
176 10b4 d009 bne +
|
||||
177 10b6 af000005 lda $050000
|
||||
178 10ba c90077 cmp #$7700 ; $050001 reset by trb
|
||||
179 10bd f004 beq ++
|
||||
180 10bf ee1002 + inc $0210 ; fail counter
|
||||
181 10c2 18 clc
|
||||
182 ++
|
||||
183 10c3 2e1102 rol $0211 ; update failure map
|
||||
184 ;---------------------------------------------------------------------
|
||||
185
|
||||
186 10c6 a98877 lda #$7788
|
||||
187 10c9 8f000005 sta $050000 ; 00 88 | 88 77 -> 88 ff
|
||||
188 10cd 0cffff tsb $ffff ; set bits (which are already cleared)
|
||||
189 10d0 ad0000 lda $0000
|
||||
190 10d3 c90100 cmp #$0001 ; $0000 not set by tsb (does not wrap!)
|
||||
191 10d6 d009 bne +
|
||||
192 10d8 af000005 lda $050000
|
||||
193 10dc c9ff77 cmp #$77ff ; $050001 all bits set by tsb
|
||||
194 10df f004 beq ++
|
||||
195 10e1 ee1002 + inc $0210 ; fail counter
|
||||
196 10e4 18 clc
|
||||
197 ++
|
||||
198 10e5 2e1102 rol $0211 ; update failure map
|
||||
199 ;---------------------------------------------------------------------
|
||||
200
|
||||
201
|
||||
202 test00pass:
|
||||
203 10e8 ad1002 lda $0210
|
||||
204 10eb 49003f eor #%0011111100000000 ; invert failure map
|
||||
205 10ee 8d1002 sta $0210
|
||||
206 10f1 d003 bne +
|
||||
207 10f3 ce1002 dec $0210 ; 0 -> FF
|
||||
208 +
|
||||
209
|
||||
210 10f6 a90000 lda #$0000
|
||||
211 10f9 48 pha
|
||||
212 10fa ab plb
|
||||
213 10fb ab plb ; program bank = 0
|
||||
214 10fc 38 sec
|
||||
215 10fd fb xce ; emulation mode
|
||||
216 10fe e230 sep #$30 ; a/m, x/y 8 bit
|
||||
217
|
||||
218 1100 4c4908 jmp theend
|
||||
147
OSBindings/Mac/Clock SignalTests/jeek816/test-816.txt
Normal file
147
OSBindings/Mac/Clock SignalTests/jeek816/test-816.txt
Normal file
@@ -0,0 +1,147 @@
|
||||
65816 behavior on VICE and true hardware
|
||||
========================================
|
||||
|
||||
Johann Klasek, j AT klasek at
|
||||
2017-12-02
|
||||
|
||||
|
||||
Testenvironment:
|
||||
|
||||
Hardware:
|
||||
Tested on LTC64, a turbo card for the Commodore 64
|
||||
in-place of CPU 6510 (which moves to the card)
|
||||
with a WDC's 65C816, 32 KByte ROM and 256 KByte RAM.
|
||||
based on magazine article c't "C64 aufgemotzt", 1987/06 p. 94
|
||||
http://klasek.at/c64/ltc64 (german)
|
||||
Redesigned and implemented by Christoph Egretzberger
|
||||
and software integration by Johann Klasek
|
||||
|
||||
Software:
|
||||
Jamaica Monitor (Jammon) 4.1, native '816
|
||||
C64 ROMs (Kernal+BASIC)
|
||||
|
||||
---------------------------------------------------------------------
|
||||
Case 1: Wrap-around for addressing mode "absolute"
|
||||
for opcodes TSB, TRB, STX, STY, LDX, LDY
|
||||
|
||||
VICE WDC
|
||||
!e
|
||||
a 1000 clc
|
||||
xce native mode
|
||||
rep #$30 16-bit for X/Y and A/M
|
||||
lda #$0404 Data Bank Register register
|
||||
pha akku in 16 bit
|
||||
plb pull DBR twice
|
||||
plb
|
||||
lda #$7777 no-wrap marker
|
||||
sta $050000 to bank 5
|
||||
sta $058887
|
||||
lda #$5555 unchanged marker
|
||||
sta $048887 into bank 4
|
||||
ldy #$8888 change marker
|
||||
tyx now in X and Y
|
||||
sty $ffff high byte of Y is where?
|
||||
ldy $ffff,x Y=5555 Y=7777 value for Y comes from which bank?
|
||||
sty $0002 save as intermediate result, because we do more
|
||||
lda $ffff A=7788 A=8888 fetch, does no wrap even on VICE
|
||||
ldx $0000 X=2088 X=20b5 see if wrapping occured (from sty above)
|
||||
eor #$ffff A=8877 A=7777 invert akku, 2nd part ...
|
||||
trb $ffff M=0088 M=8888 reset bits (which are already cleared)
|
||||
tsb $ffff M=88ff M=ffff set these bits giving a $ffff value
|
||||
ldy $ffff Y=88ff Y=ffff back to ldy test
|
||||
brk
|
||||
|
||||
!d Jammon alternate display mode
|
||||
d disassemble on current PC
|
||||
m 04ffff show memory including 050000
|
||||
m 040000 start of bank 4 (wrap around area),
|
||||
and intermediate result in 040002
|
||||
m 048887 X indexed wrap area
|
||||
m 058887 X indexed no wrap area
|
||||
z 1000 start single-stepping
|
||||
z single step until BRK or ...
|
||||
g go to BRK
|
||||
|
||||
Results VICE WDC
|
||||
|
||||
$050001 $77 $77 marker, never changed
|
||||
$050000 $77 $ff modified by TRB/TSB
|
||||
$04ffff $ff $ff modified by TRB/TSB
|
||||
$040003 $55 $77 read value (low) from LDY $FFFF,X
|
||||
$040002 $55 $77 read value (high) from LDY $FFFF,X
|
||||
read value
|
||||
$040000 $88 ?? (if no wrapping for STY $FFFF, TRB/TSB $FFFF)
|
||||
only changed if wrapping
|
||||
$058888 $77 $77 marker, never changed
|
||||
$058887 $77 $77 marker, never changed
|
||||
$048888 $55 $55 marker, never changed
|
||||
$048887 $55 $55 marker, never changed
|
||||
|
||||
A: $8877 $7777 value from $04ffff/$050000 after STY $FFFF
|
||||
X: $??88 $???? value from $040000/$040001 with high byte of Y
|
||||
in low byte of X after STY $FFFF
|
||||
Y: $88ff $ffff value from TRB/TSB $FFFF
|
||||
|
||||
|
||||
|
||||
---------------------------------------------------------------------
|
||||
Case 2: Wrap-around for Direct Page Indexed Addressing
|
||||
in Emulation Mode
|
||||
|
||||
### Variant 1
|
||||
|
||||
r
|
||||
# set M X E to "1"
|
||||
e
|
||||
# set DBR to 00
|
||||
|
||||
a 1000 sec
|
||||
xce emulation mode
|
||||
lda #$20 set highbyte
|
||||
xba
|
||||
lda #$00 lowbyte 16-bit akku
|
||||
tcd direct page address to $2000
|
||||
ldx #$20 index
|
||||
lda #$88 marker for wrap
|
||||
sta $2010
|
||||
lda #$77 marker for no-wrap
|
||||
sta $2110
|
||||
lda $f0,x which marker?
|
||||
stz $f0,x write (non-6502)
|
||||
brk
|
||||
|
||||
!d Jammon alternate display mode
|
||||
d disassemble on PC
|
||||
m 2010 show wrap area
|
||||
m 2110 show no-wrap area
|
||||
z 1000 start single-stepping
|
||||
z single step until BRK or ...
|
||||
g go to BRK
|
||||
|
||||
Results VICE WDC
|
||||
|
||||
$2010 00 00 wrap area: =00 from STZ
|
||||
$2110 77 77 no-wrap area
|
||||
|
||||
A: $2088 $2088 A low: 88 = wrap, 77 = no-wrap
|
||||
|
||||
|
||||
### Variant 2
|
||||
|
||||
Based on variant 1:
|
||||
|
||||
a 1005 lda #$01 direct page not aligned!
|
||||
tcd direct page at $2001
|
||||
ldx #$1f correction to hit the
|
||||
same memory locations
|
||||
for monitoring
|
||||
|
||||
g 1000
|
||||
|
||||
Results VICE WDC
|
||||
|
||||
$2011 88 88 wrap area: =00 from STZ
|
||||
$2111 00 00 no-wrap area
|
||||
|
||||
A: $2077 $2077 A low: 88 = wrap, 77 = no-wrap
|
||||
|
||||
@@ -91,6 +91,7 @@ SOURCES += \
|
||||
\
|
||||
$$SRC/Processors/6502/Implementation/*.cpp \
|
||||
$$SRC/Processors/6502/State/*.cpp \
|
||||
$$SRC/Processors/65816/Implementation/*.cpp \
|
||||
$$SRC/Processors/68000/Implementation/*.cpp \
|
||||
$$SRC/Processors/68000/State/*.cpp \
|
||||
$$SRC/Processors/Z80/Implementation/*.cpp \
|
||||
@@ -214,6 +215,10 @@ HEADERS += \
|
||||
$$SRC/Processors/6502/*.hpp \
|
||||
$$SRC/Processors/6502/Implementation/*.hpp \
|
||||
$$SRC/Processors/6502/State/*.hpp \
|
||||
$$SRC/Processors/6502Esque/*.hpp \
|
||||
$$SRC/Processors/6502Esque/Implementation/*.hpp \
|
||||
$$SRC/Processors/65816/*.hpp \
|
||||
$$SRC/Processors/65816/Implementation/*.hpp \
|
||||
$$SRC/Processors/68000/*.hpp \
|
||||
$$SRC/Processors/68000/Implementation/*.hpp \
|
||||
$$SRC/Processors/68000/State/*.hpp \
|
||||
|
||||
@@ -287,7 +287,7 @@ void MainWindow::launchMachine() {
|
||||
// Populate request text.
|
||||
QString requestText = romRequestBaseText;
|
||||
size_t index = 0;
|
||||
for(const auto rom: missingRoms) {
|
||||
for(const auto &rom: missingRoms) {
|
||||
requestText += "• ";
|
||||
requestText += rom.descriptive_name.c_str();
|
||||
|
||||
@@ -1020,7 +1020,7 @@ bool MainWindow::processEvent(QKeyEvent *event) {
|
||||
const auto keyboardMachine = machine->keyboard_machine();
|
||||
if(!keyboardMachine) return true;
|
||||
|
||||
auto keyboard = keyboardMachine->get_keyboard();
|
||||
auto &keyboard = keyboardMachine->get_keyboard();
|
||||
keyboard.set_key_pressed(*key, event->text().size() ? event->text()[0].toLatin1() : '\0', isPressed);
|
||||
if(keyboard.is_exclusive() || keyboard.observed_keys().find(*key) != keyboard.observed_keys().end()) {
|
||||
return false;
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -783,8 +783,10 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether a 'logical' keyboard has been requested.
|
||||
const bool logical_keyboard = arguments.selections.find("logical-keyboard") != arguments.selections.end();
|
||||
// Check whether a 'logical' keyboard has been requested, or the machine would prefer one anyway.
|
||||
const bool logical_keyboard =
|
||||
(arguments.selections.find("logical-keyboard") != arguments.selections.end()) ||
|
||||
(machine->keyboard_machine() && machine->keyboard_machine()->prefers_logical_input());
|
||||
if(logical_keyboard) {
|
||||
SDL_StartTextInput();
|
||||
}
|
||||
@@ -1162,13 +1164,8 @@ int main(int argc, char *argv[]) {
|
||||
} else {
|
||||
// This is a slightly terrible way of obtaining a symbol for the key, e.g. for letters it will always return
|
||||
// the capital letter version, at least empirically. But it'll have to do for now.
|
||||
//
|
||||
// TODO: ideally have a keyboard machine declare whether it wants either key events or text input? But that
|
||||
// doesn't match machines like the IIe that, to some extent, expose both. So then eliding as attempted above,
|
||||
// and keeping ephemeral track of which symbols have been tied to which keys for the benefit of future key up
|
||||
// events is probably the way forward?
|
||||
const char *key_name = SDL_GetKeyName(keypress.keycode);
|
||||
if(keyboard_machine->get_keyboard().set_key_pressed(key, key_name[0], keypress.is_down)) {
|
||||
if(keyboard_machine->get_keyboard().set_key_pressed(key, (strlen(key_name) == 1) ? key_name[0] : 0, keypress.is_down)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,8 +194,9 @@ std::vector<std::string> ScanTarget::bindings(ShaderType type) const {
|
||||
std::string ScanTarget::sampling_function() const {
|
||||
std::string fragment_shader;
|
||||
const auto modals = BufferingScanTarget::modals();
|
||||
const bool is_svideo = modals.display_type == DisplayType::SVideo;
|
||||
|
||||
if(modals.display_type == DisplayType::SVideo) {
|
||||
if(is_svideo) {
|
||||
fragment_shader +=
|
||||
"vec2 svideo_sample(vec2 coordinate, float angle) {";
|
||||
} else {
|
||||
@@ -203,7 +204,6 @@ std::string ScanTarget::sampling_function() const {
|
||||
"float composite_sample(vec2 coordinate, float angle) {";
|
||||
}
|
||||
|
||||
const bool is_svideo = modals.display_type == DisplayType::SVideo;
|
||||
switch(modals.input_data_type) {
|
||||
case InputDataType::Luminance1:
|
||||
case InputDataType::Luminance8:
|
||||
@@ -341,7 +341,7 @@ std::unique_ptr<Shader> ScanTarget::conversion_shader() const {
|
||||
vertex_shader +=
|
||||
"compositeAngle = (mix(startCompositeAngle, endCompositeAngle, lateral) / 32.0) * 3.141592654;"
|
||||
"compositeAmplitude = lineCompositeAmplitude / 255.0;"
|
||||
"oneOverCompositeAmplitude = mix(0.0, 255.0 / lineCompositeAmplitude, step(0.01, lineCompositeAmplitude));";
|
||||
"oneOverCompositeAmplitude = mix(0.0, 255.0 / lineCompositeAmplitude, step(0.95, lineCompositeAmplitude));";
|
||||
}
|
||||
|
||||
vertex_shader +=
|
||||
@@ -379,40 +379,42 @@ std::unique_ptr<Shader> ScanTarget::conversion_shader() const {
|
||||
|
||||
switch(modals.display_type) {
|
||||
case DisplayType::CompositeColour:
|
||||
fragment_shader +=
|
||||
"vec4 angles = compositeAngle + compositeAngleOffsets;"
|
||||
fragment_shader += R"x(
|
||||
vec4 angles = compositeAngle + compositeAngleOffsets;
|
||||
|
||||
// Sample four times over, at proper angle offsets.
|
||||
"vec4 samples = vec4("
|
||||
"composite_sample(textureCoordinates[0], angles.x),"
|
||||
"composite_sample(textureCoordinates[1], angles.y),"
|
||||
"composite_sample(textureCoordinates[2], angles.z),"
|
||||
"composite_sample(textureCoordinates[3], angles.w)"
|
||||
");"
|
||||
vec4 samples = vec4(
|
||||
composite_sample(textureCoordinates[0], angles.x),
|
||||
composite_sample(textureCoordinates[1], angles.y),
|
||||
composite_sample(textureCoordinates[2], angles.z),
|
||||
composite_sample(textureCoordinates[3], angles.w)
|
||||
);
|
||||
|
||||
// Compute a luminance for use if there's no colour information, now, before
|
||||
// modifying samples.
|
||||
"float mono_luminance = dot(samples, vec4(0.15, 0.35, 0.35, 0.15));"
|
||||
// The outer structure of the OpenGL scan target means in practice that
|
||||
// compositeAmplitude will be the same value across a piece of
|
||||
// geometry. I am therefore optimistic that this conditional will not
|
||||
// cause a divergence in fragment execution.
|
||||
if(compositeAmplitude < 0.01) {
|
||||
// Compute only a luminance for use if there's no colour information.
|
||||
fragColour3 = vec3(dot(samples, vec4(0.15, 0.35, 0.35, 0.15)));
|
||||
} else {
|
||||
// Take the average to calculate luminance, then subtract that from all four samples to
|
||||
// give chrominance.
|
||||
float luminance = dot(samples, vec4(0.25));
|
||||
|
||||
// Take the average to calculate luminance, then subtract that from all four samples to
|
||||
// give chrominance.
|
||||
"float luminance = dot(samples, vec4(0.25));"
|
||||
// Split and average chrominance.
|
||||
vec2 chrominances[4] = vec2[4](
|
||||
textureLod(qamTextureName, qamTextureCoordinates[0], 0).gb,
|
||||
textureLod(qamTextureName, qamTextureCoordinates[1], 0).gb,
|
||||
textureLod(qamTextureName, qamTextureCoordinates[2], 0).gb,
|
||||
textureLod(qamTextureName, qamTextureCoordinates[3], 0).gb
|
||||
);
|
||||
vec2 channels = (chrominances[0] + chrominances[1] + chrominances[2] + chrominances[3])*0.5 - vec2(1.0);
|
||||
|
||||
// Split and average chrominance.
|
||||
"vec2 chrominances[4] = vec2[4]("
|
||||
"textureLod(qamTextureName, qamTextureCoordinates[0], 0).gb,"
|
||||
"textureLod(qamTextureName, qamTextureCoordinates[1], 0).gb,"
|
||||
"textureLod(qamTextureName, qamTextureCoordinates[2], 0).gb,"
|
||||
"textureLod(qamTextureName, qamTextureCoordinates[3], 0).gb"
|
||||
");"
|
||||
"vec2 channels = (chrominances[0] + chrominances[1] + chrominances[2] + chrominances[3])*0.5 - vec2(1.0);"
|
||||
|
||||
// Apply a colour space conversion to get RGB.
|
||||
"fragColour3 = mix("
|
||||
"lumaChromaToRGB * vec3(luminance / (1.0 - compositeAmplitude), channels),"
|
||||
"vec3(mono_luminance),"
|
||||
"step(oneOverCompositeAmplitude, 0.01)"
|
||||
");";
|
||||
// Apply a colour space conversion to get RGB.
|
||||
fragColour3 = lumaChromaToRGB * vec3(luminance / (1.0 - compositeAmplitude), channels);
|
||||
}
|
||||
)x";
|
||||
break;
|
||||
|
||||
case DisplayType::CompositeMonochrome:
|
||||
@@ -622,7 +624,7 @@ std::unique_ptr<Shader> ScanTarget::qam_separation_shader() const {
|
||||
|
||||
"compositeAngle = compositeAngle * 2.0 * 3.141592654;"
|
||||
"compositeAmplitude = lineCompositeAmplitude / 255.0;"
|
||||
"oneOverCompositeAmplitude = mix(0.0, 255.0 / lineCompositeAmplitude, step(0.01, lineCompositeAmplitude));";
|
||||
"oneOverCompositeAmplitude = mix(0.0, 255.0 / lineCompositeAmplitude, step(0.95, lineCompositeAmplitude));";
|
||||
|
||||
if(is_svideo) {
|
||||
vertex_shader +=
|
||||
|
||||
@@ -13,24 +13,19 @@
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
|
||||
#include "../6502Esque/6502Esque.hpp"
|
||||
#include "../6502Esque/Implementation/LazyFlags.hpp"
|
||||
#include "../RegisterSizes.hpp"
|
||||
#include "../../ClockReceiver/ClockReceiver.hpp"
|
||||
|
||||
namespace CPU {
|
||||
namespace MOS6502 {
|
||||
|
||||
/*
|
||||
The list of registers that can be accessed via @c set_value_of_register and @c set_value_of_register.
|
||||
*/
|
||||
enum Register {
|
||||
LastOperationAddress,
|
||||
ProgramCounter,
|
||||
StackPointer,
|
||||
Flags,
|
||||
A,
|
||||
X,
|
||||
Y
|
||||
};
|
||||
// Adopt a bunch of things from MOS6502Esque.
|
||||
using BusOperation = CPU::MOS6502Esque::BusOperation;
|
||||
using BusHandler = CPU::MOS6502Esque::BusHandler<uint16_t>;
|
||||
using Register = CPU::MOS6502Esque::Register;
|
||||
using Flag = CPU::MOS6502Esque::Flag;
|
||||
|
||||
/*
|
||||
The list of 6502 variants supported by this implementation.
|
||||
@@ -48,76 +43,11 @@ enum Personality {
|
||||
#define has_bbrbbsrmbsmb(p) ((p) >= Personality::PRockwell65C02)
|
||||
#define has_stpwai(p) ((p) >= Personality::PWDC65C02)
|
||||
|
||||
/*
|
||||
Flags as defined on the 6502; can be used to decode the result of @c get_value_of_register(Flags) or to form a value for
|
||||
the corresponding set.
|
||||
*/
|
||||
enum Flag: uint8_t {
|
||||
Sign = 0x80,
|
||||
Overflow = 0x40,
|
||||
Always = 0x20,
|
||||
Break = 0x10,
|
||||
Decimal = 0x08,
|
||||
Interrupt = 0x04,
|
||||
Zero = 0x02,
|
||||
Carry = 0x01
|
||||
};
|
||||
|
||||
/*!
|
||||
Subclasses will be given the task of performing bus operations, allowing them to provide whatever interface they like
|
||||
between a 6502 and the rest of the system. @c BusOperation lists the types of bus operation that may be requested.
|
||||
|
||||
@c None is reserved for internal use. It will never be requested from a subclass. It is safe always to use the
|
||||
isReadOperation macro to make a binary choice between reading and writing.
|
||||
*/
|
||||
enum BusOperation {
|
||||
Read, ReadOpcode, Write, Ready, None
|
||||
};
|
||||
|
||||
/*!
|
||||
Evaluates to `true` if the operation is a read; `false` if it is a write.
|
||||
*/
|
||||
#define isReadOperation(v) (v == CPU::MOS6502::BusOperation::Read || v == CPU::MOS6502::BusOperation::ReadOpcode)
|
||||
|
||||
/*!
|
||||
An opcode that is guaranteed to cause the CPU to jam.
|
||||
*/
|
||||
extern const uint8_t JamOpcode;
|
||||
|
||||
/*!
|
||||
A class providing empty implementations of the methods a 6502 uses to access the bus. To wire the 6502 to a bus,
|
||||
machines should subclass BusHandler and then declare a realisation of the 6502 template, suplying their bus
|
||||
handler.
|
||||
*/
|
||||
class BusHandler {
|
||||
public:
|
||||
/*!
|
||||
Announces that the 6502 has performed the cycle defined by operation, address and value. On the 6502,
|
||||
all bus cycles take one clock cycle so the amoutn of time advanced is implicit.
|
||||
|
||||
@param operation The type of bus cycle: read, read opcode (i.e. read, with sync active),
|
||||
write or ready.
|
||||
@param address The value of the address bus during this bus cycle.
|
||||
@param value If this is a cycle that puts a value onto the data bus, *value is that value. If this is
|
||||
a cycle that reads the bus, the bus handler should write a value to *value. Writing to *value during
|
||||
a read cycle will produce undefined behaviour.
|
||||
|
||||
@returns The number of cycles that passed in objective time while this 6502 bus cycle was ongoing.
|
||||
On an archetypal machine this will be Cycles(1) but some architectures may choose not to clock the 6502
|
||||
during some periods; one way to simulate that is to have the bus handler return a number other than
|
||||
Cycles(1) to describe lengthened bus cycles.
|
||||
*/
|
||||
Cycles perform_bus_operation([[maybe_unused]] CPU::MOS6502::BusOperation operation, [[maybe_unused]] uint16_t address, [[maybe_unused]] uint8_t *value) {
|
||||
return Cycles(1);
|
||||
}
|
||||
|
||||
/*!
|
||||
Announces completion of all the cycles supplied to a .run_for request on the 6502. Intended to allow
|
||||
bus handlers to perform any deferred output work.
|
||||
*/
|
||||
void flush() {}
|
||||
};
|
||||
|
||||
#include "Implementation/6502Storage.hpp"
|
||||
|
||||
/*!
|
||||
@@ -205,12 +135,12 @@ class ProcessorBase: public ProcessorStorage {
|
||||
can also nominate whether the processor includes support for the ready line. Declining to support the ready line
|
||||
can produce a minor runtime performance improvement.
|
||||
*/
|
||||
template <Personality personality, typename T, bool uses_ready_line> class Processor: public ProcessorBase {
|
||||
template <Personality personality, typename BusHandler, bool uses_ready_line> class Processor: public ProcessorBase {
|
||||
public:
|
||||
/*!
|
||||
Constructs an instance of the 6502 that will use @c bus_handler for all bus communications.
|
||||
*/
|
||||
Processor(T &bus_handler) : ProcessorBase(personality), bus_handler_(bus_handler) {}
|
||||
Processor(BusHandler &bus_handler) : ProcessorBase(personality), bus_handler_(bus_handler) {}
|
||||
|
||||
/*!
|
||||
Runs the 6502 for a supplied number of cycles.
|
||||
@@ -227,7 +157,7 @@ template <Personality personality, typename T, bool uses_ready_line> class Proce
|
||||
void set_ready_line(bool active);
|
||||
|
||||
private:
|
||||
T &bus_handler_;
|
||||
BusHandler &bus_handler_;
|
||||
};
|
||||
|
||||
#include "Implementation/6502Implementation.hpp"
|
||||
|
||||
@@ -11,28 +11,51 @@
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
//#define BE_NOISY
|
||||
|
||||
using namespace CPU::MOS6502;
|
||||
|
||||
namespace {
|
||||
|
||||
template <Personality personality> class ConcreteAllRAMProcessor: public AllRAMProcessor, public BusHandler {
|
||||
using Type = CPU::MOS6502Esque::Type;
|
||||
|
||||
template <Type type> class ConcreteAllRAMProcessor: public AllRAMProcessor, public BusHandler {
|
||||
public:
|
||||
ConcreteAllRAMProcessor() :
|
||||
ConcreteAllRAMProcessor(size_t memory_size) :
|
||||
AllRAMProcessor(memory_size),
|
||||
mos6502_(*this) {
|
||||
mos6502_.set_power_on(false);
|
||||
}
|
||||
|
||||
inline Cycles perform_bus_operation(BusOperation operation, uint16_t address, uint8_t *value) {
|
||||
inline Cycles perform_bus_operation(BusOperation operation, uint32_t address, uint8_t *value) {
|
||||
timestamp_ += Cycles(1);
|
||||
|
||||
if(operation == BusOperation::ReadOpcode) {
|
||||
#ifdef BE_NOISY
|
||||
printf("[%04x] %02x a:%04x x:%04x y:%04x p:%02x s:%02x\n", address, memory_[address],
|
||||
mos6502_.get_value_of_register(Register::A),
|
||||
mos6502_.get_value_of_register(Register::X),
|
||||
mos6502_.get_value_of_register(Register::Y),
|
||||
mos6502_.get_value_of_register(Register::Flags) & 0xff,
|
||||
mos6502_.get_value_of_register(Register::StackPointer) & 0xff);
|
||||
#endif
|
||||
check_address_for_trap(address);
|
||||
}
|
||||
|
||||
if(isReadOperation(operation)) {
|
||||
*value = memory_[address];
|
||||
#ifdef BE_NOISY
|
||||
// if((address&0xff00) == 0x100) {
|
||||
printf("%04x -> %02x\n", address, *value);
|
||||
// }
|
||||
#endif
|
||||
} else {
|
||||
memory_[address] = *value;
|
||||
#ifdef BE_NOISY
|
||||
// if((address&0xff00) == 0x100) {
|
||||
printf("%04x <- %02x\n", address, *value);
|
||||
// }
|
||||
#endif
|
||||
}
|
||||
|
||||
return Cycles(1);
|
||||
@@ -63,20 +86,21 @@ template <Personality personality> class ConcreteAllRAMProcessor: public AllRAMP
|
||||
}
|
||||
|
||||
private:
|
||||
CPU::MOS6502::Processor<personality, ConcreteAllRAMProcessor, false> mos6502_;
|
||||
CPU::MOS6502Esque::Processor<type, ConcreteAllRAMProcessor, false> mos6502_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
AllRAMProcessor *AllRAMProcessor::Processor(Personality personality) {
|
||||
#define Bind(p) case p: return new ConcreteAllRAMProcessor<p>();
|
||||
switch(personality) {
|
||||
AllRAMProcessor *AllRAMProcessor::Processor(Type type) {
|
||||
#define Bind(p) case p: return new ConcreteAllRAMProcessor<p>(type == Type::TWDC65816 ? 16*1024*1024 : 64*1024);
|
||||
switch(type) {
|
||||
default:
|
||||
Bind(Personality::P6502)
|
||||
Bind(Personality::PNES6502)
|
||||
Bind(Personality::PSynertek65C02)
|
||||
Bind(Personality::PWDC65C02)
|
||||
Bind(Personality::PRockwell65C02)
|
||||
Bind(Type::T6502)
|
||||
Bind(Type::TNES6502)
|
||||
Bind(Type::TSynertek65C02)
|
||||
Bind(Type::TWDC65C02)
|
||||
Bind(Type::TRockwell65C02)
|
||||
Bind(Type::TWDC65816)
|
||||
}
|
||||
#undef Bind
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#ifndef MOS6502AllRAM_cpp
|
||||
#define MOS6502AllRAM_cpp
|
||||
|
||||
#include "../6502.hpp"
|
||||
#include "../../6502Esque/6502Selector.hpp"
|
||||
#include "../../AllRAMProcessor.hpp"
|
||||
|
||||
namespace CPU {
|
||||
@@ -17,9 +17,8 @@ namespace MOS6502 {
|
||||
|
||||
class AllRAMProcessor:
|
||||
public ::CPU::AllRAMProcessor {
|
||||
|
||||
public:
|
||||
static AllRAMProcessor *Processor(Personality personality);
|
||||
static AllRAMProcessor *Processor(CPU::MOS6502Esque::Type type);
|
||||
virtual ~AllRAMProcessor() {}
|
||||
|
||||
virtual void run_for(const Cycles cycles) = 0;
|
||||
@@ -30,7 +29,7 @@ class AllRAMProcessor:
|
||||
virtual void set_value_of_register(Register r, uint16_t value) = 0;
|
||||
|
||||
protected:
|
||||
AllRAMProcessor() : ::CPU::AllRAMProcessor(65536) {}
|
||||
AllRAMProcessor(size_t memory_size) : ::CPU::AllRAMProcessor(memory_size) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
|
||||
#include "../6502.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
using namespace CPU::MOS6502;
|
||||
|
||||
const uint8_t CPU::MOS6502::JamOpcode = 0xf2;
|
||||
|
||||
@@ -13,16 +13,6 @@
|
||||
*/
|
||||
|
||||
template <Personality personality, typename T, bool uses_ready_line> void Processor<personality, T, uses_ready_line>::run_for(const Cycles cycles) {
|
||||
static uint8_t throwaway_target;
|
||||
|
||||
// These plus program below act to give the compiler permission to update these values
|
||||
// without touching the class storage (i.e. it explicitly says they need be completely up
|
||||
// to date in this stack frame only); which saves some complicated addressing
|
||||
RegisterPair16 nextAddress = next_address_;
|
||||
BusOperation nextBusOperation = next_bus_operation_;
|
||||
uint16_t busAddress = bus_address_;
|
||||
uint8_t *busValue = bus_value_;
|
||||
|
||||
#define checkSchedule() \
|
||||
if(!scheduled_program_counter_) {\
|
||||
if(interrupt_requests_) {\
|
||||
@@ -42,9 +32,9 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
|
||||
#define bus_access() \
|
||||
interrupt_requests_ = (interrupt_requests_ & ~InterruptRequestFlags::IRQ) | irq_request_history_; \
|
||||
irq_request_history_ = irq_line_ & inverse_interrupt_flag_; \
|
||||
number_of_cycles -= bus_handler_.perform_bus_operation(nextBusOperation, busAddress, busValue); \
|
||||
nextBusOperation = BusOperation::None; \
|
||||
irq_request_history_ = irq_line_ & flags_.inverse_interrupt; \
|
||||
number_of_cycles -= bus_handler_.perform_bus_operation(next_bus_operation_, bus_address_, bus_value_); \
|
||||
next_bus_operation_ = BusOperation::None; \
|
||||
if(number_of_cycles <= Cycles(0)) break;
|
||||
|
||||
checkSchedule();
|
||||
@@ -54,12 +44,12 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
|
||||
// Deal with a potential RDY state, if this 6502 has anything connected to ready.
|
||||
while(uses_ready_line && ready_is_active_ && number_of_cycles > Cycles(0)) {
|
||||
number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, busAddress, busValue);
|
||||
number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, bus_address_, bus_value_);
|
||||
}
|
||||
|
||||
// Deal with a potential STP state, if this 6502 implements STP.
|
||||
while(has_stpwai(personality) && stop_is_active_ && number_of_cycles > Cycles(0)) {
|
||||
number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, busAddress, busValue);
|
||||
number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, bus_address_, bus_value_);
|
||||
if(interrupt_requests_ & InterruptRequestFlags::Reset) {
|
||||
stop_is_active_ = false;
|
||||
checkSchedule();
|
||||
@@ -69,8 +59,8 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
|
||||
// Deal with a potential WAI state, if this 6502 implements WAI.
|
||||
while(has_stpwai(personality) && wait_is_active_ && number_of_cycles > Cycles(0)) {
|
||||
number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, busAddress, busValue);
|
||||
interrupt_requests_ |= (irq_line_ & inverse_interrupt_flag_);
|
||||
number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, bus_address_, bus_value_);
|
||||
interrupt_requests_ |= (irq_line_ & flags_.inverse_interrupt);
|
||||
if(interrupt_requests_ & InterruptRequestFlags::NMI || irq_line_) {
|
||||
wait_is_active_ = false;
|
||||
checkSchedule();
|
||||
@@ -79,7 +69,7 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
}
|
||||
|
||||
if((!uses_ready_line || !ready_is_active_) && (!has_stpwai(personality) || (!wait_is_active_ && !stop_is_active_))) {
|
||||
if(nextBusOperation != BusOperation::None) {
|
||||
if(next_bus_operation_ != BusOperation::None) {
|
||||
bus_access();
|
||||
}
|
||||
|
||||
@@ -88,10 +78,10 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
const MicroOp cycle = *scheduled_program_counter_;
|
||||
scheduled_program_counter_++;
|
||||
|
||||
#define read_op(val, addr) nextBusOperation = BusOperation::ReadOpcode; busAddress = addr; busValue = &val; val = 0xff
|
||||
#define read_mem(val, addr) nextBusOperation = BusOperation::Read; busAddress = addr; busValue = &val; val = 0xff
|
||||
#define throwaway_read(addr) nextBusOperation = BusOperation::Read; busAddress = addr; busValue = &throwaway_target; throwaway_target = 0xff
|
||||
#define write_mem(val, addr) nextBusOperation = BusOperation::Write; busAddress = addr; busValue = &val
|
||||
#define read_op(val, addr) next_bus_operation_ = BusOperation::ReadOpcode; bus_address_ = addr; bus_value_ = &val; val = 0xff
|
||||
#define read_mem(val, addr) next_bus_operation_ = BusOperation::Read; bus_address_ = addr; bus_value_ = &val; val = 0xff
|
||||
#define throwaway_read(addr) next_bus_operation_ = BusOperation::Read; bus_address_ = addr; bus_value_ = &bus_throwaway_; bus_throwaway_ = 0xff
|
||||
#define write_mem(val, addr) next_bus_operation_ = BusOperation::Write; bus_address_ = addr; bus_value_ = &val
|
||||
|
||||
switch(cycle) {
|
||||
|
||||
@@ -155,37 +145,37 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
|
||||
case OperationBRKPickVector:
|
||||
if(is_65c02(personality)) {
|
||||
nextAddress.full = 0xfffe;
|
||||
next_address_.full = 0xfffe;
|
||||
} else {
|
||||
// NMI can usurp BRK-vector operations on the pre-C 6502s.
|
||||
nextAddress.full = (interrupt_requests_ & InterruptRequestFlags::NMI) ? 0xfffa : 0xfffe;
|
||||
next_address_.full = (interrupt_requests_ & InterruptRequestFlags::NMI) ? 0xfffa : 0xfffe;
|
||||
interrupt_requests_ &= ~InterruptRequestFlags::NMI;
|
||||
}
|
||||
continue;
|
||||
case OperationNMIPickVector: nextAddress.full = 0xfffa; continue;
|
||||
case OperationRSTPickVector: nextAddress.full = 0xfffc; continue;
|
||||
case CycleReadVectorLow: read_mem(pc_.halves.low, nextAddress.full); break;
|
||||
case CycleReadVectorHigh: read_mem(pc_.halves.high, nextAddress.full+1); break;
|
||||
case OperationNMIPickVector: next_address_.full = 0xfffa; continue;
|
||||
case OperationRSTPickVector: next_address_.full = 0xfffc; continue;
|
||||
case CycleReadVectorLow: read_mem(pc_.halves.low, next_address_.full); break;
|
||||
case CycleReadVectorHigh: read_mem(pc_.halves.high, next_address_.full+1); break;
|
||||
case OperationSetIRQFlags:
|
||||
inverse_interrupt_flag_ = 0;
|
||||
if(is_65c02(personality)) decimal_flag_ = false;
|
||||
flags_.inverse_interrupt = 0;
|
||||
if(is_65c02(personality)) flags_.decimal = 0;
|
||||
continue;
|
||||
case OperationSetNMIRSTFlags:
|
||||
if(is_65c02(personality)) decimal_flag_ = false;
|
||||
if(is_65c02(personality)) flags_.decimal = 0;
|
||||
continue;
|
||||
|
||||
case CyclePullPCL: s_++; read_mem(pc_.halves.low, s_ | 0x100); break;
|
||||
case CyclePullPCH: s_++; read_mem(pc_.halves.high, s_ | 0x100); break;
|
||||
case CyclePullA: s_++; read_mem(a_, s_ | 0x100); break;
|
||||
case CyclePullX: s_++; read_mem(x_, s_ | 0x100); break;
|
||||
case CyclePullY: s_++; read_mem(y_, s_ | 0x100); break;
|
||||
case CyclePullOperand: s_++; read_mem(operand_, s_ | 0x100); break;
|
||||
case OperationSetFlagsFromOperand: set_flags(operand_); continue;
|
||||
case OperationSetOperandFromFlagsWithBRKSet: operand_ = get_flags() | Flag::Break; continue;
|
||||
case OperationSetOperandFromFlags: operand_ = get_flags(); continue;
|
||||
case OperationSetFlagsFromA: zero_result_ = negative_result_ = a_; continue;
|
||||
case OperationSetFlagsFromX: zero_result_ = negative_result_ = x_; continue;
|
||||
case OperationSetFlagsFromY: zero_result_ = negative_result_ = y_; continue;
|
||||
case CyclePullPCL: s_++; read_mem(pc_.halves.low, s_ | 0x100); break;
|
||||
case CyclePullPCH: s_++; read_mem(pc_.halves.high, s_ | 0x100); break;
|
||||
case CyclePullA: s_++; read_mem(a_, s_ | 0x100); break;
|
||||
case CyclePullX: s_++; read_mem(x_, s_ | 0x100); break;
|
||||
case CyclePullY: s_++; read_mem(y_, s_ | 0x100); break;
|
||||
case CyclePullOperand: s_++; read_mem(operand_, s_ | 0x100); break;
|
||||
case OperationSetFlagsFromOperand: set_flags(operand_); continue;
|
||||
case OperationSetOperandFromFlagsWithBRKSet: operand_ = flags_.get() | Flag::Break; continue;
|
||||
case OperationSetOperandFromFlags: operand_ = flags_.get(); continue;
|
||||
case OperationSetFlagsFromA: flags_.set_nz(a_); continue;
|
||||
case OperationSetFlagsFromX: flags_.set_nz(x_); continue;
|
||||
case OperationSetFlagsFromY: flags_.set_nz(y_); continue;
|
||||
|
||||
case CycleIncrementPCAndReadStack: pc_.full++; throwaway_read(s_ | 0x100); break;
|
||||
case CycleReadPCLFromAddress: read_mem(pc_.halves.low, address_.full); break;
|
||||
@@ -216,17 +206,17 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
|
||||
// MARK: - Bitwise
|
||||
|
||||
case OperationORA: a_ |= operand_; negative_result_ = zero_result_ = a_; continue;
|
||||
case OperationAND: a_ &= operand_; negative_result_ = zero_result_ = a_; continue;
|
||||
case OperationEOR: a_ ^= operand_; negative_result_ = zero_result_ = a_; continue;
|
||||
case OperationORA: a_ |= operand_; flags_.set_nz(a_); continue;
|
||||
case OperationAND: a_ &= operand_; flags_.set_nz(a_); continue;
|
||||
case OperationEOR: a_ ^= operand_; flags_.set_nz(a_); continue;
|
||||
|
||||
// MARK: - Load and Store
|
||||
|
||||
case OperationLDA: a_ = negative_result_ = zero_result_ = operand_; continue;
|
||||
case OperationLDX: x_ = negative_result_ = zero_result_ = operand_; continue;
|
||||
case OperationLDY: y_ = negative_result_ = zero_result_ = operand_; continue;
|
||||
case OperationLAX: a_ = x_ = negative_result_ = zero_result_ = operand_; continue;
|
||||
case OperationCopyOperandToA: a_ = operand_; continue;
|
||||
case OperationLDA: flags_.set_nz(a_ = operand_); continue;
|
||||
case OperationLDX: flags_.set_nz(x_ = operand_); continue;
|
||||
case OperationLDY: flags_.set_nz(y_ = operand_); continue;
|
||||
case OperationLAX: flags_.set_nz(a_ = x_ = operand_); continue;
|
||||
case OperationCopyOperandToA: a_ = operand_; continue;
|
||||
|
||||
case OperationSTA: operand_ = a_; continue;
|
||||
case OperationSTX: operand_ = x_; continue;
|
||||
@@ -240,43 +230,43 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
|
||||
case OperationLXA:
|
||||
a_ = x_ = (a_ | 0xee) & operand_;
|
||||
negative_result_ = zero_result_ = a_;
|
||||
flags_.set_nz(a_);
|
||||
continue;
|
||||
|
||||
// MARK: - Compare
|
||||
|
||||
case OperationCMP: {
|
||||
const uint16_t temp16 = a_ - operand_;
|
||||
negative_result_ = zero_result_ = uint8_t(temp16);
|
||||
carry_flag_ = ((~temp16) >> 8)&1;
|
||||
flags_.set_nz(uint8_t(temp16));
|
||||
flags_.carry = ((~temp16) >> 8)&1;
|
||||
} continue;
|
||||
case OperationCPX: {
|
||||
const uint16_t temp16 = x_ - operand_;
|
||||
negative_result_ = zero_result_ = uint8_t(temp16);
|
||||
carry_flag_ = ((~temp16) >> 8)&1;
|
||||
flags_.set_nz(uint8_t(temp16));
|
||||
flags_.carry = ((~temp16) >> 8)&1;
|
||||
} continue;
|
||||
case OperationCPY: {
|
||||
const uint16_t temp16 = y_ - operand_;
|
||||
negative_result_ = zero_result_ = uint8_t(temp16);
|
||||
carry_flag_ = ((~temp16) >> 8)&1;
|
||||
flags_.set_nz(uint8_t(temp16));
|
||||
flags_.carry = ((~temp16) >> 8)&1;
|
||||
} continue;
|
||||
|
||||
// MARK: - BIT, TSB, TRB
|
||||
|
||||
case OperationBIT:
|
||||
zero_result_ = operand_ & a_;
|
||||
negative_result_ = operand_;
|
||||
overflow_flag_ = operand_&Flag::Overflow;
|
||||
flags_.zero_result = operand_ & a_;
|
||||
flags_.negative_result = operand_;
|
||||
flags_.overflow = operand_ & Flag::Overflow;
|
||||
continue;
|
||||
case OperationBITNoNV:
|
||||
zero_result_ = operand_ & a_;
|
||||
flags_.zero_result = operand_ & a_;
|
||||
continue;
|
||||
case OperationTRB:
|
||||
zero_result_ = operand_ & a_;
|
||||
flags_.zero_result = operand_ & a_;
|
||||
operand_ &= ~a_;
|
||||
continue;
|
||||
case OperationTSB:
|
||||
zero_result_ = operand_ & a_;
|
||||
flags_.zero_result = operand_ & a_;
|
||||
operand_ |= a_;
|
||||
continue;
|
||||
|
||||
@@ -295,8 +285,8 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
operand_++;
|
||||
[[fallthrough]];
|
||||
case OperationSBC:
|
||||
if(decimal_flag_ && has_decimal_mode(personality)) {
|
||||
const uint16_t notCarry = carry_flag_ ^ 0x1;
|
||||
if(flags_.decimal && has_decimal_mode(personality)) {
|
||||
const uint16_t notCarry = flags_.carry ^ 0x1;
|
||||
const uint16_t decimalResult = uint16_t(a_) - uint16_t(operand_) - notCarry;
|
||||
uint16_t temp16;
|
||||
|
||||
@@ -305,17 +295,17 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
temp16 = (temp16&0x0f) | ((temp16 > 0x0f) ? 0xfff0 : 0x00);
|
||||
temp16 += (a_&0xf0) - (operand_&0xf0);
|
||||
|
||||
overflow_flag_ = ( ( (decimalResult^a_)&(~decimalResult^operand_) )&0x80) >> 1;
|
||||
negative_result_ = uint8_t(temp16);
|
||||
zero_result_ = uint8_t(decimalResult);
|
||||
flags_.overflow = ( ( (decimalResult^a_)&(~decimalResult^operand_) )&0x80) >> 1;
|
||||
flags_.negative_result = uint8_t(temp16);
|
||||
flags_.zero_result = uint8_t(decimalResult);
|
||||
|
||||
if(temp16 > 0xff) temp16 -= 0x60;
|
||||
|
||||
carry_flag_ = (temp16 > 0xff) ? 0 : Flag::Carry;
|
||||
flags_.carry = (temp16 > 0xff) ? 0 : Flag::Carry;
|
||||
a_ = uint8_t(temp16);
|
||||
|
||||
if(is_65c02(personality)) {
|
||||
negative_result_ = zero_result_ = a_;
|
||||
flags_.set_nz(a_);
|
||||
read_mem(operand_, address_.full);
|
||||
break;
|
||||
}
|
||||
@@ -326,30 +316,30 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
[[fallthrough]];
|
||||
|
||||
case OperationADC:
|
||||
if(decimal_flag_ && has_decimal_mode(personality)) {
|
||||
const uint16_t decimalResult = uint16_t(a_) + uint16_t(operand_) + uint16_t(carry_flag_);
|
||||
if(flags_.decimal && has_decimal_mode(personality)) {
|
||||
const uint16_t decimalResult = uint16_t(a_) + uint16_t(operand_) + uint16_t(flags_.carry);
|
||||
|
||||
uint8_t low_nibble = (a_ & 0xf) + (operand_ & 0xf) + carry_flag_;
|
||||
uint8_t low_nibble = (a_ & 0xf) + (operand_ & 0xf) + flags_.carry;
|
||||
if(low_nibble >= 0xa) low_nibble = ((low_nibble + 0x6) & 0xf) + 0x10;
|
||||
uint16_t result = uint16_t(a_ & 0xf0) + uint16_t(operand_ & 0xf0) + uint16_t(low_nibble);
|
||||
negative_result_ = uint8_t(result);
|
||||
overflow_flag_ = (( (result^a_)&(result^operand_) )&0x80) >> 1;
|
||||
flags_.negative_result = uint8_t(result);
|
||||
flags_.overflow = (( (result^a_)&(result^operand_) )&0x80) >> 1;
|
||||
if(result >= 0xa0) result += 0x60;
|
||||
|
||||
carry_flag_ = (result >> 8) ? 1 : 0;
|
||||
flags_.carry = (result >> 8) ? 1 : 0;
|
||||
a_ = uint8_t(result);
|
||||
zero_result_ = uint8_t(decimalResult);
|
||||
flags_.zero_result = uint8_t(decimalResult);
|
||||
|
||||
if(is_65c02(personality)) {
|
||||
negative_result_ = zero_result_ = a_;
|
||||
flags_.set_nz(a_);
|
||||
read_mem(operand_, address_.full);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
const uint16_t result = uint16_t(a_) + uint16_t(operand_) + uint16_t(carry_flag_);
|
||||
overflow_flag_ = (( (result^a_)&(result^operand_) )&0x80) >> 1;
|
||||
negative_result_ = zero_result_ = a_ = uint8_t(result);
|
||||
carry_flag_ = (result >> 8)&1;
|
||||
const uint16_t result = uint16_t(a_) + uint16_t(operand_) + uint16_t(flags_.carry);
|
||||
flags_.overflow = (( (result^a_)&(result^operand_) )&0x80) >> 1;
|
||||
flags_.set_nz(a_ = uint8_t(result));
|
||||
flags_.carry = (result >> 8)&1;
|
||||
}
|
||||
|
||||
// fix up in case this was INS
|
||||
@@ -359,99 +349,99 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
// MARK: - Shifts and Rolls
|
||||
|
||||
case OperationASL:
|
||||
carry_flag_ = operand_ >> 7;
|
||||
flags_.carry = operand_ >> 7;
|
||||
operand_ <<= 1;
|
||||
negative_result_ = zero_result_ = operand_;
|
||||
flags_.set_nz(operand_);
|
||||
continue;
|
||||
|
||||
case OperationASO:
|
||||
carry_flag_ = operand_ >> 7;
|
||||
flags_.carry = operand_ >> 7;
|
||||
operand_ <<= 1;
|
||||
a_ |= operand_;
|
||||
negative_result_ = zero_result_ = a_;
|
||||
flags_.set_nz(a_);
|
||||
continue;
|
||||
|
||||
case OperationROL: {
|
||||
const uint8_t temp8 = uint8_t((operand_ << 1) | carry_flag_);
|
||||
carry_flag_ = operand_ >> 7;
|
||||
operand_ = negative_result_ = zero_result_ = temp8;
|
||||
const uint8_t temp8 = uint8_t((operand_ << 1) | flags_.carry);
|
||||
flags_.carry = operand_ >> 7;
|
||||
flags_.set_nz(operand_ = temp8);
|
||||
} continue;
|
||||
|
||||
case OperationRLA: {
|
||||
const uint8_t temp8 = uint8_t((operand_ << 1) | carry_flag_);
|
||||
carry_flag_ = operand_ >> 7;
|
||||
const uint8_t temp8 = uint8_t((operand_ << 1) | flags_.carry);
|
||||
flags_.carry = operand_ >> 7;
|
||||
operand_ = temp8;
|
||||
a_ &= operand_;
|
||||
negative_result_ = zero_result_ = a_;
|
||||
flags_.set_nz(a_);
|
||||
} continue;
|
||||
|
||||
case OperationLSR:
|
||||
carry_flag_ = operand_ & 1;
|
||||
flags_.carry = operand_ & 1;
|
||||
operand_ >>= 1;
|
||||
negative_result_ = zero_result_ = operand_;
|
||||
flags_.set_nz(operand_);
|
||||
continue;
|
||||
|
||||
case OperationLSE:
|
||||
carry_flag_ = operand_ & 1;
|
||||
flags_.carry = operand_ & 1;
|
||||
operand_ >>= 1;
|
||||
a_ ^= operand_;
|
||||
negative_result_ = zero_result_ = a_;
|
||||
flags_.set_nz(a_);
|
||||
continue;
|
||||
|
||||
case OperationASR:
|
||||
a_ &= operand_;
|
||||
carry_flag_ = a_ & 1;
|
||||
flags_.carry = a_ & 1;
|
||||
a_ >>= 1;
|
||||
negative_result_ = zero_result_ = a_;
|
||||
flags_.set_nz(a_);
|
||||
continue;
|
||||
|
||||
case OperationROR: {
|
||||
const uint8_t temp8 = uint8_t((operand_ >> 1) | (carry_flag_ << 7));
|
||||
carry_flag_ = operand_ & 1;
|
||||
operand_ = negative_result_ = zero_result_ = temp8;
|
||||
const uint8_t temp8 = uint8_t((operand_ >> 1) | (flags_.carry << 7));
|
||||
flags_.carry = operand_ & 1;
|
||||
flags_.set_nz(operand_ = temp8);
|
||||
} continue;
|
||||
|
||||
case OperationRRA: {
|
||||
const uint8_t temp8 = uint8_t((operand_ >> 1) | (carry_flag_ << 7));
|
||||
carry_flag_ = operand_ & 1;
|
||||
const uint8_t temp8 = uint8_t((operand_ >> 1) | (flags_.carry << 7));
|
||||
flags_.carry = operand_ & 1;
|
||||
operand_ = temp8;
|
||||
} continue;
|
||||
|
||||
case OperationDecrementOperand: operand_--; continue;
|
||||
case OperationIncrementOperand: operand_++; continue;
|
||||
|
||||
case OperationCLC: carry_flag_ = 0; continue;
|
||||
case OperationCLI: inverse_interrupt_flag_ = Flag::Interrupt; continue;
|
||||
case OperationCLV: overflow_flag_ = 0; continue;
|
||||
case OperationCLD: decimal_flag_ = 0; continue;
|
||||
case OperationCLC: flags_.carry = 0; continue;
|
||||
case OperationCLI: flags_.inverse_interrupt = Flag::Interrupt; continue;
|
||||
case OperationCLV: flags_.overflow = 0; continue;
|
||||
case OperationCLD: flags_.decimal = 0; continue;
|
||||
|
||||
case OperationSEC: carry_flag_ = Flag::Carry; continue;
|
||||
case OperationSEI: inverse_interrupt_flag_ = 0; continue;
|
||||
case OperationSED: decimal_flag_ = Flag::Decimal; continue;
|
||||
case OperationSEC: flags_.carry = Flag::Carry; continue;
|
||||
case OperationSEI: flags_.inverse_interrupt = 0; continue;
|
||||
case OperationSED: flags_.decimal = Flag::Decimal; continue;
|
||||
|
||||
case OperationINC: operand_++; negative_result_ = zero_result_ = operand_; continue;
|
||||
case OperationDEC: operand_--; negative_result_ = zero_result_ = operand_; continue;
|
||||
case OperationINA: a_++; negative_result_ = zero_result_ = a_; continue;
|
||||
case OperationDEA: a_--; negative_result_ = zero_result_ = a_; continue;
|
||||
case OperationINX: x_++; negative_result_ = zero_result_ = x_; continue;
|
||||
case OperationDEX: x_--; negative_result_ = zero_result_ = x_; continue;
|
||||
case OperationINY: y_++; negative_result_ = zero_result_ = y_; continue;
|
||||
case OperationDEY: y_--; negative_result_ = zero_result_ = y_; continue;
|
||||
case OperationINC: operand_++; flags_.set_nz(operand_); continue;
|
||||
case OperationDEC: operand_--; flags_.set_nz(operand_); continue;
|
||||
case OperationINA: a_++; flags_.set_nz(a_); continue;
|
||||
case OperationDEA: a_--; flags_.set_nz(a_); continue;
|
||||
case OperationINX: x_++; flags_.set_nz(x_); continue;
|
||||
case OperationDEX: x_--; flags_.set_nz(x_); continue;
|
||||
case OperationINY: y_++; flags_.set_nz(y_); continue;
|
||||
case OperationDEY: y_--; flags_.set_nz(y_); continue;
|
||||
|
||||
case OperationANE:
|
||||
a_ = (a_ | 0xee) & operand_ & x_;
|
||||
negative_result_ = zero_result_ = a_;
|
||||
flags_.set_nz(a_);
|
||||
continue;
|
||||
|
||||
case OperationANC:
|
||||
a_ &= operand_;
|
||||
negative_result_ = zero_result_ = a_;
|
||||
carry_flag_ = a_ >> 7;
|
||||
flags_.set_nz(a_);
|
||||
flags_.carry = a_ >> 7;
|
||||
continue;
|
||||
|
||||
case OperationLAS:
|
||||
a_ = x_ = s_ = s_ & operand_;
|
||||
negative_result_ = zero_result_ = a_;
|
||||
flags_.set_nz(a_);
|
||||
continue;
|
||||
|
||||
// MARK: - Addressing Mode Work
|
||||
@@ -464,36 +454,36 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
}
|
||||
|
||||
case CycleAddXToAddressLow:
|
||||
nextAddress.full = address_.full + x_;
|
||||
address_.halves.low = nextAddress.halves.low;
|
||||
if(address_.halves.high != nextAddress.halves.high) {
|
||||
next_address_.full = address_.full + x_;
|
||||
address_.halves.low = next_address_.halves.low;
|
||||
if(address_.halves.high != next_address_.halves.high) {
|
||||
page_crossing_stall_read();
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case CycleAddXToAddressLowRead:
|
||||
nextAddress.full = address_.full + x_;
|
||||
address_.halves.low = nextAddress.halves.low;
|
||||
next_address_.full = address_.full + x_;
|
||||
address_.halves.low = next_address_.halves.low;
|
||||
page_crossing_stall_read();
|
||||
break;
|
||||
case CycleAddYToAddressLow:
|
||||
nextAddress.full = address_.full + y_;
|
||||
address_.halves.low = nextAddress.halves.low;
|
||||
if(address_.halves.high != nextAddress.halves.high) {
|
||||
next_address_.full = address_.full + y_;
|
||||
address_.halves.low = next_address_.halves.low;
|
||||
if(address_.halves.high != next_address_.halves.high) {
|
||||
page_crossing_stall_read();
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case CycleAddYToAddressLowRead:
|
||||
nextAddress.full = address_.full + y_;
|
||||
address_.halves.low = nextAddress.halves.low;
|
||||
next_address_.full = address_.full + y_;
|
||||
address_.halves.low = next_address_.halves.low;
|
||||
page_crossing_stall_read();
|
||||
break;
|
||||
|
||||
#undef page_crossing_stall_read
|
||||
|
||||
case OperationCorrectAddressHigh:
|
||||
address_.full = nextAddress.full;
|
||||
address_.full = next_address_.full;
|
||||
continue;
|
||||
case CycleIncrementPCFetchAddressLowFromOperand:
|
||||
pc_.full++;
|
||||
@@ -548,7 +538,7 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
throwaway_read(operand_);
|
||||
break;
|
||||
|
||||
case OperationIncrementPC: pc_.full++; continue;
|
||||
case OperationIncrementPC: pc_.full++; continue;
|
||||
case CycleFetchOperandFromAddress: read_mem(operand_, address_.full); break;
|
||||
case CycleWriteOperandToAddress: write_mem(operand_, address_.full); break;
|
||||
|
||||
@@ -560,25 +550,25 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
scheduled_program_counter_ = operations_[size_t(OperationsSlot::DoBRA)]; \
|
||||
}
|
||||
|
||||
case OperationBPL: BRA(!(negative_result_&0x80)); continue;
|
||||
case OperationBMI: BRA(negative_result_&0x80); continue;
|
||||
case OperationBVC: BRA(!overflow_flag_); continue;
|
||||
case OperationBVS: BRA(overflow_flag_); continue;
|
||||
case OperationBCC: BRA(!carry_flag_); continue;
|
||||
case OperationBCS: BRA(carry_flag_); continue;
|
||||
case OperationBNE: BRA(zero_result_); continue;
|
||||
case OperationBEQ: BRA(!zero_result_); continue;
|
||||
case OperationBPL: BRA(!(flags_.negative_result&0x80)); continue;
|
||||
case OperationBMI: BRA(flags_.negative_result&0x80); continue;
|
||||
case OperationBVC: BRA(!flags_.overflow); continue;
|
||||
case OperationBVS: BRA(flags_.overflow); continue;
|
||||
case OperationBCC: BRA(!flags_.carry); continue;
|
||||
case OperationBCS: BRA(flags_.carry); continue;
|
||||
case OperationBNE: BRA(flags_.zero_result); continue;
|
||||
case OperationBEQ: BRA(!flags_.zero_result); continue;
|
||||
case OperationBRA: BRA(true); continue;
|
||||
|
||||
#undef BRA
|
||||
|
||||
case CycleAddSignedOperandToPC:
|
||||
nextAddress.full = uint16_t(pc_.full + int8_t(operand_));
|
||||
pc_.halves.low = nextAddress.halves.low;
|
||||
if(nextAddress.halves.high != pc_.halves.high) {
|
||||
uint16_t halfUpdatedPc = pc_.full;
|
||||
pc_.full = nextAddress.full;
|
||||
throwaway_read(halfUpdatedPc);
|
||||
next_address_.full = uint16_t(pc_.full + int8_t(operand_));
|
||||
pc_.halves.low = next_address_.halves.low;
|
||||
if(next_address_.halves.high != pc_.halves.high) {
|
||||
const uint16_t half_updated_pc = pc_.full;
|
||||
pc_.full = next_address_.full;
|
||||
throwaway_read(half_updated_pc);
|
||||
break;
|
||||
} else if(is_65c02(personality)) {
|
||||
// 65C02 modification to all branches: a branch that is taken but requires only a single cycle
|
||||
@@ -610,31 +600,31 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
|
||||
// MARK: - Transfers
|
||||
|
||||
case OperationTXA: zero_result_ = negative_result_ = a_ = x_; continue;
|
||||
case OperationTYA: zero_result_ = negative_result_ = a_ = y_; continue;
|
||||
case OperationTXS: s_ = x_; continue;
|
||||
case OperationTAY: zero_result_ = negative_result_ = y_ = a_; continue;
|
||||
case OperationTAX: zero_result_ = negative_result_ = x_ = a_; continue;
|
||||
case OperationTSX: zero_result_ = negative_result_ = x_ = s_; continue;
|
||||
case OperationTXA: flags_.set_nz(a_ = x_); continue;
|
||||
case OperationTYA: flags_.set_nz(a_ = y_); continue;
|
||||
case OperationTXS: s_ = x_; continue;
|
||||
case OperationTAY: flags_.set_nz(y_ = a_); continue;
|
||||
case OperationTAX: flags_.set_nz(x_ = a_); continue;
|
||||
case OperationTSX: flags_.set_nz(x_ = s_); continue;
|
||||
|
||||
case OperationARR:
|
||||
if(decimal_flag_) {
|
||||
if(flags_.decimal) {
|
||||
a_ &= operand_;
|
||||
uint8_t unshiftedA = a_;
|
||||
a_ = uint8_t((a_ >> 1) | (carry_flag_ << 7));
|
||||
zero_result_ = negative_result_ = a_;
|
||||
overflow_flag_ = (a_^(a_ << 1))&Flag::Overflow;
|
||||
a_ = uint8_t((a_ >> 1) | (flags_.carry << 7));
|
||||
flags_.set_nz(a_);
|
||||
flags_.overflow = (a_^(a_ << 1))&Flag::Overflow;
|
||||
|
||||
if((unshiftedA&0xf) + (unshiftedA&0x1) > 5) a_ = ((a_ + 6)&0xf) | (a_ & 0xf0);
|
||||
|
||||
carry_flag_ = ((unshiftedA&0xf0) + (unshiftedA&0x10) > 0x50) ? 1 : 0;
|
||||
if(carry_flag_) a_ += 0x60;
|
||||
flags_.carry = ((unshiftedA&0xf0) + (unshiftedA&0x10) > 0x50) ? 1 : 0;
|
||||
if(flags_.carry) a_ += 0x60;
|
||||
} else {
|
||||
a_ &= operand_;
|
||||
a_ = uint8_t((a_ >> 1) | (carry_flag_ << 7));
|
||||
negative_result_ = zero_result_ = a_;
|
||||
carry_flag_ = (a_ >> 6)&1;
|
||||
overflow_flag_ = (a_^(a_ << 1))&Flag::Overflow;
|
||||
a_ = uint8_t((a_ >> 1) | (flags_.carry << 7));
|
||||
flags_.set_nz(a_);
|
||||
flags_.carry = (a_ >> 6)&1;
|
||||
flags_.overflow = (a_^(a_ << 1))&Flag::Overflow;
|
||||
}
|
||||
continue;
|
||||
|
||||
@@ -642,15 +632,15 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
x_ &= a_;
|
||||
uint16_t difference = x_ - operand_;
|
||||
x_ = uint8_t(difference);
|
||||
negative_result_ = zero_result_ = x_;
|
||||
carry_flag_ = ((difference >> 8)&1)^1;
|
||||
flags_.set_nz(x_);
|
||||
flags_.carry = ((difference >> 8)&1)^1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(has_stpwai(personality) && (stop_is_active_ || wait_is_active_)) {
|
||||
break;
|
||||
}
|
||||
if(uses_ready_line && ready_line_is_enabled_ && (is_65c02(personality) || isReadOperation(nextBusOperation))) {
|
||||
if(uses_ready_line && ready_line_is_enabled_ && (is_65c02(personality) || isReadOperation(next_bus_operation_))) {
|
||||
ready_is_active_ = true;
|
||||
break;
|
||||
}
|
||||
@@ -660,11 +650,6 @@ template <Personality personality, typename T, bool uses_ready_line> void Proces
|
||||
}
|
||||
|
||||
cycles_left_to_run_ = number_of_cycles;
|
||||
next_address_ = nextAddress;
|
||||
next_bus_operation_ = nextBusOperation;
|
||||
bus_address_ = busAddress;
|
||||
bus_value_ = busValue;
|
||||
|
||||
bus_handler_.flush();
|
||||
}
|
||||
|
||||
@@ -691,13 +676,13 @@ void ProcessorBase::set_power_on(bool active) {
|
||||
}
|
||||
|
||||
void ProcessorBase::set_irq_line(bool active) {
|
||||
irq_line_ = active ? Flag::Interrupt : 0;
|
||||
irq_line_ = active ? MOS6502Esque::Flag::Interrupt : 0;
|
||||
}
|
||||
|
||||
void ProcessorBase::set_overflow_line(bool active) {
|
||||
// a leading edge will set the overflow flag
|
||||
if(active && !set_overflow_line_is_enabled_)
|
||||
overflow_flag_ = Flag::Overflow;
|
||||
flags_.overflow = MOS6502Esque::Flag::Overflow;
|
||||
set_overflow_line_is_enabled_ = active;
|
||||
}
|
||||
|
||||
@@ -709,14 +694,9 @@ void ProcessorBase::set_nmi_line(bool active) {
|
||||
}
|
||||
|
||||
uint8_t ProcessorStorage::get_flags() const {
|
||||
return carry_flag_ | overflow_flag_ | (inverse_interrupt_flag_ ^ Flag::Interrupt) | (negative_result_ & 0x80) | (zero_result_ ? 0 : Flag::Zero) | Flag::Always | decimal_flag_;
|
||||
return flags_.get();
|
||||
}
|
||||
|
||||
void ProcessorStorage::set_flags(uint8_t flags) {
|
||||
carry_flag_ = flags & Flag::Carry;
|
||||
negative_result_ = flags & Flag::Sign;
|
||||
zero_result_ = (~flags) & Flag::Zero;
|
||||
overflow_flag_ = flags & Flag::Overflow;
|
||||
inverse_interrupt_flag_ = (~flags) & Flag::Interrupt;
|
||||
decimal_flag_ = flags & Flag::Decimal;
|
||||
flags_.set(flags);
|
||||
}
|
||||
|
||||
@@ -76,12 +76,6 @@ using namespace CPU::MOS6502;
|
||||
#define JAM {CycleFetchOperand, OperationScheduleJam}
|
||||
|
||||
ProcessorStorage::ProcessorStorage(Personality personality) {
|
||||
// only the interrupt flag is defined upon reset but get_flags isn't going to
|
||||
// mask the other flags so we need to do that, at least
|
||||
carry_flag_ &= Flag::Carry;
|
||||
decimal_flag_ &= Flag::Decimal;
|
||||
overflow_flag_ &= Flag::Overflow;
|
||||
|
||||
const InstructionList operations_6502[] = {
|
||||
/* 0x00 BRK */ Program(CycleIncPCPushPCH, CyclePushPCL, OperationBRKPickVector, OperationSetOperandFromFlagsWithBRKSet, CyclePushOperand, OperationSetIRQFlags, CycleReadVectorLow, CycleReadVectorHigh),
|
||||
/* 0x01 ORA x, ind */ IndexedIndirectRead(OperationORA),
|
||||
|
||||
@@ -6,9 +6,6 @@
|
||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef MOS6502Storage_h
|
||||
#define MOS6502Storage_h
|
||||
|
||||
/*!
|
||||
A repository for all the internal state of a CPU::MOS6502::Processor; extracted into a separate base
|
||||
class in order to remove it from visibility within the main 6502.hpp.
|
||||
@@ -231,7 +228,7 @@ class ProcessorStorage {
|
||||
*/
|
||||
RegisterPair16 pc_, last_operation_pc_;
|
||||
uint8_t a_, x_, y_, s_ = 0;
|
||||
uint8_t carry_flag_, negative_result_, zero_result_, decimal_flag_, overflow_flag_, inverse_interrupt_flag_ = 0;
|
||||
MOS6502Esque::LazyFlags flags_;
|
||||
|
||||
/*
|
||||
Temporary state for the micro programs.
|
||||
@@ -246,6 +243,7 @@ class ProcessorStorage {
|
||||
BusOperation next_bus_operation_ = BusOperation::None;
|
||||
uint16_t bus_address_;
|
||||
uint8_t *bus_value_;
|
||||
static inline uint8_t bus_throwaway_;
|
||||
|
||||
/*!
|
||||
Gets the flags register.
|
||||
@@ -270,7 +268,7 @@ class ProcessorStorage {
|
||||
|
||||
enum InterruptRequestFlags: uint8_t {
|
||||
Reset = 0x80,
|
||||
IRQ = Flag::Interrupt,
|
||||
IRQ = MOS6502Esque::Flag::Interrupt,
|
||||
NMI = 0x20,
|
||||
|
||||
PowerOn = 0x10,
|
||||
@@ -288,5 +286,3 @@ class ProcessorStorage {
|
||||
// Allow state objects to capture and apply state.
|
||||
friend struct State;
|
||||
};
|
||||
|
||||
#endif /* _502Storage_h */
|
||||
|
||||
155
Processors/6502Esque/6502Esque.hpp
Normal file
155
Processors/6502Esque/6502Esque.hpp
Normal file
@@ -0,0 +1,155 @@
|
||||
//
|
||||
// 6502Esque.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 28/09/2020.
|
||||
// Copyright © 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef m6502Esque_h
|
||||
#define m6502Esque_h
|
||||
|
||||
#include "../../ClockReceiver/ClockReceiver.hpp"
|
||||
|
||||
/*
|
||||
This file defines how the CPU-controlled part of a bus looks for the 6502 and
|
||||
for other processors with a sufficiently-similar bus.
|
||||
|
||||
I'm not yet a big fan of the name I've used here, and I'm still on the fence
|
||||
about what to do when eventually I get around to the 6800 and/or 6809, which have
|
||||
very similar bus characteristics.
|
||||
|
||||
So: this is _very_ provisional stuff.
|
||||
*/
|
||||
namespace CPU {
|
||||
namespace MOS6502Esque {
|
||||
|
||||
/*
|
||||
The list of registers that can be accessed via @c set_value_of_register and @c set_value_of_register.
|
||||
*/
|
||||
enum Register {
|
||||
LastOperationAddress,
|
||||
ProgramCounter,
|
||||
StackPointer,
|
||||
Flags,
|
||||
A,
|
||||
X,
|
||||
Y,
|
||||
|
||||
// These exist on a 65816 only.
|
||||
EmulationFlag,
|
||||
DataBank,
|
||||
ProgramBank,
|
||||
Direct
|
||||
};
|
||||
|
||||
/*
|
||||
Flags as defined on the 6502; can be used to decode the result of @c get_value_of_register(Flags) or to form a value for
|
||||
the corresponding set.
|
||||
*/
|
||||
enum Flag: uint8_t {
|
||||
Sign = 0x80,
|
||||
Overflow = 0x40,
|
||||
Always = 0x20,
|
||||
Break = 0x10,
|
||||
Decimal = 0x08,
|
||||
Interrupt = 0x04,
|
||||
Zero = 0x02,
|
||||
Carry = 0x01,
|
||||
|
||||
// These are available on a 65816 only.
|
||||
MemorySize = 0x20,
|
||||
IndexSize = 0x10,
|
||||
};
|
||||
|
||||
/*!
|
||||
Bus handlers will be given the task of performing bus operations, allowing them to provide whatever interface they like
|
||||
between a 6502-esque chip and the rest of the system. @c BusOperation lists the types of bus operation that may be requested.
|
||||
*/
|
||||
enum BusOperation {
|
||||
/// 6502: indicates that a read was signalled.
|
||||
/// 65816: indicates that a read was signalled with VDA.
|
||||
Read,
|
||||
/// 6502: indicates that a read was signalled with SYNC.
|
||||
/// 65816: indicates that a read was signalled with VDA and VPA.
|
||||
ReadOpcode,
|
||||
/// 6502: never signalled.
|
||||
/// 65816: indicates that a read was signalled with VPA.
|
||||
ReadProgram,
|
||||
/// 6502: never signalled.
|
||||
/// 65816: indicates that a read was signalled with VPB.
|
||||
ReadVector,
|
||||
/// 6502: never signalled.
|
||||
/// 65816: indicates that a read was signalled, but neither VDA nor VPA were active.
|
||||
InternalOperationRead,
|
||||
|
||||
/// 6502: indicates that a write was signalled.
|
||||
/// 65816: indicates that a write was signalled with VDA.
|
||||
Write,
|
||||
/// 6502: never signalled.
|
||||
/// 65816: indicates that a write was signalled, but neither VDA nor VPA were active.
|
||||
InternalOperationWrite,
|
||||
|
||||
/// All processors: indicates that the processor is paused due to the RDY input.
|
||||
/// 65C02 and 65816: indicates a WAI is ongoing.
|
||||
Ready,
|
||||
|
||||
/// 65C02 and 65816: indicates a STP condition.
|
||||
None,
|
||||
};
|
||||
|
||||
/*!
|
||||
For a machine watching only the RWB line, evaluates to @c true if the operation should be treated as a read; @c false otherwise.
|
||||
*/
|
||||
#define isReadOperation(v) (v <= CPU::MOS6502Esque::InternalOperationRead)
|
||||
|
||||
/*!
|
||||
For a machine watching only the RWB line, evaluates to @c true if the operation is any sort of write; @c false otherwise.
|
||||
*/
|
||||
#define isWriteOperation(v) (v == CPU::MOS6502Esque::Write || v == CPU::MOS6502Esque::InternalOperationWrite)
|
||||
|
||||
/*!
|
||||
Evaluates to @c true if the operation actually expects a response; @c false otherwise.
|
||||
*/
|
||||
#define isAccessOperation(v) ((v < CPU::MOS6502Esque::Ready) && (v != CPU::MOS6502Esque::InternalOperationRead) && (v != CPU::MOS6502Esque::InternalOperationWrite))
|
||||
|
||||
/*!
|
||||
A class providing empty implementations of the methods a 6502 uses to access the bus. To wire the 6502 to a bus,
|
||||
machines should subclass BusHandler and then declare a realisation of the 6502 template, suplying their bus
|
||||
handler.
|
||||
*/
|
||||
template <typename addr_t> class BusHandler {
|
||||
public:
|
||||
using AddressType = addr_t;
|
||||
|
||||
/*!
|
||||
Announces that the 6502 has performed the cycle defined by operation, address and value. On the 6502,
|
||||
all bus cycles take one clock cycle so the amoutn of time advanced is implicit.
|
||||
|
||||
@param operation The type of bus cycle: read, read opcode (i.e. read, with sync active),
|
||||
write or ready.
|
||||
@param address The value of the address bus during this bus cycle.
|
||||
@param value If this is a cycle that puts a value onto the data bus, *value is that value. If this is
|
||||
a cycle that reads the bus, the bus handler should write a value to *value. Writing to *value during
|
||||
a read cycle will produce undefined behaviour.
|
||||
|
||||
@returns The number of cycles that passed in objective time while this 6502 bus cycle was ongoing.
|
||||
On an archetypal machine this will be Cycles(1) but some architectures may choose not to clock the 6502
|
||||
during some periods; one way to simulate that is to have the bus handler return a number other than
|
||||
Cycles(1) to describe lengthened bus cycles.
|
||||
*/
|
||||
Cycles perform_bus_operation([[maybe_unused]] BusOperation operation, [[maybe_unused]] addr_t address, [[maybe_unused]] uint8_t *value) {
|
||||
return Cycles(1);
|
||||
}
|
||||
|
||||
/*!
|
||||
Announces completion of all the cycles supplied to a .run_for request on the 6502. Intended to allow
|
||||
bus handlers to perform any deferred output work.
|
||||
*/
|
||||
void flush() {}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* m6502Esque_h */
|
||||
46
Processors/6502Esque/6502Selector.hpp
Normal file
46
Processors/6502Esque/6502Selector.hpp
Normal file
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// 6502Selector.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 28/09/2020.
|
||||
// Copyright © 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef _502Selector_h
|
||||
#define _502Selector_h
|
||||
|
||||
#include "../6502/6502.hpp"
|
||||
#include "../65816/65816.hpp"
|
||||
|
||||
namespace CPU {
|
||||
namespace MOS6502Esque {
|
||||
|
||||
enum class Type {
|
||||
TNES6502, // the NES's 6502, which is like a 6502 but lacks decimal mode (though it retains the decimal flag)
|
||||
T6502, // the original [NMOS] 6502, replete with various undocumented instructions
|
||||
TSynertek65C02, // a 6502 extended with BRA, P[H/L][X/Y], STZ, TRB, TSB and the (zp) addressing mode and a few other additions
|
||||
TRockwell65C02, // like the Synertek, but with BBR, BBS, RMB and SMB
|
||||
TWDC65C02, // like the Rockwell, but with STP and WAI
|
||||
TWDC65816, // the slightly 16-bit follow-up to the 6502
|
||||
};
|
||||
|
||||
/*
|
||||
Machines that can use either a 6502 or a 65816 can use CPU::MOS6502Esque::Processor in order to select the proper
|
||||
class in much the same way that a raw user of CPU::MOS6502::Processor would set the personality. Just provide one
|
||||
of the type enums as above as the appropriate template parameter.
|
||||
*/
|
||||
|
||||
template <Type processor_type, typename BusHandler, bool uses_ready_line> class Processor:
|
||||
public CPU::MOS6502::Processor<CPU::MOS6502::Personality(processor_type), BusHandler, uses_ready_line> {
|
||||
using CPU::MOS6502::Processor<CPU::MOS6502::Personality(processor_type), BusHandler, uses_ready_line>::Processor;
|
||||
};
|
||||
|
||||
template <typename BusHandler, bool uses_ready_line> class Processor<Type::TWDC65816, BusHandler, uses_ready_line>:
|
||||
public CPU::WDC65816::Processor<BusHandler, uses_ready_line> {
|
||||
using CPU::WDC65816::Processor<BusHandler, uses_ready_line>::Processor;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* _502Selector_h */
|
||||
83
Processors/6502Esque/Implementation/LazyFlags.hpp
Normal file
83
Processors/6502Esque/Implementation/LazyFlags.hpp
Normal file
@@ -0,0 +1,83 @@
|
||||
//
|
||||
// LazyFlags.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 05/10/2020.
|
||||
// Copyright © 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef LazyFlags_h
|
||||
#define LazyFlags_h
|
||||
|
||||
#include "../6502Esque.hpp"
|
||||
|
||||
namespace CPU {
|
||||
namespace MOS6502Esque {
|
||||
|
||||
struct LazyFlags {
|
||||
/// Bit 7 is set if the negative flag is set; otherwise it is clear.
|
||||
uint8_t negative_result = 0;
|
||||
|
||||
/// Non-zero if the zero flag is clear, zero if it is set.
|
||||
uint8_t zero_result = 0;
|
||||
|
||||
/// Contains Flag::Carry.
|
||||
uint8_t carry = 0;
|
||||
|
||||
/// Contains Flag::Decimal.
|
||||
uint8_t decimal = 0;
|
||||
|
||||
/// Contains Flag::Overflow.
|
||||
uint8_t overflow = 0;
|
||||
|
||||
/// Contains Flag::Interrupt, complemented.
|
||||
uint8_t inverse_interrupt = 0;
|
||||
|
||||
/// Sets N and Z flags per the 8-bit value @c value.
|
||||
void set_nz(uint8_t value) {
|
||||
zero_result = negative_result = value;
|
||||
}
|
||||
|
||||
/// Sets N and Z flags per the 8- or 16-bit value @c value; @c shift should be 0 to indicate an 8-bit value or 8 to indicate a 16-bit value.
|
||||
void set_nz(uint16_t value, int shift) {
|
||||
negative_result = uint8_t(value >> shift);
|
||||
zero_result = uint8_t(value | (value >> shift));
|
||||
}
|
||||
|
||||
/// Sets the Z flag per the 8- or 16-bit value @c value; @c shift should be 0 to indicate an 8-bit value or 8 to indicate a 16-bit value.
|
||||
void set_z(uint16_t value, int shift) {
|
||||
zero_result = uint8_t(value | (value >> shift));
|
||||
}
|
||||
|
||||
/// Sets the N flag per the 8- or 16-bit value @c value; @c shift should be 0 to indicate an 8-bit value or 8 to indicate a 16-bit value.
|
||||
void set_n(uint16_t value, int shift) {
|
||||
negative_result = uint8_t(value >> shift);
|
||||
}
|
||||
|
||||
void set(uint8_t flags) {
|
||||
carry = flags & Flag::Carry;
|
||||
negative_result = flags & Flag::Sign;
|
||||
zero_result = (~flags) & Flag::Zero;
|
||||
overflow = flags & Flag::Overflow;
|
||||
inverse_interrupt = (~flags) & Flag::Interrupt;
|
||||
decimal = flags & Flag::Decimal;
|
||||
}
|
||||
|
||||
uint8_t get() const {
|
||||
return carry | overflow | (inverse_interrupt ^ Flag::Interrupt) | (negative_result & 0x80) | (zero_result ? 0 : Flag::Zero) | Flag::Always | decimal;
|
||||
}
|
||||
|
||||
LazyFlags() {
|
||||
// Only the interrupt flag is defined upon reset but get_flags isn't going to
|
||||
// mask the other flags so we need to do that, at least.
|
||||
carry &= Flag::Carry;
|
||||
decimal &= Flag::Decimal;
|
||||
overflow &= Flag::Overflow;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* LazyFlags_h */
|
||||
5
Processors/6502Esque/README.md
Normal file
5
Processors/6502Esque/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# 6502Esque
|
||||
|
||||
This folder contains common code for CPUs for a 6502-esque bus interface; it also contains a special template, the 6502Selector, which allows a consumer to select between the 6502-esque chips by enum.
|
||||
|
||||
If you know exactly which processor you want, feel free to ignore this folder entirely; just go straight to the 6502, 65816 or whatever.
|
||||
90
Processors/65816/65816.hpp
Normal file
90
Processors/65816/65816.hpp
Normal file
@@ -0,0 +1,90 @@
|
||||
//
|
||||
// 65816.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 23/09/2020.
|
||||
// Copyright © 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef WDC65816_hpp
|
||||
#define WDC65816_hpp
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "../RegisterSizes.hpp"
|
||||
#include "../../ClockReceiver/ClockReceiver.hpp"
|
||||
#include "../6502Esque/6502Esque.hpp"
|
||||
#include "../6502Esque/Implementation/LazyFlags.hpp"
|
||||
|
||||
namespace CPU {
|
||||
namespace WDC65816 {
|
||||
|
||||
using BusOperation = CPU::MOS6502Esque::BusOperation;
|
||||
using Register = CPU::MOS6502Esque::Register;
|
||||
using Flag = CPU::MOS6502Esque::Flag;
|
||||
|
||||
enum ExtendedBusOutput {
|
||||
Emulation = (1 << 0),
|
||||
MemorySize = (1 << 1),
|
||||
IndexSize = (1 << 2),
|
||||
MemoryLock = (1 << 3),
|
||||
};
|
||||
|
||||
#include "Implementation/65816Storage.hpp"
|
||||
|
||||
class ProcessorBase: protected ProcessorStorage {
|
||||
public:
|
||||
inline void set_power_on(bool);
|
||||
inline void set_irq_line(bool);
|
||||
inline void set_nmi_line(bool);
|
||||
inline void set_reset_line(bool);
|
||||
inline void set_abort_line(bool);
|
||||
inline bool get_is_resetting() const;
|
||||
|
||||
/*!
|
||||
Returns the current state of all lines not ordinarily pushed to the BusHandler.
|
||||
*/
|
||||
inline int get_extended_bus_output();
|
||||
|
||||
/*!
|
||||
Provided for symmetry with the 6502; a 65816 is never jammed.
|
||||
*/
|
||||
inline bool is_jammed() const;
|
||||
|
||||
void set_value_of_register(Register r, uint16_t value);
|
||||
uint16_t get_value_of_register(Register r) const;
|
||||
};
|
||||
|
||||
template <typename BusHandler, bool uses_ready_line> class Processor: public ProcessorBase {
|
||||
public:
|
||||
/*!
|
||||
Constructs an instance of the 6502 that will use @c bus_handler for all bus communications.
|
||||
*/
|
||||
Processor(BusHandler &bus_handler) : bus_handler_(bus_handler) {}
|
||||
|
||||
/*!
|
||||
Runs the 6502 for a supplied number of cycles.
|
||||
|
||||
@param cycles The number of cycles to run the 6502 for.
|
||||
*/
|
||||
void run_for(const Cycles cycles);
|
||||
|
||||
/*!
|
||||
Sets the current level of the RDY line.
|
||||
|
||||
@param active @c true if the line is logically active; @c false otherwise.
|
||||
*/
|
||||
void set_ready_line(bool active);
|
||||
|
||||
private:
|
||||
BusHandler &bus_handler_;
|
||||
};
|
||||
|
||||
#include "Implementation/65816Implementation.hpp"
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* WDC65816_hpp */
|
||||
44
Processors/65816/Implementation/65816Base.cpp
Normal file
44
Processors/65816/Implementation/65816Base.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
//
|
||||
// 65816Base.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 28/09/2020.
|
||||
// Copyright © 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "../65816.hpp"
|
||||
|
||||
using namespace CPU::WDC65816;
|
||||
|
||||
uint16_t ProcessorBase::get_value_of_register(Register r) const {
|
||||
switch (r) {
|
||||
case Register::ProgramCounter: return registers_.pc;
|
||||
case Register::LastOperationAddress: return last_operation_pc_;
|
||||
case Register::StackPointer: return registers_.s.full;
|
||||
case Register::Flags: return get_flags();
|
||||
case Register::A: return registers_.a.full;
|
||||
case Register::X: return registers_.x.full;
|
||||
case Register::Y: return registers_.y.full;
|
||||
case Register::EmulationFlag: return registers_.emulation_flag;
|
||||
case Register::DataBank: return registers_.data_bank >> 16;
|
||||
case Register::ProgramBank: return registers_.program_bank >> 16;
|
||||
case Register::Direct: return registers_.direct;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessorBase::set_value_of_register(Register r, uint16_t value) {
|
||||
switch (r) {
|
||||
case Register::ProgramCounter: registers_.pc = value; break;
|
||||
case Register::StackPointer: registers_.s.full = value; break;
|
||||
case Register::Flags: set_flags(uint8_t(value)); break;
|
||||
case Register::A: registers_.a.full = value; break;
|
||||
case Register::X: registers_.x.full = value; break;
|
||||
case Register::Y: registers_.y.full = value; break;
|
||||
case Register::EmulationFlag: set_emulation_mode(value); break;
|
||||
case Register::DataBank: registers_.data_bank = uint32_t(value & 0xff) << 16; break;
|
||||
case Register::ProgramBank: registers_.program_bank = uint32_t(value &0xff) << 16; break;
|
||||
case Register::Direct: registers_.direct = value; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
1017
Processors/65816/Implementation/65816Implementation.hpp
Normal file
1017
Processors/65816/Implementation/65816Implementation.hpp
Normal file
File diff suppressed because it is too large
Load Diff
1101
Processors/65816/Implementation/65816Storage.cpp
Normal file
1101
Processors/65816/Implementation/65816Storage.cpp
Normal file
File diff suppressed because it is too large
Load Diff
374
Processors/65816/Implementation/65816Storage.hpp
Normal file
374
Processors/65816/Implementation/65816Storage.hpp
Normal file
@@ -0,0 +1,374 @@
|
||||
//
|
||||
// 65816Implementation.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 23/09/2020.
|
||||
// Copyright © 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
enum MicroOp: uint8_t {
|
||||
/// Fetches a byte from the program counter to the instruction buffer and increments the program counter.
|
||||
CycleFetchIncrementPC,
|
||||
/// Fetches a byte from the program counter without incrementing it.
|
||||
CycleFetchPC,
|
||||
/// Fetches a byte from the program counter without incrementing it, and throws it away.
|
||||
CycleFetchPCThrowaway,
|
||||
/// The same as CycleFetchIncrementPC but indicates valid program address rather than valid data address.
|
||||
CycleFetchOpcode,
|
||||
|
||||
/// Fetches a byte from the data address to the data buffer.
|
||||
CycleFetchData,
|
||||
/// Fetches a byte from the data address to the data buffer and increments the data address.
|
||||
CycleFetchIncrementData,
|
||||
/// Fetches from the address formed by the low byte of the data address and the high byte
|
||||
/// of the instruction buffer, throwing the result away.
|
||||
CycleFetchIncorrectDataAddress,
|
||||
/// Fetches a byte from the data address and throws it away.
|
||||
CycleFetchDataThrowaway,
|
||||
/// Fetches a byte from the data address to the data buffer, signalling VPB .
|
||||
CycleFetchVector,
|
||||
/// Fetches a byte from the data address to the data buffer and increments the data address, signalling VPB.
|
||||
CycleFetchIncrementVector,
|
||||
|
||||
// Dedicated block-move cycles; these use the data buffer as an intermediary.
|
||||
CycleFetchBlockX,
|
||||
CycleFetchBlockY,
|
||||
CycleStoreBlockY,
|
||||
|
||||
/// Stores a byte from the data buffer.
|
||||
CycleStoreData,
|
||||
/// Stores the most recent byte placed into the data buffer without removing it.
|
||||
CycleStoreDataThrowaway,
|
||||
/// Stores a byte to the data address from the data buffer and increments the data address.
|
||||
CycleStoreIncrementData,
|
||||
/// Stores a byte to the data address from the data buffer and decrements the data address.
|
||||
CycleStoreDecrementData,
|
||||
|
||||
/// Pushes a single byte from the data buffer to the stack.
|
||||
CyclePush,
|
||||
/// Fetches from the current stack location and throws the result away.
|
||||
CycleAccessStack,
|
||||
/// Pulls a single byte to the data buffer from the stack.
|
||||
CyclePull,
|
||||
/// Performs as CyclePull if the 65816 is not in emulation mode; otherwise skips itself.
|
||||
CyclePullIfNotEmulation,
|
||||
|
||||
/// Issues a BusOperation::None and regresses the micro-op counter until an established
|
||||
/// STP or WAI condition is satisfied.
|
||||
CycleRepeatingNone,
|
||||
|
||||
/// Sets the data address by copying the final two bytes of the instruction buffer and
|
||||
/// using the data register as a high byte.
|
||||
OperationConstructAbsolute,
|
||||
|
||||
/// Constructs a strictly 16-bit address from the instruction buffer.
|
||||
OperationConstructAbsolute16,
|
||||
|
||||
/// Sets the data address by copying the entire instruction buffer.
|
||||
OperationConstructAbsoluteLong,
|
||||
|
||||
/// Sets the data address to the 16-bit result of adding x to the value in the instruction buffer.
|
||||
OperationConstructAbsoluteIndexedIndirect,
|
||||
|
||||
/// Sets the data address to the 24-bit result of adding x to the low 16-bits of the value in the
|
||||
/// instruction buffer and retaining the highest 8-bits as specified.
|
||||
OperationConstructAbsoluteLongX,
|
||||
|
||||
/// Calculates an a, x address; if:
|
||||
/// there was no carry into the top byte of the address; and
|
||||
/// the process or in emulation or 8-bit index mode;
|
||||
/// then it also skips the next micro-op.
|
||||
OperationConstructAbsoluteXRead,
|
||||
|
||||
/// Calculates an a, x address.
|
||||
OperationConstructAbsoluteX,
|
||||
|
||||
// These are analogous to the X versions above.
|
||||
OperationConstructAbsoluteY,
|
||||
OperationConstructAbsoluteYRead,
|
||||
|
||||
/// Constructs the current direct address using the value in the instruction buffer.
|
||||
/// Skips the next micro-op if the low byte of the direct register is 0.
|
||||
OperationConstructDirect,
|
||||
|
||||
/// Exactly like OperationConstructDirect, but doesn't retain any single-byte wrapping
|
||||
/// behaviour in emulation mode.
|
||||
OperationConstructDirectLong,
|
||||
|
||||
/// Constructs the current direct indexed indirect address using the data bank,
|
||||
/// direct and x registers plus the value currently in the instruction buffer.
|
||||
/// Skips the next micro-op if the low byte of the direct register is 0.
|
||||
OperationConstructDirectIndexedIndirect,
|
||||
|
||||
/// Constructs the current direct indexed indirect address using the value
|
||||
/// currently in the data buffer.
|
||||
OperationConstructDirectIndirect,
|
||||
|
||||
/// Adds y to the low 16-bits currently in the instruction buffer and appends a high 8-bits
|
||||
/// also from the instruction buffer.
|
||||
OperationConstructDirectIndirectIndexedLong,
|
||||
|
||||
/// Uses the 24-bit address currently in the instruction buffer.
|
||||
OperationConstructDirectIndirectLong,
|
||||
|
||||
/// Adds the x register to the direct register to produce a 16-bit address;
|
||||
/// skips the next micro-op if the low byte of the direct register is 0.
|
||||
OperationConstructDirectX,
|
||||
|
||||
/// Adds the y register to the direct register to produce a 16-bit address;
|
||||
/// skips the next micro-op if the low byte of the direct register is 0.
|
||||
OperationConstructDirectY,
|
||||
|
||||
/// Adds the instruction buffer to the program counter, making a 16-bit result,
|
||||
/// *and stores it into the data buffer*.
|
||||
OperationConstructPER,
|
||||
|
||||
/// Adds the stack pointer to the instruction buffer to produce a 16-bit address.
|
||||
OperationConstructStackRelative,
|
||||
|
||||
/// Adds y to the value in the instruction buffer to produce a 16-bit result and
|
||||
/// prefixes the current data bank.
|
||||
OperationConstructStackRelativeIndexedIndirect,
|
||||
|
||||
/// Performs whatever operation goes with this program.
|
||||
OperationPerform,
|
||||
|
||||
/// Copies the current program counter to the data buffer.
|
||||
OperationCopyPCToData,
|
||||
OperationCopyDataToPC,
|
||||
OperationCopyInstructionToData,
|
||||
OperationCopyDataToInstruction,
|
||||
|
||||
/// Copies the current PBR to the data buffer.
|
||||
OperationCopyPBRToData,
|
||||
|
||||
/// Copies A to the data buffer.
|
||||
OperationCopyAToData,
|
||||
|
||||
/// Copies the data buffer to A.
|
||||
OperationCopyDataToA,
|
||||
|
||||
/// Fills the data buffer with three or four bytes, depending on emulation mode, containing the program
|
||||
/// counter, flags and possibly the program bank. Also puts the appropriate vector address into the
|
||||
/// address register.
|
||||
OperationPrepareException,
|
||||
|
||||
/// Sets the memory lock output for the rest of this instruction.
|
||||
OperationSetMemoryLock,
|
||||
|
||||
/// Complete this set of micr-ops.
|
||||
OperationMoveToNextProgram,
|
||||
|
||||
/// Inspects the instruction buffer and thereby selects the next set of micro-ops to schedule.
|
||||
OperationDecode,
|
||||
};
|
||||
|
||||
enum Operation: uint8_t {
|
||||
// These perform the named operation using the value in the data buffer;
|
||||
// they are implicitly AccessType::Read.
|
||||
ADC, AND, BIT, CMP, CPX, CPY, EOR, ORA, SBC, BITimm,
|
||||
|
||||
// These load the respective register from the data buffer;
|
||||
// they are implicitly AccessType::Read.
|
||||
LDA, LDX, LDY,
|
||||
PLB, PLD, PLP, // LDA, LDX and LDY can be used for PLA, PLX, PLY.
|
||||
|
||||
// These move the respective register (or value) to the data buffer;
|
||||
// they are implicitly AccessType::Write.
|
||||
STA, STX, STY, STZ,
|
||||
PHB, PHP, PHD, PHK,
|
||||
|
||||
// These modify the value in the data buffer as part of a read-modify-write.
|
||||
INC, DEC, ASL, LSR, ROL, ROR, TRB, TSB,
|
||||
|
||||
// These merely decrement A, increment or decrement X and Y, and regress
|
||||
// the program counter only if appropriate.
|
||||
MVN, MVP,
|
||||
|
||||
// These use a value straight from the instruction buffer.
|
||||
REP, SEP,
|
||||
|
||||
BCC, BCS, BEQ, BMI, BNE, BPL, BRA, BVC, BVS, BRL,
|
||||
|
||||
// These are all implicit.
|
||||
CLC, CLD, CLI, CLV, DEX, DEY, INX, INY, NOP, SEC, SED, SEI,
|
||||
TAX, TAY, TCD, TCS, TDC, TSC, TSX, TXA, TXS, TXY, TYA, TYX,
|
||||
XCE, XBA,
|
||||
|
||||
STP, WAI,
|
||||
|
||||
// These unpack values from the data buffer, which has been filled
|
||||
// from the stack.
|
||||
RTI,
|
||||
|
||||
/// Loads the PC with the contents of the data buffer.
|
||||
JMPind,
|
||||
|
||||
/// Loads the PC with the contents of the instruction bufer.
|
||||
JMP,
|
||||
|
||||
/// Loads the PC and PBR with the operand from the instruction buffer.
|
||||
JML,
|
||||
|
||||
/// Loads the PC with the operand from the instruction buffer, placing
|
||||
/// the current PC into the data buffer.
|
||||
JSR,
|
||||
|
||||
/// Loads the PC and the PBR with the operand from the instruction buffer,
|
||||
/// placing the old PC into the data buffer (and only the PC; PBR not included).
|
||||
JSL,
|
||||
|
||||
/// Loads the PC with the contents of the data buffer + 1.
|
||||
RTS,
|
||||
};
|
||||
|
||||
struct ProcessorStorageConstructor;
|
||||
|
||||
struct ProcessorStorage {
|
||||
ProcessorStorage();
|
||||
|
||||
// Frustratingly, there is not quite enough space in 16 bits to store both
|
||||
// the program offset and the operation as currently defined.
|
||||
struct Instruction {
|
||||
/// Pointers into micro_ops_ for: [0] = 16-bit operation; [1] = 8-bit operation.
|
||||
uint16_t program_offsets[2] = {0xffff, 0xffff};
|
||||
/// The operation to perform upon an OperationPerform.
|
||||
Operation operation = NOP;
|
||||
/// An index into the mx field indicating which of M or X affects whether this is an 8-bit or 16-bit field.
|
||||
/// So the program to perform is that at @c program_offsets[mx_flags[size_field]]
|
||||
uint8_t size_field = 0;
|
||||
};
|
||||
Instruction instructions[256 + 2]; // Arranged as:
|
||||
// 256 entries: instructions;
|
||||
// the entry for 'exceptions' (i.e. reset, irq, nmi); and
|
||||
// the entry for fetch-decode-execute.
|
||||
|
||||
enum class OperationSlot {
|
||||
Exception = 256,
|
||||
FetchDecodeExecute
|
||||
};
|
||||
|
||||
// A helper for testing.
|
||||
uint16_t last_operation_pc_;
|
||||
Instruction *active_instruction_;
|
||||
Cycles cycles_left_to_run_;
|
||||
|
||||
// All registers are boxed up into a struct so that they can be stored and restored in support of abort.
|
||||
struct Registers {
|
||||
// Registers.
|
||||
RegisterPair16 a;
|
||||
RegisterPair16 x, y;
|
||||
RegisterPair16 s;
|
||||
uint16_t pc;
|
||||
|
||||
// Flags aplenty.
|
||||
MOS6502Esque::LazyFlags flags;
|
||||
uint8_t mx_flags[2] = {1, 1}; // [0] = m; [1] = x. In both cases either `0` or `1`; `1` => 8-bit.
|
||||
uint16_t m_masks[2] = {0xff00, 0x00ff}; // [0] = src mask; [1] = dst mask.
|
||||
uint16_t x_masks[2] = {0xff00, 0x00ff}; // [0] = src mask; [1] = dst mask.
|
||||
uint16_t e_masks[2] = {0xff00, 0x00ff};
|
||||
int m_shift = 0;
|
||||
int x_shift = 0;
|
||||
bool emulation_flag = true;
|
||||
|
||||
// I.e. the offset for direct addressing (outside of emulation mode).
|
||||
uint16_t direct = 0;
|
||||
|
||||
// Banking registers are all stored with the relevant byte
|
||||
// shifted up bits 16–23.
|
||||
uint32_t data_bank = 0; // i.e. DBR.
|
||||
uint32_t program_bank = 0; // i.e. PBR.
|
||||
} registers_, abort_registers_copy_;
|
||||
|
||||
// The next bus transaction.
|
||||
uint32_t bus_address_ = 0;
|
||||
uint8_t *bus_value_ = nullptr;
|
||||
static inline uint8_t bus_throwaway_ = 0;
|
||||
BusOperation bus_operation_ = BusOperation::None;
|
||||
|
||||
// A bitfield for various exceptions.
|
||||
static constexpr int PowerOn = 1 << 0;
|
||||
static constexpr int Reset = 1 << 1;
|
||||
static constexpr int IRQ = Flag::Interrupt; // This makes masking a lot easier later on; this is 1 << 2.
|
||||
static constexpr int NMI = 1 << 3;
|
||||
static constexpr int Abort = 1 << 4;
|
||||
|
||||
static constexpr int default_exceptions = PowerOn;
|
||||
int pending_exceptions_ = default_exceptions;
|
||||
int selected_exceptions_ = default_exceptions;
|
||||
|
||||
bool ready_line_ = false;
|
||||
bool memory_lock_ = false;
|
||||
|
||||
// Just to be safe.
|
||||
static_assert(PowerOn != IRQ);
|
||||
static_assert(Reset != IRQ);
|
||||
static_assert(NMI != IRQ);
|
||||
static_assert(Abort != IRQ);
|
||||
|
||||
/// Sets the required exception flags necessary to exit a STP or WAI.
|
||||
int required_exceptions_ = 0;
|
||||
BusOperation stp_wai_bus_operation_ = BusOperation::None;
|
||||
|
||||
/// Defines a four-byte buffer which can be cleared or filled in single-byte increments from least significant byte
|
||||
/// to most significant.
|
||||
struct Buffer {
|
||||
uint32_t value = 0;
|
||||
int size = 0;
|
||||
int read = 0;
|
||||
|
||||
void clear() {
|
||||
value = 0;
|
||||
size = 0;
|
||||
read = 0;
|
||||
}
|
||||
|
||||
uint8_t *next_input() {
|
||||
uint8_t *const next = byte(size);
|
||||
++size;
|
||||
return next;
|
||||
}
|
||||
|
||||
uint8_t *next_output() {
|
||||
uint8_t *const next = byte(read);
|
||||
++read;
|
||||
return next;
|
||||
}
|
||||
|
||||
uint8_t *preview_output() {
|
||||
return byte(read);
|
||||
}
|
||||
|
||||
uint8_t *next_output_descending() {
|
||||
--size;
|
||||
return byte(size);
|
||||
}
|
||||
|
||||
uint8_t *any_byte() {
|
||||
return reinterpret_cast<uint8_t *>(&value);
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t *byte(int pointer) {
|
||||
assert(pointer >= 0 && pointer < 4);
|
||||
#if TARGET_RT_BIG_ENDIAN
|
||||
return reinterpret_cast<uint8_t *>(&value) + (3 ^ pointer);
|
||||
#else
|
||||
return reinterpret_cast<uint8_t *>(&value) + pointer;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
Buffer instruction_buffer_, data_buffer_;
|
||||
uint32_t data_address_;
|
||||
uint32_t data_address_increment_mask_ = 0xffff;
|
||||
uint32_t incorrect_data_address_;
|
||||
|
||||
std::vector<MicroOp> micro_ops_;
|
||||
MicroOp *next_op_ = nullptr;
|
||||
|
||||
void set_reset_state();
|
||||
void set_emulation_mode(bool);
|
||||
void set_m_x_flags(bool m, bool x);
|
||||
uint8_t get_flags() const;
|
||||
void set_flags(uint8_t);
|
||||
};
|
||||
@@ -15,14 +15,14 @@ AllRAMProcessor::AllRAMProcessor(std::size_t memory_size) :
|
||||
traps_(memory_size, false),
|
||||
timestamp_(0) {}
|
||||
|
||||
void AllRAMProcessor::set_data_at_address(uint16_t startAddress, std::size_t length, const uint8_t *data) {
|
||||
std::size_t endAddress = std::min(startAddress + length, size_t(65536));
|
||||
std::memcpy(&memory_[startAddress], data, endAddress - startAddress);
|
||||
void AllRAMProcessor::set_data_at_address(size_t start_address, std::size_t length, const uint8_t *data) {
|
||||
const size_t end_address = std::min(start_address + length, memory_.size());
|
||||
memcpy(&memory_[start_address], data, end_address - start_address);
|
||||
}
|
||||
|
||||
void AllRAMProcessor::get_data_at_address(uint16_t startAddress, std::size_t length, uint8_t *data) {
|
||||
std::size_t endAddress = std::min(startAddress + length, size_t(65536));
|
||||
std::memcpy(data, &memory_[startAddress], endAddress - startAddress);
|
||||
void AllRAMProcessor::get_data_at_address(size_t start_address, std::size_t length, uint8_t *data) {
|
||||
const size_t end_address = std::min(start_address + length, memory_.size());
|
||||
memcpy(data, &memory_[start_address], end_address - start_address);
|
||||
}
|
||||
|
||||
HalfCycles AllRAMProcessor::get_timestamp() {
|
||||
|
||||
@@ -21,8 +21,8 @@ class AllRAMProcessor {
|
||||
public:
|
||||
AllRAMProcessor(std::size_t memory_size);
|
||||
HalfCycles get_timestamp();
|
||||
void set_data_at_address(uint16_t startAddress, std::size_t length, const uint8_t *data);
|
||||
void get_data_at_address(uint16_t startAddress, std::size_t length, uint8_t *data);
|
||||
void set_data_at_address(size_t startAddress, size_t length, const uint8_t *data);
|
||||
void get_data_at_address(size_t startAddress, size_t length, uint8_t *data);
|
||||
|
||||
class TrapHandler {
|
||||
public:
|
||||
|
||||
@@ -29,7 +29,7 @@ class AllRAMProcessor:
|
||||
}
|
||||
|
||||
struct PortAccessDelegate {
|
||||
virtual uint8_t z80_all_ram_processor_input(uint16_t port) { return 0xff; }
|
||||
virtual uint8_t z80_all_ram_processor_input(uint16_t) { return 0xff; }
|
||||
};
|
||||
inline void set_port_access_delegate(PortAccessDelegate *delegate) {
|
||||
port_delegate_ = delegate;
|
||||
|
||||
31
README.md
31
README.md
@@ -2,15 +2,12 @@
|
||||
# Clock Signal
|
||||
Clock Signal ('CLK') is an emulator for tourists that seeks to be invisible. Users directly launch classic software with no emulator or per-emulated-machine learning curve.
|
||||
|
||||
macOS and source releases are [hosted on GitHub](https://github.com/TomHarte/CLK/releases). For desktop Linux it is also available as a [Snap](https://snapcraft.io/clock-signal).
|
||||
macOS and source releases are [hosted on GitHub](https://github.com/TomHarte/CLK/releases). For desktop Linux it is also available as a [Snap](https://snapcraft.io/clock-signal). On the Mac it is a native Cocoa and Metal application; under Linux, BSD and other UNIXes and UNIX-alikes it uses OpenGL and can be built either with Qt or with SDL.
|
||||
|
||||
On the Mac it is a native Cocoa and Metal application; under Linux, BSD and other UNIXes and UNIX-alikes it uses OpenGL and can be built either with Qt or with SDL.
|
||||
|
||||
So its aims are:
|
||||
This emulator seeks to offer:
|
||||
* single-click load of any piece of source media for any supported platform;
|
||||
* with a heavy signal processing tilt for accurate reproduction of original outputs;
|
||||
* that aims for the lowest possible latency; and
|
||||
* 100% accurate emulations, naturally.
|
||||
* while minimising latency.
|
||||
|
||||
It currently contains emulations of the:
|
||||
* Acorn Electron;
|
||||
@@ -28,7 +25,7 @@ It currently contains emulations of the:
|
||||
|
||||
## Single-click Loading
|
||||
|
||||
Through the combination of static analysis and runtime analysis, CLK seeks to be able automatically to select and configure the appropriate machine to run any provided disk, tape or ROM; to issue any commands necessary to run the software contained on the disk, tape or ROM; and to provide accelerated loading where feasible.
|
||||
Through static and runtime analysis CLK seeks automatically to select and configure the appropriate machine to run any provided disk, tape or ROM; to issue any commands necessary to run the software contained on the disk, tape or ROM; and to provide accelerated loading where feasible.
|
||||
|
||||
The full process of loading a title — even if you've never used the emulated machine before — is therefore:
|
||||
|
||||
@@ -37,12 +34,10 @@ The full process of loading a title — even if you've never used the emulated m
|
||||
|
||||
## Signal Processing
|
||||
|
||||
Consider an ordinary, unmodified Commodore Vic-20. Its only video output is composite. Therefore the emulated machine's only video output is composite. In order to display the video output, your GPU then decodes composite video. Therefore all composite video artefacts are present and exactly correct, not because of a post hoc filter combining all the subjective effects that this author associates with composite video but because the real signal is really being processed.
|
||||
Consider an ordinary, unmodified Commodore Vic-20. Its only video output is composite. Therefore the emulated machine's only video output is composite. In order to display the video output, your GPU must decode composite video. Therefore composite video artefacts are present and correct not because of a post hoc filter but because the real signal is really being processed.
|
||||
|
||||
Similar effort is put into audio generation. If the real machine normally generates audio at 192Khz then the emulator generates a 192Khz source signal and filters it down to whatever the host machine can output.
|
||||
|
||||
If your machine has a 4k monitor and a 96Khz audio output? Then you'll get a 4k rendering of a composite display and, assuming the emulated machine produces source audio at or above 96Khz, 96,000 individual distinct audio samples a second. Interlaced video also works and looks much as it always did on those machines that produce it.
|
||||
|
||||
### Samples
|
||||
|
||||
| 1:1 Pixel Copying | Composite Decoded |
|
||||
@@ -61,15 +56,15 @@ If your machine has a 4k monitor and a 96Khz audio output? Then you'll get a 4k
|
||||
|
||||
## Low Latency
|
||||
|
||||
The display produced is an emulated CRT, with phosphor decay. Therefore if you have a 140Hz monitor it can produce 140 distinct frames per second. Latency is dictated by the output hardware, not the emulated machine.
|
||||
The display produced is an emulated CRT, with phosphor decay. Therefore if you have a 140Hz 4k monitor it can produce 140 distinct frames per second at 4k resolution. Latency is dictated by the host hardware, not the emulated machine or emulator.
|
||||
|
||||
The machine update mechanism is influenced separately by both screen refresh and audio stream processing; audio latency is therefore generally restrained to 5–10ms regardless of your screen's refresh rate.
|
||||
|
||||
A corollary of emulating the continuous nature CRT, not merely performing end-of-frame transcriptions, is that the most common motion aliasing effects of displaying 50Hz video on a 60Hz display are minimised; you don't have to own niche equipment to benefit.
|
||||
Audio latency is disjoint from frame rate and is generlaly restrained to 5–10ms.
|
||||
|
||||
## Accurate Emulation
|
||||
|
||||
Cycle-accurate emulation for the supported target machines is fairly trite; this emulator seeks to follow that precedent. All emulation logic is written in C++ for explicit control over costs but, where a conflict arises, the presumption is towards clarity and simplicity of code. This emulator is willing to spend the processing resources available on modern hardware.
|
||||
Accuracy affects usability; the more accurate an emulator, the more likely that a user can run every piece of software they're interested in without further intervention.
|
||||
|
||||
This emulator attempts cycle-accurate emulation of all supported machines. In some cases it succeeds.
|
||||
|
||||
## Additional Screenshots
|
||||
| | |
|
||||
@@ -83,3 +78,9 @@ Cycle-accurate emulation for the supported target machines is fairly trite; this
|
||||
|
||||

|
||||

|
||||
|
||||
## Sponsorship
|
||||
|
||||
I've been asked several times whether it is possible to sponsor this project; I think that's a poor fit for this emulator's highly-malleable scope, and it makes me uncomfortable because as the author I primarily see only its defects.
|
||||
|
||||
An Amazon US wishlist is now attached in the hope of avoiding the question in future. A lot of it is old books now available only secondhand — I like to read about potential future additions well in advance of starting on them. Per the optimism of some book sellers, please don't purchase anything that is currnetly listed only at an absurd price.
|
||||
|
||||
Reference in New Issue
Block a user