mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-26 09:29:45 +00:00
Merge pull request #185 from TomHarte/CPMCatalogue
Introduces a CP/M catalogue parser, as a basic for the CPC's static analyser
This commit is contained in:
commit
f0b7e58968
@ -58,6 +58,7 @@
|
||||
4B3BA0D11D318B44005DD7A7 /* TestMachine6502.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B3BA0CD1D318B44005DD7A7 /* TestMachine6502.mm */; };
|
||||
4B3BF5B01F146265005B6C36 /* CSW.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B3BF5AE1F146264005B6C36 /* CSW.cpp */; };
|
||||
4B3F1B461E0388D200DB26EE /* PCMPatchedTrack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B3F1B441E0388D200DB26EE /* PCMPatchedTrack.cpp */; };
|
||||
4B3FE75E1F3CF68B00448EE4 /* CPM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B3FE75C1F3CF68B00448EE4 /* CPM.cpp */; };
|
||||
4B448E811F1C45A00009ABD6 /* TZX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B448E7F1F1C45A00009ABD6 /* TZX.cpp */; };
|
||||
4B448E841F1C4C480009ABD6 /* PulseQueuedTape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B448E821F1C4C480009ABD6 /* PulseQueuedTape.cpp */; };
|
||||
4B44EBF51DC987AF00A7820C /* AllSuiteA.bin in Resources */ = {isa = PBXBuildFile; fileRef = 4B44EBF41DC987AE00A7820C /* AllSuiteA.bin */; };
|
||||
@ -562,6 +563,8 @@
|
||||
4B3BF5AF1F146264005B6C36 /* CSW.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CSW.hpp; sourceTree = "<group>"; };
|
||||
4B3F1B441E0388D200DB26EE /* PCMPatchedTrack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PCMPatchedTrack.cpp; sourceTree = "<group>"; };
|
||||
4B3F1B451E0388D200DB26EE /* PCMPatchedTrack.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PCMPatchedTrack.hpp; sourceTree = "<group>"; };
|
||||
4B3FE75C1F3CF68B00448EE4 /* CPM.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CPM.cpp; path = Parsers/CPM.cpp; sourceTree = "<group>"; };
|
||||
4B3FE75D1F3CF68B00448EE4 /* CPM.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = CPM.hpp; path = Parsers/CPM.hpp; sourceTree = "<group>"; };
|
||||
4B448E7F1F1C45A00009ABD6 /* TZX.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TZX.cpp; sourceTree = "<group>"; };
|
||||
4B448E801F1C45A00009ABD6 /* TZX.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TZX.hpp; sourceTree = "<group>"; };
|
||||
4B448E821F1C4C480009ABD6 /* PulseQueuedTape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PulseQueuedTape.cpp; sourceTree = "<group>"; };
|
||||
@ -1330,6 +1333,15 @@
|
||||
path = Bridges;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4B3FE75F1F3CF6BA00448EE4 /* Parsers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4B3FE75C1F3CF68B00448EE4 /* CPM.cpp */,
|
||||
4B3FE75D1F3CF68B00448EE4 /* CPM.hpp */,
|
||||
);
|
||||
name = Parsers;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4B4A762D1DB1A35C007AAE2E /* AY38910 */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -1446,12 +1458,12 @@
|
||||
4B69FB3A1C4D908A00B5F0AA /* Tape */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4B448E821F1C4C480009ABD6 /* PulseQueuedTape.cpp */,
|
||||
4B69FB3B1C4D908A00B5F0AA /* Tape.cpp */,
|
||||
4B448E831F1C4C480009ABD6 /* PulseQueuedTape.hpp */,
|
||||
4B69FB3C1C4D908A00B5F0AA /* Tape.hpp */,
|
||||
4B69FB411C4D941400B5F0AA /* Formats */,
|
||||
4B8805F11DCFC9A2003085B1 /* Parsers */,
|
||||
4B448E821F1C4C480009ABD6 /* PulseQueuedTape.cpp */,
|
||||
4B448E831F1C4C480009ABD6 /* PulseQueuedTape.hpp */,
|
||||
4B69FB3B1C4D908A00B5F0AA /* Tape.cpp */,
|
||||
4B69FB3C1C4D908A00B5F0AA /* Tape.hpp */,
|
||||
);
|
||||
path = Tape;
|
||||
sourceTree = "<group>";
|
||||
@ -1545,6 +1557,7 @@
|
||||
4BAB62B71D3302CA00DF5BA0 /* PCMTrack.hpp */,
|
||||
4BB697CF1D4BA44900248BDF /* Encodings */,
|
||||
4BAB62B21D327F7E00DF5BA0 /* Formats */,
|
||||
4B3FE75F1F3CF6BA00448EE4 /* Parsers */,
|
||||
);
|
||||
path = Disk;
|
||||
sourceTree = "<group>";
|
||||
@ -2700,6 +2713,7 @@
|
||||
4B1497921EE4B5A800CE2596 /* ZX8081.cpp in Sources */,
|
||||
4B643F3F1D77B88000D431D6 /* DocumentController.swift in Sources */,
|
||||
4BA799951D8B656E0045123D /* StaticAnalyser.cpp in Sources */,
|
||||
4B3FE75E1F3CF68B00448EE4 /* CPM.cpp in Sources */,
|
||||
4B2BFDB21DAEF5FF001A68B8 /* Video.cpp in Sources */,
|
||||
4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */,
|
||||
4BC3B74F1CD194CC00F86E85 /* Shader.cpp in Sources */,
|
||||
|
@ -19,8 +19,8 @@ std::unique_ptr<Catalogue> StaticAnalyser::Acorn::GetDFSCatalogue(const std::sha
|
||||
std::unique_ptr<Catalogue> catalogue(new Catalogue);
|
||||
Storage::Encodings::MFM::Parser parser(false, disk);
|
||||
|
||||
std::shared_ptr<Storage::Encodings::MFM::Sector> names = parser.get_sector(0, 0);
|
||||
std::shared_ptr<Storage::Encodings::MFM::Sector> details = parser.get_sector(0, 1);
|
||||
std::shared_ptr<Storage::Encodings::MFM::Sector> names = parser.get_sector(0, 0, 0);
|
||||
std::shared_ptr<Storage::Encodings::MFM::Sector> details = parser.get_sector(0, 0, 1);
|
||||
|
||||
if(!names || !details) return nullptr;
|
||||
if(names->data.size() != 256 || details->data.size() != 256) return nullptr;
|
||||
@ -61,7 +61,7 @@ std::unique_ptr<Catalogue> StaticAnalyser::Acorn::GetDFSCatalogue(const std::sha
|
||||
uint8_t track = (uint8_t)(start_sector / 10);
|
||||
start_sector++;
|
||||
|
||||
std::shared_ptr<Storage::Encodings::MFM::Sector> next_sector = parser.get_sector(track, sector);
|
||||
std::shared_ptr<Storage::Encodings::MFM::Sector> next_sector = parser.get_sector(0, track, sector);
|
||||
if(!next_sector) break;
|
||||
|
||||
long length_from_sector = std::min(data_length, 256l);
|
||||
@ -77,13 +77,13 @@ std::unique_ptr<Catalogue> StaticAnalyser::Acorn::GetADFSCatalogue(const std::sh
|
||||
std::unique_ptr<Catalogue> catalogue(new Catalogue);
|
||||
Storage::Encodings::MFM::Parser parser(true, disk);
|
||||
|
||||
std::shared_ptr<Storage::Encodings::MFM::Sector> free_space_map_second_half = parser.get_sector(0, 1);
|
||||
std::shared_ptr<Storage::Encodings::MFM::Sector> free_space_map_second_half = parser.get_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++) {
|
||||
std::shared_ptr<Storage::Encodings::MFM::Sector> sector = parser.get_sector(0, c);
|
||||
std::shared_ptr<Storage::Encodings::MFM::Sector> sector = parser.get_sector(0, 0, c);
|
||||
if(!sector) return nullptr;
|
||||
root_directory.insert(root_directory.end(), sector->data.begin(), sector->data.end());
|
||||
}
|
||||
|
@ -7,6 +7,39 @@
|
||||
//
|
||||
|
||||
#include "StaticAnalyser.hpp"
|
||||
#include "../../Storage/Disk/Parsers/CPM.hpp"
|
||||
|
||||
static void InspectDataCatalogue(
|
||||
const std::unique_ptr<Storage::Disk::CPM::Catalogue> &data_catalogue,
|
||||
StaticAnalyser::Target &target) {
|
||||
// If there's just one file, run that.
|
||||
if(data_catalogue->files.size() == 1) {
|
||||
target.loadingCommand = "run\"" + data_catalogue->files[0].name + "\n";
|
||||
return;
|
||||
}
|
||||
|
||||
// If only one file is [potentially] BASIC, run that one.
|
||||
int basic_files = 0;
|
||||
size_t last_basic_file = 0;
|
||||
for(size_t c = 0; c < data_catalogue->files.size(); c++) {
|
||||
if(!((data_catalogue->files[c].data[18] >> 1) & 7)) {
|
||||
basic_files++;
|
||||
last_basic_file = c;
|
||||
}
|
||||
}
|
||||
if(basic_files == 1) {
|
||||
target.loadingCommand = "run\"" + data_catalogue->files[last_basic_file].name + "\n";
|
||||
return;
|
||||
}
|
||||
|
||||
// Desperation.
|
||||
target.loadingCommand = "cat\n";
|
||||
}
|
||||
|
||||
static void InspectSystemCatalogue(
|
||||
const std::unique_ptr<Storage::Disk::CPM::Catalogue> &data_catalogue,
|
||||
StaticAnalyser::Target &target) {
|
||||
}
|
||||
|
||||
void StaticAnalyser::AmstradCPC::AddTargets(
|
||||
const std::list<std::shared_ptr<Storage::Disk::Disk>> &disks,
|
||||
@ -20,7 +53,39 @@ void StaticAnalyser::AmstradCPC::AddTargets(
|
||||
target.tapes = tapes;
|
||||
target.cartridges = cartridges;
|
||||
|
||||
target.amstradcpc.model = target.disks.empty() ? AmstradCPCModel::CPC464 : AmstradCPCModel::CPC6128;
|
||||
target.amstradcpc.model = AmstradCPCModel::CPC6128;
|
||||
|
||||
if(!target.tapes.empty()) {
|
||||
target.loadingCommand = "|tape\nrun\"\n";
|
||||
}
|
||||
|
||||
if(!target.disks.empty()) {
|
||||
Storage::Disk::CPM::ParameterBlock data_format;
|
||||
data_format.sectors_per_track = 9;
|
||||
data_format.tracks = 40;
|
||||
data_format.block_size = 1024;
|
||||
data_format.first_sector = 0xc1;
|
||||
data_format.catalogue_allocation_bitmap = 0xc000;
|
||||
data_format.reserved_tracks = 0;
|
||||
|
||||
std::unique_ptr<Storage::Disk::CPM::Catalogue> data_catalogue = Storage::Disk::CPM::GetCatalogue(target.disks.front(), data_format);
|
||||
if(data_catalogue) {
|
||||
InspectDataCatalogue(data_catalogue, target);
|
||||
} else {
|
||||
Storage::Disk::CPM::ParameterBlock system_format;
|
||||
data_format.sectors_per_track = 9;
|
||||
data_format.tracks = 40;
|
||||
data_format.block_size = 1024;
|
||||
data_format.first_sector = 0x41;
|
||||
data_format.catalogue_allocation_bitmap = 0xc000;
|
||||
data_format.reserved_tracks = 2;
|
||||
|
||||
std::unique_ptr<Storage::Disk::CPM::Catalogue> system_catalogue = Storage::Disk::CPM::GetCatalogue(target.disks.front(), system_format);
|
||||
if(system_catalogue) {
|
||||
InspectSystemCatalogue(data_catalogue, target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
destination.push_back(target);
|
||||
}
|
||||
|
@ -228,25 +228,26 @@ std::unique_ptr<Encoder> Storage::Encodings::MFM::GetFMEncoder(std::vector<uint8
|
||||
Parser::Parser(bool is_mfm) :
|
||||
Storage::Disk::Controller(4000000, 1, 300),
|
||||
crc_generator_(0x1021, 0xffff),
|
||||
shift_register_(0), track_(0), is_mfm_(is_mfm) {
|
||||
shift_register_(0), is_mfm_(is_mfm),
|
||||
track_(0), head_(0) {
|
||||
Storage::Time bit_length;
|
||||
bit_length.length = 1;
|
||||
bit_length.clock_rate = is_mfm ? 500000 : 250000; // i.e. 250 kbps (including clocks)
|
||||
set_expected_bit_length(bit_length);
|
||||
|
||||
drive.reset(new Storage::Disk::Drive);
|
||||
set_drive(drive);
|
||||
drive_.reset(new Storage::Disk::Drive);
|
||||
set_drive(drive_);
|
||||
set_motor_on(true);
|
||||
}
|
||||
|
||||
Parser::Parser(bool is_mfm, const std::shared_ptr<Storage::Disk::Disk> &disk) :
|
||||
Parser(is_mfm) {
|
||||
drive->set_disk(disk);
|
||||
drive_->set_disk(disk);
|
||||
}
|
||||
|
||||
Parser::Parser(bool is_mfm, const std::shared_ptr<Storage::Disk::Track> &track) :
|
||||
Parser(is_mfm) {
|
||||
drive->set_disk_with_track(track);
|
||||
drive_->set_disk_with_track(track);
|
||||
}
|
||||
|
||||
void Parser::seek_to_track(uint8_t track) {
|
||||
@ -261,7 +262,20 @@ void Parser::seek_to_track(uint8_t track) {
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Sector> Parser::get_sector(uint8_t track, uint8_t sector) {
|
||||
std::shared_ptr<Sector> Parser::get_sector(uint8_t head, uint8_t track, uint8_t sector) {
|
||||
// Check cache for sector.
|
||||
int index = get_index(head, track, sector);
|
||||
auto cached_sector = sectors_by_index_.find(index);
|
||||
if(cached_sector != sectors_by_index_.end()) {
|
||||
return cached_sector->second;
|
||||
}
|
||||
|
||||
// Failing that, set the proper head and track, and search for the sector. get_sector automatically
|
||||
// inserts everything found into sectors_by_index_.
|
||||
if(head_ != head) {
|
||||
drive_->set_head(head);
|
||||
invalidate_track();
|
||||
}
|
||||
seek_to_track(track);
|
||||
return get_sector(sector);
|
||||
}
|
||||
@ -384,8 +398,7 @@ std::vector<uint8_t> Parser::get_track() {
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<Sector> Parser::get_next_sector()
|
||||
{
|
||||
std::shared_ptr<Sector> Parser::get_next_sector() {
|
||||
std::shared_ptr<Sector> sector(new Sector);
|
||||
index_count_ = 0;
|
||||
|
||||
@ -455,6 +468,10 @@ std::shared_ptr<Sector> Parser::get_next_sector()
|
||||
if((data_crc >> 8) != get_next_byte()) continue;
|
||||
if((data_crc & 0xff) != get_next_byte()) continue;
|
||||
|
||||
// Put this sector into the cache.
|
||||
int index = get_index(head_, track_, sector->sector);
|
||||
sectors_by_index_[index] = sector;
|
||||
|
||||
return sector;
|
||||
}
|
||||
|
||||
@ -465,7 +482,7 @@ std::shared_ptr<Sector> Parser::get_sector(uint8_t sector) {
|
||||
std::shared_ptr<Sector> first_sector;
|
||||
index_count_ = 0;
|
||||
while(!first_sector && index_count_ < 2) first_sector = get_next_sector();
|
||||
if(!first_sector) return first_sector;
|
||||
if(!first_sector) return nullptr;
|
||||
if(first_sector->sector == sector) return first_sector;
|
||||
|
||||
while(1) {
|
||||
@ -475,3 +492,7 @@ std::shared_ptr<Sector> Parser::get_sector(uint8_t sector) {
|
||||
if(next_sector->sector == sector) return next_sector;
|
||||
}
|
||||
}
|
||||
|
||||
int Parser::get_index(uint8_t head, uint8_t track, uint8_t sector) {
|
||||
return head | (track << 8) | (sector << 16);
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ class Parser: public Storage::Disk::Controller {
|
||||
|
||||
@returns a sector if one was found; @c nullptr otherwise.
|
||||
*/
|
||||
std::shared_ptr<Storage::Encodings::MFM::Sector> get_sector(uint8_t track, uint8_t sector);
|
||||
std::shared_ptr<Storage::Encodings::MFM::Sector> get_sector(uint8_t head, uint8_t track, uint8_t sector);
|
||||
|
||||
/*!
|
||||
Attempts to read the track at @c track, starting from the index hole.
|
||||
@ -92,10 +92,10 @@ class Parser: public Storage::Disk::Controller {
|
||||
private:
|
||||
Parser(bool is_mfm);
|
||||
|
||||
std::shared_ptr<Storage::Disk::Drive> drive;
|
||||
std::shared_ptr<Storage::Disk::Drive> drive_;
|
||||
unsigned int shift_register_;
|
||||
int index_count_;
|
||||
uint8_t track_;
|
||||
uint8_t track_, head_;
|
||||
int bit_count_;
|
||||
NumberTheory::CRC16 crc_generator_;
|
||||
bool is_mfm_;
|
||||
@ -110,6 +110,9 @@ class Parser: public Storage::Disk::Controller {
|
||||
std::shared_ptr<Storage::Encodings::MFM::Sector> get_next_sector();
|
||||
std::shared_ptr<Storage::Encodings::MFM::Sector> get_sector(uint8_t sector);
|
||||
std::vector<uint8_t> get_track();
|
||||
|
||||
std::map<int, std::shared_ptr<Storage::Encodings::MFM::Sector>> sectors_by_index_;
|
||||
int get_index(uint8_t head, uint8_t track, uint8_t sector);
|
||||
};
|
||||
|
||||
|
||||
|
@ -87,7 +87,7 @@ void AcornADF::store_updated_track_at_position(unsigned int head, unsigned int p
|
||||
std::vector<uint8_t> parsed_track;
|
||||
Storage::Encodings::MFM::Parser parser(true, track);
|
||||
for(unsigned int c = 0; c < sectors_per_track; c++) {
|
||||
std::shared_ptr<Storage::Encodings::MFM::Sector> sector = parser.get_sector((uint8_t)position, (uint8_t)c);
|
||||
std::shared_ptr<Storage::Encodings::MFM::Sector> sector = parser.get_sector(0, (uint8_t)position, (uint8_t)c);
|
||||
if(sector) {
|
||||
parsed_track.insert(parsed_track.end(), sector->data.begin(), sector->data.end());
|
||||
} else {
|
||||
|
@ -81,7 +81,7 @@ void SSD::store_updated_track_at_position(unsigned int head, unsigned int positi
|
||||
std::vector<uint8_t> parsed_track;
|
||||
Storage::Encodings::MFM::Parser parser(false, track);
|
||||
for(unsigned int c = 0; c < 10; c++) {
|
||||
std::shared_ptr<Storage::Encodings::MFM::Sector> sector = parser.get_sector((uint8_t)position, (uint8_t)c);
|
||||
std::shared_ptr<Storage::Encodings::MFM::Sector> sector = parser.get_sector(0, (uint8_t)position, (uint8_t)c);
|
||||
if(sector) {
|
||||
parsed_track.insert(parsed_track.end(), sector->data.begin(), sector->data.end());
|
||||
} else {
|
||||
|
120
Storage/Disk/Parsers/CPM.cpp
Normal file
120
Storage/Disk/Parsers/CPM.cpp
Normal file
@ -0,0 +1,120 @@
|
||||
//
|
||||
// CPM.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 10/08/2017.
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "CPM.hpp"
|
||||
|
||||
#include "../Encodings/MFM.hpp"
|
||||
|
||||
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);
|
||||
|
||||
// Assemble the actual bytes of the catalogue.
|
||||
std::vector<uint8_t> catalogue;
|
||||
size_t sector_size = 1;
|
||||
uint16_t catalogue_allocation_bitmap = parameters.catalogue_allocation_bitmap;
|
||||
if(!catalogue_allocation_bitmap) return nullptr;
|
||||
int sector = 0;
|
||||
int track = parameters.reserved_tracks;
|
||||
while(catalogue_allocation_bitmap) {
|
||||
if(catalogue_allocation_bitmap & 0x8000) {
|
||||
std::shared_ptr<Storage::Encodings::MFM::Sector> sector_contents = parser.get_sector(0, (uint8_t)track, (uint8_t)(parameters.first_sector + sector));
|
||||
if(!sector_contents) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
catalogue.insert(catalogue.end(), sector_contents->data.begin(), sector_contents->data.end());
|
||||
sector_size = sector_contents->data.size();
|
||||
}
|
||||
|
||||
catalogue_allocation_bitmap <<= 1;
|
||||
|
||||
sector++;
|
||||
if(sector == parameters.sectors_per_track) {
|
||||
sector = 0;
|
||||
track++;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Catalogue> result(new Catalogue);
|
||||
bool has_long_allocation_units = (parameters.tracks * parameters.sectors_per_track * (int)sector_size / parameters.block_size) >= 256;
|
||||
size_t bytes_per_catalogue_entry = (has_long_allocation_units ? 16 : 8) * (size_t)parameters.block_size;
|
||||
|
||||
// From the catalogue, create files.
|
||||
std::map<std::vector<uint8_t>, size_t> indices_by_name;
|
||||
File empty_file;
|
||||
for(size_t c = 0; c < catalogue.size(); c += 32) {
|
||||
// Skip this file if it's deleted; this is marked by it having 0xe5 as its user number
|
||||
if(catalogue[c] == 0xe5) continue;
|
||||
|
||||
// Check whether this file has yet been seen; if not then add it to the list
|
||||
std::vector<uint8_t> descriptor;
|
||||
size_t index;
|
||||
descriptor.insert(descriptor.begin(), &catalogue[c], &catalogue[c + 12]);
|
||||
auto iterator = indices_by_name.find(descriptor);
|
||||
if(iterator != indices_by_name.end()) {
|
||||
index = iterator->second;
|
||||
} else {
|
||||
File new_file;
|
||||
new_file.user_number = catalogue[c];
|
||||
for(size_t s = 0; s < 8; s++) new_file.name.push_back((char)catalogue[c + s + 1]);
|
||||
for(size_t s = 0; s < 3; s++) new_file.type.push_back((char)catalogue[c + s + 9] & 0x7f);
|
||||
new_file.read_only = catalogue[c + 9] & 0x80;
|
||||
new_file.system = catalogue[c + 10] & 0x80;
|
||||
index = result->files.size();
|
||||
result->files.push_back(new_file);
|
||||
indices_by_name[descriptor] = index;
|
||||
}
|
||||
|
||||
// figure out where this data needs to be pasted in
|
||||
size_t extent = (size_t)(catalogue[c + 12] + (catalogue[c + 14] << 5));
|
||||
int number_of_records = catalogue[c + 15];
|
||||
|
||||
size_t required_size = extent * bytes_per_catalogue_entry + (size_t)number_of_records * 128;
|
||||
if(result->files[index].data.size() < required_size) {
|
||||
result->files[index].data.resize(required_size);
|
||||
}
|
||||
|
||||
int sectors_per_block = parameters.block_size / (int)sector_size;
|
||||
int records_per_sector = (int)sector_size / 128;
|
||||
int record = 0;
|
||||
for(size_t block = 0; block < (has_long_allocation_units ? 8 : 16) && record < number_of_records; block++) {
|
||||
int block_number;
|
||||
if(has_long_allocation_units) {
|
||||
block_number = catalogue[c + 16 + (block << 1)] + (catalogue[c + 16 + (block << 1) + 1] << 8);
|
||||
} else {
|
||||
block_number = catalogue[c + 16 + block];
|
||||
}
|
||||
if(!block_number) {
|
||||
record += parameters.block_size / 128;
|
||||
continue;
|
||||
}
|
||||
int first_sector = block_number * sectors_per_block;
|
||||
|
||||
sector = first_sector % parameters.sectors_per_track;
|
||||
track = first_sector / parameters.sectors_per_track;
|
||||
|
||||
for(int s = 0; s < sectors_per_block && record < number_of_records; s++) {
|
||||
std::shared_ptr<Storage::Encodings::MFM::Sector> sector_contents = parser.get_sector(0, (uint8_t)track, (uint8_t)(parameters.first_sector + sector));
|
||||
if(!sector_contents) break;
|
||||
sector++;
|
||||
if(sector == parameters.sectors_per_track) {
|
||||
sector = 0;
|
||||
track++;
|
||||
}
|
||||
|
||||
int records_to_copy = std::min(number_of_records - record, records_per_sector);
|
||||
memcpy(&result->files[index].data[extent * bytes_per_catalogue_entry + (size_t)record * 128], sector_contents->data.data(), (size_t)records_to_copy * 128);
|
||||
record += records_to_copy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
51
Storage/Disk/Parsers/CPM.hpp
Normal file
51
Storage/Disk/Parsers/CPM.hpp
Normal file
@ -0,0 +1,51 @@
|
||||
//
|
||||
// CPM.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 10/08/2017.
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Storage_Disk_Parsers_CPM_hpp
|
||||
#define Storage_Disk_Parsers_CPM_hpp
|
||||
|
||||
#include "../Disk.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Storage {
|
||||
namespace Disk {
|
||||
namespace CPM {
|
||||
|
||||
struct ParameterBlock {
|
||||
int sectors_per_track;
|
||||
int tracks;
|
||||
int block_size;
|
||||
int first_sector;
|
||||
uint16_t catalogue_allocation_bitmap;
|
||||
int reserved_tracks;
|
||||
};
|
||||
|
||||
struct File {
|
||||
uint8_t user_number;
|
||||
std::string name;
|
||||
std::string type;
|
||||
bool read_only;
|
||||
bool system;
|
||||
std::vector<uint8_t> data;
|
||||
};
|
||||
|
||||
struct Catalogue {
|
||||
std::vector<File> files;
|
||||
};
|
||||
|
||||
std::unique_ptr<Catalogue> GetCatalogue(const std::shared_ptr<Storage::Disk::Disk> &disk, const ParameterBlock ¶meters);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* Storage_Disk_Parsers_CPM_hpp */
|
Loading…
Reference in New Issue
Block a user