Memory viewer now has a Goto command, different memory space modes, and allows viewing/editing specific banks

This commit is contained in:
Lior Halphon 2016-08-19 14:54:54 +03:00
parent f9236d12bf
commit 0734e990b3
10 changed files with 340 additions and 23 deletions

View File

@ -9,6 +9,9 @@
@property (strong) IBOutlet NSWindow *mainWindow;
@property (strong) IBOutlet NSView *memoryView;
@property (strong) IBOutlet NSPanel *memoryWindow;
@property (readonly) GB_gameboy_t *gameboy;
@property (strong) IBOutlet NSTextField *memoryBankInput;
@property (strong) IBOutlet NSToolbarItem *memoryBankItem;
-(uint8_t) readMemory:(uint16_t) addr;
-(void) writeMemory:(uint16_t) addr value:(uint8_t)value;

View File

@ -8,6 +8,8 @@
#include "HexFiend/HexFiend.h"
#include "GBMemoryByteArray.h"
/* Todo: The general Objective-C coding style conflicts with SameBoy's. This file needs a cleanup. */
@interface Document ()
{
/* NSTextViews freeze the entire app if they're modified too often and too quickly.
@ -20,6 +22,7 @@
HFController *hex_controller;
NSString *lastConsoleInput;
HFLineCountingRepresenter *lineRep;
}
@property GBAudioClient *audioClient;
@ -175,6 +178,12 @@ static uint32_t rgbEncode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b)
}
[self readFromFile:self.fileName ofType:@"gb"];
[self start];
if (hex_controller) {
/* Verify bank sanity, especially when switching models. */
[(GBMemoryByteArray *)(hex_controller.byteArray) setSelectedBank:0];
[self hexUpdateBank:self.memoryBankInput];
}
}
- (IBAction)togglePause:(id)sender
@ -221,7 +230,7 @@ static uint32_t rgbEncode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b)
HFHexTextRepresenter *hexRep = [[HFHexTextRepresenter alloc] init];
HFStringEncodingTextRepresenter *asciiRep = [[HFStringEncodingTextRepresenter alloc] init];
HFVerticalScrollerRepresenter *scrollRep = [[HFVerticalScrollerRepresenter alloc] init];
HFLineCountingRepresenter *lineRep = [[HFLineCountingRepresenter alloc] init];
lineRep = [[HFLineCountingRepresenter alloc] init];
HFStatusBarRepresenter *statusRep = [[HFStatusBarRepresenter alloc] init];
lineRep.lineNumberFormat = HFLineNumberFormatHexadecimal;
@ -250,6 +259,8 @@ static uint32_t rgbEncode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b)
[layoutView setFrame:layoutViewFrame];
[layoutView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable | NSViewMaxYMargin];
[self.memoryView addSubview:layoutView];
self.memoryBankItem.enabled = false;
}
+ (BOOL)autosavesInPlace {
@ -521,7 +532,7 @@ static uint32_t rgbEncode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b)
- (void) performAtomicBlock: (void (^)())block
{
while (!is_inited);
bool was_running = running;
bool was_running = running && !gb.debug_stopped;
if (was_running) {
[self stop];
}
@ -546,4 +557,96 @@ static uint32_t rgbEncode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b)
[self.memoryWindow makeKeyAndOrderFront:sender];
}
- (IBAction)hexGoTo:(id)sender
{
[self performAtomicBlock:^{
uint16_t addr;
if (GB_debugger_evaluate(&gb, [[sender stringValue] UTF8String], &addr, NULL)) {
NSBeep();
return;
}
addr -= lineRep.valueOffset;
if (addr >= hex_controller.byteArray.length) {
NSBeep();
return;
}
[hex_controller setSelectedContentsRanges:@[[HFRangeWrapper withRange:HFRangeMake(addr, 0)]]];
[hex_controller _ensureVisibilityOfLocation:addr];
[self.memoryWindow makeFirstResponder:self.memoryView.subviews[0].subviews[0]];
}];
}
- (IBAction)hexUpdateBank:(NSControl *)sender
{
[self performAtomicBlock:^{
uint16_t addr, bank;
if (GB_debugger_evaluate(&gb, [[sender stringValue] UTF8String], &addr, &bank)) {
NSBeep();
return;
}
if (bank == (uint16_t) -1) {
bank = addr;
}
uint16_t n_banks = 1;
switch ([(GBMemoryByteArray *)(hex_controller.byteArray) mode]) {
case GBMemoryROM:
n_banks = gb.rom_size / 0x4000;
break;
case GBMemoryVRAM:
n_banks = gb.is_cgb ? 2 : 1;
break;
case GBMemoryExternalRAM:
n_banks = gb.mbc_ram_size / 0x2000;
break;
case GBMemoryRAM:
n_banks = gb.is_cgb ? 8 : 1;
break;
case GBMemoryEntireSpace:
break;
}
bank %= n_banks;
[sender setStringValue:[NSString stringWithFormat:@"$%x", bank]];
[(GBMemoryByteArray *)(hex_controller.byteArray) setSelectedBank:bank];
[hex_controller reloadData];
}];
}
- (IBAction)hexUpdateSpace:(NSPopUpButtonCell *)sender
{
self.memoryBankItem.enabled = [sender indexOfSelectedItem] != GBMemoryEntireSpace;
GBMemoryByteArray *byteArray = (GBMemoryByteArray *)(hex_controller.byteArray);
[byteArray setMode:(GB_memory_mode_t)[sender indexOfSelectedItem]];
switch ((GB_memory_mode_t)[sender indexOfSelectedItem]) {
case GBMemoryEntireSpace:
case GBMemoryROM:
lineRep.valueOffset = 0;
byteArray.selectedBank = gb.mbc_rom_bank;
break;
case GBMemoryVRAM:
lineRep.valueOffset = 0x8000;
byteArray.selectedBank = gb.cgb_vram_bank;
break;
case GBMemoryExternalRAM:
lineRep.valueOffset = 0xA000;
byteArray.selectedBank = gb.mbc_ram_bank;
break;
case GBMemoryRAM:
lineRep.valueOffset = 0xC000;
byteArray.selectedBank = gb.cgb_ram_bank;
break;
}
[self.memoryBankInput setStringValue:[NSString stringWithFormat:@"$%x", byteArray.selectedBank]];
[hex_controller reloadData];
[self.memoryView setNeedsDisplay:YES];
}
- (GB_gameboy_t *) gameboy
{
return &gb;
}
@end

