diff options
Diffstat (limited to 'src/osd/modules/debugger/osx/debugconsole.mm')
-rw-r--r-- | src/osd/modules/debugger/osx/debugconsole.mm | 549 |
1 files changed, 549 insertions, 0 deletions
diff --git a/src/osd/modules/debugger/osx/debugconsole.mm b/src/osd/modules/debugger/osx/debugconsole.mm new file mode 100644 index 00000000000..4bd7e029295 --- /dev/null +++ b/src/osd/modules/debugger/osx/debugconsole.mm @@ -0,0 +1,549 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +//============================================================ +// +// debugconsole.m - MacOS X Cocoa debug window handling +// +//============================================================ + +#import "debugconsole.h" + +#import "debugcommandhistory.h" +#import "consoleview.h" +#import "debugview.h" +#import "deviceinfoviewer.h" +#import "devicesviewer.h" +#import "disassemblyview.h" +#import "disassemblyviewer.h" +#import "errorlogviewer.h" +#import "memoryviewer.h" +#import "pointsviewer.h" +#import "registersview.h" + +#include "debug/debugcon.h" +#include "debug/debugcpu.h" + + +@implementation MAMEDebugConsole + +- (id)initWithMachine:(running_machine &)m { + NSSplitView *regSplit, *dasmSplit; + NSScrollView *regScroll, *dasmScroll, *consoleScroll; + NSView *consoleContainer; + NSPopUpButton *actionButton; + NSRect rct; + + // initialise superclass + if (!(self = [super initWithMachine:m title:@"Debug"])) + return nil; + history = [[MAMEDebugCommandHistory alloc] init]; + auxiliaryWindows = [[NSMutableArray alloc] init]; + NSFont *const defaultFont = [[MAMEDebugView class] defaultFontForMachine:m]; + + // create the register view + regView = [[MAMERegistersView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100) machine:*machine]; + regScroll = [[NSScrollView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)]; + [regScroll setHasHorizontalScroller:YES]; + [regScroll setHasVerticalScroller:YES]; + [regScroll setAutohidesScrollers:YES]; + [regScroll setBorderType:NSBezelBorder]; + [regScroll setDocumentView:regView]; + [regView release]; + + // create the disassembly view + dasmView = [[MAMEDisassemblyView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100) machine:*machine]; + [dasmView setExpression:@"curpc"]; + dasmScroll = [[NSScrollView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)]; + [dasmScroll setHasHorizontalScroller:YES]; + [dasmScroll setHasVerticalScroller:YES]; + [dasmScroll setAutohidesScrollers:YES]; + [dasmScroll setBorderType:NSBezelBorder]; + [dasmScroll setDocumentView:dasmView]; + [dasmView release]; + + // create the console view + consoleView = [[MAMEConsoleView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100) machine:*machine]; + consoleScroll = [[NSScrollView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)]; + [consoleScroll setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)]; + [consoleScroll setHasHorizontalScroller:YES]; + [consoleScroll setHasVerticalScroller:YES]; + [consoleScroll setAutohidesScrollers:YES]; + [consoleScroll setBorderType:NSBezelBorder]; + [consoleScroll setDocumentView:consoleView]; + [consoleView release]; + + // create the command field + commandField = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 100, 19)]; + [commandField setAutoresizingMask:(NSViewWidthSizable | NSViewMaxYMargin)]; + [commandField setFont:defaultFont]; + [commandField setFocusRingType:NSFocusRingTypeNone]; + [commandField setTarget:self]; + [commandField setAction:@selector(doCommand:)]; + [commandField setDelegate:self]; + [commandField sizeToFit]; + rct = [commandField frame]; + [commandField setFrame:NSMakeRect(rct.size.height, 0, 100 - rct.size.height, rct.size.height)]; + + // create the action pull-down button + actionButton = [[self class] newActionButtonWithFrame:NSMakeRect(0, 0, rct.size.height, rct.size.height)]; + [actionButton setAutoresizingMask:(NSViewMaxXMargin | NSViewMaxYMargin)]; + [actionButton setFont:[NSFont systemFontOfSize:[defaultFont pointSize]]]; + [dasmView insertActionItemsInMenu:[actionButton menu] atIndex:1]; + + // create the container for the console and command input field + consoleContainer = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)]; + [consoleScroll setFrame:NSMakeRect(0, + rct.size.height, + 100, + [consoleContainer bounds].size.height - rct.size.height)]; + [consoleContainer addSubview:consoleScroll]; + [consoleContainer addSubview:commandField]; + [consoleContainer addSubview:actionButton]; + [consoleScroll release]; + [commandField release]; + [actionButton release]; + + // create the split between the disassembly and the console + dasmSplit = [[NSSplitView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)]; + [dasmSplit setDelegate:self]; + [dasmSplit setVertical:NO]; + [dasmSplit addSubview:dasmScroll]; + [dasmSplit addSubview:consoleContainer]; + [dasmScroll release]; + [consoleContainer release]; + + // create the split between the registers and the console + regSplit = [[NSSplitView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)]; + [regSplit setDelegate:self]; + [regSplit setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)]; + [regSplit setVertical:YES]; + [regSplit addSubview:regScroll]; + [regSplit addSubview:dasmSplit]; + [regScroll release]; + [dasmSplit release]; + + // put the split views in the window and get them into a half-reasonable state + [window setContentView:regSplit]; + [regSplit release]; + [regSplit adjustSubviews]; + [dasmSplit adjustSubviews]; + + // keyboard focus should start on the command field + [window makeFirstResponder:commandField]; + + // calculate the optimal size for everything + NSRect const available = [[NSScreen mainScreen] visibleFrame]; + NSSize const regCurrent = [regScroll frame].size; + NSSize const regSize = [NSScrollView frameSizeForContentSize:[regView maximumFrameSize] + hasHorizontalScroller:YES + hasVerticalScroller:YES + borderType:[regScroll borderType]]; + NSSize const dasmCurrent = [dasmScroll frame].size; + NSSize const dasmSize = [NSScrollView frameSizeForContentSize:[dasmView maximumFrameSize] + hasHorizontalScroller:YES + hasVerticalScroller:YES + borderType:[dasmScroll borderType]]; + NSSize const consoleCurrent = [consoleContainer frame].size; + NSSize consoleSize = [NSScrollView frameSizeForContentSize:[consoleView maximumFrameSize] + hasHorizontalScroller:YES + hasVerticalScroller:YES + borderType:[consoleScroll borderType]]; + NSRect windowFrame = [window frame]; + NSSize adjustment; + + consoleSize.width += consoleCurrent.width - [consoleScroll frame].size.width; + consoleSize.height += consoleCurrent.height - [consoleScroll frame].size.height; + adjustment.width = regSize.width - regCurrent.width; + adjustment.height = regSize.height - regCurrent.height; + adjustment.width += MAX(dasmSize.width - dasmCurrent.width, consoleSize.width - consoleCurrent.width); + + windowFrame.size.width += adjustment.width; + windowFrame.size.height += adjustment.height; // not used - better to go for fixed height + windowFrame.size.height = MIN(512.0, available.size.height); + windowFrame.size.width = MIN(windowFrame.size.width, available.size.width); + windowFrame.origin.x = available.origin.x + available.size.width - windowFrame.size.width; + windowFrame.origin.y = available.origin.y; + [window setFrame:windowFrame display:YES]; + + NSRect lhsFrame = [regScroll frame]; + NSRect rhsFrame = [dasmSplit frame]; + adjustment.width = MIN(regSize.width, ([regSplit frame].size.width - [regSplit dividerThickness]) / 2); + rhsFrame.origin.x -= lhsFrame.size.width - adjustment.width; + rhsFrame.size.width += lhsFrame.size.width - adjustment.width; + lhsFrame.size.width = adjustment.width; + [regScroll setFrame:lhsFrame]; + [dasmSplit setFrame:rhsFrame]; + + // select the current processor + [self setCPU:machine->firstcpu]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(auxiliaryWindowWillClose:) + name:MAMEAuxiliaryDebugWindowWillCloseNotification + object:nil]; + + // don't forget the return value + return self; +} + + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + + if (history != nil) + [history release]; + if (auxiliaryWindows != nil) + [auxiliaryWindows release]; + + [super dealloc]; +} + + +- (void)setCPU:(device_t *)device { + [regView selectSubviewForDevice:device]; + [dasmView selectSubviewForDevice:device]; + [window setTitle:[NSString stringWithFormat:@"Debug: %s - %s '%s'", + device->machine().system().name, + device->name(), + device->tag()]]; +} + + +- (IBAction)doCommand:(id)sender { + NSString *command = [sender stringValue]; + if ([command length] == 0) + { + debug_cpu_get_visible_cpu(*machine)->debug()->single_step(); + [history reset]; + } + else + { + debug_console_execute_command(*machine, [command UTF8String], 1); + [history add:command]; + [history edit]; + } + [sender setStringValue:@""]; +} + + +- (IBAction)debugToggleBreakpoint:(id)sender { + device_t &device = [dasmView source]->device(); + if ([dasmView cursorVisible] && (debug_cpu_get_visible_cpu(*machine) == &device)) + { + offs_t const address = [dasmView selectedAddress]; + device_debug::breakpoint *bp = [[self class] findBreakpointAtAddress:address + forDevice:device]; + + // if it doesn't exist, add a new one + NSString *command; + if (bp == NULL) + command = [NSString stringWithFormat:@"bpset 0x%lX", (unsigned long)address]; + else + command = [NSString stringWithFormat:@"bpclear 0x%X", (unsigned)bp->index()]; + debug_console_execute_command(*machine, [command UTF8String], 1); + } +} + + +- (IBAction)debugToggleBreakpointEnable:(id)sender { + device_t &device = [dasmView source]->device(); + if ([dasmView cursorVisible] && (debug_cpu_get_visible_cpu(*machine) == &device)) + { + device_debug::breakpoint *bp = [[self class] findBreakpointAtAddress:[dasmView selectedAddress] + forDevice:device]; + if (bp != NULL) + { + NSString *command; + if (bp->enabled()) + command = [NSString stringWithFormat:@"bpdisable 0x%X", (unsigned)bp->index()]; + else + command = [NSString stringWithFormat:@"bpenable 0x%X", (unsigned)bp->index()]; + debug_console_execute_command(*machine, [command UTF8String], 1); + } + } +} + + +- (IBAction)debugRunToCursor:(id)sender { + device_t &device = [dasmView source]->device(); + if ([dasmView cursorVisible] && (debug_cpu_get_visible_cpu(*machine) == &device)) + { + NSString *command = [NSString stringWithFormat:@"go 0x%lX", (unsigned long)[dasmView selectedAddress]]; + debug_console_execute_command(*machine, [command UTF8String], 1); + } +} + + +- (IBAction)debugNewMemoryWindow:(id)sender { + MAMEMemoryViewer *win = [[MAMEMemoryViewer alloc] initWithMachine:*machine console:self]; + [auxiliaryWindows addObject:win]; + [win release]; + [win activate]; +} + + +- (IBAction)debugNewDisassemblyWindow:(id)sender { + MAMEDisassemblyViewer *win = [[MAMEDisassemblyViewer alloc] initWithMachine:*machine console:self]; + [auxiliaryWindows addObject:win]; + [win release]; + [win activate]; +} + + +- (IBAction)debugNewErrorLogWindow:(id)sender { + MAMEErrorLogViewer *win = [[MAMEErrorLogViewer alloc] initWithMachine:*machine console:self]; + [auxiliaryWindows addObject:win]; + [win release]; + [win activate]; +} + + +- (IBAction)debugNewPointsWindow:(id)sender{ + MAMEPointsViewer *win = [[MAMEPointsViewer alloc] initWithMachine:*machine console:self]; + [auxiliaryWindows addObject:win]; + [win release]; + [win activate]; +} + + +- (IBAction)debugNewDevicesWindow:(id)sender { + MAMEDevicesViewer *win = [[MAMEDevicesViewer alloc] initWithMachine:*machine console:self]; + [auxiliaryWindows addObject:win]; + [win release]; + [win activate]; +} + + +- (void)debugNewMemoryWindowForSpace:(address_space *)space device:(device_t *)device expression:(NSString *)expression { + MAMEMemoryViewer *win = [[MAMEMemoryViewer alloc] initWithMachine:*machine console:self]; + [auxiliaryWindows addObject:win]; + [win release]; + if ([win selectSubviewForSpace:space]) + { + if (expression != nil) + [win setExpression:expression]; + } + else + { + [win selectSubviewForDevice:device]; + } + [win activate]; +} + + +- (void)debugNewDisassemblyWindowForSpace:(address_space *)space device:(device_t *)device expression:(NSString *)expression { + MAMEDisassemblyViewer *win = [[MAMEDisassemblyViewer alloc] initWithMachine:*machine console:self]; + [auxiliaryWindows addObject:win]; + [win release]; + if ([win selectSubviewForSpace:space]) + { + if (expression != nil) + [win setExpression:expression]; + } + else + { + [win selectSubviewForDevice:device]; + } + [win activate]; +} + + +- (void)debugNewInfoWindowForDevice:(device_t &)device { + MAMEDeviceInfoViewer *win = [[MAMEDeviceInfoViewer alloc] initWithDevice:device + machine:*machine + console:self]; + [auxiliaryWindows addObject:win]; + [win release]; + [win activate]; +} + + +- (void)showDebugger:(NSNotification *)notification { + device_t *device = (device_t * )[[[notification userInfo] objectForKey:@"MAMEDebugDevice"] pointerValue]; + if (&device->machine() == machine) + { + [self setCPU:device]; + [window makeKeyAndOrderFront:self]; + } +} + + +- (void)auxiliaryWindowWillClose:(NSNotification *)notification { + [auxiliaryWindows removeObjectIdenticalTo:[notification object]]; +} + + +- (BOOL)control:(NSControl *)control textShouldBeginEditing:(NSText *)fieldEditor { + if (control == commandField) + [history edit]; + + return YES; +} + + +- (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)command { + if (control == commandField) { + if (command == @selector(cancelOperation:)) { + [commandField setStringValue:@""]; + [history reset]; + return YES; + } else if (command == @selector(moveUp:)) { + NSString *hist = [history previous:[commandField stringValue]]; + if (hist != nil) { + [commandField setStringValue:hist]; + [commandField selectText:self]; + [(NSText *)[window firstResponder] setSelectedRange:NSMakeRange([hist length], 0)]; + } + return YES; + } else if (command == @selector(moveDown:)) { + NSString *hist = [history next:[commandField stringValue]]; + if (hist != nil) { + [commandField setStringValue:hist]; + [commandField selectText:self]; + [(NSText *)[window firstResponder] setSelectedRange:NSMakeRange([hist length], 0)]; + } + return YES; + } + } + return NO; +} + + +- (void)windowWillClose:(NSNotification *)notification { + if ([notification object] == window) + { + NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:[NSValue valueWithPointer:machine], + @"MAMEDebugMachine", + nil]; + [[NSNotificationCenter defaultCenter] postNotificationName:MAMEHideDebuggerNotification + object:self + userInfo:info]; + debug_cpu_get_visible_cpu(*machine)->debug()->go(); + } +} + + +- (CGFloat)splitView:(NSSplitView *)sender constrainMinCoordinate:(CGFloat)min ofSubviewAt:(NSInteger)offs { + return (min < 100) ? 100 : min; +} + + +- (CGFloat)splitView:(NSSplitView *)sender constrainMaxCoordinate:(CGFloat)max ofSubviewAt:(NSInteger)offs { + NSSize sz = [sender bounds].size; + CGFloat allowed = ([sender isVertical] ? sz.width : sz.height) - 100 - [sender dividerThickness]; + return (max > allowed) ? allowed : max; +} + + +- (BOOL)splitView:(NSSplitView *)sender canCollapseSubview:(NSView *)subview { + // allow registers or disassembly to be collapsed, but not console + return [[sender subviews] indexOfObjectIdenticalTo:subview] == 0; +} + + +- (void)splitView:(NSSplitView *)sender resizeSubviewsWithOldSize:(NSSize)oldSize { + // This can only deal with a single split, but that's all we use, anyway + NSRect first, second; + [sender adjustSubviews]; + first = [[[sender subviews] objectAtIndex:0] frame]; + second = [[[sender subviews] objectAtIndex:1] frame]; + if ([sender isVertical]) { + if (first.size.width < 100) { + CGFloat diff = 100 - first.size.width; + first.size.width = 100; + second.origin.x += diff; + second.size.width -= diff; + } else if (second.size.width < 100) { + CGFloat diff = 100 - second.size.width; + second.size.width = 100; + second.origin.x -= diff; + first.size.width -= diff; + } + } else { + if (first.size.height < 100) { + CGFloat diff = 100 - first.size.height; + first.size.height = 100; + second.origin.y += diff; + second.size.height -= diff; + } else if (second.size.height < 100) { + CGFloat diff = 100 - second.size.height; + second.size.height = 100; + second.origin.y -= diff; + first.size.height -= diff; + } + } + [[[sender subviews] objectAtIndex:0] setFrame:first]; + [[[sender subviews] objectAtIndex:1] setFrame:second]; +} + + +- (BOOL)validateMenuItem:(NSMenuItem *)item { + SEL const action = [item action]; + BOOL const inContextMenu = ([item menu] == [dasmView menu]); + BOOL const haveCursor = [dasmView cursorVisible]; + BOOL const isCurrent = (debug_cpu_get_visible_cpu(*machine) == &[dasmView source]->device()); + + device_debug::breakpoint *breakpoint = NULL; + if (haveCursor) + { + breakpoint = [[self class] findBreakpointAtAddress:[dasmView selectedAddress] + forDevice:[dasmView source]->device()]; + } + + if (action == @selector(debugToggleBreakpoint:)) + { + if (haveCursor) + { + if (breakpoint != NULL) + { + if (inContextMenu) + [item setTitle:@"Clear Breakpoint"]; + else + [item setTitle:@"Clear Breakpoint at Cursor"]; + } + else + { + if (inContextMenu) + [item setTitle:@"Set Breakpoint"]; + else + [item setTitle:@"Set Breakpoint at Cursor"]; + } + } + else + { + if (inContextMenu) + [item setTitle:@"Toggle Breakpoint"]; + else + [item setTitle:@"Toggle Breakpoint at Cursor"]; + } + return haveCursor && isCurrent; + } + else if (action == @selector(debugToggleBreakpointEnable:)) + { + if ((breakpoint != NULL) && !breakpoint->enabled()) + { + if (inContextMenu) + [item setTitle:@"Enable Breakpoint"]; + else + [item setTitle:@"Enable Breakpoint at Cursor"]; + } + else + { + if (inContextMenu) + [item setTitle:@"Disable Breakpoint"]; + else + [item setTitle:@"Disable Breakpoint at Cursor"]; + } + return (breakpoint != NULL) && isCurrent; + } + else if (action == @selector(debugRunToCursor:)) + { + return isCurrent; + } + else + { + return YES; + } +} + +@end |