From 555c2a4377416454ce8cdda6343b144e0531cd1c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 1 Jul 2018 12:05:41 -0400 Subject: [PATCH] Makes a first sweep at converting the storage underlying PCMSegment to `vector`. This is to remove another pain point, in preparation for the work immediately forthcoming but also work as-yet unknown. --- Storage/Disk/DiskImage/Formats/D64.cpp | 11 +- Storage/Disk/DiskImage/Formats/DMK.cpp | 5 - Storage/Disk/DiskImage/Formats/G64.cpp | 18 +-- Storage/Disk/DiskImage/Formats/HFE.cpp | 52 +++++--- Storage/Disk/DiskImage/Formats/HFE.hpp | 1 - Storage/Disk/DiskImage/Formats/NIB.cpp | 14 +-- Storage/Disk/DiskImage/Formats/OricMFMDSK.cpp | 5 +- Storage/Disk/DiskImage/Formats/WOZ.cpp | 21 ++-- Storage/Disk/Drive.cpp | 7 +- Storage/Disk/Encodings/AppleGCR/Encoder.cpp | 91 +++++++------- .../Disk/Encodings/AppleGCR/SegmentParser.cpp | 8 +- Storage/Disk/Encodings/MFM/Encoder.cpp | 17 +-- Storage/Disk/Encodings/MFM/Encoder.hpp | 8 +- Storage/Disk/Encodings/MFM/SegmentParser.cpp | 4 +- Storage/Disk/Track/PCMSegment.cpp | 42 ++----- Storage/Disk/Track/PCMSegment.hpp | 114 ++++++++++++++++-- Storage/Disk/Track/PCMTrack.cpp | 27 +++-- Storage/Disk/Track/TrackSerialiser.cpp | 5 +- 18 files changed, 265 insertions(+), 185 deletions(-) diff --git a/Storage/Disk/DiskImage/Formats/D64.cpp b/Storage/Disk/DiskImage/Formats/D64.cpp index 75aa977e0..35f277af1 100644 --- a/Storage/Disk/DiskImage/Formats/D64.cpp +++ b/Storage/Disk/DiskImage/Formats/D64.cpp @@ -79,16 +79,11 @@ std::shared_ptr D64::get_track_at_position(Track::Address address) { // // = 349 GCR bytes per sector - PCMSegment track; std::size_t track_bytes = 349 * static_cast(sectors_by_zone[zone]); - track.number_of_bits = static_cast(track_bytes) * 8; - track.data.resize(track_bytes); - uint8_t *data = &track.data[0]; - - memset(data, 0, track_bytes); + std::vector data(track_bytes); for(int sector = 0; sector < sectors_by_zone[zone]; sector++) { - uint8_t *sector_data = &data[sector * 349]; + uint8_t *sector_data = &data[static_cast(sector) * 349]; sector_data[0] = sector_data[1] = sector_data[2] = 0xff; uint8_t sector_number = static_cast(sector); // sectors count from 0 @@ -141,5 +136,5 @@ std::shared_ptr D64::get_track_at_position(Track::Address address) { Encodings::CommodoreGCR::encode_block(§or_data[target_data_offset], end_of_data); } - return std::shared_ptr(new PCMTrack(std::move(track))); + return std::shared_ptr(new PCMTrack(PCMSegment(data))); } diff --git a/Storage/Disk/DiskImage/Formats/DMK.cpp b/Storage/Disk/DiskImage/Formats/DMK.cpp index 786b09fc0..fa272b9ec 100644 --- a/Storage/Disk/DiskImage/Formats/DMK.cpp +++ b/Storage/Disk/DiskImage/Formats/DMK.cpp @@ -179,10 +179,5 @@ std::shared_ptr<::Storage::Disk::Track> DMK::get_track_at_position(::Storage::Di idam_pointer++; } - // All segments should be exactly their number of bits in length. - for(auto &segment : segments) { - segment.number_of_bits = static_cast(segment.data.size() * 8); - } - return std::make_shared(segments); } diff --git a/Storage/Disk/DiskImage/Formats/G64.cpp b/Storage/Disk/DiskImage/Formats/G64.cpp index 6b083b5b1..69c90110c 100644 --- a/Storage/Disk/DiskImage/Formats/G64.cpp +++ b/Storage/Disk/DiskImage/Formats/G64.cpp @@ -87,11 +87,10 @@ std::shared_ptr G64::get_track_at_position(Track::Address address) { if(byte_speed != current_speed || byte == static_cast(track_length-1)) { unsigned int number_of_bytes = byte - start_byte_in_current_speed; - PCMSegment segment; - segment.number_of_bits = number_of_bytes * 8; - segment.length_of_a_bit = Encodings::CommodoreGCR::length_of_a_bit_in_time_zone(current_speed); - segment.data.resize(number_of_bytes); - std::memcpy(&segment.data[0], &track_contents[start_byte_in_current_speed], number_of_bytes); + PCMSegment segment( + Encodings::CommodoreGCR::length_of_a_bit_in_time_zone(current_speed), + number_of_bytes * 8, + &track_contents[start_byte_in_current_speed]); segments.push_back(std::move(segment)); current_speed = byte_speed; @@ -101,10 +100,11 @@ std::shared_ptr G64::get_track_at_position(Track::Address address) { resulting_track.reset(new PCMTrack(std::move(segments))); } else { - PCMSegment segment; - segment.number_of_bits = track_length * 8; - segment.length_of_a_bit = Encodings::CommodoreGCR::length_of_a_bit_in_time_zone(static_cast(speed_zone_offset)); - segment.data = std::move(track_contents); + PCMSegment segment( + Encodings::CommodoreGCR::length_of_a_bit_in_time_zone(static_cast(speed_zone_offset)), + track_length * 8, + track_contents + ); resulting_track.reset(new PCMTrack(std::move(segment))); } diff --git a/Storage/Disk/DiskImage/Formats/HFE.cpp b/Storage/Disk/DiskImage/Formats/HFE.cpp index 0d7b28f4c..eb5e148ed 100644 --- a/Storage/Disk/DiskImage/Formats/HFE.cpp +++ b/Storage/Disk/DiskImage/Formats/HFE.cpp @@ -26,9 +26,6 @@ HFE::HFE(const std::string &file_name) : track_list_offset_ = static_cast(file_.get16le()) << 9; } -HFE::~HFE() { -} - HeadPosition HFE::get_maximum_head_position() { return HeadPosition(track_count_); } @@ -49,13 +46,13 @@ uint16_t HFE::seek_track(Track::Address address) { // based on an assumption of two heads. file_.seek(track_list_offset_ + address.position.as_int() * 4, SEEK_SET); - long track_offset = static_cast(file_.get16le()) << 9; - uint16_t track_length = file_.get16le(); + long track_offset = static_cast(file_.get16le()) << 9; // Track offset, in units of 512 bytes. + uint16_t track_length = file_.get16le(); // Track length, in bytes, containing both the front and back track. file_.seek(track_offset, SEEK_SET); if(address.head) file_.seek(256, SEEK_CUR); - return track_length / 2; + return track_length / 2; // Divide by two to give the track length for a single side. } std::shared_ptr HFE::get_track_at_position(Track::Address address) { @@ -64,21 +61,42 @@ std::shared_ptr HFE::get_track_at_position(Track::Address address) { std::lock_guard lock_guard(file_.get_file_access_mutex()); uint16_t track_length = seek_track(address); - segment.data.resize(track_length); - segment.number_of_bits = track_length * 8; + segment.data.resize(track_length * 8); + // HFE tracks are stored as 256 bytes for side 1, then 256 bytes for side 2, + // then 256 bytes for side 1, then 256 bytes for side 2, etc, until the final + // 512-byte segment which will contain less than the full 256 bytes. + // + // seek_track will have advanced an extra initial 256 bytes if the address + // refers to side 2, so the loop below can act ass though it were definitely + // dealing with side 1. uint16_t c = 0; while(c < track_length) { + // Decide how many bytes of at most 256 to read, and read them. uint16_t length = static_cast(std::min(256, track_length - c)); - file_.read(&segment.data[c], length); + std::vector section = file_.read(length); + + // Push those into the PCMSegment. In HFE the least-significant bit is + // serialised first. + for(uint16_t byte = 0; byte < length; ++byte) { + const size_t base = (static_cast(c) << 3) + static_cast(byte); + segment.data[base + 0] = !!(section[byte] & 0x01); + segment.data[base + 1] = !!(section[byte] & 0x02); + segment.data[base + 2] = !!(section[byte] & 0x04); + segment.data[base + 3] = !!(section[byte] & 0x08); + segment.data[base + 4] = !!(section[byte] & 0x10); + segment.data[base + 5] = !!(section[byte] & 0x20); + segment.data[base + 6] = !!(section[byte] & 0x40); + segment.data[base + 7] = !!(section[byte] & 0x80); + } + + // Advance the target pointer, and skip the next 256 bytes of the file + // (which will be for the other side of the disk). c += length; file_.seek(256, SEEK_CUR); } } - // Flip bytes; HFE's preference is that the least-significant bit - // is serialised first, but PCMTrack posts the most-significant first. - Storage::Data::BitReverse::reverse(segment.data); return std::make_shared(segment); } @@ -88,9 +106,11 @@ void HFE::set_tracks(const std::map> &tra uint16_t track_length = seek_track(track.first); lock_guard.unlock(); - PCMSegment segment = Storage::Disk::track_serialisation(*track.second, Storage::Time(1, track_length * 8)); - Storage::Data::BitReverse::reverse(segment.data); - uint16_t data_length = std::min(static_cast(segment.data.size()), track_length); + const PCMSegment segment = Storage::Disk::track_serialisation(*track.second, Storage::Time(1, track_length * 8)); + + // Convert the segment into a byte encoding, LSB first. + std::vector byte_segment = segment.byte_data(false); + uint16_t data_length = std::min(static_cast(byte_segment.size()), track_length); lock_guard.lock(); seek_track(track.first); @@ -98,7 +118,7 @@ void HFE::set_tracks(const std::map> &tra uint16_t c = 0; while(c < data_length) { uint16_t length = static_cast(std::min(256, data_length - c)); - file_.write(&segment.data[c], length); + file_.write(&byte_segment[c], length); c += length; file_.seek(256, SEEK_CUR); } diff --git a/Storage/Disk/DiskImage/Formats/HFE.hpp b/Storage/Disk/DiskImage/Formats/HFE.hpp index ca8547155..0b35f0400 100644 --- a/Storage/Disk/DiskImage/Formats/HFE.hpp +++ b/Storage/Disk/DiskImage/Formats/HFE.hpp @@ -30,7 +30,6 @@ class HFE: public DiskImage { @throws Error::UnknownVersion if the file looks correct but is an unsupported version. */ HFE(const std::string &file_name); - ~HFE(); // implemented to satisfy @c Disk HeadPosition get_maximum_head_position() override; diff --git a/Storage/Disk/DiskImage/Formats/NIB.cpp b/Storage/Disk/DiskImage/Formats/NIB.cpp index 46b6917ea..269f1a8fa 100644 --- a/Storage/Disk/DiskImage/Formats/NIB.cpp +++ b/Storage/Disk/DiskImage/Formats/NIB.cpp @@ -69,8 +69,6 @@ std::shared_ptr<::Storage::Disk::Track> NIB::get_track_at_position(::Storage::Di // tracks and headers), then treat all following FFs as a sync // region, then switch back to ordinary behaviour as soon as a // non-FF appears. - PCMSegment segment; - std::size_t start_index = 0; std::set sync_starts; @@ -93,6 +91,7 @@ std::shared_ptr<::Storage::Disk::Track> NIB::get_track_at_position(::Storage::Di } } + PCMSegment segment; if(start_index) { segment += Encodings::AppleGCR::six_and_two_sync(static_cast(start_index)); } @@ -100,13 +99,10 @@ std::shared_ptr<::Storage::Disk::Track> NIB::get_track_at_position(::Storage::Di std::size_t index = start_index; for(const auto &location: sync_starts) { // Write from index to sync_start. - PCMSegment data_segment; - data_segment.data.insert( - data_segment.data.end(), + std::vector data_segment( track_data.begin() + static_cast(index), track_data.begin() + static_cast(location)); - data_segment.number_of_bits = static_cast(data_segment.data.size() * 8); - segment += data_segment; + segment += PCMSegment(data_segment); // Add a sync from sync_start to end of 0xffs. if(location == track_length-1) break; @@ -133,8 +129,8 @@ void NIB::set_tracks(const std::map> &tra std::vector track; track.reserve(track_length); uint8_t shifter = 0; - for(unsigned int bit = 0; bit < segment.number_of_bits; ++bit) { - shifter = static_cast((shifter << 1) | segment.bit(bit)); + for(const auto bit: segment.data) { + shifter = static_cast((shifter << 1) | (bit ? 1 : 0)); if(shifter & 0x80) { track.push_back(shifter); shifter = 0; diff --git a/Storage/Disk/DiskImage/Formats/OricMFMDSK.cpp b/Storage/Disk/DiskImage/Formats/OricMFMDSK.cpp index ee4774dae..ae043887a 100644 --- a/Storage/Disk/DiskImage/Formats/OricMFMDSK.cpp +++ b/Storage/Disk/DiskImage/Formats/OricMFMDSK.cpp @@ -108,7 +108,6 @@ std::shared_ptr OricMFMDSK::get_track_at_position(Track::Address address) } } - segment.number_of_bits = static_cast(segment.data.size() * 8); return std::make_shared(segment); } @@ -123,8 +122,8 @@ void OricMFMDSK::set_tracks(const std::map WOZ::get_track_at_position(Track::Address address) { if(offset == NoSuchTrack) return nullptr; // Seek to the real track. - PCMSegment track_contents; + std::vector track_contents; + size_t number_of_bits; { std::lock_guard lock_guard(file_.get_file_access_mutex()); file_.seek(offset, SEEK_SET); @@ -114,13 +115,12 @@ std::shared_ptr WOZ::get_track_at_position(Track::Address address) { // In WOZ a track is up to 6646 bytes of data, followed by a two-byte record of the // number of bytes that actually had data in them, then a two-byte count of the number // of bits that were used. Other information follows but is not intended for emulation. - track_contents.data = file_.read(6646); + track_contents = file_.read(6646); file_.seek(2, SEEK_CUR); - track_contents.number_of_bits = file_.get16le(); - track_contents.data.resize((track_contents.number_of_bits + 7) >> 3); + number_of_bits = std::min(file_.get16le(), static_cast(6646*8)); } - return std::shared_ptr(new PCMTrack(track_contents)); + return std::shared_ptr(new PCMTrack(PCMSegment(number_of_bits, track_contents))); } void WOZ::set_tracks(const std::map> &tracks) { @@ -129,13 +129,14 @@ void WOZ::set_tracks(const std::map> &tra auto segment = Storage::Disk::track_serialisation(*pair.second, Storage::Time(1, 50000)); auto offset = static_cast(file_offset(pair.first) - 12); - memcpy(&post_crc_contents_[offset - 12], segment.data.data(), segment.number_of_bits >> 3); + std::vector segment_bytes = segment.byte_data(); + memcpy(&post_crc_contents_[offset - 12], segment_bytes.data(), segment_bytes.size()); // Write number of bytes and number of bits. - post_crc_contents_[offset + 6646] = static_cast(segment.number_of_bits >> 3); - post_crc_contents_[offset + 6647] = static_cast(segment.number_of_bits >> 11); - post_crc_contents_[offset + 6648] = static_cast(segment.number_of_bits); - post_crc_contents_[offset + 6649] = static_cast(segment.number_of_bits >> 8); + post_crc_contents_[offset + 6646] = static_cast(segment.data.size() >> 3); + post_crc_contents_[offset + 6647] = static_cast(segment.data.size() >> 11); + post_crc_contents_[offset + 6648] = static_cast(segment.data.size()); + post_crc_contents_[offset + 6649] = static_cast(segment.data.size() >> 8); // Set no splice information now provided, since it's been lost if ever it was known. post_crc_contents_[offset + 6650] = 0xff; diff --git a/Storage/Disk/Drive.cpp b/Storage/Disk/Drive.cpp index aef01b99e..994f20146 100644 --- a/Storage/Disk/Drive.cpp +++ b/Storage/Disk/Drive.cpp @@ -300,17 +300,12 @@ void Drive::begin_writing(Time bit_length, bool clamp_to_index_hole) { write_segment_.length_of_a_bit = bit_length / rotational_multiplier_; write_segment_.data.clear(); - write_segment_.number_of_bits = 0; write_start_time_ = get_time_into_track(); } void Drive::write_bit(bool value) { - bool needs_new_byte = !(write_segment_.number_of_bits&7); - if(needs_new_byte) write_segment_.data.push_back(0); - if(value) write_segment_.data[write_segment_.number_of_bits >> 3] |= 0x80 >> (write_segment_.number_of_bits & 7); - write_segment_.number_of_bits++; - + write_segment_.data.push_back(value); cycles_until_bits_written_ += cycles_per_bit_; } diff --git a/Storage/Disk/Encodings/AppleGCR/Encoder.cpp b/Storage/Disk/Encodings/AppleGCR/Encoder.cpp index 11d2da80e..7f3f5fb62 100644 --- a/Storage/Disk/Encodings/AppleGCR/Encoder.cpp +++ b/Storage/Disk/Encodings/AppleGCR/Encoder.cpp @@ -35,15 +35,18 @@ const uint8_t six_and_two_mapping[] = { Storage::Disk::PCMSegment sync(int length, int bit_size) { Storage::Disk::PCMSegment segment; - // Allocate sufficient storage. - segment.data.resize(static_cast(((length * bit_size) + 7) >> 3), 0); + // Reserve sufficient storage. + segment.data.reserve(static_cast(length * bit_size)); + // Write patters of 0xff padded with 0s to the selected bit size. while(length--) { - segment.data[segment.number_of_bits >> 3] |= 0xff >> (segment.number_of_bits & 7); - if(segment.number_of_bits & 7) { - segment.data[1 + (segment.number_of_bits >> 3)] |= 0xff << (8 - (segment.number_of_bits & 7)); - } - segment.number_of_bits += static_cast(bit_size); + int c = 8; + while(c--) + segment.data.push_back(true); + + c = bit_size - 8; + while(c--) + segment.data.push_back(false); } return segment; @@ -65,17 +68,15 @@ Storage::Disk::PCMSegment AppleGCR::header(uint8_t volume, uint8_t track, uint8_ const uint8_t checksum = volume ^ track ^ sector; // Apple headers are encoded using an FM-esque scheme rather than 6 and 2, or 5 and 3. - Storage::Disk::PCMSegment segment; - segment.data.resize(14); - segment.number_of_bits = 14*8; + std::vector data(14); - segment.data[0] = header_prologue[0]; - segment.data[1] = header_prologue[1]; - segment.data[2] = header_prologue[2]; + data[0] = header_prologue[0]; + data[1] = header_prologue[1]; + data[2] = header_prologue[2]; #define WriteFM(index, value) \ - segment.data[index+0] = static_cast(((value) >> 1) | 0xaa); \ - segment.data[index+1] = static_cast((value) | 0xaa); \ + data[index+0] = static_cast(((value) >> 1) | 0xaa); \ + data[index+1] = static_cast((value) | 0xaa); \ WriteFM(3, volume); WriteFM(5, track); @@ -84,24 +85,23 @@ Storage::Disk::PCMSegment AppleGCR::header(uint8_t volume, uint8_t track, uint8_ #undef WriteFM - segment.data[11] = epilogue[0]; - segment.data[12] = epilogue[1]; - segment.data[13] = epilogue[2]; + data[11] = epilogue[0]; + data[12] = epilogue[1]; + data[13] = epilogue[2]; - return segment; + return Storage::Disk::PCMSegment(data); } Storage::Disk::PCMSegment AppleGCR::five_and_three_data(const uint8_t *source) { - Storage::Disk::PCMSegment segment; + std::vector data(410 + 7); - segment.data.resize(410 + 7); - segment.data[0] = data_prologue[0]; - segment.data[1] = data_prologue[1]; - segment.data[2] = data_prologue[2]; + data[0] = data_prologue[0]; + data[1] = data_prologue[1]; + data[2] = data_prologue[2]; - segment.data[414] = epilogue[0]; - segment.data[411] = epilogue[1]; - segment.data[416] = epilogue[2]; + data[414] = epilogue[0]; + data[411] = epilogue[1]; + data[416] = epilogue[2]; // std::size_t source_pointer = 0; // std::size_t destination_pointer = 3; @@ -114,26 +114,23 @@ Storage::Disk::PCMSegment AppleGCR::five_and_three_data(const uint8_t *source) { // Map five-bit values up to full bytes. for(std::size_t c = 0; c < 410; ++c) { - segment.data[3 + c] = five_and_three_mapping[segment.data[3 + c]]; + data[3 + c] = five_and_three_mapping[data[3 + c]]; } - return segment; + return Storage::Disk::PCMSegment(data); } Storage::Disk::PCMSegment AppleGCR::six_and_two_data(const uint8_t *source) { - Storage::Disk::PCMSegment segment; - - segment.data.resize(349); - segment.number_of_bits = static_cast(segment.data.size() * 8); + std::vector data(349); // Add the prologue and epilogue. - segment.data[0] = data_prologue[0]; - segment.data[1] = data_prologue[1]; - segment.data[2] = data_prologue[2]; + data[0] = data_prologue[0]; + data[1] = data_prologue[1]; + data[2] = data_prologue[2]; - segment.data[346] = epilogue[0]; - segment.data[347] = epilogue[1]; - segment.data[348] = epilogue[2]; + data[346] = epilogue[0]; + data[347] = epilogue[1]; + data[348] = epilogue[2]; // Fill in byte values: the first 86 bytes contain shuffled // and combined copies of the bottom two bits of the sector @@ -141,40 +138,40 @@ Storage::Disk::PCMSegment AppleGCR::six_and_two_data(const uint8_t *source) { // six bits. const uint8_t bit_reverse[] = {0, 2, 1, 3}; for(std::size_t c = 0; c < 84; ++c) { - segment.data[3 + c] = + data[3 + c] = static_cast( bit_reverse[source[c]&3] | (bit_reverse[source[c + 86]&3] << 2) | (bit_reverse[source[c + 172]&3] << 4) ); } - segment.data[87] = + data[87] = static_cast( (bit_reverse[source[84]&3] << 0) | (bit_reverse[source[170]&3] << 2) ); - segment.data[88] = + data[88] = static_cast( (bit_reverse[source[85]&3] << 0) | (bit_reverse[source[171]&3] << 2) ); for(std::size_t c = 0; c < 256; ++c) { - segment.data[3 + 86 + c] = source[c] >> 2; + data[3 + 86 + c] = source[c] >> 2; } // Exclusive OR each byte with the one before it. - segment.data[345] = segment.data[344]; + data[345] = data[344]; std::size_t location = 344; while(location > 3) { - segment.data[location] ^= segment.data[location-1]; + data[location] ^= data[location-1]; --location; } // Map six-bit values up to full bytes. for(std::size_t c = 0; c < 343; ++c) { - segment.data[3 + c] = six_and_two_mapping[segment.data[3 + c]]; + data[3 + c] = six_and_two_mapping[data[3 + c]]; } - return segment; + return Storage::Disk::PCMSegment(data); } diff --git a/Storage/Disk/Encodings/AppleGCR/SegmentParser.cpp b/Storage/Disk/Encodings/AppleGCR/SegmentParser.cpp index 239da51ac..ffd9d515e 100644 --- a/Storage/Disk/Encodings/AppleGCR/SegmentParser.cpp +++ b/Storage/Disk/Encodings/AppleGCR/SegmentParser.cpp @@ -49,9 +49,9 @@ std::map Storage::Encodings::AppleGCR::sectors_from_segment // Scan the track 1 and 1/8th times; that's long enough to make sure that any sector which straddles the // end of the track is caught. Since they're put into a map, it doesn't matter if they're caught twice. - unsigned int extended_length = segment.number_of_bits + (segment.number_of_bits >> 3); - for(unsigned int bit = 0; bit < extended_length; ++bit) { - shift_register = static_cast((shift_register << 1) | segment.bit(bit % segment.number_of_bits)); + const size_t extended_length = segment.data.size() + (segment.data.size() >> 3); + for(size_t bit = 0; bit < extended_length; ++bit) { + shift_register = static_cast((shift_register << 1) | (segment.data[bit % segment.data.size()] ? 1 : 0)); // Apple GCR parsing: bytes always have the top bit set. if(!(shift_register&0x80)) continue; @@ -80,7 +80,7 @@ std::map Storage::Encodings::AppleGCR::sectors_from_segment new_sector.reset(new Sector); new_sector->data.reserve(412); } else { - sector_location = static_cast(bit % segment.number_of_bits); + sector_location = static_cast(bit % segment.data.size()); } } } else { diff --git a/Storage/Disk/Encodings/MFM/Encoder.cpp b/Storage/Disk/Encodings/MFM/Encoder.cpp index 84df0f7f3..115d28f4e 100644 --- a/Storage/Disk/Encodings/MFM/Encoder.cpp +++ b/Storage/Disk/Encodings/MFM/Encoder.cpp @@ -18,7 +18,7 @@ using namespace Storage::Encodings::MFM; class MFMEncoder: public Encoder { public: - MFMEncoder(std::vector &target) : Encoder(target) {} + MFMEncoder(std::vector &target) : Encoder(target) {} void add_byte(uint8_t input) { crc_generator_.add(input); @@ -74,7 +74,7 @@ class MFMEncoder: public Encoder { class FMEncoder: public Encoder { // encodes each 16-bit part as clock, data, clock, data [...] public: - FMEncoder(std::vector &target) : Encoder(target) {} + FMEncoder(std::vector &target) : Encoder(target) {} void add_byte(uint8_t input) { crc_generator_.add(input); @@ -182,16 +182,17 @@ template std::shared_ptr std::size_t max_size = expected_track_bytes + (expected_track_bytes / 10); if(segment.data.size() > max_size) segment.data.resize(max_size); - segment.number_of_bits = static_cast(segment.data.size() * 8); return std::shared_ptr(new Storage::Disk::PCMTrack(std::move(segment))); } -Encoder::Encoder(std::vector &target) : +Encoder::Encoder(std::vector &target) : target_(target) {} void Encoder::output_short(uint16_t value) { - target_.push_back(value >> 8); - target_.push_back(value & 0xff); + uint16_t mask = 0x8000; + while(mask) { + target_.push_back(!!(value & mask)); + } } void Encoder::add_crc(bool incorrectly) { @@ -254,10 +255,10 @@ std::shared_ptr Storage::Encodings::MFM::GetMFMTrackWithSe 12500); // unintelligently: double the single-density bytes/rotation (or: 500kbps @ 300 rpm) } -std::unique_ptr Storage::Encodings::MFM::GetMFMEncoder(std::vector &target) { +std::unique_ptr Storage::Encodings::MFM::GetMFMEncoder(std::vector &target) { return std::unique_ptr(new MFMEncoder(target)); } -std::unique_ptr Storage::Encodings::MFM::GetFMEncoder(std::vector &target) { +std::unique_ptr Storage::Encodings::MFM::GetFMEncoder(std::vector &target) { return std::unique_ptr(new FMEncoder(target)); } diff --git a/Storage/Disk/Encodings/MFM/Encoder.hpp b/Storage/Disk/Encodings/MFM/Encoder.hpp index e571e6b53..643cbc610 100644 --- a/Storage/Disk/Encodings/MFM/Encoder.hpp +++ b/Storage/Disk/Encodings/MFM/Encoder.hpp @@ -44,7 +44,7 @@ std::shared_ptr GetFMTrackWithSectors(const std::vector &target); + Encoder(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; @@ -59,11 +59,11 @@ class Encoder { CRC::CCITT crc_generator_; private: - std::vector &target_; + std::vector &target_; }; -std::unique_ptr GetMFMEncoder(std::vector &target); -std::unique_ptr GetFMEncoder(std::vector &target); +std::unique_ptr GetMFMEncoder(std::vector &target); +std::unique_ptr GetFMEncoder(std::vector &target); } } diff --git a/Storage/Disk/Encodings/MFM/SegmentParser.cpp b/Storage/Disk/Encodings/MFM/SegmentParser.cpp index fb97738ac..5f4e494b2 100644 --- a/Storage/Disk/Encodings/MFM/SegmentParser.cpp +++ b/Storage/Disk/Encodings/MFM/SegmentParser.cpp @@ -23,8 +23,8 @@ std::map Storage::Encodings::MFM:: std::size_t size = 0; std::size_t start_location = 0; - for(unsigned int bit = 0; bit < segment.number_of_bits; ++bit) { - shifter.add_input_bit(segment.bit(bit)); + for(const auto bit: segment.data) { + shifter.add_input_bit(bit ? 1 : 0); switch(shifter.get_token()) { case Shifter::Token::None: case Shifter::Token::Sync: diff --git a/Storage/Disk/Track/PCMSegment.cpp b/Storage/Disk/Track/PCMSegment.cpp index b4718dd42..3af3d966d 100644 --- a/Storage/Disk/Track/PCMSegment.cpp +++ b/Storage/Disk/Track/PCMSegment.cpp @@ -46,35 +46,10 @@ void PCMSegmentEventSource::reset() { } PCMSegment &PCMSegment::operator +=(const PCMSegment &rhs) { - if(!rhs.number_of_bits) return *this; - - assert(((rhs.number_of_bits+7) >> 3) == rhs.data.size()); - - if(number_of_bits&7) { - auto target_number_of_bits = number_of_bits + rhs.number_of_bits; - data.resize((target_number_of_bits + 7) >> 3); - - std::size_t first_byte = number_of_bits >> 3; - - const int shift = number_of_bits&7; - for(std::size_t source = 0; source < rhs.data.size(); ++source) { - data[first_byte + source] |= rhs.data[source] >> shift; - if(source*8 + static_cast(8 - shift) < rhs.number_of_bits) - data[first_byte + source + 1] = static_cast(rhs.data[source] << (8-shift)); - } - - number_of_bits = target_number_of_bits; - } else { - data.insert(data.end(), rhs.data.begin(), rhs.data.end()); - number_of_bits += rhs.number_of_bits; - } - - assert(((number_of_bits+7) >> 3) == data.size()); - + data.insert(data.end(), rhs.data.begin(), rhs.data.end()); return *this; } - Storage::Disk::Track::Event PCMSegmentEventSource::get_next_event() { // track the initial bit pointer for potentially considering whether this was an // initial index hole or a subsequent one later on @@ -85,9 +60,8 @@ Storage::Disk::Track::Event PCMSegmentEventSource::get_next_event() { next_event_.length.length = bit_pointer_ ? 0 : -(segment_->length_of_a_bit.length >> 1); // search for the next bit that is set, if any - const uint8_t *segment_data = segment_->data.data(); - while(bit_pointer_ < segment_->number_of_bits) { - int bit = segment_data[bit_pointer_ >> 3] & (0x80 >> (bit_pointer_&7)); + while(bit_pointer_ < segment_->data.size()) { + bool bit = segment_->data[bit_pointer_]; bit_pointer_++; // so this always points one beyond the most recent bit returned next_event_.length.length += segment_->length_of_a_bit.length; @@ -102,7 +76,7 @@ Storage::Disk::Track::Event PCMSegmentEventSource::get_next_event() { // allow an extra half bit's length to run from the position of the potential final transition // event to the end of the segment. Otherwise don't allow any extra time, as it's already // been consumed - if(initial_bit_pointer <= segment_->number_of_bits) { + if(initial_bit_pointer <= segment_->data.size()) { next_event_.length.length += (segment_->length_of_a_bit.length >> 1); bit_pointer_++; } @@ -110,7 +84,7 @@ Storage::Disk::Track::Event PCMSegmentEventSource::get_next_event() { } Storage::Time PCMSegmentEventSource::get_length() { - return segment_->length_of_a_bit * segment_->number_of_bits; + return segment_->length_of_a_bit * static_cast(segment_->data.size()); } Storage::Time PCMSegmentEventSource::seek_to(const Time &time_from_start) { @@ -118,7 +92,7 @@ Storage::Time PCMSegmentEventSource::seek_to(const Time &time_from_start) { Time length = get_length(); if(time_from_start >= length) { next_event_.type = Track::Event::IndexHole; - bit_pointer_ = segment_->number_of_bits+1; + bit_pointer_ = segment_->data.size()+1; return length; } @@ -143,3 +117,7 @@ Storage::Time PCMSegmentEventSource::seek_to(const Time &time_from_start) { // map up to the correct amount of time return half_bit_length + segment_->length_of_a_bit * static_cast(bit_pointer_ - 1); } + +const PCMSegment &PCMSegmentEventSource::segment() const { + return *segment_; +} diff --git a/Storage/Disk/Track/PCMSegment.hpp b/Storage/Disk/Track/PCMSegment.hpp index b686c36f6..de90c4f7d 100644 --- a/Storage/Disk/Track/PCMSegment.hpp +++ b/Storage/Disk/Track/PCMSegment.hpp @@ -21,28 +21,117 @@ namespace Disk { /*! A segment of PCM-sampled data. - - Bits from each byte are taken MSB to LSB. */ struct PCMSegment { + /*! + Determines the amount of space that each bit of data occupies; + allows PCMSegments of different densities. + */ Time length_of_a_bit = Time(1); - unsigned int number_of_bits = 0; - std::vector data; - PCMSegment(Time length_of_a_bit, unsigned int number_of_bits, std::vector data) - : length_of_a_bit(length_of_a_bit), number_of_bits(number_of_bits), data(data) {} - PCMSegment() {} + /*! + This is the actual data, taking advantage of the std::vector + specialisation to use whatever one-bit-per-value encoding is + most suited to this architecture. - int bit(std::size_t index) const { - return (data[index >> 3] >> (7 ^ (index & 7)))&1; + If a value is @c true then a flux transition occurs in that window. + If it is @c false then no flux transition occurs. + */ + std::vector data; + + /*! + Constructs an instance of PCMSegment with the specified @c length_of_a_bit + and @c data. + */ + PCMSegment(Time length_of_a_bit, const std::vector &data) + : length_of_a_bit(length_of_a_bit), data(data) {} + + /*! + Constructs an instance of PCMSegment where each bit window is 1 unit of time + long and @c data is populated from the supplied @c source by serialising it + from MSB to LSB for @c number_of_bits. + */ + PCMSegment(size_t number_of_bits, const uint8_t *source) + : data(number_of_bits, false) { + for(size_t c = 0; c < number_of_bits; ++c) { + if((source[c >> 3] >> (7 ^ (c & 7)))&1) { + data[c] = true; + } + } } + /*! + Constructs an instance of PCMSegment where each bit window is the length + specified by @c length_of_a_bit, and @c data is populated from the supplied + @c source by serialising it from MSB to LSB for @c number_of_bits. + */ + PCMSegment(Time length_of_a_bit, size_t number_of_bits, const uint8_t *source) + : PCMSegment(number_of_bits, source) { + this->length_of_a_bit = length_of_a_bit; + } + + /*! + Constructs an instance of PCMSegment where each bit window is the length + specified by @c length_of_a_bit, and @c data is populated from the supplied + @c source by serialising it from MSB to LSB for @c number_of_bits. + */ + PCMSegment(Time length_of_a_bit, size_t number_of_bits, const std::vector &source) : + PCMSegment(length_of_a_bit, number_of_bits, source.data()) {} + + /*! + Constructs an instance of PCMSegment where each bit window is 1 unit of time + long and @c data is populated from the supplied @c source by serialising it + from MSB to LSB for @c number_of_bits. + */ + PCMSegment(size_t number_of_bits, const std::vector &source) : + PCMSegment(number_of_bits, source.data()) {} + + /*! + Constructs an instance of PCMSegment where each bit window is 1 unit of time + long and @c data is populated from the supplied @c source by serialising it + from MSB to LSB, assuming every bit provided is used. + */ + PCMSegment(const std::vector &source) : + PCMSegment(source.size() * 8, source.data()) {} + + /*! + Constructs an instance of PCMSegment where each bit window is 1 unit of time + long and @c data is empty. + */ + PCMSegment() {} + + /// Empties the PCMSegment. void clear() { - number_of_bits = 0; data.clear(); } + /*! + Produces a byte buffer where the contents of @c data are serialised into bytes + + If @c msb_first is @c true then each byte is expected to be deserialised from + MSB to LSB. + + If @c msb_first is @c false then each byte is expected to be deserialised from + LSB to MSB. + */ + std::vector byte_data(bool msb_first = true) const { + std::vector bytes((data.size() + 7) >> 3); + size_t pointer = 0; + const size_t pointer_mask = msb_first ? 7 : 0; + for(const auto bit: data) { + if(bit) bytes[pointer >> 3] |= 1 << ((pointer & 7) ^ pointer_mask); + ++pointer; + } + return bytes; + } + + /// Appends the data of @c rhs to the current data. Does not adjust @c length_of_a_bit. PCMSegment &operator +=(const PCMSegment &rhs); + + /// @returns the total amount of time occupied by all the data stored in this segment. + Time length() const { + return length_of_a_bit * static_cast(data.size()); + } }; /*! @@ -86,6 +175,11 @@ class PCMSegmentEventSource { */ Time get_length(); + /*! + @returns a reference to the underlying segment. + */ + const PCMSegment &segment() const; + private: std::shared_ptr segment_; std::size_t bit_pointer_; diff --git a/Storage/Disk/Track/PCMTrack.cpp b/Storage/Disk/Track/PCMTrack.cpp index 069c54e4a..ec54c3b86 100644 --- a/Storage/Disk/Track/PCMTrack.cpp +++ b/Storage/Disk/Track/PCMTrack.cpp @@ -17,18 +17,18 @@ PCMTrack::PCMTrack(const std::vector &segments) : PCMTrack() { // sum total length of all segments Time total_length; for(const auto &segment : segments) { - total_length += segment.length_of_a_bit * segment.number_of_bits; + total_length += segment.length_of_a_bit * static_cast(segment.data.size()); } total_length.simplify(); // each segment is then some proportion of the total; for them all to sum to 1 they'll // need to be adjusted to be for(const auto &segment : segments) { - Time original_length_of_segment = segment.length_of_a_bit * segment.number_of_bits; + Time original_length_of_segment = segment.length_of_a_bit * static_cast(segment.data.size()); Time proportion_of_whole = original_length_of_segment / total_length; proportion_of_whole.simplify(); PCMSegment length_adjusted_segment = segment; - length_adjusted_segment.length_of_a_bit = proportion_of_whole / segment.number_of_bits; + length_adjusted_segment.length_of_a_bit = proportion_of_whole / static_cast(segment.data.size()); length_adjusted_segment.length_of_a_bit.simplify(); segment_event_sources_.emplace_back(length_adjusted_segment); } @@ -38,7 +38,7 @@ PCMTrack::PCMTrack(const PCMSegment &segment) : PCMTrack() { // a single segment necessarily fills the track PCMSegment length_adjusted_segment = segment; length_adjusted_segment.length_of_a_bit.length = 1; - length_adjusted_segment.length_of_a_bit.clock_rate = segment.number_of_bits; + length_adjusted_segment.length_of_a_bit.clock_rate = static_cast(segment.data.size()); segment_event_sources_.emplace_back(std::move(length_adjusted_segment)); } @@ -59,10 +59,14 @@ Track *PCMTrack::clone() const { } Track *PCMTrack::resampled_clone(size_t bits_per_track) { - PCMTrack *new_track = new PCMTrack(bits_per_track); + PCMTrack *new_track = new PCMTrack(static_cast(bits_per_track)); -// for(const auto &event_source : segment_event_sources_) { -// } + Time start_time; + for(const auto &event_source : segment_event_sources_) { + const PCMSegment &source = event_source.segment(); + new_track->add_segment(start_time, source, true); + start_time += source.length(); + } return new_track; } @@ -126,3 +130,12 @@ Storage::Time PCMTrack::seek_to(const Time &time_since_index_hole) { return accumulated_time; } +void PCMTrack::add_segment(const Time &start_time, const PCMSegment &segment, bool clamp_to_index_hole) { + // Write half a bit of silence to lead up to the first possible flux point. + + // Write out the bits contained in this segment. + + // Write half a bit of silence to end the segment. + + unsigned int position = start_time.length; +} diff --git a/Storage/Disk/Track/TrackSerialiser.cpp b/Storage/Disk/Track/TrackSerialiser.cpp index b575816a9..3fff2f82b 100644 --- a/Storage/Disk/Track/TrackSerialiser.cpp +++ b/Storage/Disk/Track/TrackSerialiser.cpp @@ -20,12 +20,9 @@ Storage::Disk::PCMSegment Storage::Disk::track_serialisation(const Track &track, struct ResultAccumulator: public DigitalPhaseLockedLoop::Delegate { PCMSegment result; void digital_phase_locked_loop_output_bit(int value) { - result.data.resize(1 + (result.number_of_bits >> 3)); - if(value) result.data[result.number_of_bits >> 3] |= 0x80 >> (result.number_of_bits & 7); - result.number_of_bits++; + result.data.push_back(!!value); } } result_accumulator; - result_accumulator.result.number_of_bits = 0; result_accumulator.result.length_of_a_bit = length_of_a_bit; Time length_multiplier = Time(100*length_of_a_bit.clock_rate, length_of_a_bit.length);