From 50348c9fe7907aa19c7a19a945f6bc5817161edf Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 19 Jan 2020 12:11:56 -0500 Subject: [PATCH 1/2] Adds the ability to substitute a target during encoding. --- Storage/Disk/Encodings/MFM/Encoder.cpp | 8 ++++++-- Storage/Disk/Encodings/MFM/Encoder.hpp | 4 +++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Storage/Disk/Encodings/MFM/Encoder.cpp b/Storage/Disk/Encodings/MFM/Encoder.cpp index 642db4202..c25becfca 100644 --- a/Storage/Disk/Encodings/MFM/Encoder.cpp +++ b/Storage/Disk/Encodings/MFM/Encoder.cpp @@ -241,12 +241,16 @@ template std::shared_ptr } Encoder::Encoder(std::vector &target) : - target_(target) {} + target_(&target) {} + +void Encoder::reset_target(std::vector &target) { + target_ = ⌖ +} void Encoder::output_short(uint16_t value) { uint16_t mask = 0x8000; while(mask) { - target_.push_back(!!(value & mask)); + target_->push_back(!!(value & mask)); mask >>= 1; } } diff --git a/Storage/Disk/Encodings/MFM/Encoder.hpp b/Storage/Disk/Encodings/MFM/Encoder.hpp index 225a1bb4f..48a538ec8 100644 --- a/Storage/Disk/Encodings/MFM/Encoder.hpp +++ b/Storage/Disk/Encodings/MFM/Encoder.hpp @@ -46,6 +46,8 @@ class Encoder { public: Encoder(std::vector &target); virtual ~Encoder() {} + virtual void reset_target(std::vector &target); + virtual void add_byte(uint8_t input) = 0; virtual void add_index_address_mark() = 0; virtual void add_ID_address_mark() = 0; @@ -60,7 +62,7 @@ class Encoder { CRC::CCITT crc_generator_; private: - std::vector &target_; + std::vector *target_ = nullptr; }; std::unique_ptr GetMFMEncoder(std::vector &target); From f34ddce28f486bc89001629162cd6bc6ff1c094e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 19 Jan 2020 12:38:33 -0500 Subject: [PATCH 2/2] Adds support for STX speed zones. --- Storage/Disk/DiskImage/Formats/STX.cpp | 61 +++++++++++++++++++------- 1 file changed, 44 insertions(+), 17 deletions(-) diff --git a/Storage/Disk/DiskImage/Formats/STX.cpp b/Storage/Disk/DiskImage/Formats/STX.cpp index 9ccfbada6..9b53ece13 100644 --- a/Storage/Disk/DiskImage/Formats/STX.cpp +++ b/Storage/Disk/DiskImage/Formats/STX.cpp @@ -201,7 +201,7 @@ class TrackConstructor { // Otherwise, seek to encode the sectors, using the track data to // fill in the gaps (if provided). std::unique_ptr encoder; - std::unique_ptr segment; + std::vector segments; // To reconcile the list of sectors with the WD get track-style track image, // use sector bodies as definitive and refer to the track image for in-fill. @@ -256,11 +256,18 @@ class TrackConstructor { } } - // Just create an encoder if one doesn't exist. TODO: factor in data rate. - if(!encoder) { - segment.reset(new PCMSegment); - encoder = Storage::Encodings::MFM::GetMFMEncoder(segment->data); - } + const auto encoder_at_rate = [&encoder, &segments](unsigned int rate) -> Storage::Encodings::MFM::Encoder* { + if(!encoder) { + segments.emplace_back(); + segments.back().length_of_a_bit = Storage::Time(int(rate), 1); + encoder = Storage::Encodings::MFM::GetMFMEncoder(segments.back().data); + } else if(segments.back().length_of_a_bit.length != rate) { + segments.emplace_back(); + segments.back().length_of_a_bit = Storage::Time(int(rate), 1); + encoder->reset_target(segments.back().data); + } + return encoder.get(); + }; // Write out, being wary of potential overlapping sectors, and copying from track_data_ to fill in gaps. auto location = locations.begin(); @@ -269,8 +276,9 @@ class TrackConstructor { // assert(location->position >= track_position && location->position < track_data_.end()); // Advance to location.position. + auto default_rate_encoder = encoder_at_rate(128); while(track_position < location->position) { - encoder->add_byte(*track_position); + default_rate_encoder->add_byte(*track_position); ++track_position; } @@ -279,14 +287,14 @@ class TrackConstructor { switch(location->type) { default: case Location::Address: - encoder->add_ID_address_mark(); + default_rate_encoder->add_ID_address_mark(); bytes_to_write = 6; break; case Location::Data: if(location->sector.status & 0x20) - encoder->add_deleted_data_address_mark(); + default_rate_encoder->add_deleted_data_address_mark(); else - encoder->add_data_address_mark(); + default_rate_encoder->add_data_address_mark(); bytes_to_write = location->sector.data_size() + 2; break; } @@ -306,16 +314,27 @@ class TrackConstructor { default: case Location::Address: for(size_t c = 0; c < bytes_to_write; ++c) - encoder->add_byte(location->sector.address[c]); + default_rate_encoder->add_byte(location->sector.address[c]); break; case Location::Data: { const auto body_bytes = std::min(bytes_to_write, size_t(location->sector.data_size())); - for(size_t c = 0; c < body_bytes; ++c) - encoder->add_byte(location->sector.contents[c]); + + // If timing information is attached to this sector, write each byte at the proper speed. + // (TODO: is there any benefit to optiming number of calls to encoder_at_rate?) + if(!location->sector.timing.empty()) { + for(size_t c = 0; c < body_bytes; ++c) { + encoder_at_rate(location->sector.timing[c >> 4])->add_byte(location->sector.contents[c]); + } + } else { + for(size_t c = 0; c < body_bytes; ++c) { + default_rate_encoder->add_byte(location->sector.contents[c]); + } + } // Add a CRC only if it fits (TODO: crop if necessary?). if(bytes_to_write & 127) { - encoder->add_crc((location->sector.status & 0x18) == 0x10); + default_rate_encoder = encoder_at_rate(128); + default_rate_encoder->add_crc((location->sector.status & 0x18) == 0x10); } } break; } @@ -330,20 +349,28 @@ class TrackConstructor { ++track_position; } + // Count total size of track. + size_t track_size = 0; + for(auto &segment: segments) { + track_size += segment.data.size(); + } + // Write generic padding up until the specified track size. - while(segment->data.size() < track_size_ * 16) { + while(track_size < track_size_ * 16) { encoder->add_byte(0x4e); + track_size += 16; } // Pad out to the minimum size a WD can actually make sense of. // I've no idea why it's valid for tracks to be shorter than this, // so likely I'm suffering a comprehansion deficiency. // TODO: determine why this isn't correct (or, possibly, is). - while(segment->data.size() < 5750 * 16) { + while(track_size < 5750 * 16) { encoder->add_byte(0x4e); + track_size += 16; } - return std::make_shared(*segment); + return std::make_shared(segments); } private: