diff --git a/Analyser/Static/Acorn/Disk.cpp b/Analyser/Static/Acorn/Disk.cpp index 220f6d5c9..15d8518d5 100644 --- a/Analyser/Static/Acorn/Disk.cpp +++ b/Analyser/Static/Acorn/Disk.cpp @@ -19,7 +19,7 @@ using namespace Analyser::Static::Acorn; std::unique_ptr Analyser::Static::Acorn::GetDFSCatalogue(const std::shared_ptr &disk) { // c.f. http://beebwiki.mdfs.net/Acorn_DFS_disc_format auto catalogue = std::make_unique(); - 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 Analyser::Static::Acorn::GetDFSCatalogue(const std::s */ std::unique_ptr Analyser::Static::Acorn::GetADFSCatalogue(const std::shared_ptr &disk) { auto catalogue = std::make_unique(); - 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; diff --git a/Analyser/Static/AmstradCPC/StaticAnalyser.cpp b/Analyser/Static/AmstradCPC/StaticAnalyser.cpp index 2ca6924b6..a630135b7 100644 --- a/Analyser/Static/AmstradCPC/StaticAnalyser.cpp +++ b/Analyser/Static/AmstradCPC/StaticAnalyser.cpp @@ -159,7 +159,7 @@ void InspectCatalogue( } bool CheckBootSector(const std::shared_ptr &disk, const std::unique_ptr &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 diff --git a/Analyser/Static/FAT12/StaticAnalyser.cpp b/Analyser/Static/FAT12/StaticAnalyser.cpp index 241705e33..dfca8053e 100644 --- a/Analyser/Static/FAT12/StaticAnalyser.cpp +++ b/Analyser/Static/FAT12/StaticAnalyser.cpp @@ -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) { diff --git a/Analyser/Static/Oric/StaticAnalyser.cpp b/Analyser/Static/Oric/StaticAnalyser.cpp index 2077ec265..e67bf4a8f 100644 --- a/Analyser/Static/Oric/StaticAnalyser.cpp +++ b/Analyser/Static/Oric/StaticAnalyser.cpp @@ -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; diff --git a/Analyser/Static/ZXSpectrum/StaticAnalyser.cpp b/Analyser/Static/ZXSpectrum/StaticAnalyser.cpp index 0c21161e8..27ce0d2f8 100644 --- a/Analyser/Static/ZXSpectrum/StaticAnalyser.cpp +++ b/Analyser/Static/ZXSpectrum/StaticAnalyser.cpp @@ -33,7 +33,7 @@ bool IsSpectrumTape(const std::shared_ptr &tape) { } bool IsSpectrumDisk(const std::shared_ptr &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. diff --git a/Machines/PCCompatible/PCCompatible.cpp b/Machines/PCCompatible/PCCompatible.cpp index fa2c5221d..9a335b785 100644 --- a/Machines/PCCompatible/PCCompatible.cpp +++ b/Machines/PCCompatible/PCCompatible.cpp @@ -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; } diff --git a/Storage/Disk/Controller/MFMDiskController.cpp b/Storage/Disk/Controller/MFMDiskController.cpp index ef3122291..c6d57a8c0 100644 --- a/Storage/Disk/Controller/MFMDiskController.cpp +++ b/Storage/Disk/Controller/MFMDiskController.cpp @@ -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() { diff --git a/Storage/Disk/DiskImage/Formats/AcornADF.cpp b/Storage/Disk/DiskImage/Formats/AcornADF.cpp index b7c4fd2d3..57e333c19 100644 --- a/Storage/Disk/DiskImage/Formats/AcornADF.cpp +++ b/Storage/Disk/DiskImage/Formats/AcornADF.cpp @@ -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() { diff --git a/Storage/Disk/DiskImage/Formats/CPCDSK.cpp b/Storage/Disk/DiskImage/Formats/CPCDSK.cpp index ac81e5556..28a0b9c41 100644 --- a/Storage/Disk/DiskImage/Formats/CPCDSK.cpp +++ b/Storage/Disk/DiskImage/Formats/CPCDSK.cpp @@ -214,7 +214,11 @@ std::shared_ptr 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 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); diff --git a/Storage/Disk/DiskImage/Formats/FAT12.cpp b/Storage/Disk/DiskImage/Formats/FAT12.cpp index 574e61c96..fc2d8b6db 100644 --- a/Storage/Disk/DiskImage/Formats/FAT12.cpp +++ b/Storage/Disk/DiskImage/Formats/FAT12.cpp @@ -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() { diff --git a/Storage/Disk/DiskImage/Formats/IMD.cpp b/Storage/Disk/DiskImage/Formats/IMD.cpp index 379fddaf5..b94435a9a 100644 --- a/Storage/Disk/DiskImage/Formats/IMD.cpp +++ b/Storage/Disk/DiskImage/Formats/IMD.cpp @@ -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); + } } diff --git a/Storage/Disk/DiskImage/Formats/MFMSectorDump.cpp b/Storage/Disk/DiskImage/Formats/MFMSectorDump.cpp index 9f629b986..f0ce69d27 100644 --- a/Storage/Disk/DiskImage/Formats/MFMSectorDump.cpp +++ b/Storage/Disk/DiskImage/Formats/MFMSectorDump.cpp @@ -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 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> &tracks) { @@ -44,7 +51,13 @@ void MFMSectorDump::set_tracks(const std::map @@ -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; }; diff --git a/Storage/Disk/DiskImage/Formats/MSA.cpp b/Storage/Disk/DiskImage/Formats/MSA.cpp index 2da697605..a2917d471 100644 --- a/Storage/Disk/DiskImage/Formats/MSA.cpp +++ b/Storage/Disk/DiskImage/Formats/MSA.cpp @@ -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() { diff --git a/Storage/Disk/DiskImage/Formats/OricMFMDSK.cpp b/Storage/Disk/DiskImage/Formats/OricMFMDSK.cpp index f7bd33744..9e4ec6060 100644 --- a/Storage/Disk/DiskImage/Formats/OricMFMDSK.cpp +++ b/Storage/Disk/DiskImage/Formats/OricMFMDSK.cpp @@ -119,7 +119,7 @@ void OricMFMDSK::set_tracks(const std::map parsed_track; int size = 0; diff --git a/Storage/Disk/DiskImage/Formats/PCBooter.cpp b/Storage/Disk/DiskImage/Formats/PCBooter.cpp index 54e14a8ea..20701f572 100644 --- a/Storage/Disk/DiskImage/Formats/PCBooter.cpp +++ b/Storage/Disk/DiskImage/Formats/PCBooter.cpp @@ -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, diff --git a/Storage/Disk/DiskImage/Formats/SSD.cpp b/Storage/Disk/DiskImage/Formats/SSD.cpp index 455fddc4a..b312e6ac4 100644 --- a/Storage/Disk/DiskImage/Formats/SSD.cpp +++ b/Storage/Disk/DiskImage/Formats/SSD.cpp @@ -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() { diff --git a/Storage/Disk/DiskImage/Formats/STX.cpp b/Storage/Disk/DiskImage/Formats/STX.cpp index 6aafc04fd..4c26038ed 100644 --- a/Storage/Disk/DiskImage/Formats/STX.cpp +++ b/Storage/Disk/DiskImage/Formats/STX.cpp @@ -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. diff --git a/Storage/Disk/DiskImage/Formats/Utility/ImplicitSectors.cpp b/Storage/Disk/DiskImage/Formats/Utility/ImplicitSectors.cpp index fa6993e74..b18242a5b 100644 --- a/Storage/Disk/DiskImage/Formats/Utility/ImplicitSectors.cpp +++ b/Storage/Disk/DiskImage/Formats/Utility/ImplicitSectors.cpp @@ -18,7 +18,15 @@ using namespace Storage::Disk; -std::shared_ptr 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 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 sectors; size_t byte_size = size_t(128 << size); @@ -39,17 +47,27 @@ std::shared_ptr 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 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) { diff --git a/Storage/Disk/DiskImage/Formats/Utility/ImplicitSectors.hpp b/Storage/Disk/DiskImage/Formats/Utility/ImplicitSectors.hpp index 3714837d8..d6db555c5 100644 --- a/Storage/Disk/DiskImage/Formats/Utility/ImplicitSectors.hpp +++ b/Storage/Disk/DiskImage/Formats/Utility/ImplicitSectors.hpp @@ -13,10 +13,13 @@ #include #include +#include "../../../Encodings/MFM/Encoder.hpp" + + namespace Storage::Disk { -std::shared_ptr 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_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); } diff --git a/Storage/Disk/Encodings/MFM/Constants.hpp b/Storage/Disk/Encodings/MFM/Constants.hpp index 011af4dc9..838e7d34e 100644 --- a/Storage/Disk/Encodings/MFM/Constants.hpp +++ b/Storage/Disk/Encodings/MFM/Constants.hpp @@ -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 */ diff --git a/Storage/Disk/Encodings/MFM/Encoder.cpp b/Storage/Disk/Encodings/MFM/Encoder.cpp index fbd5bbf41..cd5e6fba4 100644 --- a/Storage/Disk/Encodings/MFM/Encoder.cpp +++ b/Storage/Disk/Encodings/MFM/Encoder.cpp @@ -18,6 +18,18 @@ using namespace Storage::Encodings::MFM; +namespace { + +std::vector sector_pointers(const std::vector §ors) { + std::vector pointers; + for(const Sector §or: sectors) { + pointers.push_back(§or); + } + return pointers; +} + +} + enum class SurfaceItem { Mark, Data @@ -124,7 +136,7 @@ class FMEncoder: public Encoder { }; template std::shared_ptr - GetTrackWithSectors( + GTrackWithSectors( const std::vector §ors, 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::max(); +namespace { +template +std::shared_ptr TrackWithSectors( + const std::vector §ors, + std::optional sector_gap_length, + std::optional sector_gap_filler_byte +) { + using EncoderT = std::conditional_t; + return GTrackWithSectors( + sectors, + Defaults::post_index_address_mark_bytes, + Defaults::post_index_address_mark_value, + Defaults::pre_address_mark_bytes, + Defaults::post_address_address_mark_bytes, + sector_gap_filler_byte ? *sector_gap_filler_byte : Defaults::post_address_address_mark_value, + Defaults::pre_data_mark_bytes, + sector_gap_length ? *sector_gap_length : Defaults::post_data_bytes, + Defaults::post_data_value, + Defaults::expected_track_bytes + ); +} -static std::vector sector_pointers(const std::vector §ors) { - std::vector pointers; - for(const Sector §or: sectors) { - pointers.push_back(§or); +} + +std::shared_ptr TrackWithSectors( + Density density, + const std::vector §ors, + std::optional sector_gap_length, + std::optional sector_gap_filler_byte +) { + return TrackWithSectors( + density, + sector_pointers(sectors), + sector_gap_length, + sector_gap_filler_byte + ); +} + +std::shared_ptr TrackWithSectors( + Density density, + const std::vector §ors, + std::optional sector_gap_length, + std::optional sector_gap_filler_byte +) { + switch(density) { + case Density::Single: return TrackWithSectors(sectors, sector_gap_length, sector_gap_filler_byte); + case Density::Double: return TrackWithSectors(sectors, sector_gap_length, sector_gap_filler_byte); + case Density::High: return TrackWithSectors(sectors, sector_gap_length, sector_gap_filler_byte); } - return pointers; -} - -std::shared_ptr Storage::Encodings::MFM::GetFMTrackWithSectors(const std::vector §ors, std::size_t sector_gap_length, uint8_t sector_gap_filler_byte) { - return GetTrackWithSectors( - 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::Encodings::MFM::GetFMTrackWithSectors(const std::vector §ors, std::size_t sector_gap_length, uint8_t sector_gap_filler_byte) { - return GetTrackWithSectors( - 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::Encodings::MFM::GetMFMTrackWithSectors(const std::vector §ors, std::size_t sector_gap_length, uint8_t sector_gap_filler_byte) { - return GetTrackWithSectors( - 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::Encodings::MFM::GetMFMTrackWithSectors(const std::vector §ors, std::size_t sector_gap_length, uint8_t sector_gap_filler_byte) { - return GetTrackWithSectors( - 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 Storage::Encodings::MFM::GetMFMEncoder(std::vector &target, std::vector *fuzzy_target) { diff --git a/Storage/Disk/Encodings/MFM/Encoder.hpp b/Storage/Disk/Encodings/MFM/Encoder.hpp index babec8b35..2af653192 100644 --- a/Storage/Disk/Encodings/MFM/Encoder.hpp +++ b/Storage/Disk/Encodings/MFM/Encoder.hpp @@ -11,34 +11,78 @@ #include #include +#include #include +#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 struct Defaults; +template <> struct Defaults { + 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 { + 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 { + 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 GetMFMTrackWithSectors(const std::vector §ors, std::size_t sector_gap_length = DefaultSectorGapLength, uint8_t sector_gap_filler_byte = 0x4e); -std::shared_ptr GetMFMTrackWithSectors(const std::vector §ors, std::size_t sector_gap_length = DefaultSectorGapLength, uint8_t sector_gap_filler_byte = 0x4e); +std::shared_ptr TrackWithSectors( + Density density, + const std::vector §ors, + std::optional sector_gap_length = std::nullopt, + std::optional 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 GetFMTrackWithSectors(const std::vector §ors, std::size_t sector_gap_length = DefaultSectorGapLength, uint8_t sector_gap_filler_byte = 0xff); -std::shared_ptr GetFMTrackWithSectors(const std::vector §ors, std::size_t sector_gap_length = DefaultSectorGapLength, uint8_t sector_gap_filler_byte = 0xff); +std::shared_ptr TrackWithSectors( + Density density, + const std::vector §ors, + std::optional sector_gap_length = std::nullopt, + std::optional sector_gap_filler_byte = std::nullopt); class Encoder { public: diff --git a/Storage/Disk/Encodings/MFM/Parser.cpp b/Storage/Disk/Encodings/MFM/Parser.cpp index 79907f203..10349baef 100644 --- a/Storage/Disk/Encodings/MFM/Parser.cpp +++ b/Storage/Disk/Encodings/MFM/Parser.cpp @@ -14,8 +14,8 @@ using namespace Storage::Encodings::MFM; -Parser::Parser(bool is_mfm, const std::shared_ptr &disk) : - disk_(disk), is_mfm_(is_mfm) {} +Parser::Parser(Density density, const std::shared_ptr &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 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 sectors_by_id; for(const auto §or : sectors) { diff --git a/Storage/Disk/Encodings/MFM/Parser.hpp b/Storage/Disk/Encodings/MFM/Parser.hpp index cdf0dd034..4971459c8 100644 --- a/Storage/Disk/Encodings/MFM/Parser.hpp +++ b/Storage/Disk/Encodings/MFM/Parser.hpp @@ -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 &disk); + Parser(Density density, const std::shared_ptr &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 disk_; - bool is_mfm_ = true; + Density density_ = Density::Double; void install_sectors_from_track(const Storage::Disk::Track::Address &address); std::map> sectors_by_address_by_track_; diff --git a/Storage/Disk/Encodings/MFM/SegmentParser.cpp b/Storage/Disk/Encodings/MFM/SegmentParser.cpp index daf64abed..86a69f0e2 100644 --- a/Storage/Disk/Encodings/MFM/SegmentParser.cpp +++ b/Storage/Disk/Encodings/MFM/SegmentParser.cpp @@ -11,10 +11,13 @@ using namespace Storage::Encodings::MFM; -std::map Storage::Encodings::MFM::sectors_from_segment(const Storage::Disk::PCMSegment &&segment, bool is_double_density) { +std::map Storage::Encodings::MFM::sectors_from_segment( + const Storage::Disk::PCMSegment &segment, + Density density +) { std::map 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 new_sector; diff --git a/Storage/Disk/Encodings/MFM/SegmentParser.hpp b/Storage/Disk/Encodings/MFM/SegmentParser.hpp index 9fc1c8d24..587aa45e4 100644 --- a/Storage/Disk/Encodings/MFM/SegmentParser.hpp +++ b/Storage/Disk/Encodings/MFM/SegmentParser.hpp @@ -9,6 +9,7 @@ #ifndef SegmentParser_hpp #define SegmentParser_hpp +#include "Constants.hpp" #include "Sector.hpp" #include "../../Track/PCMSegment.hpp" #include @@ -22,7 +23,7 @@ using SectorMap = std::map; 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); } diff --git a/Storage/Disk/Encodings/MFM/Shifter.cpp b/Storage/Disk/Encodings/MFM/Shifter.cpp index 6790031aa..ba468c606 100644 --- a/Storage/Disk/Encodings/MFM/Shifter.cpp +++ b/Storage/Disk/Encodings/MFM/Shifter.cpp @@ -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: diff --git a/Storage/Disk/Encodings/MFM/Shifter.hpp b/Storage/Disk/Encodings/MFM/Shifter.hpp index a53689652..e5db207c1 100644 --- a/Storage/Disk/Encodings/MFM/Shifter.hpp +++ b/Storage/Disk/Encodings/MFM/Shifter.hpp @@ -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 owned_crc_generator_; CRC::CCITT *crc_generator_; diff --git a/Storage/Disk/Parsers/CPM.cpp b/Storage/Disk/Parsers/CPM.cpp index 23e5ab3f9..2b47f992b 100644 --- a/Storage/Disk/Parsers/CPM.cpp +++ b/Storage/Disk/Parsers/CPM.cpp @@ -16,7 +16,7 @@ using namespace Storage::Disk::CPM; std::unique_ptr Storage::Disk::CPM::GetCatalogue(const std::shared_ptr &disk, const ParameterBlock ¶meters) { - 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 catalogue; diff --git a/Storage/Disk/Parsers/FAT.cpp b/Storage/Disk/Parsers/FAT.cpp index 528c73248..7b74216dd 100644 --- a/Storage/Disk/Parsers/FAT.cpp +++ b/Storage/Disk/Parsers/FAT.cpp @@ -77,7 +77,7 @@ FAT::Directory directory_from(const std::vector &contents) { } std::optional FAT::GetVolume(const std::shared_ptr &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::GetVolume(const std::shared_ptr> FAT::GetFile(const std::shared_ptr &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 contents;