1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-01-11 08:30:55 +00:00

Makes a first sweep at converting the storage underlying PCMSegment to vector<bool>.

This is to remove another pain point, in preparation for the work immediately forthcoming but also work as-yet unknown.
This commit is contained in:
Thomas Harte 2018-07-01 12:05:41 -04:00
parent 16bef0dcd5
commit 555c2a4377
18 changed files with 265 additions and 185 deletions

View File

@ -79,16 +79,11 @@ std::shared_ptr<Track> D64::get_track_at_position(Track::Address address) {
//
// = 349 GCR bytes per sector
PCMSegment track;
std::size_t track_bytes = 349 * static_cast<std::size_t>(sectors_by_zone[zone]);
track.number_of_bits = static_cast<unsigned int>(track_bytes) * 8;
track.data.resize(track_bytes);
uint8_t *data = &track.data[0];
memset(data, 0, track_bytes);
std::vector<uint8_t> 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<size_t>(sector) * 349];
sector_data[0] = sector_data[1] = sector_data[2] = 0xff;
uint8_t sector_number = static_cast<uint8_t>(sector); // sectors count from 0
@ -141,5 +136,5 @@ std::shared_ptr<Track> D64::get_track_at_position(Track::Address address) {
Encodings::CommodoreGCR::encode_block(&sector_data[target_data_offset], end_of_data);
}
return std::shared_ptr<Track>(new PCMTrack(std::move(track)));
return std::shared_ptr<Track>(new PCMTrack(PCMSegment(data)));
}

View File

@ -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<unsigned int>(segment.data.size() * 8);
}
return std::make_shared<PCMTrack>(segments);
}

View File

