Merge remote-tracking branch 'origin/master' into wasm
This commit is contained in:
commit
405d85343f
@ -329,101 +329,103 @@ FirstChecksumWithDuplicate:
|
||||
ChecksumsEnd:
|
||||
|
||||
PalettePerChecksum:
|
||||
; | $80 means game requires DMG boot tilemap
|
||||
db 0 ; Default Palette
|
||||
db 4 ; ALLEY WAY
|
||||
db 5 ; YAKUMAN
|
||||
db 35 ; BASEBALL, (Game and Watch 2)
|
||||
db 34 ; TENNIS
|
||||
db 3 ; TETRIS
|
||||
db 31 ; QIX
|
||||
db 15 ; DR.MARIO
|
||||
db 10 ; RADARMISSION
|
||||
db 5 ; F1RACE
|
||||
db 19 ; YOSSY NO TAMAGO
|
||||
db 36 ;
|
||||
db 7 | $80 ; X
|
||||
db 37 ; MARIOLAND2
|
||||
db 30 ; YOSSY NO COOKIE
|
||||
db 44 ; ZELDA
|
||||
db 21 ;
|
||||
db 32 ;
|
||||
db 31 ; TETRIS FLASH
|
||||
db 20 ; DONKEY KONG
|
||||
db 5 ; MARIO'S PICROSS
|
||||
db 33 ;
|
||||
db 13 ; POKEMON RED, (GAMEBOYCAMERA G)
|
||||
db 14 ; POKEMON GREEN
|
||||
db 5 ; PICROSS 2
|
||||
db 29 ; YOSSY NO PANEPON
|
||||
db 5 ; KIRAKIRA KIDS
|
||||
db 18 ; GAMEBOY GALLERY
|
||||
db 9 ; POCKETCAMERA
|
||||
db 3 ;
|
||||
db 2 ; BALLOON KID
|
||||
db 26 ; KINGOFTHEZOO
|
||||
db 25 ; DMG FOOTBALL
|
||||
db 25 ; WORLD CUP
|
||||
db 41 ; OTHELLO
|
||||
db 42 ; SUPER RC PRO-AM
|
||||
db 26 ; DYNABLASTER
|
||||
db 45 ; BOY AND BLOB GB2
|
||||
db 42 ; MEGAMAN
|
||||
db 45 ; STAR WARS-NOA
|
||||
db 36 ;
|
||||
db 38 ; WAVERACE
|
||||
db 26 ;
|
||||
db 42 ; LOLO2
|
||||
db 30 ; YOSHI'S COOKIE
|
||||
db 41 ; MYSTIC QUEST
|
||||
db 34 ;
|
||||
db 34 ; TOPRANKINGTENNIS
|
||||
db 5 ; MANSELL
|
||||
db 42 ; MEGAMAN3
|
||||
db 6 ; SPACE INVADERS
|
||||
db 5 ; GAME&WATCH
|
||||
db 33 ; DONKEYKONGLAND95
|
||||
db 25 ; ASTEROIDS/MISCMD
|
||||
db 42 ; STREET FIGHTER 2
|
||||
db 42 ; DEFENDER/JOUST
|
||||
db 40 ; KILLERINSTINCT95
|
||||
db 2 ; TETRIS BLAST
|
||||
db 16 ; PINOCCHIO
|
||||
db 25 ;
|
||||
db 42 ; BA.TOSHINDEN
|
||||
db 42 ; NETTOU KOF 95
|
||||
db 5 ;
|
||||
db 0 ; TETRIS PLUS
|
||||
db 39 ; DONKEYKONGLAND 3
|
||||
db 36 ;
|
||||
db 22 ; SUPER MARIOLAND
|
||||
db 25 ; GOLF
|
||||
db 6 ; SOLARSTRIKER
|
||||
db 32 ; GBWARS
|
||||
db 12 ; KAERUNOTAMENI
|
||||
db 36 ;
|
||||
db 11 ; POKEMON BLUE
|
||||
db 39 ; DONKEYKONGLAND
|
||||
db 18 ; GAMEBOY GALLERY2
|
||||
db 39 ; DONKEYKONGLAND 2
|
||||
db 24 ; KID ICARUS
|
||||
db 31 ; TETRIS2
|
||||
db 50 ;
|
||||
db 17 ; MOGURANYA
|
||||
db 46 ;
|
||||
db 6 ; GALAGA&GALAXIAN
|
||||
db 27 ; BT2RAGNAROKWORLD
|
||||
db 0 ; KEN GRIFFEY JR
|
||||
db 47 ;
|
||||
db 41 ; MAGNETIC SOCCER
|
||||
db 41 ; VEGAS STAKES
|
||||
db 0 ;
|
||||
db 0 ; MILLI/CENTI/PEDE
|
||||
db 19 ; MARIO & YOSHI
|
||||
db 34 ; SOCCER
|
||||
db 23 ; POKEBOM
|
||||
db 18 ; G&W GALLERY
|
||||
db 29 ; TETRIS ATTACK
|
||||
palette_index: MACRO ; palette, flags
|
||||
db ((\1) * 3) | (\2) ; | $80 means game requires DMG boot tilemap
|
||||
ENDM
|
||||
palette_index 0, 0 ; Default Palette
|
||||
palette_index 4, 0 ; ALLEY WAY
|
||||
palette_index 5, 0 ; YAKUMAN
|
||||
palette_index 35, 0 ; BASEBALL, (Game and Watch 2)
|
||||
palette_index 34, 0 ; TENNIS
|
||||
palette_index 3, 0 ; TETRIS
|
||||
palette_index 31, 0 ; QIX
|
||||
palette_index 15, 0 ; DR.MARIO
|
||||
palette_index 10, 0 ; RADARMISSION
|
||||
palette_index 5, 0 ; F1RACE
|
||||
palette_index 19, 0 ; YOSSY NO TAMAGO
|
||||
palette_index 36, 0 ;
|
||||
palette_index 7, $80 ; X
|
||||
palette_index 37, 0 ; MARIOLAND2
|
||||
palette_index 30, 0 ; YOSSY NO COOKIE
|
||||
palette_index 44, 0 ; ZELDA
|
||||
palette_index 21, 0 ;
|
||||
palette_index 32, 0 ;
|
||||
palette_index 31, 0 ; TETRIS FLASH
|
||||
palette_index 20, 0 ; DONKEY KONG
|
||||
palette_index 5, 0 ; MARIO'S PICROSS
|
||||
palette_index 33, 0 ;
|
||||
palette_index 13, 0 ; POKEMON RED, (GAMEBOYCAMERA G)
|
||||
palette_index 14, 0 ; POKEMON GREEN
|
||||
palette_index 5, 0 ; PICROSS 2
|
||||
palette_index 29, 0 ; YOSSY NO PANEPON
|
||||
palette_index 5, 0 ; KIRAKIRA KIDS
|
||||
palette_index 18, 0 ; GAMEBOY GALLERY
|
||||
palette_index 9, 0 ; POCKETCAMERA
|
||||
palette_index 3, 0 ;
|
||||
palette_index 2, 0 ; BALLOON KID
|
||||
palette_index 26, 0 ; KINGOFTHEZOO
|
||||
palette_index 25, 0 ; DMG FOOTBALL
|
||||
palette_index 25, 0 ; WORLD CUP
|
||||
palette_index 41, 0 ; OTHELLO
|
||||
palette_index 42, 0 ; SUPER RC PRO-AM
|
||||
palette_index 26, 0 ; DYNABLASTER
|
||||
palette_index 45, 0 ; BOY AND BLOB GB2
|
||||
palette_index 42, 0 ; MEGAMAN
|
||||
palette_index 45, 0 ; STAR WARS-NOA
|
||||
palette_index 36, 0 ;
|
||||
palette_index 38, 0 ; WAVERACE
|
||||
palette_index 26, 0 ;
|
||||
palette_index 42, 0 ; LOLO2
|
||||
palette_index 30, 0 ; YOSHI'S COOKIE
|
||||
palette_index 41, 0 ; MYSTIC QUEST
|
||||
palette_index 34, 0 ;
|
||||
palette_index 34, 0 ; TOPRANKINGTENNIS
|
||||
palette_index 5, 0 ; MANSELL
|
||||
palette_index 42, 0 ; MEGAMAN3
|
||||
palette_index 6, 0 ; SPACE INVADERS
|
||||
palette_index 5, 0 ; GAME&WATCH
|
||||
palette_index 33, 0 ; DONKEYKONGLAND95
|
||||
palette_index 25, 0 ; ASTEROIDS/MISCMD
|
||||
palette_index 42, 0 ; STREET FIGHTER 2
|
||||
palette_index 42, 0 ; DEFENDER/JOUST
|
||||
palette_index 40, 0 ; KILLERINSTINCT95
|
||||
palette_index 2, 0 ; TETRIS BLAST
|
||||
palette_index 16, 0 ; PINOCCHIO
|
||||
palette_index 25, 0 ;
|
||||
palette_index 42, 0 ; BA.TOSHINDEN
|
||||
palette_index 42, 0 ; NETTOU KOF 95
|
||||
palette_index 5, 0 ;
|
||||
palette_index 0, 0 ; TETRIS PLUS
|
||||
palette_index 39, 0 ; DONKEYKONGLAND 3
|
||||
palette_index 36, 0 ;
|
||||
palette_index 22, 0 ; SUPER MARIOLAND
|
||||
palette_index 25, 0 ; GOLF
|
||||
palette_index 6, 0 ; SOLARSTRIKER
|
||||
palette_index 32, 0 ; GBWARS
|
||||
palette_index 12, 0 ; KAERUNOTAMENI
|
||||
palette_index 36, 0 ;
|
||||
palette_index 11, 0 ; POKEMON BLUE
|
||||
palette_index 39, 0 ; DONKEYKONGLAND
|
||||
palette_index 18, 0 ; GAMEBOY GALLERY2
|
||||
palette_index 39, 0 ; DONKEYKONGLAND 2
|
||||
palette_index 24, 0 ; KID ICARUS
|
||||
palette_index 31, 0 ; TETRIS2
|
||||
palette_index 50, 0 ;
|
||||
palette_index 17, 0 ; MOGURANYA
|
||||
palette_index 46, 0 ;
|
||||
palette_index 6, 0 ; GALAGA&GALAXIAN
|
||||
palette_index 27, 0 ; BT2RAGNAROKWORLD
|
||||
palette_index 0, 0 ; KEN GRIFFEY JR
|
||||
palette_index 47, 0 ;
|
||||
palette_index 41, 0 ; MAGNETIC SOCCER
|
||||
palette_index 41, 0 ; VEGAS STAKES
|
||||
palette_index 0, 0 ;
|
||||
palette_index 0, 0 ; MILLI/CENTI/PEDE
|
||||
palette_index 19, 0 ; MARIO & YOSHI
|
||||
palette_index 34, 0 ; SOCCER
|
||||
palette_index 23, 0 ; POKEBOM
|
||||
palette_index 18, 0 ; G&W GALLERY
|
||||
palette_index 29, 0 ; TETRIS ATTACK
|
||||
|
||||
Dups4thLetterArray:
|
||||
db "BEFAARBEKEK R-URAR INAILICE R"
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <err.h>
|
||||
|
||||
void opts(uint8_t byte, uint8_t *options)
|
||||
{
|
||||
@ -13,6 +14,17 @@ void opts(uint8_t byte, uint8_t *options)
|
||||
*(options++) = byte & (byte >> 1);
|
||||
}
|
||||
|
||||
void write_all(int fd, const void *buf, size_t count) {
|
||||
while (count) {
|
||||
ssize_t written = write(fd, buf, count);
|
||||
if (written < 0) {
|
||||
err(1, "write");
|
||||
}
|
||||
count -= written;
|
||||
buf += written;
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
static uint8_t source[0x4000];
|
||||
@ -76,15 +88,15 @@ int main()
|
||||
if (bits >= 8) {
|
||||
uint8_t outctl = control >> (bits - 8);
|
||||
assert(outctl != 1);
|
||||
write(STDOUT_FILENO, &outctl, 1);
|
||||
write(STDOUT_FILENO, literals, literals_size);
|
||||
write_all(STDOUT_FILENO, &outctl, 1);
|
||||
write_all(STDOUT_FILENO, literals, literals_size);
|
||||
bits -= 8;
|
||||
control &= (1 << bits) - 1;
|
||||
literals_size = 0;
|
||||
}
|
||||
}
|
||||
uint8_t end_byte = 1;
|
||||
write(STDOUT_FILENO, &end_byte, 1);
|
||||
write_all(STDOUT_FILENO, &end_byte, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -51,7 +51,9 @@
|
||||
JOYHatsEmulateButtonsKey: @YES,
|
||||
}];
|
||||
|
||||
[NSUserNotificationCenter defaultUserNotificationCenter].delegate = self;
|
||||
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"GBNotificationsUsed"]) {
|
||||
[NSUserNotificationCenter defaultUserNotificationCenter].delegate = self;
|
||||
}
|
||||
}
|
||||
|
||||
- (IBAction)toggleDeveloperMode:(id)sender
|
||||
|
30
Cocoa/BigSurToolbar.h
Normal file
30
Cocoa/BigSurToolbar.h
Normal file
@ -0,0 +1,30 @@
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#ifndef BigSurToolbar_h
|
||||
#define BigSurToolbar_h
|
||||
|
||||
/* Backport the toolbarStyle property to allow compilation with older SDKs*/
|
||||
#ifndef __MAC_10_16
|
||||
typedef NS_ENUM(NSInteger, NSWindowToolbarStyle) {
|
||||
// The default value. The style will be determined by the window's given configuration
|
||||
NSWindowToolbarStyleAutomatic,
|
||||
// The toolbar will appear below the window title
|
||||
NSWindowToolbarStyleExpanded,
|
||||
// The toolbar will appear below the window title and the items in the toolbar will attempt to have equal widths when possible
|
||||
NSWindowToolbarStylePreference,
|
||||
// The window title will appear inline with the toolbar when visible
|
||||
NSWindowToolbarStyleUnified,
|
||||
// Same as NSWindowToolbarStyleUnified, but with reduced margins in the toolbar allowing more focus to be on the contents of the window
|
||||
NSWindowToolbarStyleUnifiedCompact
|
||||
} API_AVAILABLE(macos(11.0));
|
||||
|
||||
@interface NSWindow (toolbarStyle)
|
||||
@property NSWindowToolbarStyle toolbarStyle API_AVAILABLE(macos(11.0));
|
||||
@end
|
||||
|
||||
@interface NSImage (SFSymbols)
|
||||
+ (instancetype)imageWithSystemSymbolName:(NSString *)symbolName accessibilityDescription:(NSString *)description API_AVAILABLE(macos(11.0));
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
@ -30,7 +30,6 @@
|
||||
@property (strong) IBOutlet NSTableView *spritesTableView;
|
||||
@property (strong) IBOutlet NSPanel *printerFeedWindow;
|
||||
@property (strong) IBOutlet NSImageView *feedImageView;
|
||||
@property (strong) IBOutlet NSButton *feedSaveButton;
|
||||
@property (strong) IBOutlet NSTextView *debuggerSideViewInput;
|
||||
@property (strong) IBOutlet NSTextView *debuggerSideView;
|
||||
@property (strong) IBOutlet GBSplitView *debuggerSplitView;
|
||||
|
@ -8,6 +8,8 @@
|
||||
#include "GBMemoryByteArray.h"
|
||||
#include "GBWarningPopover.h"
|
||||
#include "GBCheatWindowController.h"
|
||||
#include "GBTerminalTextFieldCell.h"
|
||||
#include "BigSurToolbar.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!!! */
|
||||
@ -45,7 +47,7 @@ enum model {
|
||||
bool oamUpdating;
|
||||
|
||||
NSMutableData *currentPrinterImageData;
|
||||
enum {GBAccessoryNone, GBAccessoryPrinter} accessory;
|
||||
enum {GBAccessoryNone, GBAccessoryPrinter, GBAccessoryWorkboy} accessory;
|
||||
|
||||
bool rom_warning_issued;
|
||||
|
||||
@ -136,6 +138,16 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
|
||||
[self printImage:image height:height topMargin:top_margin bottomMargin:bottom_margin exposure:exposure];
|
||||
}
|
||||
|
||||
static void setWorkboyTime(GB_gameboy_t *gb, time_t t)
|
||||
{
|
||||
[[NSUserDefaults standardUserDefaults] setInteger:time(NULL) - t forKey:@"GBWorkboyTimeOffset"];
|
||||
}
|
||||
|
||||
static time_t getWorkboyTime(GB_gameboy_t *gb)
|
||||
{
|
||||
return time(NULL) - [[NSUserDefaults standardUserDefaults] integerForKey:@"GBWorkboyTimeOffset"];
|
||||
}
|
||||
|
||||
static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
||||
{
|
||||
Document *self = (__bridge Document *)GB_get_user_data(gb);
|
||||
@ -403,6 +415,7 @@ static void rumbleCallback(GB_gameboy_t *gb, double amp)
|
||||
unsigned time_to_alarm = GB_time_to_alarm(&gb);
|
||||
|
||||
if (time_to_alarm) {
|
||||
[NSUserNotificationCenter defaultUserNotificationCenter].delegate = (id)[NSApp delegate];
|
||||
NSUserNotification *notification = [[NSUserNotification alloc] init];
|
||||
NSString *friendlyName = [[self.fileName lastPathComponent] stringByDeletingPathExtension];
|
||||
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"\\([^)]+\\)|\\[[^\\]]+\\]" options:0 error:nil];
|
||||
@ -546,6 +559,7 @@ static void rumbleCallback(GB_gameboy_t *gb, double amp)
|
||||
self.debuggerSideViewInput.textColor = [NSColor whiteColor];
|
||||
self.debuggerSideViewInput.defaultParagraphStyle = paragraph_style;
|
||||
[self.debuggerSideViewInput setString:@"registers\nbacktrace\n"];
|
||||
((GBTerminalTextFieldCell *)self.consoleInput.cell).gb = &gb;
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(updateSideView)
|
||||
name:NSTextDidChangeNotification
|
||||
@ -563,16 +577,21 @@ static void rumbleCallback(GB_gameboy_t *gb, double amp)
|
||||
self.vramStatusLabel.cell.backgroundStyle = NSBackgroundStyleRaised;
|
||||
|
||||
|
||||
[self.feedSaveButton removeFromSuperview];
|
||||
|
||||
self.consoleWindow.title = [NSString stringWithFormat:@"Debug Console – %@", [[self.fileURL path] lastPathComponent]];
|
||||
self.debuggerSplitView.dividerColor = [NSColor clearColor];
|
||||
|
||||
/* contentView.superview.subviews.lastObject is the titlebar view */
|
||||
NSView *titleView = self.printerFeedWindow.contentView.superview.subviews.lastObject;
|
||||
[titleView addSubview: self.feedSaveButton];
|
||||
self.feedSaveButton.frame = (NSRect){{268, 2}, {48, 17}};
|
||||
|
||||
if (@available(macOS 11.0, *)) {
|
||||
self.memoryWindow.toolbarStyle = NSWindowToolbarStyleExpanded;
|
||||
self.printerFeedWindow.toolbarStyle = NSWindowToolbarStyleUnifiedCompact;
|
||||
[self.printerFeedWindow.toolbar removeItemAtIndex:1];
|
||||
self.printerFeedWindow.toolbar.items.firstObject.image =
|
||||
[NSImage imageWithSystemSymbolName:@"square.and.arrow.down"
|
||||
accessibilityDescription:@"Save"];
|
||||
self.printerFeedWindow.toolbar.items.lastObject.image =
|
||||
[NSImage imageWithSystemSymbolName:@"printer"
|
||||
accessibilityDescription:@"Print"];
|
||||
}
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(updateHighpassFilter)
|
||||
name:@"GBHighpassFilterChanged"
|
||||
@ -645,7 +664,6 @@ static void rumbleCallback(GB_gameboy_t *gb, double amp)
|
||||
{
|
||||
hex_controller = [[HFController alloc] init];
|
||||
[hex_controller setBytesPerColumn:1];
|
||||
[hex_controller setFont:[NSFont userFixedPitchFontOfSize:12]];
|
||||
[hex_controller setEditMode:HFOverwriteMode];
|
||||
|
||||
[hex_controller setByteArray:[[GBMemoryByteArray alloc] initWithDocument:self]];
|
||||
@ -783,6 +801,9 @@ static void rumbleCallback(GB_gameboy_t *gb, double amp)
|
||||
else if ([anItem action] == @selector(connectPrinter:)) {
|
||||
[(NSMenuItem*)anItem setState:accessory == GBAccessoryPrinter];
|
||||
}
|
||||
else if ([anItem action] == @selector(connectWorkboy:)) {
|
||||
[(NSMenuItem*)anItem setState:accessory == GBAccessoryWorkboy];
|
||||
}
|
||||
else if ([anItem action] == @selector(toggleCheats:)) {
|
||||
[(NSMenuItem*)anItem setState:GB_cheats_enabled(&gb)];
|
||||
}
|
||||
@ -1008,6 +1029,9 @@ static void rumbleCallback(GB_gameboy_t *gb, double amp)
|
||||
[debugger_input_queue removeObjectAtIndex:0];
|
||||
}
|
||||
[has_debugger_input unlockWithCondition:[debugger_input_queue count] != 0];
|
||||
if ((id)input == [NSNull null]) {
|
||||
return NULL;
|
||||
}
|
||||
return input? strdup([input UTF8String]): NULL;
|
||||
}
|
||||
|
||||
@ -1632,13 +1656,24 @@ static void rumbleCallback(GB_gameboy_t *gb, double amp)
|
||||
scale:2.0];
|
||||
NSRect frame = self.printerFeedWindow.frame;
|
||||
frame.size = self.feedImageView.image.size;
|
||||
[self.printerFeedWindow setContentMaxSize:frame.size];
|
||||
frame.size.height += self.printerFeedWindow.frame.size.height - self.printerFeedWindow.contentView.frame.size.height;
|
||||
[self.printerFeedWindow setMaxSize:frame.size];
|
||||
[self.printerFeedWindow setFrame:frame display:NO animate: self.printerFeedWindow.isVisible];
|
||||
[self.printerFeedWindow orderFront:NULL];
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
- (void)printDocument:(id)sender
|
||||
{
|
||||
if (self.feedImageView.image.size.height == 0) {
|
||||
NSBeep(); return;
|
||||
}
|
||||
NSImageView *view = [[NSImageView alloc] initWithFrame:(NSRect){{0,0}, self.feedImageView.image.size}];
|
||||
view.image = self.feedImageView.image;
|
||||
[[NSPrintOperation printOperationWithView:view] runOperationModalForWindow:self.printerFeedWindow delegate:nil didRunSelector:NULL contextInfo:NULL];
|
||||
}
|
||||
|
||||
- (IBAction)savePrinterFeed:(id)sender
|
||||
{
|
||||
bool shouldResume = running;
|
||||
@ -1674,11 +1709,19 @@ static void rumbleCallback(GB_gameboy_t *gb, double amp)
|
||||
- (IBAction)connectPrinter:(id)sender
|
||||
{
|
||||
[self performAtomicBlock:^{
|
||||
accessory = GBAccessoryPrinter;
|
||||
accessory = GBAccessoryPrinter;
|
||||
GB_connect_printer(&gb, printImage);
|
||||
}];
|
||||
}
|
||||
|
||||
- (IBAction)connectWorkboy:(id)sender
|
||||
{
|
||||
[self performAtomicBlock:^{
|
||||
accessory = GBAccessoryWorkboy;
|
||||
GB_connect_workboy(&gb, setWorkboyTime, getWorkboyTime);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void) updateHighpassFilter
|
||||
{
|
||||
if (GB_is_inited(&gb)) {
|
||||
|
@ -19,7 +19,6 @@
|
||||
<outlet property="debuggerSplitView" destination="pUc-ZN-vl5" id="0sG-0D-cID"/>
|
||||
<outlet property="debuggerVerticalLine" destination="7bR-gM-1At" id="rfy-7Z-388"/>
|
||||
<outlet property="feedImageView" destination="Ar0-nN-eop" id="wHa-St-o4G"/>
|
||||
<outlet property="feedSaveButton" destination="RLc-0I-sYZ" id="Yy9-dG-xXY"/>
|
||||
<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"/>
|
||||
@ -116,7 +115,7 @@
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<clipView key="contentView" ambiguous="YES" drawsBackground="NO" copiesOnScroll="NO" id="EQe-Ad-L7S">
|
||||
<rect key="frame" x="0.0" y="0.0" width="591" height="376"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textView ambiguous="YES" editable="NO" drawsBackground="NO" importsGraphics="NO" richText="NO" verticallyResizable="YES" baseWritingDirection="leftToRight" findStyle="bar" allowsNonContiguousLayout="YES" id="doS-dM-hnl">
|
||||
<rect key="frame" x="0.0" y="0.0" width="591" height="376"/>
|
||||
@ -153,7 +152,7 @@
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<clipView key="contentView" ambiguous="YES" drawsBackground="NO" copiesOnScroll="NO" id="YHx-TM-zIC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="329" height="47"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textView ambiguous="YES" drawsBackground="NO" importsGraphics="NO" richText="NO" verticallyResizable="YES" allowsCharacterPickerTouchBarItem="NO" allowsUndo="YES" allowsNonContiguousLayout="YES" textCompletion="NO" spellingCorrection="YES" id="w0g-eK-jM4">
|
||||
<rect key="frame" x="0.0" y="0.0" width="329" height="47"/>
|
||||
@ -187,7 +186,7 @@
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<clipView key="contentView" ambiguous="YES" drawsBackground="NO" copiesOnScroll="NO" id="Cs9-3x-ATg">
|
||||
<rect key="frame" x="0.0" y="0.0" width="329" height="328"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textView ambiguous="YES" editable="NO" drawsBackground="NO" importsGraphics="NO" richText="NO" verticallyResizable="YES" baseWritingDirection="leftToRight" findStyle="bar" allowsNonContiguousLayout="YES" spellingCorrection="YES" id="JgV-7E-iwp">
|
||||
<rect key="frame" x="0.0" y="0.0" width="329" height="328"/>
|
||||
@ -244,9 +243,9 @@
|
||||
<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"/>
|
||||
<size key="maxSize" width="130" height="25"/>
|
||||
<popUpButton key="view" verticalHuggingPriority="750" id="vfJ-vu-gqJ">
|
||||
<rect key="frame" x="0.0" y="14" width="100" height="25"/>
|
||||
<rect key="frame" x="0.0" y="14" width="128" 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"/>
|
||||
@ -507,7 +506,7 @@
|
||||
<rect key="frame" x="0.0" y="0.0" width="512" height="408"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<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">
|
||||
<tableView 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"/>
|
||||
@ -786,9 +785,10 @@
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" resizable="YES"/>
|
||||
<rect key="contentRect" x="0.0" y="0.0" width="320" height="288"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
|
||||
<value key="minSize" type="size" width="320" height="16"/>
|
||||
<view key="contentView" id="RRS-aa-bPT">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="288"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Ar0-nN-eop" customClass="GBImageView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="288"/>
|
||||
@ -797,20 +797,25 @@
|
||||
</imageView>
|
||||
</subviews>
|
||||
</view>
|
||||
<toolbar key="toolbar" implicitIdentifier="1FF86A2B-6637-4EE6-A25A-7298D79AE84E" autosavesConfiguration="NO" allowsUserCustomization="NO" displayMode="iconAndLabel" sizeMode="regular" id="gH3-SH-7il">
|
||||
<allowedToolbarItems>
|
||||
<toolbarItem implicitItemIdentifier="15EB8D49-8C6E-42F2-9F7F-F7D7A0BBDAAF" label="Save" paletteLabel="Save" tag="-1" image="NSFolder" id="CBz-1N-o0Q">
|
||||
<connections>
|
||||
<action selector="savePrinterFeed:" target="-2" id="Dm3-h0-ch4"/>
|
||||
</connections>
|
||||
</toolbarItem>
|
||||
<toolbarItem implicitItemIdentifier="NSToolbarPrintItem" explicitItemIdentifier="Print" id="mtd-zS-DXa"/>
|
||||
<toolbarItem implicitItemIdentifier="NSToolbarSpaceItem" id="AoG-LH-J4b"/>
|
||||
<toolbarItem implicitItemIdentifier="NSToolbarFlexibleSpaceItem" id="Q0x-n5-Q2Y"/>
|
||||
</allowedToolbarItems>
|
||||
<defaultToolbarItems>
|
||||
<toolbarItem reference="CBz-1N-o0Q"/>
|
||||
<toolbarItem reference="Q0x-n5-Q2Y"/>
|
||||
<toolbarItem reference="mtd-zS-DXa"/>
|
||||
</defaultToolbarItems>
|
||||
</toolbar>
|
||||
<point key="canvasLocation" x="-159" y="356"/>
|
||||
</window>
|
||||
<button verticalHuggingPriority="750" id="RLc-0I-sYZ">
|
||||
<rect key="frame" x="0.5" y="0.0" width="48" height="25"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="roundTextured" title="Save" bezelStyle="texturedRounded" alignment="center" controlSize="mini" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="shw-MJ-B3T">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="miniSystem"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="savePrinterFeed:" target="-2" id="Y3g-fU-2te"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="-507" y="397"/>
|
||||
</button>
|
||||
<window title="Cheats" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="4Yb-Np-JrF" customClass="NSPanel">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
|
||||
<rect key="contentRect" x="0.0" y="0.0" width="692" height="272"/>
|
||||
@ -896,7 +901,7 @@
|
||||
</connections>
|
||||
</textField>
|
||||
<button verticalHuggingPriority="750" id="C3V-Ep-bMj">
|
||||
<rect key="frame" x="202.5" y="12" width="82" height="23"/>
|
||||
<rect key="frame" x="202.5" y="12" width="83" height="23"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="roundTextured" title="Import" bezelStyle="texturedRounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="mMP-KW-YNy">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
@ -970,7 +975,7 @@
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" heightSizable="YES"/>
|
||||
<clipView key="contentView" id="mzf-yu-RID">
|
||||
<rect key="frame" x="1" y="0.0" width="398" height="274"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="none" alternatingRowBackgroundColors="YES" columnReordering="NO" columnResizing="NO" multipleSelection="NO" emptySelection="NO" autosaveColumns="NO" typeSelect="NO" headerView="pvX-uJ-qK5" id="tA3-8T-bxb">
|
||||
<rect key="frame" x="0.0" y="0.0" width="398" height="249"/>
|
||||
@ -1069,6 +1074,7 @@
|
||||
</customObject>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="NSFolder" width="32" height="32"/>
|
||||
<image name="NSStopProgressFreestandingTemplate" width="14" height="14"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
@ -13,8 +13,14 @@ static inline double scale_channel(uint8_t x)
|
||||
|
||||
- (void)setObjectValue:(id)objectValue
|
||||
{
|
||||
|
||||
_integerValue = [objectValue integerValue];
|
||||
super.objectValue = [NSString stringWithFormat:@"$%04x", (uint16_t)(_integerValue & 0x7FFF)];
|
||||
uint8_t r = _integerValue & 0x1F,
|
||||
g = (_integerValue >> 5) & 0x1F,
|
||||
b = (_integerValue >> 10) & 0x1F;
|
||||
super.objectValue = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"$%04x", (uint16_t)(_integerValue & 0x7FFF)] attributes:@{
|
||||
NSForegroundColorAttributeName: r * 3 + g * 4 + b * 2 > 120? [NSColor blackColor] : [NSColor whiteColor]
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSInteger)integerValue
|
||||
|
@ -1,6 +1,7 @@
|
||||
#import "GBPreferencesWindow.h"
|
||||
#import "NSString+StringForKey.h"
|
||||
#import "GBButtons.h"
|
||||
#import "BigSurToolbar.h"
|
||||
#import <Carbon/Carbon.h>
|
||||
|
||||
@implementation GBPreferencesWindow
|
||||
@ -52,6 +53,11 @@
|
||||
return filters;
|
||||
}
|
||||
|
||||
- (NSWindowToolbarStyle)toolbarStyle
|
||||
{
|
||||
return NSWindowToolbarStylePreference;
|
||||
}
|
||||
|
||||
- (void)close
|
||||
{
|
||||
joystick_configuration_state = -1;
|
||||
@ -164,6 +170,20 @@
|
||||
return GBGameBoyButtonCount;
|
||||
}
|
||||
|
||||
- (unsigned) usesForKey:(unsigned) key
|
||||
{
|
||||
unsigned ret = 0;
|
||||
for (unsigned player = 4; player--;) {
|
||||
for (unsigned button = player == 0? GBButtonCount:GBGameBoyButtonCount; button--;) {
|
||||
NSNumber *other = [[NSUserDefaults standardUserDefaults] valueForKey:button_to_preference_name(button, player)];
|
||||
if (other && [other unsignedIntValue] == key) {
|
||||
ret++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
|
||||
{
|
||||
if ([tableColumn.identifier isEqualToString:@"keyName"]) {
|
||||
@ -176,6 +196,12 @@
|
||||
|
||||
NSNumber *key = [[NSUserDefaults standardUserDefaults] valueForKey:button_to_preference_name(row, self.playerListButton.selectedTag)];
|
||||
if (key) {
|
||||
if ([self usesForKey:[key unsignedIntValue]] > 1) {
|
||||
return [[NSAttributedString alloc] initWithString:[NSString displayStringForKeyCode: [key unsignedIntegerValue]]
|
||||
attributes:@{NSForegroundColorAttributeName: [NSColor colorWithRed:0.9375 green:0.25 blue:0.25 alpha:1.0],
|
||||
NSFontAttributeName: [NSFont boldSystemFontOfSize:[NSFont systemFontSize]]
|
||||
}];
|
||||
}
|
||||
return [NSString displayStringForKeyCode: [key unsignedIntegerValue]];
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#include <Core/gb.h>
|
||||
|
||||
@interface GBTerminalTextFieldCell : NSTextFieldCell
|
||||
|
||||
@property GB_gameboy_t *gb;
|
||||
@end
|
||||
|
@ -2,6 +2,7 @@
|
||||
#import "GBTerminalTextFieldCell.h"
|
||||
|
||||
@interface GBTerminalTextView : NSTextView
|
||||
@property GB_gameboy_t *gb;
|
||||
@end
|
||||
|
||||
@implementation GBTerminalTextFieldCell
|
||||
@ -12,10 +13,12 @@
|
||||
- (NSTextView *)fieldEditorForView:(NSView *)controlView
|
||||
{
|
||||
if (field_editor) {
|
||||
field_editor.gb = self.gb;
|
||||
return field_editor;
|
||||
}
|
||||
field_editor = [[GBTerminalTextView alloc] init];
|
||||
[field_editor setFieldEditor:YES];
|
||||
field_editor.gb = self.gb;
|
||||
return field_editor;
|
||||
}
|
||||
|
||||
@ -26,6 +29,8 @@
|
||||
NSMutableOrderedSet *lines;
|
||||
NSUInteger current_line;
|
||||
bool reverse_search_mode;
|
||||
NSRange auto_complete_range;
|
||||
uintptr_t auto_complete_context;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
@ -170,6 +175,7 @@
|
||||
-(void)setSelectedRanges:(NSArray<NSValue *> *)ranges affinity:(NSSelectionAffinity)affinity stillSelecting:(BOOL)stillSelectingFlag
|
||||
{
|
||||
reverse_search_mode = false;
|
||||
auto_complete_context = 0;
|
||||
[super setSelectedRanges:ranges affinity:affinity stillSelecting:stillSelectingFlag];
|
||||
}
|
||||
|
||||
@ -188,6 +194,38 @@
|
||||
[attributes setObject:color forKey:NSForegroundColorAttributeName];
|
||||
[[[NSAttributedString alloc] initWithString:@"Reverse search..." attributes:attributes] drawAtPoint:NSMakePoint(2, 0)];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Todo: lazy design, make it use a delegate instead of having a gb reference*/
|
||||
|
||||
- (void)insertTab:(id)sender
|
||||
{
|
||||
if (auto_complete_context == 0) {
|
||||
NSRange selection = self.selectedRange;
|
||||
if (selection.length) {
|
||||
[self delete:nil];
|
||||
}
|
||||
auto_complete_range = NSMakeRange(selection.location, 0);
|
||||
}
|
||||
char *substring = strdup([self.string substringToIndex:auto_complete_range.location].UTF8String);
|
||||
uintptr_t context = auto_complete_context;
|
||||
char *completion = GB_debugger_complete_substring(self.gb, substring, &context);
|
||||
free(substring);
|
||||
if (completion) {
|
||||
NSString *ns_completion = @(completion);
|
||||
free(completion);
|
||||
if (!ns_completion) {
|
||||
goto error;
|
||||
}
|
||||
self.selectedRange = auto_complete_range;
|
||||
auto_complete_range.length = ns_completion.length;
|
||||
[self replaceCharactersInRange:self.selectedRange withString:ns_completion];
|
||||
auto_complete_context = context;
|
||||
return;
|
||||
}
|
||||
error:
|
||||
auto_complete_context = context;
|
||||
NSBeep();
|
||||
}
|
||||
|
||||
@end
|
||||
|
115
Cocoa/GBView.m
115
Cocoa/GBView.m
@ -1,4 +1,5 @@
|
||||
#import <IOKit/pwr_mgt/IOPMLib.h>
|
||||
#import <Carbon/Carbon.h>
|
||||
#import "GBView.h"
|
||||
#import "GBViewGL.h"
|
||||
#import "GBViewMetal.h"
|
||||
@ -8,6 +9,98 @@
|
||||
#define JOYSTICK_HIGH 0x4000
|
||||
#define JOYSTICK_LOW 0x3800
|
||||
|
||||
static const uint8_t workboy_ascii_to_key[] = {
|
||||
['0'] = GB_WORKBOY_0,
|
||||
['`'] = GB_WORKBOY_UMLAUT,
|
||||
['1'] = GB_WORKBOY_1,
|
||||
['2'] = GB_WORKBOY_2,
|
||||
['3'] = GB_WORKBOY_3,
|
||||
['4'] = GB_WORKBOY_4,
|
||||
['5'] = GB_WORKBOY_5,
|
||||
['6'] = GB_WORKBOY_6,
|
||||
['7'] = GB_WORKBOY_7,
|
||||
['8'] = GB_WORKBOY_8,
|
||||
['9'] = GB_WORKBOY_9,
|
||||
|
||||
['\r'] = GB_WORKBOY_ENTER,
|
||||
[3] = GB_WORKBOY_ENTER,
|
||||
|
||||
['!'] = GB_WORKBOY_EXCLAMATION_MARK,
|
||||
['$'] = GB_WORKBOY_DOLLAR,
|
||||
['#'] = GB_WORKBOY_HASH,
|
||||
['~'] = GB_WORKBOY_TILDE,
|
||||
['*'] = GB_WORKBOY_ASTERISK,
|
||||
['+'] = GB_WORKBOY_PLUS,
|
||||
['-'] = GB_WORKBOY_MINUS,
|
||||
['('] = GB_WORKBOY_LEFT_PARENTHESIS,
|
||||
[')'] = GB_WORKBOY_RIGHT_PARENTHESIS,
|
||||
[';'] = GB_WORKBOY_SEMICOLON,
|
||||
[':'] = GB_WORKBOY_COLON,
|
||||
['%'] = GB_WORKBOY_PERCENT,
|
||||
['='] = GB_WORKBOY_EQUAL,
|
||||
[','] = GB_WORKBOY_COMMA,
|
||||
['<'] = GB_WORKBOY_LT,
|
||||
['.'] = GB_WORKBOY_DOT,
|
||||
['>'] = GB_WORKBOY_GT,
|
||||
['/'] = GB_WORKBOY_SLASH,
|
||||
['?'] = GB_WORKBOY_QUESTION_MARK,
|
||||
[' '] = GB_WORKBOY_SPACE,
|
||||
['\''] = GB_WORKBOY_QUOTE,
|
||||
['@'] = GB_WORKBOY_AT,
|
||||
|
||||
['q'] = GB_WORKBOY_Q,
|
||||
['w'] = GB_WORKBOY_W,
|
||||
['e'] = GB_WORKBOY_E,
|
||||
['r'] = GB_WORKBOY_R,
|
||||
['t'] = GB_WORKBOY_T,
|
||||
['y'] = GB_WORKBOY_Y,
|
||||
['u'] = GB_WORKBOY_U,
|
||||
['i'] = GB_WORKBOY_I,
|
||||
['o'] = GB_WORKBOY_O,
|
||||
['p'] = GB_WORKBOY_P,
|
||||
['a'] = GB_WORKBOY_A,
|
||||
['s'] = GB_WORKBOY_S,
|
||||
['d'] = GB_WORKBOY_D,
|
||||
['f'] = GB_WORKBOY_F,
|
||||
['g'] = GB_WORKBOY_G,
|
||||
['h'] = GB_WORKBOY_H,
|
||||
['j'] = GB_WORKBOY_J,
|
||||
['k'] = GB_WORKBOY_K,
|
||||
['l'] = GB_WORKBOY_L,
|
||||
['z'] = GB_WORKBOY_Z,
|
||||
['x'] = GB_WORKBOY_X,
|
||||
['c'] = GB_WORKBOY_C,
|
||||
['v'] = GB_WORKBOY_V,
|
||||
['b'] = GB_WORKBOY_B,
|
||||
['n'] = GB_WORKBOY_N,
|
||||
['m'] = GB_WORKBOY_M,
|
||||
};
|
||||
|
||||
static const uint8_t workboy_vk_to_key[] = {
|
||||
[kVK_F1] = GB_WORKBOY_CLOCK,
|
||||
[kVK_F2] = GB_WORKBOY_TEMPERATURE,
|
||||
[kVK_F3] = GB_WORKBOY_MONEY,
|
||||
[kVK_F4] = GB_WORKBOY_CALCULATOR,
|
||||
[kVK_F5] = GB_WORKBOY_DATE,
|
||||
[kVK_F6] = GB_WORKBOY_CONVERSION,
|
||||
[kVK_F7] = GB_WORKBOY_RECORD,
|
||||
[kVK_F8] = GB_WORKBOY_WORLD,
|
||||
[kVK_F9] = GB_WORKBOY_PHONE,
|
||||
[kVK_F10] = GB_WORKBOY_UNKNOWN,
|
||||
[kVK_Delete] = GB_WORKBOY_BACKSPACE,
|
||||
[kVK_Shift] = GB_WORKBOY_SHIFT_DOWN,
|
||||
[kVK_RightShift] = GB_WORKBOY_SHIFT_DOWN,
|
||||
[kVK_UpArrow] = GB_WORKBOY_UP,
|
||||
[kVK_DownArrow] = GB_WORKBOY_DOWN,
|
||||
[kVK_LeftArrow] = GB_WORKBOY_LEFT,
|
||||
[kVK_RightArrow] = GB_WORKBOY_RIGHT,
|
||||
[kVK_Escape] = GB_WORKBOY_ESCAPE,
|
||||
[kVK_ANSI_KeypadDecimal] = GB_WORKBOY_DECIMAL_POINT,
|
||||
[kVK_ANSI_KeypadClear] = GB_WORKBOY_M,
|
||||
[kVK_ANSI_KeypadMultiply] = GB_WORKBOY_H,
|
||||
[kVK_ANSI_KeypadDivide] = GB_WORKBOY_J,
|
||||
};
|
||||
|
||||
@implementation GBView
|
||||
{
|
||||
uint32_t *image_buffers[3];
|
||||
@ -188,7 +281,20 @@
|
||||
|
||||
-(void)keyDown:(NSEvent *)theEvent
|
||||
{
|
||||
if ([theEvent type] != NSEventTypeFlagsChanged && theEvent.isARepeat) return;
|
||||
unsigned short keyCode = theEvent.keyCode;
|
||||
if (GB_workboy_is_enabled(_gb)) {
|
||||
if (theEvent.keyCode < sizeof(workboy_vk_to_key) && workboy_vk_to_key[theEvent.keyCode]) {
|
||||
GB_workboy_set_key(_gb, workboy_vk_to_key[theEvent.keyCode]);
|
||||
return;
|
||||
}
|
||||
unichar c = [theEvent type] != NSEventTypeFlagsChanged? [theEvent.charactersIgnoringModifiers.lowercaseString characterAtIndex:0] : 0;
|
||||
if (c < sizeof(workboy_ascii_to_key) && workboy_ascii_to_key[c]) {
|
||||
GB_workboy_set_key(_gb, workboy_ascii_to_key[c]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool handled = false;
|
||||
|
||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||
@ -232,6 +338,15 @@
|
||||
-(void)keyUp:(NSEvent *)theEvent
|
||||
{
|
||||
unsigned short keyCode = theEvent.keyCode;
|
||||
if (GB_workboy_is_enabled(_gb)) {
|
||||
if (keyCode == kVK_Shift || keyCode == kVK_RightShift) {
|
||||
GB_workboy_set_key(_gb, GB_WORKBOY_SHIFT_UP);
|
||||
}
|
||||
else {
|
||||
GB_workboy_set_key(_gb, GB_WORKBOY_NONE);
|
||||
}
|
||||
|
||||
}
|
||||
bool handled = false;
|
||||
|
||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||
|
BIN
Cocoa/Joypad~dark.png
Normal file
BIN
Cocoa/Joypad~dark.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.1 KiB |
BIN
Cocoa/Joypad~dark@2x.png
Normal file
BIN
Cocoa/Joypad~dark@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.0 KiB |
@ -379,6 +379,12 @@
|
||||
<action selector="connectPrinter:" target="-1" id="tl1-CL-tAw"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Workboy" id="lo9-CX-BJj">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="connectWorkboy:" target="-1" id="6vS-bq-wAX"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
|
42
Cocoa/NSImageNamedDarkSupport.m
Normal file
42
Cocoa/NSImageNamedDarkSupport.m
Normal file
@ -0,0 +1,42 @@
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <objc/runtime.h>
|
||||
|
||||
@interface NSImageRep(PrivateAPI)
|
||||
@property(setter=_setAppearanceName:) NSString *_appearanceName;
|
||||
@end
|
||||
|
||||
static NSImage * (*imageNamed)(Class self, SEL _cmd, NSString *name);
|
||||
|
||||
@implementation NSImage(DarkHooks)
|
||||
|
||||
+ (NSImage *)imageNamedWithDark:(NSImageName)name
|
||||
{
|
||||
NSImage *light = imageNamed(self, _cmd, name);
|
||||
if (@available(macOS 10.14, *)) {
|
||||
NSImage *dark = imageNamed(self, _cmd, [name stringByAppendingString:@"~dark"]);
|
||||
if (!dark) {
|
||||
return light;
|
||||
}
|
||||
NSImage *ret = [[NSImage alloc] initWithSize:light.size];
|
||||
for (NSImageRep *rep in light.representations) {
|
||||
[rep _setAppearanceName:NSAppearanceNameAqua];
|
||||
[ret addRepresentation:rep];
|
||||
}
|
||||
for (NSImageRep *rep in dark.representations) {
|
||||
[rep _setAppearanceName:NSAppearanceNameDarkAqua];
|
||||
[ret addRepresentation:rep];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
return light;
|
||||
}
|
||||
|
||||
+(void)load
|
||||
{
|
||||
if (@available(macOS 10.14, *)) {
|
||||
imageNamed = (void *)[self methodForSelector:@selector(imageNamed:)];
|
||||
method_setImplementation(class_getClassMethod(self, @selector(imageNamed:)),
|
||||
[self methodForSelector:@selector(imageNamedWithDark:)]);
|
||||
}
|
||||
}
|
||||
@end
|
@ -492,7 +492,7 @@
|
||||
<rect key="frame" x="1" y="1" width="238" height="209"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView focusRingType="none" appearanceType="vibrantLight" verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnReordering="NO" columnResizing="NO" multipleSelection="NO" emptySelection="NO" autosaveColumns="NO" typeSelect="NO" id="UDd-IJ-fxX">
|
||||
<tableView focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnReordering="NO" columnResizing="NO" multipleSelection="NO" emptySelection="NO" autosaveColumns="NO" typeSelect="NO" id="UDd-IJ-fxX">
|
||||
<rect key="frame" x="0.0" y="0.0" width="238" height="209"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<size key="intercellSpacing" width="3" height="2"/>
|
||||
@ -507,8 +507,8 @@
|
||||
</tableHeaderCell>
|
||||
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="mqT-jD-eXS">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
</tableColumn>
|
||||
@ -520,8 +520,8 @@
|
||||
</tableHeaderCell>
|
||||
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" identifier="keyValue" title="Text Cell" id="tn8-0i-1q1">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
</tableColumn>
|
||||
|
BIN
Cocoa/Speaker~dark.png
Normal file
BIN
Cocoa/Speaker~dark.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.5 KiB |
BIN
Cocoa/Speaker~dark@2x.png
Normal file
BIN
Cocoa/Speaker~dark@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.9 KiB |
200
Core/debugger.c
200
Core/debugger.c
@ -132,7 +132,7 @@ static const char *value_to_string(GB_gameboy_t *gb, uint16_t value, bool prefer
|
||||
}
|
||||
|
||||
/* Avoid overflow */
|
||||
if (symbol && strlen(symbol->name) > 240) {
|
||||
if (symbol && strlen(symbol->name) >= 240) {
|
||||
symbol = NULL;
|
||||
}
|
||||
|
||||
@ -172,7 +172,7 @@ static const char *debugger_value_to_string(GB_gameboy_t *gb, value_t value, boo
|
||||
}
|
||||
|
||||
/* Avoid overflow */
|
||||
if (symbol && strlen(symbol->name) > 240) {
|
||||
if (symbol && strlen(symbol->name) >= 240) {
|
||||
symbol = NULL;
|
||||
}
|
||||
|
||||
@ -689,6 +689,7 @@ exit:
|
||||
|
||||
struct debugger_command_s;
|
||||
typedef bool debugger_command_imp_t(GB_gameboy_t *gb, char *arguments, char *modifiers, const struct debugger_command_s *command);
|
||||
typedef char *debugger_completer_imp_t(GB_gameboy_t *gb, const char *string, uintptr_t *context);
|
||||
|
||||
typedef struct debugger_command_s {
|
||||
const char *command;
|
||||
@ -697,6 +698,8 @@ typedef struct debugger_command_s {
|
||||
const char *help_string; // Null if should not appear in help
|
||||
const char *arguments_format; // For usage message
|
||||
const char *modifiers_format; // For usage message
|
||||
debugger_completer_imp_t *argument_completer;
|
||||
debugger_completer_imp_t *modifiers_completer;
|
||||
} debugger_command_t;
|
||||
|
||||
static const char *lstrip(const char *str)
|
||||
@ -832,6 +835,19 @@ static bool registers(GB_gameboy_t *gb, char *arguments, char *modifiers, const
|
||||
return true;
|
||||
}
|
||||
|
||||
static char *on_off_completer(GB_gameboy_t *gb, const char *string, uintptr_t *context)
|
||||
{
|
||||
size_t length = strlen(string);
|
||||
const char *suggestions[] = {"on", "off"};
|
||||
while (*context < sizeof(suggestions) / sizeof(suggestions[0])) {
|
||||
if (memcmp(string, suggestions[*context], length) == 0) {
|
||||
return strdup(suggestions[(*context)++] + length);
|
||||
}
|
||||
(*context)++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Enable or disable software breakpoints */
|
||||
static bool softbreak(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
|
||||
{
|
||||
@ -873,6 +889,65 @@ static uint16_t find_breakpoint(GB_gameboy_t *gb, value_t addr)
|
||||
return (uint16_t) min;
|
||||
}
|
||||
|
||||
static inline bool is_legal_symbol_char(char c)
|
||||
{
|
||||
if (c >= '0' && c <= '9') return true;
|
||||
if (c >= 'A' && c <= 'Z') return true;
|
||||
if (c >= 'a' && c <= 'z') return true;
|
||||
if (c == '_') return true;
|
||||
if (c == '.') return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static char *symbol_completer(GB_gameboy_t *gb, const char *string, uintptr_t *_context)
|
||||
{
|
||||
const char *symbol_prefix = string;
|
||||
while (*string) {
|
||||
if (!is_legal_symbol_char(*string)) {
|
||||
symbol_prefix = string + 1;
|
||||
}
|
||||
string++;
|
||||
}
|
||||
|
||||
if (*symbol_prefix == '$') {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct {
|
||||
uint16_t bank;
|
||||
uint32_t symbol;
|
||||
} *context = (void *)_context;
|
||||
|
||||
|
||||
size_t length = strlen(symbol_prefix);
|
||||
while (context->bank < 0x200) {
|
||||
if (gb->bank_symbols[context->bank] == NULL ||
|
||||
context->symbol >= gb->bank_symbols[context->bank]->n_symbols) {
|
||||
context->bank++;
|
||||
context->symbol = 0;
|
||||
continue;
|
||||
}
|
||||
const char *candidate = gb->bank_symbols[context->bank]->symbols[context->symbol++].name;
|
||||
if (memcmp(symbol_prefix, candidate, length) == 0) {
|
||||
return strdup(candidate + length);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char *j_completer(GB_gameboy_t *gb, const char *string, uintptr_t *context)
|
||||
{
|
||||
size_t length = strlen(string);
|
||||
const char *suggestions[] = {"j"};
|
||||
while (*context < sizeof(suggestions) / sizeof(suggestions[0])) {
|
||||
if (memcmp(string, suggestions[*context], length) == 0) {
|
||||
return strdup(suggestions[(*context)++] + length);
|
||||
}
|
||||
(*context)++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool breakpoint(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
|
||||
{
|
||||
bool is_jump_to = true;
|
||||
@ -1040,6 +1115,19 @@ static uint16_t find_watchpoint(GB_gameboy_t *gb, value_t addr)
|
||||
return (uint16_t) min;
|
||||
}
|
||||
|
||||
static char *rw_completer(GB_gameboy_t *gb, const char *string, uintptr_t *context)
|
||||
{
|
||||
size_t length = strlen(string);
|
||||
const char *suggestions[] = {"r", "rw", "w"};
|
||||
while (*context < sizeof(suggestions) / sizeof(suggestions[0])) {
|
||||
if (memcmp(string, suggestions[*context], length) == 0) {
|
||||
return strdup(suggestions[(*context)++] + length);
|
||||
}
|
||||
(*context)++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool watch(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
|
||||
{
|
||||
if (strlen(lstrip(arguments)) == 0) {
|
||||
@ -1277,6 +1365,19 @@ static bool should_break(GB_gameboy_t *gb, uint16_t addr, bool jump_to)
|
||||
return _should_break(gb, full_addr, jump_to);
|
||||
}
|
||||
|
||||
static char *format_completer(GB_gameboy_t *gb, const char *string, uintptr_t *context)
|
||||
{
|
||||
size_t length = strlen(string);
|
||||
const char *suggestions[] = {"a", "b", "d", "o", "x"};
|
||||
while (*context < sizeof(suggestions) / sizeof(suggestions[0])) {
|
||||
if (memcmp(string, suggestions[*context], length) == 0) {
|
||||
return strdup(suggestions[(*context)++] + length);
|
||||
}
|
||||
(*context)++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool print(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
|
||||
{
|
||||
if (strlen(lstrip(arguments)) == 0) {
|
||||
@ -1432,7 +1533,7 @@ static bool mbc(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugg
|
||||
const GB_cartridge_t *cartridge = gb->cartridge_type;
|
||||
|
||||
if (cartridge->has_ram) {
|
||||
GB_log(gb, "Cartrdige includes%s RAM: $%x bytes\n", cartridge->has_battery? " battery-backed": "", gb->mbc_ram_size);
|
||||
GB_log(gb, "Cartridge includes%s RAM: $%x bytes\n", cartridge->has_battery? " battery-backed": "", gb->mbc_ram_size);
|
||||
}
|
||||
else {
|
||||
GB_log(gb, "No cartridge RAM\n");
|
||||
@ -1740,6 +1841,19 @@ static bool apu(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugg
|
||||
return true;
|
||||
}
|
||||
|
||||
static char *wave_completer(GB_gameboy_t *gb, const char *string, uintptr_t *context)
|
||||
{
|
||||
size_t length = strlen(string);
|
||||
const char *suggestions[] = {"c", "f", "l"};
|
||||
while (*context < sizeof(suggestions) / sizeof(suggestions[0])) {
|
||||
if (memcmp(string, suggestions[*context], length) == 0) {
|
||||
return strdup(suggestions[(*context)++] + length);
|
||||
}
|
||||
(*context)++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool wave(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command)
|
||||
{
|
||||
if (strlen(lstrip(arguments)) || (modifiers && !strchr("fcl", modifiers[0]))) {
|
||||
@ -1787,7 +1901,7 @@ static const debugger_command_t commands[] = {
|
||||
{"finish", 1, finish, "Run until the current function returns"},
|
||||
{"backtrace", 2, backtrace, "Displays the current call stack"},
|
||||
{"bt", 2, }, /* Alias */
|
||||
{"sld", 3, stack_leak_detection, "Like finish, but stops if a stack leak is detected (Experimental)"},
|
||||
{"sld", 3, stack_leak_detection, "Like finish, but stops if a stack leak is detected"},
|
||||
{"ticks", 2, ticks, "Displays the number of CPU ticks since the last time 'ticks' was" HELP_NEWLINE
|
||||
"used"},
|
||||
{"registers", 1, registers, "Print values of processor registers and other important registers"},
|
||||
@ -1796,30 +1910,33 @@ static const debugger_command_t commands[] = {
|
||||
{"apu", 3, apu, "Displays information about the current state of the audio chip"},
|
||||
{"wave", 3, wave, "Prints a visual representation of the wave RAM." HELP_NEWLINE
|
||||
"Modifiers can be used for a (f)ull print (the default)," HELP_NEWLINE
|
||||
"a more (c)ompact one, or a one-(l)iner", "", "(f|c|l)"},
|
||||
"a more (c)ompact one, or a one-(l)iner", "", "(f|c|l)", .modifiers_completer = wave_completer},
|
||||
{"lcd", 3, lcd, "Displays information about the current state of the LCD controller"},
|
||||
{"palettes", 3, palettes, "Displays the current CGB palettes"},
|
||||
{"softbreak", 2, softbreak, "Enables or disables software breakpoints", "(on|off)"},
|
||||
{"softbreak", 2, softbreak, "Enables or disables software breakpoints", "(on|off)", .argument_completer = on_off_completer},
|
||||
{"breakpoint", 1, breakpoint, "Add a new breakpoint at the specified address/expression" HELP_NEWLINE
|
||||
"Can also modify the condition of existing breakpoints." HELP_NEWLINE
|
||||
"If the j modifier is used, the breakpoint will occur just before" HELP_NEWLINE
|
||||
"jumping to the target.",
|
||||
"<expression>[ if <condition expression>]", "j"},
|
||||
{"delete", 2, delete, "Delete a breakpoint by its address, or all breakpoints", "[<expression>]"},
|
||||
"<expression>[ if <condition expression>]", "j",
|
||||
.argument_completer = symbol_completer, .modifiers_completer = j_completer},
|
||||
{"delete", 2, delete, "Delete a breakpoint by its address, or all breakpoints", "[<expression>]", .argument_completer = symbol_completer},
|
||||
{"watch", 1, watch, "Add a new watchpoint at the specified address/expression." HELP_NEWLINE
|
||||
"Can also modify the condition and type of existing watchpoints." HELP_NEWLINE
|
||||
"Default watchpoint type is write-only.",
|
||||
"<expression>[ if <condition expression>]", "(r|w|rw)"},
|
||||
{"unwatch", 3, unwatch, "Delete a watchpoint by its address, or all watchpoints", "[<expression>]"},
|
||||
"<expression>[ if <condition expression>]", "(r|w|rw)",
|
||||
.argument_completer = symbol_completer, .modifiers_completer = rw_completer
|
||||
},
|
||||
{"unwatch", 3, unwatch, "Delete a watchpoint by its address, or all watchpoints", "[<expression>]", .argument_completer = symbol_completer},
|
||||
{"list", 1, list, "List all set breakpoints and watchpoints"},
|
||||
{"print", 1, print, "Evaluate and print an expression" HELP_NEWLINE
|
||||
"Use modifier to format as an address (a, default) or as a number in" HELP_NEWLINE
|
||||
"decimal (d), hexadecimal (x), octal (o) or binary (b).",
|
||||
"<expression>", "format"},
|
||||
"<expression>", "format", .argument_completer = symbol_completer, .modifiers_completer = format_completer},
|
||||
{"eval", 2, }, /* Alias */
|
||||
{"examine", 2, examine, "Examine values at address", "<expression>", "count"},
|
||||
{"examine", 2, examine, "Examine values at address", "<expression>", "count", .argument_completer = symbol_completer},
|
||||
{"x", 1, }, /* Alias */
|
||||
{"disassemble", 1, disassemble, "Disassemble instructions at address", "<expression>", "count"},
|
||||
{"disassemble", 1, disassemble, "Disassemble instructions at address", "<expression>", "count", .argument_completer = symbol_completer},
|
||||
|
||||
|
||||
{"help", 1, help, "List available commands or show help for the specified command", "[<command>]"},
|
||||
@ -2075,6 +2192,63 @@ bool GB_debugger_execute_command(GB_gameboy_t *gb, char *input)
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns true if debugger waits for more commands */
|
||||
char *GB_debugger_complete_substring(GB_gameboy_t *gb, char *input, uintptr_t *context)
|
||||
{
|
||||
char *command_string = input;
|
||||
char *arguments = strchr(input, ' ');
|
||||
if (arguments) {
|
||||
/* Actually "split" the string. */
|
||||
arguments[0] = 0;
|
||||
arguments++;
|
||||
}
|
||||
|
||||
char *modifiers = strchr(command_string, '/');
|
||||
if (modifiers) {
|
||||
/* Actually "split" the string. */
|
||||
modifiers[0] = 0;
|
||||
modifiers++;
|
||||
}
|
||||
|
||||
const debugger_command_t *command = find_command(command_string);
|
||||
if (command && command->implementation == help && arguments) {
|
||||
command_string = arguments;
|
||||
arguments = NULL;
|
||||
}
|
||||
|
||||
/* No commands and no modifiers, complete the command */
|
||||
if (!arguments && !modifiers) {
|
||||
size_t length = strlen(command_string);
|
||||
if (*context >= sizeof(commands) / sizeof(commands[0])) {
|
||||
return NULL;
|
||||
}
|
||||
for (const debugger_command_t *command = &commands[*context]; command->command; command++) {
|
||||
(*context)++;
|
||||
if (memcmp(command->command, command_string, length) == 0) { /* Is a substring? */
|
||||
return strdup(command->command + length);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (command) {
|
||||
if (arguments) {
|
||||
if (command->argument_completer) {
|
||||
return command->argument_completer(gb, arguments, context);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (modifiers) {
|
||||
if (command->modifiers_completer) {
|
||||
return command->modifiers_completer(gb, modifiers, context);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
JUMP_TO_NONE,
|
||||
JUMP_TO_BREAK,
|
||||
|
@ -34,7 +34,7 @@ bool /* Returns true if debugger waits for more commands. Not relevant for non-G
|
||||
void
|
||||
#endif
|
||||
GB_debugger_execute_command(GB_gameboy_t *gb, char *input); /* Destroys input. */
|
||||
|
||||
char *GB_debugger_complete_substring(GB_gameboy_t *gb, char *input, uintptr_t *context); /* Destroys input, result requires free */
|
||||
|
||||
void GB_debugger_load_symbol_file(GB_gameboy_t *gb, const char *path);
|
||||
const char *GB_debugger_name_for_address(GB_gameboy_t *gb, uint16_t addr);
|
||||
|
@ -1131,8 +1131,9 @@ void GB_disconnect_serial(GB_gameboy_t *gb)
|
||||
gb->serial_transfer_bit_start_callback = NULL;
|
||||
gb->serial_transfer_bit_end_callback = NULL;
|
||||
|
||||
/* Reset any internally-emulated device. Currently, only the printer. */
|
||||
/* Reset any internally-emulated device. */
|
||||
memset(&gb->printer, 0, sizeof(gb->printer));
|
||||
memset(&gb->workboy, 0, sizeof(gb->workboy));
|
||||
}
|
||||
|
||||
bool GB_is_inited(GB_gameboy_t *gb)
|
||||
|
12
Core/gb.h
12
Core/gb.h
@ -23,6 +23,7 @@
|
||||
#include "sgb.h"
|
||||
#include "cheats.h"
|
||||
#include "rumble.h"
|
||||
#include "workboy.h"
|
||||
|
||||
#define GB_STRUCT_VERSION 13
|
||||
|
||||
@ -52,6 +53,10 @@
|
||||
#error Unable to detect endianess
|
||||
#endif
|
||||
|
||||
#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8)
|
||||
#define __builtin_bswap16(x) ({ typeof(x) _x = (x); _x >> 8 | _x << 8; })
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
struct {
|
||||
uint8_t r, g, b;
|
||||
@ -368,6 +373,7 @@ struct GB_gameboy_internal_s {
|
||||
GB_printer_t printer;
|
||||
uint8_t extra_oam[0xff00 - 0xfea0];
|
||||
uint32_t ram_size; // Different between CGB and DMG
|
||||
GB_workboy_t workboy;
|
||||
);
|
||||
|
||||
/* DMA and HDMA */
|
||||
@ -434,7 +440,7 @@ struct GB_gameboy_internal_s {
|
||||
bool rumble_state;
|
||||
bool cart_ir;
|
||||
|
||||
// TODO: move to huc3 struct when breaking save compat
|
||||
// TODO: move to huc3/mbc3 struct when breaking save compat
|
||||
uint8_t huc3_mode;
|
||||
uint8_t huc3_access_index;
|
||||
uint16_t huc3_minutes, huc3_days;
|
||||
@ -442,6 +448,7 @@ struct GB_gameboy_internal_s {
|
||||
bool huc3_alarm_enabled;
|
||||
uint8_t huc3_read;
|
||||
uint8_t huc3_access_flags;
|
||||
bool mbc3_rtc_mapped;
|
||||
);
|
||||
|
||||
|
||||
@ -602,6 +609,9 @@ struct GB_gameboy_internal_s {
|
||||
GB_icd_vreset_callback_t icd_vreset_callback;
|
||||
GB_read_memory_callback_t read_memory_callback;
|
||||
GB_boot_rom_load_callback_t boot_rom_load_callback;
|
||||
GB_print_image_callback_t printer_callback;
|
||||
GB_workboy_set_time_callback workboy_set_time_callback;
|
||||
GB_workboy_get_time_callback workboy_get_time_callback;
|
||||
|
||||
/* IR */
|
||||
uint64_t cycles_since_ir_change; // In 8MHz units
|
||||
|
@ -88,7 +88,6 @@ void GB_update_mbc_mappings(GB_gameboy_t *gb)
|
||||
gb->mbc_ram_bank = gb->mbc3.ram_bank;
|
||||
if (!gb->is_mbc30) {
|
||||
gb->mbc_rom_bank &= 0x7F;
|
||||
gb->mbc_ram_bank &= 0x3;
|
||||
}
|
||||
if (gb->mbc_rom_bank == 0) {
|
||||
gb->mbc_rom_bank = 1;
|
||||
@ -135,7 +134,10 @@ void GB_configure_cart(GB_gameboy_t *gb)
|
||||
static const unsigned ram_sizes[256] = {0, 0x800, 0x2000, 0x8000, 0x20000, 0x10000};
|
||||
gb->mbc_ram_size = ram_sizes[gb->rom[0x149]];
|
||||
}
|
||||
gb->mbc_ram = malloc(gb->mbc_ram_size);
|
||||
|
||||
if (gb->mbc_ram_size) {
|
||||
gb->mbc_ram = malloc(gb->mbc_ram_size);
|
||||
}
|
||||
|
||||
/* Todo: Some games assume unintialized MBC RAM is 0xFF. It this true for all cartridges types? */
|
||||
memset(gb->mbc_ram, 0xFF, gb->mbc_ram_size);
|
||||
|
@ -183,7 +183,7 @@ static uint8_t read_mbc_ram(GB_gameboy_t *gb, uint16_t addr)
|
||||
}
|
||||
}
|
||||
|
||||
if ((!gb->mbc_ram_enable || !gb->mbc_ram_size) &&
|
||||
if ((!gb->mbc_ram_enable) &&
|
||||
gb->cartridge_type->mbc_subtype != GB_CAMERA &&
|
||||
gb->cartridge_type->mbc_type != GB_HUC1 &&
|
||||
gb->cartridge_type->mbc_type != GB_HUC3) {
|
||||
@ -195,17 +195,17 @@ static uint8_t read_mbc_ram(GB_gameboy_t *gb, uint16_t addr)
|
||||
}
|
||||
|
||||
if (gb->cartridge_type->has_rtc && gb->cartridge_type->mbc_type != GB_HUC3 &&
|
||||
gb->mbc_ram_bank >= 8 && gb->mbc_ram_bank <= 0xC) {
|
||||
gb->mbc3_rtc_mapped && gb->mbc_ram_bank <= 4) {
|
||||
/* RTC read */
|
||||
gb->rtc_latched.high |= ~0xC1; /* Not all bytes in RTC high are used. */
|
||||
return gb->rtc_latched.data[gb->mbc_ram_bank - 8];
|
||||
return gb->rtc_latched.data[gb->mbc_ram_bank];
|
||||
}
|
||||
|
||||
if (gb->camera_registers_mapped) {
|
||||
return GB_camera_read_register(gb, addr);
|
||||
}
|
||||
|
||||
if (!gb->mbc_ram) {
|
||||
if (!gb->mbc_ram || !gb->mbc_ram_size) {
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
@ -213,7 +213,11 @@ static uint8_t read_mbc_ram(GB_gameboy_t *gb, uint16_t addr)
|
||||
return GB_camera_read_image(gb, addr - 0xa100);
|
||||
}
|
||||
|
||||
uint8_t ret = gb->mbc_ram[((addr & 0x1FFF) + gb->mbc_ram_bank * 0x2000) & (gb->mbc_ram_size - 1)];
|
||||
uint8_t effective_bank = gb->mbc_ram_bank;
|
||||
if (gb->cartridge_type->mbc_type == GB_MBC3 && !gb->is_mbc30) {
|
||||
effective_bank &= 0x3;
|
||||
}
|
||||
uint8_t ret = gb->mbc_ram[((addr & 0x1FFF) + effective_bank * 0x2000) & (gb->mbc_ram_size - 1)];
|
||||
if (gb->cartridge_type->mbc_type == GB_MBC2) {
|
||||
ret |= 0xF0;
|
||||
}
|
||||
@ -509,7 +513,10 @@ static void write_mbc(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||
switch (addr & 0xF000) {
|
||||
case 0x0000: case 0x1000: gb->mbc_ram_enable = (value & 0xF) == 0xA; break;
|
||||
case 0x2000: case 0x3000: gb->mbc3.rom_bank = value; break;
|
||||
case 0x4000: case 0x5000: gb->mbc3.ram_bank = value; break;
|
||||
case 0x4000: case 0x5000:
|
||||
gb->mbc3.ram_bank = value;
|
||||
gb->mbc3_rtc_mapped = value & 8;
|
||||
break;
|
||||
case 0x6000: case 0x7000:
|
||||
if (!gb->rtc_latch && (value & 1)) { /* Todo: verify condition is correct */
|
||||
memcpy(&gb->rtc_latched, &gb->rtc_real, sizeof(gb->rtc_real));
|
||||
@ -677,7 +684,7 @@ static void write_mbc_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||
return;
|
||||
}
|
||||
|
||||
if ((!gb->mbc_ram_enable || !gb->mbc_ram_size)
|
||||
if ((!gb->mbc_ram_enable)
|
||||
&& gb->cartridge_type->mbc_type != GB_HUC1) return;
|
||||
|
||||
if (gb->cartridge_type->mbc_type == GB_HUC1 && gb->huc1.ir_mode) {
|
||||
@ -693,16 +700,21 @@ static void write_mbc_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||
return;
|
||||
}
|
||||
|
||||
if (gb->cartridge_type->has_rtc && gb->mbc_ram_bank >= 8 && gb->mbc_ram_bank <= 0xC) {
|
||||
gb->rtc_latched.data[gb->mbc_ram_bank - 8] = gb->rtc_real.data[gb->mbc_ram_bank - 8] = value;
|
||||
if (gb->cartridge_type->has_rtc && gb->mbc3_rtc_mapped && gb->mbc_ram_bank <= 4) {
|
||||
gb->rtc_latched.data[gb->mbc_ram_bank] = gb->rtc_real.data[gb->mbc_ram_bank] = value;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gb->mbc_ram) {
|
||||
if (!gb->mbc_ram || !gb->mbc_ram_size) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t effective_bank = gb->mbc_ram_bank;
|
||||
if (gb->cartridge_type->mbc_type == GB_MBC3 && !gb->is_mbc30) {
|
||||
effective_bank &= 0x3;
|
||||
}
|
||||
|
||||
gb->mbc_ram[((addr & 0x1FFF) + gb->mbc_ram_bank * 0x2000) & (gb->mbc_ram_size - 1)] = value;
|
||||
gb->mbc_ram[((addr & 0x1FFF) + effective_bank * 0x2000) & (gb->mbc_ram_size - 1)] = value;
|
||||
}
|
||||
|
||||
static void write_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||
|
@ -31,8 +31,8 @@ static void handle_command(GB_gameboy_t *gb)
|
||||
image[i] = colors[(palette >> (gb->printer.image[i] * 2)) & 3];
|
||||
}
|
||||
|
||||
if (gb->printer.callback) {
|
||||
gb->printer.callback(gb, image, gb->printer.image_offset / 160,
|
||||
if (gb->printer_callback) {
|
||||
gb->printer_callback(gb, image, gb->printer.image_offset / 160,
|
||||
gb->printer.command_data[1] >> 4, gb->printer.command_data[1] & 7,
|
||||
gb->printer.command_data[3] & 0x7F);
|
||||
}
|
||||
@ -189,13 +189,13 @@ static void byte_reieve_completed(GB_gameboy_t *gb, uint8_t byte_received)
|
||||
|
||||
static void serial_start(GB_gameboy_t *gb, bool bit_received)
|
||||
{
|
||||
gb->printer.byte_being_recieved <<= 1;
|
||||
gb->printer.byte_being_recieved |= bit_received;
|
||||
gb->printer.bits_recieved++;
|
||||
if (gb->printer.bits_recieved == 8) {
|
||||
byte_reieve_completed(gb, gb->printer.byte_being_recieved);
|
||||
gb->printer.bits_recieved = 0;
|
||||
gb->printer.byte_being_recieved = 0;
|
||||
gb->printer.byte_being_received <<= 1;
|
||||
gb->printer.byte_being_received |= bit_received;
|
||||
gb->printer.bits_received++;
|
||||
if (gb->printer.bits_received == 8) {
|
||||
byte_reieve_completed(gb, gb->printer.byte_being_received);
|
||||
gb->printer.bits_received = 0;
|
||||
gb->printer.byte_being_received = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -212,5 +212,5 @@ void GB_connect_printer(GB_gameboy_t *gb, GB_print_image_callback_t callback)
|
||||
memset(&gb->printer, 0, sizeof(gb->printer));
|
||||
GB_set_serial_transfer_bit_start_callback(gb, serial_start);
|
||||
GB_set_serial_transfer_bit_end_callback(gb, serial_end);
|
||||
gb->printer.callback = callback;
|
||||
gb->printer_callback = callback;
|
||||
}
|
||||
|
@ -48,13 +48,14 @@ typedef struct
|
||||
uint8_t image[160 * 200];
|
||||
uint16_t image_offset;
|
||||
|
||||
GB_print_image_callback_t callback;
|
||||
/* TODO: Delete me. */
|
||||
uint64_t padding;
|
||||
|
||||
uint8_t compression_run_lenth;
|
||||
bool compression_run_is_compressed;
|
||||
|
||||
uint8_t bits_recieved;
|
||||
uint8_t byte_being_recieved;
|
||||
uint8_t bits_received;
|
||||
uint8_t byte_being_received;
|
||||
bool bit_to_send;
|
||||
} GB_printer_t;
|
||||
|
||||
|
@ -40,7 +40,6 @@ int GB_save_state(GB_gameboy_t *gb, const char *path)
|
||||
if (!dump_section(f, gb->sgb, sizeof(*gb->sgb))) goto error;
|
||||
}
|
||||
|
||||
|
||||
if (fwrite(gb->mbc_ram, 1, gb->mbc_ram_size, f) != gb->mbc_ram_size) {
|
||||
goto error;
|
||||
}
|
||||
@ -116,13 +115,21 @@ void GB_save_state_to_buffer(GB_gameboy_t *gb, uint8_t *buffer)
|
||||
}
|
||||
|
||||
/* Best-effort read function for maximum future compatibility. */
|
||||
static bool read_section(FILE *f, void *dest, uint32_t size)
|
||||
static bool read_section(FILE *f, void *dest, uint32_t size, bool fix_broken_windows_saves)
|
||||
{
|
||||
uint32_t saved_size = 0;
|
||||
if (fread(&saved_size, 1, sizeof(size), f) != sizeof(size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fix_broken_windows_saves) {
|
||||
if (saved_size < 4) {
|
||||
return false;
|
||||
}
|
||||
saved_size -= 4;
|
||||
fseek(f, 4, SEEK_CUR);
|
||||
}
|
||||
|
||||
if (saved_size <= size) {
|
||||
if (fread(dest, 1, saved_size, f) != saved_size) {
|
||||
return false;
|
||||
@ -139,11 +146,21 @@ static bool read_section(FILE *f, void *dest, uint32_t size)
|
||||
}
|
||||
#undef DUMP_SECTION
|
||||
|
||||
static bool verify_state_compatibility(GB_gameboy_t *gb, GB_gameboy_t *save)
|
||||
static bool verify_and_update_state_compatibility(GB_gameboy_t *gb, GB_gameboy_t *save)
|
||||
{
|
||||
if (gb->magic != save->magic) {
|
||||
GB_log(gb, "The file is not a save state, or is from an incompatible operating system.\n");
|
||||
return false;
|
||||
if (save->ram_size == 0 && (&save->ram_size)[-1] == gb->ram_size) {
|
||||
/* This is a save state with a bad printer struct from a 32-bit OS */
|
||||
memcpy(save->extra_oam + 4, save->extra_oam, (uintptr_t)&save->ram_size - (uintptr_t)&save->extra_oam);
|
||||
}
|
||||
if (save->ram_size == 0) {
|
||||
/* Save doesn't have ram size specified, it's a pre 0.12 save state with potentially
|
||||
incorrect RAM amount if it's a CGB instance */
|
||||
if (GB_is_cgb(save)) {
|
||||
save->ram_size = 0x2000 * 8; // Incorrect RAM size
|
||||
}
|
||||
else {
|
||||
save->ram_size = gb->ram_size;
|
||||
}
|
||||
}
|
||||
|
||||
if (gb->version != save->version) {
|
||||
@ -202,7 +219,7 @@ static void sanitize_state(GB_gameboy_t *gb)
|
||||
}
|
||||
}
|
||||
|
||||
#define READ_SECTION(gb, f, section) read_section(f, GB_GET_SECTION(gb, section), GB_SECTION_SIZE(section))
|
||||
#define READ_SECTION(gb, f, section) read_section(f, GB_GET_SECTION(gb, section), GB_SECTION_SIZE(section), fix_broken_windows_saves)
|
||||
|
||||
int GB_load_state(GB_gameboy_t *gb, const char *path)
|
||||
{
|
||||
@ -219,7 +236,18 @@ int GB_load_state(GB_gameboy_t *gb, const char *path)
|
||||
return errno;
|
||||
}
|
||||
|
||||
bool fix_broken_windows_saves = false;
|
||||
if (fread(GB_GET_SECTION(&save, header), 1, GB_SECTION_SIZE(header), f) != GB_SECTION_SIZE(header)) goto error;
|
||||
if (save.magic == 0) {
|
||||
/* Potentially legacy, broken Windows save state */
|
||||
fseek(f, 4, SEEK_SET);
|
||||
if (fread(GB_GET_SECTION(&save, header), 1, GB_SECTION_SIZE(header), f) != GB_SECTION_SIZE(header)) goto error;
|
||||
fix_broken_windows_saves = true;
|
||||
}
|
||||
if (gb->magic != save.magic) {
|
||||
GB_log(gb, "The file is not a save state, or is from an incompatible operating system.\n");
|
||||
return false;
|
||||
}
|
||||
if (!READ_SECTION(&save, f, core_state)) goto error;
|
||||
if (!READ_SECTION(&save, f, dma )) goto error;
|
||||
if (!READ_SECTION(&save, f, mbc )) goto error;
|
||||
@ -229,24 +257,13 @@ int GB_load_state(GB_gameboy_t *gb, const char *path)
|
||||
if (!READ_SECTION(&save, f, rtc )) goto error;
|
||||
if (!READ_SECTION(&save, f, video )) goto error;
|
||||
|
||||
if (save.ram_size == 0) {
|
||||
/* Save doesn't have ram size specified, it's a pre 0.12 save state with potentially
|
||||
incorrect RAM amount if it's a CGB instance */
|
||||
if (GB_is_cgb(&save)) {
|
||||
save.ram_size = 0x2000 * 8; // Incorrect RAM size
|
||||
}
|
||||
else {
|
||||
save.ram_size = gb->ram_size;
|
||||
}
|
||||
}
|
||||
|
||||
if (!verify_state_compatibility(gb, &save)) {
|
||||
if (!verify_and_update_state_compatibility(gb, &save)) {
|
||||
errno = -1;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (GB_is_hle_sgb(gb)) {
|
||||
if (!read_section(f, gb->sgb, sizeof(*gb->sgb))) goto error;
|
||||
if (!read_section(f, gb->sgb, sizeof(*gb->sgb), false)) goto error;
|
||||
}
|
||||
|
||||
memset(gb->mbc_ram + save.mbc_ram_size, 0xFF, gb->mbc_ram_size - save.mbc_ram_size);
|
||||
@ -297,7 +314,7 @@ static size_t buffer_read(void *dest, size_t length, const uint8_t **buffer, siz
|
||||
return length;
|
||||
}
|
||||
|
||||
static bool buffer_read_section(const uint8_t **buffer, size_t *buffer_length, void *dest, uint32_t size)
|
||||
static bool buffer_read_section(const uint8_t **buffer, size_t *buffer_length, void *dest, uint32_t size, bool fix_broken_windows_saves)
|
||||
{
|
||||
uint32_t saved_size = 0;
|
||||
if (buffer_read(&saved_size, sizeof(size), buffer, buffer_length) != sizeof(size)) {
|
||||
@ -306,6 +323,14 @@ static bool buffer_read_section(const uint8_t **buffer, size_t *buffer_length, v
|
||||
|
||||
if (saved_size > *buffer_length) return false;
|
||||
|
||||
if (fix_broken_windows_saves) {
|
||||
if (saved_size < 4) {
|
||||
return false;
|
||||
}
|
||||
saved_size -= 4;
|
||||
*buffer += 4;
|
||||
}
|
||||
|
||||
if (saved_size <= size) {
|
||||
if (buffer_read(dest, saved_size, buffer, buffer_length) != saved_size) {
|
||||
return false;
|
||||
@ -322,15 +347,27 @@ static bool buffer_read_section(const uint8_t **buffer, size_t *buffer_length, v
|
||||
return true;
|
||||
}
|
||||
|
||||
#define READ_SECTION(gb, buffer, length, section) buffer_read_section(&buffer, &length, GB_GET_SECTION(gb, section), GB_SECTION_SIZE(section))
|
||||
#define READ_SECTION(gb, buffer, length, section) buffer_read_section(&buffer, &length, GB_GET_SECTION(gb, section), GB_SECTION_SIZE(section), fix_broken_windows_saves)
|
||||
int GB_load_state_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t length)
|
||||
{
|
||||
GB_gameboy_t save;
|
||||
|
||||
/* Every unread value should be kept the same. */
|
||||
memcpy(&save, gb, sizeof(save));
|
||||
|
||||
bool fix_broken_windows_saves = false;
|
||||
|
||||
if (buffer_read(GB_GET_SECTION(&save, header), GB_SECTION_SIZE(header), &buffer, &length) != GB_SECTION_SIZE(header)) return -1;
|
||||
if (save.magic == 0) {
|
||||
/* Potentially legacy, broken Windows save state*/
|
||||
buffer -= GB_SECTION_SIZE(header) - 4;
|
||||
length += GB_SECTION_SIZE(header) - 4;
|
||||
if (buffer_read(GB_GET_SECTION(&save, header), GB_SECTION_SIZE(header), &buffer, &length) != GB_SECTION_SIZE(header)) return -1;
|
||||
fix_broken_windows_saves = true;
|
||||
}
|
||||
if (gb->magic != save.magic) {
|
||||
GB_log(gb, "The file is not a save state, or is from an incompatible operating system.\n");
|
||||
return false;
|
||||
}
|
||||
if (!READ_SECTION(&save, buffer, length, core_state)) return -1;
|
||||
if (!READ_SECTION(&save, buffer, length, dma )) return -1;
|
||||
if (!READ_SECTION(&save, buffer, length, mbc )) return -1;
|
||||
@ -339,13 +376,14 @@ int GB_load_state_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t le
|
||||
if (!READ_SECTION(&save, buffer, length, apu )) return -1;
|
||||
if (!READ_SECTION(&save, buffer, length, rtc )) return -1;
|
||||
if (!READ_SECTION(&save, buffer, length, video )) return -1;
|
||||
|
||||
|
||||
if (!verify_state_compatibility(gb, &save)) {
|
||||
if (!verify_and_update_state_compatibility(gb, &save)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (GB_is_hle_sgb(gb)) {
|
||||
if (!buffer_read_section(&buffer, &length, gb->sgb, sizeof(*gb->sgb))) return -1;
|
||||
if (!buffer_read_section(&buffer, &length, gb->sgb, sizeof(*gb->sgb), false)) return -1;
|
||||
}
|
||||
|
||||
memset(gb->mbc_ram + save.mbc_ram_size, 0xFF, gb->mbc_ram_size - save.mbc_ram_size);
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
#define GB_PADDING(type, old_usage) type old_usage##__do_not_use
|
||||
|
||||
#define GB_SECTION(name, ...) __attribute__ ((aligned (8))) struct {} name##_section_start; __VA_ARGS__; struct {} name##_section_end
|
||||
#define GB_SECTION(name, ...) __attribute__ ((aligned (8))) union {uint8_t name##_section_start; struct {__VA_ARGS__};}; uint8_t name##_section_end[0]
|
||||
#define GB_SECTION_OFFSET(name) (offsetof(GB_gameboy_t, name##_section_start))
|
||||
#define GB_SECTION_SIZE(name) (offsetof(GB_gameboy_t, name##_section_end) - offsetof(GB_gameboy_t, name##_section_start))
|
||||
#define GB_GET_SECTION(gb, name) ((void*)&((gb)->name##_section_start))
|
||||
|
@ -291,8 +291,20 @@ void GB_rtc_run(GB_gameboy_t *gb)
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ((gb->rtc_real.high & 0x40) == 0) { /* is timer running? */
|
||||
time_t current_time = time(NULL);
|
||||
|
||||
while (gb->last_rtc_second + 60 * 60 * 24 < current_time) {
|
||||
gb->last_rtc_second += 60 * 60 * 24;
|
||||
if (++gb->rtc_real.days == 0) {
|
||||
if (gb->rtc_real.high & 1) { /* Bit 8 of days*/
|
||||
gb->rtc_real.high |= 0x80; /* Overflow bit */
|
||||
}
|
||||
gb->rtc_real.high ^= 1;
|
||||
}
|
||||
}
|
||||
|
||||
while (gb->last_rtc_second < current_time) {
|
||||
gb->last_rtc_second++;
|
||||
if (++gb->rtc_real.seconds == 60) {
|
||||
|
169
Core/workboy.c
Normal file
169
Core/workboy.c
Normal file
@ -0,0 +1,169 @@
|
||||
#include "gb.h"
|
||||
#include <time.h>
|
||||
|
||||
static inline uint8_t int_to_bcd(uint8_t i)
|
||||
{
|
||||
return (i % 10) + ((i / 10) << 4);
|
||||
}
|
||||
|
||||
static inline uint8_t bcd_to_int(uint8_t i)
|
||||
{
|
||||
return (i & 0xF) + (i >> 4) * 10;
|
||||
}
|
||||
|
||||
/*
|
||||
Note: This peripheral was never released. This is a hacky software reimplementation of it that allows
|
||||
reaccessing all of the features present in Workboy's ROM. Some of the implementation details are
|
||||
obviously wrong, but without access to the actual hardware, this is the best I can do.
|
||||
*/
|
||||
|
||||
static void serial_start(GB_gameboy_t *gb, bool bit_received)
|
||||
{
|
||||
gb->workboy.byte_being_received <<= 1;
|
||||
gb->workboy.byte_being_received |= bit_received;
|
||||
gb->workboy.bits_received++;
|
||||
if (gb->workboy.bits_received == 8) {
|
||||
gb->workboy.byte_to_send = 0;
|
||||
if (gb->workboy.mode != 'W' && gb->workboy.byte_being_received == 'R') {
|
||||
gb->workboy.byte_to_send = 'D';
|
||||
gb->workboy.key = GB_WORKBOY_NONE;
|
||||
gb->workboy.mode = gb->workboy.byte_being_received;
|
||||
gb->workboy.buffer_index = 1;
|
||||
|
||||
time_t time = gb->workboy_get_time_callback(gb);
|
||||
struct tm tm;
|
||||
tm = *localtime(&time);
|
||||
memset(gb->workboy.buffer, 0, sizeof(gb->workboy.buffer));
|
||||
|
||||
gb->workboy.buffer[0] = 4; // Unknown, unused, but appears to be expected to be 4
|
||||
gb->workboy.buffer[2] = int_to_bcd(tm.tm_sec); // Seconds, BCD
|
||||
gb->workboy.buffer[3] = int_to_bcd(tm.tm_min); // Minutes, BCD
|
||||
gb->workboy.buffer[4] = int_to_bcd(tm.tm_hour); // Hours, BCD
|
||||
gb->workboy.buffer[5] = int_to_bcd(tm.tm_mday); // Days, BCD. Upper most 2 bits are added to Year for some reason
|
||||
gb->workboy.buffer[6] = int_to_bcd(tm.tm_mon + 1); // Months, BCD
|
||||
gb->workboy.buffer[0xF] = tm.tm_year; // Years, plain number, since 1900
|
||||
|
||||
}
|
||||
else if (gb->workboy.mode != 'W' && gb->workboy.byte_being_received == 'W') {
|
||||
gb->workboy.byte_to_send = 'D'; // It is actually unknown what this value should be
|
||||
gb->workboy.key = GB_WORKBOY_NONE;
|
||||
gb->workboy.mode = gb->workboy.byte_being_received;
|
||||
gb->workboy.buffer_index = 0;
|
||||
}
|
||||
else if (gb->workboy.mode != 'W' && (gb->workboy.byte_being_received == 'O' || gb->workboy.mode == 'O')) {
|
||||
gb->workboy.mode = 'O';
|
||||
gb->workboy.byte_to_send = gb->workboy.key;
|
||||
if (gb->workboy.key != GB_WORKBOY_NONE) {
|
||||
if (gb->workboy.key & GB_WORKBOY_REQUIRE_SHIFT) {
|
||||
gb->workboy.key &= ~GB_WORKBOY_REQUIRE_SHIFT;
|
||||
if (gb->workboy.shift_down) {
|
||||
gb->workboy.byte_to_send = gb->workboy.key;
|
||||
gb->workboy.key = GB_WORKBOY_NONE;
|
||||
}
|
||||
else {
|
||||
gb->workboy.byte_to_send = GB_WORKBOY_SHIFT_DOWN;
|
||||
gb->workboy.shift_down = true;
|
||||
}
|
||||
}
|
||||
else if (gb->workboy.key & GB_WORKBOY_FORBID_SHIFT) {
|
||||
gb->workboy.key &= ~GB_WORKBOY_FORBID_SHIFT;
|
||||
if (!gb->workboy.shift_down) {
|
||||
gb->workboy.byte_to_send = gb->workboy.key;
|
||||
gb->workboy.key = GB_WORKBOY_NONE;
|
||||
}
|
||||
else {
|
||||
gb->workboy.byte_to_send = GB_WORKBOY_SHIFT_UP;
|
||||
gb->workboy.shift_down = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (gb->workboy.key == GB_WORKBOY_SHIFT_DOWN) {
|
||||
gb->workboy.shift_down = true;
|
||||
gb->workboy.user_shift_down = true;
|
||||
}
|
||||
else if (gb->workboy.key == GB_WORKBOY_SHIFT_UP) {
|
||||
gb->workboy.shift_down = false;
|
||||
gb->workboy.user_shift_down = false;
|
||||
}
|
||||
gb->workboy.byte_to_send = gb->workboy.key;
|
||||
gb->workboy.key = GB_WORKBOY_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (gb->workboy.mode == 'R') {
|
||||
if (gb->workboy.buffer_index / 2 >= sizeof(gb->workboy.buffer)) {
|
||||
gb->workboy.byte_to_send = 0;
|
||||
}
|
||||
else {
|
||||
if (gb->workboy.buffer_index & 1) {
|
||||
gb->workboy.byte_to_send = "0123456789ABCDEF"[gb->workboy.buffer[gb->workboy.buffer_index / 2] & 0xF];
|
||||
}
|
||||
else {
|
||||
gb->workboy.byte_to_send = "0123456789ABCDEF"[gb->workboy.buffer[gb->workboy.buffer_index / 2] >> 4];
|
||||
}
|
||||
gb->workboy.buffer_index++;
|
||||
}
|
||||
}
|
||||
else if (gb->workboy.mode == 'W') {
|
||||
gb->workboy.byte_to_send = 'D';
|
||||
if (gb->workboy.buffer_index < 2) {
|
||||
gb->workboy.buffer_index++;
|
||||
}
|
||||
else if ((gb->workboy.buffer_index - 2) < sizeof(gb->workboy.buffer)) {
|
||||
gb->workboy.buffer[gb->workboy.buffer_index - 2] = gb->workboy.byte_being_received;
|
||||
gb->workboy.buffer_index++;
|
||||
if (gb->workboy.buffer_index - 2 == sizeof(gb->workboy.buffer)) {
|
||||
struct tm tm = {0,};
|
||||
tm.tm_sec = bcd_to_int(gb->workboy.buffer[7]);
|
||||
tm.tm_min = bcd_to_int(gb->workboy.buffer[8]);
|
||||
tm.tm_hour = bcd_to_int(gb->workboy.buffer[9]);
|
||||
tm.tm_mday = bcd_to_int(gb->workboy.buffer[0xA]);
|
||||
tm.tm_mon = bcd_to_int(gb->workboy.buffer[0xB] & 0x3F) - 1;
|
||||
tm.tm_year = (uint8_t)(gb->workboy.buffer[0x14] + (gb->workboy.buffer[0xA] >> 6)); // What were they thinking?
|
||||
gb->workboy_set_time_callback(gb, mktime(&tm));
|
||||
gb->workboy.mode = 'O';
|
||||
}
|
||||
}
|
||||
}
|
||||
gb->workboy.bits_received = 0;
|
||||
gb->workboy.byte_being_received = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static bool serial_end(GB_gameboy_t *gb)
|
||||
{
|
||||
bool ret = gb->workboy.bit_to_send;
|
||||
gb->workboy.bit_to_send = gb->workboy.byte_to_send & 0x80;
|
||||
gb->workboy.byte_to_send <<= 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void GB_connect_workboy(GB_gameboy_t *gb,
|
||||
GB_workboy_set_time_callback set_time_callback,
|
||||
GB_workboy_get_time_callback get_time_callback)
|
||||
{
|
||||
memset(&gb->workboy, 0, sizeof(gb->workboy));
|
||||
GB_set_serial_transfer_bit_start_callback(gb, serial_start);
|
||||
GB_set_serial_transfer_bit_end_callback(gb, serial_end);
|
||||
gb->workboy_set_time_callback = set_time_callback;
|
||||
gb->workboy_get_time_callback = get_time_callback;
|
||||
}
|
||||
|
||||
bool GB_workboy_is_enabled(GB_gameboy_t *gb)
|
||||
{
|
||||
return gb->workboy.mode;
|
||||
}
|
||||
|
||||
void GB_workboy_set_key(GB_gameboy_t *gb, uint8_t key)
|
||||
{
|
||||
if (gb->workboy.user_shift_down != gb->workboy.shift_down &&
|
||||
(key & (GB_WORKBOY_REQUIRE_SHIFT | GB_WORKBOY_FORBID_SHIFT)) == 0) {
|
||||
if (gb->workboy.user_shift_down) {
|
||||
key |= GB_WORKBOY_REQUIRE_SHIFT;
|
||||
}
|
||||
else {
|
||||
key |= GB_WORKBOY_FORBID_SHIFT;
|
||||
}
|
||||
}
|
||||
gb->workboy.key = key;
|
||||
}
|
118
Core/workboy.h
Normal file
118
Core/workboy.h
Normal file
@ -0,0 +1,118 @@
|
||||
#ifndef workboy_h
|
||||
#define workboy_h
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
#include "gb_struct_def.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint8_t byte_to_send;
|
||||
bool bit_to_send;
|
||||
uint8_t byte_being_received;
|
||||
uint8_t bits_received;
|
||||
uint8_t mode;
|
||||
uint8_t key;
|
||||
bool shift_down;
|
||||
bool user_shift_down;
|
||||
uint8_t buffer[0x15];
|
||||
uint8_t buffer_index; // In nibbles during read, in bytes during write
|
||||
} GB_workboy_t;
|
||||
|
||||
typedef void (*GB_workboy_set_time_callback)(GB_gameboy_t *gb, time_t time);
|
||||
typedef time_t (*GB_workboy_get_time_callback)(GB_gameboy_t *gb);
|
||||
|
||||
enum {
|
||||
GB_WORKBOY_NONE = 0xFF,
|
||||
GB_WORKBOY_REQUIRE_SHIFT = 0x40,
|
||||
GB_WORKBOY_FORBID_SHIFT = 0x80,
|
||||
|
||||
GB_WORKBOY_CLOCK = 1,
|
||||
GB_WORKBOY_TEMPERATURE = 2,
|
||||
GB_WORKBOY_MONEY = 3,
|
||||
GB_WORKBOY_CALCULATOR = 4,
|
||||
GB_WORKBOY_DATE = 5,
|
||||
GB_WORKBOY_CONVERSION = 6,
|
||||
GB_WORKBOY_RECORD = 7,
|
||||
GB_WORKBOY_WORLD = 8,
|
||||
GB_WORKBOY_PHONE = 9,
|
||||
GB_WORKBOY_ESCAPE = 10,
|
||||
GB_WORKBOY_BACKSPACE = 11,
|
||||
GB_WORKBOY_UNKNOWN = 12,
|
||||
GB_WORKBOY_LEFT = 13,
|
||||
GB_WORKBOY_Q = 17,
|
||||
GB_WORKBOY_1 = 17 | GB_WORKBOY_REQUIRE_SHIFT,
|
||||
GB_WORKBOY_W = 18,
|
||||
GB_WORKBOY_2 = 18 | GB_WORKBOY_REQUIRE_SHIFT,
|
||||
GB_WORKBOY_E = 19,
|
||||
GB_WORKBOY_3 = 19 | GB_WORKBOY_REQUIRE_SHIFT,
|
||||
GB_WORKBOY_R = 20,
|
||||
GB_WORKBOY_T = 21,
|
||||
GB_WORKBOY_Y = 22 ,
|
||||
GB_WORKBOY_U = 23 ,
|
||||
GB_WORKBOY_I = 24,
|
||||
GB_WORKBOY_EXCLAMATION_MARK = 24 | GB_WORKBOY_REQUIRE_SHIFT,
|
||||
GB_WORKBOY_O = 25,
|
||||
GB_WORKBOY_TILDE = 25 | GB_WORKBOY_REQUIRE_SHIFT,
|
||||
GB_WORKBOY_P = 26,
|
||||
GB_WORKBOY_ASTERISK = 26 | GB_WORKBOY_REQUIRE_SHIFT,
|
||||
GB_WORKBOY_DOLLAR = 27 | GB_WORKBOY_FORBID_SHIFT,
|
||||
GB_WORKBOY_HASH = 27 | GB_WORKBOY_REQUIRE_SHIFT,
|
||||
GB_WORKBOY_A = 28,
|
||||
GB_WORKBOY_4 = 28 | GB_WORKBOY_REQUIRE_SHIFT,
|
||||
GB_WORKBOY_S = 29,
|
||||
GB_WORKBOY_5 = 29 | GB_WORKBOY_REQUIRE_SHIFT,
|
||||
GB_WORKBOY_D = 30,
|
||||
GB_WORKBOY_6 = 30 | GB_WORKBOY_REQUIRE_SHIFT,
|
||||
GB_WORKBOY_F = 31,
|
||||
GB_WORKBOY_PLUS = 31 | GB_WORKBOY_REQUIRE_SHIFT,
|
||||
GB_WORKBOY_G = 32,
|
||||
GB_WORKBOY_MINUS = 32 | GB_WORKBOY_REQUIRE_SHIFT,
|
||||
GB_WORKBOY_H = 33,
|
||||
GB_WORKBOY_J = 34,
|
||||
GB_WORKBOY_K = 35,
|
||||
GB_WORKBOY_LEFT_PARENTHESIS = 35 | GB_WORKBOY_REQUIRE_SHIFT,
|
||||
GB_WORKBOY_L = 36,
|
||||
GB_WORKBOY_RIGHT_PARENTHESIS = 36 | GB_WORKBOY_REQUIRE_SHIFT,
|
||||
GB_WORKBOY_SEMICOLON = 37 | GB_WORKBOY_FORBID_SHIFT,
|
||||
GB_WORKBOY_COLON = 37,
|
||||
GB_WORKBOY_ENTER = 38,
|
||||
GB_WORKBOY_SHIFT_DOWN = 39,
|
||||
GB_WORKBOY_Z = 40,
|
||||
GB_WORKBOY_7 = 40 | GB_WORKBOY_REQUIRE_SHIFT,
|
||||
GB_WORKBOY_X = 41,
|
||||
GB_WORKBOY_8 = 41 | GB_WORKBOY_REQUIRE_SHIFT,
|
||||
GB_WORKBOY_C = 42,
|
||||
GB_WORKBOY_9 = 42 | GB_WORKBOY_REQUIRE_SHIFT,
|
||||
GB_WORKBOY_V = 43,
|
||||
GB_WORKBOY_DECIMAL_POINT = 43 | GB_WORKBOY_REQUIRE_SHIFT,
|
||||
GB_WORKBOY_B = 44,
|
||||
GB_WORKBOY_PERCENT = 44 | GB_WORKBOY_REQUIRE_SHIFT,
|
||||
GB_WORKBOY_N = 45,
|
||||
GB_WORKBOY_EQUAL = 45 | GB_WORKBOY_REQUIRE_SHIFT,
|
||||
GB_WORKBOY_M = 46,
|
||||
GB_WORKBOY_COMMA = 47 | GB_WORKBOY_FORBID_SHIFT,
|
||||
GB_WORKBOY_LT = 47 | GB_WORKBOY_REQUIRE_SHIFT,
|
||||
GB_WORKBOY_DOT = 48 | GB_WORKBOY_FORBID_SHIFT,
|
||||
GB_WORKBOY_GT = 48 | GB_WORKBOY_REQUIRE_SHIFT,
|
||||
GB_WORKBOY_SLASH = 49 | GB_WORKBOY_FORBID_SHIFT,
|
||||
GB_WORKBOY_QUESTION_MARK = 49 | GB_WORKBOY_REQUIRE_SHIFT,
|
||||
GB_WORKBOY_SHIFT_UP = 50,
|
||||
GB_WORKBOY_0 = 51 | GB_WORKBOY_REQUIRE_SHIFT,
|
||||
GB_WORKBOY_UMLAUT = 51,
|
||||
GB_WORKBOY_SPACE = 52,
|
||||
GB_WORKBOY_QUOTE = 53 | GB_WORKBOY_FORBID_SHIFT,
|
||||
GB_WORKBOY_AT = 53 | GB_WORKBOY_REQUIRE_SHIFT,
|
||||
GB_WORKBOY_UP = 54,
|
||||
GB_WORKBOY_DOWN = 55,
|
||||
GB_WORKBOY_RIGHT = 56,
|
||||
};
|
||||
|
||||
|
||||
void GB_connect_workboy(GB_gameboy_t *gb,
|
||||
GB_workboy_set_time_callback set_time_callback,
|
||||
GB_workboy_get_time_callback get_time_callback);
|
||||
bool GB_workboy_is_enabled(GB_gameboy_t *gb);
|
||||
void GB_workboy_set_key(GB_gameboy_t *gb, uint8_t key);
|
||||
|
||||
#endif
|
2
HexFiend/HFFunctions.h
vendored
2
HexFiend/HFFunctions.h
vendored
@ -4,7 +4,7 @@
|
||||
#import <libkern/OSAtomic.h>
|
||||
|
||||
#define HFDEFAULT_FONT (@"Monaco")
|
||||
#define HFDEFAULT_FONTSIZE ((CGFloat)10.)
|
||||
#define HFDEFAULT_FONTSIZE ((CGFloat)11.)
|
||||
|
||||
#define HFZeroRange (HFRange){0, 0}
|
||||
|
||||
|
10
HexFiend/HFLineCountingRepresenter.m
vendored
10
HexFiend/HFLineCountingRepresenter.m
vendored
@ -57,8 +57,8 @@ static CGFloat maximumDigitAdvanceForFont(NSFont *font) {
|
||||
interiorShadowEdge = NSMaxXEdge;
|
||||
|
||||
_borderedEdges = (1 << NSMaxXEdge);
|
||||
_borderColor = [[NSColor darkGrayColor] retain];
|
||||
_backgroundColor = [[NSColor colorWithCalibratedWhite:(CGFloat).87 alpha:1] retain];
|
||||
_borderColor = [[NSColor controlShadowColor] retain];
|
||||
_backgroundColor = [[NSColor windowBackgroundColor] retain];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@ -82,9 +82,9 @@ static CGFloat maximumDigitAdvanceForFont(NSFont *font) {
|
||||
lineNumberFormat = (HFLineNumberFormat)[coder decodeInt64ForKey:@"HFLineNumberFormat"];
|
||||
|
||||
_borderedEdges = [coder decodeObjectForKey:@"HFBorderedEdges"] ? (NSInteger)[coder decodeInt64ForKey:@"HFBorderedEdges"] : 0;
|
||||
_borderColor = [[coder decodeObjectForKey:@"HFBorderColor"] ?: [NSColor darkGrayColor] retain];
|
||||
_backgroundColor = [[coder decodeObjectForKey:@"HFBackgroundColor"] ?: [NSColor colorWithCalibratedWhite:(CGFloat).87 alpha:1] retain];
|
||||
|
||||
_borderColor = [[NSColor controlShadowColor] retain];
|
||||
_backgroundColor = [[NSColor windowBackgroundColor] retain];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
56
HexFiend/HFLineCountingView.m
vendored
56
HexFiend/HFLineCountingView.m
vendored
@ -119,20 +119,6 @@
|
||||
[super viewWillMoveToWindow:newWindow];
|
||||
}
|
||||
|
||||
- (void)drawGradientWithClip:(NSRect)clip {
|
||||
[_representer.backgroundColor set];
|
||||
NSRectFill(clip);
|
||||
|
||||
NSInteger shadowEdge = _representer.interiorShadowEdge;
|
||||
|
||||
if (shadowEdge >= 0) {
|
||||
const CGFloat shadowWidth = 6;
|
||||
NSWindow *window = self.window;
|
||||
BOOL drawActive = (window == nil || [window isKeyWindow] || [window isMainWindow]);
|
||||
HFDrawShadow([[NSGraphicsContext currentContext] graphicsPort], self.bounds, shadowWidth, shadowEdge, drawActive, clip);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)drawDividerWithClip:(NSRect)clipRect {
|
||||
USE(clipRect);
|
||||
|
||||
@ -267,7 +253,7 @@ static inline int common_prefix_length(const char *a, const char *b) {
|
||||
NSUInteger glyphCount;
|
||||
[textStorage replaceCharactersInRange:replacementRange withString:replacementCharacters];
|
||||
if (previousTextStorageCharacterCount == 0) {
|
||||
NSDictionary *atts = [[NSDictionary alloc] initWithObjectsAndKeys:_font, NSFontAttributeName, [NSColor colorWithCalibratedWhite:(CGFloat).1 alpha:(CGFloat).8], NSForegroundColorAttributeName, nil];
|
||||
NSDictionary *atts = [[NSDictionary alloc] initWithObjectsAndKeys:_font, NSFontAttributeName, [NSColor controlTextColor], NSForegroundColorAttributeName, nil];
|
||||
[textStorage setAttributes:atts range:NSMakeRange(0, newStringLength)];
|
||||
[atts release];
|
||||
}
|
||||
@ -305,7 +291,7 @@ static inline int common_prefix_length(const char *a, const char *b) {
|
||||
[mutableStyle setAlignment:NSRightTextAlignment];
|
||||
NSParagraphStyle *paragraphStyle = [mutableStyle copy];
|
||||
[mutableStyle release];
|
||||
textAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:_font, NSFontAttributeName, [NSColor colorWithCalibratedWhite:(CGFloat).1 alpha:(CGFloat).8], NSForegroundColorAttributeName, paragraphStyle, NSParagraphStyleAttributeName, nil];
|
||||
textAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:_font, NSFontAttributeName, [NSColor controlTextColor], NSForegroundColorAttributeName, paragraphStyle, NSParagraphStyleAttributeName, nil];
|
||||
[paragraphStyle release];
|
||||
}
|
||||
|
||||
@ -456,12 +442,12 @@ static inline int common_prefix_length(const char *a, const char *b) {
|
||||
}
|
||||
}
|
||||
|
||||
if (! textAttributes) {
|
||||
if (!textAttributes) {
|
||||
NSMutableParagraphStyle *mutableStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
|
||||
[mutableStyle setAlignment:NSRightTextAlignment];
|
||||
NSParagraphStyle *paragraphStyle = [mutableStyle copy];
|
||||
[mutableStyle release];
|
||||
textAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:_font, NSFontAttributeName, [NSColor colorWithCalibratedWhite:(CGFloat).1 alpha:(CGFloat).8], NSForegroundColorAttributeName, paragraphStyle, NSParagraphStyleAttributeName, nil];
|
||||
textAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:_font, NSFontAttributeName, [NSColor controlTextColor], NSForegroundColorAttributeName, paragraphStyle, NSParagraphStyleAttributeName, nil];
|
||||
[paragraphStyle release];
|
||||
[textStorage setAttributes:textAttributes range:NSMakeRange(0, [textStorage length])];
|
||||
}
|
||||
@ -489,27 +475,28 @@ static inline int common_prefix_length(const char *a, const char *b) {
|
||||
NSUInteger linesRemaining = ll2l(HFFPToUL(ceill(_lineRangeToDraw.length + _lineRangeToDraw.location) - floorl(_lineRangeToDraw.location)));
|
||||
|
||||
CGFloat linesToVerticallyOffset = ld2f(_lineRangeToDraw.location - floorl(_lineRangeToDraw.location));
|
||||
CGFloat verticalOffset = linesToVerticallyOffset * _lineHeight + 1;
|
||||
CGFloat verticalOffset = linesToVerticallyOffset * _lineHeight + 1.5;
|
||||
NSRect textRect = self.bounds;
|
||||
textRect.size.width -= 5;
|
||||
textRect.origin.y -= verticalOffset;
|
||||
textRect.size.height += verticalOffset;
|
||||
|
||||
if (! textAttributes) {
|
||||
NSMutableParagraphStyle *mutableStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
|
||||
[mutableStyle setAlignment:NSRightTextAlignment];
|
||||
[mutableStyle setMinimumLineHeight:_lineHeight];
|
||||
[mutableStyle setMaximumLineHeight:_lineHeight];
|
||||
NSParagraphStyle *paragraphStyle = [mutableStyle copy];
|
||||
[mutableStyle release];
|
||||
textAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:_font, NSFontAttributeName, [NSColor colorWithCalibratedWhite:(CGFloat).1 alpha:(CGFloat).8], NSForegroundColorAttributeName, paragraphStyle, NSParagraphStyleAttributeName, nil];
|
||||
[paragraphStyle release];
|
||||
}
|
||||
textRect.size.height += verticalOffset + _lineHeight;
|
||||
|
||||
NSMutableParagraphStyle *mutableStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
|
||||
[mutableStyle setAlignment:NSRightTextAlignment];
|
||||
[mutableStyle setMinimumLineHeight:_lineHeight];
|
||||
[mutableStyle setMaximumLineHeight:_lineHeight];
|
||||
NSParagraphStyle *paragraphStyle = [mutableStyle copy];
|
||||
[mutableStyle release];
|
||||
NSColor *color = [[NSColor textColor] colorUsingColorSpace:[NSColorSpace deviceRGBColorSpace]];
|
||||
color = [NSColor colorWithRed:color.redComponent green:color.greenComponent blue:color.blueComponent alpha:0.75];
|
||||
NSDictionary *_textAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:[NSFont fontWithName:_font.fontName size:9], NSFontAttributeName, color, NSForegroundColorAttributeName, paragraphStyle, NSParagraphStyleAttributeName, nil];
|
||||
[paragraphStyle release];
|
||||
|
||||
|
||||
NSString *string = [self newLineStringForRange:HFRangeMake(lineIndex, linesRemaining)];
|
||||
[string drawInRect:textRect withAttributes:textAttributes];
|
||||
[string drawInRect:textRect withAttributes:_textAttributes];
|
||||
[string release];
|
||||
[_textAttributes release];
|
||||
}
|
||||
|
||||
- (void)drawLineNumbersWithClipSingleStringCellDrawing:(NSRect)clipRect {
|
||||
@ -533,7 +520,7 @@ static inline int common_prefix_length(const char *a, const char *b) {
|
||||
[mutableStyle setMaximumLineHeight:_lineHeight];
|
||||
NSParagraphStyle *paragraphStyle = [mutableStyle copy];
|
||||
[mutableStyle release];
|
||||
textAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:_font, NSFontAttributeName, [NSColor colorWithCalibratedWhite:(CGFloat).1 alpha:(CGFloat).8], NSForegroundColorAttributeName, paragraphStyle, NSParagraphStyleAttributeName, nil];
|
||||
textAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:_font, NSFontAttributeName, [NSColor controlTextColor], NSForegroundColorAttributeName, paragraphStyle, NSParagraphStyleAttributeName, nil];
|
||||
[paragraphStyle release];
|
||||
}
|
||||
|
||||
@ -568,7 +555,7 @@ static inline int common_prefix_length(const char *a, const char *b) {
|
||||
#if TIME_LINE_NUMBERS
|
||||
CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
|
||||
#endif
|
||||
NSInteger drawingMode = (useStringDrawingPath ? 1 : 3);
|
||||
NSInteger drawingMode = 3; // (useStringDrawingPath ? 1 : 3);
|
||||
switch (drawingMode) {
|
||||
// Drawing can't be done right if fonts are wider than expected, but all
|
||||
// of these have rather nasty behavior in that case. I've commented what
|
||||
@ -606,7 +593,6 @@ static inline int common_prefix_length(const char *a, const char *b) {
|
||||
}
|
||||
|
||||
- (void)drawRect:(NSRect)clipRect {
|
||||
[self drawGradientWithClip:clipRect];
|
||||
[self drawDividerWithClip:clipRect];
|
||||
[self drawLineNumbersWithClip:clipRect];
|
||||
}
|
||||
|
18
HexFiend/HFRepresenterTextView.m
vendored
18
HexFiend/HFRepresenterTextView.m
vendored
@ -441,7 +441,7 @@ enum LineCoverage_t {
|
||||
- (void)drawCaretIfNecessaryWithClip:(NSRect)clipRect {
|
||||
NSRect caretRect = NSIntersectionRect(caretRectToDraw, clipRect);
|
||||
if (! NSIsEmptyRect(caretRect)) {
|
||||
[[NSColor blackColor] set];
|
||||
[[NSColor controlTextColor] set];
|
||||
NSRectFill(caretRect);
|
||||
lastDrawnCaretRect = caretRect;
|
||||
}
|
||||
@ -456,12 +456,18 @@ enum LineCoverage_t {
|
||||
|
||||
/* This is the color when we are not in the key window */
|
||||
- (NSColor *)inactiveTextSelectionColor {
|
||||
if (@available(macOS 10.14, *)) {
|
||||
return [NSColor unemphasizedSelectedTextBackgroundColor];
|
||||
}
|
||||
return [NSColor colorWithCalibratedWhite: (CGFloat)(212./255.) alpha:1];
|
||||
}
|
||||
|
||||
/* This is the color when we are not the first responder, but we are in the key window */
|
||||
- (NSColor *)secondaryTextSelectionColor {
|
||||
return [[self primaryTextSelectionColor] blendedColorWithFraction:.66 ofColor:[NSColor colorWithCalibratedWhite:.8f alpha:1]];
|
||||
if (@available(macOS 10.14, *)) {
|
||||
return [NSColor unemphasizedSelectedTextBackgroundColor];
|
||||
}
|
||||
return [NSColor colorWithCalibratedWhite: (CGFloat)(212./255.) alpha:1];
|
||||
}
|
||||
|
||||
- (NSColor *)textSelectionColor {
|
||||
@ -826,7 +832,7 @@ enum LineCoverage_t {
|
||||
- (HFTextVisualStyleRun *)styleRunForByteAtIndex:(NSUInteger)byteIndex {
|
||||
HFTextVisualStyleRun *run = [[HFTextVisualStyleRun alloc] init];
|
||||
[run setRange:NSMakeRange(0, NSUIntegerMax)];
|
||||
[run setForegroundColor:[NSColor blackColor]];
|
||||
[run setForegroundColor:[NSColor textColor]];
|
||||
return [run autorelease];
|
||||
}
|
||||
|
||||
@ -902,8 +908,8 @@ static size_t unionAndCleanLists(NSRect *rectList, id *valueList, size_t count)
|
||||
guideIndex++;
|
||||
}
|
||||
if (rectIndex > 0) {
|
||||
[[NSColor colorWithCalibratedWhite:(CGFloat).8 alpha:1] set];
|
||||
NSRectFillListUsingOperation(lineRects, rectIndex, NSCompositePlusDarker);
|
||||
[[NSColor gridColor] set];
|
||||
NSRectFillListUsingOperation(lineRects, rectIndex, NSCompositeSourceOver);
|
||||
}
|
||||
FREE_ARRAY(lineRects);
|
||||
}
|
||||
@ -1344,7 +1350,7 @@ static size_t unionAndCleanLists(NSRect *rectList, id *valueList, size_t count)
|
||||
[self _drawDefaultLineBackgrounds:clip withLineHeight:[self lineHeight] maxLines:ll2l(HFRoundUpToNextMultipleSaturate(byteCount, bytesPerLine) / bytesPerLine)];
|
||||
[self drawSelectionIfNecessaryWithClip:clip];
|
||||
|
||||
NSColor *textColor = [NSColor blackColor];
|
||||
NSColor *textColor = [NSColor textColor];
|
||||
[textColor set];
|
||||
|
||||
if (! antialias) {
|
||||
|
44
HexFiend/HFStatusBarRepresenter.m
vendored
44
HexFiend/HFStatusBarRepresenter.m
vendored
@ -29,7 +29,7 @@
|
||||
- (void)_sharedInitStatusBarView {
|
||||
NSMutableParagraphStyle *style = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] autorelease];
|
||||
[style setAlignment:NSCenterTextAlignment];
|
||||
cellAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:[NSColor colorWithCalibratedWhite:(CGFloat).15 alpha:1], NSForegroundColorAttributeName, [NSFont labelFontOfSize:10], NSFontAttributeName, style, NSParagraphStyleAttributeName, nil];
|
||||
cellAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:[NSColor windowFrameTextColor], NSForegroundColorAttributeName, [NSFont labelFontOfSize:[NSFont smallSystemFontSize]], NSFontAttributeName, style, NSParagraphStyleAttributeName, nil];
|
||||
cell = [[NSCell alloc] initTextCell:@""];
|
||||
[cell setAlignment:NSCenterTextAlignment];
|
||||
[cell setBackgroundStyle:NSBackgroundStyleRaised];
|
||||
@ -62,51 +62,24 @@
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (void)drawDividerWithClip:(NSRect)clipRect {
|
||||
[[NSColor lightGrayColor] set];
|
||||
NSRect bounds = [self bounds];
|
||||
NSRect lineRect = bounds;
|
||||
lineRect.size.height = 1;
|
||||
NSRectFill(NSIntersectionRect(lineRect, clipRect));
|
||||
}
|
||||
|
||||
|
||||
- (NSGradient *)getGradient:(BOOL)active {
|
||||
static NSGradient *sActiveGradient;
|
||||
static NSGradient *sInactiveGradient;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
sActiveGradient = [[NSGradient alloc] initWithColorsAndLocations:
|
||||
[NSColor colorWithCalibratedWhite:.89 alpha:1.], 0.00,
|
||||
[NSColor colorWithCalibratedWhite:.77 alpha:1.], 0.9,
|
||||
[NSColor colorWithCalibratedWhite:.82 alpha:1.], 1.0,
|
||||
nil];
|
||||
|
||||
sInactiveGradient = [[NSGradient alloc] initWithColorsAndLocations:
|
||||
[NSColor colorWithCalibratedWhite:.93 alpha:1.], 0.00,
|
||||
[NSColor colorWithCalibratedWhite:.87 alpha:1.], 0.9,
|
||||
[NSColor colorWithCalibratedWhite:.90 alpha:1.], 1.0,
|
||||
nil];
|
||||
});
|
||||
return active ? sActiveGradient : sInactiveGradient;
|
||||
}
|
||||
|
||||
|
||||
- (void)drawRect:(NSRect)clip {
|
||||
USE(clip);
|
||||
NSRect bounds = [self bounds];
|
||||
// [[NSColor colorWithCalibratedWhite:(CGFloat).91 alpha:1] set];
|
||||
// NSRectFill(clip);
|
||||
|
||||
NSWindow *window = [self window];
|
||||
BOOL drawActive = (window == nil || [window isMainWindow] || [window isKeyWindow]);
|
||||
[[self getGradient:drawActive] drawInRect:bounds angle:90.];
|
||||
|
||||
[self drawDividerWithClip:clip];
|
||||
NSRect cellRect = NSMakeRect(NSMinX(bounds), HFCeil(NSMidY(bounds) - cellSize.height / 2), NSWidth(bounds), cellSize.height);
|
||||
[cell drawWithFrame:cellRect inView:self];
|
||||
}
|
||||
|
||||
- (void)setFrame:(NSRect)frame
|
||||
{
|
||||
[super setFrame:frame];
|
||||
[self.window setContentBorderThickness:frame.origin.y + frame.size.height forEdge:NSMinYEdge];
|
||||
}
|
||||
|
||||
|
||||
- (void)mouseDown:(NSEvent *)event {
|
||||
USE(event);
|
||||
HFStatusBarMode newMode = ([representer statusMode] + 1) % HFSTATUSMODECOUNT;
|
||||
@ -122,6 +95,7 @@
|
||||
- (void)viewDidMoveToWindow {
|
||||
HFRegisterViewForWindowAppearanceChanges(self, @selector(windowDidChangeKeyStatus:), !registeredForAppNotifications);
|
||||
registeredForAppNotifications = YES;
|
||||
[self.window setContentBorderThickness:self.frame.origin.y + self.frame.size.height forEdge:NSMinYEdge];
|
||||
[super viewDidMoveToWindow];
|
||||
}
|
||||
|
||||
|
12
HexFiend/HFTextRepresenter.m
vendored
12
HexFiend/HFTextRepresenter.m
vendored
@ -20,9 +20,13 @@
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
|
||||
NSColor *color1 = [NSColor colorWithCalibratedWhite:1.0 alpha:1.0];
|
||||
NSColor *color2 = [NSColor colorWithCalibratedRed:.87 green:.89 blue:1. alpha:1.];
|
||||
_rowBackgroundColors = [@[color1, color2] retain];
|
||||
if (@available(macOS 10.14, *)) {
|
||||
_rowBackgroundColors = [[NSColor alternatingContentBackgroundColors] retain];
|
||||
} else {
|
||||
NSColor *color1 = [NSColor windowBackgroundColor];
|
||||
NSColor *color2 = [NSColor colorWithDeviceWhite:0.96 alpha:1];
|
||||
_rowBackgroundColors = [@[color1, color2] retain];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
@ -237,7 +241,7 @@
|
||||
FOREACH(HFRangeWrapper *, wrapper, selectedRanges) {
|
||||
HFRange selectedRange = [wrapper HFRange];
|
||||
BOOL clippedRangeIsVisible;
|
||||
NSRange clippedSelectedRange;
|
||||
NSRange clippedSelectedRange = {0,};
|
||||
/* Necessary because zero length ranges do not intersect anything */
|
||||
if (selectedRange.length == 0) {
|
||||
/* Remember that {6, 0} is considered a subrange of {3, 3} */
|
||||
|
40
Makefile
40
Makefile
@ -16,8 +16,10 @@ endif
|
||||
ifeq ($(PLATFORM),windows32)
|
||||
_ := $(shell chcp 65001)
|
||||
EXESUFFIX:=.exe
|
||||
NATIVE_CC = clang -IWindows -Wno-deprecated-declarations
|
||||
else
|
||||
EXESUFFIX:=
|
||||
NATIVE_CC := cc
|
||||
endif
|
||||
|
||||
PB12_COMPRESS := build/pb12$(EXESUFFIX)
|
||||
@ -34,7 +36,7 @@ ifeq ($(MAKECMDGOALS),)
|
||||
MAKECMDGOALS := $(DEFAULT)
|
||||
endif
|
||||
|
||||
VERSION := 0.13
|
||||
VERSION := 0.13.6
|
||||
export VERSION
|
||||
CONF ?= debug
|
||||
SDL_AUDIO_DRIVER ?= sdl
|
||||
@ -74,6 +76,13 @@ LDFLAGS += -march=native -mtune=native
|
||||
CFLAGS += -march=native -mtune=native
|
||||
endif
|
||||
|
||||
ifeq ($(CONF),fat_release)
|
||||
override CONF := release
|
||||
FAT_FLAGS += -arch x86_64 -arch arm64
|
||||
endif
|
||||
|
||||
|
||||
|
||||
# Set compilation and linkage flags based on target, platform and configuration
|
||||
|
||||
OPEN_DIALOG = OpenDialog/gtk.c
|
||||
@ -97,6 +106,11 @@ ifeq ($(shell $(CC) -x c -c $(NULL) -o $(NULL) -Werror -Wpartial-availability 2>
|
||||
WARNINGS += -Wpartial-availability
|
||||
endif
|
||||
|
||||
# GCC's implementation of this warning has false positives, so we skip it
|
||||
ifneq ($(shell $(CC) --version 2>&1 | grep "gcc"), )
|
||||
WARNINGS += -Wno-maybe-uninitialized
|
||||
endif
|
||||
|
||||
CFLAGS += $(WARNINGS)
|
||||
|
||||
CFLAGS += -std=gnu11 -D_GNU_SOURCE -DVERSION="$(VERSION)" -I. -D_USE_MATH_DEFINES
|
||||
@ -132,9 +146,9 @@ ifeq ($(SYSROOT),/Library/Developer/CommandLineTools/SDKs/)
|
||||
$(error Could not find a macOS SDK)
|
||||
endif
|
||||
|
||||
CFLAGS += -F/Library/Frameworks -mmacosx-version-min=10.9
|
||||
CFLAGS += -F/Library/Frameworks -mmacosx-version-min=10.9 -isysroot $(SYSROOT)
|
||||
OCFLAGS += -x objective-c -fobjc-arc -Wno-deprecated-declarations -isysroot $(SYSROOT)
|
||||
LDFLAGS += -framework AppKit -framework PreferencePanes -framework Carbon -framework QuartzCore -weak_framework Metal -weak_framework MetalKit -mmacosx-version-min=10.9
|
||||
LDFLAGS += -framework AppKit -framework PreferencePanes -framework Carbon -framework QuartzCore -weak_framework Metal -weak_framework MetalKit -mmacosx-version-min=10.9 -isysroot $(SYSROOT)
|
||||
GL_LDFLAGS := -framework OpenGL
|
||||
endif
|
||||
CFLAGS += -Wno-deprecated-declarations
|
||||
@ -227,24 +241,24 @@ $(OBJ)/%.dep: %
|
||||
|
||||
$(OBJ)/Core/%.c.o: Core/%.c
|
||||
-@$(MKDIR) -p $(dir $@)
|
||||
$(CC) $(CFLAGS) -DGB_INTERNAL -c $< -o $@
|
||||
$(CC) $(CFLAGS) $(FAT_FLAGS) -DGB_INTERNAL -c $< -o $@
|
||||
|
||||
$(OBJ)/SDL/%.c.o: SDL/%.c
|
||||
-@$(MKDIR) -p $(dir $@)
|
||||
$(CC) $(CFLAGS) $(SDL_CFLAGS) $(GL_CFLAGS) -c $< -o $@
|
||||
$(CC) $(CFLAGS) $(FAT_FLAGS) $(SDL_CFLAGS) $(GL_CFLAGS) -c $< -o $@
|
||||
|
||||
$(OBJ)/%.c.o: %.c
|
||||
-@$(MKDIR) -p $(dir $@)
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
$(CC) $(CFLAGS) $(FAT_FLAGS) -c $< -o $@
|
||||
|
||||
# HexFiend requires more flags
|
||||
$(OBJ)/HexFiend/%.m.o: HexFiend/%.m
|
||||
-@$(MKDIR) -p $(dir $@)
|
||||
$(CC) $(CFLAGS) $(OCFLAGS) -c $< -o $@ -fno-objc-arc -include HexFiend/HexFiend_2_Framework_Prefix.pch
|
||||
$(CC) $(CFLAGS) $(FAT_FLAGS) $(OCFLAGS) -c $< -o $@ -fno-objc-arc -include HexFiend/HexFiend_2_Framework_Prefix.pch
|
||||
|
||||
$(OBJ)/%.m.o: %.m
|
||||
-@$(MKDIR) -p $(dir $@)
|
||||
$(CC) $(CFLAGS) $(OCFLAGS) -c $< -o $@
|
||||
$(CC) $(CFLAGS) $(FAT_FLAGS) $(OCFLAGS) -c $< -o $@
|
||||
|
||||
# Cocoa Port
|
||||
|
||||
@ -272,13 +286,13 @@ $(BIN)/SameBoy.app: $(BIN)/SameBoy.app/Contents/MacOS/SameBoy \
|
||||
|
||||
$(BIN)/SameBoy.app/Contents/MacOS/SameBoy: $(CORE_OBJECTS) $(COCOA_OBJECTS)
|
||||
-@$(MKDIR) -p $(dir $@)
|
||||
$(CC) $^ -o $@ $(LDFLAGS) -framework OpenGL -framework AudioUnit -framework AVFoundation -framework CoreVideo -framework CoreMedia -framework IOKit
|
||||
$(CC) $^ -o $@ $(LDFLAGS) $(FAT_FLAGS) -framework OpenGL -framework AudioUnit -framework AVFoundation -framework CoreVideo -framework CoreMedia -framework IOKit
|
||||
ifeq ($(CONF), release)
|
||||
$(STRIP) $@
|
||||
endif
|
||||
|
||||
$(BIN)/SameBoy.app/Contents/Resources/Base.lproj/%.nib: Cocoa/%.xib
|
||||
ibtool --compile $@ $^
|
||||
ibtool --compile $@ $^ 2>&1 | cat -
|
||||
|
||||
# Quick Look generator
|
||||
|
||||
@ -294,7 +308,7 @@ $(BIN)/SameBoy.qlgenerator: $(BIN)/SameBoy.qlgenerator/Contents/MacOS/SameBoyQL
|
||||
# once in the QL Generator. It should probably become a dylib instead.
|
||||
$(BIN)/SameBoy.qlgenerator/Contents/MacOS/SameBoyQL: $(CORE_OBJECTS) $(QUICKLOOK_OBJECTS)
|
||||
-@$(MKDIR) -p $(dir $@)
|
||||
$(CC) $^ -o $@ $(LDFLAGS) -Wl,-exported_symbols_list,QuickLook/exports.sym -bundle -framework Cocoa -framework Quicklook
|
||||
$(CC) $^ -o $@ $(LDFLAGS) $(FAT_FLAGS) -Wl,-exported_symbols_list,QuickLook/exports.sym -bundle -framework Cocoa -framework Quicklook
|
||||
|
||||
# cgb_boot_fast.bin is not a standard boot ROM, we don't expect it to exist in the user-provided
|
||||
# boot ROM directory.
|
||||
@ -307,7 +321,7 @@ $(BIN)/SameBoy.qlgenerator/Contents/Resources/cgb_boot_fast.bin: $(BIN)/BootROMs
|
||||
# Unix versions build only one binary
|
||||
$(BIN)/SDL/sameboy: $(CORE_OBJECTS) $(SDL_OBJECTS)
|
||||
-@$(MKDIR) -p $(dir $@)
|
||||
$(CC) $^ -o $@ $(LDFLAGS) $(SDL_LDFLAGS) $(GL_LDFLAGS)
|
||||
$(CC) $^ -o $@ $(LDFLAGS) $(FAT_FLAGS) $(SDL_LDFLAGS) $(GL_LDFLAGS)
|
||||
ifeq ($(CONF), release)
|
||||
$(STRIP) $@
|
||||
endif
|
||||
@ -390,7 +404,7 @@ $(OBJ)/BootROMs/SameBoyLogo.pb12: $(OBJ)/BootROMs/SameBoyLogo.2bpp $(PB12_COMPRE
|
||||
$(realpath $(PB12_COMPRESS)) < $< > $@
|
||||
|
||||
$(PB12_COMPRESS): BootROMs/pb12.c
|
||||
$(CC) $(LDFLAGS) $(CFLAGS) -Wall -Werror $< -o $@
|
||||
$(NATIVE_CC) -std=c99 -Wall -Werror $< -o $@
|
||||
|
||||
$(BIN)/BootROMs/agb_boot.bin: BootROMs/cgb_boot.asm
|
||||
$(BIN)/BootROMs/cgb_boot_fast.bin: BootROMs/cgb_boot.asm
|
||||
|
@ -61,9 +61,9 @@ static OSStatus render(CGContextRef cgContext, CFURLRef url, bool showBorder)
|
||||
}
|
||||
|
||||
if (showBorder) {
|
||||
/* Use the CGB flag to determine the cartrdige "look":
|
||||
/* Use the CGB flag to determine the cartridge "look":
|
||||
- DMG cartridges are grey
|
||||
- CGB cartrdiges are transparent
|
||||
- CGB cartridges are transparent
|
||||
- CGB cartridges that support DMG systems are black
|
||||
*/
|
||||
NSImage *effectiveTemplate = nil;
|
||||
@ -105,8 +105,9 @@ OSStatus GeneratePreviewForURL(void *thisInterface, QLPreviewRequestRef preview,
|
||||
|
||||
OSStatus GenerateThumbnailForURL(void *thisInterface, QLThumbnailRequestRef thumbnail, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options, CGSize maxSize)
|
||||
{
|
||||
extern NSString *kQLThumbnailPropertyIconFlavorKey;
|
||||
@autoreleasepool {
|
||||
CGContextRef cgContext = QLThumbnailRequestCreateContext(thumbnail, ((NSSize){1024, 1024}), true, (__bridge CFDictionaryRef)(@{@"IconFlavor" : @(0)}));
|
||||
CGContextRef cgContext = QLThumbnailRequestCreateContext(thumbnail, ((NSSize){1024, 1024}), true, (__bridge CFDictionaryRef)(@{kQLThumbnailPropertyIconFlavorKey : @(0)}));
|
||||
if (render(cgContext, url, true) == noErr) {
|
||||
QLThumbnailRequestFlushContext(thumbnail, cgContext);
|
||||
CGContextRelease(cgContext);
|
||||
@ -115,4 +116,4 @@ OSStatus GenerateThumbnailForURL(void *thisInterface, QLThumbnailRequestRef thum
|
||||
CGContextRelease(cgContext);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -46,8 +46,8 @@ On Windows, SameBoy also requires:
|
||||
* [GnuWin](http://gnuwin32.sourceforge.net/)
|
||||
* Running vcvars32 before running make. Make sure all required tools and libraries are in %PATH% and %lib%, respectively.
|
||||
|
||||
To compile, simply run `make`. The targets are `cocoa` (Default for macOS), `sdl` (Default for everything else), `libretro`, `bootroms` and `tester`. You may also specify `CONF=debug` (default), `CONF=release` or `CONF=native_release` to control optimization and symbols. `native_release` is faster than `release`, but is optimized to the host's CPU and therefore is not portable. You may set `BOOTROMS_DIR=...` to a directory containing precompiled `dmg_boot.bin` and `cgb_boot.bin` files, otherwise the build system will compile and use SameBoy's own boot ROMs.
|
||||
To compile, simply run `make`. The targets are `cocoa` (Default for macOS), `sdl` (Default for everything else), `libretro`, `bootroms` and `tester`. You may also specify `CONF=debug` (default), `CONF=release`, `CONF=native_release` or `CONF=fat_release` to control optimization, symbols and multi-architectures. `native_release` is faster than `release`, but is optimized to the host's CPU and therefore is not portable. `fat_release` is exclusive to macOS and builds x86-64 and ARM64 fat binaries; this requires using a recent enough `clang` and macOS SDK using `xcode-select`, or setting them explicitly with `CC=` and `SYSROOT=`, respectively. All other configurations will build to your host architecture. You may set `BOOTROMS_DIR=...` to a directory containing precompiled boot ROM files, otherwise the build system will compile and use SameBoy's own boot ROMs.
|
||||
|
||||
By default, the SDL port will look for resource files with a path relative to executable. If you are packaging SameBoy, you may wish to override this by setting the `DATA_DIR` variable during compilation to the target path of the directory containing all files (apart from the executable, that's not necessary) from the `build/bin/SDL` directory in the source tree. Make sure the variable ends with a `/` character.
|
||||
|
||||
SameBoy was compiled and tested on macOS, Ubuntu and 32-bit Windows 7.
|
||||
SameBoy was compiled and tested on macOS, Ubuntu and 64-bit Windows 7.
|
||||
|
39
SDL/gui.c
39
SDL/gui.c
@ -109,6 +109,7 @@ configuration_t configuration =
|
||||
.model = MODEL_CGB,
|
||||
.volume = 100,
|
||||
.rumble_mode = GB_RUMBLE_ALL_GAMES,
|
||||
.default_scale = 2,
|
||||
};
|
||||
|
||||
|
||||
@ -170,6 +171,11 @@ void update_viewport(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void rescale_window(void)
|
||||
{
|
||||
SDL_SetWindowSize(window, GB_get_screen_width(&gb) * configuration.default_scale, GB_get_screen_height(&gb) * configuration.default_scale);
|
||||
}
|
||||
|
||||
/* Does NOT check for bounds! */
|
||||
static void draw_char(uint32_t *buffer, unsigned width, unsigned height, unsigned char ch, uint32_t color)
|
||||
{
|
||||
@ -435,6 +441,12 @@ const char *current_scaling_mode(unsigned index)
|
||||
[configuration.scaling_mode];
|
||||
}
|
||||
|
||||
const char *current_default_scale(unsigned index)
|
||||
{
|
||||
return (const char *[]){"1x", "2x", "3x", "4x", "5x", "6x", "7x", "8x"}
|
||||
[configuration.default_scale - 1];
|
||||
}
|
||||
|
||||
const char *current_color_correction_mode(unsigned index)
|
||||
{
|
||||
return (const char *[]){"Disabled", "Correct Color Curves", "Emulate Hardware", "Preserve Brightness", "Reduce Contrast"}
|
||||
@ -475,6 +487,32 @@ void cycle_scaling_backwards(unsigned index)
|
||||
render_texture(NULL, NULL);
|
||||
}
|
||||
|
||||
void cycle_default_scale(unsigned index)
|
||||
{
|
||||
if (configuration.default_scale == GB_SDL_DEFAULT_SCALE_MAX) {
|
||||
configuration.default_scale = 1;
|
||||
}
|
||||
else {
|
||||
configuration.default_scale++;
|
||||
}
|
||||
|
||||
rescale_window();
|
||||
update_viewport();
|
||||
}
|
||||
|
||||
void cycle_default_scale_backwards(unsigned index)
|
||||
{
|
||||
if (configuration.default_scale == 1) {
|
||||
configuration.default_scale = GB_SDL_DEFAULT_SCALE_MAX;
|
||||
}
|
||||
else {
|
||||
configuration.default_scale--;
|
||||
}
|
||||
|
||||
rescale_window();
|
||||
update_viewport();
|
||||
}
|
||||
|
||||
static void cycle_color_correction(unsigned index)
|
||||
{
|
||||
if (configuration.color_correction_mode == GB_COLOR_CORRECTION_REDUCE_CONTRAST) {
|
||||
@ -643,6 +681,7 @@ const char *blending_mode_string(unsigned index)
|
||||
|
||||
static const struct menu_item graphics_menu[] = {
|
||||
{"Scaling Mode:", cycle_scaling, current_scaling_mode, cycle_scaling_backwards},
|
||||
{"Default Window Scale:", cycle_default_scale, current_default_scale, cycle_default_scale_backwards},
|
||||
{"Scaling Filter:", cycle_filter, current_filter_name, cycle_filter_backwards},
|
||||
{"Color Correction:", cycle_color_correction, current_color_correction_mode, cycle_color_correction_backwards},
|
||||
{"Frame Blending:", cycle_blending_mode, blending_mode_string, cycle_blending_mode_backwards},
|
||||
|
@ -41,6 +41,7 @@ enum pending_command {
|
||||
GB_SDL_QUIT_COMMAND,
|
||||
};
|
||||
|
||||
#define GB_SDL_DEFAULT_SCALE_MAX 8
|
||||
|
||||
extern enum pending_command pending_command;
|
||||
extern unsigned command_parameter;
|
||||
@ -107,6 +108,8 @@ typedef struct {
|
||||
GB_border_mode_t border_mode;
|
||||
uint8_t volume;
|
||||
GB_rumble_mode_t rumble_mode;
|
||||
|
||||
uint8_t default_scale;
|
||||
} configuration_t;
|
||||
|
||||
extern configuration_t configuration;
|
||||
|
55
SDL/main.c
55
SDL/main.c
@ -624,12 +624,47 @@ int main(int argc, char **argv)
|
||||
|
||||
SDL_Init(SDL_INIT_EVERYTHING);
|
||||
|
||||
strcpy(prefs_path, resource_path("prefs.bin"));
|
||||
if (access(prefs_path, R_OK | W_OK) != 0) {
|
||||
char *prefs_dir = SDL_GetPrefPath("", "SameBoy");
|
||||
snprintf(prefs_path, sizeof(prefs_path) - 1, "%sprefs.bin", prefs_dir);
|
||||
SDL_free(prefs_dir);
|
||||
}
|
||||
|
||||
FILE *prefs_file = fopen(prefs_path, "rb");
|
||||
if (prefs_file) {
|
||||
fread(&configuration, 1, sizeof(configuration), prefs_file);
|
||||
fclose(prefs_file);
|
||||
|
||||
/* Sanitize for stability */
|
||||
configuration.color_correction_mode %= GB_COLOR_CORRECTION_REDUCE_CONTRAST +1;
|
||||
configuration.scaling_mode %= GB_SDL_SCALING_MAX;
|
||||
configuration.default_scale %= GB_SDL_DEFAULT_SCALE_MAX + 1;
|
||||
configuration.blending_mode %= GB_FRAME_BLENDING_MODE_ACCURATE + 1;
|
||||
configuration.highpass_mode %= GB_HIGHPASS_MAX;
|
||||
configuration.model %= MODEL_MAX;
|
||||
configuration.sgb_revision %= SGB_MAX;
|
||||
configuration.dmg_palette %= 3;
|
||||
configuration.border_mode %= GB_BORDER_ALWAYS + 1;
|
||||
configuration.rumble_mode %= GB_RUMBLE_ALL_GAMES + 1;
|
||||
}
|
||||
|
||||
if (configuration.model >= MODEL_MAX) {
|
||||
configuration.model = MODEL_CGB;
|
||||
}
|
||||
|
||||
if (configuration.default_scale == 0) {
|
||||
configuration.default_scale = 2;
|
||||
}
|
||||
|
||||
atexit(save_configuration);
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
|
||||
window = SDL_CreateWindow("SameBoy v" xstr(VERSION), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
|
||||
160 * 2, 144 * 2, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
|
||||
160 * configuration.default_scale, 144 * configuration.default_scale, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
|
||||
SDL_SetWindowMinimumSize(window, 160, 144);
|
||||
|
||||
if (fullscreen) {
|
||||
@ -660,24 +695,6 @@ int main(int argc, char **argv)
|
||||
|
||||
SDL_EventState(SDL_DROPFILE, SDL_ENABLE);
|
||||
|
||||
strcpy(prefs_path, resource_path("prefs.bin"));
|
||||
if (access(prefs_path, R_OK | W_OK) != 0) {
|
||||
char *prefs_dir = SDL_GetPrefPath("", "SameBoy");
|
||||
snprintf(prefs_path, sizeof(prefs_path) - 1, "%sprefs.bin", prefs_dir);
|
||||
SDL_free(prefs_dir);
|
||||
}
|
||||
|
||||
FILE *prefs_file = fopen(prefs_path, "rb");
|
||||
if (prefs_file) {
|
||||
fread(&configuration, 1, sizeof(configuration), prefs_file);
|
||||
fclose(prefs_file);
|
||||
}
|
||||
if (configuration.model >= MODEL_MAX) {
|
||||
configuration.model = MODEL_CGB;
|
||||
}
|
||||
|
||||
atexit(save_configuration);
|
||||
|
||||
if (!init_shader_with_name(&shader, configuration.filter)) {
|
||||
init_shader_with_name(&shader, "NearestNeighbor");
|
||||
}
|
||||
|
@ -158,8 +158,5 @@ STATIC vec4 scale(sampler2D image, vec2 position, vec2 input_resolution, vec2 ou
|
||||
ret *= output_resolution.y - pixel_position.y;
|
||||
}
|
||||
|
||||
// Gamma correction
|
||||
ret = pow(ret, vec4(0.72));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ STATIC vec3 rgb_to_hq_colorspace(vec4 rgb)
|
||||
STATIC bool is_different(vec4 a, vec4 b)
|
||||
{
|
||||
vec3 diff = abs(rgb_to_hq_colorspace(a) - rgb_to_hq_colorspace(b));
|
||||
return diff.x > 0.188 || diff.y > 0.027 || diff.z > 0.031;
|
||||
return diff.x > 0.018 || diff.y > 0.002 || diff.z > 0.005;
|
||||
}
|
||||
|
||||
#define P(m, r) ((pattern & (m)) == (r))
|
||||
@ -31,7 +31,7 @@ STATIC vec4 scale(sampler2D image, vec2 position, vec2 input_resolution, vec2 ou
|
||||
{
|
||||
// o = offset, the width of a pixel
|
||||
vec2 o = 1.0 / input_resolution;
|
||||
|
||||
|
||||
/* We always calculate the top left pixel. If we need a different pixel, we flip the image */
|
||||
|
||||
// p = the position within a pixel [0...1]
|
||||
@ -84,7 +84,7 @@ STATIC vec4 scale(sampler2D image, vec2 position, vec2 input_resolution, vec2 ou
|
||||
return interp_3px(w4, 2.0, w0, 1.0, w3, 1.0);
|
||||
}
|
||||
if (P(0x2f,0x2f)) {
|
||||
return interp_3px(w4, 1.04, w3, 1.0, w1, 1.0);
|
||||
return interp_3px(w4, 4.0, w3, 1.0, w1, 1.0);
|
||||
}
|
||||
if (P(0xbf,0x37) || P(0xdb,0x13)) {
|
||||
return interp_3px(w4, 5.0, w1, 2.0, w3, 1.0);
|
||||
@ -110,6 +110,6 @@ STATIC vec4 scale(sampler2D image, vec2 position, vec2 input_resolution, vec2 ou
|
||||
P(0x3b,0x1b)) {
|
||||
return interp_3px(w4, 2.0, w3, 1.0, w1, 1.0);
|
||||
}
|
||||
|
||||
|
||||
return interp_3px(w4, 6.0, w3, 1.0, w1, 1.0);
|
||||
}
|
||||
|
@ -9,14 +9,22 @@ uniform vec2 origin;
|
||||
#define equal(x, y) ((x) == (y))
|
||||
#define inequal(x, y) ((x) != (y))
|
||||
#define STATIC
|
||||
#define GAMMA (2.2)
|
||||
|
||||
out vec4 frag_color;
|
||||
|
||||
vec4 _texture(sampler2D t, vec2 pos)
|
||||
{
|
||||
return pow(texture(t, pos), vec4(GAMMA));
|
||||
}
|
||||
|
||||
#define texture _texture
|
||||
|
||||
#line 1
|
||||
{filter}
|
||||
|
||||
|
||||
#define BLEND_BIAS (1.0/3.0)
|
||||
#define BLEND_BIAS (2.0/5.0)
|
||||
|
||||
#define DISABLED 0
|
||||
#define SIMPLE 1
|
||||
@ -35,7 +43,7 @@ void main()
|
||||
switch (frame_blending_mode) {
|
||||
default:
|
||||
case DISABLED:
|
||||
frag_color = scale(image, position, input_resolution, output_resolution);
|
||||
frag_color = pow(scale(image, position, input_resolution, output_resolution), vec4(1.0 / GAMMA));
|
||||
return;
|
||||
case SIMPLE:
|
||||
ratio = 0.5;
|
||||
@ -58,7 +66,7 @@ void main()
|
||||
break;
|
||||
}
|
||||
|
||||
frag_color = mix(scale(image, position, input_resolution, output_resolution),
|
||||
scale(previous_image, position, input_resolution, output_resolution), ratio);
|
||||
frag_color = pow(mix(scale(image, position, input_resolution, output_resolution),
|
||||
scale(previous_image, position, input_resolution, output_resolution), ratio), vec4(1.0 / GAMMA));
|
||||
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ typedef texture2d<half> sampler2D;
|
||||
#define equal(x, y) all((x) == (y))
|
||||
#define inequal(x, y) any((x) != (y))
|
||||
#define STATIC static
|
||||
#define GAMMA (2.2)
|
||||
|
||||
typedef struct {
|
||||
float4 position [[position]];
|
||||
@ -36,7 +37,7 @@ vertex rasterizer_data vertex_shader(uint index [[ vertex_id ]],
|
||||
static inline float4 texture(texture2d<half> texture, float2 pos)
|
||||
{
|
||||
constexpr sampler texture_sampler;
|
||||
return float4(texture.sample(texture_sampler, pos));
|
||||
return pow(float4(texture.sample(texture_sampler, pos)), GAMMA);
|
||||
}
|
||||
|
||||
#line 1
|
||||
@ -87,7 +88,7 @@ fragment float4 fragment_shader(rasterizer_data in [[stage_in]],
|
||||
break;
|
||||
}
|
||||
|
||||
return mix(scale(image, in.texcoords, input_resolution, *output_resolution),
|
||||
scale(previous_image, in.texcoords, input_resolution, *output_resolution), ratio);
|
||||
return pow(mix(scale(image, in.texcoords, input_resolution, *output_resolution),
|
||||
scale(previous_image, in.texcoords, input_resolution, *output_resolution), ratio), 1 / GAMMA);
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ STATIC vec3 rgb_to_hq_colospace(vec4 rgb)
|
||||
STATIC bool is_different(vec4 a, vec4 b)
|
||||
{
|
||||
vec3 diff = abs(rgb_to_hq_colospace(a) - rgb_to_hq_colospace(b));
|
||||
return diff.x > 0.125 || diff.y > 0.027 || diff.z > 0.031;
|
||||
return diff.x > 0.018 || diff.y > 0.002 || diff.z > 0.005;
|
||||
}
|
||||
|
||||
#define P(m, r) ((pattern & (m)) == (r))
|
||||
|
@ -389,47 +389,12 @@ static void boot_rom_load(GB_gameboy_t *gb, GB_boot_rom_t type)
|
||||
}
|
||||
}
|
||||
|
||||
static void init_for_current_model(unsigned id)
|
||||
static void retro_set_memory_maps(void)
|
||||
{
|
||||
unsigned i = id;
|
||||
enum model effective_model;
|
||||
|
||||
effective_model = model[i];
|
||||
if (effective_model == MODEL_AUTO) {
|
||||
effective_model = auto_model;
|
||||
}
|
||||
|
||||
|
||||
if (GB_is_inited(&gameboy[i])) {
|
||||
GB_switch_model_and_reset(&gameboy[i], libretro_to_internal_model[effective_model]);
|
||||
}
|
||||
else {
|
||||
GB_init(&gameboy[i], libretro_to_internal_model[effective_model]);
|
||||
}
|
||||
|
||||
GB_set_boot_rom_load_callback(&gameboy[i], boot_rom_load);
|
||||
|
||||
/* When running multiple devices they are assumed to use the same resolution */
|
||||
|
||||
GB_set_pixels_output(&gameboy[i],
|
||||
(uint32_t *)(frame_buf + GB_get_screen_width(&gameboy[0]) * GB_get_screen_height(&gameboy[0]) * i));
|
||||
GB_set_rgb_encode_callback(&gameboy[i], rgb_encode);
|
||||
GB_set_sample_rate(&gameboy[i], AUDIO_FREQUENCY);
|
||||
GB_apu_set_sample_callback(&gameboy[i], audio_callback);
|
||||
GB_set_rumble_callback(&gameboy[i], rumble_callback);
|
||||
|
||||
/* todo: attempt to make these more generic */
|
||||
GB_set_vblank_callback(&gameboy[0], (GB_vblank_callback_t) vblank1);
|
||||
if (emulated_devices == 2) {
|
||||
GB_set_vblank_callback(&gameboy[1], (GB_vblank_callback_t) vblank2);
|
||||
if (link_cable_emulation) {
|
||||
set_link_cable_state(true);
|
||||
}
|
||||
}
|
||||
|
||||
struct retro_memory_descriptor descs[11];
|
||||
size_t size;
|
||||
uint16_t bank;
|
||||
unsigned i;
|
||||
|
||||
|
||||
/* todo: add netplay awareness for this so achievements can be granted on the respective client */
|
||||
@ -489,6 +454,45 @@ static void init_for_current_model(unsigned id)
|
||||
mmaps.descriptors = descs;
|
||||
mmaps.num_descriptors = sizeof(descs) / sizeof(descs[0]);
|
||||
environ_cb(RETRO_ENVIRONMENT_SET_MEMORY_MAPS, &mmaps);
|
||||
}
|
||||
|
||||
static void init_for_current_model(unsigned id)
|
||||
{
|
||||
unsigned i = id;
|
||||
enum model effective_model;
|
||||
|
||||
effective_model = model[i];
|
||||
if (effective_model == MODEL_AUTO) {
|
||||
effective_model = auto_model;
|
||||
}
|
||||
|
||||
|
||||
if (GB_is_inited(&gameboy[i])) {
|
||||
GB_switch_model_and_reset(&gameboy[i], libretro_to_internal_model[effective_model]);
|
||||
}
|
||||
else {
|
||||
GB_init(&gameboy[i], libretro_to_internal_model[effective_model]);
|
||||
}
|
||||
|
||||
GB_set_boot_rom_load_callback(&gameboy[i], boot_rom_load);
|
||||
|
||||
/* When running multiple devices they are assumed to use the same resolution */
|
||||
|
||||
GB_set_pixels_output(&gameboy[i],
|
||||
(uint32_t *)(frame_buf + GB_get_screen_width(&gameboy[0]) * GB_get_screen_height(&gameboy[0]) * i));
|
||||
GB_set_rgb_encode_callback(&gameboy[i], rgb_encode);
|
||||
GB_set_sample_rate(&gameboy[i], AUDIO_FREQUENCY);
|
||||
GB_apu_set_sample_callback(&gameboy[i], audio_callback);
|
||||
GB_set_rumble_callback(&gameboy[i], rumble_callback);
|
||||
|
||||
/* todo: attempt to make these more generic */
|
||||
GB_set_vblank_callback(&gameboy[0], (GB_vblank_callback_t) vblank1);
|
||||
if (emulated_devices == 2) {
|
||||
GB_set_vblank_callback(&gameboy[1], (GB_vblank_callback_t) vblank2);
|
||||
if (link_cable_emulation) {
|
||||
set_link_cable_state(true);
|
||||
}
|
||||
}
|
||||
|
||||
/* Let's be extremely nitpicky about how devices and descriptors are set */
|
||||
if (emulated_devices == 1 && (model[0] == MODEL_SGB || model[0] == MODEL_SGB2)) {
|
||||
@ -1070,6 +1074,9 @@ bool retro_load_game(const struct retro_game_info *info)
|
||||
}
|
||||
|
||||
check_variables();
|
||||
|
||||
retro_set_memory_maps();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user