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:
parent
3adf3dc547
commit
a6e453a452
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
|
@ -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?
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user