View File

@ -10,6 +10,8 @@
<outlet property="consoleOutput" destination="doS-dM-hnl" id="Gn5-ju-Wb0"/>
<outlet property="consoleWindow" destination="21F-Ah-yHX" id="eQ4-ug-LsT"/>
<outlet property="mainWindow" destination="xOd-HO-29H" id="h8M-YB-vcC"/>
<outlet property="memoryBankInput" destination="rdV-q6-hc6" id="KBx-9T-2mX"/>
<outlet property="memoryBankItem" destination="bWC-FW-IYP" id="Lf2-dh-z32"/>
<outlet property="memoryView" destination="8hr-8o-3rN" id="fF0-rh-8ND"/>
<outlet property="memoryWindow" destination="mRm-dL-mCj" id="VPR-lu-vtI"/>
<outlet property="view" destination="uqf-pe-VAF" id="HMP-rf-Yqk"/>
@ -69,10 +71,10 @@
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<color key="backgroundColor" red="0.14901960784313725" green="0.14901960784313725" blue="0.14901960784313725" alpha="1" colorSpace="calibratedRGB"/>
<size key="minSize" width="600" height="376"/>
<size key="maxSize" width="600" height="10000000"/>
<size key="maxSize" width="1160" height="10000000"/>
<color key="insertionPointColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<size key="minSize" width="600" height="376"/>
<size key="maxSize" width="600" height="10000000"/>
<size key="maxSize" width="1160" height="10000000"/>
<allowedInputSourceLocales>
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
</allowedInputSourceLocales>
@ -84,7 +86,7 @@
<rect key="frame" x="-100" y="-100" width="87" height="18"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="cwi-6E-rbh">
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="cwi-6E-rbh">
<rect key="frame" x="584" y="0.0" width="16" height="376"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
@ -125,7 +127,79 @@
<rect key="frame" x="0.0" y="0.0" width="528" height="320"/>
<autoresizingMask key="autoresizingMask"/>
</view>
<point key="canvasLocation" x="-102" y="60"/>
<toolbar key="toolbar" implicitIdentifier="D857E961-E523-4295-83F8-0849316E827C" autosavesConfiguration="NO" allowsUserCustomization="NO" displayMode="iconAndLabel" sizeMode="regular" id="82v-uB-RPi">
<allowedToolbarItems>
<toolbarItem implicitItemIdentifier="NSToolbarSpaceItem" id="WUk-8p-S6B"/>
<toolbarItem implicitItemIdentifier="NSToolbarFlexibleSpaceItem" id="E3z-um-6KG"/>
<toolbarItem implicitItemIdentifier="4F6AAE25-1E9D-4111-9E5B-91F0792E56CD" label="Address Space" paletteLabel="Address Space" id="VTy-lj-K0H">
<nil key="toolTip"/>
<size key="minSize" width="100" height="25"/>
<size key="maxSize" width="100" height="25"/>
<popUpButton key="view" verticalHuggingPriority="750" id="vfJ-vu-gqJ">
<rect key="frame" x="0.0" y="14" width="100" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="bpD-j9-omo">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="gTX-6Z-mOH">
<items>
<menuItem title="Entire Space" id="Zp5-J9-dd3"/>
<menuItem title="ROM" tag="1" id="ANn-pq-5SA"/>
<menuItem title="Video RAM" tag="2" id="m35-GY-cKE"/>
<menuItem title="Cartridge RAM" tag="3" id="AOc-Up-PNm"/>
<menuItem title="RAM" tag="4" id="dg4-zf-bo4"/>
</items>
</menu>
<connections>
<action selector="hexUpdateSpace:" target="-2" id="1xc-Wa-RGp"/>
</connections>
</popUpButtonCell>
</popUpButton>
</toolbarItem>
<toolbarItem implicitItemIdentifier="D16C64D2-2F0D-4033-A1EC-A1E699522ECE" label="Bank" paletteLabel="Bank" id="bWC-FW-IYP">
<nil key="toolTip"/>
<size key="minSize" width="64" height="22"/>
<size key="maxSize" width="64" height="22"/>
<textField key="view" verticalHuggingPriority="750" id="rdV-q6-hc6">
<rect key="frame" x="0.0" y="14" width="64" height="22"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" id="JCn-Y1-eHS">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<action selector="hexUpdateBank:" target="-2" id="Mx9-WI-wgO"/>
</connections>
</textField>
</toolbarItem>
<toolbarItem implicitItemIdentifier="F9723DA8-D79F-43AB-876B-783DD0204AA6" label="Go to" paletteLabel="Go to" id="rLO-D7-zRG">
<nil key="toolTip"/>
<size key="minSize" width="96" height="22"/>
<size key="maxSize" width="128" height="22"/>
<textField key="view" verticalHuggingPriority="750" id="EJd-jG-hmH">
<rect key="frame" x="0.0" y="14" width="96" height="22"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" state="on" borderStyle="bezel" bezelStyle="round" id="vg5-Nn-abb">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<action selector="hexGoTo:" target="-2" id="7WG-8C-SK8"/>
</connections>
</textField>
</toolbarItem>
</allowedToolbarItems>
<defaultToolbarItems>
<toolbarItem reference="VTy-lj-K0H"/>
<toolbarItem reference="bWC-FW-IYP"/>
<toolbarItem reference="E3z-um-6KG"/>
<toolbarItem reference="rLO-D7-zRG"/>
</defaultToolbarItems>
</toolbar>
<point key="canvasLocation" x="-185" y="61"/>
</window>
<menuItem title="Cartridge RAM" id="ylM-ah-PNQ"/>
</objects>
</document>

