1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-02-19 07:31:15 +00:00

Introdice alternative tape timings for the +4.

This commit is contained in:
Thomas Harte 2025-01-15 22:11:26 -05:00
parent 3adf3dc547
commit a6e453a452
16 changed files with 135 additions and 65 deletions

View File

@ -18,6 +18,9 @@ MultiMediaTarget::MultiMediaTarget(const std::vector<std::unique_ptr<::Machine::
}
bool MultiMediaTarget::insert_media(const Analyser::Static::Media &media) {
// TODO: copy media afresh for each target machine; media
// generally has mutable state.
bool inserted = false;
for(const auto &target : targets_) {
inserted |= target->insert_media(media);

View File

@ -127,9 +127,9 @@ public:
potential_platforms_ |= platforms;
// Check whether the instance itself has any input on target platforms.
TargetPlatform::TypeDistinguisher *const distinguisher =
dynamic_cast<TargetPlatform::TypeDistinguisher *>(instance.get());
if(distinguisher) potential_platforms_ &= distinguisher->target_platform_type();
TargetPlatform::Distinguisher *const distinguisher =
dynamic_cast<TargetPlatform::Distinguisher *>(instance.get());
if(distinguisher) potential_platforms_ &= distinguisher->target_platforms();
}
/// Concstructs a new instance of @c InstanceT supplying @c args and adds it to the back of @c list using @c insert_instance.

View File

@ -14,6 +14,7 @@
#include "../../Storage/Disk/Disk.hpp"
#include "../../Storage/MassStorage/MassStorageDevice.hpp"
#include "../../Storage/Tape/Tape.hpp"
#include "../../Storage/TargetPlatforms.hpp"
#include "../../Reflection/Struct.hpp"
#include <memory>
@ -37,6 +38,20 @@ struct Media {
return disks.empty() && tapes.empty() && cartridges.empty() && mass_storage_devices.empty();
}
void set_target_platforms(const TargetPlatform::Type target) {
const auto propagate = [&](const auto &list) {
for(const auto &item: list) {
if(auto *recipient = dynamic_cast<TargetPlatform::Recipient *>(item.get())) {
recipient->set_target_platforms(target);
}
}
};
propagate(disks);
propagate(tapes);
propagate(cartridges);
propagate(mass_storage_devices);
}
Media &operator +=(const Media &rhs) {
const auto append = [&](auto &destination, auto &source) {
destination.insert(destination.end(), source.begin(), source.end());

View File

@ -330,6 +330,10 @@ public:
*value = 0xff ^ (play_button_ ? 0x4 :0x0);
break;
case 0xfdd0:
*value = 0xff;
break;
default:
printf("TODO: read @ %04x\n", address);
break;
@ -340,6 +344,12 @@ public:
keyboard_mask_ = *value;
break;
case 0xfdd0: {
// const auto low = address & 3;
// const auto high = (address >> 2) & 3;
// TODO: set up ROMs.
} break;
default:
printf("TODO: write of %02x @ %04x\n", *value, address);
break;

View File

@ -57,7 +57,7 @@ public:
case 0xff1a: return uint8_t(character_position_reload_ >> 8) | 0xfc;
case 0xff1b: return uint8_t(character_position_reload_);
case 0xff1c: return uint8_t(vertical_counter_ >> 8);
case 0xff1c: return uint8_t(vertical_counter_ >> 8) | 0xfe;
case 0xff1d: return uint8_t(vertical_counter_);
case 0xff1e: return uint8_t(horizontal_counter_ >> 1);
case 0xff1f:
@ -163,7 +163,7 @@ public:
case 0xff1a: load_high10(character_position_reload_); break;
case 0xff1b: load_low8(character_position_reload_); break;
case 0xff1c: vertical_counter_ = (vertical_counter_ & 0x00ff) | ((value & 3) << 8); break;
case 0xff1c: vertical_counter_ = (vertical_counter_ & 0x00ff) | ((value & 1) << 8); break;
case 0xff1d: vertical_counter_ = (vertical_counter_ & 0xff00) | value; break;
case 0xff1e:
// TODO: possibly should be deferred, if falling out of phase?

View File

@ -20,7 +20,7 @@ namespace MachineTypes {
*/
struct MediaTarget {
/*!
Requests that the machine insert @c media as a modification to current state
Requests that the machine insert @c media as a modification to current state.
@returns @c true if any media was inserted; @c false otherwise.
*/

View File

@ -44,13 +44,19 @@ enum class Error {
receive the supplied static analyser result. The machine has been allocated
on the heap. It is the caller's responsibility to delete the class when finished.
*/
std::unique_ptr<DynamicMachine> MachineForTargets(const Analyser::Static::TargetList &targets, const ::ROMMachine::ROMFetcher &rom_fetcher, Error &error);
std::unique_ptr<DynamicMachine> MachineForTargets(
const Analyser::Static::TargetList &,
const ::ROMMachine::ROMFetcher &,
Error &);
/*!
Allocates an instance of DynamicMaachine holding the machine described
by @c target. It is the caller's responsibility to delete the class when finished.
*/
std::unique_ptr<DynamicMachine> MachineForTarget(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher, Machine::Error &error);
std::unique_ptr<DynamicMachine> MachineForTarget(
const Analyser::Static::Target *,
const ROMMachine::ROMFetcher &,
Machine::Error &);
/*!
Returns a short string name for the machine identified by the target,
@ -90,6 +96,7 @@ std::map<std::string, std::unique_ptr<Reflection::Struct>> AllOptionsByMachineNa
NB: Usually the instances of Target can be dynamic_casted to Reflection::Struct in order to determine available properties.
*/
std::map<std::string, std::unique_ptr<Analyser::Static::Target>> TargetsByMachineName(bool meaningful_without_media_only);
std::map<std::string, std::unique_ptr<Analyser::Static::Target>>
TargetsByMachineName(bool meaningful_without_media_only);
}

View File

@ -95,7 +95,7 @@ class DiskImageHolderBase: public Disk {
Implements TargetPlatform::TypeDistinguisher to return either no information whatsoever, if
the underlying image doesn't implement TypeDistinguisher, or else to pass the call along.
*/
template <typename T> class DiskImageHolder: public DiskImageHolderBase, public TargetPlatform::TypeDistinguisher {
template <typename T> class DiskImageHolder: public DiskImageHolderBase, public TargetPlatform::Distinguisher {
public:
template <typename... Ts> DiskImageHolder(Ts&&... args) :
disk_image_(args...) {}
@ -112,9 +112,9 @@ public:
private:
T disk_image_;
TargetPlatform::Type target_platform_type() final {
if constexpr (std::is_base_of<TargetPlatform::TypeDistinguisher, T>::value) {
return static_cast<TargetPlatform::TypeDistinguisher *>(&disk_image_)->target_platform_type();
TargetPlatform::Type target_platforms() final {
if constexpr (std::is_base_of<TargetPlatform::Distinguisher, T>::value) {
return static_cast<TargetPlatform::Distinguisher *>(&disk_image_)->target_platforms();
} else {
return TargetPlatform::Type(~0);
}

View File

@ -24,7 +24,7 @@ namespace Storage::Disk {
of which is variably clocked (albeit not at flux transition resolution; as a result IPF files tend to be
close in size to more primitive formats).
*/
class IPF: public DiskImage, public TargetPlatform::TypeDistinguisher {
class IPF: public DiskImage, public TargetPlatform::Distinguisher {
public:
/*!
Construct an @c IPF containing content from the file with name @c file_name.
@ -72,7 +72,7 @@ private:
std::map<Track::Address, TrackDescription> tracks_;
bool is_sps_format_ = false;
TargetPlatform::Type target_platform_type() final {
TargetPlatform::Type target_platforms() final {
return TargetPlatform::Type(platform_type_);
}
TargetPlatform::IntType platform_type_ = TargetPlatform::Amiga;

View File

@ -51,7 +51,8 @@ using namespace Storage::Tape;
PRG::PRG(const std::string &file_name) : Tape(serialiser_), serialiser_(file_name) {}
PRG::Serialiser::Serialiser(const std::string &file_name) :
file_(file_name, FileHolder::FileMode::Read)
file_(file_name, FileHolder::FileMode::Read),
timings_(false)
{
// There's really no way to validate other than that if this file is larger than 64kb,
// of if load address + length > 65536 then it's broken.
@ -65,26 +66,28 @@ PRG::Serialiser::Serialiser(const std::string &file_name) :
throw ErrorBadFormat;
}
Storage::Tape::Pulse PRG::Serialiser::next_pulse() {
// The below are in microseconds per pole.
constexpr unsigned int leader_zero_length = 179;
constexpr unsigned int zero_length = 169;
constexpr unsigned int one_length = 247;
constexpr unsigned int marker_length = 328;
void PRG::set_target_platforms(TargetPlatform::Type type) {
serialiser_.set_target_platforms(type);
}
bit_phase_ = (bit_phase_+1)&3;
void PRG::Serialiser::set_target_platforms(TargetPlatform::Type type) {
timings_ = Timings(type & TargetPlatform::Type::Plus4);
}
Storage::Tape::Pulse PRG::Serialiser::next_pulse() {
bit_phase_ = (bit_phase_ + 1)&3;
if(!bit_phase_) get_next_output_token();
Pulse pulse;
pulse.length.clock_rate = 1'000'000;
pulse.type = (bit_phase_&1) ? Pulse::High : Pulse::Low;
switch(output_token_) {
case Leader: pulse.length.length = leader_zero_length; break;
case Zero: pulse.length.length = (bit_phase_&2) ? one_length : zero_length; break;
case One: pulse.length.length = (bit_phase_&2) ? zero_length : one_length; break;
case WordMarker: pulse.length.length = (bit_phase_&2) ? one_length : marker_length; break;
case EndOfBlock: pulse.length.length = (bit_phase_&2) ? zero_length : marker_length; break;
case Silence: pulse.type = Pulse::Zero; pulse.length.length = 5000; break;
case Leader: pulse.length.length = timings_.leader_zero_length; break;
case Zero: pulse.length.length = (bit_phase_&2) ? timings_.one_length : timings_.zero_length; break;
case One: pulse.length.length = (bit_phase_&2) ? timings_.zero_length : timings_.one_length; break;
case WordMarker: pulse.length.length = (bit_phase_&2) ? timings_.one_length : timings_.marker_length; break;
case EndOfBlock: pulse.length.length = (bit_phase_&2) ? timings_.zero_length : timings_.marker_length; break;
case Silence: pulse.type = Pulse::Zero; pulse.length.length = 5000; break;
}
return pulse;
}

View File

@ -10,6 +10,7 @@
#include "../Tape.hpp"
#include "../../FileHolder.hpp"
#include "../../TargetPlatforms.hpp"
#include <cstdint>
#include <string>
@ -19,7 +20,7 @@ namespace Storage::Tape {
/*!
Provides a @c Tape containing a .PRG, which is a direct local file.
*/
class PRG: public Tape {
class PRG: public Tape, public TargetPlatform::Recipient {
public:
/*!
Constructs a @c T64 containing content from the file with name @c file_name, of type @c type.
@ -34,8 +35,11 @@ public:
};
private:
void set_target_platforms(TargetPlatform::Type) override;
struct Serialiser: public TapeSerialiser {
Serialiser(const std::string &file_name);
void set_target_platforms(TargetPlatform::Type);
private:
bool is_at_end() const override;
Pulse next_pulse() override;
@ -68,6 +72,20 @@ private:
uint8_t output_byte_;
uint8_t check_digit_;
uint8_t copy_mask_ = 0x80;
struct Timings {
Timings(bool is_plus4) :
leader_zero_length( is_plus4 ? 240 : 179),
zero_length( is_plus4 ? 240 : 169),
one_length( is_plus4 ? 480 : 247),
marker_length( is_plus4 ? 960 : 328) {}
// The below are in microseconds per pole.
unsigned int leader_zero_length;
unsigned int zero_length;
unsigned int one_length;
unsigned int marker_length;
} timings_;
} serialiser_;
};

View File

@ -323,7 +323,7 @@ void UEF::Serialiser::queue_bit(const int bit) {
// MARK: - TypeDistinguisher
TargetPlatform::Type UEF::target_platform_type() {
TargetPlatform::Type UEF::target_platforms() {
return serialiser_.target_platform_type();
}

View File

@ -21,7 +21,7 @@ namespace Storage::Tape {
/*!
Provides a @c Tape containing a UEF tape image, a slightly-convoluted description of pulses.
*/
class UEF : public Tape, public TargetPlatform::TypeDistinguisher {
class UEF : public Tape, public TargetPlatform::Distinguisher {
public:
/*!
Constructs a @c UEF containing content from the file with name @c file_name.
@ -35,7 +35,7 @@ public:
};
private:
TargetPlatform::Type target_platform_type() override;
TargetPlatform::Type target_platforms() override;
struct Serialiser: public PulseQueuedSerialiser {
Serialiser(const std::string &file_name);

View File

@ -105,7 +105,7 @@ Pulse ZX80O81P::Serialiser::next_pulse() {
return pulse;
}
TargetPlatform::Type ZX80O81P::target_platform_type() {
TargetPlatform::Type ZX80O81P::target_platforms() {
return serialiser_.target_platform_type();
}

View File

@ -22,7 +22,7 @@ namespace Storage::Tape {
/*!
Provides a @c Tape containing a ZX80-format .O tape image, which is a byte stream capture.
*/
class ZX80O81P: public Tape, public TargetPlatform::TypeDistinguisher {
class ZX80O81P: public Tape, public TargetPlatform::Distinguisher {
public:
/*!
Constructs a @c ZX80O containing content from the file with name @c file_name.
@ -37,7 +37,7 @@ public:
private:
// TargetPlatform::TypeDistinguisher.
TargetPlatform::Type target_platform_type() override;
TargetPlatform::Type target_platforms() override;
struct Serialiser: public TapeSerialiser {
Serialiser(const std::string &file_name);

View File

@ -12,45 +12,59 @@ namespace TargetPlatform {
using IntType = int;
constexpr IntType bit(int index) {
return 1 << index;
}
// The below is somehwat overspecified because some of the file formats already supported by this
// emulator can self-specify platforms beyond those the emulator otherwise implements.
enum Type: IntType {
AmstradCPC = 1 << 0,
AppleII = 1 << 1,
AppleIIgs = 1 << 2,
Atari2600 = 1 << 3,
AtariST = 1 << 4,
AcornAtom = 1 << 5,
AcornElectron = 1 << 6,
Amiga = 1 << 7,
Archimedes = 1 << 8,
BBCMaster = 1 << 9,
BBCModelA = 1 << 10,
BBCModelB = 1 << 11,
Coleco = 1 << 12,
Commodore = 1 << 13,
DiskII = 1 << 14,
Enterprise = 1 << 15,
Sega = 1 << 16,
Macintosh = 1 << 17,
MSX = 1 << 18,
Oric = 1 << 19,
ZX80 = 1 << 20,
ZX81 = 1 << 21,
ZXSpectrum = 1 << 22,
PCCompatible = 1 << 23,
FAT12 = 1 << 24,
AcornAtom = bit(0),
AcornElectron = bit(1),
Amiga = bit(2),
AmstradCPC = bit(3),
AppleII = bit(4),
AppleIIgs = bit(5),
Archimedes = bit(6),
Atari2600 = bit(7),
AtariST = bit(8),
BBCMaster = bit(9),
BBCModelA = bit(10),
BBCModelB = bit(11),
C64 = bit(12),
Coleco = bit(13),
DiskII = bit(14),
Enterprise = bit(15),
FAT12 = bit(16),
Macintosh = bit(17),
MSX = bit(18),
Oric = bit(19),
PCCompatible = bit(20),
Plus4 = bit(21),
Sega = bit(22),
Vic20 = bit(23),
ZX80 = bit(24),
ZX81 = bit(25),
ZXSpectrum = bit(26),
Acorn = AcornAtom | AcornElectron | BBCMaster | BBCModelA | BBCModelB | Archimedes,
Commodore8bit = C64 | Plus4 | Vic20,
Commodore = Amiga | Commodore8bit,
ZX8081 = ZX80 | ZX81,
AllCartridge = Atari2600 | AcornElectron | Coleco | MSX,
AllDisk = Acorn | AmstradCPC | Commodore | Oric | MSX | ZXSpectrum | Macintosh | AtariST | DiskII | Amiga | PCCompatible | FAT12,
AllTape = Acorn | AmstradCPC | Commodore | Oric | ZX8081 | MSX | ZXSpectrum,
AllDisk = Acorn | Commodore | AmstradCPC | C64 | Oric | MSX | ZXSpectrum | Macintosh | AtariST | DiskII | PCCompatible | FAT12,
AllTape = Acorn | AmstradCPC | Commodore8bit | Oric | ZX8081 | MSX | ZXSpectrum,
};
class TypeDistinguisher {
class Distinguisher {
public:
virtual Type target_platform_type() = 0;
virtual Type target_platforms() = 0;
};
class Recipient {
public:
virtual void set_target_platforms(Type) = 0;
};
}