1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-27 01:31:42 +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_[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.

View File

@ -45,7 +45,7 @@ class ConcreteMachine:
}
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) {
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;
@ -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;

View File

@ -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<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()) {
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<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) {

View File

@ -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;
};
}

View File

@ -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<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;
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);
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) {

View File

@ -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(&microdisc_);
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

View File

@ -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) {

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
override func data(ofType typeName: String) throws -> Data {
throw NSError(domain: NSOSStatusErrorDomain, code: unimpErr, userInfo: nil)

View File

@ -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

View File

@ -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<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

View File

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

View File

@ -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

View File

@ -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 <NSObject>

View File

@ -10,6 +10,9 @@
@import CoreVideo;
@import GLKit;
@interface CSOpenGLView () <NSDraggingDestination>
@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 <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

View File

@ -56,11 +56,7 @@ static std::list<std::shared_ptr<Storage::Cartridge::Cartridge>>
return acorn_cartridges;
}
void StaticAnalyser::Acorn::AddTargets(
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) {
void StaticAnalyser::Acorn::AddTargets(const Media &media, std::list<Target> &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<Storage::Tape::Tape> tape = tapes.front();
if(media.tapes.size() > 0) {
std::shared_ptr<Storage::Tape::Tape> tape = media.tapes.front();
std::list<File> 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<Storage::Disk::Disk> disk = disks.front();
if(media.disks.size() > 0) {
std::shared_ptr<Storage::Disk::Disk> disk = media.disks.front();
std::unique_ptr<Catalogue> 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);
}

View File

@ -14,12 +14,7 @@
namespace StaticAnalyser {
namespace Acorn {
void AddTargets(
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
);
void AddTargets(const Media &media, std::list<Target> &destination);
}
}

View File

@ -128,28 +128,24 @@ static void InspectSystemCatalogue(
InspectDataCatalogue(catalogue, target);
}
void StaticAnalyser::AmstradCPC::AddTargets(
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) {
void StaticAnalyser::AmstradCPC::AddTargets(const Media &media, std::list<Target> &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<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) {
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<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) {
InspectSystemCatalogue(target.disks.front(), *system_catalogue, target);
InspectSystemCatalogue(target.media.disks.front(), *system_catalogue, target);
}
}
}

View File

@ -14,12 +14,7 @@
namespace StaticAnalyser {
namespace AmstradCPC {
void AddTargets(
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
);
void AddTargets(const Media &media, std::list<Target> &destination);
}
}

View File

@ -178,24 +178,18 @@ static void DeterminePagingForCartridge(StaticAnalyser::Target &target, const St
}
}
void StaticAnalyser::Atari::AddTargets(
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) {
// TODO: sanity checking; is this image really for an Atari 2600?
void StaticAnalyser::Atari::AddTargets(const Media &media, std::list<Target> &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<Storage::Cartridge::Cartridge::Segment> &segments = cartridges.front()->get_segments();
if(!media.cartridges.empty()) {
const std::list<Storage::Cartridge::Cartridge::Segment> &segments = media.cartridges.front()->get_segments();
if(segments.size() == 1) {
const Storage::Cartridge::Cartridge::Segment &segment = segments.front();

View File

@ -14,12 +14,7 @@
namespace StaticAnalyser {
namespace Atari {
void AddTargets(
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
);
void AddTargets(const Media &media, std::list<Target> &destination);
}
}

View File

@ -38,11 +38,7 @@ static std::list<std::shared_ptr<Storage::Cartridge::Cartridge>>
return vic20_cartridges;
}
void StaticAnalyser::Commodore::AddTargets(
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) {
void StaticAnalyser::Commodore::AddTargets(const Media &media, std::list<Target> &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<File> 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<File> 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);
}

View File

@ -14,12 +14,7 @@
namespace StaticAnalyser {
namespace Commodore {
void AddTargets(
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
);
void AddTargets(const Media &media, 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);
}
void StaticAnalyser::Oric::AddTargets(
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) {
void StaticAnalyser::Oric::AddTargets(const Media &media, std::list<Target> &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<File> 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);
}

View File

@ -14,12 +14,7 @@
namespace StaticAnalyser {
namespace Oric {
void AddTargets(
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
);
void AddTargets(const Media &media, std::list<Target> &destination);
}
}

View File

@ -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<Target> StaticAnalyser::GetTargets(const char *file_name) {
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
// 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;
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();
}
}

View File

@ -52,6 +52,15 @@ enum class AmstradCPCModel {
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,
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<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;
Media media;
};
/*!
@ -115,6 +121,11 @@ struct Target {
*/
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 */

View File

@ -27,15 +27,10 @@ static std::vector<Storage::Data::ZX8081::File> GetFiles(const std::shared_ptr<S
return files;
}
void StaticAnalyser::ZX8081::AddTargets(
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) {
if(!tapes.empty()) {
std::vector<Storage::Data::ZX8081::File> files = GetFiles(tapes.front());
tapes.front()->reset();
void StaticAnalyser::ZX8081::AddTargets(const Media &media, std::list<Target> &destination) {
if(!media.tapes.empty()) {
std::vector<Storage::Data::ZX8081::File> 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) {

View File

@ -14,12 +14,7 @@
namespace StaticAnalyser {
namespace ZX8081 {
void AddTargets(
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
);
void AddTargets(const Media &media, std::list<Target> &destination);
}
}