View File

@ -2,6 +2,16 @@
#import "HexFiend/HexFiend.h"
#import "HexFiend/HFByteArray.h"
typedef enum {
GBMemoryEntireSpace,
GBMemoryROM,
GBMemoryVRAM,
GBMemoryExternalRAM,
GBMemoryRAM
} GB_memory_mode_t;
@interface GBMemoryByteArray : HFByteArray
- (instancetype) initWithDocument:(Document *)document;
@property uint16_t selectedBank;
@property GB_memory_mode_t mode;
@end

View File

@ -17,16 +17,83 @@
- (unsigned long long)length
{
return 0x10000;
switch (_mode) {
case GBMemoryEntireSpace:
return 0x10000;
case GBMemoryROM:
return 0x8000;
case GBMemoryVRAM:
return 0x2000;
case GBMemoryExternalRAM:
return 0x2000;
case GBMemoryRAM:
return 0x2000;
}
}
- (void)copyBytes:(unsigned char *)dst range:(HFRange)range
{
uint16_t addr = (uint16_t) range.location;
unsigned long long length = range.length;
while (length) {
*(dst++) = [_document readMemory:addr++];
length--;
__block uint16_t addr = (uint16_t) range.location;
__block unsigned long long length = range.length;
if (_mode == GBMemoryEntireSpace) {
while (length) {
*(dst++) = [_document readMemory:addr++];
length--;
}
}
else {
[_document performAtomicBlock:^{
unsigned char *_dst = dst;
uint16_t bank_backup = 0;
GB_gameboy_t *gb = _document.gameboy;
switch (_mode) {
case GBMemoryROM:
bank_backup = gb->mbc_rom_bank;
gb->mbc_rom_bank = self.selectedBank;
break;
case GBMemoryVRAM:
bank_backup = gb->cgb_vram_bank;
if (gb->is_cgb) {
gb->cgb_vram_bank = self.selectedBank;
}
addr += 0x8000;
break;
case GBMemoryExternalRAM:
bank_backup = gb->mbc_ram_bank;
gb->mbc_ram_bank = self.selectedBank;
addr += 0xA000;
break;
case GBMemoryRAM:
bank_backup = gb->cgb_ram_bank;
if (gb->is_cgb) {
gb->cgb_ram_bank = self.selectedBank;
}
addr += 0xC000;
break;
default:
assert(false);
}
while (length) {
*(_dst++) = [_document readMemory:addr++];
length--;
}
switch (_mode) {
case GBMemoryROM:
gb->mbc_rom_bank = bank_backup;
break;
case GBMemoryVRAM:
gb->cgb_vram_bank = bank_backup;
break;
case GBMemoryExternalRAM:
gb->mbc_ram_bank = bank_backup;
break;
case GBMemoryRAM:
gb->cgb_ram_bank = bank_backup;
break;
default:
assert(false);
}
}];
}
}
@ -49,15 +116,60 @@
{
if (slice.length != lrange.length) return; /* Insertion is not allowed, only overwriting. */
[_document performAtomicBlock:^{
uint16_t addr = (uint16_t) lrange.location;
uint16_t bank_backup = 0;
GB_gameboy_t *gb = _document.gameboy;
switch (_mode) {
case GBMemoryROM:
bank_backup = gb->mbc_rom_bank;
gb->mbc_rom_bank = self.selectedBank;
break;
case GBMemoryVRAM:
bank_backup = gb->cgb_vram_bank;
if (gb->is_cgb) {
gb->cgb_vram_bank = self.selectedBank;
}
addr += 0x8000;
break;
case GBMemoryExternalRAM:
bank_backup = gb->mbc_ram_bank;
gb->mbc_ram_bank = self.selectedBank;
addr += 0xA000;
break;
case GBMemoryRAM:
bank_backup = gb->cgb_ram_bank;
if (gb->is_cgb) {
gb->cgb_ram_bank = self.selectedBank;
}
addr += 0xC000;
break;
default:
break;
}
uint8_t values[lrange.length];
[slice copyBytes:values range:HFRangeMake(0, lrange.length)];
uint16_t addr = (uint16_t) lrange.location;
uint8_t *src = values;
unsigned long long length = lrange.length;
while (length) {
[_document writeMemory:addr++ value:*(src++)];
length--;
}
switch (_mode) {
case GBMemoryROM:
gb->mbc_rom_bank = bank_backup;
break;
case GBMemoryVRAM:
gb->cgb_vram_bank = bank_backup;
break;
case GBMemoryExternalRAM:
gb->mbc_ram_bank = bank_backup;
break;
case GBMemoryRAM:
gb->cgb_ram_bank = bank_backup;
break;
default:
break;
}
}];
}

View File

@ -333,11 +333,11 @@ static struct {
};
value_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
unsigned int length, bool *error,
size_t length, bool *error,
uint16_t *watchpoint_address, uint8_t *watchpoint_new_value);
static lvalue_t debugger_evaluate_lvalue(GB_gameboy_t *gb, const char *string,
unsigned int length, bool *error,
size_t length, bool *error,
uint16_t *watchpoint_address, uint8_t *watchpoint_new_value)
{
*error = false;
@ -410,19 +410,19 @@ static lvalue_t debugger_evaluate_lvalue(GB_gameboy_t *gb, const char *string,
case 'p': if (string[1] == 'c') return (lvalue_t){LVALUE_REG16, .register_address = &gb->pc};
}
}
GB_log(gb, "Unknown register: %.*s\n", length, string);
GB_log(gb, "Unknown register: %.*s\n", (unsigned int) length, string);
*error = true;
return (lvalue_t){0,};
}
GB_log(gb, "Expression is not an lvalue: %.*s\n", length, string);
GB_log(gb, "Expression is not an lvalue: %.*s\n", (unsigned int) length, string);
*error = true;
return (lvalue_t){0,};
}
#define ERROR ((value_t){0,})
value_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
unsigned int length, bool *error,
size_t length, bool *error,
uint16_t *watchpoint_address, uint8_t *watchpoint_new_value)
{
*error = false;
@ -567,7 +567,7 @@ value_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
return (value_t){true, symbol->bank, symbol->addr};
}
GB_log(gb, "Unknown register or symbol: %.*s\n", length, string);
GB_log(gb, "Unknown register or symbol: %.*s\n", (unsigned int) length, string);
*error = true;
return ERROR;
}
@ -581,7 +581,7 @@ value_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
}
uint16_t literal = (uint16_t) (strtol(string, &end, base));
if (end != string + length) {
GB_log(gb, "Failed to parse: %.*s\n", length, string);
GB_log(gb, "Failed to parse: %.*s\n", (unsigned int) length, string);
*error = true;
return ERROR;
}
@ -1559,3 +1559,17 @@ const char *GB_debugger_name_for_address(GB_gameboy_t *gb, uint16_t addr)
if (symbol && symbol->addr == addr) return symbol->name;
return NULL;
}
/* The public version of debugger_evaluate */
bool GB_debugger_evaluate(GB_gameboy_t *gb, const char *string, uint16_t *result, uint16_t *result_bank)
{
bool error = false;
value_t value = debugger_evaluate(gb, string, strlen(string), &error, NULL, NULL);
if (result) {
*result = value.value;
}
if (result_bank) {
*result_bank = value.has_bank? value.value : -1;
}
return error;
}

