Added VRAM-debugging APIs, added VRAM viewer to Cocoa Port, fixed uninitialized VRAM, fixed memory viewer crash

This commit is contained in:
Lior Halphon 2016-10-27 00:14:02 +03:00
parent 47aaf44017
commit 388fb600de
15 changed files with 1185 additions and 12 deletions

View File

@ -1,7 +1,8 @@
#import <Cocoa/Cocoa.h>
#include "GBView.h"
#include "GBImageView.h"
@interface Document : NSDocument <NSWindowDelegate>
@interface Document : NSDocument <NSWindowDelegate, GBImageViewDelegate, NSTableViewDataSource, NSTableViewDelegate>
@property (strong) IBOutlet GBView *view;
@property (strong) IBOutlet NSTextView *consoleOutput;
@property (strong) IBOutlet NSPanel *consoleWindow;
@ -12,6 +13,18 @@
@property (readonly) GB_gameboy_t *gameboy;
@property (strong) IBOutlet NSTextField *memoryBankInput;
@property (strong) IBOutlet NSToolbarItem *memoryBankItem;
@property (strong) IBOutlet GBImageView *tilesetImageView;
@property (strong) IBOutlet NSPopUpButton *tilesetPaletteButton;
@property (strong) IBOutlet GBImageView *tilemapImageView;
@property (strong) IBOutlet NSPopUpButton *tilemapPaletteButton;
@property (strong) IBOutlet NSPopUpButton *tilemapMapButton;
@property (strong) IBOutlet NSPopUpButton *TilemapSetButton;
@property (strong) IBOutlet NSButton *gridButton;
@property (strong) IBOutlet NSTabView *vramTabView;
@property (strong) IBOutlet NSPanel *vramWindow;
@property (strong) IBOutlet NSTextField *vramStatusLabel;
@property (strong) IBOutlet NSTableView *paletteTableView;
@property (strong) IBOutlet NSTableView *spritesTableView;
-(uint8_t) readMemory:(uint16_t) addr;
-(void) writeMemory:(uint16_t) addr value:(uint8_t)value;

View File

