diff --git a/Analyser/Static/Enterprise/StaticAnalyser.cpp b/Analyser/Static/Enterprise/StaticAnalyser.cpp index ec0904ab0..02a3166f0 100644 --- a/Analyser/Static/Enterprise/StaticAnalyser.cpp +++ b/Analyser/Static/Enterprise/StaticAnalyser.cpp @@ -40,6 +40,8 @@ Analyser::Static::TargetList Analyser::Static::Enterprise::GetTargets(const Medi target->basic_version = Target::BASICVersion::Any; // Inspect any supplied disks. + // + // TODO: how best can these be discerned from MS-DOS and MSX disks? if(!media.disks.empty()) { // DOS will be needed. target->dos = Target::DOS::EXDOS; diff --git a/Analyser/Static/PCCompatible/StaticAnalyser.cpp b/Analyser/Static/PCCompatible/StaticAnalyser.cpp new file mode 100644 index 000000000..dbac1dd0b --- /dev/null +++ b/Analyser/Static/PCCompatible/StaticAnalyser.cpp @@ -0,0 +1,25 @@ +// +// StaticAnalyser.cpp +// Clock Signal +// +// Created by Thomas Harte on 03/10/2019. +// Copyright © 2019 Thomas Harte. All rights reserved. +// + +#include "StaticAnalyser.hpp" +#include "Target.hpp" + +Analyser::Static::TargetList Analyser::Static::PCCompatible::GetTargets(const Media &media, const std::string &, TargetPlatform::IntType) { + // This analyser can comprehend disks only. + if(media.disks.empty()) return {}; + + // No analysis is applied yet. + Analyser::Static::TargetList targets; + + using Target = Analyser::Static::PCCompatible::Target; + auto *const target = new Target(); + target->media = media; + targets.push_back(std::unique_ptr(target)); + + return targets; +} diff --git a/Analyser/Static/PCCompatible/StaticAnalyser.hpp b/Analyser/Static/PCCompatible/StaticAnalyser.hpp new file mode 100644 index 000000000..40a08bea7 --- /dev/null +++ b/Analyser/Static/PCCompatible/StaticAnalyser.hpp @@ -0,0 +1,22 @@ +// +// StaticAnalyser.hpp +// Clock Signal +// +// Created by Thomas Harte on 29/11/2019. +// Copyright © 2023 Thomas Harte. All rights reserved. +// + +#ifndef Analyser_Static_PCCompatible_StaticAnalyser_hpp +#define Analyser_Static_PCCompatible_StaticAnalyser_hpp + +#include "../StaticAnalyser.hpp" +#include "../../../Storage/TargetPlatforms.hpp" +#include + +namespace Analyser::Static::PCCompatible { + +TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms); + +} + +#endif /* Analyser_Static_PCCompatible_StaticAnalyser_hpp */ diff --git a/Analyser/Static/PCCompatible/Target.hpp b/Analyser/Static/PCCompatible/Target.hpp new file mode 100644 index 000000000..ef8e9deb0 --- /dev/null +++ b/Analyser/Static/PCCompatible/Target.hpp @@ -0,0 +1,33 @@ +// +// Target.hpp +// Clock Signal +// +// Created by Thomas Harte on 29/11/2023. +// Copyright © 2023 Thomas Harte. All rights reserved. +// + +#ifndef Analyser_Static_PCCompatible_Target_h +#define Analyser_Static_PCCompatible_Target_h + +#include "../../../Reflection/Struct.hpp" +#include "../StaticAnalyser.hpp" + +namespace Analyser::Static::PCCompatible { + +struct Target: public Analyser::Static::Target, public Reflection::StructImpl { + ReflectableEnum(VideoAdaptor, + MDA, + CGA); + VideoAdaptor adaptor = VideoAdaptor::MDA; + + Target() : Analyser::Static::Target(Machine::PCCompatible) { + if(needs_declare()) { + DeclareField(adaptor); + AnnounceEnum(VideoAdaptor); + } + } +}; + +} + +#endif /* Analyser_Static_PCCompatible_Target_h */ diff --git a/Analyser/Static/StaticAnalyser.cpp b/Analyser/Static/StaticAnalyser.cpp index ecae04603..c7e2578bc 100644 --- a/Analyser/Static/StaticAnalyser.cpp +++ b/Analyser/Static/StaticAnalyser.cpp @@ -28,6 +28,7 @@ #include "Macintosh/StaticAnalyser.hpp" #include "MSX/StaticAnalyser.hpp" #include "Oric/StaticAnalyser.hpp" +#include "PCCompatible/StaticAnalyser.hpp" #include "Sega/StaticAnalyser.hpp" #include "ZX8081/StaticAnalyser.hpp" #include "ZXSpectrum/StaticAnalyser.hpp" @@ -177,6 +178,7 @@ static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform:: Disk::DiskImageHolder, TargetPlatform::Acorn | TargetPlatform::AmstradCPC | TargetPlatform::Commodore | TargetPlatform::Oric | TargetPlatform::ZXSpectrum) // HFE (TODO: switch to AllDisk once the MSX stops being so greedy) + Format("ima", result.disks, Disk::DiskImageHolder, TargetPlatform::PCCompatible) // IMG (Enterprise/MS-DOS style) Format("img", result.disks, Disk::DiskImageHolder, TargetPlatform::Macintosh) // IMG (DiskCopy 4.2) Format("image", result.disks, Disk::DiskImageHolder, TargetPlatform::Macintosh) // IMG (DiskCopy 4.2) Format("img", result.disks, Disk::DiskImageHolder, TargetPlatform::Enterprise) // IMG (Enterprise/MS-DOS style) @@ -289,6 +291,7 @@ TargetList Analyser::Static::GetTargets(const std::string &file_name) { Append(Macintosh); Append(MSX); Append(Oric); + Append(PCCompatible); Append(Sega); Append(ZX8081); Append(ZXSpectrum); diff --git a/Machines/PCCompatible/PCCompatible.cpp b/Machines/PCCompatible/PCCompatible.cpp index 17a96f10a..27995d5ed 100644 --- a/Machines/PCCompatible/PCCompatible.cpp +++ b/Machines/PCCompatible/PCCompatible.cpp @@ -30,8 +30,13 @@ #include "../../Outputs/CRT/CRT.hpp" #include "../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp" +#include "../../Storage/Disk/Track/TrackSerialiser.hpp" +#include "../../Storage/Disk/Encodings/MFM/Constants.hpp" +#include "../../Storage/Disk/Encodings/MFM/SegmentParser.hpp" + #include "../AudioProducer.hpp" #include "../KeyboardMachine.hpp" +#include "../MediaTarget.hpp" #include "../ScanProducer.hpp" #include "../TimedMachine.hpp" @@ -101,10 +106,15 @@ class FloppyController { printf("TODO: implement FDC command %d\n", uint8_t(decoder_.command())); break; + case Command::ReadData: + printf("FDC: Read %d:%d at %d/%d\n", decoder_.target().drive, decoder_.target().head, decoder_.geometry().cylinder, decoder_.geometry().head); + break; + case Command::Seek: printf("FDC: Seek %d:%d to %d\n", decoder_.target().drive, decoder_.target().head, decoder_.seek_target()); drives_[decoder_.target().drive].track = decoder_.seek_target(); drives_[decoder_.target().drive].side = decoder_.target().head; + drives_[decoder_.target().drive].cache_track(); drives_[decoder_.target().drive].raised_interrupt = true; drives_[decoder_.target().drive].status = decoder_.drive_head() | uint8_t(Intel::i8272::Status0::SeekEnded); @@ -113,6 +123,8 @@ class FloppyController { case Command::Recalibrate: printf("FDC: Recalibrate\n"); drives_[decoder_.target().drive].track = 0; + drives_[decoder_.target().drive].cache_track(); + drives_[decoder_.target().drive].raised_interrupt = true; drives_[decoder_.target().drive].status = decoder_.target().drive | uint8_t(Intel::i8272::Status0::SeekEnded); pic_.apply_edge<6>(true); @@ -192,6 +204,10 @@ class FloppyController { } } + void set_disk(std::shared_ptr disk, int drive) { + drives_[drive].disk = disk; + } + private: void reset() { printf("FDC reset\n"); @@ -230,6 +246,32 @@ class FloppyController { bool side = false; bool motor = false; bool exists = true; + + std::shared_ptr disk; + Storage::Encodings::MFM::SectorMap cached_track; + void cache_track() { + if(!disk) { + return; + } + cached_track.clear(); + + auto raw_track = disk->get_track_at_position( + Storage::Disk::Track::Address( + side, + Storage::Disk::HeadPosition(track) + ) + ); + if(!raw_track) { + return; + } + + const bool is_double_density = true; // TODO: use MFM flag here. + auto serialisation = Storage::Disk::track_serialisation( + *raw_track, + is_double_density ? Storage::Encodings::MFM::MFMBitLength : Storage::Encodings::MFM::FMBitLength + ); + cached_track = Storage::Encodings::MFM::sectors_from_segment(std::move(serialisation), is_double_density); + } } drives_[4]; std::string drive_name(int c) const { @@ -1088,8 +1130,9 @@ class ConcreteMachine: public Machine, public MachineTypes::TimedMachine, public MachineTypes::AudioProducer, - public MachineTypes::ScanProducer, public MachineTypes::MappedKeyboardMachine, + public MachineTypes::MediaTarget, + public MachineTypes::ScanProducer, public Activity::Source { public: @@ -1126,6 +1169,9 @@ class ConcreteMachine: // Give the MDA something to read from. const auto &font_contents = roms.find(font)->second; mda_.set_source(context.memory.at(0xb'0000), font_contents); + + // ... and insert media. + insert_media(target.media); } ~ConcreteMachine() { @@ -1238,6 +1284,17 @@ class ConcreteMachine: } } + // MARK: - MediaTarget + bool insert_media(const Analyser::Static::Media &media) override { + int c = 0; + for(auto &disk : media.disks) { + fdc_.set_disk(disk, c); + c++; + if(c == 4) break; + } + return true; + } + // MARK: - MappedKeyboardMachine. MappedKeyboardMachine::KeyboardMapper *get_keyboard_mapper() override { return &keyboard_mapper_; diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 9085363c4..6a77713d5 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 423820112B17CBC800964EFE /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 423820102B17CBC800964EFE /* StaticAnalyser.cpp */; }; + 423820122B17CBC800964EFE /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 423820102B17CBC800964EFE /* StaticAnalyser.cpp */; }; 423BDC4A2AB24699008E37B6 /* 8088Tests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 423BDC492AB24699008E37B6 /* 8088Tests.mm */; }; 42437B332AC70833006DFED1 /* HDV.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B6FD0342923061300EC4760 /* HDV.cpp */; }; 425739382B051EA800B7D1E4 /* PCCompatible.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 425739372B051EA800B7D1E4 /* PCCompatible.cpp */; }; @@ -1128,6 +1130,9 @@ /* Begin PBXFileReference section */ 4238200B2B1295AD00964EFE /* Status.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Status.hpp; sourceTree = ""; }; 4238200C2B15998800964EFE /* Results.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Results.hpp; sourceTree = ""; }; + 4238200E2B17CBC800964EFE /* StaticAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaticAnalyser.hpp; sourceTree = ""; }; + 4238200F2B17CBC800964EFE /* Target.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = ""; }; + 423820102B17CBC800964EFE /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StaticAnalyser.cpp; sourceTree = ""; }; 423BDC492AB24699008E37B6 /* 8088Tests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = 8088Tests.mm; sourceTree = ""; }; 42437B342ACF02A9006DFED1 /* Flags.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Flags.hpp; sourceTree = ""; }; 42437B352ACF0AA2006DFED1 /* Perform.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Perform.hpp; sourceTree = ""; }; @@ -2346,6 +2351,16 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 4238200D2B17CBC800964EFE /* PCCompatible */ = { + isa = PBXGroup; + children = ( + 4238200E2B17CBC800964EFE /* StaticAnalyser.hpp */, + 4238200F2B17CBC800964EFE /* Target.hpp */, + 423820102B17CBC800964EFE /* StaticAnalyser.cpp */, + ); + path = PCCompatible; + sourceTree = ""; + }; 42437B372ACF2798006DFED1 /* Implementation */ = { isa = PBXGroup; children = ( @@ -3581,6 +3596,7 @@ 4BB4BFB622A4372E0069048D /* Macintosh */, 4B89450F201967B4007DE474 /* MSX */, 4B8944F6201967B4007DE474 /* Oric */, + 4238200D2B17CBC800964EFE /* PCCompatible */, 4B7F1894215486A100388727 /* Sega */, 4B894504201967B4007DE474 /* ZX8081 */, 4B0F1BAF2602645900B85C66 /* ZXSpectrum */, @@ -5724,6 +5740,7 @@ 4B0ACC2D23775819008902D0 /* IntelligentKeyboard.cpp in Sources */, 4B894539201967B4007DE474 /* Tape.cpp in Sources */, 4B92E26B234AE35100CD6D1B /* MFP68901.cpp in Sources */, + 423820122B17CBC800964EFE /* StaticAnalyser.cpp in Sources */, 4B7F1898215486A200388727 /* StaticAnalyser.cpp in Sources */, 4B15A9FD208249BB005E6C8D /* StaticAnalyser.cpp in Sources */, 4B055AD31FAE9B0B0060FFFF /* Microdisc.cpp in Sources */, @@ -5994,6 +6011,7 @@ 4B0ACC2A23775819008902D0 /* Video.cpp in Sources */, 4B54C0BF1F8D8F450050900F /* Keyboard.cpp in Sources */, 4B3FE75E1F3CF68B00448EE4 /* CPM.cpp in Sources */, + 423820112B17CBC800964EFE /* StaticAnalyser.cpp in Sources */, 4BC6236D26F4235400F83DFE /* Copper.cpp in Sources */, 4B2BFDB21DAEF5FF001A68B8 /* Video.cpp in Sources */, 4BEDA3BF25B25563000C2DBD /* Decoder.cpp in Sources */, diff --git a/OSBindings/Mac/Clock Signal/Info.plist b/OSBindings/Mac/Clock Signal/Info.plist index eba1e15b4..bec7b908b 100644 --- a/OSBindings/Mac/Clock Signal/Info.plist +++ b/OSBindings/Mac/Clock Signal/Info.plist @@ -672,6 +672,26 @@ NSDocumentClass $(PRODUCT_MODULE_NAME).MachineDocument + + CFBundleTypeExtensions + + ima + + CFBundleTypeName + Raw DOS disk image + CFBundleTypeOSTypes + + ???? + + CFBundleTypeRole + Viewer + LSHandlerRank + Owner + LSTypeIsPackage + + NSDocumentClass + $(PRODUCT_MODULE_NAME).MachineDocument + CFBundleTypeExtensions diff --git a/OSBindings/Qt/clksignal.pro b/OSBindings/Qt/clksignal.pro index 95cfe635e..c77ccee80 100644 --- a/OSBindings/Qt/clksignal.pro +++ b/OSBindings/Qt/clksignal.pro @@ -52,6 +52,7 @@ SOURCES += \ $$SRC/Analyser/Static/Macintosh/*.cpp \ $$SRC/Analyser/Static/MSX/*.cpp \ $$SRC/Analyser/Static/Oric/*.cpp \ + $$SRC/Analyser/Static/PCCompatible/*.cpp \ $$SRC/Analyser/Static/Sega/*.cpp \ $$SRC/Analyser/Static/ZX8081/*.cpp \ $$SRC/Analyser/Static/ZXSpectrum/*.cpp \ @@ -174,6 +175,7 @@ HEADERS += \ $$SRC/Analyser/Static/Macintosh/*.hpp \ $$SRC/Analyser/Static/MSX/*.hpp \ $$SRC/Analyser/Static/Oric/*.hpp \ + $$SRC/Analyser/Static/PCCompatible/*.hpp \ $$SRC/Analyser/Static/Sega/*.hpp \ $$SRC/Analyser/Static/ZX8081/*.hpp \ \ diff --git a/OSBindings/SDL/SConstruct b/OSBindings/SDL/SConstruct index 3cadda6a2..af28fb3ef 100644 --- a/OSBindings/SDL/SConstruct +++ b/OSBindings/SDL/SConstruct @@ -36,6 +36,7 @@ SOURCES += glob.glob('../../Analyser/Static/Enterprise/*.cpp') SOURCES += glob.glob('../../Analyser/Static/Macintosh/*.cpp') SOURCES += glob.glob('../../Analyser/Static/MSX/*.cpp') SOURCES += glob.glob('../../Analyser/Static/Oric/*.cpp') +SOURCES += glob.glob('../../Analyser/Static/PCCompatible/*.cpp') SOURCES += glob.glob('../../Analyser/Static/Sega/*.cpp') SOURCES += glob.glob('../../Analyser/Static/ZX8081/*.cpp') SOURCES += glob.glob('../../Analyser/Static/ZXSpectrum/*.cpp') diff --git a/Storage/Disk/DiskImage/Formats/MFMSectorDump.cpp b/Storage/Disk/DiskImage/Formats/MFMSectorDump.cpp index b00546296..9f629b986 100644 --- a/Storage/Disk/DiskImage/Formats/MFMSectorDump.cpp +++ b/Storage/Disk/DiskImage/Formats/MFMSectorDump.cpp @@ -44,7 +44,6 @@ void MFMSectorDump::set_tracks(const std::map> &tracks) final; @@ -30,6 +29,7 @@ class MFMSectorDump: public DiskImage { protected: Storage::FileHolder file_; + void set_geometry(int sectors_per_track, uint8_t sector_size, uint8_t first_sector, bool is_double_density); private: virtual long get_file_offset_for_position(Track::Address address) = 0; diff --git a/Storage/Disk/Encodings/MFM/SegmentParser.hpp b/Storage/Disk/Encodings/MFM/SegmentParser.hpp index d5b37f181..9fc1c8d24 100644 --- a/Storage/Disk/Encodings/MFM/SegmentParser.hpp +++ b/Storage/Disk/Encodings/MFM/SegmentParser.hpp @@ -15,12 +15,14 @@ namespace Storage::Encodings::MFM { +using SectorMap = std::map; + /*! Scans @c segment for all included sectors, returning a set that maps from location within the segment (counted in bits from the beginning and pointing to the location the disk had reached upon detection of the ID mark) to sector. */ -std::map sectors_from_segment(const Disk::PCMSegment &&segment, bool is_double_density); +SectorMap sectors_from_segment(const Disk::PCMSegment &&segment, bool is_double_density); } diff --git a/Storage/TargetPlatforms.hpp b/Storage/TargetPlatforms.hpp index 790e428c2..eeed8657b 100644 --- a/Storage/TargetPlatforms.hpp +++ b/Storage/TargetPlatforms.hpp @@ -38,11 +38,12 @@ enum Type: IntType { ZX80 = 1 << 19, ZX81 = 1 << 20, ZXSpectrum = 1 << 21, + PCCompatible = 1 << 22, Acorn = AcornAtom | AcornElectron | BBCMaster | BBCModelA | BBCModelB, ZX8081 = ZX80 | ZX81, AllCartridge = Atari2600 | AcornElectron | Coleco | MSX, - AllDisk = Acorn | AmstradCPC | Commodore | Oric | MSX | ZXSpectrum | Macintosh | AtariST | DiskII | Amiga, + AllDisk = Acorn | AmstradCPC | Commodore | Oric | MSX | ZXSpectrum | Macintosh | AtariST | DiskII | Amiga | PCCompatible, AllTape = Acorn | AmstradCPC | Commodore | Oric | ZX8081 | MSX | ZXSpectrum, };