View File

@ -11,4 +11,5 @@ void GB_debugger_test_read_watchpoint(GB_gameboy_t *gb, uint16_t addr);
void GB_debugger_load_symbol_file(GB_gameboy_t *gb, const char *path);
const GB_bank_symbol_t *GB_debugger_find_symbol(GB_gameboy_t *gb, uint16_t addr);
const char *GB_debugger_name_for_address(GB_gameboy_t *gb, uint16_t addr);
bool GB_debugger_evaluate(GB_gameboy_t *gb, const char *string, uint16_t *result, uint16_t *result_bank); /* result_bank is -1 if unused. */
#endif /* debugger_h */

View File

@ -367,7 +367,7 @@ You create an HFController via <tt>[[HFController alloc] init]</tt>. After that
- (unsigned long long)contentsLength;
- (void) reloadData;
- (void)_ensureVisibilityOfLocation:(unsigned long long)location;
@end
/*! A notification posted whenever any of the HFController's properties change. The object is the HFController. The userInfo contains one key, HFControllerChangedPropertiesKey, which contains an NSNumber with the changed properties as a HFControllerPropertyBits bitmask. This is useful for external objects to be notified of changes. HFRepresenters added to the HFController are notified via the controllerDidChange: message.

View File

@ -1796,7 +1796,7 @@ static BOOL rangesAreInAscendingOrder(NSEnumerator *rangeEnumerator) {
[cachedData release];
cachedData = nil;
[self _updateDisplayedRange];
[self _addPropertyChangeBits: HFControllerContentValue];
[self _addPropertyChangeBits: HFControllerContentValue | HFControllerContentLength];
END_TRANSACTION();
}

View File

@ -368,7 +368,7 @@ static inline int common_prefix_length(const char *a, const char *b) {
[self getLineNumberFormatString:formatString length:sizeof formatString];
while (lineCount--) {
int charCount = sprintf(buffer + bufferIndex, formatString, lineValue);
int charCount = sprintf(buffer + bufferIndex, formatString, lineValue + self.representer.valueOffset);
HFASSERT(charCount > 0);
bufferIndex += charCount;
buffer[bufferIndex++] = '\n';