From f68565a33f53505b6d661c7f2e28c0dd85101e2c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 17 Aug 2017 10:48:29 -0400 Subject: [PATCH 1/2] Split the static analyser functionality so that it's possible just to ask for the set of media implied by a particular file. Extended ConfigurationTarget so that media alone can be pushed to a machine. --- Machines/AmstradCPC/AmstradCPC.cpp | 20 ++- Machines/Atari2600/Atari2600.cpp | 6 +- Machines/Commodore/Vic-20/Vic20.cpp | 63 ++++--- Machines/ConfigurationTarget.hpp | 10 +- Machines/Electron/Electron.cpp | 32 ++-- Machines/Oric/Oric.cpp | 30 ++-- Machines/ZX8081/ZX8081.cpp | 14 +- .../Documents/MachineDocument.swift | 4 + .../Mac/Clock Signal/Views/CSOpenGLView.h | 9 +- .../Mac/Clock Signal/Views/CSOpenGLView.m | 26 +++ StaticAnalyser/Acorn/StaticAnalyser.cpp | 22 +-- StaticAnalyser/Acorn/StaticAnalyser.hpp | 7 +- StaticAnalyser/AmstradCPC/StaticAnalyser.cpp | 22 +-- StaticAnalyser/AmstradCPC/StaticAnalyser.hpp | 7 +- StaticAnalyser/Atari/StaticAnalyser.cpp | 16 +- StaticAnalyser/Atari/StaticAnalyser.hpp | 7 +- StaticAnalyser/Commodore/StaticAnalyser.cpp | 18 +- StaticAnalyser/Commodore/StaticAnalyser.hpp | 7 +- StaticAnalyser/Oric/StaticAnalyser.cpp | 16 +- StaticAnalyser/Oric/StaticAnalyser.hpp | 7 +- StaticAnalyser/StaticAnalyser.cpp | 159 ++++++++++-------- StaticAnalyser/StaticAnalyser.hpp | 19 ++- StaticAnalyser/ZX8081/StaticAnalyser.cpp | 15 +- StaticAnalyser/ZX8081/StaticAnalyser.hpp | 7 +- 24 files changed, 300 insertions(+), 243 deletions(-) diff --git a/Machines/AmstradCPC/AmstradCPC.cpp b/Machines/AmstradCPC/AmstradCPC.cpp index f3900e1f6..c5afe939d 100644 --- a/Machines/AmstradCPC/AmstradCPC.cpp +++ b/Machines/AmstradCPC/AmstradCPC.cpp @@ -776,23 +776,29 @@ class ConcreteMachine: read_pointers_[2] = write_pointers_[2]; read_pointers_[3] = roms_[upper_rom_].data(); + // Type whatever is required. + if(target.loadingCommand.length()) { + set_typer_for_string(target.loadingCommand.c_str()); + } + + insert_media(target.media); + } + + bool insert_media(const StaticAnalyser::Media &media) { // If there are any tapes supplied, use the first of them. - if(!target.tapes.empty()) { - tape_player_.set_tape(target.tapes.front()); + if(!media.tapes.empty()) { + tape_player_.set_tape(media.tapes.front()); } // Insert up to four disks. int c = 0; - for(auto &disk : target.disks) { + for(auto &disk : media.disks) { fdc_.set_disk(disk, c); c++; if(c == 4) break; } - // Type whatever is required. - if(target.loadingCommand.length()) { - set_typer_for_string(target.loadingCommand.c_str()); - } + return !media.tapes.empty() || (!media.disks.empty() && has_fdc_); } // See header; provides the system ROMs. diff --git a/Machines/Atari2600/Atari2600.cpp b/Machines/Atari2600/Atari2600.cpp index 29e44e8c0..85f867854 100644 --- a/Machines/Atari2600/Atari2600.cpp +++ b/Machines/Atari2600/Atari2600.cpp @@ -45,7 +45,7 @@ class ConcreteMachine: } void configure_as_target(const StaticAnalyser::Target &target) { - const std::vector &rom = target.cartridges.front()->get_segments().front().data; + const std::vector &rom = target.media.cartridges.front()->get_segments().front().data; switch(target.atari.paging_model) { case StaticAnalyser::Atari2600PagingModel::ActivisionStack: bus_.reset(new Cartridge::Cartridge(rom)); break; case StaticAnalyser::Atari2600PagingModel::CBSRamPlus: bus_.reset(new Cartridge::Cartridge(rom)); break; @@ -81,6 +81,10 @@ class ConcreteMachine: } } + bool insert_media(const StaticAnalyser::Media &media) { + return false; + } + void set_digital_input(Atari2600DigitalInput input, bool state) { switch (input) { case Atari2600DigitalInputJoy1Up: bus_->mos6532_.update_port_input(0, 0x10, state); break; diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index d1cb99383..7095f4138 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -274,34 +274,6 @@ class ConcreteMachine: } void configure_as_target(const StaticAnalyser::Target &target) { - if(target.tapes.size()) { - tape_->set_tape(target.tapes.front()); - } - - if(target.disks.size()) { - // construct the 1540 - c1540_.reset(new ::Commodore::C1540::Machine); - - // attach it to the serial bus - c1540_->set_serial_bus(serial_bus_); - - // hand it the disk - c1540_->set_disk(target.disks.front()); - - // install the ROM if it was previously set - install_disk_rom(); - } - - if(target.cartridges.size()) { - rom_address_ = 0xa000; - std::vector rom_image = target.cartridges.front()->get_segments().front().data; - rom_length_ = (uint16_t)(rom_image.size()); - - rom_ = new uint8_t[0x2000]; - memcpy(rom_, rom_image.data(), rom_image.size()); - write_to_map(processor_read_memory_map_, rom_, rom_address_, 0x2000); - } - if(target.loadingCommand.length()) { set_typer_for_string(target.loadingCommand.c_str()); } @@ -317,6 +289,41 @@ class ConcreteMachine: set_memory_size(ThirtyTwoKB); break; } + + if(target.media.disks.size()) { + // construct the 1540 + c1540_.reset(new ::Commodore::C1540::Machine); + + // attach it to the serial bus + c1540_->set_serial_bus(serial_bus_); + + // install the ROM if it was previously set + install_disk_rom(); + } + + insert_media(target.media); + } + + bool insert_media(const StaticAnalyser::Media &media) { + if(!media.tapes.empty()) { + tape_->set_tape(media.tapes.front()); + } + + if(!media.disks.empty() && c1540_) { + c1540_->set_disk(media.disks.front()); + } + + if(!media.cartridges.empty()) { + rom_address_ = 0xa000; + std::vector rom_image = media.cartridges.front()->get_segments().front().data; + rom_length_ = (uint16_t)(rom_image.size()); + + rom_ = new uint8_t[0x2000]; + memcpy(rom_, rom_image.data(), rom_image.size()); + write_to_map(processor_read_memory_map_, rom_, rom_address_, 0x2000); + } + + return !media.tapes.empty() || (!media.disks.empty() && c1540_ != nullptr) || !media.cartridges.empty(); } void set_key_state(uint16_t key, bool isPressed) { diff --git a/Machines/ConfigurationTarget.hpp b/Machines/ConfigurationTarget.hpp index 4e48d6455..1a658db78 100644 --- a/Machines/ConfigurationTarget.hpp +++ b/Machines/ConfigurationTarget.hpp @@ -15,11 +15,19 @@ namespace ConfigurationTarget { /*! A ConfigurationTarget::Machine is anything that can accept a StaticAnalyser::Target - and configure itself appropriately. + and configure itself appropriately, or accept a list of media subsequently to insert. */ class Machine { public: + /// Instructs the machine to configure itself as described by @c target and insert the included media. virtual void configure_as_target(const StaticAnalyser::Target &target) = 0; + + /*! + Requests that the machine insert @c media as a modification to current state + + @returns @c true if any media was inserted; @c false otherwise. + */ + virtual bool insert_media(const StaticAnalyser::Media &media) = 0; }; } diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index 424459d2d..c4072061a 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -81,11 +81,15 @@ class ConcreteMachine: } void configure_as_target(const StaticAnalyser::Target &target) { - if(target.tapes.size()) { - tape_.set_tape(target.tapes.front()); + if(target.loadingCommand.length()) { + set_typer_for_string(target.loadingCommand.c_str()); } - if(target.disks.size()) { + if(target.acorn.should_shift_restart) { + shift_restart_counter_ = 1000000; + } + + if(target.acorn.has_dfs || target.acorn.has_adfs) { plus3_.reset(new Plus3); if(target.acorn.has_dfs) { @@ -95,23 +99,27 @@ class ConcreteMachine: set_rom(ROMSlot4, adfs_, true); set_rom(ROMSlot5, std::vector(adfs_.begin() + 16384, adfs_.end()), true); } + } - plus3_->set_disk(target.disks.front(), 0); + insert_media(target.media); + } + + bool insert_media(const StaticAnalyser::Media &media) { + if(!media.tapes.empty()) { + tape_.set_tape(media.tapes.front()); + } + + if(!media.disks.empty() && plus3_) { + plus3_->set_disk(media.disks.front(), 0); } ROMSlot slot = ROMSlot12; - for(std::shared_ptr cartridge : target.cartridges) { + for(std::shared_ptr cartridge : media.cartridges) { set_rom(slot, cartridge->get_segments().front().data, false); slot = (ROMSlot)(((int)slot + 1)&15); } - if(target.loadingCommand.length()) { - set_typer_for_string(target.loadingCommand.c_str()); - } - - if(target.acorn.should_shift_restart) { - shift_restart_counter_ = 1000000; - } + return !media.tapes.empty() || !media.disks.empty() || !media.cartridges.empty(); } Cycles perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) { diff --git a/Machines/Oric/Oric.cpp b/Machines/Oric/Oric.cpp index db2fd9df9..0df5d8be0 100644 --- a/Machines/Oric/Oric.cpp +++ b/Machines/Oric/Oric.cpp @@ -91,24 +91,14 @@ class ConcreteMachine: // to satisfy ConfigurationTarget::Machine void configure_as_target(const StaticAnalyser::Target &target) { - if(target.tapes.size()) { - via_.tape->set_tape(target.tapes.front()); - } - - if(target.loadingCommand.length()) { - set_typer_for_string(target.loadingCommand.c_str()); - } - if(target.oric.has_microdisc) { microdisc_is_enabled_ = true; microdisc_did_change_paging_flags(µdisc_); microdisc_.set_delegate(this); } - int drive_index = 0; - for(auto disk : target.disks) { - if(drive_index < 4) microdisc_.set_disk(disk, drive_index); - drive_index++; + if(target.loadingCommand.length()) { + set_typer_for_string(target.loadingCommand.c_str()); } if(target.oric.use_atmos_rom) { @@ -126,6 +116,22 @@ class ConcreteMachine: scan_keyboard_address_ = 0xf43c; tape_speed_address_ = 0x67; } + + insert_media(target.media); + } + + bool insert_media(const StaticAnalyser::Media &media) { + if(media.tapes.size()) { + via_.tape->set_tape(media.tapes.front()); + } + + int drive_index = 0; + for(auto disk : media.disks) { + if(drive_index < 4) microdisc_.set_disk(disk, drive_index); + drive_index++; + } + + return !media.tapes.empty() || (!media.disks.empty() && microdisc_is_enabled_); } // to satisfy CPU::MOS6502::BusHandler diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index 6806f14c2..c79b9e58a 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -268,13 +268,19 @@ class ConcreteMachine: } Memory::Fuzz(ram_); - if(target.tapes.size()) { - tape_player_.set_tape(target.tapes.front()); - } - if(target.loadingCommand.length()) { set_typer_for_string(target.loadingCommand.c_str()); } + + insert_media(target.media); + } + + bool insert_media(const StaticAnalyser::Media &media) { + if(!media.tapes.empty()) { + tape_player_.set_tape(media.tapes.front()); + } + + return !media.tapes.empty(); } void set_typer_for_string(const char *string) { diff --git a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift index 38a0f312e..83d5c3c15 100644 --- a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift +++ b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift @@ -164,6 +164,10 @@ class MachineDocument: } } + final func openGLView(_ view: CSOpenGLView, didReceiveFileAt URL: URL) { + // TODO: pass to machine. + } + // MARK: NSDocument overrides override func data(ofType typeName: String) throws -> Data { throw NSError(domain: NSOSStatusErrorDomain, code: unimpErr, userInfo: nil) diff --git a/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.h b/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.h index 3e18f5371..12daa4fb1 100644 --- a/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.h +++ b/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.h @@ -15,12 +15,19 @@ /*! Requests that the delegate produce an image of its current output state. May be called on any queue or thread. - @param view The view makin the request. + @param view The view making the request. @param onlyIfDirty If @c YES then the delegate may decline to redraw if its output would be identical to the previous frame. If @c NO then the delegate must draw. */ - (void)openGLView:(nonnull CSOpenGLView *)view drawViewOnlyIfDirty:(BOOL)onlyIfDirty; +/*! + Announces receipt of a file by drag and drop to the delegate. + @param view The view making the request. + @param URL The file URL of the received file. +*/ +- (void)openGLView:(nonnull CSOpenGLView *)view didReceiveFileAtURL:(nonnull NSURL *)URL; + @end @protocol CSOpenGLViewResponderDelegate diff --git a/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m b/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m index 08a2ca5b1..0e6330dcc 100644 --- a/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m +++ b/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m @@ -10,6 +10,9 @@ @import CoreVideo; @import GLKit; +@interface CSOpenGLView () +@end + @implementation CSOpenGLView { CVDisplayLinkRef _displayLink; } @@ -105,6 +108,9 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt self.pixelFormat = pixelFormat; self.openGLContext = context; self.wantsBestResolutionOpenGLSurface = YES; + + // Register to receive dragged and dropped file URLs. + [self registerForDraggedTypes:@[(__bridge NSString *)kUTTypeFileURL]]; } - (void)drawRect:(NSRect)dirtyRect @@ -150,4 +156,24 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt [self.responderDelegate flagsChanged:theEvent]; } +#pragma mark - NSDraggingDestination + +- (BOOL)performDragOperation:(id )sender +{ + for(NSPasteboardItem *item in [[sender draggingPasteboard] pasteboardItems]) + { + NSURL *URL = [NSURL URLWithString:[item stringForType:(__bridge NSString *)kUTTypeFileURL]]; + + NSLog(@"%@", URL); + [self.delegate openGLView:self didReceiveFileAtURL:URL]; + } + return YES; +} + +- (NSDragOperation)draggingEntered:(id < NSDraggingInfo >)sender +{ + // we'll drag and drop, yeah? + return NSDragOperationLink; +} + @end diff --git a/StaticAnalyser/Acorn/StaticAnalyser.cpp b/StaticAnalyser/Acorn/StaticAnalyser.cpp index 695c232cd..5fe262bb4 100644 --- a/StaticAnalyser/Acorn/StaticAnalyser.cpp +++ b/StaticAnalyser/Acorn/StaticAnalyser.cpp @@ -56,11 +56,7 @@ static std::list> return acorn_cartridges; } -void StaticAnalyser::Acorn::AddTargets( - const std::list> &disks, - const std::list> &tapes, - const std::list> &cartridges, - std::list &destination) { +void StaticAnalyser::Acorn::AddTargets(const Media &media, std::list &destination) { Target target; target.machine = Target::Electron; target.probability = 1.0; // TODO: a proper estimation @@ -69,11 +65,11 @@ void StaticAnalyser::Acorn::AddTargets( target.acorn.should_shift_restart = false; // strip out inappropriate cartridges - target.cartridges = AcornCartridgesFrom(cartridges); + target.media.cartridges = AcornCartridgesFrom(media.cartridges); // if there are any tapes, attempt to get data from the first - if(tapes.size() > 0) { - std::shared_ptr tape = tapes.front(); + if(media.tapes.size() > 0) { + std::shared_ptr tape = media.tapes.front(); std::list files = GetFiles(tape); tape->reset(); @@ -102,17 +98,17 @@ void StaticAnalyser::Acorn::AddTargets( // then the loading command is *RUN. Otherwise it's CHAIN"". target.loadingCommand = is_basic ? "CHAIN\"\"\n" : "*RUN\n"; - target.tapes = tapes; + target.media.tapes = media.tapes; } } - if(disks.size() > 0) { - std::shared_ptr disk = disks.front(); + if(media.disks.size() > 0) { + std::shared_ptr disk = media.disks.front(); std::unique_ptr dfs_catalogue, adfs_catalogue; dfs_catalogue = GetDFSCatalogue(disk); if(dfs_catalogue == nullptr) adfs_catalogue = GetADFSCatalogue(disk); if(dfs_catalogue || adfs_catalogue) { - target.disks = disks; + target.media.disks = media.disks; target.acorn.has_dfs = !!dfs_catalogue; target.acorn.has_adfs = !!adfs_catalogue; @@ -124,6 +120,6 @@ void StaticAnalyser::Acorn::AddTargets( } } - if(target.tapes.size() || target.disks.size() || target.cartridges.size()) + if(target.media.tapes.size() || target.media.disks.size() || target.media.cartridges.size()) destination.push_back(target); } diff --git a/StaticAnalyser/Acorn/StaticAnalyser.hpp b/StaticAnalyser/Acorn/StaticAnalyser.hpp index 240abcffa..c7ebbbf52 100644 --- a/StaticAnalyser/Acorn/StaticAnalyser.hpp +++ b/StaticAnalyser/Acorn/StaticAnalyser.hpp @@ -14,12 +14,7 @@ namespace StaticAnalyser { namespace Acorn { -void AddTargets( - const std::list> &disks, - const std::list> &tapes, - const std::list> &cartridges, - std::list &destination -); +void AddTargets(const Media &media, std::list &destination); } } diff --git a/StaticAnalyser/AmstradCPC/StaticAnalyser.cpp b/StaticAnalyser/AmstradCPC/StaticAnalyser.cpp index 5fec52032..ebce0c606 100644 --- a/StaticAnalyser/AmstradCPC/StaticAnalyser.cpp +++ b/StaticAnalyser/AmstradCPC/StaticAnalyser.cpp @@ -128,28 +128,24 @@ static void InspectSystemCatalogue( InspectDataCatalogue(catalogue, target); } -void StaticAnalyser::AmstradCPC::AddTargets( - const std::list> &disks, - const std::list> &tapes, - const std::list> &cartridges, - std::list &destination) { +void StaticAnalyser::AmstradCPC::AddTargets(const Media &media, std::list &destination) { Target target; target.machine = Target::AmstradCPC; target.probability = 1.0; - target.disks = disks; - target.tapes = tapes; - target.cartridges = cartridges; + target.media.disks = media.disks; + target.media.tapes = media.tapes; + target.media.cartridges = media.cartridges; target.amstradcpc.model = AmstradCPCModel::CPC6128; - if(!target.tapes.empty()) { + if(!target.media.tapes.empty()) { // Ugliness flows here: assume the CPC isn't smart enough to pause between pressing // enter and responding to the follow-on prompt to press a key, so just type for // a while. Yuck! target.loadingCommand = "|tape\nrun\"\n1234567890"; } - if(!target.disks.empty()) { + if(!target.media.disks.empty()) { Storage::Disk::CPM::ParameterBlock data_format; data_format.sectors_per_track = 9; data_format.tracks = 40; @@ -158,7 +154,7 @@ void StaticAnalyser::AmstradCPC::AddTargets( data_format.catalogue_allocation_bitmap = 0xc000; data_format.reserved_tracks = 0; - std::unique_ptr data_catalogue = Storage::Disk::CPM::GetCatalogue(target.disks.front(), data_format); + std::unique_ptr data_catalogue = Storage::Disk::CPM::GetCatalogue(target.media.disks.front(), data_format); if(data_catalogue) { InspectDataCatalogue(*data_catalogue, target); } else { @@ -170,9 +166,9 @@ void StaticAnalyser::AmstradCPC::AddTargets( system_format.catalogue_allocation_bitmap = 0xc000; system_format.reserved_tracks = 2; - std::unique_ptr system_catalogue = Storage::Disk::CPM::GetCatalogue(target.disks.front(), system_format); + std::unique_ptr system_catalogue = Storage::Disk::CPM::GetCatalogue(target.media.disks.front(), system_format); if(system_catalogue) { - InspectSystemCatalogue(target.disks.front(), *system_catalogue, target); + InspectSystemCatalogue(target.media.disks.front(), *system_catalogue, target); } } } diff --git a/StaticAnalyser/AmstradCPC/StaticAnalyser.hpp b/StaticAnalyser/AmstradCPC/StaticAnalyser.hpp index f798f5bcd..03811a376 100644 --- a/StaticAnalyser/AmstradCPC/StaticAnalyser.hpp +++ b/StaticAnalyser/AmstradCPC/StaticAnalyser.hpp @@ -14,12 +14,7 @@ namespace StaticAnalyser { namespace AmstradCPC { -void AddTargets( - const std::list> &disks, - const std::list> &tapes, - const std::list> &cartridges, - std::list &destination -); +void AddTargets(const Media &media, std::list &destination); } } diff --git a/StaticAnalyser/Atari/StaticAnalyser.cpp b/StaticAnalyser/Atari/StaticAnalyser.cpp index 9d83ce257..927629495 100644 --- a/StaticAnalyser/Atari/StaticAnalyser.cpp +++ b/StaticAnalyser/Atari/StaticAnalyser.cpp @@ -178,24 +178,18 @@ static void DeterminePagingForCartridge(StaticAnalyser::Target &target, const St } } -void StaticAnalyser::Atari::AddTargets( - const std::list> &disks, - const std::list> &tapes, - const std::list> &cartridges, - std::list &destination) { - // TODO: sanity checking; is this image really for an Atari 2600? +void StaticAnalyser::Atari::AddTargets(const Media &media, std::list &destination) { + // TODO: sanity checking; is this image really for an Atari 2600. Target target; target.machine = Target::Atari2600; target.probability = 1.0; - target.disks = disks; - target.tapes = tapes; - target.cartridges = cartridges; + target.media.cartridges = media.cartridges; target.atari.paging_model = Atari2600PagingModel::None; target.atari.uses_superchip = false; // try to figure out the paging scheme - if(!cartridges.empty()) { - const std::list &segments = cartridges.front()->get_segments(); + if(!media.cartridges.empty()) { + const std::list &segments = media.cartridges.front()->get_segments(); if(segments.size() == 1) { const Storage::Cartridge::Cartridge::Segment &segment = segments.front(); diff --git a/StaticAnalyser/Atari/StaticAnalyser.hpp b/StaticAnalyser/Atari/StaticAnalyser.hpp index d6fc23ef1..6276583c0 100644 --- a/StaticAnalyser/Atari/StaticAnalyser.hpp +++ b/StaticAnalyser/Atari/StaticAnalyser.hpp @@ -14,12 +14,7 @@ namespace StaticAnalyser { namespace Atari { -void AddTargets( - const std::list> &disks, - const std::list> &tapes, - const std::list> &cartridges, - std::list &destination -); +void AddTargets(const Media &media, std::list &destination); } } diff --git a/StaticAnalyser/Commodore/StaticAnalyser.cpp b/StaticAnalyser/Commodore/StaticAnalyser.cpp index 79668e950..f06ef5483 100644 --- a/StaticAnalyser/Commodore/StaticAnalyser.cpp +++ b/StaticAnalyser/Commodore/StaticAnalyser.cpp @@ -38,11 +38,7 @@ static std::list> return vic20_cartridges; } -void StaticAnalyser::Commodore::AddTargets( - const std::list> &disks, - const std::list> &tapes, - const std::list> &cartridges, - std::list &destination) { +void StaticAnalyser::Commodore::AddTargets(const Media &media, std::list &destination) { Target target; target.machine = Target::Vic20; // TODO: machine estimation target.probability = 1.0; // TODO: a proper estimation @@ -52,26 +48,26 @@ void StaticAnalyser::Commodore::AddTargets( bool is_disk = false; // strip out inappropriate cartridges - target.cartridges = Vic20CartridgesFrom(cartridges); + target.media.cartridges = Vic20CartridgesFrom(media.cartridges); // check disks - for(auto &disk : disks) { + for(auto &disk : media.disks) { std::list disk_files = GetFiles(disk); if(disk_files.size()) { is_disk = true; files.splice(files.end(), disk_files); - target.disks = disks; + target.media.disks.push_back(disk); if(!device) device = 8; } } // check tapes - for(auto &tape : tapes) { + for(auto &tape : media.tapes) { std::list tape_files = GetFiles(tape); tape->reset(); if(tape_files.size()) { files.splice(files.end(), tape_files); - target.tapes = tapes; + target.media.tapes.push_back(tape); if(!device) device = 1; } } @@ -140,6 +136,6 @@ void StaticAnalyser::Commodore::AddTargets( } } - if(target.tapes.size() || target.cartridges.size() || target.disks.size()) + if(target.media.tapes.size() || target.media.cartridges.size() || target.media.disks.size()) destination.push_back(target); } diff --git a/StaticAnalyser/Commodore/StaticAnalyser.hpp b/StaticAnalyser/Commodore/StaticAnalyser.hpp index e06fb57f3..df845c972 100644 --- a/StaticAnalyser/Commodore/StaticAnalyser.hpp +++ b/StaticAnalyser/Commodore/StaticAnalyser.hpp @@ -14,12 +14,7 @@ namespace StaticAnalyser { namespace Commodore { -void AddTargets( - const std::list> &disks, - const std::list> &tapes, - const std::list> &cartridges, - std::list &destination -); +void AddTargets(const Media &media, std::list &destination); } } diff --git a/StaticAnalyser/Oric/StaticAnalyser.cpp b/StaticAnalyser/Oric/StaticAnalyser.cpp index 915004994..55b84b267 100644 --- a/StaticAnalyser/Oric/StaticAnalyser.cpp +++ b/StaticAnalyser/Oric/StaticAnalyser.cpp @@ -72,11 +72,7 @@ static int Basic11Score(const StaticAnalyser::MOS6502::Disassembly &disassembly) return Score(disassembly, rom_functions, variable_locations); } -void StaticAnalyser::Oric::AddTargets( - const std::list> &disks, - const std::list> &tapes, - const std::list> &cartridges, - std::list &destination) { +void StaticAnalyser::Oric::AddTargets(const Media &media, std::list &destination) { Target target; target.machine = Target::Oric; target.probability = 1.0; @@ -84,7 +80,7 @@ void StaticAnalyser::Oric::AddTargets( int basic10_votes = 0; int basic11_votes = 0; - for(auto &tape : tapes) { + for(auto &tape : media.tapes) { std::list tape_files = GetFiles(tape); tape->reset(); if(tape_files.size()) { @@ -100,15 +96,15 @@ void StaticAnalyser::Oric::AddTargets( } } - target.tapes.push_back(tape); + target.media.tapes.push_back(tape); target.loadingCommand = "CLOAD\"\"\n"; } } // trust that any disk supplied can be handled by the Microdisc. TODO: check. - if(!disks.empty()) { + if(!media.disks.empty()) { target.oric.has_microdisc = true; - target.disks = disks; + target.media.disks = media.disks; } else { target.oric.has_microdisc = false; } @@ -117,6 +113,6 @@ void StaticAnalyser::Oric::AddTargets( target.oric.use_atmos_rom = basic11_votes >= basic10_votes; if(target.oric.has_microdisc) target.oric.use_atmos_rom = true; - if(target.tapes.size() || target.disks.size() || target.cartridges.size()) + if(target.media.tapes.size() || target.media.disks.size() || target.media.cartridges.size()) destination.push_back(target); } diff --git a/StaticAnalyser/Oric/StaticAnalyser.hpp b/StaticAnalyser/Oric/StaticAnalyser.hpp index 275278089..3b660367f 100644 --- a/StaticAnalyser/Oric/StaticAnalyser.hpp +++ b/StaticAnalyser/Oric/StaticAnalyser.hpp @@ -14,12 +14,7 @@ namespace StaticAnalyser { namespace Oric { -void AddTargets( - const std::list> &disks, - const std::list> &tapes, - const std::list> &cartridges, - std::list &destination -); +void AddTargets(const Media &media, std::list &destination); } } diff --git a/StaticAnalyser/StaticAnalyser.cpp b/StaticAnalyser/StaticAnalyser.cpp index 37e1a6c52..1d20722e7 100644 --- a/StaticAnalyser/StaticAnalyser.cpp +++ b/StaticAnalyser/StaticAnalyser.cpp @@ -53,6 +53,86 @@ enum class TargetPlatform: TargetPlatformType { using namespace StaticAnalyser; +static Media GetMediaAndPlatforms(const char *file_name, TargetPlatformType &potential_platforms) { + // Get the extension, if any; it will be assumed that extensions are reliable, so an extension is a broad-phase + // test as to file format. + const char *mixed_case_extension = strrchr(file_name, '.'); + char *lowercase_extension = nullptr; + if(mixed_case_extension) { + lowercase_extension = strdup(mixed_case_extension+1); + char *parser = lowercase_extension; + while(*parser) { + *parser = (char)tolower(*parser); + parser++; + } + } + + Media result; +#define Insert(list, class, platforms) \ + list.emplace_back(new Storage::class(file_name));\ + potential_platforms |= (TargetPlatformType)(platforms);\ + +#define TryInsert(list, class, platforms) \ + try {\ + Insert(list, class, platforms) \ + } catch(...) {} + +#define Format(extension, list, class, platforms) \ + if(!strcmp(lowercase_extension, extension)) { \ + TryInsert(list, class, platforms) \ + } + + if(lowercase_extension) { + Format("80", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // 80 + Format("81", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // 81 + Format("a26", result.cartridges, Cartridge::BinaryDump, TargetPlatform::Atari2600) // A26 + Format("adf", result.disks, Disk::AcornADF, TargetPlatform::Acorn) // ADF + Format("bin", result.cartridges, Cartridge::BinaryDump, TargetPlatform::Atari2600) // BIN + Format("cdt", result.tapes, Tape::TZX, TargetPlatform::AmstradCPC) // CDT + Format("csw", result.tapes, Tape::CSW, TargetPlatform::AllTape) // CSW + Format("d64", result.disks, Disk::D64, TargetPlatform::Commodore) // D64 + Format("dsd", result.disks, Disk::SSD, TargetPlatform::Acorn) // DSD + Format("dsk", result.disks, Disk::CPCDSK, TargetPlatform::AmstradCPC) // DSK (Amstrad CPC) + Format("dsk", result.disks, Disk::OricMFMDSK, TargetPlatform::Oric) // DSK (Oric) + Format("g64", result.disks, Disk::G64, TargetPlatform::Commodore) // G64 + 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 + + // PRG + if(!strcmp(lowercase_extension, "prg")) { + // try instantiating as a ROM; failing that accept as a tape + try { + Insert(result.cartridges, Cartridge::PRG, TargetPlatform::Commodore) + } catch(...) { + try { + Insert(result.tapes, Tape::PRG, TargetPlatform::Commodore) + } catch(...) {} + } + } + + Format("rom", result.cartridges, Cartridge::BinaryDump, TargetPlatform::Acorn) // ROM + Format("ssd", result.disks, Disk::SSD, TargetPlatform::Acorn) // SSD + Format("tap", result.tapes, Tape::CommodoreTAP, TargetPlatform::Commodore) // TAP (Commodore) + Format("tap", result.tapes, Tape::OricTAP, TargetPlatform::Oric) // TAP (Oric) + Format("tzx", result.tapes, Tape::TZX, TargetPlatform::ZX8081) // TZX + Format("uef", result.tapes, Tape::UEF, TargetPlatform::Acorn) // UEF (tape) + +#undef Format +#undef Insert +#undef TryInsert + + free(lowercase_extension); + } + + return result; +} + +Media StaticAnalyser::GetMedia(const char *file_name) { + TargetPlatformType throwaway; + return GetMediaAndPlatforms(file_name, throwaway); +} + std::list StaticAnalyser::GetTargets(const char *file_name) { std::list targets; @@ -71,80 +151,21 @@ std::list StaticAnalyser::GetTargets(const char *file_name) { // Collect all disks, tapes and ROMs as can be extrapolated from this file, forming the // union of all platforms this file might be a target for. - std::list> disks; - std::list> tapes; - std::list> cartridges; TargetPlatformType potential_platforms = 0; + Media media = GetMediaAndPlatforms(file_name, potential_platforms); -#define Insert(list, class, platforms) \ - list.emplace_back(new Storage::class(file_name));\ - potential_platforms |= (TargetPlatformType)(platforms);\ - -#define TryInsert(list, class, platforms) \ - try {\ - Insert(list, class, platforms) \ - } catch(...) {} - -#define Format(extension, list, class, platforms) \ - if(!strcmp(lowercase_extension, extension)) { \ - TryInsert(list, class, platforms) \ - } - - if(lowercase_extension) { - Format("80", tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // 80 - Format("81", tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // 81 - Format("a26", cartridges, Cartridge::BinaryDump, TargetPlatform::Atari2600) // A26 - Format("adf", disks, Disk::AcornADF, TargetPlatform::Acorn) // ADF - Format("bin", cartridges, Cartridge::BinaryDump, TargetPlatform::Atari2600) // BIN - Format("cdt", tapes, Tape::TZX, TargetPlatform::AmstradCPC) // CDT - Format("csw", tapes, Tape::CSW, TargetPlatform::AllTape) // CSW - Format("d64", disks, Disk::D64, TargetPlatform::Commodore) // D64 - Format("dsd", disks, Disk::SSD, TargetPlatform::Acorn) // DSD - Format("dsk", disks, Disk::CPCDSK, TargetPlatform::AmstradCPC) // DSK (Amstrad CPC) - Format("dsk", disks, Disk::OricMFMDSK, TargetPlatform::Oric) // DSK (Oric) - Format("g64", disks, Disk::G64, TargetPlatform::Commodore) // G64 - Format("o", tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // O - Format("p", tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // P - Format("p81", tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // P81 - - // PRG - if(!strcmp(lowercase_extension, "prg")) { - // try instantiating as a ROM; failing that accept as a tape - try { - Insert(cartridges, Cartridge::PRG, TargetPlatform::Commodore) - } catch(...) { - try { - Insert(tapes, Tape::PRG, TargetPlatform::Commodore) - } catch(...) {} - } - } - - Format("rom", cartridges, Cartridge::BinaryDump, TargetPlatform::Acorn) // ROM - Format("ssd", disks, Disk::SSD, TargetPlatform::Acorn) // SSD - Format("tap", tapes, Tape::CommodoreTAP, TargetPlatform::Commodore) // TAP (Commodore) - Format("tap", tapes, Tape::OricTAP, TargetPlatform::Oric) // TAP (Oric) - Format("tzx", tapes, Tape::TZX, TargetPlatform::ZX8081) // TZX - Format("uef", tapes, Tape::UEF, TargetPlatform::Acorn) // UEF (tape) - -#undef Format -#undef Insert -#undef TryInsert - - // Hand off to platform-specific determination of whether these things are actually compatible and, - // if so, how to load them. (TODO) - if(potential_platforms & (TargetPlatformType)TargetPlatform::Acorn) Acorn::AddTargets(disks, tapes, cartridges, targets); - if(potential_platforms & (TargetPlatformType)TargetPlatform::AmstradCPC) AmstradCPC::AddTargets(disks, tapes, cartridges, targets); - if(potential_platforms & (TargetPlatformType)TargetPlatform::Atari2600) Atari::AddTargets(disks, tapes, cartridges, targets); - if(potential_platforms & (TargetPlatformType)TargetPlatform::Commodore) Commodore::AddTargets(disks, tapes, cartridges, targets); - if(potential_platforms & (TargetPlatformType)TargetPlatform::Oric) Oric::AddTargets(disks, tapes, cartridges, targets); - if(potential_platforms & (TargetPlatformType)TargetPlatform::ZX8081) ZX8081::AddTargets(disks, tapes, cartridges, targets); - - free(lowercase_extension); - } + // Hand off to platform-specific determination of whether these things are actually compatible and, + // if so, how to load them. + if(potential_platforms & (TargetPlatformType)TargetPlatform::Acorn) Acorn::AddTargets(media, targets); + if(potential_platforms & (TargetPlatformType)TargetPlatform::AmstradCPC) AmstradCPC::AddTargets(media, targets); + if(potential_platforms & (TargetPlatformType)TargetPlatform::Atari2600) Atari::AddTargets(media, targets); + if(potential_platforms & (TargetPlatformType)TargetPlatform::Commodore) Commodore::AddTargets(media, targets); + if(potential_platforms & (TargetPlatformType)TargetPlatform::Oric) Oric::AddTargets(media, targets); + if(potential_platforms & (TargetPlatformType)TargetPlatform::ZX8081) ZX8081::AddTargets(media, targets); // Reset any tapes to their initial position for(auto target : targets) { - for(auto tape : target.tapes) { + for(auto tape : media.tapes) { tape->reset(); } } diff --git a/StaticAnalyser/StaticAnalyser.hpp b/StaticAnalyser/StaticAnalyser.hpp index df63a43fa..64bb8eb62 100644 --- a/StaticAnalyser/StaticAnalyser.hpp +++ b/StaticAnalyser/StaticAnalyser.hpp @@ -52,6 +52,15 @@ enum class AmstradCPCModel { CPC6128 }; +/*! + A list of disks, tapes and cartridges. +*/ +struct Media { + std::list> disks; + std::list> tapes; + std::list> cartridges; +}; + /*! A list of disks, tapes and cartridges plus information about the machine to which to attach them and its configuration, and instructions on how to launch the software attached, plus a measure of confidence in this target's correctness. @@ -102,10 +111,7 @@ struct Target { }; std::string loadingCommand; - - std::list> disks; - std::list> tapes; - std::list> cartridges; + Media media; }; /*! @@ -115,6 +121,11 @@ struct Target { */ std::list GetTargets(const char *file_name); +/*! + Inspects the supplied file and determines the media included. +*/ +Media GetMedia(const char *file_name); + } #endif /* StaticAnalyser_hpp */ diff --git a/StaticAnalyser/ZX8081/StaticAnalyser.cpp b/StaticAnalyser/ZX8081/StaticAnalyser.cpp index 2451d1aee..7449a0063 100644 --- a/StaticAnalyser/ZX8081/StaticAnalyser.cpp +++ b/StaticAnalyser/ZX8081/StaticAnalyser.cpp @@ -27,15 +27,10 @@ static std::vector GetFiles(const std::shared_ptr> &disks, - const std::list> &tapes, - const std::list> &cartridges, - std::list &destination) { - - if(!tapes.empty()) { - std::vector files = GetFiles(tapes.front()); - tapes.front()->reset(); +void StaticAnalyser::ZX8081::AddTargets(const Media &media, std::list &destination) { + if(!media.tapes.empty()) { + std::vector files = GetFiles(media.tapes.front()); + media.tapes.front()->reset(); if(!files.empty()) { StaticAnalyser::Target target; target.machine = Target::ZX8081; @@ -47,7 +42,7 @@ void StaticAnalyser::ZX8081::AddTargets( } else { target.zx8081.memory_model = ZX8081MemoryModel::Unexpanded; } - target.tapes = tapes; + target.media.tapes = media.tapes; // TODO: how to run software once loaded? Might require a BASIC detokeniser. if(target.zx8081.isZX81) { diff --git a/StaticAnalyser/ZX8081/StaticAnalyser.hpp b/StaticAnalyser/ZX8081/StaticAnalyser.hpp index 470e5920f..a352988be 100644 --- a/StaticAnalyser/ZX8081/StaticAnalyser.hpp +++ b/StaticAnalyser/ZX8081/StaticAnalyser.hpp @@ -14,12 +14,7 @@ namespace StaticAnalyser { namespace ZX8081 { -void AddTargets( - const std::list> &disks, - const std::list> &tapes, - const std::list> &cartridges, - std::list &destination -); +void AddTargets(const Media &media, std::list &destination); } } From 378f231499fde37858fe7ce055eb175ee7207ea7 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 17 Aug 2017 11:00:08 -0400 Subject: [PATCH 2/2] Fully wired in drag-and-drop for media insertion. --- .../Documents/MachineDocument.swift | 5 ++++- .../Clock Signal/Machine/CSMachine+Target.h | 3 ++- .../Mac/Clock Signal/Machine/CSMachine.mm | 10 +++++++++- .../Machine/StaticAnalyser/CSStaticAnalyser.h | 7 +++++++ .../Machine/StaticAnalyser/CSStaticAnalyser.mm | 18 ++++++++++++++++++ 5 files changed, 40 insertions(+), 3 deletions(-) diff --git a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift index 83d5c3c15..46a191fcd 100644 --- a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift +++ b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift @@ -165,7 +165,10 @@ class MachineDocument: } final func openGLView(_ view: CSOpenGLView, didReceiveFileAt URL: URL) { - // TODO: pass to machine. + let mediaSet = CSMediaSet(fileAt: URL) + if let mediaSet = mediaSet { + mediaSet.apply(to: self.machine) + } } // MARK: NSDocument overrides diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine+Target.h b/OSBindings/Mac/Clock Signal/Machine/CSMachine+Target.h index 6e720d929..f2f299396 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine+Target.h +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine+Target.h @@ -12,6 +12,7 @@ @interface CSMachine(Target) -- (void)applyTarget:(StaticAnalyser::Target)target; +- (void)applyTarget:(const StaticAnalyser::Target &)target; +- (void)applyMedia:(const StaticAnalyser::Media &)media; @end diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm index a5aaa10ef..58401636b 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm @@ -139,7 +139,7 @@ struct MachineDelegate: CRTMachine::Machine::Delegate { typeRecipient->set_typer_for_string([paste UTF8String]); } -- (void)applyTarget:(StaticAnalyser::Target)target { +- (void)applyTarget:(const StaticAnalyser::Target &)target { @synchronized(self) { ConfigurationTarget::Machine *const configurationTarget = dynamic_cast(self.machine); @@ -147,4 +147,12 @@ struct MachineDelegate: CRTMachine::Machine::Delegate { } } +- (void)applyMedia:(const StaticAnalyser::Media &)media { + @synchronized(self) { + ConfigurationTarget::Machine *const configurationTarget = + dynamic_cast(self.machine); + if(configurationTarget) configurationTarget->insert_media(media); + } +} + @end diff --git a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.h b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.h index 90bf509a6..bf3499e84 100644 --- a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.h +++ b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.h @@ -22,3 +22,10 @@ - (void)applyToMachine:(CSMachine *)machine; @end + +@interface CSMediaSet : NSObject + +- (instancetype)initWithFileAtURL:(NSURL *)url; +- (void)applyToMachine:(CSMachine *)machine; + +@end diff --git a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm index 7a3b12b09..3ed7a118e 100644 --- a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm +++ b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm @@ -69,3 +69,21 @@ } @end + +@implementation CSMediaSet { + StaticAnalyser::Media _media; +} + +- (instancetype)initWithFileAtURL:(NSURL *)url { + self = [super init]; + if(self) { + _media = StaticAnalyser::GetMedia([url fileSystemRepresentation]); + } + return self; +} + +- (void)applyToMachine:(CSMachine *)machine { + [machine applyMedia:_media]; +} + +@end