mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-13 22:32:03 +00:00
Merge pull request #734 from TomHarte/FuzzyBits
Adds PCMSegementEventSource support for 'fuzzy' bits
This commit is contained in:
commit
8f94da9daf
@ -45,20 +45,20 @@ class WD1770: public Storage::Disk::MFMController {
|
||||
void run_for(const Cycles cycles);
|
||||
|
||||
enum Flag: uint8_t {
|
||||
NotReady = 0x80,
|
||||
NotReady = 0x80, // 0x80
|
||||
MotorOn = 0x80,
|
||||
WriteProtect = 0x40,
|
||||
RecordType = 0x20,
|
||||
WriteProtect = 0x40, // 0x40
|
||||
RecordType = 0x20, // 0x20
|
||||
SpinUp = 0x20,
|
||||
HeadLoaded = 0x20,
|
||||
RecordNotFound = 0x10,
|
||||
RecordNotFound = 0x10, // 0x10
|
||||
SeekError = 0x10,
|
||||
CRCError = 0x08,
|
||||
LostData = 0x04,
|
||||
CRCError = 0x08, // 0x08
|
||||
LostData = 0x04, // 0x04
|
||||
TrackZero = 0x04,
|
||||
DataRequest = 0x02,
|
||||
DataRequest = 0x02, // 0x02
|
||||
Index = 0x02,
|
||||
Busy = 0x01
|
||||
Busy = 0x01 // 0x01
|
||||
};
|
||||
|
||||
/// @returns The current value of the IRQ line output.
|
||||
|
64
NumberTheory/LFSR.hpp
Normal file
64
NumberTheory/LFSR.hpp
Normal file
@ -0,0 +1,64 @@
|
||||
//
|
||||
// LFSR.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 19/01/2020.
|
||||
// Copyright © 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef LFSR_h
|
||||
#define LFSR_h
|
||||
|
||||
template <typename IntType> struct LSFRPolynomial {
|
||||
};
|
||||
|
||||
// The following were taken 'at random' from https://users.ece.cmu.edu/~koopman/lfsr/index.html
|
||||
template <> struct LSFRPolynomial<uint64_t> {
|
||||
static constexpr uint64_t value = 0x80000000000019E2;
|
||||
};
|
||||
|
||||
template <> struct LSFRPolynomial<uint32_t> {
|
||||
static constexpr uint32_t value = 0x80000C34;
|
||||
};
|
||||
|
||||
template <> struct LSFRPolynomial<uint16_t> {
|
||||
static constexpr uint16_t value = 0x853E;
|
||||
};
|
||||
|
||||
template <> struct LSFRPolynomial<uint8_t> {
|
||||
static constexpr uint8_t value = 0xAF;
|
||||
};
|
||||
|
||||
/*!
|
||||
Provides a linear-feedback shift register with a random initial state; if no polynomial is supplied
|
||||
then one will be picked that is guaranteed to give the maximal number of LFSR states that can fit
|
||||
in the specified int type.
|
||||
*/
|
||||
template <typename IntType = uint64_t, IntType polynomial = LSFRPolynomial<IntType>::value> class LFSR {
|
||||
public:
|
||||
LFSR() {
|
||||
// Randomise the value, ensuring it doesn't end up being 0.
|
||||
while(!value_) {
|
||||
uint8_t *value_byte = reinterpret_cast<uint8_t *>(&value_);
|
||||
for(size_t c = 0; c < sizeof(IntType); ++c) {
|
||||
*value_byte = uint8_t(uint64_t(rand()) * 255 / RAND_MAX);
|
||||
++value_byte;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Advances the LSFR, returning either an @c IntType of value @c 1 or @c 0,
|
||||
determining the bit that was just shifted out.
|
||||
*/
|
||||
IntType next() {
|
||||
const auto result = value_ & 1;
|
||||
value_ = (value_ >> 1) ^ (result * polynomial);
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
IntType value_ = 0;
|
||||
};
|
||||
|
||||
#endif /* LFSR_h */
|
@ -1185,6 +1185,7 @@
|
||||
4B7BA03523CEB86000B98D9E /* BD500.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = BD500.cpp; path = Oric/BD500.cpp; sourceTree = "<group>"; };
|
||||
4B7BA03623CEB86000B98D9E /* BD500.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = BD500.hpp; path = Oric/BD500.hpp; sourceTree = "<group>"; };
|
||||
4B7BA03823CEB8D200B98D9E /* DiskController.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = DiskController.hpp; path = Oric/DiskController.hpp; sourceTree = "<group>"; };
|
||||
4B7BA03923D5302C00B98D9E /* LFSR.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = LFSR.hpp; path = ../../NumberTheory/LFSR.hpp; sourceTree = "<group>"; };
|
||||
4B7F188C2154825D00388727 /* MasterSystem.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MasterSystem.cpp; sourceTree = "<group>"; };
|
||||
4B7F188D2154825D00388727 /* MasterSystem.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MasterSystem.hpp; sourceTree = "<group>"; };
|
||||
4B7F1895215486A100388727 /* StaticAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaticAnalyser.hpp; sourceTree = "<group>"; };
|
||||
@ -3186,6 +3187,7 @@
|
||||
children = (
|
||||
4BB697C61D4B558F00248BDF /* Factors.hpp */,
|
||||
4BF8295F1D8F3C87001BAE39 /* CRC.hpp */,
|
||||
4B7BA03923D5302C00B98D9E /* LFSR.hpp */,
|
||||
);
|
||||
name = NumberTheory;
|
||||
sourceTree = "<group>";
|
||||
|
@ -158,7 +158,7 @@ CPCDSK::CPCDSK(const std::string &file_name) :
|
||||
}
|
||||
|
||||
// As per the weak/fuzzy sector extension, multiple samplings may be stored here.
|
||||
// Plan to tead as many as there were.
|
||||
// Plan to read as many as there were.
|
||||
sector.samples.emplace_back();
|
||||
sector.samples.resize(number_of_samplings);
|
||||
while(number_of_samplings--) {
|
||||
|
@ -259,12 +259,12 @@ class TrackConstructor {
|
||||
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);
|
||||
segments.back().length_of_a_bit = Storage::Time(int(rate + 1), 1);
|
||||
encoder = Storage::Encodings::MFM::GetMFMEncoder(segments.back().data, &segments.back().fuzzy_mask);
|
||||
} 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);
|
||||
segments.back().length_of_a_bit = Storage::Time(int(rate + 1), 1);
|
||||
encoder->reset_target(segments.back().data, &segments.back().fuzzy_mask);
|
||||
}
|
||||
return encoder.get();
|
||||
};
|
||||
@ -276,7 +276,7 @@ class TrackConstructor {
|
||||
// assert(location->position >= track_position && location->position < track_data_.end());
|
||||
|
||||
// Advance to location.position.
|
||||
auto default_rate_encoder = encoder_at_rate(128);
|
||||
auto default_rate_encoder = encoder_at_rate(127);
|
||||
while(track_position < location->position) {
|
||||
default_rate_encoder->add_byte(*track_position);
|
||||
++track_position;
|
||||
@ -323,17 +323,23 @@ class TrackConstructor {
|
||||
// (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]);
|
||||
encoder_at_rate(location->sector.timing[c >> 4])->add_byte(
|
||||
location->sector.contents[c],
|
||||
location->sector.fuzzy_mask.empty() ? 0x00 : location->sector.fuzzy_mask[c]
|
||||
);
|
||||
}
|
||||
} else {
|
||||
for(size_t c = 0; c < body_bytes; ++c) {
|
||||
default_rate_encoder->add_byte(location->sector.contents[c]);
|
||||
default_rate_encoder->add_byte(
|
||||
location->sector.contents[c],
|
||||
location->sector.fuzzy_mask.empty() ? 0x00 : location->sector.fuzzy_mask[c]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Add a CRC only if it fits (TODO: crop if necessary?).
|
||||
if(bytes_to_write & 127) {
|
||||
default_rate_encoder = encoder_at_rate(128);
|
||||
default_rate_encoder = encoder_at_rate(127);
|
||||
default_rate_encoder->add_crc((location->sector.status & 0x18) == 0x10);
|
||||
}
|
||||
} break;
|
||||
|
@ -24,12 +24,12 @@ enum class SurfaceItem {
|
||||
|
||||
class MFMEncoder: public Encoder {
|
||||
public:
|
||||
MFMEncoder(std::vector<bool> &target) : Encoder(target) {}
|
||||
MFMEncoder(std::vector<bool> &target, std::vector<bool> *fuzzy_target = nullptr) : Encoder(target, fuzzy_target) {}
|
||||
virtual ~MFMEncoder() {}
|
||||
|
||||
void add_byte(uint8_t input) {
|
||||
void add_byte(uint8_t input, uint8_t fuzzy_mask = 0) final {
|
||||
crc_generator_.add(input);
|
||||
uint16_t spread_value =
|
||||
const uint16_t spread_value =
|
||||
static_cast<uint16_t>(
|
||||
((input & 0x01) << 0) |
|
||||
((input & 0x02) << 1) |
|
||||
@ -40,27 +40,40 @@ class MFMEncoder: public Encoder {
|
||||
((input & 0x40) << 6) |
|
||||
((input & 0x80) << 7)
|
||||
);
|
||||
uint16_t or_bits = static_cast<uint16_t>((spread_value << 1) | (spread_value >> 1) | (last_output_ << 15));
|
||||
uint16_t output = spread_value | ((~or_bits) & 0xaaaa);
|
||||
output_short(output);
|
||||
const uint16_t or_bits = static_cast<uint16_t>((spread_value << 1) | (spread_value >> 1) | (last_output_ << 15));
|
||||
const uint16_t output = spread_value | ((~or_bits) & 0xaaaa);
|
||||
|
||||
const uint16_t spread_mask =
|
||||
static_cast<uint16_t>(
|
||||
((fuzzy_mask & 0x01) << 0) |
|
||||
((fuzzy_mask & 0x02) << 1) |
|
||||
((fuzzy_mask & 0x04) << 2) |
|
||||
((fuzzy_mask & 0x08) << 3) |
|
||||
((fuzzy_mask & 0x10) << 4) |
|
||||
((fuzzy_mask & 0x20) << 5) |
|
||||
((fuzzy_mask & 0x40) << 6) |
|
||||
((fuzzy_mask & 0x80) << 7)
|
||||
);
|
||||
|
||||
output_short(output, spread_mask);
|
||||
}
|
||||
|
||||
void add_index_address_mark() {
|
||||
void add_index_address_mark() final {
|
||||
for(int c = 0; c < 3; c++) output_short(MFMIndexSync);
|
||||
add_byte(IndexAddressByte);
|
||||
}
|
||||
|
||||
void add_ID_address_mark() {
|
||||
void add_ID_address_mark() final {
|
||||
output_sync();
|
||||
add_byte(IDAddressByte);
|
||||
}
|
||||
|
||||
void add_data_address_mark() {
|
||||
void add_data_address_mark() final {
|
||||
output_sync();
|
||||
add_byte(DataAddressByte);
|
||||
}
|
||||
|
||||
void add_deleted_data_address_mark() {
|
||||
void add_deleted_data_address_mark() final {
|
||||
output_sync();
|
||||
add_byte(DeletedDataAddressByte);
|
||||
}
|
||||
@ -76,9 +89,9 @@ class MFMEncoder: public Encoder {
|
||||
|
||||
private:
|
||||
uint16_t last_output_;
|
||||
void output_short(uint16_t value) {
|
||||
void output_short(uint16_t value, uint16_t fuzzy_mask = 0) final {
|
||||
last_output_ = value;
|
||||
Encoder::output_short(value);
|
||||
Encoder::output_short(value, fuzzy_mask);
|
||||
}
|
||||
|
||||
void output_sync() {
|
||||
@ -90,9 +103,9 @@ class MFMEncoder: public Encoder {
|
||||
class FMEncoder: public Encoder {
|
||||
// encodes each 16-bit part as clock, data, clock, data [...]
|
||||
public:
|
||||
FMEncoder(std::vector<bool> &target) : Encoder(target) {}
|
||||
FMEncoder(std::vector<bool> &target, std::vector<bool> *fuzzy_target = nullptr) : Encoder(target, fuzzy_target) {}
|
||||
|
||||
void add_byte(uint8_t input) {
|
||||
void add_byte(uint8_t input, uint8_t fuzzy_mask = 0) final {
|
||||
crc_generator_.add(input);
|
||||
output_short(
|
||||
static_cast<uint16_t>(
|
||||
@ -105,28 +118,39 @@ class FMEncoder: public Encoder {
|
||||
((input & 0x40) << 6) |
|
||||
((input & 0x80) << 7) |
|
||||
0xaaaa
|
||||
));
|
||||
),
|
||||
static_cast<uint16_t>(
|
||||
((fuzzy_mask & 0x01) << 0) |
|
||||
((fuzzy_mask & 0x02) << 1) |
|
||||
((fuzzy_mask & 0x04) << 2) |
|
||||
((fuzzy_mask & 0x08) << 3) |
|
||||
((fuzzy_mask & 0x10) << 4) |
|
||||
((fuzzy_mask & 0x20) << 5) |
|
||||
((fuzzy_mask & 0x40) << 6) |
|
||||
((fuzzy_mask & 0x80) << 7)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void add_index_address_mark() {
|
||||
void add_index_address_mark() final {
|
||||
crc_generator_.reset();
|
||||
crc_generator_.add(IndexAddressByte);
|
||||
output_short(FMIndexAddressMark);
|
||||
}
|
||||
|
||||
void add_ID_address_mark() {
|
||||
void add_ID_address_mark() final {
|
||||
crc_generator_.reset();
|
||||
crc_generator_.add(IDAddressByte);
|
||||
output_short(FMIDAddressMark);
|
||||
}
|
||||
|
||||
void add_data_address_mark() {
|
||||
void add_data_address_mark() final {
|
||||
crc_generator_.reset();
|
||||
crc_generator_.add(DataAddressByte);
|
||||
output_short(FMDataAddressMark);
|
||||
}
|
||||
|
||||
void add_deleted_data_address_mark() {
|
||||
void add_deleted_data_address_mark() final {
|
||||
crc_generator_.reset();
|
||||
crc_generator_.add(DeletedDataAddressByte);
|
||||
output_short(FMDeletedDataAddressMark);
|
||||
@ -162,6 +186,7 @@ template<class T> std::shared_ptr<Storage::Disk::Track>
|
||||
total_sector_bytes += size_t(128 << sector->size) + 2;
|
||||
}
|
||||
|
||||
// Seek appropriate gap sizes, if the defaults don't allow all data to fit.
|
||||
while(true) {
|
||||
const size_t size =
|
||||
mark_size +
|
||||
@ -210,7 +235,6 @@ template<class T> std::shared_ptr<Storage::Disk::Track>
|
||||
for(std::size_t c = 0; c < pre_data_mark_bytes; c++) shifter.add_byte(0x00);
|
||||
|
||||
// Data, if attached.
|
||||
// TODO: allow for weak/fuzzy data.
|
||||
if(!sector->samples.empty()) {
|
||||
if(sector->is_deleted)
|
||||
shifter.add_deleted_data_address_mark();
|
||||
@ -219,8 +243,30 @@ template<class T> std::shared_ptr<Storage::Disk::Track>
|
||||
|
||||
std::size_t c = 0;
|
||||
std::size_t declared_length = static_cast<std::size_t>(128 << sector->size);
|
||||
for(c = 0; c < sector->samples[0].size() && c < declared_length; c++) {
|
||||
shifter.add_byte(sector->samples[0][c]);
|
||||
if(sector->samples.size() > 1) {
|
||||
// For each byte, mark as fuzzy any bits that differ. Which isn't exactly the
|
||||
// same thing as obeying the multiple samples, as it discards the implied
|
||||
// probabilities of different values.
|
||||
for(c = 0; c < sector->samples[0].size() && c < declared_length; c++) {
|
||||
auto sample_iterator = sector->samples.begin();
|
||||
uint8_t value = (*sample_iterator)[c], fuzzy_mask = 0;
|
||||
|
||||
++sample_iterator;
|
||||
while(sample_iterator != sector->samples.end()) {
|
||||
// Mark as fuzzy any bits that differ here from the
|
||||
// canonical value, and zero them out in the original.
|
||||
// That might cause them to retrigger, but who cares?
|
||||
fuzzy_mask |= value ^ (*sample_iterator)[c];
|
||||
value &= ~fuzzy_mask;
|
||||
|
||||
++sample_iterator;
|
||||
}
|
||||
shifter.add_byte(sector->samples[0][c], fuzzy_mask);
|
||||
}
|
||||
} else {
|
||||
for(c = 0; c < sector->samples[0].size() && c < declared_length; c++) {
|
||||
shifter.add_byte(sector->samples[0][c]);
|
||||
}
|
||||
}
|
||||
for(; c < declared_length; c++) {
|
||||
shifter.add_byte(0x00);
|
||||
@ -240,23 +286,36 @@ template<class T> std::shared_ptr<Storage::Disk::Track>
|
||||
return std::make_shared<Storage::Disk::PCMTrack>(std::move(segment));
|
||||
}
|
||||
|
||||
Encoder::Encoder(std::vector<bool> &target) :
|
||||
target_(&target) {}
|
||||
Encoder::Encoder(std::vector<bool> &target, std::vector<bool> *fuzzy_target) :
|
||||
target_(&target), fuzzy_target_(fuzzy_target) {}
|
||||
|
||||
void Encoder::reset_target(std::vector<bool> &target) {
|
||||
void Encoder::reset_target(std::vector<bool> &target, std::vector<bool> *fuzzy_target) {
|
||||
target_ = ⌖
|
||||
fuzzy_target_ = fuzzy_target;
|
||||
}
|
||||
|
||||
void Encoder::output_short(uint16_t value) {
|
||||
void Encoder::output_short(uint16_t value, uint16_t fuzzy_mask) {
|
||||
const bool write_fuzzy_bits = fuzzy_mask;
|
||||
|
||||
if(write_fuzzy_bits) {
|
||||
assert(fuzzy_target_);
|
||||
|
||||
// Zero-fill the bits to date, to cover any shorts written without fuzzy bits,
|
||||
// and make sure the value has a 0 anywhere it should be fuzzy.
|
||||
fuzzy_target_->resize(target_->size());
|
||||
value &= ~fuzzy_mask;
|
||||
}
|
||||
|
||||
uint16_t mask = 0x8000;
|
||||
while(mask) {
|
||||
target_->push_back(!!(value & mask));
|
||||
target_->push_back(value & mask);
|
||||
if(write_fuzzy_bits) fuzzy_target_->push_back(fuzzy_mask & mask);
|
||||
mask >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
void Encoder::add_crc(bool incorrectly) {
|
||||
uint16_t crc_value = crc_generator_.get_value();
|
||||
const uint16_t crc_value = crc_generator_.get_value();
|
||||
add_byte(crc_value >> 8);
|
||||
add_byte((crc_value & 0xff) ^ (incorrectly ? 1 : 0));
|
||||
}
|
||||
@ -315,10 +374,10 @@ std::shared_ptr<Storage::Disk::Track> Storage::Encodings::MFM::GetMFMTrackWithSe
|
||||
12500); // unintelligently: double the single-density bytes/rotation (or: 500kbps @ 300 rpm)
|
||||
}
|
||||
|
||||
std::unique_ptr<Encoder> Storage::Encodings::MFM::GetMFMEncoder(std::vector<bool> &target) {
|
||||
return std::make_unique<MFMEncoder>(target);
|
||||
std::unique_ptr<Encoder> Storage::Encodings::MFM::GetMFMEncoder(std::vector<bool> &target, std::vector<bool> *fuzzy_target) {
|
||||
return std::make_unique<MFMEncoder>(target, fuzzy_target);
|
||||
}
|
||||
|
||||
std::unique_ptr<Encoder> Storage::Encodings::MFM::GetFMEncoder(std::vector<bool> &target) {
|
||||
return std::make_unique<FMEncoder>(target);
|
||||
std::unique_ptr<Encoder> Storage::Encodings::MFM::GetFMEncoder(std::vector<bool> &target, std::vector<bool> *fuzzy_target) {
|
||||
return std::make_unique<FMEncoder>(target, fuzzy_target);
|
||||
}
|
||||
|
@ -44,16 +44,16 @@ std::shared_ptr<Storage::Disk::Track> GetFMTrackWithSectors(const std::vector<co
|
||||
|
||||
class Encoder {
|
||||
public:
|
||||
Encoder(std::vector<bool> &target);
|
||||
Encoder(std::vector<bool> &target, std::vector<bool> *fuzzy_target);
|
||||
virtual ~Encoder() {}
|
||||
virtual void reset_target(std::vector<bool> &target);
|
||||
virtual void reset_target(std::vector<bool> &target, std::vector<bool> *fuzzy_target = nullptr);
|
||||
|
||||
virtual void add_byte(uint8_t input) = 0;
|
||||
virtual void add_byte(uint8_t input, uint8_t fuzzy_mask = 0) = 0;
|
||||
virtual void add_index_address_mark() = 0;
|
||||
virtual void add_ID_address_mark() = 0;
|
||||
virtual void add_data_address_mark() = 0;
|
||||
virtual void add_deleted_data_address_mark() = 0;
|
||||
virtual void output_short(uint16_t value);
|
||||
virtual void output_short(uint16_t value, uint16_t fuzzy_mask = 0);
|
||||
|
||||
/// Outputs the CRC for all data since the last address mask; if @c incorrectly is @c true then outputs an incorrect CRC.
|
||||
void add_crc(bool incorrectly);
|
||||
@ -63,10 +63,11 @@ class Encoder {
|
||||
|
||||
private:
|
||||
std::vector<bool> *target_ = nullptr;
|
||||
std::vector<bool> *fuzzy_target_ = nullptr;
|
||||
};
|
||||
|
||||
std::unique_ptr<Encoder> GetMFMEncoder(std::vector<bool> &target);
|
||||
std::unique_ptr<Encoder> GetFMEncoder(std::vector<bool> &target);
|
||||
std::unique_ptr<Encoder> GetMFMEncoder(std::vector<bool> &target, std::vector<bool> *fuzzy_target = nullptr);
|
||||
std::unique_ptr<Encoder> GetFMEncoder(std::vector<bool> &target, std::vector<bool> *fuzzy_target = nullptr);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "PCMSegment.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
|
||||
using namespace Storage::Disk;
|
||||
|
||||
@ -70,31 +71,33 @@ void PCMSegment::rotate_right(size_t length) {
|
||||
}
|
||||
|
||||
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
|
||||
// Track the initial bit pointer for potentially considering whether this was an
|
||||
// initial index hole or a subsequent one later on.
|
||||
const std::size_t initial_bit_pointer = bit_pointer_;
|
||||
|
||||
// if starting from the beginning, pull half a bit backward, as if the initial bit
|
||||
// is set, it should be in the centre of its window
|
||||
// If starting from the beginning, pull half a bit backward, as if the initial bit
|
||||
// is set, it should be in the centre of its window.
|
||||
next_event_.length.length = bit_pointer_ ? 0 : -(segment_->length_of_a_bit.length >> 1);
|
||||
|
||||
// search for the next bit that is set, if any
|
||||
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
|
||||
++bit_pointer_; // so this always points one beyond the most recent bit returned
|
||||
next_event_.length.length += segment_->length_of_a_bit.length;
|
||||
|
||||
// if this bit is set, return the event
|
||||
if(bit) return next_event_;
|
||||
// if this bit is set, or is fuzzy and a random bit of 1 is selected, return the event.
|
||||
if(bit ||
|
||||
(!segment_->fuzzy_mask.empty() && segment_->fuzzy_mask[bit_pointer_] && lfsr_.next())
|
||||
) return next_event_;
|
||||
}
|
||||
|
||||
// if the end is reached without a bit being set, it'll be index holes from now on
|
||||
// If the end is reached without a bit being set, it'll be index holes from now on.
|
||||
next_event_.type = Track::Event::IndexHole;
|
||||
|
||||
// test whether this is the very first time that bits have been exhausted. If so then
|
||||
// Test whether this is the very first time that bits have been exhausted. If so then
|
||||
// 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
|
||||
// been consumed.
|
||||
if(initial_bit_pointer <= segment_->data.size()) {
|
||||
next_event_.length.length += (segment_->length_of_a_bit.length >> 1);
|
||||
bit_pointer_++;
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "../../Storage.hpp"
|
||||
#include "../../../NumberTheory/LFSR.hpp"
|
||||
#include "Track.hpp"
|
||||
|
||||
namespace Storage {
|
||||
@ -39,6 +40,13 @@ struct PCMSegment {
|
||||
*/
|
||||
std::vector<bool> data;
|
||||
|
||||
/*!
|
||||
If a segment has a fuzzy mask then anywhere the mask has a value
|
||||
of @c true, a random bit will be ORd onto whatever is in the
|
||||
corresponding slot in @c data.
|
||||
*/
|
||||
std::vector<bool> fuzzy_mask;
|
||||
|
||||
/*!
|
||||
Constructs an instance of PCMSegment with the specified @c length_of_a_bit
|
||||
and @c data.
|
||||
@ -192,6 +200,7 @@ class PCMSegmentEventSource {
|
||||
std::shared_ptr<PCMSegment> segment_;
|
||||
std::size_t bit_pointer_;
|
||||
Track::Event next_event_;
|
||||
LFSR<uint64_t> lfsr_;
|
||||
};
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user