mirror of
https://github.com/TomHarte/CLK.git
synced 2025-08-09 05:25:01 +00:00
Merge pull request #460 from TomHarte/RWTSAcceleration
Introduces optional quick loading of Apple DOS 3.3 programs
This commit is contained in:
@@ -260,3 +260,7 @@ void DiskII::set_activity_observer(Activity::Observer *observer) {
|
|||||||
drives_[0].set_activity_observer(observer, "Drive 1", true);
|
drives_[0].set_activity_observer(observer, "Drive 1", true);
|
||||||
drives_[1].set_activity_observer(observer, "Drive 2", true);
|
drives_[1].set_activity_observer(observer, "Drive 2", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Storage::Disk::Drive &DiskII::get_drive(int index) {
|
||||||
|
return drives_[index];
|
||||||
|
}
|
||||||
|
@@ -81,6 +81,10 @@ class DiskII:
|
|||||||
// The Disk II functions as a potential target for @c Activity::Sources.
|
// The Disk II functions as a potential target for @c Activity::Sources.
|
||||||
void set_activity_observer(Activity::Observer *observer);
|
void set_activity_observer(Activity::Observer *observer);
|
||||||
|
|
||||||
|
// Returns the Storage::Disk::Drive in use for drive @c index.
|
||||||
|
// *NOT FOR HARDWARE EMULATION USAGE*.
|
||||||
|
Storage::Disk::Drive &get_drive(int index);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum class Control {
|
enum class Control {
|
||||||
P0, P1, P2, P3,
|
P0, P1, P2, P3,
|
||||||
|
@@ -24,20 +24,29 @@
|
|||||||
#include "DiskIICard.hpp"
|
#include "DiskIICard.hpp"
|
||||||
#include "Video.hpp"
|
#include "Video.hpp"
|
||||||
|
|
||||||
#include "../../ClockReceiver/ForceInline.hpp"
|
|
||||||
|
|
||||||
#include "../../Analyser/Static/AppleII/Target.hpp"
|
#include "../../Analyser/Static/AppleII/Target.hpp"
|
||||||
|
#include "../../ClockReceiver/ForceInline.hpp"
|
||||||
|
#include "../../Configurable/Configurable.hpp"
|
||||||
|
#include "../../Storage/Disk/Track/TrackSerialiser.hpp"
|
||||||
|
#include "../../Storage/Disk/Encodings/AppleGCR/SegmentParser.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<Configurable::Option>> AppleII::get_options() {
|
||||||
|
std::vector<std::unique_ptr<Configurable::Option>> options;
|
||||||
|
options.emplace_back(new Configurable::BooleanOption("Accelerate DOS 3.3", "quickload"));
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
class ConcreteMachine:
|
class ConcreteMachine:
|
||||||
public CRTMachine::Machine,
|
public CRTMachine::Machine,
|
||||||
public ConfigurationTarget::Machine,
|
public ConfigurationTarget::Machine,
|
||||||
public KeyboardMachine::Machine,
|
public KeyboardMachine::Machine,
|
||||||
|
public Configurable::Device,
|
||||||
public CPU::MOS6502::BusHandler,
|
public CPU::MOS6502::BusHandler,
|
||||||
public Inputs::Keyboard,
|
public Inputs::Keyboard,
|
||||||
public AppleII::Machine,
|
public AppleII::Machine,
|
||||||
@@ -123,6 +132,10 @@ class ConcreteMachine:
|
|||||||
pick_card_messaging_group(card);
|
pick_card_messaging_group(card);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AppleII::DiskIICard *diskii_card() {
|
||||||
|
return dynamic_cast<AppleII::DiskIICard *>(cards_[5].get());
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Memory Map
|
// MARK: - Memory Map
|
||||||
struct MemoryBlock {
|
struct MemoryBlock {
|
||||||
uint8_t *read_pointer = nullptr;
|
uint8_t *read_pointer = nullptr;
|
||||||
@@ -157,6 +170,9 @@ class ConcreteMachine:
|
|||||||
// MARK - typing
|
// MARK - typing
|
||||||
std::unique_ptr<Utility::StringSerialiser> string_serialiser_;
|
std::unique_ptr<Utility::StringSerialiser> string_serialiser_;
|
||||||
|
|
||||||
|
// MARK - quick loading
|
||||||
|
bool should_load_quickly_ = false;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ConcreteMachine():
|
ConcreteMachine():
|
||||||
m6502_(*this),
|
m6502_(*this),
|
||||||
@@ -205,7 +221,7 @@ class ConcreteMachine:
|
|||||||
return &speaker_;
|
return &speaker_;
|
||||||
}
|
}
|
||||||
|
|
||||||
forceinline Cycles perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) {
|
forceinline Cycles perform_bus_operation(const CPU::MOS6502::BusOperation operation, const uint16_t address, uint8_t *const value) {
|
||||||
++ cycles_since_video_update_;
|
++ cycles_since_video_update_;
|
||||||
++ cycles_since_card_update_;
|
++ cycles_since_card_update_;
|
||||||
cycles_since_audio_update_ += Cycles(7);
|
cycles_since_audio_update_ += Cycles(7);
|
||||||
@@ -232,21 +248,101 @@ class ConcreteMachine:
|
|||||||
d000 to e000 : the low ROM area, which can contain indepdently-paged RAM with a language card
|
d000 to e000 : the low ROM area, which can contain indepdently-paged RAM with a language card
|
||||||
e000 onward : the rest of ROM, also potentially replaced with RAM by a language card
|
e000 onward : the rest of ROM, also potentially replaced with RAM by a language card
|
||||||
*/
|
*/
|
||||||
|
uint16_t accessed_address = address;
|
||||||
MemoryBlock *block = nullptr;
|
MemoryBlock *block = nullptr;
|
||||||
if(address < 0x200) block = &memory_blocks_[0];
|
if(address < 0x200) block = &memory_blocks_[0];
|
||||||
else if(address < 0xc000) {
|
else if(address < 0xc000) {
|
||||||
if(address < 0x6000 && !isReadOperation(operation)) update_video();
|
if(address < 0x6000 && !isReadOperation(operation)) update_video();
|
||||||
block = &memory_blocks_[1];
|
block = &memory_blocks_[1];
|
||||||
address -= 0x200;
|
accessed_address -= 0x200;
|
||||||
}
|
}
|
||||||
else if(address < 0xd000) block = nullptr;
|
else if(address < 0xd000) block = nullptr;
|
||||||
else if(address < 0xe000) {block = &memory_blocks_[2]; address -= 0xd000; }
|
else if(address < 0xe000) {block = &memory_blocks_[2]; accessed_address -= 0xd000; }
|
||||||
else { block = &memory_blocks_[3]; address -= 0xe000; }
|
else { block = &memory_blocks_[3]; accessed_address -= 0xe000; }
|
||||||
|
|
||||||
bool has_updated_cards = false;
|
bool has_updated_cards = false;
|
||||||
if(block) {
|
if(block) {
|
||||||
if(isReadOperation(operation)) *value = block->read_pointer[address];
|
if(isReadOperation(operation)) *value = block->read_pointer[accessed_address];
|
||||||
else if(block->write_pointer) block->write_pointer[address] = *value;
|
else if(block->write_pointer) block->write_pointer[accessed_address] = *value;
|
||||||
|
|
||||||
|
if(should_load_quickly_) {
|
||||||
|
// Check for a prima facie entry into RWTS.
|
||||||
|
if(operation == CPU::MOS6502::BusOperation::ReadOpcode && address == 0xb7b5) {
|
||||||
|
// Grab the IO control block address for inspection.
|
||||||
|
uint16_t io_control_block_address =
|
||||||
|
static_cast<uint16_t>(
|
||||||
|
(m6502_.get_value_of_register(CPU::MOS6502::Register::A) << 8) |
|
||||||
|
m6502_.get_value_of_register(CPU::MOS6502::Register::Y)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Verify that this is table type one, for execution on card six,
|
||||||
|
// against drive 1 or 2, and that the command is either a seek or a sector read.
|
||||||
|
if(
|
||||||
|
ram_[io_control_block_address+0x00] == 0x01 &&
|
||||||
|
ram_[io_control_block_address+0x01] == 0x60 &&
|
||||||
|
ram_[io_control_block_address+0x02] > 0 && ram_[io_control_block_address+0x02] < 3 &&
|
||||||
|
ram_[io_control_block_address+0x0c] < 2
|
||||||
|
) {
|
||||||
|
const uint8_t iob_track = ram_[io_control_block_address+4];
|
||||||
|
const uint8_t iob_sector = ram_[io_control_block_address+5];
|
||||||
|
const uint8_t iob_drive = ram_[io_control_block_address+2] - 1;
|
||||||
|
|
||||||
|
// Get the track identified and store the new head position.
|
||||||
|
auto track = diskii_card()->get_drive(iob_drive).step_to(Storage::Disk::HeadPosition(iob_track));
|
||||||
|
|
||||||
|
// DOS 3.3 keeps the current track (unspecified drive) in 0x478; the current track for drive 1 and drive 2
|
||||||
|
// is also kept in that Disk II card's screen hole.
|
||||||
|
ram_[0x478] = iob_track;
|
||||||
|
if(ram_[io_control_block_address+0x02] == 1) {
|
||||||
|
ram_[0x47e] = iob_track;
|
||||||
|
} else {
|
||||||
|
ram_[0x4fe] = iob_track;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether this is a read, not merely a seek.
|
||||||
|
if(ram_[io_control_block_address+0x0c] == 1) {
|
||||||
|
// Apple the DOS 3.3 formula to map the requested logical sector to a physical sector.
|
||||||
|
const int physical_sector = (iob_sector == 15) ? 15 : ((iob_sector * 13) % 15);
|
||||||
|
|
||||||
|
// Parse the entire track. TODO: cache these.
|
||||||
|
auto sector_map = Storage::Encodings::AppleGCR::sectors_from_segment(
|
||||||
|
Storage::Disk::track_serialisation(*track, Storage::Time(1, 50000)));
|
||||||
|
|
||||||
|
bool found_sector = false;
|
||||||
|
for(const auto &pair: sector_map) {
|
||||||
|
if(pair.second.address.sector == physical_sector) {
|
||||||
|
found_sector = true;
|
||||||
|
|
||||||
|
// Copy the sector contents to their destination.
|
||||||
|
uint16_t target = static_cast<uint16_t>(
|
||||||
|
ram_[io_control_block_address+8] |
|
||||||
|
(ram_[io_control_block_address+9] << 8)
|
||||||
|
);
|
||||||
|
|
||||||
|
for(size_t c = 0; c < 256; ++c) {
|
||||||
|
ram_[target] = pair.second.data[c];
|
||||||
|
++target;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set no error encountered.
|
||||||
|
ram_[io_control_block_address + 0xd] = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(found_sector) {
|
||||||
|
// Set no error in the flags register too, and RTS.
|
||||||
|
m6502_.set_value_of_register(CPU::MOS6502::Register::Flags, m6502_.get_value_of_register(CPU::MOS6502::Register::Flags) & ~1);
|
||||||
|
*value = 0x60;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No error encountered; RTS.
|
||||||
|
m6502_.set_value_of_register(CPU::MOS6502::Register::Flags, m6502_.get_value_of_register(CPU::MOS6502::Register::Flags) & ~1);
|
||||||
|
*value = 0x60;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Assume a vapour read unless it turns out otherwise; this is a little
|
// Assume a vapour read unless it turns out otherwise; this is a little
|
||||||
// wasteful but works for now.
|
// wasteful but works for now.
|
||||||
@@ -279,6 +375,12 @@ class ConcreteMachine:
|
|||||||
*value = keyboard_input_;
|
*value = keyboard_input_;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 0xc061: // Switch input 0.
|
||||||
|
case 0xc062: // Switch input 1.
|
||||||
|
case 0xc063: // Switch input 2.
|
||||||
|
*value &= 0x7f;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Write-only switches.
|
// Write-only switches.
|
||||||
@@ -480,8 +582,9 @@ class ConcreteMachine:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool insert_media(const Analyser::Static::Media &media) override {
|
bool insert_media(const Analyser::Static::Media &media) override {
|
||||||
if(!media.disks.empty() && cards_[5]) {
|
if(!media.disks.empty()) {
|
||||||
dynamic_cast<AppleII::DiskIICard *>(cards_[5].get())->set_disk(media.disks[0], 0);
|
auto diskii = diskii_card();
|
||||||
|
if(diskii) diskii->set_disk(media.disks[0], 0);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -492,6 +595,30 @@ class ConcreteMachine:
|
|||||||
if(card) card->set_activity_observer(observer);
|
if(card) card->set_activity_observer(observer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Options
|
||||||
|
std::vector<std::unique_ptr<Configurable::Option>> get_options() override {
|
||||||
|
return AppleII::get_options();
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_selections(const Configurable::SelectionSet &selections_by_option) override {
|
||||||
|
bool quickload;
|
||||||
|
if(Configurable::get_quick_load_tape(selections_by_option, quickload)) {
|
||||||
|
should_load_quickly_ = quickload;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Configurable::SelectionSet get_accurate_selections() override {
|
||||||
|
Configurable::SelectionSet selection_set;
|
||||||
|
Configurable::append_quick_load_tape_selection(selection_set, false);
|
||||||
|
return selection_set;
|
||||||
|
}
|
||||||
|
|
||||||
|
Configurable::SelectionSet get_user_friendly_selections() override {
|
||||||
|
Configurable::SelectionSet selection_set;
|
||||||
|
Configurable::append_quick_load_tape_selection(selection_set, true);
|
||||||
|
return selection_set;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -9,8 +9,16 @@
|
|||||||
#ifndef AppleII_hpp
|
#ifndef AppleII_hpp
|
||||||
#define AppleII_hpp
|
#define AppleII_hpp
|
||||||
|
|
||||||
|
#include "../../Configurable/Configurable.hpp"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace AppleII {
|
namespace AppleII {
|
||||||
|
|
||||||
|
/// @returns The options available for an Apple II.
|
||||||
|
std::vector<std::unique_ptr<Configurable::Option>> get_options();
|
||||||
|
|
||||||
class Machine {
|
class Machine {
|
||||||
public:
|
public:
|
||||||
virtual ~Machine();
|
virtual ~Machine();
|
||||||
|
@@ -57,3 +57,7 @@ void DiskIICard::set_component_prefers_clocking(ClockingHint::Source *component,
|
|||||||
diskii_clocking_preference_ = preference;
|
diskii_clocking_preference_ = preference;
|
||||||
set_select_constraints((preference != ClockingHint::Preference::RealTime) ? (IO | Device) : 0);
|
set_select_constraints((preference != ClockingHint::Preference::RealTime) ? (IO | Device) : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Storage::Disk::Drive &DiskIICard::get_drive(int drive) {
|
||||||
|
return diskii_.get_drive(drive);
|
||||||
|
}
|
||||||
|
@@ -32,6 +32,7 @@ class DiskIICard: public Card, public ClockingHint::Observer {
|
|||||||
void set_activity_observer(Activity::Observer *observer) override;
|
void set_activity_observer(Activity::Observer *observer) override;
|
||||||
|
|
||||||
void set_disk(const std::shared_ptr<Storage::Disk::Disk> &disk, int drive);
|
void set_disk(const std::shared_ptr<Storage::Disk::Disk> &disk, int drive);
|
||||||
|
Storage::Disk::Drive &get_drive(int drive);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference clocking) override;
|
void set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference clocking) override;
|
||||||
|
@@ -12,6 +12,7 @@
|
|||||||
#include "../../Configurable/Configurable.hpp"
|
#include "../../Configurable/Configurable.hpp"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace Electron {
|
namespace Electron {
|
||||||
|
@@ -129,6 +129,7 @@ std::string Machine::LongNameForTargetMachine(Analyser::Machine machine) {
|
|||||||
std::map<std::string, std::vector<std::unique_ptr<Configurable::Option>>> Machine::AllOptionsByMachineName() {
|
std::map<std::string, std::vector<std::unique_ptr<Configurable::Option>>> Machine::AllOptionsByMachineName() {
|
||||||
std::map<std::string, std::vector<std::unique_ptr<Configurable::Option>>> options;
|
std::map<std::string, std::vector<std::unique_ptr<Configurable::Option>>> options;
|
||||||
|
|
||||||
|
options.emplace(std::make_pair(LongNameForTargetMachine(Analyser::Machine::Electron), AppleII::get_options()));
|
||||||
options.emplace(std::make_pair(LongNameForTargetMachine(Analyser::Machine::Electron), Electron::get_options()));
|
options.emplace(std::make_pair(LongNameForTargetMachine(Analyser::Machine::Electron), Electron::get_options()));
|
||||||
options.emplace(std::make_pair(LongNameForTargetMachine(Analyser::Machine::MSX), MSX::get_options()));
|
options.emplace(std::make_pair(LongNameForTargetMachine(Analyser::Machine::MSX), MSX::get_options()));
|
||||||
options.emplace(std::make_pair(LongNameForTargetMachine(Analyser::Machine::Oric), Oric::get_options()));
|
options.emplace(std::make_pair(LongNameForTargetMachine(Analyser::Machine::Oric), Oric::get_options()));
|
||||||
|
@@ -612,6 +612,7 @@
|
|||||||
4BC39569208EE6CF0044766B /* DiskIICard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC39566208EE6CF0044766B /* DiskIICard.cpp */; };
|
4BC39569208EE6CF0044766B /* DiskIICard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC39566208EE6CF0044766B /* DiskIICard.cpp */; };
|
||||||
4BC3B74F1CD194CC00F86E85 /* Shader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC3B74D1CD194CC00F86E85 /* Shader.cpp */; };
|
4BC3B74F1CD194CC00F86E85 /* Shader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC3B74D1CD194CC00F86E85 /* Shader.cpp */; };
|
||||||
4BC3B7521CD1956900F86E85 /* OutputShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC3B7501CD1956900F86E85 /* OutputShader.cpp */; };
|
4BC3B7521CD1956900F86E85 /* OutputShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC3B7501CD1956900F86E85 /* OutputShader.cpp */; };
|
||||||
|
4BC5FC3020CDDDEF00410AA0 /* AppleIIOptions.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4BC5FC2E20CDDDEE00410AA0 /* AppleIIOptions.xib */; };
|
||||||
4BC751B21D157E61006C31D9 /* 6522Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC751B11D157E61006C31D9 /* 6522Tests.swift */; };
|
4BC751B21D157E61006C31D9 /* 6522Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC751B11D157E61006C31D9 /* 6522Tests.swift */; };
|
||||||
4BC76E691C98E31700E6EF73 /* FIRFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC76E671C98E31700E6EF73 /* FIRFilter.cpp */; };
|
4BC76E691C98E31700E6EF73 /* FIRFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC76E671C98E31700E6EF73 /* FIRFilter.cpp */; };
|
||||||
4BC76E6B1C98F43700E6EF73 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BC76E6A1C98F43700E6EF73 /* Accelerate.framework */; };
|
4BC76E6B1C98F43700E6EF73 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BC76E6A1C98F43700E6EF73 /* Accelerate.framework */; };
|
||||||
@@ -1353,6 +1354,7 @@
|
|||||||
4BC3B74E1CD194CC00F86E85 /* Shader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Shader.hpp; sourceTree = "<group>"; };
|
4BC3B74E1CD194CC00F86E85 /* Shader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Shader.hpp; sourceTree = "<group>"; };
|
||||||
4BC3B7501CD1956900F86E85 /* OutputShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OutputShader.cpp; sourceTree = "<group>"; };
|
4BC3B7501CD1956900F86E85 /* OutputShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OutputShader.cpp; sourceTree = "<group>"; };
|
||||||
4BC3B7511CD1956900F86E85 /* OutputShader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = OutputShader.hpp; sourceTree = "<group>"; };
|
4BC3B7511CD1956900F86E85 /* OutputShader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = OutputShader.hpp; sourceTree = "<group>"; };
|
||||||
|
4BC5FC2F20CDDDEE00410AA0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = "Clock Signal/Base.lproj/AppleIIOptions.xib"; sourceTree = SOURCE_ROOT; };
|
||||||
4BC751B11D157E61006C31D9 /* 6522Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 6522Tests.swift; sourceTree = "<group>"; };
|
4BC751B11D157E61006C31D9 /* 6522Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 6522Tests.swift; sourceTree = "<group>"; };
|
||||||
4BC76E671C98E31700E6EF73 /* FIRFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FIRFilter.cpp; sourceTree = "<group>"; };
|
4BC76E671C98E31700E6EF73 /* FIRFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FIRFilter.cpp; sourceTree = "<group>"; };
|
||||||
4BC76E681C98E31700E6EF73 /* FIRFilter.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FIRFilter.hpp; sourceTree = "<group>"; };
|
4BC76E681C98E31700E6EF73 /* FIRFilter.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FIRFilter.hpp; sourceTree = "<group>"; };
|
||||||
@@ -1986,6 +1988,7 @@
|
|||||||
4B55CE5E1C3B7D960093A61B /* MachineDocument.swift */,
|
4B55CE5E1C3B7D960093A61B /* MachineDocument.swift */,
|
||||||
4B8FE2211DA19FB20090D3CE /* MachinePanel.swift */,
|
4B8FE2211DA19FB20090D3CE /* MachinePanel.swift */,
|
||||||
4B95FA9C1F11893B0008E395 /* ZX8081OptionsPanel.swift */,
|
4B95FA9C1F11893B0008E395 /* ZX8081OptionsPanel.swift */,
|
||||||
|
4BC5FC2E20CDDDEE00410AA0 /* AppleIIOptions.xib */,
|
||||||
4B8FE2131DA19D5F0090D3CE /* Atari2600Options.xib */,
|
4B8FE2131DA19D5F0090D3CE /* Atari2600Options.xib */,
|
||||||
4B8FE2151DA19D5F0090D3CE /* MachineDocument.xib */,
|
4B8FE2151DA19D5F0090D3CE /* MachineDocument.xib */,
|
||||||
4B2A332B1DB86821002876E3 /* OricOptions.xib */,
|
4B2A332B1DB86821002876E3 /* OricOptions.xib */,
|
||||||
@@ -3277,6 +3280,7 @@
|
|||||||
4B2C45421E3C3896002A2389 /* cartridge.png in Resources */,
|
4B2C45421E3C3896002A2389 /* cartridge.png in Resources */,
|
||||||
4BB73EA91B587A5100552FC2 /* Assets.xcassets in Resources */,
|
4BB73EA91B587A5100552FC2 /* Assets.xcassets in Resources */,
|
||||||
4B79E4451E3AF38600141F11 /* floppy35.png in Resources */,
|
4B79E4451E3AF38600141F11 /* floppy35.png in Resources */,
|
||||||
|
4BC5FC3020CDDDEF00410AA0 /* AppleIIOptions.xib in Resources */,
|
||||||
4BA141BD2072E8A500A31EC9 /* MachinePicker.xib in Resources */,
|
4BA141BD2072E8A500A31EC9 /* MachinePicker.xib in Resources */,
|
||||||
4B1EDB451E39A0AC009D6819 /* chip.png in Resources */,
|
4B1EDB451E39A0AC009D6819 /* chip.png in Resources */,
|
||||||
4B2A332D1DB86821002876E3 /* OricOptions.xib in Resources */,
|
4B2A332D1DB86821002876E3 /* OricOptions.xib in Resources */,
|
||||||
@@ -4036,6 +4040,14 @@
|
|||||||
name = MainMenu.xib;
|
name = MainMenu.xib;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
4BC5FC2E20CDDDEE00410AA0 /* AppleIIOptions.xib */ = {
|
||||||
|
isa = PBXVariantGroup;
|
||||||
|
children = (
|
||||||
|
4BC5FC2F20CDDDEE00410AA0 /* Base */,
|
||||||
|
);
|
||||||
|
name = AppleIIOptions.xib;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
4BD61662206B2AC700236112 /* QuickLoadOptions.xib */ = {
|
4BD61662206B2AC700236112 /* QuickLoadOptions.xib */ = {
|
||||||
isa = PBXVariantGroup;
|
isa = PBXVariantGroup;
|
||||||
children = (
|
children = (
|
||||||
|
49
OSBindings/Mac/Clock Signal/Base.lproj/AppleIIOptions.xib
Normal file
49
OSBindings/Mac/Clock Signal/Base.lproj/AppleIIOptions.xib
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14113" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="macosx"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14113"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<objects>
|
||||||
|
<customObject id="-2" userLabel="File's Owner" customClass="MachineDocument" customModule="Clock_Signal" customModuleProvider="target">
|
||||||
|
<connections>
|
||||||
|
<outlet property="optionsPanel" destination="ZW7-Bw-4RP" id="JpE-wG-zRR"/>
|
||||||
|
</connections>
|
||||||
|
</customObject>
|
||||||
|
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||||
|
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||||
|
<window title="Options" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" hidesOnDeactivate="YES" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="ZW7-Bw-4RP" customClass="MachinePanel" customModule="Clock_Signal" customModuleProvider="target">
|
||||||
|
<windowStyleMask key="styleMask" titled="YES" closable="YES" utility="YES" nonactivatingPanel="YES" HUD="YES"/>
|
||||||
|
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||||
|
<rect key="contentRect" x="83" y="102" width="200" height="54"/>
|
||||||
|
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="900"/>
|
||||||
|
<view key="contentView" id="tpZ-0B-QQu">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="200" height="54"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<button translatesAutoresizingMaskIntoConstraints="NO" id="e1J-pw-zGw">
|
||||||
|
<rect key="frame" x="18" y="18" width="164" height="18"/>
|
||||||
|
<buttonCell key="cell" type="check" title="Accelerate DOS 3.3" bezelStyle="regularSquare" imagePosition="left" alignment="left" state="on" inset="2" id="tD6-UB-ESB">
|
||||||
|
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="setFastLoading:" target="ZW7-Bw-4RP" id="JmG-Ks-jSh"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="e1J-pw-zGw" secondAttribute="bottom" constant="20" id="5ce-DO-a4T"/>
|
||||||
|
<constraint firstItem="e1J-pw-zGw" firstAttribute="leading" secondItem="tpZ-0B-QQu" secondAttribute="leading" constant="20" id="HSD-3d-Bl7"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="e1J-pw-zGw" secondAttribute="trailing" constant="20" id="Q9M-FH-92N"/>
|
||||||
|
<constraint firstItem="e1J-pw-zGw" firstAttribute="top" secondItem="tpZ-0B-QQu" secondAttribute="top" constant="20" id="ul9-lf-Y3u"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="fastLoadingButton" destination="e1J-pw-zGw" id="jj7-OZ-mOH"/>
|
||||||
|
</connections>
|
||||||
|
<point key="canvasLocation" x="175" y="30"/>
|
||||||
|
</window>
|
||||||
|
</objects>
|
||||||
|
</document>
|
@@ -183,7 +183,7 @@ static Analyser::Static::ZX8081::Target::MemoryModel ZX8081MemoryModelFromSize(K
|
|||||||
|
|
||||||
- (NSString *)optionsPanelNibName {
|
- (NSString *)optionsPanelNibName {
|
||||||
switch(_targets.front()->machine) {
|
switch(_targets.front()->machine) {
|
||||||
case Analyser::Machine::AmstradCPC: return nil;
|
case Analyser::Machine::AppleII: return @"AppleIIOptions";
|
||||||
case Analyser::Machine::Atari2600: return @"Atari2600Options";
|
case Analyser::Machine::Atari2600: return @"Atari2600Options";
|
||||||
case Analyser::Machine::Electron: return @"QuickLoadCompositeOptions";
|
case Analyser::Machine::Electron: return @"QuickLoadCompositeOptions";
|
||||||
case Analyser::Machine::MSX: return @"QuickLoadCompositeOptions";
|
case Analyser::Machine::MSX: return @"QuickLoadCompositeOptions";
|
||||||
|
@@ -74,17 +74,15 @@ std::shared_ptr<Track> AppleDSK::get_track_at_position(Track::Address address) {
|
|||||||
|
|
||||||
// In either case below, the code aims for exactly 50,000 bits per track.
|
// In either case below, the code aims for exactly 50,000 bits per track.
|
||||||
if(sectors_per_track_ == 16) {
|
if(sectors_per_track_ == 16) {
|
||||||
|
// Write gap 1.
|
||||||
|
segment += Encodings::AppleGCR::six_and_two_sync(16);
|
||||||
|
|
||||||
// Write the sectors.
|
// Write the sectors.
|
||||||
for(uint8_t c = 0; c < 16; ++c) {
|
for(uint8_t c = 0; c < 16; ++c) {
|
||||||
segment += Encodings::AppleGCR::six_and_two_sync(10);
|
|
||||||
segment += Encodings::AppleGCR::header(254, track, c);
|
segment += Encodings::AppleGCR::header(254, track, c);
|
||||||
segment += Encodings::AppleGCR::six_and_two_sync(10);
|
segment += Encodings::AppleGCR::six_and_two_sync(7); // Gap 2: 7 sync words.
|
||||||
segment += Encodings::AppleGCR::six_and_two_data(&track_data[logical_sector_for_physical_sector(c) * 256]);
|
segment += Encodings::AppleGCR::six_and_two_data(&track_data[logical_sector_for_physical_sector(c) * 256]);
|
||||||
}
|
segment += Encodings::AppleGCR::six_and_two_sync(16); // Gap 3: 16 sync words.
|
||||||
|
|
||||||
// Pad if necessary.
|
|
||||||
if(segment.number_of_bits < 50000) {
|
|
||||||
segment += Encodings::AppleGCR::six_and_two_sync((50000 - segment.number_of_bits) / 10);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// TODO: 5 and 3, 13-sector format. If DSK actually supports it?
|
// TODO: 5 and 3, 13-sector format. If DSK actually supports it?
|
||||||
|
@@ -77,6 +77,18 @@ void Drive::step(HeadPosition offset) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Track> Drive::step_to(HeadPosition offset) {
|
||||||
|
HeadPosition old_head_position = head_position_;
|
||||||
|
head_position_ = std::max(offset, HeadPosition(0));
|
||||||
|
|
||||||
|
if(head_position_ != old_head_position) {
|
||||||
|
track_ = nullptr;
|
||||||
|
setup_track();
|
||||||
|
}
|
||||||
|
|
||||||
|
return track_;
|
||||||
|
}
|
||||||
|
|
||||||
void Drive::set_head(int head) {
|
void Drive::set_head(int head) {
|
||||||
head = std::min(head, available_heads_ - 1);
|
head = std::min(head, available_heads_ - 1);
|
||||||
if(head != head_) {
|
if(head != head_) {
|
||||||
|
@@ -127,6 +127,17 @@ class Drive: public ClockingHint::Source, public TimedEventLoop {
|
|||||||
/// The caller can specify whether to add an LED based on disk motor.
|
/// The caller can specify whether to add an LED based on disk motor.
|
||||||
void set_activity_observer(Activity::Observer *observer, const std::string &name, bool add_motor_led);
|
void set_activity_observer(Activity::Observer *observer, const std::string &name, bool add_motor_led);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Attempts to step to the specified offset and returns the track there if one exists; an uninitialised
|
||||||
|
track otherwise.
|
||||||
|
|
||||||
|
This is unambiguously **NOT A REALISTIC DRIVE FUNCTION**; real drives cannot step to a given offset.
|
||||||
|
So it is **NOT FOR HARDWARE EMULATION USAGE**.
|
||||||
|
|
||||||
|
It's for the benefit of user-optional fast-loading mechanisms **ONLY**.
|
||||||
|
*/
|
||||||
|
std::shared_ptr<Track> step_to(HeadPosition offset);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Drives contain an entire disk; from that a certain track
|
// Drives contain an entire disk; from that a certain track
|
||||||
// will be currently under the head.
|
// will be currently under the head.
|
||||||
|
@@ -46,16 +46,18 @@ void PCMSegmentEventSource::reset() {
|
|||||||
PCMSegment &PCMSegment::operator +=(const PCMSegment &rhs) {
|
PCMSegment &PCMSegment::operator +=(const PCMSegment &rhs) {
|
||||||
if(!rhs.number_of_bits) return *this;
|
if(!rhs.number_of_bits) return *this;
|
||||||
|
|
||||||
|
assert(((rhs.number_of_bits+7) >> 3) == rhs.data.size());
|
||||||
|
|
||||||
if(number_of_bits&7) {
|
if(number_of_bits&7) {
|
||||||
auto target_number_of_bits = number_of_bits + rhs.number_of_bits;
|
auto target_number_of_bits = number_of_bits + rhs.number_of_bits;
|
||||||
data.resize((target_number_of_bits + 7) >> 3);
|
data.resize((target_number_of_bits + 7) >> 3);
|
||||||
|
|
||||||
std::size_t first_byte = number_of_bits >> 3;
|
std::size_t first_byte = number_of_bits >> 3;
|
||||||
|
|
||||||
int shift = number_of_bits&7;
|
const int shift = number_of_bits&7;
|
||||||
for(std::size_t source = 0; source < rhs.data.size(); ++source) {
|
for(std::size_t source = 0; source < rhs.data.size(); ++source) {
|
||||||
data[first_byte + source] |= rhs.data[source] >> shift;
|
data[first_byte + source] |= rhs.data[source] >> shift;
|
||||||
if(source*8 + static_cast<std::size_t>(shift) < rhs.number_of_bits)
|
if(source*8 + static_cast<std::size_t>(8 - shift) < rhs.number_of_bits)
|
||||||
data[first_byte + source + 1] = static_cast<uint8_t>(rhs.data[source] << (8-shift));
|
data[first_byte + source + 1] = static_cast<uint8_t>(rhs.data[source] << (8-shift));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,6 +67,8 @@ PCMSegment &PCMSegment::operator +=(const PCMSegment &rhs) {
|
|||||||
number_of_bits += rhs.number_of_bits;
|
number_of_bits += rhs.number_of_bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(((number_of_bits+7) >> 3) == data.size());
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -39,22 +39,22 @@ class HeadPosition {
|
|||||||
position_ += rhs.position_;
|
position_ += rhs.position_;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
bool operator ==(const HeadPosition &rhs) {
|
bool operator ==(const HeadPosition &rhs) const {
|
||||||
return position_ == rhs.position_;
|
return position_ == rhs.position_;
|
||||||
}
|
}
|
||||||
bool operator !=(const HeadPosition &rhs) {
|
bool operator !=(const HeadPosition &rhs) const {
|
||||||
return position_ != rhs.position_;
|
return position_ != rhs.position_;
|
||||||
}
|
}
|
||||||
bool operator <(const HeadPosition &rhs) {
|
bool operator <(const HeadPosition &rhs) const {
|
||||||
return position_ < rhs.position_;
|
return position_ < rhs.position_;
|
||||||
}
|
}
|
||||||
bool operator <=(const HeadPosition &rhs) {
|
bool operator <=(const HeadPosition &rhs) const {
|
||||||
return position_ <= rhs.position_;
|
return position_ <= rhs.position_;
|
||||||
}
|
}
|
||||||
bool operator >(const HeadPosition &rhs) {
|
bool operator >(const HeadPosition &rhs) const {
|
||||||
return position_ > rhs.position_;
|
return position_ > rhs.position_;
|
||||||
}
|
}
|
||||||
bool operator >=(const HeadPosition &rhs) {
|
bool operator >=(const HeadPosition &rhs) const {
|
||||||
return position_ >= rhs.position_;
|
return position_ >= rhs.position_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user