From 72bc5f8d7b7477af5bb353acf0b1a4a3162b5251 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 21 Apr 2018 14:33:42 -0700 Subject: [PATCH 01/44] Adds a class to contain the Disk II and begins Apple GCR conversion routines. --- Components/DiskII/DiskII.cpp | 9 ++++ Components/DiskII/DiskII.hpp | 40 ++++++++++++++ .../Clock Signal.xcodeproj/project.pbxproj | 26 ++++++++- .../xcschemes/Clock Signal.xcscheme | 2 +- OSBindings/SDL/SConstruct | 1 + ROMImages/Oric/readme.txt | 1 + Storage/Disk/Encodings/AppleGCR.cpp | 53 +++++++++++++++++++ Storage/Disk/Encodings/AppleGCR.hpp | 53 +++++++++++++++++++ Storage/Disk/Encodings/CommodoreGCR.hpp | 2 +- 9 files changed, 184 insertions(+), 3 deletions(-) create mode 100644 Components/DiskII/DiskII.cpp create mode 100644 Components/DiskII/DiskII.hpp create mode 100644 Storage/Disk/Encodings/AppleGCR.cpp create mode 100644 Storage/Disk/Encodings/AppleGCR.hpp diff --git a/Components/DiskII/DiskII.cpp b/Components/DiskII/DiskII.cpp new file mode 100644 index 000000000..0c92ad670 --- /dev/null +++ b/Components/DiskII/DiskII.cpp @@ -0,0 +1,9 @@ +// +// DiskII.cpp +// Clock Signal +// +// Created by Thomas Harte on 20/04/2018. +// Copyright © 2018 Thomas Harte. All rights reserved. +// + +#include "DiskII.hpp" diff --git a/Components/DiskII/DiskII.hpp b/Components/DiskII/DiskII.hpp new file mode 100644 index 000000000..49006bf3e --- /dev/null +++ b/Components/DiskII/DiskII.hpp @@ -0,0 +1,40 @@ +// +// DiskII.hpp +// Clock Signal +// +// Created by Thomas Harte on 20/04/2018. +// Copyright © 2018 Thomas Harte. All rights reserved. +// + +#ifndef DiskII_hpp +#define DiskII_hpp + +#include "../../ClockReceiver/ClockReceiver.hpp" +#include + +namespace Apple { + +/*! + Provides an emulation of the Apple Disk II. +*/ +class DiskII { + public: + enum class Control { + P0, P1, P2, P3, + Motor, + }; + enum class Mode { + Read, Write + }; + void set_control(Control control, bool on); + void set_mode(Mode mode); + void select_drive(int drive); + void set_shift_register(uint8_t value); + uint8_t get_shift_register(); + + void run_for(const Cycles cycles); +}; + +} + +#endif /* DiskII_hpp */ diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 432e5621c..18ab9f86b 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -157,6 +157,8 @@ 4B2C45421E3C3896002A2389 /* cartridge.png in Resources */ = {isa = PBXBuildFile; fileRef = 4B2C45411E3C3896002A2389 /* cartridge.png */; }; 4B2E2D9A1C3A06EC00138695 /* Atari2600.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2E2D971C3A06EC00138695 /* Atari2600.cpp */; }; 4B2E2D9D1C3A070400138695 /* Electron.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2E2D9B1C3A070400138695 /* Electron.cpp */; }; + 4B302184208A550100773308 /* DiskII.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B302183208A550100773308 /* DiskII.cpp */; }; + 4B302185208A550100773308 /* DiskII.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B302183208A550100773308 /* DiskII.cpp */; }; 4B30512D1D989E2200B4FED8 /* Drive.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B30512B1D989E2200B4FED8 /* Drive.cpp */; }; 4B3051301D98ACC600B4FED8 /* Plus3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B30512E1D98ACC600B4FED8 /* Plus3.cpp */; }; 4B322E011F5A2990004EB04C /* Z80AllRAM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B322DFD1F5A2981004EB04C /* Z80AllRAM.cpp */; }; @@ -579,6 +581,8 @@ 4BB299F81B587D8400A49093 /* txsn in Resources */ = {isa = PBXBuildFile; fileRef = 4BB298EC1B587D8400A49093 /* txsn */; }; 4BB299F91B587D8400A49093 /* tyan in Resources */ = {isa = PBXBuildFile; fileRef = 4BB298ED1B587D8400A49093 /* tyan */; }; 4BB2A9AF1E13367E001A5C23 /* CRCTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BB2A9AE1E13367E001A5C23 /* CRCTests.mm */; }; + 4BB2CB2A208BDDCF00FD192E /* AppleGCR.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BB2CB28208BDDCF00FD192E /* AppleGCR.cpp */; }; + 4BB2CB2B208BDDCF00FD192E /* AppleGCR.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BB2CB28208BDDCF00FD192E /* AppleGCR.cpp */; }; 4BB697CB1D4B6D3E00248BDF /* TimedEventLoop.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BB697C91D4B6D3E00248BDF /* TimedEventLoop.cpp */; }; 4BB697CE1D4BA44400248BDF /* CommodoreGCR.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BB697CC1D4BA44400248BDF /* CommodoreGCR.cpp */; }; 4BB73EA21B587A5100552FC2 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB73EA11B587A5100552FC2 /* AppDelegate.swift */; }; @@ -754,6 +758,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 = ""; }; + 4B302182208A550100773308 /* DiskII.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = DiskII.hpp; sourceTree = ""; }; + 4B302183208A550100773308 /* DiskII.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DiskII.cpp; sourceTree = ""; }; 4B30512B1D989E2200B4FED8 /* Drive.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Drive.cpp; sourceTree = ""; }; 4B30512C1D989E2200B4FED8 /* Drive.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Drive.hpp; sourceTree = ""; }; 4B30512E1D98ACC600B4FED8 /* Plus3.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Plus3.cpp; path = Electron/Plus3.cpp; sourceTree = ""; }; @@ -1275,6 +1281,8 @@ 4BB298EC1B587D8400A49093 /* txsn */ = {isa = PBXFileReference; lastKnownFileType = file; path = txsn; sourceTree = ""; }; 4BB298ED1B587D8400A49093 /* tyan */ = {isa = PBXFileReference; lastKnownFileType = file; path = tyan; sourceTree = ""; }; 4BB2A9AE1E13367E001A5C23 /* CRCTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CRCTests.mm; sourceTree = ""; }; + 4BB2CB28208BDDCF00FD192E /* AppleGCR.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = AppleGCR.cpp; path = Encodings/AppleGCR.cpp; sourceTree = ""; }; + 4BB2CB29208BDDCF00FD192E /* AppleGCR.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = AppleGCR.hpp; path = Encodings/AppleGCR.hpp; sourceTree = ""; }; 4BB697C61D4B558F00248BDF /* Factors.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Factors.hpp; path = ../../NumberTheory/Factors.hpp; sourceTree = ""; }; 4BB697C91D4B6D3E00248BDF /* TimedEventLoop.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TimedEventLoop.cpp; sourceTree = ""; }; 4BB697CA1D4B6D3E00248BDF /* TimedEventLoop.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TimedEventLoop.hpp; sourceTree = ""; }; @@ -1674,6 +1682,15 @@ name = Electron; sourceTree = ""; }; + 4B302181208A550100773308 /* DiskII */ = { + isa = PBXGroup; + children = ( + 4B302182208A550100773308 /* DiskII.hpp */, + 4B302183208A550100773308 /* DiskII.cpp */, + ); + path = DiskII; + sourceTree = ""; + }; 4B31B88E1FBFBCD800C140D5 /* Configurable */ = { isa = PBXGroup; children = ( @@ -2630,9 +2647,11 @@ 4BB697CF1D4BA44900248BDF /* Encodings */ = { isa = PBXGroup; children = ( - 4B7136831F78724F008B8ED9 /* MFM */, + 4BB2CB28208BDDCF00FD192E /* AppleGCR.cpp */, 4BB697CC1D4BA44400248BDF /* CommodoreGCR.cpp */, + 4BB2CB29208BDDCF00FD192E /* AppleGCR.hpp */, 4BB697CD1D4BA44400248BDF /* CommodoreGCR.hpp */, + 4B7136831F78724F008B8ED9 /* MFM */, ); name = Encodings; sourceTree = ""; @@ -2850,6 +2869,7 @@ 4BC9DF4A1D04691600F44158 /* Components */ = { isa = PBXGroup; children = ( + 4B302181208A550100773308 /* DiskII */, 4B595FAA2086DFBA0083CAA8 /* AudioToggle */, 4BD468F81D8DF4290084958B /* 1770 */, 4BC9DF4B1D04691600F44158 /* 6522 */, @@ -3598,6 +3618,7 @@ 4B894525201967B4007DE474 /* Tape.cpp in Sources */, 4B055ACD1FAE9B030060FFFF /* Keyboard.cpp in Sources */, 4B055AB21FAE860F0060FFFF /* CommodoreTAP.cpp in Sources */, + 4BB2CB2B208BDDCF00FD192E /* AppleGCR.cpp in Sources */, 4B055ADF1FAE9B4C0060FFFF /* IRQDelegatePortHandler.cpp in Sources */, 4B055AB51FAE860F0060FFFF /* TapePRG.cpp in Sources */, 4B055AE01FAE9B660060FFFF /* CRT.cpp in Sources */, @@ -3623,6 +3644,7 @@ 4BB0A65C2044FD3000FB3688 /* SN76489.cpp in Sources */, 4B595FAE2086DFBA0083CAA8 /* AudioToggle.cpp in Sources */, 4B055AB91FAE86170060FFFF /* Acorn.cpp in Sources */, + 4B302185208A550100773308 /* DiskII.cpp in Sources */, 4B055A931FAE85B50060FFFF /* BinaryDump.cpp in Sources */, 4B89452D201967B4007DE474 /* Tape.cpp in Sources */, 4B055AD61FAE9B130060FFFF /* MemoryFuzzer.cpp in Sources */, @@ -3654,6 +3676,7 @@ 4B4518A01F75FD1C00926311 /* CPCDSK.cpp in Sources */, 4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */, 4B322E041F5A2E3C004EB04C /* Z80Base.cpp in Sources */, + 4BB2CB2A208BDDCF00FD192E /* AppleGCR.cpp in Sources */, 4B894530201967B4007DE474 /* StaticAnalyser.cpp in Sources */, 4B4518A31F75FD1C00926311 /* HFE.cpp in Sources */, 4B1B88BB202E2EC100B67DFF /* MultiKeyboardMachine.cpp in Sources */, @@ -3707,6 +3730,7 @@ 4B448E811F1C45A00009ABD6 /* TZX.cpp in Sources */, 4BEBFB512002DB30000708CC /* DiskROM.cpp in Sources */, 4B89451C201967B4007DE474 /* Disk.cpp in Sources */, + 4B302184208A550100773308 /* DiskII.cpp in Sources */, 4BEA52631DF339D7007E74F2 /* SoundGenerator.cpp in Sources */, 4BAE495920328897004BE78E /* ZX8081OptionsPanel.swift in Sources */, 4B89451A201967B4007DE474 /* ConfidenceSummary.cpp in Sources */, diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme index f9049689f..782b17c09 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme +++ b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme @@ -68,7 +68,7 @@ (five_and_three_encoding_for_value( source[0] >> 3 )); + destination[1] = static_cast(five_and_three_encoding_for_value( (source[0] << 2) | (source[1] >> 6) )); + destination[2] = static_cast(five_and_three_encoding_for_value( source[1] >> 1 )); + destination[3] = static_cast(five_and_three_encoding_for_value( (source[1] << 4) | (source[2] >> 4) )); + destination[4] = static_cast(five_and_three_encoding_for_value( (source[2] << 1) | (source[3] >> 7) )); + destination[5] = static_cast(five_and_three_encoding_for_value( source[3] >> 2 )); + destination[6] = static_cast(five_and_three_encoding_for_value( (source[3] << 3) | (source[4] >> 5) )); + destination[7] = static_cast(five_and_three_encoding_for_value( source[4] )); +} + +unsigned int AppleGCR::six_and_two_encoding_for_value(int value) { + static const unsigned int values[] = { + 0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6, + 0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3, + 0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc, + 0xbd, 0xbe, 0xbf, 0xcb, 0xcd, 0xce, 0xcf, 0xd3, + 0xd6, 0xd7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, + 0xdf, 0xe5, 0xe6, 0xe7, 0xe9, 0xea, 0xeb, 0xec, + 0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, + 0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff + }; + return values[value & 0x3f]; +} + +void AppleGCR::encode_six_and_two_block(uint8_t *destination, uint8_t *source) { + destination[0] = static_cast(six_and_two_encoding_for_value( source[0] >> 2 )); + destination[1] = static_cast(six_and_two_encoding_for_value( (source[0] << 4) | (source[1] >> 4) )); + destination[2] = static_cast(six_and_two_encoding_for_value( (source[1] << 2) | (source[2] >> 6) )); + destination[3] = static_cast(six_and_two_encoding_for_value( source[2] )); +} diff --git a/Storage/Disk/Encodings/AppleGCR.hpp b/Storage/Disk/Encodings/AppleGCR.hpp new file mode 100644 index 000000000..f152bb4b2 --- /dev/null +++ b/Storage/Disk/Encodings/AppleGCR.hpp @@ -0,0 +1,53 @@ +// +// AppleGCR.hpp +// Clock Signal +// +// Created by Thomas Harte on 21/04/2018. +// Copyright © 2018 Thomas Harte. All rights reserved. +// + +#ifndef AppleGCR_hpp +#define AppleGCR_hpp + +#include + +namespace Storage { +namespace Encodings { + +namespace AppleGCR { + + /*! + @returns the eight-bit 13-sector GCR encoding for the low five bits of @c value. + */ + unsigned int five_and_three_encoding_for_value(int value); + + /*! + @returns the eight-bit 16-sector GCR encoding for the low six bits of @c value. + */ + unsigned int six_and_two_encoding_for_value(int value); + + /*! + A block is defined to be five source bytes, which encodes to eight GCR bytes. + */ + void encode_five_and_three_block(uint8_t *destination, uint8_t *source); + + /*! + A block is defined to be three source bytes, which encodes to four GCR bytes. + */ + void encode_six_and_two_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 composed by splitting the dectet into two qintets, decoding each and composing the resulting nibbles. + */ +// unsigned int decoding_from_dectet(unsigned int dectet); +} + +} +} + +#endif /* AppleGCR_hpp */ diff --git a/Storage/Disk/Encodings/CommodoreGCR.hpp b/Storage/Disk/Encodings/CommodoreGCR.hpp index 467b68691..fe78d9391 100644 --- a/Storage/Disk/Encodings/CommodoreGCR.hpp +++ b/Storage/Disk/Encodings/CommodoreGCR.hpp @@ -43,7 +43,7 @@ namespace CommodoreGCR { unsigned int decoding_from_quintet(unsigned int quintet); /*! - @returns the byte composted of the low five bit five-bit GCR + @returns the byte composed by splitting the dectet into two qintets, decoding each and composing the resulting nibbles. */ unsigned int decoding_from_dectet(unsigned int dectet); } From 6592745e53f065c8dee82a20c5f7dd2c4dfd34e1 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 21 Apr 2018 21:21:57 -0700 Subject: [PATCH 02/44] Adds the bare minimum to respond to attempts to open NIB files with an Apple II. --- Analyser/Static/AppleII/StaticAnalyser.cpp | 9 +++- Analyser/Static/AppleII/Target.hpp | 26 +++++++++++ Analyser/Static/StaticAnalyser.cpp | 2 + Machines/AppleII/AppleII.cpp | 10 ++++ .../Clock Signal.xcodeproj/project.pbxproj | 10 ++++ OSBindings/Mac/Clock Signal/Info.plist | 21 +++++++++ Storage/Disk/DiskImage/Formats/DMK.hpp | 2 +- Storage/Disk/DiskImage/Formats/MSXDSK.hpp | 2 +- Storage/Disk/DiskImage/Formats/NIB.cpp | 44 ++++++++++++++++++ Storage/Disk/DiskImage/Formats/NIB.hpp | 46 +++++++++++++++++++ 10 files changed, 169 insertions(+), 3 deletions(-) create mode 100644 Analyser/Static/AppleII/Target.hpp create mode 100644 Storage/Disk/DiskImage/Formats/NIB.cpp create mode 100644 Storage/Disk/DiskImage/Formats/NIB.hpp diff --git a/Analyser/Static/AppleII/StaticAnalyser.cpp b/Analyser/Static/AppleII/StaticAnalyser.cpp index c2996bbee..c12292336 100644 --- a/Analyser/Static/AppleII/StaticAnalyser.cpp +++ b/Analyser/Static/AppleII/StaticAnalyser.cpp @@ -7,7 +7,14 @@ // #include "StaticAnalyser.hpp" +#include "Target.hpp" Analyser::Static::TargetList Analyser::Static::AppleII::GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms) { - return TargetList(); + auto target = std::unique_ptr(new Target); + target->machine = Machine::AppleII; + target->media = media; + + TargetList targets; + targets.push_back(std::move(target)); + return targets; } diff --git a/Analyser/Static/AppleII/Target.hpp b/Analyser/Static/AppleII/Target.hpp new file mode 100644 index 000000000..0f47d3a62 --- /dev/null +++ b/Analyser/Static/AppleII/Target.hpp @@ -0,0 +1,26 @@ +// +// Target.hpp +// Clock Signal +// +// Created by Thomas Harte on 21/04/2018. +// Copyright © 2018 Thomas Harte. All rights reserved. +// + +#ifndef Target_h +#define Target_h + +#include "../StaticAnalyser.hpp" + +namespace Analyser { +namespace Static { +namespace AppleII { + +struct Target: public ::Analyser::Static::Target { + // TODO: probably some Disk II options here? +}; + +} +} +} + +#endif /* Target_h */ diff --git a/Analyser/Static/StaticAnalyser.cpp b/Analyser/Static/StaticAnalyser.cpp index c4ae7a7be..2ca13cac9 100644 --- a/Analyser/Static/StaticAnalyser.cpp +++ b/Analyser/Static/StaticAnalyser.cpp @@ -36,6 +36,7 @@ #include "../../Storage/Disk/DiskImage/Formats/DMK.hpp" #include "../../Storage/Disk/DiskImage/Formats/HFE.hpp" #include "../../Storage/Disk/DiskImage/Formats/MSXDSK.hpp" +#include "../../Storage/Disk/DiskImage/Formats/NIB.hpp" #include "../../Storage/Disk/DiskImage/Formats/OricMFMDSK.hpp" #include "../../Storage/Disk/DiskImage/Formats/SSD.hpp" @@ -101,6 +102,7 @@ static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform:: Disk::DiskImageHolder, TargetPlatform::Acorn | TargetPlatform::AmstradCPC | TargetPlatform::Commodore | TargetPlatform::Oric) // HFE (TODO: switch to AllDisk once the MSX stops being so greedy) + Format("nib", result.disks, Disk::DiskImageHolder, TargetPlatform::AppleII) // NIB Format("o", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // O Format("p", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // P Format("p81", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // P81 diff --git a/Machines/AppleII/AppleII.cpp b/Machines/AppleII/AppleII.cpp index fda5dd81c..f66fea105 100644 --- a/Machines/AppleII/AppleII.cpp +++ b/Machines/AppleII/AppleII.cpp @@ -8,6 +8,7 @@ #include "AppleII.hpp" +#include "../ConfigurationTarget.hpp" #include "../CRTMachine.hpp" #include "../KeyboardMachine.hpp" #include "../Utility/MemoryFuzzer.hpp" @@ -25,6 +26,7 @@ namespace { class ConcreteMachine: public CRTMachine::Machine, + public ConfigurationTarget::Machine, public KeyboardMachine::Machine, public CPU::MOS6502::BusHandler, public Inputs::Keyboard, @@ -218,6 +220,14 @@ class ConcreteMachine: Inputs::Keyboard &get_keyboard() override { return *this; } + + // MARK: ConfigurationTarget + void configure_as_target(const Analyser::Static::Target *target) override { + } + + bool insert_media(const Analyser::Static::Media &media) override { + return true; + } }; } diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 18ab9f86b..2905d456e 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -120,6 +120,8 @@ 4B0E04FA1FC9FA3100F43484 /* 9918.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0E04F91FC9FA3100F43484 /* 9918.cpp */; }; 4B0E04FB1FC9FA3100F43484 /* 9918.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0E04F91FC9FA3100F43484 /* 9918.cpp */; }; 4B0E61071FF34737002A9DBD /* MSX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0E61051FF34737002A9DBD /* MSX.cpp */; }; + 4B0F94FE208C1A1600FE41D9 /* NIB.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0F94FC208C1A1600FE41D9 /* NIB.cpp */; }; + 4B0F94FF208C1A1600FE41D9 /* NIB.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0F94FC208C1A1600FE41D9 /* NIB.cpp */; }; 4B121F951E05E66800BFDA12 /* PCMPatchedTrackTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B121F941E05E66800BFDA12 /* PCMPatchedTrackTests.mm */; }; 4B121F9B1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B121F9A1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm */; }; 4B12C0ED1FCFA98D005BFD93 /* Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B12C0EB1FCFA98D005BFD93 /* Keyboard.cpp */; }; @@ -697,6 +699,9 @@ 4B0E04F91FC9FA3100F43484 /* 9918.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = 9918.cpp; path = 9918/9918.cpp; sourceTree = ""; }; 4B0E61051FF34737002A9DBD /* MSX.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = MSX.cpp; path = Parsers/MSX.cpp; sourceTree = ""; }; 4B0E61061FF34737002A9DBD /* MSX.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = MSX.hpp; path = Parsers/MSX.hpp; sourceTree = ""; }; + 4B0F94FC208C1A1600FE41D9 /* NIB.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = NIB.cpp; sourceTree = ""; }; + 4B0F94FD208C1A1600FE41D9 /* NIB.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = NIB.hpp; sourceTree = ""; }; + 4B0F9500208C42A300FE41D9 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Target.hpp; path = AppleII/Target.hpp; sourceTree = ""; }; 4B121F941E05E66800BFDA12 /* PCMPatchedTrackTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PCMPatchedTrackTests.mm; sourceTree = ""; }; 4B121F9A1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PCMSegmentEventSourceTests.mm; sourceTree = ""; }; 4B12C0EB1FCFA98D005BFD93 /* Keyboard.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Keyboard.cpp; path = MSX/Keyboard.cpp; sourceTree = ""; }; @@ -1534,6 +1539,7 @@ children = ( 4B15A9FA208249BB005E6C8D /* StaticAnalyser.cpp */, 4B15A9FB208249BB005E6C8D /* StaticAnalyser.hpp */, + 4B0F9500208C42A300FE41D9 /* Target.hpp */, ); name = AppleII; sourceTree = ""; @@ -1858,6 +1864,7 @@ 4B4518951F75FD1B00926311 /* HFE.cpp */, 4B58601C1F806AB200AEE2E3 /* MFMSectorDump.cpp */, 4BEBFB4B2002C4BF000708CC /* MSXDSK.cpp */, + 4B0F94FC208C1A1600FE41D9 /* NIB.cpp */, 4B4518971F75FD1B00926311 /* OricMFMDSK.cpp */, 4B4518991F75FD1B00926311 /* SSD.cpp */, 4B45188E1F75FD1B00926311 /* AcornADF.hpp */, @@ -1868,6 +1875,7 @@ 4B4518961F75FD1B00926311 /* HFE.hpp */, 4B58601D1F806AB200AEE2E3 /* MFMSectorDump.hpp */, 4BEBFB4C2002C4BF000708CC /* MSXDSK.hpp */, + 4B0F94FD208C1A1600FE41D9 /* NIB.hpp */, 4B4518981F75FD1B00926311 /* OricMFMDSK.hpp */, 4B45189A1F75FD1B00926311 /* SSD.hpp */, 4BFDD7891F7F2DB4008579B9 /* Utility */, @@ -3638,6 +3646,7 @@ 4B055AE71FAE9B6F0060FFFF /* Shader.cpp in Sources */, 4B894523201967B4007DE474 /* StaticAnalyser.cpp in Sources */, 4B055AEC1FAE9BA20060FFFF /* Z80Base.cpp in Sources */, + 4B0F94FF208C1A1600FE41D9 /* NIB.cpp in Sources */, 4B0E04EB1FC9E78800F43484 /* CAS.cpp in Sources */, 4B055AE31FAE9B6F0060FFFF /* TextureBuilder.cpp in Sources */, 4BB0A65D2045009000FB3688 /* ColecoVision.cpp in Sources */, @@ -3740,6 +3749,7 @@ 4B4518851F75E91A00926311 /* DiskController.cpp in Sources */, 4B8334841F5DA0360097E338 /* Z80Storage.cpp in Sources */, 4BA61EB01D91515900B3C876 /* NSData+StdVector.mm in Sources */, + 4B0F94FE208C1A1600FE41D9 /* NIB.cpp in Sources */, 4B89452A201967B4007DE474 /* File.cpp in Sources */, 4B4DC8211D2C2425003C5BF8 /* Vic20.cpp in Sources */, 4B71368E1F788112008B8ED9 /* Parser.cpp in Sources */, diff --git a/OSBindings/Mac/Clock Signal/Info.plist b/OSBindings/Mac/Clock Signal/Info.plist index 12a8aee31..d3a56a987 100644 --- a/OSBindings/Mac/Clock Signal/Info.plist +++ b/OSBindings/Mac/Clock Signal/Info.plist @@ -393,6 +393,27 @@ NSDocumentClass $(PRODUCT_MODULE_NAME).MachineDocument + + CFBundleTypeExtensions + + nib + + CFBundleTypeIconFile + floppy525 + CFBundleTypeName + Apple II Disk Image + CFBundleTypeRole + Viewer + LSItemContentTypes + + public.item + nl.xs4all.gp.virtualii.nibdisk + + LSTypeIsPackage + 0 + NSDocumentClass + $(PRODUCT_MODULE_NAME).MachineDocument + CFBundleExecutable $(EXECUTABLE_NAME) diff --git a/Storage/Disk/DiskImage/Formats/DMK.hpp b/Storage/Disk/DiskImage/Formats/DMK.hpp index e0b616829..6be4eaed4 100644 --- a/Storage/Disk/DiskImage/Formats/DMK.hpp +++ b/Storage/Disk/DiskImage/Formats/DMK.hpp @@ -18,7 +18,7 @@ namespace Storage { namespace Disk { /*! - Provides a @c Disk containing a DMK disk image — mostly a decoded byte stream, but with + Provides a @c DiskImage containing a DMK disk image — mostly a decoded byte stream, but with a record of IDAM locations. */ class DMK: public DiskImage { diff --git a/Storage/Disk/DiskImage/Formats/MSXDSK.hpp b/Storage/Disk/DiskImage/Formats/MSXDSK.hpp index 0b6fba53c..8bf8936c1 100644 --- a/Storage/Disk/DiskImage/Formats/MSXDSK.hpp +++ b/Storage/Disk/DiskImage/Formats/MSXDSK.hpp @@ -17,7 +17,7 @@ namespace Storage { namespace Disk { /*! - Provides a @c Disk containing an MSX-style disk image: + Provides a @c DiskImage descriging an MSX-style disk image: a sector dump of appropriate proportions. */ class MSXDSK: public MFMSectorDump { diff --git a/Storage/Disk/DiskImage/Formats/NIB.cpp b/Storage/Disk/DiskImage/Formats/NIB.cpp new file mode 100644 index 000000000..015816cfb --- /dev/null +++ b/Storage/Disk/DiskImage/Formats/NIB.cpp @@ -0,0 +1,44 @@ +// +// NIB.cpp +// Clock Signal +// +// Created by Thomas Harte on 21/04/2018. +// Copyright © 2018 Thomas Harte. All rights reserved. +// + +#include "NIB.hpp" + +using namespace Storage::Disk; + +namespace { + +const std::size_t track_length = 6656; +const std::size_t number_of_tracks = 35; + +} + +NIB::NIB(const std::string &file_name) : + file_(file_name) { + // A NIB should be 35 tracks, each 6656 bytes long. + if(file_.stats().st_size != track_length*number_of_tracks) { + throw ErrorNotNIB; + } + + // TODO: all other validation. +} + +int NIB::get_head_position_count() { + return 35; +} + +int NIB::get_head_count() { + return 1; +} + +bool NIB::get_is_read_only() { + return true; +} + +std::shared_ptr<::Storage::Disk::Track> NIB::get_track_at_position(::Storage::Disk::Track::Address address) { + return nullptr; +} diff --git a/Storage/Disk/DiskImage/Formats/NIB.hpp b/Storage/Disk/DiskImage/Formats/NIB.hpp new file mode 100644 index 000000000..b1113f7e0 --- /dev/null +++ b/Storage/Disk/DiskImage/Formats/NIB.hpp @@ -0,0 +1,46 @@ +// +// NIB.hpp +// Clock Signal +// +// Created by Thomas Harte on 21/04/2018. +// Copyright © 2018 Thomas Harte. All rights reserved. +// + +#ifndef NIB_hpp +#define NIB_hpp + +#include "../DiskImage.hpp" +#include "../../../FileHolder.hpp" + +namespace Storage { +namespace Disk { + +/*! + Provides a @c DiskImage describing an Apple NIB disk image: + mostly a bit stream capture, but syncs are implicitly packed + into 8 bits instead of 9. +*/ +class NIB: public DiskImage { + public: + NIB(const std::string &file_name); + + enum { + ErrorNotNIB, + }; + + int get_head_position_count() override; + int get_head_count() override; + bool get_is_read_only() override; + + std::shared_ptr<::Storage::Disk::Track> get_track_at_position(::Storage::Disk::Track::Address address) override; + + private: + FileHolder file_; + long get_file_offset_for_position(Track::Address address); + +}; + +} +} + +#endif /* NIB_hpp */ From d447e81abde72f782f3c66aea4d76144c068910f Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 23 Apr 2018 19:57:45 -0700 Subject: [PATCH 03/44] Adds provisional support for WOZ files. --- Analyser/Static/StaticAnalyser.cpp | 2 + .../Clock Signal.xcodeproj/project.pbxproj | 8 ++ .../xcschemes/Clock Signal.xcscheme | 2 +- OSBindings/Mac/Clock Signal/Info.plist | 1 + Storage/Disk/DiskImage/Formats/WOZ.cpp | 100 ++++++++++++++++++ Storage/Disk/DiskImage/Formats/WOZ.hpp | 46 ++++++++ 6 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 Storage/Disk/DiskImage/Formats/WOZ.cpp create mode 100644 Storage/Disk/DiskImage/Formats/WOZ.hpp diff --git a/Analyser/Static/StaticAnalyser.cpp b/Analyser/Static/StaticAnalyser.cpp index 2ca13cac9..c929125c0 100644 --- a/Analyser/Static/StaticAnalyser.cpp +++ b/Analyser/Static/StaticAnalyser.cpp @@ -39,6 +39,7 @@ #include "../../Storage/Disk/DiskImage/Formats/NIB.hpp" #include "../../Storage/Disk/DiskImage/Formats/OricMFMDSK.hpp" #include "../../Storage/Disk/DiskImage/Formats/SSD.hpp" +#include "../../Storage/Disk/DiskImage/Formats/WOZ.hpp" // Tapes #include "../../Storage/Tape/Formats/CAS.hpp" @@ -129,6 +130,7 @@ static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform:: Format("tsx", result.tapes, Tape::TZX, TargetPlatform::MSX) // TSX Format("tzx", result.tapes, Tape::TZX, TargetPlatform::ZX8081) // TZX Format("uef", result.tapes, Tape::UEF, TargetPlatform::Acorn) // UEF (tape) + Format("woz", result.disks, Disk::DiskImageHolder, TargetPlatform::AppleII) // WOZ #undef Format #undef Insert diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 2905d456e..bafaf0bc9 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -222,6 +222,8 @@ 4B69FB441C4D941400B5F0AA /* TapeUEF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B69FB421C4D941400B5F0AA /* TapeUEF.cpp */; }; 4B69FB461C4D950F00B5F0AA /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B69FB451C4D950F00B5F0AA /* libz.tbd */; }; 4B6A4C991F58F09E00E3F787 /* 6502Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B6A4C951F58F09E00E3F787 /* 6502Base.cpp */; }; + 4B6ED2F0208E2F8A0047B343 /* WOZ.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B6ED2EE208E2F8A0047B343 /* WOZ.cpp */; }; + 4B6ED2F1208E2F8A0047B343 /* WOZ.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B6ED2EE208E2F8A0047B343 /* WOZ.cpp */; }; 4B7136861F78724F008B8ED9 /* Encoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7136841F78724F008B8ED9 /* Encoder.cpp */; }; 4B7136891F78725F008B8ED9 /* Shifter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7136871F78725F008B8ED9 /* Shifter.cpp */; }; 4B71368E1F788112008B8ED9 /* Parser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B71368C1F788112008B8ED9 /* Parser.cpp */; }; @@ -889,6 +891,8 @@ 4B6A4C911F58F09E00E3F787 /* 6502AllRAM.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = 6502AllRAM.cpp; sourceTree = ""; }; 4B6A4C921F58F09E00E3F787 /* 6502AllRAM.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 6502AllRAM.hpp; sourceTree = ""; }; 4B6A4C951F58F09E00E3F787 /* 6502Base.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = 6502Base.cpp; sourceTree = ""; }; + 4B6ED2EE208E2F8A0047B343 /* WOZ.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = WOZ.cpp; sourceTree = ""; }; + 4B6ED2EF208E2F8A0047B343 /* WOZ.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = WOZ.hpp; sourceTree = ""; }; 4B7041271F92C26900735E45 /* JoystickMachine.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = JoystickMachine.hpp; sourceTree = ""; }; 4B70412A1F92C2A700735E45 /* Joystick.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Joystick.hpp; sourceTree = ""; }; 4B70EF6A1FFDCDF400A3494E /* ROMSlotHandler.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = ROMSlotHandler.hpp; path = MSX/ROMSlotHandler.hpp; sourceTree = ""; }; @@ -1879,6 +1883,8 @@ 4B4518981F75FD1B00926311 /* OricMFMDSK.hpp */, 4B45189A1F75FD1B00926311 /* SSD.hpp */, 4BFDD7891F7F2DB4008579B9 /* Utility */, + 4B6ED2EE208E2F8A0047B343 /* WOZ.cpp */, + 4B6ED2EF208E2F8A0047B343 /* WOZ.hpp */, ); path = Formats; sourceTree = ""; @@ -3536,6 +3542,7 @@ 4BB0A65E204500A900FB3688 /* StaticAnalyser.cpp in Sources */, 4B055AC11FAE98DC0060FFFF /* MachineForTarget.cpp in Sources */, 4BBB70A9202014E2002FE009 /* MultiCRTMachine.cpp in Sources */, + 4B6ED2F1208E2F8A0047B343 /* WOZ.cpp in Sources */, 4B055AD81FAE9B180060FFFF /* Video.cpp in Sources */, 4B89452F201967B4007DE474 /* StaticAnalyser.cpp in Sources */, 4B894531201967B4007DE474 /* StaticAnalyser.cpp in Sources */, @@ -3808,6 +3815,7 @@ 4B894526201967B4007DE474 /* StaticAnalyser.cpp in Sources */, 4BEE0A6F1D72496600532C7B /* Cartridge.cpp in Sources */, 4B8805FB1DCFF807003085B1 /* Oric.cpp in Sources */, + 4B6ED2F0208E2F8A0047B343 /* WOZ.cpp in Sources */, 4BFE7B871FC39BF100160B38 /* StandardOptions.cpp in Sources */, 4B15A9FC208249BB005E6C8D /* StaticAnalyser.cpp in Sources */, 4B5FADC01DE3BF2B00AEC565 /* Microdisc.cpp in Sources */, diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme index 782b17c09..f9049689f 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme +++ b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme @@ -68,7 +68,7 @@ CFBundleTypeExtensions nib + woz CFBundleTypeIconFile floppy525 diff --git a/Storage/Disk/DiskImage/Formats/WOZ.cpp b/Storage/Disk/DiskImage/Formats/WOZ.cpp new file mode 100644 index 000000000..1d76b5f6c --- /dev/null +++ b/Storage/Disk/DiskImage/Formats/WOZ.cpp @@ -0,0 +1,100 @@ +// +// WOZ.cpp +// Clock Signal +// +// Created by Thomas Harte on 23/04/2018. +// Copyright © 2018 Thomas Harte. All rights reserved. +// + +#include "WOZ.hpp" + +#include "../../Track/PCMTrack.hpp" + +using namespace Storage::Disk; + +WOZ::WOZ(const std::string &file_name) : + file_(file_name) { + + const char signature[8] = { + 'W', 'O', 'Z', '1', + static_cast(0xff), 0x0a, 0x0d, 0x0a + }; + if(!file_.check_signature(signature, 8)) throw ErrorNotWOZ; + + // TODO: check CRC32, instead of skipping it. + file_.seek(4, SEEK_CUR); + + // Parse all chunks up front. + while(true) { + const uint32_t chunk_id = file_.get32le(); + const uint32_t chunk_size = file_.get32le(); + if(file_.eof()) break; + + long end_of_chunk = file_.tell() + static_cast(chunk_size); + + #define CK(str) (str[0] | (str[1] << 8) | (str[2] << 16) | (str[3] << 24)) + switch(chunk_id) { + case CK("INFO"): { + const uint8_t version = file_.get8(); + if(version != 1) break; + is_3_5_disk_ = file_.get8() == 2; + is_read_only_ = file_.get8() == 1; + /* Ignored: + 1 byte: Synchronized; 1 = Cross track sync was used during imaging. + 1 byte: Cleaned; 1 = MC3470 fake bits have been removed. + 32 bytes: Cretor; a UTF-8 string. + */ + } break; + + case CK("TMAP"): { + file_.read(track_map_, 160); + } break; + + case CK("TRKS"): { + tracks_offset_ = file_.tell(); + } break; + + // TODO: parse META chunks. + + default: + break; + } + #undef CK + + file_.seek(end_of_chunk, SEEK_SET); + } +} + +int WOZ::get_head_position_count() { + // TODO: deal with the elephant in the room of non-integral track coordinates. + return is_3_5_disk_ ? 80 : 160; +} + +int WOZ::get_head_count() { + return is_3_5_disk_ ? 2 : 1; +} + +std::shared_ptr WOZ::get_track_at_position(Track::Address address) { + // Out-of-bounds => no track. + if(address.head >= get_head_count()) return nullptr; + if(address.position >= get_head_position_count()) return nullptr; + + // Calculate table position; if this track is defined to be unformatted, return no track. + const int table_position = address.head * get_head_position_count() + address.position; + if(track_map_[table_position] == 0xff) return nullptr; + + // Seek to the real track. + file_.seek(tracks_offset_ + track_map_[table_position] * 6656, SEEK_SET); + + PCMSegment track_contents; + track_contents.data = file_.read(6646); + track_contents.data.resize(file_.get16le()); + track_contents.number_of_bits = file_.get16le(); + + const uint16_t splice_point = file_.get16le(); + if(splice_point != 0xffff) { + // TODO: expand track from splice_point? + } + + return std::shared_ptr(new PCMTrack(track_contents)); +} diff --git a/Storage/Disk/DiskImage/Formats/WOZ.hpp b/Storage/Disk/DiskImage/Formats/WOZ.hpp new file mode 100644 index 000000000..16abba35c --- /dev/null +++ b/Storage/Disk/DiskImage/Formats/WOZ.hpp @@ -0,0 +1,46 @@ +// +// WOZ.hpp +// Clock Signal +// +// Created by Thomas Harte on 23/04/2018. +// Copyright © 2018 Thomas Harte. All rights reserved. +// + +#ifndef WOZ_hpp +#define WOZ_hpp + +#include "../DiskImage.hpp" +#include "../../../FileHolder.hpp" + +#include + +namespace Storage { +namespace Disk { + +/*! + Provides a @c DiskImage containing a WOZ — a bit stream representation of a floppy. +*/ +class WOZ: public DiskImage { + public: + WOZ(const std::string &file_name); + + enum { + ErrorNotWOZ + }; + + int get_head_position_count() override; + int get_head_count() override; + std::shared_ptr get_track_at_position(Track::Address address) override; + + private: + Storage::FileHolder file_; + bool is_read_only_ = false; + bool is_3_5_disk_ = false; + uint8_t track_map_[160]; + long tracks_offset_ = 0; +}; + +} +} + +#endif /* WOZ_hpp */ From bce070274516f57ba6dcf9c8a5037d808f005eb5 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 23 Apr 2018 19:59:03 -0700 Subject: [PATCH 04/44] Makes some faulty steps further towards providing Apple GCR assistance. --- Storage/Disk/Encodings/AppleGCR.cpp | 90 +++++++++++++++++++++++++++++ Storage/Disk/Encodings/AppleGCR.hpp | 16 +++++ 2 files changed, 106 insertions(+) diff --git a/Storage/Disk/Encodings/AppleGCR.cpp b/Storage/Disk/Encodings/AppleGCR.cpp index 9a1a94ea6..c33d19c0e 100644 --- a/Storage/Disk/Encodings/AppleGCR.cpp +++ b/Storage/Disk/Encodings/AppleGCR.cpp @@ -51,3 +51,93 @@ void AppleGCR::encode_six_and_two_block(uint8_t *destination, uint8_t *source) { destination[2] = static_cast(six_and_two_encoding_for_value( (source[1] << 2) | (source[2] >> 6) )); destination[3] = static_cast(six_and_two_encoding_for_value( source[2] )); } + +/*! + Produces a PCM segment containing @c length sync bytes, each aligned to the beginning of + a @c bit_size -sized window. +*/ +static Storage::Disk::PCMSegment sync(int length, int bit_size) { + Storage::Disk::PCMSegment segment; + + // Allocate sufficient storage. + segment.data.resize(static_cast(((length * bit_size) + 7) >> 3), 0); + + while(length--) { + segment.data[segment.number_of_bits >> 3] |= 0xff >> (segment.number_of_bits & 7); + if(segment.number_of_bits & 7) { + segment.data[1 + (segment.number_of_bits >> 3)] |= 0xff << (8 - (segment.number_of_bits & 7)); + } + segment.number_of_bits += static_cast(bit_size); + } + + return segment; +} + +Storage::Disk::PCMSegment AppleGCR::six_and_two_sync(int length) { + return sync(length, 9); +} + +Storage::Disk::PCMSegment AppleGCR::five_and_three_sync(int length) { + return sync(length, 10); +} + +Storage::Disk::PCMSegment AppleGCR::header(uint8_t volume, uint8_t track, uint8_t sector) { + const uint8_t checksum = volume ^ track ^ sector; + + // Apple headers are encoded using an FM-esque scheme rather than 6 and 2, or 5 and 3. + Storage::Disk::PCMSegment segment; + segment.data.resize(14); + segment.number_of_bits = 14*8; + + segment.data[0] = header_prologue[0]; + segment.data[1] = header_prologue[1]; + segment.data[2] = header_prologue[2]; + +#define WriteFM(index, value) \ + segment.data[index+0] = static_cast((value >> 1) | 0xaa); \ + segment.data[index+1] = static_cast(value | 0xaa); \ + + WriteFM(3, volume); + WriteFM(5, track); + WriteFM(7, sector); + WriteFM(9, checksum); + +#undef WriteFM + + segment.data[11] = epilogue[0]; + segment.data[12] = epilogue[1]; + segment.data[13] = epilogue[2]; + + return segment; +} + +Storage::Disk::PCMSegment AppleGCR::five_and_three_data(uint8_t *source) { + Storage::Disk::PCMSegment segment; + + segment.data.resize(410 + 7); + segment.data[0] = header_prologue[0]; + segment.data[1] = header_prologue[1]; + segment.data[2] = header_prologue[2]; + + std::size_t source_pointer = 0; + std::size_t destination_pointer = 3; + while(source_pointer < 255) { + encode_five_and_three_block(&segment.data[destination_pointer], &source[source_pointer]); + + source_pointer += 5; + destination_pointer += 8; + } + + return segment; +} + +Storage::Disk::PCMSegment AppleGCR::six_and_two_data(uint8_t *source) { + Storage::Disk::PCMSegment segment; + + segment.data.resize(342 + 7); + segment.data[0] = header_prologue[0]; + segment.data[1] = header_prologue[1]; + segment.data[2] = header_prologue[2]; + + return segment; +} diff --git a/Storage/Disk/Encodings/AppleGCR.hpp b/Storage/Disk/Encodings/AppleGCR.hpp index f152bb4b2..a09df4624 100644 --- a/Storage/Disk/Encodings/AppleGCR.hpp +++ b/Storage/Disk/Encodings/AppleGCR.hpp @@ -10,6 +10,7 @@ #define AppleGCR_hpp #include +#include "../../Disk/Track/PCMSegment.hpp" namespace Storage { namespace Encodings { @@ -45,6 +46,21 @@ namespace AppleGCR { @returns the byte composed by splitting the dectet into two qintets, decoding each and composing the resulting nibbles. */ // unsigned int decoding_from_dectet(unsigned int dectet); + + /// Describes the standard three-byte prologue that begins a header. + const uint8_t header_prologue[3] = {0xd5, 0xaa, 0x96}; + /// Describes the standard three-byte prologue that begins a data section. + const uint8_t data_prologue[3] = {0xd5, 0xaa, 0xad}; + /// Describes the epilogue that ends both data sections and headers. + const uint8_t epilogue[3] = {0xde, 0xaa, 0xeb}; + + Storage::Disk::PCMSegment header(uint8_t volume, uint8_t track, uint8_t sector); + + Storage::Disk::PCMSegment six_and_two_data(uint8_t *source); + Storage::Disk::PCMSegment six_and_two_sync(int length); + + Storage::Disk::PCMSegment five_and_three_data(uint8_t *source); + Storage::Disk::PCMSegment five_and_three_sync(int length); } } From 38b2302b599e36e3eb6ce1ba278628ce4ec57da4 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 23 Apr 2018 19:59:19 -0700 Subject: [PATCH 05/44] Corrects minor documentation errors. --- Storage/Disk/DiskImage/Formats/HFE.hpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Storage/Disk/DiskImage/Formats/HFE.hpp b/Storage/Disk/DiskImage/Formats/HFE.hpp index ab1a274b9..8283a0f6a 100644 --- a/Storage/Disk/DiskImage/Formats/HFE.hpp +++ b/Storage/Disk/DiskImage/Formats/HFE.hpp @@ -18,21 +18,20 @@ namespace Storage { namespace Disk { /*! - Provides a @c Disk containing an HFE disk image — a bit stream representation of a floppy. + Provides a @c DiskImage containing an HFE — a bit stream representation of a floppy. */ class HFE: public DiskImage { public: /*! - Construct an @c SSD containing content from the file with name @c file_name. + Construct an @c HFE containing content from the file with name @c file_name. - @throws ErrorCantOpen if this file can't be opened. - @throws ErrorNotSSD if the file doesn't appear to contain a .SSD format image. + @throws ErrorNotHFE if the file doesn't appear to contain a .SSD format image. */ HFE(const std::string &file_name); ~HFE(); enum { - ErrorNotHFE, + ErrorNotHFE }; // implemented to satisfy @c Disk From 4cbe5068a9e3fc01941a10499700fd37357c4a1f Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 23 Apr 2018 20:01:12 -0700 Subject: [PATCH 06/44] Works further towards NIB, but still isn't close. --- Storage/Disk/DiskImage/Formats/NIB.cpp | 19 ++++++++++--------- Storage/Disk/DiskImage/Formats/NIB.hpp | 2 -- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/Storage/Disk/DiskImage/Formats/NIB.cpp b/Storage/Disk/DiskImage/Formats/NIB.cpp index 015816cfb..075a5bf12 100644 --- a/Storage/Disk/DiskImage/Formats/NIB.cpp +++ b/Storage/Disk/DiskImage/Formats/NIB.cpp @@ -28,17 +28,18 @@ NIB::NIB(const std::string &file_name) : } int NIB::get_head_position_count() { - return 35; -} - -int NIB::get_head_count() { - return 1; -} - -bool NIB::get_is_read_only() { - return true; + return number_of_tracks * 4; } std::shared_ptr<::Storage::Disk::Track> NIB::get_track_at_position(::Storage::Disk::Track::Address address) { + // NIBs contain data for even-numbered tracks underneath a single head only. + if(address.head || (address.position&1)) return nullptr; + +// const int file_track = address.position >> 1; +// file_.seek(static_cast(file_track * track_length), SEEK_SET); +// std::vector track_data = file_.read(track_length); + + // TODO: determine which FFs are syncs, and produce track. + return nullptr; } diff --git a/Storage/Disk/DiskImage/Formats/NIB.hpp b/Storage/Disk/DiskImage/Formats/NIB.hpp index b1113f7e0..fff52ca95 100644 --- a/Storage/Disk/DiskImage/Formats/NIB.hpp +++ b/Storage/Disk/DiskImage/Formats/NIB.hpp @@ -29,8 +29,6 @@ class NIB: public DiskImage { }; int get_head_position_count() override; - int get_head_count() override; - bool get_is_read_only() override; std::shared_ptr<::Storage::Disk::Track> get_track_at_position(::Storage::Disk::Track::Address address) override; From e92e06a5f416135e9d434cb64c438cfcf825f97c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 23 Apr 2018 20:20:14 -0700 Subject: [PATCH 07/44] Doubled down on the ROMMachine::ROMFetcher typedef. --- Analyser/Static/AppleII/Target.hpp | 2 +- Machines/AmstradCPC/AmstradCPC.cpp | 2 +- Machines/ColecoVision/ColecoVision.cpp | 2 +- Machines/Commodore/1540/C1540.hpp | 2 +- Machines/Commodore/1540/Implementation/C1540.cpp | 2 +- Machines/Commodore/Vic-20/Vic20.cpp | 2 +- Machines/Electron/Electron.cpp | 2 +- Machines/MSX/MSX.cpp | 2 +- Machines/Oric/Oric.cpp | 2 +- Machines/ZX8081/ZX8081.cpp | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Analyser/Static/AppleII/Target.hpp b/Analyser/Static/AppleII/Target.hpp index 0f47d3a62..7bd90107d 100644 --- a/Analyser/Static/AppleII/Target.hpp +++ b/Analyser/Static/AppleII/Target.hpp @@ -16,7 +16,7 @@ namespace Static { namespace AppleII { struct Target: public ::Analyser::Static::Target { - // TODO: probably some Disk II options here? + bool has_disk_; // TODO: Disk II versus IWM? }; } diff --git a/Machines/AmstradCPC/AmstradCPC.cpp b/Machines/AmstradCPC/AmstradCPC.cpp index e5278fec6..58ab1bce2 100644 --- a/Machines/AmstradCPC/AmstradCPC.cpp +++ b/Machines/AmstradCPC/AmstradCPC.cpp @@ -941,7 +941,7 @@ class ConcreteMachine: } // Obtains the system ROMs. - bool set_rom_fetcher(const std::function>>(const std::string &machine, const std::vector &names)> &roms_with_names) override { + bool set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names) override { auto roms = roms_with_names( "AmstradCPC", { diff --git a/Machines/ColecoVision/ColecoVision.cpp b/Machines/ColecoVision/ColecoVision.cpp index 67915f5d9..a371f619e 100644 --- a/Machines/ColecoVision/ColecoVision.cpp +++ b/Machines/ColecoVision/ColecoVision.cpp @@ -183,7 +183,7 @@ class ConcreteMachine: } // Obtains the system ROMs. - bool set_rom_fetcher(const std::function>>(const std::string &machine, const std::vector &names)> &roms_with_names) override { + bool set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names) override { auto roms = roms_with_names( "ColecoVision", { "coleco.rom" }); diff --git a/Machines/Commodore/1540/C1540.hpp b/Machines/Commodore/1540/C1540.hpp index 5d9b521c3..87376097d 100644 --- a/Machines/Commodore/1540/C1540.hpp +++ b/Machines/Commodore/1540/C1540.hpp @@ -31,7 +31,7 @@ class Machine: public MachineBase, public ROMMachine::Machine { /*! Sets the source for this drive's ROM image. */ - bool set_rom_fetcher(const std::function>>(const std::string &machine, const std::vector &names)> &roms_with_names); + bool set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names); /*! Sets the serial bus to which this drive should attach itself. diff --git a/Machines/Commodore/1540/Implementation/C1540.cpp b/Machines/Commodore/1540/Implementation/C1540.cpp index f1677ef3b..b3d702bc2 100644 --- a/Machines/Commodore/1540/Implementation/C1540.cpp +++ b/Machines/Commodore/1540/Implementation/C1540.cpp @@ -82,7 +82,7 @@ Cycles MachineBase::perform_bus_operation(CPU::MOS6502::BusOperation operation, return Cycles(1); } -bool Machine::set_rom_fetcher(const std::function>>(const std::string &machine, const std::vector &names)> &roms_with_names) { +bool Machine::set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names) { std::string rom_name; switch(personality_) { case Personality::C1540: rom_name = "1540.bin"; break; diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index fa9e54939..39deb6516 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -336,7 +336,7 @@ class ConcreteMachine: } // Obtains the system ROMs. - bool set_rom_fetcher(const std::function>>(const std::string &machine, const std::vector &names)> &roms_with_names) override { + bool set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names) override { rom_fetcher_ = roms_with_names; auto roms = roms_with_names( diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index ef9eb8417..36a2ff5fd 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -90,7 +90,7 @@ class ConcreteMachine: } // Obtains the system ROMs. - bool set_rom_fetcher(const std::function>>(const std::string &machine, const std::vector &names)> &roms_with_names) override { + bool set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names) override { auto roms = roms_with_names( "Electron", { diff --git a/Machines/MSX/MSX.cpp b/Machines/MSX/MSX.cpp index e94b9ed50..e1c693094 100644 --- a/Machines/MSX/MSX.cpp +++ b/Machines/MSX/MSX.cpp @@ -464,7 +464,7 @@ class ConcreteMachine: } // Obtains the system ROMs. - bool set_rom_fetcher(const std::function>>(const std::string &machine, const std::vector &names)> &roms_with_names) override { + bool set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names) override { auto roms = roms_with_names( "MSX", { diff --git a/Machines/Oric/Oric.cpp b/Machines/Oric/Oric.cpp index 03c86be0f..f0292fd11 100644 --- a/Machines/Oric/Oric.cpp +++ b/Machines/Oric/Oric.cpp @@ -220,7 +220,7 @@ class ConcreteMachine: } // Obtains the system ROMs. - bool set_rom_fetcher(const std::function>>(const std::string &machine, const std::vector &names)> &roms_with_names) override { + bool set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names) override { auto roms = roms_with_names( "Oric", { diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index 9b9e52dfe..fd15e1e46 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -346,7 +346,7 @@ template class ConcreteMachine: } // Obtains the system ROMs. - bool set_rom_fetcher(const std::function>>(const std::string &machine, const std::vector &names)> &roms_with_names) override { + bool set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names) override { const auto roms = roms_with_names( "ZX8081", { From 7463edaa1b39ea76aa9c894667a1e184ebe60c65 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 23 Apr 2018 21:14:45 -0700 Subject: [PATCH 08/44] Attempts to bring card support to the Apple II, and adds a 'has disk' flag. --- Analyser/Static/AppleII/StaticAnalyser.cpp | 2 + Analyser/Static/AppleII/Target.hpp | 2 +- Machines/AppleII/AppleII.cpp | 51 ++++++++++++++++++- Machines/AppleII/Card.hpp | 27 ++++++++++ .../Clock Signal.xcodeproj/project.pbxproj | 2 + .../StaticAnalyser/CSStaticAnalyser.mm | 3 +- 6 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 Machines/AppleII/Card.hpp diff --git a/Analyser/Static/AppleII/StaticAnalyser.cpp b/Analyser/Static/AppleII/StaticAnalyser.cpp index c12292336..b3825d960 100644 --- a/Analyser/Static/AppleII/StaticAnalyser.cpp +++ b/Analyser/Static/AppleII/StaticAnalyser.cpp @@ -14,6 +14,8 @@ Analyser::Static::TargetList Analyser::Static::AppleII::GetTargets(const Media & target->machine = Machine::AppleII; target->media = media; + target->has_disk = !target->media.disks.empty(); + TargetList targets; targets.push_back(std::move(target)); return targets; diff --git a/Analyser/Static/AppleII/Target.hpp b/Analyser/Static/AppleII/Target.hpp index 7bd90107d..9d058ac75 100644 --- a/Analyser/Static/AppleII/Target.hpp +++ b/Analyser/Static/AppleII/Target.hpp @@ -16,7 +16,7 @@ namespace Static { namespace AppleII { struct Target: public ::Analyser::Static::Target { - bool has_disk_; // TODO: Disk II versus IWM? + bool has_disk; // TODO: Disk II versus IWM? }; } diff --git a/Machines/AppleII/AppleII.cpp b/Machines/AppleII/AppleII.cpp index f66fea105..f550d2247 100644 --- a/Machines/AppleII/AppleII.cpp +++ b/Machines/AppleII/AppleII.cpp @@ -18,8 +18,11 @@ #include "../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp" +#include "Card.hpp" #include "Video.hpp" +#include "../../Analyser/Static/AppleII/Target.hpp" + #include namespace { @@ -57,6 +60,16 @@ class ConcreteMachine: void update_audio() { speaker_.run_for(audio_queue_, cycles_since_audio_update_.divide(Cycles(audio_divider))); } + void update_cards() { + cycles_since_card_update_ += stretched_cycles_since_card_update_ / 7; + stretched_cycles_since_card_update_ %= 7; + for(int c = 0; c < 7; ++c) { + if(cards_[c]) + cards_[c]->run_for(cycles_since_card_update_, stretched_cycles_since_card_update_); + } + cycles_since_card_update_ = 0; + stretched_cycles_since_card_update_ = 0; + } uint8_t ram_[48*1024]; std::vector rom_; @@ -69,6 +82,11 @@ class ConcreteMachine: Outputs::Speaker::LowpassSpeaker speaker_; Cycles cycles_since_audio_update_; + ROMMachine::ROMFetcher rom_fetcher_; + AppleII::Card *cards_[7] = {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}; + Cycles cycles_since_card_update_; + int stretched_cycles_since_card_update_ = 0; + public: ConcreteMachine(): m6502_(*this), @@ -110,6 +128,7 @@ class ConcreteMachine: Cycles perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) { ++ cycles_since_video_update_; + ++ cycles_since_card_update_; cycles_since_audio_update_ += Cycles(7); switch(address) { @@ -123,7 +142,6 @@ class ConcreteMachine: switch(address) { default: // printf("Unknown access to %04x\n", address); - *value = 0xff; break; case 0xc000: *value = keyboard_input_; @@ -160,6 +178,28 @@ class ConcreteMachine: break; } + if(address >= 0xc100 && address < 0xc800) { + /* + Decode the area conventionally used by cards for ROMs: + 0xCn00 — 0xCnff: card n. + */ + const int card_number = (address - 0xc100) >> 8; + if(cards_[card_number]) { + update_cards(); + cards_[card_number]->perform_bus_operation(operation, address & 0xff, value); + } + } else if(address >= 0xc090 && address < 0xc100) { + /* + Decode the area conventionally used by cards for registers: + C0n0--C0nF: card n - 8. + */ + const int card_number = (address - 0xc080) >> 4; + if(cards_[card_number]) { + update_cards(); + cards_[card_number]->perform_bus_operation(operation, address, value); + } + } + // The Apple II has a slightly weird timing pattern: every 65th CPU cycle is stretched // by an extra 1/7th. That's because one cycle lasts 3.5 NTSC colour clocks, so after // 65 cycles a full line of 227.5 colour clocks have passed. But the high-rate binary @@ -169,6 +209,7 @@ class ConcreteMachine: cycles_into_current_line_ = (cycles_into_current_line_ + 1) % 65; if(!cycles_into_current_line_) { ++ cycles_since_audio_update_; + ++ stretched_cycles_since_card_update_; } return Cycles(1); @@ -180,7 +221,7 @@ class ConcreteMachine: audio_queue_.perform(); } - bool set_rom_fetcher(const std::function>>(const std::string &machine, const std::vector &names)> &roms_with_names) override { + bool set_rom_fetcher(const ROMMachine::ROMFetcher &roms_with_names) override { auto roms = roms_with_names( "AppleII", { @@ -194,6 +235,8 @@ class ConcreteMachine: character_rom_ = std::move(*roms[1]); + rom_fetcher_ = roms_with_names; + return true; } @@ -223,6 +266,10 @@ class ConcreteMachine: // MARK: ConfigurationTarget void configure_as_target(const Analyser::Static::Target *target) override { + auto *const apple_target = dynamic_cast(target); + if(apple_target->has_disk) { + // ... add Disk II + } } bool insert_media(const Analyser::Static::Media &media) override { diff --git a/Machines/AppleII/Card.hpp b/Machines/AppleII/Card.hpp new file mode 100644 index 000000000..7f02a270a --- /dev/null +++ b/Machines/AppleII/Card.hpp @@ -0,0 +1,27 @@ +// +// Card.h +// Clock Signal +// +// Created by Thomas Harte on 23/04/2018. +// Copyright © 2018 Thomas Harte. All rights reserved. +// + +#ifndef Card_h +#define Card_h + +#include "../../ClockReceiver/ClockReceiver.hpp" + +namespace AppleII { + +class Card { + public: + /*! Advances time by @c cycles, of which @c stretches were stretched. */ + virtual void run_for(Cycles half_cycles, int stretches) {} + + /*! Performs a bus operation; the card is implicitly selected. */ + virtual void perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) = 0; +}; + +} + +#endif /* Card_h */ diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index bafaf0bc9..0785cb63f 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -1332,6 +1332,7 @@ 4BBFBB6A1EE8401E00C01E7A /* ZX8081.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ZX8081.cpp; path = Parsers/ZX8081.cpp; sourceTree = ""; }; 4BBFBB6B1EE8401E00C01E7A /* ZX8081.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ZX8081.hpp; path = Parsers/ZX8081.hpp; sourceTree = ""; }; 4BBFFEE51F7B27F1005F3FEB /* TrackSerialiser.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = TrackSerialiser.cpp; sourceTree = ""; }; + 4BC39565208EDFCE0044766B /* Card.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Card.hpp; sourceTree = ""; }; 4BC3B74D1CD194CC00F86E85 /* Shader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Shader.cpp; sourceTree = ""; }; 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 = ""; }; @@ -1555,6 +1556,7 @@ 4B15AA0A2082C799005E6C8D /* Video.cpp */, 4B15AA092082C799005E6C8D /* AppleII.hpp */, 4B15AA0B2082C799005E6C8D /* Video.hpp */, + 4BC39565208EDFCE0044766B /* Card.hpp */, ); path = AppleII; sourceTree = ""; diff --git a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm index 2ed5386b0..29862a206 100644 --- a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm +++ b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm @@ -15,6 +15,7 @@ #include "../../../../../Analyser/Static/Acorn/Target.hpp" #include "../../../../../Analyser/Static/AmstradCPC/Target.hpp" +#include "../../../../../Analyser/Static/AppleII/Target.hpp" #include "../../../../../Analyser/Static/Commodore/Target.hpp" #include "../../../../../Analyser/Static/MSX/Target.hpp" #include "../../../../../Analyser/Static/Oric/Target.hpp" @@ -156,7 +157,7 @@ static Analyser::Static::ZX8081::Target::MemoryModel ZX8081MemoryModelFromSize(K - (instancetype)initWithAppleII { self = [super init]; if(self) { - using Target = Analyser::Static::Target; + using Target = Analyser::Static::AppleII::Target; std::unique_ptr target(new Target); target->machine = Analyser::Machine::AppleII; _targets.push_back(std::move(target)); From 4bff44377add9cb0386b3e29c69e1d28ffbe2745 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 23 Apr 2018 22:11:31 -0700 Subject: [PATCH 09/44] Attempts to route Disk II requests to the thing itself. --- Components/DiskII/DiskII.cpp | 28 ++++++++ Machines/AppleII/AppleII.cpp | 14 ++-- Machines/AppleII/Card.hpp | 1 + Machines/AppleII/DiskIICard.cpp | 64 +++++++++++++++++++ Machines/AppleII/DiskIICard.hpp | 35 ++++++++++ Machines/ROMMachine.hpp | 1 + .../Clock Signal.xcodeproj/project.pbxproj | 10 ++- 7 files changed, 144 insertions(+), 9 deletions(-) create mode 100644 Machines/AppleII/DiskIICard.cpp create mode 100644 Machines/AppleII/DiskIICard.hpp diff --git a/Components/DiskII/DiskII.cpp b/Components/DiskII/DiskII.cpp index 0c92ad670..b93d12097 100644 --- a/Components/DiskII/DiskII.cpp +++ b/Components/DiskII/DiskII.cpp @@ -7,3 +7,31 @@ // #include "DiskII.hpp" + +#include + +using namespace Apple; + +void DiskII::set_control(Control control, bool on) { + printf("Set control %d %s\n", control, on ? "on" : "off"); +} + +void DiskII::set_mode(Mode mode) { + printf("Set mode %d\n", mode); +} + +void DiskII::select_drive(int drive) { + printf("Select drive %d\n", drive); +} + +void DiskII::set_shift_register(uint8_t value) { + printf("Set shift register\n"); +} + +uint8_t DiskII::get_shift_register() { + printf("Get shift register\n"); + return 0xff; +} + +void DiskII::run_for(const Cycles cycles) { +} diff --git a/Machines/AppleII/AppleII.cpp b/Machines/AppleII/AppleII.cpp index f550d2247..868a6a2e7 100644 --- a/Machines/AppleII/AppleII.cpp +++ b/Machines/AppleII/AppleII.cpp @@ -19,6 +19,7 @@ #include "../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp" #include "Card.hpp" +#include "DiskIICard.hpp" #include "Video.hpp" #include "../../Analyser/Static/AppleII/Target.hpp" @@ -61,11 +62,8 @@ class ConcreteMachine: speaker_.run_for(audio_queue_, cycles_since_audio_update_.divide(Cycles(audio_divider))); } void update_cards() { - cycles_since_card_update_ += stretched_cycles_since_card_update_ / 7; - stretched_cycles_since_card_update_ %= 7; for(int c = 0; c < 7; ++c) { - if(cards_[c]) - cards_[c]->run_for(cycles_since_card_update_, stretched_cycles_since_card_update_); + if(cards_[c]) cards_[c]->run_for(cycles_since_card_update_, stretched_cycles_since_card_update_); } cycles_since_card_update_ = 0; stretched_cycles_since_card_update_ = 0; @@ -83,7 +81,7 @@ class ConcreteMachine: Cycles cycles_since_audio_update_; ROMMachine::ROMFetcher rom_fetcher_; - AppleII::Card *cards_[7] = {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}; + std::unique_ptr cards_[7]; Cycles cycles_since_card_update_; int stretched_cycles_since_card_update_ = 0; @@ -193,10 +191,10 @@ class ConcreteMachine: Decode the area conventionally used by cards for registers: C0n0--C0nF: card n - 8. */ - const int card_number = (address - 0xc080) >> 4; + const int card_number = (address - 0xc090) >> 4; if(cards_[card_number]) { update_cards(); - cards_[card_number]->perform_bus_operation(operation, address, value); + cards_[card_number]->perform_bus_operation(operation, 0x100 | (address&0xf), value); } } @@ -268,7 +266,7 @@ class ConcreteMachine: void configure_as_target(const Analyser::Static::Target *target) override { auto *const apple_target = dynamic_cast(target); if(apple_target->has_disk) { - // ... add Disk II + cards_[6].reset(new AppleII::DiskIICard(rom_fetcher_, true)); } } diff --git a/Machines/AppleII/Card.hpp b/Machines/AppleII/Card.hpp index 7f02a270a..d7c00cdb6 100644 --- a/Machines/AppleII/Card.hpp +++ b/Machines/AppleII/Card.hpp @@ -9,6 +9,7 @@ #ifndef Card_h #define Card_h +#include "../../Processors/6502/6502.hpp" #include "../../ClockReceiver/ClockReceiver.hpp" namespace AppleII { diff --git a/Machines/AppleII/DiskIICard.cpp b/Machines/AppleII/DiskIICard.cpp new file mode 100644 index 000000000..d536dfe63 --- /dev/null +++ b/Machines/AppleII/DiskIICard.cpp @@ -0,0 +1,64 @@ +// +// DiskII.cpp +// Clock Signal +// +// Created by Thomas Harte on 23/04/2018. +// Copyright © 2018 Thomas Harte. All rights reserved. +// + +#include "DiskIICard.hpp" + +using namespace AppleII; + +DiskIICard::DiskIICard(const ROMMachine::ROMFetcher &rom_fetcher, bool is_16_sector) { + auto roms = rom_fetcher( + "DiskII", + { + "boot.rom", + "state-machine.rom" + }); + boot_ = std::move(*roms[0]); + state_machine_ = std::move(*roms[1]); +} + +void DiskIICard::perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) { + if(isReadOperation(operation) && address < 0x100) { + *value &= boot_[address]; + } else { + using Control = Apple::DiskII::Control; + using Mode = Apple::DiskII::Mode; + switch(address & 0xf) { + case 0x0: diskii_.set_control(Control::P0, false); break; + case 0x1: diskii_.set_control(Control::P0, true); break; + case 0x2: diskii_.set_control(Control::P1, false); break; + case 0x3: diskii_.set_control(Control::P1, true); break; + case 0x4: diskii_.set_control(Control::P2, false); break; + case 0x5: diskii_.set_control(Control::P2, true); break; + case 0x6: diskii_.set_control(Control::P3, false); break; + case 0x7: diskii_.set_control(Control::P3, true); break; + + case 0x8: diskii_.set_control(Control::Motor, false); break; + case 0x9: diskii_.set_control(Control::Motor, true); break; + + case 0xa: diskii_.select_drive(0); break; + case 0xb: diskii_.select_drive(1); break; + + case 0xc: + /* shift register? */ + if(isReadOperation(operation)) + *value = diskii_.get_shift_register(); + break; + case 0xd: + /* data register? */ + diskii_.set_shift_register(*value); + break; + + case 0xe: diskii_.set_mode(Mode::Read); break; + case 0xf: diskii_.set_mode(Mode::Write); break; + } + } +} + +void DiskIICard::run_for(Cycles cycles, int stretches) { + diskii_.run_for(cycles); +} diff --git a/Machines/AppleII/DiskIICard.hpp b/Machines/AppleII/DiskIICard.hpp new file mode 100644 index 000000000..08b80027a --- /dev/null +++ b/Machines/AppleII/DiskIICard.hpp @@ -0,0 +1,35 @@ +// +// DiskII.hpp +// Clock Signal +// +// Created by Thomas Harte on 23/04/2018. +// Copyright © 2018 Thomas Harte. All rights reserved. +// + +#ifndef DiskIICard_hpp +#define DiskIICard_hpp + +#include "Card.hpp" +#include "../ROMMachine.hpp" +#include "../../Components/DiskII/DiskII.hpp" + +#include +#include + +namespace AppleII { + +class DiskIICard: public Card { + public: + DiskIICard(const ROMMachine::ROMFetcher &rom_fetcher, bool is_16_sector); + void perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) override; + void run_for(Cycles cycles, int stretches) override; + + private: + std::vector boot_; + std::vector state_machine_; + Apple::DiskII diskii_; +}; + +} + +#endif /* DiskIICard_hpp */ diff --git a/Machines/ROMMachine.hpp b/Machines/ROMMachine.hpp index 4c40e8bad..42c34e07f 100644 --- a/Machines/ROMMachine.hpp +++ b/Machines/ROMMachine.hpp @@ -11,6 +11,7 @@ #include #include +#include #include namespace ROMMachine { diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 0785cb63f..6c3663dea 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -606,6 +606,8 @@ 4BBF99181C8FBA6F0075DAFB /* TextureTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBF99121C8FBA6F0075DAFB /* TextureTarget.cpp */; }; 4BBFBB6C1EE8401E00C01E7A /* ZX8081.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBFBB6A1EE8401E00C01E7A /* ZX8081.cpp */; }; 4BBFFEE61F7B27F1005F3FEB /* TrackSerialiser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBFFEE51F7B27F1005F3FEB /* TrackSerialiser.cpp */; }; + 4BC39568208EE6CF0044766B /* DiskIICard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC39566208EE6CF0044766B /* DiskIICard.cpp */; }; + 4BC39569208EE6CF0044766B /* DiskIICard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC39566208EE6CF0044766B /* DiskIICard.cpp */; }; 4BC3B74F1CD194CC00F86E85 /* Shader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC3B74D1CD194CC00F86E85 /* Shader.cpp */; }; 4BC3B7521CD1956900F86E85 /* OutputShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC3B7501CD1956900F86E85 /* OutputShader.cpp */; }; 4BC751B21D157E61006C31D9 /* 6522Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC751B11D157E61006C31D9 /* 6522Tests.swift */; }; @@ -1333,6 +1335,8 @@ 4BBFBB6B1EE8401E00C01E7A /* ZX8081.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ZX8081.hpp; path = Parsers/ZX8081.hpp; sourceTree = ""; }; 4BBFFEE51F7B27F1005F3FEB /* TrackSerialiser.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = TrackSerialiser.cpp; sourceTree = ""; }; 4BC39565208EDFCE0044766B /* Card.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Card.hpp; sourceTree = ""; }; + 4BC39566208EE6CF0044766B /* DiskIICard.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DiskIICard.cpp; sourceTree = ""; }; + 4BC39567208EE6CF0044766B /* DiskIICard.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DiskIICard.hpp; sourceTree = ""; }; 4BC3B74D1CD194CC00F86E85 /* Shader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Shader.cpp; sourceTree = ""; }; 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 = ""; }; @@ -1553,10 +1557,12 @@ isa = PBXGroup; children = ( 4B15AA0C2082C799005E6C8D /* AppleII.cpp */, + 4BC39566208EE6CF0044766B /* DiskIICard.cpp */, 4B15AA0A2082C799005E6C8D /* Video.cpp */, 4B15AA092082C799005E6C8D /* AppleII.hpp */, - 4B15AA0B2082C799005E6C8D /* Video.hpp */, 4BC39565208EDFCE0044766B /* Card.hpp */, + 4BC39567208EE6CF0044766B /* DiskIICard.hpp */, + 4B15AA0B2082C799005E6C8D /* Video.hpp */, ); path = AppleII; sourceTree = ""; @@ -3607,6 +3613,7 @@ 4B15AA0E2082C799005E6C8D /* Video.cpp in Sources */, 4B055A991FAE85CB0060FFFF /* DiskController.cpp in Sources */, 4B055ACC1FAE9B030060FFFF /* Electron.cpp in Sources */, + 4BC39569208EE6CF0044766B /* DiskIICard.cpp in Sources */, 4B055AB11FAE86070060FFFF /* Tape.cpp in Sources */, 4BFE7B881FC39D8900160B38 /* StandardOptions.cpp in Sources */, 4B894533201967B4007DE474 /* 6502.cpp in Sources */, @@ -3711,6 +3718,7 @@ 4B595FAD2086DFBA0083CAA8 /* AudioToggle.cpp in Sources */, 4B1497921EE4B5A800CE2596 /* ZX8081.cpp in Sources */, 4B643F3F1D77B88000D431D6 /* DocumentController.swift in Sources */, + 4BC39568208EE6CF0044766B /* DiskIICard.cpp in Sources */, 4B4518861F75E91A00926311 /* MFMDiskController.cpp in Sources */, 4B54C0BF1F8D8F450050900F /* Keyboard.cpp in Sources */, 4B3FE75E1F3CF68B00448EE4 /* CPM.cpp in Sources */, From 56d88f23ef53c800772fcf3c19d71935f2a8bf0d Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 23 Apr 2018 22:29:36 -0700 Subject: [PATCH 10/44] Teeters closer and closer to trying actually to run the Disk II state machine. --- Components/DiskII/DiskII.cpp | 41 +++++++++++++++++++++++++++++---- Components/DiskII/DiskII.hpp | 9 +++++++- Machines/AppleII/DiskIICard.cpp | 9 ++++---- 3 files changed, 50 insertions(+), 9 deletions(-) diff --git a/Components/DiskII/DiskII.cpp b/Components/DiskII/DiskII.cpp index b93d12097..cab110b8c 100644 --- a/Components/DiskII/DiskII.cpp +++ b/Components/DiskII/DiskII.cpp @@ -18,20 +18,53 @@ void DiskII::set_control(Control control, bool on) { void DiskII::set_mode(Mode mode) { printf("Set mode %d\n", mode); + state_ = (state_ & ~0x08) | ((mode == Mode::Write) ? 0x8 : 0x0); } void DiskII::select_drive(int drive) { printf("Select drive %d\n", drive); } -void DiskII::set_shift_register(uint8_t value) { - printf("Set shift register\n"); +void DiskII::set_data_register(uint8_t value) { + printf("Set data register (?)\n"); + state_ |= 0x4; + data_register_ = value; } uint8_t DiskII::get_shift_register() { - printf("Get shift register\n"); - return 0xff; + printf("Get shift register (?)\n"); + state_ &= ~0x4; + return shift_register_; } void DiskII::run_for(const Cycles cycles) { +/* +... address the P6 ROM with an index byte built up as: ++-------+-------+-------+-------+-------+-------+-------+-------+ +| STATE | STATE | STATE | PULSE | Q7 | Q6 | SR | STATE | +| bit 0 | bit 2 | bit 3 | | | | MSB | bit 1 | ++-------+-------+-------+-------+-------+-------+-------+-------+ + 7 6 5 4 3 2 1 0 + +... + +The bytes in the P6 ROM has the high four bits reversed compared to the BAPD charts, so you will have to reverse them after fetching the byte. + +*/ + + uint8_t command = 0; + switch(command) { + case 0x0: shift_register_ = 0; break; // clear + case 0x9: shift_register_ = static_cast(shift_register_ << 1); break; // shift left, bringing in a zero + case 0xd: shift_register_ = static_cast((shift_register_ << 1) | 1); break; // shift left, bringing in a one + case 0xa: + shift_register_ = (shift_register_ >> 1) | (is_write_protected() ? 0x80 : 0x00); + break; // shift right, bringing in write protected status + case 0xb: shift_register_ = data_register_; break; // load + default: break; + } +} + +bool DiskII::is_write_protected() { + return true; } diff --git a/Components/DiskII/DiskII.hpp b/Components/DiskII/DiskII.hpp index 49006bf3e..45155918f 100644 --- a/Components/DiskII/DiskII.hpp +++ b/Components/DiskII/DiskII.hpp @@ -29,10 +29,17 @@ class DiskII { void set_control(Control control, bool on); void set_mode(Mode mode); void select_drive(int drive); - void set_shift_register(uint8_t value); + void set_data_register(uint8_t value); uint8_t get_shift_register(); void run_for(const Cycles cycles); + + private: + uint8_t state_ = 0; + uint8_t shift_register_ = 0; + uint8_t data_register_ = 0; + + bool is_write_protected(); }; } diff --git a/Machines/AppleII/DiskIICard.cpp b/Machines/AppleII/DiskIICard.cpp index d536dfe63..38bbb203d 100644 --- a/Machines/AppleII/DiskIICard.cpp +++ b/Machines/AppleII/DiskIICard.cpp @@ -43,14 +43,15 @@ void DiskIICard::perform_bus_operation(CPU::MOS6502::BusOperation operation, uin case 0xa: diskii_.select_drive(0); break; case 0xb: diskii_.select_drive(1); break; - case 0xc: + case 0xc: { /* shift register? */ + const uint8_t shift_value = diskii_.get_shift_register(); if(isReadOperation(operation)) - *value = diskii_.get_shift_register(); - break; + *value = shift_value; + } break; case 0xd: /* data register? */ - diskii_.set_shift_register(*value); + diskii_.set_data_register(*value); break; case 0xe: diskii_.set_mode(Mode::Read); break; From af61bbc3e2fb0e0ee6a8f4690bb7ff7d2307c59f Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 24 Apr 2018 08:29:05 -0700 Subject: [PATCH 11/44] Attempts actual performance of the state machine. --- Components/DiskII/DiskII.cpp | 52 ++++++++++++++++++++++++--------- Components/DiskII/DiskII.hpp | 5 ++++ Machines/AppleII/DiskIICard.cpp | 4 +-- Machines/AppleII/DiskIICard.hpp | 1 - 4 files changed, 45 insertions(+), 17 deletions(-) diff --git a/Components/DiskII/DiskII.cpp b/Components/DiskII/DiskII.cpp index cab110b8c..a7802385c 100644 --- a/Components/DiskII/DiskII.cpp +++ b/Components/DiskII/DiskII.cpp @@ -14,26 +14,28 @@ using namespace Apple; void DiskII::set_control(Control control, bool on) { printf("Set control %d %s\n", control, on ? "on" : "off"); + // TODO: seeking, motor control. } void DiskII::set_mode(Mode mode) { printf("Set mode %d\n", mode); - state_ = (state_ & ~0x08) | ((mode == Mode::Write) ? 0x8 : 0x0); + inputs_ = (inputs_ & ~0x08) | ((mode == Mode::Write) ? 0x2: 0x0); } void DiskII::select_drive(int drive) { printf("Select drive %d\n", drive); + // TODO: select a drive. } void DiskII::set_data_register(uint8_t value) { printf("Set data register (?)\n"); - state_ |= 0x4; + inputs_ |= 0x1; data_register_ = value; } uint8_t DiskII::get_shift_register() { - printf("Get shift register (?)\n"); - state_ &= ~0x4; + printf("[%02x] ", shift_register_); + inputs_ &= ~0x1; return shift_register_; } @@ -51,20 +53,42 @@ void DiskII::run_for(const Cycles cycles) { The bytes in the P6 ROM has the high four bits reversed compared to the BAPD charts, so you will have to reverse them after fetching the byte. */ + // TODO: optimise the resting state. - uint8_t command = 0; - switch(command) { - case 0x0: shift_register_ = 0; break; // clear - case 0x9: shift_register_ = static_cast(shift_register_ << 1); break; // shift left, bringing in a zero - case 0xd: shift_register_ = static_cast((shift_register_ << 1) | 1); break; // shift left, bringing in a one - case 0xa: - shift_register_ = (shift_register_ >> 1) | (is_write_protected() ? 0x80 : 0x00); - break; // shift right, bringing in write protected status - case 0xb: shift_register_ = data_register_; break; // load - default: break; + int integer_cycles = cycles.as_int(); + while(integer_cycles--) { + const int address = + (inputs_ << 2) | + ((shift_register_&0x80) >> 6) | + ((state_&0x2) >> 1) | + ((state_&0x1) << 7) | + ((state_&0x4) << 4) | + ((state_&0x8) << 2); + // TODO: add pulse state in bit 4. + + const uint8_t update = state_machine_[static_cast(address)]; + state_ = update >> 4; + state_ = ((state_ & 0x8) ? 0x1 : 0x0) | ((state_ & 0x4) ? 0x2 : 0x0) | ((state_ & 0x2) ? 0x4 : 0x0) | ((state_ & 0x1) ? 0x8 : 0x0); + + uint8_t command = update & 0xf; + switch(command) { + case 0x0: shift_register_ = 0; break; // clear + case 0x9: shift_register_ = static_cast(shift_register_ << 1); break; // shift left, bringing in a zero + case 0xd: shift_register_ = static_cast((shift_register_ << 1) | 1); break; // shift left, bringing in a one + case 0xb: shift_register_ = data_register_; break; // load + case 0xa: + shift_register_ = (shift_register_ >> 1) | (is_write_protected() ? 0x80 : 0x00); + break; // shift right, bringing in write protected status + default: break; + } } } bool DiskII::is_write_protected() { return true; } + +void DiskII::set_state_machine(const std::vector &state_machine) { + state_machine_ = state_machine; + // TODO: shuffle ordering here? +} diff --git a/Components/DiskII/DiskII.hpp b/Components/DiskII/DiskII.hpp index 45155918f..faa845d17 100644 --- a/Components/DiskII/DiskII.hpp +++ b/Components/DiskII/DiskII.hpp @@ -10,7 +10,9 @@ #define DiskII_hpp #include "../../ClockReceiver/ClockReceiver.hpp" + #include +#include namespace Apple { @@ -33,13 +35,16 @@ class DiskII { uint8_t get_shift_register(); void run_for(const Cycles cycles); + void set_state_machine(const std::vector &); private: uint8_t state_ = 0; + uint8_t inputs_ = 0; uint8_t shift_register_ = 0; uint8_t data_register_ = 0; bool is_write_protected(); + std::vector state_machine_; }; } diff --git a/Machines/AppleII/DiskIICard.cpp b/Machines/AppleII/DiskIICard.cpp index 38bbb203d..76c8229f8 100644 --- a/Machines/AppleII/DiskIICard.cpp +++ b/Machines/AppleII/DiskIICard.cpp @@ -18,7 +18,7 @@ DiskIICard::DiskIICard(const ROMMachine::ROMFetcher &rom_fetcher, bool is_16_sec "state-machine.rom" }); boot_ = std::move(*roms[0]); - state_machine_ = std::move(*roms[1]); + diskii_.set_state_machine(*roms[1]); } void DiskIICard::perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) { @@ -61,5 +61,5 @@ void DiskIICard::perform_bus_operation(CPU::MOS6502::BusOperation operation, uin } void DiskIICard::run_for(Cycles cycles, int stretches) { - diskii_.run_for(cycles); + diskii_.run_for(Cycles(cycles.as_int() * 2)); } diff --git a/Machines/AppleII/DiskIICard.hpp b/Machines/AppleII/DiskIICard.hpp index 08b80027a..9213adf52 100644 --- a/Machines/AppleII/DiskIICard.hpp +++ b/Machines/AppleII/DiskIICard.hpp @@ -26,7 +26,6 @@ class DiskIICard: public Card { private: std::vector boot_; - std::vector state_machine_; Apple::DiskII diskii_; }; From 99de8f1c5c1a0ee81ef00e1b9fa6415b26b3293a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 24 Apr 2018 09:03:03 -0700 Subject: [PATCH 12/44] Inverts the pulse strobe. --- Components/DiskII/DiskII.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Components/DiskII/DiskII.cpp b/Components/DiskII/DiskII.cpp index a7802385c..df6f50396 100644 --- a/Components/DiskII/DiskII.cpp +++ b/Components/DiskII/DiskII.cpp @@ -34,7 +34,7 @@ void DiskII::set_data_register(uint8_t value) { } uint8_t DiskII::get_shift_register() { - printf("[%02x] ", shift_register_); +// printf("[%02x] ", shift_register_); inputs_ &= ~0x1; return shift_register_; } @@ -63,8 +63,9 @@ The bytes in the P6 ROM has the high four bits reversed compared to the BAPD cha ((state_&0x2) >> 1) | ((state_&0x1) << 7) | ((state_&0x4) << 4) | - ((state_&0x8) << 2); - // TODO: add pulse state in bit 4. + ((state_&0x8) << 2) | + 0x10; + // TODO: apply proper pulse state in bit 4. const uint8_t update = state_machine_[static_cast(address)]; state_ = update >> 4; @@ -81,6 +82,8 @@ The bytes in the P6 ROM has the high four bits reversed compared to the BAPD cha break; // shift right, bringing in write protected status default: break; } + +// printf(" -> %02x performing %02x (address was %02x)\n", state_, command, address); } } @@ -90,5 +93,6 @@ bool DiskII::is_write_protected() { void DiskII::set_state_machine(const std::vector &state_machine) { state_machine_ = state_machine; +// run_for(Cycles(15)); // TODO: shuffle ordering here? } From 2f2390b5aab7f9d33e5cf6fd4f5735b23f4f0687 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 24 Apr 2018 09:03:30 -0700 Subject: [PATCH 13/44] Adds F12 as a reset key, triggers cards upon a flush. --- Machines/AppleII/AppleII.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Machines/AppleII/AppleII.cpp b/Machines/AppleII/AppleII.cpp index 868a6a2e7..dd720d1a8 100644 --- a/Machines/AppleII/AppleII.cpp +++ b/Machines/AppleII/AppleII.cpp @@ -216,6 +216,7 @@ class ConcreteMachine: void flush() { update_video(); update_audio(); + update_cards(); audio_queue_.perform(); } @@ -243,6 +244,11 @@ class ConcreteMachine: } void set_key_pressed(Key key, char value, bool is_pressed) override { + if(key == Key::F12) { + m6502_.set_reset_line(is_pressed); + return; + } + if(is_pressed) { // If no ASCII value is supplied, look for a few special cases. if(!value) { From 7061537ff51591a9ecfc934ce5c73c491a8853a6 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 24 Apr 2018 19:44:45 -0700 Subject: [PATCH 14/44] Makes joined-up attempt to run data through the Disk II. --- Components/DiskII/DiskII.cpp | 75 +++++++++++++++++++++++++++++---- Components/DiskII/DiskII.hpp | 16 ++++++- Machines/AppleII/AppleII.cpp | 5 +++ Machines/AppleII/DiskIICard.cpp | 4 ++ Machines/AppleII/DiskIICard.hpp | 4 ++ Storage/Disk/Drive.cpp | 5 ++- Storage/Disk/Drive.hpp | 4 +- 7 files changed, 100 insertions(+), 13 deletions(-) diff --git a/Components/DiskII/DiskII.cpp b/Components/DiskII/DiskII.cpp index df6f50396..bc2a5b9e0 100644 --- a/Components/DiskII/DiskII.cpp +++ b/Components/DiskII/DiskII.cpp @@ -12,30 +12,76 @@ using namespace Apple; +namespace { + const uint8_t input_command = 0x1; + const uint8_t input_mode = 0x2; + const uint8_t input_flux = 0x4; +} + +DiskII::DiskII() : + drives_{{2045454, 300, 1}, {2045454, 300, 1}} +{ +} + void DiskII::set_control(Control control, bool on) { printf("Set control %d %s\n", control, on ? "on" : "off"); - // TODO: seeking, motor control. + + int previous_stepper_mask = stepper_mask_; + switch(control) { + case Control::P0: stepper_mask_ = (stepper_mask_ & 0xe) | (on ? 0x1 : 0x0); break; + case Control::P1: stepper_mask_ = (stepper_mask_ & 0xd) | (on ? 0x2 : 0x0); break; + case Control::P2: stepper_mask_ = (stepper_mask_ & 0xb) | (on ? 0x4 : 0x0); break; + case Control::P3: stepper_mask_ = (stepper_mask_ & 0x7) | (on ? 0x8 : 0x0); break; + + case Control::Motor: + // TODO: does the motor control trigger both motors at once? + drives_[0].set_motor_on(on); + drives_[1].set_motor_on(on); + break; + } + + // If the stepper magnet selections have changed, and any is on, see how + // that moves the head. + if(previous_stepper_mask ^ stepper_mask_ && stepper_mask_) { + // Convert from a representation of bits set to the centre of pull. + int position = 0; + if(stepper_mask_&2) position += 2; + if(stepper_mask_&4) position += 4; + if(stepper_mask_&8) position += 6; + // TODO: both 0 and 4 turned on should produce position 7 + const int bits_set = (stepper_mask_&1) + ((stepper_mask_ >> 1)&1) + ((stepper_mask_ >> 2)&1) + ((stepper_mask_ >> 3)&1); + position /= bits_set; + + // Compare to the stepper position to decide whether that pulls in the current cog notch, + // or grabs a later one. + int change = ((position - stepper_position_ + 4)&7) - 4; + drives_[active_drive_].step(change); + + stepper_position_ = position; + } } void DiskII::set_mode(Mode mode) { printf("Set mode %d\n", mode); - inputs_ = (inputs_ & ~0x08) | ((mode == Mode::Write) ? 0x2: 0x0); + inputs_ = (inputs_ & ~input_mode) | ((mode == Mode::Write) ? input_mode : 0); } void DiskII::select_drive(int drive) { printf("Select drive %d\n", drive); - // TODO: select a drive. + active_drive_ = drive & 1; + drives_[active_drive_].set_event_delegate(this); + drives_[active_drive_^1].set_event_delegate(nullptr); } void DiskII::set_data_register(uint8_t value) { printf("Set data register (?)\n"); - inputs_ |= 0x1; + inputs_ |= input_command; data_register_ = value; } uint8_t DiskII::get_shift_register() { // printf("[%02x] ", shift_register_); - inputs_ &= ~0x1; + inputs_ &= ~input_command; return shift_register_; } @@ -63,9 +109,8 @@ The bytes in the P6 ROM has the high four bits reversed compared to the BAPD cha ((state_&0x2) >> 1) | ((state_&0x1) << 7) | ((state_&0x4) << 4) | - ((state_&0x8) << 2) | - 0x10; - // TODO: apply proper pulse state in bit 4. + ((state_&0x8) << 2); + inputs_ |= input_flux; const uint8_t update = state_machine_[static_cast(address)]; state_ = update >> 4; @@ -84,6 +129,10 @@ The bytes in the P6 ROM has the high four bits reversed compared to the BAPD cha } // printf(" -> %02x performing %02x (address was %02x)\n", state_, command, address); + + // TODO: surely there's a less heavyweight solution than this? + drives_[0].run_for(Cycles(1)); + drives_[1].run_for(Cycles(1)); } } @@ -96,3 +145,13 @@ void DiskII::set_state_machine(const std::vector &state_machine) { // run_for(Cycles(15)); // TODO: shuffle ordering here? } + +void DiskII::set_disk(const std::shared_ptr &disk, int drive) { + drives_[drive].set_disk(disk); +} + +void DiskII::process_event(const Storage::Disk::Track::Event &event) { + if(event.type == Storage::Disk::Track::Event::FluxTransition) { + inputs_ &= ~input_flux; + } +} diff --git a/Components/DiskII/DiskII.hpp b/Components/DiskII/DiskII.hpp index faa845d17..ccb0130b1 100644 --- a/Components/DiskII/DiskII.hpp +++ b/Components/DiskII/DiskII.hpp @@ -11,6 +11,9 @@ #include "../../ClockReceiver/ClockReceiver.hpp" +#include "../../Storage/Disk/Disk.hpp" +#include "../../Storage/Disk/Drive.hpp" + #include #include @@ -19,8 +22,10 @@ namespace Apple { /*! Provides an emulation of the Apple Disk II. */ -class DiskII { +class DiskII: public Storage::Disk::Drive::EventDelegate { public: + DiskII(); + enum class Control { P0, P1, P2, P3, Motor, @@ -37,14 +42,23 @@ class DiskII { void run_for(const Cycles cycles); void set_state_machine(const std::vector &); + void set_disk(const std::shared_ptr &disk, int drive); + private: + void process_event(const Storage::Disk::Track::Event &event) override; + uint8_t state_ = 0; uint8_t inputs_ = 0; uint8_t shift_register_ = 0; uint8_t data_register_ = 0; + int stepper_mask_ = 0; + int stepper_position_ = 0; + bool is_write_protected(); std::vector state_machine_; + Storage::Disk::Drive drives_[2]; + int active_drive_ = 0; }; } diff --git a/Machines/AppleII/AppleII.cpp b/Machines/AppleII/AppleII.cpp index dd720d1a8..fc2b1cf10 100644 --- a/Machines/AppleII/AppleII.cpp +++ b/Machines/AppleII/AppleII.cpp @@ -274,9 +274,14 @@ class ConcreteMachine: if(apple_target->has_disk) { cards_[6].reset(new AppleII::DiskIICard(rom_fetcher_, true)); } + + insert_media(apple_target->media); } bool insert_media(const Analyser::Static::Media &media) override { + if(!media.disks.empty() && cards_[6]) { + dynamic_cast(cards_[6].get())->set_disk(media.disks[0], 0); + } return true; } }; diff --git a/Machines/AppleII/DiskIICard.cpp b/Machines/AppleII/DiskIICard.cpp index 76c8229f8..6b54fb866 100644 --- a/Machines/AppleII/DiskIICard.cpp +++ b/Machines/AppleII/DiskIICard.cpp @@ -63,3 +63,7 @@ void DiskIICard::perform_bus_operation(CPU::MOS6502::BusOperation operation, uin void DiskIICard::run_for(Cycles cycles, int stretches) { diskii_.run_for(Cycles(cycles.as_int() * 2)); } + +void DiskIICard::set_disk(const std::shared_ptr &disk, int drive) { + diskii_.set_disk(disk, drive); +} diff --git a/Machines/AppleII/DiskIICard.hpp b/Machines/AppleII/DiskIICard.hpp index 9213adf52..7f03d0ddc 100644 --- a/Machines/AppleII/DiskIICard.hpp +++ b/Machines/AppleII/DiskIICard.hpp @@ -11,9 +11,12 @@ #include "Card.hpp" #include "../ROMMachine.hpp" + #include "../../Components/DiskII/DiskII.hpp" +#include "../../Storage/Disk/Disk.hpp" #include +#include #include namespace AppleII { @@ -23,6 +26,7 @@ class DiskIICard: public Card { DiskIICard(const ROMMachine::ROMFetcher &rom_fetcher, bool is_16_sector); void perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) override; void run_for(Cycles cycles, int stretches) override; + void set_disk(const std::shared_ptr &disk, int drive); private: std::vector boot_; diff --git a/Storage/Disk/Drive.cpp b/Storage/Disk/Drive.cpp index 63f68cbbf..7bbc3c958 100644 --- a/Storage/Disk/Drive.cpp +++ b/Storage/Disk/Drive.cpp @@ -47,6 +47,7 @@ bool Drive::get_is_track_zero() { } void Drive::step(int direction) { + printf("Step %d\n", direction); int old_head_position = head_position_; head_position_ = std::max(head_position_ + direction, 0); @@ -70,7 +71,7 @@ Storage::Time Drive::get_time_into_track() { Time result(cycles_since_index_hole_, static_cast(get_input_clock_rate())); result /= rotational_multiplier_; result.simplify(); - assert(result <= Time(1)); +// assert(result <= Time(1)); return result; } @@ -168,7 +169,7 @@ void Drive::get_next_event(const Time &duration_already_passed) { void Drive::process_next_event() { // TODO: ready test here. if(current_event_.type == Track::Event::IndexHole) { - assert(get_time_into_track() == Time(1) || get_time_into_track() == Time(0)); +// assert(get_time_into_track() == Time(1) || get_time_into_track() == Time(0)); if(ready_index_count_ < 2) ready_index_count_++; cycles_since_index_hole_ = 0; } diff --git a/Storage/Disk/Drive.hpp b/Storage/Disk/Drive.hpp index 7f321068c..b57f68cbd 100644 --- a/Storage/Disk/Drive.hpp +++ b/Storage/Disk/Drive.hpp @@ -110,10 +110,10 @@ class Drive: public Sleeper, public TimedEventLoop { If the drive is in write mode, announces that all queued bits have now been written. If the controller provides further bits now then there will be no gap in written data. */ - virtual void process_write_completed() = 0; + virtual void process_write_completed() {} /// Informs the delegate of the passing of @c cycles. - virtual void advance(const Cycles cycles) = 0; + virtual void advance(const Cycles cycles) {} }; /// Sets the current event delegate. From 376b26c1e4851f456871646c7a7ef4ff1fa505c8 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 24 Apr 2018 20:16:14 -0700 Subject: [PATCH 15/44] Simplifies the rotational multiplier upon construction, to mitigate against scale issues later. --- Storage/Disk/Drive.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Storage/Disk/Drive.cpp b/Storage/Disk/Drive.cpp index 7bbc3c958..f32cb31f7 100644 --- a/Storage/Disk/Drive.cpp +++ b/Storage/Disk/Drive.cpp @@ -19,6 +19,7 @@ Drive::Drive(unsigned int input_clock_rate, int revolutions_per_minute, int numb Storage::TimedEventLoop(input_clock_rate), rotational_multiplier_(60, revolutions_per_minute), available_heads_(number_of_heads) { + rotational_multiplier_.simplify(); } Drive::~Drive() { From 2685e9087ecbbb42edca82293384cf81ed84bb8d Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 24 Apr 2018 20:24:44 -0700 Subject: [PATCH 16/44] Changes the default-assigned Disk II card slot from 7 to 6. --- Machines/AppleII/AppleII.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Machines/AppleII/AppleII.cpp b/Machines/AppleII/AppleII.cpp index fc2b1cf10..3c39319a0 100644 --- a/Machines/AppleII/AppleII.cpp +++ b/Machines/AppleII/AppleII.cpp @@ -272,15 +272,15 @@ class ConcreteMachine: void configure_as_target(const Analyser::Static::Target *target) override { auto *const apple_target = dynamic_cast(target); if(apple_target->has_disk) { - cards_[6].reset(new AppleII::DiskIICard(rom_fetcher_, true)); + cards_[5].reset(new AppleII::DiskIICard(rom_fetcher_, true)); } insert_media(apple_target->media); } bool insert_media(const Analyser::Static::Media &media) override { - if(!media.disks.empty() && cards_[6]) { - dynamic_cast(cards_[6].get())->set_disk(media.disks[0], 0); + if(!media.disks.empty() && cards_[5]) { + dynamic_cast(cards_[5].get())->set_disk(media.disks[0], 0); } return true; } From b4f6dee954d5cb9e96968e974624401810081680 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 24 Apr 2018 20:25:02 -0700 Subject: [PATCH 17/44] Ensures the contextually-proper boot and state machine ROMs are requested. --- Machines/AppleII/DiskIICard.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Machines/AppleII/DiskIICard.cpp b/Machines/AppleII/DiskIICard.cpp index 6b54fb866..f61b18ffa 100644 --- a/Machines/AppleII/DiskIICard.cpp +++ b/Machines/AppleII/DiskIICard.cpp @@ -14,8 +14,8 @@ DiskIICard::DiskIICard(const ROMMachine::ROMFetcher &rom_fetcher, bool is_16_sec auto roms = rom_fetcher( "DiskII", { - "boot.rom", - "state-machine.rom" + is_16_sector ? "boot-16.rom" : "boot-13.rom", + is_16_sector ? "state-machine-16.rom" : "state-machine-13.rom" }); boot_ = std::move(*roms[0]); diskii_.set_state_machine(*roms[1]); From 4c6dc597f40257bcba5a779d16f13b584a315465 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 25 Apr 2018 19:54:39 -0400 Subject: [PATCH 18/44] Converts Time::get into a template, introduces a via-a-double fallback for the timed event loop. --- Components/DiskII/DiskII.cpp | 2 +- Storage/Disk/Controller/DiskController.cpp | 2 +- Storage/Disk/Drive.cpp | 3 +-- Storage/Disk/Track/PCMSegment.cpp | 2 +- Storage/Disk/Track/TrackSerialiser.cpp | 2 +- Storage/Storage.hpp | 8 ++---- Storage/Tape/Parsers/Acorn.cpp | 2 +- Storage/Tape/Parsers/Commodore.cpp | 2 +- Storage/Tape/Parsers/Oric.cpp | 2 +- Storage/Tape/Parsers/ZX8081.cpp | 2 +- Storage/TimedEventLoop.cpp | 31 +++++++++++++++++----- 11 files changed, 36 insertions(+), 22 deletions(-) diff --git a/Components/DiskII/DiskII.cpp b/Components/DiskII/DiskII.cpp index bc2a5b9e0..31a3455f8 100644 --- a/Components/DiskII/DiskII.cpp +++ b/Components/DiskII/DiskII.cpp @@ -19,7 +19,7 @@ namespace { } DiskII::DiskII() : - drives_{{2045454, 300, 1}, {2045454, 300, 1}} + drives_{{2000000, 300, 1}, {2045454, 300, 1}} { } diff --git a/Storage/Disk/Controller/DiskController.cpp b/Storage/Disk/Controller/DiskController.cpp index c37f0d8c7..e6788c873 100644 --- a/Storage/Disk/Controller/DiskController.cpp +++ b/Storage/Disk/Controller/DiskController.cpp @@ -66,7 +66,7 @@ void Controller::set_expected_bit_length(Time bit_length) { // this conversion doesn't need to be exact because there's a lot of variation to be taken // account of in rotation speed, air turbulence, etc, so a direct conversion will do - int clocks_per_bit = static_cast(cycles_per_bit.get_unsigned_int()); + int clocks_per_bit = cycles_per_bit.get(); pll_.reset(new DigitalPhaseLockedLoop(clocks_per_bit, 3)); pll_->set_delegate(this); } diff --git a/Storage/Disk/Drive.cpp b/Storage/Disk/Drive.cpp index f32cb31f7..66ce42233 100644 --- a/Storage/Disk/Drive.cpp +++ b/Storage/Disk/Drive.cpp @@ -117,7 +117,7 @@ void Drive::run_for(const Cycles cycles) { int cycles_until_next_event = static_cast(get_cycles_until_next_event()); int cycles_to_run_for = std::min(cycles_until_next_event, number_of_cycles); if(!is_reading_ && cycles_until_bits_written_ > zero) { - int write_cycles_target = static_cast(cycles_until_bits_written_.get_unsigned_int()); + int write_cycles_target = cycles_until_bits_written_.get(); if(cycles_until_bits_written_.length % cycles_until_bits_written_.clock_rate) write_cycles_target++; cycles_to_run_for = std::min(cycles_to_run_for, write_cycles_target); } @@ -168,7 +168,6 @@ void Drive::get_next_event(const Time &duration_already_passed) { } void Drive::process_next_event() { - // TODO: ready test here. if(current_event_.type == Track::Event::IndexHole) { // assert(get_time_into_track() == Time(1) || get_time_into_track() == Time(0)); if(ready_index_count_ < 2) ready_index_count_++; diff --git a/Storage/Disk/Track/PCMSegment.cpp b/Storage/Disk/Track/PCMSegment.cpp index ee7ea3b01..da07e0248 100644 --- a/Storage/Disk/Track/PCMSegment.cpp +++ b/Storage/Disk/Track/PCMSegment.cpp @@ -106,7 +106,7 @@ Storage::Time PCMSegmentEventSource::seek_to(const Time &time_from_start) { // bit_pointer_ always records _the next bit_ that might trigger an event, // so should be one beyond the one reached by a seek. Time relative_time = time_from_start - half_bit_length; - bit_pointer_ = 1 + (relative_time / segment_->length_of_a_bit).get_unsigned_int(); + bit_pointer_ = 1 + (relative_time / segment_->length_of_a_bit).get(); // map up to the correct amount of time return half_bit_length + segment_->length_of_a_bit * static_cast(bit_pointer_ - 1); diff --git a/Storage/Disk/Track/TrackSerialiser.cpp b/Storage/Disk/Track/TrackSerialiser.cpp index af7b0a387..be67dba35 100644 --- a/Storage/Disk/Track/TrackSerialiser.cpp +++ b/Storage/Disk/Track/TrackSerialiser.cpp @@ -40,7 +40,7 @@ Storage::Disk::PCMSegment Storage::Disk::track_serialisation(Track &track, Time Time extended_length = next_event.length * length_multiplier + time_error; time_error.clock_rate = extended_length.clock_rate; time_error.length = extended_length.length % extended_length.clock_rate; - pll.run_for(Cycles(static_cast(extended_length.get_unsigned_int()))); + pll.run_for(Cycles(extended_length.get())); pll.add_pulse(); // If the PLL is now sufficiently primed, restart, and start recording bits this time. diff --git a/Storage/Storage.hpp b/Storage/Storage.hpp index c55364356..f51043e4a 100644 --- a/Storage/Storage.hpp +++ b/Storage/Storage.hpp @@ -47,12 +47,8 @@ struct Time { /*! @returns the floating point conversion of this @c Time. This will often be less precise. */ - inline float get_float() const { - return static_cast(length) / static_cast(clock_rate); - } - - inline unsigned int get_unsigned_int() const { - return length / clock_rate; + template T get() const { + return static_cast(length) / static_cast(clock_rate); } inline bool operator < (const Time &other) const { diff --git a/Storage/Tape/Parsers/Acorn.cpp b/Storage/Tape/Parsers/Acorn.cpp index 627f02eac..d9d84d561 100644 --- a/Storage/Tape/Parsers/Acorn.cpp +++ b/Storage/Tape/Parsers/Acorn.cpp @@ -75,7 +75,7 @@ Shifter::Shifter() : } void Shifter::process_pulse(const Storage::Tape::Tape::Pulse &pulse) { - pll_.run_for(Cycles(static_cast(static_cast(PLLClockRate) * pulse.length.get_float()))); + pll_.run_for(Cycles(static_cast(static_cast(PLLClockRate) * pulse.length.get()))); bool is_high = pulse.type == Storage::Tape::Tape::Pulse::High; if(is_high != was_high_) { diff --git a/Storage/Tape/Parsers/Commodore.cpp b/Storage/Tape/Parsers/Commodore.cpp index 59e619b9f..c6ebad897 100644 --- a/Storage/Tape/Parsers/Commodore.cpp +++ b/Storage/Tape/Parsers/Commodore.cpp @@ -279,7 +279,7 @@ void Parser::process_pulse(const Storage::Tape::Tape::Pulse &pulse) wave_period_ = 0.0f; } - wave_period_ += pulse.length.get_float(); + wave_period_ += pulse.length.get(); previous_was_high_ = is_high; } diff --git a/Storage/Tape/Parsers/Oric.cpp b/Storage/Tape/Parsers/Oric.cpp index 662b2638f..884456c83 100644 --- a/Storage/Tape/Parsers/Oric.cpp +++ b/Storage/Tape/Parsers/Oric.cpp @@ -62,7 +62,7 @@ void Parser::process_pulse(const Storage::Tape::Tape::Pulse &pulse) cycle_length_ = 0.0f; } wave_was_high_ = wave_is_high; - cycle_length_ += pulse.length.get_float(); + cycle_length_ += pulse.length.get(); } void Parser::inspect_waves(const std::vector &waves) diff --git a/Storage/Tape/Parsers/ZX8081.cpp b/Storage/Tape/Parsers/ZX8081.cpp index bb47e9f53..f63b5772f 100644 --- a/Storage/Tape/Parsers/ZX8081.cpp +++ b/Storage/Tape/Parsers/ZX8081.cpp @@ -31,7 +31,7 @@ void Parser::process_pulse(const Storage::Tape::Tape::Pulse &pulse) { void Parser::post_pulse() { const float expected_pulse_length = 300.0f / 1000000.0f; const float expected_gap_length = 1300.0f / 1000000.0f; - float pulse_time = pulse_time_.get_float(); + float pulse_time = pulse_time_.get(); if(pulse_time > expected_gap_length * 1.25f) { push_wave(WaveType::LongGap); diff --git a/Storage/TimedEventLoop.cpp b/Storage/TimedEventLoop.cpp index bd9bea643..1f1682115 100644 --- a/Storage/TimedEventLoop.cpp +++ b/Storage/TimedEventLoop.cpp @@ -64,7 +64,9 @@ void TimedEventLoop::jump_to_next_event() { } void TimedEventLoop::set_next_event_time_interval(Time interval) { - // Calculate [interval]*[input clock rate] + [subcycles until this event]. + // Calculate [interval]*[input clock rate] + [subcycles until this event] + // = interval.numerator * input clock / interval.denominator + subcycles.numerator / subcycles.denominator + // = (interval.numerator * input clock * subcycles.denominator + subcycles.numerator * interval.denominator) / (interval.denominator * subcycles.denominator) int64_t denominator = static_cast(interval.clock_rate) * static_cast(subcycles_until_event_.clock_rate); int64_t numerator = static_cast(subcycles_until_event_.clock_rate) * static_cast(input_clock_rate_) * static_cast(interval.length) + @@ -72,7 +74,7 @@ void TimedEventLoop::set_next_event_time_interval(Time interval) { // Simplify if necessary: try just simplifying the interval and recalculating; if that doesn't // work then try simplifying the whole thing. - if(numerator < 0 || denominator < 0 || denominator > std::numeric_limits::max()) { + if(numerator < 0 || denominator < 0 || denominator > std::numeric_limits::max()) { interval.simplify(); denominator = static_cast(interval.clock_rate) * static_cast(subcycles_until_event_.clock_rate); numerator = @@ -80,19 +82,36 @@ void TimedEventLoop::set_next_event_time_interval(Time interval) { static_cast(interval.clock_rate) * static_cast(subcycles_until_event_.length); } - if(numerator < 0 || denominator < 0 || denominator > std::numeric_limits::max()) { + if(numerator < 0 || denominator < 0 || denominator > std::numeric_limits::max()) { int64_t common_divisor = NumberTheory::greatest_common_divisor(numerator % denominator, denominator); denominator /= common_divisor; numerator /= common_divisor; } - // TODO: if that doesn't work then reduce precision. + // If even that doesn't work then reduce precision. + if(numerator < 0 || denominator < 0 || denominator > std::numeric_limits::max()) { + const double double_interval = interval.get(); + const double double_subcycles_remaining = subcycles_until_event_.get(); + const double output = double_interval * static_cast(input_clock_rate_) + double_subcycles_remaining; + + if(output < 1.0) { + denominator = std::numeric_limits::max(); + numerator = static_cast(denominator * output); + } else { + numerator = std::numeric_limits::max(); + denominator = static_cast(numerator / output); + } + } // So this event will fire in the integral number of cycles from now, putting us at the remainder // number of subcycles + const int addition = static_cast(numerator / denominator); assert(cycles_until_event_ == 0); - cycles_until_event_ += static_cast(numerator / denominator); - assert(cycles_until_event_ >= 0); + assert(addition >= 0); + if(addition < 0) { + assert(false); + } + cycles_until_event_ += addition; subcycles_until_event_.length = static_cast(numerator % denominator); subcycles_until_event_.clock_rate = static_cast(denominator); subcycles_until_event_.simplify(); From c90e122eb2dac19a133a5b26e2b6814ddae91008 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 25 Apr 2018 19:59:32 -0400 Subject: [PATCH 19/44] Switches casts around to avoid potential undefined behaviour of left-shifting signed numbers. --- Storage/FileHolder.cpp | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Storage/FileHolder.cpp b/Storage/FileHolder.cpp index 9c4c19871..d63d2520d 100644 --- a/Storage/FileHolder.cpp +++ b/Storage/FileHolder.cpp @@ -42,48 +42,48 @@ FileHolder::FileHolder(const std::string &file_name, FileMode ideal_mode) } uint32_t FileHolder::get32le() { - uint32_t result = (uint32_t)std::fgetc(file_); - result |= (uint32_t)(std::fgetc(file_) << 8); - result |= (uint32_t)(std::fgetc(file_) << 16); - result |= (uint32_t)(std::fgetc(file_) << 24); + uint32_t result = static_cast(std::fgetc(file_)); + result |= static_cast(std::fgetc(file_)) << 8; + result |= static_cast(std::fgetc(file_)) << 16; + result |= static_cast(std::fgetc(file_)) << 24; return result; } uint32_t FileHolder::get32be() { - uint32_t result = (uint32_t)(std::fgetc(file_) << 24); - result |= (uint32_t)(std::fgetc(file_) << 16); - result |= (uint32_t)(std::fgetc(file_) << 8); - result |= (uint32_t)std::fgetc(file_); + uint32_t result = static_cast(std::fgetc(file_)) << 24; + result |= static_cast(std::fgetc(file_)) << 16; + result |= static_cast(std::fgetc(file_)) << 8; + result |= static_cast(std::fgetc(file_)); return result; } uint32_t FileHolder::get24le() { - uint32_t result = (uint32_t)std::fgetc(file_); - result |= (uint32_t)(std::fgetc(file_) << 8); - result |= (uint32_t)(std::fgetc(file_) << 16); + uint32_t result = static_cast(std::fgetc(file_)); + result |= static_cast(std::fgetc(file_)) << 8; + result |= static_cast(std::fgetc(file_)) << 16; return result; } uint32_t FileHolder::get24be() { - uint32_t result = (uint32_t)(std::fgetc(file_) << 16); - result |= (uint32_t)(std::fgetc(file_) << 8); - result |= (uint32_t)std::fgetc(file_); + uint32_t result = static_cast(std::fgetc(file_)) << 16; + result |= static_cast(std::fgetc(file_)) << 8; + result |= static_cast(std::fgetc(file_)); return result; } uint16_t FileHolder::get16le() { uint16_t result = static_cast(std::fgetc(file_)); - result |= static_cast(std::fgetc(file_) << 8); + result |= static_cast(static_cast(std::fgetc(file_)) << 8); return result; } uint16_t FileHolder::get16be() { - uint16_t result = static_cast(std::fgetc(file_) << 8); + uint16_t result = static_cast(static_cast(std::fgetc(file_)) << 8); result |= static_cast(std::fgetc(file_)); return result; From d59db504a351784cf868210c7a285d13048eb1dc Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 25 Apr 2018 21:59:18 -0400 Subject: [PATCH 20/44] Adjusted stepper logic; some disks load now. --- Components/DiskII/DiskII.cpp | 28 +++++++++++++--------------- Storage/Disk/Drive.cpp | 2 +- Storage/TimedEventLoop.cpp | 1 + 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/Components/DiskII/DiskII.cpp b/Components/DiskII/DiskII.cpp index 31a3455f8..ee0633bff 100644 --- a/Components/DiskII/DiskII.cpp +++ b/Components/DiskII/DiskII.cpp @@ -24,8 +24,6 @@ DiskII::DiskII() : } void DiskII::set_control(Control control, bool on) { - printf("Set control %d %s\n", control, on ? "on" : "off"); - int previous_stepper_mask = stepper_mask_; switch(control) { case Control::P0: stepper_mask_ = (stepper_mask_ & 0xe) | (on ? 0x1 : 0x0); break; @@ -40,41 +38,41 @@ void DiskII::set_control(Control control, bool on) { break; } +// printf("%0x: Set control %d %s\n", stepper_mask_, control, on ? "on" : "off"); + // If the stepper magnet selections have changed, and any is on, see how // that moves the head. if(previous_stepper_mask ^ stepper_mask_ && stepper_mask_) { // Convert from a representation of bits set to the centre of pull. - int position = 0; - if(stepper_mask_&2) position += 2; - if(stepper_mask_&4) position += 4; - if(stepper_mask_&8) position += 6; - // TODO: both 0 and 4 turned on should produce position 7 + int direction = 0; + if(stepper_mask_&1) direction += (((stepper_position_ - 0) + 4)&7) - 4; + if(stepper_mask_&2) direction += (((stepper_position_ - 2) + 4)&7) - 4; + if(stepper_mask_&4) direction += (((stepper_position_ - 4) + 4)&7) - 4; + if(stepper_mask_&8) direction += (((stepper_position_ - 6) + 4)&7) - 4; const int bits_set = (stepper_mask_&1) + ((stepper_mask_ >> 1)&1) + ((stepper_mask_ >> 2)&1) + ((stepper_mask_ >> 3)&1); - position /= bits_set; + direction /= bits_set; // Compare to the stepper position to decide whether that pulls in the current cog notch, // or grabs a later one. - int change = ((position - stepper_position_ + 4)&7) - 4; - drives_[active_drive_].step(change); - - stepper_position_ = position; + drives_[active_drive_].step(-direction); + stepper_position_ = (stepper_position_ - direction + 8) & 7; } } void DiskII::set_mode(Mode mode) { - printf("Set mode %d\n", mode); +// printf("Set mode %d\n", mode); inputs_ = (inputs_ & ~input_mode) | ((mode == Mode::Write) ? input_mode : 0); } void DiskII::select_drive(int drive) { - printf("Select drive %d\n", drive); +// printf("Select drive %d\n", drive); active_drive_ = drive & 1; drives_[active_drive_].set_event_delegate(this); drives_[active_drive_^1].set_event_delegate(nullptr); } void DiskII::set_data_register(uint8_t value) { - printf("Set data register (?)\n"); +// printf("Set data register (?)\n"); inputs_ |= input_command; data_register_ = value; } diff --git a/Storage/Disk/Drive.cpp b/Storage/Disk/Drive.cpp index 66ce42233..4fcf69108 100644 --- a/Storage/Disk/Drive.cpp +++ b/Storage/Disk/Drive.cpp @@ -48,9 +48,9 @@ bool Drive::get_is_track_zero() { } void Drive::step(int direction) { - printf("Step %d\n", direction); int old_head_position = head_position_; head_position_ = std::max(head_position_ + direction, 0); +// printf("Step %d -> %d\n", direction, head_position_); // If the head moved, flush the old track. if(head_position_ != old_head_position) { diff --git a/Storage/TimedEventLoop.cpp b/Storage/TimedEventLoop.cpp index 1f1682115..1dc0a259f 100644 --- a/Storage/TimedEventLoop.cpp +++ b/Storage/TimedEventLoop.cpp @@ -90,6 +90,7 @@ void TimedEventLoop::set_next_event_time_interval(Time interval) { // If even that doesn't work then reduce precision. if(numerator < 0 || denominator < 0 || denominator > std::numeric_limits::max()) { +// printf("."); const double double_interval = interval.get(); const double double_subcycles_remaining = subcycles_until_event_.get(); const double output = double_interval * static_cast(input_clock_rate_) + double_subcycles_remaining; From 244721a6f8f2b4ae84f684a686308ac6420caf2b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 25 Apr 2018 22:26:01 -0400 Subject: [PATCH 21/44] Corrects graphics mode address generation. --- Machines/AppleII/Video.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Machines/AppleII/Video.hpp b/Machines/AppleII/Video.hpp index 5b53324b7..e00d0a6a4 100644 --- a/Machines/AppleII/Video.hpp +++ b/Machines/AppleII/Video.hpp @@ -110,7 +110,7 @@ template class Video: public VideoBase { const int pixel_row = row_ & 7; const uint16_t row_address = static_cast((character_row >> 3) * 40 + ((character_row&7) << 7)); const uint16_t text_address = static_cast(((video_page_+1) * 0x400) + row_address); - const uint16_t graphics_address = static_cast(((video_page_+1) * 0x1000) + row_address + ((pixel_row&7) << 9)); + const uint16_t graphics_address = static_cast(((video_page_+1) * 0x2000) + row_address + ((pixel_row&7) << 10)); const int row_shift = (row_&4); GraphicsMode pixel_mode = (!mixed_mode_ || row_ < 160) ? line_mode : GraphicsMode::Text; From 850a394eb5d54cb5eb7e25247eb996c55732c3d0 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 26 Apr 2018 19:26:43 -0400 Subject: [PATCH 22/44] =?UTF-8?q?Corrects=20graphics=20'carry'=20=E2=80=94?= =?UTF-8?q?=20the=20potential=20holdover=20into=20delayed=20bytes.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Machines/AppleII/Video.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Machines/AppleII/Video.hpp b/Machines/AppleII/Video.hpp index e00d0a6a4..196fb77ca 100644 --- a/Machines/AppleII/Video.hpp +++ b/Machines/AppleII/Video.hpp @@ -103,6 +103,7 @@ template class Video: public VideoBase { if(row_ < 192) { if(!column_) { pixel_pointer_ = reinterpret_cast(crt_->allocate_write_area(80, 2)); + graphics_carry_ = 0; } const int pixel_end = std::min(40, ending_column); @@ -143,9 +144,9 @@ template class Video: public VideoBase { const uint8_t graphic = bus_handler_.perform_read(static_cast(graphics_address + c)); pixel_pointer_[c] = scaled_byte[graphic]; if(graphic & 0x80) { - reinterpret_cast(&pixel_pointer_[c])[0] |= graphics_carry_ << 7; + reinterpret_cast(&pixel_pointer_[c])[0] |= graphics_carry_; } - graphics_carry_ = pixel_pointer_[c] & 1; + graphics_carry_ = (graphic >> 6) & 1; } break; } From 41075356e229a1ea5d6ffefdcf98e0535990521f Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 26 Apr 2018 22:49:07 -0400 Subject: [PATCH 23/44] Makes a first attempt at NIB support. --- Components/DiskII/DiskII.cpp | 2 +- Storage/Disk/DiskImage/Formats/NIB.cpp | 67 +++++++++++++++++++++++--- Storage/Disk/Track/PCMSegment.hpp | 2 +- 3 files changed, 61 insertions(+), 10 deletions(-) diff --git a/Components/DiskII/DiskII.cpp b/Components/DiskII/DiskII.cpp index ee0633bff..57ff5a4be 100644 --- a/Components/DiskII/DiskII.cpp +++ b/Components/DiskII/DiskII.cpp @@ -78,7 +78,7 @@ void DiskII::set_data_register(uint8_t value) { } uint8_t DiskII::get_shift_register() { -// printf("[%02x] ", shift_register_); +// if(shift_register_ & 0x80) printf("[%02x] ", shift_register_); inputs_ &= ~input_command; return shift_register_; } diff --git a/Storage/Disk/DiskImage/Formats/NIB.cpp b/Storage/Disk/DiskImage/Formats/NIB.cpp index 075a5bf12..295c2696b 100644 --- a/Storage/Disk/DiskImage/Formats/NIB.cpp +++ b/Storage/Disk/DiskImage/Formats/NIB.cpp @@ -8,11 +8,16 @@ #include "NIB.hpp" +#include "../../Track/PCMTrack.hpp" +#include "../../Encodings/AppleGCR.hpp" + +#include + using namespace Storage::Disk; namespace { -const std::size_t track_length = 6656; +const long track_length = 6656; const std::size_t number_of_tracks = 35; } @@ -24,7 +29,7 @@ NIB::NIB(const std::string &file_name) : throw ErrorNotNIB; } - // TODO: all other validation. + // TODO: all other validation. I.e. does this look like a GCR disk? } int NIB::get_head_position_count() { @@ -33,13 +38,59 @@ int NIB::get_head_position_count() { std::shared_ptr<::Storage::Disk::Track> NIB::get_track_at_position(::Storage::Disk::Track::Address address) { // NIBs contain data for even-numbered tracks underneath a single head only. - if(address.head || (address.position&1)) return nullptr; + if(address.head) return nullptr; -// const int file_track = address.position >> 1; -// file_.seek(static_cast(file_track * track_length), SEEK_SET); -// std::vector track_data = file_.read(track_length); + const long file_track = static_cast(address.position >> 2); + file_.seek(file_track * track_length, SEEK_SET); + std::vector track_data = file_.read(track_length); - // TODO: determine which FFs are syncs, and produce track. + // NIB files leave sync bytes implicit and make no guarantees + // about overall track positioning. So the approach taken here + // is to look for the epilogue sequence (which concludes all Apple + // tracks and headers), then treat all following FFs as a sync + // region, then switch back to ordinary behaviour as soon as a + // non-FF appears. + std::vector segments; - return nullptr; + std::size_t start_index = 0; + std::set sync_starts; + + // Establish where syncs start by finding instances of 0xd5 0xaa and then regressing + // from each along all preceding FFs. + for(size_t index = 0; index < track_data.size(); ++index) { + if(track_data[index] == 0xd5 && track_data[(index+1)%track_data.size()] == 0xaa) { + size_t start = index - 1; + while(track_data[start] == 0xff) + start = (start + track_data.size() - 1) % track_data.size(); + + sync_starts.insert((start + 1) % track_data.size()); + if(start > index) + start_index = start; + } + } + + if(start_index) + segments.push_back(Encodings::AppleGCR::six_and_two_sync(static_cast(start_index))); + + std::size_t index = start_index; + for(const auto &location: sync_starts) { + // Write from index to sync_start. + PCMSegment data_segment; + data_segment.data.insert( + data_segment.data.end(), + track_data.begin() + static_cast(index), + track_data.begin() + static_cast(location)); + data_segment.number_of_bits = static_cast(data_segment.data.size() * 8); + segments.push_back(std::move(data_segment)); + + // Add a sync from sync_start to end of 0xffs. + if(location == track_length-1) break; + + index = location; + while(index < track_length && track_data[index] == 0xff) + ++index; + segments.push_back(Encodings::AppleGCR::six_and_two_sync(static_cast(index - location))); + } + + return std::shared_ptr(new PCMTrack(segments)); } diff --git a/Storage/Disk/Track/PCMSegment.hpp b/Storage/Disk/Track/PCMSegment.hpp index 96a2364de..ea9bd4779 100644 --- a/Storage/Disk/Track/PCMSegment.hpp +++ b/Storage/Disk/Track/PCMSegment.hpp @@ -25,7 +25,7 @@ namespace Disk { Bits from each byte are taken MSB to LSB. */ struct PCMSegment { - Time length_of_a_bit; + Time length_of_a_bit = Time(1); unsigned int number_of_bits = 0; std::vector data; From 992a99d792c0854b21f573e52f0db106addf6b06 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 27 Apr 2018 19:53:35 -0400 Subject: [PATCH 24/44] Improves validation of suspected sync regions. --- Storage/Disk/DiskImage/Formats/NIB.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Storage/Disk/DiskImage/Formats/NIB.cpp b/Storage/Disk/DiskImage/Formats/NIB.cpp index 295c2696b..41cab6631 100644 --- a/Storage/Disk/DiskImage/Formats/NIB.cpp +++ b/Storage/Disk/DiskImage/Formats/NIB.cpp @@ -60,12 +60,17 @@ std::shared_ptr<::Storage::Disk::Track> NIB::get_track_at_position(::Storage::Di for(size_t index = 0; index < track_data.size(); ++index) { if(track_data[index] == 0xd5 && track_data[(index+1)%track_data.size()] == 0xaa) { size_t start = index - 1; - while(track_data[start] == 0xff) + size_t length = 0; + while(track_data[start] == 0xff) { start = (start + track_data.size() - 1) % track_data.size(); + ++length; + } - sync_starts.insert((start + 1) % track_data.size()); - if(start > index) - start_index = start; + if(length >= 5) { + sync_starts.insert((start + 1) % track_data.size()); + if(start > index) + start_index = start; + } } } From 5c74044e6291ee925a0072c10d04ae554691b0f6 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 27 Apr 2018 21:38:08 -0400 Subject: [PATCH 25/44] Unifies constants. --- Storage/Disk/DiskImage/Formats/MSXDSK.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Storage/Disk/DiskImage/Formats/MSXDSK.cpp b/Storage/Disk/DiskImage/Formats/MSXDSK.cpp index 9fefffffd..7cd8b6bd1 100644 --- a/Storage/Disk/DiskImage/Formats/MSXDSK.cpp +++ b/Storage/Disk/DiskImage/Formats/MSXDSK.cpp @@ -11,8 +11,9 @@ #include "Utility/ImplicitSectors.hpp" namespace { - static const int sectors_per_track = 9; - static const int sector_size = 2; + const int sectors_per_track = 9; + const int sector_size = 2; + const off_t track_size = (128 << sector_size)*sectors_per_track; } using namespace Storage::Disk; @@ -22,7 +23,6 @@ MSXDSK::MSXDSK(const std::string &file_name) : // The only sanity check here is whether a sensible // geometry can be guessed. off_t file_size = file_.stats().st_size; - const off_t track_size = 512*9; // Throw if there would seemingly be an incomplete track. if(file_size % track_size) throw ErrorNotMSXDSK; From b98d5b790a5cb82a51344281c6fa1c70a3cebd40 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 27 Apr 2018 23:18:45 -0400 Subject: [PATCH 26/44] Finally unifies disk image file exceptions, and adds a placeholder for Apple DSK. --- Analyser/Static/StaticAnalyser.cpp | 2 + .../Clock Signal.xcodeproj/project.pbxproj | 12 ++++- Storage/Disk/DiskImage/DiskImage.hpp | 5 ++ Storage/Disk/DiskImage/Formats/AcornADF.cpp | 8 ++-- Storage/Disk/DiskImage/Formats/AcornADF.hpp | 8 +--- Storage/Disk/DiskImage/Formats/AppleDSK.cpp | 37 +++++++++++++++ Storage/Disk/DiskImage/Formats/AppleDSK.hpp | 47 +++++++++++++++++++ Storage/Disk/DiskImage/Formats/CPCDSK.cpp | 4 +- Storage/Disk/DiskImage/Formats/CPCDSK.hpp | 8 +--- Storage/Disk/DiskImage/Formats/D64.cpp | 2 +- Storage/Disk/DiskImage/Formats/D64.hpp | 8 +--- Storage/Disk/DiskImage/Formats/DMK.cpp | 8 ++-- Storage/Disk/DiskImage/Formats/DMK.hpp | 6 +-- Storage/Disk/DiskImage/Formats/G64.cpp | 4 +- Storage/Disk/DiskImage/Formats/G64.hpp | 12 ++--- Storage/Disk/DiskImage/Formats/HFE.cpp | 4 +- Storage/Disk/DiskImage/Formats/HFE.hpp | 8 ++-- Storage/Disk/DiskImage/Formats/MSXDSK.cpp | 8 ++-- Storage/Disk/DiskImage/Formats/MSXDSK.hpp | 5 -- Storage/Disk/DiskImage/Formats/NIB.cpp | 2 +- Storage/Disk/DiskImage/Formats/NIB.hpp | 4 -- Storage/Disk/DiskImage/Formats/OricMFMDSK.cpp | 4 +- Storage/Disk/DiskImage/Formats/OricMFMDSK.hpp | 4 -- Storage/Disk/DiskImage/Formats/SSD.cpp | 6 +-- Storage/Disk/DiskImage/Formats/SSD.hpp | 8 +--- Storage/Disk/DiskImage/Formats/WOZ.cpp | 6 ++- Storage/Disk/DiskImage/Formats/WOZ.hpp | 6 +-- Storage/FileHolder.cpp | 2 +- Storage/FileHolder.hpp | 4 +- 29 files changed, 150 insertions(+), 92 deletions(-) create mode 100644 Storage/Disk/DiskImage/Formats/AppleDSK.cpp create mode 100644 Storage/Disk/DiskImage/Formats/AppleDSK.hpp diff --git a/Analyser/Static/StaticAnalyser.cpp b/Analyser/Static/StaticAnalyser.cpp index c929125c0..7d85ac366 100644 --- a/Analyser/Static/StaticAnalyser.cpp +++ b/Analyser/Static/StaticAnalyser.cpp @@ -30,6 +30,7 @@ // Disks #include "../../Storage/Disk/DiskImage/Formats/AcornADF.hpp" +#include "../../Storage/Disk/DiskImage/Formats/AppleDSK.hpp" #include "../../Storage/Disk/DiskImage/Formats/CPCDSK.hpp" #include "../../Storage/Disk/DiskImage/Formats/D64.hpp" #include "../../Storage/Disk/DiskImage/Formats/G64.hpp" @@ -95,6 +96,7 @@ static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform:: Format("dmk", result.disks, Disk::DiskImageHolder, TargetPlatform::MSX) // DMK Format("dsd", result.disks, Disk::DiskImageHolder, TargetPlatform::Acorn) // DSD Format("dsk", result.disks, Disk::DiskImageHolder, TargetPlatform::AmstradCPC) // DSK (Amstrad CPC) + Format("dsk", result.disks, Disk::DiskImageHolder, TargetPlatform::AppleII) // DSK (Apple) Format("dsk", result.disks, Disk::DiskImageHolder, TargetPlatform::MSX) // DSK (MSX) Format("dsk", result.disks, Disk::DiskImageHolder, TargetPlatform::Oric) // DSK (Oric) Format("g64", result.disks, Disk::DiskImageHolder, TargetPlatform::Commodore) // G64 diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 6c3663dea..467ff17f5 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -8,6 +8,8 @@ /* Begin PBXBuildFile section */ 4B01A6881F22F0DB001FD6E3 /* Z80MemptrTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B01A6871F22F0DB001FD6E3 /* Z80MemptrTests.swift */; }; + 4B0333AF2094081A0050B93D /* AppleDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0333AD2094081A0050B93D /* AppleDSK.cpp */; }; + 4B0333B02094081A0050B93D /* AppleDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0333AD2094081A0050B93D /* AppleDSK.cpp */; }; 4B049CDD1DA3C82F00322067 /* BCDTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B049CDC1DA3C82F00322067 /* BCDTest.swift */; }; 4B055A7A1FAE78A00060FFFF /* SDL2.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B055A771FAE78210060FFFF /* SDL2.framework */; }; 4B055A7E1FAE84AA0060FFFF /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B055A7C1FAE84A50060FFFF /* main.cpp */; }; @@ -680,6 +682,8 @@ /* Begin PBXFileReference section */ 4B01A6871F22F0DB001FD6E3 /* Z80MemptrTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Z80MemptrTests.swift; sourceTree = ""; }; + 4B0333AD2094081A0050B93D /* AppleDSK.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = AppleDSK.cpp; sourceTree = ""; }; + 4B0333AE2094081A0050B93D /* AppleDSK.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = AppleDSK.hpp; sourceTree = ""; }; 4B046DC31CFE651500E9E45E /* CRTMachine.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CRTMachine.hpp; sourceTree = ""; }; 4B047075201ABC180047AB0D /* Cartridge.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Cartridge.hpp; sourceTree = ""; }; 4B049CDC1DA3C82F00322067 /* BCDTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BCDTest.swift; sourceTree = ""; }; @@ -1869,6 +1873,7 @@ isa = PBXGroup; children = ( 4B45188D1F75FD1B00926311 /* AcornADF.cpp */, + 4B0333AD2094081A0050B93D /* AppleDSK.cpp */, 4B45188F1F75FD1B00926311 /* CPCDSK.cpp */, 4B4518911F75FD1B00926311 /* D64.cpp */, 4BAF2B4C2004580C00480230 /* DMK.cpp */, @@ -1879,7 +1884,9 @@ 4B0F94FC208C1A1600FE41D9 /* NIB.cpp */, 4B4518971F75FD1B00926311 /* OricMFMDSK.cpp */, 4B4518991F75FD1B00926311 /* SSD.cpp */, + 4B6ED2EE208E2F8A0047B343 /* WOZ.cpp */, 4B45188E1F75FD1B00926311 /* AcornADF.hpp */, + 4B0333AE2094081A0050B93D /* AppleDSK.hpp */, 4B4518901F75FD1B00926311 /* CPCDSK.hpp */, 4B4518921F75FD1B00926311 /* D64.hpp */, 4BAF2B4D2004580C00480230 /* DMK.hpp */, @@ -1890,9 +1897,8 @@ 4B0F94FD208C1A1600FE41D9 /* NIB.hpp */, 4B4518981F75FD1B00926311 /* OricMFMDSK.hpp */, 4B45189A1F75FD1B00926311 /* SSD.hpp */, - 4BFDD7891F7F2DB4008579B9 /* Utility */, - 4B6ED2EE208E2F8A0047B343 /* WOZ.cpp */, 4B6ED2EF208E2F8A0047B343 /* WOZ.hpp */, + 4BFDD7891F7F2DB4008579B9 /* Utility */, ); path = Formats; sourceTree = ""; @@ -3650,6 +3656,7 @@ 4BAF2B4F2004580C00480230 /* DMK.cpp in Sources */, 4B055AD01FAE9B030060FFFF /* Tape.cpp in Sources */, 4B055A961FAE85BB0060FFFF /* Commodore.cpp in Sources */, + 4B0333B02094081A0050B93D /* AppleDSK.cpp in Sources */, 4B055ADE1FAE9B4C0060FFFF /* 6522Base.cpp in Sources */, 4B894535201967B4007DE474 /* AddressMapper.cpp in Sources */, 4B055AD41FAE9B0B0060FFFF /* Oric.cpp in Sources */, @@ -3727,6 +3734,7 @@ 4BBFFEE61F7B27F1005F3FEB /* TrackSerialiser.cpp in Sources */, 4BAE49582032881E004BE78E /* CSZX8081.mm in Sources */, 4BC3B74F1CD194CC00F86E85 /* Shader.cpp in Sources */, + 4B0333AF2094081A0050B93D /* AppleDSK.cpp in Sources */, 4B894518201967B4007DE474 /* ConfidenceCounter.cpp in Sources */, 4B89452E201967B4007DE474 /* StaticAnalyser.cpp in Sources */, 4B38F3481F2EC11D00D9235D /* AmstradCPC.cpp in Sources */, diff --git a/Storage/Disk/DiskImage/DiskImage.hpp b/Storage/Disk/DiskImage/DiskImage.hpp index 7237c8b53..86dbcb657 100644 --- a/Storage/Disk/DiskImage/DiskImage.hpp +++ b/Storage/Disk/DiskImage/DiskImage.hpp @@ -18,6 +18,11 @@ namespace Storage { namespace Disk { +enum class Error { + InvalidFormat = -2, + UnknownVersion = -3 +}; + /*! Models a disk image as a collection of tracks, plus a range of possible track positions. diff --git a/Storage/Disk/DiskImage/Formats/AcornADF.cpp b/Storage/Disk/DiskImage/Formats/AcornADF.cpp index 653b10bd2..df5864453 100644 --- a/Storage/Disk/DiskImage/Formats/AcornADF.cpp +++ b/Storage/Disk/DiskImage/Formats/AcornADF.cpp @@ -20,18 +20,18 @@ using namespace Storage::Disk; AcornADF::AcornADF(const std::string &file_name) : MFMSectorDump(file_name) { // very loose validation: the file needs to be a multiple of 256 bytes // and not ungainly large - if(file_.stats().st_size % static_cast(128 << sector_size)) throw ErrorNotAcornADF; - if(file_.stats().st_size < 7 * static_cast(128 << sector_size)) throw ErrorNotAcornADF; + if(file_.stats().st_size % static_cast(128 << sector_size)) throw Error::InvalidFormat; + if(file_.stats().st_size < 7 * static_cast(128 << sector_size)) throw Error::InvalidFormat; // check that the initial directory's 'Hugo's are present file_.seek(513, SEEK_SET); uint8_t bytes[4]; file_.read(bytes, 4); - if(bytes[0] != 'H' || bytes[1] != 'u' || bytes[2] != 'g' || bytes[3] != 'o') throw ErrorNotAcornADF; + if(bytes[0] != 'H' || bytes[1] != 'u' || bytes[2] != 'g' || bytes[3] != 'o') throw Error::InvalidFormat; file_.seek(0x6fb, SEEK_SET); file_.read(bytes, 4); - if(bytes[0] != 'H' || bytes[1] != 'u' || bytes[2] != 'g' || bytes[3] != 'o') throw ErrorNotAcornADF; + if(bytes[0] != 'H' || bytes[1] != 'u' || bytes[2] != 'g' || bytes[3] != 'o') throw Error::InvalidFormat; set_geometry(sectors_per_track, sector_size, 0, true); } diff --git a/Storage/Disk/DiskImage/Formats/AcornADF.hpp b/Storage/Disk/DiskImage/Formats/AcornADF.hpp index 7cd115308..11e24a95c 100644 --- a/Storage/Disk/DiskImage/Formats/AcornADF.hpp +++ b/Storage/Disk/DiskImage/Formats/AcornADF.hpp @@ -24,15 +24,11 @@ class AcornADF: public MFMSectorDump { /*! Construct an @c AcornADF containing content from the file with name @c file_name. - @throws ErrorCantOpen if this file can't be opened. - @throws ErrorNotAcornADF if the file doesn't appear to contain an Acorn .ADF format image. + @throws Storage::FileHolder::Error::CantOpen if this file can't be opened. + @throws Error::InvalidFormat if the file doesn't appear to contain an Acorn .ADF format image. */ AcornADF(const std::string &file_name); - enum { - ErrorNotAcornADF, - }; - int get_head_position_count() override; int get_head_count() override; diff --git a/Storage/Disk/DiskImage/Formats/AppleDSK.cpp b/Storage/Disk/DiskImage/Formats/AppleDSK.cpp new file mode 100644 index 000000000..cf1c68568 --- /dev/null +++ b/Storage/Disk/DiskImage/Formats/AppleDSK.cpp @@ -0,0 +1,37 @@ +// +// AppleDSK.cpp +// Clock Signal +// +// Created by Thomas Harte on 27/04/2018. +// Copyright © 2018 Thomas Harte. All rights reserved. +// + +#include "AppleDSK.hpp" + +using namespace Storage::Disk; + +namespace { + const int number_of_tracks = 35; + const int bytes_per_sector = 256; +} + +AppleDSK::AppleDSK(const std::string &file_name) : + file_(file_name) { + if(file_.stats().st_size % number_of_tracks*bytes_per_sector) throw Error::InvalidFormat; + + sectors_per_track_ = static_cast(file_.stats().st_size / (number_of_tracks*bytes_per_sector)); + if(sectors_per_track_ != 13 && sectors_per_track_ != 16) throw Error::InvalidFormat; +} + +int AppleDSK::get_head_position_count() { + return number_of_tracks * 4; +} + +std::shared_ptr AppleDSK::get_track_at_position(Track::Address address) { + const long file_offset = (address.position >> 2) * bytes_per_sector * sectors_per_track_; + file_.seek(file_offset, SEEK_SET); + +// std::vector track_data = file_.read(bytes_per_sector * sectors_per_track_); + + return nullptr; +} diff --git a/Storage/Disk/DiskImage/Formats/AppleDSK.hpp b/Storage/Disk/DiskImage/Formats/AppleDSK.hpp new file mode 100644 index 000000000..465c73e87 --- /dev/null +++ b/Storage/Disk/DiskImage/Formats/AppleDSK.hpp @@ -0,0 +1,47 @@ +// +// AppleDSK.hpp +// Clock Signal +// +// Created by Thomas Harte on 27/04/2018. +// Copyright © 2018 Thomas Harte. All rights reserved. +// + +#ifndef AppleDSK_hpp +#define AppleDSK_hpp + +#include "../DiskImage.hpp" +#include "../../../FileHolder.hpp" + +#include + +namespace Storage { +namespace Disk { + +/*! + Provides a @c DiskImage containing an Apple DSK disk image — a representation of sector contents, + implicitly numbered and located. +*/ +class AppleDSK: public DiskImage { + public: + /*! + Construct an @c AppleDSK containing content from the file with name @c file_name. + + @throws Storage::FileHolder::Error::CantOpen if this file can't be opened. + @throws Error::InvalidFormat if the file doesn't appear to contain a .G64 format image. + */ + AppleDSK(const std::string &file_name); + + // implemented to satisfy @c Disk + int get_head_position_count() override; + std::shared_ptr get_track_at_position(Track::Address address) override; + + private: + Storage::FileHolder file_; + int sectors_per_track_; +}; + +} +} + + +#endif /* AppleDSK_hpp */ diff --git a/Storage/Disk/DiskImage/Formats/CPCDSK.cpp b/Storage/Disk/DiskImage/Formats/CPCDSK.cpp index 2fa9f5751..db5090b97 100644 --- a/Storage/Disk/DiskImage/Formats/CPCDSK.cpp +++ b/Storage/Disk/DiskImage/Formats/CPCDSK.cpp @@ -27,7 +27,7 @@ CPCDSK::CPCDSK(const std::string &file_name) : is_extended_ = true; file.seek(0, SEEK_SET); if(!file.check_signature("EXTENDED")) - throw ErrorNotCPCDSK; + throw Error::InvalidFormat; } // Don't really care about about the creator; skip. @@ -145,7 +145,7 @@ CPCDSK::CPCDSK(const std::string &file_name) : if(declared_data_size > data_size) { number_of_samplings = declared_data_size / data_size; if(declared_data_size % data_size) - throw ErrorNotCPCDSK; + throw Error::InvalidFormat; } else { stored_data_size = declared_data_size; } diff --git a/Storage/Disk/DiskImage/Formats/CPCDSK.hpp b/Storage/Disk/DiskImage/Formats/CPCDSK.hpp index 5d19db4dc..d946ea3f8 100644 --- a/Storage/Disk/DiskImage/Formats/CPCDSK.hpp +++ b/Storage/Disk/DiskImage/Formats/CPCDSK.hpp @@ -27,15 +27,11 @@ class CPCDSK: public DiskImage { /*! Construct an @c AcornADF containing content from the file with name @c file_name. - @throws ErrorCantOpen if this file can't be opened. - @throws ErrorNotAcornADF if the file doesn't appear to contain an Acorn .ADF format image. + @throws Storage::FileHolder::Error::CantOpen if this file can't be opened. + @throws Error::InvalidFormat if the file doesn't appear to contain an Acorn .ADF format image. */ CPCDSK(const std::string &file_name); - enum { - ErrorNotCPCDSK, - }; - // implemented to satisfy @c Disk int get_head_position_count() override; int get_head_count() override; diff --git a/Storage/Disk/DiskImage/Formats/D64.cpp b/Storage/Disk/DiskImage/Formats/D64.cpp index 8522b7319..d82826b1c 100644 --- a/Storage/Disk/DiskImage/Formats/D64.cpp +++ b/Storage/Disk/DiskImage/Formats/D64.cpp @@ -22,7 +22,7 @@ D64::D64(const std::string &file_name) : // in D64, this is it for validation without imposing potential false-negative tests — check that // the file size appears to be correct. Stone-age stuff. if(file_.stats().st_size != 174848 && file_.stats().st_size != 196608) - throw ErrorNotD64; + throw Error::InvalidFormat; number_of_tracks_ = (file_.stats().st_size == 174848) ? 35 : 40; diff --git a/Storage/Disk/DiskImage/Formats/D64.hpp b/Storage/Disk/DiskImage/Formats/D64.hpp index d395ecd15..a412607ac 100644 --- a/Storage/Disk/DiskImage/Formats/D64.hpp +++ b/Storage/Disk/DiskImage/Formats/D64.hpp @@ -23,15 +23,11 @@ class D64: public DiskImage { /*! Construct a @c D64 containing content from the file with name @c file_name. - @throws ErrorCantOpen if this file can't be opened. - @throws ErrorNotD64 if the file doesn't appear to contain a .D64 format image. + @throws Storage::FileHolder::Error::CantOpen if this file can't be opened. + @throws Error::InvalidFormat if the file doesn't appear to contain a .D64 format image. */ D64(const std::string &file_name); - enum { - ErrorNotD64, - }; - // implemented to satisfy @c Disk int get_head_position_count() override; using DiskImage::get_is_read_only; diff --git a/Storage/Disk/DiskImage/Formats/DMK.cpp b/Storage/Disk/DiskImage/Formats/DMK.cpp index 13a1af8c6..92fe728a7 100644 --- a/Storage/Disk/DiskImage/Formats/DMK.cpp +++ b/Storage/Disk/DiskImage/Formats/DMK.cpp @@ -35,9 +35,9 @@ std::unique_ptr new_encoder(Storage::Disk::PCM DMK::DMK(const std::string &file_name) : file_(file_name) { // Determine whether this DMK represents a read-only disk (whether intentionally, - // or by virtue of placement). + // or by virtue of filesystem placement). uint8_t read_only_byte = file_.get8(); - if(read_only_byte != 0x00 && read_only_byte != 0xff) throw ErrorNotDMK; + if(read_only_byte != 0x00 && read_only_byte != 0xff) throw Error::InvalidFormat; is_read_only_ = (read_only_byte == 0xff) || file_.get_is_known_read_only(); // Read track count and size. @@ -46,7 +46,7 @@ DMK::DMK(const std::string &file_name) : // Track length must be at least 0x80, as that's the size of the IDAM // table before track contents. - if(track_length_ < 0x80) throw ErrorNotDMK; + if(track_length_ < 0x80) throw Error::InvalidFormat; // Read the file flags and apply them. uint8_t flags = file_.get8(); @@ -58,7 +58,7 @@ DMK::DMK(const std::string &file_name) : // "in the emulator's native format". file_.seek(0xc, SEEK_SET); uint32_t format = file_.get32le(); - if(format) throw ErrorNotDMK; + if(format) throw Error::InvalidFormat; } int DMK::get_head_position_count() { diff --git a/Storage/Disk/DiskImage/Formats/DMK.hpp b/Storage/Disk/DiskImage/Formats/DMK.hpp index 6be4eaed4..af772530e 100644 --- a/Storage/Disk/DiskImage/Formats/DMK.hpp +++ b/Storage/Disk/DiskImage/Formats/DMK.hpp @@ -26,14 +26,10 @@ class DMK: public DiskImage { /*! Construct a @c DMK containing content from the file with name @c file_name. - @throws ErrorNotDMK if this file doesn't appear to be a DMK. + @throws Error::InvalidFormat if this file doesn't appear to be a DMK. */ DMK(const std::string &file_name); - enum { - ErrorNotDMK - }; - // implemented to satisfy @c Disk int get_head_position_count() override; int get_head_count() override; diff --git a/Storage/Disk/DiskImage/Formats/G64.cpp b/Storage/Disk/DiskImage/Formats/G64.cpp index c80460c88..87ee86763 100644 --- a/Storage/Disk/DiskImage/Formats/G64.cpp +++ b/Storage/Disk/DiskImage/Formats/G64.cpp @@ -19,11 +19,11 @@ using namespace Storage::Disk; G64::G64(const std::string &file_name) : file_(file_name) { // read and check the file signature - if(!file_.check_signature("GCR-1541")) throw ErrorNotG64; + if(!file_.check_signature("GCR-1541")) throw Error::InvalidFormat; // check the version number int version = file_.get8(); - if(version != 0) throw ErrorUnknownVersion; + if(version != 0) throw Error::UnknownVersion; // get the number of tracks and track size number_of_tracks_ = file_.get8(); diff --git a/Storage/Disk/DiskImage/Formats/G64.hpp b/Storage/Disk/DiskImage/Formats/G64.hpp index c10ef7e04..2b27fca73 100644 --- a/Storage/Disk/DiskImage/Formats/G64.hpp +++ b/Storage/Disk/DiskImage/Formats/G64.hpp @@ -25,18 +25,12 @@ class G64: public DiskImage { /*! Construct a @c G64 containing content from the file with name @c file_name. - @throws ErrorCantOpen if this file can't be opened. - @throws ErrorNotG64 if the file doesn't appear to contain a .G64 format image. - @throws ErrorUnknownVersion if this file appears to be a .G64 but has an unrecognised version number. + @throws Storage::FileHolder::Error::CantOpen if this file can't be opened. + @throws Error::InvalidFormat if the file doesn't appear to contain a .G64 format image. + @throws Error::UnknownVersion if this file appears to be a .G64 but has an unrecognised version number. */ G64(const std::string &file_name); - enum { - ErrorCantOpen, - ErrorNotG64, - ErrorUnknownVersion - }; - // implemented to satisfy @c Disk int get_head_position_count() override; std::shared_ptr get_track_at_position(Track::Address address) override; diff --git a/Storage/Disk/DiskImage/Formats/HFE.cpp b/Storage/Disk/DiskImage/Formats/HFE.cpp index 14e0c0004..05549a2cb 100644 --- a/Storage/Disk/DiskImage/Formats/HFE.cpp +++ b/Storage/Disk/DiskImage/Formats/HFE.cpp @@ -16,9 +16,9 @@ using namespace Storage::Disk; HFE::HFE(const std::string &file_name) : file_(file_name) { - if(!file_.check_signature("HXCPICFE")) throw ErrorNotHFE; + if(!file_.check_signature("HXCPICFE")) throw Error::InvalidFormat; - if(file_.get8()) throw ErrorNotHFE; + if(file_.get8()) throw Error::UnknownVersion; track_count_ = file_.get8(); head_count_ = file_.get8(); diff --git a/Storage/Disk/DiskImage/Formats/HFE.hpp b/Storage/Disk/DiskImage/Formats/HFE.hpp index 8283a0f6a..bf66af727 100644 --- a/Storage/Disk/DiskImage/Formats/HFE.hpp +++ b/Storage/Disk/DiskImage/Formats/HFE.hpp @@ -25,15 +25,13 @@ class HFE: public DiskImage { /*! Construct an @c HFE containing content from the file with name @c file_name. - @throws ErrorNotHFE if the file doesn't appear to contain a .SSD format image. + @throws Storage::FileHolder::Error::CantOpen if this file can't be opened. + @throws Error::InvalidFormat if the file doesn't appear to contain an .HFE format image. + @throws Error::UnknownVersion if the file looks correct but is an unsupported version. */ HFE(const std::string &file_name); ~HFE(); - enum { - ErrorNotHFE - }; - // implemented to satisfy @c Disk int get_head_position_count() override; int get_head_count() override; diff --git a/Storage/Disk/DiskImage/Formats/MSXDSK.cpp b/Storage/Disk/DiskImage/Formats/MSXDSK.cpp index 7cd8b6bd1..9b03de6d6 100644 --- a/Storage/Disk/DiskImage/Formats/MSXDSK.cpp +++ b/Storage/Disk/DiskImage/Formats/MSXDSK.cpp @@ -25,16 +25,16 @@ MSXDSK::MSXDSK(const std::string &file_name) : off_t file_size = file_.stats().st_size; // Throw if there would seemingly be an incomplete track. - if(file_size % track_size) throw ErrorNotMSXDSK; + if(file_size % track_size) throw Error::InvalidFormat; track_count_ = static_cast(file_size / track_size); head_count_ = 1; // Throw if too large or too small or too large for single sided and // clearly not double sided. - if(track_count_ < 40) throw ErrorNotMSXDSK; - if(track_count_ > 82*2) throw ErrorNotMSXDSK; - if(track_count_ > 82 && track_count_&1) throw ErrorNotMSXDSK; + if(track_count_ < 40) throw Error::InvalidFormat; + if(track_count_ > 82*2) throw Error::InvalidFormat; + if(track_count_ > 82 && track_count_&1) throw Error::InvalidFormat; // The below effectively prefers the idea of a single-sided 80-track disk // to a double-sided 40-track disk. Emulators have to guess. diff --git a/Storage/Disk/DiskImage/Formats/MSXDSK.hpp b/Storage/Disk/DiskImage/Formats/MSXDSK.hpp index 8bf8936c1..74d7f9247 100644 --- a/Storage/Disk/DiskImage/Formats/MSXDSK.hpp +++ b/Storage/Disk/DiskImage/Formats/MSXDSK.hpp @@ -23,11 +23,6 @@ namespace Disk { class MSXDSK: public MFMSectorDump { public: MSXDSK(const std::string &file_name); - - enum { - ErrorNotMSXDSK, - }; - int get_head_position_count() override; int get_head_count() override; diff --git a/Storage/Disk/DiskImage/Formats/NIB.cpp b/Storage/Disk/DiskImage/Formats/NIB.cpp index 41cab6631..afc5870f3 100644 --- a/Storage/Disk/DiskImage/Formats/NIB.cpp +++ b/Storage/Disk/DiskImage/Formats/NIB.cpp @@ -26,7 +26,7 @@ NIB::NIB(const std::string &file_name) : file_(file_name) { // A NIB should be 35 tracks, each 6656 bytes long. if(file_.stats().st_size != track_length*number_of_tracks) { - throw ErrorNotNIB; + throw Error::InvalidFormat; } // TODO: all other validation. I.e. does this look like a GCR disk? diff --git a/Storage/Disk/DiskImage/Formats/NIB.hpp b/Storage/Disk/DiskImage/Formats/NIB.hpp index fff52ca95..bde1f4322 100644 --- a/Storage/Disk/DiskImage/Formats/NIB.hpp +++ b/Storage/Disk/DiskImage/Formats/NIB.hpp @@ -24,10 +24,6 @@ class NIB: public DiskImage { public: NIB(const std::string &file_name); - enum { - ErrorNotNIB, - }; - int get_head_position_count() override; std::shared_ptr<::Storage::Disk::Track> get_track_at_position(::Storage::Disk::Track::Address address) override; diff --git a/Storage/Disk/DiskImage/Formats/OricMFMDSK.cpp b/Storage/Disk/DiskImage/Formats/OricMFMDSK.cpp index d43700067..8c0f754e4 100644 --- a/Storage/Disk/DiskImage/Formats/OricMFMDSK.cpp +++ b/Storage/Disk/DiskImage/Formats/OricMFMDSK.cpp @@ -19,14 +19,14 @@ using namespace Storage::Disk; OricMFMDSK::OricMFMDSK(const std::string &file_name) : file_(file_name) { if(!file_.check_signature("MFM_DISK")) - throw ErrorNotOricMFMDSK; + throw Error::InvalidFormat; head_count_ = file_.get32le(); track_count_ = file_.get32le(); geometry_type_ = file_.get32le(); if(geometry_type_ < 1 || geometry_type_ > 2) - throw ErrorNotOricMFMDSK; + throw Error::InvalidFormat; } int OricMFMDSK::get_head_position_count() { diff --git a/Storage/Disk/DiskImage/Formats/OricMFMDSK.hpp b/Storage/Disk/DiskImage/Formats/OricMFMDSK.hpp index c6b6d60be..14fa16ecd 100644 --- a/Storage/Disk/DiskImage/Formats/OricMFMDSK.hpp +++ b/Storage/Disk/DiskImage/Formats/OricMFMDSK.hpp @@ -29,10 +29,6 @@ class OricMFMDSK: public DiskImage { */ OricMFMDSK(const std::string &file_name); - enum { - ErrorNotOricMFMDSK, - }; - // implemented to satisfy @c DiskImage int get_head_position_count() override; int get_head_count() override; diff --git a/Storage/Disk/DiskImage/Formats/SSD.cpp b/Storage/Disk/DiskImage/Formats/SSD.cpp index 280521d7d..7d8647023 100644 --- a/Storage/Disk/DiskImage/Formats/SSD.cpp +++ b/Storage/Disk/DiskImage/Formats/SSD.cpp @@ -23,9 +23,9 @@ SSD::SSD(const std::string &file_name) : MFMSectorDump(file_name) { // very loose validation: the file needs to be a multiple of 256 bytes // and not ungainly large - if(file_.stats().st_size & 255) throw ErrorNotSSD; - if(file_.stats().st_size < 512) throw ErrorNotSSD; - if(file_.stats().st_size > 800*256) throw ErrorNotSSD; + if(file_.stats().st_size & 255) throw Error::InvalidFormat; + if(file_.stats().st_size < 512) throw Error::InvalidFormat; + if(file_.stats().st_size > 800*256) throw Error::InvalidFormat; // this has two heads if the suffix is .dsd, one if it's .ssd head_count_ = (tolower(file_name[file_name.size() - 3]) == 'd') ? 2 : 1; diff --git a/Storage/Disk/DiskImage/Formats/SSD.hpp b/Storage/Disk/DiskImage/Formats/SSD.hpp index 58f67027c..7eeba9173 100644 --- a/Storage/Disk/DiskImage/Formats/SSD.hpp +++ b/Storage/Disk/DiskImage/Formats/SSD.hpp @@ -22,15 +22,11 @@ class SSD: public MFMSectorDump { /*! Construct an @c SSD containing content from the file with name @c file_name. - @throws ErrorCantOpen if this file can't be opened. - @throws ErrorNotSSD if the file doesn't appear to contain a .SSD format image. + @throws Storage::FileHolder::Error::CantOpen if this file can't be opened. + @throws Error::InvalidFormat if the file doesn't appear to contain a .SSD format image. */ SSD(const std::string &file_name); - enum { - ErrorNotSSD, - }; - int get_head_position_count() override; int get_head_count() override; diff --git a/Storage/Disk/DiskImage/Formats/WOZ.cpp b/Storage/Disk/DiskImage/Formats/WOZ.cpp index 1d76b5f6c..da6395a4e 100644 --- a/Storage/Disk/DiskImage/Formats/WOZ.cpp +++ b/Storage/Disk/DiskImage/Formats/WOZ.cpp @@ -19,12 +19,13 @@ WOZ::WOZ(const std::string &file_name) : 'W', 'O', 'Z', '1', static_cast(0xff), 0x0a, 0x0d, 0x0a }; - if(!file_.check_signature(signature, 8)) throw ErrorNotWOZ; + if(!file_.check_signature(signature, 8)) throw Error::InvalidFormat; // TODO: check CRC32, instead of skipping it. file_.seek(4, SEEK_CUR); // Parse all chunks up front. + bool has_tmap = false; while(true) { const uint32_t chunk_id = file_.get32le(); const uint32_t chunk_size = file_.get32le(); @@ -48,6 +49,7 @@ WOZ::WOZ(const std::string &file_name) : case CK("TMAP"): { file_.read(track_map_, 160); + has_tmap = true; } break; case CK("TRKS"): { @@ -63,6 +65,8 @@ WOZ::WOZ(const std::string &file_name) : file_.seek(end_of_chunk, SEEK_SET); } + + if(tracks_offset_ == -1 || !has_tmap) throw Error::InvalidFormat; } int WOZ::get_head_position_count() { diff --git a/Storage/Disk/DiskImage/Formats/WOZ.hpp b/Storage/Disk/DiskImage/Formats/WOZ.hpp index 16abba35c..afae68e5e 100644 --- a/Storage/Disk/DiskImage/Formats/WOZ.hpp +++ b/Storage/Disk/DiskImage/Formats/WOZ.hpp @@ -24,10 +24,6 @@ class WOZ: public DiskImage { public: WOZ(const std::string &file_name); - enum { - ErrorNotWOZ - }; - int get_head_position_count() override; int get_head_count() override; std::shared_ptr get_track_at_position(Track::Address address) override; @@ -37,7 +33,7 @@ class WOZ: public DiskImage { bool is_read_only_ = false; bool is_3_5_disk_ = false; uint8_t track_map_[160]; - long tracks_offset_ = 0; + long tracks_offset_ = -1; }; } diff --git a/Storage/FileHolder.cpp b/Storage/FileHolder.cpp index d63d2520d..2825db34b 100644 --- a/Storage/FileHolder.cpp +++ b/Storage/FileHolder.cpp @@ -38,7 +38,7 @@ FileHolder::FileHolder(const std::string &file_name, FileMode ideal_mode) break; } - if(!file_) throw ErrorCantOpen; + if(!file_) throw Error::CantOpen; } uint32_t FileHolder::get32le() { diff --git a/Storage/FileHolder.hpp b/Storage/FileHolder.hpp index f4e14b1df..e639b03d3 100644 --- a/Storage/FileHolder.hpp +++ b/Storage/FileHolder.hpp @@ -20,8 +20,8 @@ namespace Storage { class FileHolder final { public: - enum { - ErrorCantOpen = -1 + enum class Error { + CantOpen = -1 }; enum class FileMode { From 7f03f5d02f5b7b959d009ca187d4dbc5b58e5308 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 28 Apr 2018 15:18:48 -0400 Subject: [PATCH 27/44] Makes a first attempt at six-and-two encoding for DSKs. --- Storage/Disk/DiskImage/Formats/AppleDSK.cpp | 28 +++- Storage/Disk/Encodings/AppleGCR.cpp | 144 +++++++++++++------- Storage/Disk/Encodings/AppleGCR.hpp | 16 ++- 3 files changed, 127 insertions(+), 61 deletions(-) diff --git a/Storage/Disk/DiskImage/Formats/AppleDSK.cpp b/Storage/Disk/DiskImage/Formats/AppleDSK.cpp index cf1c68568..1b11fd490 100644 --- a/Storage/Disk/DiskImage/Formats/AppleDSK.cpp +++ b/Storage/Disk/DiskImage/Formats/AppleDSK.cpp @@ -8,6 +8,9 @@ #include "AppleDSK.hpp" +#include "../../Track/PCMTrack.hpp" +#include "../../Encodings/AppleGCR.hpp" + using namespace Storage::Disk; namespace { @@ -30,8 +33,29 @@ int AppleDSK::get_head_position_count() { std::shared_ptr AppleDSK::get_track_at_position(Track::Address address) { const long file_offset = (address.position >> 2) * bytes_per_sector * sectors_per_track_; file_.seek(file_offset, SEEK_SET); + const std::vector track_data = file_.read(static_cast(bytes_per_sector * sectors_per_track_)); -// std::vector track_data = file_.read(bytes_per_sector * sectors_per_track_); + std::vector segments; + const uint8_t track = static_cast(address.position >> 2); - return nullptr; + // In either case below, the code aims for exactly 50,000 bits per track. + if(sectors_per_track_ == 16) { + + // Write the sectors. + for(uint8_t c = 0; c < 16; ++c) { + segments.push_back(Encodings::AppleGCR::six_and_two_sync(10)); + segments.push_back(Encodings::AppleGCR::header(0, track, c)); + segments.push_back(Encodings::AppleGCR::six_and_two_sync(10)); + segments.push_back(Encodings::AppleGCR::six_and_two_data(&track_data[c * 256])); + segments.push_back(Encodings::AppleGCR::six_and_two_sync(10)); + } + + int encoded_length = (80 + 112 + 80 + 2848 + 80) * sectors_per_track_; + segments.push_back(Encodings::AppleGCR::six_and_two_sync((50000 - encoded_length) >> 3)); + + } else { + + } + + return std::shared_ptr(new PCMTrack(segments)); } diff --git a/Storage/Disk/Encodings/AppleGCR.cpp b/Storage/Disk/Encodings/AppleGCR.cpp index c33d19c0e..20d81106b 100644 --- a/Storage/Disk/Encodings/AppleGCR.cpp +++ b/Storage/Disk/Encodings/AppleGCR.cpp @@ -8,55 +8,31 @@ #include "AppleGCR.hpp" -using namespace Storage::Encodings; +namespace { -unsigned int AppleGCR::five_and_three_encoding_for_value(int value) { - static const unsigned int values[] = { - 0xab, 0xad, 0xae, 0xaf, 0xb5, 0xb6, 0xb7, 0xba, - 0xbb, 0xbd, 0xbe, 0xbf, 0xd6, 0xd7, 0xda, 0xdb, - 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xed, 0xee, 0xef, - 0xf5, 0xf6, 0xf7, 0xfa, 0xfb, 0xfd, 0xfe, 0xff - }; - return values[value & 0x1f]; -} +const unsigned int five_and_three_mapping[] = { + 0xab, 0xad, 0xae, 0xaf, 0xb5, 0xb6, 0xb7, 0xba, + 0xbb, 0xbd, 0xbe, 0xbf, 0xd6, 0xd7, 0xda, 0xdb, + 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xed, 0xee, 0xef, + 0xf5, 0xf6, 0xf7, 0xfa, 0xfb, 0xfd, 0xfe, 0xff +}; -void AppleGCR::encode_five_and_three_block(uint8_t *destination, uint8_t *source) { - destination[0] = static_cast(five_and_three_encoding_for_value( source[0] >> 3 )); - destination[1] = static_cast(five_and_three_encoding_for_value( (source[0] << 2) | (source[1] >> 6) )); - destination[2] = static_cast(five_and_three_encoding_for_value( source[1] >> 1 )); - destination[3] = static_cast(five_and_three_encoding_for_value( (source[1] << 4) | (source[2] >> 4) )); - destination[4] = static_cast(five_and_three_encoding_for_value( (source[2] << 1) | (source[3] >> 7) )); - destination[5] = static_cast(five_and_three_encoding_for_value( source[3] >> 2 )); - destination[6] = static_cast(five_and_three_encoding_for_value( (source[3] << 3) | (source[4] >> 5) )); - destination[7] = static_cast(five_and_three_encoding_for_value( source[4] )); -} - -unsigned int AppleGCR::six_and_two_encoding_for_value(int value) { - static const unsigned int values[] = { - 0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6, - 0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3, - 0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc, - 0xbd, 0xbe, 0xbf, 0xcb, 0xcd, 0xce, 0xcf, 0xd3, - 0xd6, 0xd7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, - 0xdf, 0xe5, 0xe6, 0xe7, 0xe9, 0xea, 0xeb, 0xec, - 0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, - 0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff - }; - return values[value & 0x3f]; -} - -void AppleGCR::encode_six_and_two_block(uint8_t *destination, uint8_t *source) { - destination[0] = static_cast(six_and_two_encoding_for_value( source[0] >> 2 )); - destination[1] = static_cast(six_and_two_encoding_for_value( (source[0] << 4) | (source[1] >> 4) )); - destination[2] = static_cast(six_and_two_encoding_for_value( (source[1] << 2) | (source[2] >> 6) )); - destination[3] = static_cast(six_and_two_encoding_for_value( source[2] )); -} +const uint8_t six_and_two_mapping[] = { + 0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6, + 0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3, + 0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc, + 0xbd, 0xbe, 0xbf, 0xcb, 0xcd, 0xce, 0xcf, 0xd3, + 0xd6, 0xd7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, + 0xdf, 0xe5, 0xe6, 0xe7, 0xe9, 0xea, 0xeb, 0xec, + 0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, + 0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff +}; /*! Produces a PCM segment containing @c length sync bytes, each aligned to the beginning of a @c bit_size -sized window. */ -static Storage::Disk::PCMSegment sync(int length, int bit_size) { +Storage::Disk::PCMSegment sync(int length, int bit_size) { Storage::Disk::PCMSegment segment; // Allocate sufficient storage. @@ -73,6 +49,30 @@ static Storage::Disk::PCMSegment sync(int length, int bit_size) { return segment; } +} + +using namespace Storage::Encodings; + + +/*void AppleGCR::encode_five_and_three_block(uint8_t *destination, uint8_t *source) { + destination[0] = static_cast(five_and_three_encoding_for_value( source[0] >> 3 )); + destination[1] = static_cast(five_and_three_encoding_for_value( (source[0] << 2) | (source[1] >> 6) )); + destination[2] = static_cast(five_and_three_encoding_for_value( source[1] >> 1 )); + destination[3] = static_cast(five_and_three_encoding_for_value( (source[1] << 4) | (source[2] >> 4) )); + destination[4] = static_cast(five_and_three_encoding_for_value( (source[2] << 1) | (source[3] >> 7) )); + destination[5] = static_cast(five_and_three_encoding_for_value( source[3] >> 2 )); + destination[6] = static_cast(five_and_three_encoding_for_value( (source[3] << 3) | (source[4] >> 5) )); + destination[7] = static_cast(five_and_three_encoding_for_value( source[4] )); +}*/ + + +/*void AppleGCR::encode_six_and_two_block(uint8_t *destination, uint8_t *source) { + destination[0] = static_cast(six_and_two_encoding_for_value( source[0] >> 2 )); + destination[1] = static_cast(six_and_two_encoding_for_value( (source[0] << 4) | (source[1] >> 4) )); + destination[2] = static_cast(six_and_two_encoding_for_value( (source[1] << 2) | (source[2] >> 6) )); + destination[3] = static_cast(six_and_two_encoding_for_value( source[2] )); +}*/ + Storage::Disk::PCMSegment AppleGCR::six_and_two_sync(int length) { return sync(length, 9); } @@ -111,7 +111,7 @@ Storage::Disk::PCMSegment AppleGCR::header(uint8_t volume, uint8_t track, uint8_ return segment; } -Storage::Disk::PCMSegment AppleGCR::five_and_three_data(uint8_t *source) { +Storage::Disk::PCMSegment AppleGCR::five_and_three_data(const uint8_t *source) { Storage::Disk::PCMSegment segment; segment.data.resize(410 + 7); @@ -119,25 +119,63 @@ Storage::Disk::PCMSegment AppleGCR::five_and_three_data(uint8_t *source) { segment.data[1] = header_prologue[1]; segment.data[2] = header_prologue[2]; - std::size_t source_pointer = 0; - std::size_t destination_pointer = 3; - while(source_pointer < 255) { - encode_five_and_three_block(&segment.data[destination_pointer], &source[source_pointer]); - - source_pointer += 5; - destination_pointer += 8; - } +// std::size_t source_pointer = 0; +// std::size_t destination_pointer = 3; +// while(source_pointer < 255) { +// encode_five_and_three_block(&segment.data[destination_pointer], &source[source_pointer]); +// +// source_pointer += 5; +// destination_pointer += 8; +// } return segment; } -Storage::Disk::PCMSegment AppleGCR::six_and_two_data(uint8_t *source) { +Storage::Disk::PCMSegment AppleGCR::six_and_two_data(const uint8_t *source) { Storage::Disk::PCMSegment segment; - segment.data.resize(342 + 7); + segment.data.resize(349); + + // Add the prologue and epilogue. segment.data[0] = header_prologue[0]; segment.data[1] = header_prologue[1]; segment.data[2] = header_prologue[2]; + segment.data[346] = epilogue[0]; + segment.data[347] = epilogue[1]; + segment.data[348] = epilogue[2]; + + // Fill in byte values: the first 86 bytes contain shuffled + // and combined copies of the bottom two bits of the sector + // contents; the 256 bytes afterwards are the remaining + // six bits. + const uint8_t bit_shuffle[] = {0, 2, 1, 3}; + for(std::size_t c = 0; c < 85; ++c) { + segment.data[3 + c] = + static_cast( + bit_shuffle[source[c]&3] | + (bit_shuffle[source[c + 85]&3] << 2) | + (bit_shuffle[source[c + 170]&3] << 4) + ); + } + segment.data[3 + 85] = bit_shuffle[source[255]&3]; + + for(std::size_t c = 0; c < 256; ++c) { + segment.data[3 + 85 + c] = source[c] >> 2; + } + + // Exclusive OR each byte with the one before it. + segment.data[344] = segment.data[343]; + std::size_t location = 343; + while(location > 3) { + segment.data[location] ^= segment.data[location-1]; + --location; + } + + // Map six-bit values up to full bytes. + for(std::size_t c = 0; c < 343; ++c) { + segment.data[c] = six_and_two_mapping[segment.data[c]]; + } + return segment; } diff --git a/Storage/Disk/Encodings/AppleGCR.hpp b/Storage/Disk/Encodings/AppleGCR.hpp index a09df4624..9073e3a7d 100644 --- a/Storage/Disk/Encodings/AppleGCR.hpp +++ b/Storage/Disk/Encodings/AppleGCR.hpp @@ -20,22 +20,22 @@ namespace AppleGCR { /*! @returns the eight-bit 13-sector GCR encoding for the low five bits of @c value. */ - unsigned int five_and_three_encoding_for_value(int value); +// unsigned int five_and_three_encoding_for_value(int value); /*! @returns the eight-bit 16-sector GCR encoding for the low six bits of @c value. */ - unsigned int six_and_two_encoding_for_value(int value); +// unsigned int six_and_two_encoding_for_value(int value); /*! A block is defined to be five source bytes, which encodes to eight GCR bytes. */ - void encode_five_and_three_block(uint8_t *destination, uint8_t *source); +// void encode_five_and_three_block(uint8_t *destination, uint8_t *source); /*! A block is defined to be three source bytes, which encodes to four GCR bytes. */ - void encode_six_and_two_block(uint8_t *destination, uint8_t *source); +// void encode_six_and_two_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. @@ -54,12 +54,16 @@ namespace AppleGCR { /// Describes the epilogue that ends both data sections and headers. const uint8_t epilogue[3] = {0xde, 0xaa, 0xeb}; + /*! + Produces the Apple-standard '4 and 4' per-sector header. This is the same + for both the 13- and 16-sector formats, and is 112 bits long. + */ Storage::Disk::PCMSegment header(uint8_t volume, uint8_t track, uint8_t sector); - Storage::Disk::PCMSegment six_and_two_data(uint8_t *source); + Storage::Disk::PCMSegment six_and_two_data(const uint8_t *source); Storage::Disk::PCMSegment six_and_two_sync(int length); - Storage::Disk::PCMSegment five_and_three_data(uint8_t *source); + Storage::Disk::PCMSegment five_and_three_data(const uint8_t *source); Storage::Disk::PCMSegment five_and_three_sync(int length); } From 5b35c88be2ce0e923255a6378beebbed8ac651d5 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 28 Apr 2018 15:47:50 -0400 Subject: [PATCH 28/44] Corrections: data segments now correctly announce their number of bits, and tracks aren't oversized. --- Storage/Disk/DiskImage/Formats/AppleDSK.cpp | 7 ++++--- Storage/Disk/Encodings/AppleGCR.cpp | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Storage/Disk/DiskImage/Formats/AppleDSK.cpp b/Storage/Disk/DiskImage/Formats/AppleDSK.cpp index 1b11fd490..b32a63876 100644 --- a/Storage/Disk/DiskImage/Formats/AppleDSK.cpp +++ b/Storage/Disk/DiskImage/Formats/AppleDSK.cpp @@ -40,7 +40,6 @@ std::shared_ptr AppleDSK::get_track_at_position(Track::Address address) { // In either case below, the code aims for exactly 50,000 bits per track. if(sectors_per_track_ == 16) { - // Write the sectors. for(uint8_t c = 0; c < 16; ++c) { segments.push_back(Encodings::AppleGCR::six_and_two_sync(10)); @@ -50,9 +49,11 @@ std::shared_ptr AppleDSK::get_track_at_position(Track::Address address) { segments.push_back(Encodings::AppleGCR::six_and_two_sync(10)); } + // Pad if necessary. int encoded_length = (80 + 112 + 80 + 2848 + 80) * sectors_per_track_; - segments.push_back(Encodings::AppleGCR::six_and_two_sync((50000 - encoded_length) >> 3)); - + if(encoded_length < 50000) { + segments.push_back(Encodings::AppleGCR::six_and_two_sync((50000 - encoded_length) >> 3)); + } } else { } diff --git a/Storage/Disk/Encodings/AppleGCR.cpp b/Storage/Disk/Encodings/AppleGCR.cpp index 20d81106b..a7a137b9e 100644 --- a/Storage/Disk/Encodings/AppleGCR.cpp +++ b/Storage/Disk/Encodings/AppleGCR.cpp @@ -135,6 +135,7 @@ Storage::Disk::PCMSegment AppleGCR::six_and_two_data(const uint8_t *source) { Storage::Disk::PCMSegment segment; segment.data.resize(349); + segment.number_of_bits = static_cast(segment.data.size() * 8); // Add the prologue and epilogue. segment.data[0] = header_prologue[0]; @@ -174,7 +175,7 @@ Storage::Disk::PCMSegment AppleGCR::six_and_two_data(const uint8_t *source) { // Map six-bit values up to full bytes. for(std::size_t c = 0; c < 343; ++c) { - segment.data[c] = six_and_two_mapping[segment.data[c]]; + segment.data[3 + c] = six_and_two_mapping[segment.data[3 + c]]; } return segment; From 45cf28e0eb870713e41479991eb4fadff0923b17 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 28 Apr 2018 15:48:49 -0400 Subject: [PATCH 29/44] Corrects sync lengths. --- Storage/Disk/Encodings/AppleGCR.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Storage/Disk/Encodings/AppleGCR.cpp b/Storage/Disk/Encodings/AppleGCR.cpp index a7a137b9e..98ef10d29 100644 --- a/Storage/Disk/Encodings/AppleGCR.cpp +++ b/Storage/Disk/Encodings/AppleGCR.cpp @@ -74,11 +74,11 @@ using namespace Storage::Encodings; }*/ Storage::Disk::PCMSegment AppleGCR::six_and_two_sync(int length) { - return sync(length, 9); + return sync(length, 10); } Storage::Disk::PCMSegment AppleGCR::five_and_three_sync(int length) { - return sync(length, 10); + return sync(length, 9); } Storage::Disk::PCMSegment AppleGCR::header(uint8_t volume, uint8_t track, uint8_t sector) { From fabcb261dc66a885218f249efc3031ec9a694448 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 28 Apr 2018 23:17:06 -0400 Subject: [PATCH 30/44] Corrects data prologue usage and off-by-one error in checksum placement. --- Storage/Disk/Encodings/AppleGCR.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Storage/Disk/Encodings/AppleGCR.cpp b/Storage/Disk/Encodings/AppleGCR.cpp index 98ef10d29..d843b9927 100644 --- a/Storage/Disk/Encodings/AppleGCR.cpp +++ b/Storage/Disk/Encodings/AppleGCR.cpp @@ -94,8 +94,8 @@ Storage::Disk::PCMSegment AppleGCR::header(uint8_t volume, uint8_t track, uint8_ segment.data[2] = header_prologue[2]; #define WriteFM(index, value) \ - segment.data[index+0] = static_cast((value >> 1) | 0xaa); \ - segment.data[index+1] = static_cast(value | 0xaa); \ + segment.data[index+0] = static_cast(((value) >> 1) | 0xaa); \ + segment.data[index+1] = static_cast((value) | 0xaa); \ WriteFM(3, volume); WriteFM(5, track); @@ -138,9 +138,9 @@ Storage::Disk::PCMSegment AppleGCR::six_and_two_data(const uint8_t *source) { segment.number_of_bits = static_cast(segment.data.size() * 8); // Add the prologue and epilogue. - segment.data[0] = header_prologue[0]; - segment.data[1] = header_prologue[1]; - segment.data[2] = header_prologue[2]; + segment.data[0] = data_prologue[0]; + segment.data[1] = data_prologue[1]; + segment.data[2] = data_prologue[2]; segment.data[346] = epilogue[0]; segment.data[347] = epilogue[1]; @@ -162,12 +162,12 @@ Storage::Disk::PCMSegment AppleGCR::six_and_two_data(const uint8_t *source) { segment.data[3 + 85] = bit_shuffle[source[255]&3]; for(std::size_t c = 0; c < 256; ++c) { - segment.data[3 + 85 + c] = source[c] >> 2; + segment.data[3 + 85 + 1 + c] = source[c] >> 2; } // Exclusive OR each byte with the one before it. - segment.data[344] = segment.data[343]; - std::size_t location = 343; + segment.data[345] = segment.data[344]; + std::size_t location = 344; while(location > 3) { segment.data[location] ^= segment.data[location-1]; --location; From c62db6665ae17848553c1734b733f3fa0ae4e190 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 29 Apr 2018 11:20:23 -0400 Subject: [PATCH 31/44] Corrects storage of lower two bit pairs. It turns out the non-integral result of 256/3 is handled differently than my guess. --- Storage/Disk/Encodings/AppleGCR.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Storage/Disk/Encodings/AppleGCR.cpp b/Storage/Disk/Encodings/AppleGCR.cpp index d843b9927..3843859c1 100644 --- a/Storage/Disk/Encodings/AppleGCR.cpp +++ b/Storage/Disk/Encodings/AppleGCR.cpp @@ -115,9 +115,13 @@ Storage::Disk::PCMSegment AppleGCR::five_and_three_data(const uint8_t *source) { Storage::Disk::PCMSegment segment; segment.data.resize(410 + 7); - segment.data[0] = header_prologue[0]; - segment.data[1] = header_prologue[1]; - segment.data[2] = header_prologue[2]; + segment.data[0] = data_prologue[0]; + segment.data[1] = data_prologue[1]; + segment.data[2] = data_prologue[2]; + + segment.data[414] = epilogue[0]; + segment.data[411] = epilogue[1]; + segment.data[416] = epilogue[2]; // std::size_t source_pointer = 0; // std::size_t destination_pointer = 3; @@ -151,15 +155,11 @@ Storage::Disk::PCMSegment AppleGCR::six_and_two_data(const uint8_t *source) { // contents; the 256 bytes afterwards are the remaining // six bits. const uint8_t bit_shuffle[] = {0, 2, 1, 3}; - for(std::size_t c = 0; c < 85; ++c) { - segment.data[3 + c] = - static_cast( - bit_shuffle[source[c]&3] | - (bit_shuffle[source[c + 85]&3] << 2) | - (bit_shuffle[source[c + 170]&3] << 4) - ); + for(std::size_t c = 0; c < 84; ++c) { + segment.data[3 + c] = bit_shuffle[source[c]&3]; + if(c + 86 < 256) segment.data[3 + c] |= bit_shuffle[source[c + 86]&3] << 2; + if(c + 172 < 256) segment.data[3 + c] |= bit_shuffle[source[c + 172]&3] << 4; } - segment.data[3 + 85] = bit_shuffle[source[255]&3]; for(std::size_t c = 0; c < 256; ++c) { segment.data[3 + 85 + 1 + c] = source[c] >> 2; From 5cd15147ebaf8f3c73ad5c20b72dd1dec2b9cb60 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 29 Apr 2018 16:18:14 -0400 Subject: [PATCH 32/44] Introduces interleaving of sector numbers. --- Storage/Disk/DiskImage/Formats/AppleDSK.cpp | 9 +++++++-- Storage/Disk/DiskImage/Formats/AppleDSK.hpp | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Storage/Disk/DiskImage/Formats/AppleDSK.cpp b/Storage/Disk/DiskImage/Formats/AppleDSK.cpp index b32a63876..5915f56cd 100644 --- a/Storage/Disk/DiskImage/Formats/AppleDSK.cpp +++ b/Storage/Disk/DiskImage/Formats/AppleDSK.cpp @@ -41,12 +41,17 @@ std::shared_ptr AppleDSK::get_track_at_position(Track::Address address) { // In either case below, the code aims for exactly 50,000 bits per track. if(sectors_per_track_ == 16) { // Write the sectors. - for(uint8_t c = 0; c < 16; ++c) { + uint8_t sector_number_ = 0; + for(std::size_t c = 0; c < 16; ++c) { segments.push_back(Encodings::AppleGCR::six_and_two_sync(10)); - segments.push_back(Encodings::AppleGCR::header(0, track, c)); + segments.push_back(Encodings::AppleGCR::header(0, track, sector_number_)); segments.push_back(Encodings::AppleGCR::six_and_two_sync(10)); segments.push_back(Encodings::AppleGCR::six_and_two_data(&track_data[c * 256])); segments.push_back(Encodings::AppleGCR::six_and_two_sync(10)); + + // DOS and Pro DOS interleave sectors on disk, and they're represented in a disk + // image in physical order rather than logical. So that skew needs to be applied here. + sector_number_ = (sector_number_ + (is_prodos_ ? 8 : 7)) % 15; } // Pad if necessary. diff --git a/Storage/Disk/DiskImage/Formats/AppleDSK.hpp b/Storage/Disk/DiskImage/Formats/AppleDSK.hpp index 465c73e87..92fd152d0 100644 --- a/Storage/Disk/DiskImage/Formats/AppleDSK.hpp +++ b/Storage/Disk/DiskImage/Formats/AppleDSK.hpp @@ -37,7 +37,8 @@ class AppleDSK: public DiskImage { private: Storage::FileHolder file_; - int sectors_per_track_; + int sectors_per_track_ = 16; + bool is_prodos_ = false; }; } From a9d4fe0b41b2bcde5b7c36e289461824908f7ccd Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 29 Apr 2018 16:34:10 -0400 Subject: [PATCH 33/44] Introduces filetype wiring for DO and PO files. Also corrects sector numbering logic to ensure there is a sector 15. --- Analyser/Static/StaticAnalyser.cpp | 2 ++ OSBindings/Mac/Clock Signal/Info.plist | 3 ++- Storage/Disk/DiskImage/Formats/AppleDSK.cpp | 15 ++++++++++++++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/Analyser/Static/StaticAnalyser.cpp b/Analyser/Static/StaticAnalyser.cpp index 7d85ac366..b822bc76c 100644 --- a/Analyser/Static/StaticAnalyser.cpp +++ b/Analyser/Static/StaticAnalyser.cpp @@ -94,6 +94,7 @@ static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform:: Format("csw", result.tapes, Tape::CSW, TargetPlatform::AllTape) // CSW Format("d64", result.disks, Disk::DiskImageHolder, TargetPlatform::Commodore) // D64 Format("dmk", result.disks, Disk::DiskImageHolder, TargetPlatform::MSX) // DMK + Format("do", result.disks, Disk::DiskImageHolder, TargetPlatform::AppleII) // DO Format("dsd", result.disks, Disk::DiskImageHolder, TargetPlatform::Acorn) // DSD Format("dsk", result.disks, Disk::DiskImageHolder, TargetPlatform::AmstradCPC) // DSK (Amstrad CPC) Format("dsk", result.disks, Disk::DiskImageHolder, TargetPlatform::AppleII) // DSK (Apple) @@ -108,6 +109,7 @@ static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform:: Format("nib", result.disks, Disk::DiskImageHolder, TargetPlatform::AppleII) // NIB Format("o", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // O Format("p", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // P + Format("po", result.disks, Disk::DiskImageHolder, TargetPlatform::AppleII) // PO Format("p81", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // P81 // PRG diff --git a/OSBindings/Mac/Clock Signal/Info.plist b/OSBindings/Mac/Clock Signal/Info.plist index 11051c044..dfa0820d8 100644 --- a/OSBindings/Mac/Clock Signal/Info.plist +++ b/OSBindings/Mac/Clock Signal/Info.plist @@ -398,6 +398,8 @@ nib woz + do + po CFBundleTypeIconFile floppy525 @@ -408,7 +410,6 @@ LSItemContentTypes public.item - nl.xs4all.gp.virtualii.nibdisk LSTypeIsPackage 0 diff --git a/Storage/Disk/DiskImage/Formats/AppleDSK.cpp b/Storage/Disk/DiskImage/Formats/AppleDSK.cpp index 5915f56cd..fe9521365 100644 --- a/Storage/Disk/DiskImage/Formats/AppleDSK.cpp +++ b/Storage/Disk/DiskImage/Formats/AppleDSK.cpp @@ -24,6 +24,18 @@ AppleDSK::AppleDSK(const std::string &file_name) : sectors_per_track_ = static_cast(file_.stats().st_size / (number_of_tracks*bytes_per_sector)); if(sectors_per_track_ != 13 && sectors_per_track_ != 16) throw Error::InvalidFormat; + + // Check whether this is a Pro DOS disk by inspecting the filename. + if(sectors_per_track_ == 16) { + size_t string_index = file_name.size()-1; + while(file_name[string_index] != '.') { + if(file_name[string_index] == 'p') { + is_prodos_ = true; + break; + } + --string_index; + } + } } int AppleDSK::get_head_position_count() { @@ -51,7 +63,8 @@ std::shared_ptr AppleDSK::get_track_at_position(Track::Address address) { // DOS and Pro DOS interleave sectors on disk, and they're represented in a disk // image in physical order rather than logical. So that skew needs to be applied here. - sector_number_ = (sector_number_ + (is_prodos_ ? 8 : 7)) % 15; + sector_number_ += is_prodos_ ? 8 : 7; + if(sector_number_ > 0xf) sector_number_ %= 15; } // Pad if necessary. From 10c0e687f56efa554a8424689b57270cfdf6f0f5 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 29 Apr 2018 17:51:10 -0400 Subject: [PATCH 34/44] Attempts to introduce sleeping for the Disk II. --- ClockReceiver/Sleeper.hpp | 2 +- Components/DiskII/DiskII.cpp | 99 ++++++++++++++-------- Components/DiskII/DiskII.hpp | 12 ++- Machines/AmstradCPC/AmstradCPC.cpp | 2 +- Machines/Commodore/Vic-20/Vic20.cpp | 2 +- Machines/MSX/MSX.cpp | 2 +- Storage/Disk/Controller/DiskController.cpp | 2 +- Storage/Disk/Controller/DiskController.hpp | 2 +- 8 files changed, 82 insertions(+), 41 deletions(-) diff --git a/ClockReceiver/Sleeper.hpp b/ClockReceiver/Sleeper.hpp index 2a6e00e08..08dadfb92 100644 --- a/ClockReceiver/Sleeper.hpp +++ b/ClockReceiver/Sleeper.hpp @@ -30,7 +30,7 @@ class Sleeper { class SleepObserver { public: /// Called to inform an observer that the component @c component has either gone to sleep or become awake. - virtual void set_component_is_sleeping(void *component, bool is_sleeping) = 0; + virtual void set_component_is_sleeping(Sleeper *component, bool is_sleeping) = 0; }; /// Registers @c observer as the new sleep observer; diff --git a/Components/DiskII/DiskII.cpp b/Components/DiskII/DiskII.cpp index 57ff5a4be..b90a117f8 100644 --- a/Components/DiskII/DiskII.cpp +++ b/Components/DiskII/DiskII.cpp @@ -13,14 +13,17 @@ using namespace Apple; namespace { - const uint8_t input_command = 0x1; - const uint8_t input_mode = 0x2; + const uint8_t input_command = 0x1; // i.e. Q6 + const uint8_t input_mode = 0x2; // i.e. Q7 const uint8_t input_flux = 0x4; } DiskII::DiskII() : - drives_{{2000000, 300, 1}, {2045454, 300, 1}} + inputs_(input_command), + drives_{{2045454, 300, 1}, {2045454, 300, 1}} { + drives_[0].set_sleep_observer(this); + drives_[1].set_sleep_observer(this); } void DiskII::set_control(Control control, bool on) { @@ -38,7 +41,7 @@ void DiskII::set_control(Control control, bool on) { break; } -// printf("%0x: Set control %d %s\n", stepper_mask_, control, on ? "on" : "off"); + printf("%0x: Set control %d %s\n", stepper_mask_, control, on ? "on" : "off"); // If the stepper magnet selections have changed, and any is on, see how // that moves the head. @@ -62,6 +65,7 @@ void DiskII::set_control(Control control, bool on) { void DiskII::set_mode(Mode mode) { // printf("Set mode %d\n", mode); inputs_ = (inputs_ & ~input_mode) | ((mode == Mode::Write) ? input_mode : 0); + set_controller_can_sleep(); } void DiskII::select_drive(int drive) { @@ -75,11 +79,13 @@ void DiskII::set_data_register(uint8_t value) { // printf("Set data register (?)\n"); inputs_ |= input_command; data_register_ = value; + set_controller_can_sleep(); } uint8_t DiskII::get_shift_register() { // if(shift_register_ & 0x80) printf("[%02x] ", shift_register_); inputs_ &= ~input_command; + set_controller_can_sleep(); return shift_register_; } @@ -97,41 +103,56 @@ void DiskII::run_for(const Cycles cycles) { The bytes in the P6 ROM has the high four bits reversed compared to the BAPD charts, so you will have to reverse them after fetching the byte. */ - // TODO: optimise the resting state. + if(is_sleeping()) return; int integer_cycles = cycles.as_int(); - while(integer_cycles--) { - const int address = - (inputs_ << 2) | - ((shift_register_&0x80) >> 6) | - ((state_&0x2) >> 1) | - ((state_&0x1) << 7) | - ((state_&0x4) << 4) | - ((state_&0x8) << 2); - inputs_ |= input_flux; - const uint8_t update = state_machine_[static_cast(address)]; - state_ = update >> 4; - state_ = ((state_ & 0x8) ? 0x1 : 0x0) | ((state_ & 0x4) ? 0x2 : 0x0) | ((state_ & 0x2) ? 0x4 : 0x0) | ((state_ & 0x1) ? 0x8 : 0x0); + if(!controller_can_sleep_) { + while(integer_cycles--) { + const int address = + (inputs_ << 2) | + ((shift_register_&0x80) >> 6) | + ((state_&0x2) >> 1) | + ((state_&0x1) << 7) | + ((state_&0x4) << 4) | + ((state_&0x8) << 2); + inputs_ |= input_flux; - uint8_t command = update & 0xf; - switch(command) { - case 0x0: shift_register_ = 0; break; // clear - case 0x9: shift_register_ = static_cast(shift_register_ << 1); break; // shift left, bringing in a zero - case 0xd: shift_register_ = static_cast((shift_register_ << 1) | 1); break; // shift left, bringing in a one - case 0xb: shift_register_ = data_register_; break; // load - case 0xa: - shift_register_ = (shift_register_ >> 1) | (is_write_protected() ? 0x80 : 0x00); - break; // shift right, bringing in write protected status - default: break; + const uint8_t update = state_machine_[static_cast(address)]; + state_ = update >> 4; + state_ = ((state_ & 0x8) ? 0x1 : 0x0) | ((state_ & 0x4) ? 0x2 : 0x0) | ((state_ & 0x2) ? 0x4 : 0x0) | ((state_ & 0x1) ? 0x8 : 0x0); + + uint8_t command = update & 0xf; + switch(command) { + case 0x0: shift_register_ = 0; break; // clear + case 0x9: shift_register_ = static_cast(shift_register_ << 1); break; // shift left, bringing in a zero + case 0xd: shift_register_ = static_cast((shift_register_ << 1) | 1); break; // shift left, bringing in a one + case 0xb: shift_register_ = data_register_; break; // load + case 0xa: + shift_register_ = (shift_register_ >> 1) | (is_write_protected() ? 0x80 : 0x00); + break; // shift right, bringing in write protected status + default: break; + } + + // TODO: surely there's a less heavyweight solution than this? + if(!drive_is_sleeping_[0]) drives_[0].run_for(Cycles(1)); + if(!drive_is_sleeping_[1]) drives_[1].run_for(Cycles(1)); } - -// printf(" -> %02x performing %02x (address was %02x)\n", state_, command, address); - - // TODO: surely there's a less heavyweight solution than this? - drives_[0].run_for(Cycles(1)); - drives_[1].run_for(Cycles(1)); + } else { + if(!drive_is_sleeping_[0]) drives_[0].run_for(cycles); + if(!drive_is_sleeping_[1]) drives_[1].run_for(cycles); } + + set_controller_can_sleep(); +} + +void DiskII::set_controller_can_sleep() { + // Permit the controller to sleep if it's in sense write protect mode, and the shift register + // has already filled with the result of shifting eight times. + controller_can_sleep_ = + (inputs_ == (input_command | input_flux)) && + (shift_register_ == (is_write_protected() ? 0xff : 0x00)); + if(is_sleeping()) update_sleep_observer(); } bool DiskII::is_write_protected() { @@ -140,7 +161,6 @@ bool DiskII::is_write_protected() { void DiskII::set_state_machine(const std::vector &state_machine) { state_machine_ = state_machine; -// run_for(Cycles(15)); // TODO: shuffle ordering here? } @@ -151,5 +171,16 @@ void DiskII::set_disk(const std::shared_ptr &disk, int driv void DiskII::process_event(const Storage::Disk::Track::Event &event) { if(event.type == Storage::Disk::Track::Event::FluxTransition) { inputs_ &= ~input_flux; + set_controller_can_sleep(); } } + +void DiskII::set_component_is_sleeping(Sleeper *component, bool is_sleeping) { + drive_is_sleeping_[0] = drives_[0].is_sleeping(); + drive_is_sleeping_[1] = drives_[1].is_sleeping(); + update_sleep_observer(); +} + +bool DiskII::is_sleeping() { + return controller_can_sleep_ && drive_is_sleeping_[0] && drive_is_sleeping_[1]; +} diff --git a/Components/DiskII/DiskII.hpp b/Components/DiskII/DiskII.hpp index ccb0130b1..26f9d6adb 100644 --- a/Components/DiskII/DiskII.hpp +++ b/Components/DiskII/DiskII.hpp @@ -10,6 +10,7 @@ #define DiskII_hpp #include "../../ClockReceiver/ClockReceiver.hpp" +#include "../../ClockReceiver/Sleeper.hpp" #include "../../Storage/Disk/Disk.hpp" #include "../../Storage/Disk/Drive.hpp" @@ -22,7 +23,10 @@ namespace Apple { /*! Provides an emulation of the Apple Disk II. */ -class DiskII: public Storage::Disk::Drive::EventDelegate { +class DiskII: + public Storage::Disk::Drive::EventDelegate, + public Sleeper::SleepObserver, + public Sleeper { public: DiskII(); @@ -43,9 +47,11 @@ class DiskII: public Storage::Disk::Drive::EventDelegate { void set_state_machine(const std::vector &); void set_disk(const std::shared_ptr &disk, int drive); + bool is_sleeping() override; private: void process_event(const Storage::Disk::Track::Event &event) override; + void set_component_is_sleeping(Sleeper *component, bool is_sleeping) override; uint8_t state_ = 0; uint8_t inputs_ = 0; @@ -58,7 +64,11 @@ class DiskII: public Storage::Disk::Drive::EventDelegate { bool is_write_protected(); std::vector state_machine_; Storage::Disk::Drive drives_[2]; + bool drive_is_sleeping_[2]; + bool controller_can_sleep_ = false; int active_drive_ = 0; + + void set_controller_can_sleep(); }; } diff --git a/Machines/AmstradCPC/AmstradCPC.cpp b/Machines/AmstradCPC/AmstradCPC.cpp index 58ab1bce2..db1720cb5 100644 --- a/Machines/AmstradCPC/AmstradCPC.cpp +++ b/Machines/AmstradCPC/AmstradCPC.cpp @@ -961,7 +961,7 @@ class ConcreteMachine: return true; } - void set_component_is_sleeping(void *component, bool is_sleeping) override final { + void set_component_is_sleeping(Sleeper *component, bool is_sleeping) override final { fdc_is_sleeping_ = fdc_.is_sleeping(); tape_player_is_sleeping_ = tape_player_.is_sleeping(); } diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index 39deb6516..56111292d 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -747,7 +747,7 @@ class ConcreteMachine: return selection_set; } - void set_component_is_sleeping(void *component, bool is_sleeping) override { + void set_component_is_sleeping(Sleeper *component, bool is_sleeping) override { tape_is_sleeping_ = is_sleeping; set_use_fast_tape(); } diff --git a/Machines/MSX/MSX.cpp b/Machines/MSX/MSX.cpp index e1c693094..6b57f714c 100644 --- a/Machines/MSX/MSX.cpp +++ b/Machines/MSX/MSX.cpp @@ -551,7 +551,7 @@ class ConcreteMachine: } // MARK: - Sleeper - void set_component_is_sleeping(void *component, bool is_sleeping) override { + void set_component_is_sleeping(Sleeper *component, bool is_sleeping) override { tape_player_is_sleeping_ = tape_player_.is_sleeping(); set_use_fast_tape(); } diff --git a/Storage/Disk/Controller/DiskController.cpp b/Storage/Disk/Controller/DiskController.cpp index e6788c873..9a0b992f1 100644 --- a/Storage/Disk/Controller/DiskController.cpp +++ b/Storage/Disk/Controller/DiskController.cpp @@ -22,7 +22,7 @@ Controller::Controller(Cycles clock_rate) : set_drive(empty_drive_); } -void Controller::set_component_is_sleeping(void *component, bool is_sleeping) { +void Controller::set_component_is_sleeping(Sleeper *component, bool is_sleeping) { update_sleep_observer(); } diff --git a/Storage/Disk/Controller/DiskController.hpp b/Storage/Disk/Controller/DiskController.hpp index 7eb0ce085..ac079e6c3 100644 --- a/Storage/Disk/Controller/DiskController.hpp +++ b/Storage/Disk/Controller/DiskController.hpp @@ -113,7 +113,7 @@ class Controller: public DigitalPhaseLockedLoop::Delegate, public Drive::EventDe std::shared_ptr empty_drive_; - void set_component_is_sleeping(void *component, bool is_sleeping); + void set_component_is_sleeping(Sleeper *component, bool is_sleeping); // for Drive::EventDelegate void process_event(const Track::Event &event); From 265bc80d44e5618f394833313c3cafaaf5bb4027 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 29 Apr 2018 17:52:29 -0400 Subject: [PATCH 35/44] Attempts to introduce sleeping to the Disk II. --- Machines/AppleII/AppleII.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Machines/AppleII/AppleII.cpp b/Machines/AppleII/AppleII.cpp index 3c39319a0..5b4041f7d 100644 --- a/Machines/AppleII/AppleII.cpp +++ b/Machines/AppleII/AppleII.cpp @@ -230,7 +230,10 @@ class ConcreteMachine: if(!roms[0] || !roms[1]) return false; rom_ = std::move(*roms[0]); - rom_start_address_ = static_cast(0x10000 - rom_.size()); + if(rom_.size() > 12*1024) { + rom_.erase(rom_.begin(), rom_.begin() + static_cast(rom_.size()) - 12*1024); + } + rom_start_address_ = 0xd000;//static_cast(0x10000 - rom_.size()); character_rom_ = std::move(*roms[1]); From aacf26f05df6252ab6b3f4bf93187584eeb55fa5 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 30 Apr 2018 22:03:09 -0400 Subject: [PATCH 36/44] Removed logged comment. --- Components/DiskII/DiskII.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Components/DiskII/DiskII.cpp b/Components/DiskII/DiskII.cpp index b90a117f8..102669e35 100644 --- a/Components/DiskII/DiskII.cpp +++ b/Components/DiskII/DiskII.cpp @@ -41,7 +41,7 @@ void DiskII::set_control(Control control, bool on) { break; } - printf("%0x: Set control %d %s\n", stepper_mask_, control, on ? "on" : "off"); +// printf("%0x: Set control %d %s\n", stepper_mask_, control, on ? "on" : "off"); // If the stepper magnet selections have changed, and any is on, see how // that moves the head. From e7618bb32e1e840205d4d0ce28b1f9f77dcf0516 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 30 Apr 2018 22:04:05 -0400 Subject: [PATCH 37/44] Corrects types (/chickens out). --- OSBindings/Mac/Clock SignalTests/AtariStaticAnalyserTests.mm | 2 +- OSBindings/Mac/Clock SignalTests/MSXStaticAnalyserTests.mm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/AtariStaticAnalyserTests.mm b/OSBindings/Mac/Clock SignalTests/AtariStaticAnalyserTests.mm index 849c03fcf..c05aef2ba 100644 --- a/OSBindings/Mac/Clock SignalTests/AtariStaticAnalyserTests.mm +++ b/OSBindings/Mac/Clock SignalTests/AtariStaticAnalyserTests.mm @@ -594,7 +594,7 @@ static NSDictionary *romRecordsBySHA1 = @{ for(int c = 0; c < CC_SHA1_DIGEST_LENGTH; c++) [sha1 appendFormat:@"%02x", sha1Bytes[c]]; // get an analysis of the file - TargetList targets = Analyser::Static::GetTargets([fullPath UTF8String]); + auto targets = Analyser::Static::GetTargets([fullPath UTF8String]); // grab the ROM record AtariROMRecord *romRecord = romRecordsBySHA1[sha1]; diff --git a/OSBindings/Mac/Clock SignalTests/MSXStaticAnalyserTests.mm b/OSBindings/Mac/Clock SignalTests/MSXStaticAnalyserTests.mm index bdf529e0f..f04690449 100644 --- a/OSBindings/Mac/Clock SignalTests/MSXStaticAnalyserTests.mm +++ b/OSBindings/Mac/Clock SignalTests/MSXStaticAnalyserTests.mm @@ -212,7 +212,7 @@ static NSDictionary *romRecordsBySHA1 = @{ for(int c = 0; c < CC_SHA1_DIGEST_LENGTH; c++) [sha1 appendFormat:@"%02x", sha1Bytes[c]]; // get an analysis of the file - TargetList targets = Analyser::Static::GetTargets([fullPath UTF8String]); + auto targets = Analyser::Static::GetTargets([fullPath UTF8String]); // grab the ROM record MSXROMRecord *romRecord = romRecordsBySHA1[sha1]; From b32538f3c8d910fd503cc4e25df8e7714dfa3496 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 30 Apr 2018 22:05:44 -0400 Subject: [PATCH 38/44] Adds an additional test. --- .../Mac/Clock SignalTests/PCMTrackTests.mm | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/OSBindings/Mac/Clock SignalTests/PCMTrackTests.mm b/OSBindings/Mac/Clock SignalTests/PCMTrackTests.mm index f928f5f70..221ffee3c 100644 --- a/OSBindings/Mac/Clock SignalTests/PCMTrackTests.mm +++ b/OSBindings/Mac/Clock SignalTests/PCMTrackTests.mm @@ -51,4 +51,33 @@ XCTAssert(events[8].length == transition_length, "Time taken in transition between speed zones should be half of a bit length in the first part plus half of a bit length in the second"); } +- (void)testComplicatedTrackSeek { + std::vector segments; + + Storage::Disk::PCMSegment sync_segment; + sync_segment.data.resize(10); + sync_segment.number_of_bits = 10*8; + + Storage::Disk::PCMSegment header_segment; + header_segment.data.resize(14); + header_segment.number_of_bits = 14*8; + + Storage::Disk::PCMSegment data_segment; + data_segment.data.resize(349); + data_segment.number_of_bits = 349*8; + + for(std::size_t c = 0; c < 16; ++c) { + segments.push_back(sync_segment); + segments.push_back(header_segment); + segments.push_back(sync_segment); + segments.push_back(data_segment); + segments.push_back(sync_segment); + } + + Storage::Disk::PCMTrack track(segments); + Storage::Time late_time(967445, 2045454); + track.seek_to(late_time); + track.get_next_event(); +} + @end From dbd9282efcb87e18165fd61430e204feec0048df Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 30 Apr 2018 22:07:17 -0400 Subject: [PATCH 39/44] Experimentally switches to `double`s for `TimedEventLoop` time tracking. --- Storage/TimedEventLoop.cpp | 60 ++++++++------------------------------ Storage/TimedEventLoop.hpp | 2 +- 2 files changed, 13 insertions(+), 49 deletions(-) diff --git a/Storage/TimedEventLoop.cpp b/Storage/TimedEventLoop.cpp index 1dc0a259f..191a5bc00 100644 --- a/Storage/TimedEventLoop.cpp +++ b/Storage/TimedEventLoop.cpp @@ -11,6 +11,7 @@ #include #include +#include using namespace Storage; @@ -54,7 +55,7 @@ unsigned int TimedEventLoop::get_input_clock_rate() { } void TimedEventLoop::reset_timer() { - subcycles_until_event_.set_zero(); + subcycles_until_event_ = 0.0; cycles_until_event_ = 0; } @@ -63,59 +64,22 @@ void TimedEventLoop::jump_to_next_event() { process_next_event(); } +char text[256]; + void TimedEventLoop::set_next_event_time_interval(Time interval) { // Calculate [interval]*[input clock rate] + [subcycles until this event] - // = interval.numerator * input clock / interval.denominator + subcycles.numerator / subcycles.denominator - // = (interval.numerator * input clock * subcycles.denominator + subcycles.numerator * interval.denominator) / (interval.denominator * subcycles.denominator) - int64_t denominator = static_cast(interval.clock_rate) * static_cast(subcycles_until_event_.clock_rate); - int64_t numerator = - static_cast(subcycles_until_event_.clock_rate) * static_cast(input_clock_rate_) * static_cast(interval.length) + - static_cast(interval.clock_rate) * static_cast(subcycles_until_event_.length); - - // Simplify if necessary: try just simplifying the interval and recalculating; if that doesn't - // work then try simplifying the whole thing. - if(numerator < 0 || denominator < 0 || denominator > std::numeric_limits::max()) { - interval.simplify(); - denominator = static_cast(interval.clock_rate) * static_cast(subcycles_until_event_.clock_rate); - numerator = - static_cast(subcycles_until_event_.clock_rate) * static_cast(input_clock_rate_) * static_cast(interval.length) + - static_cast(interval.clock_rate) * static_cast(subcycles_until_event_.length); - } - - if(numerator < 0 || denominator < 0 || denominator > std::numeric_limits::max()) { - int64_t common_divisor = NumberTheory::greatest_common_divisor(numerator % denominator, denominator); - denominator /= common_divisor; - numerator /= common_divisor; - } - - // If even that doesn't work then reduce precision. - if(numerator < 0 || denominator < 0 || denominator > std::numeric_limits::max()) { -// printf("."); - const double double_interval = interval.get(); - const double double_subcycles_remaining = subcycles_until_event_.get(); - const double output = double_interval * static_cast(input_clock_rate_) + double_subcycles_remaining; - - if(output < 1.0) { - denominator = std::numeric_limits::max(); - numerator = static_cast(denominator * output); - } else { - numerator = std::numeric_limits::max(); - denominator = static_cast(numerator / output); - } - } + double double_interval = interval.get() * static_cast(input_clock_rate_) + subcycles_until_event_; // So this event will fire in the integral number of cycles from now, putting us at the remainder // number of subcycles - const int addition = static_cast(numerator / denominator); - assert(cycles_until_event_ == 0); - assert(addition >= 0); - if(addition < 0) { - assert(false); - } + const int addition = static_cast(double_interval); cycles_until_event_ += addition; - subcycles_until_event_.length = static_cast(numerator % denominator); - subcycles_until_event_.clock_rate = static_cast(denominator); - subcycles_until_event_.simplify(); + subcycles_until_event_ = fmod(double_interval, 1.0); + + assert(cycles_until_event_ >= 0); + assert(subcycles_until_event_ >= 0.0); + + sprintf(text, " + %0.8f -> %d / %0.4f", interval.get(), cycles_until_event_, subcycles_until_event_); } Time TimedEventLoop::get_time_into_next_event() { diff --git a/Storage/TimedEventLoop.hpp b/Storage/TimedEventLoop.hpp index 8f6549916..31d174cd8 100644 --- a/Storage/TimedEventLoop.hpp +++ b/Storage/TimedEventLoop.hpp @@ -102,7 +102,7 @@ namespace Storage { private: unsigned int input_clock_rate_ = 0; int cycles_until_event_ = 0; - Time subcycles_until_event_; + double subcycles_until_event_ = 0.0; }; } From 79002d69628972fd5ba45e1d8b4d96db917ffb99 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 30 Apr 2018 22:07:54 -0400 Subject: [PATCH 40/44] Adds an additional assert. --- Storage/Disk/Drive.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Storage/Disk/Drive.cpp b/Storage/Disk/Drive.cpp index 4fcf69108..5ef0dac5f 100644 --- a/Storage/Disk/Drive.cpp +++ b/Storage/Disk/Drive.cpp @@ -163,6 +163,7 @@ void Drive::get_next_event(const Time &duration_already_passed) { // divide interval, which is in terms of a single rotation of the disk, by rotation speed to // convert it into revolutions per second; this is achieved by multiplying by rotational_multiplier_ assert(current_event_.length <= Time(1) && current_event_.length >= Time(0)); + assert(current_event_.length > duration_already_passed); Time interval = (current_event_.length - duration_already_passed) * rotational_multiplier_; set_next_event_time_interval(interval); } From 9da481b060da9be29ffe15b2b9958647081229c9 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 30 Apr 2018 22:08:51 -0400 Subject: [PATCH 41/44] Slightly simplifies syntax. --- Storage/Disk/DiskImage/Formats/HFE.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Storage/Disk/DiskImage/Formats/HFE.cpp b/Storage/Disk/DiskImage/Formats/HFE.cpp index 05549a2cb..59c6f3636 100644 --- a/Storage/Disk/DiskImage/Formats/HFE.cpp +++ b/Storage/Disk/DiskImage/Formats/HFE.cpp @@ -79,9 +79,7 @@ std::shared_ptr HFE::get_track_at_position(Track::Address address) { // Flip bytes; HFE's preference is that the least-significant bit // is serialised first, but PCMTrack posts the most-significant first. Storage::Data::BitReverse::reverse(segment.data); - - std::shared_ptr track(new PCMTrack(segment)); - return track; + return std::make_shared(segment); } void HFE::set_tracks(const std::map> &tracks) { From f4097290c2abe1788a9abc01cad0843182765ac2 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 30 Apr 2018 22:23:57 -0400 Subject: [PATCH 42/44] Made various corrections following a quick for-loop constness audit. --- Analyser/Static/AmstradCPC/StaticAnalyser.cpp | 12 ++++++------ Analyser/Static/MSX/StaticAnalyser.cpp | 2 +- Analyser/Static/Oric/StaticAnalyser.cpp | 18 +++++++++--------- Analyser/Static/StaticAnalyser.cpp | 2 +- Concurrency/AsyncTaskQueue.cpp | 2 +- Machines/Commodore/Vic-20/Vic20.cpp | 2 +- Machines/Oric/Oric.cpp | 2 +- .../Mac/Clock Signal/Machine/CSROMFetcher.mm | 2 +- .../Clock SignalTests/PCMPatchedTrackTests.mm | 2 +- OSBindings/SDL/main.cpp | 12 ++++++------ Outputs/CRT/Internals/Shaders/Shader.cpp | 2 +- SignalProcessing/FIRFilter.cpp | 4 ++-- .../Disk/DiskImage/DiskImageImplementation.hpp | 2 +- .../Disk/DiskImage/Formats/MFMSectorDump.cpp | 2 +- Storage/Disk/DiskImage/Formats/OricMFMDSK.cpp | 2 +- .../Formats/Utility/ImplicitSectors.cpp | 2 +- Storage/Disk/Encodings/MFM/Parser.cpp | 2 +- Storage/Disk/Track/PCMTrack.cpp | 4 ++-- Storage/Tape/Formats/TZX.cpp | 2 +- 19 files changed, 39 insertions(+), 39 deletions(-) diff --git a/Analyser/Static/AmstradCPC/StaticAnalyser.cpp b/Analyser/Static/AmstradCPC/StaticAnalyser.cpp index cc7ed4ecf..f302ab3ee 100644 --- a/Analyser/Static/AmstradCPC/StaticAnalyser.cpp +++ b/Analyser/Static/AmstradCPC/StaticAnalyser.cpp @@ -64,14 +64,14 @@ static void InspectCatalogue( std::vector candidate_files; candidate_files.reserve(catalogue.files.size()); - for(auto &file : catalogue.files) { + for(const auto &file : catalogue.files) { candidate_files.push_back(&file); } // Remove all files with untypable characters. candidate_files.erase( std::remove_if(candidate_files.begin(), candidate_files.end(), [](const Storage::Disk::CPM::File *file) { - for(auto c : file->name + file->type) { + for(const auto c : file->name + file->type) { if(c < 32) return true; } return false; @@ -80,7 +80,7 @@ static void InspectCatalogue( // If that leaves a mix of 'system' (i.e. hidden) and non-system files, remove the system files. bool are_all_system = true; - for(auto file : candidate_files) { + for(const auto &file : candidate_files) { if(!file->system) { are_all_system = false; break; @@ -137,13 +137,13 @@ static void InspectCatalogue( std::map name_counts; std::map indices_by_name; std::size_t index = 0; - for(auto file : candidate_files) { + for(const auto &file : candidate_files) { name_counts[file->name]++; indices_by_name[file->name] = index; index++; } if(name_counts.size() == 2) { - for(auto &pair : name_counts) { + for(const auto &pair : name_counts) { if(pair.second == 1) { target->loading_command = RunCommandFor(*candidate_files[indices_by_name[pair.first]]); return; @@ -214,7 +214,7 @@ Analyser::Static::TargetList Analyser::Static::AmstradCPC::GetTargets(const Medi system_format.catalogue_allocation_bitmap = 0xc000; system_format.reserved_tracks = 2; - for(const auto &disk: media.disks) { + for(auto &disk: media.disks) { // Check for an ordinary catalogue. std::unique_ptr data_catalogue = Storage::Disk::CPM::GetCatalogue(disk, data_format); if(data_catalogue) { diff --git a/Analyser/Static/MSX/StaticAnalyser.cpp b/Analyser/Static/MSX/StaticAnalyser.cpp index 24385bea5..8f0fdb1f6 100644 --- a/Analyser/Static/MSX/StaticAnalyser.cpp +++ b/Analyser/Static/MSX/StaticAnalyser.cpp @@ -272,7 +272,7 @@ Analyser::Static::TargetList Analyser::Static::MSX::GetTargets(const Media &medi std::unique_ptr target(new Target); // Check tapes for loadable files. - for(const auto &tape : media.tapes) { + for(auto &tape : media.tapes) { std::vector files_on_tape = GetFiles(tape); if(!files_on_tape.empty()) { switch(files_on_tape.front().type) { diff --git a/Analyser/Static/Oric/StaticAnalyser.cpp b/Analyser/Static/Oric/StaticAnalyser.cpp index dcd16b924..35cbae8c1 100644 --- a/Analyser/Static/Oric/StaticAnalyser.cpp +++ b/Analyser/Static/Oric/StaticAnalyser.cpp @@ -23,15 +23,15 @@ using namespace Analyser::Static::Oric; static int Score(const Analyser::Static::MOS6502::Disassembly &disassembly, const std::set &rom_functions, const std::set &variable_locations) { int score = 0; - for(auto address : disassembly.outward_calls) score += (rom_functions.find(address) != rom_functions.end()) ? 1 : -1; - for(auto address : disassembly.external_stores) score += (variable_locations.find(address) != variable_locations.end()) ? 1 : -1; - for(auto address : disassembly.external_loads) score += (variable_locations.find(address) != variable_locations.end()) ? 1 : -1; + for(const auto address : disassembly.outward_calls) score += (rom_functions.find(address) != rom_functions.end()) ? 1 : -1; + for(const auto address : disassembly.external_stores) score += (variable_locations.find(address) != variable_locations.end()) ? 1 : -1; + for(const auto address : disassembly.external_loads) score += (variable_locations.find(address) != variable_locations.end()) ? 1 : -1; return score; } static int Basic10Score(const Analyser::Static::MOS6502::Disassembly &disassembly) { - std::set rom_functions = { + const std::set rom_functions = { 0x0228, 0x022b, 0xc3ca, 0xc3f8, 0xc448, 0xc47c, 0xc4b5, 0xc4e3, 0xc4e0, 0xc524, 0xc56f, 0xc5a2, 0xc5f8, 0xc60a, 0xc6a5, 0xc6de, 0xc719, 0xc738, 0xc773, 0xc824, 0xc832, 0xc841, 0xc8c1, 0xc8fe, 0xc91f, 0xc93f, 0xc941, 0xc91e, 0xc98b, 0xc996, 0xc9b3, 0xc9e0, 0xca0a, 0xca1c, @@ -47,7 +47,7 @@ static int Basic10Score(const Analyser::Static::MOS6502::Disassembly &disassembl 0xf43c, 0xf4ef, 0xf523, 0xf561, 0xf535, 0xf57b, 0xf5d3, 0xf71a, 0xf73f, 0xf7e4, 0xf7e0, 0xf82f, 0xf88f, 0xf8af, 0xf8b5, 0xf920, 0xf967, 0xf960, 0xf9c9, 0xfa14, 0xfa85, 0xfa9b, 0xfab1, 0xfac7, 0xfafa, 0xfb10, 0xfb26, 0xfbb6, 0xfbfe }; - std::set variable_locations = { + const std::set variable_locations = { 0x0228, 0x0229, 0x022a, 0x022b, 0x022c, 0x022d, 0x0230 }; @@ -55,7 +55,7 @@ static int Basic10Score(const Analyser::Static::MOS6502::Disassembly &disassembl } static int Basic11Score(const Analyser::Static::MOS6502::Disassembly &disassembly) { - std::set rom_functions = { + const std::set rom_functions = { 0x0238, 0x023b, 0x023e, 0x0241, 0x0244, 0x0247, 0xc3c6, 0xc3f4, 0xc444, 0xc47c, 0xc4a8, 0xc4d3, 0xc4e0, 0xc524, 0xc55f, 0xc592, 0xc5e8, 0xc5fa, 0xc692, 0xc6b3, 0xc6ee, 0xc70d, 0xc748, 0xc7fd, 0xc809, 0xc816, 0xc82f, 0xc855, 0xc8c1, 0xc915, 0xc952, 0xc971, 0xc973, 0xc9a0, 0xc9bd, 0xc9c8, 0xc9e5, 0xca12, @@ -72,7 +72,7 @@ static int Basic11Score(const Analyser::Static::MOS6502::Disassembly &disassembl 0xf88f, 0xf8af, 0xf8b5, 0xf920, 0xf967, 0xf9aa, 0xf9c9, 0xfa14, 0xfa9f, 0xfab5, 0xfacb, 0xfae1, 0xfb14, 0xfb2a, 0xfb40, 0xfbd0, 0xfc18 }; - std::set variable_locations = { + const std::set variable_locations = { 0x0244, 0x0245, 0x0246, 0x0247, 0x0248, 0x0249, 0x024a, 0x024b, 0x024c }; @@ -112,7 +112,7 @@ Analyser::Static::TargetList Analyser::Static::Oric::GetTargets(const Media &med std::vector tape_files = GetFiles(tape); tape->reset(); if(tape_files.size()) { - for(auto file : tape_files) { + for(const auto &file : tape_files) { if(file.data_type == File::MachineCode) { std::vector entry_points = {file.starting_address}; Analyser::Static::MOS6502::Disassembly disassembly = @@ -131,7 +131,7 @@ Analyser::Static::TargetList Analyser::Static::Oric::GetTargets(const Media &med if(!media.disks.empty()) { // Only the Microdisc is emulated right now, so accept only disks that it can boot from. - for(const auto &disk: media.disks) { + for(auto &disk: media.disks) { Storage::Encodings::MFM::Parser parser(true, disk); if(IsMicrodisc(parser)) { target->has_microdrive = true; diff --git a/Analyser/Static/StaticAnalyser.cpp b/Analyser/Static/StaticAnalyser.cpp index b822bc76c..bfd1a589c 100644 --- a/Analyser/Static/StaticAnalyser.cpp +++ b/Analyser/Static/StaticAnalyser.cpp @@ -174,7 +174,7 @@ TargetList Analyser::Static::GetTargets(const std::string &file_name) { #undef Append // Reset any tapes to their initial position - for(auto &target : targets) { + for(const auto &target : targets) { for(auto &tape : target->media.tapes) { tape->reset(); } diff --git a/Concurrency/AsyncTaskQueue.cpp b/Concurrency/AsyncTaskQueue.cpp index 070301007..4bde40272 100644 --- a/Concurrency/AsyncTaskQueue.cpp +++ b/Concurrency/AsyncTaskQueue.cpp @@ -98,7 +98,7 @@ void DeferringAsyncTaskQueue::perform() { std::shared_ptr>> deferred_tasks = deferred_tasks_; deferred_tasks_.reset(); enqueue([deferred_tasks] { - for(auto &function : *deferred_tasks) { + for(const auto &function : *deferred_tasks) { function(); } }); diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index 56111292d..e119014bd 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -499,7 +499,7 @@ class ConcreteMachine: Range(0x0000, 0x0400), Range(0x1000, 0x2000), }}; - for(auto &video_range : video_ranges) { + for(const auto &video_range : video_ranges) { for(auto addr = video_range.start; addr < video_range.end; addr += 0x400) { auto destination_address = (addr & 0x1fff) | (((addr & 0x8000) >> 2) ^ 0x2000); if(processor_read_memory_map_[addr >> 10]) { diff --git a/Machines/Oric/Oric.cpp b/Machines/Oric/Oric.cpp index f0292fd11..1848dd312 100644 --- a/Machines/Oric/Oric.cpp +++ b/Machines/Oric/Oric.cpp @@ -302,7 +302,7 @@ class ConcreteMachine: } int drive_index = 0; - for(auto disk : media.disks) { + for(auto &disk : media.disks) { if(drive_index < 4) microdisc_.set_disk(disk, drive_index); drive_index++; } diff --git a/OSBindings/Mac/Clock Signal/Machine/CSROMFetcher.mm b/OSBindings/Mac/Clock Signal/Machine/CSROMFetcher.mm index e95259a07..499046097 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSROMFetcher.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSROMFetcher.mm @@ -18,7 +18,7 @@ ROMMachine::ROMFetcher CSROMFetcher() { return [] (const std::string &machine, const std::vector &names) -> std::vector>> { NSString *subDirectory = [@"ROMImages/" stringByAppendingString:[NSString stringWithUTF8String:machine.c_str()]]; std::vector>> results; - for(auto &name: names) { + for(const auto &name: names) { NSData *fileData = [[NSBundle mainBundle] dataForResource:[NSString stringWithUTF8String:name.c_str()] withExtension:nil subdirectory:subDirectory]; if(!fileData) diff --git a/OSBindings/Mac/Clock SignalTests/PCMPatchedTrackTests.mm b/OSBindings/Mac/Clock SignalTests/PCMPatchedTrackTests.mm index 2d66ebb4a..e0a2858cd 100644 --- a/OSBindings/Mac/Clock SignalTests/PCMPatchedTrackTests.mm +++ b/OSBindings/Mac/Clock SignalTests/PCMPatchedTrackTests.mm @@ -59,7 +59,7 @@ - (Storage::Time)timeForEvents:(const std::vector &)events { Storage::Time result(0); - for(auto event : events) { + for(const auto &event : events) { result += event.length; } return result; diff --git a/OSBindings/SDL/main.cpp b/OSBindings/SDL/main.cpp index 3631ce3ac..94a50728f 100644 --- a/OSBindings/SDL/main.cpp +++ b/OSBindings/SDL/main.cpp @@ -208,16 +208,16 @@ int main(int argc, char *argv[]) { std::cout << "Required machine type and configuration is determined from the file. Machines with further options:" << std::endl << std::endl; auto all_options = Machine::AllOptionsByMachineName(); - for(auto &machine_options: all_options) { + for(const auto &machine_options: all_options) { std::cout << machine_options.first << ":" << std::endl; - for(auto &option: machine_options.second) { + for(const auto &option: machine_options.second) { std::cout << '\t' << "--" << option->short_name; Configurable::ListOption *list_option = dynamic_cast(option.get()); if(list_option) { std::cout << "={"; bool is_first = true; - for(auto option: list_option->options) { + for(const auto &option: list_option->options) { if(!is_first) std::cout << '|'; is_first = false; std::cout << option; @@ -261,7 +261,7 @@ int main(int argc, char *argv[]) { machine_name = machine; std::vector>> results; - for(auto &name: names) { + for(const auto &name: names) { std::string local_path = "/usr/local/share/CLK/" + machine + "/" + name; FILE *file = std::fopen(local_path.c_str(), "rb"); if(!file) { @@ -300,7 +300,7 @@ int main(int argc, char *argv[]) { case ::Machine::Error::MissingROM: std::cerr << "Could not find system ROMs; please install to /usr/local/share/CLK/ or /usr/share/CLK/." << std::endl; std::cerr << "One or more of the following were needed but not found:" << std::endl; - for(auto &name: rom_names) { + for(const auto &name: rom_names) { std::cerr << machine_name << '/' << name << std::endl; } break; @@ -379,7 +379,7 @@ int main(int argc, char *argv[]) { configurable_device->set_selections(configurable_device->get_user_friendly_selections()); // Consider transcoding any list selections that map to Boolean options. - for(auto &option: configurable_device->get_options()) { + for(const auto &option: configurable_device->get_options()) { // Check for a corresponding selection. auto selection = arguments.selections.find(option->short_name); if(selection != arguments.selections.end()) { diff --git a/Outputs/CRT/Internals/Shaders/Shader.cpp b/Outputs/CRT/Internals/Shaders/Shader.cpp index 72fa3b086..e378b66ee 100644 --- a/Outputs/CRT/Internals/Shaders/Shader.cpp +++ b/Outputs/CRT/Internals/Shaders/Shader.cpp @@ -53,7 +53,7 @@ Shader::Shader(const std::string &vertex_shader, const std::string &fragment_sha glAttachShader(shader_program_, vertex); glAttachShader(shader_program_, fragment); - for(auto &binding : attribute_bindings) { + for(const auto &binding : attribute_bindings) { glBindAttribLocation(shader_program_, binding.index, binding.name.c_str()); } diff --git a/SignalProcessing/FIRFilter.cpp b/SignalProcessing/FIRFilter.cpp index 696dcf9af..5a9525af1 100644 --- a/SignalProcessing/FIRFilter.cpp +++ b/SignalProcessing/FIRFilter.cpp @@ -92,7 +92,7 @@ void FIRFilter::coefficients_for_idealised_filter_response(short *filter_coeffic std::vector FIRFilter::get_coefficients() const { std::vector coefficients; - for(auto short_coefficient: filter_coefficients_) { + for(const auto short_coefficient: filter_coefficients_) { coefficients.push_back(static_cast(short_coefficient) / FixedMultiplier); } return coefficients; @@ -129,7 +129,7 @@ FIRFilter::FIRFilter(std::size_t number_of_taps, float input_sample_rate, float } FIRFilter::FIRFilter(const std::vector &coefficients) { - for(auto coefficient: coefficients) { + for(const auto coefficient: coefficients) { filter_coefficients_.push_back(static_cast(coefficient * FixedMultiplier)); } } diff --git a/Storage/Disk/DiskImage/DiskImageImplementation.hpp b/Storage/Disk/DiskImage/DiskImageImplementation.hpp index c73e70bf3..0f051cfa4 100644 --- a/Storage/Disk/DiskImage/DiskImageImplementation.hpp +++ b/Storage/Disk/DiskImage/DiskImageImplementation.hpp @@ -24,7 +24,7 @@ template void DiskImageHolder::flush_tracks() { using TrackMap = std::map>; std::shared_ptr track_copies(new TrackMap); - for(auto &address : unwritten_tracks_) { + for(const auto &address : unwritten_tracks_) { track_copies->insert(std::make_pair(address, std::shared_ptr(cached_tracks_[address]->clone()))); } unwritten_tracks_.clear(); diff --git a/Storage/Disk/DiskImage/Formats/MFMSectorDump.cpp b/Storage/Disk/DiskImage/Formats/MFMSectorDump.cpp index b83f4a697..7e510cd68 100644 --- a/Storage/Disk/DiskImage/Formats/MFMSectorDump.cpp +++ b/Storage/Disk/DiskImage/Formats/MFMSectorDump.cpp @@ -42,7 +42,7 @@ void MFMSectorDump::set_tracks(const std::map(sectors_per_track_-1), sector_size_, is_double_density_); long file_offset = get_file_offset_for_position(track.first); diff --git a/Storage/Disk/DiskImage/Formats/OricMFMDSK.cpp b/Storage/Disk/DiskImage/Formats/OricMFMDSK.cpp index 8c0f754e4..38ec3ac75 100644 --- a/Storage/Disk/DiskImage/Formats/OricMFMDSK.cpp +++ b/Storage/Disk/DiskImage/Formats/OricMFMDSK.cpp @@ -113,7 +113,7 @@ std::shared_ptr OricMFMDSK::get_track_at_position(Track::Address address) } void OricMFMDSK::set_tracks(const std::map> &tracks) { - for(auto &track : tracks) { + for(const auto &track : tracks) { PCMSegment segment = Storage::Disk::track_serialisation(*track.second, Storage::Encodings::MFM::MFMBitLength); Storage::Encodings::MFM::Shifter shifter; shifter.set_is_double_density(true); diff --git a/Storage/Disk/DiskImage/Formats/Utility/ImplicitSectors.cpp b/Storage/Disk/DiskImage/Formats/Utility/ImplicitSectors.cpp index bf94f0c5f..ca0015a25 100644 --- a/Storage/Disk/DiskImage/Formats/Utility/ImplicitSectors.cpp +++ b/Storage/Disk/DiskImage/Formats/Utility/ImplicitSectors.cpp @@ -52,7 +52,7 @@ void Storage::Disk::decode_sectors(Track &track, uint8_t *const destination, uin is_double_density); std::size_t byte_size = static_cast(128 << sector_size); - for(auto &pair : sectors) { + for(const auto &pair : sectors) { if(pair.second.address.sector > last_sector) continue; if(pair.second.address.sector < first_sector) continue; if(pair.second.size != sector_size) continue; diff --git a/Storage/Disk/Encodings/MFM/Parser.cpp b/Storage/Disk/Encodings/MFM/Parser.cpp index 184612545..154beab9c 100644 --- a/Storage/Disk/Encodings/MFM/Parser.cpp +++ b/Storage/Disk/Encodings/MFM/Parser.cpp @@ -32,7 +32,7 @@ void Parser::install_sectors_from_track(const Storage::Disk::Track::Address &add is_mfm_); std::map sectors_by_id; - for(auto §or : sectors) { + for(const auto §or : sectors) { sectors_by_id.insert(std::make_pair(sector.second.address.sector, std::move(sector.second))); } sectors_by_address_by_track_.insert(std::make_pair(address, std::move(sectors_by_id))); diff --git a/Storage/Disk/Track/PCMTrack.cpp b/Storage/Disk/Track/PCMTrack.cpp index 9966b4376..caa1197a5 100644 --- a/Storage/Disk/Track/PCMTrack.cpp +++ b/Storage/Disk/Track/PCMTrack.cpp @@ -16,14 +16,14 @@ PCMTrack::PCMTrack() : segment_pointer_(0) {} PCMTrack::PCMTrack(const std::vector &segments) : PCMTrack() { // sum total length of all segments Time total_length; - for(auto segment : segments) { + for(const auto &segment : segments) { total_length += segment.length_of_a_bit * segment.number_of_bits; } total_length.simplify(); // each segment is then some proportion of the total; for them all to sum to 1 they'll // need to be adjusted to be - for(auto segment : segments) { + for(const auto &segment : segments) { Time original_length_of_segment = segment.length_of_a_bit * segment.number_of_bits; Time proportion_of_whole = original_length_of_segment / total_length; proportion_of_whole.simplify(); diff --git a/Storage/Tape/Formats/TZX.cpp b/Storage/Tape/Formats/TZX.cpp index 2695fae19..7a0c693a1 100644 --- a/Storage/Tape/Formats/TZX.cpp +++ b/Storage/Tape/Formats/TZX.cpp @@ -190,7 +190,7 @@ void TZX::get_generalised_segment(uint32_t output_symbols, uint8_t max_pulses_pe } // Output waves. - for(auto length : symbol.pulse_lengths) { + for(const auto length : symbol.pulse_lengths) { if(!length) break; post_pulse(length); } From 05e31d75941e359871be6becbdb55294b67e1aa7 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 1 May 2018 19:52:12 -0400 Subject: [PATCH 43/44] Mutates `testComplicatedTrackSeek` into an actual test. Which frustratingly passes. --- .../Mac/Clock SignalTests/PCMTrackTests.mm | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/PCMTrackTests.mm b/OSBindings/Mac/Clock SignalTests/PCMTrackTests.mm index 221ffee3c..e531a63b1 100644 --- a/OSBindings/Mac/Clock SignalTests/PCMTrackTests.mm +++ b/OSBindings/Mac/Clock SignalTests/PCMTrackTests.mm @@ -57,14 +57,17 @@ Storage::Disk::PCMSegment sync_segment; sync_segment.data.resize(10); sync_segment.number_of_bits = 10*8; + memset(sync_segment.data.data(), 0xff, sync_segment.data.size()); Storage::Disk::PCMSegment header_segment; header_segment.data.resize(14); header_segment.number_of_bits = 14*8; + memset(header_segment.data.data(), 0xff, header_segment.data.size()); Storage::Disk::PCMSegment data_segment; data_segment.data.resize(349); data_segment.number_of_bits = 349*8; + memset(data_segment.data.data(), 0xff, data_segment.data.size()); for(std::size_t c = 0; c < 16; ++c) { segments.push_back(sync_segment); @@ -76,8 +79,19 @@ Storage::Disk::PCMTrack track(segments); Storage::Time late_time(967445, 2045454); - track.seek_to(late_time); - track.get_next_event(); + const auto offset = track.seek_to(late_time); + XCTAssert(offset <= late_time, "Found location should be at or before sought time"); + + const auto difference = late_time - offset; + const double difference_duration = difference.get(); + XCTAssert(difference_duration >= 0.0 && difference_duration < 0.005, "Next event should occur soon"); + + const double offset_duration = offset.get(); + XCTAssert(offset_duration >= 0.0 && offset_duration < 0.5, "Next event should occur soon"); + + auto next_event = track.get_next_event(); + double next_event_duration = next_event.length.get(); + XCTAssert(next_event_duration >= 0.0 && next_event_duration < 0.005, "Next event should occur soon"); } @end From 5e34c1b6b8fa283d71e2b1687aaad02c0f29bf54 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 1 May 2018 20:31:42 -0400 Subject: [PATCH 44/44] Switches to producing a single segment for NIBs and DSKs. I've now seemingly verified that the values read back by the CPU are those I'm intending to produce, so I'm at a loss. --- Storage/Disk/DiskImage/Formats/AppleDSK.cpp | 18 ++++++------- Storage/Disk/DiskImage/Formats/NIB.cpp | 13 ++++----- Storage/Disk/Track/PCMSegment.cpp | 30 +++++++++++++++++++++ Storage/Disk/Track/PCMSegment.hpp | 2 ++ 4 files changed, 47 insertions(+), 16 deletions(-) diff --git a/Storage/Disk/DiskImage/Formats/AppleDSK.cpp b/Storage/Disk/DiskImage/Formats/AppleDSK.cpp index fe9521365..a3c86c2e7 100644 --- a/Storage/Disk/DiskImage/Formats/AppleDSK.cpp +++ b/Storage/Disk/DiskImage/Formats/AppleDSK.cpp @@ -47,7 +47,7 @@ std::shared_ptr AppleDSK::get_track_at_position(Track::Address address) { file_.seek(file_offset, SEEK_SET); const std::vector track_data = file_.read(static_cast(bytes_per_sector * sectors_per_track_)); - std::vector segments; + Storage::Disk::PCMSegment segment; const uint8_t track = static_cast(address.position >> 2); // In either case below, the code aims for exactly 50,000 bits per track. @@ -55,11 +55,10 @@ std::shared_ptr AppleDSK::get_track_at_position(Track::Address address) { // Write the sectors. uint8_t sector_number_ = 0; for(std::size_t c = 0; c < 16; ++c) { - segments.push_back(Encodings::AppleGCR::six_and_two_sync(10)); - segments.push_back(Encodings::AppleGCR::header(0, track, sector_number_)); - segments.push_back(Encodings::AppleGCR::six_and_two_sync(10)); - segments.push_back(Encodings::AppleGCR::six_and_two_data(&track_data[c * 256])); - segments.push_back(Encodings::AppleGCR::six_and_two_sync(10)); + segment += Encodings::AppleGCR::six_and_two_sync(10); + segment += Encodings::AppleGCR::header(0, track, sector_number_); + segment += Encodings::AppleGCR::six_and_two_sync(10); + segment += Encodings::AppleGCR::six_and_two_data(&track_data[c * 256]); // DOS and Pro DOS interleave sectors on disk, and they're represented in a disk // image in physical order rather than logical. So that skew needs to be applied here. @@ -68,13 +67,12 @@ std::shared_ptr AppleDSK::get_track_at_position(Track::Address address) { } // Pad if necessary. - int encoded_length = (80 + 112 + 80 + 2848 + 80) * sectors_per_track_; - if(encoded_length < 50000) { - segments.push_back(Encodings::AppleGCR::six_and_two_sync((50000 - encoded_length) >> 3)); + if(segment.number_of_bits < 50000) { + segment += Encodings::AppleGCR::six_and_two_sync((50000 - segment.number_of_bits) >> 3); } } else { } - return std::shared_ptr(new PCMTrack(segments)); + return std::make_shared(segment); } diff --git a/Storage/Disk/DiskImage/Formats/NIB.cpp b/Storage/Disk/DiskImage/Formats/NIB.cpp index afc5870f3..656ac1b3e 100644 --- a/Storage/Disk/DiskImage/Formats/NIB.cpp +++ b/Storage/Disk/DiskImage/Formats/NIB.cpp @@ -50,7 +50,7 @@ std::shared_ptr<::Storage::Disk::Track> NIB::get_track_at_position(::Storage::Di // tracks and headers), then treat all following FFs as a sync // region, then switch back to ordinary behaviour as soon as a // non-FF appears. - std::vector segments; + PCMSegment segment; std::size_t start_index = 0; std::set sync_starts; @@ -74,8 +74,9 @@ std::shared_ptr<::Storage::Disk::Track> NIB::get_track_at_position(::Storage::Di } } - if(start_index) - segments.push_back(Encodings::AppleGCR::six_and_two_sync(static_cast(start_index))); + if(start_index) { + segment += Encodings::AppleGCR::six_and_two_sync(static_cast(start_index)); + } std::size_t index = start_index; for(const auto &location: sync_starts) { @@ -86,7 +87,7 @@ std::shared_ptr<::Storage::Disk::Track> NIB::get_track_at_position(::Storage::Di track_data.begin() + static_cast(index), track_data.begin() + static_cast(location)); data_segment.number_of_bits = static_cast(data_segment.data.size() * 8); - segments.push_back(std::move(data_segment)); + segment += data_segment; // Add a sync from sync_start to end of 0xffs. if(location == track_length-1) break; @@ -94,8 +95,8 @@ std::shared_ptr<::Storage::Disk::Track> NIB::get_track_at_position(::Storage::Di index = location; while(index < track_length && track_data[index] == 0xff) ++index; - segments.push_back(Encodings::AppleGCR::six_and_two_sync(static_cast(index - location))); + segment += Encodings::AppleGCR::six_and_two_sync(static_cast(index - location)); } - return std::shared_ptr(new PCMTrack(segments)); + return std::make_shared(segment); } diff --git a/Storage/Disk/Track/PCMSegment.cpp b/Storage/Disk/Track/PCMSegment.cpp index da07e0248..119084c47 100644 --- a/Storage/Disk/Track/PCMSegment.cpp +++ b/Storage/Disk/Track/PCMSegment.cpp @@ -43,6 +43,36 @@ void PCMSegmentEventSource::reset() { next_event_.type = Track::Event::FluxTransition; } +PCMSegment &PCMSegment::operator +=(const PCMSegment &rhs) { + if(!rhs.number_of_bits) return *this; + + if(number_of_bits&7) { + auto target_number_of_bits = number_of_bits + rhs.number_of_bits; + data.resize((target_number_of_bits + 7) >> 3); + + std::size_t first_byte = number_of_bits >> 3; + + int shift = number_of_bits&7; + data[first_byte] |= rhs.data[0] >> shift; + for(std::size_t target = first_byte+1; target < (data.size()-1); ++target) { + data[target] = + static_cast( + (rhs.data[target - first_byte - 1] << (8 - shift)) | + (rhs.data[target - first_byte] >> shift) + ); + } + data.back() = static_cast(rhs.data.back() << (8 - shift)); + + number_of_bits = target_number_of_bits; + } else { + data.insert(data.end(), rhs.data.begin(), rhs.data.end()); + number_of_bits += rhs.number_of_bits; + } + + return *this; +} + + Storage::Disk::Track::Event PCMSegmentEventSource::get_next_event() { // track the initial bit pointer for potentially considering whether this was an // initial index hole or a subsequent one later on diff --git a/Storage/Disk/Track/PCMSegment.hpp b/Storage/Disk/Track/PCMSegment.hpp index ea9bd4779..b7687a505 100644 --- a/Storage/Disk/Track/PCMSegment.hpp +++ b/Storage/Disk/Track/PCMSegment.hpp @@ -41,6 +41,8 @@ struct PCMSegment { number_of_bits = 0; data.clear(); } + + PCMSegment &operator +=(const PCMSegment &rhs); }; /*!