mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-27 16:31:31 +00:00
Merge pull request #1265 from TomHarte/PCHD
Introduce high-density tracks.
This commit is contained in:
commit
02b2b9d47e
@ -19,10 +19,10 @@ 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);
|
||||
const Storage::Encodings::MFM::Sector *const names = parser.sector(0, 0, 0);
|
||||
const Storage::Encodings::MFM::Sector *const details = parser.sector(0, 0, 1);
|
||||
|
||||
if(!names || !details) return nullptr;
|
||||
if(names->samples.empty() || details->samples.empty()) return nullptr;
|
||||
@ -65,7 +65,7 @@ std::unique_ptr<Catalogue> Analyser::Static::Acorn::GetDFSCatalogue(const std::s
|
||||
uint8_t track = uint8_t(start_sector / 10);
|
||||
start_sector++;
|
||||
|
||||
Storage::Encodings::MFM::Sector *next_sector = parser.get_sector(0, track, sector);
|
||||
const Storage::Encodings::MFM::Sector *next_sector = parser.sector(0, track, sector);
|
||||
if(!next_sector) break;
|
||||
|
||||
long length_from_sector = std::min(data_length, 256l);
|
||||
@ -84,15 +84,15 @@ 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);
|
||||
const Storage::Encodings::MFM::Sector *free_space_map_second_half = parser.sector(0, 0, 1);
|
||||
if(!free_space_map_second_half) return nullptr;
|
||||
|
||||
std::vector<uint8_t> root_directory;
|
||||
root_directory.reserve(5 * 256);
|
||||
for(uint8_t c = 2; c < 7; c++) {
|
||||
const Storage::Encodings::MFM::Sector *const sector = parser.get_sector(0, 0, c);
|
||||
const Storage::Encodings::MFM::Sector *const sector = parser.sector(0, 0, c);
|
||||
if(!sector) return nullptr;
|
||||
root_directory.insert(root_directory.end(), sector->samples[0].begin(), sector->samples[0].end());
|
||||
}
|
||||
@ -166,7 +166,7 @@ std::unique_ptr<Catalogue> Analyser::Static::Acorn::GetADFSCatalogue(const std::
|
||||
|
||||
new_file.data.reserve(size);
|
||||
while(new_file.data.size() < size) {
|
||||
const Storage::Encodings::MFM::Sector *const sector = parser.get_sector(start_sector / (80 * 16), (start_sector / 16) % 80, start_sector % 16);
|
||||
const Storage::Encodings::MFM::Sector *const sector = parser.sector(start_sector / (80 * 16), (start_sector / 16) % 80, start_sector % 16);
|
||||
if(!sector) break;
|
||||
|
||||
const auto length_from_sector = std::min(size - new_file.data.size(), sector->samples[0].size());
|
||||
|
@ -159,8 +159,8 @@ 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::Sector *boot_sector = parser.get_sector(0, 0, 0x41);
|
||||
Storage::Encodings::MFM::Parser parser(Storage::Encodings::MFM::Density::Double, disk);
|
||||
const Storage::Encodings::MFM::Sector *boot_sector = parser.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
|
||||
// this disk was formatted and the filler byte never replaced.
|
||||
|
@ -37,7 +37,7 @@ class CommodoreGCRParser: public Storage::Disk::Controller {
|
||||
|
||||
@returns a sector if one was found; @c nullptr otherwise.
|
||||
*/
|
||||
std::shared_ptr<Sector> get_sector(uint8_t track, uint8_t sector) {
|
||||
std::shared_ptr<Sector> sector(uint8_t track, uint8_t sector) {
|
||||
int difference = int(track) - int(track_);
|
||||
track_ = track;
|
||||
|
||||
@ -182,7 +182,7 @@ std::vector<File> Analyser::Static::Commodore::GetFiles(const std::shared_ptr<St
|
||||
uint8_t next_track = 18;
|
||||
uint8_t next_sector = 1;
|
||||
while(1) {
|
||||
sector = parser.get_sector(next_track, next_sector);
|
||||
sector = parser.sector(next_track, next_sector);
|
||||
if(!sector) break;
|
||||
directory.insert(directory.end(), sector->data.begin(), sector->data.end());
|
||||
next_track = sector->data[0];
|
||||
@ -221,7 +221,7 @@ std::vector<File> Analyser::Static::Commodore::GetFiles(const std::shared_ptr<St
|
||||
|
||||
bool is_first_sector = true;
|
||||
while(next_track) {
|
||||
sector = parser.get_sector(next_track, next_sector);
|
||||
sector = parser.sector(next_track, next_sector);
|
||||
if(!sector) break;
|
||||
|
||||
next_track = sector->data[0];
|
||||
|
@ -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) {
|
||||
|
@ -85,7 +85,7 @@ bool is_microdisc(Storage::Encodings::MFM::Parser &parser) {
|
||||
/*
|
||||
The Microdisc boot sector is sector 2 of track 0 and contains a 23-byte signature.
|
||||
*/
|
||||
Storage::Encodings::MFM::Sector *sector = parser.get_sector(0, 0, 2);
|
||||
const Storage::Encodings::MFM::Sector *sector = parser.sector(0, 0, 2);
|
||||
if(!sector) return false;
|
||||
if(sector->samples.empty()) return false;
|
||||
|
||||
@ -108,7 +108,7 @@ bool is_400_loader(Storage::Encodings::MFM::Parser &parser, uint16_t range_start
|
||||
use disassembly to test for likely matches.
|
||||
*/
|
||||
|
||||
Storage::Encodings::MFM::Sector *sector = parser.get_sector(0, 0, 1);
|
||||
const Storage::Encodings::MFM::Sector *sector = parser.sector(0, 0, 1);
|
||||
if(!sector) return false;
|
||||
if(sector->samples.empty()) return false;
|
||||
|
||||
@ -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;
|
||||
|
@ -33,14 +33,14 @@ 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.
|
||||
Storage::Encodings::MFM::Sector *boot_sector = nullptr;
|
||||
const Storage::Encodings::MFM::Sector *boot_sector = nullptr;
|
||||
uint8_t sector_mask = 0;
|
||||
while(!boot_sector) {
|
||||
boot_sector = parser.get_sector(0, 0, sector_mask + 1);
|
||||
boot_sector = parser.sector(0, 0, sector_mask + 1);
|
||||
sector_mask += 0x40;
|
||||
if(!sector_mask) break;
|
||||
}
|
||||
|
@ -34,8 +34,7 @@
|
||||
#include "../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp"
|
||||
|
||||
#include "../../Storage/Disk/Track/TrackSerialiser.hpp"
|
||||
#include "../../Storage/Disk/Encodings/MFM/Constants.hpp"
|
||||
#include "../../Storage/Disk/Encodings/MFM/SegmentParser.hpp"
|
||||
#include "../../Storage/Disk/Encodings/MFM/Parser.hpp"
|
||||
|
||||
#include "../AudioProducer.hpp"
|
||||
#include "../KeyboardMachine.hpp"
|
||||
@ -150,50 +149,32 @@ class FloppyController {
|
||||
auto target = decoder_.geometry();
|
||||
bool complete = false;
|
||||
while(!complete) {
|
||||
bool found_sector = false;
|
||||
const auto sector = drives_[decoder_.target().drive].sector(target.head, target.sector);
|
||||
|
||||
for(auto &pair: drives_[decoder_.target().drive].sectors(decoder_.target().head)) {
|
||||
// TODO: I suspect that not all these fields are tested for equality.
|
||||
if(
|
||||
(pair.second.address.track == target.cylinder) &&
|
||||
(pair.second.address.sector == target.sector) &&
|
||||
(pair.second.address.side == target.head) &&
|
||||
(pair.second.size == target.size) &&
|
||||
(pair.second.is_deleted == (decoder_.command() == Command::ReadDeletedData))
|
||||
) {
|
||||
found_sector = true;
|
||||
bool wrote_in_full = true;
|
||||
if(sector) {
|
||||
// TODO: I _think_ I'm supposed to validate the rest of the address here?
|
||||
|
||||
for(int c = 0; c < 128 << target.size; c++) {
|
||||
const auto access_result = dma_.write(2, pair.second.samples[0].data()[c]);
|
||||
switch(access_result) {
|
||||
default: break;
|
||||
case AccessResult::NotAccepted:
|
||||
complete = true;
|
||||
wrote_in_full = false;
|
||||
break;
|
||||
case AccessResult::AcceptedWithEOP:
|
||||
complete = true;
|
||||
break;
|
||||
}
|
||||
if(access_result != AccessResult::Accepted) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for(int c = 0; c < 128 << target.size; c++) {
|
||||
const auto access_result = dma_.write(2, sector->samples[0].data()[c]);
|
||||
switch(access_result) {
|
||||
// Default: keep going.
|
||||
default: continue;
|
||||
|
||||
if(!wrote_in_full) {
|
||||
status_.set(Intel::i8272::Status1::OverRun);
|
||||
status_.set(Intel::i8272::Status0::AbnormalTermination);
|
||||
// Anything else: update flags and exit.
|
||||
case AccessResult::NotAccepted:
|
||||
complete = true;
|
||||
status_.set(Intel::i8272::Status1::OverRun);
|
||||
status_.set(Intel::i8272::Status0::AbnormalTermination);
|
||||
break;
|
||||
case AccessResult::AcceptedWithEOP:
|
||||
complete = true;
|
||||
break;
|
||||
}
|
||||
|
||||
++target.sector; // TODO: multitrack?
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!found_sector) {
|
||||
++target.sector; // TODO: multitrack?
|
||||
} else {
|
||||
status_.set(Intel::i8272::Status1::EndOfCylinder);
|
||||
status_.set(Intel::i8272::Status0::AbnormalTermination);
|
||||
break;
|
||||
@ -342,58 +323,19 @@ class FloppyController {
|
||||
bool exists = true;
|
||||
|
||||
bool has_disk() const {
|
||||
return bool(disk);
|
||||
return static_cast<bool>(parser_);
|
||||
}
|
||||
|
||||
void set_disk(std::shared_ptr<Storage::Disk::Disk> image) {
|
||||
disk = image;
|
||||
cached.clear();
|
||||
parser_ = std::make_unique<Storage::Encodings::MFM::Parser>(image);
|
||||
}
|
||||
|
||||
Storage::Encodings::MFM::SectorMap §ors(bool side) {
|
||||
if(cached.track == track && cached.side == side) {
|
||||
return cached.sectors;
|
||||
}
|
||||
|
||||
cached.track = track;
|
||||
cached.side = side;
|
||||
cached.sectors.clear();
|
||||
|
||||
if(!disk) {
|
||||
return cached.sectors;
|
||||
}
|
||||
|
||||
auto raw_track = disk->get_track_at_position(
|
||||
Storage::Disk::Track::Address(
|
||||
side,
|
||||
Storage::Disk::HeadPosition(track)
|
||||
)
|
||||
);
|
||||
if(!raw_track) {
|
||||
return cached.sectors;
|
||||
}
|
||||
|
||||
const bool is_double_density = true; // TODO: use MFM flag here.
|
||||
auto serialisation = Storage::Disk::track_serialisation(
|
||||
*raw_track,
|
||||
is_double_density ? Storage::Encodings::MFM::MFMBitLength : Storage::Encodings::MFM::FMBitLength
|
||||
);
|
||||
cached.sectors = Storage::Encodings::MFM::sectors_from_segment(std::move(serialisation), is_double_density);
|
||||
return cached.sectors;
|
||||
const Storage::Encodings::MFM::Sector *sector(int head, uint8_t sector) {
|
||||
return parser_ ? parser_->sector(head, track, sector) : nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
struct {
|
||||
uint8_t track = 0xff;
|
||||
bool side;
|
||||
Storage::Encodings::MFM::SectorMap sectors;
|
||||
|
||||
void clear() {
|
||||
track = 0xff;
|
||||
sectors.clear();
|
||||
}
|
||||
} cached;
|
||||
std::shared_ptr<Storage::Disk::Disk> disk;
|
||||
std::unique_ptr<Storage::Encodings::MFM::Parser> parser_;
|
||||
|
||||
} drives_[4];
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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() {
|
||||
|
@ -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);
|
||||
|
@ -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() {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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() {
|
||||
|
@ -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.
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -18,6 +18,62 @@
|
||||
|
||||
using namespace Storage::Encodings::MFM;
|
||||
|
||||
namespace {
|
||||
|
||||
std::vector<const Sector *> sector_pointers(const std::vector<Sector> §ors) {
|
||||
std::vector<const Sector *> pointers;
|
||||
for(const Sector §or: sectors) {
|
||||
pointers.push_back(§or);
|
||||
}
|
||||
return pointers;
|
||||
}
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
enum class SurfaceItem {
|
||||
Mark,
|
||||
Data
|
||||
@ -124,7 +180,7 @@ class FMEncoder: public Encoder {
|
||||
};
|
||||
|
||||
template<class T> std::shared_ptr<Storage::Disk::Track>
|
||||
GetTrackWithSectors(
|
||||
GTrackWithSectors(
|
||||
const std::vector<const Sector *> §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 +337,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> TTrackWithSectors(
|
||||
const std::vector<const Sector *> §ors,
|
||||
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> §ors) {
|
||||
std::vector<const Sector *> pointers;
|
||||
for(const Sector §or: sectors) {
|
||||
pointers.push_back(§or);
|
||||
}
|
||||
|
||||
std::shared_ptr<Storage::Disk::Track> Storage::Encodings::MFM::TrackWithSectors(
|
||||
Density density,
|
||||
const std::vector<Sector> §ors,
|
||||
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> Storage::Encodings::MFM::TrackWithSectors(
|
||||
Density density,
|
||||
const std::vector<const Sector *> §ors,
|
||||
std::optional<std::size_t> sector_gap_length,
|
||||
std::optional<uint8_t> sector_gap_filler_byte
|
||||
) {
|
||||
switch(density) {
|
||||
case Density::Single: return TTrackWithSectors<Density::Single>(sectors, sector_gap_length, sector_gap_filler_byte);
|
||||
case Density::Double: return TTrackWithSectors<Density::Double>(sectors, sector_gap_length, sector_gap_filler_byte);
|
||||
case Density::High: return TTrackWithSectors<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> §ors, 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 *> §ors, 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> §ors, 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 *> §ors, 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) {
|
||||
|
@ -11,34 +11,34 @@
|
||||
|
||||
#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;
|
||||
/*!
|
||||
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> §ors, 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 *> §ors, 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> §ors,
|
||||
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> §ors, 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 *> §ors, 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 *> §ors,
|
||||
std::optional<std::size_t> sector_gap_length = std::nullopt,
|
||||
std::optional<uint8_t> sector_gap_filler_byte = std::nullopt);
|
||||
|
||||
class Encoder {
|
||||
public:
|
||||
|
@ -14,40 +14,58 @@
|
||||
|
||||
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(const std::shared_ptr<Storage::Disk::Disk> &disk) :
|
||||
disk_(disk) {}
|
||||
|
||||
void Parser::install_sectors_from_track(const Storage::Disk::Track::Address &address) {
|
||||
Parser::Parser(Density density, const std::shared_ptr<Storage::Disk::Disk> &disk) :
|
||||
disk_(disk), density_(density) {}
|
||||
|
||||
void Parser::install_track(const Storage::Disk::Track::Address &address) {
|
||||
if(sectors_by_address_by_track_.find(address) != sectors_by_address_by_track_.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::shared_ptr<Storage::Disk::Track> track = disk_->get_track_at_position(address);
|
||||
const auto track = disk_->get_track_at_position(address);
|
||||
if(!track) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::map<std::size_t, Sector> sectors = sectors_from_segment(
|
||||
Storage::Disk::track_serialisation(*track, is_mfm_ ? MFMBitLength : FMBitLength),
|
||||
is_mfm_);
|
||||
|
||||
std::map<int, Storage::Encodings::MFM::Sector> sectors_by_id;
|
||||
for(const auto §or : sectors) {
|
||||
sectors_by_id.insert(std::make_pair(sector.second.address.sector, std::move(sector.second)));
|
||||
if(density_) {
|
||||
append(parse_track(*track, *density_), sectors_by_id);
|
||||
return;
|
||||
} else {
|
||||
// Just try all three in succession.
|
||||
append(parse_track(*track, Density::Single), sectors_by_id);
|
||||
append(parse_track(*track, Density::Double), sectors_by_id);
|
||||
append(parse_track(*track, Density::High), sectors_by_id);;
|
||||
}
|
||||
sectors_by_address_by_track_.insert(std::make_pair(address, std::move(sectors_by_id)));
|
||||
|
||||
sectors_by_address_by_track_.emplace(address, std::move(sectors_by_id));
|
||||
}
|
||||
|
||||
Sector *Parser::get_sector(int head, int track, uint8_t sector) {
|
||||
Disk::Track::Address address(head, Storage::Disk::HeadPosition(track));
|
||||
install_sectors_from_track(address);
|
||||
SectorMap Parser::parse_track(const Storage::Disk::Track &track, Density density) {
|
||||
return sectors_from_segment(
|
||||
Storage::Disk::track_serialisation(track, bit_length(density)),
|
||||
density);
|
||||
}
|
||||
|
||||
auto sectors = sectors_by_address_by_track_.find(address);
|
||||
void Parser::append(const SectorMap &source, std::map<int, Sector> &destination) {
|
||||
for(const auto §or : source) {
|
||||
destination.emplace(sector.second.address.sector, std::move(sector.second));
|
||||
}
|
||||
}
|
||||
|
||||
const Sector *Parser::sector(int head, int track, uint8_t sector) {
|
||||
const Disk::Track::Address address(head, Storage::Disk::HeadPosition(track));
|
||||
install_track(address);
|
||||
|
||||
const auto sectors = sectors_by_address_by_track_.find(address);
|
||||
if(sectors == sectors_by_address_by_track_.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto stored_sector = sectors->second.find(sector);
|
||||
const auto stored_sector = sectors->second.find(sector);
|
||||
if(stored_sector == sectors->second.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -9,10 +9,14 @@
|
||||
#ifndef Parser_hpp
|
||||
#define Parser_hpp
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Sector.hpp"
|
||||
#include "SegmentParser.hpp"
|
||||
#include "../../Track/Track.hpp"
|
||||
#include "../../Drive.hpp"
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace Storage::Encodings::MFM {
|
||||
|
||||
/*!
|
||||
@ -20,7 +24,11 @@ namespace Storage::Encodings::MFM {
|
||||
*/
|
||||
class Parser {
|
||||
public:
|
||||
Parser(bool is_mfm, const std::shared_ptr<Storage::Disk::Disk> &disk);
|
||||
/// Creates a parser that will only attempt to interpret the underlying disk as being of @c density.
|
||||
Parser(Density density, const std::shared_ptr<Storage::Disk::Disk> &disk);
|
||||
|
||||
/// Creates a parser that will automatically try all available FM and MFM densities to try to extract sectors.
|
||||
Parser(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
|
||||
@ -28,14 +36,24 @@ class Parser {
|
||||
|
||||
@returns a sector if one was found; @c nullptr otherwise.
|
||||
*/
|
||||
Storage::Encodings::MFM::Sector *get_sector(int head, int track, uint8_t sector);
|
||||
const Storage::Encodings::MFM::Sector *sector(int head, int track, uint8_t sector);
|
||||
|
||||
// TODO: set_sector.
|
||||
|
||||
private:
|
||||
std::shared_ptr<Storage::Disk::Disk> disk_;
|
||||
bool is_mfm_ = true;
|
||||
std::optional<Density> density_;
|
||||
|
||||
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_;
|
||||
void install_track(const Storage::Disk::Track::Address &address);
|
||||
static SectorMap parse_track(const Storage::Disk::Track &track, Density density);
|
||||
static void append(const SectorMap &source, std::map<int, Sector> &destination);
|
||||
|
||||
// Maps from a track address, i.e. head and position, to a map from
|
||||
// sector IDs to sectors.
|
||||
std::map<
|
||||
Storage::Disk::Track::Address,
|
||||
std::map<int, Storage::Encodings::MFM::Sector>
|
||||
> sectors_by_address_by_track_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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_;
|
||||
|
@ -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 ¶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<uint8_t> catalogue;
|
||||
@ -29,7 +29,7 @@ std::unique_ptr<Storage::Disk::CPM::Catalogue> Storage::Disk::CPM::GetCatalogue(
|
||||
if(catalogue_allocation_bitmap & 0x8000) {
|
||||
std::size_t size_read = 0;
|
||||
do {
|
||||
Storage::Encodings::MFM::Sector *sector_contents = parser.get_sector(0, uint8_t(track), uint8_t(parameters.first_sector + sector));
|
||||
const Storage::Encodings::MFM::Sector *sector_contents = parser.sector(0, uint8_t(track), uint8_t(parameters.first_sector + sector));
|
||||
if(!sector_contents || sector_contents->samples.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -138,7 +138,7 @@ std::unique_ptr<Storage::Disk::CPM::Catalogue> Storage::Disk::CPM::GetCatalogue(
|
||||
track = first_sector / parameters.sectors_per_track;
|
||||
|
||||
for(int s = 0; s < sectors_per_block && record < number_of_records; s++) {
|
||||
Storage::Encodings::MFM::Sector *sector_contents = parser.get_sector(0, uint8_t(track), uint8_t(parameters.first_sector + sector));
|
||||
const Storage::Encodings::MFM::Sector *sector_contents = parser.sector(0, uint8_t(track), uint8_t(parameters.first_sector + sector));
|
||||
if(!sector_contents || sector_contents->samples.empty()) break;
|
||||
sector++;
|
||||
if(sector == parameters.sectors_per_track) {
|
||||
|
@ -77,10 +77,10 @@ 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);
|
||||
const Storage::Encodings::MFM::Sector *const boot_sector = parser.sector(0, 0, 1);
|
||||
if(!boot_sector || boot_sector->samples.empty() || boot_sector->samples[0].size() < 512) {
|
||||
return std::nullopt;
|
||||
}
|
||||
@ -108,8 +108,8 @@ std::optional<FAT::Volume> FAT::GetVolume(const std::shared_ptr<Storage::Disk::D
|
||||
const int sector_number = volume.reserved_sectors + c;
|
||||
const auto address = volume.chs_for_sector(sector_number);
|
||||
|
||||
Storage::Encodings::MFM::Sector *const fat_sector =
|
||||
parser.get_sector(address.head, address.cylinder, uint8_t(address.sector));
|
||||
const Storage::Encodings::MFM::Sector *const fat_sector =
|
||||
parser.sector(address.head, address.cylinder, uint8_t(address.sector));
|
||||
if(!fat_sector || fat_sector->samples.empty() || fat_sector->samples[0].size() != volume.bytes_per_sector) {
|
||||
return std::nullopt;
|
||||
}
|
||||
@ -130,8 +130,8 @@ std::optional<FAT::Volume> FAT::GetVolume(const std::shared_ptr<Storage::Disk::D
|
||||
const auto sector_number = int(volume.reserved_sectors + c + volume.sectors_per_fat*volume.fat_copies);
|
||||
const auto address = volume.chs_for_sector(sector_number);
|
||||
|
||||
Storage::Encodings::MFM::Sector *const sector =
|
||||
parser.get_sector(address.head, address.cylinder, uint8_t(address.sector));
|
||||
const Storage::Encodings::MFM::Sector *const sector =
|
||||
parser.sector(address.head, address.cylinder, uint8_t(address.sector));
|
||||
if(!sector || sector->samples.empty() || sector->samples[0].size() != volume.bytes_per_sector) {
|
||||
return std::nullopt;
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -156,8 +156,8 @@ std::optional<std::vector<uint8_t>> FAT::GetFile(const std::shared_ptr<Storage::
|
||||
for(int c = 0; c < volume.sectors_per_cluster; c++) {
|
||||
const auto address = volume.chs_for_sector(sector + c);
|
||||
|
||||
Storage::Encodings::MFM::Sector *const sector_contents =
|
||||
parser.get_sector(address.head, address.cylinder, uint8_t(address.sector));
|
||||
const Storage::Encodings::MFM::Sector *const sector_contents =
|
||||
parser.sector(address.head, address.cylinder, uint8_t(address.sector));
|
||||
if(!sector_contents || sector_contents->samples.empty() || sector_contents->samples[0].size() != volume.bytes_per_sector) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user