mirror of
https://github.com/holub/mame
synced 2025-06-15 08:57:05 +03:00
515 lines
16 KiB
Plaintext
515 lines
16 KiB
Plaintext
// license:BSD-3-Clause
|
|
// copyright-holders:Vas Crabb
|
|
//============================================================
|
|
//
|
|
// debugwindowhandler.m - MacOS X Cocoa debug window handling
|
|
//
|
|
//============================================================
|
|
|
|
#include "emu.h"
|
|
#import "debugwindowhandler.h"
|
|
|
|
#import "debugconsole.h"
|
|
#import "debugcommandhistory.h"
|
|
#import "debugview.h"
|
|
|
|
#include "debugger.h"
|
|
|
|
#include "util/xmlfile.h"
|
|
|
|
|
|
//============================================================
|
|
// NOTIFICATIONS
|
|
//============================================================
|
|
|
|
NSString *const MAMEHideDebuggerNotification = @"MAMEHideDebuggerNotification";
|
|
NSString *const MAMEShowDebuggerNotification = @"MAMEShowDebuggerNotification";
|
|
NSString *const MAMEAuxiliaryDebugWindowWillCloseNotification = @"MAMEAuxiliaryDebugWindowWillCloseNotification";
|
|
NSString *const MAMESaveDebuggerConfigurationNotification = @"MAMESaveDebuggerConfigurationNotification";
|
|
|
|
|
|
//============================================================
|
|
// MAMEDebugWindowHandler class
|
|
//============================================================
|
|
|
|
@implementation MAMEDebugWindowHandler
|
|
|
|
+ (void)addCommonActionItems:(NSMenu *)menu {
|
|
[menu addItemWithTitle:@"Break"
|
|
action:@selector(debugBreak:)
|
|
keyEquivalent:@""];
|
|
|
|
NSMenuItem *runParentItem = [menu addItemWithTitle:@"Run"
|
|
action:@selector(debugRun:)
|
|
keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF5FunctionKey]];
|
|
NSMenu *runMenu = [[NSMenu allocWithZone:[NSMenu menuZone]] initWithTitle:@"Run"];
|
|
[runParentItem setSubmenu:runMenu];
|
|
[runMenu release];
|
|
[runParentItem setKeyEquivalentModifierMask:0];
|
|
[[runMenu addItemWithTitle:@"and Hide Debugger"
|
|
action:@selector(debugRunAndHide:)
|
|
keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF12FunctionKey]]
|
|
setKeyEquivalentModifierMask:0];
|
|
[[runMenu addItemWithTitle:@"to Next CPU"
|
|
action:@selector(debugRunToNextCPU:)
|
|
keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF6FunctionKey]]
|
|
setKeyEquivalentModifierMask:0];
|
|
[[runMenu addItemWithTitle:@"until Next Interrupt on Current CPU"
|
|
action:@selector(debugRunToNextInterrupt:)
|
|
keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF7FunctionKey]]
|
|
setKeyEquivalentModifierMask:0];
|
|
[[runMenu addItemWithTitle:@"until Next VBLANK"
|
|
action:@selector(debugRunToNextVBLANK:)
|
|
keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF8FunctionKey]]
|
|
setKeyEquivalentModifierMask:0];
|
|
|
|
NSMenuItem *stepParentItem = [menu addItemWithTitle:@"Step" action:NULL keyEquivalent:@""];
|
|
NSMenu *stepMenu = [[NSMenu allocWithZone:[NSMenu menuZone]] initWithTitle:@"Step"];
|
|
[stepParentItem setSubmenu:stepMenu];
|
|
[stepMenu release];
|
|
[[stepMenu addItemWithTitle:@"Into"
|
|
action:@selector(debugStepInto:)
|
|
keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF11FunctionKey]]
|
|
setKeyEquivalentModifierMask:0];
|
|
[[stepMenu addItemWithTitle:@"Over"
|
|
action:@selector(debugStepOver:)
|
|
keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF10FunctionKey]]
|
|
setKeyEquivalentModifierMask:0];
|
|
[[stepMenu addItemWithTitle:@"Out"
|
|
action:@selector(debugStepOut:)
|
|
keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF10FunctionKey]]
|
|
setKeyEquivalentModifierMask:NSShiftKeyMask];
|
|
|
|
NSMenuItem *resetParentItem = [menu addItemWithTitle:@"Reset" action:NULL keyEquivalent:@""];
|
|
NSMenu *resetMenu = [[NSMenu allocWithZone:[NSMenu menuZone]] initWithTitle:@"Reset"];
|
|
[resetParentItem setSubmenu:resetMenu];
|
|
[resetMenu release];
|
|
[[resetMenu addItemWithTitle:@"Soft"
|
|
action:@selector(debugSoftReset:)
|
|
keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF3FunctionKey]]
|
|
setKeyEquivalentModifierMask:0];
|
|
[[resetMenu addItemWithTitle:@"Hard"
|
|
action:@selector(debugHardReset:)
|
|
keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF3FunctionKey]]
|
|
setKeyEquivalentModifierMask:NSShiftKeyMask];
|
|
|
|
[menu addItem:[NSMenuItem separatorItem]];
|
|
|
|
NSMenuItem *newParentItem = [menu addItemWithTitle:@"New" action:NULL keyEquivalent:@""];
|
|
NSMenu *newMenu = [[NSMenu allocWithZone:[NSMenu menuZone]] initWithTitle:@"New"];
|
|
[newParentItem setSubmenu:newMenu];
|
|
[newMenu release];
|
|
[newMenu addItemWithTitle:@"Memory Window"
|
|
action:@selector(debugNewMemoryWindow:)
|
|
keyEquivalent:@"d"];
|
|
[newMenu addItemWithTitle:@"Disassembly Window"
|
|
action:@selector(debugNewDisassemblyWindow:)
|
|
keyEquivalent:@"a"];
|
|
[newMenu addItemWithTitle:@"Error Log Window"
|
|
action:@selector(debugNewErrorLogWindow:)
|
|
keyEquivalent:@"l"];
|
|
[newMenu addItemWithTitle:@"(Break|Watch)points Window"
|
|
action:@selector(debugNewPointsWindow:)
|
|
keyEquivalent:@"b"];
|
|
[newMenu addItemWithTitle:@"Devices Window"
|
|
action:@selector(debugNewDevicesWindow:)
|
|
keyEquivalent:@"D"];
|
|
|
|
[menu addItem:[NSMenuItem separatorItem]];
|
|
|
|
[menu addItemWithTitle:@"Close Window" action:@selector(performClose:) keyEquivalent:@"w"];
|
|
[menu addItemWithTitle:@"Quit" action:@selector(debugExit:) keyEquivalent:@"q"];
|
|
}
|
|
|
|
|
|
+ (NSPopUpButton *)newActionButtonWithFrame:(NSRect)frame {
|
|
NSPopUpButton *actionButton = [[NSPopUpButton alloc] initWithFrame:frame pullsDown:YES];
|
|
[actionButton setTitle:@""];
|
|
[actionButton addItemWithTitle:@""];
|
|
[actionButton setBezelStyle:NSShadowlessSquareBezelStyle];
|
|
[actionButton setFocusRingType:NSFocusRingTypeNone];
|
|
[[actionButton cell] setArrowPosition:NSPopUpArrowAtCenter];
|
|
[[self class] addCommonActionItems:[actionButton menu]];
|
|
return actionButton;
|
|
}
|
|
|
|
|
|
+ (device_debug::breakpoint *)findBreakpointAtAddress:(offs_t)address forDevice:(device_t &)device {
|
|
device_debug *const cpuinfo = device.debug();
|
|
device_debug::breakpoint *bp = cpuinfo->breakpoint_first();
|
|
while ((bp != nullptr) && (address != bp->address())) bp = bp->next();
|
|
return bp;
|
|
}
|
|
|
|
|
|
- (id)initWithMachine:(running_machine &)m title:(NSString *)t {
|
|
if (!(self = [super init]))
|
|
return nil;
|
|
|
|
window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 320, 240)
|
|
styleMask:(NSTitledWindowMask |
|
|
NSClosableWindowMask |
|
|
NSMiniaturizableWindowMask |
|
|
NSResizableWindowMask)
|
|
backing:NSBackingStoreBuffered
|
|
defer:YES];
|
|
[window setReleasedWhenClosed:NO];
|
|
[window setDelegate:self];
|
|
[window setTitle:t];
|
|
[window setContentMinSize:NSMakeSize(320, 240)];
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(saveConfig:)
|
|
name:MAMESaveDebuggerConfigurationNotification
|
|
object:nil];
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(showDebugger:)
|
|
name:MAMEShowDebuggerNotification
|
|
object:nil];
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(hideDebugger:)
|
|
name:MAMEHideDebuggerNotification
|
|
object:nil];
|
|
|
|
machine = &m;
|
|
|
|
return self;
|
|
}
|
|
|
|
|
|
- (void)dealloc {
|
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
|
|
if (window != nil)
|
|
{
|
|
[window orderOut:self];
|
|
[window release];
|
|
}
|
|
|
|
[super dealloc];
|
|
}
|
|
|
|
|
|
- (void)activate {
|
|
[window makeKeyAndOrderFront:self];
|
|
}
|
|
|
|
|
|
- (IBAction)debugBreak:(id)sender {
|
|
if (machine->debug_flags & DEBUG_FLAG_ENABLED)
|
|
machine->debugger().cpu().get_visible_cpu()->debug()->halt_on_next_instruction("User-initiated break\n");
|
|
}
|
|
|
|
|
|
- (IBAction)debugRun:(id)sender {
|
|
machine->debugger().cpu().get_visible_cpu()->debug()->go();
|
|
}
|
|
|
|
|
|
- (IBAction)debugRunAndHide:(id)sender {
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:MAMEHideDebuggerNotification
|
|
object:self
|
|
userInfo:[NSDictionary dictionaryWithObject:[NSValue valueWithPointer:machine]
|
|
forKey:@"MAMEDebugMachine"]];
|
|
machine->debugger().cpu().get_visible_cpu()->debug()->go();
|
|
}
|
|
|
|
|
|
- (IBAction)debugRunToNextCPU:(id)sender {
|
|
machine->debugger().cpu().get_visible_cpu()->debug()->go_next_device();
|
|
}
|
|
|
|
|
|
- (IBAction)debugRunToNextInterrupt:(id)sender {
|
|
machine->debugger().cpu().get_visible_cpu()->debug()->go_interrupt();
|
|
}
|
|
|
|
|
|
- (IBAction)debugRunToNextVBLANK:(id)sender {
|
|
machine->debugger().cpu().get_visible_cpu()->debug()->go_vblank();
|
|
}
|
|
|
|
|
|
- (IBAction)debugStepInto:(id)sender {
|
|
machine->debugger().cpu().get_visible_cpu()->debug()->single_step();
|
|
}
|
|
|
|
|
|
- (IBAction)debugStepOver:(id)sender {
|
|
machine->debugger().cpu().get_visible_cpu()->debug()->single_step_over();
|
|
}
|
|
|
|
|
|
- (IBAction)debugStepOut:(id)sender {
|
|
machine->debugger().cpu().get_visible_cpu()->debug()->single_step_out();
|
|
}
|
|
|
|
|
|
- (IBAction)debugSoftReset:(id)sender {
|
|
machine->schedule_soft_reset();
|
|
machine->debugger().cpu().get_visible_cpu()->debug()->go();
|
|
}
|
|
|
|
|
|
- (IBAction)debugHardReset:(id)sender {
|
|
machine->schedule_hard_reset();
|
|
}
|
|
|
|
|
|
- (IBAction)debugExit:(id)sender {
|
|
machine->schedule_exit();
|
|
}
|
|
|
|
|
|
- (void)showDebugger:(NSNotification *)notification {
|
|
running_machine *m = (running_machine *)[[[notification userInfo] objectForKey:@"MAMEDebugMachine"] pointerValue];
|
|
if (m == machine)
|
|
{
|
|
if (![window isVisible] && ![window isMiniaturized])
|
|
[window orderFront:self];
|
|
}
|
|
}
|
|
|
|
|
|
- (void)hideDebugger:(NSNotification *)notification {
|
|
running_machine *m = (running_machine *)[[[notification userInfo] objectForKey:@"MAMEDebugMachine"] pointerValue];
|
|
if (m == machine)
|
|
[window orderOut:self];
|
|
}
|
|
|
|
|
|
- (void)saveConfig:(NSNotification *)notification {
|
|
running_machine *m = (running_machine *)[[[notification userInfo] objectForKey:@"MAMEDebugMachine"] pointerValue];
|
|
if (m == machine)
|
|
{
|
|
util::xml::data_node *parentnode = (util::xml::data_node *)[[[notification userInfo] objectForKey:@"MAMEDebugParentNode"] pointerValue];
|
|
util::xml::data_node *node = parentnode->add_child("window", nullptr);
|
|
if (node)
|
|
[self saveConfigurationToNode:node];
|
|
}
|
|
}
|
|
|
|
|
|
- (void)saveConfigurationToNode:(util::xml::data_node *)node {
|
|
NSRect frame = [window frame];
|
|
node->set_attribute_float("position_x", frame.origin.x);
|
|
node->set_attribute_float("position_y", frame.origin.y);
|
|
node->set_attribute_float("size_x", frame.size.width);
|
|
node->set_attribute_float("size_y", frame.size.height);
|
|
}
|
|
|
|
|
|
- (void)restoreConfigurationFromNode:(util::xml::data_node const *)node {
|
|
NSRect frame = [window frame];
|
|
frame.origin.x = node->get_attribute_float("position_x", frame.origin.x);
|
|
frame.origin.y = node->get_attribute_float("position_y", frame.origin.y);
|
|
frame.size.width = node->get_attribute_float("size_x", frame.size.width);
|
|
frame.size.height = node->get_attribute_float("size_y", frame.size.height);
|
|
|
|
NSSize min = [window minSize];
|
|
frame.size.width = std::max(frame.size.width, min.width);
|
|
frame.size.height = std::max(frame.size.height, min.height);
|
|
|
|
NSSize max = [window maxSize];
|
|
frame.size.width = std::min(frame.size.width, max.width);
|
|
frame.size.height = std::min(frame.size.height, max.height);
|
|
|
|
[window setFrame:frame display:YES];
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
//============================================================
|
|
// MAMEAuxiliaryDebugWindowHandler class
|
|
//============================================================
|
|
|
|
@implementation MAMEAuxiliaryDebugWindowHandler
|
|
|
|
+ (void)cascadeWindow:(NSWindow *)window {
|
|
static NSPoint lastPosition = { 0, 0 };
|
|
if (NSEqualPoints(lastPosition, NSZeroPoint)) {
|
|
NSRect available = [[NSScreen mainScreen] visibleFrame];
|
|
lastPosition = NSMakePoint(available.origin.x + 12, available.origin.y + available.size.height - 8);
|
|
}
|
|
lastPosition = [window cascadeTopLeftFromPoint:lastPosition];
|
|
}
|
|
|
|
|
|
- (id)initWithMachine:(running_machine &)m title:(NSString *)t console:(MAMEDebugConsole *)c {
|
|
if (!(self = [super initWithMachine:m title:t]))
|
|
return nil;
|
|
console = c;
|
|
return self;
|
|
}
|
|
|
|
|
|
- (void)dealloc {
|
|
[super dealloc];
|
|
}
|
|
|
|
|
|
- (IBAction)debugNewMemoryWindow:(id)sender {
|
|
[console debugNewMemoryWindow:sender];
|
|
}
|
|
|
|
|
|
- (IBAction)debugNewDisassemblyWindow:(id)sender {
|
|
[console debugNewDisassemblyWindow:sender];
|
|
}
|
|
|
|
|
|
- (IBAction)debugNewErrorLogWindow:(id)sender {
|
|
[console debugNewErrorLogWindow:sender];
|
|
}
|
|
|
|
|
|
- (IBAction)debugNewPointsWindow:(id)sender {
|
|
[console debugNewPointsWindow:sender];
|
|
}
|
|
|
|
|
|
- (IBAction)debugNewDevicesWindow:(id)sender {
|
|
[console debugNewDevicesWindow:sender];
|
|
}
|
|
|
|
|
|
- (void)windowWillClose:(NSNotification *)notification {
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:MAMEAuxiliaryDebugWindowWillCloseNotification
|
|
object:self];
|
|
}
|
|
|
|
- (void)cascadeWindowWithDesiredSize:(NSSize)desired forView:(NSView *)view {
|
|
// convert desired size to an adjustment and apply it to the current window frame
|
|
NSSize const current = [view frame].size;
|
|
desired.width -= current.width;
|
|
desired.height -= current.height;
|
|
NSRect windowFrame = [window frame];
|
|
windowFrame.size.width += desired.width;
|
|
windowFrame.size.height += desired.height;
|
|
|
|
// limit the size to the minimum size
|
|
NSSize const minimum = [window minSize];
|
|
windowFrame.size.width = std::max(windowFrame.size.width, minimum.width);
|
|
windowFrame.size.height = std::max(windowFrame.size.height, minimum.height);
|
|
|
|
// limit the size to the main screen size
|
|
NSRect const available = [[NSScreen mainScreen] visibleFrame];
|
|
windowFrame.size.width = std::min(windowFrame.size.width, available.size.width);
|
|
windowFrame.size.height = std::min(windowFrame.size.height, available.size.height);
|
|
|
|
// arbitrary additional height limit
|
|
windowFrame.size.height = std::min(windowFrame.size.height, CGFloat(320));
|
|
|
|
// place it in the bottom right corner and apply
|
|
windowFrame.origin.x = available.origin.x + available.size.width - windowFrame.size.width;
|
|
windowFrame.origin.y = available.origin.y;
|
|
[window setFrame:windowFrame display:YES];
|
|
[[self class] cascadeWindow:window];
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
//============================================================
|
|
// MAMEExpreesionAuxiliaryDebugWindowHandler class
|
|
//============================================================
|
|
|
|
@implementation MAMEExpressionAuxiliaryDebugWindowHandler
|
|
|
|
- (id)initWithMachine:(running_machine &)m title:(NSString *)t console:(MAMEDebugConsole *)c {
|
|
if (!(self = [super initWithMachine:m title:t console:c]))
|
|
return nil;
|
|
history = [[MAMEDebugCommandHistory alloc] init];
|
|
return self;
|
|
}
|
|
|
|
|
|
- (void)dealloc {
|
|
if (history != nil)
|
|
[history release];
|
|
[super dealloc];
|
|
}
|
|
|
|
|
|
- (id <MAMEDebugViewExpressionSupport>)documentView {
|
|
return nil;
|
|
}
|
|
|
|
|
|
- (NSString *)expression {
|
|
return [[self documentView] expression];
|
|
}
|
|
|
|
- (void)setExpression:(NSString *)expression {
|
|
[history add:expression];
|
|
[[self documentView] setExpression:expression];
|
|
[expressionField setStringValue:expression];
|
|
[expressionField selectText:self];
|
|
}
|
|
|
|
|
|
- (IBAction)doExpression:(id)sender {
|
|
NSString *expr = [sender stringValue];
|
|
if ([expr length] > 0) {
|
|
[history add:expr];
|
|
[[self documentView] setExpression:expr];
|
|
} else {
|
|
[sender setStringValue:[[self documentView] expression]];
|
|
[history reset];
|
|
}
|
|
[sender selectText:self];
|
|
}
|
|
|
|
|
|
- (BOOL)control:(NSControl *)control textShouldBeginEditing:(NSText *)fieldEditor
|
|
{
|
|
if (control == expressionField)
|
|
[history edit];
|
|
|
|
return YES;
|
|
}
|
|
|
|
|
|
- (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)command {
|
|
if (control == expressionField) {
|
|
if (command == @selector(cancelOperation:)) {
|
|
[history reset];
|
|
[expressionField setStringValue:[[self documentView] expression]];
|
|
[expressionField selectText:self];
|
|
return YES;
|
|
} else if (command == @selector(moveUp:)) {
|
|
NSString *hist = [history previous:[expressionField stringValue]];
|
|
if (hist != nil) {
|
|
[expressionField setStringValue:hist];
|
|
[expressionField selectText:self];
|
|
}
|
|
return YES;
|
|
} else if (command == @selector(moveDown:)) {
|
|
NSString *hist = [history next:[expressionField stringValue]];
|
|
if (hist != nil) {
|
|
[expressionField setStringValue:hist];
|
|
[expressionField selectText:self];
|
|
}
|
|
return YES;
|
|
}
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
|
|
- (void)saveConfigurationToNode:(util::xml::data_node *)node {
|
|
[super saveConfigurationToNode:node];
|
|
node->add_child("expression", [[self expression] UTF8String]);
|
|
}
|
|
|
|
|
|
- (void)restoreConfigurationFromNode:(util::xml::data_node const *)node {
|
|
[super restoreConfigurationFromNode:node];
|
|
util::xml::data_node const *const expr = node->get_child("expression");
|
|
if (expr && expr->get_value())
|
|
[self setExpression:[NSString stringWithUTF8String:expr->get_value()]];
|
|
}
|
|
|
|
@end
|