From dab358011195dda172b4cef2c45438848d7f117c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 22 Aug 2016 22:18:05 -0400 Subject: [PATCH 01/66] Experimental: can I afford a lower sampling rate if there's a low-pass filter in effect? --- Machines/Commodore/Vic-20/Vic20.cpp | 3 +++ Outputs/Speaker.hpp | 3 +++ 2 files changed, 6 insertions(+) diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index 408ae8735..140f243d0 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -139,6 +139,9 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin _tape.run_for_cycles(1); } } + + // f7af: find tape header, exit with header in buffer + // F8C0: Read tape block } else { diff --git a/Outputs/Speaker.hpp b/Outputs/Speaker.hpp index 72e595aa9..413a37bf4 100644 --- a/Outputs/Speaker.hpp +++ b/Outputs/Speaker.hpp @@ -34,6 +34,9 @@ class Speaker { float get_ideal_clock_rate_in_range(float minimum, float maximum) { + // return twice the cut off, if applicable + if(_high_frequency_cut_off > 0.0f && _input_cycles_per_second >= _high_frequency_cut_off * 2.0f && _input_cycles_per_second <= _high_frequency_cut_off * 2.0f) return _high_frequency_cut_off * 2.0f; + // return exactly the input rate if possible if(_input_cycles_per_second >= minimum && _input_cycles_per_second <= maximum) return _input_cycles_per_second; From 0d077691b02e8f0ba7c3eafe7031523d067fdc4a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 23 Aug 2016 21:10:22 -0400 Subject: [PATCH 02/66] Resolved warning. --- OSBindings/Mac/Clock Signal/Base.lproj/Vic20Document.xib | 2 -- 1 file changed, 2 deletions(-) diff --git a/OSBindings/Mac/Clock Signal/Base.lproj/Vic20Document.xib b/OSBindings/Mac/Clock Signal/Base.lproj/Vic20Document.xib index b50cdf757..d96154920 100644 --- a/OSBindings/Mac/Clock Signal/Base.lproj/Vic20Document.xib +++ b/OSBindings/Mac/Clock Signal/Base.lproj/Vic20Document.xib @@ -48,8 +48,6 @@ - - From 5ffd9e4f0d368853330e7bafc5d34592ba9cfe6f Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 23 Aug 2016 21:35:59 -0400 Subject: [PATCH 03/66] This is probably how the static analyser interface will look? --- .../Clock Signal.xcodeproj/project.pbxproj | 14 ++++ StaticAnalyser/StaticAnalyser.cpp | 9 +++ StaticAnalyser/StaticAnalyser.hpp | 64 +++++++++++++++++++ 3 files changed, 87 insertions(+) create mode 100644 StaticAnalyser/StaticAnalyser.cpp create mode 100644 StaticAnalyser/StaticAnalyser.hpp diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 060d6ef83..c25aabfb6 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -341,6 +341,7 @@ 4BD5F1951D13528900631CD1 /* CSBestEffortUpdater.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.m */; }; 4BEF6AAA1D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BEF6AA91D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm */; }; 4BEF6AAC1D35D1C400E73575 /* DPLLTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BEF6AAB1D35D1C400E73575 /* DPLLTests.swift */; }; + 4BF1354C1D6D2C300054B2EA /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BF1354A1D6D2C300054B2EA /* StaticAnalyser.cpp */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -760,6 +761,8 @@ 4BEF6AA81D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DigitalPhaseLockedLoopBridge.h; sourceTree = ""; }; 4BEF6AA91D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DigitalPhaseLockedLoopBridge.mm; sourceTree = ""; }; 4BEF6AAB1D35D1C400E73575 /* DPLLTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DPLLTests.swift; sourceTree = ""; }; + 4BF1354A1D6D2C300054B2EA /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StaticAnalyser.cpp; path = ../../StaticAnalyser/StaticAnalyser.cpp; sourceTree = ""; }; + 4BF1354B1D6D2C300054B2EA /* StaticAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = StaticAnalyser.hpp; path = ../../StaticAnalyser/StaticAnalyser.hpp; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1345,6 +1348,7 @@ 4BB73EDD1B587CA500552FC2 /* Processors */, 4BB73E9F1B587A5100552FC2 /* Products */, 4B2409591C45DF85004DA684 /* SignalProcessing */, + 4BF1354D1D6D2C360054B2EA /* StaticAnalyser */, 4B69FB391C4D908A00B5F0AA /* Storage */, ); indentWidth = 4; @@ -1507,6 +1511,15 @@ path = Resources; sourceTree = ""; }; + 4BF1354D1D6D2C360054B2EA /* StaticAnalyser */ = { + isa = PBXGroup; + children = ( + 4BF1354A1D6D2C300054B2EA /* StaticAnalyser.cpp */, + 4BF1354B1D6D2C300054B2EA /* StaticAnalyser.hpp */, + ); + name = StaticAnalyser; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -1945,6 +1958,7 @@ 4B69FB3D1C4D908A00B5F0AA /* Tape.cpp in Sources */, 4B55CE5D1C3B7D6F0093A61B /* CSOpenGLView.m in Sources */, 4BB697CB1D4B6D3E00248BDF /* TimedEventLoop.cpp in Sources */, + 4BF1354C1D6D2C300054B2EA /* StaticAnalyser.cpp in Sources */, 4B2A53A31D117D36003C6002 /* CSVic20.mm in Sources */, 4B2A53A21D117D36003C6002 /* CSElectron.mm in Sources */, 4B2E2D9A1C3A06EC00138695 /* Atari2600.cpp in Sources */, diff --git a/StaticAnalyser/StaticAnalyser.cpp b/StaticAnalyser/StaticAnalyser.cpp new file mode 100644 index 000000000..320063b44 --- /dev/null +++ b/StaticAnalyser/StaticAnalyser.cpp @@ -0,0 +1,9 @@ +// +// StaticAnalyser.cpp +// Clock Signal +// +// Created by Thomas Harte on 23/08/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#include "StaticAnalyser.hpp" diff --git a/StaticAnalyser/StaticAnalyser.hpp b/StaticAnalyser/StaticAnalyser.hpp new file mode 100644 index 000000000..f53d6c01f --- /dev/null +++ b/StaticAnalyser/StaticAnalyser.hpp @@ -0,0 +1,64 @@ +// +// StaticAnalyser.hpp +// Clock Signal +// +// Created by Thomas Harte on 23/08/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#ifndef StaticAnalyser_hpp +#define StaticAnalyser_hpp + +#include "../Storage/Disk/Disk.hpp" +#include "../Storage/Tape/Tape.hpp" +#include +#include +#include + +namespace StaticAnalyser { + +enum Machine { + Atari2600, + Electron, + Vic20 +}; + +struct Target { + Machine machine; + float probability; + + union { + enum class Vic20 { + Unexpanded, + EightKB, + ThirtyTwoKB + } Vic20; + } MemoryModel; + + union { + enum class Electron { + ADFS, + DFS + } Electron; + enum class Vic20 { + C1540 + } Vic20; + } ExternalHardware; + + std::string loadingCommand; + union { + enum class Electron { + TypeCommand, + HoldShift + } Electron; + enum class Vic20 { + TypeCommand, + } Vic20; + } LoadingMethod; +}; + +std::list GetTargets(std::shared_ptr disk, std::shared_ptr tape, std::shared_ptr> rom); + +} + +#endif /* StaticAnalyser_hpp */ From e68ff6404541365406baec8fec84b01a9c4d66f4 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 27 Aug 2016 13:42:51 -0400 Subject: [PATCH 04/66] Actually, this is less prescriptive. --- .../Clock Signal.xcodeproj/project.pbxproj | 14 +++++++++ StaticAnalyser/Commodore/Tape.cpp | 9 ++++++ StaticAnalyser/Commodore/Tape.hpp | 30 +++++++++++++++++++ StaticAnalyser/StaticAnalyser.cpp | 21 +++++++++++++ StaticAnalyser/StaticAnalyser.hpp | 16 +++++----- 5 files changed, 82 insertions(+), 8 deletions(-) create mode 100644 StaticAnalyser/Commodore/Tape.cpp create mode 100644 StaticAnalyser/Commodore/Tape.hpp diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index c25aabfb6..2452b3d58 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -334,6 +334,7 @@ 4BC751B21D157E61006C31D9 /* 6522Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC751B11D157E61006C31D9 /* 6522Tests.swift */; }; 4BC76E691C98E31700E6EF73 /* FIRFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC76E671C98E31700E6EF73 /* FIRFilter.cpp */; }; 4BC76E6B1C98F43700E6EF73 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BC76E6A1C98F43700E6EF73 /* Accelerate.framework */; }; + 4BC830D11D6E7C690000A26F /* Tape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC830CF1D6E7C690000A26F /* Tape.cpp */; }; 4BC91B831D1F160E00884B76 /* CommodoreTAP.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC91B811D1F160E00884B76 /* CommodoreTAP.cpp */; }; 4BC9DF451D044FCA00F44158 /* ROMImages in Resources */ = {isa = PBXBuildFile; fileRef = 4BC9DF441D044FCA00F44158 /* ROMImages */; }; 4BC9DF4F1D04691600F44158 /* 6560.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC9DF4D1D04691600F44158 /* 6560.cpp */; }; @@ -749,6 +750,8 @@ 4BC76E671C98E31700E6EF73 /* FIRFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FIRFilter.cpp; sourceTree = ""; }; 4BC76E681C98E31700E6EF73 /* FIRFilter.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FIRFilter.hpp; sourceTree = ""; }; 4BC76E6A1C98F43700E6EF73 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; }; + 4BC830CF1D6E7C690000A26F /* Tape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Tape.cpp; path = ../../StaticAnalyser/Commodore/Tape.cpp; sourceTree = ""; }; + 4BC830D01D6E7C690000A26F /* Tape.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Tape.hpp; path = ../../StaticAnalyser/Commodore/Tape.hpp; sourceTree = ""; }; 4BC91B811D1F160E00884B76 /* CommodoreTAP.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CommodoreTAP.cpp; sourceTree = ""; }; 4BC91B821D1F160E00884B76 /* CommodoreTAP.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CommodoreTAP.hpp; sourceTree = ""; }; 4BC9DF441D044FCA00F44158 /* ROMImages */ = {isa = PBXFileReference; lastKnownFileType = folder; name = ROMImages; path = ../../../../ROMImages; sourceTree = ""; }; @@ -1466,6 +1469,15 @@ path = Shaders; sourceTree = ""; }; + 4BC830D21D6E7C6D0000A26F /* Commodore */ = { + isa = PBXGroup; + children = ( + 4BC830CF1D6E7C690000A26F /* Tape.cpp */, + 4BC830D01D6E7C690000A26F /* Tape.hpp */, + ); + name = Commodore; + sourceTree = ""; + }; 4BC9DF4A1D04691600F44158 /* Components */ = { isa = PBXGroup; children = ( @@ -1516,6 +1528,7 @@ children = ( 4BF1354A1D6D2C300054B2EA /* StaticAnalyser.cpp */, 4BF1354B1D6D2C300054B2EA /* StaticAnalyser.hpp */, + 4BC830D21D6E7C6D0000A26F /* Commodore */, ); name = StaticAnalyser; sourceTree = ""; @@ -1945,6 +1958,7 @@ 4BC76E691C98E31700E6EF73 /* FIRFilter.cpp in Sources */, 4B55CE5F1C3B7D960093A61B /* MachineDocument.swift in Sources */, 4B2A53A11D117D36003C6002 /* CSAtari2600.mm in Sources */, + 4BC830D11D6E7C690000A26F /* Tape.cpp in Sources */, 4B69FB441C4D941400B5F0AA /* TapeUEF.cpp in Sources */, 4B4DC8211D2C2425003C5BF8 /* Vic20.cpp in Sources */, 4BAB62B51D327F7E00DF5BA0 /* G64.cpp in Sources */, diff --git a/StaticAnalyser/Commodore/Tape.cpp b/StaticAnalyser/Commodore/Tape.cpp new file mode 100644 index 000000000..ea826fe3f --- /dev/null +++ b/StaticAnalyser/Commodore/Tape.cpp @@ -0,0 +1,9 @@ +// +// Tape.cpp +// Clock Signal +// +// Created by Thomas Harte on 24/08/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#include "Tape.hpp" diff --git a/StaticAnalyser/Commodore/Tape.hpp b/StaticAnalyser/Commodore/Tape.hpp new file mode 100644 index 000000000..7734b683f --- /dev/null +++ b/StaticAnalyser/Commodore/Tape.hpp @@ -0,0 +1,30 @@ +// +// Tape.hpp +// Clock Signal +// +// Created by Thomas Harte on 24/08/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#ifndef StaticAnalyser_Commodore_Tape_hpp +#define StaticAnalyser_Commodore_Tape_hpp + +#include +#include "../StaticAnalyser.hpp" + +namespace StaticAnalyser { +namespace Commodore { + +struct File { + uint16_t starting_address; + uint16_t ending_address; + enum { + Program, + Stream + } Type; + std::vector data; +}; + +} +} +#endif /* Tape_hpp */ diff --git a/StaticAnalyser/StaticAnalyser.cpp b/StaticAnalyser/StaticAnalyser.cpp index 320063b44..a0c95f7c2 100644 --- a/StaticAnalyser/StaticAnalyser.cpp +++ b/StaticAnalyser/StaticAnalyser.cpp @@ -7,3 +7,24 @@ // #include "StaticAnalyser.hpp" + +using namespace StaticAnalyser; + +std::list GetTargets(std::shared_ptr disk, std::shared_ptr tape, std::shared_ptr> rom) +{ + std::list targets; + + if(disk) + { + } + + if(tape) + { + } + + if(rom) + { + } + + return targets; +} diff --git a/StaticAnalyser/StaticAnalyser.hpp b/StaticAnalyser/StaticAnalyser.hpp index f53d6c01f..e1dd0986c 100644 --- a/StaticAnalyser/StaticAnalyser.hpp +++ b/StaticAnalyser/StaticAnalyser.hpp @@ -9,8 +9,8 @@ #ifndef StaticAnalyser_hpp #define StaticAnalyser_hpp -#include "../Storage/Disk/Disk.hpp" #include "../Storage/Tape/Tape.hpp" +#include "../Storage/Disk/Disk.hpp" #include #include #include @@ -47,17 +47,17 @@ struct Target { std::string loadingCommand; union { - enum class Electron { - TypeCommand, + enum class BBCElectron { HoldShift - } Electron; - enum class Vic20 { - TypeCommand, - } Vic20; + } BBCElectron; } LoadingMethod; + + std::list> disks; + std::list> tapes; + // TODO: ROMs. Probably can't model as raw data, but then how to handle bus complexities? }; -std::list GetTargets(std::shared_ptr disk, std::shared_ptr tape, std::shared_ptr> rom); +std::list GetTargets(const char *file_name); } From 55ada536ac967dc4ab85d8db8492d6f68dd27c8c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 27 Aug 2016 14:25:16 -0400 Subject: [PATCH 05/66] Added a test call, further mutated result structure. --- Machines/Commodore/Vic-20/Vic20.cpp | 4 ++++ StaticAnalyser/StaticAnalyser.cpp | 30 +++++++++++++++++++++-------- StaticAnalyser/StaticAnalyser.hpp | 28 +++++++++++++-------------- 3 files changed, 40 insertions(+), 22 deletions(-) diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index 140f243d0..8fbc9eb60 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -10,6 +10,7 @@ #include #include "../../../Storage/Tape/Formats/TapePRG.hpp" +#include "../../../StaticAnalyser/StaticAnalyser.hpp" using namespace Commodore::Vic20; @@ -244,6 +245,9 @@ void Machine::set_rom(ROMSlot slot, size_t length, const uint8_t *data) void Machine::set_prg(const char *file_name, size_t length, const uint8_t *data) { + // TEST! + StaticAnalyser::GetTargets(file_name); + if(length > 2) { _rom_address = (uint16_t)(data[0] | (data[1] << 8)); diff --git a/StaticAnalyser/StaticAnalyser.cpp b/StaticAnalyser/StaticAnalyser.cpp index a0c95f7c2..9f387cf2d 100644 --- a/StaticAnalyser/StaticAnalyser.cpp +++ b/StaticAnalyser/StaticAnalyser.cpp @@ -7,24 +7,38 @@ // #include "StaticAnalyser.hpp" +#include using namespace StaticAnalyser; -std::list GetTargets(std::shared_ptr disk, std::shared_ptr tape, std::shared_ptr> rom) +std::list StaticAnalyser::GetTargets(const char *file_name) { std::list targets; - if(disk) + // Get the extension, if any; it will be assumed that extensions are reliable, so an extension is a broad-phase + // test as to file format. + const char *mixed_case_extension = strrchr(file_name, '.'); + char *lowercase_extension = nullptr; + if(mixed_case_extension) { + lowercase_extension = strdup(mixed_case_extension); + char *parser = lowercase_extension; + while(*parser) + { + *parser = (char)tolower(*parser); + parser++; + } } - if(tape) - { - } + // Collect all disks, tapes and ROMs as can be extrapolated from this file, forming the + // union of all platforms this file might be a target for. + std::list> disks; + std::list> tapes; - if(rom) - { - } + // Obtain the union of all platforms that + printf("Lowercase extension: %s", lowercase_extension); + + free(lowercase_extension); return targets; } diff --git a/StaticAnalyser/StaticAnalyser.hpp b/StaticAnalyser/StaticAnalyser.hpp index e1dd0986c..2aa0073f3 100644 --- a/StaticAnalyser/StaticAnalyser.hpp +++ b/StaticAnalyser/StaticAnalyser.hpp @@ -32,25 +32,25 @@ struct Target { Unexpanded, EightKB, ThirtyTwoKB - } Vic20; - } MemoryModel; + } vic20; + } memoryModel; union { - enum class Electron { - ADFS, - DFS - } Electron; - enum class Vic20 { - C1540 - } Vic20; - } ExternalHardware; + struct { + bool adfs; + bool dfs; + } acorn; + struct { + bool c1540; + } vic20; + } externalHardware; std::string loadingCommand; union { - enum class BBCElectron { - HoldShift - } BBCElectron; - } LoadingMethod; + struct { + bool holdShift; + } acorn; + } loadingMethod; std::list> disks; std::list> tapes; From 8c333059a8bf30012ac2d61bb7e3a7414a93a3d0 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 27 Aug 2016 16:40:21 -0400 Subject: [PATCH 06/66] Turning this into a slog: gave `UEF` a more appropriate name, got as far as now having to decide what to do about ROMs as to structure. I guess they're machine specific, so specific classes? --- .../Machine/Wrappers/CSElectron.mm | 2 +- StaticAnalyser/StaticAnalyser.cpp | 64 ++++++++++++++++++- Storage/Tape/Formats/TapeUEF.cpp | 16 +++-- Storage/Tape/Formats/TapeUEF.hpp | 6 +- 4 files changed, 75 insertions(+), 13 deletions(-) diff --git a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSElectron.mm b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSElectron.mm index d2a6f48ce..05ea202d8 100644 --- a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSElectron.mm +++ b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSElectron.mm @@ -41,7 +41,7 @@ - (BOOL)openUEFAtURL:(NSURL *)URL { @synchronized(self) { try { - std::shared_ptr tape(new Storage::UEF([URL fileSystemRepresentation])); + std::shared_ptr tape(new Storage::TapeUEF([URL fileSystemRepresentation])); _electron.set_tape(tape); return YES; } catch(...) { diff --git a/StaticAnalyser/StaticAnalyser.cpp b/StaticAnalyser/StaticAnalyser.cpp index 9f387cf2d..2d2bf9576 100644 --- a/StaticAnalyser/StaticAnalyser.cpp +++ b/StaticAnalyser/StaticAnalyser.cpp @@ -7,8 +7,24 @@ // #include "StaticAnalyser.hpp" + #include +#include "../Storage/Disk/Formats/D64.hpp" +#include "../Storage/Disk/Formats/G64.hpp" + +#include "../Storage/Tape/Formats/CommodoreTAP.hpp" +#include "../Storage/Tape/Formats/TapePRG.hpp" +#include "../Storage/Tape/Formats/TapeUEF.hpp" + +typedef int TargetPlatformType; +enum class TargetPlatform: TargetPlatformType { + Acorn = 1 << 0, + Atari2600 = 1 << 1, + Commodore = 1 << 2 +}; + + using namespace StaticAnalyser; std::list StaticAnalyser::GetTargets(const char *file_name) @@ -21,7 +37,7 @@ std::list StaticAnalyser::GetTargets(const char *file_name) char *lowercase_extension = nullptr; if(mixed_case_extension) { - lowercase_extension = strdup(mixed_case_extension); + lowercase_extension = strdup(mixed_case_extension+1); char *parser = lowercase_extension; while(*parser) { @@ -34,8 +50,52 @@ std::list StaticAnalyser::GetTargets(const char *file_name) // union of all platforms this file might be a target for. std::list> disks; std::list> tapes; + TargetPlatformType potential_platforms = 0; - // Obtain the union of all platforms that +#define Format(extension, list, class, platforms) \ + if(!strcmp(lowercase_extension, extension)) \ + { \ + try { \ + list.emplace_back(new Storage::class(file_name));\ + potential_platforms |= (TargetPlatformType)(platforms);\ + } catch(...) {}\ + } + + // A26 + if(!strcmp(lowercase_extension, "a26")) + { + } + + // BIN + if(!strcmp(lowercase_extension, "bin")) + { + } + + Format("d64", disks, D64, TargetPlatform::Commodore) // D64 + Format("g64", disks, G64, TargetPlatform::Commodore) // G64 + + // PRG + if(!strcmp(lowercase_extension, "prg")) + { + // try instantiating as a ROM; failing that accept as a tape + try { + tapes.emplace_back(new Storage::TapePRG(file_name)); + potential_platforms |= (TargetPlatformType)TargetPlatform::Commodore; + } catch(...) {} + } + + // ROM + if(!strcmp(lowercase_extension, "rom")) + { + } + + Format("tap", tapes, CommodoreTAP, TargetPlatform::Commodore) // TAP + Format("uef", tapes, TapeUEF, TargetPlatform::Acorn) // UEF (tape) + +#undef Format + + // Hand off to platform-specific determination of whether these things are actually compatible and, + // if so, how to load them. (TODO) printf("Lowercase extension: %s", lowercase_extension); diff --git a/Storage/Tape/Formats/TapeUEF.cpp b/Storage/Tape/Formats/TapeUEF.cpp index 7ed0d8818..7cb0140cf 100644 --- a/Storage/Tape/Formats/TapeUEF.cpp +++ b/Storage/Tape/Formats/TapeUEF.cpp @@ -10,6 +10,8 @@ #include #include +using namespace Storage; + static float gzgetfloat(gzFile file) { uint8_t bytes[4]; @@ -41,7 +43,7 @@ static float gzgetfloat(gzFile file) return result; } -Storage::UEF::UEF(const char *file_name) : +TapeUEF::TapeUEF(const char *file_name) : _chunk_id(0), _chunk_length(0), _chunk_position(0), _time_base(1200) { @@ -67,17 +69,17 @@ Storage::UEF::UEF(const char *file_name) : find_next_tape_chunk(); } -Storage::UEF::~UEF() +TapeUEF::~TapeUEF() { gzclose(_file); } -void Storage::UEF::reset() +void TapeUEF::reset() { gzseek(_file, 12, SEEK_SET); } -Storage::Tape::Pulse Storage::UEF::get_next_pulse() +Tape::Pulse TapeUEF::get_next_pulse() { Pulse next_pulse; @@ -145,7 +147,7 @@ Storage::Tape::Pulse Storage::UEF::get_next_pulse() return next_pulse; } -void Storage::UEF::find_next_tape_chunk() +void TapeUEF::find_next_tape_chunk() { int reset_count = 0; _chunk_position = 0; @@ -234,7 +236,7 @@ void Storage::UEF::find_next_tape_chunk() } } -bool Storage::UEF::chunk_is_finished() +bool TapeUEF::chunk_is_finished() { switch(_chunk_id) { @@ -250,7 +252,7 @@ bool Storage::UEF::chunk_is_finished() } } -bool Storage::UEF::get_next_bit() +bool TapeUEF::get_next_bit() { switch(_chunk_id) { diff --git a/Storage/Tape/Formats/TapeUEF.hpp b/Storage/Tape/Formats/TapeUEF.hpp index e67bdd9e7..fa68c83b9 100644 --- a/Storage/Tape/Formats/TapeUEF.hpp +++ b/Storage/Tape/Formats/TapeUEF.hpp @@ -18,15 +18,15 @@ namespace Storage { /*! Provides a @c Tape containing a UEF tape image, a slightly-convoluted description of pulses. */ -class UEF : public Tape { +class TapeUEF : public Tape { public: /*! Constructs a @c UEF containing content from the file with name @c file_name. @throws ErrorNotUEF if this file could not be opened and recognised as a valid UEF. */ - UEF(const char *file_name); - ~UEF(); + TapeUEF(const char *file_name); + ~TapeUEF(); enum { ErrorNotUEF From c0402d0c2b61eb934e1a7a4f2dacbbf18a5b9e7b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 27 Aug 2016 17:09:45 -0400 Subject: [PATCH 07/66] Gave tapes their own namespace. --- Machines/Commodore/Vic-20/Vic20.cpp | 8 ++++---- Machines/Commodore/Vic-20/Vic20.hpp | 6 +++--- Machines/Electron/Electron.cpp | 6 +++--- Machines/Electron/Electron.hpp | 6 +++--- .../Clock Signal/Machine/Wrappers/CSElectron.mm | 2 +- .../Mac/Clock Signal/Machine/Wrappers/CSVic20.mm | 2 +- StaticAnalyser/StaticAnalyser.cpp | 8 ++++---- StaticAnalyser/StaticAnalyser.hpp | 2 +- Storage/Tape/Formats/CommodoreTAP.cpp | 3 ++- Storage/Tape/Formats/CommodoreTAP.hpp | 2 ++ Storage/Tape/Formats/TapePRG.cpp | 15 ++++++++------- Storage/Tape/Formats/TapePRG.hpp | 12 +++++++----- Storage/Tape/Formats/TapeUEF.cpp | 15 ++++++++------- Storage/Tape/Formats/TapeUEF.hpp | 8 +++++--- Storage/Tape/Tape.cpp | 7 ++++--- Storage/Tape/Tape.hpp | 6 ++++-- 16 files changed, 60 insertions(+), 48 deletions(-) diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index 8fbc9eb60..5b8f61de4 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -262,14 +262,14 @@ void Machine::set_prg(const char *file_name, size_t length, const uint8_t *data) } else { - set_tape(std::shared_ptr(new Storage::TapePRG(file_name))); + set_tape(std::shared_ptr(new Storage::Tape::PRG(file_name))); } } } #pragma mar - Tape -void Machine::set_tape(std::shared_ptr tape) +void Machine::set_tape(std::shared_ptr tape) { _tape.set_tape(tape); if(_should_automatically_load_media) set_typer_for_string("LOAD\nRUN\n"); @@ -422,9 +422,9 @@ Tape::Tape() : TapePlayer(1022727) {} void Tape::set_motor_control(bool enabled) {} void Tape::set_tape_output(bool set) {} -void Tape::process_input_pulse(Storage::Tape::Pulse pulse) +void Tape::process_input_pulse(Storage::Tape::PRG::Pulse pulse) { - bool new_input_level = pulse.type == Storage::Tape::Pulse::Low; + bool new_input_level = pulse.type == Storage::Tape::PRG::Pulse::Low; if(_input_level != new_input_level) { _input_level = new_input_level; diff --git a/Machines/Commodore/Vic-20/Vic20.hpp b/Machines/Commodore/Vic-20/Vic20.hpp index bb4121dd9..5ab24cc98 100644 --- a/Machines/Commodore/Vic-20/Vic20.hpp +++ b/Machines/Commodore/Vic-20/Vic20.hpp @@ -213,7 +213,7 @@ class SerialPort : public ::Commodore::Serial::Port { std::weak_ptr _userPortVIA; }; -class Tape: public Storage::TapePlayer { +class Tape: public Storage::Tape::TapePlayer { public: Tape(); @@ -232,7 +232,7 @@ class Tape: public Storage::TapePlayer { private: Delegate *_delegate; - virtual void process_input_pulse(Storage::Tape::Pulse pulse); + virtual void process_input_pulse(Storage::Tape::Tape::Pulse pulse); bool _input_level; }; @@ -261,7 +261,7 @@ class Machine: void set_rom(ROMSlot slot, size_t length, const uint8_t *data); void set_prg(const char *file_name, size_t length, const uint8_t *data); - void set_tape(std::shared_ptr tape); + void set_tape(std::shared_ptr tape); void set_disk(std::shared_ptr disk); void set_key_state(Key key, bool isPressed) { _keyboardVIA->set_key_state(key, isPressed); } diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index 30c9a0ad4..2a4de4af2 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -458,7 +458,7 @@ void Machine::synchronise() update_audio(); } -void Machine::set_tape(std::shared_ptr tape) +void Machine::set_tape(std::shared_ptr tape) { _tape.set_tape(tape); } @@ -971,14 +971,14 @@ inline uint8_t Tape::get_data_register() return (uint8_t)(_data_register >> 2); } -inline void Tape::process_input_pulse(Storage::Tape::Pulse pulse) +inline void Tape::process_input_pulse(Storage::Tape::Tape::Pulse pulse) { _crossings[0] = _crossings[1]; _crossings[1] = _crossings[2]; _crossings[2] = _crossings[3]; _crossings[3] = Tape::Unrecognised; - if(pulse.type != Storage::Tape::Pulse::Zero) + 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; diff --git a/Machines/Electron/Electron.hpp b/Machines/Electron/Electron.hpp index 6ec7b81a2..551de717e 100644 --- a/Machines/Electron/Electron.hpp +++ b/Machines/Electron/Electron.hpp @@ -62,7 +62,7 @@ enum Key: uint16_t { TerminateSequence = 0, NotMapped = 0xfffe, }; -class Tape: public Storage::TapePlayer { +class Tape: public Storage::Tape::TapePlayer { public: Tape(); @@ -86,7 +86,7 @@ class Tape: public Storage::TapePlayer { inline void set_is_in_input_mode(bool is_in_input_mode); private: - void process_input_pulse(Storage::Tape::Pulse pulse); + void process_input_pulse(Storage::Tape::Tape::Pulse pulse); inline void push_tape_bit(uint16_t bit); inline void get_next_tape_pulse(); @@ -145,7 +145,7 @@ class Machine: Machine(); void set_rom(ROMSlot slot, size_t length, const uint8_t *data); - void set_tape(std::shared_ptr tape); + void set_tape(std::shared_ptr tape); void set_key_state(Key key, bool isPressed); void clear_all_keys(); diff --git a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSElectron.mm b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSElectron.mm index 05ea202d8..4da7966e5 100644 --- a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSElectron.mm +++ b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSElectron.mm @@ -41,7 +41,7 @@ - (BOOL)openUEFAtURL:(NSURL *)URL { @synchronized(self) { try { - std::shared_ptr tape(new Storage::TapeUEF([URL fileSystemRepresentation])); + std::shared_ptr tape(new Storage::Tape::UEF([URL fileSystemRepresentation])); _electron.set_tape(tape); return YES; } catch(...) { diff --git a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.mm b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.mm index 0f03fb7e2..720f41509 100644 --- a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.mm +++ b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.mm @@ -49,7 +49,7 @@ using namespace Commodore::Vic20; - (BOOL)openTAPAtURL:(NSURL *)URL { @synchronized(self) { try { - std::shared_ptr tape(new Storage::CommodoreTAP([URL fileSystemRepresentation])); + std::shared_ptr tape(new Storage::Tape::CommodoreTAP([URL fileSystemRepresentation])); _vic20.set_tape(tape); return YES; } catch(...) { diff --git a/StaticAnalyser/StaticAnalyser.cpp b/StaticAnalyser/StaticAnalyser.cpp index 2d2bf9576..526764f13 100644 --- a/StaticAnalyser/StaticAnalyser.cpp +++ b/StaticAnalyser/StaticAnalyser.cpp @@ -49,7 +49,7 @@ std::list StaticAnalyser::GetTargets(const char *file_name) // Collect all disks, tapes and ROMs as can be extrapolated from this file, forming the // union of all platforms this file might be a target for. std::list> disks; - std::list> tapes; + std::list> tapes; TargetPlatformType potential_platforms = 0; #define Format(extension, list, class, platforms) \ @@ -79,7 +79,7 @@ std::list StaticAnalyser::GetTargets(const char *file_name) { // try instantiating as a ROM; failing that accept as a tape try { - tapes.emplace_back(new Storage::TapePRG(file_name)); + tapes.emplace_back(new Storage::Tape::PRG(file_name)); potential_platforms |= (TargetPlatformType)TargetPlatform::Commodore; } catch(...) {} } @@ -89,8 +89,8 @@ std::list StaticAnalyser::GetTargets(const char *file_name) { } - Format("tap", tapes, CommodoreTAP, TargetPlatform::Commodore) // TAP - Format("uef", tapes, TapeUEF, TargetPlatform::Acorn) // UEF (tape) + Format("tap", tapes, Tape::CommodoreTAP, TargetPlatform::Commodore) // TAP + Format("uef", tapes, Tape::UEF, TargetPlatform::Acorn) // UEF (tape) #undef Format diff --git a/StaticAnalyser/StaticAnalyser.hpp b/StaticAnalyser/StaticAnalyser.hpp index 2aa0073f3..0a9785413 100644 --- a/StaticAnalyser/StaticAnalyser.hpp +++ b/StaticAnalyser/StaticAnalyser.hpp @@ -53,7 +53,7 @@ struct Target { } loadingMethod; std::list> disks; - std::list> tapes; + std::list> tapes; // TODO: ROMs. Probably can't model as raw data, but then how to handle bus complexities? }; diff --git a/Storage/Tape/Formats/CommodoreTAP.cpp b/Storage/Tape/Formats/CommodoreTAP.cpp index f574a3fd7..0a7de2876 100644 --- a/Storage/Tape/Formats/CommodoreTAP.cpp +++ b/Storage/Tape/Formats/CommodoreTAP.cpp @@ -11,6 +11,7 @@ #include using namespace Storage; +using namespace Tape; CommodoreTAP::CommodoreTAP(const char *file_name) { @@ -64,7 +65,7 @@ void CommodoreTAP::reset() _current_pulse.type = Pulse::High; } -Tape::Pulse CommodoreTAP::get_next_pulse() +Storage::Tape::Tape::Pulse CommodoreTAP::get_next_pulse() { if(_current_pulse.type == Pulse::High) { diff --git a/Storage/Tape/Formats/CommodoreTAP.hpp b/Storage/Tape/Formats/CommodoreTAP.hpp index 7d4a9de17..60fd3c369 100644 --- a/Storage/Tape/Formats/CommodoreTAP.hpp +++ b/Storage/Tape/Formats/CommodoreTAP.hpp @@ -13,6 +13,7 @@ #include namespace Storage { +namespace Tape { /*! Provides a @c Tape containing a Commodore-format tape image, which is simply a timed list of downward-going zero crossings. @@ -43,6 +44,7 @@ class CommodoreTAP: public Tape { Pulse _current_pulse; }; +} } #endif /* CommodoreTAP_hpp */ diff --git a/Storage/Tape/Formats/TapePRG.cpp b/Storage/Tape/Formats/TapePRG.cpp index cc3f5db04..34bb7c7d3 100644 --- a/Storage/Tape/Formats/TapePRG.cpp +++ b/Storage/Tape/Formats/TapePRG.cpp @@ -47,8 +47,9 @@ #include using namespace Storage; +using namespace Tape; -TapePRG::TapePRG(const char *file_name) : _file(nullptr), _bitPhase(3), _filePhase(FilePhaseLeadIn), _phaseOffset(0), _copy_mask(0x80) +PRG::PRG(const char *file_name) : _file(nullptr), _bitPhase(3), _filePhase(FilePhaseLeadIn), _phaseOffset(0), _copy_mask(0x80) { struct stat file_stats; stat(file_name, &file_stats); @@ -69,12 +70,12 @@ TapePRG::TapePRG(const char *file_name) : _file(nullptr), _bitPhase(3), _filePha throw ErrorBadFormat; } -TapePRG::~TapePRG() +PRG::~PRG() { if(_file) fclose(_file); } -Tape::Pulse TapePRG::get_next_pulse() +Storage::Tape::Tape::Pulse PRG::get_next_pulse() { // these are all microseconds per pole static const unsigned int leader_zero_length = 179; @@ -87,7 +88,7 @@ Tape::Pulse TapePRG::get_next_pulse() Tape::Pulse pulse; pulse.length.clock_rate = 1000000; - pulse.type = (_bitPhase&1) ? Pulse::High : Pulse::Low; + pulse.type = (_bitPhase&1) ? Tape::Pulse::High : Tape::Pulse::Low; switch(_outputToken) { case Leader: pulse.length.length = leader_zero_length; break; @@ -95,12 +96,12 @@ Tape::Pulse TapePRG::get_next_pulse() case One: pulse.length.length = (_bitPhase&2) ? zero_length : one_length; break; case WordMarker: pulse.length.length = (_bitPhase&2) ? one_length : marker_length; break; case EndOfBlock: pulse.length.length = (_bitPhase&2) ? zero_length : marker_length; break; - case Silence: pulse.type = Pulse::Zero; pulse.length.length = 5000; break; + case Silence: pulse.type = Tape::Pulse::Zero; pulse.length.length = 5000; break; } return pulse; } -void TapePRG::reset() +void PRG::reset() { _bitPhase = 3; fseek(_file, 2, SEEK_SET); @@ -109,7 +110,7 @@ void TapePRG::reset() _copy_mask = 0x80; } -void TapePRG::get_next_output_token() +void PRG::get_next_output_token() { static const int block_length = 192; // not counting the checksum static const int countdown_bytes = 9; diff --git a/Storage/Tape/Formats/TapePRG.hpp b/Storage/Tape/Formats/TapePRG.hpp index e516a961f..0ad03a9d3 100644 --- a/Storage/Tape/Formats/TapePRG.hpp +++ b/Storage/Tape/Formats/TapePRG.hpp @@ -6,18 +6,19 @@ // Copyright © 2016 Thomas Harte. All rights reserved. // -#ifndef TapePRG_hpp -#define TapePRG_hpp +#ifndef Storage_Tape_PRG_hpp +#define Storage_Tape_PRG_hpp #include "../Tape.hpp" #include namespace Storage { +namespace Tape { /*! Provides a @c Tape containing a .PRG, which is a direct local file. */ -class TapePRG: public Tape { +class PRG: public Tape { public: /*! Constructs a @c T64 containing content from the file with name @c file_name, of type @c type. @@ -26,8 +27,8 @@ class TapePRG: public Tape { @param type The type of data the file should contain. @throws ErrorBadFormat if this file could not be opened and recognised as the specified type. */ - TapePRG(const char *file_name); - ~TapePRG(); + PRG(const char *file_name); + ~PRG(); enum { ErrorBadFormat @@ -64,6 +65,7 @@ class TapePRG: public Tape { uint8_t _copy_mask; }; +} } #endif /* T64_hpp */ diff --git a/Storage/Tape/Formats/TapeUEF.cpp b/Storage/Tape/Formats/TapeUEF.cpp index 7cb0140cf..0ae10c707 100644 --- a/Storage/Tape/Formats/TapeUEF.cpp +++ b/Storage/Tape/Formats/TapeUEF.cpp @@ -11,6 +11,7 @@ #include using namespace Storage; +using namespace Tape; static float gzgetfloat(gzFile file) { @@ -43,7 +44,7 @@ static float gzgetfloat(gzFile file) return result; } -TapeUEF::TapeUEF(const char *file_name) : +UEF::UEF(const char *file_name) : _chunk_id(0), _chunk_length(0), _chunk_position(0), _time_base(1200) { @@ -69,17 +70,17 @@ TapeUEF::TapeUEF(const char *file_name) : find_next_tape_chunk(); } -TapeUEF::~TapeUEF() +UEF::~UEF() { gzclose(_file); } -void TapeUEF::reset() +void UEF::reset() { gzseek(_file, 12, SEEK_SET); } -Tape::Pulse TapeUEF::get_next_pulse() +Storage::Tape::Tape::Pulse UEF::get_next_pulse() { Pulse next_pulse; @@ -147,7 +148,7 @@ Tape::Pulse TapeUEF::get_next_pulse() return next_pulse; } -void TapeUEF::find_next_tape_chunk() +void UEF::find_next_tape_chunk() { int reset_count = 0; _chunk_position = 0; @@ -236,7 +237,7 @@ void TapeUEF::find_next_tape_chunk() } } -bool TapeUEF::chunk_is_finished() +bool UEF::chunk_is_finished() { switch(_chunk_id) { @@ -252,7 +253,7 @@ bool TapeUEF::chunk_is_finished() } } -bool TapeUEF::get_next_bit() +bool UEF::get_next_bit() { switch(_chunk_id) { diff --git a/Storage/Tape/Formats/TapeUEF.hpp b/Storage/Tape/Formats/TapeUEF.hpp index fa68c83b9..12d2286c1 100644 --- a/Storage/Tape/Formats/TapeUEF.hpp +++ b/Storage/Tape/Formats/TapeUEF.hpp @@ -14,19 +14,20 @@ #include namespace Storage { +namespace Tape { /*! Provides a @c Tape containing a UEF tape image, a slightly-convoluted description of pulses. */ -class TapeUEF : public Tape { +class UEF : public Tape { public: /*! Constructs a @c UEF containing content from the file with name @c file_name. @throws ErrorNotUEF if this file could not be opened and recognised as a valid UEF. */ - TapeUEF(const char *file_name); - ~TapeUEF(); + UEF(const char *file_name); + ~UEF(); enum { ErrorNotUEF @@ -72,6 +73,7 @@ class TapeUEF : public Tape { bool chunk_is_finished(); }; +} } #endif /* TapeUEF_hpp */ diff --git a/Storage/Tape/Tape.cpp b/Storage/Tape/Tape.cpp index 8717a5ce4..70dd9cdcb 100644 --- a/Storage/Tape/Tape.cpp +++ b/Storage/Tape/Tape.cpp @@ -10,8 +10,9 @@ #include "../../NumberTheory/Factors.hpp" using namespace Storage; +using namespace Tape; -void Tape::seek(Time seek_time) +void Storage::Tape::Tape::seek(Time seek_time) { // TODO: as best we can } @@ -20,7 +21,7 @@ TapePlayer::TapePlayer(unsigned int input_clock_rate) : TimedEventLoop(input_clock_rate) {} -void TapePlayer::set_tape(std::shared_ptr tape) +void TapePlayer::set_tape(std::shared_ptr tape) { _tape = tape; reset_timer(); @@ -41,7 +42,7 @@ void TapePlayer::get_next_pulse() { _current_pulse.length.length = 1; _current_pulse.length.clock_rate = 1; - _current_pulse.type = Storage::Tape::Pulse::Zero; + _current_pulse.type = Tape::Pulse::Zero; } set_next_event_time_interval(_current_pulse.length); diff --git a/Storage/Tape/Tape.hpp b/Storage/Tape/Tape.hpp index 47ebea129..082ff035d 100644 --- a/Storage/Tape/Tape.hpp +++ b/Storage/Tape/Tape.hpp @@ -13,6 +13,7 @@ #include "../TimedEventLoop.hpp" namespace Storage { +namespace Tape { /*! Models a tape as a sequence of pulses, each pulse being of arbitrary length and described @@ -51,7 +52,7 @@ class TapePlayer: public TimedEventLoop { public: TapePlayer(unsigned int input_clock_rate); - void set_tape(std::shared_ptr tape); + void set_tape(std::shared_ptr tape); bool has_tape(); void run_for_cycles(int number_of_cycles); @@ -64,10 +65,11 @@ class TapePlayer: public TimedEventLoop { private: inline void get_next_pulse(); - std::shared_ptr _tape; + std::shared_ptr _tape; Tape::Pulse _current_pulse; }; +} } #endif /* Tape_hpp */ From 56c0d70c1fc82c5375635337f12f76172a1f8c6c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 27 Aug 2016 17:15:09 -0400 Subject: [PATCH 08/66] Gave disks their own namespace. --- Machines/Commodore/1540/C1540.cpp | 4 ++-- Machines/Commodore/1540/C1540.hpp | 4 ++-- Machines/Commodore/Vic-20/Vic20.cpp | 2 +- Machines/Commodore/Vic-20/Vic20.hpp | 2 +- .../Clock Signal/Machine/Wrappers/CSVic20.mm | 12 +++++----- StaticAnalyser/StaticAnalyser.cpp | 6 ++--- StaticAnalyser/StaticAnalyser.hpp | 2 +- Storage/Disk/Disk.hpp | 2 ++ Storage/Disk/DiskDrive.cpp | 23 ++++++++++--------- Storage/Disk/DiskDrive.hpp | 6 +++-- Storage/Disk/Formats/D64.cpp | 2 +- Storage/Disk/Formats/D64.hpp | 5 +++- Storage/Disk/Formats/G64.cpp | 1 + Storage/Disk/Formats/G64.hpp | 4 +++- Storage/Disk/PCMTrack.cpp | 4 ++-- Storage/Disk/PCMTrack.hpp | 2 ++ 16 files changed, 47 insertions(+), 34 deletions(-) diff --git a/Machines/Commodore/1540/C1540.cpp b/Machines/Commodore/1540/C1540.cpp index d4a45dff7..2756ad058 100644 --- a/Machines/Commodore/1540/C1540.cpp +++ b/Machines/Commodore/1540/C1540.cpp @@ -14,7 +14,7 @@ using namespace Commodore::C1540; Machine::Machine() : _shift_register(0), - Storage::DiskDrive(1000000, 4, 300) + Storage::Disk::Drive(1000000, 4, 300) { // create a serial port and a VIA to run it _serialPortVIA.reset(new SerialPortVIA); @@ -106,7 +106,7 @@ void Machine::run_for_cycles(int number_of_cycles) { CPU6502::Processor::run_for_cycles(number_of_cycles); if(_driveVIA.get_motor_enabled()) // TODO: motor speed up/down - Storage::DiskDrive::run_for_cycles(number_of_cycles); + Storage::Disk::Drive::run_for_cycles(number_of_cycles); } #pragma mark - 6522 delegate diff --git a/Machines/Commodore/1540/C1540.hpp b/Machines/Commodore/1540/C1540.hpp index f052ea7bc..7a1b5e0e4 100644 --- a/Machines/Commodore/1540/C1540.hpp +++ b/Machines/Commodore/1540/C1540.hpp @@ -216,7 +216,7 @@ class Machine: public CPU6502::Processor, public MOS::MOS6522IRQDelegate::Delegate, public DriveVIA::Delegate, - public Storage::DiskDrive { + public Storage::Disk::Drive { public: Machine(); @@ -251,7 +251,7 @@ class Machine: std::shared_ptr _serialPort; DriveVIA _driveVIA; - std::shared_ptr _disk; + std::shared_ptr _disk; int _shift_register, _bit_window_offset; virtual void process_input_bit(int value, unsigned int cycles_since_index_hole); diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index 5b8f61de4..e1a7b993c 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -282,7 +282,7 @@ void Machine::tape_did_change_input(Tape *tape) #pragma mark - Disc -void Machine::set_disk(std::shared_ptr disk) +void Machine::set_disk(std::shared_ptr disk) { // construct the 1540 _c1540.reset(new ::Commodore::C1540::Machine); diff --git a/Machines/Commodore/Vic-20/Vic20.hpp b/Machines/Commodore/Vic-20/Vic20.hpp index 5ab24cc98..59fec787b 100644 --- a/Machines/Commodore/Vic-20/Vic20.hpp +++ b/Machines/Commodore/Vic-20/Vic20.hpp @@ -262,7 +262,7 @@ class Machine: void set_rom(ROMSlot slot, size_t length, const uint8_t *data); void set_prg(const char *file_name, size_t length, const uint8_t *data); void set_tape(std::shared_ptr tape); - void set_disk(std::shared_ptr disk); + void set_disk(std::shared_ptr disk); void set_key_state(Key key, bool isPressed) { _keyboardVIA->set_key_state(key, isPressed); } void clear_all_keys() { _keyboardVIA->clear_all_keys(); } diff --git a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.mm b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.mm index 720f41509..7198aafe7 100644 --- a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.mm +++ b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.mm @@ -59,21 +59,21 @@ using namespace Commodore::Vic20; } - (BOOL)openG64AtURL:(NSURL *)URL { - return [self openDisk:^std::shared_ptr{ - return std::shared_ptr(new Storage::G64([URL fileSystemRepresentation])); + return [self openDisk:^std::shared_ptr{ + return std::shared_ptr(new Storage::Disk::G64([URL fileSystemRepresentation])); }]; } - (BOOL)openD64AtURL:(NSURL *)URL { - return [self openDisk:^std::shared_ptr{ - return std::shared_ptr(new Storage::D64([URL fileSystemRepresentation])); + return [self openDisk:^std::shared_ptr{ + return std::shared_ptr(new Storage::Disk::D64([URL fileSystemRepresentation])); }]; } -- (BOOL)openDisk:(std::shared_ptr (^)())opener { +- (BOOL)openDisk:(std::shared_ptr (^)())opener { @synchronized(self) { try { - std::shared_ptr disk = opener(); + std::shared_ptr disk = opener(); _vic20.set_disk(disk); return YES; } catch(...) { diff --git a/StaticAnalyser/StaticAnalyser.cpp b/StaticAnalyser/StaticAnalyser.cpp index 526764f13..6755343de 100644 --- a/StaticAnalyser/StaticAnalyser.cpp +++ b/StaticAnalyser/StaticAnalyser.cpp @@ -48,7 +48,7 @@ std::list StaticAnalyser::GetTargets(const char *file_name) // Collect all disks, tapes and ROMs as can be extrapolated from this file, forming the // union of all platforms this file might be a target for. - std::list> disks; + std::list> disks; std::list> tapes; TargetPlatformType potential_platforms = 0; @@ -71,8 +71,8 @@ std::list StaticAnalyser::GetTargets(const char *file_name) { } - Format("d64", disks, D64, TargetPlatform::Commodore) // D64 - Format("g64", disks, G64, TargetPlatform::Commodore) // G64 + Format("d64", disks, Disk::D64, TargetPlatform::Commodore) // D64 + Format("g64", disks, Disk::G64, TargetPlatform::Commodore) // G64 // PRG if(!strcmp(lowercase_extension, "prg")) diff --git a/StaticAnalyser/StaticAnalyser.hpp b/StaticAnalyser/StaticAnalyser.hpp index 0a9785413..f229128b1 100644 --- a/StaticAnalyser/StaticAnalyser.hpp +++ b/StaticAnalyser/StaticAnalyser.hpp @@ -52,7 +52,7 @@ struct Target { } acorn; } loadingMethod; - std::list> disks; + std::list> disks; std::list> tapes; // TODO: ROMs. Probably can't model as raw data, but then how to handle bus complexities? }; diff --git a/Storage/Disk/Disk.hpp b/Storage/Disk/Disk.hpp index 08e3ee607..c17712b85 100644 --- a/Storage/Disk/Disk.hpp +++ b/Storage/Disk/Disk.hpp @@ -13,6 +13,7 @@ #include "../Storage.hpp" namespace Storage { +namespace Disk { /*! Models a single track on a disk as a series of events, each event being of arbitrary length @@ -78,6 +79,7 @@ class Disk { virtual std::shared_ptr get_track_at_position(unsigned int position) = 0; }; +} } #endif /* Disk_hpp */ diff --git a/Storage/Disk/DiskDrive.cpp b/Storage/Disk/DiskDrive.cpp index 3492a5bcb..c9953b40c 100644 --- a/Storage/Disk/DiskDrive.cpp +++ b/Storage/Disk/DiskDrive.cpp @@ -9,8 +9,9 @@ #include "DiskDrive.hpp" using namespace Storage; +using namespace Disk; -DiskDrive::DiskDrive(unsigned int clock_rate, unsigned int clock_rate_multiplier, unsigned int revolutions_per_minute) : +Drive::Drive(unsigned int clock_rate, unsigned int clock_rate_multiplier, unsigned int revolutions_per_minute) : _clock_rate(clock_rate * clock_rate_multiplier), _clock_rate_multiplier(clock_rate_multiplier), _head_position(0), @@ -22,7 +23,7 @@ DiskDrive::DiskDrive(unsigned int clock_rate, unsigned int clock_rate_multiplier _rotational_multiplier.simplify(); } -void DiskDrive::set_expected_bit_length(Time bit_length) +void Drive::set_expected_bit_length(Time bit_length) { _bit_length = bit_length; @@ -33,23 +34,23 @@ void DiskDrive::set_expected_bit_length(Time bit_length) _pll->set_delegate(this); } -void DiskDrive::set_disk(std::shared_ptr disk) +void Drive::set_disk(std::shared_ptr disk) { _disk = disk; set_track(Time()); } -bool DiskDrive::has_disk() +bool Drive::has_disk() { return (bool)_disk; } -bool DiskDrive::get_is_track_zero() +bool Drive::get_is_track_zero() { return _head_position == 0; } -void DiskDrive::step(int direction) +void Drive::step(int direction) { _head_position = std::max(_head_position + direction, 0); Time extra_time = get_time_into_next_event() / _rotational_multiplier; @@ -58,7 +59,7 @@ void DiskDrive::step(int direction) set_track(_time_into_track); } -void DiskDrive::set_track(Time initial_offset) +void Drive::set_track(Time initial_offset) { _track = _disk->get_track_at_position((unsigned int)_head_position); // TODO: probably a better implementation of the empty track? @@ -80,7 +81,7 @@ void DiskDrive::set_track(Time initial_offset) reset_timer_to_offset(offset * _rotational_multiplier); } -void DiskDrive::run_for_cycles(int number_of_cycles) +void Drive::run_for_cycles(int number_of_cycles) { if(has_disk()) { @@ -96,7 +97,7 @@ void DiskDrive::run_for_cycles(int number_of_cycles) #pragma mark - Track timed event loop -void DiskDrive::get_next_event() +void Drive::get_next_event() { if(_track) _current_event = _track->get_next_event(); @@ -112,7 +113,7 @@ void DiskDrive::get_next_event() set_next_event_time_interval(_current_event.length * _rotational_multiplier); } -void DiskDrive::process_next_event() +void Drive::process_next_event() { switch(_current_event.type) { @@ -131,7 +132,7 @@ void DiskDrive::process_next_event() #pragma mark - PLL delegate -void DiskDrive::digital_phase_locked_loop_output_bit(int value) +void Drive::digital_phase_locked_loop_output_bit(int value) { process_input_bit(value, _cycles_since_index_hole); } diff --git a/Storage/Disk/DiskDrive.hpp b/Storage/Disk/DiskDrive.hpp index 216a1626b..017e34464 100644 --- a/Storage/Disk/DiskDrive.hpp +++ b/Storage/Disk/DiskDrive.hpp @@ -14,6 +14,7 @@ #include "../TimedEventLoop.hpp" namespace Storage { +namespace Disk { /*! Provides the shell for emulating a disk drive — something that takes a disk and has a drive head @@ -26,13 +27,13 @@ namespace Storage { TODO: double sided disks, communication of head size and permissible stepping extents, appropriate simulation of gain. */ -class DiskDrive: public DigitalPhaseLockedLoop::Delegate, public TimedEventLoop { +class Drive: public DigitalPhaseLockedLoop::Delegate, public TimedEventLoop { public: /*! Constructs a @c DiskDrive that will be run at @c clock_rate and runs its PLL at @c clock_rate*clock_rate_multiplier, spinning inserted disks at @c revolutions_per_minute. */ - DiskDrive(unsigned int clock_rate, unsigned int clock_rate_multiplier, unsigned int revolutions_per_minute); + Drive(unsigned int clock_rate, unsigned int clock_rate_multiplier, unsigned int revolutions_per_minute); /*! Communicates to the PLL the expected length of a bit. @@ -106,6 +107,7 @@ class DiskDrive: public DigitalPhaseLockedLoop::Delegate, public TimedEventLoop Time _time_into_track; }; +} } #endif /* DiskDrive_hpp */ diff --git a/Storage/Disk/Formats/D64.cpp b/Storage/Disk/Formats/D64.cpp index 38765eb50..4bba3eb5a 100644 --- a/Storage/Disk/Formats/D64.cpp +++ b/Storage/Disk/Formats/D64.cpp @@ -13,7 +13,7 @@ #include "../PCMTrack.hpp" #include "../../../Storage/Disk/Encodings/CommodoreGCR.hpp" -using namespace Storage; +using namespace Storage::Disk; D64::D64(const char *file_name) { diff --git a/Storage/Disk/Formats/D64.hpp b/Storage/Disk/Formats/D64.hpp index eefc32f80..93c4d4cb5 100644 --- a/Storage/Disk/Formats/D64.hpp +++ b/Storage/Disk/Formats/D64.hpp @@ -12,6 +12,7 @@ #include "../Disk.hpp" namespace Storage { +namespace Disk { /*! Provies a @c Disk containing a D64 disk image — a decoded sector dump of a C1540-format disk. @@ -42,5 +43,7 @@ class D64: public Disk { uint16_t _disk_id; }; -}; +} +} + #endif /* D64_hpp */ diff --git a/Storage/Disk/Formats/G64.cpp b/Storage/Disk/Formats/G64.cpp index 475537929..3403e0188 100644 --- a/Storage/Disk/Formats/G64.cpp +++ b/Storage/Disk/Formats/G64.cpp @@ -13,6 +13,7 @@ #include "../Encodings/CommodoreGCR.hpp" using namespace Storage; +using namespace Disk; G64::G64(const char *file_name) { diff --git a/Storage/Disk/Formats/G64.hpp b/Storage/Disk/Formats/G64.hpp index 957c83c42..82ab9ae97 100644 --- a/Storage/Disk/Formats/G64.hpp +++ b/Storage/Disk/Formats/G64.hpp @@ -12,6 +12,7 @@ #include "../Disk.hpp" namespace Storage { +namespace Disk { /*! Provies a @c Disk containing a G64 disk image — a raw but perfectly-clocked GCR stream. @@ -45,6 +46,7 @@ class G64: public Disk { uint16_t _maximum_track_size; }; -}; +} +} #endif /* G64_hpp */ diff --git a/Storage/Disk/PCMTrack.cpp b/Storage/Disk/PCMTrack.cpp index 2a8d53f56..a655ab969 100644 --- a/Storage/Disk/PCMTrack.cpp +++ b/Storage/Disk/PCMTrack.cpp @@ -9,7 +9,7 @@ #include "PCMTrack.hpp" #include "../../NumberTheory/Factors.hpp" -using namespace Storage; +using namespace Storage::Disk; PCMTrack::PCMTrack(std::vector segments) { @@ -61,7 +61,7 @@ PCMTrack::Event PCMTrack::get_next_event() return _next_event; } -Time PCMTrack::seek_to(Time time_since_index_hole) +Storage::Time PCMTrack::seek_to(Time time_since_index_hole) { _segment_pointer = 0; diff --git a/Storage/Disk/PCMTrack.hpp b/Storage/Disk/PCMTrack.hpp index 99e426281..751473ef5 100644 --- a/Storage/Disk/PCMTrack.hpp +++ b/Storage/Disk/PCMTrack.hpp @@ -13,6 +13,7 @@ #include namespace Storage { +namespace Disk { /*! A segment of PCM-sampled data. @@ -68,6 +69,7 @@ class PCMTrack: public Track { size_t _bit_pointer; }; +} } #endif /* PCMTrack_hpp */ From 82c8459055ead78d1e6b534bceb2c7790ba7a875 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 27 Aug 2016 17:18:12 -0400 Subject: [PATCH 09/66] Minor tidying of namespace usage declarations. --- Storage/Disk/DiskDrive.cpp | 3 +-- Storage/Disk/Formats/G64.cpp | 3 +-- Storage/Tape/Formats/CommodoreTAP.cpp | 3 +-- Storage/Tape/Formats/TapePRG.cpp | 3 +-- Storage/Tape/Formats/TapeUEF.cpp | 3 +-- Storage/Tape/Tape.cpp | 5 ++--- 6 files changed, 7 insertions(+), 13 deletions(-) diff --git a/Storage/Disk/DiskDrive.cpp b/Storage/Disk/DiskDrive.cpp index c9953b40c..29f9cb4eb 100644 --- a/Storage/Disk/DiskDrive.cpp +++ b/Storage/Disk/DiskDrive.cpp @@ -8,8 +8,7 @@ #include "DiskDrive.hpp" -using namespace Storage; -using namespace Disk; +using namespace Storage::Disk; Drive::Drive(unsigned int clock_rate, unsigned int clock_rate_multiplier, unsigned int revolutions_per_minute) : _clock_rate(clock_rate * clock_rate_multiplier), diff --git a/Storage/Disk/Formats/G64.cpp b/Storage/Disk/Formats/G64.cpp index 3403e0188..546155433 100644 --- a/Storage/Disk/Formats/G64.cpp +++ b/Storage/Disk/Formats/G64.cpp @@ -12,8 +12,7 @@ #include "../PCMTrack.hpp" #include "../Encodings/CommodoreGCR.hpp" -using namespace Storage; -using namespace Disk; +using namespace Storage::Disk; G64::G64(const char *file_name) { diff --git a/Storage/Tape/Formats/CommodoreTAP.cpp b/Storage/Tape/Formats/CommodoreTAP.cpp index 0a7de2876..3fc5bf669 100644 --- a/Storage/Tape/Formats/CommodoreTAP.cpp +++ b/Storage/Tape/Formats/CommodoreTAP.cpp @@ -10,8 +10,7 @@ #include #include -using namespace Storage; -using namespace Tape; +using namespace Storage::Tape; CommodoreTAP::CommodoreTAP(const char *file_name) { diff --git a/Storage/Tape/Formats/TapePRG.cpp b/Storage/Tape/Formats/TapePRG.cpp index 34bb7c7d3..d728e73e5 100644 --- a/Storage/Tape/Formats/TapePRG.cpp +++ b/Storage/Tape/Formats/TapePRG.cpp @@ -46,8 +46,7 @@ #include -using namespace Storage; -using namespace Tape; +using namespace Storage::Tape; PRG::PRG(const char *file_name) : _file(nullptr), _bitPhase(3), _filePhase(FilePhaseLeadIn), _phaseOffset(0), _copy_mask(0x80) { diff --git a/Storage/Tape/Formats/TapeUEF.cpp b/Storage/Tape/Formats/TapeUEF.cpp index 0ae10c707..bac0b4627 100644 --- a/Storage/Tape/Formats/TapeUEF.cpp +++ b/Storage/Tape/Formats/TapeUEF.cpp @@ -10,8 +10,7 @@ #include #include -using namespace Storage; -using namespace Tape; +using namespace Storage::Tape; static float gzgetfloat(gzFile file) { diff --git a/Storage/Tape/Tape.cpp b/Storage/Tape/Tape.cpp index 70dd9cdcb..e8856d2c1 100644 --- a/Storage/Tape/Tape.cpp +++ b/Storage/Tape/Tape.cpp @@ -9,8 +9,7 @@ #include "Tape.hpp" #include "../../NumberTheory/Factors.hpp" -using namespace Storage; -using namespace Tape; +using namespace Storage::Tape; void Storage::Tape::Tape::seek(Time seek_time) { @@ -52,7 +51,7 @@ void TapePlayer::run_for_cycles(int number_of_cycles) { if(has_tape()) { - ::TimedEventLoop::run_for_cycles(number_of_cycles); + TimedEventLoop::run_for_cycles(number_of_cycles); } } From a1b3a18d11b41aa5bf27c4e89841d8e82649586e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 27 Aug 2016 18:17:40 -0400 Subject: [PATCH 10/66] Started forcing a resolution on ROMs by doing. But have immediately misstepped. Rename coming momentarily... --- .../Clock Signal.xcodeproj/project.pbxproj | 20 +++++++ StaticAnalyser/StaticAnalyser.cpp | 11 +++- Storage/ROM/PRG.cpp | 56 +++++++++++++++++++ Storage/ROM/PRG.hpp | 35 ++++++++++++ Storage/ROM/ROM.cpp | 9 +++ Storage/ROM/ROM.hpp | 20 +++++++ 6 files changed, 148 insertions(+), 3 deletions(-) create mode 100644 Storage/ROM/PRG.cpp create mode 100644 Storage/ROM/PRG.hpp create mode 100644 Storage/ROM/ROM.cpp create mode 100644 Storage/ROM/ROM.hpp diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 2452b3d58..6947d5a15 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -340,6 +340,8 @@ 4BC9DF4F1D04691600F44158 /* 6560.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC9DF4D1D04691600F44158 /* 6560.cpp */; }; 4BC9E1EE1D23449A003FCEE4 /* 6502InterruptTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC9E1ED1D23449A003FCEE4 /* 6502InterruptTests.swift */; }; 4BD5F1951D13528900631CD1 /* CSBestEffortUpdater.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.m */; }; + 4BEE0A641D72454000532C7B /* PRG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE0A621D72454000532C7B /* PRG.cpp */; }; + 4BEE0A681D7248B300532C7B /* ROM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE0A661D7248B300532C7B /* ROM.cpp */; }; 4BEF6AAA1D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BEF6AA91D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm */; }; 4BEF6AAC1D35D1C400E73575 /* DPLLTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BEF6AAB1D35D1C400E73575 /* DPLLTests.swift */; }; 4BF1354C1D6D2C300054B2EA /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BF1354A1D6D2C300054B2EA /* StaticAnalyser.cpp */; }; @@ -761,6 +763,10 @@ 4BCA98C21D065CA20062F44C /* 6522.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 6522.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 = ""; }; + 4BEE0A621D72454000532C7B /* PRG.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PRG.cpp; path = ROM/PRG.cpp; sourceTree = ""; }; + 4BEE0A631D72454000532C7B /* PRG.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = PRG.hpp; path = ROM/PRG.hpp; sourceTree = ""; }; + 4BEE0A661D7248B300532C7B /* ROM.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ROM.cpp; path = ROM/ROM.cpp; sourceTree = ""; }; + 4BEE0A671D7248B300532C7B /* ROM.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ROM.hpp; path = ROM/ROM.hpp; sourceTree = ""; }; 4BEF6AA81D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DigitalPhaseLockedLoopBridge.h; sourceTree = ""; }; 4BEF6AA91D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DigitalPhaseLockedLoopBridge.mm; sourceTree = ""; }; 4BEF6AAB1D35D1C400E73575 /* DPLLTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DPLLTests.swift; sourceTree = ""; }; @@ -990,6 +996,7 @@ 4BB697CA1D4B6D3E00248BDF /* TimedEventLoop.hpp */, 4BAB62AA1D3272D200DF5BA0 /* Disk */, 4B69FB3A1C4D908A00B5F0AA /* Tape */, + 4BEE0A651D72454400532C7B /* ROM */, ); name = Storage; path = ../../Storage; @@ -1523,6 +1530,17 @@ path = Resources; sourceTree = ""; }; + 4BEE0A651D72454400532C7B /* ROM */ = { + isa = PBXGroup; + children = ( + 4BEE0A621D72454000532C7B /* PRG.cpp */, + 4BEE0A631D72454000532C7B /* PRG.hpp */, + 4BEE0A661D7248B300532C7B /* ROM.cpp */, + 4BEE0A671D7248B300532C7B /* ROM.hpp */, + ); + name = ROM; + sourceTree = ""; + }; 4BF1354D1D6D2C360054B2EA /* StaticAnalyser */ = { isa = PBXGroup; children = ( @@ -1945,6 +1963,7 @@ 4BB697CE1D4BA44400248BDF /* CommodoreGCR.cpp in Sources */, 4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */, 4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */, + 4BEE0A681D7248B300532C7B /* ROM.cpp in Sources */, 4BB697C71D4B558F00248BDF /* Factors.cpp in Sources */, 4B55CE591C3B7D360093A61B /* ElectronDocument.swift in Sources */, 4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */, @@ -1954,6 +1973,7 @@ 4BD5F1951D13528900631CD1 /* CSBestEffortUpdater.m in Sources */, 4B0BE4281D3481E700D5256B /* DigitalPhaseLockedLoop.cpp in Sources */, 4B73C71A1D036BD90074D992 /* Vic20Document.swift in Sources */, + 4BEE0A641D72454000532C7B /* PRG.cpp in Sources */, 4BBF99181C8FBA6F0075DAFB /* TextureTarget.cpp in Sources */, 4BC76E691C98E31700E6EF73 /* FIRFilter.cpp in Sources */, 4B55CE5F1C3B7D960093A61B /* MachineDocument.swift in Sources */, diff --git a/StaticAnalyser/StaticAnalyser.cpp b/StaticAnalyser/StaticAnalyser.cpp index 6755343de..083edaee0 100644 --- a/StaticAnalyser/StaticAnalyser.cpp +++ b/StaticAnalyser/StaticAnalyser.cpp @@ -79,9 +79,14 @@ std::list StaticAnalyser::GetTargets(const char *file_name) { // try instantiating as a ROM; failing that accept as a tape try { - tapes.emplace_back(new Storage::Tape::PRG(file_name)); - potential_platforms |= (TargetPlatformType)TargetPlatform::Commodore; - } catch(...) {} + } + catch(...) + { + try { + tapes.emplace_back(new Storage::Tape::PRG(file_name)); + potential_platforms |= (TargetPlatformType)TargetPlatform::Commodore; + } catch(...) {} + } } // ROM diff --git a/Storage/ROM/PRG.cpp b/Storage/ROM/PRG.cpp new file mode 100644 index 000000000..66ea7c78d --- /dev/null +++ b/Storage/ROM/PRG.cpp @@ -0,0 +1,56 @@ +// +// PRG.cpp +// Clock Signal +// +// Created by Thomas Harte on 27/08/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#include "PRG.hpp" + +#include +#include + +using namespace Storage::ROM; + +PRG::PRG(const char *file_name) : _contents(nullptr) +{ + struct stat file_stats; + stat(file_name, &file_stats); + + // accept only files sized 1, 2, 4 or 8kb + if( + file_stats.st_size != 0x400 + 2 && + file_stats.st_size != 0x800 + 2 && + file_stats.st_size != 0x1000 + 2 && + file_stats.st_size != 0x2000 + 2) + throw ErrorNotROM; + + // get the loading address, and the rest of the contents + FILE *file = fopen(file_name, "rb"); + + int loading_address = fgetc(file); + loading_address |= fgetc(file) << 8; + + _contents = new uint8_t[file_stats.st_size - 2]; + fread(_contents, 1, (size_t)(file_stats.st_size - 2), file); + fclose(file); + + // accept only files intended to load at 0xa000 + if(loading_address != 0xa000) + throw ErrorNotROM; + + // also accept only cartridges with the proper signature + if( + _contents[4] != 0x41 || + _contents[5] != 0x30 || + _contents[6] != 0xc3 || + _contents[7] != 0xc2 || + _contents[8] != 0xcd) + throw ErrorNotROM; +} + +PRG::~PRG() +{ + delete[] _contents; +} diff --git a/Storage/ROM/PRG.hpp b/Storage/ROM/PRG.hpp new file mode 100644 index 000000000..decd58ab6 --- /dev/null +++ b/Storage/ROM/PRG.hpp @@ -0,0 +1,35 @@ +// +// PRG.hpp +// Clock Signal +// +// Created by Thomas Harte on 27/08/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#ifndef PRG_hpp +#define PRG_hpp + +#include +#include "ROM.hpp" + +namespace Storage { +namespace ROM { + +class PRG : public ROM { + public: + PRG(const char *file_name); + ~PRG(); + + enum { + ErrorNotROM + }; + + private: + uint8_t *_contents; + uint16_t _size; +}; + +} +} + +#endif /* PRG_hpp */ diff --git a/Storage/ROM/ROM.cpp b/Storage/ROM/ROM.cpp new file mode 100644 index 000000000..904e7a527 --- /dev/null +++ b/Storage/ROM/ROM.cpp @@ -0,0 +1,9 @@ +// +// ROM.cpp +// Clock Signal +// +// Created by Thomas Harte on 27/08/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#include "ROM.hpp" diff --git a/Storage/ROM/ROM.hpp b/Storage/ROM/ROM.hpp new file mode 100644 index 000000000..08ffd024c --- /dev/null +++ b/Storage/ROM/ROM.hpp @@ -0,0 +1,20 @@ +// +// ROM.hpp +// Clock Signal +// +// Created by Thomas Harte on 27/08/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#ifndef ROM_hpp +#define ROM_hpp + +namespace Storage { +namespace ROM { + +class ROM {}; + +} +} + +#endif /* ROM_hpp */ From 24938326ac86ea8d5651928e2373bd5c426521ea Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 27 Aug 2016 18:26:51 -0400 Subject: [PATCH 11/66] ROMs definitely have no behaviour other than responding to memory accesses. Cartridges might. So picked the more general term. Sketched out a class at least to parse PRG as though it were a cartridge. Hence the static analyser can guess at whether a PRG is a cartridge or an ordinary program. --- .../Clock Signal.xcodeproj/project.pbxproj | 38 +++++++++++-------- StaticAnalyser/StaticAnalyser.cpp | 12 +++++- StaticAnalyser/StaticAnalyser.hpp | 4 +- .../{ROM/ROM.cpp => Cartridge/Cartridge.cpp} | 2 +- .../{ROM/ROM.hpp => Cartridge/Cartridge.hpp} | 10 ++--- Storage/{ROM => Cartridge/Formats}/PRG.cpp | 2 +- Storage/{ROM => Cartridge/Formats}/PRG.hpp | 10 ++--- 7 files changed, 48 insertions(+), 30 deletions(-) rename Storage/{ROM/ROM.cpp => Cartridge/Cartridge.cpp} (84%) rename Storage/{ROM/ROM.hpp => Cartridge/Cartridge.hpp} (59%) rename Storage/{ROM => Cartridge/Formats}/PRG.cpp (97%) rename Storage/{ROM => Cartridge/Formats}/PRG.hpp (69%) diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 6947d5a15..9d63cc952 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -340,8 +340,8 @@ 4BC9DF4F1D04691600F44158 /* 6560.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC9DF4D1D04691600F44158 /* 6560.cpp */; }; 4BC9E1EE1D23449A003FCEE4 /* 6502InterruptTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC9E1ED1D23449A003FCEE4 /* 6502InterruptTests.swift */; }; 4BD5F1951D13528900631CD1 /* CSBestEffortUpdater.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.m */; }; - 4BEE0A641D72454000532C7B /* PRG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE0A621D72454000532C7B /* PRG.cpp */; }; - 4BEE0A681D7248B300532C7B /* ROM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE0A661D7248B300532C7B /* ROM.cpp */; }; + 4BEE0A6F1D72496600532C7B /* Cartridge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE0A6A1D72496600532C7B /* Cartridge.cpp */; }; + 4BEE0A701D72496600532C7B /* PRG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE0A6D1D72496600532C7B /* PRG.cpp */; }; 4BEF6AAA1D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BEF6AA91D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm */; }; 4BEF6AAC1D35D1C400E73575 /* DPLLTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BEF6AAB1D35D1C400E73575 /* DPLLTests.swift */; }; 4BF1354C1D6D2C300054B2EA /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BF1354A1D6D2C300054B2EA /* StaticAnalyser.cpp */; }; @@ -763,10 +763,10 @@ 4BCA98C21D065CA20062F44C /* 6522.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 6522.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 = ""; }; - 4BEE0A621D72454000532C7B /* PRG.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PRG.cpp; path = ROM/PRG.cpp; sourceTree = ""; }; - 4BEE0A631D72454000532C7B /* PRG.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = PRG.hpp; path = ROM/PRG.hpp; sourceTree = ""; }; - 4BEE0A661D7248B300532C7B /* ROM.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ROM.cpp; path = ROM/ROM.cpp; sourceTree = ""; }; - 4BEE0A671D7248B300532C7B /* ROM.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ROM.hpp; path = ROM/ROM.hpp; sourceTree = ""; }; + 4BEE0A6A1D72496600532C7B /* Cartridge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Cartridge.cpp; sourceTree = ""; }; + 4BEE0A6B1D72496600532C7B /* Cartridge.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Cartridge.hpp; sourceTree = ""; }; + 4BEE0A6D1D72496600532C7B /* PRG.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PRG.cpp; sourceTree = ""; }; + 4BEE0A6E1D72496600532C7B /* PRG.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PRG.hpp; sourceTree = ""; }; 4BEF6AA81D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DigitalPhaseLockedLoopBridge.h; sourceTree = ""; }; 4BEF6AA91D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DigitalPhaseLockedLoopBridge.mm; sourceTree = ""; }; 4BEF6AAB1D35D1C400E73575 /* DPLLTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DPLLTests.swift; sourceTree = ""; }; @@ -994,9 +994,9 @@ 4BB697C91D4B6D3E00248BDF /* TimedEventLoop.cpp */, 4BAB62AE1D32730D00DF5BA0 /* Storage.hpp */, 4BB697CA1D4B6D3E00248BDF /* TimedEventLoop.hpp */, + 4BEE0A691D72496600532C7B /* Cartridge */, 4BAB62AA1D3272D200DF5BA0 /* Disk */, 4B69FB3A1C4D908A00B5F0AA /* Tape */, - 4BEE0A651D72454400532C7B /* ROM */, ); name = Storage; path = ../../Storage; @@ -1530,15 +1530,23 @@ path = Resources; sourceTree = ""; }; - 4BEE0A651D72454400532C7B /* ROM */ = { + 4BEE0A691D72496600532C7B /* Cartridge */ = { isa = PBXGroup; children = ( - 4BEE0A621D72454000532C7B /* PRG.cpp */, - 4BEE0A631D72454000532C7B /* PRG.hpp */, - 4BEE0A661D7248B300532C7B /* ROM.cpp */, - 4BEE0A671D7248B300532C7B /* ROM.hpp */, + 4BEE0A6A1D72496600532C7B /* Cartridge.cpp */, + 4BEE0A6B1D72496600532C7B /* Cartridge.hpp */, + 4BEE0A6C1D72496600532C7B /* Formats */, ); - name = ROM; + path = Cartridge; + sourceTree = ""; + }; + 4BEE0A6C1D72496600532C7B /* Formats */ = { + isa = PBXGroup; + children = ( + 4BEE0A6D1D72496600532C7B /* PRG.cpp */, + 4BEE0A6E1D72496600532C7B /* PRG.hpp */, + ); + path = Formats; sourceTree = ""; }; 4BF1354D1D6D2C360054B2EA /* StaticAnalyser */ = { @@ -1963,7 +1971,6 @@ 4BB697CE1D4BA44400248BDF /* CommodoreGCR.cpp in Sources */, 4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */, 4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */, - 4BEE0A681D7248B300532C7B /* ROM.cpp in Sources */, 4BB697C71D4B558F00248BDF /* Factors.cpp in Sources */, 4B55CE591C3B7D360093A61B /* ElectronDocument.swift in Sources */, 4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */, @@ -1973,7 +1980,6 @@ 4BD5F1951D13528900631CD1 /* CSBestEffortUpdater.m in Sources */, 4B0BE4281D3481E700D5256B /* DigitalPhaseLockedLoop.cpp in Sources */, 4B73C71A1D036BD90074D992 /* Vic20Document.swift in Sources */, - 4BEE0A641D72454000532C7B /* PRG.cpp in Sources */, 4BBF99181C8FBA6F0075DAFB /* TextureTarget.cpp in Sources */, 4BC76E691C98E31700E6EF73 /* FIRFilter.cpp in Sources */, 4B55CE5F1C3B7D960093A61B /* MachineDocument.swift in Sources */, @@ -1999,6 +2005,8 @@ 4BC3B7521CD1956900F86E85 /* OutputShader.cpp in Sources */, 4B4C83701D4F623200CD541F /* D64.cpp in Sources */, 4B14145B1B58879D00E04248 /* CPU6502.cpp in Sources */, + 4BEE0A6F1D72496600532C7B /* Cartridge.cpp in Sources */, + 4BEE0A701D72496600532C7B /* PRG.cpp in Sources */, 4B2A53A01D117D36003C6002 /* CSMachine.mm in Sources */, 4BC91B831D1F160E00884B76 /* CommodoreTAP.cpp in Sources */, 4B2A539F1D117D36003C6002 /* CSAudioQueue.m in Sources */, diff --git a/StaticAnalyser/StaticAnalyser.cpp b/StaticAnalyser/StaticAnalyser.cpp index 083edaee0..a808a1bc0 100644 --- a/StaticAnalyser/StaticAnalyser.cpp +++ b/StaticAnalyser/StaticAnalyser.cpp @@ -10,6 +10,8 @@ #include +#include "../Storage/Cartridge/Formats/PRG.hpp" + #include "../Storage/Disk/Formats/D64.hpp" #include "../Storage/Disk/Formats/G64.hpp" @@ -50,8 +52,13 @@ std::list StaticAnalyser::GetTargets(const char *file_name) // union of all platforms this file might be a target for. std::list> disks; std::list> tapes; + std::list> cartridges; TargetPlatformType potential_platforms = 0; +#define Insert(list, class, platforms) \ + list.emplace_back(new Storage::class(file_name));\ + potential_platforms |= (TargetPlatformType)(platforms);\ + #define Format(extension, list, class, platforms) \ if(!strcmp(lowercase_extension, extension)) \ { \ @@ -79,12 +86,12 @@ std::list StaticAnalyser::GetTargets(const char *file_name) { // try instantiating as a ROM; failing that accept as a tape try { + Insert(cartridges, Cartridge::PRG, TargetPlatform::Commodore) } catch(...) { try { - tapes.emplace_back(new Storage::Tape::PRG(file_name)); - potential_platforms |= (TargetPlatformType)TargetPlatform::Commodore; + Insert(tapes, Tape::PRG, TargetPlatform::Commodore) } catch(...) {} } } @@ -98,6 +105,7 @@ std::list StaticAnalyser::GetTargets(const char *file_name) Format("uef", tapes, Tape::UEF, TargetPlatform::Acorn) // UEF (tape) #undef Format +#undef Insert // Hand off to platform-specific determination of whether these things are actually compatible and, // if so, how to load them. (TODO) diff --git a/StaticAnalyser/StaticAnalyser.hpp b/StaticAnalyser/StaticAnalyser.hpp index f229128b1..d7810ada9 100644 --- a/StaticAnalyser/StaticAnalyser.hpp +++ b/StaticAnalyser/StaticAnalyser.hpp @@ -11,6 +11,8 @@ #include "../Storage/Tape/Tape.hpp" #include "../Storage/Disk/Disk.hpp" +#include "../Storage/Cartridge/Cartridge.hpp" + #include #include #include @@ -54,7 +56,7 @@ struct Target { std::list> disks; std::list> tapes; - // TODO: ROMs. Probably can't model as raw data, but then how to handle bus complexities? + std::list> cartridges; }; std::list GetTargets(const char *file_name); diff --git a/Storage/ROM/ROM.cpp b/Storage/Cartridge/Cartridge.cpp similarity index 84% rename from Storage/ROM/ROM.cpp rename to Storage/Cartridge/Cartridge.cpp index 904e7a527..eef001e7b 100644 --- a/Storage/ROM/ROM.cpp +++ b/Storage/Cartridge/Cartridge.cpp @@ -6,4 +6,4 @@ // Copyright © 2016 Thomas Harte. All rights reserved. // -#include "ROM.hpp" +#include "Cartridge.hpp" diff --git a/Storage/ROM/ROM.hpp b/Storage/Cartridge/Cartridge.hpp similarity index 59% rename from Storage/ROM/ROM.hpp rename to Storage/Cartridge/Cartridge.hpp index 08ffd024c..902684072 100644 --- a/Storage/ROM/ROM.hpp +++ b/Storage/Cartridge/Cartridge.hpp @@ -1,18 +1,18 @@ // -// ROM.hpp +// Cartridge.hpp // Clock Signal // // Created by Thomas Harte on 27/08/2016. // Copyright © 2016 Thomas Harte. All rights reserved. // -#ifndef ROM_hpp -#define ROM_hpp +#ifndef Storage_Cartridge_hpp +#define Storage_Cartridge_hpp namespace Storage { -namespace ROM { +namespace Cartridge { -class ROM {}; +class Cartridge {}; } } diff --git a/Storage/ROM/PRG.cpp b/Storage/Cartridge/Formats/PRG.cpp similarity index 97% rename from Storage/ROM/PRG.cpp rename to Storage/Cartridge/Formats/PRG.cpp index 66ea7c78d..422edd621 100644 --- a/Storage/ROM/PRG.cpp +++ b/Storage/Cartridge/Formats/PRG.cpp @@ -11,7 +11,7 @@ #include #include -using namespace Storage::ROM; +using namespace Storage::Cartridge; PRG::PRG(const char *file_name) : _contents(nullptr) { diff --git a/Storage/ROM/PRG.hpp b/Storage/Cartridge/Formats/PRG.hpp similarity index 69% rename from Storage/ROM/PRG.hpp rename to Storage/Cartridge/Formats/PRG.hpp index decd58ab6..2ad1adefc 100644 --- a/Storage/ROM/PRG.hpp +++ b/Storage/Cartridge/Formats/PRG.hpp @@ -6,16 +6,16 @@ // Copyright © 2016 Thomas Harte. All rights reserved. // -#ifndef PRG_hpp -#define PRG_hpp +#ifndef Storage_Cartridge_PRG_hpp +#define Storage_Cartridge_PRG_hpp #include -#include "ROM.hpp" +#include "../Cartridge.hpp" namespace Storage { -namespace ROM { +namespace Cartridge { -class PRG : public ROM { +class PRG : public Cartridge { public: PRG(const char *file_name); ~PRG(); From d9f00651543bb2b1891c5911e0f759956e55befa Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 28 Aug 2016 12:20:40 -0400 Subject: [PATCH 12/66] Sketched out just enough classes to get through the get-contents-into-memory step of static analysis. --- .../Clock Signal.xcodeproj/project.pbxproj | 12 +++++ StaticAnalyser/StaticAnalyser.cpp | 40 +++++++---------- StaticAnalyser/StaticAnalyser.hpp | 21 ++++++--- Storage/Cartridge/Cartridge.hpp | 45 ++++++++++++++++++- Storage/Cartridge/Formats/A26.cpp | 15 +++++++ Storage/Cartridge/Formats/A26.hpp | 30 +++++++++++++ Storage/Cartridge/Formats/AcornROM.cpp | 15 +++++++ Storage/Cartridge/Formats/AcornROM.hpp | 29 ++++++++++++ Storage/Cartridge/Formats/PRG.cpp | 22 +++++---- Storage/Cartridge/Formats/PRG.hpp | 6 --- 10 files changed, 184 insertions(+), 51 deletions(-) create mode 100644 Storage/Cartridge/Formats/A26.cpp create mode 100644 Storage/Cartridge/Formats/A26.hpp create mode 100644 Storage/Cartridge/Formats/AcornROM.cpp create mode 100644 Storage/Cartridge/Formats/AcornROM.hpp diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 9d63cc952..e33b3cec4 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -26,6 +26,8 @@ 4B2E2D951C399D1200138695 /* ElectronDocument.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B2E2D931C399D1200138695 /* ElectronDocument.xib */; }; 4B2E2D9A1C3A06EC00138695 /* Atari2600.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2E2D971C3A06EC00138695 /* Atari2600.cpp */; }; 4B2E2D9D1C3A070400138695 /* Electron.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2E2D9B1C3A070400138695 /* Electron.cpp */; }; + 4B37EE7F1D734596006A09A4 /* A26.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B37EE7D1D734596006A09A4 /* A26.cpp */; }; + 4B37EE821D7345A6006A09A4 /* AcornROM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B37EE801D7345A6006A09A4 /* AcornROM.cpp */; }; 4B3BA0C31D318AEC005DD7A7 /* C1540Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B3BA0C21D318AEB005DD7A7 /* C1540Tests.swift */; }; 4B3BA0CE1D318B44005DD7A7 /* C1540Bridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B3BA0C61D318B44005DD7A7 /* C1540Bridge.mm */; }; 4B3BA0CF1D318B44005DD7A7 /* MOS6522Bridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B3BA0C91D318B44005DD7A7 /* MOS6522Bridge.mm */; }; @@ -406,6 +408,10 @@ 4B2E2D991C3A06EC00138695 /* Atari2600Inputs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Atari2600Inputs.h; sourceTree = ""; }; 4B2E2D9B1C3A070400138695 /* Electron.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Electron.cpp; path = Electron/Electron.cpp; sourceTree = ""; }; 4B2E2D9C1C3A070400138695 /* Electron.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Electron.hpp; path = Electron/Electron.hpp; sourceTree = ""; }; + 4B37EE7D1D734596006A09A4 /* A26.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = A26.cpp; sourceTree = ""; }; + 4B37EE7E1D734596006A09A4 /* A26.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = A26.hpp; sourceTree = ""; }; + 4B37EE801D7345A6006A09A4 /* AcornROM.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AcornROM.cpp; sourceTree = ""; }; + 4B37EE811D7345A6006A09A4 /* AcornROM.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = AcornROM.hpp; sourceTree = ""; }; 4B3BA0C21D318AEB005DD7A7 /* C1540Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = C1540Tests.swift; sourceTree = ""; }; 4B3BA0C51D318B44005DD7A7 /* C1540Bridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = C1540Bridge.h; sourceTree = ""; }; 4B3BA0C61D318B44005DD7A7 /* C1540Bridge.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = C1540Bridge.mm; sourceTree = ""; }; @@ -1545,6 +1551,10 @@ children = ( 4BEE0A6D1D72496600532C7B /* PRG.cpp */, 4BEE0A6E1D72496600532C7B /* PRG.hpp */, + 4B37EE7D1D734596006A09A4 /* A26.cpp */, + 4B37EE7E1D734596006A09A4 /* A26.hpp */, + 4B37EE801D7345A6006A09A4 /* AcornROM.cpp */, + 4B37EE811D7345A6006A09A4 /* AcornROM.hpp */, ); path = Formats; sourceTree = ""; @@ -1972,6 +1982,7 @@ 4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */, 4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */, 4BB697C71D4B558F00248BDF /* Factors.cpp in Sources */, + 4B37EE7F1D734596006A09A4 /* A26.cpp in Sources */, 4B55CE591C3B7D360093A61B /* ElectronDocument.swift in Sources */, 4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */, 4BC3B74F1CD194CC00F86E85 /* Shader.cpp in Sources */, @@ -2010,6 +2021,7 @@ 4B2A53A01D117D36003C6002 /* CSMachine.mm in Sources */, 4BC91B831D1F160E00884B76 /* CommodoreTAP.cpp in Sources */, 4B2A539F1D117D36003C6002 /* CSAudioQueue.m in Sources */, + 4B37EE821D7345A6006A09A4 /* AcornROM.cpp in Sources */, 4BB73EA21B587A5100552FC2 /* AppDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/StaticAnalyser/StaticAnalyser.cpp b/StaticAnalyser/StaticAnalyser.cpp index a808a1bc0..9c0f39ea6 100644 --- a/StaticAnalyser/StaticAnalyser.cpp +++ b/StaticAnalyser/StaticAnalyser.cpp @@ -10,6 +10,8 @@ #include +#include "../Storage/Cartridge/Formats/A26.hpp" +#include "../Storage/Cartridge/Formats/AcornROM.hpp" #include "../Storage/Cartridge/Formats/PRG.hpp" #include "../Storage/Disk/Formats/D64.hpp" @@ -26,7 +28,6 @@ enum class TargetPlatform: TargetPlatformType { Commodore = 1 << 2 }; - using namespace StaticAnalyser; std::list StaticAnalyser::GetTargets(const char *file_name) @@ -59,27 +60,21 @@ std::list StaticAnalyser::GetTargets(const char *file_name) list.emplace_back(new Storage::class(file_name));\ potential_platforms |= (TargetPlatformType)(platforms);\ +#define TryInsert(list, class, platforms) \ + try {\ + Insert(list, class, platforms) \ + } catch(...) {} + #define Format(extension, list, class, platforms) \ if(!strcmp(lowercase_extension, extension)) \ { \ - try { \ - list.emplace_back(new Storage::class(file_name));\ - potential_platforms |= (TargetPlatformType)(platforms);\ - } catch(...) {}\ + TryInsert(list, class, platforms) \ } - // A26 - if(!strcmp(lowercase_extension, "a26")) - { - } - - // BIN - if(!strcmp(lowercase_extension, "bin")) - { - } - - Format("d64", disks, Disk::D64, TargetPlatform::Commodore) // D64 - Format("g64", disks, Disk::G64, TargetPlatform::Commodore) // G64 + Format("a26", cartridges, Cartridge::A26, TargetPlatform::Atari2600) // A26 + Format("bin", cartridges, Cartridge::A26, TargetPlatform::Atari2600) // BIN + Format("d64", disks, Disk::D64, TargetPlatform::Commodore) // D64 + Format("g64", disks, Disk::G64, TargetPlatform::Commodore) // G64 // PRG if(!strcmp(lowercase_extension, "prg")) @@ -97,12 +92,9 @@ std::list StaticAnalyser::GetTargets(const char *file_name) } // ROM - if(!strcmp(lowercase_extension, "rom")) - { - } - - Format("tap", tapes, Tape::CommodoreTAP, TargetPlatform::Commodore) // TAP - Format("uef", tapes, Tape::UEF, TargetPlatform::Acorn) // UEF (tape) + Format("rom", cartridges, Cartridge::AcornROM, TargetPlatform::Acorn) // ROM + Format("tap", tapes, Tape::CommodoreTAP, TargetPlatform::Commodore) // TAP + Format("uef", tapes, Tape::UEF, TargetPlatform::Acorn) // UEF (tape) #undef Format #undef Insert @@ -110,8 +102,6 @@ std::list StaticAnalyser::GetTargets(const char *file_name) // Hand off to platform-specific determination of whether these things are actually compatible and, // if so, how to load them. (TODO) - printf("Lowercase extension: %s", lowercase_extension); - free(lowercase_extension); return targets; } diff --git a/StaticAnalyser/StaticAnalyser.hpp b/StaticAnalyser/StaticAnalyser.hpp index d7810ada9..23d60867c 100644 --- a/StaticAnalyser/StaticAnalyser.hpp +++ b/StaticAnalyser/StaticAnalyser.hpp @@ -19,14 +19,16 @@ namespace StaticAnalyser { -enum Machine { - Atari2600, - Electron, - Vic20 -}; - +/*! + A list of disks, tapes and cartridges plus information about the machine to which to attach them and its configuration, + and instructions on how to launch the software attached, plus a measure of confidence in this target's correctness. +*/ struct Target { - Machine machine; + enum { + Atari2600, + Electron, + Vic20 + } machine; float probability; union { @@ -59,6 +61,11 @@ struct Target { std::list> cartridges; }; +/*! + Attempts, through any available means, to return a list of potential targets for the file with the given name. + + @returns The list of potential targets, sorted from most to least probable. +*/ std::list GetTargets(const char *file_name); } diff --git a/Storage/Cartridge/Cartridge.hpp b/Storage/Cartridge/Cartridge.hpp index 902684072..19e4a48d2 100644 --- a/Storage/Cartridge/Cartridge.hpp +++ b/Storage/Cartridge/Cartridge.hpp @@ -9,10 +9,53 @@ #ifndef Storage_Cartridge_hpp #define Storage_Cartridge_hpp +#include +#include +#include + namespace Storage { namespace Cartridge { -class Cartridge {}; +/*! + Provides a base class for cartridges; the bus provided to cartridges and therefore + the interface they support is extremely machine-dependent so unlike disks and tapes, + no model is imposed; this class seeks merely to be a base class for fully-descriptive + summaries of the contents of emulator files that themselves describe cartridges. + + Consumers will almost certainly seek to dynamic_cast to something more appropriate, + however some cartridge container formats have no exposition beyond the ROM dump, + making the base class 100% descriptive. +*/ +class Cartridge { + public: + struct Segment { + Segment(int start_address, int end_address, std::vector data) : + start_address(start_address), end_address(end_address), data(std::move(data)) {} + + /// Indicates that an address is unknown. + static const int UnknownAddress; + + /// The initial CPU-exposed starting address for this segment; may be @c UnknownAddress. + int start_address; + /*! + The initial CPU-exposed ending address for this segment; may be @c UnknownAddress. Not necessarily equal + to start_address + data_length due to potential paging. + */ + int end_address; + + /*! + The data contents for this segment. If @c start_address and @c end_address are suppled then + the first end_address - start_address bytes will be those initially visible. The size will + not necessarily be the same as @c end_address - @c start_address due to potential paging. + */ + std::vector data; + }; + + const std::list &get_segments() { return _segments; } + + protected: + std::list _segments; +}; } } diff --git a/Storage/Cartridge/Formats/A26.cpp b/Storage/Cartridge/Formats/A26.cpp new file mode 100644 index 000000000..9bed6998e --- /dev/null +++ b/Storage/Cartridge/Formats/A26.cpp @@ -0,0 +1,15 @@ +// +// A26.cpp +// Clock Signal +// +// Created by Thomas Harte on 28/08/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#include "A26.hpp" + +using namespace Storage::Cartridge; + +A26::A26(const char *file_name) +{ +} diff --git a/Storage/Cartridge/Formats/A26.hpp b/Storage/Cartridge/Formats/A26.hpp new file mode 100644 index 000000000..6aa024568 --- /dev/null +++ b/Storage/Cartridge/Formats/A26.hpp @@ -0,0 +1,30 @@ +// +// A26.hpp +// Clock Signal +// +// Created by Thomas Harte on 28/08/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#ifndef Storage_Cartridge_A26_hpp +#define Storage_Cartridge_A26_hpp + +#include "../Cartridge.hpp" + +namespace Storage { +namespace Cartridge { + +class A26 : public Cartridge { + public: + A26(const char *file_name); + + enum { + ErrorNotAcornROM + }; +}; + +} +} + + +#endif /* A26_hpp */ diff --git a/Storage/Cartridge/Formats/AcornROM.cpp b/Storage/Cartridge/Formats/AcornROM.cpp new file mode 100644 index 000000000..f6d29742a --- /dev/null +++ b/Storage/Cartridge/Formats/AcornROM.cpp @@ -0,0 +1,15 @@ +// +// AcornROM.cpp +// Clock Signal +// +// Created by Thomas Harte on 28/08/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#include "AcornROM.hpp" + +using namespace Storage::Cartridge; + +AcornROM::AcornROM(const char *file_name) +{ +} diff --git a/Storage/Cartridge/Formats/AcornROM.hpp b/Storage/Cartridge/Formats/AcornROM.hpp new file mode 100644 index 000000000..f5f923ce7 --- /dev/null +++ b/Storage/Cartridge/Formats/AcornROM.hpp @@ -0,0 +1,29 @@ +// +// AcornROM.hpp +// Clock Signal +// +// Created by Thomas Harte on 28/08/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#ifndef Storage_Cartridge_AcornROM_hpp +#define Storage_Cartridge_AcornROM_hpp + +#include "../Cartridge.hpp" + +namespace Storage { +namespace Cartridge { + +class AcornROM : public Cartridge { + public: + AcornROM(const char *file_name); + + enum { + ErrorNotAcornROM + }; +}; + +} +} + +#endif /* AcornROM_hpp */ diff --git a/Storage/Cartridge/Formats/PRG.cpp b/Storage/Cartridge/Formats/PRG.cpp index 422edd621..fcbfd36a7 100644 --- a/Storage/Cartridge/Formats/PRG.cpp +++ b/Storage/Cartridge/Formats/PRG.cpp @@ -13,7 +13,7 @@ using namespace Storage::Cartridge; -PRG::PRG(const char *file_name) : _contents(nullptr) +PRG::PRG(const char *file_name) { struct stat file_stats; stat(file_name, &file_stats); @@ -32,8 +32,9 @@ PRG::PRG(const char *file_name) : _contents(nullptr) int loading_address = fgetc(file); loading_address |= fgetc(file) << 8; - _contents = new uint8_t[file_stats.st_size - 2]; - fread(_contents, 1, (size_t)(file_stats.st_size - 2), file); + size_t data_length = (size_t)file_stats.st_size - 2; + std::vector contents(data_length); + fread(&contents[0], 1, (size_t)(data_length), file); fclose(file); // accept only files intended to load at 0xa000 @@ -42,15 +43,12 @@ PRG::PRG(const char *file_name) : _contents(nullptr) // also accept only cartridges with the proper signature if( - _contents[4] != 0x41 || - _contents[5] != 0x30 || - _contents[6] != 0xc3 || - _contents[7] != 0xc2 || - _contents[8] != 0xcd) + contents[4] != 0x41 || + contents[5] != 0x30 || + contents[6] != 0xc3 || + contents[7] != 0xc2 || + contents[8] != 0xcd) throw ErrorNotROM; -} -PRG::~PRG() -{ - delete[] _contents; + _segments.emplace_back(0xa000, 0xa000 + data_length, std::move(contents)); } diff --git a/Storage/Cartridge/Formats/PRG.hpp b/Storage/Cartridge/Formats/PRG.hpp index 2ad1adefc..00722d8e2 100644 --- a/Storage/Cartridge/Formats/PRG.hpp +++ b/Storage/Cartridge/Formats/PRG.hpp @@ -9,7 +9,6 @@ #ifndef Storage_Cartridge_PRG_hpp #define Storage_Cartridge_PRG_hpp -#include #include "../Cartridge.hpp" namespace Storage { @@ -18,15 +17,10 @@ namespace Cartridge { class PRG : public Cartridge { public: PRG(const char *file_name); - ~PRG(); enum { ErrorNotROM }; - - private: - uint8_t *_contents; - uint16_t _size; }; } From 29c972f4b86d93dcfcf486ccf7d003ab5c929474 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 28 Aug 2016 12:43:17 -0400 Subject: [PATCH 13/66] Added hacky segue into analysis for all Electron formats. Added analyser to try to differentiate Acorn-format ROMs from other things called .rom, which are likely to be numerous. --- .../Documents/ElectronDocument.swift | 2 + .../Machine/Wrappers/CSElectron.h | 2 + .../Machine/Wrappers/CSElectron.mm | 5 +++ Storage/Cartridge/Formats/AcornROM.cpp | 44 +++++++++++++++++++ 4 files changed, 53 insertions(+) diff --git a/OSBindings/Mac/Clock Signal/Documents/ElectronDocument.swift b/OSBindings/Mac/Clock Signal/Documents/ElectronDocument.swift index e2981da95..55c1761e7 100644 --- a/OSBindings/Mac/Clock Signal/Documents/ElectronDocument.swift +++ b/OSBindings/Mac/Clock Signal/Documents/ElectronDocument.swift @@ -45,6 +45,8 @@ class ElectronDocument: MachineDocument { } override func readFromURL(url: NSURL, ofType typeName: String) throws { + electron.analyse(url) + if let pathExtension = url.pathExtension { switch pathExtension.lowercaseString { case "uef": diff --git a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSElectron.h b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSElectron.h index 4d23a3536..61e3754d8 100644 --- a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSElectron.h +++ b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSElectron.h @@ -17,6 +17,8 @@ - (void)setROM:(nonnull NSData *)rom slot:(int)slot; - (BOOL)openUEFAtURL:(nonnull NSURL *)URL; +- (void)analyse:(NSURL *)url; + @property (nonatomic, assign) BOOL useFastLoadingHack; @property (nonatomic, assign) BOOL useTelevisionOutput; diff --git a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSElectron.mm b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSElectron.mm index 4da7966e5..bda7bf9d3 100644 --- a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSElectron.mm +++ b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSElectron.mm @@ -10,6 +10,7 @@ #include "Electron.hpp" #import "CSMachine+Subclassing.h" +#include "StaticAnalyser.hpp" #include "TapeUEF.hpp" @implementation CSElectron { @@ -20,6 +21,10 @@ return &_electron; } +- (void)analyse:(NSURL *)url { + StaticAnalyser::GetTargets([url fileSystemRepresentation]); +} + - (void)setOSROM:(nonnull NSData *)rom { @synchronized(self) { _electron.set_rom(Electron::ROMSlotOS, rom.length, (const uint8_t *)rom.bytes); diff --git a/Storage/Cartridge/Formats/AcornROM.cpp b/Storage/Cartridge/Formats/AcornROM.cpp index f6d29742a..afa397353 100644 --- a/Storage/Cartridge/Formats/AcornROM.cpp +++ b/Storage/Cartridge/Formats/AcornROM.cpp @@ -8,8 +8,52 @@ #include "AcornROM.hpp" +#include +#include + using namespace Storage::Cartridge; AcornROM::AcornROM(const char *file_name) { + // the file should be exactly 16 kb + struct stat file_stats; + stat(file_name, &file_stats); + if(file_stats.st_size != 0x4000) throw ErrorNotAcornROM; + + // grab contents + FILE *file = fopen(file_name, "rb"); + if(!file) throw ErrorNotAcornROM; + size_t data_length = (size_t)file_stats.st_size; + std::vector contents(data_length); + fread(&contents[0], 1, (size_t)(data_length), file); + fclose(file); + + // perform sanity checks... + + // is a copyright string present? + uint8_t copyright_offset = contents[7]; + if( + contents[copyright_offset] != 0x00 || + contents[copyright_offset+1] != 0x28 || + contents[copyright_offset+2] != 0x43 || + contents[copyright_offset+3] != 0x29 + ) throw ErrorNotAcornROM; + + // is the language entry point valid? + if(!( + (contents[0] == 0x00 && contents[1] == 0x00 && contents[2] == 0x00) || + (contents[0] != 0x00 && contents[2] >= 0x80 && contents[2] < 0xc0) + )) throw ErrorNotAcornROM; + + // is the service entry point valid? + if(!(contents[5] >= 0x80 && contents[5] < 0xc0)) throw ErrorNotAcornROM; + + // probability of a random binary blob that isn't an Acorn ROM proceeding to here: + // 1/(2^32) * + // ( ((2^24)-1)/(2^24)*(1/4) + 1/(2^24) ) * + // 1/4 + // = something very improbable — around 1/16th of 1 in 2^32, but not exactly. + + // enshrine + _segments.emplace_back(0x8000, 0xc000, std::move(contents)); } From d1abfc040c9df49efbc9966f282b93e84bda00ba Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 29 Aug 2016 08:48:49 -0400 Subject: [PATCH 14/66] Addressed my dithering here: the file format containers themselves should do nothing but inspect the data to find out whether it is of the correct format. The machine steps are there for machine-specific validation. So it's probably easier to treat a binary ROM image just as a binary ROM image. Therefore, the Acorn-specific .rom detection is now in an Acorn-specific area. --- .../Clock Signal.xcodeproj/project.pbxproj | 32 +++++---- .../Machine/Wrappers/CSElectron.h | 2 +- StaticAnalyser/Acorn/AcornAnalyser.cpp | 67 +++++++++++++++++++ StaticAnalyser/Acorn/AcornAnalyser.hpp | 27 ++++++++ StaticAnalyser/StaticAnalyser.cpp | 23 +++++-- Storage/Cartridge/Cartridge.cpp | 2 + Storage/Cartridge/Formats/A26.cpp | 15 ----- Storage/Cartridge/Formats/A26.hpp | 30 --------- Storage/Cartridge/Formats/AcornROM.cpp | 59 ---------------- Storage/Cartridge/Formats/BinaryDump.cpp | 35 ++++++++++ .../Formats/{AcornROM.hpp => BinaryDump.hpp} | 12 ++-- 11 files changed, 174 insertions(+), 130 deletions(-) create mode 100644 StaticAnalyser/Acorn/AcornAnalyser.cpp create mode 100644 StaticAnalyser/Acorn/AcornAnalyser.hpp delete mode 100644 Storage/Cartridge/Formats/A26.cpp delete mode 100644 Storage/Cartridge/Formats/A26.hpp delete mode 100644 Storage/Cartridge/Formats/AcornROM.cpp create mode 100644 Storage/Cartridge/Formats/BinaryDump.cpp rename Storage/Cartridge/Formats/{AcornROM.hpp => BinaryDump.hpp} (56%) diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index e33b3cec4..3b4cf2a74 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -26,8 +26,7 @@ 4B2E2D951C399D1200138695 /* ElectronDocument.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B2E2D931C399D1200138695 /* ElectronDocument.xib */; }; 4B2E2D9A1C3A06EC00138695 /* Atari2600.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2E2D971C3A06EC00138695 /* Atari2600.cpp */; }; 4B2E2D9D1C3A070400138695 /* Electron.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2E2D9B1C3A070400138695 /* Electron.cpp */; }; - 4B37EE7F1D734596006A09A4 /* A26.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B37EE7D1D734596006A09A4 /* A26.cpp */; }; - 4B37EE821D7345A6006A09A4 /* AcornROM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B37EE801D7345A6006A09A4 /* AcornROM.cpp */; }; + 4B37EE821D7345A6006A09A4 /* BinaryDump.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B37EE801D7345A6006A09A4 /* BinaryDump.cpp */; }; 4B3BA0C31D318AEC005DD7A7 /* C1540Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B3BA0C21D318AEB005DD7A7 /* C1540Tests.swift */; }; 4B3BA0CE1D318B44005DD7A7 /* C1540Bridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B3BA0C61D318B44005DD7A7 /* C1540Bridge.mm */; }; 4B3BA0CF1D318B44005DD7A7 /* MOS6522Bridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B3BA0C91D318B44005DD7A7 /* MOS6522Bridge.mm */; }; @@ -341,6 +340,7 @@ 4BC9DF451D044FCA00F44158 /* ROMImages in Resources */ = {isa = PBXBuildFile; fileRef = 4BC9DF441D044FCA00F44158 /* ROMImages */; }; 4BC9DF4F1D04691600F44158 /* 6560.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC9DF4D1D04691600F44158 /* 6560.cpp */; }; 4BC9E1EE1D23449A003FCEE4 /* 6502InterruptTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC9E1ED1D23449A003FCEE4 /* 6502InterruptTests.swift */; }; + 4BD14B111D74627C0088EAD6 /* AcornAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD14B0F1D74627C0088EAD6 /* AcornAnalyser.cpp */; }; 4BD5F1951D13528900631CD1 /* CSBestEffortUpdater.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.m */; }; 4BEE0A6F1D72496600532C7B /* Cartridge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE0A6A1D72496600532C7B /* Cartridge.cpp */; }; 4BEE0A701D72496600532C7B /* PRG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE0A6D1D72496600532C7B /* PRG.cpp */; }; @@ -408,10 +408,8 @@ 4B2E2D991C3A06EC00138695 /* Atari2600Inputs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Atari2600Inputs.h; sourceTree = ""; }; 4B2E2D9B1C3A070400138695 /* Electron.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Electron.cpp; path = Electron/Electron.cpp; sourceTree = ""; }; 4B2E2D9C1C3A070400138695 /* Electron.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Electron.hpp; path = Electron/Electron.hpp; sourceTree = ""; }; - 4B37EE7D1D734596006A09A4 /* A26.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = A26.cpp; sourceTree = ""; }; - 4B37EE7E1D734596006A09A4 /* A26.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = A26.hpp; sourceTree = ""; }; - 4B37EE801D7345A6006A09A4 /* AcornROM.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AcornROM.cpp; sourceTree = ""; }; - 4B37EE811D7345A6006A09A4 /* AcornROM.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = AcornROM.hpp; sourceTree = ""; }; + 4B37EE801D7345A6006A09A4 /* BinaryDump.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BinaryDump.cpp; sourceTree = ""; }; + 4B37EE811D7345A6006A09A4 /* BinaryDump.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = BinaryDump.hpp; sourceTree = ""; }; 4B3BA0C21D318AEB005DD7A7 /* C1540Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = C1540Tests.swift; sourceTree = ""; }; 4B3BA0C51D318B44005DD7A7 /* C1540Bridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = C1540Bridge.h; sourceTree = ""; }; 4B3BA0C61D318B44005DD7A7 /* C1540Bridge.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = C1540Bridge.mm; sourceTree = ""; }; @@ -767,6 +765,8 @@ 4BC9DF4E1D04691600F44158 /* 6560.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 6560.hpp; sourceTree = ""; }; 4BC9E1ED1D23449A003FCEE4 /* 6502InterruptTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 6502InterruptTests.swift; sourceTree = ""; }; 4BCA98C21D065CA20062F44C /* 6522.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 6522.hpp; sourceTree = ""; }; + 4BD14B0F1D74627C0088EAD6 /* AcornAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AcornAnalyser.cpp; path = ../../StaticAnalyser/Acorn/AcornAnalyser.cpp; sourceTree = ""; }; + 4BD14B101D74627C0088EAD6 /* AcornAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = AcornAnalyser.hpp; path = ../../StaticAnalyser/Acorn/AcornAnalyser.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 = ""; }; 4BEE0A6A1D72496600532C7B /* Cartridge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Cartridge.cpp; sourceTree = ""; }; @@ -1519,6 +1519,15 @@ path = 6560; sourceTree = ""; }; + 4BD14B121D7462810088EAD6 /* Acorn */ = { + isa = PBXGroup; + children = ( + 4BD14B0F1D74627C0088EAD6 /* AcornAnalyser.cpp */, + 4BD14B101D74627C0088EAD6 /* AcornAnalyser.hpp */, + ); + name = Acorn; + sourceTree = ""; + }; 4BD5F1961D1352A000631CD1 /* Updater */ = { isa = PBXGroup; children = ( @@ -1551,10 +1560,8 @@ children = ( 4BEE0A6D1D72496600532C7B /* PRG.cpp */, 4BEE0A6E1D72496600532C7B /* PRG.hpp */, - 4B37EE7D1D734596006A09A4 /* A26.cpp */, - 4B37EE7E1D734596006A09A4 /* A26.hpp */, - 4B37EE801D7345A6006A09A4 /* AcornROM.cpp */, - 4B37EE811D7345A6006A09A4 /* AcornROM.hpp */, + 4B37EE801D7345A6006A09A4 /* BinaryDump.cpp */, + 4B37EE811D7345A6006A09A4 /* BinaryDump.hpp */, ); path = Formats; sourceTree = ""; @@ -1565,6 +1572,7 @@ 4BF1354A1D6D2C300054B2EA /* StaticAnalyser.cpp */, 4BF1354B1D6D2C300054B2EA /* StaticAnalyser.hpp */, 4BC830D21D6E7C6D0000A26F /* Commodore */, + 4BD14B121D7462810088EAD6 /* Acorn */, ); name = StaticAnalyser; sourceTree = ""; @@ -1979,10 +1987,10 @@ 4BAB62AD1D3272D200DF5BA0 /* Disk.cpp in Sources */, 4BC9DF4F1D04691600F44158 /* 6560.cpp in Sources */, 4BB697CE1D4BA44400248BDF /* CommodoreGCR.cpp in Sources */, + 4BD14B111D74627C0088EAD6 /* AcornAnalyser.cpp in Sources */, 4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */, 4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */, 4BB697C71D4B558F00248BDF /* Factors.cpp in Sources */, - 4B37EE7F1D734596006A09A4 /* A26.cpp in Sources */, 4B55CE591C3B7D360093A61B /* ElectronDocument.swift in Sources */, 4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */, 4BC3B74F1CD194CC00F86E85 /* Shader.cpp in Sources */, @@ -2021,7 +2029,7 @@ 4B2A53A01D117D36003C6002 /* CSMachine.mm in Sources */, 4BC91B831D1F160E00884B76 /* CommodoreTAP.cpp in Sources */, 4B2A539F1D117D36003C6002 /* CSAudioQueue.m in Sources */, - 4B37EE821D7345A6006A09A4 /* AcornROM.cpp in Sources */, + 4B37EE821D7345A6006A09A4 /* BinaryDump.cpp in Sources */, 4BB73EA21B587A5100552FC2 /* AppDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSElectron.h b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSElectron.h index 61e3754d8..03444749a 100644 --- a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSElectron.h +++ b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSElectron.h @@ -17,7 +17,7 @@ - (void)setROM:(nonnull NSData *)rom slot:(int)slot; - (BOOL)openUEFAtURL:(nonnull NSURL *)URL; -- (void)analyse:(NSURL *)url; +- (void)analyse:(nonnull NSURL *)url; @property (nonatomic, assign) BOOL useFastLoadingHack; @property (nonatomic, assign) BOOL useTelevisionOutput; diff --git a/StaticAnalyser/Acorn/AcornAnalyser.cpp b/StaticAnalyser/Acorn/AcornAnalyser.cpp new file mode 100644 index 000000000..d064aa8b1 --- /dev/null +++ b/StaticAnalyser/Acorn/AcornAnalyser.cpp @@ -0,0 +1,67 @@ +// +// AcornAnalyser.cpp +// Clock Signal +// +// Created by Thomas Harte on 29/08/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#include "AcornAnalyser.hpp" + +using namespace StaticAnalyser::Acorn; + +static std::list> + AcornCartridgesFrom(const std::list> &cartridges) +{ + std::list> acorn_cartridges; + + for(std::shared_ptr cartridge : cartridges) + { + const std::list &segments = cartridge->get_segments(); + + // only one mapped item is allowed + if(segments.size() != 1) continue; + + // which must be 16 kb in size + Storage::Cartridge::Cartridge::Segment segment = segments.front(); + if(segment.data.size() != 0x4000) continue; + + // is a copyright string present? + uint8_t copyright_offset = segment.data[7]; + if( + segment.data[copyright_offset] != 0x00 || + segment.data[copyright_offset+1] != 0x28 || + segment.data[copyright_offset+2] != 0x43 || + segment.data[copyright_offset+3] != 0x29 + ) continue; + + // is the language entry point valid? + if(!( + (segment.data[0] == 0x00 && segment.data[1] == 0x00 && segment.data[2] == 0x00) || + (segment.data[0] != 0x00 && segment.data[2] >= 0x80 && segment.data[2] < 0xc0) + )) continue; + + // is the service entry point valid? + if(!(segment.data[5] >= 0x80 && segment.data[5] < 0xc0)) continue; + + // probability of a random binary blob that isn't an Acorn ROM proceeding to here: + // 1/(2^32) * + // ( ((2^24)-1)/(2^24)*(1/4) + 1/(2^24) ) * + // 1/4 + // = something very improbable — around 1/16th of 1 in 2^32, but not exactly. + acorn_cartridges.push_back(cartridge); + } + + return acorn_cartridges; +} + +void StaticAnalyser::Acorn::AddTargets( + const std::list> &disks, + const std::list> &tapes, + const std::list> &cartridges, + std::list &destination) +{ + // strip out inappropriate cartridges + std::list> acornCartridges = AcornCartridgesFrom(cartridges); +} + diff --git a/StaticAnalyser/Acorn/AcornAnalyser.hpp b/StaticAnalyser/Acorn/AcornAnalyser.hpp new file mode 100644 index 000000000..ccfaa75f3 --- /dev/null +++ b/StaticAnalyser/Acorn/AcornAnalyser.hpp @@ -0,0 +1,27 @@ +// +// AcornAnalyser.hpp +// Clock Signal +// +// Created by Thomas Harte on 29/08/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#ifndef AcornAnalyser_hpp +#define AcornAnalyser_hpp + +#include "../StaticAnalyser.hpp" + +namespace StaticAnalyser { +namespace Acorn { + +void AddTargets( + const std::list> &disks, + const std::list> &tapes, + const std::list> &cartridges, + std::list &destination +); + +} +} + +#endif /* AcornAnalyser_hpp */ diff --git a/StaticAnalyser/StaticAnalyser.cpp b/StaticAnalyser/StaticAnalyser.cpp index 9c0f39ea6..404da6e8c 100644 --- a/StaticAnalyser/StaticAnalyser.cpp +++ b/StaticAnalyser/StaticAnalyser.cpp @@ -10,13 +10,18 @@ #include -#include "../Storage/Cartridge/Formats/A26.hpp" -#include "../Storage/Cartridge/Formats/AcornROM.hpp" +// Analysers +#include "Acorn/AcornAnalyser.hpp" + +// Cartridges +#include "../Storage/Cartridge/Formats/BinaryDump.hpp" #include "../Storage/Cartridge/Formats/PRG.hpp" +// Disks #include "../Storage/Disk/Formats/D64.hpp" #include "../Storage/Disk/Formats/G64.hpp" +// Tapes #include "../Storage/Tape/Formats/CommodoreTAP.hpp" #include "../Storage/Tape/Formats/TapePRG.hpp" #include "../Storage/Tape/Formats/TapeUEF.hpp" @@ -71,10 +76,10 @@ std::list StaticAnalyser::GetTargets(const char *file_name) TryInsert(list, class, platforms) \ } - Format("a26", cartridges, Cartridge::A26, TargetPlatform::Atari2600) // A26 - Format("bin", cartridges, Cartridge::A26, TargetPlatform::Atari2600) // BIN - Format("d64", disks, Disk::D64, TargetPlatform::Commodore) // D64 - Format("g64", disks, Disk::G64, TargetPlatform::Commodore) // G64 + Format("a26", cartridges, Cartridge::BinaryDump, TargetPlatform::Atari2600) // A26 + Format("bin", cartridges, Cartridge::BinaryDump, TargetPlatform::Atari2600) // BIN + Format("d64", disks, Disk::D64, TargetPlatform::Commodore) // D64 + Format("g64", disks, Disk::G64, TargetPlatform::Commodore) // G64 // PRG if(!strcmp(lowercase_extension, "prg")) @@ -92,7 +97,7 @@ std::list StaticAnalyser::GetTargets(const char *file_name) } // ROM - Format("rom", cartridges, Cartridge::AcornROM, TargetPlatform::Acorn) // ROM + Format("rom", cartridges, Cartridge::BinaryDump, TargetPlatform::Acorn) // ROM Format("tap", tapes, Tape::CommodoreTAP, TargetPlatform::Commodore) // TAP Format("uef", tapes, Tape::UEF, TargetPlatform::Acorn) // UEF (tape) @@ -101,6 +106,10 @@ std::list StaticAnalyser::GetTargets(const char *file_name) // Hand off to platform-specific determination of whether these things are actually compatible and, // if so, how to load them. (TODO) + if(potential_platforms & (TargetPlatformType)TargetPlatform::Acorn) + { + Acorn::AddTargets(disks, tapes, cartridges, targets); + } free(lowercase_extension); return targets; diff --git a/Storage/Cartridge/Cartridge.cpp b/Storage/Cartridge/Cartridge.cpp index eef001e7b..d197548e1 100644 --- a/Storage/Cartridge/Cartridge.cpp +++ b/Storage/Cartridge/Cartridge.cpp @@ -7,3 +7,5 @@ // #include "Cartridge.hpp" + +const int Storage::Cartridge::Cartridge::Segment::UnknownAddress = -1; diff --git a/Storage/Cartridge/Formats/A26.cpp b/Storage/Cartridge/Formats/A26.cpp deleted file mode 100644 index 9bed6998e..000000000 --- a/Storage/Cartridge/Formats/A26.cpp +++ /dev/null @@ -1,15 +0,0 @@ -// -// A26.cpp -// Clock Signal -// -// Created by Thomas Harte on 28/08/2016. -// Copyright © 2016 Thomas Harte. All rights reserved. -// - -#include "A26.hpp" - -using namespace Storage::Cartridge; - -A26::A26(const char *file_name) -{ -} diff --git a/Storage/Cartridge/Formats/A26.hpp b/Storage/Cartridge/Formats/A26.hpp deleted file mode 100644 index 6aa024568..000000000 --- a/Storage/Cartridge/Formats/A26.hpp +++ /dev/null @@ -1,30 +0,0 @@ -// -// A26.hpp -// Clock Signal -// -// Created by Thomas Harte on 28/08/2016. -// Copyright © 2016 Thomas Harte. All rights reserved. -// - -#ifndef Storage_Cartridge_A26_hpp -#define Storage_Cartridge_A26_hpp - -#include "../Cartridge.hpp" - -namespace Storage { -namespace Cartridge { - -class A26 : public Cartridge { - public: - A26(const char *file_name); - - enum { - ErrorNotAcornROM - }; -}; - -} -} - - -#endif /* A26_hpp */ diff --git a/Storage/Cartridge/Formats/AcornROM.cpp b/Storage/Cartridge/Formats/AcornROM.cpp deleted file mode 100644 index afa397353..000000000 --- a/Storage/Cartridge/Formats/AcornROM.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// -// AcornROM.cpp -// Clock Signal -// -// Created by Thomas Harte on 28/08/2016. -// Copyright © 2016 Thomas Harte. All rights reserved. -// - -#include "AcornROM.hpp" - -#include -#include - -using namespace Storage::Cartridge; - -AcornROM::AcornROM(const char *file_name) -{ - // the file should be exactly 16 kb - struct stat file_stats; - stat(file_name, &file_stats); - if(file_stats.st_size != 0x4000) throw ErrorNotAcornROM; - - // grab contents - FILE *file = fopen(file_name, "rb"); - if(!file) throw ErrorNotAcornROM; - size_t data_length = (size_t)file_stats.st_size; - std::vector contents(data_length); - fread(&contents[0], 1, (size_t)(data_length), file); - fclose(file); - - // perform sanity checks... - - // is a copyright string present? - uint8_t copyright_offset = contents[7]; - if( - contents[copyright_offset] != 0x00 || - contents[copyright_offset+1] != 0x28 || - contents[copyright_offset+2] != 0x43 || - contents[copyright_offset+3] != 0x29 - ) throw ErrorNotAcornROM; - - // is the language entry point valid? - if(!( - (contents[0] == 0x00 && contents[1] == 0x00 && contents[2] == 0x00) || - (contents[0] != 0x00 && contents[2] >= 0x80 && contents[2] < 0xc0) - )) throw ErrorNotAcornROM; - - // is the service entry point valid? - if(!(contents[5] >= 0x80 && contents[5] < 0xc0)) throw ErrorNotAcornROM; - - // probability of a random binary blob that isn't an Acorn ROM proceeding to here: - // 1/(2^32) * - // ( ((2^24)-1)/(2^24)*(1/4) + 1/(2^24) ) * - // 1/4 - // = something very improbable — around 1/16th of 1 in 2^32, but not exactly. - - // enshrine - _segments.emplace_back(0x8000, 0xc000, std::move(contents)); -} diff --git a/Storage/Cartridge/Formats/BinaryDump.cpp b/Storage/Cartridge/Formats/BinaryDump.cpp new file mode 100644 index 000000000..37e2a8ea7 --- /dev/null +++ b/Storage/Cartridge/Formats/BinaryDump.cpp @@ -0,0 +1,35 @@ +// +// BinaryDump.cpp +// Clock Signal +// +// Created by Thomas Harte on 28/08/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#include "BinaryDump.hpp" + +#include +#include + +using namespace Storage::Cartridge; + +BinaryDump::BinaryDump(const char *file_name) +{ + // the file should be exactly 16 kb + struct stat file_stats; + stat(file_name, &file_stats); + + // grab contents + FILE *file = fopen(file_name, "rb"); + if(!file) throw ErrorNotAccessible; + size_t data_length = (size_t)file_stats.st_size; + std::vector contents(data_length); + fread(&contents[0], 1, (size_t)(data_length), file); + fclose(file); + + // enshrine + _segments.emplace_back( + ::Storage::Cartridge::Cartridge::Segment::UnknownAddress, + ::Storage::Cartridge::Cartridge::Segment::UnknownAddress, + std::move(contents)); +} diff --git a/Storage/Cartridge/Formats/AcornROM.hpp b/Storage/Cartridge/Formats/BinaryDump.hpp similarity index 56% rename from Storage/Cartridge/Formats/AcornROM.hpp rename to Storage/Cartridge/Formats/BinaryDump.hpp index f5f923ce7..b9d65a390 100644 --- a/Storage/Cartridge/Formats/AcornROM.hpp +++ b/Storage/Cartridge/Formats/BinaryDump.hpp @@ -1,25 +1,25 @@ // -// AcornROM.hpp +// BinaryDump.hpp // Clock Signal // // Created by Thomas Harte on 28/08/2016. // Copyright © 2016 Thomas Harte. All rights reserved. // -#ifndef Storage_Cartridge_AcornROM_hpp -#define Storage_Cartridge_AcornROM_hpp +#ifndef Storage_Cartridge_BinaryDump_hpp +#define Storage_Cartridge_BinaryDump_hpp #include "../Cartridge.hpp" namespace Storage { namespace Cartridge { -class AcornROM : public Cartridge { +class BinaryDump : public Cartridge { public: - AcornROM(const char *file_name); + BinaryDump(const char *file_name); enum { - ErrorNotAcornROM + ErrorNotAccessible }; }; From 0032ad2634171ce542fa6f3850ee18503bbb0505 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 29 Aug 2016 21:10:38 -0400 Subject: [PATCH 15/66] Edging ever onwards; killed forced attempt at uniformity in targets, sketched out the interface for a next-file-from call to Acorn tapes. --- .../Clock Signal.xcodeproj/project.pbxproj | 6 ++++ StaticAnalyser/Acorn/AcornAnalyser.cpp | 10 +++++- StaticAnalyser/Acorn/Tape.cpp | 16 +++++++++ StaticAnalyser/Acorn/Tape.hpp | 34 +++++++++++++++++++ StaticAnalyser/StaticAnalyser.hpp | 27 ++++++--------- 5 files changed, 75 insertions(+), 18 deletions(-) create mode 100644 StaticAnalyser/Acorn/Tape.cpp create mode 100644 StaticAnalyser/Acorn/Tape.hpp diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 3b4cf2a74..55a2cfd86 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -47,6 +47,7 @@ 4B73C71A1D036BD90074D992 /* Vic20Document.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B73C7191D036BD90074D992 /* Vic20Document.swift */; }; 4B73C71D1D036C030074D992 /* Vic20Document.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B73C71B1D036C030074D992 /* Vic20Document.xib */; }; 4B92EACA1B7C112B00246143 /* 6502TimingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */; }; + 4B96F7221D75119A0058BB2D /* Tape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B96F7201D75119A0058BB2D /* Tape.cpp */; }; 4BAB62AD1D3272D200DF5BA0 /* Disk.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BAB62AB1D3272D200DF5BA0 /* Disk.cpp */; }; 4BAB62B51D327F7E00DF5BA0 /* G64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BAB62B31D327F7E00DF5BA0 /* G64.cpp */; }; 4BAB62B81D3302CA00DF5BA0 /* PCMTrack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BAB62B61D3302CA00DF5BA0 /* PCMTrack.cpp */; }; @@ -443,6 +444,8 @@ 4B73C7191D036BD90074D992 /* Vic20Document.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Vic20Document.swift; sourceTree = ""; }; 4B73C71C1D036C030074D992 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = "Clock Signal/Base.lproj/Vic20Document.xib"; sourceTree = SOURCE_ROOT; }; 4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 6502TimingTests.swift; sourceTree = ""; }; + 4B96F7201D75119A0058BB2D /* Tape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Tape.cpp; path = ../../StaticAnalyser/Acorn/Tape.cpp; sourceTree = ""; }; + 4B96F7211D75119A0058BB2D /* Tape.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Tape.hpp; path = ../../StaticAnalyser/Acorn/Tape.hpp; sourceTree = ""; }; 4BAB62AB1D3272D200DF5BA0 /* Disk.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Disk.cpp; sourceTree = ""; }; 4BAB62AC1D3272D200DF5BA0 /* Disk.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Disk.hpp; sourceTree = ""; }; 4BAB62AE1D32730D00DF5BA0 /* Storage.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Storage.hpp; sourceTree = ""; }; @@ -1524,6 +1527,8 @@ children = ( 4BD14B0F1D74627C0088EAD6 /* AcornAnalyser.cpp */, 4BD14B101D74627C0088EAD6 /* AcornAnalyser.hpp */, + 4B96F7201D75119A0058BB2D /* Tape.cpp */, + 4B96F7211D75119A0058BB2D /* Tape.hpp */, ); name = Acorn; sourceTree = ""; @@ -1997,6 +2002,7 @@ 4B55CE581C3B7D360093A61B /* Atari2600Document.swift in Sources */, 4BBB14311CD2CECE00BDB55C /* IntermediateShader.cpp in Sources */, 4BD5F1951D13528900631CD1 /* CSBestEffortUpdater.m in Sources */, + 4B96F7221D75119A0058BB2D /* Tape.cpp in Sources */, 4B0BE4281D3481E700D5256B /* DigitalPhaseLockedLoop.cpp in Sources */, 4B73C71A1D036BD90074D992 /* Vic20Document.swift in Sources */, 4BBF99181C8FBA6F0075DAFB /* TextureTarget.cpp in Sources */, diff --git a/StaticAnalyser/Acorn/AcornAnalyser.cpp b/StaticAnalyser/Acorn/AcornAnalyser.cpp index d064aa8b1..174934463 100644 --- a/StaticAnalyser/Acorn/AcornAnalyser.cpp +++ b/StaticAnalyser/Acorn/AcornAnalyser.cpp @@ -63,5 +63,13 @@ void StaticAnalyser::Acorn::AddTargets( { // strip out inappropriate cartridges std::list> acornCartridges = AcornCartridgesFrom(cartridges); -} + // if there are any tapes, attempt to get data from the first + if(tapes.size() > 0) + { + std::shared_ptr tape = tapes.front(); + + tape->reset(); + + } +} diff --git a/StaticAnalyser/Acorn/Tape.cpp b/StaticAnalyser/Acorn/Tape.cpp new file mode 100644 index 000000000..0c37bf498 --- /dev/null +++ b/StaticAnalyser/Acorn/Tape.cpp @@ -0,0 +1,16 @@ +// +// Tape.cpp +// Clock Signal +// +// Created by Thomas Harte on 29/08/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#include "Tape.hpp" + +using namespace StaticAnalyser::Acorn; + +std::unique_ptr StaticAnalyser::Acorn::GetNextFile(const std::shared_ptr &tape) +{ + return nullptr; +} diff --git a/StaticAnalyser/Acorn/Tape.hpp b/StaticAnalyser/Acorn/Tape.hpp new file mode 100644 index 000000000..fb4a5adb0 --- /dev/null +++ b/StaticAnalyser/Acorn/Tape.hpp @@ -0,0 +1,34 @@ +// +// Tape.hpp +// Clock Signal +// +// Created by Thomas Harte on 29/08/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#ifndef StaticAnalyser_Acorn_Tape_hpp +#define StaticAnalyser_Acorn_Tape_hpp + +#include +#include +#include + +#include "../../Storage/Tape/Tape.hpp" + +namespace StaticAnalyser { +namespace Acorn { + +struct File { + std::string name; + uint16_t load_address; + uint16_t execution_address; + bool is_protected; + std::vector data; +}; + +std::unique_ptr GetNextFile(const std::shared_ptr &tape); + +} +} + +#endif /* Tape_hpp */ diff --git a/StaticAnalyser/StaticAnalyser.hpp b/StaticAnalyser/StaticAnalyser.hpp index 23d60867c..1e3b59a22 100644 --- a/StaticAnalyser/StaticAnalyser.hpp +++ b/StaticAnalyser/StaticAnalyser.hpp @@ -32,29 +32,22 @@ struct Target { float probability; union { - enum class Vic20 { - Unexpanded, - EightKB, - ThirtyTwoKB + struct { + enum class Vic20 { + Unexpanded, + EightKB, + ThirtyTwoKB + } memoryModel; + bool has_c1540; } vic20; - } memoryModel; - union { struct { - bool adfs; - bool dfs; + bool has_adfs; + bool has_dfs; } acorn; - struct { - bool c1540; - } vic20; - } externalHardware; + }; std::string loadingCommand; - union { - struct { - bool holdShift; - } acorn; - } loadingMethod; std::list> disks; std::list> tapes; From 963a4799081c213a938a651002660159d5d53955 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 29 Aug 2016 21:53:06 -0400 Subject: [PATCH 16/66] Made a quick first attempt at getting a file name from Acorn tape, failing terribly but at least formalising tapes being able to signal their end. --- StaticAnalyser/Acorn/AcornAnalyser.cpp | 5 +- StaticAnalyser/Acorn/Tape.cpp | 103 +++++++++++++++++++++++++ Storage/Tape/Formats/CommodoreTAP.cpp | 26 ++++++- Storage/Tape/Formats/CommodoreTAP.hpp | 2 + Storage/Tape/Formats/TapePRG.cpp | 9 ++- Storage/Tape/Formats/TapePRG.hpp | 3 + Storage/Tape/Formats/TapeUEF.cpp | 24 ++++-- Storage/Tape/Formats/TapeUEF.hpp | 2 + Storage/Tape/Tape.hpp | 2 + 9 files changed, 163 insertions(+), 13 deletions(-) diff --git a/StaticAnalyser/Acorn/AcornAnalyser.cpp b/StaticAnalyser/Acorn/AcornAnalyser.cpp index 174934463..652cef36f 100644 --- a/StaticAnalyser/Acorn/AcornAnalyser.cpp +++ b/StaticAnalyser/Acorn/AcornAnalyser.cpp @@ -8,6 +8,8 @@ #include "AcornAnalyser.hpp" +#include "Tape.hpp" + using namespace StaticAnalyser::Acorn; static std::list> @@ -68,8 +70,7 @@ void StaticAnalyser::Acorn::AddTargets( if(tapes.size() > 0) { std::shared_ptr tape = tapes.front(); - tape->reset(); - + GetNextFile(tape); } } diff --git a/StaticAnalyser/Acorn/Tape.cpp b/StaticAnalyser/Acorn/Tape.cpp index 0c37bf498..0bb0b4fca 100644 --- a/StaticAnalyser/Acorn/Tape.cpp +++ b/StaticAnalyser/Acorn/Tape.cpp @@ -10,7 +10,110 @@ using namespace StaticAnalyser::Acorn; +struct TapeParser { + float wave_lengths[4]; + int wave_length_pointer; + + TapeParser() : wave_length_pointer(0) {} + + int GetNextBit(const std::shared_ptr &tape) + { + while(!tape->is_at_end()) + { + // skip any gaps + Storage::Tape::Tape::Pulse next_pulse = tape->get_next_pulse(); + while(next_pulse.type == Storage::Tape::Tape::Pulse::Zero) + { + next_pulse = tape->get_next_pulse(); + } + + wave_lengths[wave_length_pointer] = next_pulse.length.get_float(); + wave_length_pointer++; + + // if first wave is too short or too long, drop it + if(wave_lengths[0] < 1.0f / 4800.0f || wave_lengths[0] > 5.0f / 4800.0f) + { + rotate(1); + continue; + } + + // if first two waves add up to a correct-length cycle, pop them and this is a 0 + if(wave_length_pointer >= 2) + { + float length = wave_lengths[0] + wave_lengths[1]; + if(length >= 3.0f / 4800.0f && length < 5.0f / 4800.0f) + { + rotate(2); + return 0; + } + } + + // if all four waves add up to a correct-length cycle, pop them and this is a 1 + if(wave_length_pointer >= 4) + { + float length = wave_lengths[0] + wave_lengths[1] + wave_lengths[2] + wave_lengths[3]; + if(length >= 3.0f / 4800.0f && length < 5.0f / 4800.0f) + { + rotate(4); + return 1; + } + } + } + + return 0; + } + + int GetNextByte(const std::shared_ptr &tape) + { + int value = 0; + int c = 10; + if(GetNextBit(tape)) return -1; + while(c--) + { + value = (value >> 1) | (GetNextBit(tape) << 9); + } + if(!GetNextBit(tape)) return -1; + return c; + } + + private: + void rotate(int places) + { + wave_length_pointer -= places; + memmove(wave_lengths, &wave_lengths[places], (size_t)(4 - places) * sizeof(float)); + } +}; + std::unique_ptr StaticAnalyser::Acorn::GetNextFile(const std::shared_ptr &tape) { + int shift_register = 0; + TapeParser parser; + +#define shift() \ + shift_register = (shift_register >> 1) | (parser.GetNextBit(tape) << 9) + + // find next area of high tone + while(!tape->is_at_end() && (shift_register != 0x3ff)) + { + shift(); + } + + // find next 0x2a (swallowing stop bit) + while(!tape->is_at_end() && (shift_register != 0x055)) + { + shift(); + if(shift_register != 0x3ff) printf("%d\n", shift_register >> 9); + } + + // read out name + char name[10]; + int name_ptr = 0; + while(!tape->is_at_end() && name_ptr < 10) + { + name[name_ptr] = (char)parser.GetNextByte(tape); + if(!name[name_ptr]) break; + name_ptr++; + } + return nullptr; } diff --git a/Storage/Tape/Formats/CommodoreTAP.cpp b/Storage/Tape/Formats/CommodoreTAP.cpp index 3fc5bf669..556c55fd0 100644 --- a/Storage/Tape/Formats/CommodoreTAP.cpp +++ b/Storage/Tape/Formats/CommodoreTAP.cpp @@ -12,7 +12,7 @@ using namespace Storage::Tape; -CommodoreTAP::CommodoreTAP(const char *file_name) +CommodoreTAP::CommodoreTAP(const char *file_name) : _is_at_end(false) { _file = fopen(file_name, "rb"); @@ -62,10 +62,21 @@ void CommodoreTAP::reset() { fseek(_file, 0x14, SEEK_SET); _current_pulse.type = Pulse::High; + _is_at_end = false; +} + +bool CommodoreTAP::is_at_end() +{ + return _is_at_end; } Storage::Tape::Tape::Pulse CommodoreTAP::get_next_pulse() { + if(_is_at_end) + { + return _current_pulse; + } + if(_current_pulse.type == Pulse::High) { uint32_t next_length; @@ -81,8 +92,17 @@ Storage::Tape::Tape::Pulse CommodoreTAP::get_next_pulse() next_length |= (uint32_t)(fgetc(_file) << 16); } - _current_pulse.length.length = next_length; - _current_pulse.type = Pulse::Low; + if(feof(_file)) + { + _is_at_end = true; + _current_pulse.length.length = _current_pulse.length.clock_rate; + _current_pulse.type = Pulse::Zero; + } + else + { + _current_pulse.length.length = next_length; + _current_pulse.type = Pulse::Low; + } } else _current_pulse.type = Pulse::High; diff --git a/Storage/Tape/Formats/CommodoreTAP.hpp b/Storage/Tape/Formats/CommodoreTAP.hpp index 60fd3c369..836071447 100644 --- a/Storage/Tape/Formats/CommodoreTAP.hpp +++ b/Storage/Tape/Formats/CommodoreTAP.hpp @@ -35,6 +35,7 @@ class CommodoreTAP: public Tape { // implemented to satisfy @c Tape Pulse get_next_pulse(); void reset(); + bool is_at_end(); private: FILE *_file; @@ -42,6 +43,7 @@ class CommodoreTAP: public Tape { uint32_t _file_size; Pulse _current_pulse; + bool _is_at_end; }; } diff --git a/Storage/Tape/Formats/TapePRG.cpp b/Storage/Tape/Formats/TapePRG.cpp index d728e73e5..80d723191 100644 --- a/Storage/Tape/Formats/TapePRG.cpp +++ b/Storage/Tape/Formats/TapePRG.cpp @@ -109,6 +109,11 @@ void PRG::reset() _copy_mask = 0x80; } +bool PRG::is_at_end() +{ + return _filePhase == FilePhaseAtEnd; +} + void PRG::get_next_output_token() { static const int block_length = 192; // not counting the checksum @@ -116,10 +121,10 @@ void PRG::get_next_output_token() static const int leadin_length = 20000; static const int block_leadin_length = 5000; - if(_filePhase == FilePhaseHeaderDataGap) + if(_filePhase == FilePhaseHeaderDataGap || _filePhase == FilePhaseAtEnd) { _outputToken = Silence; - _filePhase = FilePhaseData; + if(_filePhase != FilePhaseAtEnd) _filePhase = FilePhaseData; return; } diff --git a/Storage/Tape/Formats/TapePRG.hpp b/Storage/Tape/Formats/TapePRG.hpp index 0ad03a9d3..f9159fecf 100644 --- a/Storage/Tape/Formats/TapePRG.hpp +++ b/Storage/Tape/Formats/TapePRG.hpp @@ -37,6 +37,8 @@ class PRG: public Tape { // implemented to satisfy @c Tape Pulse get_next_pulse(); void reset(); + bool is_at_end(); + private: FILE *_file; uint16_t _load_address; @@ -47,6 +49,7 @@ class PRG: public Tape { FilePhaseHeader, FilePhaseHeaderDataGap, FilePhaseData, + FilePhaseAtEnd } _filePhase; int _phaseOffset; diff --git a/Storage/Tape/Formats/TapeUEF.cpp b/Storage/Tape/Formats/TapeUEF.cpp index bac0b4627..ee2008947 100644 --- a/Storage/Tape/Formats/TapeUEF.cpp +++ b/Storage/Tape/Formats/TapeUEF.cpp @@ -45,7 +45,8 @@ static float gzgetfloat(gzFile file) UEF::UEF(const char *file_name) : _chunk_id(0), _chunk_length(0), _chunk_position(0), - _time_base(1200) + _time_base(1200), + _is_at_end(false) { _file = gzopen(file_name, "rb"); @@ -77,12 +78,26 @@ UEF::~UEF() void UEF::reset() { gzseek(_file, 12, SEEK_SET); + _is_at_end = false; +} + +bool UEF::is_at_end() +{ + return _is_at_end; } Storage::Tape::Tape::Pulse UEF::get_next_pulse() { Pulse next_pulse; + if(_is_at_end) + { + next_pulse.type = Pulse::Zero; + next_pulse.length.length = _time_base * 4; + next_pulse.length.clock_rate = _time_base * 4; + return next_pulse; + } + if(!_bit_position && chunk_is_finished()) { find_next_tape_chunk(); @@ -149,7 +164,6 @@ Storage::Tape::Tape::Pulse UEF::get_next_pulse() void UEF::find_next_tape_chunk() { - int reset_count = 0; _chunk_position = 0; _bit_position = 0; @@ -170,10 +184,8 @@ void UEF::find_next_tape_chunk() if(gzeof(_file)) { - reset_count++; - if(reset_count == 2) break; - reset(); - continue; + _is_at_end = true; + return; } switch(_chunk_id) diff --git a/Storage/Tape/Formats/TapeUEF.hpp b/Storage/Tape/Formats/TapeUEF.hpp index 12d2286c1..2b507d1b6 100644 --- a/Storage/Tape/Formats/TapeUEF.hpp +++ b/Storage/Tape/Formats/TapeUEF.hpp @@ -36,6 +36,7 @@ class UEF : public Tape { // implemented to satisfy @c Tape Pulse get_next_pulse(); void reset(); + bool is_at_end(); private: gzFile _file; @@ -59,6 +60,7 @@ class UEF : public Tape { uint8_t _current_byte; uint32_t _chunk_position; + bool _is_at_end; bool _current_bit; uint32_t _bit_position; diff --git a/Storage/Tape/Tape.hpp b/Storage/Tape/Tape.hpp index 082ff035d..2293f462d 100644 --- a/Storage/Tape/Tape.hpp +++ b/Storage/Tape/Tape.hpp @@ -36,7 +36,9 @@ class Tape { }; virtual Pulse get_next_pulse() = 0; + virtual void reset() = 0; + virtual bool is_at_end() = 0; virtual void seek(Time seek_time); // TODO }; From 6218f05b8c4f3a6e39e17eedecb97861e7229d26 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 29 Aug 2016 22:05:06 -0400 Subject: [PATCH 17/66] This loads the name. That'll do for now. --- StaticAnalyser/Acorn/Tape.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/StaticAnalyser/Acorn/Tape.cpp b/StaticAnalyser/Acorn/Tape.cpp index 0bb0b4fca..a594e21dc 100644 --- a/StaticAnalyser/Acorn/Tape.cpp +++ b/StaticAnalyser/Acorn/Tape.cpp @@ -31,7 +31,7 @@ struct TapeParser { wave_length_pointer++; // if first wave is too short or too long, drop it - if(wave_lengths[0] < 1.0f / 4800.0f || wave_lengths[0] > 5.0f / 4800.0f) + if(wave_lengths[0] < 1.0f / 4800.0f || wave_lengths[0] >= 5.0f / 4800.0f) { rotate(1); continue; @@ -66,21 +66,21 @@ struct TapeParser { int GetNextByte(const std::shared_ptr &tape) { int value = 0; - int c = 10; + int c = 8; if(GetNextBit(tape)) return -1; while(c--) { - value = (value >> 1) | (GetNextBit(tape) << 9); + value = (value >> 1) | (GetNextBit(tape) << 7); } if(!GetNextBit(tape)) return -1; - return c; + return value; } private: void rotate(int places) { wave_length_pointer -= places; - memmove(wave_lengths, &wave_lengths[places], (size_t)(4 - places) * sizeof(float)); + if(places < 4) memmove(wave_lengths, &wave_lengths[places], (size_t)(4 - places) * sizeof(float)); } }; @@ -99,10 +99,9 @@ std::unique_ptr StaticAnalyser::Acorn::GetNextFile(const std::shared_ptris_at_end() && (shift_register != 0x055)) + while(!tape->is_at_end() && (shift_register != 0x254)) { shift(); - if(shift_register != 0x3ff) printf("%d\n", shift_register >> 9); } // read out name From b3521ed1872c04bac21f0ec6760002ad30450f4b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 30 Aug 2016 07:38:08 -0400 Subject: [PATCH 18/66] Started working towards capturing everything there is to know about a file. CRC calculation appears to be flawed somehow at the moment. --- StaticAnalyser/Acorn/Tape.cpp | 148 +++++++++++++++++++++++++++------- StaticAnalyser/Acorn/Tape.hpp | 20 ++++- 2 files changed, 138 insertions(+), 30 deletions(-) diff --git a/StaticAnalyser/Acorn/Tape.cpp b/StaticAnalyser/Acorn/Tape.cpp index a594e21dc..b407f4566 100644 --- a/StaticAnalyser/Acorn/Tape.cpp +++ b/StaticAnalyser/Acorn/Tape.cpp @@ -11,36 +11,33 @@ using namespace StaticAnalyser::Acorn; struct TapeParser { - float wave_lengths[4]; - int wave_length_pointer; - TapeParser() : wave_length_pointer(0) {} + TapeParser(const std::shared_ptr &tape) : _wave_length_pointer(0), _tape(tape) {} - int GetNextBit(const std::shared_ptr &tape) + int get_next_bit() { - while(!tape->is_at_end()) + while(!_tape->is_at_end()) { // skip any gaps - Storage::Tape::Tape::Pulse next_pulse = tape->get_next_pulse(); + Storage::Tape::Tape::Pulse next_pulse = _tape->get_next_pulse(); while(next_pulse.type == Storage::Tape::Tape::Pulse::Zero) { - next_pulse = tape->get_next_pulse(); + next_pulse = _tape->get_next_pulse(); } - wave_lengths[wave_length_pointer] = next_pulse.length.get_float(); - wave_length_pointer++; + _wave_lengths[_wave_length_pointer] = next_pulse.length.get_float(); + _wave_length_pointer++; // if first wave is too short or too long, drop it - if(wave_lengths[0] < 1.0f / 4800.0f || wave_lengths[0] >= 5.0f / 4800.0f) + if(_wave_lengths[0] < 1.0f / 4800.0f || _wave_lengths[0] >= 5.0f / 4800.0f) { rotate(1); - continue; } // if first two waves add up to a correct-length cycle, pop them and this is a 0 - if(wave_length_pointer >= 2) + if(_wave_length_pointer >= 2) { - float length = wave_lengths[0] + wave_lengths[1]; + float length = _wave_lengths[0] + _wave_lengths[1]; if(length >= 3.0f / 4800.0f && length < 5.0f / 4800.0f) { rotate(2); @@ -49,70 +46,165 @@ struct TapeParser { } // if all four waves add up to a correct-length cycle, pop them and this is a 1 - if(wave_length_pointer >= 4) + if(_wave_length_pointer == 4) { - float length = wave_lengths[0] + wave_lengths[1] + wave_lengths[2] + wave_lengths[3]; + float length = _wave_lengths[0] + _wave_lengths[1] + _wave_lengths[2] + _wave_lengths[3]; if(length >= 3.0f / 4800.0f && length < 5.0f / 4800.0f) { rotate(4); return 1; } + else + { + rotate(1); + } } } return 0; } - int GetNextByte(const std::shared_ptr &tape) + int get_next_byte() { int value = 0; int c = 8; - if(GetNextBit(tape)) return -1; + if(get_next_bit()) + { + _error_flag = true; + return -1; + } while(c--) { - value = (value >> 1) | (GetNextBit(tape) << 7); + value = (value >> 1) | (get_next_bit() << 7); } - if(!GetNextBit(tape)) return -1; + if(!get_next_bit()) + { + _error_flag = true; + return -1; + } + add_to_crc((uint8_t)value); return value; } + int get_next_short() + { + int result = get_next_byte(); + result |= get_next_byte() << 8; + return result; + } + + int get_next_word() + { + int result = get_next_short(); + result |= get_next_short() << 8; + return result; + } + + void reset_error_flag() + { + _error_flag = false; + } + + bool get_error_flag() + { + return _error_flag; + } + + void reset_crc() + { + _crc = 0; + } + + void add_to_crc(uint8_t value) + { + _crc ^= (uint16_t)value << 8; + for(int c = 0; c < 8; c++) + { + uint16_t exclusive_or = (_crc&0x8000) ? 0x1021 : 0x0000; + _crc = (uint16_t)(_crc << 1) ^ exclusive_or; + } + } + + uint16_t get_crc() + { + return _crc; + } + + bool is_at_end() + { + return _tape->is_at_end(); + } + private: void rotate(int places) { - wave_length_pointer -= places; - if(places < 4) memmove(wave_lengths, &wave_lengths[places], (size_t)(4 - places) * sizeof(float)); + _wave_length_pointer -= places; + if(places < 4) memmove(_wave_lengths, &_wave_lengths[places], (size_t)(4 - places) * sizeof(float)); } + + + uint16_t _crc; + bool _error_flag; + + float _wave_lengths[4]; + int _wave_length_pointer; + std::shared_ptr _tape; }; -std::unique_ptr StaticAnalyser::Acorn::GetNextFile(const std::shared_ptr &tape) +static std::unique_ptr GetNextChunk(TapeParser &parser) { + std::unique_ptr new_chunk(new File::Chunk); int shift_register = 0; - TapeParser parser; #define shift() \ - shift_register = (shift_register >> 1) | (parser.GetNextBit(tape) << 9) + shift_register = (shift_register >> 1) | (parser.get_next_bit() << 9) // find next area of high tone - while(!tape->is_at_end() && (shift_register != 0x3ff)) + while(!parser.is_at_end() && (shift_register != 0x3ff)) { shift(); } // find next 0x2a (swallowing stop bit) - while(!tape->is_at_end() && (shift_register != 0x254)) + while(!parser.is_at_end() && (shift_register != 0x254)) { shift(); } + parser.reset_crc(); + parser.reset_error_flag(); + // read out name char name[10]; int name_ptr = 0; - while(!tape->is_at_end() && name_ptr < 10) + while(!parser.is_at_end() && name_ptr < 10) { - name[name_ptr] = (char)parser.GetNextByte(tape); + name[name_ptr] = (char)parser.get_next_byte(); if(!name[name_ptr]) break; name_ptr++; } + // addresses + new_chunk->load_address = (uint32_t)parser.get_next_word(); + new_chunk->execution_address = (uint32_t)parser.get_next_word(); + new_chunk->block_number = (uint16_t)parser.get_next_short(); + new_chunk->block_length = (uint16_t)parser.get_next_short(); + new_chunk->block_flag = (uint8_t)parser.get_next_byte(); + new_chunk->next_address = (uint32_t)parser.get_next_word(); + + uint16_t calculated_crc = parser.get_crc(); + uint16_t stored_crc = (uint16_t)parser.get_next_short(); + + printf("%04x v %04x", calculated_crc, stored_crc); + + return nullptr; +} + +std::unique_ptr StaticAnalyser::Acorn::GetNextFile(const std::shared_ptr &tape) +{ + TapeParser parser(tape); + + GetNextChunk(parser); + return nullptr; } diff --git a/StaticAnalyser/Acorn/Tape.hpp b/StaticAnalyser/Acorn/Tape.hpp index fb4a5adb0..55d4f1337 100644 --- a/StaticAnalyser/Acorn/Tape.hpp +++ b/StaticAnalyser/Acorn/Tape.hpp @@ -9,6 +9,7 @@ #ifndef StaticAnalyser_Acorn_Tape_hpp #define StaticAnalyser_Acorn_Tape_hpp +#include #include #include #include @@ -20,10 +21,25 @@ namespace Acorn { struct File { std::string name; - uint16_t load_address; - uint16_t execution_address; + uint32_t load_address; + uint32_t execution_address; bool is_protected; std::vector data; + + struct Chunk { + uint32_t load_address; + uint32_t execution_address; + uint16_t block_number; + uint16_t block_length; + uint8_t block_flag; + uint32_t next_address; + + bool header_crc_matched; + bool data_crc_matched; + std::vector data; + }; + + std::list chunks; }; std::unique_ptr GetNextFile(const std::shared_ptr &tape); From 7b5d5858ff6b7a6910e25a457c8d76120a739571 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 30 Aug 2016 08:13:40 -0400 Subject: [PATCH 19/66] CRCs are just stored reversed. Of course. So this should be sufficient to load each chunk of an Acorn file. --- StaticAnalyser/Acorn/Tape.cpp | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/StaticAnalyser/Acorn/Tape.cpp b/StaticAnalyser/Acorn/Tape.cpp index b407f4566..dface332f 100644 --- a/StaticAnalyser/Acorn/Tape.cpp +++ b/StaticAnalyser/Acorn/Tape.cpp @@ -156,8 +156,8 @@ static std::unique_ptr GetNextChunk(TapeParser &parser) std::unique_ptr new_chunk(new File::Chunk); int shift_register = 0; -#define shift() \ - shift_register = (shift_register >> 1) | (parser.get_next_bit() << 9) +// TODO: move this into the parser +#define shift() shift_register = (shift_register >> 1) | (parser.get_next_bit() << 9) // find next area of high tone while(!parser.is_at_end() && (shift_register != 0x3ff)) @@ -171,6 +171,8 @@ static std::unique_ptr GetNextChunk(TapeParser &parser) shift(); } +#undef shift + parser.reset_crc(); parser.reset_error_flag(); @@ -192,12 +194,31 @@ static std::unique_ptr GetNextChunk(TapeParser &parser) new_chunk->block_flag = (uint8_t)parser.get_next_byte(); new_chunk->next_address = (uint32_t)parser.get_next_word(); - uint16_t calculated_crc = parser.get_crc(); - uint16_t stored_crc = (uint16_t)parser.get_next_short(); + uint16_t calculated_header_crc = parser.get_crc(); + uint16_t stored_header_crc = (uint16_t)parser.get_next_short(); + stored_header_crc = (uint16_t)((stored_header_crc >> 8) | (stored_header_crc << 8)); + new_chunk->header_crc_matched = stored_header_crc == calculated_header_crc; - printf("%04x v %04x", calculated_crc, stored_crc); + parser.reset_crc(); + new_chunk->data.reserve(new_chunk->block_length); + for(int c = 0; c < new_chunk->block_length; c++) + { + new_chunk->data.push_back((uint8_t)parser.get_next_byte()); + } - return nullptr; + if(new_chunk->block_length) + { + uint16_t calculated_data_crc = parser.get_crc(); + uint16_t stored_data_crc = (uint16_t)parser.get_next_short(); + stored_data_crc = (uint16_t)((stored_data_crc >> 8) | (stored_data_crc << 8)); + new_chunk->data_crc_matched = stored_data_crc == calculated_data_crc; + } + else + { + new_chunk->data_crc_matched = true; + } + + return parser.get_error_flag() ? nullptr : std::move(new_chunk); } std::unique_ptr StaticAnalyser::Acorn::GetNextFile(const std::shared_ptr &tape) From d8c0da7ccbccee54eb5115926d8be4b370ca0e6d Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 31 Aug 2016 19:57:09 -0400 Subject: [PATCH 20/66] I think this should lead to a full representation of files on an Acorn tape, but subject to `is_at_end` working. --- StaticAnalyser/Acorn/AcornAnalyser.cpp | 2 +- StaticAnalyser/Acorn/Tape.cpp | 57 ++++++++++++++++++++++++-- StaticAnalyser/Acorn/Tape.hpp | 3 +- 3 files changed, 56 insertions(+), 6 deletions(-) diff --git a/StaticAnalyser/Acorn/AcornAnalyser.cpp b/StaticAnalyser/Acorn/AcornAnalyser.cpp index 652cef36f..ac91be23d 100644 --- a/StaticAnalyser/Acorn/AcornAnalyser.cpp +++ b/StaticAnalyser/Acorn/AcornAnalyser.cpp @@ -71,6 +71,6 @@ void StaticAnalyser::Acorn::AddTargets( { std::shared_ptr tape = tapes.front(); tape->reset(); - GetNextFile(tape); + GetFiles(tape); } } diff --git a/StaticAnalyser/Acorn/Tape.cpp b/StaticAnalyser/Acorn/Tape.cpp index dface332f..1a266c5ba 100644 --- a/StaticAnalyser/Acorn/Tape.cpp +++ b/StaticAnalyser/Acorn/Tape.cpp @@ -185,6 +185,7 @@ static std::unique_ptr GetNextChunk(TapeParser &parser) if(!name[name_ptr]) break; name_ptr++; } + new_chunk->name = name; // addresses new_chunk->load_address = (uint32_t)parser.get_next_word(); @@ -206,7 +207,7 @@ static std::unique_ptr GetNextChunk(TapeParser &parser) new_chunk->data.push_back((uint8_t)parser.get_next_byte()); } - if(new_chunk->block_length) + if(new_chunk->block_length && !new_chunk->block_flag&0x40) { uint16_t calculated_data_crc = parser.get_crc(); uint16_t stored_data_crc = (uint16_t)parser.get_next_short(); @@ -221,11 +222,59 @@ static std::unique_ptr GetNextChunk(TapeParser &parser) return parser.get_error_flag() ? nullptr : std::move(new_chunk); } -std::unique_ptr StaticAnalyser::Acorn::GetNextFile(const std::shared_ptr &tape) +std::unique_ptr GetNextFile(TapeParser &parser) +{ + std::unique_ptr chunk; + + // find next chunk with a block number of 0 + while(!parser.is_at_end()) + { + chunk = GetNextChunk(parser); + if(!chunk) continue; + if(chunk->block_number) continue; + } + + // accumulate chunks for as long as block number is sequential and the end-of-file bit isn't set + std::unique_ptr file(new File); + file->chunks.push_back(*chunk); + while(!parser.is_at_end()) + { + std::unique_ptr next_chunk = GetNextChunk(parser); + if(!next_chunk) return nullptr; + if(next_chunk->block_number != chunk->block_number + 1) return nullptr; + file->chunks.push_back(*next_chunk); + chunk = std::move(next_chunk); + if(chunk->block_flag&0x80) break; + } + + // accumulate total data, copy flags appropriately + file->name = file->chunks.front().name; + file->load_address = file->chunks.front().load_address; + file->execution_address = file->chunks.front().execution_address; + file->is_protected = !!(file->chunks.back().block_flag & 0x01); // I think the last flags are the ones that count; TODO: check. + + // copy all data into a single big block + for(File::Chunk chunk : file->chunks) + { + file->data.insert(file->data.end(), chunk.data.begin(), chunk.data.end()); + } + + return file; +} + +std::list StaticAnalyser::Acorn::GetFiles(const std::shared_ptr &tape) { TapeParser parser(tape); + std::list file_list; - GetNextChunk(parser); + while(!parser.is_at_end()) + { + std::unique_ptr next_file = GetNextFile(parser); + if(next_file) + { + file_list.push_back(*next_file); + } + } - return nullptr; + return file_list; } diff --git a/StaticAnalyser/Acorn/Tape.hpp b/StaticAnalyser/Acorn/Tape.hpp index 55d4f1337..570ebab1e 100644 --- a/StaticAnalyser/Acorn/Tape.hpp +++ b/StaticAnalyser/Acorn/Tape.hpp @@ -27,6 +27,7 @@ struct File { std::vector data; struct Chunk { + std::string name; uint32_t load_address; uint32_t execution_address; uint16_t block_number; @@ -42,7 +43,7 @@ struct File { std::list chunks; }; -std::unique_ptr GetNextFile(const std::shared_ptr &tape); +std::list GetFiles(const std::shared_ptr &tape); } } From 9d7962d6c0c1c968346c23df050f4f7bcd0e6afa Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 31 Aug 2016 20:24:13 -0400 Subject: [PATCH 21/66] Closed the loop on Electron analysis for now. Including loading command detection. --- StaticAnalyser/Acorn/AcornAnalyser.cpp | 44 ++++++++++++++++++++++++-- StaticAnalyser/Acorn/Tape.cpp | 6 ++-- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/StaticAnalyser/Acorn/AcornAnalyser.cpp b/StaticAnalyser/Acorn/AcornAnalyser.cpp index ac91be23d..b96d0f603 100644 --- a/StaticAnalyser/Acorn/AcornAnalyser.cpp +++ b/StaticAnalyser/Acorn/AcornAnalyser.cpp @@ -63,14 +63,54 @@ void StaticAnalyser::Acorn::AddTargets( const std::list> &cartridges, std::list &destination) { + Target target; + target.machine = Target::Electron; + target.probability = 1.0; // TODO: a proper estimation + // strip out inappropriate cartridges - std::list> acornCartridges = AcornCartridgesFrom(cartridges); + target.cartridges = AcornCartridgesFrom(cartridges); // if there are any tapes, attempt to get data from the first if(tapes.size() > 0) { std::shared_ptr tape = tapes.front(); tape->reset(); - GetFiles(tape); + + // continue if there are any files + std::list files = GetFiles(tape); + if(files.size()) + { + bool is_basic = true; + + // protected files are always for *RUNning only + if(files.front().is_protected) is_basic = false; + + // check also for a continuous threading of BASIC lines; if none then this probably isn't BASIC code, + // so that's also justification to *RUN + size_t pointer = 0; + uint8_t *data = &files.front().data[0]; + size_t data_size = files.front().data.size(); + while(1) + { + if(pointer >= data_size-1 || data[pointer] != 13) + { + is_basic = false; + break; + } + if((data[pointer+1]&0x7f) == 0x7f) break; + pointer += data[pointer+3]; + } + + // Inspect first file. If it's protected or doesn't look like BASIC + // then the loading command is *RUN. Otherwise it's CHAIN"". + target.loadingCommand = is_basic ? "CHAIN\"\"\n" : "*RUN\n"; + + target.tapes = tapes; + } } + + // TODO: disks + + if(target.tapes.size() || target.cartridges.size()) + destination.push_back(target); } diff --git a/StaticAnalyser/Acorn/Tape.cpp b/StaticAnalyser/Acorn/Tape.cpp index 1a266c5ba..e31821e8f 100644 --- a/StaticAnalyser/Acorn/Tape.cpp +++ b/StaticAnalyser/Acorn/Tape.cpp @@ -20,7 +20,7 @@ struct TapeParser { { // skip any gaps Storage::Tape::Tape::Pulse next_pulse = _tape->get_next_pulse(); - while(next_pulse.type == Storage::Tape::Tape::Pulse::Zero) + while(!_tape->is_at_end() && next_pulse.type == Storage::Tape::Tape::Pulse::Zero) { next_pulse = _tape->get_next_pulse(); } @@ -231,9 +231,11 @@ std::unique_ptr GetNextFile(TapeParser &parser) { chunk = GetNextChunk(parser); if(!chunk) continue; - if(chunk->block_number) continue; + if(!chunk->block_number) break; } + if(!chunk) return nullptr; + // accumulate chunks for as long as block number is sequential and the end-of-file bit isn't set std::unique_ptr file(new File); file->chunks.push_back(*chunk); From 7bc2b6b161af17091da6dff6f33af30e061b3cf4 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 31 Aug 2016 20:43:29 -0400 Subject: [PATCH 22/66] Added the minor piece that will allow analysis results to be obtained from Objective-C, and exposed to Swift in sufficient detail for it to be able to pass on to the machine. --- .../Clock Signal.xcodeproj/project.pbxproj | 16 ++++++ .../Clock Signal/Machine/CSMachine+Target.h | 17 +++++++ .../Mac/Clock Signal/Machine/CSMachine.mm | 3 ++ .../Machine/StaticAnalyser/CSStaticAnalyser.h | 20 ++++++++ .../StaticAnalyser/CSStaticAnalyser.mm | 50 +++++++++++++++++++ .../Updater/CSBestEffortUpdater.h | 2 +- 6 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 OSBindings/Mac/Clock Signal/Machine/CSMachine+Target.h create mode 100644 OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.h create mode 100644 OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 55a2cfd86..d9ceada4e 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -40,6 +40,7 @@ 4B55CE591C3B7D360093A61B /* ElectronDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B55CE571C3B7D360093A61B /* ElectronDocument.swift */; }; 4B55CE5D1C3B7D6F0093A61B /* CSOpenGLView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B55CE5C1C3B7D6F0093A61B /* CSOpenGLView.m */; }; 4B55CE5F1C3B7D960093A61B /* MachineDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B55CE5E1C3B7D960093A61B /* MachineDocument.swift */; }; + 4B643F3A1D77AD1900D431D6 /* CSStaticAnalyser.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B643F391D77AD1900D431D6 /* CSStaticAnalyser.mm */; }; 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 */; }; @@ -434,6 +435,9 @@ 4B55CE5B1C3B7D6F0093A61B /* CSOpenGLView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSOpenGLView.h; sourceTree = ""; }; 4B55CE5C1C3B7D6F0093A61B /* CSOpenGLView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSOpenGLView.m; sourceTree = ""; }; 4B55CE5E1C3B7D960093A61B /* MachineDocument.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MachineDocument.swift; sourceTree = ""; }; + 4B643F381D77AD1900D431D6 /* CSStaticAnalyser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSStaticAnalyser.h; path = StaticAnalyser/CSStaticAnalyser.h; sourceTree = ""; }; + 4B643F391D77AD1900D431D6 /* CSStaticAnalyser.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = CSStaticAnalyser.mm; path = StaticAnalyser/CSStaticAnalyser.mm; sourceTree = ""; }; + 4B643F3C1D77AE5C00D431D6 /* CSMachine+Target.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CSMachine+Target.h"; sourceTree = ""; }; 4B69FB3B1C4D908A00B5F0AA /* Tape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Tape.cpp; sourceTree = ""; }; 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 = ""; }; @@ -881,6 +885,8 @@ 4B2A53961D117D36003C6002 /* CSMachine.mm */, 4B2A53971D117D36003C6002 /* KeyCodes.h */, 4B2A53981D117D36003C6002 /* Wrappers */, + 4B643F3B1D77AD6D00D431D6 /* StaticAnalyser */, + 4B643F3C1D77AE5C00D431D6 /* CSMachine+Target.h */, ); path = Machine; sourceTree = ""; @@ -997,6 +1003,15 @@ path = Views; sourceTree = ""; }; + 4B643F3B1D77AD6D00D431D6 /* StaticAnalyser */ = { + isa = PBXGroup; + children = ( + 4B643F381D77AD1900D431D6 /* CSStaticAnalyser.h */, + 4B643F391D77AD1900D431D6 /* CSStaticAnalyser.mm */, + ); + name = StaticAnalyser; + sourceTree = ""; + }; 4B69FB391C4D908A00B5F0AA /* Storage */ = { isa = PBXGroup; children = ( @@ -2016,6 +2031,7 @@ 4BBF99141C8FBA6F0075DAFB /* CRTInputBufferBuilder.cpp in Sources */, 4B2409551C45AB05004DA684 /* Speaker.cpp in Sources */, 4B6C73BD1D387AE500AFCFCA /* DiskDrive.cpp in Sources */, + 4B643F3A1D77AD1900D431D6 /* CSStaticAnalyser.mm in Sources */, 4B4DC8281D2C2470003C5BF8 /* C1540.cpp in Sources */, 4B1E85751D170228001EF87D /* Typer.cpp in Sources */, 4B2E2D9D1C3A070400138695 /* Electron.cpp in Sources */, diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine+Target.h b/OSBindings/Mac/Clock Signal/Machine/CSMachine+Target.h new file mode 100644 index 000000000..6e720d929 --- /dev/null +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine+Target.h @@ -0,0 +1,17 @@ +// +// Target.h +// Clock Signal +// +// Created by Thomas Harte on 31/08/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#import + +#include "StaticAnalyser.hpp" + +@interface CSMachine(Target) + +- (void)applyTarget:(StaticAnalyser::Target)target; + +@end diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm index 195788843..af551587d 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm @@ -8,6 +8,7 @@ #import "CSMachine.h" #import "CSMachine+Subclassing.h" +#import "CSMachine+Target.h" #include "Typer.hpp" @interface CSMachine() @@ -122,4 +123,6 @@ struct MachineDelegate: CRTMachine::Machine::Delegate { typeRecipient->set_typer_for_string([paste UTF8String]); } +- (void)applyTarget:(StaticAnalyser::Target)target {} + @end diff --git a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.h b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.h new file mode 100644 index 000000000..b57aeb86c --- /dev/null +++ b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.h @@ -0,0 +1,20 @@ +// +// CSStaticAnalyser.h +// Clock Signal +// +// Created by Thomas Harte on 31/08/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#import + +@class CSMachine; + +@interface CSStaticAnalyser : NSObject + +- (instancetype)initWithFileAtURL:(NSURL *)url; + +@property(nonatomic, readonly) Class documentClass; +- (void)applyToMachine:(CSMachine *)machine; + +@end diff --git a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm new file mode 100644 index 000000000..8ea40d5b8 --- /dev/null +++ b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm @@ -0,0 +1,50 @@ +// +// CSStaticAnalyser.m +// Clock Signal +// +// Created by Thomas Harte on 31/08/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#import "CSStaticAnalyser.h" + +#import "CSMachine.h" +#import "CSMachine+Target.h" +#import "Clock_Signal-Swift.h" +#include "StaticAnalyser.hpp" + +@implementation CSStaticAnalyser +{ + StaticAnalyser::Target _target; +} + +- (instancetype)initWithFileAtURL:(NSURL *)url +{ + self = [super init]; + if(self) + { + std::list targets = StaticAnalyser::GetTargets([url fileSystemRepresentation]); + if(!targets.size()) return nil; + _target = targets.front(); + } + return self; +} + +- (Class)documentClass +{ + switch(_target.machine) + { + case StaticAnalyser::Target::Electron: return [ElectronDocument class]; + case StaticAnalyser::Target::Vic20: return [Vic20Document class]; + case StaticAnalyser::Target::Atari2600: return [Atari2600Document class]; + } + + return nil; +} + +- (void)applyToMachine:(CSMachine *)machine +{ + [machine applyTarget:_target]; +} + +@end diff --git a/OSBindings/Mac/Clock Signal/Updater/CSBestEffortUpdater.h b/OSBindings/Mac/Clock Signal/Updater/CSBestEffortUpdater.h index eed663901..f8608a600 100644 --- a/OSBindings/Mac/Clock Signal/Updater/CSBestEffortUpdater.h +++ b/OSBindings/Mac/Clock Signal/Updater/CSBestEffortUpdater.h @@ -7,7 +7,7 @@ // #import -@import CoreVideo; +#import @class CSBestEffortUpdater; From a9b67dfba0bc2430e3fa39a2c37b63c09ed9c557 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 31 Aug 2016 21:21:07 -0400 Subject: [PATCH 23/66] Introduced an `NSDocumentController` subclass. --- .../Clock Signal.xcodeproj/project.pbxproj | 12 +++++++++++ .../Mac/Clock Signal/Base.lproj/MainMenu.xib | 3 ++- .../DocumentController.swift | 20 +++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 OSBindings/Mac/Clock Signal/Document Controller/DocumentController.swift diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 060d6ef83..172f4654c 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -39,6 +39,7 @@ 4B55CE591C3B7D360093A61B /* ElectronDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B55CE571C3B7D360093A61B /* ElectronDocument.swift */; }; 4B55CE5D1C3B7D6F0093A61B /* CSOpenGLView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B55CE5C1C3B7D6F0093A61B /* CSOpenGLView.m */; }; 4B55CE5F1C3B7D960093A61B /* MachineDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B55CE5E1C3B7D960093A61B /* MachineDocument.swift */; }; + 4B643F3F1D77B88000D431D6 /* DocumentController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B643F3E1D77B88000D431D6 /* DocumentController.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 */; }; @@ -425,6 +426,7 @@ 4B55CE5B1C3B7D6F0093A61B /* CSOpenGLView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSOpenGLView.h; sourceTree = ""; }; 4B55CE5C1C3B7D6F0093A61B /* CSOpenGLView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSOpenGLView.m; sourceTree = ""; }; 4B55CE5E1C3B7D960093A61B /* MachineDocument.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MachineDocument.swift; sourceTree = ""; }; + 4B643F3E1D77B88000D431D6 /* DocumentController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DocumentController.swift; sourceTree = ""; }; 4B69FB3B1C4D908A00B5F0AA /* Tape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Tape.cpp; sourceTree = ""; }; 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 = ""; }; @@ -976,6 +978,14 @@ path = Views; sourceTree = ""; }; + 4B643F3D1D77B88000D431D6 /* Document Controller */ = { + isa = PBXGroup; + children = ( + 4B643F3E1D77B88000D431D6 /* DocumentController.swift */, + ); + path = "Document Controller"; + sourceTree = ""; + }; 4B69FB391C4D908A00B5F0AA /* Storage */ = { isa = PBXGroup; children = ( @@ -1365,6 +1375,7 @@ 4BB73EA01B587A5100552FC2 /* Clock Signal */ = { isa = PBXGroup; children = ( + 4B643F3D1D77B88000D431D6 /* Document Controller */, 4BE5F85A1C3E1C2500C43F01 /* Resources */, 4BB73ECF1B587A6700552FC2 /* Clock Signal.entitlements */, 4B1414501B58848C00E04248 /* ClockSignal-Bridging-Header.h */, @@ -1919,6 +1930,7 @@ 4BB697CE1D4BA44400248BDF /* CommodoreGCR.cpp in Sources */, 4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */, 4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */, + 4B643F3F1D77B88000D431D6 /* DocumentController.swift in Sources */, 4BB697C71D4B558F00248BDF /* Factors.cpp in Sources */, 4B55CE591C3B7D360093A61B /* ElectronDocument.swift in Sources */, 4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */, diff --git a/OSBindings/Mac/Clock Signal/Base.lproj/MainMenu.xib b/OSBindings/Mac/Clock Signal/Base.lproj/MainMenu.xib index 012c3874d..5c4f1b32f 100644 --- a/OSBindings/Mac/Clock Signal/Base.lproj/MainMenu.xib +++ b/OSBindings/Mac/Clock Signal/Base.lproj/MainMenu.xib @@ -1,5 +1,5 @@ - + @@ -13,6 +13,7 @@ + diff --git a/OSBindings/Mac/Clock Signal/Document Controller/DocumentController.swift b/OSBindings/Mac/Clock Signal/Document Controller/DocumentController.swift new file mode 100644 index 000000000..eedf4b3bd --- /dev/null +++ b/OSBindings/Mac/Clock Signal/Document Controller/DocumentController.swift @@ -0,0 +1,20 @@ +// +// DocumentController.swift +// Clock Signal +// +// Created by Thomas Harte on 18/06/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +import Cocoa + +class DocumentController: NSDocumentController { + override func documentForURL(url: NSURL) -> NSDocument? { + return super.documentForURL(url) + } +// override func newDocument(sender: AnyObject?) { +// let window = NSWindow(contentViewController: NewDocumentViewController()) +// window.title = "Choose a Machine" +// window.makeKeyAndOrderFront(self) +// } +} From 6f62803814bc1f62512b12f97d39ef0208372b3c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 1 Sep 2016 08:35:28 -0400 Subject: [PATCH 24/66] Resolved dropping of every other file. --- StaticAnalyser/Acorn/Tape.cpp | 74 +++++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 29 deletions(-) diff --git a/StaticAnalyser/Acorn/Tape.cpp b/StaticAnalyser/Acorn/Tape.cpp index e31821e8f..17e148982 100644 --- a/StaticAnalyser/Acorn/Tape.cpp +++ b/StaticAnalyser/Acorn/Tape.cpp @@ -8,6 +8,8 @@ #include "Tape.hpp" +#include + using namespace StaticAnalyser::Acorn; struct TapeParser { @@ -115,16 +117,6 @@ struct TapeParser { _crc = 0; } - void add_to_crc(uint8_t value) - { - _crc ^= (uint16_t)value << 8; - for(int c = 0; c < 8; c++) - { - uint16_t exclusive_or = (_crc&0x8000) ? 0x1021 : 0x0000; - _crc = (uint16_t)(_crc << 1) ^ exclusive_or; - } - } - uint16_t get_crc() { return _crc; @@ -136,13 +128,22 @@ struct TapeParser { } private: + void add_to_crc(uint8_t value) + { + _crc ^= (uint16_t)value << 8; + for(int c = 0; c < 8; c++) + { + uint16_t exclusive_or = (_crc&0x8000) ? 0x1021 : 0x0000; + _crc = (uint16_t)(_crc << 1) ^ exclusive_or; + } + } + void rotate(int places) { _wave_length_pointer -= places; if(places < 4) memmove(_wave_lengths, &_wave_lengths[places], (size_t)(4 - places) * sizeof(float)); } - uint16_t _crc; bool _error_flag; @@ -222,31 +223,33 @@ static std::unique_ptr GetNextChunk(TapeParser &parser) return parser.get_error_flag() ? nullptr : std::move(new_chunk); } -std::unique_ptr GetNextFile(TapeParser &parser) +std::unique_ptr GetNextFile(std::deque &chunks) { - std::unique_ptr chunk; - // find next chunk with a block number of 0 - while(!parser.is_at_end()) + while(chunks.size() && chunks.front().block_number) { - chunk = GetNextChunk(parser); - if(!chunk) continue; - if(!chunk->block_number) break; + chunks.pop_front(); } - if(!chunk) return nullptr; + if(!chunks.size()) return nullptr; // accumulate chunks for as long as block number is sequential and the end-of-file bit isn't set std::unique_ptr file(new File); - file->chunks.push_back(*chunk); - while(!parser.is_at_end()) + + uint16_t block_number = chunks.front().block_number; + file->chunks.push_back(chunks.front()); + chunks.pop_front(); + + while(chunks.size()) { - std::unique_ptr next_chunk = GetNextChunk(parser); - if(!next_chunk) return nullptr; - if(next_chunk->block_number != chunk->block_number + 1) return nullptr; - file->chunks.push_back(*next_chunk); - chunk = std::move(next_chunk); - if(chunk->block_flag&0x80) break; + if(chunks.front().block_number != block_number + 1) return nullptr; + + bool was_last = chunks.front().block_flag & 0x80; + file->chunks.push_back(chunks.front()); + chunks.pop_front(); + block_number++; + + if(was_last) break; } // accumulate total data, copy flags appropriately @@ -267,11 +270,24 @@ std::unique_ptr GetNextFile(TapeParser &parser) std::list StaticAnalyser::Acorn::GetFiles(const std::shared_ptr &tape) { TapeParser parser(tape); - std::list file_list; + // populate chunk list + std::deque chunk_list; while(!parser.is_at_end()) { - std::unique_ptr next_file = GetNextFile(parser); + std::unique_ptr chunk = GetNextChunk(parser); + if(chunk) + { + chunk_list.push_back(*chunk); + } + } + + // decompose into file list + std::list file_list; + + while(chunk_list.size()) + { + std::unique_ptr next_file = GetNextFile(chunk_list); if(next_file) { file_list.push_back(*next_file); From 93f5b5303e5024049e8a7e0c13acaabd65a2970c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 5 Sep 2016 17:17:52 -0400 Subject: [PATCH 25/66] Factored out the stuff I expect to be common to this tape nonsense, started looking at the one currently-failing tape. More on the latter to do. --- StaticAnalyser/Acorn/AcornAnalyser.cpp | 2 +- StaticAnalyser/Acorn/Tape.cpp | 250 +++++++++++++------------ 2 files changed, 135 insertions(+), 117 deletions(-) diff --git a/StaticAnalyser/Acorn/AcornAnalyser.cpp b/StaticAnalyser/Acorn/AcornAnalyser.cpp index 0803c39a2..ce9fc5d5c 100644 --- a/StaticAnalyser/Acorn/AcornAnalyser.cpp +++ b/StaticAnalyser/Acorn/AcornAnalyser.cpp @@ -84,7 +84,7 @@ void StaticAnalyser::Acorn::AddTargets( bool is_basic = true; // protected files are always for *RUNning only - if(files.front().is_protected) is_basic = false; +// if(files.front().is_protected) is_basic = false; // check also for a continuous threading of BASIC lines; if none then this probably isn't BASIC code, // so that's also justification to *RUN diff --git a/StaticAnalyser/Acorn/Tape.cpp b/StaticAnalyser/Acorn/Tape.cpp index 17e148982..2a814ff2a 100644 --- a/StaticAnalyser/Acorn/Tape.cpp +++ b/StaticAnalyser/Acorn/Tape.cpp @@ -12,122 +12,151 @@ using namespace StaticAnalyser::Acorn; -struct TapeParser { +/*! + A partly-abstract base class to help in the authorship of tape format parsers; + provides hooks for a +*/ +template class TapeParser { + public: + TapeParser(const std::shared_ptr &tape) : _tape(tape) {} - TapeParser(const std::shared_ptr &tape) : _wave_length_pointer(0), _tape(tape) {} - - int get_next_bit() - { - while(!_tape->is_at_end()) + std::unique_ptr get_next_symbol() { - // skip any gaps - Storage::Tape::Tape::Pulse next_pulse = _tape->get_next_pulse(); - while(!_tape->is_at_end() && next_pulse.type == Storage::Tape::Tape::Pulse::Zero) + while(!_tape->is_at_end()) { - next_pulse = _tape->get_next_pulse(); - } + std::unique_ptr symbol = dequeue_next_symbol(_wave_queue); + if(symbol) return symbol; - _wave_lengths[_wave_length_pointer] = next_pulse.length.get_float(); - _wave_length_pointer++; - - // if first wave is too short or too long, drop it - if(_wave_lengths[0] < 1.0f / 4800.0f || _wave_lengths[0] >= 5.0f / 4800.0f) - { - rotate(1); - } - - // if first two waves add up to a correct-length cycle, pop them and this is a 0 - if(_wave_length_pointer >= 2) - { - float length = _wave_lengths[0] + _wave_lengths[1]; - if(length >= 3.0f / 4800.0f && length < 5.0f / 4800.0f) + while(!_tape->is_at_end()) { - rotate(2); - return 0; + Storage::Tape::Tape::Pulse next_pulse = _tape->get_next_pulse(); + std::unique_ptr next_wave = get_wave_type_for_pulse(next_pulse); + if(next_wave) + { + _wave_queue.push_back(*next_wave); + break; + } } } - // if all four waves add up to a correct-length cycle, pop them and this is a 1 - if(_wave_length_pointer == 4) - { - float length = _wave_lengths[0] + _wave_lengths[1] + _wave_lengths[2] + _wave_lengths[3]; - if(length >= 3.0f / 4800.0f && length < 5.0f / 4800.0f) - { - rotate(4); - return 1; - } - else - { - rotate(1); - } - } + return nullptr; } - return 0; - } + void reset_error_flag() { _error_flag = false; } + bool get_error_flag() { return _error_flag; } + bool is_at_end() { return _tape->is_at_end(); } - int get_next_byte() - { - int value = 0; - int c = 8; - if(get_next_bit()) - { - _error_flag = true; - return -1; - } - while(c--) - { - value = (value >> 1) | (get_next_bit() << 7); - } - if(!get_next_bit()) - { - _error_flag = true; - return -1; - } - add_to_crc((uint8_t)value); - return value; - } - - int get_next_short() - { - int result = get_next_byte(); - result |= get_next_byte() << 8; - return result; - } - - int get_next_word() - { - int result = get_next_short(); - result |= get_next_short() << 8; - return result; - } - - void reset_error_flag() - { - _error_flag = false; - } - - bool get_error_flag() - { - return _error_flag; - } - - void reset_crc() - { - _crc = 0; - } - - uint16_t get_crc() - { - return _crc; - } - - bool is_at_end() - { - return _tape->is_at_end(); - } + protected: + bool _error_flag; private: + virtual std::unique_ptr get_wave_type_for_pulse(Storage::Tape::Tape::Pulse) = 0; + virtual std::unique_ptr dequeue_next_symbol(std::deque _wave_queue) = 0; + std::deque _wave_queue; + std::shared_ptr _tape; +}; + +enum class WaveType { + Short, Long, Unrecognised +}; + +enum class SymbolType { + One, Zero +}; + +class Acorn1200BaudTapeParser: public TapeParser { + public: + Acorn1200BaudTapeParser(const std::shared_ptr &tape) : TapeParser(tape) {} + + int get_next_bit() + { + std::unique_ptr symbol = get_next_symbol(); + return (symbol && *symbol == SymbolType::One) ? 1 : 0; + } + + int get_next_byte() + { + int value = 0; + int c = 8; + if(get_next_bit()) + { + _error_flag = true; + return -1; + } + while(c--) + { + value = (value >> 1) | (get_next_bit() << 7); + } + if(!get_next_bit()) + { + _error_flag = true; + return -1; + } + add_to_crc((uint8_t)value); + return value; + } + + int get_next_short() + { + int result = get_next_byte(); + result |= get_next_byte() << 8; + return result; + } + + int get_next_word() + { + int result = get_next_short(); + result |= get_next_short() << 8; + return result; + } + + void reset_crc() { _crc = 0; } + uint16_t get_crc() { return _crc; } + + private: + std::unique_ptr get_wave_type_for_pulse(Storage::Tape::Tape::Pulse pulse) + { + WaveType wave_type = WaveType::Unrecognised; + 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) wave_type = WaveType::Short; + if(pulse_length >= 0.35 / 1200.0 && pulse_length < 0.7 / 1200.0) wave_type = WaveType::Long; + break; + } + + return std::unique_ptr(new WaveType(wave_type)); + } + + std::unique_ptr dequeue_next_symbol(std::deque _wave_queue) + { + while(_wave_queue.size() && _wave_queue.front() == WaveType::Unrecognised) + { + _wave_queue.pop_front(); + } + + if(_wave_queue.size() >= 2 && _wave_queue[0] == WaveType::Long && _wave_queue[1] == WaveType::Long) + { + _wave_queue.erase(_wave_queue.begin(), _wave_queue.begin()+2); + return std::unique_ptr(new SymbolType(SymbolType::Zero)); + } + + if( _wave_queue.size() >= 4 && + _wave_queue[0] == WaveType::Short && + _wave_queue[1] == WaveType::Short && + _wave_queue[2] == WaveType::Short && + _wave_queue[3] == WaveType::Short) + { + _wave_queue.erase(_wave_queue.begin(), _wave_queue.begin()+4); + return std::unique_ptr(new SymbolType(SymbolType::One)); + } + + return nullptr; + } + void add_to_crc(uint8_t value) { _crc ^= (uint16_t)value << 8; @@ -138,21 +167,10 @@ struct TapeParser { } } - void rotate(int places) - { - _wave_length_pointer -= places; - if(places < 4) memmove(_wave_lengths, &_wave_lengths[places], (size_t)(4 - places) * sizeof(float)); - } - uint16_t _crc; - bool _error_flag; - - float _wave_lengths[4]; - int _wave_length_pointer; - std::shared_ptr _tape; }; -static std::unique_ptr GetNextChunk(TapeParser &parser) +static std::unique_ptr GetNextChunk(Acorn1200BaudTapeParser &parser) { std::unique_ptr new_chunk(new File::Chunk); int shift_register = 0; @@ -269,7 +287,7 @@ std::unique_ptr GetNextFile(std::deque &chunks) std::list StaticAnalyser::Acorn::GetFiles(const std::shared_ptr &tape) { - TapeParser parser(tape); + Acorn1200BaudTapeParser parser(tape); // populate chunk list std::deque chunk_list; From 68874b6080ec269ff553ee0620826fda35a389a9 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 5 Sep 2016 17:53:57 -0400 Subject: [PATCH 26/66] Fixed accidental implicit assumption that Acorn files will be at least two blocks. --- StaticAnalyser/Acorn/Tape.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/StaticAnalyser/Acorn/Tape.cpp b/StaticAnalyser/Acorn/Tape.cpp index 2a814ff2a..7d342fa62 100644 --- a/StaticAnalyser/Acorn/Tape.cpp +++ b/StaticAnalyser/Acorn/Tape.cpp @@ -51,7 +51,7 @@ template class TapeParser { private: virtual std::unique_ptr get_wave_type_for_pulse(Storage::Tape::Tape::Pulse) = 0; - virtual std::unique_ptr dequeue_next_symbol(std::deque _wave_queue) = 0; + virtual std::unique_ptr dequeue_next_symbol(std::deque &_wave_queue) = 0; std::deque _wave_queue; std::shared_ptr _tape; }; @@ -131,7 +131,7 @@ class Acorn1200BaudTapeParser: public TapeParser { return std::unique_ptr(new WaveType(wave_type)); } - std::unique_ptr dequeue_next_symbol(std::deque _wave_queue) + std::unique_ptr dequeue_next_symbol(std::deque &_wave_queue) { while(_wave_queue.size() && _wave_queue.front() == WaveType::Unrecognised) { @@ -254,13 +254,11 @@ std::unique_ptr GetNextFile(std::deque &chunks) // accumulate chunks for as long as block number is sequential and the end-of-file bit isn't set std::unique_ptr file(new File); - uint16_t block_number = chunks.front().block_number; - file->chunks.push_back(chunks.front()); - chunks.pop_front(); + uint16_t block_number = 0; while(chunks.size()) { - if(chunks.front().block_number != block_number + 1) return nullptr; + if(chunks.front().block_number != block_number) return nullptr; bool was_last = chunks.front().block_flag & 0x80; file->chunks.push_back(chunks.front()); From e25fee2332d6f266cc013ca2f90980081649dbda Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 5 Sep 2016 17:55:40 -0400 Subject: [PATCH 27/66] I'm going back to believing in this test. --- StaticAnalyser/Acorn/AcornAnalyser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/StaticAnalyser/Acorn/AcornAnalyser.cpp b/StaticAnalyser/Acorn/AcornAnalyser.cpp index ce9fc5d5c..0803c39a2 100644 --- a/StaticAnalyser/Acorn/AcornAnalyser.cpp +++ b/StaticAnalyser/Acorn/AcornAnalyser.cpp @@ -84,7 +84,7 @@ void StaticAnalyser::Acorn::AddTargets( bool is_basic = true; // protected files are always for *RUNning only -// if(files.front().is_protected) is_basic = false; + if(files.front().is_protected) is_basic = false; // check also for a continuous threading of BASIC lines; if none then this probably isn't BASIC code, // so that's also justification to *RUN From 623929721223393ccb00d53c703c4d227807d06a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 5 Sep 2016 18:15:15 -0400 Subject: [PATCH 28/66] Fixed off-by-one error on filename lengths and order-of-operations mistake preventing data CRC from being checked. --- StaticAnalyser/Acorn/Tape.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/StaticAnalyser/Acorn/Tape.cpp b/StaticAnalyser/Acorn/Tape.cpp index 7d342fa62..77f5b54a4 100644 --- a/StaticAnalyser/Acorn/Tape.cpp +++ b/StaticAnalyser/Acorn/Tape.cpp @@ -196,14 +196,15 @@ static std::unique_ptr GetNextChunk(Acorn1200BaudTapeParser &parser parser.reset_error_flag(); // read out name - char name[10]; + char name[11]; int name_ptr = 0; - while(!parser.is_at_end() && name_ptr < 10) + while(!parser.is_at_end() && name_ptr < 11) { name[name_ptr] = (char)parser.get_next_byte(); if(!name[name_ptr]) break; name_ptr++; } + name[10] = '\0'; new_chunk->name = name; // addresses @@ -226,7 +227,7 @@ static std::unique_ptr GetNextChunk(Acorn1200BaudTapeParser &parser new_chunk->data.push_back((uint8_t)parser.get_next_byte()); } - if(new_chunk->block_length && !new_chunk->block_flag&0x40) + if(new_chunk->block_length && !(new_chunk->block_flag&0x40)) { uint16_t calculated_data_crc = parser.get_crc(); uint16_t stored_data_crc = (uint16_t)parser.get_next_short(); From 11850b872dbf33f15f823856bcbcadba57671fe8 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 5 Sep 2016 18:28:43 -0400 Subject: [PATCH 29/66] Sought to emulate 0111 as a longer 0110 to test a particular HQ UEF. Some progress. Not great. --- StaticAnalyser/Acorn/AcornAnalyser.cpp | 2 +- StaticAnalyser/Acorn/Tape.cpp | 4 ++-- Storage/Tape/Formats/TapeUEF.cpp | 21 ++++++++++++++++----- Storage/Tape/Formats/TapeUEF.hpp | 4 ++++ 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/StaticAnalyser/Acorn/AcornAnalyser.cpp b/StaticAnalyser/Acorn/AcornAnalyser.cpp index 0803c39a2..db261ff6c 100644 --- a/StaticAnalyser/Acorn/AcornAnalyser.cpp +++ b/StaticAnalyser/Acorn/AcornAnalyser.cpp @@ -112,6 +112,6 @@ void StaticAnalyser::Acorn::AddTargets( // TODO: disks - if(target.tapes.size() || target.cartridges.size()) +// if(target.tapes.size() || target.cartridges.size()) destination.push_back(target); } diff --git a/StaticAnalyser/Acorn/Tape.cpp b/StaticAnalyser/Acorn/Tape.cpp index 77f5b54a4..fcb893fb5 100644 --- a/StaticAnalyser/Acorn/Tape.cpp +++ b/StaticAnalyser/Acorn/Tape.cpp @@ -198,13 +198,13 @@ static std::unique_ptr GetNextChunk(Acorn1200BaudTapeParser &parser // read out name char name[11]; int name_ptr = 0; - while(!parser.is_at_end() && name_ptr < 11) + while(!parser.is_at_end() && name_ptr < sizeof(name)) { name[name_ptr] = (char)parser.get_next_byte(); if(!name[name_ptr]) break; name_ptr++; } - name[10] = '\0'; + name[sizeof(name)-1] = '\0'; new_chunk->name = name; // addresses diff --git a/Storage/Tape/Formats/TapeUEF.cpp b/Storage/Tape/Formats/TapeUEF.cpp index 5151a1cf9..7221e56fc 100644 --- a/Storage/Tape/Formats/TapeUEF.cpp +++ b/Storage/Tape/Formats/TapeUEF.cpp @@ -7,8 +7,9 @@ // #include "TapeUEF.hpp" -#include -#include +#include +#include +#include using namespace Storage::Tape; @@ -123,6 +124,7 @@ Storage::Tape::Tape::Pulse UEF::get_next_pulse() _bit_position = (_bit_position+1)&(_current_bit ? 3 : 1); break; + case 0x0111: // TODO: insert dummy byte case 0x0110: next_pulse.type = (_bit_position&1) ? Pulse::High : Pulse::Low; next_pulse.length.length = 1; @@ -219,9 +221,14 @@ void UEF::find_next_tape_chunk() _chunk_duration.length |= (uint16_t)(gzgetc(_file) << 8); gzseek(_file, _chunk_length - 2, SEEK_CUR); return; -// case 0x0111: // carrier tone with dummy byte - // TODO: read lengths -// return; + case 0x0111: // carrier tone with dummy byte + _high_tone_with_dummy.pre_length = (uint16_t)gzgetc(_file); + _high_tone_with_dummy.pre_length |= (uint16_t)(gzgetc(_file) << 8); + _high_tone_with_dummy.post_length = (uint16_t)gzgetc(_file); + _high_tone_with_dummy.post_length |= (uint16_t)(gzgetc(_file) << 8); + _chunk_duration.length = _high_tone_with_dummy.post_length + _high_tone_with_dummy.pre_length + 10; + gzseek(_file, _chunk_length - 4, SEEK_CUR); + return; case 0x0114: // security cycles { // read number of cycles @@ -244,6 +251,7 @@ void UEF::find_next_tape_chunk() break; default: + printf("!!! Skipping %04x\n", _chunk_id); gzseek(_file, _chunk_length, SEEK_CUR); break; } @@ -257,6 +265,7 @@ bool UEF::chunk_is_finished() case 0x0100: return (_implicit_data_chunk.position / 10) == _chunk_length; case 0x0102: return (_explicit_data_chunk.position / 8) == _chunk_length; case 0x0114: + case 0x0111: case 0x0110: return _chunk_position == _chunk_duration.length; case 0x0112: @@ -310,6 +319,8 @@ bool UEF::get_next_bit() } break; + case 0x0111: + case 0x0110: _chunk_position++; return true; diff --git a/Storage/Tape/Formats/TapeUEF.hpp b/Storage/Tape/Formats/TapeUEF.hpp index 2b507d1b6..a4079b0fb 100644 --- a/Storage/Tape/Formats/TapeUEF.hpp +++ b/Storage/Tape/Formats/TapeUEF.hpp @@ -56,6 +56,10 @@ class UEF : public Tape { uint8_t current_byte; uint32_t position; } _explicit_data_chunk; + + struct { + unsigned int pre_length, post_length; + } _high_tone_with_dummy; }; uint8_t _current_byte; From 463b74301d84d02885088978b73049f309f49ada Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 5 Sep 2016 19:59:58 -0400 Subject: [PATCH 30/66] Sought to reduce the amount of heap nonsense. --- StaticAnalyser/Acorn/Tape.cpp | 103 ++++++++++++++++++---------------- 1 file changed, 55 insertions(+), 48 deletions(-) diff --git a/StaticAnalyser/Acorn/Tape.cpp b/StaticAnalyser/Acorn/Tape.cpp index fcb893fb5..0e5fc5890 100644 --- a/StaticAnalyser/Acorn/Tape.cpp +++ b/StaticAnalyser/Acorn/Tape.cpp @@ -18,29 +18,7 @@ using namespace StaticAnalyser::Acorn; */ template class TapeParser { public: - TapeParser(const std::shared_ptr &tape) : _tape(tape) {} - - std::unique_ptr get_next_symbol() - { - while(!_tape->is_at_end()) - { - std::unique_ptr symbol = dequeue_next_symbol(_wave_queue); - if(symbol) return symbol; - - while(!_tape->is_at_end()) - { - Storage::Tape::Tape::Pulse next_pulse = _tape->get_next_pulse(); - std::unique_ptr next_wave = get_wave_type_for_pulse(next_pulse); - if(next_wave) - { - _wave_queue.push_back(*next_wave); - break; - } - } - } - - return nullptr; - } + TapeParser(const std::shared_ptr &tape) : _tape(tape), _has_next_symbol(false) {} void reset_error_flag() { _error_flag = false; } bool get_error_flag() { return _error_flag; } @@ -48,11 +26,42 @@ template class TapeParser { protected: bool _error_flag; + void push_wave(WaveType wave) + { + _wave_queue.push_back(wave); + inspect_waves(_wave_queue); + } + + void remove_waves(int number_of_waves) + { + _wave_queue.erase(_wave_queue.begin(), _wave_queue.begin()+number_of_waves); + } + + void push_symbol(SymbolType symbol, int number_of_waves) + { + _has_next_symbol = true; + _next_symbol = symbol; + remove_waves(number_of_waves); + } + + SymbolType get_next_symbol() + { + while(!_has_next_symbol && !is_at_end()) + { + process_pulse(_tape->get_next_pulse()); + } + _has_next_symbol = false; + return _next_symbol; + } private: - virtual std::unique_ptr get_wave_type_for_pulse(Storage::Tape::Tape::Pulse) = 0; - virtual std::unique_ptr dequeue_next_symbol(std::deque &_wave_queue) = 0; - std::deque _wave_queue; + virtual void process_pulse(Storage::Tape::Tape::Pulse pulse) = 0; + virtual void inspect_waves(const std::vector &waves) = 0; + + std::vector _wave_queue; + SymbolType _next_symbol; + bool _has_next_symbol; + std::shared_ptr _tape; }; @@ -70,8 +79,8 @@ class Acorn1200BaudTapeParser: public TapeParser { int get_next_bit() { - std::unique_ptr symbol = get_next_symbol(); - return (symbol && *symbol == SymbolType::One) ? 1 : 0; + SymbolType symbol = get_next_symbol(); + return (symbol == SymbolType::One) ? 1 : 0; } int get_next_byte() @@ -114,47 +123,45 @@ class Acorn1200BaudTapeParser: public TapeParser { uint16_t get_crc() { return _crc; } private: - std::unique_ptr get_wave_type_for_pulse(Storage::Tape::Tape::Pulse pulse) + void process_pulse(Storage::Tape::Tape::Pulse pulse) { - WaveType wave_type = WaveType::Unrecognised; 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) wave_type = WaveType::Short; - if(pulse_length >= 0.35 / 1200.0 && pulse_length < 0.7 / 1200.0) wave_type = WaveType::Long; + 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; } break; } - return std::unique_ptr(new WaveType(wave_type)); + push_wave(WaveType::Unrecognised); } - std::unique_ptr dequeue_next_symbol(std::deque &_wave_queue) + void inspect_waves(const std::vector &waves) { - while(_wave_queue.size() && _wave_queue.front() == WaveType::Unrecognised) + while(waves.size() && waves[0] == WaveType::Unrecognised) { - _wave_queue.pop_front(); + remove_waves(1); + return; } - if(_wave_queue.size() >= 2 && _wave_queue[0] == WaveType::Long && _wave_queue[1] == WaveType::Long) + if(waves.size() >= 2 && waves[0] == WaveType::Long && waves[1] == WaveType::Long) { - _wave_queue.erase(_wave_queue.begin(), _wave_queue.begin()+2); - return std::unique_ptr(new SymbolType(SymbolType::Zero)); + push_symbol(SymbolType::Zero, 2); + return; } - if( _wave_queue.size() >= 4 && - _wave_queue[0] == WaveType::Short && - _wave_queue[1] == WaveType::Short && - _wave_queue[2] == WaveType::Short && - _wave_queue[3] == WaveType::Short) + if( waves.size() >= 4 && + waves[0] == WaveType::Short && + waves[1] == WaveType::Short && + waves[2] == WaveType::Short && + waves[3] == WaveType::Short) { - _wave_queue.erase(_wave_queue.begin(), _wave_queue.begin()+4); - return std::unique_ptr(new SymbolType(SymbolType::One)); + push_symbol(SymbolType::One, 4); + return; } - - return nullptr; } void add_to_crc(uint8_t value) From 05c24222d8eda11008837b4046c0ea411fe0d0cf Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 5 Sep 2016 20:02:35 -0400 Subject: [PATCH 31/66] Liberated the tape parser template. --- .../Clock Signal.xcodeproj/project.pbxproj | 2 + StaticAnalyser/Acorn/Tape.cpp | 56 +-------------- StaticAnalyser/TapeParser.hpp | 69 +++++++++++++++++++ 3 files changed, 73 insertions(+), 54 deletions(-) create mode 100644 StaticAnalyser/TapeParser.hpp diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 9775d8e94..fdbe15019 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -776,6 +776,7 @@ 4BCA98C21D065CA20062F44C /* 6522.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 6522.hpp; sourceTree = ""; }; 4BD14B0F1D74627C0088EAD6 /* AcornAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AcornAnalyser.cpp; path = ../../StaticAnalyser/Acorn/AcornAnalyser.cpp; sourceTree = ""; }; 4BD14B101D74627C0088EAD6 /* AcornAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = AcornAnalyser.hpp; path = ../../StaticAnalyser/Acorn/AcornAnalyser.hpp; sourceTree = ""; }; + 4BD328FD1D7E3EB5003B8C44 /* TapeParser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = TapeParser.hpp; path = ../../StaticAnalyser/TapeParser.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 = ""; }; 4BEE0A6A1D72496600532C7B /* Cartridge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Cartridge.cpp; sourceTree = ""; }; @@ -1604,6 +1605,7 @@ 4BF1354B1D6D2C300054B2EA /* StaticAnalyser.hpp */, 4BC830D21D6E7C6D0000A26F /* Commodore */, 4BD14B121D7462810088EAD6 /* Acorn */, + 4BD328FD1D7E3EB5003B8C44 /* TapeParser.hpp */, ); name = StaticAnalyser; sourceTree = ""; diff --git a/StaticAnalyser/Acorn/Tape.cpp b/StaticAnalyser/Acorn/Tape.cpp index 0e5fc5890..cf6104025 100644 --- a/StaticAnalyser/Acorn/Tape.cpp +++ b/StaticAnalyser/Acorn/Tape.cpp @@ -9,62 +9,10 @@ #include "Tape.hpp" #include +#include "../TapeParser.hpp" using namespace StaticAnalyser::Acorn; -/*! - A partly-abstract base class to help in the authorship of tape format parsers; - provides hooks for a -*/ -template class TapeParser { - public: - TapeParser(const std::shared_ptr &tape) : _tape(tape), _has_next_symbol(false) {} - - void reset_error_flag() { _error_flag = false; } - bool get_error_flag() { return _error_flag; } - bool is_at_end() { return _tape->is_at_end(); } - - protected: - bool _error_flag; - void push_wave(WaveType wave) - { - _wave_queue.push_back(wave); - inspect_waves(_wave_queue); - } - - void remove_waves(int number_of_waves) - { - _wave_queue.erase(_wave_queue.begin(), _wave_queue.begin()+number_of_waves); - } - - void push_symbol(SymbolType symbol, int number_of_waves) - { - _has_next_symbol = true; - _next_symbol = symbol; - remove_waves(number_of_waves); - } - - SymbolType get_next_symbol() - { - while(!_has_next_symbol && !is_at_end()) - { - process_pulse(_tape->get_next_pulse()); - } - _has_next_symbol = false; - return _next_symbol; - } - - private: - virtual void process_pulse(Storage::Tape::Tape::Pulse pulse) = 0; - virtual void inspect_waves(const std::vector &waves) = 0; - - std::vector _wave_queue; - SymbolType _next_symbol; - bool _has_next_symbol; - - std::shared_ptr _tape; -}; - enum class WaveType { Short, Long, Unrecognised }; @@ -73,7 +21,7 @@ enum class SymbolType { One, Zero }; -class Acorn1200BaudTapeParser: public TapeParser { +class Acorn1200BaudTapeParser: public StaticAnalyer::TapeParser { public: Acorn1200BaudTapeParser(const std::shared_ptr &tape) : TapeParser(tape) {} diff --git a/StaticAnalyser/TapeParser.hpp b/StaticAnalyser/TapeParser.hpp new file mode 100644 index 000000000..581e2d2fc --- /dev/null +++ b/StaticAnalyser/TapeParser.hpp @@ -0,0 +1,69 @@ +// +// TapeParser.hpp +// Clock Signal +// +// Created by Thomas Harte on 05/09/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#ifndef TapeParser_hpp +#define TapeParser_hpp + +namespace StaticAnalyer { + +/*! + A partly-abstract base class to help in the authorship of tape format parsers; + provides hooks for a +*/ +template class TapeParser { + public: + TapeParser(const std::shared_ptr &tape) : _tape(tape), _has_next_symbol(false) {} + + void reset_error_flag() { _error_flag = false; } + bool get_error_flag() { return _error_flag; } + bool is_at_end() { return _tape->is_at_end(); } + + protected: + bool _error_flag; + void push_wave(WaveType wave) + { + _wave_queue.push_back(wave); + inspect_waves(_wave_queue); + } + + void remove_waves(int number_of_waves) + { + _wave_queue.erase(_wave_queue.begin(), _wave_queue.begin()+number_of_waves); + } + + void push_symbol(SymbolType symbol, int number_of_waves) + { + _has_next_symbol = true; + _next_symbol = symbol; + remove_waves(number_of_waves); + } + + SymbolType get_next_symbol() + { + while(!_has_next_symbol && !is_at_end()) + { + process_pulse(_tape->get_next_pulse()); + } + _has_next_symbol = false; + return _next_symbol; + } + + private: + virtual void process_pulse(Storage::Tape::Tape::Pulse pulse) = 0; + virtual void inspect_waves(const std::vector &waves) = 0; + + std::vector _wave_queue; + SymbolType _next_symbol; + bool _has_next_symbol; + + std::shared_ptr _tape; +}; + +} + +#endif /* TapeParser_hpp */ From 963e307d0cf7af27f96f592458db3c7e510a4289 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 5 Sep 2016 22:06:39 -0400 Subject: [PATCH 32/66] Increased documentation. --- .../Clock Signal.xcodeproj/project.pbxproj | 4 +-- StaticAnalyser/TapeParser.hpp | 33 +++++++++++++++++-- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index fdbe15019..a96b60c01 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -1603,9 +1603,9 @@ children = ( 4BF1354A1D6D2C300054B2EA /* StaticAnalyser.cpp */, 4BF1354B1D6D2C300054B2EA /* StaticAnalyser.hpp */, - 4BC830D21D6E7C6D0000A26F /* Commodore */, - 4BD14B121D7462810088EAD6 /* Acorn */, 4BD328FD1D7E3EB5003B8C44 /* TapeParser.hpp */, + 4BD14B121D7462810088EAD6 /* Acorn */, + 4BC830D21D6E7C6D0000A26F /* Commodore */, ); name = StaticAnalyser; sourceTree = ""; diff --git a/StaticAnalyser/TapeParser.hpp b/StaticAnalyser/TapeParser.hpp index 581e2d2fc..7adc3c20c 100644 --- a/StaticAnalyser/TapeParser.hpp +++ b/StaticAnalyser/TapeParser.hpp @@ -13,29 +13,54 @@ namespace StaticAnalyer { /*! A partly-abstract base class to help in the authorship of tape format parsers; - provides hooks for a + provides hooks for pulse classification from pulses to waves and for symbol identification from + waves. + + Very optional, not intended to box in the approaches taken for analysis. */ template class TapeParser { public: - TapeParser(const std::shared_ptr &tape) : _tape(tape), _has_next_symbol(false) {} + /// Instantiates a new parser with the supplied @c tape. + TapeParser(const std::shared_ptr &tape) : _tape(tape), _has_next_symbol(false), _error_flag(false) {} + /// Resets the error flag. void reset_error_flag() { _error_flag = false; } + /// @returns @c true if an error has occurred since the error flag was last reset; @c false otherwise. bool get_error_flag() { return _error_flag; } + /// @returns @c true if the encapsulated tape has reached its end; @c false otherwise. bool is_at_end() { return _tape->is_at_end(); } protected: bool _error_flag; + + /*! + Adds @c wave to the back of the list of recognised waves and calls @c inspect_waves to check for a new symbol. + + Expected to be called by subclasses from @c process_pulse as and when recognised waves arise. + */ void push_wave(WaveType wave) { _wave_queue.push_back(wave); inspect_waves(_wave_queue); } + /*! + Removes @c nunber_of_waves waves from the front of the list. + + Expected to be called by subclasses from @c process_pulse if it is recognised that the first set of waves + do not form a valid symbol. + */ void remove_waves(int number_of_waves) { _wave_queue.erase(_wave_queue.begin(), _wave_queue.begin()+number_of_waves); } + /*! + Sets @c symbol as the newly-recognised symbol and removes @c nunber_of_waves waves from the front of the list. + + Expected to be called by subclasses from @c process_pulse when it recognises that the first @c number_of_waves + waves together represent @c symbol. + */ void push_symbol(SymbolType symbol, int number_of_waves) { _has_next_symbol = true; @@ -43,6 +68,10 @@ template class TapeParser { remove_waves(number_of_waves); } + /*! + Asks the parser to continue taking pulses from the tape until either the subclass next declares a symbol + or the tape runs out, returning the most-recently declared symbol. + */ SymbolType get_next_symbol() { while(!_has_next_symbol && !is_at_end()) From cfe8251166a53b311018ac2dca7bdc4d16ad7d2b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 5 Sep 2016 22:12:52 -0400 Subject: [PATCH 33/66] Further documented interface, fixed a potential parsing blockage. --- StaticAnalyser/Acorn/Tape.cpp | 19 +++++++++++++------ StaticAnalyser/TapeParser.hpp | 11 +++++++++++ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/StaticAnalyser/Acorn/Tape.cpp b/StaticAnalyser/Acorn/Tape.cpp index cf6104025..62adde4ca 100644 --- a/StaticAnalyser/Acorn/Tape.cpp +++ b/StaticAnalyser/Acorn/Tape.cpp @@ -101,13 +101,20 @@ class Acorn1200BaudTapeParser: public StaticAnalyer::TapeParser= 4 && - waves[0] == WaveType::Short && - waves[1] == WaveType::Short && - waves[2] == WaveType::Short && - waves[3] == WaveType::Short) + if(waves.size() >= 4) { - push_symbol(SymbolType::One, 4); + // If this makes a 1, post it. + if( waves[0] == WaveType::Short && + waves[1] == WaveType::Short && + waves[2] == WaveType::Short && + waves[3] == WaveType::Short) + { + push_symbol(SymbolType::One, 4); + return; + } + + // Otherwise, eject at least one wave as all options are exhausted. + remove_waves(1); return; } } diff --git a/StaticAnalyser/TapeParser.hpp b/StaticAnalyser/TapeParser.hpp index 7adc3c20c..581be2fab 100644 --- a/StaticAnalyser/TapeParser.hpp +++ b/StaticAnalyser/TapeParser.hpp @@ -83,7 +83,18 @@ template class TapeParser { } private: + /*! + Should be implemented by subclasses. Consumes @c pulse. Is likely either to call @c push_wave + or to take no action. + */ virtual void process_pulse(Storage::Tape::Tape::Pulse pulse) = 0; + + /*! + Should be implemented by subclasses. Inspects @c waves for a potential new symbol. If one is + found should call @c push_symbol. May wish alternatively to call @c remove_waves to have entries + removed from the start of @c waves that cannot form a valid symbol. Need not do anything while + the waves at the start of @c waves may end up forming a symbol but the symbol is not yet complete. + */ virtual void inspect_waves(const std::vector &waves) = 0; std::vector _wave_queue; From 8b933182afbc3384708865c5c14960404dcae952 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 6 Sep 2016 06:39:40 -0400 Subject: [PATCH 34/66] Added enough wiring (hopefully) that the Commodore tape's GetFiles should be called when appropriate. --- .../Clock Signal.xcodeproj/project.pbxproj | 6 ++++ .../Commodore/CommodoreAnalyser.cpp | 36 +++++++++++++++++++ .../Commodore/CommodoreAnalyser.hpp | 27 ++++++++++++++ StaticAnalyser/Commodore/Tape.cpp | 9 +++++ StaticAnalyser/Commodore/Tape.hpp | 2 ++ StaticAnalyser/StaticAnalyser.cpp | 7 ++-- 6 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 StaticAnalyser/Commodore/CommodoreAnalyser.cpp create mode 100644 StaticAnalyser/Commodore/CommodoreAnalyser.hpp diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index a96b60c01..ff9aee474 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -335,6 +335,7 @@ 4BBF99181C8FBA6F0075DAFB /* TextureTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBF99121C8FBA6F0075DAFB /* TextureTarget.cpp */; }; 4BC3B74F1CD194CC00F86E85 /* Shader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC3B74D1CD194CC00F86E85 /* Shader.cpp */; }; 4BC3B7521CD1956900F86E85 /* OutputShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC3B7501CD1956900F86E85 /* OutputShader.cpp */; }; + 4BC5E4921D7ED365008CF980 /* CommodoreAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC5E4901D7ED365008CF980 /* CommodoreAnalyser.cpp */; }; 4BC751B21D157E61006C31D9 /* 6522Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC751B11D157E61006C31D9 /* 6522Tests.swift */; }; 4BC76E691C98E31700E6EF73 /* FIRFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC76E671C98E31700E6EF73 /* FIRFilter.cpp */; }; 4BC76E6B1C98F43700E6EF73 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BC76E6A1C98F43700E6EF73 /* Accelerate.framework */; }; @@ -761,6 +762,8 @@ 4BC3B74E1CD194CC00F86E85 /* Shader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Shader.hpp; sourceTree = ""; }; 4BC3B7501CD1956900F86E85 /* OutputShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OutputShader.cpp; sourceTree = ""; }; 4BC3B7511CD1956900F86E85 /* OutputShader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = OutputShader.hpp; sourceTree = ""; }; + 4BC5E4901D7ED365008CF980 /* CommodoreAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommodoreAnalyser.cpp; path = ../../StaticAnalyser/Commodore/CommodoreAnalyser.cpp; sourceTree = ""; }; + 4BC5E4911D7ED365008CF980 /* CommodoreAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = CommodoreAnalyser.hpp; path = ../../StaticAnalyser/Commodore/CommodoreAnalyser.hpp; sourceTree = ""; }; 4BC751B11D157E61006C31D9 /* 6522Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 6522Tests.swift; sourceTree = ""; }; 4BC76E671C98E31700E6EF73 /* FIRFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FIRFilter.cpp; sourceTree = ""; }; 4BC76E681C98E31700E6EF73 /* FIRFilter.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FIRFilter.hpp; sourceTree = ""; }; @@ -1515,6 +1518,8 @@ 4BC830D21D6E7C6D0000A26F /* Commodore */ = { isa = PBXGroup; children = ( + 4BC5E4901D7ED365008CF980 /* CommodoreAnalyser.cpp */, + 4BC5E4911D7ED365008CF980 /* CommodoreAnalyser.hpp */, 4BC830CF1D6E7C690000A26F /* Tape.cpp */, 4BC830D01D6E7C690000A26F /* Tape.hpp */, ); @@ -2038,6 +2043,7 @@ 4BC76E691C98E31700E6EF73 /* FIRFilter.cpp in Sources */, 4B55CE5F1C3B7D960093A61B /* MachineDocument.swift in Sources */, 4B2A53A11D117D36003C6002 /* CSAtari2600.mm in Sources */, + 4BC5E4921D7ED365008CF980 /* CommodoreAnalyser.cpp in Sources */, 4BC830D11D6E7C690000A26F /* Tape.cpp in Sources */, 4B69FB441C4D941400B5F0AA /* TapeUEF.cpp in Sources */, 4B4DC8211D2C2425003C5BF8 /* Vic20.cpp in Sources */, diff --git a/StaticAnalyser/Commodore/CommodoreAnalyser.cpp b/StaticAnalyser/Commodore/CommodoreAnalyser.cpp new file mode 100644 index 000000000..90d30574e --- /dev/null +++ b/StaticAnalyser/Commodore/CommodoreAnalyser.cpp @@ -0,0 +1,36 @@ +// +// CommodoreAnalyser.cpp +// Clock Signal +// +// Created by Thomas Harte on 06/09/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#include "CommodoreAnalyser.hpp" + +#include "Tape.hpp" + +using namespace StaticAnalyser::Commodore; + +void StaticAnalyser::Commodore::AddTargets( + const std::list> &disks, + const std::list> &tapes, + const std::list> &cartridges, + std::list &destination) +{ + Target target; + target.machine = Target::Vic20; // TODO: machine estimation + target.probability = 1.0; // TODO: a proper estimation + + // strip out inappropriate cartridges +// target.cartridges = AcornCartridgesFrom(cartridges); + + // if there are any tapes, attempt to get data from the first + if(tapes.size() > 0) + { + std::shared_ptr tape = tapes.front(); + tape->reset(); + std::list files = GetFiles(tape); + tape->reset(); + } +} \ No newline at end of file diff --git a/StaticAnalyser/Commodore/CommodoreAnalyser.hpp b/StaticAnalyser/Commodore/CommodoreAnalyser.hpp new file mode 100644 index 000000000..003f1cc83 --- /dev/null +++ b/StaticAnalyser/Commodore/CommodoreAnalyser.hpp @@ -0,0 +1,27 @@ +// +// CommodoreAnalyser.hpp +// Clock Signal +// +// Created by Thomas Harte on 06/09/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#ifndef CommodoreAnalyser_hpp +#define CommodoreAnalyser_hpp + +#include "../StaticAnalyser.hpp" + +namespace StaticAnalyser { +namespace Commodore { + +void AddTargets( + const std::list> &disks, + const std::list> &tapes, + const std::list> &cartridges, + std::list &destination +); + +} +} + +#endif /* CommodoreAnalyser_hpp */ diff --git a/StaticAnalyser/Commodore/Tape.cpp b/StaticAnalyser/Commodore/Tape.cpp index ea826fe3f..ce236084b 100644 --- a/StaticAnalyser/Commodore/Tape.cpp +++ b/StaticAnalyser/Commodore/Tape.cpp @@ -7,3 +7,12 @@ // #include "Tape.hpp" + +using namespace StaticAnalyser::Commodore; + +std::list StaticAnalyser::Commodore::GetFiles(const std::shared_ptr &tape) +{ + std::list file_list; + + return file_list; +} diff --git a/StaticAnalyser/Commodore/Tape.hpp b/StaticAnalyser/Commodore/Tape.hpp index 7734b683f..1d92db3d1 100644 --- a/StaticAnalyser/Commodore/Tape.hpp +++ b/StaticAnalyser/Commodore/Tape.hpp @@ -25,6 +25,8 @@ struct File { std::vector data; }; +std::list GetFiles(const std::shared_ptr &tape); + } } #endif /* Tape_hpp */ diff --git a/StaticAnalyser/StaticAnalyser.cpp b/StaticAnalyser/StaticAnalyser.cpp index 404da6e8c..9a56221a4 100644 --- a/StaticAnalyser/StaticAnalyser.cpp +++ b/StaticAnalyser/StaticAnalyser.cpp @@ -12,6 +12,7 @@ // Analysers #include "Acorn/AcornAnalyser.hpp" +#include "Commodore/CommodoreAnalyser.hpp" // Cartridges #include "../Storage/Cartridge/Formats/BinaryDump.hpp" @@ -106,10 +107,8 @@ std::list StaticAnalyser::GetTargets(const char *file_name) // Hand off to platform-specific determination of whether these things are actually compatible and, // if so, how to load them. (TODO) - if(potential_platforms & (TargetPlatformType)TargetPlatform::Acorn) - { - Acorn::AddTargets(disks, tapes, cartridges, targets); - } + if(potential_platforms & (TargetPlatformType)TargetPlatform::Acorn) Acorn::AddTargets(disks, tapes, cartridges, targets); + if(potential_platforms & (TargetPlatformType)TargetPlatform::Commodore) Commodore::AddTargets(disks, tapes, cartridges, targets); free(lowercase_extension); return targets; From 149cbb327b57ec8125bd40335fab415942c0fffa Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 6 Sep 2016 06:59:51 -0400 Subject: [PATCH 35/66] Made something of a first attempt at a parser. Which is failing terribly. --- StaticAnalyser/Acorn/Tape.cpp | 30 ++++------ StaticAnalyser/Commodore/Tape.cpp | 98 ++++++++++++++++++++++++++++++- 2 files changed, 108 insertions(+), 20 deletions(-) diff --git a/StaticAnalyser/Acorn/Tape.cpp b/StaticAnalyser/Acorn/Tape.cpp index 62adde4ca..eeabfc61b 100644 --- a/StaticAnalyser/Acorn/Tape.cpp +++ b/StaticAnalyser/Acorn/Tape.cpp @@ -89,34 +89,26 @@ class Acorn1200BaudTapeParser: public StaticAnalyer::TapeParser &waves) { - while(waves.size() && waves[0] == WaveType::Unrecognised) - { - remove_waves(1); - return; - } + if(waves.size() < 2) return; - if(waves.size() >= 2 && waves[0] == WaveType::Long && waves[1] == WaveType::Long) + if(waves[0] == WaveType::Long && waves[1] == WaveType::Long) { push_symbol(SymbolType::Zero, 2); return; } - if(waves.size() >= 4) - { - // If this makes a 1, post it. - if( waves[0] == WaveType::Short && - waves[1] == WaveType::Short && - waves[2] == WaveType::Short && - waves[3] == WaveType::Short) - { - push_symbol(SymbolType::One, 4); - return; - } + if(waves.size() < 4) return; - // Otherwise, eject at least one wave as all options are exhausted. - remove_waves(1); + if( waves[0] == WaveType::Short && + waves[1] == WaveType::Short && + waves[2] == WaveType::Short && + waves[3] == WaveType::Short) + { + push_symbol(SymbolType::One, 4); return; } + + remove_waves(1); } void add_to_crc(uint8_t value) diff --git a/StaticAnalyser/Commodore/Tape.cpp b/StaticAnalyser/Commodore/Tape.cpp index ce236084b..1700eed44 100644 --- a/StaticAnalyser/Commodore/Tape.cpp +++ b/StaticAnalyser/Commodore/Tape.cpp @@ -8,11 +8,107 @@ #include "Tape.hpp" +#include +#include "../TapeParser.hpp" + using namespace StaticAnalyser::Commodore; +enum class WaveType { + Short, Medium, Long, Unrecognised +}; + +enum class SymbolType { + One, Zero, Word, EndOfBlock, LeadIn +}; + +class CommodoreROMTapeParser: public StaticAnalyer::TapeParser { + public: + CommodoreROMTapeParser(const std::shared_ptr &tape) : + TapeParser(tape), + _wave_period(0.0f), + _previous_was_high(false) {} + + void spin() + { + while(!is_at_end()) + { + SymbolType symbol = get_next_symbol(); + switch(symbol) + { + case SymbolType::One: printf("1"); break; + case SymbolType::Zero: printf("0"); break; + case SymbolType::Word: printf(" "); break; + case SymbolType::EndOfBlock: printf("\n"); break; + case SymbolType::LeadIn: printf("-"); break; + } + } + } + + private: + void process_pulse(Storage::Tape::Tape::Pulse pulse) + { + bool is_high = pulse.type == Storage::Tape::Tape::Pulse::High; + _wave_period += pulse.length.get_float(); + + if(!is_high && _previous_was_high) + { + if(_wave_period >= 0.000592 && _wave_period < 0.000752) push_wave(WaveType::Long); + else if(_wave_period >= 0.000432 && _wave_period < 0.000592) push_wave(WaveType::Medium); + else if(_wave_period >= 0.000272 && _wave_period < 0.000432) push_wave(WaveType::Short); + else push_wave(WaveType::Unrecognised); + + _wave_period = 0.0f; + } + + _previous_was_high = is_high; + } + bool _previous_was_high; + float _wave_period; + + void inspect_waves(const std::vector &waves) + { + if(waves.size() < 2) return; + + if(waves[0] == WaveType::Long && waves[1] == WaveType::Medium) + { + push_symbol(SymbolType::Word, 2); + return; + } + + if(waves[0] == WaveType::Long && waves[1] == WaveType::Short) + { + push_symbol(SymbolType::EndOfBlock, 2); + return; + } + + if(waves[0] == WaveType::Short && waves[1] == WaveType::Medium) + { + push_symbol(SymbolType::Zero, 2); + return; + } + + if(waves[0] == WaveType::Medium && waves[1] == WaveType::Short) + { + push_symbol(SymbolType::One, 2); + return; + } + + if(waves[0] == WaveType::Short) + { + push_symbol(SymbolType::LeadIn, 1); + return; + } + + // Otherwise, eject at least one wave as all options are exhausted. + remove_waves(1); + } +}; + std::list StaticAnalyser::Commodore::GetFiles(const std::shared_ptr &tape) { - std::list file_list; + CommodoreROMTapeParser parser(tape); + parser.spin(); + std::list file_list; return file_list; } From 8901e94f0f53b0261efa567ff4a583c9d5d20f85 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 6 Sep 2016 07:06:46 -0400 Subject: [PATCH 36/66] Fixed: was counting between low->high transitions rather than vice versa by adding low time then checking. --- StaticAnalyser/Commodore/Tape.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/StaticAnalyser/Commodore/Tape.cpp b/StaticAnalyser/Commodore/Tape.cpp index 1700eed44..870066666 100644 --- a/StaticAnalyser/Commodore/Tape.cpp +++ b/StaticAnalyser/Commodore/Tape.cpp @@ -48,8 +48,6 @@ class CommodoreROMTapeParser: public StaticAnalyer::TapeParser= 0.000592 && _wave_period < 0.000752) push_wave(WaveType::Long); @@ -60,6 +58,7 @@ class CommodoreROMTapeParser: public StaticAnalyer::TapeParser Date: Tue, 6 Sep 2016 08:49:32 -0400 Subject: [PATCH 37/66] Added a header parser for Commodore tapes. No time to grab file bodies now; time to go to work. --- .../Clock Signal.xcodeproj/project.pbxproj | 6 + StaticAnalyser/Commodore/Tape.cpp | 205 +++++++++++++++++- StaticAnalyser/Commodore/Tape.hpp | 2 + StaticAnalyser/Commodore/Utilities.cpp | 63 ++++++ StaticAnalyser/Commodore/Utilities.hpp | 22 ++ 5 files changed, 296 insertions(+), 2 deletions(-) create mode 100644 StaticAnalyser/Commodore/Utilities.cpp create mode 100644 StaticAnalyser/Commodore/Utilities.hpp diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index ff9aee474..f9890f142 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -336,6 +336,7 @@ 4BC3B74F1CD194CC00F86E85 /* Shader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC3B74D1CD194CC00F86E85 /* Shader.cpp */; }; 4BC3B7521CD1956900F86E85 /* OutputShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC3B7501CD1956900F86E85 /* OutputShader.cpp */; }; 4BC5E4921D7ED365008CF980 /* CommodoreAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC5E4901D7ED365008CF980 /* CommodoreAnalyser.cpp */; }; + 4BC5E4951D7EE0E0008CF980 /* Utilities.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC5E4931D7EE0E0008CF980 /* Utilities.cpp */; }; 4BC751B21D157E61006C31D9 /* 6522Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC751B11D157E61006C31D9 /* 6522Tests.swift */; }; 4BC76E691C98E31700E6EF73 /* FIRFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC76E671C98E31700E6EF73 /* FIRFilter.cpp */; }; 4BC76E6B1C98F43700E6EF73 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BC76E6A1C98F43700E6EF73 /* Accelerate.framework */; }; @@ -764,6 +765,8 @@ 4BC3B7511CD1956900F86E85 /* OutputShader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = OutputShader.hpp; sourceTree = ""; }; 4BC5E4901D7ED365008CF980 /* CommodoreAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommodoreAnalyser.cpp; path = ../../StaticAnalyser/Commodore/CommodoreAnalyser.cpp; sourceTree = ""; }; 4BC5E4911D7ED365008CF980 /* CommodoreAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = CommodoreAnalyser.hpp; path = ../../StaticAnalyser/Commodore/CommodoreAnalyser.hpp; sourceTree = ""; }; + 4BC5E4931D7EE0E0008CF980 /* Utilities.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Utilities.cpp; path = ../../StaticAnalyser/Commodore/Utilities.cpp; sourceTree = ""; }; + 4BC5E4941D7EE0E0008CF980 /* Utilities.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Utilities.hpp; path = ../../StaticAnalyser/Commodore/Utilities.hpp; sourceTree = ""; }; 4BC751B11D157E61006C31D9 /* 6522Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 6522Tests.swift; sourceTree = ""; }; 4BC76E671C98E31700E6EF73 /* FIRFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FIRFilter.cpp; sourceTree = ""; }; 4BC76E681C98E31700E6EF73 /* FIRFilter.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FIRFilter.hpp; sourceTree = ""; }; @@ -1522,6 +1525,8 @@ 4BC5E4911D7ED365008CF980 /* CommodoreAnalyser.hpp */, 4BC830CF1D6E7C690000A26F /* Tape.cpp */, 4BC830D01D6E7C690000A26F /* Tape.hpp */, + 4BC5E4931D7EE0E0008CF980 /* Utilities.cpp */, + 4BC5E4941D7EE0E0008CF980 /* Utilities.hpp */, ); name = Commodore; sourceTree = ""; @@ -2024,6 +2029,7 @@ 4B2BFC5F1D613E0200BA3AA9 /* TapePRG.cpp in Sources */, 4BAB62AD1D3272D200DF5BA0 /* Disk.cpp in Sources */, 4BC9DF4F1D04691600F44158 /* 6560.cpp in Sources */, + 4BC5E4951D7EE0E0008CF980 /* Utilities.cpp in Sources */, 4BB697CE1D4BA44400248BDF /* CommodoreGCR.cpp in Sources */, 4BD14B111D74627C0088EAD6 /* AcornAnalyser.cpp in Sources */, 4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */, diff --git a/StaticAnalyser/Commodore/Tape.cpp b/StaticAnalyser/Commodore/Tape.cpp index 870066666..3676a0210 100644 --- a/StaticAnalyser/Commodore/Tape.cpp +++ b/StaticAnalyser/Commodore/Tape.cpp @@ -8,8 +8,8 @@ #include "Tape.hpp" -#include #include "../TapeParser.hpp" +#include "Utilities.hpp" using namespace StaticAnalyser::Commodore; @@ -21,12 +21,114 @@ enum class SymbolType { One, Zero, Word, EndOfBlock, LeadIn }; +struct Header { + enum { + RelocatableProgram, + DataBlock, + NonRelocatableProgram, + SequenceHeader, + EndOfTape, + Unknown + } type; + + std::vector data; + std::wstring name; + std::vector raw_name; + uint16_t starting_address; + uint16_t ending_address; + bool parity_was_valid; + bool duplicate_matched; +}; + class CommodoreROMTapeParser: public StaticAnalyer::TapeParser { public: CommodoreROMTapeParser(const std::shared_ptr &tape) : TapeParser(tape), _wave_period(0.0f), - _previous_was_high(false) {} + _previous_was_high(false), + _parity_byte(0) {} + + /*! + Advances to the next header block on the tape, then consumes, parses, and returns it. + Returns @c nullptr if any wave-encoding level errors are encountered. + */ + std::unique_ptr
get_next_header() + { + std::unique_ptr
header(new Header); + reset_error_flag(); + + // find and proceed beyond lead-in tone + proceed_to_symbol(SymbolType::LeadIn); + + // look for landing zone + proceed_to_landing_zone(true); + reset_parity_byte(); + + // get header type + uint8_t header_type = get_next_byte(); + switch(header_type) + { + default: header->type = Header::Unknown; break; + case 0x01: header->type = Header::RelocatableProgram; break; + case 0x02: header->type = Header::DataBlock; break; + case 0x03: header->type = Header::NonRelocatableProgram; break; + case 0x04: header->type = Header::SequenceHeader; break; + case 0x05: header->type = Header::EndOfTape; break; + } + + // grab rest of data + header->data.reserve(191); + for(size_t c = 0; c < 191; c++) + { + header->data.push_back(get_next_byte()); + } + + uint8_t parity_byte = get_parity_byte(); + header->parity_was_valid = get_next_byte() == parity_byte; + + // check that the duplicate matches + proceed_to_landing_zone(false); + header->duplicate_matched = true; + if(get_next_byte() != header_type) header->duplicate_matched = false; + for(size_t c = 0; c < 191; c++) + { + if(header->data[c] != get_next_byte()) header->duplicate_matched = false; + } + if(get_next_byte() != parity_byte) header->duplicate_matched = false; + + // parse if this is not pure data + if(header->type != Header::DataBlock) + { + header->starting_address = (uint16_t)(header->data[0] | (header->data[1] << 8)); + header->ending_address = (uint16_t)(header->data[2] | (header->data[3] << 8)); + + for(size_t c = 0; c < 16; c++) + { + header->raw_name.push_back(header->data[4 + c]); + } + header->name = petscii_from_bytes(&header->raw_name[0], 16, false); + } + + if(get_error_flag()) return nullptr; + return header; + } + + std::unique_ptr> get_next_data() + { + std::unique_ptr> data(new std::vector); + + // find and proceed beyond lead-in tone to the next landing zone + proceed_to_symbol(SymbolType::LeadIn); + proceed_to_landing_zone(true); + + // accumulate until the next lead-in tone is hit +// while(!is_at_end()) +// { +// data->push_back(get_next_byte()); +// } + + return data; + } void spin() { @@ -45,6 +147,100 @@ class CommodoreROMTapeParser: public StaticAnalyer::TapeParser> 1) | (((next_symbol == SymbolType::One) ? 1 : 0) << 8); + } + + int check = byte_plus_parity; + check ^= (check >> 4); + check ^= (check >> 2); + check ^= (check >> 1); + if((check&1) == (byte_plus_parity >> 8)) _error_flag = true; + + add_parity_byte((uint8_t)byte_plus_parity); + return (uint8_t)byte_plus_parity; + } + + /*! + Returns the result of two consecutive @c get_next_byte calls, arranged in little-endian format. + */ + uint16_t get_next_short() + { + uint16_t value = get_next_byte(); + value |= get_next_byte() << 8; + return value; + } + + /*! + Per the contract with StaticAnalyser::TapeParser; sums time across pulses. If this pulse + indicates a high to low transition, inspects the time since the last transition, to produce + a long, medium, short or unrecognised wave period. + */ void process_pulse(Storage::Tape::Tape::Pulse pulse) { bool is_high = pulse.type == Storage::Tape::Tape::Pulse::High; @@ -64,6 +260,10 @@ class CommodoreROMTapeParser: public StaticAnalyer::TapeParser &waves) { if(waves.size() < 2) return; @@ -106,6 +306,7 @@ class CommodoreROMTapeParser: public StaticAnalyer::TapeParser StaticAnalyser::Commodore::GetFiles(const std::shared_ptr &tape) { CommodoreROMTapeParser parser(tape); + parser.get_next_header(); parser.spin(); std::list file_list; diff --git a/StaticAnalyser/Commodore/Tape.hpp b/StaticAnalyser/Commodore/Tape.hpp index 1d92db3d1..8ade612cb 100644 --- a/StaticAnalyser/Commodore/Tape.hpp +++ b/StaticAnalyser/Commodore/Tape.hpp @@ -16,6 +16,8 @@ namespace StaticAnalyser { namespace Commodore { struct File { + std::wstring name; + std::vector raw_name; uint16_t starting_address; uint16_t ending_address; enum { diff --git a/StaticAnalyser/Commodore/Utilities.cpp b/StaticAnalyser/Commodore/Utilities.cpp new file mode 100644 index 000000000..2f3a7e093 --- /dev/null +++ b/StaticAnalyser/Commodore/Utilities.cpp @@ -0,0 +1,63 @@ +// +// Utilities.cpp +// Clock Signal +// +// Created by Thomas Harte on 06/09/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#include "Utilities.hpp" + +std::wstring StaticAnalyser::Commodore::petscii_from_bytes(const uint8_t *string, int length, bool shifted) +{ + std::wstring result; + + wchar_t unshifted_characters[256] = + { + L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\r', L'\0', L'\0', + L'\0', L'\0', L'\0', L'\0', L'\b', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', + L' ', L'!', L'"', L'#', L'$', L'%', L'&', L'\'', L'(', L')', L'*', L'+', L',', L'-', L'.', L'/', + L'0', L'1', L'2', L'3', L'4', L'5', L'6', L'7', L'8', L'9', L'"', L';', L'<', L'=', L'>', L'?', + L'@', L'A', L'B', L'C', L'D', L'E', L'F', L'G', L'H', L'I', L'J', L'K', L'L', L'M', L'N', L'O', + L'P', L'Q', L'R', L'S', L'T', L'U', L'V', L'W', L'X', L'Y', L'Z', L'[', L'£', L']', L'↑', L'←', + L'─', L'♠', L'│', L'─', L'�', L'�', L'�', L'�', L'�', L'╮', L'╰', L'╯', L'�', L'╲', L'╱', L'�', + L'�', L'●', L'�', L'♥', L'�', L'╭', L'╳', L'○', L'♣', L'�', L'♦', L'┼', L'�', L'│', L'π', L'◥', + L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\r', L'\0', L'\0', + L'\0', L'\0', L'\0', L'\0', L'\b', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', + L' ', L'▌', L'▄', L'▔', L'▁', L'▏', L'▒', L'▕', L'�', L'◤', L'�', L'├', L'▗', L'└', L'┐', L'▂', + L'┌', L'┴', L'┬', L'┤', L'▎', L'▍', L'�', L'�', L'�', L'▃', L'�', L'▖', L'▝', L'┘', L'▘', L'▚', + L'─', L'♠', L'│', L'─', L'�', L'�', L'�', L'�', L'�', L'╮', L'╰', L'╯', L'�', L'╲', L'╱', L'�', + L'�', L'●', L'�', L'♥', L'�', L'╭', L'╳', L'○', L'♣', L'�', L'♦', L'┼', L'�', L'│', L'π', L'◥', + L' ', L'▌', L'▄', L'▔', L'▁', L'▏', L'▒', L'▕', L'�', L'◤', L'�', L'├', L'▗', L'└', L'┐', L'▂', + L'┌', L'┴', L'┬', L'┤', L'▎', L'▍', L'�', L'�', L'�', L'▃', L'�', L'▖', L'▝', L'┘', L'▘', L'π', + }; + + wchar_t shifted_characters[256] = + { + L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\r', L'\0', L'\0', + L'\0', L'\0', L'\0', L'\0', L'\b', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', + L' ', L'!', L'"', L'#', L'$', L'%', L'&', L'\'', L'(', L')', L'*', L'+', L',', L'-', L'.', L'/', + L'0', L'1', L'2', L'3', L'4', L'5', L'6', L'7', L'8', L'9', L'"', L';', L'<', L'=', L'>', L'?', + L'@', L'a', L'b', L'c', L'd', L'e', L'f', L'g', L'h', L'i', L'j', L'k', L'l', L'm', L'n', L'o', + L'p', L'q', L'r', L's', L't', L'u', L'v', L'w', L'x', L'y', L'z', L'[', L'£', L']', L'↑', L'←', + L'─', L'A', L'B', L'C', L'D', L'E', L'F', L'G', L'H', L'I', L'J', L'K', L'L', L'M', L'N', L'O', + L'P', L'Q', L'R', L'S', L'T', L'U', L'V', L'W', L'X', L'Y', L'Z', L'┼', L'�', L'│', L'▒', L'◥', + L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\r', L'\0', L'\0', + L'\0', L'\0', L'\0', L'\0', L'\b', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', + L' ', L'▌', L'▄', L'▔', L'▁', L'▏', L'▒', L'▕', L'�', L'�', L'�', L'├', L'▗', L'└', L'┐', L'▂', + L'┌', L'┴', L'┬', L'┤', L'▎', L'▍', L'�', L'�', L'�', L'▃', L'✓', L'▖', L'▝', L'┘', L'▘', L'▚', + L'─', L'A', L'B', L'C', L'D', L'E', L'F', L'G', L'H', L'I', L'J', L'K', L'L', L'M', L'N', L'O', + L'P', L'Q', L'R', L'S', L'T', L'U', L'V', L'W', L'X', L'Y', L'Z', L'┼', L'�', L'│', L'▒', L'�', + L' ', L'▌', L'▄', L'▔', L'▁', L'▏', L'▒', L'▕', L'�', L'�', L'�', L'├', L'▗', L'└', L'┐', L'▂', + L'┌', L'┴', L'┬', L'┤', L'▎', L'▍', L'�', L'�', L'�', L'▃', L'✓', L'▖', L'▝', L'┘', L'▘', L'▒', + }; + + wchar_t *table = shifted ? shifted_characters : unshifted_characters; + for(int c = 0; c < length; c++) + { + wchar_t next_character = table[string[c]]; + if(next_character) result.push_back(next_character); + } + + return result; +} diff --git a/StaticAnalyser/Commodore/Utilities.hpp b/StaticAnalyser/Commodore/Utilities.hpp new file mode 100644 index 000000000..1f4672424 --- /dev/null +++ b/StaticAnalyser/Commodore/Utilities.hpp @@ -0,0 +1,22 @@ +// +// Utilities.hpp +// Clock Signal +// +// Created by Thomas Harte on 06/09/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#ifndef Analyser_Commodore_Utilities_hpp +#define Analyser_Commodore_Utilities_hpp + +#include + +namespace StaticAnalyser { +namespace Commodore { + +std::wstring petscii_from_bytes(const uint8_t *string, int length, bool shifted); + +} +} + +#endif /* Utilities_hpp */ From d7d5f24aab7da6be9c4a1fe70002f5c7ff037033 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 6 Sep 2016 21:51:04 -0400 Subject: [PATCH 38/66] Attempted, at least, to insert a dummy byte as per the specification. --- Storage/Tape/Formats/TapeUEF.cpp | 33 ++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/Storage/Tape/Formats/TapeUEF.cpp b/Storage/Tape/Formats/TapeUEF.cpp index 7221e56fc..4db40f626 100644 --- a/Storage/Tape/Formats/TapeUEF.cpp +++ b/Storage/Tape/Formats/TapeUEF.cpp @@ -124,7 +124,36 @@ Storage::Tape::Tape::Pulse UEF::get_next_pulse() _bit_position = (_bit_position+1)&(_current_bit ? 3 : 1); break; - case 0x0111: // TODO: insert dummy byte + case 0x0111: + if(_chunk_position < _high_tone_with_dummy.pre_length || _chunk_position >= _high_tone_with_dummy.pre_length+10) + { + next_pulse.type = (_bit_position&1) ? Pulse::High : Pulse::Low; + next_pulse.length.length = 1; + next_pulse.length.clock_rate = _time_base * 4; + _bit_position ^= 1; + + if(!_bit_position) _chunk_position++; + } + else + { + // to output 0xaa: 0 + if(!_bit_position) + { + _current_bit = (0x354 >> (_chunk_position - _high_tone_with_dummy.pre_length))&1; + } + + next_pulse.type = (_bit_position&1) ? Pulse::High : Pulse::Low; + next_pulse.length.length = _current_bit ? 1 : 2; + next_pulse.length.clock_rate = _time_base * 4; + _bit_position = (_bit_position+1)&(_current_bit ? 3 : 1); + + if(!_bit_position) + { + _chunk_position++; + } + } + break; + case 0x0110: next_pulse.type = (_bit_position&1) ? Pulse::High : Pulse::Low; next_pulse.length.length = 1; @@ -303,7 +332,7 @@ bool UEF::get_next_bit() } break; - // TODO: 0x0104, 0x0111 + // TODO: 0x0104 case 0x0114: { From d66516fd6233f77d60d54974fc175564294356c8 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 6 Sep 2016 22:10:33 -0400 Subject: [PATCH 39/66] Finished parser for data blocks. --- StaticAnalyser/Commodore/Tape.cpp | 60 ++++++++++++++++++++++++------- 1 file changed, 47 insertions(+), 13 deletions(-) diff --git a/StaticAnalyser/Commodore/Tape.cpp b/StaticAnalyser/Commodore/Tape.cpp index 3676a0210..8d9963b2d 100644 --- a/StaticAnalyser/Commodore/Tape.cpp +++ b/StaticAnalyser/Commodore/Tape.cpp @@ -40,6 +40,12 @@ struct Header { bool duplicate_matched; }; +struct Data { + std::vector data; + bool parity_was_valid; + bool duplicate_matched; +}; + class CommodoreROMTapeParser: public StaticAnalyer::TapeParser { public: CommodoreROMTapeParser(const std::shared_ptr &tape) : @@ -113,20 +119,40 @@ class CommodoreROMTapeParser: public StaticAnalyer::TapeParser> get_next_data() + std::unique_ptr get_next_data() { - std::unique_ptr> data(new std::vector); + std::unique_ptr data(new Data); // find and proceed beyond lead-in tone to the next landing zone proceed_to_symbol(SymbolType::LeadIn); proceed_to_landing_zone(true); + reset_parity_byte(); - // accumulate until the next lead-in tone is hit -// while(!is_at_end()) -// { -// data->push_back(get_next_byte()); -// } + // accumulate until the next non-word marker is hit + while(!is_at_end()) + { + SymbolType start_symbol = get_next_symbol(); + if(start_symbol != SymbolType::Word) break; + data->data.push_back(get_next_byte_contents()); + } + // the above has reead the parity byte to the end of the data; if it matched the calculated parity it'll now be zero + data->parity_was_valid = !get_parity_byte(); + + // compare to the duplicate + proceed_to_symbol(SymbolType::LeadIn); + proceed_to_landing_zone(false); + reset_parity_byte(); + data->duplicate_matched = true; + for(size_t c = 0; c < data->data.size(); c++) + { + if(get_next_byte() != data->data[c]) data->duplicate_matched = false; + } + + // remove the captured parity + data->data.erase(data->data.end()-1); + + if(get_error_flag()) return nullptr; return data; } @@ -199,15 +225,22 @@ class CommodoreROMTapeParser: public StaticAnalyer::TapeParser StaticAnalyser::Commodore::GetFiles(const std::shared_ptr &tape) { CommodoreROMTapeParser parser(tape); - parser.get_next_header(); + std::unique_ptr
header = parser.get_next_header(); + std::unique_ptr data = parser.get_next_data(); parser.spin(); std::list file_list; From 6522a9a6c2b2f6cd5d0e37cef220ebdf9c66561a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 7 Sep 2016 07:34:48 -0400 Subject: [PATCH 40/66] Pulled the strands together provisionally to complete Commodore ROM-format tape parsing. Things are going to get complicated if or when I need to tackle turbo loaders. --- StaticAnalyser/Commodore/Tape.cpp | 70 +++++++++++++++++++++++++++---- StaticAnalyser/Commodore/Tape.hpp | 7 ++-- 2 files changed, 67 insertions(+), 10 deletions(-) diff --git a/StaticAnalyser/Commodore/Tape.cpp b/StaticAnalyser/Commodore/Tape.cpp index 8d9963b2d..65511293a 100644 --- a/StaticAnalyser/Commodore/Tape.cpp +++ b/StaticAnalyser/Commodore/Tape.cpp @@ -24,9 +24,9 @@ enum class SymbolType { struct Header { enum { RelocatableProgram, - DataBlock, NonRelocatableProgram, - SequenceHeader, + DataSequenceHeader, + DataBlock, EndOfTape, Unknown } type; @@ -78,7 +78,7 @@ class CommodoreROMTapeParser: public StaticAnalyer::TapeParsertype = Header::RelocatableProgram; break; case 0x02: header->type = Header::DataBlock; break; case 0x03: header->type = Header::NonRelocatableProgram; break; - case 0x04: header->type = Header::SequenceHeader; break; + case 0x04: header->type = Header::DataSequenceHeader; break; case 0x05: header->type = Header::EndOfTape; break; } @@ -339,10 +339,66 @@ class CommodoreROMTapeParser: public StaticAnalyer::TapeParser StaticAnalyser::Commodore::GetFiles(const std::shared_ptr &tape) { CommodoreROMTapeParser parser(tape); - std::unique_ptr
header = parser.get_next_header(); - std::unique_ptr data = parser.get_next_data(); - parser.spin(); - std::list file_list; + + std::unique_ptr
header = parser.get_next_header(); + + while(!parser.is_at_end()) + { + if(!header) + { + header = parser.get_next_header(); + continue; + } + + switch(header->type) + { + case Header::DataSequenceHeader: + { + File new_file; + new_file.name = header->name; + new_file.raw_name = header->raw_name; + new_file.starting_address = header->starting_address; + new_file.ending_address = header->ending_address; + new_file.type = File::DataSequence; + + new_file.data.swap(header->data); + while(1) + { + header = parser.get_next_header(); + if(header->type != Header::DataBlock) break; + std::copy(header->data.begin(), header->data.end(), std::back_inserter(new_file.data)); + } + + file_list.push_back(new_file); + } + break; + + case Header::RelocatableProgram: + case Header::NonRelocatableProgram: + { + std::unique_ptr data = parser.get_next_data(); + + File new_file; + new_file.name = header->name; + new_file.raw_name = header->raw_name; + new_file.starting_address = header->starting_address; + new_file.ending_address = header->ending_address; + new_file.data.swap(data->data); + new_file.type = (header->type == Header::RelocatableProgram) ? File::RelocatableProgram : File::NonRelocatableProgram; + + file_list.push_back(new_file); + + header = parser.get_next_header(); + } + break; + + default: + header = parser.get_next_header(); + break; + } + } + + return file_list; } diff --git a/StaticAnalyser/Commodore/Tape.hpp b/StaticAnalyser/Commodore/Tape.hpp index 8ade612cb..f156fd02a 100644 --- a/StaticAnalyser/Commodore/Tape.hpp +++ b/StaticAnalyser/Commodore/Tape.hpp @@ -21,9 +21,10 @@ struct File { uint16_t starting_address; uint16_t ending_address; enum { - Program, - Stream - } Type; + RelocatableProgram, + NonRelocatableProgram, + DataSequence, + } type; std::vector data; }; From 556b77f2fd6188036a49466104fe079fd21d601e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 7 Sep 2016 07:39:47 -0400 Subject: [PATCH 41/66] Added some TODOs, re-enabled master Acorn decision. --- StaticAnalyser/Acorn/AcornAnalyser.cpp | 2 +- StaticAnalyser/Commodore/CommodoreAnalyser.cpp | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/StaticAnalyser/Acorn/AcornAnalyser.cpp b/StaticAnalyser/Acorn/AcornAnalyser.cpp index db261ff6c..0803c39a2 100644 --- a/StaticAnalyser/Acorn/AcornAnalyser.cpp +++ b/StaticAnalyser/Acorn/AcornAnalyser.cpp @@ -112,6 +112,6 @@ void StaticAnalyser::Acorn::AddTargets( // TODO: disks -// if(target.tapes.size() || target.cartridges.size()) + if(target.tapes.size() || target.cartridges.size()) destination.push_back(target); } diff --git a/StaticAnalyser/Commodore/CommodoreAnalyser.cpp b/StaticAnalyser/Commodore/CommodoreAnalyser.cpp index 90d30574e..7fce5cf3c 100644 --- a/StaticAnalyser/Commodore/CommodoreAnalyser.cpp +++ b/StaticAnalyser/Commodore/CommodoreAnalyser.cpp @@ -32,5 +32,16 @@ void StaticAnalyser::Commodore::AddTargets( tape->reset(); std::list files = GetFiles(tape); tape->reset(); + + // continue if there are any files + if(files.size()) + { + // TODO: decide between ,1 (don't relocate; for machine code) and ,0 (relocate; for BASIC) + // TODO: decide memory model (based on extents and sizes) + // TODO: decide machine (disassemble?) + } } + + if(target.tapes.size() || target.cartridges.size() || target.disks.size()) + destination.push_back(target); } \ No newline at end of file From 50175a9aed15db7a1648853d85142f05da7e7be6 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 7 Sep 2016 22:17:19 -0400 Subject: [PATCH 42/66] Added logic to try to spot when the first program is BASIC and, if so, what the correct memory model is, then to get that information to the Vic. Though it currently then gets overwritten by the view controller. Grrrr. --- Machines/Commodore/Vic-20/Vic20.cpp | 33 ++++++++- Machines/Commodore/Vic-20/Vic20.hpp | 2 + .../Documents/Vic20Document.swift | 4 ++ .../Clock Signal/Machine/Wrappers/CSVic20.mm | 6 ++ .../Commodore/CommodoreAnalyser.cpp | 70 +++++++++++++++++-- StaticAnalyser/StaticAnalyser.hpp | 12 ++-- 6 files changed, 116 insertions(+), 11 deletions(-) diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index e1a7b993c..76eedc4cd 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -269,10 +269,39 @@ void Machine::set_prg(const char *file_name, size_t length, const uint8_t *data) #pragma mar - Tape +void Machine::configure_as_target(const StaticAnalyser::Target &target) +{ + if(target.tapes.size()) + { + _tape.set_tape(target.tapes.front()); + } + + if(_should_automatically_load_media) + { + if(target.loadingCommand.length()) // TODO: and automatic loading option enabled + { + set_typer_for_string(target.loadingCommand.c_str()); + } + + switch(target.vic20.memory_model) + { + case StaticAnalyser::Vic20MemoryModel::Unexpanded: + set_memory_size(Default); + break; + case StaticAnalyser::Vic20MemoryModel::EightKB: + set_memory_size(ThreeKB); + break; + case StaticAnalyser::Vic20MemoryModel::ThirtyTwoKB: + set_memory_size(ThirtyTwoKB); + break; + } + } +} + void Machine::set_tape(std::shared_ptr tape) { - _tape.set_tape(tape); - if(_should_automatically_load_media) set_typer_for_string("LOAD\nRUN\n"); +// _tape.set_tape(tape); +// if(_should_automatically_load_media) set_typer_for_string("LOAD\nRUN\n"); } void Machine::tape_did_change_input(Tape *tape) diff --git a/Machines/Commodore/Vic-20/Vic20.hpp b/Machines/Commodore/Vic-20/Vic20.hpp index 59fec787b..8e6fe001b 100644 --- a/Machines/Commodore/Vic-20/Vic20.hpp +++ b/Machines/Commodore/Vic-20/Vic20.hpp @@ -21,6 +21,7 @@ #include "../../../Storage/Tape/Tape.hpp" #include "../../../Storage/Disk/Disk.hpp" +#include "../../../StaticAnalyser/StaticAnalyser.hpp" namespace Commodore { namespace Vic20 { @@ -260,6 +261,7 @@ class Machine: ~Machine(); void set_rom(ROMSlot slot, size_t length, const uint8_t *data); + void configure_as_target(const StaticAnalyser::Target &target); void set_prg(const char *file_name, size_t length, const uint8_t *data); void set_tape(std::shared_ptr tape); void set_disk(std::shared_ptr disk); diff --git a/OSBindings/Mac/Clock Signal/Documents/Vic20Document.swift b/OSBindings/Mac/Clock Signal/Documents/Vic20Document.swift index 6eb656b3b..a45cc8919 100644 --- a/OSBindings/Mac/Clock Signal/Documents/Vic20Document.swift +++ b/OSBindings/Mac/Clock Signal/Documents/Vic20Document.swift @@ -41,6 +41,10 @@ class Vic20Document: MachineDocument { return "Vic20Document" } + override func configureAs(analysis: CSStaticAnalyser) { + analysis.applyToMachine(vic20) + } + override func readFromURL(url: NSURL, ofType typeName: String) throws { if let pathExtension = url.pathExtension { switch pathExtension.lowercaseString { diff --git a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.mm b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.mm index 7198aafe7..9dad2a4a7 100644 --- a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.mm +++ b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.mm @@ -46,6 +46,12 @@ using namespace Commodore::Vic20; [self setROM:rom slot:Drive]; } +- (void)applyTarget:(StaticAnalyser::Target)target { + @synchronized(self) { + _vic20.configure_as_target(target); + } +} + - (BOOL)openTAPAtURL:(NSURL *)URL { @synchronized(self) { try { diff --git a/StaticAnalyser/Commodore/CommodoreAnalyser.cpp b/StaticAnalyser/Commodore/CommodoreAnalyser.cpp index 7fce5cf3c..ce9890f20 100644 --- a/StaticAnalyser/Commodore/CommodoreAnalyser.cpp +++ b/StaticAnalyser/Commodore/CommodoreAnalyser.cpp @@ -36,12 +36,74 @@ void StaticAnalyser::Commodore::AddTargets( // continue if there are any files if(files.size()) { - // TODO: decide between ,1 (don't relocate; for machine code) and ,0 (relocate; for BASIC) - // TODO: decide memory model (based on extents and sizes) - // TODO: decide machine (disassemble?) + bool is_basic = true; + + // decide whether this is a BASIC file based on the proposition that: + // (1) they're always relocatable; and + // (2) they have a per-line structure of: + // [4 bytes: address of start of next line] + // [4 bytes: this line number] + // ... null-terminated code ... + // (with a next line address of 0000 indicating end of program)ß + if(files.front().type != File::RelocatableProgram) is_basic = false; + else + { + uint16_t line_address = 0; + int line_number = -1; + + uint16_t starting_address = files.front().starting_address; + line_address = starting_address; + is_basic = false; + while(1) + { + if(line_address - starting_address >= files.front().data.size() + 2) break; + + uint16_t next_line_address = files.front().data[line_address - starting_address]; + next_line_address |= files.front().data[line_address - starting_address + 1] << 8; + + if(!next_line_address) + { + is_basic = true; + break; + } + if(next_line_address < line_address + 5) break; + + if(line_address - starting_address >= files.front().data.size() + 5) break; + uint16_t next_line_number = files.front().data[line_address - starting_address + 2]; + next_line_number |= files.front().data[line_address - starting_address + 3] << 8; + + if(next_line_number <= line_number) break; + + line_number = (uint16_t)next_line_number; + line_address = next_line_address; + } + } + + target.vic20.memory_model = Vic20MemoryModel::Unexpanded; + if(is_basic) + { + target.loadingCommand = "LOAD\"\",1,0\nRUN\n"; + switch(files.front().starting_address) + { + case 0x1001: + default: break; + case 0x1201: + target.vic20.memory_model = Vic20MemoryModel::ThirtyTwoKB; + break; + case 0x0401: + target.vic20.memory_model = Vic20MemoryModel::EightKB; + break; + } + } + else + { + // TODO: this is machine code. So, ummm? + } + + target.tapes = tapes; } } if(target.tapes.size() || target.cartridges.size() || target.disks.size()) destination.push_back(target); -} \ No newline at end of file +} diff --git a/StaticAnalyser/StaticAnalyser.hpp b/StaticAnalyser/StaticAnalyser.hpp index 1e3b59a22..691d5e364 100644 --- a/StaticAnalyser/StaticAnalyser.hpp +++ b/StaticAnalyser/StaticAnalyser.hpp @@ -19,6 +19,12 @@ namespace StaticAnalyser { +enum class Vic20MemoryModel { + Unexpanded, + EightKB, + ThirtyTwoKB +}; + /*! A list of disks, tapes and cartridges plus information about the machine to which to attach them and its configuration, and instructions on how to launch the software attached, plus a measure of confidence in this target's correctness. @@ -33,11 +39,7 @@ struct Target { union { struct { - enum class Vic20 { - Unexpanded, - EightKB, - ThirtyTwoKB - } memoryModel; + Vic20MemoryModel memory_model; bool has_c1540; } vic20; From 8c84f3581abf91aefe166af9bd9cbc26077fb379 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 8 Sep 2016 05:32:17 -0400 Subject: [PATCH 43/66] Attempted to bring some uniformity in application of configurations. --- Machines/Commodore/Vic-20/Vic20.hpp | 5 ++-- Machines/ConfigurationTarget.hpp | 27 +++++++++++++++++++ Machines/Electron/Electron.hpp | 7 ++--- .../Clock Signal.xcodeproj/project.pbxproj | 2 ++ .../Documents/ElectronDocument.swift | 4 --- .../Documents/MachineDocument.swift | 1 + .../Documents/Vic20Document.swift | 4 --- .../Mac/Clock Signal/Machine/CSMachine.mm | 10 ++++++- .../Machine/Wrappers/CSElectron.mm | 7 ----- .../Clock Signal/Machine/Wrappers/CSVic20.mm | 6 ----- 10 files changed, 46 insertions(+), 27 deletions(-) create mode 100644 Machines/ConfigurationTarget.hpp diff --git a/Machines/Commodore/Vic-20/Vic20.hpp b/Machines/Commodore/Vic-20/Vic20.hpp index 8e6fe001b..4a9a65865 100644 --- a/Machines/Commodore/Vic-20/Vic20.hpp +++ b/Machines/Commodore/Vic-20/Vic20.hpp @@ -9,6 +9,7 @@ #ifndef Vic20_hpp #define Vic20_hpp +#include "../../ConfigurationTarget.hpp" #include "../../CRTMachine.hpp" #include "../../Typer.hpp" @@ -21,7 +22,6 @@ #include "../../../Storage/Tape/Tape.hpp" #include "../../../Storage/Disk/Disk.hpp" -#include "../../../StaticAnalyser/StaticAnalyser.hpp" namespace Commodore { namespace Vic20 { @@ -254,7 +254,8 @@ class Machine: public CRTMachine::Machine, public MOS::MOS6522IRQDelegate::Delegate, public Utility::TypeRecipient, - public Tape::Delegate { + public Tape::Delegate, + public ConfigurationTarget::Machine { public: Machine(); diff --git a/Machines/ConfigurationTarget.hpp b/Machines/ConfigurationTarget.hpp new file mode 100644 index 000000000..10521bef2 --- /dev/null +++ b/Machines/ConfigurationTarget.hpp @@ -0,0 +1,27 @@ +// +// ConfigurationTarget.h +// Clock Signal +// +// Created by Thomas Harte on 08/09/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#ifndef ConfigurationTarget_hpp +#define ConfigurationTarget_hpp + +#include "../StaticAnalyser/StaticAnalyser.hpp" + +namespace ConfigurationTarget { + +/*! + A ConfigurationTarget::Machine is anything that can accept a StaticAnalyser::Target + and configure itself appropriately. +*/ +class Machine { + public: + virtual void configure_as_target(const StaticAnalyser::Target &target) =0; +}; + +} + +#endif /* ConfigurationTarget_h */ diff --git a/Machines/Electron/Electron.hpp b/Machines/Electron/Electron.hpp index 19b021dbf..b22efe095 100644 --- a/Machines/Electron/Electron.hpp +++ b/Machines/Electron/Electron.hpp @@ -11,8 +11,8 @@ #include "../../Processors/6502/CPU6502.hpp" #include "../../Storage/Tape/Tape.hpp" -#include "../../StaticAnalyser/StaticAnalyser.hpp" +#include "../ConfigurationTarget.hpp" #include "../CRTMachine.hpp" #include "../Typer.hpp" @@ -139,8 +139,9 @@ class Speaker: public ::Outputs::Filter { class Machine: public CPU6502::Processor, public CRTMachine::Machine, - Tape::Delegate, - public Utility::TypeRecipient { + public Tape::Delegate, + public Utility::TypeRecipient, + public ConfigurationTarget::Machine { public: Machine(); diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index f9890f142..79bc323ab 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -454,6 +454,7 @@ 4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 6502TimingTests.swift; sourceTree = ""; }; 4B96F7201D75119A0058BB2D /* Tape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Tape.cpp; path = ../../StaticAnalyser/Acorn/Tape.cpp; sourceTree = ""; }; 4B96F7211D75119A0058BB2D /* Tape.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Tape.hpp; path = ../../StaticAnalyser/Acorn/Tape.hpp; sourceTree = ""; }; + 4BA9C3CF1D8164A9002DDB61 /* ConfigurationTarget.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ConfigurationTarget.hpp; sourceTree = ""; }; 4BAB62AB1D3272D200DF5BA0 /* Disk.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Disk.cpp; sourceTree = ""; }; 4BAB62AC1D3272D200DF5BA0 /* Disk.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Disk.hpp; sourceTree = ""; }; 4BAB62AE1D32730D00DF5BA0 /* Storage.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Storage.hpp; sourceTree = ""; }; @@ -1474,6 +1475,7 @@ 4B2E2D9E1C3A070900138695 /* Electron */, 4B1E85731D170228001EF87D /* Typer.cpp */, 4B1E85741D170228001EF87D /* Typer.hpp */, + 4BA9C3CF1D8164A9002DDB61 /* ConfigurationTarget.hpp */, ); name = Machines; path = ../../Machines; diff --git a/OSBindings/Mac/Clock Signal/Documents/ElectronDocument.swift b/OSBindings/Mac/Clock Signal/Documents/ElectronDocument.swift index 57ecf6d89..d587dbc32 100644 --- a/OSBindings/Mac/Clock Signal/Documents/ElectronDocument.swift +++ b/OSBindings/Mac/Clock Signal/Documents/ElectronDocument.swift @@ -44,10 +44,6 @@ class ElectronDocument: MachineDocument { return "ElectronDocument" } - override func configureAs(analysis: CSStaticAnalyser) { - analysis.applyToMachine(electron) - } - /* override func readFromURL(url: NSURL, ofType typeName: String) throws { if let pathExtension = url.pathExtension { switch pathExtension.lowercaseString { diff --git a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift index 8a2e8707a..9e85e3e50 100644 --- a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift +++ b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift @@ -100,6 +100,7 @@ class MachineDocument: // MARK: configuring func configureAs(analysis: CSStaticAnalyser) { + analysis.applyToMachine(self.machine) } // MARK: the pasteboard diff --git a/OSBindings/Mac/Clock Signal/Documents/Vic20Document.swift b/OSBindings/Mac/Clock Signal/Documents/Vic20Document.swift index a45cc8919..6eb656b3b 100644 --- a/OSBindings/Mac/Clock Signal/Documents/Vic20Document.swift +++ b/OSBindings/Mac/Clock Signal/Documents/Vic20Document.swift @@ -41,10 +41,6 @@ class Vic20Document: MachineDocument { return "Vic20Document" } - override func configureAs(analysis: CSStaticAnalyser) { - analysis.applyToMachine(vic20) - } - override func readFromURL(url: NSURL, ofType typeName: String) throws { if let pathExtension = url.pathExtension { switch pathExtension.lowercaseString { diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm index af551587d..2b67f9caa 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm @@ -9,7 +9,9 @@ #import "CSMachine.h" #import "CSMachine+Subclassing.h" #import "CSMachine+Target.h" + #include "Typer.hpp" +#include "ConfigurationTarget.hpp" @interface CSMachine() - (void)speaker:(Outputs::Speaker *)speaker didCompleteSamples:(const int16_t *)samples length:(int)length; @@ -123,6 +125,12 @@ struct MachineDelegate: CRTMachine::Machine::Delegate { typeRecipient->set_typer_for_string([paste UTF8String]); } -- (void)applyTarget:(StaticAnalyser::Target)target {} +- (void)applyTarget:(StaticAnalyser::Target)target { + @synchronized(self) { + ConfigurationTarget::Machine *const configurationTarget = + dynamic_cast(self.machine); + if(configurationTarget) configurationTarget->configure_as_target(target); + } +} @end diff --git a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSElectron.mm b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSElectron.mm index 34725c090..d3635d2a5 100644 --- a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSElectron.mm +++ b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSElectron.mm @@ -10,7 +10,6 @@ #include "Electron.hpp" #import "CSMachine+Subclassing.h" -#import "CSMachine+Target.h" #include "StaticAnalyser.hpp" #include "TapeUEF.hpp" @@ -38,12 +37,6 @@ } } -- (void)applyTarget:(StaticAnalyser::Target)target { - @synchronized(self) { - _electron.configure_as_target(target); - } -} - - (void)setROM:(nonnull NSData *)rom slot:(int)slot { @synchronized(self) { _electron.set_rom((Electron::ROMSlot)slot, rom.length, (const uint8_t *)rom.bytes); diff --git a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.mm b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.mm index 9dad2a4a7..7198aafe7 100644 --- a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.mm +++ b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.mm @@ -46,12 +46,6 @@ using namespace Commodore::Vic20; [self setROM:rom slot:Drive]; } -- (void)applyTarget:(StaticAnalyser::Target)target { - @synchronized(self) { - _vic20.configure_as_target(target); - } -} - - (BOOL)openTAPAtURL:(NSURL *)URL { @synchronized(self) { try { From 908dc40569125da1141086baf0e87a8689b1f676 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 8 Sep 2016 07:38:34 -0400 Subject: [PATCH 44/66] If loading automatically, assume that whatever was in the machine target set up the machine and don't override it. Too dodgy? More thought required. --- .../Documents/Vic20Document.swift | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/OSBindings/Mac/Clock Signal/Documents/Vic20Document.swift b/OSBindings/Mac/Clock Signal/Documents/Vic20Document.swift index 6eb656b3b..23dd816e4 100644 --- a/OSBindings/Mac/Clock Signal/Documents/Vic20Document.swift +++ b/OSBindings/Mac/Clock Signal/Documents/Vic20Document.swift @@ -161,18 +161,21 @@ class Vic20Document: MachineDocument { vic20.shouldLoadAutomatically = loadAutomatically self.loadAutomaticallyButton?.state = loadAutomatically ? NSOnState : NSOffState - let memorySize = standardUserDefaults.integerForKey(self.memorySizeUserDefaultsKey) - var indexToSelect: Int? - switch memorySize { - case 32: indexToSelect = 2 - case 8: indexToSelect = 1 - default: indexToSelect = 0 - } - if let indexToSelect = indexToSelect { - self.memorySizeButton?.selectItemAtIndex(indexToSelect) - setMemorySize(indexToSelect) + if !loadAutomatically { + let memorySize = standardUserDefaults.integerForKey(self.memorySizeUserDefaultsKey) + var indexToSelect: Int? + switch memorySize { + case 32: indexToSelect = 2 + case 8: indexToSelect = 1 + default: indexToSelect = 0 + } + if let indexToSelect = indexToSelect { + self.memorySizeButton?.selectItemAtIndex(indexToSelect) + setMemorySize(indexToSelect) + } } + // TODO: this should be part of the configuration let country = standardUserDefaults.integerForKey(self.countryUserDefaultsKey) setCountry(country) self.countryButton?.selectItemAtIndex(country) From 54557d7f13de0518f4a515e10b4f9643a142ebce Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 8 Sep 2016 07:39:17 -0400 Subject: [PATCH 45/66] Added reminder, ensured the couldn't-find-data case doesn't cause a crash. Though it should imply some manual investigation. --- StaticAnalyser/Commodore/CommodoreAnalyser.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/StaticAnalyser/Commodore/CommodoreAnalyser.cpp b/StaticAnalyser/Commodore/CommodoreAnalyser.cpp index ce9890f20..f744a8b85 100644 --- a/StaticAnalyser/Commodore/CommodoreAnalyser.cpp +++ b/StaticAnalyser/Commodore/CommodoreAnalyser.cpp @@ -98,6 +98,8 @@ void StaticAnalyser::Commodore::AddTargets( else { // TODO: this is machine code. So, ummm? + printf("Need to deal with machine code from %04x to %04x???\n", files.front().starting_address, files.front().ending_address); + target.loadingCommand = "LOAD\"\",1,1\nRUN\n"; } target.tapes = tapes; From 2b053436e5dc08ea1908fcbb0509b18d8f93a0d5 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 8 Sep 2016 07:39:43 -0400 Subject: [PATCH 46/66] Missed from previous commit; removed assumption that get_next_data always succeeds. --- StaticAnalyser/Commodore/Tape.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/StaticAnalyser/Commodore/Tape.cpp b/StaticAnalyser/Commodore/Tape.cpp index 65511293a..5aa93d250 100644 --- a/StaticAnalyser/Commodore/Tape.cpp +++ b/StaticAnalyser/Commodore/Tape.cpp @@ -378,16 +378,18 @@ std::list StaticAnalyser::Commodore::GetFiles(const std::shared_ptr data = parser.get_next_data(); + if(data) + { + File new_file; + new_file.name = header->name; + new_file.raw_name = header->raw_name; + new_file.starting_address = header->starting_address; + new_file.ending_address = header->ending_address; + new_file.data.swap(data->data); + new_file.type = (header->type == Header::RelocatableProgram) ? File::RelocatableProgram : File::NonRelocatableProgram; - File new_file; - new_file.name = header->name; - new_file.raw_name = header->raw_name; - new_file.starting_address = header->starting_address; - new_file.ending_address = header->ending_address; - new_file.data.swap(data->data); - new_file.type = (header->type == Header::RelocatableProgram) ? File::RelocatableProgram : File::NonRelocatableProgram; - - file_list.push_back(new_file); + file_list.push_back(new_file); + } header = parser.get_next_header(); } From 1e7b5330f5c66e6c8a84576c38dd47c1e482f6fb Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 8 Sep 2016 07:41:26 -0400 Subject: [PATCH 47/66] =?UTF-8?q?More=20or=20less=20rewrote,=20to=20use=20?= =?UTF-8?q?a=20filled-per-chunk=20buffer=20of=20upcoming=20pulses=20rather?= =?UTF-8?q?=20than=20working=20them=20out=20as=20requests=20come.=20Which?= =?UTF-8?q?=20is=20more=20straightforward=20=E2=80=94=20all=20the=20code?= =?UTF-8?q?=20for=20a=20particular=20chunk=20goes=20in=20exactly=20one=20p?= =?UTF-8?q?lace=20=E2=80=94=20and=20much=20easier=20to=20extend.=20So=20th?= =?UTF-8?q?rew=20in=20a=20provisional=200104=20implementation.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Storage/Tape/Formats/TapeUEF.cpp | 442 ++++++++++++++++--------------- Storage/Tape/Formats/TapeUEF.hpp | 50 ++-- 2 files changed, 240 insertions(+), 252 deletions(-) diff --git a/Storage/Tape/Formats/TapeUEF.cpp b/Storage/Tape/Formats/TapeUEF.cpp index 4db40f626..a0c74cf55 100644 --- a/Storage/Tape/Formats/TapeUEF.cpp +++ b/Storage/Tape/Formats/TapeUEF.cpp @@ -9,6 +9,7 @@ #include "TapeUEF.hpp" #include #include +#include #include using namespace Storage::Tape; @@ -44,10 +45,31 @@ static float gzgetfloat(gzFile file) return result; } +static int gzget16(gzFile file) +{ + int result = gzgetc(file); + result |= (gzgetc(file) << 8); + return result; +} + +static int gzget24(gzFile file) +{ + int result = gzget16(file); + result |= (gzgetc(file) << 16); + return result; +} + +static int gzget32(gzFile file) +{ + int result = gzget16(file); + result |= (gzget16(file) << 16); + return result; +} + UEF::UEF(const char *file_name) : - _chunk_id(0), _chunk_length(0), _chunk_position(0), _time_base(1200), - _is_at_end(false) + _is_at_end(false), + _pulse_pointer(0) { _file = gzopen(file_name, "rb"); @@ -67,8 +89,7 @@ UEF::UEF(const char *file_name) : throw ErrorNotUEF; } - _start_of_next_chunk = gztell(_file); - find_next_tape_chunk(); + parse_next_tape_chunk(); } UEF::~UEF() @@ -76,12 +97,13 @@ UEF::~UEF() gzclose(_file); } +#pragma mark - Public methods + void UEF::reset() { gzseek(_file, 12, SEEK_SET); _is_at_end = false; - _start_of_next_chunk = gztell(_file); - find_next_tape_chunk(); + parse_next_tape_chunk(); } bool UEF::is_at_end() @@ -101,119 +123,29 @@ Storage::Tape::Tape::Pulse UEF::get_next_pulse() return next_pulse; } - if(!_bit_position && chunk_is_finished()) + next_pulse = _queued_pulses[_pulse_pointer]; + _pulse_pointer++; + if(_pulse_pointer == _queued_pulses.size()) { - find_next_tape_chunk(); + _queued_pulses.clear(); + _pulse_pointer = 0; + parse_next_tape_chunk(); } - - switch(_chunk_id) - { - case 0x0100: case 0x0102: - // In the ordinary ("1200 baud") data encoding format, - // a zero bit is encoded as one complete cycle at the base frequency. - // A one bit is two complete cycles at twice the base frequency. - - if(!_bit_position) - { - _current_bit = get_next_bit(); - } - - next_pulse.type = (_bit_position&1) ? Pulse::High : Pulse::Low; - next_pulse.length.length = _current_bit ? 1 : 2; - next_pulse.length.clock_rate = _time_base * 4; - _bit_position = (_bit_position+1)&(_current_bit ? 3 : 1); - break; - - case 0x0111: - if(_chunk_position < _high_tone_with_dummy.pre_length || _chunk_position >= _high_tone_with_dummy.pre_length+10) - { - next_pulse.type = (_bit_position&1) ? Pulse::High : Pulse::Low; - next_pulse.length.length = 1; - next_pulse.length.clock_rate = _time_base * 4; - _bit_position ^= 1; - - if(!_bit_position) _chunk_position++; - } - else - { - // to output 0xaa: 0 - if(!_bit_position) - { - _current_bit = (0x354 >> (_chunk_position - _high_tone_with_dummy.pre_length))&1; - } - - next_pulse.type = (_bit_position&1) ? Pulse::High : Pulse::Low; - next_pulse.length.length = _current_bit ? 1 : 2; - next_pulse.length.clock_rate = _time_base * 4; - _bit_position = (_bit_position+1)&(_current_bit ? 3 : 1); - - if(!_bit_position) - { - _chunk_position++; - } - } - break; - - case 0x0110: - next_pulse.type = (_bit_position&1) ? Pulse::High : Pulse::Low; - next_pulse.length.length = 1; - next_pulse.length.clock_rate = _time_base * 4; - _bit_position ^= 1; - - if(!_bit_position) _chunk_position++; - break; - - case 0x0114: - if(!_bit_position) - { - _current_bit = get_next_bit(); - if(_first_is_pulse && !_chunk_position) - { - _bit_position++; - } - } - - next_pulse.type = (_bit_position&1) ? Pulse::High : Pulse::Low; - next_pulse.length.length = _current_bit ? 1 : 2; - next_pulse.length.clock_rate = _time_base * 4; - _bit_position ^= 1; - - if((_chunk_id == 0x0114) && (_chunk_position == _chunk_duration.length-1) && _last_is_pulse) - { - _chunk_position++; - } - break; - - case 0x0112: - case 0x0116: - next_pulse.type = Pulse::Zero; - next_pulse.length = _chunk_duration; - _chunk_position++; - break; - } - return next_pulse; } -void UEF::find_next_tape_chunk() +#pragma mark - Chunk navigator + +void UEF::parse_next_tape_chunk() { - _chunk_position = 0; - _bit_position = 0; - - while(1) + while(!_queued_pulses.size()) { - gzseek(_file, _start_of_next_chunk, SEEK_SET); + // read chunk details + uint16_t chunk_id = (uint16_t)gzget16(_file); + uint32_t chunk_length = (uint32_t)gzget32(_file); - // 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); - - _start_of_next_chunk = gztell(_file) + _chunk_length; + // figure out where the next chunk will start + z_off_t start_of_next_chunk = gztell(_file) + chunk_length; if(gzeof(_file)) { @@ -221,55 +153,18 @@ void UEF::find_next_tape_chunk() return; } - switch(_chunk_id) + switch(chunk_id) { - case 0x0100: // implicit bit pattern - _implicit_data_chunk.position = 0; - return; + case 0x0100: queue_implicit_bit_pattern(chunk_length); break; + case 0x0102: queue_explicit_bit_pattern(chunk_length); break; + case 0x0112: queue_integer_gap(); break; + case 0x0116: queue_floating_point_gap(); break; - case 0x0102: // explicit bit patterns - _explicit_data_chunk.position = 0; - return; + case 0x0110: queue_carrier_tone(); break; + case 0x0111: queue_carrier_tone_with_dummy(); break; - case 0x0112: // integer gap - _chunk_duration.length = (uint16_t)gzgetc(_file); - _chunk_duration.length |= (uint16_t)(gzgetc(_file) << 8); - _chunk_duration.clock_rate = _time_base; - return; - - case 0x0116: // floating point gap - { - float length = gzgetfloat(_file); - _chunk_duration.length = (unsigned int)(length * 4000000); - _chunk_duration.clock_rate = 4000000; - } - return; - - case 0x0110: // carrier tone - _chunk_duration.length = (uint16_t)gzgetc(_file); - _chunk_duration.length |= (uint16_t)(gzgetc(_file) << 8); - gzseek(_file, _chunk_length - 2, SEEK_CUR); - return; - case 0x0111: // carrier tone with dummy byte - _high_tone_with_dummy.pre_length = (uint16_t)gzgetc(_file); - _high_tone_with_dummy.pre_length |= (uint16_t)(gzgetc(_file) << 8); - _high_tone_with_dummy.post_length = (uint16_t)gzgetc(_file); - _high_tone_with_dummy.post_length |= (uint16_t)(gzgetc(_file) << 8); - _chunk_duration.length = _high_tone_with_dummy.post_length + _high_tone_with_dummy.pre_length + 10; - gzseek(_file, _chunk_length - 4, SEEK_CUR); - return; - case 0x0114: // security cycles - { - // read number of cycles - _chunk_duration.length = (uint32_t)gzgetc(_file); - _chunk_duration.length |= (uint32_t)gzgetc(_file) << 8; - _chunk_duration.length |= (uint32_t)gzgetc(_file) << 16; - - // Ps and Ws - _first_is_pulse = gzgetc(_file) == 'P'; - _last_is_pulse = gzgetc(_file) == 'P'; - } - break; + case 0x0114: queue_security_cycles(); break; + case 0x0104: queue_defined_data(chunk_length); break; case 0x113: // change of base rate { @@ -280,80 +175,187 @@ void UEF::find_next_tape_chunk() break; default: - printf("!!! Skipping %04x\n", _chunk_id); - gzseek(_file, _chunk_length, SEEK_CUR); + printf("!!! Skipping %04x\n", chunk_id); break; } + + gzseek(_file, start_of_next_chunk, SEEK_SET); } } -bool UEF::chunk_is_finished() +#pragma mark - Chunk parsers + +void UEF::queue_implicit_bit_pattern(uint32_t length) { - switch(_chunk_id) + while(length--) { - case 0x0100: return (_implicit_data_chunk.position / 10) == _chunk_length; - case 0x0102: return (_explicit_data_chunk.position / 8) == _chunk_length; - case 0x0114: - case 0x0111: - case 0x0110: return _chunk_position == _chunk_duration.length; - - case 0x0112: - case 0x0116: return _chunk_position ? true : false; - - default: return true; + queue_implicit_byte((uint8_t)gzgetc(_file)); } } -bool UEF::get_next_bit() +void UEF::queue_explicit_bit_pattern(uint32_t length) { - switch(_chunk_id) + size_t length_in_bits = (length << 3) - (size_t)gzgetc(_file); + uint8_t current_byte = 0; + for(size_t bit = 0; bit < length_in_bits; bit++) { - case 0x0100: - { - uint32_t bit_position = _implicit_data_chunk.position%10; - _implicit_data_chunk.position++; - if(!bit_position) _implicit_data_chunk.current_byte = (uint8_t)gzgetc(_file); - if(bit_position == 0) return false; - if(bit_position == 9) return true; - bool result = (_implicit_data_chunk.current_byte&1) ? true : false; - _implicit_data_chunk.current_byte >>= 1; - return result; - } - break; - - case 0x0102: - { - uint32_t bit_position = _explicit_data_chunk.position%8; - _explicit_data_chunk.position++; - if(!bit_position) _explicit_data_chunk.current_byte = (uint8_t)gzgetc(_file); - bool result = (_explicit_data_chunk.current_byte&1) ? true : false; - _explicit_data_chunk.current_byte >>= 1; - return result; - } - break; - - // TODO: 0x0104 - - case 0x0114: - { - uint32_t bit_position = _chunk_position%8; - _chunk_position++; - if(!bit_position && _chunk_position < _chunk_duration.length) - { - _current_byte = (uint8_t)gzgetc(_file); - } - bool result = (_current_byte&1) ? true : false; - _current_byte >>= 1; - return result; - } - break; - - case 0x0111: - - case 0x0110: - _chunk_position++; - return true; - - default: return true; + if(!(bit&7)) current_byte = (uint8_t)gzgetc(_file); + queue_bit(current_byte&1); + current_byte >>= 1; + } +} + +void UEF::queue_integer_gap() +{ + Time duration; + duration.length = (unsigned int)gzget16(_file); + duration.clock_rate = _time_base; + _queued_pulses.emplace_back(Pulse::Zero, duration); +} + +void UEF::queue_floating_point_gap() +{ + float length = gzgetfloat(_file); + Time duration; + duration.length = (unsigned int)(length * 4000000); + duration.clock_rate = 4000000; + _queued_pulses.emplace_back(Pulse::Zero, duration); +} + +void UEF::queue_carrier_tone() +{ + unsigned int number_of_cycles = (unsigned int)gzget16(_file); + while(number_of_cycles--) queue_bit(1); +} + +void UEF::queue_carrier_tone_with_dummy() +{ + unsigned int pre_cycles = (unsigned int)gzget16(_file); + unsigned int post_cycles = (unsigned int)gzget16(_file); + while(pre_cycles--) queue_bit(1); + queue_implicit_byte(0xaa); + while(post_cycles--) queue_bit(1); +} + +void UEF::queue_security_cycles() +{ + int number_of_cycles = gzget24(_file); + bool first_is_pulse = gzgetc(_file) == 'P'; + bool last_is_pulse = gzgetc(_file) == 'P'; + + uint8_t current_byte = 0; + for(int cycle = 0; cycle < number_of_cycles; cycle++) + { + if(!(cycle&7)) current_byte = (uint8_t)gzgetc(_file); + int bit = (current_byte >> 7); + current_byte <<= 1; + + Time duration; + duration.length = bit ? 1 : 2; + duration.clock_rate = _time_base * 4; + + if(!cycle && first_is_pulse) + { + _queued_pulses.emplace_back(Pulse::High, duration); + } + else if(cycle == number_of_cycles-1 && last_is_pulse) + { + _queued_pulses.emplace_back(Pulse::Low, duration); + } + else + { + _queued_pulses.emplace_back(Pulse::Low, duration); + _queued_pulses.emplace_back(Pulse::High, duration); + } + } +} + +void UEF::queue_defined_data(uint32_t length) +{ + if(length < 3) return; + + int bits_per_packet = gzgetc(_file); + char parity_type = (char)gzgetc(_file); + int number_of_stop_bits = gzgetc(_file); + + bool has_extra_stop_wave = (number_of_stop_bits < 0); + number_of_stop_bits = abs(number_of_stop_bits); + + length -= 3; + while(length--) + { + uint8_t byte = (uint8_t)gzgetc(_file); + + uint8_t parity_value = byte; + parity_value ^= (parity_value >> 4); + parity_value ^= (parity_value >> 2); + parity_value ^= (parity_value >> 1); + + queue_bit(0); + int c = bits_per_packet; + while(c--) + { + queue_bit(byte&1); + byte >>= 1; + } + + switch(parity_type) + { + default: break; + case 'E': queue_bit(parity_value&1); break; + case 'O': queue_bit((parity_value&1) ^ 1); break; + } + + int stop_bits = number_of_stop_bits; + while(stop_bits--) queue_bit(1); + if(has_extra_stop_wave) + { + Time duration; + duration.length = 1; + duration.clock_rate = _time_base * 4; + _queued_pulses.emplace_back(Pulse::Low, duration); + _queued_pulses.emplace_back(Pulse::High, duration); + } + } +} + +#pragma mark - Queuing helpers + +void UEF::queue_implicit_byte(uint8_t byte) +{ + queue_bit(0); + int c = 8; + while(c--) + { + queue_bit(byte&1); + byte >>= 1; + } + queue_bit(1); +} + +void UEF::queue_bit(int bit) +{ + // TODO: allow for 300-baud encoding + if(bit) + { + // Encode a 1 as four high-frequency waves + Time duration; + duration.length = 1; + duration.clock_rate = _time_base * 4; + + _queued_pulses.emplace_back(Pulse::Low, duration); + _queued_pulses.emplace_back(Pulse::High, duration); + _queued_pulses.emplace_back(Pulse::Low, duration); + _queued_pulses.emplace_back(Pulse::High, duration); + } + else + { + // Encode a 0 as two low-frequency waves + Time duration; + duration.length = 2; + duration.clock_rate = _time_base * 4; + + _queued_pulses.emplace_back(Pulse::Low, duration); + _queued_pulses.emplace_back(Pulse::High, duration); } } diff --git a/Storage/Tape/Formats/TapeUEF.hpp b/Storage/Tape/Formats/TapeUEF.hpp index a4079b0fb..508d5d4dc 100644 --- a/Storage/Tape/Formats/TapeUEF.hpp +++ b/Storage/Tape/Formats/TapeUEF.hpp @@ -11,7 +11,8 @@ #include "../Tape.hpp" #include -#include +#include +#include namespace Storage { namespace Tape { @@ -41,42 +42,27 @@ class UEF : public Tape { private: gzFile _file; unsigned int _time_base; - z_off_t _start_of_next_chunk; - - uint16_t _chunk_id; - uint32_t _chunk_length; - - union { - struct { - uint8_t current_byte; - uint32_t position; - } _implicit_data_chunk; - - struct { - uint8_t current_byte; - uint32_t position; - } _explicit_data_chunk; - - struct { - unsigned int pre_length, post_length; - } _high_tone_with_dummy; - }; - - uint8_t _current_byte; - uint32_t _chunk_position; bool _is_at_end; - bool _current_bit; - uint32_t _bit_position; + std::vector _queued_pulses; + size_t _pulse_pointer; - Time _chunk_duration; + void parse_next_tape_chunk(); - bool _first_is_pulse; - bool _last_is_pulse; + void queue_implicit_bit_pattern(uint32_t length); + void queue_explicit_bit_pattern(uint32_t length); - void find_next_tape_chunk(); - bool get_next_bit(); - bool chunk_is_finished(); + void queue_integer_gap(); + void queue_floating_point_gap(); + + void queue_carrier_tone(); + void queue_carrier_tone_with_dummy(); + + void queue_security_cycles(); + void queue_defined_data(uint32_t length); + + void queue_bit(int bit); + void queue_implicit_byte(uint8_t byte); }; } From 3e925e80a3bbbf654e00695b6fdcfabd2bea17a0 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 8 Sep 2016 07:41:44 -0400 Subject: [PATCH 48/66] Added a field-filling constructor for pulses. --- Storage/Tape/Tape.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Storage/Tape/Tape.hpp b/Storage/Tape/Tape.hpp index 2293f462d..e7c4749a0 100644 --- a/Storage/Tape/Tape.hpp +++ b/Storage/Tape/Tape.hpp @@ -29,10 +29,13 @@ namespace Tape { class Tape { public: struct Pulse { - enum { + enum Type { High, Low, Zero } type; Time length; + + Pulse(Type type, Time length) : type(type), length(length) {} + Pulse() {} }; virtual Pulse get_next_pulse() = 0; From 01e5dae512e5b92f79e539ee8cb05af6bf8e977c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 8 Sep 2016 07:48:20 -0400 Subject: [PATCH 49/66] Threw in 300-baud support. Why not? --- Storage/Tape/Formats/TapeUEF.cpp | 37 ++++++++++++++++++++------------ Storage/Tape/Formats/TapeUEF.hpp | 1 + 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/Storage/Tape/Formats/TapeUEF.cpp b/Storage/Tape/Formats/TapeUEF.cpp index a0c74cf55..1885096a1 100644 --- a/Storage/Tape/Formats/TapeUEF.cpp +++ b/Storage/Tape/Formats/TapeUEF.cpp @@ -69,7 +69,8 @@ static int gzget32(gzFile file) UEF::UEF(const char *file_name) : _time_base(1200), _is_at_end(false), - _pulse_pointer(0) + _pulse_pointer(0), + _is_300_baud(false) { _file = gzopen(file_name, "rb"); @@ -166,7 +167,7 @@ void UEF::parse_next_tape_chunk() case 0x0114: queue_security_cycles(); break; case 0x0104: queue_defined_data(chunk_length); break; - case 0x113: // change of base rate + case 0x0113: // change of base rate { // TODO: something smarter than just converting this to an int float new_time_base = gzgetfloat(_file); @@ -174,6 +175,13 @@ void UEF::parse_next_tape_chunk() } break; + case 0x0117: + { + int baud_rate = gzget16(_file); + _is_300_baud = (baud_rate == 300); + } + break; + default: printf("!!! Skipping %04x\n", chunk_id); break; @@ -335,26 +343,27 @@ void UEF::queue_implicit_byte(uint8_t byte) void UEF::queue_bit(int bit) { - // TODO: allow for 300-baud encoding + int number_of_cycles; + Time duration; + duration.clock_rate = _time_base * 4; + if(bit) { - // Encode a 1 as four high-frequency waves - Time duration; + // encode high-frequency waves duration.length = 1; - duration.clock_rate = _time_base * 4; - - _queued_pulses.emplace_back(Pulse::Low, duration); - _queued_pulses.emplace_back(Pulse::High, duration); - _queued_pulses.emplace_back(Pulse::Low, duration); - _queued_pulses.emplace_back(Pulse::High, duration); + number_of_cycles = 2; } else { - // Encode a 0 as two low-frequency waves - Time duration; + // encode low-frequency waves duration.length = 2; - duration.clock_rate = _time_base * 4; + number_of_cycles = 1; + } + if(_is_300_baud) number_of_cycles *= 4; + + while(number_of_cycles--) + { _queued_pulses.emplace_back(Pulse::Low, duration); _queued_pulses.emplace_back(Pulse::High, duration); } diff --git a/Storage/Tape/Formats/TapeUEF.hpp b/Storage/Tape/Formats/TapeUEF.hpp index 508d5d4dc..d879a4309 100644 --- a/Storage/Tape/Formats/TapeUEF.hpp +++ b/Storage/Tape/Formats/TapeUEF.hpp @@ -43,6 +43,7 @@ class UEF : public Tape { gzFile _file; unsigned int _time_base; bool _is_at_end; + bool _is_300_baud; std::vector _queued_pulses; size_t _pulse_pointer; From 24251a276804913fb7f8c3222388dbacfb7f07f4 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 8 Sep 2016 07:49:43 -0400 Subject: [PATCH 50/66] Negligible indentation fix. --- Storage/Tape/Formats/TapeUEF.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Storage/Tape/Formats/TapeUEF.cpp b/Storage/Tape/Formats/TapeUEF.cpp index 1885096a1..548800b96 100644 --- a/Storage/Tape/Formats/TapeUEF.cpp +++ b/Storage/Tape/Formats/TapeUEF.cpp @@ -310,7 +310,7 @@ void UEF::queue_defined_data(uint32_t length) switch(parity_type) { default: break; - case 'E': queue_bit(parity_value&1); break; + case 'E': queue_bit(parity_value&1); break; case 'O': queue_bit((parity_value&1) ^ 1); break; } From 1de6097f06f1f2fafa9e4193fb89429c350c5b08 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 8 Sep 2016 21:08:03 -0400 Subject: [PATCH 51/66] Shuffled C stuff out on top. --- Storage/Tape/Formats/TapeUEF.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Storage/Tape/Formats/TapeUEF.cpp b/Storage/Tape/Formats/TapeUEF.cpp index 548800b96..9ecf4e1ab 100644 --- a/Storage/Tape/Formats/TapeUEF.cpp +++ b/Storage/Tape/Formats/TapeUEF.cpp @@ -12,7 +12,7 @@ #include #include -using namespace Storage::Tape; +#pragma mark - ZLib extensions static float gzgetfloat(gzFile file) { @@ -66,6 +66,8 @@ static int gzget32(gzFile file) return result; } +using namespace Storage::Tape; + UEF::UEF(const char *file_name) : _time_base(1200), _is_at_end(false), From 27fedaf89217b067328cdff55bc264170bae4a18 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 8 Sep 2016 21:09:05 -0400 Subject: [PATCH 52/66] The error flag is no longer directly exposed. I also tweaked the Commodore import numbers just a touch. But I think I need proper calibration. --- StaticAnalyser/Acorn/Tape.cpp | 4 ++-- StaticAnalyser/Commodore/Tape.cpp | 23 +++++++++++++++-------- StaticAnalyser/TapeParser.hpp | 8 +++++++- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/StaticAnalyser/Acorn/Tape.cpp b/StaticAnalyser/Acorn/Tape.cpp index eeabfc61b..c91573094 100644 --- a/StaticAnalyser/Acorn/Tape.cpp +++ b/StaticAnalyser/Acorn/Tape.cpp @@ -37,7 +37,7 @@ class Acorn1200BaudTapeParser: public StaticAnalyer::TapeParser get_next_data() { std::unique_ptr data(new Data); + reset_error_flag(); // find and proceed beyond lead-in tone to the next landing zone proceed_to_symbol(SymbolType::LeadIn); @@ -216,7 +217,7 @@ class CommodoreROMTapeParser: public StaticAnalyer::TapeParser> 1) | (((next_symbol == SymbolType::One) ? 1 : 0) << 8); } @@ -253,7 +254,8 @@ class CommodoreROMTapeParser: public StaticAnalyer::TapeParser> 4); check ^= (check >> 2); check ^= (check >> 1); - if((check&1) == (byte_plus_parity >> 8)) _error_flag = true; + if((check&1) == (byte_plus_parity >> 8)) + set_error_flag(); add_parity_byte((uint8_t)byte_plus_parity); return (uint8_t)byte_plus_parity; @@ -276,12 +278,17 @@ class CommodoreROMTapeParser: public StaticAnalyer::TapeParser 0.000364s cycle + // medium: 262µs => 0.000524s cycle + // long: 342µs => 0.000684s cycle bool is_high = pulse.type == Storage::Tape::Tape::Pulse::High; if(!is_high && _previous_was_high) { - if(_wave_period >= 0.000592 && _wave_period < 0.000752) push_wave(WaveType::Long); - else if(_wave_period >= 0.000432 && _wave_period < 0.000592) push_wave(WaveType::Medium); - else if(_wave_period >= 0.000272 && _wave_period < 0.000432) push_wave(WaveType::Short); + if(_wave_period >= 0.000764) push_wave(WaveType::Unrecognised); + else if(_wave_period >= 0.000604) push_wave(WaveType::Long); + else if(_wave_period >= 0.000444) push_wave(WaveType::Medium); + else if(_wave_period >= 0.000284) push_wave(WaveType::Short); else push_wave(WaveType::Unrecognised); _wave_period = 0.0f; @@ -363,9 +370,10 @@ std::list StaticAnalyser::Commodore::GetFiles(const std::shared_ptrdata); - while(1) + while(!parser.is_at_end()) { header = parser.get_next_header(); + if(!header) continue; if(header->type != Header::DataBlock) break; std::copy(header->data.begin(), header->data.end(), std::back_inserter(new_file.data)); } @@ -401,6 +409,5 @@ std::list StaticAnalyser::Commodore::GetFiles(const std::shared_ptr class TapeParser { bool is_at_end() { return _tape->is_at_end(); } protected: - bool _error_flag; /*! Adds @c wave to the back of the list of recognised waves and calls @c inspect_waves to check for a new symbol. @@ -82,7 +81,14 @@ template class TapeParser { return _next_symbol; } + void set_error_flag() + { + _error_flag = true; + } + private: + bool _error_flag; + /*! Should be implemented by subclasses. Consumes @c pulse. Is likely either to call @c push_wave or to take no action. From a5b17932caa75aa69245c639ab0a8084b2448446 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 8 Sep 2016 21:09:37 -0400 Subject: [PATCH 53/66] Attempted further to improve memory model guesswork. With many further improvements to make... --- StaticAnalyser/Commodore/CommodoreAnalyser.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/StaticAnalyser/Commodore/CommodoreAnalyser.cpp b/StaticAnalyser/Commodore/CommodoreAnalyser.cpp index f744a8b85..22b9d74ec 100644 --- a/StaticAnalyser/Commodore/CommodoreAnalyser.cpp +++ b/StaticAnalyser/Commodore/CommodoreAnalyser.cpp @@ -83,6 +83,8 @@ void StaticAnalyser::Commodore::AddTargets( if(is_basic) { target.loadingCommand = "LOAD\"\",1,0\nRUN\n"; + + // make a first guess based on loading address switch(files.front().starting_address) { case 0x1001: @@ -94,6 +96,21 @@ void StaticAnalyser::Commodore::AddTargets( target.vic20.memory_model = Vic20MemoryModel::EightKB; break; } + + // An unexpanded machine has 3583 bytes free for BASIC; + // a 3kb expanded machine has 6655 bytes free. + + // we'll be relocating though, so up size if necessary + size_t file_size = files.front().data.size(); + if(file_size > 6655) + { + target.vic20.memory_model = Vic20MemoryModel::ThirtyTwoKB; + } + else if(file_size > 3583) + { + if(target.vic20.memory_model == Vic20MemoryModel::Unexpanded) + target.vic20.memory_model = Vic20MemoryModel::EightKB; + } } else { From 1ca4a2a01201d1d02bf62fb6173e0ae05d0fa875 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 10 Sep 2016 17:01:17 -0400 Subject: [PATCH 54/66] =?UTF-8?q?Sought=20to=20be=20more=20intelligent=20i?= =?UTF-8?q?n=20handling=20cases=20where=20the=20two=20parts=20of=20a=20hea?= =?UTF-8?q?der=20or=20data=20field=20don't=20match=20=E2=80=94=20if=20eith?= =?UTF-8?q?er=20looks=20good=20then=20use=20it=20in=20isolation.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- StaticAnalyser/Commodore/Tape.cpp | 112 +++++++++++++++++++----------- 1 file changed, 70 insertions(+), 42 deletions(-) diff --git a/StaticAnalyser/Commodore/Tape.cpp b/StaticAnalyser/Commodore/Tape.cpp index 775a6e6f9..1cb9fe8ec 100644 --- a/StaticAnalyser/Commodore/Tape.cpp +++ b/StaticAnalyser/Commodore/Tape.cpp @@ -55,10 +55,74 @@ class CommodoreROMTapeParser: public StaticAnalyer::TapeParser get_next_header() + { + return duplicate_match
( + get_next_header_body(true), + get_next_header_body(false) + ); + } + + /*! + Advances to the next block on the tape, treating it as data, then consumes, parses, and returns it. + Returns @c nullptr if any wave-encoding level errors are encountered. + */ + std::unique_ptr get_next_data() + { + return duplicate_match( + get_next_data_body(true), + get_next_data_body(false) + ); + } + + void spin() + { + while(!is_at_end()) + { + SymbolType symbol = get_next_symbol(); + switch(symbol) + { + case SymbolType::One: printf("1"); break; + case SymbolType::Zero: printf("0"); break; + case SymbolType::Word: printf(" "); break; + case SymbolType::EndOfBlock: printf("\n"); break; + case SymbolType::LeadIn: printf("-"); break; + } + } + } + + private: + /*! + Template for the logic in selecting which of two copies of something to consider authoritative, + including setting the duplicate_matched flag. + */ + template + std::unique_ptr duplicate_match(std::unique_ptr first_copy, std::unique_ptr second_copy) + { + // if only one copy was parsed successfully, return it + if(!first_copy) return second_copy; + if(!second_copy) return first_copy; + + // if no copies were second_copy, return nullptr + if(!first_copy && !second_copy) return nullptr; + + // otherwise plan to return either one with a correct check digit, doing a comparison with the other + std::unique_ptr *copy_to_return = &first_copy; + if(!first_copy->parity_was_valid && second_copy->parity_was_valid) copy_to_return = &second_copy; + + (*copy_to_return)->duplicate_matched = true; + if(first_copy->data.size() != second_copy->data.size()) + (*copy_to_return)->duplicate_matched = false; + else + (*copy_to_return)->duplicate_matched = !(memcmp(&first_copy->data[0], &second_copy->data[0], first_copy->data.size())); + + return std::move(*copy_to_return); + } + + std::unique_ptr
get_next_header_body(bool is_original) { std::unique_ptr
header(new Header); reset_error_flag(); @@ -67,7 +131,7 @@ class CommodoreROMTapeParser: public StaticAnalyer::TapeParserparity_was_valid = get_next_byte() == parity_byte; - // check that the duplicate matches - proceed_to_landing_zone(false); - header->duplicate_matched = true; - if(get_next_byte() != header_type) header->duplicate_matched = false; - for(size_t c = 0; c < 191; c++) - { - if(header->data[c] != get_next_byte()) header->duplicate_matched = false; - } - if(get_next_byte() != parity_byte) header->duplicate_matched = false; - // parse if this is not pure data if(header->type != Header::DataBlock) { @@ -119,14 +173,15 @@ class CommodoreROMTapeParser: public StaticAnalyer::TapeParser get_next_data() + + std::unique_ptr get_next_data_body(bool is_original) { std::unique_ptr data(new Data); reset_error_flag(); // find and proceed beyond lead-in tone to the next landing zone proceed_to_symbol(SymbolType::LeadIn); - proceed_to_landing_zone(true); + proceed_to_landing_zone(is_original); reset_parity_byte(); // accumulate until the next non-word marker is hit @@ -139,41 +194,14 @@ class CommodoreROMTapeParser: public StaticAnalyer::TapeParserparity_was_valid = !get_parity_byte(); - - // compare to the duplicate - proceed_to_symbol(SymbolType::LeadIn); - proceed_to_landing_zone(false); - reset_parity_byte(); - data->duplicate_matched = true; - for(size_t c = 0; c < data->data.size(); c++) - { - if(get_next_byte() != data->data[c]) data->duplicate_matched = false; - } + data->duplicate_matched = false; // remove the captured parity data->data.erase(data->data.end()-1); - if(get_error_flag()) return nullptr; return data; } - void spin() - { - while(!is_at_end()) - { - SymbolType symbol = get_next_symbol(); - switch(symbol) - { - case SymbolType::One: printf("1"); break; - case SymbolType::Zero: printf("0"); break; - case SymbolType::Word: printf(" "); break; - case SymbolType::EndOfBlock: printf("\n"); break; - case SymbolType::LeadIn: printf("-"); break; - } - } - } - - private: /*! Finds and completes the next landing zone. */ From eeec516fa60a21a5e411035b065200f7cc6f8c0e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 11 Sep 2016 17:09:00 -0400 Subject: [PATCH 55/66] Implemented seeking on tapes, mucked about a bit more with the Commodore analyser, at least temporarily removed cropping from the Vic emulator. --- Components/6560/6560.hpp | 15 ++- Machines/Commodore/Vic-20/Vic20.cpp | 4 + .../Clock Signal.xcodeproj/project.pbxproj | 6 + .../Commodore/CommodoreAnalyser.cpp | 112 ++++++++---------- StaticAnalyser/Commodore/File.cpp | 50 ++++++++ StaticAnalyser/Commodore/File.hpp | 36 ++++++ StaticAnalyser/Commodore/Tape.hpp | 14 +-- Storage/Tape/Formats/CommodoreTAP.cpp | 4 +- Storage/Tape/Formats/CommodoreTAP.hpp | 5 +- Storage/Tape/Formats/TapePRG.cpp | 4 +- Storage/Tape/Formats/TapePRG.hpp | 5 +- Storage/Tape/Formats/TapeUEF.cpp | 4 +- Storage/Tape/Formats/TapeUEF.hpp | 5 +- Storage/Tape/Tape.cpp | 31 ++++- Storage/Tape/Tape.hpp | 25 +++- 15 files changed, 224 insertions(+), 96 deletions(-) create mode 100644 StaticAnalyser/Commodore/File.cpp create mode 100644 StaticAnalyser/Commodore/File.hpp diff --git a/Components/6560/6560.hpp b/Components/6560/6560.hpp index b445c5dd3..1211f66ff 100644 --- a/Components/6560/6560.hpp +++ b/Components/6560/6560.hpp @@ -65,9 +65,6 @@ template class MOS6560 { // default to NTSC set_output_mode(OutputMode::NTSC); - - // show only the centre - _crt->set_visible_area(_crt->get_rect_for_area(16, 237, 11*4, 55*4, 4.0f / 3.0f)); } void set_clock_rate(double clock_rate) @@ -125,6 +122,18 @@ template class MOS6560 { } _crt->set_new_display_type((unsigned int)(_timing.cycles_per_line*4), display_type); +// _crt->set_visible_area(Outputs::CRT::Rect(0.1f, 0.1f, 0.8f, 0.8f)); + +// switch(output_mode) +// { +// case OutputMode::PAL: +// _crt->set_visible_area(_crt->get_rect_for_area(16, 237, 15*4, 55*4, 4.0f / 3.0f)); +// break; +// case OutputMode::NTSC: +// _crt->set_visible_area(_crt->get_rect_for_area(16, 237, 11*4, 55*4, 4.0f / 3.0f)); +// break; +// } + for(int c = 0; c < 16; c++) { _colours[c] = (uint8_t)((luminances[c] << 4) | chrominances[c]); diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index 76eedc4cd..6c0c91ae2 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -269,6 +269,10 @@ void Machine::set_prg(const char *file_name, size_t length, const uint8_t *data) #pragma mar - Tape +// LAB_FBDB = new tape byte setup; +// loops at LAB_F92F +// LAB_F8C0 = initiate tape read + void Machine::configure_as_target(const StaticAnalyser::Target &target) { if(target.tapes.size()) diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 79bc323ab..5d29d7266 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -347,6 +347,7 @@ 4BC9E1EE1D23449A003FCEE4 /* 6502InterruptTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC9E1ED1D23449A003FCEE4 /* 6502InterruptTests.swift */; }; 4BD14B111D74627C0088EAD6 /* AcornAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD14B0F1D74627C0088EAD6 /* AcornAnalyser.cpp */; }; 4BD5F1951D13528900631CD1 /* CSBestEffortUpdater.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.m */; }; + 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 */; }; 4BEF6AAA1D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BEF6AA91D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm */; }; @@ -786,6 +787,8 @@ 4BD328FD1D7E3EB5003B8C44 /* TapeParser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = TapeParser.hpp; path = ../../StaticAnalyser/TapeParser.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 = ""; }; + 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 = ""; }; 4BEE0A6B1D72496600532C7B /* Cartridge.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Cartridge.hpp; sourceTree = ""; }; 4BEE0A6D1D72496600532C7B /* PRG.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PRG.cpp; sourceTree = ""; }; @@ -1529,6 +1532,8 @@ 4BC830D01D6E7C690000A26F /* Tape.hpp */, 4BC5E4931D7EE0E0008CF980 /* Utilities.cpp */, 4BC5E4941D7EE0E0008CF980 /* Utilities.hpp */, + 4BE77A2C1D84ADFB00BC3827 /* File.cpp */, + 4BE77A2D1D84ADFB00BC3827 /* File.hpp */, ); name = Commodore; sourceTree = ""; @@ -2055,6 +2060,7 @@ 4BC830D11D6E7C690000A26F /* Tape.cpp in Sources */, 4B69FB441C4D941400B5F0AA /* TapeUEF.cpp in Sources */, 4B4DC8211D2C2425003C5BF8 /* Vic20.cpp in Sources */, + 4BE77A2E1D84ADFB00BC3827 /* File.cpp in Sources */, 4BAB62B51D327F7E00DF5BA0 /* G64.cpp in Sources */, 4BBF99141C8FBA6F0075DAFB /* CRTInputBufferBuilder.cpp in Sources */, 4B2409551C45AB05004DA684 /* Speaker.cpp in Sources */, diff --git a/StaticAnalyser/Commodore/CommodoreAnalyser.cpp b/StaticAnalyser/Commodore/CommodoreAnalyser.cpp index 22b9d74ec..f5da45244 100644 --- a/StaticAnalyser/Commodore/CommodoreAnalyser.cpp +++ b/StaticAnalyser/Commodore/CommodoreAnalyser.cpp @@ -36,54 +36,25 @@ void StaticAnalyser::Commodore::AddTargets( // continue if there are any files if(files.size()) { - bool is_basic = true; - - // decide whether this is a BASIC file based on the proposition that: - // (1) they're always relocatable; and - // (2) they have a per-line structure of: - // [4 bytes: address of start of next line] - // [4 bytes: this line number] - // ... null-terminated code ... - // (with a next line address of 0000 indicating end of program)ß - if(files.front().type != File::RelocatableProgram) is_basic = false; - else - { - uint16_t line_address = 0; - int line_number = -1; - - uint16_t starting_address = files.front().starting_address; - line_address = starting_address; - is_basic = false; - while(1) - { - if(line_address - starting_address >= files.front().data.size() + 2) break; - - uint16_t next_line_address = files.front().data[line_address - starting_address]; - next_line_address |= files.front().data[line_address - starting_address + 1] << 8; - - if(!next_line_address) - { - is_basic = true; - break; - } - if(next_line_address < line_address + 5) break; - - if(line_address - starting_address >= files.front().data.size() + 5) break; - uint16_t next_line_number = files.front().data[line_address - starting_address + 2]; - next_line_number |= files.front().data[line_address - starting_address + 3] << 8; - - if(next_line_number <= line_number) break; - - line_number = (uint16_t)next_line_number; - line_address = next_line_address; - } - } + target.tapes = tapes; target.vic20.memory_model = Vic20MemoryModel::Unexpanded; - if(is_basic) + if(files.front().is_basic()) { target.loadingCommand = "LOAD\"\",1,0\nRUN\n"; + // make a first guess based on file size + size_t file_size = files.front().data.size(); + if(file_size > 6655) target.vic20.memory_model = Vic20MemoryModel::ThirtyTwoKB; + else if(file_size > 3583) target.vic20.memory_model = Vic20MemoryModel::EightKB; + else target.vic20.memory_model = Vic20MemoryModel::Unexpanded; + } + else + { + // TODO: this is machine code. So, ummm? + printf("Need to deal with machine code from %04x to %04x???\n", files.front().starting_address, files.front().ending_address); + target.loadingCommand = "LOAD\"\",1,1\nRUN\n"; + // make a first guess based on loading address switch(files.front().starting_address) { @@ -96,30 +67,49 @@ void StaticAnalyser::Commodore::AddTargets( target.vic20.memory_model = Vic20MemoryModel::EightKB; break; } + } - // An unexpanded machine has 3583 bytes free for BASIC; - // a 3kb expanded machine has 6655 bytes free. - // we'll be relocating though, so up size if necessary - size_t file_size = files.front().data.size(); - if(file_size > 6655) + // General approach: increase memory size conservatively such that the largest file found will fit. + for(File &file : files) + { + size_t file_size = file.data.size(); + //bool is_basic = file.is_basic(); + + /*if(is_basic) { - target.vic20.memory_model = Vic20MemoryModel::ThirtyTwoKB; - } - else if(file_size > 3583) - { - if(target.vic20.memory_model == Vic20MemoryModel::Unexpanded) + // BASIC files may be relocated, so the only limit is size. + // + // An unexpanded machine has 3583 bytes free for BASIC; + // a 3kb expanded machine has 6655 bytes free. + if(file_size > 6655) + target.vic20.memory_model = Vic20MemoryModel::ThirtyTwoKB; + else if(target.vic20.memory_model == Vic20MemoryModel::Unexpanded && file_size > 3583) target.vic20.memory_model = Vic20MemoryModel::EightKB; } - } - else - { - // TODO: this is machine code. So, ummm? - printf("Need to deal with machine code from %04x to %04x???\n", files.front().starting_address, files.front().ending_address); - target.loadingCommand = "LOAD\"\",1,1\nRUN\n"; - } + else + {*/ +// if(!file.type == File::NonRelocatableProgram) +// { + // Non-BASIC files may be relocatable but, if so, by what logic? + // Given that this is unknown, take starting address as literal + // and check against memory windows. + // + // (ignoring colour memory...) + // An unexpanded Vic has memory between 0x0000 and 0x0400; and between 0x1000 and 0x2000. + // A 3kb expanded Vic fills in the gap and has memory between 0x0000 and 0x2000. + // A 32kb expanded Vic has memory in the entire low 32kb. + uint16_t starting_address = file.starting_address; - target.tapes = tapes; + // If anything above the 8kb mark is touched, mark as a 32kb machine; otherwise if the + // region 0x0400 to 0x1000 is touched and this is an unexpanded machine, mark as 3kb. + if(starting_address + file_size > 0x2000) + target.vic20.memory_model = Vic20MemoryModel::ThirtyTwoKB; + else if(target.vic20.memory_model == Vic20MemoryModel::Unexpanded && !(starting_address >= 0x1000 || starting_address+file_size < 0x0400)) + target.vic20.memory_model = Vic20MemoryModel::ThirtyTwoKB; +// } + // } + } } } diff --git a/StaticAnalyser/Commodore/File.cpp b/StaticAnalyser/Commodore/File.cpp new file mode 100644 index 000000000..c8b887bfd --- /dev/null +++ b/StaticAnalyser/Commodore/File.cpp @@ -0,0 +1,50 @@ +// +// File.cpp +// Clock Signal +// +// Created by Thomas Harte on 10/09/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#include "File.hpp" + +bool StaticAnalyser::Commodore::File::is_basic() +{ + // BASIC files are always relocatable (?) + if(type != File::RelocatableProgram) return false; + + uint16_t line_address = starting_address; + int line_number = -1; + + // decide whether this is a BASIC file based on the proposition that: + // (1) they're always relocatable; and + // (2) they have a per-line structure of: + // [4 bytes: address of start of next line] + // [4 bytes: this line number] + // ... null-terminated code ... + // (with a next line address of 0000 indicating end of program)ß + while(1) + { + if(line_address - starting_address >= data.size() + 2) break; + + uint16_t next_line_address = data[line_address - starting_address]; + next_line_address |= data[line_address - starting_address + 1] << 8; + + if(!next_line_address) + { + return true; + } + if(next_line_address < line_address + 5) break; + + if(line_address - starting_address >= data.size() + 5) break; + uint16_t next_line_number = data[line_address - starting_address + 2]; + next_line_number |= data[line_address - starting_address + 3] << 8; + + if(next_line_number <= line_number) break; + + line_number = (uint16_t)next_line_number; + line_address = next_line_address; + } + + return false; +} diff --git a/StaticAnalyser/Commodore/File.hpp b/StaticAnalyser/Commodore/File.hpp new file mode 100644 index 000000000..ea81aa493 --- /dev/null +++ b/StaticAnalyser/Commodore/File.hpp @@ -0,0 +1,36 @@ +// +// File.hpp +// Clock Signal +// +// Created by Thomas Harte on 10/09/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#ifndef File_hpp +#define File_hpp + +#include +#include + +namespace StaticAnalyser { +namespace Commodore { + +struct File { + std::wstring name; + std::vector raw_name; + uint16_t starting_address; + uint16_t ending_address; + enum { + RelocatableProgram, + NonRelocatableProgram, + DataSequence, + } type; + std::vector data; + + bool is_basic(); +}; + +} +} + +#endif /* File_hpp */ diff --git a/StaticAnalyser/Commodore/Tape.hpp b/StaticAnalyser/Commodore/Tape.hpp index f156fd02a..9a16bcdd0 100644 --- a/StaticAnalyser/Commodore/Tape.hpp +++ b/StaticAnalyser/Commodore/Tape.hpp @@ -11,23 +11,11 @@ #include #include "../StaticAnalyser.hpp" +#include "File.hpp" namespace StaticAnalyser { namespace Commodore { -struct File { - std::wstring name; - std::vector raw_name; - uint16_t starting_address; - uint16_t ending_address; - enum { - RelocatableProgram, - NonRelocatableProgram, - DataSequence, - } type; - std::vector data; -}; - std::list GetFiles(const std::shared_ptr &tape); } diff --git a/Storage/Tape/Formats/CommodoreTAP.cpp b/Storage/Tape/Formats/CommodoreTAP.cpp index 556c55fd0..9f7c50697 100644 --- a/Storage/Tape/Formats/CommodoreTAP.cpp +++ b/Storage/Tape/Formats/CommodoreTAP.cpp @@ -58,7 +58,7 @@ CommodoreTAP::~CommodoreTAP() fclose(_file); } -void CommodoreTAP::reset() +void CommodoreTAP::virtual_reset() { fseek(_file, 0x14, SEEK_SET); _current_pulse.type = Pulse::High; @@ -70,7 +70,7 @@ bool CommodoreTAP::is_at_end() return _is_at_end; } -Storage::Tape::Tape::Pulse CommodoreTAP::get_next_pulse() +Storage::Tape::Tape::Pulse CommodoreTAP::virtual_get_next_pulse() { if(_is_at_end) { diff --git a/Storage/Tape/Formats/CommodoreTAP.hpp b/Storage/Tape/Formats/CommodoreTAP.hpp index 836071447..0f62e8040 100644 --- a/Storage/Tape/Formats/CommodoreTAP.hpp +++ b/Storage/Tape/Formats/CommodoreTAP.hpp @@ -33,11 +33,12 @@ class CommodoreTAP: public Tape { }; // implemented to satisfy @c Tape - Pulse get_next_pulse(); - void reset(); bool is_at_end(); private: + void virtual_reset(); + Pulse virtual_get_next_pulse(); + FILE *_file; bool _updated_layout; uint32_t _file_size; diff --git a/Storage/Tape/Formats/TapePRG.cpp b/Storage/Tape/Formats/TapePRG.cpp index 80d723191..4013960f3 100644 --- a/Storage/Tape/Formats/TapePRG.cpp +++ b/Storage/Tape/Formats/TapePRG.cpp @@ -74,7 +74,7 @@ PRG::~PRG() if(_file) fclose(_file); } -Storage::Tape::Tape::Pulse PRG::get_next_pulse() +Storage::Tape::Tape::Pulse PRG::virtual_get_next_pulse() { // these are all microseconds per pole static const unsigned int leader_zero_length = 179; @@ -100,7 +100,7 @@ Storage::Tape::Tape::Pulse PRG::get_next_pulse() return pulse; } -void PRG::reset() +void PRG::virtual_reset() { _bitPhase = 3; fseek(_file, 2, SEEK_SET); diff --git a/Storage/Tape/Formats/TapePRG.hpp b/Storage/Tape/Formats/TapePRG.hpp index f9159fecf..f092532ed 100644 --- a/Storage/Tape/Formats/TapePRG.hpp +++ b/Storage/Tape/Formats/TapePRG.hpp @@ -35,11 +35,12 @@ class PRG: public Tape { }; // implemented to satisfy @c Tape - Pulse get_next_pulse(); - void reset(); bool is_at_end(); private: + Pulse virtual_get_next_pulse(); + void virtual_reset(); + FILE *_file; uint16_t _load_address; uint16_t _length; diff --git a/Storage/Tape/Formats/TapeUEF.cpp b/Storage/Tape/Formats/TapeUEF.cpp index 9ecf4e1ab..bd5e3075c 100644 --- a/Storage/Tape/Formats/TapeUEF.cpp +++ b/Storage/Tape/Formats/TapeUEF.cpp @@ -102,7 +102,7 @@ UEF::~UEF() #pragma mark - Public methods -void UEF::reset() +void UEF::virtual_reset() { gzseek(_file, 12, SEEK_SET); _is_at_end = false; @@ -114,7 +114,7 @@ bool UEF::is_at_end() return _is_at_end; } -Storage::Tape::Tape::Pulse UEF::get_next_pulse() +Storage::Tape::Tape::Pulse UEF::virtual_get_next_pulse() { Pulse next_pulse; diff --git a/Storage/Tape/Formats/TapeUEF.hpp b/Storage/Tape/Formats/TapeUEF.hpp index d879a4309..306f5273a 100644 --- a/Storage/Tape/Formats/TapeUEF.hpp +++ b/Storage/Tape/Formats/TapeUEF.hpp @@ -35,11 +35,12 @@ class UEF : public Tape { }; // implemented to satisfy @c Tape - Pulse get_next_pulse(); - void reset(); bool is_at_end(); private: + void virtual_reset(); + Pulse virtual_get_next_pulse(); + gzFile _file; unsigned int _time_base; bool _is_at_end; diff --git a/Storage/Tape/Tape.cpp b/Storage/Tape/Tape.cpp index e8856d2c1..8cff0314c 100644 --- a/Storage/Tape/Tape.cpp +++ b/Storage/Tape/Tape.cpp @@ -11,15 +11,38 @@ using namespace Storage::Tape; -void Storage::Tape::Tape::seek(Time seek_time) -{ - // TODO: as best we can -} +#pragma mark - Lifecycle TapePlayer::TapePlayer(unsigned int input_clock_rate) : TimedEventLoop(input_clock_rate) {} +#pragma mark - Seeking + +void Storage::Tape::Tape::seek(Time &seek_time) +{ + _current_time.set_zero(); + _next_time.set_zero(); + while(_next_time < seek_time) get_next_pulse(); +} + +void Storage::Tape::Tape::reset() +{ + _current_time.set_zero(); + _next_time.set_zero(); + virtual_reset(); +} + +Tape::Pulse Tape::get_next_pulse() +{ + Tape::Pulse pulse = virtual_get_next_pulse(); + _current_time = _next_time; + _next_time += pulse.length; + return pulse; +} + +#pragma mark - Player + void TapePlayer::set_tape(std::shared_ptr tape) { _tape = tape; diff --git a/Storage/Tape/Tape.hpp b/Storage/Tape/Tape.hpp index e7c4749a0..ca3100f9b 100644 --- a/Storage/Tape/Tape.hpp +++ b/Storage/Tape/Tape.hpp @@ -38,12 +38,31 @@ class Tape { Pulse() {} }; - virtual Pulse get_next_pulse() = 0; + /*! + If at the start of the tape returns the first stored pulse. Otherwise advances past + the last-returned pulse and returns the next. - virtual void reset() = 0; + @returns the pulse that begins at the current cursor position. + */ + Pulse get_next_pulse(); + + /// Returns the tape to the beginning. + void reset(); + + /// @returns @c true if the tape has progressed beyond all recorded content; @c false otherwise. virtual bool is_at_end() = 0; - virtual void seek(Time seek_time); // TODO + /// @returns the amount of time preceeding the most recently-returned pulse. + virtual Time get_current_time() { return _current_time; } + + /// Advances or reverses the tape to the last time before or at @c time from which a pulse starts. + virtual void seek(Time &time); + + private: + Time _current_time, _next_time; + + virtual Pulse virtual_get_next_pulse() = 0; + virtual void virtual_reset() = 0; }; /*! From e54a2326a32e11dfe7ea80cf88961b906a97dcb0 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 11 Sep 2016 17:34:02 -0400 Subject: [PATCH 56/66] Made attempt to run at zero cost while processing tape input. --- Machines/Commodore/Vic-20/Vic20.cpp | 14 +++++++++++--- Machines/Commodore/Vic-20/Vic20.hpp | 1 + 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index 6c0c91ae2..49f9c7a28 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -15,7 +15,8 @@ using namespace Commodore::Vic20; Machine::Machine() : - _rom(nullptr) + _rom(nullptr), + _is_running_at_zero_cost(false) { // create 6522s, serial port and bus _userPortVIA.reset(new UserPortVIA); @@ -115,7 +116,7 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin // } // run the phase-1 part of this cycle, in which the VIC accesses memory - _mos6560->run_for_cycles(1); + if(!_is_running_at_zero_cost) _mos6560->run_for_cycles(1); // run the phase-2 part of the cycle, which is whatever the 6502 said it should be if(isReadOperation(operation)) @@ -165,7 +166,14 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin } _tape.run_for_cycles(1); if(_c1540) _c1540->run_for_cycles(1); - return 1; + + if(_use_fast_tape_hack && operation == CPU6502::BusOperation::ReadOpcode) + { + if(address == 0xF98E) _is_running_at_zero_cost = true; + if(address == 0xff56) _is_running_at_zero_cost = false; + } + + return _is_running_at_zero_cost ? 0 : 1; } #pragma mark - 6522 delegate diff --git a/Machines/Commodore/Vic-20/Vic20.hpp b/Machines/Commodore/Vic-20/Vic20.hpp index 4a9a65865..7893dea7d 100644 --- a/Machines/Commodore/Vic-20/Vic20.hpp +++ b/Machines/Commodore/Vic-20/Vic20.hpp @@ -334,6 +334,7 @@ class Machine: // Tape Tape _tape; bool _use_fast_tape_hack, _should_automatically_load_media; + bool _is_running_at_zero_cost; // Disk std::shared_ptr<::Commodore::C1540::Machine> _c1540; From 40660fe680b88ee4c3233f2be26045bc79157025 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 12 Sep 2016 22:06:03 -0400 Subject: [PATCH 57/66] Made yet another guess at Commodore analysis. Elevated fast tape-related unnatural speed up to the OS-side mechanisms. --- Machines/CRTMachine.hpp | 15 ++++++- Machines/Commodore/Vic-20/Vic20.cpp | 40 +++++++++++++------ .../Documents/MachineDocument.swift | 4 ++ .../Mac/Clock Signal/Machine/CSMachine.h | 3 ++ .../Mac/Clock Signal/Machine/CSMachine.mm | 12 ++++++ .../Updater/CSBestEffortUpdater.h | 1 + .../Updater/CSBestEffortUpdater.m | 4 +- .../Commodore/CommodoreAnalyser.cpp | 33 ++++++--------- 8 files changed, 76 insertions(+), 36 deletions(-) diff --git a/Machines/CRTMachine.hpp b/Machines/CRTMachine.hpp index 6e00b482f..9cc2f869f 100644 --- a/Machines/CRTMachine.hpp +++ b/Machines/CRTMachine.hpp @@ -21,6 +21,8 @@ namespace CRTMachine { */ class Machine { public: + Machine() : clock_is_unlimited_(false) {} + virtual void setup_output(float aspect_ratio) = 0; virtual void close_output() = 0; @@ -33,23 +35,34 @@ class Machine { double get_clock_rate() { return clock_rate_; } + bool get_clock_is_unlimited() { + return clock_is_unlimited_; + } class Delegate { public: virtual void machine_did_change_clock_rate(Machine *machine) = 0; + virtual void machine_did_change_clock_is_unlimited(Machine *machine) = 0; }; void set_delegate(Delegate *delegate) { this->delegate_ = delegate; } protected: - double clock_rate_; void set_clock_rate(double clock_rate) { if(clock_rate_ != clock_rate) { clock_rate_ = clock_rate; if(delegate_) delegate_->machine_did_change_clock_rate(this); } } + void set_clock_is_unlimited(bool clock_is_unlimited) { + if(clock_is_unlimited != clock_is_unlimited_) { + clock_is_unlimited_ = clock_is_unlimited; + if(delegate_) delegate_->machine_did_change_clock_is_unlimited(this); + } + } private: Delegate *delegate_; + double clock_rate_; + bool clock_is_unlimited_; }; } diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index 49f9c7a28..606bbdebf 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -130,10 +130,12 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin } *value = result; - // test for PC at F92F + // This combined with the stuff below constitutes the fast tape hack. Performed here: if the + // PC hits the start of the loop that just waits for an interesting tape interrupt to have + // occurred then skip both 6522s and the tape ahead to the next interrupt without any further + // CPU or 6560 costs. if(_use_fast_tape_hack && _tape.has_tape() && address == 0xf92f && operation == CPU6502::BusOperation::ReadOpcode) { - // advance time on the tape and the VIAs until an interrupt is signalled while(!_userPortVIA->get_interrupt_line() && !_keyboardVIA->get_interrupt_line()) { _userPortVIA->run_for_half_cycles(2); @@ -141,9 +143,6 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin _tape.run_for_cycles(1); } } - - // f7af: find tape header, exit with header in buffer - // F8C0: Read tape block } else { @@ -167,13 +166,32 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin _tape.run_for_cycles(1); if(_c1540) _c1540->run_for_cycles(1); - if(_use_fast_tape_hack && operation == CPU6502::BusOperation::ReadOpcode) + // If using fast tape then: + // if the PC hits 0xf98e, the ROM's tape loading routine, then begin zero cost processing; + // if the PC heads into RAM + // + // Where 'zero cost processing' is taken to be taking the 6560 off the bus (because I know it's + // expensive, and not relevant) then running the tape, the CPU and both 6522s as usual but not + // counting cycles towards the processing budget. So the limit is the host machine. + // + // Note the additional test above for PC hitting 0xf92f, which is a loop in the ROM that waits + // for an interesting interrupt. Up there the fast tape hack goes even further in also cutting + // the CPU out of the action. + if(_use_fast_tape_hack && _tape.has_tape()) { - if(address == 0xF98E) _is_running_at_zero_cost = true; - if(address == 0xff56) _is_running_at_zero_cost = false; + if(address == 0xf98e && operation == CPU6502::BusOperation::ReadOpcode) + { + _is_running_at_zero_cost = true; + set_clock_is_unlimited(true); + } + if(address < 0xe000 && operation == CPU6502::BusOperation::ReadOpcode) + { + _is_running_at_zero_cost = false; + set_clock_is_unlimited(false); + } } - return _is_running_at_zero_cost ? 0 : 1; + return 1; } #pragma mark - 6522 delegate @@ -277,10 +295,6 @@ void Machine::set_prg(const char *file_name, size_t length, const uint8_t *data) #pragma mar - Tape -// LAB_FBDB = new tape byte setup; -// loops at LAB_F92F -// LAB_F8C0 = initiate tape read - void Machine::configure_as_target(const StaticAnalyser::Target &target) { if(target.tapes.size()) diff --git a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift index 9e85e3e50..fe8937330 100644 --- a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift +++ b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift @@ -73,6 +73,10 @@ class MachineDocument: setupClockRate() } + func machineDidChangeClockIsUnlimited(machine: CSMachine!) { + self.bestEffortUpdater.runAsUnlimited = machine.clockIsUnlimited + } + private func setupClockRate() { // establish and provide the audio queue, taking advice as to an appropriate sampling rate let maximumSamplingRate = CSAudioQueue.preferredSamplingRate() diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.h b/OSBindings/Mac/Clock Signal/Machine/CSMachine.h index 1a503e8c5..843644503 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.h +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.h @@ -13,6 +13,7 @@ @class CSMachine; @protocol CSMachineDelegate - (void)machineDidChangeClockRate:(CSMachine *)machine; +- (void)machineDidChangeClockIsUnlimited:(CSMachine *)machine; @end @interface CSMachine : NSObject @@ -28,7 +29,9 @@ @property (nonatomic, strong) CSAudioQueue *audioQueue; @property (nonatomic, readonly) CSOpenGLView *view; @property (nonatomic, weak) id delegate; + @property (nonatomic, readonly) double clockRate; +@property (nonatomic, readonly) BOOL clockIsUnlimited; - (void)paste:(NSString *)string; diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm index 2b67f9caa..950427945 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm @@ -16,6 +16,7 @@ @interface CSMachine() - (void)speaker:(Outputs::Speaker *)speaker didCompleteSamples:(const int16_t *)samples length:(int)length; - (void)machineDidChangeClockRate; +- (void)machineDidChangeClockIsUnlimited; @end struct SpeakerDelegate: public Outputs::Speaker::Delegate { @@ -30,6 +31,9 @@ struct MachineDelegate: CRTMachine::Machine::Delegate { void machine_did_change_clock_rate(CRTMachine::Machine *sender) { [machine machineDidChangeClockRate]; } + void machine_did_change_clock_is_unlimited(CRTMachine::Machine *sender) { + [machine machineDidChangeClockIsUnlimited]; + } }; @implementation CSMachine { @@ -56,6 +60,10 @@ struct MachineDelegate: CRTMachine::Machine::Delegate { [self.delegate machineDidChangeClockRate:self]; } +- (void)machineDidChangeClockIsUnlimited { + [self.delegate machineDidChangeClockIsUnlimited:self]; +} + - (void)dealloc { [_view performWithGLContext:^{ @synchronized(self) { @@ -119,6 +127,10 @@ struct MachineDelegate: CRTMachine::Machine::Delegate { return self.machine->get_clock_rate(); } +- (BOOL)clockIsUnlimited { + return self.machine->get_clock_is_unlimited() ? YES : NO; +} + - (void)paste:(NSString *)paste { Utility::TypeRecipient *typeRecipient = dynamic_cast(self.machine); if(typeRecipient) diff --git a/OSBindings/Mac/Clock Signal/Updater/CSBestEffortUpdater.h b/OSBindings/Mac/Clock Signal/Updater/CSBestEffortUpdater.h index f8608a600..2ed7a43bc 100644 --- a/OSBindings/Mac/Clock Signal/Updater/CSBestEffortUpdater.h +++ b/OSBindings/Mac/Clock Signal/Updater/CSBestEffortUpdater.h @@ -21,6 +21,7 @@ @interface CSBestEffortUpdater : NSObject @property (nonatomic, assign) double clockRate; +@property (nonatomic, assign) BOOL runAsUnlimited; @property (nonatomic, weak) id delegate; - (void)update; diff --git a/OSBindings/Mac/Clock Signal/Updater/CSBestEffortUpdater.m b/OSBindings/Mac/Clock Signal/Updater/CSBestEffortUpdater.m index 781b978bb..10c0a133d 100644 --- a/OSBindings/Mac/Clock Signal/Updater/CSBestEffortUpdater.m +++ b/OSBindings/Mac/Clock Signal/Updater/CSBestEffortUpdater.m @@ -44,8 +44,10 @@ double cyclesToRunFor = timeToRunFor * self.clockRate + _cyclesError; _cyclesError = fmod(cyclesToRunFor, 1.0); - NSUInteger integerCyclesToRunFor = (NSUInteger)cyclesToRunFor; + NSUInteger integerCyclesToRunFor = (NSUInteger)MIN(cyclesToRunFor, self.clockRate * 0.5); + // treat 'unlimited' as running at a factor of 10 + if(self.runAsUnlimited) integerCyclesToRunFor *= 10; [self.delegate bestEffortUpdater:self runForCycles:integerCyclesToRunFor didSkipPreviousUpdate:_hasSkipped]; } _previousTimeInterval = timeInterval; diff --git a/StaticAnalyser/Commodore/CommodoreAnalyser.cpp b/StaticAnalyser/Commodore/CommodoreAnalyser.cpp index f5da45244..787c4124b 100644 --- a/StaticAnalyser/Commodore/CommodoreAnalyser.cpp +++ b/StaticAnalyser/Commodore/CommodoreAnalyser.cpp @@ -42,33 +42,24 @@ void StaticAnalyser::Commodore::AddTargets( if(files.front().is_basic()) { target.loadingCommand = "LOAD\"\",1,0\nRUN\n"; - - // make a first guess based on file size - size_t file_size = files.front().data.size(); - if(file_size > 6655) target.vic20.memory_model = Vic20MemoryModel::ThirtyTwoKB; - else if(file_size > 3583) target.vic20.memory_model = Vic20MemoryModel::EightKB; - else target.vic20.memory_model = Vic20MemoryModel::Unexpanded; } else { - // TODO: this is machine code. So, ummm? - printf("Need to deal with machine code from %04x to %04x???\n", files.front().starting_address, files.front().ending_address); target.loadingCommand = "LOAD\"\",1,1\nRUN\n"; - - // make a first guess based on loading address - switch(files.front().starting_address) - { - case 0x1001: - default: break; - case 0x1201: - target.vic20.memory_model = Vic20MemoryModel::ThirtyTwoKB; - break; - case 0x0401: - target.vic20.memory_model = Vic20MemoryModel::EightKB; - break; - } } + // make a first guess based on loading address + switch(files.front().starting_address) + { + case 0x1001: + default: break; + case 0x1201: + target.vic20.memory_model = Vic20MemoryModel::ThirtyTwoKB; + break; + case 0x0401: + target.vic20.memory_model = Vic20MemoryModel::EightKB; + break; + } // General approach: increase memory size conservatively such that the largest file found will fit. for(File &file : files) From c3a795328de9fc8532b514068602384e8281da82 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 12 Sep 2016 22:15:38 -0400 Subject: [PATCH 58/66] Windows have titles again. Also I've owned up to not knowing how to edit UEFs right now. --- .../Clock Signal/Document Controller/DocumentController.swift | 1 + OSBindings/Mac/Clock Signal/Info.plist | 2 +- .../Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.h | 1 + .../Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm | 3 +++ 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/OSBindings/Mac/Clock Signal/Document Controller/DocumentController.swift b/OSBindings/Mac/Clock Signal/Document Controller/DocumentController.swift index b5f7c10dc..847db82ad 100644 --- a/OSBindings/Mac/Clock Signal/Document Controller/DocumentController.swift +++ b/OSBindings/Mac/Clock Signal/Document Controller/DocumentController.swift @@ -14,6 +14,7 @@ class DocumentController: NSDocumentController { if let documentClass = analyser.documentClass as? NSDocument.Type { let document = documentClass.init() if let machineDocument = document as? MachineDocument { + machineDocument.setDisplayName(analyser.displayName) machineDocument.configureAs(analyser) return machineDocument } diff --git a/OSBindings/Mac/Clock Signal/Info.plist b/OSBindings/Mac/Clock Signal/Info.plist index ec505d1aa..cd2e9b763 100644 --- a/OSBindings/Mac/Clock Signal/Info.plist +++ b/OSBindings/Mac/Clock Signal/Info.plist @@ -35,7 +35,7 @@ CFBundleTypeName Electron/BBC Tape Image CFBundleTypeRole - Editor + Viewer LSItemContentTypes LSTypeIsPackage diff --git a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.h b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.h index b57aeb86c..c0ef69354 100644 --- a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.h +++ b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.h @@ -15,6 +15,7 @@ - (instancetype)initWithFileAtURL:(NSURL *)url; @property(nonatomic, readonly) Class documentClass; +@property(nonatomic, readonly) NSString *displayName; - (void)applyToMachine:(CSMachine *)machine; @end diff --git a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm index 241ec1cdb..29f985e3e 100644 --- a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm +++ b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm @@ -27,6 +27,9 @@ std::list targets = StaticAnalyser::GetTargets([url fileSystemRepresentation]); if(!targets.size()) return nil; _target = targets.front(); + + // TODO: can this better be supplied by the analyser? + _displayName = [[url pathComponents] lastObject]; } return self; } From e3571e8b9e48d5bba57ced9cffeda72ff1437969 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 12 Sep 2016 22:22:23 -0400 Subject: [PATCH 59/66] Added insurance against an infinite loop should the tape be exhausted. --- Machines/Commodore/Vic-20/Vic20.cpp | 7 +++++-- Storage/Tape/Tape.cpp | 5 +++++ Storage/Tape/Tape.hpp | 1 + 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index 606bbdebf..918b44f77 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -136,7 +136,7 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin // CPU or 6560 costs. if(_use_fast_tape_hack && _tape.has_tape() && address == 0xf92f && operation == CPU6502::BusOperation::ReadOpcode) { - while(!_userPortVIA->get_interrupt_line() && !_keyboardVIA->get_interrupt_line()) + while(!_userPortVIA->get_interrupt_line() && !_keyboardVIA->get_interrupt_line() && !_tape.get_tape()->is_at_end()) { _userPortVIA->run_for_half_cycles(2); _keyboardVIA->run_for_half_cycles(2); @@ -184,7 +184,10 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin _is_running_at_zero_cost = true; set_clock_is_unlimited(true); } - if(address < 0xe000 && operation == CPU6502::BusOperation::ReadOpcode) + if( + (address < 0xe000 && operation == CPU6502::BusOperation::ReadOpcode) || + _tape.get_tape()->is_at_end() + ) { _is_running_at_zero_cost = false; set_clock_is_unlimited(false); diff --git a/Storage/Tape/Tape.cpp b/Storage/Tape/Tape.cpp index 8cff0314c..d0283ffbe 100644 --- a/Storage/Tape/Tape.cpp +++ b/Storage/Tape/Tape.cpp @@ -50,6 +50,11 @@ void TapePlayer::set_tape(std::shared_ptr tape) get_next_pulse(); } +std::shared_ptr TapePlayer::get_tape() +{ + return _tape; +} + bool TapePlayer::has_tape() { return (bool)_tape; diff --git a/Storage/Tape/Tape.hpp b/Storage/Tape/Tape.hpp index ca3100f9b..cbf330e49 100644 --- a/Storage/Tape/Tape.hpp +++ b/Storage/Tape/Tape.hpp @@ -78,6 +78,7 @@ class TapePlayer: public TimedEventLoop { void set_tape(std::shared_ptr tape); bool has_tape(); + std::shared_ptr get_tape(); void run_for_cycles(int number_of_cycles); void run_for_input_pulse(); From df7aed7e8b907d8bbc1ca9967ef65d7f7d167a1b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 13 Sep 2016 07:26:51 -0400 Subject: [PATCH 60/66] The Commodore analyser now at least hands off to somebody else a chance to parse disks. --- .../Clock Signal.xcodeproj/project.pbxproj | 6 + .../Commodore/CommodoreAnalyser.cpp | 169 ++++++++++-------- StaticAnalyser/Commodore/Disk.cpp | 19 ++ StaticAnalyser/Commodore/Disk.hpp | 25 +++ StaticAnalyser/Commodore/Tape.hpp | 5 +- 5 files changed, 148 insertions(+), 76 deletions(-) create mode 100644 StaticAnalyser/Commodore/Disk.cpp create mode 100644 StaticAnalyser/Commodore/Disk.hpp diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 5d29d7266..d6856d087 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -50,6 +50,7 @@ 4B73C71D1D036C030074D992 /* Vic20Document.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B73C71B1D036C030074D992 /* Vic20Document.xib */; }; 4B92EACA1B7C112B00246143 /* 6502TimingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */; }; 4B96F7221D75119A0058BB2D /* Tape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B96F7201D75119A0058BB2D /* Tape.cpp */; }; + 4BA22B071D8817CE0008C640 /* Disk.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BA22B051D8817CE0008C640 /* Disk.cpp */; }; 4BAB62AD1D3272D200DF5BA0 /* Disk.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BAB62AB1D3272D200DF5BA0 /* Disk.cpp */; }; 4BAB62B51D327F7E00DF5BA0 /* G64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BAB62B31D327F7E00DF5BA0 /* G64.cpp */; }; 4BAB62B81D3302CA00DF5BA0 /* PCMTrack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BAB62B61D3302CA00DF5BA0 /* PCMTrack.cpp */; }; @@ -455,6 +456,8 @@ 4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 6502TimingTests.swift; sourceTree = ""; }; 4B96F7201D75119A0058BB2D /* Tape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Tape.cpp; path = ../../StaticAnalyser/Acorn/Tape.cpp; sourceTree = ""; }; 4B96F7211D75119A0058BB2D /* Tape.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Tape.hpp; path = ../../StaticAnalyser/Acorn/Tape.hpp; sourceTree = ""; }; + 4BA22B051D8817CE0008C640 /* Disk.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Disk.cpp; path = ../../StaticAnalyser/Commodore/Disk.cpp; sourceTree = ""; }; + 4BA22B061D8817CE0008C640 /* Disk.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Disk.hpp; path = ../../StaticAnalyser/Commodore/Disk.hpp; sourceTree = ""; }; 4BA9C3CF1D8164A9002DDB61 /* ConfigurationTarget.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ConfigurationTarget.hpp; sourceTree = ""; }; 4BAB62AB1D3272D200DF5BA0 /* Disk.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Disk.cpp; sourceTree = ""; }; 4BAB62AC1D3272D200DF5BA0 /* Disk.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Disk.hpp; sourceTree = ""; }; @@ -1534,6 +1537,8 @@ 4BC5E4941D7EE0E0008CF980 /* Utilities.hpp */, 4BE77A2C1D84ADFB00BC3827 /* File.cpp */, 4BE77A2D1D84ADFB00BC3827 /* File.hpp */, + 4BA22B051D8817CE0008C640 /* Disk.cpp */, + 4BA22B061D8817CE0008C640 /* Disk.hpp */, ); name = Commodore; sourceTree = ""; @@ -2077,6 +2082,7 @@ 4B2A53A31D117D36003C6002 /* CSVic20.mm in Sources */, 4B2A53A21D117D36003C6002 /* CSElectron.mm in Sources */, 4B2E2D9A1C3A06EC00138695 /* Atari2600.cpp in Sources */, + 4BA22B071D8817CE0008C640 /* Disk.cpp in Sources */, 4BC3B7521CD1956900F86E85 /* OutputShader.cpp in Sources */, 4B4C83701D4F623200CD541F /* D64.cpp in Sources */, 4B14145B1B58879D00E04248 /* CPU6502.cpp in Sources */, diff --git a/StaticAnalyser/Commodore/CommodoreAnalyser.cpp b/StaticAnalyser/Commodore/CommodoreAnalyser.cpp index 787c4124b..47cab467c 100644 --- a/StaticAnalyser/Commodore/CommodoreAnalyser.cpp +++ b/StaticAnalyser/Commodore/CommodoreAnalyser.cpp @@ -8,7 +8,9 @@ #include "CommodoreAnalyser.hpp" +#include "File.hpp" #include "Tape.hpp" +#include "Disk.hpp" using namespace StaticAnalyser::Commodore; @@ -22,88 +24,107 @@ void StaticAnalyser::Commodore::AddTargets( target.machine = Target::Vic20; // TODO: machine estimation target.probability = 1.0; // TODO: a proper estimation + int device = 0; + std::list files; + // strip out inappropriate cartridges // target.cartridges = AcornCartridgesFrom(cartridges); - // if there are any tapes, attempt to get data from the first - if(tapes.size() > 0) + // check disks + for(auto &disk : disks) { - std::shared_ptr tape = tapes.front(); - tape->reset(); - std::list files = GetFiles(tape); - tape->reset(); - - // continue if there are any files - if(files.size()) + std::list disk_files = GetFiles(disk); + if(disk_files.size()) { - target.tapes = tapes; - - target.vic20.memory_model = Vic20MemoryModel::Unexpanded; - if(files.front().is_basic()) - { - target.loadingCommand = "LOAD\"\",1,0\nRUN\n"; - } - else - { - target.loadingCommand = "LOAD\"\",1,1\nRUN\n"; - } - - // make a first guess based on loading address - switch(files.front().starting_address) - { - case 0x1001: - default: break; - case 0x1201: - target.vic20.memory_model = Vic20MemoryModel::ThirtyTwoKB; - break; - case 0x0401: - target.vic20.memory_model = Vic20MemoryModel::EightKB; - break; - } - - // General approach: increase memory size conservatively such that the largest file found will fit. - for(File &file : files) - { - size_t file_size = file.data.size(); - //bool is_basic = file.is_basic(); - - /*if(is_basic) - { - // BASIC files may be relocated, so the only limit is size. - // - // An unexpanded machine has 3583 bytes free for BASIC; - // a 3kb expanded machine has 6655 bytes free. - if(file_size > 6655) - target.vic20.memory_model = Vic20MemoryModel::ThirtyTwoKB; - else if(target.vic20.memory_model == Vic20MemoryModel::Unexpanded && file_size > 3583) - target.vic20.memory_model = Vic20MemoryModel::EightKB; - } - else - {*/ -// if(!file.type == File::NonRelocatableProgram) -// { - // Non-BASIC files may be relocatable but, if so, by what logic? - // Given that this is unknown, take starting address as literal - // and check against memory windows. - // - // (ignoring colour memory...) - // An unexpanded Vic has memory between 0x0000 and 0x0400; and between 0x1000 and 0x2000. - // A 3kb expanded Vic fills in the gap and has memory between 0x0000 and 0x2000. - // A 32kb expanded Vic has memory in the entire low 32kb. - uint16_t starting_address = file.starting_address; - - // If anything above the 8kb mark is touched, mark as a 32kb machine; otherwise if the - // region 0x0400 to 0x1000 is touched and this is an unexpanded machine, mark as 3kb. - if(starting_address + file_size > 0x2000) - target.vic20.memory_model = Vic20MemoryModel::ThirtyTwoKB; - else if(target.vic20.memory_model == Vic20MemoryModel::Unexpanded && !(starting_address >= 0x1000 || starting_address+file_size < 0x0400)) - target.vic20.memory_model = Vic20MemoryModel::ThirtyTwoKB; -// } - // } - } + files.splice(files.end(), disk_files); + target.disks = disks; + if(!device) device = 8; } } + // check tapes + for(auto &tape : tapes) + { + std::list tape_files = GetFiles(tape); + if(tape_files.size()) + { + files.splice(files.end(), tape_files); + target.tapes = tapes; + if(!device) device = 1; + } + } + + if(files.size()) + { + target.vic20.memory_model = Vic20MemoryModel::Unexpanded; + if(files.front().is_basic()) + { + char command[16]; + snprintf(command, 16, "LOAD\"\",%d,0\nRUN\n", device); + target.loadingCommand = command; + } + else + { + char command[16]; + snprintf(command, 16, "LOAD\"\",%d,1\nRUN\n", device); + target.loadingCommand = command; + } + + // make a first guess based on loading address + switch(files.front().starting_address) + { + case 0x1001: + default: break; + case 0x1201: + target.vic20.memory_model = Vic20MemoryModel::ThirtyTwoKB; + break; + case 0x0401: + target.vic20.memory_model = Vic20MemoryModel::EightKB; + break; + } + + // General approach: increase memory size conservatively such that the largest file found will fit. + for(File &file : files) + { + size_t file_size = file.data.size(); +// bool is_basic = file.is_basic(); + + /*if(is_basic) + { + // BASIC files may be relocated, so the only limit is size. + // + // An unexpanded machine has 3583 bytes free for BASIC; + // a 3kb expanded machine has 6655 bytes free. + if(file_size > 6655) + target.vic20.memory_model = Vic20MemoryModel::ThirtyTwoKB; + else if(target.vic20.memory_model == Vic20MemoryModel::Unexpanded && file_size > 3583) + target.vic20.memory_model = Vic20MemoryModel::EightKB; + } + else + {*/ +// if(!file.type == File::NonRelocatableProgram) +// { + // Non-BASIC files may be relocatable but, if so, by what logic? + // Given that this is unknown, take starting address as literal + // and check against memory windows. + // + // (ignoring colour memory...) + // An unexpanded Vic has memory between 0x0000 and 0x0400; and between 0x1000 and 0x2000. + // A 3kb expanded Vic fills in the gap and has memory between 0x0000 and 0x2000. + // A 32kb expanded Vic has memory in the entire low 32kb. + uint16_t starting_address = file.starting_address; + + // If anything above the 8kb mark is touched, mark as a 32kb machine; otherwise if the + // region 0x0400 to 0x1000 is touched and this is an unexpanded machine, mark as 3kb. + if(starting_address + file_size > 0x2000) + target.vic20.memory_model = Vic20MemoryModel::ThirtyTwoKB; + else if(target.vic20.memory_model == Vic20MemoryModel::Unexpanded && !(starting_address >= 0x1000 || starting_address+file_size < 0x0400)) + target.vic20.memory_model = Vic20MemoryModel::ThirtyTwoKB; +// } + } + } + + if(target.tapes.size() || target.cartridges.size() || target.disks.size()) destination.push_back(target); } diff --git a/StaticAnalyser/Commodore/Disk.cpp b/StaticAnalyser/Commodore/Disk.cpp new file mode 100644 index 000000000..3fdf5022b --- /dev/null +++ b/StaticAnalyser/Commodore/Disk.cpp @@ -0,0 +1,19 @@ +// +// Disk.cpp +// Clock Signal +// +// Created by Thomas Harte on 13/09/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#include "Disk.hpp" + +using namespace StaticAnalyser::Commodore; + +std::list StaticAnalyser::Commodore::GetFiles(const std::shared_ptr &disk) +{ + std::list _files; + + return _files; +} + diff --git a/StaticAnalyser/Commodore/Disk.hpp b/StaticAnalyser/Commodore/Disk.hpp new file mode 100644 index 000000000..373d00124 --- /dev/null +++ b/StaticAnalyser/Commodore/Disk.hpp @@ -0,0 +1,25 @@ +// +// Disk.hpp +// Clock Signal +// +// Created by Thomas Harte on 13/09/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#ifndef StaticAnalyser_Commodore_Disk_hpp +#define StaticAnalyser_Commodore_Disk_hpp + +#include "../../Storage/Disk/Disk.hpp" +#include "File.hpp" +#include + +namespace StaticAnalyser { +namespace Commodore { + +std::list GetFiles(const std::shared_ptr &disk); + +} +} + + +#endif /* Disk_hpp */ diff --git a/StaticAnalyser/Commodore/Tape.hpp b/StaticAnalyser/Commodore/Tape.hpp index 9a16bcdd0..8318b0837 100644 --- a/StaticAnalyser/Commodore/Tape.hpp +++ b/StaticAnalyser/Commodore/Tape.hpp @@ -9,9 +9,9 @@ #ifndef StaticAnalyser_Commodore_Tape_hpp #define StaticAnalyser_Commodore_Tape_hpp -#include -#include "../StaticAnalyser.hpp" +#include "../../Storage/Tape/Tape.hpp" #include "File.hpp" +#include namespace StaticAnalyser { namespace Commodore { @@ -20,4 +20,5 @@ std::list GetFiles(const std::shared_ptr &tape); } } + #endif /* Tape_hpp */ From 9d6dcb80a786c22abc6462721bd402406340228b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 13 Sep 2016 21:53:36 -0400 Subject: [PATCH 61/66] Started work on a GCR parser and the helper functions that lie behind that. --- StaticAnalyser/Commodore/Disk.cpp | 170 +++++++++++++++++++++++- Storage/Disk/Encodings/CommodoreGCR.cpp | 48 ++++--- Storage/Disk/Encodings/CommodoreGCR.hpp | 10 ++ 3 files changed, 209 insertions(+), 19 deletions(-) diff --git a/StaticAnalyser/Commodore/Disk.cpp b/StaticAnalyser/Commodore/Disk.cpp index 3fdf5022b..6275f95fa 100644 --- a/StaticAnalyser/Commodore/Disk.cpp +++ b/StaticAnalyser/Commodore/Disk.cpp @@ -7,13 +7,179 @@ // #include "Disk.hpp" +#include "../../Storage/Disk/DiskDrive.hpp" +#include "../../Storage/Disk/Encodings/CommodoreGCR.hpp" + +#include +#include +#include using namespace StaticAnalyser::Commodore; +class CommodoreGCRParser: public Storage::Disk::Drive { + public: + CommodoreGCRParser() : Storage::Disk::Drive(4000000, 4, 300), shift_register_(0) {} + + struct Sector + { + uint8_t sector, track; + std::array data; + bool header_checksum_matched; + bool data_checksum_matched; + }; + + std::unique_ptr get_sector(uint8_t track, uint8_t sector) + { + return nullptr; + } + + std::unique_ptr get_sector(uint8_t sector) + { + std::unique_ptr first_sector = get_next_sector(); + if(!first_sector) return first_sector; + if(first_sector->sector == sector) return first_sector; + + while(1) + { + std::unique_ptr next_sector = get_next_sector(); + if(next_sector->sector == first_sector->sector) return nullptr; + if(next_sector->sector == sector) return next_sector; + } + } + + std::unique_ptr get_next_sector() + { + std::unique_ptr sector(new Sector); + index_count_ = 0; + + while(index_count_ < 2) + { + // look for a sector header + while(1) + { + if(proceed_to_next_block() == 0x08) break; + if(index_count_ >= 2) return nullptr; + } + + // get sector details, skip if this looks malformed + uint8_t checksum = (uint8_t)get_next_byte(); + sector->sector = (uint8_t)get_next_byte(); + sector->track = (uint8_t)get_next_byte(); + uint8_t disk_id[2]; + disk_id[0] = (uint8_t)get_next_byte(); + disk_id[1] = (uint8_t)get_next_byte(); + if(checksum != (sector->sector ^ sector->track ^ disk_id[0] ^ disk_id[1])) continue; + + // look for the following data + while(1) + { + if(proceed_to_next_block() == 0x07) break; + if(index_count_ >= 2) return nullptr; + } + + checksum = 0; + for(size_t c = 0; c < 256; c++) + { + sector->data[c] = (uint8_t)get_next_byte(); + checksum ^= sector->data[c]; + } + + if(checksum == get_next_byte()) return sector; + } + + return nullptr; + } + + private: + unsigned int shift_register_; + int index_count_; + int bit_count_; + + void process_input_bit(int value, unsigned int cycles_since_index_hole) + { + shift_register_ = ((shift_register_ << 1) | (unsigned int)value) & 0x3ff; + bit_count_++; + } + + unsigned int proceed_to_next_block() + { + // find GCR lead-in + proceed_to_shift_value(0x3ff); + if(shift_register_ != 0x3ff) return 0xff; + + // find end of lead-in + while(shift_register_ == 0x3ff && index_count_ < 2) + { + run_for_cycles(1); + } + + // continue for a further nine bits + bit_count_ = 0; + while(bit_count_ < 9 && index_count_ < 2) + { + run_for_cycles(1); + } + + return Storage::Encodings::CommodoreGCR::decoding_from_dectet(shift_register_); + } + + unsigned int get_next_byte() + { + bit_count_ = 0; + while(bit_count_ < 10) run_for_cycles(1); + return Storage::Encodings::CommodoreGCR::decoding_from_dectet(shift_register_); + } + + void proceed_to_shift_value(unsigned int shift_value) + { + index_count_ = 0; + while(shift_register_ != shift_value && index_count_ < 2) + { + run_for_cycles(1); + } + } + + void process_index_hole() + { + index_count_++; + } + +}; + std::list StaticAnalyser::Commodore::GetFiles(const std::shared_ptr &disk) { - std::list _files; + std::list files; + CommodoreGCRParser parser; + parser.set_disk(disk); - return _files; + // find any sector whatsoever to establish the current track + std::unique_ptr sector; + + // attempt to grab a sector from track 0 + while(!parser.get_is_track_zero()) parser.step(-1); + parser.set_expected_bit_length(Storage::Encodings::CommodoreGCR::length_of_a_bit_in_time_zone(0)); + sector = parser.get_next_sector(); + if(!sector) return files; + + // step out to track 18 (== 36) + for(int c = 0; c < 36; c++) parser.step(1); + + // assemble disk contents, starting with sector 1 + parser.set_expected_bit_length(Storage::Encodings::CommodoreGCR::length_of_a_bit_in_time_zone(1)); + std::vector directory; + sector = parser.get_sector(1); + while(sector) + { + directory.insert(directory.end(), sector->data.begin(), sector->data.end()); + uint8_t next_track = sector->data[0]; + uint8_t next_sector = sector->data[1]; + + if(!next_track) break; + sector = parser.get_sector(next_sector); + + // TODO: track changes. Allegedly not possible, but definitely happening. + } + + return files; } diff --git a/Storage/Disk/Encodings/CommodoreGCR.cpp b/Storage/Disk/Encodings/CommodoreGCR.cpp index b86836673..3cdd84fc6 100644 --- a/Storage/Disk/Encodings/CommodoreGCR.cpp +++ b/Storage/Disk/Encodings/CommodoreGCR.cpp @@ -7,6 +7,7 @@ // #include "CommodoreGCR.hpp" +#include using namespace Storage; @@ -23,34 +24,47 @@ unsigned int Storage::Encodings::CommodoreGCR::encoding_for_nibble(uint8_t nibbl { switch(nibble & 0xf) { - case 0x0: return 0x0a; - case 0x1: return 0x0b; - case 0x2: return 0x12; - case 0x3: return 0x13; - case 0x4: return 0x0e; - case 0x5: return 0x0f; - case 0x6: return 0x16; - case 0x7: return 0x17; - - case 0x8: return 0x09; - case 0x9: return 0x19; - case 0xa: return 0x1a; - case 0xb: return 0x1b; - case 0xc: return 0x0d; - case 0xd: return 0x1d; - case 0xe: return 0x1e; - case 0xf: return 0x15; + case 0x0: return 0x0a; case 0x1: return 0x0b; + case 0x2: return 0x12; case 0x3: return 0x13; + case 0x4: return 0x0e; case 0x5: return 0x0f; + case 0x6: return 0x16; case 0x7: return 0x17; + case 0x8: return 0x09; case 0x9: return 0x19; + case 0xa: return 0x1a; case 0xb: return 0x1b; + case 0xc: return 0x0d; case 0xd: return 0x1d; + case 0xe: return 0x1e; case 0xf: return 0x15; // for the benefit of the compiler; clearly unreachable default: return 0xff; } } +unsigned int Storage::Encodings::CommodoreGCR::decoding_from_quintet(unsigned int quintet) +{ + switch(quintet & 0x1f) + { + case 0x0a: return 0x0; case 0x0b: return 0x1; + case 0x12: return 0x2; case 0x13: return 0x3; + case 0x0e: return 0x4; case 0x0f: return 0x5; + case 0x16: return 0x6; case 0x17: return 0x7; + case 0x09: return 0x8; case 0x19: return 0x9; + case 0x1a: return 0xa; case 0x1b: return 0xb; + case 0x0d: return 0xc; case 0x1d: return 0xd; + case 0x1e: return 0xe; case 0x15: return 0xf; + + default: return std::numeric_limits::max(); + } +} + unsigned int Storage::Encodings::CommodoreGCR::encoding_for_byte(uint8_t byte) { return encoding_for_nibble(byte) | (encoding_for_nibble(byte >> 4) << 5); } +unsigned int Storage::Encodings::CommodoreGCR::decoding_from_dectet(unsigned int dectet) +{ + return decoding_from_quintet(dectet) | (decoding_from_quintet(dectet >> 5) << 4); +} + void Storage::Encodings::CommodoreGCR::encode_block(uint8_t *destination, uint8_t *source) { unsigned int encoded_bytes[4] = { diff --git a/Storage/Disk/Encodings/CommodoreGCR.hpp b/Storage/Disk/Encodings/CommodoreGCR.hpp index 76504bb6f..34346e253 100644 --- a/Storage/Disk/Encodings/CommodoreGCR.hpp +++ b/Storage/Disk/Encodings/CommodoreGCR.hpp @@ -36,6 +36,16 @@ namespace CommodoreGCR { A block is defined to be four source bytes, which encodes to five GCR bytes. */ void encode_block(uint8_t *destination, uint8_t *source); + + /*! + @returns the four bit nibble for the five-bit GCR @c quintet if a valid GCR value; INT_MAX otherwise. + */ + unsigned int decoding_from_quintet(unsigned int quintet); + + /*! + @returns the byte composted of the low five bit five-bit GCR + */ + unsigned int decoding_from_dectet(unsigned int dectet); } } } From 8992feb8cd19b93fa8fcd718d91638c98fe57ee7 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 15 Sep 2016 07:54:42 -0400 Subject: [PATCH 62/66] Completed a first parse at the GCR parser, proving how incredibly slow my drive is. --- StaticAnalyser/Commodore/Disk.cpp | 110 ++++++++++++++++++++++++------ StaticAnalyser/Commodore/File.hpp | 6 ++ 2 files changed, 97 insertions(+), 19 deletions(-) diff --git a/StaticAnalyser/Commodore/Disk.cpp b/StaticAnalyser/Commodore/Disk.cpp index 6275f95fa..aae3ce9a5 100644 --- a/StaticAnalyser/Commodore/Disk.cpp +++ b/StaticAnalyser/Commodore/Disk.cpp @@ -9,6 +9,7 @@ #include "Disk.hpp" #include "../../Storage/Disk/DiskDrive.hpp" #include "../../Storage/Disk/Encodings/CommodoreGCR.hpp" +#include "Utilities.hpp" #include #include @@ -18,7 +19,11 @@ using namespace StaticAnalyser::Commodore; class CommodoreGCRParser: public Storage::Disk::Drive { public: - CommodoreGCRParser() : Storage::Disk::Drive(4000000, 4, 300), shift_register_(0) {} + CommodoreGCRParser() : Storage::Disk::Drive(4000000, 4, 300), shift_register_(0), track_(1) + { + // Make sure this drive really is at track '1'. + while(!get_is_track_zero()) step(-1); + } struct Sector { @@ -30,7 +35,24 @@ class CommodoreGCRParser: public Storage::Disk::Drive { std::unique_ptr get_sector(uint8_t track, uint8_t sector) { - return nullptr; + int difference = (int)track - (int)track_; + track_ = track; + + if(difference) + { + int direction = difference < 0 ? -1 : 1; + difference *= 2 * direction; + + for(int c = 0; c < difference; c++) step(direction); + + unsigned int zone = 3; + if(track >= 18) zone = 2; + else if(track >= 25) zone = 1; + else if(track >= 31) zone = 0; + set_expected_bit_length(Storage::Encodings::CommodoreGCR::length_of_a_bit_in_time_zone(zone)); + } + + return get_sector(sector); } std::unique_ptr get_sector(uint8_t sector) @@ -94,6 +116,7 @@ class CommodoreGCRParser: public Storage::Disk::Drive { unsigned int shift_register_; int index_count_; int bit_count_; + uint8_t track_; void process_input_bit(int value, unsigned int cycles_since_index_hole) { @@ -155,29 +178,78 @@ std::list StaticAnalyser::Commodore::GetFiles(const std::shared_ptr sector; - // attempt to grab a sector from track 0 - while(!parser.get_is_track_zero()) parser.step(-1); - parser.set_expected_bit_length(Storage::Encodings::CommodoreGCR::length_of_a_bit_in_time_zone(0)); - sector = parser.get_next_sector(); - if(!sector) return files; - - // step out to track 18 (== 36) - for(int c = 0; c < 36; c++) parser.step(1); - - // assemble disk contents, starting with sector 1 - parser.set_expected_bit_length(Storage::Encodings::CommodoreGCR::length_of_a_bit_in_time_zone(1)); + // assemble directory std::vector directory; - sector = parser.get_sector(1); - while(sector) + uint8_t next_track = 18; + uint8_t next_sector = 1; + while(1) { + sector = parser.get_sector(next_track, next_sector); + if(!sector) break; directory.insert(directory.end(), sector->data.begin(), sector->data.end()); - uint8_t next_track = sector->data[0]; - uint8_t next_sector = sector->data[1]; + next_track = sector->data[0]; + next_sector = sector->data[1]; if(!next_track) break; - sector = parser.get_sector(next_sector); + } - // TODO: track changes. Allegedly not possible, but definitely happening. + // parse directory + size_t header_pointer = (size_t)-32; + while(header_pointer+32+31 < directory.size()) + { + header_pointer += 32; + + File new_file; + switch(directory[header_pointer + 2] & 7) + { + case 0: // DEL files + default: continue; // Unknown file types + + case 1: new_file.type = File::DataSequence; break; + case 2: new_file.type = File::RelocatableProgram; break; // TODO: need a "don't know about relocatable" program? + case 3: new_file.type = File::User; break; +// case 4: new_file.type = File::Relative; break; // Can't handle REL files yet + } + + next_track = directory[header_pointer + 3]; + next_sector = directory[header_pointer + 4]; + + new_file.raw_name.reserve(16); + for(size_t c = 0; c < 16; c++) + { + new_file.raw_name.push_back(directory[header_pointer + 5 + c]); + } + new_file.name = petscii_from_bytes(&new_file.raw_name[0], 16, false); + + size_t number_of_sectors = (size_t)directory[header_pointer + 0x1e] + ((size_t)directory[header_pointer + 0x1f] << 8); + new_file.data.reserve((number_of_sectors - 1) * 254 + 252); + + bool is_first_sector = true; + while(next_track) + { + sector = parser.get_sector(next_track, next_sector); + if(!sector) break; + + next_track = sector->data[0]; + next_sector = sector->data[1]; + if(is_first_sector) + { + new_file.starting_address = (uint16_t)sector->data[2] | (uint16_t)(sector->data[3] << 8); + } + + if(next_track) + { + new_file.data.insert(new_file.data.end(), sector->data.begin() + (is_first_sector ? 4 : 2), sector->data.end()); + } + else + { + new_file.data.insert(new_file.data.end(), sector->data.begin() + 2, sector->data.begin() + next_sector); + } + + is_first_sector = false; + } + + if(!next_track) files.push_back(new_file); } return files; diff --git a/StaticAnalyser/Commodore/File.hpp b/StaticAnalyser/Commodore/File.hpp index ea81aa493..6a4eda72d 100644 --- a/StaticAnalyser/Commodore/File.hpp +++ b/StaticAnalyser/Commodore/File.hpp @@ -16,14 +16,20 @@ namespace StaticAnalyser { namespace Commodore { struct File { + File() : is_closed(false), is_locked(false) {} + std::wstring name; std::vector raw_name; uint16_t starting_address; uint16_t ending_address; + bool is_locked; + bool is_closed; enum { RelocatableProgram, NonRelocatableProgram, DataSequence, + User, + Relative } type; std::vector data; From 9827466297f59594e0a606cea3de266d0aee9f5a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 15 Sep 2016 07:58:32 -0400 Subject: [PATCH 63/66] Minor post-haste cleaning. --- StaticAnalyser/Commodore/Disk.cpp | 119 +++++++++++++++--------------- 1 file changed, 61 insertions(+), 58 deletions(-) diff --git a/StaticAnalyser/Commodore/Disk.cpp b/StaticAnalyser/Commodore/Disk.cpp index aae3ce9a5..850a5ad90 100644 --- a/StaticAnalyser/Commodore/Disk.cpp +++ b/StaticAnalyser/Commodore/Disk.cpp @@ -19,7 +19,7 @@ using namespace StaticAnalyser::Commodore; class CommodoreGCRParser: public Storage::Disk::Drive { public: - CommodoreGCRParser() : Storage::Disk::Drive(4000000, 4, 300), shift_register_(0), track_(1) + CommodoreGCRParser() : Storage::Disk::Drive(4000000, 1, 300), shift_register_(0), track_(1) { // Make sure this drive really is at track '1'. while(!get_is_track_zero()) step(-1); @@ -33,6 +33,11 @@ class CommodoreGCRParser: public Storage::Disk::Drive { bool data_checksum_matched; }; + /*! + Attempts to read the sector located at @c track and @c sector. + + @returns a sector if one was found; @c nullptr otherwise. + */ std::unique_ptr get_sector(uint8_t track, uint8_t sector) { int difference = (int)track - (int)track_; @@ -55,6 +60,61 @@ class CommodoreGCRParser: public Storage::Disk::Drive { return get_sector(sector); } + private: + unsigned int shift_register_; + int index_count_; + int bit_count_; + uint8_t track_; + + void process_input_bit(int value, unsigned int cycles_since_index_hole) + { + shift_register_ = ((shift_register_ << 1) | (unsigned int)value) & 0x3ff; + bit_count_++; + } + + unsigned int proceed_to_next_block() + { + // find GCR lead-in + proceed_to_shift_value(0x3ff); + if(shift_register_ != 0x3ff) return 0xff; + + // find end of lead-in + while(shift_register_ == 0x3ff && index_count_ < 2) + { + run_for_cycles(1); + } + + // continue for a further nine bits + bit_count_ = 0; + while(bit_count_ < 9 && index_count_ < 2) + { + run_for_cycles(1); + } + + return Storage::Encodings::CommodoreGCR::decoding_from_dectet(shift_register_); + } + + unsigned int get_next_byte() + { + bit_count_ = 0; + while(bit_count_ < 10) run_for_cycles(1); + return Storage::Encodings::CommodoreGCR::decoding_from_dectet(shift_register_); + } + + void proceed_to_shift_value(unsigned int shift_value) + { + index_count_ = 0; + while(shift_register_ != shift_value && index_count_ < 2) + { + run_for_cycles(1); + } + } + + void process_index_hole() + { + index_count_++; + } + std::unique_ptr get_sector(uint8_t sector) { std::unique_ptr first_sector = get_next_sector(); @@ -111,62 +171,6 @@ class CommodoreGCRParser: public Storage::Disk::Drive { return nullptr; } - - private: - unsigned int shift_register_; - int index_count_; - int bit_count_; - uint8_t track_; - - void process_input_bit(int value, unsigned int cycles_since_index_hole) - { - shift_register_ = ((shift_register_ << 1) | (unsigned int)value) & 0x3ff; - bit_count_++; - } - - unsigned int proceed_to_next_block() - { - // find GCR lead-in - proceed_to_shift_value(0x3ff); - if(shift_register_ != 0x3ff) return 0xff; - - // find end of lead-in - while(shift_register_ == 0x3ff && index_count_ < 2) - { - run_for_cycles(1); - } - - // continue for a further nine bits - bit_count_ = 0; - while(bit_count_ < 9 && index_count_ < 2) - { - run_for_cycles(1); - } - - return Storage::Encodings::CommodoreGCR::decoding_from_dectet(shift_register_); - } - - unsigned int get_next_byte() - { - bit_count_ = 0; - while(bit_count_ < 10) run_for_cycles(1); - return Storage::Encodings::CommodoreGCR::decoding_from_dectet(shift_register_); - } - - void proceed_to_shift_value(unsigned int shift_value) - { - index_count_ = 0; - while(shift_register_ != shift_value && index_count_ < 2) - { - run_for_cycles(1); - } - } - - void process_index_hole() - { - index_count_++; - } - }; std::list StaticAnalyser::Commodore::GetFiles(const std::shared_ptr &disk) @@ -254,4 +258,3 @@ std::list StaticAnalyser::Commodore::GetFiles(const std::shared_ptr Date: Thu, 15 Sep 2016 19:21:09 -0400 Subject: [PATCH 64/66] With minor cleaning, this should now do Commodore disks correctly (?) --- StaticAnalyser/Commodore/CommodoreAnalyser.cpp | 6 ++++-- StaticAnalyser/Commodore/Disk.cpp | 9 +-------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/StaticAnalyser/Commodore/CommodoreAnalyser.cpp b/StaticAnalyser/Commodore/CommodoreAnalyser.cpp index 47cab467c..2c88a6fbf 100644 --- a/StaticAnalyser/Commodore/CommodoreAnalyser.cpp +++ b/StaticAnalyser/Commodore/CommodoreAnalyser.cpp @@ -26,6 +26,7 @@ void StaticAnalyser::Commodore::AddTargets( int device = 0; std::list files; + bool is_disk = false; // strip out inappropriate cartridges // target.cartridges = AcornCartridgesFrom(cartridges); @@ -36,6 +37,7 @@ void StaticAnalyser::Commodore::AddTargets( std::list disk_files = GetFiles(disk); if(disk_files.size()) { + is_disk = true; files.splice(files.end(), disk_files); target.disks = disks; if(!device) device = 8; @@ -60,13 +62,13 @@ void StaticAnalyser::Commodore::AddTargets( if(files.front().is_basic()) { char command[16]; - snprintf(command, 16, "LOAD\"\",%d,0\nRUN\n", device); + snprintf(command, 16, "LOAD\"%s\",%d,0\nRUN\n", is_disk ? "*" : "", device); target.loadingCommand = command; } else { char command[16]; - snprintf(command, 16, "LOAD\"\",%d,1\nRUN\n", device); + snprintf(command, 16, "LOAD\"%s\",%d,1\nRUN\n", is_disk ? "*" : "", device); target.loadingCommand = command; } diff --git a/StaticAnalyser/Commodore/Disk.cpp b/StaticAnalyser/Commodore/Disk.cpp index 850a5ad90..df987d691 100644 --- a/StaticAnalyser/Commodore/Disk.cpp +++ b/StaticAnalyser/Commodore/Disk.cpp @@ -236,19 +236,12 @@ std::list StaticAnalyser::Commodore::GetFiles(const std::shared_ptrdata[0]; next_sector = sector->data[1]; - if(is_first_sector) - { - new_file.starting_address = (uint16_t)sector->data[2] | (uint16_t)(sector->data[3] << 8); - } + if(is_first_sector) new_file.starting_address = (uint16_t)sector->data[2] | (uint16_t)(sector->data[3] << 8); if(next_track) - { new_file.data.insert(new_file.data.end(), sector->data.begin() + (is_first_sector ? 4 : 2), sector->data.end()); - } else - { new_file.data.insert(new_file.data.end(), sector->data.begin() + 2, sector->data.begin() + next_sector); - } is_first_sector = false; } From 92af19098c8612577a167b1e352fb0ab6c5d4a06 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 15 Sep 2016 19:24:59 -0400 Subject: [PATCH 65/66] Improved file naming. --- .../Clock Signal.xcodeproj/project.pbxproj | 24 +++++++++---------- .../{AcornAnalyser.cpp => StaticAnalyser.cpp} | 2 +- .../{AcornAnalyser.hpp => StaticAnalyser.hpp} | 4 ++-- ...mmodoreAnalyser.cpp => StaticAnalyser.cpp} | 2 +- ...mmodoreAnalyser.hpp => StaticAnalyser.hpp} | 4 ++-- StaticAnalyser/StaticAnalyser.cpp | 4 ++-- 6 files changed, 20 insertions(+), 20 deletions(-) rename StaticAnalyser/Acorn/{AcornAnalyser.cpp => StaticAnalyser.cpp} (99%) rename StaticAnalyser/Acorn/{AcornAnalyser.hpp => StaticAnalyser.hpp} (84%) rename StaticAnalyser/Commodore/{CommodoreAnalyser.cpp => StaticAnalyser.cpp} (99%) rename StaticAnalyser/Commodore/{CommodoreAnalyser.hpp => StaticAnalyser.hpp} (83%) diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index d6856d087..3caa89e2d 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -336,7 +336,7 @@ 4BBF99181C8FBA6F0075DAFB /* TextureTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBF99121C8FBA6F0075DAFB /* TextureTarget.cpp */; }; 4BC3B74F1CD194CC00F86E85 /* Shader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC3B74D1CD194CC00F86E85 /* Shader.cpp */; }; 4BC3B7521CD1956900F86E85 /* OutputShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC3B7501CD1956900F86E85 /* OutputShader.cpp */; }; - 4BC5E4921D7ED365008CF980 /* CommodoreAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC5E4901D7ED365008CF980 /* CommodoreAnalyser.cpp */; }; + 4BC5E4921D7ED365008CF980 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC5E4901D7ED365008CF980 /* StaticAnalyser.cpp */; }; 4BC5E4951D7EE0E0008CF980 /* Utilities.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC5E4931D7EE0E0008CF980 /* Utilities.cpp */; }; 4BC751B21D157E61006C31D9 /* 6522Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC751B11D157E61006C31D9 /* 6522Tests.swift */; }; 4BC76E691C98E31700E6EF73 /* FIRFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC76E671C98E31700E6EF73 /* FIRFilter.cpp */; }; @@ -346,7 +346,7 @@ 4BC9DF451D044FCA00F44158 /* ROMImages in Resources */ = {isa = PBXBuildFile; fileRef = 4BC9DF441D044FCA00F44158 /* ROMImages */; }; 4BC9DF4F1D04691600F44158 /* 6560.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC9DF4D1D04691600F44158 /* 6560.cpp */; }; 4BC9E1EE1D23449A003FCEE4 /* 6502InterruptTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC9E1ED1D23449A003FCEE4 /* 6502InterruptTests.swift */; }; - 4BD14B111D74627C0088EAD6 /* AcornAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD14B0F1D74627C0088EAD6 /* AcornAnalyser.cpp */; }; + 4BD14B111D74627C0088EAD6 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD14B0F1D74627C0088EAD6 /* StaticAnalyser.cpp */; }; 4BD5F1951D13528900631CD1 /* CSBestEffortUpdater.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.m */; }; 4BE77A2E1D84ADFB00BC3827 /* File.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE77A2C1D84ADFB00BC3827 /* File.cpp */; }; 4BEE0A6F1D72496600532C7B /* Cartridge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE0A6A1D72496600532C7B /* Cartridge.cpp */; }; @@ -768,8 +768,8 @@ 4BC3B74E1CD194CC00F86E85 /* Shader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Shader.hpp; sourceTree = ""; }; 4BC3B7501CD1956900F86E85 /* OutputShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OutputShader.cpp; sourceTree = ""; }; 4BC3B7511CD1956900F86E85 /* OutputShader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = OutputShader.hpp; sourceTree = ""; }; - 4BC5E4901D7ED365008CF980 /* CommodoreAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommodoreAnalyser.cpp; path = ../../StaticAnalyser/Commodore/CommodoreAnalyser.cpp; sourceTree = ""; }; - 4BC5E4911D7ED365008CF980 /* CommodoreAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = CommodoreAnalyser.hpp; path = ../../StaticAnalyser/Commodore/CommodoreAnalyser.hpp; sourceTree = ""; }; + 4BC5E4901D7ED365008CF980 /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StaticAnalyser.cpp; path = ../../StaticAnalyser/Commodore/StaticAnalyser.cpp; sourceTree = ""; }; + 4BC5E4911D7ED365008CF980 /* StaticAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = StaticAnalyser.hpp; path = ../../StaticAnalyser/Commodore/StaticAnalyser.hpp; sourceTree = ""; }; 4BC5E4931D7EE0E0008CF980 /* Utilities.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Utilities.cpp; path = ../../StaticAnalyser/Commodore/Utilities.cpp; sourceTree = ""; }; 4BC5E4941D7EE0E0008CF980 /* Utilities.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Utilities.hpp; path = ../../StaticAnalyser/Commodore/Utilities.hpp; sourceTree = ""; }; 4BC751B11D157E61006C31D9 /* 6522Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 6522Tests.swift; sourceTree = ""; }; @@ -785,8 +785,8 @@ 4BC9DF4E1D04691600F44158 /* 6560.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 6560.hpp; sourceTree = ""; }; 4BC9E1ED1D23449A003FCEE4 /* 6502InterruptTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 6502InterruptTests.swift; sourceTree = ""; }; 4BCA98C21D065CA20062F44C /* 6522.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 6522.hpp; sourceTree = ""; }; - 4BD14B0F1D74627C0088EAD6 /* AcornAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AcornAnalyser.cpp; path = ../../StaticAnalyser/Acorn/AcornAnalyser.cpp; sourceTree = ""; }; - 4BD14B101D74627C0088EAD6 /* AcornAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = AcornAnalyser.hpp; path = ../../StaticAnalyser/Acorn/AcornAnalyser.hpp; sourceTree = ""; }; + 4BD14B0F1D74627C0088EAD6 /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StaticAnalyser.cpp; path = ../../StaticAnalyser/Acorn/StaticAnalyser.cpp; sourceTree = ""; }; + 4BD14B101D74627C0088EAD6 /* StaticAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = StaticAnalyser.hpp; path = ../../StaticAnalyser/Acorn/StaticAnalyser.hpp; sourceTree = ""; }; 4BD328FD1D7E3EB5003B8C44 /* TapeParser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = TapeParser.hpp; path = ../../StaticAnalyser/TapeParser.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 = ""; }; @@ -1529,8 +1529,8 @@ 4BC830D21D6E7C6D0000A26F /* Commodore */ = { isa = PBXGroup; children = ( - 4BC5E4901D7ED365008CF980 /* CommodoreAnalyser.cpp */, - 4BC5E4911D7ED365008CF980 /* CommodoreAnalyser.hpp */, + 4BC5E4901D7ED365008CF980 /* StaticAnalyser.cpp */, + 4BC5E4911D7ED365008CF980 /* StaticAnalyser.hpp */, 4BC830CF1D6E7C690000A26F /* Tape.cpp */, 4BC830D01D6E7C690000A26F /* Tape.hpp */, 4BC5E4931D7EE0E0008CF980 /* Utilities.cpp */, @@ -1574,8 +1574,8 @@ 4BD14B121D7462810088EAD6 /* Acorn */ = { isa = PBXGroup; children = ( - 4BD14B0F1D74627C0088EAD6 /* AcornAnalyser.cpp */, - 4BD14B101D74627C0088EAD6 /* AcornAnalyser.hpp */, + 4BD14B0F1D74627C0088EAD6 /* StaticAnalyser.cpp */, + 4BD14B101D74627C0088EAD6 /* StaticAnalyser.hpp */, 4B96F7201D75119A0058BB2D /* Tape.cpp */, 4B96F7211D75119A0058BB2D /* Tape.hpp */, ); @@ -2043,7 +2043,7 @@ 4BC9DF4F1D04691600F44158 /* 6560.cpp in Sources */, 4BC5E4951D7EE0E0008CF980 /* Utilities.cpp in Sources */, 4BB697CE1D4BA44400248BDF /* CommodoreGCR.cpp in Sources */, - 4BD14B111D74627C0088EAD6 /* AcornAnalyser.cpp in Sources */, + 4BD14B111D74627C0088EAD6 /* StaticAnalyser.cpp in Sources */, 4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */, 4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */, 4B643F3F1D77B88000D431D6 /* DocumentController.swift in Sources */, @@ -2061,7 +2061,7 @@ 4BC76E691C98E31700E6EF73 /* FIRFilter.cpp in Sources */, 4B55CE5F1C3B7D960093A61B /* MachineDocument.swift in Sources */, 4B2A53A11D117D36003C6002 /* CSAtari2600.mm in Sources */, - 4BC5E4921D7ED365008CF980 /* CommodoreAnalyser.cpp in Sources */, + 4BC5E4921D7ED365008CF980 /* StaticAnalyser.cpp in Sources */, 4BC830D11D6E7C690000A26F /* Tape.cpp in Sources */, 4B69FB441C4D941400B5F0AA /* TapeUEF.cpp in Sources */, 4B4DC8211D2C2425003C5BF8 /* Vic20.cpp in Sources */, diff --git a/StaticAnalyser/Acorn/AcornAnalyser.cpp b/StaticAnalyser/Acorn/StaticAnalyser.cpp similarity index 99% rename from StaticAnalyser/Acorn/AcornAnalyser.cpp rename to StaticAnalyser/Acorn/StaticAnalyser.cpp index 0803c39a2..42efa33be 100644 --- a/StaticAnalyser/Acorn/AcornAnalyser.cpp +++ b/StaticAnalyser/Acorn/StaticAnalyser.cpp @@ -6,7 +6,7 @@ // Copyright © 2016 Thomas Harte. All rights reserved. // -#include "AcornAnalyser.hpp" +#include "StaticAnalyser.hpp" #include "Tape.hpp" diff --git a/StaticAnalyser/Acorn/AcornAnalyser.hpp b/StaticAnalyser/Acorn/StaticAnalyser.hpp similarity index 84% rename from StaticAnalyser/Acorn/AcornAnalyser.hpp rename to StaticAnalyser/Acorn/StaticAnalyser.hpp index ccfaa75f3..240abcffa 100644 --- a/StaticAnalyser/Acorn/AcornAnalyser.hpp +++ b/StaticAnalyser/Acorn/StaticAnalyser.hpp @@ -6,8 +6,8 @@ // Copyright © 2016 Thomas Harte. All rights reserved. // -#ifndef AcornAnalyser_hpp -#define AcornAnalyser_hpp +#ifndef StaticAnalyser_Acorn_StaticAnalyser_hpp +#define StaticAnalyser_Acorn_StaticAnalyser_hpp #include "../StaticAnalyser.hpp" diff --git a/StaticAnalyser/Commodore/CommodoreAnalyser.cpp b/StaticAnalyser/Commodore/StaticAnalyser.cpp similarity index 99% rename from StaticAnalyser/Commodore/CommodoreAnalyser.cpp rename to StaticAnalyser/Commodore/StaticAnalyser.cpp index 2c88a6fbf..28e5f1dba 100644 --- a/StaticAnalyser/Commodore/CommodoreAnalyser.cpp +++ b/StaticAnalyser/Commodore/StaticAnalyser.cpp @@ -6,7 +6,7 @@ // Copyright © 2016 Thomas Harte. All rights reserved. // -#include "CommodoreAnalyser.hpp" +#include "StaticAnalyser.hpp" #include "File.hpp" #include "Tape.hpp" diff --git a/StaticAnalyser/Commodore/CommodoreAnalyser.hpp b/StaticAnalyser/Commodore/StaticAnalyser.hpp similarity index 83% rename from StaticAnalyser/Commodore/CommodoreAnalyser.hpp rename to StaticAnalyser/Commodore/StaticAnalyser.hpp index 003f1cc83..e06fb57f3 100644 --- a/StaticAnalyser/Commodore/CommodoreAnalyser.hpp +++ b/StaticAnalyser/Commodore/StaticAnalyser.hpp @@ -6,8 +6,8 @@ // Copyright © 2016 Thomas Harte. All rights reserved. // -#ifndef CommodoreAnalyser_hpp -#define CommodoreAnalyser_hpp +#ifndef StaticAnalyser_Commodore_StaticAnalyser_hpp +#define StaticAnalyser_Commodore_StaticAnalyser_hpp #include "../StaticAnalyser.hpp" diff --git a/StaticAnalyser/StaticAnalyser.cpp b/StaticAnalyser/StaticAnalyser.cpp index 9a56221a4..5e8af04d6 100644 --- a/StaticAnalyser/StaticAnalyser.cpp +++ b/StaticAnalyser/StaticAnalyser.cpp @@ -11,8 +11,8 @@ #include // Analysers -#include "Acorn/AcornAnalyser.hpp" -#include "Commodore/CommodoreAnalyser.hpp" +#include "Acorn/StaticAnalyser.hpp" +#include "Commodore/StaticAnalyser.hpp" // Cartridges #include "../Storage/Cartridge/Formats/BinaryDump.hpp" From ee8510984f314b5424fd38012b76a10c7036a64e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 15 Sep 2016 19:34:45 -0400 Subject: [PATCH 66/66] Added just enough wiring to restore the 2600 to functionality. --- Machines/Atari2600/Atari2600.cpp | 8 ++++-- Machines/Atari2600/Atari2600.hpp | 8 ++++-- Machines/Electron/Electron.hpp | 1 - .../Clock Signal.xcodeproj/project.pbxproj | 14 ++++++++++ .../Documents/Atari2600Document.swift | 4 --- .../Machine/Wrappers/CSAtari2600.h | 1 - .../Machine/Wrappers/CSAtari2600.mm | 6 ---- StaticAnalyser/Atari/StaticAnalyser.cpp | 28 +++++++++++++++++++ StaticAnalyser/Atari/StaticAnalyser.hpp | 27 ++++++++++++++++++ StaticAnalyser/StaticAnalyser.cpp | 2 ++ 10 files changed, 83 insertions(+), 16 deletions(-) create mode 100644 StaticAnalyser/Atari/StaticAnalyser.cpp create mode 100644 StaticAnalyser/Atari/StaticAnalyser.hpp diff --git a/Machines/Atari2600/Atari2600.cpp b/Machines/Atari2600/Atari2600.cpp index 6d76cdadb..8970ad2d1 100644 --- a/Machines/Atari2600/Atari2600.cpp +++ b/Machines/Atari2600/Atari2600.cpp @@ -736,8 +736,12 @@ void Machine::set_switch_is_enabled(Atari2600Switch input, bool state) } } -void Machine::set_rom(size_t length, const uint8_t *data) +void Machine::configure_as_target(const StaticAnalyser::Target &target) { + if(!target.cartridges.front()->get_segments().size()) return; + Storage::Cartridge::Cartridge::Segment segment = target.cartridges.front()->get_segments().front(); + size_t length = segment.data.size(); + _rom_size = 1024; while(_rom_size < length && _rom_size < 32768) _rom_size <<= 1; @@ -750,7 +754,7 @@ void Machine::set_rom(size_t length, const uint8_t *data) while(offset < _rom_size) { size_t copy_length = std::min(copy_step, _rom_size - offset); - memcpy(&_rom[offset], data, copy_length); + memcpy(&_rom[offset], &segment.data[0], copy_length); offset += copy_length; } diff --git a/Machines/Atari2600/Atari2600.hpp b/Machines/Atari2600/Atari2600.hpp index 3b9fa0131..daeab499a 100644 --- a/Machines/Atari2600/Atari2600.hpp +++ b/Machines/Atari2600/Atari2600.hpp @@ -15,6 +15,7 @@ #include "../../Components/6532/6532.hpp" #include "../CRTMachine.hpp" +#include "../ConfigurationTarget.hpp" #include "Atari2600Inputs.h" namespace Atari2600 { @@ -72,13 +73,16 @@ class PIA: public MOS::MOS6532 { }; -class Machine: public CPU6502::Processor, public CRTMachine::Machine { +class Machine: + public CPU6502::Processor, + public CRTMachine::Machine, + public ConfigurationTarget::Machine { public: Machine(); ~Machine(); - void set_rom(size_t length, const uint8_t *data); + void configure_as_target(const StaticAnalyser::Target &target); void switch_region(); void set_digital_input(Atari2600DigitalInput input, bool state); diff --git a/Machines/Electron/Electron.hpp b/Machines/Electron/Electron.hpp index b22efe095..10a6f33b8 100644 --- a/Machines/Electron/Electron.hpp +++ b/Machines/Electron/Electron.hpp @@ -148,7 +148,6 @@ class Machine: void set_rom(ROMSlot slot, size_t length, const uint8_t *data); void configure_as_target(const StaticAnalyser::Target &target); -// void set_tape(std::shared_ptr tape); void set_key_state(Key key, bool isPressed); void clear_all_keys(); diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 3caa89e2d..9cbe1041e 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -51,6 +51,7 @@ 4B92EACA1B7C112B00246143 /* 6502TimingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */; }; 4B96F7221D75119A0058BB2D /* Tape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B96F7201D75119A0058BB2D /* Tape.cpp */; }; 4BA22B071D8817CE0008C640 /* Disk.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BA22B051D8817CE0008C640 /* Disk.cpp */; }; + 4BA799951D8B656E0045123D /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BA799931D8B656E0045123D /* StaticAnalyser.cpp */; }; 4BAB62AD1D3272D200DF5BA0 /* Disk.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BAB62AB1D3272D200DF5BA0 /* Disk.cpp */; }; 4BAB62B51D327F7E00DF5BA0 /* G64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BAB62B31D327F7E00DF5BA0 /* G64.cpp */; }; 4BAB62B81D3302CA00DF5BA0 /* PCMTrack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BAB62B61D3302CA00DF5BA0 /* PCMTrack.cpp */; }; @@ -458,6 +459,8 @@ 4B96F7211D75119A0058BB2D /* Tape.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Tape.hpp; path = ../../StaticAnalyser/Acorn/Tape.hpp; sourceTree = ""; }; 4BA22B051D8817CE0008C640 /* Disk.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Disk.cpp; path = ../../StaticAnalyser/Commodore/Disk.cpp; sourceTree = ""; }; 4BA22B061D8817CE0008C640 /* Disk.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Disk.hpp; path = ../../StaticAnalyser/Commodore/Disk.hpp; sourceTree = ""; }; + 4BA799931D8B656E0045123D /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StaticAnalyser.cpp; path = ../../StaticAnalyser/Atari/StaticAnalyser.cpp; sourceTree = ""; }; + 4BA799941D8B656E0045123D /* StaticAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = StaticAnalyser.hpp; path = ../../StaticAnalyser/Atari/StaticAnalyser.hpp; sourceTree = ""; }; 4BA9C3CF1D8164A9002DDB61 /* ConfigurationTarget.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ConfigurationTarget.hpp; sourceTree = ""; }; 4BAB62AB1D3272D200DF5BA0 /* Disk.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Disk.cpp; sourceTree = ""; }; 4BAB62AC1D3272D200DF5BA0 /* Disk.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Disk.hpp; sourceTree = ""; }; @@ -1074,6 +1077,15 @@ path = Formats; sourceTree = ""; }; + 4BA799961D8B65730045123D /* Atari */ = { + isa = PBXGroup; + children = ( + 4BA799931D8B656E0045123D /* StaticAnalyser.cpp */, + 4BA799941D8B656E0045123D /* StaticAnalyser.hpp */, + ); + name = Atari; + sourceTree = ""; + }; 4BAB62AA1D3272D200DF5BA0 /* Disk */ = { isa = PBXGroup; children = ( @@ -1627,6 +1639,7 @@ 4BF1354B1D6D2C300054B2EA /* StaticAnalyser.hpp */, 4BD328FD1D7E3EB5003B8C44 /* TapeParser.hpp */, 4BD14B121D7462810088EAD6 /* Acorn */, + 4BA799961D8B65730045123D /* Atari */, 4BC830D21D6E7C6D0000A26F /* Commodore */, ); name = StaticAnalyser; @@ -2048,6 +2061,7 @@ 4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */, 4B643F3F1D77B88000D431D6 /* DocumentController.swift in Sources */, 4BB697C71D4B558F00248BDF /* Factors.cpp in Sources */, + 4BA799951D8B656E0045123D /* StaticAnalyser.cpp in Sources */, 4B55CE591C3B7D360093A61B /* ElectronDocument.swift in Sources */, 4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */, 4BC3B74F1CD194CC00F86E85 /* Shader.cpp in Sources */, diff --git a/OSBindings/Mac/Clock Signal/Documents/Atari2600Document.swift b/OSBindings/Mac/Clock Signal/Documents/Atari2600Document.swift index be4448411..ac9cd7170 100644 --- a/OSBindings/Mac/Clock Signal/Documents/Atari2600Document.swift +++ b/OSBindings/Mac/Clock Signal/Documents/Atari2600Document.swift @@ -31,10 +31,6 @@ class Atari2600Document: MachineDocument { return "Atari2600Document" } - override func readFromData(data: NSData, ofType typeName: String) throws { - atari2600.setROM(data) - } - override func windowControllerDidLoadNib(aController: NSWindowController) { super.windowControllerDidLoadNib(aController) diff --git a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSAtari2600.h b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSAtari2600.h index 4e842c19a..3fa47b124 100644 --- a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSAtari2600.h +++ b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSAtari2600.h @@ -11,7 +11,6 @@ @interface CSAtari2600 : CSMachine -- (void)setROM:(nonnull NSData *)rom; - (void)setState:(BOOL)state forDigitalInput:(Atari2600DigitalInput)digitalInput; - (void)setResetLineEnabled:(BOOL)enabled; diff --git a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSAtari2600.mm b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSAtari2600.mm index ceb1c2a15..44a16dee9 100644 --- a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSAtari2600.mm +++ b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSAtari2600.mm @@ -49,12 +49,6 @@ struct CRTDelegate: public Outputs::CRT::Delegate { } } -- (void)setROM:(NSData *)rom { - @synchronized(self) { - _atari2600.set_rom(rom.length, (const uint8_t *)rom.bytes); - } -} - - (void)setState:(BOOL)state forDigitalInput:(Atari2600DigitalInput)digitalInput { @synchronized(self) { _atari2600.set_digital_input(digitalInput, state ? true : false); diff --git a/StaticAnalyser/Atari/StaticAnalyser.cpp b/StaticAnalyser/Atari/StaticAnalyser.cpp new file mode 100644 index 000000000..231b40e6b --- /dev/null +++ b/StaticAnalyser/Atari/StaticAnalyser.cpp @@ -0,0 +1,28 @@ +// +// StaticAnalyser.cpp +// Clock Signal +// +// Created by Thomas Harte on 15/09/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#include "StaticAnalyser.hpp" + +using namespace StaticAnalyser::Atari; + +void StaticAnalyser::Atari::AddTargets( + const std::list> &disks, + const std::list> &tapes, + const std::list> &cartridges, + std::list &destination) +{ + // TODO: any sort of sanity checking at all; at the minute just trust the file type + // approximation already performed. + Target target; + target.machine = Target::Atari2600; + target.probability = 1.0; + target.disks = disks; + target.tapes = tapes; + target.cartridges = cartridges; + destination.push_back(target); +} diff --git a/StaticAnalyser/Atari/StaticAnalyser.hpp b/StaticAnalyser/Atari/StaticAnalyser.hpp new file mode 100644 index 000000000..d6fc23ef1 --- /dev/null +++ b/StaticAnalyser/Atari/StaticAnalyser.hpp @@ -0,0 +1,27 @@ +// +// StaticAnalyser.hpp +// Clock Signal +// +// Created by Thomas Harte on 15/09/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#ifndef StaticAnalyser_Atari_StaticAnalyser_hpp +#define StaticAnalyser_Atari_StaticAnalyser_hpp + +#include "../StaticAnalyser.hpp" + +namespace StaticAnalyser { +namespace Atari { + +void AddTargets( + const std::list> &disks, + const std::list> &tapes, + const std::list> &cartridges, + std::list &destination +); + +} +} + +#endif /* StaticAnalyser_hpp */ diff --git a/StaticAnalyser/StaticAnalyser.cpp b/StaticAnalyser/StaticAnalyser.cpp index 5e8af04d6..2f57788f2 100644 --- a/StaticAnalyser/StaticAnalyser.cpp +++ b/StaticAnalyser/StaticAnalyser.cpp @@ -12,6 +12,7 @@ // Analysers #include "Acorn/StaticAnalyser.hpp" +#include "Atari/StaticAnalyser.hpp" #include "Commodore/StaticAnalyser.hpp" // Cartridges @@ -109,6 +110,7 @@ std::list StaticAnalyser::GetTargets(const char *file_name) // if so, how to load them. (TODO) if(potential_platforms & (TargetPlatformType)TargetPlatform::Acorn) Acorn::AddTargets(disks, tapes, cartridges, targets); if(potential_platforms & (TargetPlatformType)TargetPlatform::Commodore) Commodore::AddTargets(disks, tapes, cartridges, targets); + if(potential_platforms & (TargetPlatformType)TargetPlatform::Atari2600) Atari::AddTargets(disks, tapes, cartridges, targets); free(lowercase_extension); return targets;