1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-17 21:30:14 +00:00

Introduce high-density tracks.

This commit is contained in:
Thomas Harte 2023-12-10 22:17:23 -05:00
parent 671e59b27a
commit 98e05fb0e3
31 changed files with 278 additions and 121 deletions

View File

@ -19,7 +19,7 @@ using namespace Analyser::Static::Acorn;
std::unique_ptr<Catalogue> Analyser::Static::Acorn::GetDFSCatalogue(const std::shared_ptr<Storage::Disk::Disk> &disk) {
// c.f. http://beebwiki.mdfs.net/Acorn_DFS_disc_format
auto catalogue = std::make_unique<Catalogue>();
Storage::Encodings::MFM::Parser parser(false, disk);
Storage::Encodings::MFM::Parser parser(Storage::Encodings::MFM::Density::Single, disk);
const Storage::Encodings::MFM::Sector *const names = parser.get_sector(0, 0, 0);
const Storage::Encodings::MFM::Sector *const details = parser.get_sector(0, 0, 1);
@ -84,7 +84,7 @@ std::unique_ptr<Catalogue> Analyser::Static::Acorn::GetDFSCatalogue(const std::s
*/
std::unique_ptr<Catalogue> Analyser::Static::Acorn::GetADFSCatalogue(const std::shared_ptr<Storage::Disk::Disk> &disk) {
auto catalogue = std::make_unique<Catalogue>();
Storage::Encodings::MFM::Parser parser(true, disk);
Storage::Encodings::MFM::Parser parser(Storage::Encodings::MFM::Density::Double, disk);
Storage::Encodings::MFM::Sector *free_space_map_second_half = parser.get_sector(0, 0, 1);
if(!free_space_map_second_half) return nullptr;

View File

@ -159,7 +159,7 @@ void InspectCatalogue(
}
bool CheckBootSector(const std::shared_ptr<Storage::Disk::Disk> &disk, const std::unique_ptr<Analyser::Static::AmstradCPC::Target> &target) {
Storage::Encodings::MFM::Parser parser(true, disk);
Storage::Encodings::MFM::Parser parser(Storage::Encodings::MFM::Density::Double, disk);
Storage::Encodings::MFM::Sector *boot_sector = parser.get_sector(0, 0, 0x41);
if(boot_sector != nullptr && !boot_sector->samples.empty() && boot_sector->samples[0].size() == 512) {
// Check that the first 64 bytes of the sector aren't identical; if they are then probably

View File

@ -44,7 +44,7 @@ Analyser::Static::TargetList Analyser::Static::FAT12::GetTargets(const Media &me
Storage::Disk::track_serialisation(
*track_zero,
Storage::Encodings::MFM::MFMBitLength
), true);
), Storage::Encodings::MFM::Density::Double);
// If no sectors were found, assume this disk was either single density or high density, which both imply the PC.
if(sector_map.empty() || sector_map.size() > 10) {

View File

@ -175,7 +175,7 @@ Analyser::Static::TargetList Analyser::Static::Oric::GetTargets(const Media &med
// 8-DOS is recognised by a dedicated Disk II analyser, so check only for Microdisc,
// Jasmin and BD-DOS formats here.
for(auto &disk: media.disks) {
Storage::Encodings::MFM::Parser parser(true, disk);
Storage::Encodings::MFM::Parser parser(Storage::Encodings::MFM::Density::Double, disk);
if(is_microdisc(parser)) {
target->disk_interface = Target::DiskInterface::Microdisc;

View File

@ -33,7 +33,7 @@ bool IsSpectrumTape(const std::shared_ptr<Storage::Tape::Tape> &tape) {
}
bool IsSpectrumDisk(const std::shared_ptr<Storage::Disk::Disk> &disk) {
Storage::Encodings::MFM::Parser parser(true, disk);
Storage::Encodings::MFM::Parser parser(Storage::Encodings::MFM::Density::Double, disk);
// Get logical sector 1; the Spectrum appears to support various physical
// sectors as sector 1.

View File

@ -373,12 +373,43 @@ class FloppyController {
return cached.sectors;
}
const bool is_double_density = true; // TODO: use MFM flag here.
auto serialisation = Storage::Disk::track_serialisation(
const bool is_mfm = true; // TODO: use MFM flag here.
Storage::Disk::PCMSegment serialisation;
// If attempting to load as FM then use the FM bit length only;
// if MFM decoding is enabled then try both double- and high-density
// parsings, keeping whichever provides the most meaningful reading.
//
// TODO: there must be a hardware selection for this somewhere on real hardware.
// Though not necessarily on an XT.
if(!is_mfm) {
serialisation = Storage::Disk::track_serialisation(
*raw_track,
Storage::Encodings::MFM::FMBitLength
);
cached.sectors = Storage::Encodings::MFM::sectors_from_segment(std::move(serialisation), Storage::Encodings::MFM::Density::Single);
return cached.sectors;
}
// Get double-density candidate.
serialisation = Storage::Disk::track_serialisation(
*raw_track,
is_double_density ? Storage::Encodings::MFM::MFMBitLength : Storage::Encodings::MFM::FMBitLength
Storage::Encodings::MFM::MFMBitLength
);
cached.sectors = Storage::Encodings::MFM::sectors_from_segment(std::move(serialisation), is_double_density);
auto double_candidate = Storage::Encodings::MFM::sectors_from_segment(std::move(serialisation), Storage::Encodings::MFM::Density::High);
// Get high-density candidate.
serialisation = Storage::Disk::track_serialisation(
*raw_track,
Storage::Encodings::MFM::HDMFMBitLength
);
auto high_candidate = Storage::Encodings::MFM::sectors_from_segment(std::move(serialisation), Storage::Encodings::MFM::Density::Double);
// Pick the more populous.
cached.sectors = high_candidate.size() > double_candidate.size() ?
std::move(high_candidate) : std::move(double_candidate);
return cached.sectors;
}

View File

@ -32,7 +32,7 @@ void MFMController::set_is_double_density(bool is_double_density) {
bit_length.clock_rate = is_double_density ? 500000 : 250000;
set_expected_bit_length(bit_length);
shifter_.set_is_double_density(is_double_density);
shifter_.set_is_mfm(is_double_density);
}
bool MFMController::get_is_double_density() {

View File

@ -39,7 +39,7 @@ AcornADF::AcornADF(const std::string &file_name) : MFMSectorDump(file_name) {
head_count_ = 1 + (file_.stats().st_size > sectors_per_track * sizeT(128 << sector_size) * 80);
// Announce disk geometry.
set_geometry(sectors_per_track, sector_size, 0, true);
set_geometry(sectors_per_track, sector_size, 0, Encodings::MFM::Density::Double);
}
HeadPosition AcornADF::get_maximum_head_position() {

View File

@ -214,7 +214,11 @@ std::shared_ptr<Track> CPCDSK::get_track_at_position(::Storage::Disk::Track::Add
}
// TODO: FM encoding, data rate?
return Storage::Encodings::MFM::GetMFMTrackWithSectors(sectors, track->gap3_length, track->filler_byte);
return Storage::Encodings::MFM::TrackWithSectors(
Storage::Encodings::MFM::Density::Double,
sectors,
track->gap3_length,
track->filler_byte);
}
void CPCDSK::set_tracks(const std::map<::Storage::Disk::Track::Address, std::shared_ptr<::Storage::Disk::Track>> &tracks) {
@ -225,7 +229,7 @@ void CPCDSK::set_tracks(const std::map<::Storage::Disk::Track::Address, std::sha
std::map<std::size_t, Storage::Encodings::MFM::Sector> sectors =
Storage::Encodings::MFM::sectors_from_segment(
Storage::Disk::track_serialisation(*pair.second, is_double_density ? Storage::Encodings::MFM::MFMBitLength : Storage::Encodings::MFM::FMBitLength),
is_double_density);
Storage::Encodings::MFM::Density::Double);
// Find slot for track, making it if neccessary.
const std::size_t chronological_track = index_for_track(pair.first);

View File

@ -41,7 +41,12 @@ FAT12::FAT12(const std::string &file_name) :
}
if(log_sector_size >= 5) throw Error::InvalidFormat;
set_geometry(sector_count_, log_sector_size, 1, true);
set_geometry(
sector_count_,
log_sector_size,
1,
sector_count_ > 10 ? Encodings::MFM::Density::High : Encodings::MFM::Density::Double
);
}
HeadPosition FAT12::get_maximum_head_position() {

View File

@ -157,7 +157,13 @@ std::shared_ptr<::Storage::Disk::Track> IMD::get_track_at_position(::Storage::Di
// Mode also indicates data density, but I don't have a good strategy for reconciling that if
// it were to disagree with the density implied by the quantity of sectors. So a broad 'is it MFM' test is
// applied only.
return (mode >= 3) ?
Storage::Encodings::MFM::GetMFMTrackWithSectors(sectors) :
Storage::Encodings::MFM::GetFMTrackWithSectors(sectors);
using namespace Storage::Encodings::MFM;
if(mode < 3) {
return TrackWithSectors(Density::Single, sectors);
}
if(sector_size * sector_count >= 6912) {
return TrackWithSectors(Density::High, sectors);
} else {
return TrackWithSectors(Density::Double, sectors);
}
}

View File

@ -14,10 +14,10 @@ using namespace Storage::Disk;
MFMSectorDump::MFMSectorDump(const std::string &file_name) : file_(file_name) {}
void MFMSectorDump::set_geometry(int sectors_per_track, uint8_t sector_size, uint8_t first_sector, bool is_double_density) {
void MFMSectorDump::set_geometry(int sectors_per_track, uint8_t sector_size, uint8_t first_sector, Encodings::MFM::Density density) {
sectors_per_track_ = sectors_per_track;
sector_size_ = sector_size;
is_double_density_ = is_double_density;
density_ = density;
first_sector_ = first_sector;
}
@ -34,7 +34,14 @@ std::shared_ptr<Track> MFMSectorDump::get_track_at_position(Track::Address addre
file_.read(sectors, sizeof(sectors));
}
return track_for_sectors(sectors, sectors_per_track_, uint8_t(address.position.as_int()), uint8_t(address.head), first_sector_, sector_size_, is_double_density_);
return track_for_sectors(
sectors,
sectors_per_track_,
uint8_t(address.position.as_int()),
uint8_t(address.head),
first_sector_,
sector_size_,
density_);
}
void MFMSectorDump::set_tracks(const std::map<Track::Address, std::shared_ptr<Track>> &tracks) {
@ -44,7 +51,13 @@ void MFMSectorDump::set_tracks(const std::map<Track::Address, std::shared_ptr<Tr
// in one loop, then write in another.
for(const auto &track : tracks) {
decode_sectors(*track.second, parsed_track, first_sector_, first_sector_ + uint8_t(sectors_per_track_-1), sector_size_, is_double_density_);
decode_sectors(
*track.second,
parsed_track,
first_sector_,
first_sector_ + uint8_t(sectors_per_track_-1),
sector_size_,
density_);
const long file_offset = get_file_offset_for_position(track.first);
std::lock_guard lock_guard(file_.get_file_access_mutex());

View File

@ -11,6 +11,7 @@
#include "../DiskImage.hpp"
#include "../../../FileHolder.hpp"
#include "../../Encodings/MFM/Constants.hpp"
#include <string>
@ -29,14 +30,14 @@ class MFMSectorDump: public DiskImage {
protected:
Storage::FileHolder file_;
void set_geometry(int sectors_per_track, uint8_t sector_size, uint8_t first_sector, bool is_double_density);
void set_geometry(int sectors_per_track, uint8_t sector_size, uint8_t first_sector, Encodings::MFM::Density density);
private:
virtual long get_file_offset_for_position(Track::Address address) = 0;
int sectors_per_track_ = 0;
uint8_t sector_size_ = 0;
bool is_double_density_ = true;
Encodings::MFM::Density density_ = Encodings::MFM::Density::Single;
uint8_t first_sector_ = 0;
};

View File

@ -84,7 +84,7 @@ std::shared_ptr<::Storage::Disk::Track> MSA::get_track_at_position(::Storage::Di
const auto &track = uncompressed_tracks_[size_t(position - starting_track_) * size_t(sides_) + size_t(address.head)];
assert(!track.empty());
return track_for_sectors(track.data(), sectors_per_track_, uint8_t(position), uint8_t(address.head), 1, 2, true);
return track_for_sectors(track.data(), sectors_per_track_, uint8_t(position), uint8_t(address.head), 1, 2, Storage::Encodings::MFM::Density::Double);
}
HeadPosition MSA::get_maximum_head_position() {

View File

@ -119,7 +119,7 @@ void OricMFMDSK::set_tracks(const std::map<Track::Address, std::shared_ptr<Track
for(const auto &track : tracks) {
PCMSegment segment = Storage::Disk::track_serialisation(*track.second, Storage::Encodings::MFM::MFMBitLength);
Storage::Encodings::MFM::Shifter shifter;
shifter.set_is_double_density(true);
shifter.set_is_mfm(true);
shifter.set_should_obey_syncs(true);
std::vector<uint8_t> parsed_track;
int size = 0;

View File

@ -47,7 +47,8 @@ PCBooter::PCBooter(const std::string &file_name) :
break;
}
set_geometry(sector_count_, 2, 1, true);
// TODO: probably single density, actually?
set_geometry(sector_count_, 2, 1, Encodings::MFM::Density::Double);
// TODO: check that an appropriate INT or similar is in the boot sector?
// Should probably factor out the "does this look like a PC boot sector?" test,

View File

@ -29,7 +29,7 @@ SSD::SSD(const std::string &file_name) : MFMSectorDump(file_name) {
if(track_count_ < 40) track_count_ = 40;
else if(track_count_ < 80) track_count_ = 80;
set_geometry(sectors_per_track, sector_size, 0, false);
set_geometry(sectors_per_track, sector_size, 0, Encodings::MFM::Density::Single);
}
HeadPosition SSD::get_maximum_head_position() {

View File

@ -136,7 +136,7 @@ class TrackConstructor {
using Shifter = Storage::Encodings::MFM::Shifter;
Shifter shifter;
shifter.set_should_obey_syncs(true);
shifter.set_is_double_density(true);
shifter.set_is_mfm(true);
result.emplace_back();
@ -469,7 +469,7 @@ std::shared_ptr<::Storage::Disk::Track> STX::get_track_at_position(::Storage::Di
// If this is a trivial .ST-style sector dump, life is easy.
if(!(flags & 1)) {
const auto sector_contents = file_.read(sector_count * 512);
return track_for_sectors(sector_contents.data(), sector_count, uint8_t(address.position.as_int()), uint8_t(address.head), 1, 2, true);
return track_for_sectors(sector_contents.data(), sector_count, uint8_t(address.position.as_int()), uint8_t(address.head), 1, 2, Storage::Encodings::MFM::Density::Double);
}
// Grab sector records, if provided.

View File

@ -18,7 +18,15 @@
using namespace Storage::Disk;
std::shared_ptr<Track> Storage::Disk::track_for_sectors(const uint8_t *const source, int number_of_sectors, uint8_t track, uint8_t side, uint8_t first_sector, uint8_t size, bool is_double_density) {
std::shared_ptr<Track> Storage::Disk::track_for_sectors(
const uint8_t *const source,
int number_of_sectors,
uint8_t track,
uint8_t side,
uint8_t first_sector,
uint8_t size,
Storage::Encodings::MFM::Density density
) {
std::vector<Storage::Encodings::MFM::Sector> sectors;
size_t byte_size = size_t(128 << size);
@ -39,17 +47,27 @@ std::shared_ptr<Track> Storage::Disk::track_for_sectors(const uint8_t *const sou
}
if(!sectors.empty()) {
return is_double_density ? Storage::Encodings::MFM::GetMFMTrackWithSectors(sectors) : Storage::Encodings::MFM::GetFMTrackWithSectors(sectors);
return TrackWithSectors(density, sectors);
}
return nullptr;
}
void Storage::Disk::decode_sectors(Track &track, uint8_t *const destination, uint8_t first_sector, uint8_t last_sector, uint8_t sector_size, bool is_double_density) {
void Storage::Disk::decode_sectors(
const Track &track,
uint8_t *const destination,
uint8_t first_sector,
uint8_t last_sector,
uint8_t sector_size,
Storage::Encodings::MFM::Density density
) {
std::map<std::size_t, Storage::Encodings::MFM::Sector> sectors =
Storage::Encodings::MFM::sectors_from_segment(
Storage::Disk::track_serialisation(track, is_double_density ? Storage::Encodings::MFM::MFMBitLength : Storage::Encodings::MFM::FMBitLength),
is_double_density);
Storage::Disk::track_serialisation(
track,
Storage::Encodings::MFM::bit_length(density)
),
density);
std::size_t byte_size = size_t(128 << sector_size);
for(const auto &pair : sectors) {

View File

@ -13,10 +13,13 @@
#include <memory>
#include <vector>
#include "../../../Encodings/MFM/Encoder.hpp"
namespace Storage::Disk {
std::shared_ptr<Track> track_for_sectors(const uint8_t *source, int number_of_sectors, uint8_t track, uint8_t side, uint8_t first_sector, uint8_t size, bool is_double_density);
void decode_sectors(Track &track, uint8_t *destination, uint8_t first_sector, uint8_t last_sector, uint8_t sector_size, bool is_double_density);
std::shared_ptr<Track> track_for_sectors(const uint8_t *source, int number_of_sectors, uint8_t track, uint8_t side, uint8_t first_sector, uint8_t size, Storage::Encodings::MFM::Density density);
void decode_sectors(const Track &track, uint8_t *destination, uint8_t first_sector, uint8_t last_sector, uint8_t sector_size, Storage::Encodings::MFM::Density density);
}

View File

@ -13,6 +13,14 @@
namespace Storage::Encodings::MFM {
enum class Density {
Single, Double, High
};
constexpr bool is_mfm(Density density) {
return density != Density::Single;
}
const uint8_t IndexAddressByte = 0xfc;
const uint8_t IDAddressByte = 0xfe;
const uint8_t DataAddressByte = 0xfb;
@ -30,9 +38,18 @@ const uint16_t MFMPostSyncCRCValue = 0xcdb4; // the value the CRC generator sho
const uint8_t MFMIndexSyncByteValue = 0xc2;
const uint8_t MFMSyncByteValue = 0xa1;
const Time HDMFMBitLength = Time(1, 200000);
const Time MFMBitLength = Time(1, 100000);
const Time FMBitLength = Time(1, 50000);
constexpr Time bit_length(Density density) {
switch(density) {
case Density::Single: return FMBitLength;
case Density::Double: return MFMBitLength;
case Density::High: return HDMFMBitLength;
}
}
}
#endif /* Constants_h */

View File

@ -18,6 +18,18 @@
using namespace Storage::Encodings::MFM;
namespace {
std::vector<const Sector *> sector_pointers(const std::vector<Sector> &sectors) {
std::vector<const Sector *> pointers;
for(const Sector &sector: sectors) {
pointers.push_back(&sector);
}
return pointers;
}
}
enum class SurfaceItem {
Mark,
Data
@ -124,7 +136,7 @@ class FMEncoder: public Encoder {
};
template<class T> std::shared_ptr<Storage::Disk::Track>
GetTrackWithSectors(
GTrackWithSectors(
const std::vector<const Sector *> &sectors,
std::size_t post_index_address_mark_bytes, uint8_t post_index_address_mark_value,
std::size_t pre_address_mark_bytes,
@ -281,58 +293,55 @@ void Encoder::add_crc(bool incorrectly) {
add_byte((crc_value & 0xff) ^ (incorrectly ? 1 : 0));
}
const std::size_t Storage::Encodings::MFM::DefaultSectorGapLength = std::numeric_limits<std::size_t>::max();
namespace {
template <Density density>
std::shared_ptr<Storage::Disk::Track> TrackWithSectors(
const std::vector<const Sector *> &sectors,
std::optional<std::size_t> sector_gap_length,
std::optional<uint8_t> sector_gap_filler_byte
) {
using EncoderT = std::conditional_t<density == Density::Single, FMEncoder, MFMEncoder>;
return GTrackWithSectors<EncoderT>(
sectors,
Defaults<density>::post_index_address_mark_bytes,
Defaults<density>::post_index_address_mark_value,
Defaults<density>::pre_address_mark_bytes,
Defaults<density>::post_address_address_mark_bytes,
sector_gap_filler_byte ? *sector_gap_filler_byte : Defaults<density>::post_address_address_mark_value,
Defaults<density>::pre_data_mark_bytes,
sector_gap_length ? *sector_gap_length : Defaults<density>::post_data_bytes,
Defaults<density>::post_data_value,
Defaults<density>::expected_track_bytes
);
}
static std::vector<const Sector *> sector_pointers(const std::vector<Sector> &sectors) {
std::vector<const Sector *> pointers;
for(const Sector &sector: sectors) {
pointers.push_back(&sector);
}
std::shared_ptr<Storage::Disk::Track> TrackWithSectors(
Density density,
const std::vector<Sector> &sectors,
std::optional<std::size_t> sector_gap_length,
std::optional<uint8_t> sector_gap_filler_byte
) {
return TrackWithSectors(
density,
sector_pointers(sectors),
sector_gap_length,
sector_gap_filler_byte
);
}
std::shared_ptr<Storage::Disk::Track> TrackWithSectors(
Density density,
const std::vector<const Sector *> &sectors,
std::optional<std::size_t> sector_gap_length,
std::optional<uint8_t> sector_gap_filler_byte
) {
switch(density) {
case Density::Single: return TrackWithSectors<Density::Single>(sectors, sector_gap_length, sector_gap_filler_byte);
case Density::Double: return TrackWithSectors<Density::Double>(sectors, sector_gap_length, sector_gap_filler_byte);
case Density::High: return TrackWithSectors<Density::High>(sectors, sector_gap_length, sector_gap_filler_byte);
}
return pointers;
}
std::shared_ptr<Storage::Disk::Track> Storage::Encodings::MFM::GetFMTrackWithSectors(const std::vector<Sector> &sectors, std::size_t sector_gap_length, uint8_t sector_gap_filler_byte) {
return GetTrackWithSectors<FMEncoder>(
sector_pointers(sectors),
26, 0xff,
6,
11, sector_gap_filler_byte,
6,
(sector_gap_length != DefaultSectorGapLength) ? sector_gap_length : 27, 0xff,
6250); // i.e. 250kbps (including clocks) * 60 = 15000kpm, at 300 rpm => 50 kbits/rotation => 6250 bytes/rotation
}
std::shared_ptr<Storage::Disk::Track> Storage::Encodings::MFM::GetFMTrackWithSectors(const std::vector<const Sector *> &sectors, std::size_t sector_gap_length, uint8_t sector_gap_filler_byte) {
return GetTrackWithSectors<FMEncoder>(
sectors,
26, 0xff,
6,
11, sector_gap_filler_byte,
6,
(sector_gap_length != DefaultSectorGapLength) ? sector_gap_length : 27, 0xff,
6250); // i.e. 250kbps (including clocks) * 60 = 15000kpm, at 300 rpm => 50 kbits/rotation => 6250 bytes/rotation
}
std::shared_ptr<Storage::Disk::Track> Storage::Encodings::MFM::GetMFMTrackWithSectors(const std::vector<Sector> &sectors, std::size_t sector_gap_length, uint8_t sector_gap_filler_byte) {
return GetTrackWithSectors<MFMEncoder>(
sector_pointers(sectors),
50, 0x4e,
12,
22, sector_gap_filler_byte,
12,
(sector_gap_length != DefaultSectorGapLength) ? sector_gap_length : 54, 0xff,
12500); // unintelligently: double the single-density bytes/rotation (or: 500kbps @ 300 rpm)
}
std::shared_ptr<Storage::Disk::Track> Storage::Encodings::MFM::GetMFMTrackWithSectors(const std::vector<const Sector *> &sectors, std::size_t sector_gap_length, uint8_t sector_gap_filler_byte) {
return GetTrackWithSectors<MFMEncoder>(
sectors,
50, 0x4e,
12,
22, sector_gap_filler_byte,
12,
(sector_gap_length != DefaultSectorGapLength) ? sector_gap_length : 54, 0xff,
12500); // unintelligently: double the single-density bytes/rotation (or: 500kbps @ 300 rpm)
}
std::unique_ptr<Encoder> Storage::Encodings::MFM::GetMFMEncoder(std::vector<bool> &target, std::vector<bool> *fuzzy_target) {

View File

@ -11,34 +11,78 @@
#include <cstdint>
#include <memory>
#include <optional>
#include <vector>
#include "Constants.hpp"
#include "Sector.hpp"
#include "../../Track/Track.hpp"
#include "../../../../Numeric/CRC.hpp"
namespace Storage::Encodings::MFM {
extern const std::size_t DefaultSectorGapLength;
template <Density density> struct Defaults;
template <> struct Defaults<Density::Single> {
static constexpr size_t expected_track_bytes = 6250;
static constexpr size_t post_index_address_mark_bytes = 26;
static constexpr uint8_t post_index_address_mark_value = 0xff;
static constexpr size_t pre_address_mark_bytes = 6;
static constexpr size_t post_address_address_mark_bytes = 11;
static constexpr uint8_t post_address_address_mark_value = 0xff;
static constexpr size_t pre_data_mark_bytes = 6;
static constexpr size_t post_data_bytes = 27;
static constexpr uint8_t post_data_value = 0xff;
};
template <> struct Defaults<Density::Double> {
static constexpr size_t expected_track_bytes = 12500;
static constexpr size_t post_index_address_mark_bytes = 50;
static constexpr uint8_t post_index_address_mark_value = 0x4e;
static constexpr size_t pre_address_mark_bytes = 12;
static constexpr size_t post_address_address_mark_bytes = 22;
static constexpr uint8_t post_address_address_mark_value = 0x4e;
static constexpr size_t pre_data_mark_bytes = 12;
static constexpr size_t post_data_bytes = 54;
static constexpr uint8_t post_data_value = 0xff;
};
template <> struct Defaults<Density::High> {
static constexpr size_t expected_track_bytes = 25000;
static constexpr size_t post_index_address_mark_bytes = 50;
static constexpr uint8_t post_index_address_mark_value = 0x4e;
static constexpr size_t pre_address_mark_bytes = 12;
static constexpr size_t post_address_address_mark_bytes = 22;
static constexpr uint8_t post_address_address_mark_value = 0x4e;
static constexpr size_t pre_data_mark_bytes = 12;
static constexpr size_t post_data_bytes = 54;
static constexpr uint8_t post_data_value = 0xff;
};
/*!
Converts a vector of sectors into a properly-encoded MFM track.
Converts a vector of sectors into a properly-encoded FM or MFM track.
@param sectors The sectors to write.
@param sector_gap_length If specified, sets the distance in whole bytes between each ID and its data.
@param sector_gap_filler_byte If specified, sets the value (unencoded) that is used to populate the gap between each ID and its data.
*/
std::shared_ptr<Storage::Disk::Track> GetMFMTrackWithSectors(const std::vector<Sector> &sectors, std::size_t sector_gap_length = DefaultSectorGapLength, uint8_t sector_gap_filler_byte = 0x4e);
std::shared_ptr<Storage::Disk::Track> GetMFMTrackWithSectors(const std::vector<const Sector *> &sectors, std::size_t sector_gap_length = DefaultSectorGapLength, uint8_t sector_gap_filler_byte = 0x4e);
std::shared_ptr<Storage::Disk::Track> TrackWithSectors(
Density density,
const std::vector<Sector> &sectors,
std::optional<std::size_t> sector_gap_length = std::nullopt,
std::optional<uint8_t> sector_gap_filler_byte = std::nullopt);
/*!
Converts a vector of sectors into a properly-encoded FM track.
@param sectors The sectors to write.
@param sector_gap_length If specified, sets the distance in whole bytes between each ID and its data.
@param sector_gap_filler_byte If specified, sets the value (unencoded) that is used to populate the gap between each ID and its data.
*/
std::shared_ptr<Storage::Disk::Track> GetFMTrackWithSectors(const std::vector<Sector> &sectors, std::size_t sector_gap_length = DefaultSectorGapLength, uint8_t sector_gap_filler_byte = 0xff);
std::shared_ptr<Storage::Disk::Track> GetFMTrackWithSectors(const std::vector<const Sector *> &sectors, std::size_t sector_gap_length = DefaultSectorGapLength, uint8_t sector_gap_filler_byte = 0xff);
std::shared_ptr<Storage::Disk::Track> TrackWithSectors(
Density density,
const std::vector<const Sector *> &sectors,
std::optional<std::size_t> sector_gap_length = std::nullopt,
std::optional<uint8_t> sector_gap_filler_byte = std::nullopt);
class Encoder {
public:

View File

@ -14,8 +14,8 @@
using namespace Storage::Encodings::MFM;
Parser::Parser(bool is_mfm, const std::shared_ptr<Storage::Disk::Disk> &disk) :
disk_(disk), is_mfm_(is_mfm) {}
Parser::Parser(Density density, const std::shared_ptr<Storage::Disk::Disk> &disk) :
disk_(disk), density_(density) {}
void Parser::install_sectors_from_track(const Storage::Disk::Track::Address &address) {
if(sectors_by_address_by_track_.find(address) != sectors_by_address_by_track_.end()) {
@ -28,8 +28,8 @@ void Parser::install_sectors_from_track(const Storage::Disk::Track::Address &add
}
std::map<std::size_t, Sector> sectors = sectors_from_segment(
Storage::Disk::track_serialisation(*track, is_mfm_ ? MFMBitLength : FMBitLength),
is_mfm_);
Storage::Disk::track_serialisation(*track, bit_length(density_)),
density_);
std::map<int, Storage::Encodings::MFM::Sector> sectors_by_id;
for(const auto &sector : sectors) {

View File

@ -9,6 +9,7 @@
#ifndef Parser_hpp
#define Parser_hpp
#include "Constants.hpp"
#include "Sector.hpp"
#include "../../Track/Track.hpp"
#include "../../Drive.hpp"
@ -20,7 +21,7 @@ namespace Storage::Encodings::MFM {
*/
class Parser {
public:
Parser(bool is_mfm, const std::shared_ptr<Storage::Disk::Disk> &disk);
Parser(Density density, const std::shared_ptr<Storage::Disk::Disk> &disk);
/*!
Seeks to the physical track at @c head and @c track. Searches on it for a sector
@ -32,7 +33,7 @@ class Parser {
private:
std::shared_ptr<Storage::Disk::Disk> disk_;
bool is_mfm_ = true;
Density density_ = Density::Double;
void install_sectors_from_track(const Storage::Disk::Track::Address &address);
std::map<Storage::Disk::Track::Address, std::map<int, Storage::Encodings::MFM::Sector>> sectors_by_address_by_track_;

View File

@ -11,10 +11,13 @@
using namespace Storage::Encodings::MFM;
std::map<std::size_t, Storage::Encodings::MFM::Sector> Storage::Encodings::MFM::sectors_from_segment(const Storage::Disk::PCMSegment &&segment, bool is_double_density) {
std::map<std::size_t, Storage::Encodings::MFM::Sector> Storage::Encodings::MFM::sectors_from_segment(
const Storage::Disk::PCMSegment &segment,
Density density
) {
std::map<std::size_t, Sector> result;
Shifter shifter;
shifter.set_is_double_density(is_double_density);
shifter.set_is_mfm(is_mfm(density));
shifter.set_should_obey_syncs(true);
std::unique_ptr<Storage::Encodings::MFM::Sector> new_sector;

View File

@ -9,6 +9,7 @@
#ifndef SegmentParser_hpp
#define SegmentParser_hpp
#include "Constants.hpp"
#include "Sector.hpp"
#include "../../Track/PCMSegment.hpp"
#include <map>
@ -22,7 +23,7 @@ using SectorMap = std::map<std::size_t, Sector>;
the segment (counted in bits from the beginning and pointing to the location the disk
had reached upon detection of the ID mark) to sector.
*/
SectorMap sectors_from_segment(const Disk::PCMSegment &&segment, bool is_double_density);
SectorMap sectors_from_segment(const Disk::PCMSegment &segment, Density density);
}

View File

@ -16,9 +16,9 @@ using namespace Storage::Encodings::MFM;
Shifter::Shifter() : owned_crc_generator_(new CRC::CCITT()), crc_generator_(owned_crc_generator_.get()) {}
Shifter::Shifter(CRC::CCITT *crc_generator) : crc_generator_(crc_generator) {}
void Shifter::set_is_double_density(bool is_double_density) {
is_double_density_ = is_double_density;
if(!is_double_density) is_awaiting_marker_value_ = false;
void Shifter::set_is_mfm(bool is_mfm) {
is_mfm_ = is_mfm;
if(!is_mfm) is_awaiting_marker_value_ = false;
}
void Shifter::set_should_obey_syncs(bool should_obey_syncs) {
@ -31,7 +31,7 @@ void Shifter::add_input_bit(int value) {
token_ = Token::None;
if(should_obey_syncs_) {
if(!is_double_density_) {
if(!is_mfm_) {
switch(shift_register_ & 0xffff) {
case Storage::Encodings::MFM::FMIndexAddressMark:
token_ = Token::Index;
@ -100,7 +100,7 @@ void Shifter::add_input_bit(int value) {
token_ = Token::Byte;
bits_since_token_ = 0;
if(is_awaiting_marker_value_ && is_double_density_) {
if(is_awaiting_marker_value_ && is_mfm_) {
is_awaiting_marker_value_ = false;
switch(get_byte()) {
case Storage::Encodings::MFM::IndexAddressByte:

View File

@ -20,7 +20,7 @@ namespace Storage::Encodings::MFM {
a stream of MFM tokens as output. So e.g. it is suitable for use in parsing
the output of a PLL windowing of disk events.
It supports both FM and MFM parsing; see @c set_is_double_density.
It supports both FM and MFM parsing; see @c set_is_mfm.
It will ordinarily honour sync patterns; that should be turned off when within
a sector because false syncs can occur. See @c set_should_obey_syncs.
@ -48,7 +48,7 @@ class Shifter {
Shifter();
Shifter(CRC::CCITT *crc_generator);
void set_is_double_density(bool is_double_density);
void set_is_mfm(bool is_mfm);
void set_should_obey_syncs(bool should_obey_syncs);
void add_input_bit(int bit);
@ -72,7 +72,7 @@ class Shifter {
Token token_ = None;
// Input configuration.
bool is_double_density_ = false;
bool is_mfm_ = false;
std::unique_ptr<CRC::CCITT> owned_crc_generator_;
CRC::CCITT *crc_generator_;

View File

@ -16,7 +16,7 @@
using namespace Storage::Disk::CPM;
std::unique_ptr<Storage::Disk::CPM::Catalogue> Storage::Disk::CPM::GetCatalogue(const std::shared_ptr<Storage::Disk::Disk> &disk, const ParameterBlock &parameters) {
Storage::Encodings::MFM::Parser parser(true, disk);
Storage::Encodings::MFM::Parser parser(Encodings::MFM::Density::Double, disk);
// Assemble the actual bytes of the catalogue.
std::vector<uint8_t> catalogue;

View File

@ -77,7 +77,7 @@ FAT::Directory directory_from(const std::vector<uint8_t> &contents) {
}
std::optional<FAT::Volume> FAT::GetVolume(const std::shared_ptr<Storage::Disk::Disk> &disk) {
Storage::Encodings::MFM::Parser parser(true, disk);
Storage::Encodings::MFM::Parser parser(Storage::Encodings::MFM::Density::Double, disk);
// Grab the boot sector; that'll be enough to establish the volume.
Storage::Encodings::MFM::Sector *const boot_sector = parser.get_sector(0, 0, 1);
@ -143,7 +143,7 @@ std::optional<FAT::Volume> FAT::GetVolume(const std::shared_ptr<Storage::Disk::D
}
std::optional<std::vector<uint8_t>> FAT::GetFile(const std::shared_ptr<Storage::Disk::Disk> &disk, const Volume &volume, const File &file) {
Storage::Encodings::MFM::Parser parser(true, disk);
Storage::Encodings::MFM::Parser parser(Storage::Encodings::MFM::Density::Double, disk);
std::vector<uint8_t> contents;