From 6a65c7a52a79bad49c129730efd0545b7249f70f Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 10 Aug 2017 17:10:21 -0400 Subject: [PATCH 1/8] Started working on a CPC-oriented analyser; for now I just want to be able to make a good guess at the appropriate file to load from a disk. As it turns out, the CPC simply adopts the CP/M format, so a generic parser is appropriate. This is its beginning. --- .../Clock Signal.xcodeproj/project.pbxproj | 22 ++++++++-- StaticAnalyser/AmstradCPC/StaticAnalyser.cpp | 13 ++++++ Storage/Disk/Parsers/CPM.cpp | 43 +++++++++++++++++++ Storage/Disk/Parsers/CPM.hpp | 39 +++++++++++++++++ 4 files changed, 113 insertions(+), 4 deletions(-) create mode 100644 Storage/Disk/Parsers/CPM.cpp create mode 100644 Storage/Disk/Parsers/CPM.hpp diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 1522bb3df..263dd9bb3 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -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 = ""; }; 4B3F1B441E0388D200DB26EE /* PCMPatchedTrack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PCMPatchedTrack.cpp; sourceTree = ""; }; 4B3F1B451E0388D200DB26EE /* PCMPatchedTrack.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PCMPatchedTrack.hpp; sourceTree = ""; }; + 4B3FE75C1F3CF68B00448EE4 /* CPM.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CPM.cpp; path = Parsers/CPM.cpp; sourceTree = ""; }; + 4B3FE75D1F3CF68B00448EE4 /* CPM.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = CPM.hpp; path = Parsers/CPM.hpp; sourceTree = ""; }; 4B448E7F1F1C45A00009ABD6 /* TZX.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TZX.cpp; sourceTree = ""; }; 4B448E801F1C45A00009ABD6 /* TZX.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TZX.hpp; sourceTree = ""; }; 4B448E821F1C4C480009ABD6 /* PulseQueuedTape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PulseQueuedTape.cpp; sourceTree = ""; }; @@ -1330,6 +1333,15 @@ path = Bridges; sourceTree = ""; }; + 4B3FE75F1F3CF6BA00448EE4 /* Parsers */ = { + isa = PBXGroup; + children = ( + 4B3FE75C1F3CF68B00448EE4 /* CPM.cpp */, + 4B3FE75D1F3CF68B00448EE4 /* CPM.hpp */, + ); + name = Parsers; + sourceTree = ""; + }; 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 = ""; @@ -1545,6 +1557,7 @@ 4BAB62B71D3302CA00DF5BA0 /* PCMTrack.hpp */, 4BB697CF1D4BA44900248BDF /* Encodings */, 4BAB62B21D327F7E00DF5BA0 /* Formats */, + 4B3FE75F1F3CF6BA00448EE4 /* Parsers */, ); path = Disk; sourceTree = ""; @@ -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 */, diff --git a/StaticAnalyser/AmstradCPC/StaticAnalyser.cpp b/StaticAnalyser/AmstradCPC/StaticAnalyser.cpp index 8e15fdd2b..9f0b2273f 100644 --- a/StaticAnalyser/AmstradCPC/StaticAnalyser.cpp +++ b/StaticAnalyser/AmstradCPC/StaticAnalyser.cpp @@ -7,6 +7,7 @@ // #include "StaticAnalyser.hpp" +#include "../../Storage/Disk/Parsers/CPM.hpp" void StaticAnalyser::AmstradCPC::AddTargets( const std::list> &disks, @@ -22,5 +23,17 @@ void StaticAnalyser::AmstradCPC::AddTargets( target.amstradcpc.model = target.disks.empty() ? AmstradCPCModel::CPC464 : AmstradCPCModel::CPC6128; + if(!target.disks.empty()) { + // This is CPC data format. + Storage::Disk::CPM::ParameterBlock parameters; + parameters.sectors_per_track = 9; + parameters.sector_size = 512; + parameters.first_sector = 0xc1; + parameters.catalogue_allocation_bitmap = 0xc000; + parameters.reserved_tracks = 0; + + Storage::Disk::CPM::GetCatalogue(target.disks.front(), parameters); + } + destination.push_back(target); } diff --git a/Storage/Disk/Parsers/CPM.cpp b/Storage/Disk/Parsers/CPM.cpp new file mode 100644 index 000000000..96302b788 --- /dev/null +++ b/Storage/Disk/Parsers/CPM.cpp @@ -0,0 +1,43 @@ +// +// 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::GetCatalogue(const std::shared_ptr &disk, const ParameterBlock ¶meters) { + Storage::Encodings::MFM::Parser parser(true, disk); + + // Assemble the actual bytes of the catalogue. + std::vector catalogue; + uint16_t catalogue_allocation_bitmap = parameters.catalogue_allocation_bitmap; + int sector = 0; + int track = parameters.reserved_tracks; + while(catalogue_allocation_bitmap) { + if(catalogue_allocation_bitmap & 0x8000) { + std::shared_ptr sector_contents = parser.get_sector((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()); + } + + catalogue_allocation_bitmap <<= 1; + + sector++; + if(sector == parameters.sectors_per_track) { + sector = 0; + track++; + } + } + + return nullptr; +} diff --git a/Storage/Disk/Parsers/CPM.hpp b/Storage/Disk/Parsers/CPM.hpp new file mode 100644 index 000000000..4ee333e98 --- /dev/null +++ b/Storage/Disk/Parsers/CPM.hpp @@ -0,0 +1,39 @@ +// +// 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 + +namespace Storage { +namespace Disk { +namespace CPM { + +struct ParameterBlock { + int sectors_per_track; + int sector_size; + int first_sector; + uint16_t catalogue_allocation_bitmap; + int reserved_tracks; +}; + +struct File { +}; + +struct Catalogue { +}; + +std::unique_ptr GetCatalogue(const std::shared_ptr &disk, const ParameterBlock ¶meters); + +} +} +} + +#endif /* Storage_Disk_Parsers_CPM_hpp */ From 994179f1883b046e709b3e18a4b642cf6b9c5ffb Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 10 Aug 2017 22:33:08 -0400 Subject: [PATCH 2/8] Taking a whole bunch of guesses, this might be correct. --- StaticAnalyser/AmstradCPC/StaticAnalyser.cpp | 3 +- Storage/Disk/Parsers/CPM.cpp | 73 +++++++++++++++++++- Storage/Disk/Parsers/CPM.hpp | 14 +++- 3 files changed, 87 insertions(+), 3 deletions(-) diff --git a/StaticAnalyser/AmstradCPC/StaticAnalyser.cpp b/StaticAnalyser/AmstradCPC/StaticAnalyser.cpp index 9f0b2273f..73ee8930e 100644 --- a/StaticAnalyser/AmstradCPC/StaticAnalyser.cpp +++ b/StaticAnalyser/AmstradCPC/StaticAnalyser.cpp @@ -27,9 +27,10 @@ void StaticAnalyser::AmstradCPC::AddTargets( // This is CPC data format. Storage::Disk::CPM::ParameterBlock parameters; parameters.sectors_per_track = 9; - parameters.sector_size = 512; + parameters.block_size = 1024; parameters.first_sector = 0xc1; parameters.catalogue_allocation_bitmap = 0xc000; + parameters.logic_extents_per_physical = 1; parameters.reserved_tracks = 0; Storage::Disk::CPM::GetCatalogue(target.disks.front(), parameters); diff --git a/Storage/Disk/Parsers/CPM.cpp b/Storage/Disk/Parsers/CPM.cpp index 96302b788..cf340ca18 100644 --- a/Storage/Disk/Parsers/CPM.cpp +++ b/Storage/Disk/Parsers/CPM.cpp @@ -17,7 +17,9 @@ std::unique_ptr Storage::Disk::CPM::GetCatalogue( // Assemble the actual bytes of the catalogue. std::vector 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) { @@ -28,6 +30,7 @@ std::unique_ptr Storage::Disk::CPM::GetCatalogue( } catalogue.insert(catalogue.end(), sector_contents->data.begin(), sector_contents->data.end()); + sector_size = sector_contents->data.size(); } catalogue_allocation_bitmap <<= 1; @@ -39,5 +42,73 @@ std::unique_ptr Storage::Disk::CPM::GetCatalogue( } } - return nullptr; + std::unique_ptr result(new Catalogue); + + // From the catalogue, create files. + std::map, 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 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; + + printf("%s\n", new_file.name.c_str()); + } + + // figure out where this data needs to be pasted in + int extent = catalogue[c + 12] + (catalogue[c + 14] << 5); + int number_of_records = catalogue[c + 15]; + + size_t required_size = (size_t)(extent * 128 + number_of_records) * 128; + if(result->files[index].data.size() < required_size) { + result->files[index].data.resize(required_size); + } + + printf("%d records for extent %d: ", number_of_records, extent); + 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 < 16; block++) { + int block_number = catalogue[c + 16 + block]; + if(!block_number) 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 sector_contents = parser.get_sector((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 * 16384 + record * 128], sector_contents->data.data(), records_to_copy * 128); + record += records_to_copy; + } + } + printf("\n"); + } + + return result; } diff --git a/Storage/Disk/Parsers/CPM.hpp b/Storage/Disk/Parsers/CPM.hpp index 4ee333e98..78f62d5b5 100644 --- a/Storage/Disk/Parsers/CPM.hpp +++ b/Storage/Disk/Parsers/CPM.hpp @@ -10,7 +10,11 @@ #define Storage_Disk_Parsers_CPM_hpp #include "../Disk.hpp" + +#include #include +#include +#include namespace Storage { namespace Disk { @@ -18,16 +22,24 @@ namespace CPM { struct ParameterBlock { int sectors_per_track; - int sector_size; + int block_size; int first_sector; + int logic_extents_per_physical; 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 data; }; struct Catalogue { + std::vector files; }; std::unique_ptr GetCatalogue(const std::shared_ptr &disk, const ParameterBlock ¶meters); From 6be58514846649ed30ef2e69c70fd5ea327d9965 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 10 Aug 2017 22:34:29 -0400 Subject: [PATCH 3/8] Cleaned up. --- Storage/Disk/Parsers/CPM.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Storage/Disk/Parsers/CPM.cpp b/Storage/Disk/Parsers/CPM.cpp index cf340ca18..2ebbe2e1f 100644 --- a/Storage/Disk/Parsers/CPM.cpp +++ b/Storage/Disk/Parsers/CPM.cpp @@ -68,8 +68,6 @@ std::unique_ptr Storage::Disk::CPM::GetCatalogue( index = result->files.size(); result->files.push_back(new_file); indices_by_name[descriptor] = index; - - printf("%s\n", new_file.name.c_str()); } // figure out where this data needs to be pasted in @@ -81,7 +79,6 @@ std::unique_ptr Storage::Disk::CPM::GetCatalogue( result->files[index].data.resize(required_size); } - printf("%d records for extent %d: ", number_of_records, extent); int sectors_per_block = parameters.block_size / (int)sector_size; int records_per_sector = (int)sector_size / 128; int record = 0; @@ -103,11 +100,10 @@ std::unique_ptr Storage::Disk::CPM::GetCatalogue( } int records_to_copy = std::min(number_of_records - record, records_per_sector); - memcpy(&result->files[index].data[extent * 16384 + record * 128], sector_contents->data.data(), records_to_copy * 128); + memcpy(&result->files[index].data[(size_t)(extent * 16384 + record * 128)], sector_contents->data.data(), (size_t)records_to_copy * 128); record += records_to_copy; } } - printf("\n"); } return result; From 734099a956518eb39a07135dca18c1b707d56be0 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 11 Aug 2017 10:29:13 -0400 Subject: [PATCH 4/8] Threw a sector cache into my MFM parser, in an attempt to cut down analysis costs. Also made it aware of multiple heads. --- StaticAnalyser/Acorn/Disk.cpp | 10 ++++---- Storage/Disk/Encodings/MFM.cpp | 39 ++++++++++++++++++++++++------- Storage/Disk/Encodings/MFM.hpp | 9 ++++--- Storage/Disk/Formats/AcornADF.cpp | 2 +- Storage/Disk/Formats/SSD.cpp | 2 +- Storage/Disk/Parsers/CPM.cpp | 4 ++-- 6 files changed, 45 insertions(+), 21 deletions(-) diff --git a/StaticAnalyser/Acorn/Disk.cpp b/StaticAnalyser/Acorn/Disk.cpp index 7a3f9aa6f..8f9245814 100644 --- a/StaticAnalyser/Acorn/Disk.cpp +++ b/StaticAnalyser/Acorn/Disk.cpp @@ -19,8 +19,8 @@ std::unique_ptr StaticAnalyser::Acorn::GetDFSCatalogue(const std::sha std::unique_ptr catalogue(new Catalogue); Storage::Encodings::MFM::Parser parser(false, disk); - std::shared_ptr names = parser.get_sector(0, 0); - std::shared_ptr details = parser.get_sector(0, 1); + std::shared_ptr names = parser.get_sector(0, 0, 0); + std::shared_ptr 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 StaticAnalyser::Acorn::GetDFSCatalogue(const std::sha uint8_t track = (uint8_t)(start_sector / 10); start_sector++; - std::shared_ptr next_sector = parser.get_sector(track, sector); + std::shared_ptr 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 StaticAnalyser::Acorn::GetADFSCatalogue(const std::sh std::unique_ptr catalogue(new Catalogue); Storage::Encodings::MFM::Parser parser(true, disk); - std::shared_ptr free_space_map_second_half = parser.get_sector(0, 1); + std::shared_ptr free_space_map_second_half = parser.get_sector(0, 0, 1); if(!free_space_map_second_half) return nullptr; std::vector root_directory; root_directory.reserve(5 * 256); for(uint8_t c = 2; c < 7; c++) { - std::shared_ptr sector = parser.get_sector(0, c); + std::shared_ptr sector = parser.get_sector(0, 0, c); if(!sector) return nullptr; root_directory.insert(root_directory.end(), sector->data.begin(), sector->data.end()); } diff --git a/Storage/Disk/Encodings/MFM.cpp b/Storage/Disk/Encodings/MFM.cpp index 1aa908e15..1c2f19a4c 100644 --- a/Storage/Disk/Encodings/MFM.cpp +++ b/Storage/Disk/Encodings/MFM.cpp @@ -228,25 +228,26 @@ std::unique_ptr Storage::Encodings::MFM::GetFMEncoder(std::vector &disk) : Parser(is_mfm) { - drive->set_disk(disk); + drive_->set_disk(disk); } Parser::Parser(bool is_mfm, const std::shared_ptr &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 Parser::get_sector(uint8_t track, uint8_t sector) { +std::shared_ptr 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 Parser::get_track() { } -std::shared_ptr Parser::get_next_sector() -{ +std::shared_ptr Parser::get_next_sector() { std::shared_ptr sector(new Sector); index_count_ = 0; @@ -455,6 +468,10 @@ std::shared_ptr 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 Parser::get_sector(uint8_t sector) { std::shared_ptr 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 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); +} diff --git a/Storage/Disk/Encodings/MFM.hpp b/Storage/Disk/Encodings/MFM.hpp index f482c93bd..37a8178f3 100644 --- a/Storage/Disk/Encodings/MFM.hpp +++ b/Storage/Disk/Encodings/MFM.hpp @@ -75,7 +75,7 @@ class Parser: public Storage::Disk::Controller { @returns a sector if one was found; @c nullptr otherwise. */ - std::shared_ptr get_sector(uint8_t track, uint8_t sector); + std::shared_ptr 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 drive; + std::shared_ptr 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 get_next_sector(); std::shared_ptr get_sector(uint8_t sector); std::vector get_track(); + + std::map> sectors_by_index_; + int get_index(uint8_t head, uint8_t track, uint8_t sector); }; diff --git a/Storage/Disk/Formats/AcornADF.cpp b/Storage/Disk/Formats/AcornADF.cpp index 779bc29c0..f4e0664d7 100644 --- a/Storage/Disk/Formats/AcornADF.cpp +++ b/Storage/Disk/Formats/AcornADF.cpp @@ -87,7 +87,7 @@ void AcornADF::store_updated_track_at_position(unsigned int head, unsigned int p std::vector parsed_track; Storage::Encodings::MFM::Parser parser(true, track); for(unsigned int c = 0; c < sectors_per_track; c++) { - std::shared_ptr sector = parser.get_sector((uint8_t)position, (uint8_t)c); + std::shared_ptr 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 { diff --git a/Storage/Disk/Formats/SSD.cpp b/Storage/Disk/Formats/SSD.cpp index 4903784d5..5045e536b 100644 --- a/Storage/Disk/Formats/SSD.cpp +++ b/Storage/Disk/Formats/SSD.cpp @@ -81,7 +81,7 @@ void SSD::store_updated_track_at_position(unsigned int head, unsigned int positi std::vector parsed_track; Storage::Encodings::MFM::Parser parser(false, track); for(unsigned int c = 0; c < 10; c++) { - std::shared_ptr sector = parser.get_sector((uint8_t)position, (uint8_t)c); + std::shared_ptr 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 { diff --git a/Storage/Disk/Parsers/CPM.cpp b/Storage/Disk/Parsers/CPM.cpp index 2ebbe2e1f..749e8387b 100644 --- a/Storage/Disk/Parsers/CPM.cpp +++ b/Storage/Disk/Parsers/CPM.cpp @@ -24,7 +24,7 @@ std::unique_ptr Storage::Disk::CPM::GetCatalogue( int track = parameters.reserved_tracks; while(catalogue_allocation_bitmap) { if(catalogue_allocation_bitmap & 0x8000) { - std::shared_ptr sector_contents = parser.get_sector((uint8_t)track, (uint8_t)(parameters.first_sector + sector)); + std::shared_ptr sector_contents = parser.get_sector(0, (uint8_t)track, (uint8_t)(parameters.first_sector + sector)); if(!sector_contents) { return nullptr; } @@ -91,7 +91,7 @@ std::unique_ptr Storage::Disk::CPM::GetCatalogue( track = first_sector / parameters.sectors_per_track; for(int s = 0; s < sectors_per_block && record < number_of_records; s++) { - std::shared_ptr sector_contents = parser.get_sector((uint8_t)track, (uint8_t)(parameters.first_sector + sector)); + std::shared_ptr 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) { From 026101a2688a435f7785ae84c2f413c715365cd9 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 11 Aug 2017 10:46:50 -0400 Subject: [PATCH 5/8] Killed logic_extents_per_physical, since I don't know how to handle it, and instituted tracks, to allow a decision about short versus long allocation units. --- Storage/Disk/Parsers/CPM.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Storage/Disk/Parsers/CPM.hpp b/Storage/Disk/Parsers/CPM.hpp index 78f62d5b5..a51c8b5cc 100644 --- a/Storage/Disk/Parsers/CPM.hpp +++ b/Storage/Disk/Parsers/CPM.hpp @@ -22,9 +22,9 @@ namespace CPM { struct ParameterBlock { int sectors_per_track; + int tracks; int block_size; int first_sector; - int logic_extents_per_physical; uint16_t catalogue_allocation_bitmap; int reserved_tracks; }; From 388dd997624d78a263f66e188aa585863be9264c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 11 Aug 2017 10:47:12 -0400 Subject: [PATCH 6/8] Advanced this just enough to suggest a loading command for most things. --- StaticAnalyser/AmstradCPC/StaticAnalyser.cpp | 68 +++++++++++++++++--- 1 file changed, 58 insertions(+), 10 deletions(-) diff --git a/StaticAnalyser/AmstradCPC/StaticAnalyser.cpp b/StaticAnalyser/AmstradCPC/StaticAnalyser.cpp index 73ee8930e..0a49946a2 100644 --- a/StaticAnalyser/AmstradCPC/StaticAnalyser.cpp +++ b/StaticAnalyser/AmstradCPC/StaticAnalyser.cpp @@ -9,6 +9,35 @@ #include "StaticAnalyser.hpp" #include "../../Storage/Disk/Parsers/CPM.hpp" +static void InspectDataCatalogue( + const std::unique_ptr &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; + } +} + +static void InspectSystemCatalogue( + const std::unique_ptr &data_catalogue, + StaticAnalyser::Target &target) { +} + void StaticAnalyser::AmstradCPC::AddTargets( const std::list> &disks, const std::list> &tapes, @@ -21,19 +50,38 @@ 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()) { - // This is CPC data format. - Storage::Disk::CPM::ParameterBlock parameters; - parameters.sectors_per_track = 9; - parameters.block_size = 1024; - parameters.first_sector = 0xc1; - parameters.catalogue_allocation_bitmap = 0xc000; - parameters.logic_extents_per_physical = 1; - parameters.reserved_tracks = 0; + 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; - Storage::Disk::CPM::GetCatalogue(target.disks.front(), parameters); + std::unique_ptr 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 system_catalogue = Storage::Disk::CPM::GetCatalogue(target.disks.front(), system_format); + if(system_catalogue) { + InspectSystemCatalogue(data_catalogue, target); + } + } } destination.push_back(target); From dea782cff9dbe5096e82b71fc4caa09e1607fb83 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 11 Aug 2017 10:47:45 -0400 Subject: [PATCH 7/8] Added a "yeah, I don't know" fallback. --- StaticAnalyser/AmstradCPC/StaticAnalyser.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/StaticAnalyser/AmstradCPC/StaticAnalyser.cpp b/StaticAnalyser/AmstradCPC/StaticAnalyser.cpp index 0a49946a2..91059e263 100644 --- a/StaticAnalyser/AmstradCPC/StaticAnalyser.cpp +++ b/StaticAnalyser/AmstradCPC/StaticAnalyser.cpp @@ -31,6 +31,9 @@ static void InspectDataCatalogue( target.loadingCommand = "run\"" + data_catalogue->files[last_basic_file].name + "\n"; return; } + + // Desperation. + target.loadingCommand = "cat\n"; } static void InspectSystemCatalogue( From 0411b51582b772dc6ba9ce87b0b5541651d667da Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 11 Aug 2017 10:59:37 -0400 Subject: [PATCH 8/8] Added an attempt to deal with 16-bit allocation units, and to ensure middle-of-file holes are respected. --- Storage/Disk/Parsers/CPM.cpp | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/Storage/Disk/Parsers/CPM.cpp b/Storage/Disk/Parsers/CPM.cpp index 749e8387b..eaf737404 100644 --- a/Storage/Disk/Parsers/CPM.cpp +++ b/Storage/Disk/Parsers/CPM.cpp @@ -43,6 +43,8 @@ std::unique_ptr Storage::Disk::CPM::GetCatalogue( } std::unique_ptr 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, size_t> indices_by_name; @@ -71,10 +73,10 @@ std::unique_ptr Storage::Disk::CPM::GetCatalogue( } // figure out where this data needs to be pasted in - int extent = catalogue[c + 12] + (catalogue[c + 14] << 5); + size_t extent = (size_t)(catalogue[c + 12] + (catalogue[c + 14] << 5)); int number_of_records = catalogue[c + 15]; - size_t required_size = (size_t)(extent * 128 + number_of_records) * 128; + 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); } @@ -82,9 +84,17 @@ std::unique_ptr Storage::Disk::CPM::GetCatalogue( 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 < 16; block++) { - int block_number = catalogue[c + 16 + block]; - if(!block_number) continue; + 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; @@ -100,7 +110,7 @@ std::unique_ptr Storage::Disk::CPM::GetCatalogue( } int records_to_copy = std::min(number_of_records - record, records_per_sector); - memcpy(&result->files[index].data[(size_t)(extent * 16384 + record * 128)], sector_contents->data.data(), (size_t)records_to_copy * 128); + 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; } }