From 368bff1a82f90a924adf0e52bc277c1d4d7781c6 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 10 Jul 2017 21:43:58 -0400 Subject: [PATCH 1/9] Added a shell class that will one day be able to parse CSW files, plus the logic and metadata to instantiate it when a CSW presents itself. --- .../Clock Signal.xcodeproj/project.pbxproj | 20 ++++++--- OSBindings/Mac/Clock Signal/Info.plist | 24 ++++++++++ StaticAnalyser/StaticAnalyser.cpp | 4 ++ Storage/Tape/Formats/CSW.cpp | 27 +++++++++++ Storage/Tape/Formats/CSW.hpp | 45 +++++++++++++++++++ Storage/Tape/Formats/ZX80O81P.hpp | 4 +- 6 files changed, 115 insertions(+), 9 deletions(-) create mode 100644 Storage/Tape/Formats/CSW.cpp create mode 100644 Storage/Tape/Formats/CSW.hpp diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 23cdc4e5e..cf82f9e73 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -51,6 +51,7 @@ 4B3BA0CF1D318B44005DD7A7 /* MOS6522Bridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B3BA0C91D318B44005DD7A7 /* MOS6522Bridge.mm */; }; 4B3BA0D01D318B44005DD7A7 /* MOS6532Bridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B3BA0CB1D318B44005DD7A7 /* MOS6532Bridge.mm */; }; 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 */; }; 4B44EBF51DC987AF00A7820C /* AllSuiteA.bin in Resources */ = {isa = PBXBuildFile; fileRef = 4B44EBF41DC987AE00A7820C /* AllSuiteA.bin */; }; 4B44EBF71DC9883B00A7820C /* 6502_functional_test.bin in Resources */ = {isa = PBXBuildFile; fileRef = 4B44EBF61DC9883B00A7820C /* 6502_functional_test.bin */; }; @@ -539,6 +540,8 @@ 4B3BA0CB1D318B44005DD7A7 /* MOS6532Bridge.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MOS6532Bridge.mm; sourceTree = ""; }; 4B3BA0CC1D318B44005DD7A7 /* TestMachine6502.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestMachine6502.h; sourceTree = ""; }; 4B3BA0CD1D318B44005DD7A7 /* TestMachine6502.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TestMachine6502.mm; sourceTree = ""; }; + 4B3BF5AE1F146264005B6C36 /* CSW.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CSW.cpp; sourceTree = ""; }; + 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 = ""; }; 4B44EBF41DC987AE00A7820C /* AllSuiteA.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; name = AllSuiteA.bin; path = AllSuiteA/AllSuiteA.bin; sourceTree = ""; }; @@ -1394,17 +1397,19 @@ 4B69FB411C4D941400B5F0AA /* Formats */ = { isa = PBXGroup; children = ( - 4B69FB451C4D950F00B5F0AA /* libz.tbd */, - 4B69FB421C4D941400B5F0AA /* TapeUEF.cpp */, - 4B69FB431C4D941400B5F0AA /* TapeUEF.hpp */, 4BC91B811D1F160E00884B76 /* CommodoreTAP.cpp */, - 4BC91B821D1F160E00884B76 /* CommodoreTAP.hpp */, - 4B2BFC5D1D613E0200BA3AA9 /* TapePRG.cpp */, - 4B2BFC5E1D613E0200BA3AA9 /* TapePRG.hpp */, + 4B3BF5AE1F146264005B6C36 /* CSW.cpp */, 4B59199A1DAC6C46005BB85C /* OricTAP.cpp */, - 4B59199B1DAC6C46005BB85C /* OricTAP.hpp */, + 4B2BFC5D1D613E0200BA3AA9 /* TapePRG.cpp */, + 4B69FB421C4D941400B5F0AA /* TapeUEF.cpp */, 4B1497861EE4A1DA00CE2596 /* ZX80O81P.cpp */, + 4BC91B821D1F160E00884B76 /* CommodoreTAP.hpp */, + 4B3BF5AF1F146264005B6C36 /* CSW.hpp */, + 4B59199B1DAC6C46005BB85C /* OricTAP.hpp */, + 4B2BFC5E1D613E0200BA3AA9 /* TapePRG.hpp */, + 4B69FB431C4D941400B5F0AA /* TapeUEF.hpp */, 4B1497871EE4A1DA00CE2596 /* ZX80O81P.hpp */, + 4B69FB451C4D950F00B5F0AA /* libz.tbd */, ); path = Formats; sourceTree = ""; @@ -2593,6 +2598,7 @@ 4BD69F941D98760000243FE1 /* AcornADF.cpp in Sources */, 4BBF99181C8FBA6F0075DAFB /* TextureTarget.cpp in Sources */, 4BC76E691C98E31700E6EF73 /* FIRFilter.cpp in Sources */, + 4B3BF5B01F146265005B6C36 /* CSW.cpp in Sources */, 4B2A332A1DB8544D002876E3 /* MemoryFuzzer.cpp in Sources */, 4B55CE5F1C3B7D960093A61B /* MachineDocument.swift in Sources */, 4B2A332F1DB86869002876E3 /* OricOptionsPanel.swift in Sources */, diff --git a/OSBindings/Mac/Clock Signal/Info.plist b/OSBindings/Mac/Clock Signal/Info.plist index 42a1aec16..4ca666175 100644 --- a/OSBindings/Mac/Clock Signal/Info.plist +++ b/OSBindings/Mac/Clock Signal/Info.plist @@ -141,6 +141,8 @@ Electron/BBC Disk Image CFBundleTypeRole Editor + LSTypeIsPackage + 0 NSDocumentClass $(PRODUCT_MODULE_NAME).MachineDocument @@ -155,6 +157,8 @@ Disk Image CFBundleTypeRole Editor + LSTypeIsPackage + 0 NSDocumentClass $(PRODUCT_MODULE_NAME).MachineDocument @@ -170,6 +174,8 @@ ZX80 Tape Image CFBundleTypeRole Viewer + LSTypeIsPackage + 0 NSDocumentClass $(PRODUCT_MODULE_NAME).MachineDocument @@ -185,6 +191,24 @@ ZX81 Tape Image CFBundleTypeRole Viewer + LSTypeIsPackage + 0 + NSDocumentClass + $(PRODUCT_MODULE_NAME).MachineDocument + + + CFBundleTypeExtensions + + csw + + CFBundleTypeIconFile + cassette + CFBundleTypeName + Tape Image + CFBundleTypeRole + Viewer + LSTypeIsPackage + 0 NSDocumentClass $(PRODUCT_MODULE_NAME).MachineDocument diff --git a/StaticAnalyser/StaticAnalyser.cpp b/StaticAnalyser/StaticAnalyser.cpp index 2b18a921c..41c1b87ce 100644 --- a/StaticAnalyser/StaticAnalyser.cpp +++ b/StaticAnalyser/StaticAnalyser.cpp @@ -30,6 +30,7 @@ // Tapes #include "../Storage/Tape/Formats/CommodoreTAP.hpp" +#include "../Storage/Tape/Formats/CSW.hpp" #include "../Storage/Tape/Formats/OricTAP.hpp" #include "../Storage/Tape/Formats/TapePRG.hpp" #include "../Storage/Tape/Formats/TapeUEF.hpp" @@ -42,6 +43,8 @@ enum class TargetPlatform: TargetPlatformType { Commodore = 1 << 2, Oric = 1 << 3, ZX8081 = 1 << 4, + + AllTape = Acorn | Commodore | Oric | ZX8081, }; using namespace StaticAnalyser; @@ -94,6 +97,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("csw", tapes, Tape::CSW, TargetPlatform::AllTape) // CSW Format("d64", disks, Disk::D64, TargetPlatform::Commodore) // D64 Format("dsd", disks, Disk::SSD, TargetPlatform::Acorn) // DSD Format("dsk", disks, Disk::OricMFMDSK, TargetPlatform::Oric) // DSK diff --git a/Storage/Tape/Formats/CSW.cpp b/Storage/Tape/Formats/CSW.cpp new file mode 100644 index 000000000..957c8a448 --- /dev/null +++ b/Storage/Tape/Formats/CSW.cpp @@ -0,0 +1,27 @@ +// +// CSW.cpp +// Clock Signal +// +// Created by Thomas Harte on 10/07/2017. +// Copyright © 2017 Thomas Harte. All rights reserved. +// + +#include "CSW.hpp" + +using namespace Storage::Tape; + +CSW::CSW(const char *file_name) : + Storage::FileHolder(file_name) { +} + +bool CSW::is_at_end() { + return true; +} + +void CSW::virtual_reset() { +} + +Tape::Pulse CSW::virtual_get_next_pulse() { + Tape::Pulse pulse; + return pulse; +} diff --git a/Storage/Tape/Formats/CSW.hpp b/Storage/Tape/Formats/CSW.hpp new file mode 100644 index 000000000..ed98b3bed --- /dev/null +++ b/Storage/Tape/Formats/CSW.hpp @@ -0,0 +1,45 @@ +// +// CSW.hpp +// Clock Signal +// +// Created by Thomas Harte on 10/07/2017. +// Copyright © 2017 Thomas Harte. All rights reserved. +// + +#ifndef CSW_hpp +#define CSW_hpp + +#include "../Tape.hpp" +#include "../../FileHolder.hpp" + +namespace Storage { +namespace Tape { + +/*! + Provides a @c Tape containing a CSW tape image, which is a compressed 1-bit sampling. +*/ +class CSW: public Tape, public Storage::FileHolder { + public: + /*! + Constructs a @c CSW containing content from the file with name @c file_name. + + @throws ErrorNotCSW if this file could not be opened and recognised as a valid CSW file. + */ + CSW(const char *file_name); + + enum { + ErrorNotCSW + }; + + // implemented to satisfy @c Tape + bool is_at_end(); + + private: + void virtual_reset(); + Pulse virtual_get_next_pulse(); +}; + +} +} + +#endif /* CSW_hpp */ diff --git a/Storage/Tape/Formats/ZX80O81P.hpp b/Storage/Tape/Formats/ZX80O81P.hpp index b2ceb5ff8..efc2a5425 100644 --- a/Storage/Tape/Formats/ZX80O81P.hpp +++ b/Storage/Tape/Formats/ZX80O81P.hpp @@ -24,9 +24,9 @@ namespace Tape { class ZX80O81P: public Tape, public Storage::FileHolder { public: /*! - Constructs an @c ZX80O containing content from the file with name @c file_name. + Constructs a @c ZX80O containing content from the file with name @c file_name. - @throws ErrorNotZX80O if this file could not be opened and recognised as a valid ZX80-format .O. + @throws ErrorNotZX80O81P if this file could not be opened and recognised as a valid ZX80-format .O. */ ZX80O81P(const char *file_name); From 33eadb554917fb1a459dbc3768b7a93102cd307b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 11 Jul 2017 22:41:10 -0400 Subject: [PATCH 2/9] Started taking further steps towards CSW support; reading the ZLib documentation is next. --- Storage/Tape/Formats/CSW.cpp | 60 ++++++++++++++++++++++++++++++++++++ Storage/Tape/Formats/CSW.hpp | 11 +++++++ Storage/Tape/Tape.hpp | 2 ++ 3 files changed, 73 insertions(+) diff --git a/Storage/Tape/Formats/CSW.cpp b/Storage/Tape/Formats/CSW.cpp index 957c8a448..9ce4b090f 100644 --- a/Storage/Tape/Formats/CSW.cpp +++ b/Storage/Tape/Formats/CSW.cpp @@ -12,8 +12,68 @@ using namespace Storage::Tape; CSW::CSW(const char *file_name) : Storage::FileHolder(file_name) { + if(file_stats_.st_size < 0x20) throw ErrorNotCSW; + + // Check signature. + char identifier[22]; + char signature[] = "Compressed Square Wave"; + fread(identifier, 1, 22, file_); + if(memcmp(identifier, signature, strlen(signature))) throw ErrorNotCSW; + + // Check terminating byte. + if(fgetc(file_) != 0x1a) throw ErrorNotCSW; + + // Get version file number. + uint8_t major_version = (uint8_t)fgetc(file_); + uint8_t minor_version = (uint8_t)fgetc(file_); + + // Reject if this is an unknown version. + if(major_version > 2 || !major_version || minor_version > 1) throw ErrorNotCSW; + + // The header now diverges based on version. + if(major_version == 1) { + pulse_.length.clock_rate = fgetc16le(); + + if(fgetc(file_) != 1) throw ErrorNotCSW; + compression_type_ = RLE; + + pulse_.type = (fgetc(file_) & 1) ? Pulse::High : Pulse::Low; + + fseek(file_, 0x20, SEEK_SET); + } else { + pulse_.length.clock_rate = fgetc32le(); + number_of_waves_ = fgetc32le(); + switch(fgetc(file_)) { + case 1: compression_type_ = RLE; break; + case 2: compression_type_ = ZRLE; break; + default: throw ErrorNotCSW; + } + + pulse_.type = (fgetc(file_) & 1) ? Pulse::High : Pulse::Low; + uint8_t extension_length = (uint8_t)fgetc(file_); + + if(file_stats_.st_size < 0x34 + extension_length) throw ErrorNotCSW; + fseek(file_, 0x34 + extension_length, SEEK_SET); + } + + if(compression_type_ == ZRLE) { + inflation_stream_.zalloc = Z_NULL; + inflation_stream_.zfree = Z_NULL; + inflation_stream_.opaque = Z_NULL; + inflation_stream_.avail_in = 0; + inflation_stream_.next_in = Z_NULL; + int result = inflateInit(&inflation_stream_); + if(result != Z_OK) throw ErrorNotCSW; + } } +CSW::~CSW() { + if(compression_type_ == ZRLE) { + inflateEnd(&inflation_stream_); + } +} + + bool CSW::is_at_end() { return true; } diff --git a/Storage/Tape/Formats/CSW.hpp b/Storage/Tape/Formats/CSW.hpp index ed98b3bed..4063e530b 100644 --- a/Storage/Tape/Formats/CSW.hpp +++ b/Storage/Tape/Formats/CSW.hpp @@ -12,6 +12,8 @@ #include "../Tape.hpp" #include "../../FileHolder.hpp" +#include + namespace Storage { namespace Tape { @@ -26,6 +28,7 @@ class CSW: public Tape, public Storage::FileHolder { @throws ErrorNotCSW if this file could not be opened and recognised as a valid CSW file. */ CSW(const char *file_name); + ~CSW(); enum { ErrorNotCSW @@ -37,6 +40,14 @@ class CSW: public Tape, public Storage::FileHolder { private: void virtual_reset(); Pulse virtual_get_next_pulse(); + + Pulse pulse_; + enum CompressionType { + RLE, + ZRLE + } compression_type_; + uint32_t number_of_waves_; + z_stream inflation_stream_; }; } diff --git a/Storage/Tape/Tape.hpp b/Storage/Tape/Tape.hpp index 7ed5920da..483f8e551 100644 --- a/Storage/Tape/Tape.hpp +++ b/Storage/Tape/Tape.hpp @@ -58,6 +58,8 @@ class Tape { /// Advances or reverses the tape to the last time before or at @c time from which a pulse starts. virtual void seek(Time &time); + virtual ~Tape() {}; + private: Time current_time_, next_time_; From f09fe30af5316081c87b7531a1f6367c4e7c63c0 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 12 Jul 2017 21:23:59 -0400 Subject: [PATCH 3/9] Attempted a full implementation of CSW. All in memory for now. --- Storage/Tape/Formats/CSW.cpp | 59 +++++++++++++++++++++++++----------- Storage/Tape/Formats/CSW.hpp | 12 ++++++-- 2 files changed, 51 insertions(+), 20 deletions(-) diff --git a/Storage/Tape/Formats/CSW.cpp b/Storage/Tape/Formats/CSW.cpp index 9ce4b090f..52ab856da 100644 --- a/Storage/Tape/Formats/CSW.cpp +++ b/Storage/Tape/Formats/CSW.cpp @@ -11,7 +11,8 @@ using namespace Storage::Tape; CSW::CSW(const char *file_name) : - Storage::FileHolder(file_name) { + Storage::FileHolder(file_name), + source_data_pointer_(0) { if(file_stats_.st_size < 0x20) throw ErrorNotCSW; // Check signature. @@ -31,6 +32,7 @@ CSW::CSW(const char *file_name) : if(major_version > 2 || !major_version || minor_version > 1) throw ErrorNotCSW; // The header now diverges based on version. + uint32_t number_of_waves = 0; if(major_version == 1) { pulse_.length.clock_rate = fgetc16le(); @@ -42,7 +44,7 @@ CSW::CSW(const char *file_name) : fseek(file_, 0x20, SEEK_SET); } else { pulse_.length.clock_rate = fgetc32le(); - number_of_waves_ = fgetc32le(); + number_of_waves = fgetc32le(); switch(fgetc(file_)) { case 1: compression_type_ = RLE; break; case 2: compression_type_ = ZRLE; break; @@ -57,31 +59,54 @@ CSW::CSW(const char *file_name) : } if(compression_type_ == ZRLE) { - inflation_stream_.zalloc = Z_NULL; - inflation_stream_.zfree = Z_NULL; - inflation_stream_.opaque = Z_NULL; - inflation_stream_.avail_in = 0; - inflation_stream_.next_in = Z_NULL; - int result = inflateInit(&inflation_stream_); - if(result != Z_OK) throw ErrorNotCSW; + source_data_.resize((size_t)number_of_waves); + + std::vector file_data; + size_t remaining_data = (size_t)file_stats_.st_size - (size_t)ftell(file_); + file_data.resize(remaining_data); + fread(file_data.data(), sizeof(uint8_t), remaining_data, file_); + + uLongf output_length = (uLongf)remaining_data; + uncompress(source_data_.data(), &output_length, file_data.data(), file_data.size()); + } else { + rle_start_ = ftell(file_); + } + + invert_pulse(); +} + +uint8_t CSW::get_next_byte() { + switch(compression_type_) { + case RLE: return (uint8_t)fgetc(file_); + case ZRLE: { + if(source_data_pointer_ == source_data_.size()) return 0xff; + uint8_t result = source_data_[source_data_pointer_]; + source_data_pointer_++; + return result; + } } } -CSW::~CSW() { - if(compression_type_ == ZRLE) { - inflateEnd(&inflation_stream_); - } +void CSW::invert_pulse() { + pulse_.type = (pulse_.type == Pulse::High) ? Pulse::Low : Pulse::High; } - bool CSW::is_at_end() { - return true; + switch(compression_type_) { + case RLE: return (bool)feof(file_); + case ZRLE: return source_data_pointer_ == source_data_.size(); + } } void CSW::virtual_reset() { + switch(compression_type_) { + case RLE: fseek(file_, rle_start_, SEEK_SET); break; + case ZRLE: source_data_pointer_ = 0; break; + } } Tape::Pulse CSW::virtual_get_next_pulse() { - Tape::Pulse pulse; - return pulse; + invert_pulse(); + pulse_.length.length = get_next_byte(); + return pulse_; } diff --git a/Storage/Tape/Formats/CSW.hpp b/Storage/Tape/Formats/CSW.hpp index 4063e530b..426310de1 100644 --- a/Storage/Tape/Formats/CSW.hpp +++ b/Storage/Tape/Formats/CSW.hpp @@ -13,6 +13,7 @@ #include "../../FileHolder.hpp" #include +#include namespace Storage { namespace Tape { @@ -28,7 +29,6 @@ class CSW: public Tape, public Storage::FileHolder { @throws ErrorNotCSW if this file could not be opened and recognised as a valid CSW file. */ CSW(const char *file_name); - ~CSW(); enum { ErrorNotCSW @@ -46,8 +46,14 @@ class CSW: public Tape, public Storage::FileHolder { RLE, ZRLE } compression_type_; - uint32_t number_of_waves_; - z_stream inflation_stream_; + + uint8_t get_next_byte(); + void invert_pulse(); + + std::vector source_data_; + size_t source_data_pointer_; + + long rle_start_; }; } From 33d16ae0cd82eb59eca867445b59ea22a0ca9df3 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 12 Jul 2017 21:34:08 -0400 Subject: [PATCH 4/9] Fixes: individual static analysers reset tapes, for potential successors. The ZX81 file analyser no longer overruns its buffer upon receiving a file that is shorter than 11 bytes. --- StaticAnalyser/Acorn/StaticAnalyser.cpp | 1 + StaticAnalyser/Commodore/StaticAnalyser.cpp | 1 + StaticAnalyser/Oric/StaticAnalyser.cpp | 3 ++- StaticAnalyser/ZX8081/StaticAnalyser.cpp | 1 + Storage/Data/ZX8081.cpp | 2 +- 5 files changed, 6 insertions(+), 2 deletions(-) diff --git a/StaticAnalyser/Acorn/StaticAnalyser.cpp b/StaticAnalyser/Acorn/StaticAnalyser.cpp index 98edd262d..695c232cd 100644 --- a/StaticAnalyser/Acorn/StaticAnalyser.cpp +++ b/StaticAnalyser/Acorn/StaticAnalyser.cpp @@ -75,6 +75,7 @@ void StaticAnalyser::Acorn::AddTargets( if(tapes.size() > 0) { std::shared_ptr tape = tapes.front(); std::list files = GetFiles(tape); + tape->reset(); // continue if there are any files if(files.size()) { diff --git a/StaticAnalyser/Commodore/StaticAnalyser.cpp b/StaticAnalyser/Commodore/StaticAnalyser.cpp index c1d6aa472..79668e950 100644 --- a/StaticAnalyser/Commodore/StaticAnalyser.cpp +++ b/StaticAnalyser/Commodore/StaticAnalyser.cpp @@ -68,6 +68,7 @@ void StaticAnalyser::Commodore::AddTargets( // check tapes for(auto &tape : tapes) { std::list tape_files = GetFiles(tape); + tape->reset(); if(tape_files.size()) { files.splice(files.end(), tape_files); target.tapes = tapes; diff --git a/StaticAnalyser/Oric/StaticAnalyser.cpp b/StaticAnalyser/Oric/StaticAnalyser.cpp index 342ddd728..915004994 100644 --- a/StaticAnalyser/Oric/StaticAnalyser.cpp +++ b/StaticAnalyser/Oric/StaticAnalyser.cpp @@ -84,8 +84,9 @@ void StaticAnalyser::Oric::AddTargets( int basic10_votes = 0; int basic11_votes = 0; - for(auto tape : tapes) { + for(auto &tape : tapes) { std::list tape_files = GetFiles(tape); + tape->reset(); if(tape_files.size()) { for(auto file : tape_files) { if(file.data_type == File::MachineCode) { diff --git a/StaticAnalyser/ZX8081/StaticAnalyser.cpp b/StaticAnalyser/ZX8081/StaticAnalyser.cpp index f7e86d02a..93fd276c0 100644 --- a/StaticAnalyser/ZX8081/StaticAnalyser.cpp +++ b/StaticAnalyser/ZX8081/StaticAnalyser.cpp @@ -35,6 +35,7 @@ void StaticAnalyser::ZX8081::AddTargets( if(!tapes.empty()) { std::vector files = GetFiles(tapes.front()); + tapes.front()->reset(); if(!files.empty()) { StaticAnalyser::Target target; target.machine = Target::ZX8081; diff --git a/Storage/Data/ZX8081.cpp b/Storage/Data/ZX8081.cpp index da5c6e481..836c85dde 100644 --- a/Storage/Data/ZX8081.cpp +++ b/Storage/Data/ZX8081.cpp @@ -51,7 +51,7 @@ static std::shared_ptr ZX81FileFromData(const std::vector &data) // Look for a file name. size_t data_pointer = 0; int c = 11; - while(c--) { + while(c < data.size() && c--) { if(data[data_pointer] & 0x80) break; data_pointer++; } From ae1a130843aa8a5fe1d8fea0862bfcd8330b085d Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 13 Jul 2017 20:57:27 -0400 Subject: [PATCH 5/9] Fixed: length of 0 is a special case. --- Storage/Tape/Formats/CSW.cpp | 17 +++++++++++++++++ Storage/Tape/Formats/CSW.hpp | 1 + 2 files changed, 18 insertions(+) diff --git a/Storage/Tape/Formats/CSW.cpp b/Storage/Tape/Formats/CSW.cpp index 52ab856da..1c1f73184 100644 --- a/Storage/Tape/Formats/CSW.cpp +++ b/Storage/Tape/Formats/CSW.cpp @@ -87,6 +87,22 @@ uint8_t CSW::get_next_byte() { } } +uint32_t CSW::get_next_int32le() { + switch(compression_type_) { + case RLE: return fgetc32le(); + case ZRLE: { + if(source_data_pointer_ > source_data_.size() - 4) return 0xffff; + uint32_t result = (uint32_t)( + (source_data_[source_data_pointer_ + 0] << 0) | + (source_data_[source_data_pointer_ + 1] << 8) | + (source_data_[source_data_pointer_ + 2] << 16) | + (source_data_[source_data_pointer_ + 3] << 24)); + source_data_pointer_ += 4; + return result; + } + } +} + void CSW::invert_pulse() { pulse_.type = (pulse_.type == Pulse::High) ? Pulse::Low : Pulse::High; } @@ -108,5 +124,6 @@ void CSW::virtual_reset() { Tape::Pulse CSW::virtual_get_next_pulse() { invert_pulse(); pulse_.length.length = get_next_byte(); + if(!pulse_.length.length) pulse_.length.length = get_next_int32le(); return pulse_; } diff --git a/Storage/Tape/Formats/CSW.hpp b/Storage/Tape/Formats/CSW.hpp index 426310de1..6f240e10b 100644 --- a/Storage/Tape/Formats/CSW.hpp +++ b/Storage/Tape/Formats/CSW.hpp @@ -48,6 +48,7 @@ class CSW: public Tape, public Storage::FileHolder { } compression_type_; uint8_t get_next_byte(); + uint32_t get_next_int32le(); void invert_pulse(); std::vector source_data_; From 648618d2802d5f2a7f86f0efd1f6d1a61abb7b89 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 13 Jul 2017 21:26:05 -0400 Subject: [PATCH 6/9] Tweaked bit timing decision. --- Storage/Tape/Parsers/Acorn.cpp | 41 +++++++++++++--------------------- 1 file changed, 15 insertions(+), 26 deletions(-) diff --git a/Storage/Tape/Parsers/Acorn.cpp b/Storage/Tape/Parsers/Acorn.cpp index 35865a88f..1f7e2d8c7 100644 --- a/Storage/Tape/Parsers/Acorn.cpp +++ b/Storage/Tape/Parsers/Acorn.cpp @@ -14,27 +14,22 @@ Parser::Parser() : ::Storage::Tape::Parser(), crc_(0x1021, 0x0000) {} -int Parser::get_next_bit(const std::shared_ptr &tape) -{ +int Parser::get_next_bit(const std::shared_ptr &tape) { SymbolType symbol = get_next_symbol(tape); return (symbol == SymbolType::One) ? 1 : 0; } -int Parser::get_next_byte(const std::shared_ptr &tape) -{ +int Parser::get_next_byte(const std::shared_ptr &tape) { int value = 0; int c = 8; - if(get_next_bit(tape)) - { + if(get_next_bit(tape)) { set_error_flag(); return -1; } - while(c--) - { + while(c--) { value = (value >> 1) | (get_next_bit(tape) << 7); } - if(!get_next_bit(tape)) - { + if(!get_next_bit(tape)) { set_error_flag(); return -1; } @@ -42,15 +37,13 @@ int Parser::get_next_byte(const std::shared_ptr &tape) return value; } -int Parser::get_next_short(const std::shared_ptr &tape) -{ +int Parser::get_next_short(const std::shared_ptr &tape) { int result = get_next_byte(tape); result |= get_next_byte(tape) << 8; return result; } -int Parser::get_next_word(const std::shared_ptr &tape) -{ +int Parser::get_next_word(const std::shared_ptr &tape) { int result = get_next_short(tape); result |= get_next_short(tape) << 8; return result; @@ -59,28 +52,25 @@ int Parser::get_next_word(const std::shared_ptr &tape) void Parser::reset_crc() { crc_.reset(); } uint16_t Parser::get_crc() { return crc_.get_value(); } -void Parser::process_pulse(Storage::Tape::Tape::Pulse pulse) -{ - switch(pulse.type) - { +void Parser::process_pulse(Storage::Tape::Tape::Pulse pulse) { + switch(pulse.type) { default: break; case Storage::Tape::Tape::Pulse::High: case Storage::Tape::Tape::Pulse::Low: float pulse_length = pulse.length.get_float(); - if(pulse_length >= 0.35 / 2400.0 && pulse_length < 0.7 / 2400.0) { push_wave(WaveType::Short); return; } - if(pulse_length >= 0.35 / 1200.0 && pulse_length < 0.7 / 1200.0) { push_wave(WaveType::Long); return; } + if(pulse_length >= 0.35 / 2400.0 && pulse_length < 0.7 / 1200.0) { + push_wave(pulse_length > 1.0 / 3000.0 ? WaveType::Long : WaveType::Short); return; + } break; } push_wave(WaveType::Unrecognised); } -void Parser::inspect_waves(const std::vector &waves) -{ +void Parser::inspect_waves(const std::vector &waves) { if(waves.size() < 2) return; - if(waves[0] == WaveType::Long && waves[1] == WaveType::Long) - { + if(waves[0] == WaveType::Long && waves[1] == WaveType::Long) { push_symbol(SymbolType::Zero, 2); return; } @@ -90,8 +80,7 @@ void Parser::inspect_waves(const std::vector &waves) if( waves[0] == WaveType::Short && waves[1] == WaveType::Short && waves[2] == WaveType::Short && - waves[3] == WaveType::Short) - { + waves[3] == WaveType::Short) { push_symbol(SymbolType::One, 4); return; } From fc8313430a4e526405d1ee2e86364731385b402d Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 13 Jul 2017 21:26:45 -0400 Subject: [PATCH 7/9] Added an early exit if what was read as a header turns out pretty much certainly not to be a header. --- StaticAnalyser/Acorn/Tape.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/StaticAnalyser/Acorn/Tape.cpp b/StaticAnalyser/Acorn/Tape.cpp index f3b0931f0..358b643ea 100644 --- a/StaticAnalyser/Acorn/Tape.cpp +++ b/StaticAnalyser/Acorn/Tape.cpp @@ -60,6 +60,8 @@ static std::unique_ptr GetNextChunk(const std::shared_ptr> 8) | (stored_header_crc << 8)); new_chunk->header_crc_matched = stored_header_crc == calculated_header_crc; + if(!new_chunk->header_crc_matched) return nullptr; + parser.reset_crc(); new_chunk->data.reserve(new_chunk->block_length); for(int c = 0; c < new_chunk->block_length; c++) { From 481487f0845776cc4ac17cb8c2e2a82b16436621 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 13 Jul 2017 22:39:30 -0400 Subject: [PATCH 8/9] Oh yuck, it looks like I've repeated this same test in two different places. Must figure out where to factor it out to. But in the meantime, the emulated Electron has just loaded its first CSW. --- Machines/Electron/Tape.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Machines/Electron/Tape.cpp b/Machines/Electron/Tape.cpp index 50c4b7e86..a95d94995 100644 --- a/Machines/Electron/Tape.cpp +++ b/Machines/Electron/Tape.cpp @@ -78,8 +78,11 @@ void Tape::process_input_pulse(Storage::Tape::Tape::Pulse pulse) { crossings_[3] = Tape::Unrecognised; if(pulse.type != Storage::Tape::Tape::Pulse::Zero) { float pulse_length = (float)pulse.length.length / (float)pulse.length.clock_rate; - if(pulse_length >= 0.35 / 2400.0 && pulse_length < 0.7 / 2400.0) crossings_[3] = Tape::Short; - if(pulse_length >= 0.35 / 1200.0 && pulse_length < 0.7 / 1200.0) crossings_[3] = Tape::Long; + if(pulse_length >= 0.35 / 2400.0 && pulse_length < 0.7 / 1200.0) { + crossings_[3] = pulse_length > 1.0 / 3000.0 ? Tape::Long : Tape::Short; + } +// if(pulse_length >= 0.35 / 2400.0 && pulse_length < 0.7 / 2400.0) crossings_[3] = Tape::Short; +// if(pulse_length >= 0.35 / 1200.0 && pulse_length < 0.7 / 1200.0) crossings_[3] = Tape::Long; } if(crossings_[0] == Tape::Long && crossings_[1] == Tape::Long) { From b743566339ca0223d412cb7fd71344cb9496df98 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 15 Jul 2017 15:19:03 -0400 Subject: [PATCH 9/9] Corrected under-request of data: was erroneously supplying the size of input as the expected size of output. --- Storage/Tape/Formats/CSW.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Storage/Tape/Formats/CSW.cpp b/Storage/Tape/Formats/CSW.cpp index 1c1f73184..2a065cb03 100644 --- a/Storage/Tape/Formats/CSW.cpp +++ b/Storage/Tape/Formats/CSW.cpp @@ -66,8 +66,9 @@ CSW::CSW(const char *file_name) : file_data.resize(remaining_data); fread(file_data.data(), sizeof(uint8_t), remaining_data, file_); - uLongf output_length = (uLongf)remaining_data; + uLongf output_length = (uLongf)number_of_waves; uncompress(source_data_.data(), &output_length, file_data.data(), file_data.size()); + source_data_.resize((size_t)output_length); } else { rle_start_ = ftell(file_); }