PDP-8-E-Simulator/Main/MainController.m

325 lines
9.9 KiB
Objective-C

/*
* PDP-8/E Simulator
*
* Copyright © 1994-2015 Bernhard Baehr
*
* MainController.m - Main Application Controller Class
*
* 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 <PreferencePanes/PreferencePanes.h>
#import "MainController.h"
#import "CPUWindowController.h"
#import "PDP8.h"
#import "BreakpointArray.h"
#import "BreakpointController.h"
#import "GeneralPreferences.h"
#import "CPUPreferences.h"
#import "PluginManager.h"
#import "Utilities.h"
#import "KeepInMenuWindow.h"
@implementation MainController
- (void) notifyStopPDP8:(NSNotification *)notification
{
if (memoryInspectorVisibleBeforeGo) {
[memoryInspectorDrawer open:self];
memoryInspectorVisibleBeforeGo = NO;
}
if (breakpointPanelVisibleBeforeGo) {
[breakpointPanel orderBack:self];
breakpointPanelVisibleBeforeGo = NO;
}
if (bootstrapPanelVisibleBeforeGo) {
[bootstrapPanel orderBack:self];
bootstrapPanelVisibleBeforeGo = NO;
}
}
- (void) notifyPreferencesChanged:(NSNotification *)notification
{
// NSLog (@"MainController notifyPreferencesChanged");
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[pdp8 mountEAE:[defaults boolForKey:CPU_PREFS_EAE_KEY]];
/* The preferences may be inconsistent here because NSUserDefaultsController sends the
notification before the km8eClick: method of CPUPreferences is called. */
BOOL hasKM8E = [defaults boolForKey:CPU_PREFS_KM8E_KEY];
unsigned memsize = [defaults integerForKey:CPU_PREFS_MEMORYSIZE_KEY];
memsize = hasKM8E ? max(memsize, 2 * PDP8_FIELDSIZE) : PDP8_FIELDSIZE;
BOOL hasTimesharing = hasKM8E ? [defaults boolForKey:CPU_PREFS_TIMESHARING_KEY] : NO;
[pdp8 mountKM8E:hasKM8E memorySize:memsize timesharingEnabled:hasTimesharing];
[pdp8 setTraceSpeed:[defaults floatForKey:GENERAL_PREFS_TRACE_SPEED_KEY]];
[pdp8 setGoSpeed:[defaults integerForKey:GENERAL_PREFS_GO_SPEED_KEY]];
}
- (void) cancelEditing
{
/* Cancel editing (in memory table views). Otherwise the [tableview reload]
caused by [pdp8 reset] ends editing normally, and this causes values from
the active input field to be written back to the PDP-8 memory. Send the
cancelOperation: only to NSTextView subclasses to avoid a beep caused by
other responders. */
NSResponder *firstResponder = [[NSApp keyWindow] firstResponder];
if ([[firstResponder class] isSubclassOfClass:[NSTextView class]])
[firstResponder doCommandBySelector:@selector(cancelOperation:)];
}
- (IBAction) reset:(id)sender
{
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:NSLocalizedString(@"Do you really want to reset the PDP-8/E?", @"")];
[alert setInformativeText:NSLocalizedString(
@"All registers and the memory content will be set to zero. "
@"The preferences and mounted tapes, disks etc. will be preserved. "
@"All breakpoints and break opcodes will be disabled.", @"")];
[[alert addButtonWithTitle:NSLocalizedString(@"Yes", @"")] setKeyEquivalent:@"\r"];
[[alert addButtonWithTitle:NSLocalizedString(@"No", @"")] setKeyEquivalent:@"\e"];
if ([alert runModal] == NSAlertFirstButtonReturn) {
[self cancelEditing];
[pdp8 reset];
[breakpoints setAllValues:0];
[breakopcodes setAllValues:0];
[pluginManager resetAllDevices];
}
[alert release];
}
- (IBAction) step:(id)sender
{
[self cancelEditing];
[pdp8 step];
}
- (IBAction) trace:(id)sender
{
[self cancelEditing];
[pdp8 trace:NO_STOP_ADDRESS];
}
- (IBAction) go:(id)sender
{
int s = [memoryInspectorDrawer state];
memoryInspectorVisibleBeforeGo = s == NSDrawerOpenState || s == NSDrawerOpeningState;
breakpointPanelVisibleBeforeGo = [breakpointPanel isVisible];
bootstrapPanelVisibleBeforeGo = [bootstrapPanel isVisible];
[breakpointPanel orderOut:self];
[bootstrapPanel orderOut:self];
[memoryInspectorDrawer close:self];
[self cancelEditing];
[pdp8 go:NO_STOP_ADDRESS];
}
- (IBAction) stop:(id)sender
{
[pdp8 stop];
}
- (IBAction) showPreferencesPanel:(id)sender
{
[preferencesPanel makeKeyAndOrderFront:self];
}
- (IBAction) showHideBreakpointPanel:(id)sender
{
if ([breakpointPanel isVisible])
[breakpointPanel orderOut:sender];
else {
[breakpointPanel setBecomesKeyOnlyIfNeeded:YES];
[breakpointPanel orderFront:sender];
}
}
- (IBAction) showHideBootstrapPanel:(id)sender
{
if ([bootstrapPanel isVisible])
[bootstrapPanel orderOut:sender];
else {
[bootstrapPanel setBecomesKeyOnlyIfNeeded:YES];
[bootstrapPanel orderFront:sender];
}
}
- (IBAction) toggleMemoryInspectorDrawer:(id)sender
{
[memoryInspectorDrawer toggle:sender];
}
- (IBAction) loadPaperTape:(id)sender
{
NSOpenPanel *panel = [NSOpenPanel openPanel];
[panel setTitle:NSLocalizedString(@"Load a Paper Tape", @"")];
[panel setPrompt:NSLocalizedString(@"Load", @"")];
[loadPaperTapeFieldView retain];
// [loadPaperTapeFieldStepper setIntValue:[pdp8 getIF]];
int lastField = ([pdp8 memorySize] >> 12) - 1;
[loadPaperTapeFieldStepper setMaxValue:lastField];
[loadPaperTapeFieldStepper setEnabled:lastField != 0];
[[loadPaperTapeFieldStepper target] performSelector:[loadPaperTapeFieldStepper action]
withObject:loadPaperTapeFieldStepper];
[panel setAccessoryView:loadPaperTapeFieldView];
if ([panel runModalForDirectory:
[[NSUserDefaults standardUserDefaults] stringForKey:LAST_FILE_PANEL_DIR_KEY]
file:nil types:[NSArray arrayWithObjects:
NSFileTypeForHFSTypeCode(0x504e4348l /* 'PNCH' */),
@"BIN", @"bin", @"BN", @"bn", @"RIM", @"rim", @"PT", @"pt", nil]] == NSOKButton) {
[self cancelEditing];
NSString *error = [pdp8 loadPaperTape:[panel filename]
toField:[loadPaperTapeFieldStepper intValue]];
if (error) {
NSAlert *alert = [[NSAlert alloc] init];
[alert setAlertStyle:NSWarningAlertStyle];
[alert setMessageText:error];
[alert setInformativeText:[panel filename]];
[alert runModal];
[alert release];
}
}
[[NSUserDefaults standardUserDefaults] setObject:[panel directory] forKey:LAST_FILE_PANEL_DIR_KEY];
}
- (IBAction) clearAllFlags:(id)sender
{
[self cancelEditing];
[pdp8 clearAllFlags];
}
- (IBAction) loadExtendedAddress:(id)sender
{
[self cancelEditing];
[pdp8 loadExtendedAddress];
}
- (BOOL) validateMenuItem:(NSMenuItem *)menuItem
{
SEL action = [menuItem action];
if (action == @selector(stop:))
return [pdp8 isRunning];
if (action == @selector(step:) || action == @selector(trace:) || action == @selector(go:))
return ! [pdp8 isHalted] && [pdp8 isStopped];
if (action == @selector(reset:) || action == @selector(loadPaperTape:) ||
action == @selector(clearAllFlags:) || action == @selector(loadExtendedAddress:))
return [pdp8 isStopped];
if (action == @selector(showHideBreakpointPanel:)) {
if ([menuItem respondsToSelector:@selector(setState:)])
[menuItem setState:[breakpointPanel isVisible]];
return ! [pdp8 isGoing];
}
if (action == @selector(showHideBootstrapPanel:)) {
if ([menuItem respondsToSelector:@selector(setState:)])
[menuItem setState:[bootstrapPanel isVisible]];
return ! [pdp8 isGoing];
}
if (action == @selector(toggleMemoryInspectorDrawer:)) {
if ([menuItem respondsToSelector:@selector(setState:)]) {
int s = [memoryInspectorDrawer state];
[menuItem setState:s == NSDrawerOpenState || s == NSDrawerOpeningState ?
NSOnState : NSOffState];
}
return [[memoryInspectorDrawer parentWindow] isVisible] && ! [pdp8 isGoing];
}
if (action == @selector(performZoomAll:)) { // don't know how to archieve this automatically
NSWindow *window;
NSEnumerator *enumerator = [[NSApp windows] objectEnumerator];
while ((window = [enumerator nextObject])) {
if ([window level] == NSNormalWindowLevel &&
[window isZoomable] && [window isVisible])
return TRUE;
}
return FALSE;
}
return TRUE;
}
- (BOOL) validateToolbarItem:(NSToolbarItem *)toolbarItem
{
return [self validateMenuItem:(NSMenuItem *)toolbarItem];
}
- (void) applicationWillFinishLaunching:(NSNotification *)notification
{
if (! runningOnTigerOrNewer()) {
NSRunAlertPanel (
NSLocalizedString(
@"This version of the PDP-8/E Simulator needs Mac OS X 10.4 or better", @""),
NSLocalizedString(@"For older Systems (down to System 2.0.1 on a Mac 512Ke), there "
"is still the old version 1.5 of the PDP-8/E Simulator available.", @""),
NSLocalizedString(@"Quit", @""), nil, nil);
exit (0);
}
LOG_ASSERTING ();
}
- (void) applicationDidFinishLaunching:(NSNotification *)notification
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(notifyStopPDP8:) name:PDP8_STOP_NOTIFICATION object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(notifyPreferencesChanged:) name:NSUserDefaultsDidChangeNotification
object:nil];
[self notifyPreferencesChanged:nil];
[breakpoints loadFromPrefs:@"Breakpoints"];
[breakopcodes loadFromPrefs:@"BreakOpcodes"];
[[cpuWindow toolbar] validateVisibleItems]; // revalidate because of the HALT/SINGSTEP console key
[cpuWindow orderFrontFromDefaults:self];
}
- (void) applicationWillTerminate:(NSNotification *)notification
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSUserDefaultsDidChangeNotification
object:nil];
}
- (IBAction) performZoomAll:(id)sender // don't know how to archieve this automatically
{
NSWindow *window;
NSEnumerator *enumerator = [[NSApp windows] objectEnumerator];
while ((window = [enumerator nextObject])) {
if ([window level] == NSNormalWindowLevel && [window isZoomable] && [window isVisible])
[window performZoom:sender];
}
}
@end