Skip to content

Commit

Permalink
Merge pull request #1265 from TomHarte/PCHD
Browse files Browse the repository at this point in the history
Introduce high-density tracks.
  • Loading branch information
TomHarte authored Dec 12, 2023
2 parents 50b4132 + d523242 commit 02b2b9d
Show file tree
Hide file tree
Showing 32 changed files with 340 additions and 237 deletions.
16 changes: 8 additions & 8 deletions Analyser/Static/Acorn/Disk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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());
}
Expand Down Expand Up @@ -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());
Expand Down
4 changes: 2 additions & 2 deletions Analyser/Static/AmstradCPC/StaticAnalyser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 3 additions & 3 deletions Analyser/Static/Commodore/Disk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -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];
Expand Down
2 changes: 1 addition & 1 deletion Analyser/Static/FAT12/StaticAnalyser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
6 changes: 3 additions & 3 deletions Analyser/Static/Oric/StaticAnalyser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

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

Expand Down Expand Up @@ -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;
Expand Down
6 changes: 3 additions & 3 deletions Analyser/Static/ZXSpectrum/StaticAnalyser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
112 changes: 27 additions & 85 deletions Machines/PCCompatible/PCCompatible.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -150,50 +149,32 @@ class FloppyController {
auto target = decoder_.geometry();
bool complete = false;
while(!complete) {
bool found_sector = false;

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;

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

if(!wrote_in_full) {
status_.set(Intel::i8272::Status1::OverRun);
status_.set(Intel::i8272::Status0::AbnormalTermination);
const auto sector = drives_[decoder_.target().drive].sector(target.head, target.sector);

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, sector->samples[0].data()[c]);
switch(access_result) {
// Default: keep going.
default: continue;

// 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;
Expand Down Expand Up @@ -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 &sectors(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];

Expand Down
2 changes: 1 addition & 1 deletion Storage/Disk/Controller/MFMDiskController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
2 changes: 1 addition & 1 deletion Storage/Disk/DiskImage/Formats/AcornADF.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
8 changes: 6 additions & 2 deletions Storage/Disk/DiskImage/Formats/CPCDSK.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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);
Expand Down
7 changes: 6 additions & 1 deletion Storage/Disk/DiskImage/Formats/FAT12.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
12 changes: 9 additions & 3 deletions Storage/Disk/DiskImage/Formats/IMD.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Loading

0 comments on commit 02b2b9d

Please sign in to comment.