@ -87,11 +87,10 @@ std::shared_ptr<Track> G64::get_track_at_position(Track::Address address) {
if(byte_speed != current_speed || byte == static_cast<uint16_t>(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<Track> 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<unsigned int>(speed_zone_offset));
segment.data = std::move(track_contents);
PCMSegment segment(
Encodings::CommodoreGCR::length_of_a_bit_in_time_zone(static_cast<unsigned int>(speed_zone_offset)),
track_length * 8,
track_contents
);
resulting_track.reset(new PCMTrack(std::move(segment)));
}

View File

@ -26,9 +26,6 @@ HFE::HFE(const std::string &file_name) :
track_list_offset_ = static_cast<long>(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<long>(file_.get16le()) << 9;
uint16_t track_length = file_.get16le();
long track_offset = static_cast<long>(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<Track> HFE::get_track_at_position(Track::Address address) {
@ -64,21 +61,42 @@ std::shared_ptr<Track> HFE::get_track_at_position(Track::Address address) {
std::lock_guard<std::mutex> 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<uint16_t>(std::min(256, track_length - c));
file_.read(&segment.data[c], length);
std::vector<uint8_t> 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<size_t>(c) << 3) + static_cast<size_t>(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<PCMTrack>(segment);
}
@ -88,9 +106,11 @@ void HFE::set_tracks(const std::map<Track::Address, std::shared_ptr<Track>> &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<uint16_t>(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<uint8_t> byte_segment = segment.byte_data(false);
uint16_t data_length = std::min(static_cast<uint16_t>(byte_segment.size()), track_length);
lock_guard.lock();
seek_track(track.first);
@ -98,7 +118,7 @@ void HFE::set_tracks(const std::map<Track::Address, std::shared_ptr<Track>> &tra
uint16_t c = 0;
while(c < data_length) {
uint16_t length = static_cast<uint16_t>(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);
}

View File

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

View File

@ -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<size_t> 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<int>(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<uint8_t> data_segment(
track_data.begin() + static_cast<off_t>(index),
track_data.begin() + static_cast<off_t>(location));
data_segment.number_of_bits = static_cast<unsigned int>(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<Track::Address, std::shared_ptr<Track>> &tra
std::vector<uint8_t> track;
track.reserve(track_length);
uint8_t shifter = 0;
for(unsigned int bit = 0; bit < segment.number_of_bits; ++bit) {
shifter = static_cast<uint8_t>((shifter << 1) | segment.bit(bit));
for(const auto bit: segment.data) {
shifter = static_cast<uint8_t>((shifter << 1) | (bit ? 1 : 0));
if(shifter & 0x80) {
track.push_back(shifter);
shifter = 0;

View File

@ -108,7 +108,6 @@ std::shared_ptr<Track> OricMFMDSK::get_track_at_position(Track::Address address)
}
}
segment.number_of_bits = static_cast<unsigned int>(segment.data.size() * 8);
return std::make_shared<PCMTrack>(segment);
}
@ -123,8 +122,8 @@ void OricMFMDSK::set_tracks(const std::map<Track::Address, std::shared_ptr<Track
int offset = 0;
bool capture_size = false;
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);
if(shifter.get_token() == Storage::Encodings::MFM::Shifter::Token::None) continue;
parsed_track.push_back(shifter.get_byte());

View File

@ -106,7 +106,8 @@ std::shared_ptr<Track> WOZ::get_track_at_position(Track::Address address) {
if(offset == NoSuchTrack) return nullptr;
// Seek to the real track.
PCMSegment track_contents;
std::vector<uint8_t> track_contents;
size_t number_of_bits;
{
std::lock_guard<std::mutex> lock_guard(file_.get_file_access_mutex());
file_.seek(offset, SEEK_SET);
@ -114,13 +115,12 @@ std::shared_ptr<Track> 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<uint16_t>(6646*8));
}
return std::shared_ptr<PCMTrack>(new PCMTrack(track_contents));
return std::shared_ptr<PCMTrack>(new PCMTrack(PCMSegment(number_of_bits, track_contents)));
}
void WOZ::set_tracks(const std::map<Track::Address, std::shared_ptr<Track>> &tracks) {
@ -129,13 +129,14 @@ void WOZ::set_tracks(const std::map<Track::Address, std::shared_ptr<Track>> &tra
auto segment = Storage::Disk::track_serialisation(*pair.second, Storage::Time(1, 50000));
auto offset = static_cast<std::size_t>(file_offset(pair.first) - 12);
memcpy(&post_crc_contents_[offset - 12], segment.data.data(), segment.number_of_bits >> 3);
std::vector<uint8_t> 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<uint8_t>(segment.number_of_bits >> 3);
post_crc_contents_[offset + 6647] = static_cast<uint8_t>(segment.number_of_bits >> 11);
post_crc_contents_[offset + 6648] = static_cast<uint8_t>(segment.number_of_bits);
post_crc_contents_[offset + 6649] = static_cast<uint8_t>(segment.number_of_bits >> 8);
post_crc_contents_[offset + 6646] = static_cast<uint8_t>(segment.data.size() >> 3);
post_crc_contents_[offset + 6647] = static_cast<uint8_t>(segment.data.size() >> 11);
post_crc_contents_[offset + 6648] = static_cast<uint8_t>(segment.data.size());
post_crc_contents_[offset + 6649] = static_cast<uint8_t>(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;

View File

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

View File

@ -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<size_t>(((length * bit_size) + 7) >> 3), 0);
// Reserve sufficient storage.
segment.data.reserve(static_cast<size_t>(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<unsigned int>(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<uint8_t> 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<uint8_t>(((value) >> 1) | 0xaa); \
segment.data[index+1] = static_cast<uint8_t>((value) | 0xaa); \
data[index+0] = static_cast<uint8_t>(((value) >> 1) | 0xaa); \
data[index+1] = static_cast<uint8_t>((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<uint8_t> 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<unsigned int>(segment.data.size() * 8);
std::vector<uint8_t> 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<uint8_t>(
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<uint8_t>(
(bit_reverse[source[84]&3] << 0) |
(bit_reverse[source[170]&3] << 2)
);
segment.data[88] =
data[88] =
static_cast<uint8_t>(
(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);
}

View File

@ -49,9 +49,9 @@ std::map<std::size_t, Sector> 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<uint_fast8_t>((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<uint_fast8_t>((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<std::size_t, Sector> Storage::Encodings::AppleGCR::sectors_from_segment
new_sector.reset(new Sector);
new_sector->data.reserve(412);
} else {
sector_location = static_cast<std::size_t>(bit % segment.number_of_bits);
sector_location = static_cast<std::size_t>(bit % segment.data.size());
}
}
} else {

View File

@ -18,7 +18,7 @@ using namespace Storage::Encodings::MFM;
class MFMEncoder: public Encoder {
public:
MFMEncoder(std::vector<uint8_t> &target) : Encoder(target) {}
MFMEncoder(std::vector<bool> &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<uint8_t> &target) : Encoder(target) {}
FMEncoder(std::vector<bool> &target) : Encoder(target) {}
void add_byte(uint8_t input) {
crc_generator_.add(input);
@ -182,16 +182,17 @@ template<class T> std::shared_ptr<Storage::Disk::Track>
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<unsigned int>(segment.data.size() * 8);
return std::shared_ptr<Storage::Disk::Track>(new Storage::Disk::PCMTrack(std::move(segment)));
}
Encoder::Encoder(std::vector<uint8_t> &target) :
Encoder::Encoder(std::vector<bool> &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::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<uint8_t> &target) {
std::unique_ptr<Encoder> Storage::Encodings::MFM::GetMFMEncoder(std::vector<bool> &target) {
return std::unique_ptr<Encoder>(new MFMEncoder(target));
}
std::unique_ptr<Encoder> Storage::Encodings::MFM::GetFMEncoder(std::vector<uint8_t> &target) {
std::unique_ptr<Encoder> Storage::Encodings::MFM::GetFMEncoder(std::vector<bool> &target) {
return std::unique_ptr<Encoder>(new FMEncoder(target));
}

View File

@ -44,7 +44,7 @@ std::shared_ptr<Storage::Disk::Track> GetFMTrackWithSectors(const std::vector<co
class Encoder {
public:
Encoder(std::vector<uint8_t> &target);
Encoder(std::vector<bool> &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<uint8_t> &target_;
std::vector<bool> &target_;
};
std::unique_ptr<Encoder> GetMFMEncoder(std::vector<uint8_t> &target);
std::unique_ptr<Encoder> GetFMEncoder(std::vector<uint8_t> &target);
std::unique_ptr<Encoder> GetMFMEncoder(std::vector<bool> &target);
std::unique_ptr<Encoder> GetFMEncoder(std::vector<bool> &target);
}
}

View File

@ -23,8 +23,8 @@ std::map<std::size_t, Storage::Encodings::MFM::Sector> 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:

View File

@ -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<std::size_t>(8 - shift) < rhs.number_of_bits)
data[first_byte + source + 1] = static_cast<uint8_t>(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<unsigned int>(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<unsigned int>(bit_pointer_ - 1);
}
const PCMSegment &PCMSegmentEventSource::segment() const {
return *segment_;
}

View File

@ -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<uint8_t> data;
PCMSegment(Time length_of_a_bit, unsigned int number_of_bits, std::vector<uint8_t> 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<bool>
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<bool> 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<bool> &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<uint8_t> &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<uint8_t> &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<uint8_t> &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<uint8_t> byte_data(bool msb_first = true) const {
std::vector<uint8_t> 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<unsigned int>(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<PCMSegment> segment_;
std::size_t bit_pointer_;

View File

@ -17,18 +17,18 @@ PCMTrack::PCMTrack(const std::vector<PCMSegment> &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<unsigned int>(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<unsigned int>(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<unsigned int>(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<unsigned int>(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<unsigned int>(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;
}

View File

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