mirror of
https://github.com/DerekK19/PDP-8-E-Simulator.git
synced 2024-05-31 15:41:30 +00:00
281 lines
9.4 KiB
Objective-C
281 lines
9.4 KiB
Objective-C
/*
|
|
* PDP-8/E Simulator
|
|
*
|
|
* Copyright © 1994-2015 Bernhard Baehr
|
|
*
|
|
* PluginManager.m - Manager for I/O Device Plugins
|
|
*
|
|
* This file is part of PDP-8/E Simulator.
|
|
*
|
|
* PDP-8/E Simulator is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
|
|
#import <Cocoa/Cocoa.h>
|
|
|
|
#import "PluginManager.h"
|
|
#import "PluginAPI.h"
|
|
#import "PDP8.h"
|
|
#import "SkipController.h"
|
|
#import "IOFlagController.h"
|
|
#import "Assembler.h"
|
|
#import "Disassembler.h"
|
|
#import "OctalFormatter.h"
|
|
#import "HelpMenuManager.h"
|
|
#import "Unicode.h"
|
|
#import "NSFileManager+Additions.h"
|
|
|
|
|
|
#define PLUGIN_EXTENSION @"pdp8Plugin"
|
|
#define PLUGIN_APPSUPPORT_PATH @"Application Support/PDP-8:E Simulator"
|
|
#define PLUGIN_NEXTTOAPP_PATH @"../PDP-8:E Simulator Plugins"
|
|
|
|
|
|
@implementation PluginManager
|
|
|
|
|
|
- (BOOL) canInstallIOTs:(NSArray *)mnemonics withIOAddresses:(NSArray *)ioAddresses
|
|
noLoadMessage:(NSString *)noLoadMessage
|
|
{
|
|
NSString *ioAddress;
|
|
|
|
if (! mnemonics || ! [mnemonics isKindOfClass:[NSArray class]]) {
|
|
NSRunAlertPanel (NSLocalizedString(
|
|
@"Invalid IOT information in the I/O description of the plug-in", @""),
|
|
noLoadMessage, nil, nil, nil);
|
|
return NO;
|
|
}
|
|
if (! ioAddresses || ! [ioAddresses isKindOfClass:[NSArray class]]) {
|
|
NSRunAlertPanel (NSLocalizedString(
|
|
@"Invalid I/O address information in the I/O description of the plug-in.", @""),
|
|
noLoadMessage, nil, nil, nil);
|
|
return NO;
|
|
}
|
|
if ([mnemonics count] != 8 * [ioAddresses count]) {
|
|
NSRunAlertPanel (NSLocalizedString(
|
|
@"The number of IOTs does not match number of I/O addresses in the I/O description "
|
|
"of the plug-in.", @""), noLoadMessage, nil, nil, nil);
|
|
return NO;
|
|
}
|
|
NSEnumerator *enumerator = [ioAddresses objectEnumerator];
|
|
while ((ioAddress = [enumerator nextObject])) {
|
|
NSNumber *number;
|
|
if (! [[OctalFormatter formatterWithBitMask:077 wildcardAllowed:NO] getObjectValue:&number
|
|
forString:ioAddress errorDescription:nil]) {
|
|
NSRunAlertPanel ([NSString stringWithFormat:NSLocalizedString(
|
|
@"Invalid I/O address %C%@%C in the I/O description of the plug-in.",
|
|
@""), UNICODE_LEFT_DOUBLEQUOTE, ioAddress, UNICODE_RIGHT_DOUBLEQUOTE],
|
|
noLoadMessage, nil, nil, nil);
|
|
return NO;
|
|
}
|
|
int addr = [number intValue];
|
|
if (! [pdp8 isIOAddressAvailable:addr]) {
|
|
NSRunAlertPanel ([NSString stringWithFormat:NSLocalizedString(
|
|
@"The I/O address %2.2o requested by the plug-in is already in use.", @""),
|
|
addr], noLoadMessage, nil, nil, nil);
|
|
return NO;
|
|
}
|
|
}
|
|
return YES;
|
|
}
|
|
|
|
|
|
- (void) installIOTs:(NSArray *)mnemonics withAddresses:(NSArray *)ioAddresses forPlugin:(PDP8Plugin *)plugin
|
|
{
|
|
int i;
|
|
NSString *ioAddress;
|
|
|
|
int base = 0;
|
|
NSEnumerator *ioAddrEnum = [ioAddresses objectEnumerator];
|
|
while ((ioAddress = [ioAddrEnum nextObject])) {
|
|
NSNumber *number;
|
|
[[OctalFormatter formatterWithBitMask:077 wildcardAllowed:NO] getObjectValue:&number
|
|
forString:ioAddress errorDescription:nil];
|
|
int addr = [number intValue];
|
|
[pdp8 setPluginPointer:plugin forIOAddress:addr];
|
|
NSArray *iots = [plugin iotsForAddress:addr];
|
|
if (iots) {
|
|
NSArray *skiptests = [plugin skiptestsForAddress:addr];
|
|
int opcode = 06000 | (addr << 3);
|
|
for (i = 0; i < 8; i++) {
|
|
NSValue *iot = [iots objectAtIndex:i];
|
|
[pdp8 setIOT:iot forOpcode:opcode | i];
|
|
NSValue *skiptest = skiptests ? [skiptests objectAtIndex:i] : nil;
|
|
[skipController addSkiptest:skiptest forInstruction:iot];
|
|
NSString *mnemonic = [mnemonics objectAtIndex:base + i];
|
|
[[Assembler sharedAssembler] addMnemonic:mnemonic forIOT:opcode | i];
|
|
[[Disassembler sharedDisassembler] addMnemonic:mnemonic forIOT:opcode | i];
|
|
}
|
|
}
|
|
base += 8;
|
|
}
|
|
}
|
|
|
|
|
|
- (BOOL) canInstallIOFlags:(NSArray *)ioFlags noLoadMessage:(NSString *)noLoadMessage
|
|
{
|
|
if (! ioFlags || ! [ioFlags isKindOfClass:[NSArray class]]) {
|
|
NSRunAlertPanel (NSLocalizedString(
|
|
@"Invalid I/O flag information in the I/O description of the plug-in.", @""),
|
|
noLoadMessage, nil, nil, nil);
|
|
return NO;
|
|
}
|
|
if ([ioFlags count] > [ioFlagController numberOfAvailableFlags]) {
|
|
NSRunAlertPanel (NSLocalizedString(@"There are not enough I/O flags available.", @""),
|
|
noLoadMessage, nil, nil, nil);
|
|
return NO;
|
|
}
|
|
return YES;
|
|
}
|
|
|
|
|
|
- (void) installIOFlags:(NSArray *)ioFlags forPlugin:(PDP8Plugin *)plugin
|
|
{
|
|
NSString *ioFlagName;
|
|
|
|
NSEnumerator *enumerator = [ioFlags objectEnumerator];
|
|
while ((ioFlagName = [enumerator nextObject]))
|
|
[plugin setIOFlag:[ioFlagController addIODevice:ioFlagName] forIOFlagName:ioFlagName];
|
|
}
|
|
|
|
|
|
- (void) resetAllDevices
|
|
{
|
|
PDP8Plugin *plugin;
|
|
|
|
NSEnumerator *enumerator = [pluginInstances objectEnumerator];
|
|
while ((plugin = [enumerator nextObject]))
|
|
[plugin resetDevice];
|
|
}
|
|
|
|
|
|
- (NSString *) noLoadMessage:(NSString *)pluginName
|
|
{
|
|
return [NSString stringWithFormat:NSLocalizedString(@"The plug-in %C%@%C will not be loaded.", @""),
|
|
UNICODE_LEFT_DOUBLEQUOTE, pluginName, UNICODE_RIGHT_DOUBLEQUOTE];
|
|
}
|
|
|
|
|
|
- (PDP8Plugin *) loadPlugin:(NSBundle *)bundle
|
|
{
|
|
PDP8Plugin *plugin = nil;
|
|
Class principalClass = [bundle principalClass];
|
|
if (principalClass && [principalClass isSubclassOfClass:[PDP8Plugin class]]) {
|
|
plugin = [[principalClass alloc] init];
|
|
if (plugin) {
|
|
[plugin apiVersion]; // plugin must overrides this method, otherwise we crash
|
|
[plugin setBundle:bundle];
|
|
[plugin setPDP8:pdp8];
|
|
NSDictionary *ioInfo = [plugin ioInformation];
|
|
NSArray *ioFlagNames = [ioInfo objectForKey:IO_INFO_IOFLAGS_KEY];
|
|
NSArray *ioAddresses = [ioInfo objectForKey:IO_INFO_IOADDRESSES_KEY];
|
|
NSArray *mnemonics = [ioInfo objectForKey:IO_INFO_IOTS_KEY];
|
|
if ([self canInstallIOFlags:ioFlagNames
|
|
noLoadMessage:[self noLoadMessage:[plugin pluginName]]] &&
|
|
[self canInstallIOTs:mnemonics withIOAddresses:ioAddresses
|
|
noLoadMessage:[self noLoadMessage:[plugin pluginName]]]) {
|
|
[self installIOFlags:[ioInfo objectForKey:IO_INFO_IOFLAGS_KEY]
|
|
forPlugin:plugin];
|
|
[self installIOTs:mnemonics withAddresses:ioAddresses forPlugin:plugin];
|
|
[plugin loadNibs];
|
|
[plugin pluginDidLoad];
|
|
[[HelpMenuManager sharedHelpMenuManager] addBundleHelp:bundle];
|
|
} else {
|
|
[plugin release];
|
|
// unload is a 10.5 method (does nothing on 10.4), but we use the 10.4 SDK
|
|
// so use performSelector: to avoid unknown message warning
|
|
[bundle performSelector:@selector(unload)];
|
|
return nil;
|
|
}
|
|
} else {
|
|
NSRunAlertPanel (NSLocalizedString(@"Cannot instantiate the plug-in.", @""),
|
|
[self noLoadMessage:[[bundle bundlePath] lastPathComponent]], nil, nil, nil);
|
|
}
|
|
} else {
|
|
NSRunAlertPanel (principalClass ?
|
|
NSLocalizedString(@"The plug-in has an invalid principal class.", @"") :
|
|
NSLocalizedString(@"Cannot determine the principal class of the plug-in.", @""),
|
|
[self noLoadMessage:[[bundle bundlePath] lastPathComponent]], nil, nil, nil);
|
|
|
|
}
|
|
return [plugin autorelease];
|
|
}
|
|
|
|
|
|
- (NSArray *) loadAllPlugins
|
|
{
|
|
NSEnumerator *searchPathEnum;
|
|
NSString *path;
|
|
NSString *bundleName;
|
|
|
|
NSMutableArray *bundleSearchPaths = [NSMutableArray array];
|
|
NSMutableArray *plugins = [NSMutableArray array];
|
|
|
|
searchPathEnum = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,
|
|
NSAllDomainsMask - NSSystemDomainMask, YES) objectEnumerator];
|
|
while ((path = [searchPathEnum nextObject]))
|
|
[bundleSearchPaths addObject:[path stringByAppendingPathComponent:PLUGIN_APPSUPPORT_PATH]];
|
|
[bundleSearchPaths addObject:[[[NSBundle mainBundle] bundlePath]
|
|
stringByAppendingPathComponent:PLUGIN_NEXTTOAPP_PATH]];
|
|
[bundleSearchPaths addObject:[[NSBundle mainBundle] builtInPlugInsPath]];
|
|
searchPathEnum = [bundleSearchPaths objectEnumerator];
|
|
while ((path = [searchPathEnum nextObject])) {
|
|
NSDirectoryEnumerator *bundlePathEnum =
|
|
[[NSFileManager defaultManager] enumeratorAtPath:path];
|
|
while (bundlePathEnum && (bundleName = [bundlePathEnum nextObject])) {
|
|
[bundlePathEnum skipDescendents];
|
|
if ([[bundleName pathExtension] isEqualToString:PLUGIN_EXTENSION]) {
|
|
NSBundle *bundle = [NSBundle bundleWithPath:
|
|
[[NSFileManager defaultManager] resolveAliasPath:
|
|
[path stringByAppendingPathComponent:bundleName]]];
|
|
if (bundle) {
|
|
PDP8Plugin *plugin = [self loadPlugin:bundle];
|
|
if (plugin)
|
|
[plugins addObject:plugin];
|
|
} else
|
|
NSRunAlertPanel (
|
|
NSLocalizedString(@"Loading of the plug-in bundle failed.",
|
|
@""), [self noLoadMessage:bundleName], nil, nil, nil);
|
|
}
|
|
}
|
|
}
|
|
return plugins;
|
|
}
|
|
|
|
|
|
- (void) notifyApplicationWillFinishLaunching:(NSNotification *)notification
|
|
{
|
|
pluginInstances = [[self loadAllPlugins] retain];
|
|
}
|
|
|
|
|
|
- (void) notifyApplicationDidFinishLaunching:(NSNotification *)notification
|
|
{
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:PLUGINS_LOADED_NOTIFICATION object:nil];
|
|
}
|
|
|
|
|
|
- (void) awakeFromNib
|
|
{
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(notifyApplicationWillFinishLaunching:)
|
|
name:NSApplicationWillFinishLaunchingNotification object:nil];
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(notifyApplicationDidFinishLaunching:)
|
|
name:NSApplicationDidFinishLaunchingNotification object:nil];
|
|
}
|
|
|
|
|
|
@end
|