@ -7,10 +7,12 @@
#include "debugger.h"
#include "memory.h"
#include "camera.h"
#include "display.h"
#include "HexFiend/HexFiend.h"
#include "GBMemoryByteArray.h"
/* Todo: The general Objective-C coding style conflicts with SameBoy's. This file needs a cleanup. */
/* Todo: Split into category files! This is so messy!!! */
@interface Document ()
{
@ -30,6 +32,11 @@
AVCaptureSession *cameraSession;
AVCaptureConnection *cameraConnection;
AVCaptureStillImageOutput *cameraOutput;
GB_oam_info_t oamInfo[40];
uint16_t oamCount;
uint8_t oamHeight;
bool oamUpdating;
}
@property GBAudioClient *audioClient;
@ -140,6 +147,7 @@ static uint8_t cameraGetPixel(GB_gameboy_t *gb, uint8_t x, uint8_t y)
self.view.mouseHidingEnabled = (self.mainWindow.styleMask & NSFullScreenWindowMask) != 0;
[self.view flip];
GB_set_pixels_output(&gb, self.view.pixels);
[self reloadVRAMData: nil];
}
- (void) run
@ -262,6 +270,7 @@ static uint8_t cameraGetPixel(GB_gameboy_t *gb, uint8_t x, uint8_t y)
window_frame.size.height = MAX([[NSUserDefaults standardUserDefaults] integerForKey:@"LastWindowHeight"],
window_frame.size.height);
[self.mainWindow setFrame:window_frame display:YES];
self.vramStatusLabel.cell.backgroundStyle = NSBackgroundStyleRaised;
[self start];
}
@ -450,6 +459,7 @@ static uint8_t cameraGetPixel(GB_gameboy_t *gb, uint8_t x, uint8_t y)
NSString *nsstring = @(string); // For ref-counting
dispatch_async(dispatch_get_main_queue(), ^{
[hex_controller reloadData];
[self reloadVRAMData: nil];
NSFont *font = [NSFont userFixedPitchFontOfSize:12];
NSUnderlineStyle underline = NSUnderlineStyleNone;
@ -595,6 +605,28 @@ static uint8_t cameraGetPixel(GB_gameboy_t *gb, uint8_t x, uint8_t y)
}
}
+ (NSImage *) imageFromData:(NSData *)data width:(NSUInteger) width height:(NSUInteger) height scale:(double) scale
{
CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef) data);
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
CGImageRef iref = CGImageCreate(width,
height,
8,
32,
4 * width,
colorSpaceRef,
bitmapInfo,
provider,
NULL,
YES,
renderingIntent);
return [[NSImage alloc] initWithCGImage:iref size:NSMakeSize(width * scale, height * scale)];
}
- (void) reloadMemoryView
{
if (self.memoryWindow.isVisible) {
@ -602,6 +634,79 @@ static uint8_t cameraGetPixel(GB_gameboy_t *gb, uint8_t x, uint8_t y)
}
}
- (IBAction) reloadVRAMData: (id) sender
{
if (self.vramWindow.isVisible) {
switch ([self.vramTabView.tabViewItems indexOfObject:self.vramTabView.selectedTabViewItem]) {
case 0:
/* Tileset */
{
GB_palette_type_t palette_type = GB_PALETTE_NONE;
NSUInteger palette_menu_index = self.tilesetPaletteButton.indexOfSelectedItem;
if (palette_menu_index) {
palette_type = palette_menu_index > 8? GB_PALETTE_OAM : GB_PALETTE_BACKGROUND;
}
size_t bufferLength = 256 * 192 * 4;
NSMutableData *data = [NSMutableData dataWithCapacity:bufferLength];
data.length = bufferLength;
GB_draw_tileset(&gb, (uint32_t *)data.mutableBytes, palette_type, (palette_menu_index - 1) & 7);
self.tilesetImageView.image = [Document imageFromData:data width:256 height:192 scale:1.0];
self.tilesetImageView.layer.magnificationFilter = kCAFilterNearest;
}
break;
case 1:
/* Tilemap */
{
GB_palette_type_t palette_type = GB_PALETTE_NONE;
NSUInteger palette_menu_index = self.tilemapPaletteButton.indexOfSelectedItem;
if (palette_menu_index > 1) {
palette_type = palette_menu_index > 9? GB_PALETTE_OAM : GB_PALETTE_BACKGROUND;
}
else if (palette_menu_index == 1) {
palette_type = GB_PALETTE_AUTO;
}
size_t bufferLength = 256 * 256 * 4;
NSMutableData *data = [NSMutableData dataWithCapacity:bufferLength];
data.length = bufferLength;
GB_draw_tilemap(&gb, (uint32_t *)data.mutableBytes, palette_type, (palette_menu_index - 2) & 7,
(GB_map_type_t) self.tilemapMapButton.indexOfSelectedItem,
(GB_tileset_type_t) self.TilemapSetButton.indexOfSelectedItem);
self.tilemapImageView.image = [Document imageFromData:data width:256 height:256 scale:1.0];
self.tilemapImageView.layer.magnificationFilter = kCAFilterNearest;
}
break;
case 2:
/* OAM */
{
oamCount = GB_get_oam_info(&gb, oamInfo);
oamHeight = (gb.io_registers[GB_IO_LCDC] & 4) ? 16:8;
dispatch_async(dispatch_get_main_queue(), ^{
if (!oamUpdating) {
oamUpdating = true;
[self.spritesTableView reloadData];
oamUpdating = false;
}
});
}
break;
case 3:
/* Palettes */
{
dispatch_async(dispatch_get_main_queue(), ^{
[self.paletteTableView reloadData];
});
}
break;
}
}
}
- (IBAction) showMemory:(id)sender
{
if (!hex_controller) {
@ -777,4 +882,195 @@ static uint8_t cameraGetPixel(GB_gameboy_t *gb, uint8_t x, uint8_t y)
return ret;
}
- (IBAction)toggleTilesetGrid:(NSButton *)sender
{
if (sender.state) {
self.tilesetImageView.horizontalGrids = @[
[[GBImageViewGridConfiguration alloc] initWithColor:[NSColor colorWithCalibratedRed:0 green:0 blue:0 alpha:0.25] size:8],
[[GBImageViewGridConfiguration alloc] initWithColor:[NSColor colorWithCalibratedRed:0 green:0 blue:0 alpha:0.5] size:128],
];
self.tilesetImageView.verticalGrids = @[
[[GBImageViewGridConfiguration alloc] initWithColor:[NSColor colorWithCalibratedRed:0 green:0 blue:0 alpha:0.25] size:8],
[[GBImageViewGridConfiguration alloc] initWithColor:[NSColor colorWithCalibratedRed:0 green:0 blue:0 alpha:0.5] size:64],
];
self.tilemapImageView.horizontalGrids = @[
[[GBImageViewGridConfiguration alloc] initWithColor:[NSColor colorWithCalibratedRed:0 green:0 blue:0 alpha:0.25] size:8],
];
self.tilemapImageView.verticalGrids = @[
[[GBImageViewGridConfiguration alloc] initWithColor:[NSColor colorWithCalibratedRed:0 green:0 blue:0 alpha:0.25] size:8],
];
}
else {
self.tilesetImageView.horizontalGrids = nil;
self.tilesetImageView.verticalGrids = nil;
self.tilemapImageView.horizontalGrids = nil;
self.tilemapImageView.verticalGrids = nil;
}
}
- (IBAction)vramTabChanged:(NSSegmentedControl *)sender
{
[self.vramTabView selectTabViewItemAtIndex:[sender selectedSegment]];
[self reloadVRAMData:sender];
[self.vramTabView.selectedTabViewItem.view addSubview:self.gridButton];
self.gridButton.hidden = [sender selectedSegment] >= 2;
NSUInteger height_diff = self.vramWindow.frame.size.height - self.vramWindow.contentView.frame.size.height;
CGRect window_rect = self.vramWindow.frame;
window_rect.origin.y += window_rect.size.height;
switch ([sender selectedSegment]) {
case 0:
window_rect.size.height = 384 + height_diff + 48;
break;
case 1:
case 2:
window_rect.size.height = 512 + height_diff + 48;
break;
case 3:
window_rect.size.height = 20 * 16 + height_diff + 24;
break;
default:
break;
}
window_rect.origin.y -= window_rect.size.height;
[self.vramWindow setFrame:window_rect display:YES animate:YES];
}
- (void)mouseDidLeaveImageView:(GBImageView *)view
{
self.vramStatusLabel.stringValue = @"";
}
- (void)imageView:(GBImageView *)view mouseMovedToX:(NSUInteger)x Y:(NSUInteger)y
{
if (view == self.tilesetImageView) {
uint8_t bank = x >= 128? 1 : 0;
x &= 127;
uint16_t tile = x / 8 + y / 8 * 16;
self.vramStatusLabel.stringValue = [NSString stringWithFormat:@"Tile number $%02x at %d:$%04x", tile & 0xFF, bank, 0x8000 + tile * 0x10];
}
else if (view == self.tilemapImageView) {
uint16_t map_offset = x / 8 + y / 8 * 32;
uint16_t map_base = 0x1800;
GB_map_type_t map_type = (GB_map_type_t) self.tilemapMapButton.indexOfSelectedItem;
GB_tileset_type_t tileset_type = (GB_tileset_type_t) self.TilemapSetButton.indexOfSelectedItem;
if (map_type == GB_MAP_9C00 || (map_type == GB_MAP_AUTO && gb.io_registers[GB_IO_LCDC] & 0x08)) {
map_base = 0x1c00;
}
if (tileset_type == GB_TILESET_AUTO) {
tileset_type = (gb.io_registers[GB_IO_LCDC] & 0x10)? GB_TILESET_8800 : GB_TILESET_8000;
}
uint8_t tile = gb.vram[map_base + map_offset];
uint16_t tile_address = 0;
if (tileset_type == GB_TILESET_8000) {
tile_address = 0x8000 + tile * 0x10;
}
else {
tile_address = 0x9000 + (int8_t)tile * 0x10;
}
if (gb.is_cgb) {
uint8_t attributes = gb.vram[map_base + map_offset + 0x2000];
self.vramStatusLabel.stringValue = [NSString stringWithFormat:@"Tile number $%02x (%d:$%04x) at map address $%04x (Attributes: %c%c%c%d%d)",
tile,
attributes & 0x8? 1 : 0,
tile_address,
0x8000 + map_base + map_offset,
(attributes & 0x80) ? 'P' : '-',
(attributes & 0x40) ? 'V' : '-',
(attributes & 0x20) ? 'H' : '-',
attributes & 0x8? 1 : 0,
attributes & 0x7
];
}
else {
self.vramStatusLabel.stringValue = [NSString stringWithFormat:@"Tile number $%02x ($%04x) at map address $%04x",
tile,
tile_address,
0x8000 + map_base + map_offset
];
}
}
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
if (tableView == self.paletteTableView) {
return 16; /* 8 BG palettes, 8 OBJ palettes*/
}
else if (tableView == self.spritesTableView) {
return oamCount;
}
return 0;
}
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
NSUInteger columnIndex = [[tableView tableColumns] indexOfObject:tableColumn];
if (tableView == self.paletteTableView) {
if (columnIndex == 0) {
return [NSString stringWithFormat:@"%s %d", row >=8? "Object" : "Background", (int)(row & 7)];
}
uint16_t index = columnIndex - 1 + (row & 7) * 4;
return @(((row >= 8? gb.sprite_palletes_data : gb.background_palletes_data)[(index << 1) + 1] << 8) |
(row >= 8? gb.sprite_palletes_data : gb.background_palletes_data)[(index << 1)]);
}
else if (tableView == self.spritesTableView) {
switch (columnIndex) {
case 0:
return [Document imageFromData:[NSData dataWithBytesNoCopy:oamInfo[row].image length:64 * 4] width:8 height:oamHeight scale:16.0/oamHeight];
case 1:
return @((int)oamInfo[row].x - 8);
case 2:
return @((int)oamInfo[row].y - 16);
case 3:
return [NSString stringWithFormat:@"$%02x", oamInfo[row].tile];
case 4:
return [NSString stringWithFormat:@"$%04x", 0x8000 + oamInfo[row].tile * 0x10];
case 5:
return [NSString stringWithFormat:@"$%04x", oamInfo[row].oam_addr];
case 6:
if (gb.cgb_mode) {
return [NSString stringWithFormat:@"%c%c%c%d%d",
oamInfo[row].flags & 0x80? 'P' : '-',
oamInfo[row].flags & 0x40? 'Y' : '-',
oamInfo[row].flags & 0x20? 'X' : '-',
oamInfo[row].flags & 0x08? 1 : 0,
oamInfo[row].flags & 0x07];
}
return [NSString stringWithFormat:@"%c%c%c%d",
oamInfo[row].flags & 0x80? 'P' : '-',
oamInfo[row].flags & 0x40? 'Y' : '-',
oamInfo[row].flags & 0x20? 'X' : '-',
oamInfo[row].flags & 0x10? 1 : 0];
case 7:
return oamInfo[row].obscured_by_line_limit? @"Dropped: Too many sprites in line": @"";
}
}
return nil;
}
- (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)row
{
return tableView == self.spritesTableView;
}
- (BOOL)tableView:(NSTableView *)tableView shouldEditTableColumn:(nullable NSTableColumn *)tableColumn row:(NSInteger)row
{
return NO;
}
- (IBAction)showVRAMViewer:(id)sender
{
[self.vramWindow makeKeyAndOrderFront:sender];
}
@end

