1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-30 07:29:06 +00:00

Merge pull request #207 from TomHarte/DragAndDrop

Establishes drag and drop as a mechanism to change the media inserted into a machine while it is running
This commit is contained in:
Thomas Harte 2017-08-17 11:19:56 -04:00 committed by GitHub
commit 58063b69a6
28 changed files with 339 additions and 245 deletions

View File

@ -776,23 +776,29 @@ class ConcreteMachine:
read_pointers_[2] = write_pointers_[2]; read_pointers_[2] = write_pointers_[2];
read_pointers_[3] = roms_[upper_rom_].data(); 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 there are any tapes supplied, use the first of them.
if(!target.tapes.empty()) { if(!media.tapes.empty()) {
tape_player_.set_tape(target.tapes.front()); tape_player_.set_tape(media.tapes.front());
} }
// Insert up to four disks. // Insert up to four disks.
int c = 0; int c = 0;
for(auto &disk : target.disks) { for(auto &disk : media.disks) {
fdc_.set_disk(disk, c); fdc_.set_disk(disk, c);
c++; c++;
if(c == 4) break; if(c == 4) break;
} }
// Type whatever is required. return !media.tapes.empty() || (!media.disks.empty() && has_fdc_);
if(target.loadingCommand.length()) {
set_typer_for_string(target.loadingCommand.c_str());
}
} }
// See header; provides the system ROMs. // See header; provides the system ROMs.

View File

