1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-25 04:29:09 +00:00

Merge pull request #734 from TomHarte/FuzzyBits

Adds PCMSegementEventSource support for 'fuzzy' bits
This commit is contained in:
Thomas Harte 2020-01-19 21:48:07 -05:00 committed by GitHub
commit 8f94da9daf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 209 additions and 65 deletions

View File

@ -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
View 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 */

View File

@ -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>";

View File

@ -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--) {

View File

@ -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;

View File

@ -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_ = &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);
}

View File

@ -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);
}
}

View File

@ -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_++;

View File

@ -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_;
};
}