View File

@ -6,15 +6,27 @@
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="Document">
<connections>
<outlet property="TilemapSetButton" destination="k4c-Vg-MBu" id="Ak1-6d-B1N"/>
<outlet property="consoleInput" destination="l22-S8-uji" id="Heu-am-YgB"/>
<outlet property="consoleOutput" destination="doS-dM-hnl" id="Gn5-ju-Wb0"/>
<outlet property="consoleWindow" destination="21F-Ah-yHX" id="eQ4-ug-LsT"/>
<outlet property="gridButton" destination="fL6-2S-Rgd" id="jtV-jh-GHC"/>
<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="paletteTableView" destination="gfC-d3-dmq" id="fTC-eL-Qg3"/>
<outlet property="spritesTableView" destination="TOc-XJ-w9w" id="O4R-4Z-9hU"/>
<outlet property="tilemapImageView" destination="LlK-tV-bjv" id="nSY-Xd-BjZ"/>
<outlet property="tilemapMapButton" destination="YIJ-Qc-SIZ" id="BB7-Gg-7XP"/>
<outlet property="tilemapPaletteButton" destination="loB-0k-Qff" id="2Or-7l-6vn"/>
<outlet property="tilesetImageView" destination="QcQ-ex-36R" id="8SY-Ag-eIf"/>
<outlet property="tilesetPaletteButton" destination="TLv-xS-X5K" id="uE2-K1-5sj"/>
<outlet property="view" destination="uqf-pe-VAF" id="HMP-rf-Yqk"/>
<outlet property="vramStatusLabel" destination="6vK-IP-PmP" id="A2d-dI-R3J"/>
<outlet property="vramTabView" destination="AZz-Mh-rPA" id="ZCG-SP-Lfw"/>
<outlet property="vramWindow" destination="mbr-db-iZh" id="VZj-IV-t6O"/>
<outlet property="window" destination="xOd-HO-29H" id="JIz-fz-R2o"/>
</connections>
</customObject>
@ -50,6 +62,7 @@
</window>
<window title="Debug Console" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" hidesOnDeactivate="YES" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="21F-Ah-yHX" customClass="NSPanel">
<windowStyleMask key="styleMask" titled="YES" closable="YES" resizable="YES" utility="YES" HUD="YES"/>
<windowCollectionBehavior key="collectionBehavior" fullScreenAuxiliary="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="272" y="172" width="600" height="400"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
@ -201,5 +214,470 @@
<point key="canvasLocation" x="-185" y="61"/>
</window>
<menuItem title="Cartridge RAM" id="ylM-ah-PNQ"/>
<window title="VRAM Viewer" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" hidesOnDeactivate="YES" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="mbr-db-iZh" customClass="NSPanel">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" unifiedTitleAndToolbar="YES"/>
<windowCollectionBehavior key="collectionBehavior" fullScreenAuxiliary="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="283" y="305" width="512" height="432"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
<view key="contentView" id="GYW-dv-Um1">
<rect key="frame" x="0.0" y="0.0" width="512" height="432"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<box verticalHuggingPriority="750" title="Box" boxType="separator" titlePosition="noTitle" id="ucG-cD-wfs">
<rect key="frame" x="0.0" y="406" width="512" height="5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<color key="borderColor" white="0.0" alpha="0.41999999999999998" colorSpace="calibratedWhite"/>
<color key="fillColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<font key="titleFont" metaFont="system"/>
</box>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="6vK-IP-PmP">
<rect key="frame" x="-2" y="4" width="516" height="14"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" id="umk-4r-VNg">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<tabView drawsBackground="NO" type="noTabsNoBorder" initialItem="pXb-od-Wb1" id="AZz-Mh-rPA">
<rect key="frame" x="0.0" y="24" width="512" height="408"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<font key="font" metaFont="system"/>
<tabViewItems>
<tabViewItem label="Tileset" identifier="1" id="pXb-od-Wb1">
<view key="view" id="lCG-Gt-XMF">
<rect key="frame" x="0.0" y="0.0" width="512" height="408"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" id="QcQ-ex-36R" customClass="GBImageView">
<rect key="frame" x="0.0" y="0.0" width="512" height="384"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="axesIndependently" id="pXc-O8-jg5"/>
<connections>
<action selector="reloadVRAMData:" target="-2" id="jGo-SC-0uA"/>
<outlet property="delegate" destination="-2" id="xoo-Uo-872"/>
</connections>
</imageView>
<popUpButton focusRingType="none" verticalHuggingPriority="750" id="TLv-xS-X5K">
<rect key="frame" x="4" y="388" width="128" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="roundRect" title="None" bezelStyle="roundedRect" alignment="left" controlSize="mini" lineBreakMode="truncatingTail" state="on" borderStyle="border" focusRingType="none" imageScaling="proportionallyDown" inset="2" selectedItem="G8p-CH-PlV" id="1jI-s4-4YY">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="miniSystem"/>
<menu key="menu" id="S6C-6Y-U3e">
<items>
<menuItem title="None" state="on" id="G8p-CH-PlV"/>
<menuItem title="Background Palette 0" id="Vf5-1r-wB0"/>
<menuItem title="Background Palette 1" id="Hnm-oM-jIq"/>
<menuItem title="Background Palette 2" id="IBU-wD-IfI"/>
<menuItem title="Background Palette 3" id="Pje-be-VHT"/>
<menuItem title="Background Palette 4" id="E1K-4g-hvR"/>
<menuItem title="Background Palette 5" id="juQ-bJ-lIt"/>
<menuItem title="Background Palette 6" id="T1z-Jc-wIg"/>
<menuItem title="Background Palette 7" id="oxC-qV-3EG"/>
<menuItem title="Object Palette 0" id="nnZ-qm-fjY"/>
<menuItem title="Object Palette 1" id="B9u-1l-0j9"/>
<menuItem title="Object Palette 2" id="XVN-y3-Wp5"/>
<menuItem title="Object Palette 3" id="bDp-1h-ShO"/>
<menuItem title="Object Palette 4" id="Idr-Th-IVi"/>
<menuItem title="Object Palette 5" id="NRU-gL-bIa"/>
<menuItem title="Object Palette 6" id="HtN-zW-2PB"/>
<menuItem title="Object Palette 7" id="HWA-Tl-Egd"/>
</items>
</menu>
</popUpButtonCell>
<connections>
<action selector="reloadVRAMData:" target="-2" id="Qtf-p4-Rqh"/>
</connections>
</popUpButton>
<button verticalHuggingPriority="750" id="fL6-2S-Rgd">
<rect key="frame" x="452" y="388" width="56" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="roundRect" title="Grid" bezelStyle="roundedRect" alignment="center" controlSize="mini" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="pDn-9a-Se6">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES" changeBackground="YES" changeGray="YES"/>
<font key="font" metaFont="miniSystem"/>
</buttonCell>
<connections>
<action selector="toggleTilesetGrid:" target="-2" id="Nth-BU-nhl"/>
</connections>
</button>
</subviews>
</view>
</tabViewItem>
<tabViewItem label="Tilemap" identifier="2" id="kaY-Wy-Yt1">
<view key="view" id="c6j-cM-dDx">
<rect key="frame" x="0.0" y="0.0" width="512" height="408"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" id="LlK-tV-bjv" customClass="GBImageView">
<rect key="frame" x="0.0" y="0.0" width="512" height="384"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="axesIndependently" id="RvP-El-ILj"/>
<connections>
<action selector="reloadVRAMData:" target="-2" id="bz5-IW-cDd"/>
<outlet property="delegate" destination="-2" id="EAG-Zh-oRi"/>
</connections>
</imageView>
<popUpButton focusRingType="none" verticalHuggingPriority="750" id="loB-0k-Qff">
<rect key="frame" x="4" y="387" width="128" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="roundRect" title="Effective Palettes" bezelStyle="roundedRect" alignment="left" controlSize="mini" lineBreakMode="truncatingTail" state="on" borderStyle="border" focusRingType="none" imageScaling="proportionallyDown" inset="2" selectedItem="oUH-Sa-Ec1" id="Eij-Cp-URa">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="miniSystem"/>
<menu key="menu" id="KYk-85-hF3">
<items>
<menuItem title="None" id="BhZ-lk-ned"/>
<menuItem title="Effective Palettes" state="on" id="oUH-Sa-Ec1"/>
<menuItem title="Background Palette 0" id="q5g-HD-mPF"/>
<menuItem title="Background Palette 1" id="1qY-bS-27N"/>
<menuItem title="Background Palette 2" id="4JV-8v-9q6"/>
<menuItem title="Background Palette 3" id="58P-TR-RjD"/>
<menuItem title="Background Palette 4" id="voq-Pf-ZKh"/>
<menuItem title="Background Palette 5" id="e7w-HT-Q5N"/>
<menuItem title="Background Palette 6" id="Tgx-oy-4YI"/>
<menuItem title="Background Palette 7" id="3lv-Bj-iqf"/>
<menuItem title="Object Palette 0" id="z29-eH-Mq8"/>
<menuItem title="Object Palette 1" id="2Ef-30-IRL"/>
<menuItem title="Object Palette 2" id="Rrs-nT-Zpe"/>
<menuItem title="Object Palette 3" id="C1P-ei-elS"/>
<menuItem title="Object Palette 4" id="DeX-Ey-45n"/>
<menuItem title="Object Palette 5" id="luQ-S6-lpg"/>
<menuItem title="Object Palette 6" id="pWP-64-0lY"/>
<menuItem title="Object Palette 7" id="qGs-73-oac"/>
</items>
</menu>
</popUpButtonCell>
<connections>
<action selector="reloadVRAMData:" target="-2" id="BmB-JE-W8g"/>
</connections>
</popUpButton>
<popUpButton focusRingType="none" verticalHuggingPriority="750" id="YIJ-Qc-SIZ">
<rect key="frame" x="135" y="387" width="96" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="roundRect" title="Effective Tilemap" bezelStyle="roundedRect" alignment="left" controlSize="mini" lineBreakMode="truncatingTail" state="on" borderStyle="border" focusRingType="none" imageScaling="proportionallyDown" inset="2" selectedItem="XRF-Vj-3gs" id="3W1-Db-wDn">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="miniSystem"/>
<menu key="menu" id="3J9-rv-1Q7">
<items>
<menuItem title="Effective Tilemap" state="on" id="XRF-Vj-3gs"/>
<menuItem title="Tilemap at $9800" id="mDI-Ed-ziW"/>
<menuItem title="Tilemap at $9C00" id="7yJ-ih-44c"/>
</items>
</menu>
</popUpButtonCell>
<connections>
<action selector="reloadVRAMData:" target="-2" id="xwp-Ju-p00"/>
</connections>
</popUpButton>
<popUpButton focusRingType="none" verticalHuggingPriority="750" id="k4c-Vg-MBu">
<rect key="frame" x="235" y="387" width="96" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="roundRect" title="Effective Tileset" bezelStyle="roundedRect" alignment="left" controlSize="mini" lineBreakMode="truncatingTail" state="on" borderStyle="border" focusRingType="none" imageScaling="proportionallyDown" inset="2" selectedItem="CRe-dX-rzY" id="h53-sb-Odg">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="miniSystem"/>
<menu key="menu" id="ztO-sk-5BM">
<items>
<menuItem title="Effective Tileset" state="on" id="CRe-dX-rzY"/>
<menuItem title="Tileset at $8800" id="OU7-CN-Spr"/>
<menuItem title="Tileset at $8000" id="5Af-BS-Ljj"/>
</items>
</menu>
</popUpButtonCell>
<connections>
<action selector="reloadVRAMData:" target="-2" id="BfH-bq-6Px"/>
</connections>
</popUpButton>
</subviews>
</view>
</tabViewItem>
<tabViewItem label="Sprites" identifier="" id="a08-eg-Maw">
<view key="view" id="EiO-p0-3xn">
<rect key="frame" x="0.0" y="0.0" width="512" height="408"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<scrollView borderType="none" autohidesScrollers="YES" horizontalLineScroll="20" horizontalPageScroll="10" verticalLineScroll="20" verticalPageScroll="10" usesPredominantAxisScrolling="NO" id="krD-gH-o5I">
<rect key="frame" x="0.0" y="0.0" width="512" height="408"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<clipView key="contentView" drawsBackground="NO" id="3VT-AA-xVT">
<rect key="frame" x="0.0" y="17" width="512" height="391"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView appearanceType="vibrantLight" verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="none" alternatingRowBackgroundColors="YES" columnReordering="NO" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" typeSelect="NO" rowHeight="18" headerView="of1-KC-dXC" id="TOc-XJ-w9w">
<rect key="frame" x="0.0" y="0.0" width="512" height="391"/>
<autoresizingMask key="autoresizingMask"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
<tableColumns>
<tableColumn width="32" minWidth="32" maxWidth="1000" id="hRp-Kh-nWC">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<imageCell key="dataCell" scrollable="YES" lineBreakMode="clipping" refusesFirstResponder="YES" alignment="left" id="Jhk-KW-Hoc" customClass="GBImageCell"/>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
<tableColumn width="28" minWidth="28" maxWidth="1000" id="Vrl-in-npm">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="center" title="X">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" id="czf-Bn-nZN">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
<tableColumn width="28" minWidth="28" maxWidth="1000" id="636-Td-Zcm">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="center" title="Y">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" id="zh6-hI-Ss8">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
<tableColumn width="28" minWidth="28" maxWidth="1000" id="vMj-ya-pGG">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="center" title="Tile">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" id="0s8-eF-rgd">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
<tableColumn width="68" minWidth="40" maxWidth="1000" id="U5B-eh-aer">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="center" title="Tile Addr.">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" id="PtW-wF-c0o">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
<tableColumn width="68" minWidth="40" maxWidth="1000" id="LSg-Sc-Sdr">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="center" title="OAM Addr.">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" id="a7k-83-iPE">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
<tableColumn width="65" minWidth="40" maxWidth="1000" id="S9B-Hc-6Ee">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="center" title="Attributes">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" id="sJa-oU-QZp">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
<tableColumn width="168" minWidth="40" maxWidth="1000" id="8fQ-EC-E5K">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="center">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" selectable="YES" editable="YES" id="YSL-O0-ZwU">
<font key="font" metaFont="miniSystem"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
</tableColumns>
<connections>
<outlet property="dataSource" destination="-2" id="ypl-SU-g5n"/>
<outlet property="delegate" destination="-2" id="HzE-pT-cX5"/>
</connections>
</tableView>
</subviews>
<nil key="backgroundColor"/>
</clipView>
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="n53-qA-NpY">
<rect key="frame" x="0.0" y="392" width="512" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="mqp-NY-g8d">
<rect key="frame" x="224" y="17" width="15" height="102"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<tableHeaderView key="headerView" id="of1-KC-dXC">
<rect key="frame" x="0.0" y="0.0" width="512" height="17"/>
<autoresizingMask key="autoresizingMask"/>
</tableHeaderView>
</scrollView>
</subviews>
</view>
</tabViewItem>
<tabViewItem label="Palettes" identifier="" id="vLb-Nh-UYE">
<view key="view" id="qIg-ci-WTA">
<rect key="frame" x="0.0" y="0.0" width="512" height="408"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<scrollView borderType="none" horizontalLineScroll="20" horizontalPageScroll="10" verticalLineScroll="20" verticalPageScroll="10" hasHorizontalScroller="NO" hasVerticalScroller="NO" usesPredominantAxisScrolling="NO" horizontalScrollElasticity="none" verticalScrollElasticity="none" id="iSs-Ow-wwb">
<rect key="frame" x="0.0" y="0.0" width="512" height="408"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<clipView key="contentView" id="bP9-su-zQw">
<rect key="frame" x="0.0" y="0.0" width="512" height="408"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnReordering="NO" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" typeSelect="NO" rowHeight="18" id="gfC-d3-dmq">
<rect key="frame" x="0.0" y="0.0" width="512" height="20"/>
<autoresizingMask key="autoresizingMask"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<tableViewGridLines key="gridStyleMask" dashed="YES"/>
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
<tableColumns>
<tableColumn width="125" minWidth="40" maxWidth="1000" id="Aje-FQ-fUw">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" refusesFirstResponder="YES" title="Text Cell" id="Ygb-xo-X8v">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
<tableColumn width="93" minWidth="40" maxWidth="1000" id="4EN-0j-lda">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" refusesFirstResponder="YES" title="Text Cell" id="3a2-A4-XQW" customClass="GBColorCell">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
<tableColumn identifier="" width="93" minWidth="10" maxWidth="3.4028234663852886e+38" id="syl-os-nSf">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" refusesFirstResponder="YES" alignment="left" title="Text Cell" id="3ql-bn-AxP" customClass="GBColorCell">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
<tableColumn identifier="" width="93" minWidth="10" maxWidth="3.4028234663852886e+38" id="Qw3-u2-c1s">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" refusesFirstResponder="YES" title="Text Cell" id="9at-mH-PWc" customClass="GBColorCell">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
<tableColumn identifier="" width="93" minWidth="10" maxWidth="3.4028234663852886e+38" id="gTl-gN-qLn">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" refusesFirstResponder="YES" alignment="left" title="Text Cell" id="dY8-Zu-d4t" customClass="GBColorCell">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
</tableColumn>
</tableColumns>
<connections>
<outlet property="dataSource" destination="-2" id="F2f-nK-QTN"/>
<outlet property="delegate" destination="-2" id="7hn-MM-DDD"/>
</connections>
</tableView>
</subviews>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</clipView>
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="OS3-sw-bjv">
<rect key="frame" x="-100" y="-100" width="510" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="4HA-2m-8TZ">
<rect key="frame" x="-100" y="-100" width="15" height="102"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
</subviews>
</view>
</tabViewItem>
</tabViewItems>
</tabView>
</subviews>
</view>
<toolbar key="toolbar" implicitIdentifier="7FF8B5E5-F61B-408C-9634-A5D496FE5E70" autosavesConfiguration="NO" allowsUserCustomization="NO" displayMode="iconOnly" sizeMode="regular" id="SVR-To-n7n">
<allowedToolbarItems>
<toolbarItem implicitItemIdentifier="NSToolbarFlexibleSpaceItem" id="hnI-48-dYt"/>
<toolbarItem implicitItemIdentifier="B9D3CF98-8020-4389-9372-F99361440794" label="" paletteLabel="" id="fmK-Jl-Koj">
<nil key="toolTip"/>
<size key="minSize" width="100" height="25"/>
<size key="maxSize" width="268" height="25"/>
<segmentedControl key="view" verticalHuggingPriority="750" id="Aul-vO-dCK">
<rect key="frame" x="0.0" y="14" width="268" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<segmentedCell key="cell" borderStyle="border" alignment="left" style="texturedSquare" trackingMode="selectOne" id="HhR-ky-5NN">
<font key="font" metaFont="system"/>
<segments>
<segment label="Tileset" selected="YES"/>
<segment label="Tilemap" tag="1"/>
<segment label="Sprites"/>
<segment label="Palettes"/>
</segments>
</segmentedCell>
</segmentedControl>
<connections>
<action selector="vramTabChanged:" target="-2" id="r3e-95-wEJ"/>
</connections>
</toolbarItem>
</allowedToolbarItems>
<defaultToolbarItems>
<toolbarItem reference="hnI-48-dYt"/>
<toolbarItem reference="fmK-Jl-Koj"/>
<toolbarItem reference="hnI-48-dYt"/>
</defaultToolbarItems>
</toolbar>
<contentBorderThickness minY="24"/>
<point key="canvasLocation" x="182" y="760"/>
</window>
</objects>
</document>

