diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index f9b4aa0f6..659c4f7d0 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -350,6 +350,7 @@ 4BD14B111D74627C0088EAD6 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD14B0F1D74627C0088EAD6 /* StaticAnalyser.cpp */; }; 4BD468F71D8DF41D0084958B /* 1770.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD468F51D8DF41D0084958B /* 1770.cpp */; }; 4BD5F1951D13528900631CD1 /* CSBestEffortUpdater.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.m */; }; + 4BD69F941D98760000243FE1 /* AcornADF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD69F921D98760000243FE1 /* AcornADF.cpp */; }; 4BE77A2E1D84ADFB00BC3827 /* File.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE77A2C1D84ADFB00BC3827 /* File.cpp */; }; 4BEE0A6F1D72496600532C7B /* Cartridge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE0A6A1D72496600532C7B /* Cartridge.cpp */; }; 4BEE0A701D72496600532C7B /* PRG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE0A6D1D72496600532C7B /* PRG.cpp */; }; @@ -802,6 +803,8 @@ 4BD468F61D8DF41D0084958B /* 1770.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = 1770.hpp; path = 1770/1770.hpp; sourceTree = ""; }; 4BD5F1931D13528900631CD1 /* CSBestEffortUpdater.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSBestEffortUpdater.h; path = Updater/CSBestEffortUpdater.h; sourceTree = ""; }; 4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CSBestEffortUpdater.m; path = Updater/CSBestEffortUpdater.m; sourceTree = ""; }; + 4BD69F921D98760000243FE1 /* AcornADF.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AcornADF.cpp; sourceTree = ""; }; + 4BD69F931D98760000243FE1 /* AcornADF.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = AcornADF.hpp; sourceTree = ""; }; 4BE77A2C1D84ADFB00BC3827 /* File.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = File.cpp; path = ../../StaticAnalyser/Commodore/File.cpp; sourceTree = ""; }; 4BE77A2D1D84ADFB00BC3827 /* File.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = File.hpp; path = ../../StaticAnalyser/Commodore/File.hpp; sourceTree = ""; }; 4BEE0A6A1D72496600532C7B /* Cartridge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Cartridge.cpp; sourceTree = ""; }; @@ -1133,6 +1136,8 @@ 4B4C836F1D4F623200CD541F /* D64.hpp */, 4BF829611D8F536B001BAE39 /* SSD.cpp */, 4BF829621D8F536B001BAE39 /* SSD.hpp */, + 4BD69F921D98760000243FE1 /* AcornADF.cpp */, + 4BD69F931D98760000243FE1 /* AcornADF.hpp */, ); path = Formats; sourceTree = ""; @@ -2113,6 +2118,7 @@ 4BD5F1951D13528900631CD1 /* CSBestEffortUpdater.m in Sources */, 4B96F7221D75119A0058BB2D /* Tape.cpp in Sources */, 4B0BE4281D3481E700D5256B /* DigitalPhaseLockedLoop.cpp in Sources */, + 4BD69F941D98760000243FE1 /* AcornADF.cpp in Sources */, 4B73C71A1D036BD90074D992 /* Vic20Document.swift in Sources */, 4BBF99181C8FBA6F0075DAFB /* TextureTarget.cpp in Sources */, 4BC76E691C98E31700E6EF73 /* FIRFilter.cpp in Sources */, diff --git a/StaticAnalyser/Acorn/Disk.cpp b/StaticAnalyser/Acorn/Disk.cpp index 65da5793a..b670e1e4f 100644 --- a/StaticAnalyser/Acorn/Disk.cpp +++ b/StaticAnalyser/Acorn/Disk.cpp @@ -19,7 +19,7 @@ class FMParser: public Storage::Disk::Drive { FMParser(bool is_mfm) : Storage::Disk::Drive(4000000, 1, 300), crc_generator_(0x1021, 0xffff), - shift_register_(0), track_(0) + shift_register_(0), track_(0), is_mfm_(is_mfm) { // Make sure this drive really is at track '1'. while(!get_is_track_zero()) step(-1); @@ -58,6 +58,7 @@ class FMParser: public Storage::Disk::Drive { int bit_count_; std::shared_ptr sector_cache_[65536]; NumberTheory::CRC16 crc_generator_; + bool is_mfm_; void process_input_bit(int value, unsigned int cycles_since_index_hole) { @@ -98,7 +99,18 @@ class FMParser: public Storage::Disk::Drive { while(1) { run_for_cycles(1); - if(shift_register_ == Storage::Encodings::MFM::FMIDAddressMark) break; + if(is_mfm_) + { + if(shift_register_ == Storage::Encodings::MFM::MFMAddressMark) + { + uint8_t mark = get_next_byte(); + if(mark == Storage::Encodings::MFM::MFMIDAddressByte) break; + } + } + else + { + if(shift_register_ == Storage::Encodings::MFM::FMIDAddressMark) break; + } if(index_count_ >= 2) return nullptr; } @@ -115,8 +127,20 @@ class FMParser: public Storage::Disk::Drive { while(1) { run_for_cycles(1); - if(shift_register_ == Storage::Encodings::MFM::FMDataAddressMark) break; - if(shift_register_ == Storage::Encodings::MFM::FMIDAddressMark) return nullptr; + if(is_mfm_) + { + if(shift_register_ == Storage::Encodings::MFM::MFMAddressMark) + { + uint8_t mark = get_next_byte(); + if(mark == Storage::Encodings::MFM::MFMDataAddressByte) break; + if(mark == Storage::Encodings::MFM::MFMIDAddressByte) return nullptr; + } + } + else + { + if(shift_register_ == Storage::Encodings::MFM::FMDataAddressMark) break; + if(shift_register_ == Storage::Encodings::MFM::FMIDAddressMark) return nullptr; + } if(index_count_ >= 2) return nullptr; } @@ -165,7 +189,7 @@ std::unique_ptr StaticAnalyser::Acorn::GetDFSCatalogue(const std::sha std::shared_ptr names = parser.get_sector(0, 0); std::shared_ptr details = parser.get_sector(0, 1); - if(!names || !details) return catalogue; + if(!names || !details) return nullptr; if(names->data.size() != 256 || details->data.size() != 256) return nullptr; uint8_t final_file_offset = details->data[5]; diff --git a/StaticAnalyser/Acorn/StaticAnalyser.cpp b/StaticAnalyser/Acorn/StaticAnalyser.cpp index e1c55932c..0472c9e54 100644 --- a/StaticAnalyser/Acorn/StaticAnalyser.cpp +++ b/StaticAnalyser/Acorn/StaticAnalyser.cpp @@ -69,6 +69,7 @@ void StaticAnalyser::Acorn::AddTargets( target.probability = 1.0; // TODO: a proper estimation target.acorn.has_dfs = false; target.acorn.has_adfs = false; + target.acorn.should_hold_shift = false; // strip out inappropriate cartridges target.cartridges = AcornCartridgesFrom(cartridges); @@ -116,14 +117,15 @@ void StaticAnalyser::Acorn::AddTargets( if(disks.size() > 0) { std::shared_ptr disk = disks.front(); - std::unique_ptr catalogue = GetDFSCatalogue(disk); - if(catalogue == nullptr) catalogue = GetADFSCatalogue(disk); - if(catalogue) + std::unique_ptr dfs_catalogue, adfs_catalogue; + dfs_catalogue = GetDFSCatalogue(disk); + if(dfs_catalogue == nullptr) adfs_catalogue = GetADFSCatalogue(disk); + if(dfs_catalogue || adfs_catalogue) { target.disks = disks; - target.acorn.has_dfs = true; + target.acorn.has_dfs = !!dfs_catalogue; - switch(catalogue->bootOption) + switch((dfs_catalogue ?: adfs_catalogue)->bootOption) { case Catalogue::BootOption::None: target.loadingCommand = "*CAT\n"; break; default: target.acorn.should_hold_shift = true; break; diff --git a/StaticAnalyser/StaticAnalyser.cpp b/StaticAnalyser/StaticAnalyser.cpp index 98d799b05..78647447e 100644 --- a/StaticAnalyser/StaticAnalyser.cpp +++ b/StaticAnalyser/StaticAnalyser.cpp @@ -20,6 +20,7 @@ #include "../Storage/Cartridge/Formats/PRG.hpp" // Disks +#include "../Storage/Disk/Formats/AcornADF.hpp" #include "../Storage/Disk/Formats/D64.hpp" #include "../Storage/Disk/Formats/G64.hpp" #include "../Storage/Disk/Formats/SSD.hpp" @@ -80,6 +81,7 @@ std::list StaticAnalyser::GetTargets(const char *file_name) } Format("a26", cartridges, Cartridge::BinaryDump, TargetPlatform::Atari2600) // A26 + Format("adf", disks, Disk::AcornADF, TargetPlatform::Acorn) // ADF Format("bin", cartridges, Cartridge::BinaryDump, TargetPlatform::Atari2600) // BIN Format("d64", disks, Disk::D64, TargetPlatform::Commodore) // D64 Format("dsd", disks, Disk::SSD, TargetPlatform::Acorn) // DSD diff --git a/Storage/Disk/Encodings/MFM.cpp b/Storage/Disk/Encodings/MFM.cpp index cc48d0520..949588528 100644 --- a/Storage/Disk/Encodings/MFM.cpp +++ b/Storage/Disk/Encodings/MFM.cpp @@ -49,23 +49,23 @@ template class MFMShifter: public Shifter { } void add_index_address_mark() { - static_cast(this)->output_short(output_ = 0x5224); - add_byte(0xfc); + static_cast(this)->output_short(output_ = MFMIndexAddressMark); + add_byte(MFMIndexAddressByte); } void add_ID_address_mark() { - static_cast(this)->output_short(output_ = 0x4489); - add_byte(0xfe); + static_cast(this)->output_short(output_ = MFMAddressMark); + add_byte(MFMIDAddressByte); } void add_data_address_mark() { - static_cast(this)->output_short(output_ = 0x4489); - add_byte(0xfb); + static_cast(this)->output_short(output_ = MFMAddressMark); + add_byte(MFMDataAddressByte); } void add_deleted_data_address_mark() { - static_cast(this)->output_short(output_ = 0x4489); - add_byte(0xf8); + static_cast(this)->output_short(output_ = MFMAddressMark); + add_byte(MFMDeletedDataAddressByte); } private: diff --git a/Storage/Disk/Encodings/MFM.hpp b/Storage/Disk/Encodings/MFM.hpp index 07563cbce..affb2fb78 100644 --- a/Storage/Disk/Encodings/MFM.hpp +++ b/Storage/Disk/Encodings/MFM.hpp @@ -22,6 +22,13 @@ const uint16_t FMIDAddressMark = 0xf57e; // data 0xfe, with clock 0xc7 => 1111 const uint16_t FMDataAddressMark = 0xf56f; // data 0xfb, with clock 0xc7 => 1111 1011 with clock 1100 0111 => 1111 0101 0110 1111 const uint16_t FMDeletedDataAddressMark = 0xf56a; // data 0xf8, with clock 0xc7 => 1111 1000 with clock 1100 0111 => 1111 0101 0110 1010 +const uint16_t MFMIndexAddressMark = 0x5224; +const uint16_t MFMAddressMark = 0x4489; +const uint8_t MFMIndexAddressByte = 0xfc; +const uint8_t MFMIDAddressByte = 0xfe; +const uint8_t MFMDataAddressByte = 0xfb; +const uint8_t MFMDeletedDataAddressByte = 0xf8; + struct Sector { uint8_t track, side, sector; diff --git a/Storage/Disk/Formats/AcornADF.cpp b/Storage/Disk/Formats/AcornADF.cpp new file mode 100644 index 000000000..69d95a99f --- /dev/null +++ b/Storage/Disk/Formats/AcornADF.cpp @@ -0,0 +1,82 @@ +// +// AcornADF.cpp +// Clock Signal +// +// Created by Thomas Harte on 25/09/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#include "AcornADF.hpp" + +#include +#include "../Encodings/MFM.hpp" + +using namespace Storage::Disk; + +AcornADF::AcornADF(const char *file_name) : _file(nullptr) +{ + struct stat file_stats; + stat(file_name, &file_stats); + + // very loose validation: the file needs to be a multiple of 256 bytes + // and not ungainly large + if(file_stats.st_size & 255) throw ErrorNotAcornADF; + if(file_stats.st_size < 2048) throw ErrorNotAcornADF; + + _file = fopen(file_name, "rb"); + if(!_file) throw ErrorCantOpen; + + // check that the initial directory's 'Hugo's are present + fseek(_file, 513, SEEK_SET); + uint8_t bytes[4]; + fread(bytes, 1, 4, _file); + if(bytes[0] != 'H' || bytes[1] != 'u' || bytes[2] != 'g' || bytes[3] != 'o') throw ErrorNotAcornADF; + + fseek(_file, 0x6fb, SEEK_SET); + fread(bytes, 1, 4, _file); + if(bytes[0] != 'H' || bytes[1] != 'u' || bytes[2] != 'g' || bytes[3] != 'o') throw ErrorNotAcornADF; +} + +AcornADF::~AcornADF() +{ + if(_file) fclose(_file); +} + +unsigned int AcornADF::get_head_position_count() +{ + return 80; +} + +unsigned int AcornADF::get_head_count() +{ + return 2; +} + +std::shared_ptr AcornADF::get_track_at_position(unsigned int head, unsigned int position) +{ + std::shared_ptr track; + + if(head >= 2) return track; + long file_offset = (position * 2 + head) * 256 * 16; + fseek(_file, file_offset, SEEK_SET); + + std::vector sectors; + for(int sector = 0; sector < 16; sector++) + { + Storage::Encodings::MFM::Sector new_sector; + new_sector.track = (uint8_t)position; + new_sector.side = 0; + new_sector.sector = (uint8_t)sector; + + new_sector.data.resize(256); + fread(&new_sector.data[0], 1, 256, _file); + if(feof(_file)) + break; + + sectors.push_back(std::move(new_sector)); + } + + if(sectors.size()) return Storage::Encodings::MFM::GetMFMTrackWithSectors(sectors); + + return track; +} diff --git a/Storage/Disk/Formats/AcornADF.hpp b/Storage/Disk/Formats/AcornADF.hpp new file mode 100644 index 000000000..64aeb54b1 --- /dev/null +++ b/Storage/Disk/Formats/AcornADF.hpp @@ -0,0 +1,48 @@ +// +// AcornADF.hpp +// Clock Signal +// +// Created by Thomas Harte on 25/09/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#ifndef AcornADF_hpp +#define AcornADF_hpp + +#include "../Disk.hpp" + +namespace Storage { +namespace Disk { + +/*! + Provies a @c Disk containing an ADF disk image — a decoded sector dump of an Acorn ADFS disk. +*/ +class AcornADF: public Disk { + public: + /*! + Construct an @c AcornADF containing content from the file with name @c file_name. + + @throws ErrorCantOpen if this file can't be opened. + @throws ErrorNotAcornADF if the file doesn't appear to contain an Acorn .ADF format image. + */ + AcornADF(const char *file_name); + ~AcornADF(); + + enum { + ErrorCantOpen, + ErrorNotAcornADF, + }; + + // implemented to satisfy @c Disk + unsigned int get_head_position_count(); + unsigned int get_head_count(); + std::shared_ptr get_track_at_position(unsigned int head, unsigned int position); + + private: + FILE *_file; +}; + +} +} + +#endif /* AcornADF_hpp */ diff --git a/Storage/Disk/Formats/SSD.cpp b/Storage/Disk/Formats/SSD.cpp index 30e3dd63f..333fac821 100644 --- a/Storage/Disk/Formats/SSD.cpp +++ b/Storage/Disk/Formats/SSD.cpp @@ -69,7 +69,8 @@ std::shared_ptr SSD::get_track_at_position(unsigned int head, unsigned in new_sector.data.resize(256); fread(&new_sector.data[0], 1, 256, _file); - if(feof(_file)) break; + if(feof(_file)) + break; sectors.push_back(std::move(new_sector)); } diff --git a/Storage/Disk/Formats/SSD.hpp b/Storage/Disk/Formats/SSD.hpp index 7fcd8e26d..543e554ea 100644 --- a/Storage/Disk/Formats/SSD.hpp +++ b/Storage/Disk/Formats/SSD.hpp @@ -20,10 +20,10 @@ namespace Disk { class SSD: public Disk { public: /*! - Construct a @c D64 containing content from the file with name @c file_name. + Construct an @c SSD containing content from the file with name @c file_name. @throws ErrorCantOpen if this file can't be opened. - @throws ErrorNotD64 if the file doesn't appear to contain a .D64 format image. + @throws ErrorNotSSD if the file doesn't appear to contain a .SSD format image. */ SSD(const char *file_name); ~SSD();