@ -45,7 +45,7 @@ class ConcreteMachine:
} }
void configure_as_target(const StaticAnalyser::Target &target) { void configure_as_target(const StaticAnalyser::Target &target) {
const std::vector<uint8_t> &rom = target.cartridges.front()->get_segments().front().data; const std::vector<uint8_t> &rom = target.media.cartridges.front()->get_segments().front().data;
switch(target.atari.paging_model) { switch(target.atari.paging_model) {
case StaticAnalyser::Atari2600PagingModel::ActivisionStack: bus_.reset(new Cartridge::Cartridge<Cartridge::ActivisionStack>(rom)); break; case StaticAnalyser::Atari2600PagingModel::ActivisionStack: bus_.reset(new Cartridge::Cartridge<Cartridge::ActivisionStack>(rom)); break;
case StaticAnalyser::Atari2600PagingModel::CBSRamPlus: bus_.reset(new Cartridge::Cartridge<Cartridge::CBSRAMPlus>(rom)); break; case StaticAnalyser::Atari2600PagingModel::CBSRamPlus: bus_.reset(new Cartridge::Cartridge<Cartridge::CBSRAMPlus>(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) { void set_digital_input(Atari2600DigitalInput input, bool state) {
switch (input) { switch (input) {
case Atari2600DigitalInputJoy1Up: bus_->mos6532_.update_port_input(0, 0x10, state); break; case Atari2600DigitalInputJoy1Up: bus_->mos6532_.update_port_input(0, 0x10, state); break;

View File

@ -274,34 +274,6 @@ class ConcreteMachine:
} }
void configure_as_target(const StaticAnalyser::Target &target) { 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<uint8_t> 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()) { if(target.loadingCommand.length()) {
set_typer_for_string(target.loadingCommand.c_str()); set_typer_for_string(target.loadingCommand.c_str());
} }
@ -317,6 +289,41 @@ class ConcreteMachine:
set_memory_size(ThirtyTwoKB); set_memory_size(ThirtyTwoKB);
break; 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<uint8_t> 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) { void set_key_state(uint16_t key, bool isPressed) {

View File

@ -15,11 +15,19 @@ namespace ConfigurationTarget {
/*! /*!
A ConfigurationTarget::Machine is anything that can accept a StaticAnalyser::Target 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 { class Machine {
public: 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; 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;
}; };
} }

View File

@ -81,11 +81,15 @@ class ConcreteMachine:
} }
void configure_as_target(const StaticAnalyser::Target &target) { void configure_as_target(const StaticAnalyser::Target &target) {
if(target.tapes.size()) { if(target.loadingCommand.length()) {
tape_.set_tape(target.tapes.front()); 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); plus3_.reset(new Plus3);
if(target.acorn.has_dfs) { if(target.acorn.has_dfs) {
@ -95,23 +99,27 @@ class ConcreteMachine:
set_rom(ROMSlot4, adfs_, true); set_rom(ROMSlot4, adfs_, true);
set_rom(ROMSlot5, std::vector<uint8_t>(adfs_.begin() + 16384, adfs_.end()), true); set_rom(ROMSlot5, std::vector<uint8_t>(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; ROMSlot slot = ROMSlot12;
for(std::shared_ptr<Storage::Cartridge::Cartridge> cartridge : target.cartridges) { for(std::shared_ptr<Storage::Cartridge::Cartridge> cartridge : media.cartridges) {
set_rom(slot, cartridge->get_segments().front().data, false); set_rom(slot, cartridge->get_segments().front().data, false);
slot = (ROMSlot)(((int)slot + 1)&15); slot = (ROMSlot)(((int)slot + 1)&15);
} }
if(target.loadingCommand.length()) { return !media.tapes.empty() || !media.disks.empty() || !media.cartridges.empty();
set_typer_for_string(target.loadingCommand.c_str());
}
if(target.acorn.should_shift_restart) {
shift_restart_counter_ = 1000000;
}
} }
Cycles perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) { Cycles perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) {

View File

@ -91,24 +91,14 @@ class ConcreteMachine:
// to satisfy ConfigurationTarget::Machine // to satisfy ConfigurationTarget::Machine
void configure_as_target(const StaticAnalyser::Target &target) { 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) { if(target.oric.has_microdisc) {
microdisc_is_enabled_ = true; microdisc_is_enabled_ = true;
microdisc_did_change_paging_flags(&microdisc_); microdisc_did_change_paging_flags(&microdisc_);
microdisc_.set_delegate(this); microdisc_.set_delegate(this);
} }
int drive_index = 0; if(target.loadingCommand.length()) {
for(auto disk : target.disks) { set_typer_for_string(target.loadingCommand.c_str());
if(drive_index < 4) microdisc_.set_disk(disk, drive_index);
drive_index++;
} }
if(target.oric.use_atmos_rom) { if(target.oric.use_atmos_rom) {
@ -126,6 +116,22 @@ class ConcreteMachine:
scan_keyboard_address_ = 0xf43c; scan_keyboard_address_ = 0xf43c;
tape_speed_address_ = 0x67; 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 // to satisfy CPU::MOS6502::BusHandler

View File

@ -268,13 +268,19 @@ class ConcreteMachine:
} }
Memory::Fuzz(ram_); Memory::Fuzz(ram_);
if(target.tapes.size()) {
tape_player_.set_tape(target.tapes.front());
}
if(target.loadingCommand.length()) { if(target.loadingCommand.length()) {
set_typer_for_string(target.loadingCommand.c_str()); 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) { void set_typer_for_string(const char *string) {

View File

@ -164,6 +164,13 @@ class MachineDocument:
} }
} }
final func openGLView(_ view: CSOpenGLView, didReceiveFileAt URL: URL) {
let mediaSet = CSMediaSet(fileAt: URL)
if let mediaSet = mediaSet {
mediaSet.apply(to: self.machine)
}
}
// MARK: NSDocument overrides // MARK: NSDocument overrides
override func data(ofType typeName: String) throws -> Data { override func data(ofType typeName: String) throws -> Data {
throw NSError(domain: NSOSStatusErrorDomain, code: unimpErr, userInfo: nil) throw NSError(domain: NSOSStatusErrorDomain, code: unimpErr, userInfo: nil)

View File

@ -12,6 +12,7 @@
@interface CSMachine(Target) @interface CSMachine(Target)
- (void)applyTarget:(StaticAnalyser::Target)target; - (void)applyTarget:(const StaticAnalyser::Target &)target;
- (void)applyMedia:(const StaticAnalyser::Media &)media;
@end @end

View File

@ -139,7 +139,7 @@ struct MachineDelegate: CRTMachine::Machine::Delegate {
typeRecipient->set_typer_for_string([paste UTF8String]); typeRecipient->set_typer_for_string([paste UTF8String]);
} }
- (void)applyTarget:(StaticAnalyser::Target)target { - (void)applyTarget:(const StaticAnalyser::Target &)target {
@synchronized(self) { @synchronized(self) {
ConfigurationTarget::Machine *const configurationTarget = ConfigurationTarget::Machine *const configurationTarget =
dynamic_cast<ConfigurationTarget::Machine *>(self.machine); dynamic_cast<ConfigurationTarget::Machine *>(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<ConfigurationTarget::Machine *>(self.machine);
if(configurationTarget) configurationTarget->insert_media(media);
}
}
@end @end

View File

@ -22,3 +22,10 @@
- (void)applyToMachine:(CSMachine *)machine; - (void)applyToMachine:(CSMachine *)machine;
@end @end
@interface CSMediaSet : NSObject
- (instancetype)initWithFileAtURL:(NSURL *)url;
- (void)applyToMachine:(CSMachine *)machine;
@end

View File

@ -69,3 +69,21 @@
} }
@end @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

View File

@ -15,12 +15,19 @@
/*! /*!
Requests that the delegate produce an image of its current output state. May be called on Requests that the delegate produce an image of its current output state. May be called on
any queue or thread. 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 @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. identical to the previous frame. If @c NO then the delegate must draw.
*/ */
- (void)openGLView:(nonnull CSOpenGLView *)view drawViewOnlyIfDirty:(BOOL)onlyIfDirty; - (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 @end
@protocol CSOpenGLViewResponderDelegate <NSObject> @protocol CSOpenGLViewResponderDelegate <NSObject>

View File

@ -10,6 +10,9 @@
@import CoreVideo; @import CoreVideo;
@import GLKit; @import GLKit;
@interface CSOpenGLView () <NSDraggingDestination>
@end
@implementation CSOpenGLView { @implementation CSOpenGLView {
CVDisplayLinkRef _displayLink; CVDisplayLinkRef _displayLink;
} }
@ -105,6 +108,9 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
self.pixelFormat = pixelFormat; self.pixelFormat = pixelFormat;
self.openGLContext = context; self.openGLContext = context;
self.wantsBestResolutionOpenGLSurface = YES; self.wantsBestResolutionOpenGLSurface = YES;
// Register to receive dragged and dropped file URLs.
[self registerForDraggedTypes:@[(__bridge NSString *)kUTTypeFileURL]];
} }
- (void)drawRect:(NSRect)dirtyRect - (void)drawRect:(NSRect)dirtyRect
@ -150,4 +156,24 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
[self.responderDelegate flagsChanged:theEvent]; [self.responderDelegate flagsChanged:theEvent];
} }
#pragma mark - NSDraggingDestination
- (BOOL)performDragOperation:(id <NSDraggingInfo>)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 @end

View File

@ -56,11 +56,7 @@ static std::list<std::shared_ptr<Storage::Cartridge::Cartridge>>
return acorn_cartridges; return acorn_cartridges;
} }
void StaticAnalyser::Acorn::AddTargets( void StaticAnalyser::Acorn::AddTargets(const Media &media, std::list<Target> &destination) {
const std::list<std::shared_ptr<Storage::Disk::Disk>> &disks,
const std::list<std::shared_ptr<Storage::Tape::Tape>> &tapes,
const std::list<std::shared_ptr<Storage::Cartridge::Cartridge>> &cartridges,
std::list<StaticAnalyser::Target> &destination) {
Target target; Target target;
target.machine = Target::Electron; target.machine = Target::Electron;
target.probability = 1.0; // TODO: a proper estimation target.probability = 1.0; // TODO: a proper estimation
@ -69,11 +65,11 @@ void StaticAnalyser::Acorn::AddTargets(
target.acorn.should_shift_restart = false; target.acorn.should_shift_restart = false;
// strip out inappropriate cartridges // 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 there are any tapes, attempt to get data from the first
if(tapes.size() > 0) { if(media.tapes.size() > 0) {
std::shared_ptr<Storage::Tape::Tape> tape = tapes.front(); std::shared_ptr<Storage::Tape::Tape> tape = media.tapes.front();
std::list<File> files = GetFiles(tape); std::list<File> files = GetFiles(tape);
tape->reset(); tape->reset();
@ -102,17 +98,17 @@ void StaticAnalyser::Acorn::AddTargets(
// then the loading command is *RUN. Otherwise it's CHAIN"". // then the loading command is *RUN. Otherwise it's CHAIN"".
target.loadingCommand = is_basic ? "CHAIN\"\"\n" : "*RUN\n"; target.loadingCommand = is_basic ? "CHAIN\"\"\n" : "*RUN\n";
target.tapes = tapes; target.media.tapes = media.tapes;
} }
} }
if(disks.size() > 0) { if(media.disks.size() > 0) {
std::shared_ptr<Storage::Disk::Disk> disk = disks.front(); std::shared_ptr<Storage::Disk::Disk> disk = media.disks.front();
std::unique_ptr<Catalogue> dfs_catalogue, adfs_catalogue; std::unique_ptr<Catalogue> dfs_catalogue, adfs_catalogue;
dfs_catalogue = GetDFSCatalogue(disk); dfs_catalogue = GetDFSCatalogue(disk);
if(dfs_catalogue == nullptr) adfs_catalogue = GetADFSCatalogue(disk); if(dfs_catalogue == nullptr) adfs_catalogue = GetADFSCatalogue(disk);
if(dfs_catalogue || adfs_catalogue) { if(dfs_catalogue || adfs_catalogue) {
target.disks = disks; target.media.disks = media.disks;
target.acorn.has_dfs = !!dfs_catalogue; target.acorn.has_dfs = !!dfs_catalogue;
target.acorn.has_adfs = !!adfs_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); destination.push_back(target);
} }

View File

@ -14,12 +14,7 @@
namespace StaticAnalyser { namespace StaticAnalyser {
namespace Acorn { namespace Acorn {
void AddTargets( void AddTargets(const Media &media, std::list<Target> &destination);
const std::list<std::shared_ptr<Storage::Disk::Disk>> &disks,
const std::list<std::shared_ptr<Storage::Tape::Tape>> &tapes,
const std::list<std::shared_ptr<Storage::Cartridge::Cartridge>> &cartridges,
std::list<Target> &destination
);
} }
} }

View File

@ -128,28 +128,24 @@ static void InspectSystemCatalogue(
InspectDataCatalogue(catalogue, target); InspectDataCatalogue(catalogue, target);
} }
void StaticAnalyser::AmstradCPC::AddTargets( void StaticAnalyser::AmstradCPC::AddTargets(const Media &media, std::list<Target> &destination) {
const std::list<std::shared_ptr<Storage::Disk::Disk>> &disks,
const std::list<std::shared_ptr<Storage::Tape::Tape>> &tapes,
const std::list<std::shared_ptr<Storage::Cartridge::Cartridge>> &cartridges,
std::list<StaticAnalyser::Target> &destination) {
Target target; Target target;
target.machine = Target::AmstradCPC; target.machine = Target::AmstradCPC;
target.probability = 1.0; target.probability = 1.0;
target.disks = disks; target.media.disks = media.disks;
target.tapes = tapes; target.media.tapes = media.tapes;
target.cartridges = cartridges; target.media.cartridges = media.cartridges;
target.amstradcpc.model = AmstradCPCModel::CPC6128; 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 // 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 // enter and responding to the follow-on prompt to press a key, so just type for
// a while. Yuck! // a while. Yuck!
target.loadingCommand = "|tape\nrun\"\n1234567890"; target.loadingCommand = "|tape\nrun\"\n1234567890";
} }
if(!target.disks.empty()) { if(!target.media.disks.empty()) {
Storage::Disk::CPM::ParameterBlock data_format; Storage::Disk::CPM::ParameterBlock data_format;
data_format.sectors_per_track = 9; data_format.sectors_per_track = 9;
data_format.tracks = 40; data_format.tracks = 40;
@ -158,7 +154,7 @@ void StaticAnalyser::AmstradCPC::AddTargets(
data_format.catalogue_allocation_bitmap = 0xc000; data_format.catalogue_allocation_bitmap = 0xc000;
data_format.reserved_tracks = 0; data_format.reserved_tracks = 0;
std::unique_ptr<Storage::Disk::CPM::Catalogue> data_catalogue = Storage::Disk::CPM::GetCatalogue(target.disks.front(), data_format); std::unique_ptr<Storage::Disk::CPM::Catalogue> data_catalogue = Storage::Disk::CPM::GetCatalogue(target.media.disks.front(), data_format);
if(data_catalogue) { if(data_catalogue) {
InspectDataCatalogue(*data_catalogue, target); InspectDataCatalogue(*data_catalogue, target);
} else { } else {
@ -170,9 +166,9 @@ void StaticAnalyser::AmstradCPC::AddTargets(
system_format.catalogue_allocation_bitmap = 0xc000; system_format.catalogue_allocation_bitmap = 0xc000;
system_format.reserved_tracks = 2; system_format.reserved_tracks = 2;
std::unique_ptr<Storage::Disk::CPM::Catalogue> system_catalogue = Storage::Disk::CPM::GetCatalogue(target.disks.front(), system_format); std::unique_ptr<Storage::Disk::CPM::Catalogue> system_catalogue = Storage::Disk::CPM::GetCatalogue(target.media.disks.front(), system_format);
if(system_catalogue) { if(system_catalogue) {
InspectSystemCatalogue(target.disks.front(), *system_catalogue, target); InspectSystemCatalogue(target.media.disks.front(), *system_catalogue, target);
} }
} }
} }

View File

@ -14,12 +14,7 @@
namespace StaticAnalyser { namespace StaticAnalyser {
namespace AmstradCPC { namespace AmstradCPC {
void AddTargets( void AddTargets(const Media &media, std::list<Target> &destination);
const std::list<std::shared_ptr<Storage::Disk::Disk>> &disks,
const std::list<std::shared_ptr<Storage::Tape::Tape>> &tapes,
const std::list<std::shared_ptr<Storage::Cartridge::Cartridge>> &cartridges,
std::list<Target> &destination
);
} }
} }

View File

@ -178,24 +178,18 @@ static void DeterminePagingForCartridge(StaticAnalyser::Target &target, const St
} }
} }
void StaticAnalyser::Atari::AddTargets( void StaticAnalyser::Atari::AddTargets(const Media &media, std::list<Target> &destination) {
const std::list<std::shared_ptr<Storage::Disk::Disk>> &disks, // TODO: sanity checking; is this image really for an Atari 2600.
const std::list<std::shared_ptr<Storage::Tape::Tape>> &tapes,
const std::list<std::shared_ptr<Storage::Cartridge::Cartridge>> &cartridges,
std::list<StaticAnalyser::Target> &destination) {
// TODO: sanity checking; is this image really for an Atari 2600?
Target target; Target target;
target.machine = Target::Atari2600; target.machine = Target::Atari2600;
target.probability = 1.0; target.probability = 1.0;
target.disks = disks; target.media.cartridges = media.cartridges;
target.tapes = tapes;
target.cartridges = cartridges;
target.atari.paging_model = Atari2600PagingModel::None; target.atari.paging_model = Atari2600PagingModel::None;
target.atari.uses_superchip = false; target.atari.uses_superchip = false;
// try to figure out the paging scheme // try to figure out the paging scheme
if(!cartridges.empty()) { if(!media.cartridges.empty()) {
const std::list<Storage::Cartridge::Cartridge::Segment> &segments = cartridges.front()->get_segments(); const std::list<Storage::Cartridge::Cartridge::Segment> &segments = media.cartridges.front()->get_segments();
if(segments.size() == 1) { if(segments.size() == 1) {
const Storage::Cartridge::Cartridge::Segment &segment = segments.front(); const Storage::Cartridge::Cartridge::Segment &segment = segments.front();

View File

@ -14,12 +14,7 @@
namespace StaticAnalyser { namespace StaticAnalyser {
namespace Atari { namespace Atari {
void AddTargets( void AddTargets(const Media &media, std::list<Target> &destination);
const std::list<std::shared_ptr<Storage::Disk::Disk>> &disks,
const std::list<std::shared_ptr<Storage::Tape::Tape>> &tapes,
const std::list<std::shared_ptr<Storage::Cartridge::Cartridge>> &cartridges,
std::list<Target> &destination
);
} }
} }

View File

@ -38,11 +38,7 @@ static std::list<std::shared_ptr<Storage::Cartridge::Cartridge>>
return vic20_cartridges; return vic20_cartridges;
} }
void StaticAnalyser::Commodore::AddTargets( void StaticAnalyser::Commodore::AddTargets(const Media &media, std::list<Target> &destination) {
const std::list<std::shared_ptr<Storage::Disk::Disk>> &disks,
const std::list<std::shared_ptr<Storage::Tape::Tape>> &tapes,
const std::list<std::shared_ptr<Storage::Cartridge::Cartridge>> &cartridges,
std::list<StaticAnalyser::Target> &destination) {
Target target; Target target;
target.machine = Target::Vic20; // TODO: machine estimation target.machine = Target::Vic20; // TODO: machine estimation
target.probability = 1.0; // TODO: a proper estimation target.probability = 1.0; // TODO: a proper estimation
@ -52,26 +48,26 @@ void StaticAnalyser::Commodore::AddTargets(
bool is_disk = false; bool is_disk = false;
// strip out inappropriate cartridges // strip out inappropriate cartridges
target.cartridges = Vic20CartridgesFrom(cartridges); target.media.cartridges = Vic20CartridgesFrom(media.cartridges);
// check disks // check disks
for(auto &disk : disks) { for(auto &disk : media.disks) {
std::list<File> disk_files = GetFiles(disk); std::list<File> disk_files = GetFiles(disk);
if(disk_files.size()) { if(disk_files.size()) {
is_disk = true; is_disk = true;
files.splice(files.end(), disk_files); files.splice(files.end(), disk_files);
target.disks = disks; target.media.disks.push_back(disk);
if(!device) device = 8; if(!device) device = 8;
} }
} }
// check tapes // check tapes
for(auto &tape : tapes) { for(auto &tape : media.tapes) {
std::list<File> tape_files = GetFiles(tape); std::list<File> tape_files = GetFiles(tape);
tape->reset(); tape->reset();
if(tape_files.size()) { if(tape_files.size()) {
files.splice(files.end(), tape_files); files.splice(files.end(), tape_files);
target.tapes = tapes; target.media.tapes.push_back(tape);
if(!device) device = 1; 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); destination.push_back(target);
} }

View File

@ -14,12 +14,7 @@
namespace StaticAnalyser { namespace StaticAnalyser {
namespace Commodore { namespace Commodore {
void AddTargets( void AddTargets(const Media &media, std::list<Target> &destination);
const std::list<std::shared_ptr<Storage::Disk::Disk>> &disks,
const std::list<std::shared_ptr<Storage::Tape::Tape>> &tapes,
const std::list<std::shared_ptr<Storage::Cartridge::Cartridge>> &cartridges,
std::list<Target> &destination
);
} }
} }

View File

@ -72,11 +72,7 @@ static int Basic11Score(const StaticAnalyser::MOS6502::Disassembly &disassembly)
return Score(disassembly, rom_functions, variable_locations); return Score(disassembly, rom_functions, variable_locations);
} }
void StaticAnalyser::Oric::AddTargets( void StaticAnalyser::Oric::AddTargets(const Media &media, std::list<Target> &destination) {
const std::list<std::shared_ptr<Storage::Disk::Disk>> &disks,
const std::list<std::shared_ptr<Storage::Tape::Tape>> &tapes,
const std::list<std::shared_ptr<Storage::Cartridge::Cartridge>> &cartridges,
std::list<StaticAnalyser::Target> &destination) {
Target target; Target target;
target.machine = Target::Oric; target.machine = Target::Oric;
target.probability = 1.0; target.probability = 1.0;
@ -84,7 +80,7 @@ void StaticAnalyser::Oric::AddTargets(
int basic10_votes = 0; int basic10_votes = 0;
int basic11_votes = 0; int basic11_votes = 0;
for(auto &tape : tapes) { for(auto &tape : media.tapes) {
std::list<File> tape_files = GetFiles(tape); std::list<File> tape_files = GetFiles(tape);
tape->reset(); tape->reset();
if(tape_files.size()) { 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"; target.loadingCommand = "CLOAD\"\"\n";
} }
} }
// trust that any disk supplied can be handled by the Microdisc. TODO: check. // 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.oric.has_microdisc = true;
target.disks = disks; target.media.disks = media.disks;
} else { } else {
target.oric.has_microdisc = false; target.oric.has_microdisc = false;
} }
@ -117,6 +113,6 @@ void StaticAnalyser::Oric::AddTargets(
target.oric.use_atmos_rom = basic11_votes >= basic10_votes; target.oric.use_atmos_rom = basic11_votes >= basic10_votes;
if(target.oric.has_microdisc) target.oric.use_atmos_rom = true; 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); destination.push_back(target);
} }

View File

@ -14,12 +14,7 @@
namespace StaticAnalyser { namespace StaticAnalyser {
namespace Oric { namespace Oric {
void AddTargets( void AddTargets(const Media &media, std::list<Target> &destination);
const std::list<std::shared_ptr<Storage::Disk::Disk>> &disks,
const std::list<std::shared_ptr<Storage::Tape::Tape>> &tapes,
const std::list<std::shared_ptr<Storage::Cartridge::Cartridge>> &cartridges,
std::list<Target> &destination
);
} }
} }

View File

@ -53,6 +53,86 @@ enum class TargetPlatform: TargetPlatformType {
using namespace StaticAnalyser; 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<Target> StaticAnalyser::GetTargets(const char *file_name) { std::list<Target> StaticAnalyser::GetTargets(const char *file_name) {
std::list<Target> targets; std::list<Target> targets;
@ -71,80 +151,21 @@ std::list<Target> StaticAnalyser::GetTargets(const char *file_name) {
// Collect all disks, tapes and ROMs as can be extrapolated from this file, forming the // 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. // union of all platforms this file might be a target for.
std::list<std::shared_ptr<Storage::Disk::Disk>> disks;
std::list<std::shared_ptr<Storage::Tape::Tape>> tapes;
std::list<std::shared_ptr<Storage::Cartridge::Cartridge>> cartridges;
TargetPlatformType potential_platforms = 0; TargetPlatformType potential_platforms = 0;
Media media = GetMediaAndPlatforms(file_name, potential_platforms);
#define Insert(list, class, platforms) \ // Hand off to platform-specific determination of whether these things are actually compatible and,
list.emplace_back(new Storage::class(file_name));\ // if so, how to load them.
potential_platforms |= (TargetPlatformType)(platforms);\ if(potential_platforms & (TargetPlatformType)TargetPlatform::Acorn) Acorn::AddTargets(media, targets);
if(potential_platforms & (TargetPlatformType)TargetPlatform::AmstradCPC) AmstradCPC::AddTargets(media, targets);
#define TryInsert(list, class, platforms) \ if(potential_platforms & (TargetPlatformType)TargetPlatform::Atari2600) Atari::AddTargets(media, targets);
try {\ if(potential_platforms & (TargetPlatformType)TargetPlatform::Commodore) Commodore::AddTargets(media, targets);
Insert(list, class, platforms) \ if(potential_platforms & (TargetPlatformType)TargetPlatform::Oric) Oric::AddTargets(media, targets);
} catch(...) {} if(potential_platforms & (TargetPlatformType)TargetPlatform::ZX8081) ZX8081::AddTargets(media, targets);
#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);
}
// Reset any tapes to their initial position // Reset any tapes to their initial position
for(auto target : targets) { for(auto target : targets) {
for(auto tape : target.tapes) { for(auto tape : media.tapes) {
tape->reset(); tape->reset();
} }
} }

View File

@ -52,6 +52,15 @@ enum class AmstradCPCModel {
CPC6128 CPC6128
}; };
/*!
A list of disks, tapes and cartridges.
*/
struct Media {
std::list<std::shared_ptr<Storage::Disk::Disk>> disks;
std::list<std::shared_ptr<Storage::Tape::Tape>> tapes;
std::list<std::shared_ptr<Storage::Cartridge::Cartridge>> cartridges;
};
/*! /*!
A list of disks, tapes and cartridges plus information about the machine to which to attach them and its configuration, 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. 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::string loadingCommand;
Media media;
std::list<std::shared_ptr<Storage::Disk::Disk>> disks;
std::list<std::shared_ptr<Storage::Tape::Tape>> tapes;
std::list<std::shared_ptr<Storage::Cartridge::Cartridge>> cartridges;
}; };
/*! /*!
@ -115,6 +121,11 @@ struct Target {
*/ */
std::list<Target> GetTargets(const char *file_name); std::list<Target> GetTargets(const char *file_name);
/*!
Inspects the supplied file and determines the media included.
*/
Media GetMedia(const char *file_name);
} }
#endif /* StaticAnalyser_hpp */ #endif /* StaticAnalyser_hpp */

View File

@ -27,15 +27,10 @@ static std::vector<Storage::Data::ZX8081::File> GetFiles(const std::shared_ptr<S
return files; return files;
} }
void StaticAnalyser::ZX8081::AddTargets( void StaticAnalyser::ZX8081::AddTargets(const Media &media, std::list<Target> &destination) {
const std::list<std::shared_ptr<Storage::Disk::Disk>> &disks, if(!media.tapes.empty()) {
const std::list<std::shared_ptr<Storage::Tape::Tape>> &tapes, std::vector<Storage::Data::ZX8081::File> files = GetFiles(media.tapes.front());
const std::list<std::shared_ptr<Storage::Cartridge::Cartridge>> &cartridges, media.tapes.front()->reset();
std::list<StaticAnalyser::Target> &destination) {
if(!tapes.empty()) {
std::vector<Storage::Data::ZX8081::File> files = GetFiles(tapes.front());
tapes.front()->reset();
if(!files.empty()) { if(!files.empty()) {
StaticAnalyser::Target target; StaticAnalyser::Target target;
target.machine = Target::ZX8081; target.machine = Target::ZX8081;
@ -47,7 +42,7 @@ void StaticAnalyser::ZX8081::AddTargets(
} else { } else {
target.zx8081.memory_model = ZX8081MemoryModel::Unexpanded; 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. // TODO: how to run software once loaded? Might require a BASIC detokeniser.
if(target.zx8081.isZX81) { if(target.zx8081.isZX81) {

View File

@ -14,12 +14,7 @@
namespace StaticAnalyser { namespace StaticAnalyser {
namespace ZX8081 { namespace ZX8081 {
void AddTargets( void AddTargets(const Media &media, std::list<Target> &destination);
const std::list<std::shared_ptr<Storage::Disk::Disk>> &disks,
const std::list<std::shared_ptr<Storage::Tape::Tape>> &tapes,
const std::list<std::shared_ptr<Storage::Cartridge::Cartridge>> &cartridges,
std::list<Target> &destination
);
} }
} }