5
Cocoa/GBColorCell.h Normal file
View File

@ -0,0 +1,5 @@
#import <Cocoa/Cocoa.h>
@interface GBColorCell : NSTextFieldCell
@end

42
Cocoa/GBColorCell.m Normal file
View File

@ -0,0 +1,42 @@
#import "GBColorCell.h"
static inline double scale_channel(uint8_t x)
{
x &= 0x1f;
return x / 31.0;
}
@implementation GBColorCell
{
NSInteger _integerValue;
}
- (void)setObjectValue:(id)objectValue
{
_integerValue = [objectValue integerValue];
super.objectValue = [NSString stringWithFormat:@"$%04x", (uint16_t)(_integerValue & 0x7FFF)];
}
- (NSInteger)integerValue
{
return _integerValue;
}
- (int)intValue
{
return (int)_integerValue;
}
- (NSColor *) backgroundColor
{
uint16_t color = self.integerValue;
return [NSColor colorWithRed:scale_channel(color) green:scale_channel(color >> 5) blue:scale_channel(color >> 10) alpha:1.0];
}
- (BOOL)drawsBackground
{
return YES;
}
@end

5
Cocoa/GBImageCell.h Normal file
View File

@ -0,0 +1,5 @@
#import <Cocoa/Cocoa.h>
@interface GBImageCell : NSImageCell
@end

