diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 5b7f842c8..24c2b772f 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -27,6 +27,7 @@ 4B55CE5F1C3B7D960093A61B /* MachineDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B55CE5E1C3B7D960093A61B /* MachineDocument.swift */; }; 4B69FB3D1C4D908A00B5F0AA /* Tape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B69FB3B1C4D908A00B5F0AA /* Tape.cpp */; }; 4B69FB441C4D941400B5F0AA /* TapeUEF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B69FB421C4D941400B5F0AA /* TapeUEF.cpp */; }; + 4B69FB461C4D950F00B5F0AA /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B69FB451C4D950F00B5F0AA /* libz.tbd */; }; 4B92EACA1B7C112B00246143 /* TimingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B92EAC91B7C112B00246143 /* TimingTests.swift */; }; 4BB298EE1B587D8400A49093 /* 6502_functional_test.bin in Resources */ = {isa = PBXBuildFile; fileRef = 4BB297E01B587D8300A49093 /* 6502_functional_test.bin */; }; 4BB298EF1B587D8400A49093 /* AllSuiteA.bin in Resources */ = {isa = PBXBuildFile; fileRef = 4BB297E11B587D8300A49093 /* AllSuiteA.bin */; }; @@ -361,6 +362,7 @@ 4B69FB3C1C4D908A00B5F0AA /* Tape.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Tape.hpp; sourceTree = ""; }; 4B69FB421C4D941400B5F0AA /* TapeUEF.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TapeUEF.cpp; sourceTree = ""; }; 4B69FB431C4D941400B5F0AA /* TapeUEF.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TapeUEF.hpp; sourceTree = ""; }; + 4B69FB451C4D950F00B5F0AA /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; 4B92EAC91B7C112B00246143 /* TimingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimingTests.swift; sourceTree = ""; }; 4BAE587D1C447B7A005B9AF0 /* KeyCodes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeyCodes.h; sourceTree = ""; }; 4BB297DF1B587D8200A49093 /* Clock SignalTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Clock SignalTests-Bridging-Header.h"; sourceTree = ""; }; @@ -655,6 +657,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 4B69FB461C4D950F00B5F0AA /* libz.tbd in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -796,6 +799,7 @@ 4B69FB411C4D941400B5F0AA /* Formats */ = { isa = PBXGroup; children = ( + 4B69FB451C4D950F00B5F0AA /* libz.tbd */, 4B69FB421C4D941400B5F0AA /* TapeUEF.cpp */, 4B69FB431C4D941400B5F0AA /* TapeUEF.hpp */, ); diff --git a/OSBindings/Mac/Clock Signal/Documents/ElectronDocument.swift b/OSBindings/Mac/Clock Signal/Documents/ElectronDocument.swift index 75e42624a..e735c1a26 100644 --- a/OSBindings/Mac/Clock Signal/Documents/ElectronDocument.swift +++ b/OSBindings/Mac/Clock Signal/Documents/ElectronDocument.swift @@ -39,10 +39,11 @@ class ElectronDocument: MachineDocument { print(url) print(typeName) switch typeName { + case "Electron/BBC Tape Image": // this somewhat implies I've misunderstood the info.plist, doesn't it? + electron.openUEFAtURL(url) default: let fileWrapper = try NSFileWrapper(URL: url, options: NSFileWrapperReadingOptions(rawValue: 0)) try self.readFromFileWrapper(fileWrapper, ofType: typeName) - break; } } diff --git a/OSBindings/Mac/Clock Signal/Wrappers/CSElectron.h b/OSBindings/Mac/Clock Signal/Wrappers/CSElectron.h index aefd4d4e0..306e91ef7 100644 --- a/OSBindings/Mac/Clock Signal/Wrappers/CSElectron.h +++ b/OSBindings/Mac/Clock Signal/Wrappers/CSElectron.h @@ -14,6 +14,7 @@ - (void)setOSROM:(nonnull NSData *)rom; - (void)setBASICROM:(nonnull NSData *)rom; - (void)setROM:(nonnull NSData *)rom slot:(int)slot; +- (void)openUEFAtURL:(NSURL *)URL; - (void)setKey:(uint16_t)key isPressed:(BOOL)isPressed; diff --git a/OSBindings/Mac/Clock Signal/Wrappers/CSElectron.mm b/OSBindings/Mac/Clock Signal/Wrappers/CSElectron.mm index 446209f94..a7b47dbb0 100644 --- a/OSBindings/Mac/Clock Signal/Wrappers/CSElectron.mm +++ b/OSBindings/Mac/Clock Signal/Wrappers/CSElectron.mm @@ -10,6 +10,7 @@ #import "Electron.hpp" #import "CSMachine+Subclassing.h" +#import "TapeUEF.hpp" @implementation CSElectron { Electron::Machine _electron; @@ -47,6 +48,11 @@ _electron.get_crt()->set_delegate(delegate); } +- (void)openUEFAtURL:(NSURL *)URL { + Storage::UEF tape([URL fileSystemRepresentation]); +// _electron. +} + - (BOOL)setSpeakerDelegate:(Outputs::Speaker::Delegate *)delegate sampleRate:(int)sampleRate { _electron.get_speaker()->set_output_rate(sampleRate, 512); _electron.get_speaker()->set_output_quality(15); diff --git a/Storage/Tape/Formats/TapeUEF.cpp b/Storage/Tape/Formats/TapeUEF.cpp index c724a8c15..6fd56d3ab 100644 --- a/Storage/Tape/Formats/TapeUEF.cpp +++ b/Storage/Tape/Formats/TapeUEF.cpp @@ -7,3 +7,97 @@ // #include "TapeUEF.hpp" +#include + +Storage::UEF::UEF(const char *file_name) : + _chunk_id(0), _chunk_length(0), _chunk_position(0), + _time_base(1200) +{ + _file = gzopen(file_name, "rb"); + + char identifier[10]; + int bytes_read = gzread(_file, identifier, 10); + if(bytes_read < 10 || strcmp(identifier, "UEF File!")) + { + // exception? + } + + int minor, major; + minor = gzgetc(_file); + major = gzgetc(_file); + + if(major > 0 || minor > 10 || major < 0 || minor < 0) + { + // exception? + } + + find_next_tape_chunk(); +} + +Storage::UEF::~UEF() +{ + gzclose(_file); +} + +void Storage::UEF::reset() +{ + gzseek(_file, 12, SEEK_SET); +} + +Storage::Tape::Pulse Storage::UEF::get_next_pulse() +{ + Pulse next_pulse; + + return next_pulse; +} + +void Storage::UEF::find_next_tape_chunk() +{ + int reset_count = 0; + + while(1) + { + // read chunk ID + _chunk_id = (uint16_t)gzgetc(_file); + _chunk_id |= (uint16_t)(gzgetc(_file) << 8); + + _chunk_length = (uint32_t)(gzgetc(_file) << 0); + _chunk_length |= (uint32_t)(gzgetc(_file) << 8); + _chunk_length |= (uint32_t)(gzgetc(_file) << 16); + _chunk_length |= (uint32_t)(gzgetc(_file) << 24); + + printf("%04x: %d\n", _chunk_id, _chunk_length); + + if (gzeof(_file)) + { + reset_count++; + if(reset_count == 2) break; + reset(); + continue; + } + + switch(_chunk_id) + { + case 0x0100: case 0x0102: // implicit and explicit bit patterns + case 0x0112: case 0x0116: // gaps + return; + + case 0x0110: // carrier tone + // TODO: read length + return; + case 0x0111: // carrier tone with dummy byte + // TODO: read length + return; + case 0x0114: // security cycles + // TODO: read number, Ps and Ws + break; + + case 0x113: // change of base rate + break; + + default: + gzseek(_file, _chunk_length, SEEK_CUR); + break; + } + } +} diff --git a/Storage/Tape/Formats/TapeUEF.hpp b/Storage/Tape/Formats/TapeUEF.hpp index 5cdf7869e..bf13f0ad5 100644 --- a/Storage/Tape/Formats/TapeUEF.hpp +++ b/Storage/Tape/Formats/TapeUEF.hpp @@ -10,13 +10,30 @@ #define TapeUEF_hpp #include "../Tape.hpp" +#include +#include -class UEF : public Storage::Tape { +namespace Storage { + +class UEF : public Tape { public: UEF(const char *file_name); - Cycle get_next_cycle(); + ~UEF(); + + Pulse get_next_pulse(); + void reset(); private: + gzFile _file; + unsigned int _time_base; + + uint16_t _chunk_id; + uint32_t _chunk_length; + uint32_t _chunk_position; + + void find_next_tape_chunk(); }; +} + #endif /* TapeUEF_hpp */ diff --git a/Storage/Tape/Tape.hpp b/Storage/Tape/Tape.hpp index 022bec740..9ee6c1fd0 100644 --- a/Storage/Tape/Tape.hpp +++ b/Storage/Tape/Tape.hpp @@ -20,14 +20,14 @@ class Tape { unsigned int length, clock_rate; }; - struct Cycle { + struct Pulse { enum { High, Low, Zero } type; Time length; }; - virtual Cycle get_next_cycle() = 0; + virtual Pulse get_next_pulse() = 0; virtual void reset() = 0; virtual void seek(Time seek_time);