10
Cocoa/GBImageCell.m Normal file
View File

@ -0,0 +1,10 @@
#import "GBImageCell.h"
@implementation GBImageCell
- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
{
CGContextRef context = [[NSGraphicsContext currentContext] CGContext];
CGContextSetInterpolationQuality(context, kCGInterpolationNone);
[super drawWithFrame:cellFrame inView:controlView];
}
@end

21
Cocoa/GBImageView.h Normal file
View File

@ -0,0 +1,21 @@
#import <Cocoa/Cocoa.h>
@protocol GBImageViewDelegate;
@interface GBImageViewGridConfiguration : NSObject
@property NSColor *color;
@property NSUInteger size;
- (instancetype) initWithColor: (NSColor *) color size: (NSUInteger) size;
@end
@interface GBImageView : NSImageView
@property (nonatomic) NSArray *horizontalGrids;
@property (nonatomic) NSArray *verticalGrids;
@property (weak) IBOutlet id<GBImageViewDelegate> delegate;
@end
@protocol GBImageViewDelegate <NSObject>
@optional
- (void) mouseDidLeaveImageView: (GBImageView *)view;
- (void) imageView: (GBImageView *)view mouseMovedToX:(NSUInteger) x Y:(NSUInteger) y;
@end

92
Cocoa/GBImageView.m Normal file
View File

@ -0,0 +1,92 @@
#import "GBImageView.h"
@implementation GBImageViewGridConfiguration
- (instancetype)initWithColor:(NSColor *)color size:(NSUInteger)size
{
self = [super init];
self.color = color;
self.size = size;
return self;
}
@end
@implementation GBImageView
{
NSTrackingArea *trackingArea;
}
- (void)drawRect:(NSRect)dirtyRect
{
CGContextRef context = [[NSGraphicsContext currentContext] CGContext];
CGContextSetInterpolationQuality(context, kCGInterpolationNone);
[super drawRect:dirtyRect];
CGFloat y_ratio = self.frame.size.height / self.image.size.height;
CGFloat x_ratio = self.frame.size.width / self.image.size.width;
for (GBImageViewGridConfiguration *conf in self.verticalGrids) {
[conf.color set];
for (CGFloat y = conf.size * y_ratio; y < self.frame.size.height; y += conf.size * y_ratio) {
NSBezierPath *line = [NSBezierPath bezierPath];
[line moveToPoint:NSMakePoint(0, y + 0.5)];
[line lineToPoint:NSMakePoint(self.frame.size.width, y + 0.5)];
[line setLineWidth:1.0];
[line stroke];
}
}
for (GBImageViewGridConfiguration *conf in self.horizontalGrids) {
[conf.color set];
for (CGFloat x = conf.size * x_ratio; x < self.frame.size.width; x += conf.size * x_ratio) {
NSBezierPath *line = [NSBezierPath bezierPath];
[line moveToPoint:NSMakePoint(x + 0.5, 0)];
[line lineToPoint:NSMakePoint(x + 0.5, self.frame.size.height)];
[line setLineWidth:1.0];
[line stroke];
}
}
}
- (void)setHorizontalGrids:(NSArray *)horizontalGrids
{
self->_horizontalGrids = horizontalGrids;
[self setNeedsDisplay];
}
- (void)setVerticalGrids:(NSArray *)verticalGrids
{
self->_verticalGrids = verticalGrids;
[self setNeedsDisplay];
}
- (void)updateTrackingAreas
{
if(trackingArea != nil) {
[self removeTrackingArea:trackingArea];
}
trackingArea = [ [NSTrackingArea alloc] initWithRect:[self bounds]
options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | NSTrackingMouseMoved
owner:self
userInfo:nil];
[self addTrackingArea:trackingArea];
}
- (void)mouseExited:(NSEvent *)theEvent
{
if ([self.delegate respondsToSelector:@selector(mouseDidLeaveImageView:)]) {
[self.delegate mouseDidLeaveImageView:self];
}
}
- (void)mouseMoved:(NSEvent *)theEvent
{
if ([self.delegate respondsToSelector:@selector(imageView:mouseMovedToX:Y:)]) {
NSPoint location = [self convertPoint:theEvent.locationInWindow fromView:nil];
location.x /= self.bounds.size.width;
location.y /= self.bounds.size.height;
location.y = 1 - location.y;
location.x *= self.image.size.width;
location.y *= self.image.size.height;
[self.delegate imageView:self mouseMovedToX:(NSUInteger)location.x Y:(NSUInteger)location.y];
}
}
@end

View File

@ -375,6 +375,12 @@
<action selector="showMemory:" target="-1" id="Ngn-2u-n12"/>
</connections>
</menuItem>
<menuItem title="Show VRAM Viewer" id="EYc-et-cG0">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="showVRAMViewer:" target="-1" id="nhw-4h-Sl4"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>

View File

@ -11,14 +11,12 @@
#include <Windows.h>
#endif
#pragma pack(push, 1)
typedef struct {
typedef struct __attribute__((packed)) {
uint8_t y;
uint8_t x;
uint8_t tile;
uint8_t flags;
} GB_sprite_t;
#pragma pack(pop)
static uint32_t get_pixel(GB_gameboy_t *gb, uint8_t x, uint8_t y)
{
@ -439,3 +437,178 @@ updateSTAT:
gb->io_registers[GB_IO_IF] |= 2;
}
}
void GB_draw_tileset(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette_type, uint8_t palette_index)
{
uint32_t none_palette[4];
uint32_t *palette = NULL;
switch (gb->is_cgb? palette_type : GB_PALETTE_NONE) {
default:
case GB_PALETTE_NONE:
none_palette[0] = gb->rgb_encode_callback(gb, 0xFF, 0xFF, 0xFF);
none_palette[1] = gb->rgb_encode_callback(gb, 0xAA, 0xAA, 0xAA);
none_palette[2] = gb->rgb_encode_callback(gb, 0x55, 0x55, 0x55);
none_palette[3] = gb->rgb_encode_callback(gb, 0, 0, 0 );
palette = none_palette;
break;
case GB_PALETTE_BACKGROUND:
palette = gb->background_palletes_rgb + (4 * (palette_index & 7));
break;
case GB_PALETTE_OAM:
palette = gb->sprite_palletes_rgb + (4 * (palette_index & 7));
break;
}
for (unsigned y = 0; y < 192; y++) {
for (unsigned x = 0; x < 256; x++) {
if (x >= 128 && !gb->is_cgb) {
*(dest++) = gb->background_palletes_rgb[0];
continue;
}
uint16_t tile = (x % 128) / 8 + y / 8 * 16;
uint16_t tile_address = tile * 0x10 + (x >= 128? 0x2000 : 0);
uint8_t pixel = (((gb->vram[tile_address + (y & 7) * 2 ] >> ((~x)&7)) & 1 ) |
((gb->vram[tile_address + (y & 7) * 2 + 1] >> ((~x)&7)) & 1) << 1);
if (!gb->cgb_mode) {
if (palette_type == GB_PALETTE_BACKGROUND) {
pixel = ((gb->io_registers[GB_IO_BGP] >> (pixel << 1)) & 3);
}
else if (!gb->cgb_mode) {
if (palette_type == GB_PALETTE_OAM) {
pixel = ((gb->io_registers[palette_index == 0? GB_IO_OBP0 : GB_IO_OBP1] >> (pixel << 1)) & 3);
}
}
}
*(dest++) = palette[pixel];
}
}
}
void GB_draw_tilemap(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette_type, uint8_t palette_index, GB_map_type_t map_type, GB_tileset_type_t tileset_type)
{
uint32_t none_palette[4];
uint32_t *palette = NULL;
uint16_t map = 0x1800;
switch (gb->is_cgb? palette_type : GB_PALETTE_NONE) {
case GB_PALETTE_NONE:
none_palette[0] = gb->rgb_encode_callback(gb, 0xFF, 0xFF, 0xFF);
none_palette[1] = gb->rgb_encode_callback(gb, 0xAA, 0xAA, 0xAA);
none_palette[2] = gb->rgb_encode_callback(gb, 0x55, 0x55, 0x55);
none_palette[3] = gb->rgb_encode_callback(gb, 0, 0, 0 );
palette = none_palette;
break;
case GB_PALETTE_BACKGROUND:
palette = gb->background_palletes_rgb + (4 * (palette_index & 7));
break;
case GB_PALETTE_OAM:
palette = gb->sprite_palletes_rgb + (4 * (palette_index & 7));
break;
case GB_PALETTE_AUTO:
break;
}
if (map_type == GB_MAP_9C00 || (map_type == GB_MAP_AUTO && gb->io_registers[GB_IO_LCDC] & 0x08)) {
map = 0x1c00;
}
if (tileset_type == GB_TILESET_AUTO) {
tileset_type = (gb->io_registers[GB_IO_LCDC] & 0x10)? GB_TILESET_8800 : GB_TILESET_8000;
}
for (unsigned y = 0; y < 256; y++) {
for (unsigned x = 0; x < 256; x++) {
uint8_t tile = gb->vram[map + x/8 + y/8 * 32];
uint16_t tile_address;
uint8_t attributes = 0;
if (tileset_type == GB_TILESET_8800) {
tile_address = tile * 0x10;
}
else {
tile_address = (int8_t) tile * 0x10 + 0x1000;
}
if (gb->cgb_mode) {
attributes = gb->vram[map + x/8 + y/8 * 32 + 0x2000];
}
if (attributes & 0x8) {
tile_address += 0x2000;
}
uint8_t pixel = (((gb->vram[tile_address + (((attributes & 0x40)? ~y : y) & 7) * 2 ] >> (((attributes & 0x20)? x : ~x)&7)) & 1 ) |
((gb->vram[tile_address + (((attributes & 0x40)? ~y : y) & 7) * 2 + 1] >> (((attributes & 0x20)? x : ~x)&7)) & 1) << 1);
if (!gb->cgb_mode && (palette_type == GB_PALETTE_BACKGROUND || palette_type == GB_PALETTE_AUTO)) {
pixel = ((gb->io_registers[GB_IO_BGP] >> (pixel << 1)) & 3);
}
if (palette) {
*(dest++) = palette[pixel];
}
else {
*(dest++) = gb->background_palletes_rgb[(attributes & 7) * 4 + pixel];
}
}
}
}
uint8_t GB_get_oam_info(GB_gameboy_t *gb, GB_oam_info_t *dest)
{
uint8_t count = 0;
unsigned sprite_height = (gb->io_registers[GB_IO_LCDC] & 4) ? 16:8;
uint8_t oam_to_dest_index[40] = {0,};
for (unsigned y = 0; y < 144; y++) {
GB_sprite_t *sprite = (GB_sprite_t *) &gb->oam;
uint8_t sprites_in_line = 0;
for (uint8_t i = 0; i < 40; i++, sprite++) {
int sprite_y = sprite->y - 16;
bool obscured = false;
// Is sprite not in this line?
if (sprite_y > y || sprite_y + sprite_height <= y) continue;
if (++sprites_in_line == 11) obscured = true;
GB_oam_info_t *info = NULL;
if (!oam_to_dest_index[i]) {
info = dest + count;
oam_to_dest_index[i] = ++count;
info->x = sprite->x;
info->y = sprite->y;
info->tile = sprite_height == 16? sprite->tile & 0xFE : sprite->tile;
info->flags = sprite->flags;
info->obscured_by_line_limit = false;
info->oam_addr = 0xFE00 + i * sizeof(*sprite);
}
else {
info = dest + oam_to_dest_index[i] - 1;
}
info->obscured_by_line_limit |= obscured;
}
}
for (unsigned i = 0; i < count; i++) {
uint16_t vram_address = dest[i].tile * 0x10;
uint8_t flags = dest[i].flags;
uint8_t palette = gb->cgb_mode? (flags & 7) : ((flags & 0x10)? 1 : 0);
for (unsigned y = 0; y < sprite_height; y++) {
for (unsigned x = 0; x < 8; x++) {
uint8_t color = (((gb->vram[vram_address ] >> ((~x)&7)) & 1 ) |
((gb->vram[vram_address + 1] >> ((~x)&7)) & 1) << 1 );
if (!gb->cgb_mode) {
color = (gb->io_registers[palette? GB_IO_OBP1:GB_IO_OBP0] >> (color << 1)) & 3;
}
dest[i].image[((flags & 0x20)?7-x:x) + ((flags & 0x40)?sprite_height - 1 -y:y) * 8] = gb->sprite_palletes_rgb[palette * 4 + color];
}
vram_address += 2;
}
}
return count;
}

View File

@ -4,4 +4,35 @@
#include "gb.h"
void GB_display_run(GB_gameboy_t *gb);
void GB_palette_changed(GB_gameboy_t *gb, bool background_palette, uint8_t index);
typedef enum {
GB_PALETTE_NONE,
GB_PALETTE_BACKGROUND,
GB_PALETTE_OAM,
GB_PALETTE_AUTO,
} GB_palette_type_t;
typedef enum {
GB_MAP_AUTO,
GB_MAP_9800,
GB_MAP_9C00,
} GB_map_type_t;
typedef enum {
GB_TILESET_AUTO,
GB_TILESET_8800,
GB_TILESET_8000,
} GB_tileset_type_t;
typedef struct {
uint32_t image[128];
uint8_t x, y, tile, flags;
uint16_t oam_addr;
bool obscured_by_line_limit;
} GB_oam_info_t;
void GB_draw_tileset(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette_type, uint8_t palette_index);
void GB_draw_tilemap(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette_type, uint8_t palette_index, GB_map_type_t map_type, GB_tileset_type_t tileset_type);
uint8_t GB_get_oam_info(GB_gameboy_t *gb, GB_oam_info_t *dest);
#endif /* display_h */

View File

@ -95,7 +95,9 @@ void GB_init(GB_gameboy_t *gb)
gb->magic = (uintptr_t)'SAME';
gb->version = GB_STRUCT_VERSION;
gb->ram = malloc(gb->ram_size = 0x2000);
memset(gb->ram, 0, gb->ram_size);
gb->vram = malloc(gb->vram_size = 0x2000);
memset(gb->vram, 0, gb->vram_size);
gb->mbc_rom_bank = 1;
gb->last_rtc_second = time(NULL);
@ -121,7 +123,9 @@ void GB_init_cgb(GB_gameboy_t *gb)
gb->magic = (uintptr_t)'SAME';
gb->version = GB_STRUCT_VERSION;
gb->ram = malloc(gb->ram_size = 0x2000 * 8);
memset(gb->ram, 0, gb->ram_size);
gb->vram = malloc(gb->vram_size = 0x2000 * 2);
memset(gb->vram, 0, gb->vram_size);
gb->is_cgb = true;
gb->cgb_mode = true;

View File

@ -19,11 +19,7 @@
/* Used for the anchor range and location */
#define NO_SELECTION ULLONG_MAX
#if ! NDEBUG
#define VALIDATE_SELECTION() [self _ensureSelectionIsValid]
#else
#define VALIDATE_SELECTION() do { } while (0)
#endif
#define BENCHMARK_BYTEARRAYS 0
@ -456,7 +452,6 @@ static inline Class preferredByteArrayClass(void) {
return [HFRangeWrapper organizeAndMergeRanges:result];
}
#if ! NDEBUG
- (void)_ensureSelectionIsValid {
HFASSERT(selectedContentsRanges != nil);
HFASSERT([selectedContentsRanges count] > 0);
@ -464,11 +459,13 @@ static inline Class preferredByteArrayClass(void) {
FOREACH(HFRangeWrapper*, wrapper, selectedContentsRanges) {
EXPECT_CLASS(wrapper, HFRangeWrapper);
HFRange range = [wrapper HFRange];
HFASSERT(HFRangeIsSubrangeOfRange(range, HFRangeMake(0, [self contentsLength])));
if (!HFRangeIsSubrangeOfRange(range, HFRangeMake(0, [self contentsLength]))){
[self setSelectedContentsRanges:@[[HFRangeWrapper withRange:HFRangeMake(0, 0)]]];
return;
}
if (onlyOneWrapper == NO) HFASSERT(range.length > 0); /* If we have more than one wrapper, then none of them should be zero length */
}
}
#endif
- (void)_setSingleSelectedContentsRange:(HFRange)newSelection {
HFASSERT(HFRangeIsSubrangeOfRange(newSelection, HFRangeMake(0, [self contentsLength])));

View File

@ -48,7 +48,7 @@ endif
ifeq ($(PLATFORM),Darwin)
CFLAGS += -F/Library/Frameworks
OCFLAGS += -x objective-c -fobjc-arc -Wno-deprecated-declarations -isysroot $(shell xcode-select -p)/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk -mmacosx-version-min=10.9
LDFLAGS += -framework AppKit -framework Carbon
LDFLAGS += -framework AppKit -framework Carbon -framework QuartzCore
SDL_LDFLAGS := -framework SDL
endif