mirror of
https://github.com/libretro/bsnes-libretro.git
synced 2024-11-26 18:40:47 +00:00
Resync to upstream
This commit is contained in:
commit
44d97b17d0
12
bsnes/gb/.github/actions/sanity_tests.sh
vendored
12
bsnes/gb/.github/actions/sanity_tests.sh
vendored
@ -1,10 +1,10 @@
|
||||
set -e
|
||||
|
||||
./build/bin/tester/sameboy_tester --jobs 5 \
|
||||
--length 40 .github/actions/cgb_sound.gb \
|
||||
--length 45 .github/actions/cgb_sound.gb \
|
||||
--length 10 .github/actions/cgb-acid2.gbc \
|
||||
--length 10 .github/actions/dmg-acid2.gb \
|
||||
--dmg --length 40 .github/actions/dmg_sound-2.gb \
|
||||
--dmg --length 45 .github/actions/dmg_sound-2.gb \
|
||||
--dmg --length 20 .github/actions/oam_bug-2.gb
|
||||
|
||||
mv .github/actions/dmg{,-mode}-acid2.bmp
|
||||
@ -15,18 +15,18 @@ mv .github/actions/dmg{,-mode}-acid2.bmp
|
||||
set +e
|
||||
|
||||
FAILED_TESTS=`
|
||||
shasum .github/actions/*.bmp | grep -q -E -v \(\
|
||||
44ce0c7d49254df0637849c9155080ac7dc3ef3d\ \ .github/actions/cgb-acid2.bmp\|\
|
||||
shasum .github/actions/*.bmp | grep -E -v \(\
|
||||
5283564df0cf5bb78a7a90aff026c1a4692fd39e\ \ .github/actions/cgb-acid2.bmp\|\
|
||||
dbcc438dcea13b5d1b80c5cd06bda2592cc5d9e0\ \ .github/actions/cgb_sound.bmp\|\
|
||||
0caadf9634e40247ae9c15ff71992e8f77bbf89e\ \ .github/actions/dmg-acid2.bmp\|\
|
||||
c50daed36c57a8170ff362042694786676350997\ \ .github/actions/dmg-mode-acid2.bmp\|\
|
||||
a732077f98f43d9231453b1764d9f797a836924d\ \ .github/actions/dmg-mode-acid2.bmp\|\
|
||||
c9e944b7e01078bdeba1819bc2fa9372b111f52d\ \ .github/actions/dmg_sound-2.bmp\|\
|
||||
f0172cc91867d3343fbd113a2bb98100074be0de\ \ .github/actions/oam_bug-2.bmp\
|
||||
\)`
|
||||
|
||||
if [ -n "$FAILED_TESTS" ] ; then
|
||||
echo "Failed the following tests:"
|
||||
echo $FAILED_TESTS | tr " " "\n" | grep -q -o -E "[^/]+\.bmp" | sed s/.bmp// | sort
|
||||
echo $FAILED_TESTS | tr " " "\n" | grep -o -E "[^/]+\.bmp" | sed s/.bmp// | sort
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
4
bsnes/gb/.github/workflows/sanity.yml
vendored
4
bsnes/gb/.github/workflows/sanity.yml
vendored
@ -6,7 +6,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [macos-latest, ubuntu-latest, ubuntu-16.04]
|
||||
os: [macos-latest, ubuntu-latest, ubuntu-18.04]
|
||||
cc: [gcc, clang]
|
||||
include:
|
||||
- os: macos-latest
|
||||
@ -33,4 +33,4 @@ jobs:
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: sameboy-canary-${{ matrix.os }}-${{ matrix.cc }}
|
||||
path: build/bin
|
||||
path: build/bin
|
||||
|
@ -25,6 +25,7 @@ Start:
|
||||
|
||||
; Init waveform
|
||||
ld c, $10
|
||||
ld hl, $FF30
|
||||
.waveformLoop
|
||||
ldi [hl], a
|
||||
cpl
|
||||
@ -44,7 +45,6 @@ Start:
|
||||
ldh [$25], a
|
||||
ld a, $77
|
||||
ldh [$24], a
|
||||
ld hl, $FF30
|
||||
|
||||
; Init BG palette
|
||||
ld a, $fc
|
||||
@ -190,10 +190,9 @@ ENDC
|
||||
IF !DEF(FAST)
|
||||
call DoIntroAnimation
|
||||
|
||||
ld a, 45
|
||||
ld a, 48 ; frames to wait after playing the chime
|
||||
ldh [WaitLoopCounter], a
|
||||
; Wait ~0.75 seconds
|
||||
ld b, a
|
||||
ld b, 4 ; frames to wait before playing the chime
|
||||
call WaitBFrames
|
||||
|
||||
; Play first sound
|
||||
@ -1187,7 +1186,7 @@ ChangeAnimationPalette:
|
||||
call WaitFrame
|
||||
call LoadPalettesFromHRAM
|
||||
; Delay the wait loop while the user is selecting a palette
|
||||
ld a, 45
|
||||
ld a, 48
|
||||
ldh [WaitLoopCounter], a
|
||||
pop de
|
||||
pop bc
|
||||
|
@ -86,7 +86,7 @@ static uint32_t color_to_int(NSColor *color)
|
||||
}
|
||||
|
||||
if ([[NSProcessInfo processInfo].arguments containsObject:@"--update-launch"]) {
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
[NSApp activateIgnoringOtherApps:true];
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,7 +106,7 @@ static uint32_t color_to_int(NSColor *color)
|
||||
NSRect new = [_preferencesWindow frameRectForContentRect:tab.frame];
|
||||
new.origin.x = old.origin.x;
|
||||
new.origin.y = old.origin.y + (old.size.height - new.size.height);
|
||||
[_preferencesWindow setFrame:new display:YES animate:_preferencesWindow.visible];
|
||||
[_preferencesWindow setFrame:new display:true animate:_preferencesWindow.visible];
|
||||
[_preferencesWindow.contentView addSubview:tab];
|
||||
}
|
||||
|
||||
@ -171,7 +171,7 @@ static uint32_t color_to_int(NSColor *color)
|
||||
|
||||
- (void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification
|
||||
{
|
||||
[[NSDocumentController sharedDocumentController] openDocumentWithContentsOfFile:notification.identifier display:YES];
|
||||
[[NSDocumentController sharedDocumentController] openDocumentWithContentsOfFile:notification.identifier display:true];
|
||||
}
|
||||
|
||||
- (void)updateFound
|
||||
@ -242,7 +242,7 @@ static uint32_t color_to_int(NSColor *color)
|
||||
[[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@UPDATE_SERVER "/latest_version"] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.updatesSpinner stopAnimation:nil];
|
||||
[self.updatesButton setEnabled:YES];
|
||||
[self.updatesButton setEnabled:true];
|
||||
});
|
||||
if ([(NSHTTPURLResponse *)response statusCode] == 200) {
|
||||
NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||
@ -312,7 +312,7 @@ static uint32_t color_to_int(NSColor *color)
|
||||
_downloadDirectory = [[[NSFileManager defaultManager] URLForDirectory:NSItemReplacementDirectory
|
||||
inDomain:NSUserDomainMask
|
||||
appropriateForURL:[[NSBundle mainBundle] bundleURL]
|
||||
create:YES
|
||||
create:true
|
||||
error:nil] path];
|
||||
NSTask *unzipTask;
|
||||
if (!_downloadDirectory) {
|
||||
|
@ -321,7 +321,7 @@ static void infraredStateChanged(GB_gameboy_t *gb, bool on)
|
||||
{
|
||||
if (_gbsVisualizer) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[_gbsVisualizer setNeedsDisplay:YES];
|
||||
[_gbsVisualizer setNeedsDisplay:true];
|
||||
});
|
||||
}
|
||||
[self.view flip];
|
||||
@ -422,7 +422,7 @@ static void infraredStateChanged(GB_gameboy_t *gb, bool on)
|
||||
if (![[NSUserDefaults standardUserDefaults] boolForKey:@"Mute"]) {
|
||||
[self.audioClient start];
|
||||
}
|
||||
hex_timer = [NSTimer timerWithTimeInterval:0.25 target:self selector:@selector(reloadMemoryView) userInfo:nil repeats:YES];
|
||||
hex_timer = [NSTimer timerWithTimeInterval:0.25 target:self selector:@selector(reloadMemoryView) userInfo:nil repeats:true];
|
||||
[[NSRunLoop mainRunLoop] addTimer:hex_timer forMode:NSDefaultRunLoopMode];
|
||||
|
||||
/* Clear pending alarms, don't play alarms while playing */
|
||||
@ -502,7 +502,7 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
|
||||
[audioLock unlock];
|
||||
[self.audioClient stop];
|
||||
self.audioClient = nil;
|
||||
self.view.mouseHidingEnabled = NO;
|
||||
self.view.mouseHidingEnabled = false;
|
||||
GB_save_battery(&gb, [[[self.fileURL URLByDeletingPathExtension] URLByAppendingPathExtension:@"sav"].path UTF8String]);
|
||||
GB_save_cheats(&gb, [[[self.fileURL URLByDeletingPathExtension] URLByAppendingPathExtension:@"cht"].path UTF8String]);
|
||||
unsigned time_to_alarm = GB_time_to_alarm(&gb);
|
||||
@ -593,12 +593,7 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
|
||||
current_model = (enum model)[sender tag];
|
||||
}
|
||||
|
||||
if (!modelsChanging && [sender tag] == MODEL_NONE) {
|
||||
GB_reset(&gb);
|
||||
}
|
||||
else {
|
||||
GB_switch_model_and_reset(&gb, [self internalModel]);
|
||||
}
|
||||
GB_switch_model_and_reset(&gb, [self internalModel]);
|
||||
|
||||
if (old_width != GB_get_screen_width(&gb)) {
|
||||
[self.view screenSizeChanged];
|
||||
@ -689,13 +684,13 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
|
||||
window_frame.size.width);
|
||||
window_frame.size.height = MAX([[NSUserDefaults standardUserDefaults] integerForKey:@"LastWindowHeight"],
|
||||
window_frame.size.height);
|
||||
[self.mainWindow setFrame:window_frame display:YES];
|
||||
[self.mainWindow setFrame:window_frame display:true];
|
||||
self.vramStatusLabel.cell.backgroundStyle = NSBackgroundStyleRaised;
|
||||
|
||||
NSUInteger height_diff = self.vramWindow.frame.size.height - self.vramWindow.contentView.frame.size.height;
|
||||
CGRect vram_window_rect = self.vramWindow.frame;
|
||||
vram_window_rect.size.height = 384 + height_diff + 48;
|
||||
[self.vramWindow setFrame:vram_window_rect display:YES animate:NO];
|
||||
[self.vramWindow setFrame:vram_window_rect display:true animate:false];
|
||||
|
||||
|
||||
self.consoleWindow.title = [NSString stringWithFormat:@"Debug Console – %@", [self.fileURL.path lastPathComponent]];
|
||||
@ -851,7 +846,7 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
|
||||
|
||||
+ (BOOL)autosavesInPlace
|
||||
{
|
||||
return YES;
|
||||
return true;
|
||||
}
|
||||
|
||||
- (NSString *)windowNibName
|
||||
@ -863,7 +858,7 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
|
||||
|
||||
- (BOOL)readFromFile:(NSString *)fileName ofType:(NSString *)type
|
||||
{
|
||||
return YES;
|
||||
return true;
|
||||
}
|
||||
|
||||
- (IBAction)changeGBSTrack:(id)sender
|
||||
@ -902,7 +897,7 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
|
||||
{
|
||||
GB_set_rendering_disabled(&gb, true);
|
||||
_view = nil;
|
||||
for (NSView *view in _mainWindow.contentView.subviews) {
|
||||
for (NSView *view in [_mainWindow.contentView.subviews copy]) {
|
||||
[view removeFromSuperview];
|
||||
}
|
||||
[[NSBundle mainBundle] loadNibNamed:@"GBS" owner:self topLevelObjects:nil];
|
||||
@ -1066,7 +1061,7 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
|
||||
- (void) windowWillExitFullScreen:(NSNotification *)notification
|
||||
{
|
||||
fullScreen = false;
|
||||
self.view.mouseHidingEnabled = NO;
|
||||
self.view.mouseHidingEnabled = false;
|
||||
}
|
||||
|
||||
- (NSRect)windowWillUseStandardFrame:(NSWindow *)window defaultFrame:(NSRect)newFrame
|
||||
@ -1161,14 +1156,14 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
|
||||
}
|
||||
|
||||
if (![console_output_timer isValid]) {
|
||||
console_output_timer = [NSTimer timerWithTimeInterval:(NSTimeInterval)0.05 target:self selector:@selector(appendPendingOutput) userInfo:nil repeats:NO];
|
||||
console_output_timer = [NSTimer timerWithTimeInterval:(NSTimeInterval)0.05 target:self selector:@selector(appendPendingOutput) userInfo:nil repeats:false];
|
||||
[[NSRunLoop mainRunLoop] addTimer:console_output_timer forMode:NSDefaultRunLoopMode];
|
||||
}
|
||||
|
||||
[console_output_lock unlock];
|
||||
|
||||
/* Make sure mouse is not hidden while debugging */
|
||||
self.view.mouseHidingEnabled = NO;
|
||||
self.view.mouseHidingEnabled = false;
|
||||
}
|
||||
|
||||
- (IBAction)showConsoleWindow:(id)sender
|
||||
@ -1395,7 +1390,7 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
|
||||
bitmapInfo,
|
||||
provider,
|
||||
NULL,
|
||||
YES,
|
||||
true,
|
||||
renderingIntent);
|
||||
CGDataProviderRelease(provider);
|
||||
CGColorSpaceRelease(colorSpaceRef);
|
||||
@ -1603,7 +1598,7 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
|
||||
}
|
||||
[self.memoryBankInput setStringValue:[NSString stringWithFormat:@"$%x", byteArray.selectedBank]];
|
||||
[hex_controller reloadData];
|
||||
[self.memoryView setNeedsDisplay:YES];
|
||||
[self.memoryView setNeedsDisplay:true];
|
||||
}
|
||||
|
||||
- (GB_gameboy_t *) gameboy
|
||||
@ -1613,7 +1608,7 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
|
||||
|
||||
+ (BOOL)canConcurrentlyReadDocumentsOfType:(NSString *)typeName
|
||||
{
|
||||
return YES;
|
||||
return true;
|
||||
}
|
||||
|
||||
- (void)cameraRequestUpdate
|
||||
@ -1754,14 +1749,14 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
|
||||
window_rect.size.height = 512 + height_diff + 48;
|
||||
break;
|
||||
case 3:
|
||||
window_rect.size.height = 20 * 16 + height_diff + 24;
|
||||
window_rect.size.height = 20 * 16 + height_diff + 34;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
window_rect.origin.y -= window_rect.size.height;
|
||||
[self.vramWindow setFrame:window_rect display:YES animate:YES];
|
||||
[self.vramWindow setFrame:window_rect display:true animate:true];
|
||||
}
|
||||
|
||||
- (void)mouseDidLeaveImageView:(GBImageView *)view
|
||||
@ -1856,7 +1851,7 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
|
||||
case 0:
|
||||
return [Document imageFromData:[NSData dataWithBytesNoCopy:oamInfo[row].image
|
||||
length:64 * 4 * 2
|
||||
freeWhenDone:NO]
|
||||
freeWhenDone:false]
|
||||
width:8
|
||||
height:oamHeight
|
||||
scale:16.0/oamHeight];
|
||||
@ -1899,7 +1894,7 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
|
||||
|
||||
- (BOOL)tableView:(NSTableView *)tableView shouldEditTableColumn:(nullable NSTableColumn *)tableColumn row:(NSInteger)row
|
||||
{
|
||||
return NO;
|
||||
return false;
|
||||
}
|
||||
|
||||
- (IBAction)showVRAMViewer:(id)sender
|
||||
@ -1929,7 +1924,7 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
|
||||
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 setFrame:frame display:NO animate: self.printerFeedWindow.isVisible];
|
||||
[self.printerFeedWindow setFrame:frame display:false animate: self.printerFeedWindow.isVisible];
|
||||
[self.printerFeedWindow orderFront:NULL];
|
||||
});
|
||||
|
||||
@ -1960,8 +1955,8 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
|
||||
NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithCGImage:cgRef];
|
||||
[imageRep setSize:(NSSize){160, self.feedImageView.image.size.height / 2}];
|
||||
NSData *data = [imageRep representationUsingType:NSBitmapImageFileTypePNG properties:@{}];
|
||||
[data writeToURL:savePanel.URL atomically:NO];
|
||||
[self.printerFeedWindow setIsVisible:NO];
|
||||
[data writeToURL:savePanel.URL atomically:false];
|
||||
[self.printerFeedWindow setIsVisible:false];
|
||||
}
|
||||
if (shouldResume) {
|
||||
[self start];
|
||||
@ -2082,9 +2077,9 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
|
||||
- (BOOL)splitView:(GBSplitView *)splitView canCollapseSubview:(NSView *)subview;
|
||||
{
|
||||
if ([[splitView arrangedSubviews] lastObject] == subview) {
|
||||
return YES;
|
||||
return true;
|
||||
}
|
||||
return NO;
|
||||
return false;
|
||||
}
|
||||
|
||||
- (CGFloat)splitView:(GBSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMinimumPosition ofSubviewAt:(NSInteger)dividerIndex
|
||||
@ -2100,9 +2095,9 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
|
||||
- (BOOL)splitView:(GBSplitView *)splitView shouldAdjustSizeOfSubview:(NSView *)view
|
||||
{
|
||||
if ([[splitView arrangedSubviews] lastObject] == view) {
|
||||
return NO;
|
||||
return false;
|
||||
}
|
||||
return YES;
|
||||
return true;
|
||||
}
|
||||
|
||||
- (void)splitViewDidResizeSubviews:(NSNotification *)notification
|
||||
@ -2206,4 +2201,115 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
|
||||
{
|
||||
return &gb;
|
||||
}
|
||||
|
||||
- (NSImage *)takeScreenshot
|
||||
{
|
||||
NSImage *ret = nil;
|
||||
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"GBFilterScreenshots"]) {
|
||||
ret = [_view renderToImage];
|
||||
}
|
||||
if (!ret) {
|
||||
ret = [Document imageFromData:[NSData dataWithBytesNoCopy:_view.currentBuffer
|
||||
length:GB_get_screen_width(&gb) * GB_get_screen_height(&gb) * 4
|
||||
freeWhenDone:false]
|
||||
width:GB_get_screen_width(&gb)
|
||||
height:GB_get_screen_height(&gb)
|
||||
scale:1.0];
|
||||
}
|
||||
[ret lockFocus];
|
||||
NSBitmapImageRep *bitmapRep = [[NSBitmapImageRep alloc] initWithFocusedViewRect:NSMakeRect(0, 0,
|
||||
ret.size.width, ret.size.height)];
|
||||
[ret unlockFocus];
|
||||
ret = [[NSImage alloc] initWithSize:ret.size];
|
||||
[ret addRepresentation:bitmapRep];
|
||||
return ret;
|
||||
}
|
||||
|
||||
- (NSString *)screenshotFilename
|
||||
{
|
||||
NSDate *date = [NSDate date];
|
||||
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
|
||||
dateFormatter.dateStyle = NSDateFormatterLongStyle;
|
||||
dateFormatter.timeStyle = NSDateFormatterMediumStyle;
|
||||
return [[NSString stringWithFormat:@"%@ – %@.png",
|
||||
self.fileURL.lastPathComponent.stringByDeletingPathExtension,
|
||||
[dateFormatter stringFromDate:date]] stringByReplacingOccurrencesOfString:@":" withString:@"."]; // Gotta love Mac OS Classic
|
||||
|
||||
}
|
||||
|
||||
- (IBAction)saveScreenshot:(id)sender
|
||||
{
|
||||
NSString *folder = [[NSUserDefaults standardUserDefaults] stringForKey:@"GBScreenshotFolder"];
|
||||
BOOL isDirectory = false;
|
||||
if (folder) {
|
||||
[[NSFileManager defaultManager] fileExistsAtPath:folder isDirectory:&isDirectory];
|
||||
}
|
||||
if (!folder) {
|
||||
bool shouldResume = running;
|
||||
[self stop];
|
||||
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
|
||||
openPanel.canChooseFiles = false;
|
||||
openPanel.canChooseDirectories = true;
|
||||
openPanel.message = @"Choose a folder for screenshots";
|
||||
[openPanel beginSheetModalForWindow:self.mainWindow completionHandler:^(NSInteger result) {
|
||||
if (result == NSModalResponseOK) {
|
||||
[[NSUserDefaults standardUserDefaults] setObject:openPanel.URL.path
|
||||
forKey:@"GBScreenshotFolder"];
|
||||
[self saveScreenshot:sender];
|
||||
}
|
||||
if (shouldResume) {
|
||||
[self start];
|
||||
}
|
||||
|
||||
}];
|
||||
return;
|
||||
}
|
||||
NSImage *image = [self takeScreenshot];
|
||||
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
|
||||
dateFormatter.dateStyle = NSDateFormatterLongStyle;
|
||||
dateFormatter.timeStyle = NSDateFormatterMediumStyle;
|
||||
NSString *filename = [self screenshotFilename];
|
||||
filename = [folder stringByAppendingPathComponent:filename];
|
||||
unsigned i = 2;
|
||||
while ([[NSFileManager defaultManager] fileExistsAtPath:filename]) {
|
||||
filename = [[filename stringByDeletingPathExtension] stringByAppendingFormat:@" %d.png", i++];
|
||||
}
|
||||
|
||||
NSBitmapImageRep *imageRep = (NSBitmapImageRep *)image.representations.firstObject;
|
||||
NSData *data = [imageRep representationUsingType:NSBitmapImageFileTypePNG properties:@{}];
|
||||
[data writeToFile:filename atomically:false];
|
||||
[self.osdView displayText:@"Screenshot saved"];
|
||||
}
|
||||
|
||||
- (IBAction)saveScreenshotAs:(id)sender
|
||||
{
|
||||
bool shouldResume = running;
|
||||
[self stop];
|
||||
NSImage *image = [self takeScreenshot];
|
||||
NSSavePanel *savePanel = [NSSavePanel savePanel];
|
||||
[savePanel setNameFieldStringValue:[self screenshotFilename]];
|
||||
[savePanel beginSheetModalForWindow:self.mainWindow completionHandler:^(NSInteger result) {
|
||||
if (result == NSModalResponseOK) {
|
||||
[savePanel orderOut:self];
|
||||
NSBitmapImageRep *imageRep = (NSBitmapImageRep *)image.representations.firstObject;
|
||||
NSData *data = [imageRep representationUsingType:NSBitmapImageFileTypePNG properties:@{}];
|
||||
[data writeToURL:savePanel.URL atomically:false];
|
||||
[[NSUserDefaults standardUserDefaults] setObject:savePanel.URL.path.stringByDeletingLastPathComponent
|
||||
forKey:@"GBScreenshotFolder"];
|
||||
}
|
||||
if (shouldResume) {
|
||||
[self start];
|
||||
}
|
||||
}];
|
||||
[self.osdView displayText:@"Screenshot saved"];
|
||||
}
|
||||
|
||||
- (IBAction)copyScreenshot:(id)sender
|
||||
{
|
||||
NSImage *image = [self takeScreenshot];
|
||||
[[NSPasteboard generalPasteboard] clearContents];
|
||||
[[NSPasteboard generalPasteboard] writeObjects:@[image]];
|
||||
[self.osdView displayText:@"Screenshot copied"];
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -1004,7 +1004,7 @@
|
||||
</buttonCell>
|
||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
</tableColumn>
|
||||
<tableColumn width="50" minWidth="40" maxWidth="1000" id="9DZ-oW-Scx">
|
||||
<tableColumn width="60" minWidth="40" maxWidth="1000" id="9DZ-oW-Scx">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Enabled">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -1029,7 +1029,7 @@
|
||||
</textFieldCell>
|
||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
</tableColumn>
|
||||
<tableColumn editable="NO" width="144" minWidth="40" maxWidth="1000" id="ACq-gU-K36">
|
||||
<tableColumn editable="NO" width="134" minWidth="40" maxWidth="1000" id="ACq-gU-K36">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Action">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -90,7 +90,7 @@ static OSStatus render(
|
||||
{
|
||||
OSErr err = AudioOutputUnitStart(audioUnit);
|
||||
NSAssert1(err == noErr, @"Error starting unit: %hd", err);
|
||||
_playing = YES;
|
||||
_playing = true;
|
||||
|
||||
}
|
||||
|
||||
@ -98,7 +98,7 @@ static OSStatus render(
|
||||
-(void) stop
|
||||
{
|
||||
AudioOutputUnitStop(audioUnit);
|
||||
_playing = NO;
|
||||
_playing = false;
|
||||
}
|
||||
|
||||
-(void) dealloc
|
||||
@ -108,4 +108,4 @@ static OSStatus render(
|
||||
AudioComponentInstanceDispose(audioUnit);
|
||||
}
|
||||
|
||||
@end
|
||||
@end
|
||||
|
@ -5,12 +5,12 @@
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
self.wantsLayer = YES;
|
||||
self.wantsLayer = true;
|
||||
}
|
||||
|
||||
- (BOOL)wantsUpdateLayer
|
||||
{
|
||||
return YES;
|
||||
return true;
|
||||
}
|
||||
|
||||
- (void)updateLayer
|
||||
|
@ -114,7 +114,7 @@
|
||||
return _fieldEditor;
|
||||
}
|
||||
_fieldEditor = [[GBCheatTextView alloc] initWithFrame:controlView.frame];
|
||||
_fieldEditor.fieldEditor = YES;
|
||||
_fieldEditor.fieldEditor = true;
|
||||
_fieldEditor.usesAddressFormat = self.usesAddressFormat;
|
||||
return _fieldEditor;
|
||||
}
|
||||
|
@ -52,7 +52,7 @@
|
||||
if (row >= cheatCount) {
|
||||
switch (columnIndex) {
|
||||
case 0:
|
||||
return @(YES);
|
||||
return @YES;
|
||||
|
||||
case 1:
|
||||
return @NO;
|
||||
@ -67,7 +67,7 @@
|
||||
|
||||
switch (columnIndex) {
|
||||
case 0:
|
||||
return @(NO);
|
||||
return @NO;
|
||||
|
||||
case 1:
|
||||
return @(cheats[row]->enabled);
|
||||
|
@ -13,13 +13,13 @@ static inline double scale_channel(uint8_t x)
|
||||
|
||||
- (void)setObjectValue:(id)objectValue
|
||||
{
|
||||
|
||||
_integerValue = [objectValue integerValue];
|
||||
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]
|
||||
NSForegroundColorAttributeName: r * 3 + g * 4 + b * 2 > 120? [NSColor blackColor] : [NSColor whiteColor],
|
||||
NSFontAttributeName: [NSFont userFixedPitchFontOfSize:12]
|
||||
}];
|
||||
}
|
||||
|
||||
@ -36,13 +36,14 @@ static inline double scale_channel(uint8_t x)
|
||||
|
||||
- (NSColor *) backgroundColor
|
||||
{
|
||||
/* Todo: color correction */
|
||||
uint16_t color = self.integerValue;
|
||||
return [NSColor colorWithRed:scale_channel(color) green:scale_channel(color >> 5) blue:scale_channel(color >> 10) alpha:1.0];
|
||||
}
|
||||
|
||||
- (BOOL)drawsBackground
|
||||
{
|
||||
return YES;
|
||||
return true;
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -34,6 +34,6 @@
|
||||
- (void) filterChanged
|
||||
{
|
||||
self.shader = nil;
|
||||
[self setNeedsDisplay:YES];
|
||||
[self setNeedsDisplay:true];
|
||||
}
|
||||
@end
|
||||
|
@ -28,4 +28,5 @@
|
||||
@property (nonatomic, weak) IBOutlet NSButton *autoUpdatesCheckbox;
|
||||
@property (weak) IBOutlet NSSlider *volumeSlider;
|
||||
@property (weak) IBOutlet NSButton *OSDCheckbox;
|
||||
@property (weak) IBOutlet NSButton *screenshotFilterCheckbox;
|
||||
@end
|
||||
|
@ -2,6 +2,7 @@
|
||||
#import "NSString+StringForKey.h"
|
||||
#import "GBButtons.h"
|
||||
#import "BigSurToolbar.h"
|
||||
#import "GBViewMetal.h"
|
||||
#import <Carbon/Carbon.h>
|
||||
|
||||
@implementation GBPreferencesWindow
|
||||
@ -32,6 +33,7 @@
|
||||
NSSlider *_volumeSlider;
|
||||
NSButton *_autoUpdatesCheckbox;
|
||||
NSButton *_OSDCheckbox;
|
||||
NSButton *_screenshotFilterCheckbox;
|
||||
}
|
||||
|
||||
+ (NSArray *)filterList
|
||||
@ -67,8 +69,8 @@
|
||||
- (void)close
|
||||
{
|
||||
joystick_configuration_state = -1;
|
||||
[self.configureJoypadButton setEnabled:YES];
|
||||
[self.skipButton setEnabled:NO];
|
||||
[self.configureJoypadButton setEnabled:true];
|
||||
[self.skipButton setEnabled:false];
|
||||
[self.configureJoypadButton setTitle:@"Configure Controller"];
|
||||
[super close];
|
||||
}
|
||||
@ -266,12 +268,12 @@
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
is_button_being_modified = true;
|
||||
button_being_modified = row;
|
||||
tableView.enabled = NO;
|
||||
self.playerListButton.enabled = NO;
|
||||
tableView.enabled = false;
|
||||
self.playerListButton.enabled = false;
|
||||
[tableView reloadData];
|
||||
[self makeFirstResponder:self];
|
||||
});
|
||||
return NO;
|
||||
return false;
|
||||
}
|
||||
|
||||
-(void)keyDown:(NSEvent *)theEvent
|
||||
@ -287,8 +289,8 @@
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] setInteger:theEvent.keyCode
|
||||
forKey:button_to_preference_name(button_being_modified, self.playerListButton.selectedTag)];
|
||||
self.controlsTableView.enabled = YES;
|
||||
self.playerListButton.enabled = YES;
|
||||
self.controlsTableView.enabled = true;
|
||||
self.playerListButton.enabled = true;
|
||||
[self.controlsTableView reloadData];
|
||||
[self makeFirstResponder:self.controlsTableView];
|
||||
}
|
||||
@ -408,8 +410,8 @@
|
||||
|
||||
- (IBAction) configureJoypad:(id)sender
|
||||
{
|
||||
[self.configureJoypadButton setEnabled:NO];
|
||||
[self.skipButton setEnabled:YES];
|
||||
[self.configureJoypadButton setEnabled:false];
|
||||
[self.skipButton setEnabled:true];
|
||||
joystick_being_configured = nil;
|
||||
[self advanceConfigurationStateMachine];
|
||||
}
|
||||
@ -430,8 +432,8 @@
|
||||
}
|
||||
else {
|
||||
joystick_configuration_state = -1;
|
||||
[self.configureJoypadButton setEnabled:YES];
|
||||
[self.skipButton setEnabled:NO];
|
||||
[self.configureJoypadButton setEnabled:true];
|
||||
[self.skipButton setEnabled:false];
|
||||
[self.configureJoypadButton setTitle:@"Configure Joypad"];
|
||||
}
|
||||
}
|
||||
@ -563,8 +565,8 @@
|
||||
- (IBAction)selectOtherBootROMFolder:(id)sender
|
||||
{
|
||||
NSOpenPanel *panel = [[NSOpenPanel alloc] init];
|
||||
[panel setCanChooseDirectories:YES];
|
||||
[panel setCanChooseFiles:NO];
|
||||
[panel setCanChooseDirectories:true];
|
||||
[panel setCanChooseFiles:false];
|
||||
[panel setPrompt:@"Select"];
|
||||
[panel setDirectoryURL:[[NSUserDefaults standardUserDefaults] URLForKey:@"GBBootROMsFolder"]];
|
||||
[panel beginSheetModalForWindow:self completionHandler:^(NSModalResponse result) {
|
||||
@ -588,12 +590,12 @@
|
||||
[self.bootROMsFolderItem setTitle:[url lastPathComponent]];
|
||||
NSImage *icon = [[NSWorkspace sharedWorkspace] iconForFile:[url path]];
|
||||
[icon setSize:NSMakeSize(16, 16)];
|
||||
[self.bootROMsFolderItem setHidden:NO];
|
||||
[self.bootROMsFolderItem setHidden:false];
|
||||
[self.bootROMsFolderItem setImage:icon];
|
||||
[self.bootROMsButton selectItemAtIndex:1];
|
||||
}
|
||||
else {
|
||||
[self.bootROMsFolderItem setHidden:YES];
|
||||
[self.bootROMsFolderItem setHidden:true];
|
||||
[self.bootROMsButton selectItemAtIndex:0];
|
||||
}
|
||||
}
|
||||
@ -766,4 +768,27 @@
|
||||
forKey:@"GBOSDEnabled"];
|
||||
|
||||
}
|
||||
|
||||
- (IBAction)changeFilterScreenshots:(id)sender
|
||||
{
|
||||
[[NSUserDefaults standardUserDefaults] setBool:[(NSButton *)sender state] == NSOnState
|
||||
forKey:@"GBFilterScreenshots"];
|
||||
}
|
||||
|
||||
- (NSButton *)screenshotFilterCheckbox
|
||||
{
|
||||
return _screenshotFilterCheckbox;
|
||||
}
|
||||
|
||||
- (void)setScreenshotFilterCheckbox:(NSButton *)screenshotFilterCheckbox
|
||||
{
|
||||
_screenshotFilterCheckbox = screenshotFilterCheckbox;
|
||||
if (![GBViewMetal isSupported]) {
|
||||
[_screenshotFilterCheckbox setEnabled:false];
|
||||
}
|
||||
else {
|
||||
[_screenshotFilterCheckbox setState: [[NSUserDefaults standardUserDefaults] boolForKey:@"GBFilterScreenshots"]];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -8,7 +8,7 @@
|
||||
- (void)setDividerColor:(NSColor *)color
|
||||
{
|
||||
_dividerColor = color;
|
||||
[self setNeedsDisplay:YES];
|
||||
[self setNeedsDisplay:true];
|
||||
}
|
||||
|
||||
- (NSColor *)dividerColor
|
||||
|
@ -17,7 +17,7 @@
|
||||
return field_editor;
|
||||
}
|
||||
field_editor = [[GBTerminalTextView alloc] init];
|
||||
[field_editor setFieldEditor:YES];
|
||||
[field_editor setFieldEditor:true];
|
||||
field_editor.gb = self.gb;
|
||||
return field_editor;
|
||||
}
|
||||
@ -109,7 +109,7 @@
|
||||
[self updateReverseSearch];
|
||||
}
|
||||
else {
|
||||
[self setNeedsDisplay:YES];
|
||||
[self setNeedsDisplay:true];
|
||||
reverse_search_mode = true;
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ typedef enum {
|
||||
@property (nonatomic, weak) IBOutlet Document *document;
|
||||
@property (nonatomic) GB_gameboy_t *gb;
|
||||
@property (nonatomic) GB_frame_blending_mode_t frameBlendingMode;
|
||||
@property (nonatomic, getter=isMouseHidingEnabled) BOOL mouseHidingEnabled;
|
||||
@property (nonatomic, getter=isMouseHidingEnabled) bool mouseHidingEnabled;
|
||||
@property (nonatomic) bool isRewinding;
|
||||
@property (nonatomic, strong) NSView *internalView;
|
||||
@property (weak) GBOSDView *osdView;
|
||||
@ -27,4 +27,5 @@ typedef enum {
|
||||
- (uint32_t *)previousBuffer;
|
||||
- (void)screenSizeChanged;
|
||||
- (void)setRumble: (double)amp;
|
||||
- (NSImage *)renderToImage;
|
||||
@end
|
||||
|
@ -106,9 +106,9 @@ static const uint8_t workboy_vk_to_key[] = {
|
||||
{
|
||||
uint32_t *image_buffers[3];
|
||||
unsigned char current_buffer;
|
||||
BOOL mouse_hidden;
|
||||
bool mouse_hidden;
|
||||
NSTrackingArea *tracking_area;
|
||||
BOOL _mouseHidingEnabled;
|
||||
bool _mouseHidingEnabled;
|
||||
bool axisActive[2];
|
||||
bool underclockKeyDown;
|
||||
double clockMultiplier;
|
||||
@ -183,7 +183,7 @@ static const uint8_t workboy_vk_to_key[] = {
|
||||
- (void) setFrameBlendingMode:(GB_frame_blending_mode_t)frameBlendingMode
|
||||
{
|
||||
_frameBlendingMode = frameBlendingMode;
|
||||
[self setNeedsDisplay:YES];
|
||||
[self setNeedsDisplay:true];
|
||||
}
|
||||
|
||||
|
||||
@ -585,7 +585,7 @@ static const uint8_t workboy_vk_to_key[] = {
|
||||
|
||||
- (BOOL)acceptsFirstResponder
|
||||
{
|
||||
return YES;
|
||||
return true;
|
||||
}
|
||||
|
||||
- (void)mouseEntered:(NSEvent *)theEvent
|
||||
@ -610,7 +610,7 @@ static const uint8_t workboy_vk_to_key[] = {
|
||||
[super mouseExited:theEvent];
|
||||
}
|
||||
|
||||
- (void)setMouseHidingEnabled:(BOOL)mouseHidingEnabled
|
||||
- (void)setMouseHidingEnabled:(bool)mouseHidingEnabled
|
||||
{
|
||||
if (mouseHidingEnabled == _mouseHidingEnabled) return;
|
||||
|
||||
@ -625,7 +625,7 @@ static const uint8_t workboy_vk_to_key[] = {
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isMouseHidingEnabled
|
||||
- (bool)isMouseHidingEnabled
|
||||
{
|
||||
return _mouseHidingEnabled;
|
||||
}
|
||||
@ -658,7 +658,7 @@ static const uint8_t workboy_vk_to_key[] = {
|
||||
|
||||
if ( [[pboard types] containsObject:NSURLPboardType] ) {
|
||||
NSURL *fileURL = [NSURL URLFromPasteboard:pboard];
|
||||
if (GB_is_stave_state(fileURL.fileSystemRepresentation)) {
|
||||
if (GB_is_save_state(fileURL.fileSystemRepresentation)) {
|
||||
return NSDragOperationGeneric;
|
||||
}
|
||||
}
|
||||
@ -677,4 +677,10 @@ static const uint8_t workboy_vk_to_key[] = {
|
||||
return false;
|
||||
}
|
||||
|
||||
- (NSImage *)renderToImage;
|
||||
{
|
||||
/* Not going to support this on OpenGL, OpenGL is too much of a terrible API for me
|
||||
to bother figuring out how the hell something so trivial can be done. */
|
||||
return nil;
|
||||
}
|
||||
@end
|
||||
|
@ -19,7 +19,7 @@
|
||||
NSOpenGLContext *context = [[NSOpenGLContext alloc] initWithFormat:pf shareContext:nil];
|
||||
|
||||
self.internalView = [[GBOpenGLView alloc] initWithFrame:self.frame pixelFormat:pf];
|
||||
((GBOpenGLView *)self.internalView).wantsBestResolutionOpenGLSurface = YES;
|
||||
((GBOpenGLView *)self.internalView).wantsBestResolutionOpenGLSurface = true;
|
||||
((GBOpenGLView *)self.internalView).openGLContext = context;
|
||||
}
|
||||
|
||||
@ -27,8 +27,8 @@
|
||||
{
|
||||
[super flip];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.internalView setNeedsDisplay:YES];
|
||||
[self setNeedsDisplay:YES];
|
||||
[self.internalView setNeedsDisplay:true];
|
||||
[self setNeedsDisplay:true];
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
#import <CoreImage/CoreImage.h>
|
||||
#import "GBViewMetal.h"
|
||||
#pragma clang diagnostic ignored "-Wpartial-availability"
|
||||
|
||||
@ -51,8 +52,9 @@ static const vector_float2 rect[] =
|
||||
MTKView *view = [[MTKView alloc] initWithFrame:self.frame device:(device = MTLCreateSystemDefaultDevice())];
|
||||
view.delegate = self;
|
||||
self.internalView = view;
|
||||
view.paused = YES;
|
||||
view.enableSetNeedsDisplay = YES;
|
||||
view.paused = true;
|
||||
view.enableSetNeedsDisplay = true;
|
||||
view.framebufferOnly = false;
|
||||
|
||||
vertices = [device newBufferWithBytes:rect
|
||||
length:sizeof(rect)
|
||||
@ -92,7 +94,7 @@ static const vector_float2 rect[] =
|
||||
withString:scaler_source];
|
||||
|
||||
MTLCompileOptions *options = [[MTLCompileOptions alloc] init];
|
||||
options.fastMathEnabled = YES;
|
||||
options.fastMathEnabled = true;
|
||||
id<MTLLibrary> library = [device newLibraryWithSource:shader_source
|
||||
options:options
|
||||
error:&error];
|
||||
@ -208,8 +210,23 @@ static const vector_float2 rect[] =
|
||||
{
|
||||
[super flip];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[(MTKView *)self.internalView setNeedsDisplay:YES];
|
||||
[(MTKView *)self.internalView setNeedsDisplay:true];
|
||||
});
|
||||
}
|
||||
|
||||
- (NSImage *)renderToImage
|
||||
{
|
||||
CIImage *ciImage = [CIImage imageWithMTLTexture:[[(MTKView *)self.internalView currentDrawable] texture]
|
||||
options:@{
|
||||
kCIImageColorSpace: (__bridge_transfer id)CGColorSpaceCreateDeviceRGB()
|
||||
}];
|
||||
ciImage = [ciImage imageByApplyingTransform:CGAffineTransformTranslate(CGAffineTransformMakeScale(1, -1),
|
||||
0, ciImage.extent.size.height)];
|
||||
CIContext *context = [CIContext context];
|
||||
CGImageRef cgImage = [context createCGImage:ciImage fromRect:ciImage.extent];
|
||||
NSImage *ret = [[NSImage alloc] initWithCGImage:cgImage size:self.internalView.bounds.size];
|
||||
CGImageRelease(cgImage);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -10,7 +10,7 @@ static GBWarningPopover *lastPopover;
|
||||
lastPopover = [[self alloc] init];
|
||||
|
||||
[lastPopover setBehavior:NSPopoverBehaviorApplicationDefined];
|
||||
[lastPopover setAnimates:YES];
|
||||
[lastPopover setAnimates:true];
|
||||
lastPopover.contentViewController = [[NSViewController alloc] initWithNibName:@"PopoverView" bundle:nil];
|
||||
NSTextField *field = (NSTextField *)lastPopover.contentViewController.view;
|
||||
[field setStringValue:contents];
|
||||
@ -20,7 +20,7 @@ static GBWarningPopover *lastPopover;
|
||||
[lastPopover setContentSize:textSize];
|
||||
|
||||
if (!view.window.isVisible) {
|
||||
[view.window setIsVisible:YES];
|
||||
[view.window setIsVisible:true];
|
||||
}
|
||||
|
||||
[lastPopover showRelativeToRect:view.bounds
|
||||
|
@ -316,6 +316,23 @@
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="5GS-tt-E0a"/>
|
||||
<menuItem title="Save Screenshot" keyEquivalent="s" id="0J3-yf-iXs">
|
||||
<connections>
|
||||
<action selector="saveScreenshot:" target="-1" id="gJd-ml-J8p"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Save Screenshot As…" alternate="YES" keyEquivalent="s" id="98X-Fp-Uny">
|
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="saveScreenshotAs:" target="-1" id="Cxc-Gx-ql1"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Copy Screenshot" keyEquivalent="S" id="vbX-pB-QC8">
|
||||
<connections>
|
||||
<action selector="copyScreenshot:" target="-1" id="XJC-EB-HNl"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="zk7-gf-LXN"/>
|
||||
<menuItem title="Game Boy" tag="1" id="g7C-LA-VAr">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
|
@ -24,10 +24,10 @@
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
|
||||
<windowCollectionBehavior key="collectionBehavior" fullScreenAuxiliary="YES"/>
|
||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||
<rect key="contentRect" x="196" y="240" width="292" height="224"/>
|
||||
<rect key="contentRect" x="196" y="240" width="320" height="224"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
|
||||
<view key="contentView" id="EiT-Mj-1SZ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="292" height="224"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="224"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
</view>
|
||||
<toolbar key="toolbar" implicitIdentifier="689472FF-3BCD-4B1F-98F8-989CCB01AE27" autosavesConfiguration="NO" allowsUserCustomization="NO" displayMode="iconAndLabel" sizeMode="regular" id="pYZ-Pe-8hq">
|
||||
@ -90,6 +90,7 @@
|
||||
<outlet property="rewindPopupButton" destination="7fg-Ww-JjR" id="Ka2-TP-B1x"/>
|
||||
<outlet property="rtcPopupButton" destination="tFf-H1-XUL" id="zxb-4h-aqg"/>
|
||||
<outlet property="rumbleModePopupButton" destination="Ogs-xG-b4b" id="vuw-VN-MTp"/>
|
||||
<outlet property="screenshotFilterCheckbox" destination="spQ-Md-OFi" id="f9y-Ek-XQV"/>
|
||||
<outlet property="sgbPopupButton" destination="dza-T7-RkX" id="B0o-Nb-pIH"/>
|
||||
<outlet property="skipButton" destination="d2I-jU-sLb" id="udX-8K-0sK"/>
|
||||
<outlet property="temperatureSlider" destination="NuA-mL-AJZ" id="w11-n7-Bmj"/>
|
||||
@ -98,12 +99,12 @@
|
||||
<point key="canvasLocation" x="183" y="354"/>
|
||||
</window>
|
||||
<customView id="sRK-wO-K6R">
|
||||
<rect key="frame" x="0.0" y="0.0" width="292" height="395"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="421"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="T91-rh-rRp">
|
||||
<rect key="frame" x="18" y="358" width="256" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="18" y="384" width="284" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Graphics filter:" id="pXg-WY-8Q5">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -111,8 +112,8 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6pP-kK-EEC">
|
||||
<rect key="frame" x="30" y="325" width="234" height="26"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="30" y="351" width="262" height="26"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Nearest neighbor (Pixelated)" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="neN-eo-LA7" id="I1w-05-lGl">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
@ -147,9 +148,20 @@
|
||||
<action selector="graphicFilterChanged:" target="QvC-M9-y7g" id="n87-t4-fbV"/>
|
||||
</connections>
|
||||
</popUpButton>
|
||||
<button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="spQ-Md-OFi">
|
||||
<rect key="frame" x="32" y="330" width="259" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="check" title="Apply filter to screenshots" bezelStyle="regularSquare" imagePosition="left" inset="2" id="JbP-bE-w8A">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="changeFilterScreenshots:" target="QvC-M9-y7g" id="t82-FI-eSe"/>
|
||||
</connections>
|
||||
</button>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Wc3-2K-6CD">
|
||||
<rect key="frame" x="18" y="303" width="256" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="18" y="307" width="284" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Color correction:" id="5Si-hz-EK3">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -157,8 +169,8 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="VEz-N4-uP6">
|
||||
<rect key="frame" x="30" y="270" width="234" height="26"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="30" y="274" width="262" height="26"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Disabled" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="D2J-wV-1vu" id="fNJ-Fi-yOm">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
@ -179,9 +191,26 @@
|
||||
<action selector="colorCorrectionChanged:" target="QvC-M9-y7g" id="Oq4-B5-nO6"/>
|
||||
</connections>
|
||||
</popUpButton>
|
||||
<slider verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="NuA-mL-AJZ">
|
||||
<rect key="frame" x="32" y="218" width="259" height="28"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<sliderCell key="cell" continuous="YES" state="on" alignment="left" minValue="-256" maxValue="256" tickMarkPosition="below" numberOfTickMarks="3" sliderType="linear" id="KX7-G9-k0O"/>
|
||||
<connections>
|
||||
<action selector="lightTemperatureChanged:" target="QvC-M9-y7g" id="he8-ib-I3Y"/>
|
||||
</connections>
|
||||
</slider>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cCm-Oa-FbN">
|
||||
<rect key="frame" x="20" y="252" width="280" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Ambient light temperature:" id="Lso-GQ-pBl">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="MLC-Rx-FgO">
|
||||
<rect key="frame" x="18" y="194" width="252" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="18" y="194" width="280" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Frame blending" id="UCa-EO-tzh">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -189,8 +218,8 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="lxk-db-Sxv">
|
||||
<rect key="frame" x="30" y="165" width="234" height="22"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="30" y="165" width="262" height="22"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Disabled" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="iHP-Yz-fiH" id="aQ6-HN-7Aj">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
@ -209,8 +238,8 @@
|
||||
</connections>
|
||||
</popUpButton>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="8fG-zm-hpr">
|
||||
<rect key="frame" x="18" y="143" width="252" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="18" y="143" width="280" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Color palette for monochrome models:" id="LAN-8Y-T7H">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -218,8 +247,8 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Iwr-eI-SD1">
|
||||
<rect key="frame" x="30" y="114" width="234" height="22"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="30" y="114" width="262" height="22"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Greyscale" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="Ajr-5r-iIk" id="rEU-jh-m3j">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
@ -239,8 +268,8 @@
|
||||
</connections>
|
||||
</popUpButton>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="3Kz-cf-5X6">
|
||||
<rect key="frame" x="18" y="92" width="248" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="18" y="92" width="276" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Display border:" id="HZd-qi-yyk">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -248,8 +277,8 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="R9D-FV-bpd">
|
||||
<rect key="frame" x="30" y="60" width="234" height="25"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="30" y="60" width="262" height="25"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Never" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="1" imageScaling="proportionallyDown" inset="2" selectedItem="heL-AV-0az" id="DY9-2D-h1L">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
@ -267,26 +296,9 @@
|
||||
<action selector="displayBorderChanged:" target="QvC-M9-y7g" id="GoA-BU-v3h"/>
|
||||
</connections>
|
||||
</popUpButton>
|
||||
<slider verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="NuA-mL-AJZ">
|
||||
<rect key="frame" x="30" y="218" width="234" height="24"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<sliderCell key="cell" continuous="YES" state="on" alignment="left" minValue="-256" maxValue="256" tickMarkPosition="below" numberOfTickMarks="3" sliderType="linear" id="KX7-G9-k0O"/>
|
||||
<connections>
|
||||
<action selector="lightTemperatureChanged:" target="QvC-M9-y7g" id="he8-ib-I3Y"/>
|
||||
</connections>
|
||||
</slider>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cCm-Oa-FbN">
|
||||
<rect key="frame" x="20" y="248" width="252" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Ambient light temperature:" id="Lso-GQ-pBl">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Vfj-tg-7OP">
|
||||
<rect key="frame" x="18" y="38" width="256" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="18" y="38" width="284" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="check" title="Keep aspect ratio" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="lsj-rC-Eo6">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -296,8 +308,8 @@
|
||||
</connections>
|
||||
</button>
|
||||
<button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="quK-gY-Z6h">
|
||||
<rect key="frame" x="18" y="18" width="252" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="18" y="18" width="280" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="check" title="On-screen display" bezelStyle="regularSquare" imagePosition="left" inset="2" id="oeT-cD-YRw">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -307,15 +319,15 @@
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<point key="canvasLocation" x="-176" y="677.5"/>
|
||||
<point key="canvasLocation" x="-176" y="690.5"/>
|
||||
</customView>
|
||||
<customView id="ymk-46-SX7">
|
||||
<rect key="frame" x="0.0" y="0.0" width="292" height="375"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="375"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="w9w-yX-KxB">
|
||||
<rect key="frame" x="18" y="283" width="256" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="18" y="283" width="284" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Rewinding duration:" id="JaO-5h-ugl">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -323,8 +335,8 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="o3Z-34-FJk">
|
||||
<rect key="frame" x="18" y="228" width="252" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="18" y="228" width="280" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Real Time Clock emulation:" id="Qoi-ub-YtI">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -332,8 +344,8 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="MI2-ql-f6M">
|
||||
<rect key="frame" x="18" y="338" width="256" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="18" y="338" width="284" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Boot ROMs location:" id="nj0-Cb-gEA">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -341,8 +353,8 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Wg8-hJ-df9">
|
||||
<rect key="frame" x="18" y="160" width="256" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="18" y="160" width="284" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Game Boy revision:" id="GIA-ep-SBi">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -350,8 +362,8 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="LFw-Uk-cPR">
|
||||
<rect key="frame" x="30" y="127" width="234" height="26"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="30" y="127" width="262" height="26"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<popUpButtonCell key="cell" type="push" title="DMG-CPU B" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="2" imageScaling="proportionallyDown" inset="2" autoenablesItems="NO" selectedItem="aXT-sE-m5Z" id="FuX-Hc-uO7">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
@ -369,8 +381,8 @@
|
||||
</connections>
|
||||
</popUpButton>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="MAq-1X-Gpo">
|
||||
<rect key="frame" x="16" y="50" width="256" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="16" y="50" width="284" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Game Boy Color revision:" id="edD-t7-vwk">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -378,8 +390,8 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="dlD-sk-SHO">
|
||||
<rect key="frame" x="28" y="17" width="234" height="26"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="28" y="17" width="262" height="26"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<popUpButtonCell key="cell" type="push" title="CPU-CGB E" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="517" imageScaling="proportionallyDown" inset="2" autoenablesItems="NO" selectedItem="3lF-1Q-2SS" id="Edt-1S-dXz">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
@ -399,8 +411,8 @@
|
||||
</connections>
|
||||
</popUpButton>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="tAa-0A-0fP">
|
||||
<rect key="frame" x="18" y="105" width="256" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="18" y="105" width="284" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Super Game Boy model:" id="d0g-rk-FK0">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -408,12 +420,12 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<box verticalHuggingPriority="750" fixedFrame="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="mdm-eW-ia1">
|
||||
<rect key="frame" x="12" y="183" width="268" height="5"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="12" y="183" width="296" height="5"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
</box>
|
||||
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="dza-T7-RkX">
|
||||
<rect key="frame" x="28" y="72" width="234" height="26"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="28" y="72" width="262" height="26"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Super Game Boy (NTSC)" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="4" imageScaling="proportionallyDown" inset="2" autoenablesItems="NO" selectedItem="x5A-7f-ef9" id="2Mt-ci-bB0">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
@ -430,8 +442,8 @@
|
||||
</connections>
|
||||
</popUpButton>
|
||||
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="tFf-H1-XUL">
|
||||
<rect key="frame" x="30" y="199" width="234" height="22"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="30" y="199" width="262" height="22"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Sync to system clock" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="arp-Qi-Xix" id="uRs-Ag-Sbw">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
@ -449,8 +461,8 @@
|
||||
</connections>
|
||||
</popUpButton>
|
||||
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="wC4-aJ-mhQ">
|
||||
<rect key="frame" x="30" y="305" width="234" height="26"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="30" y="305" width="262" height="26"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Use built-in boot ROMs" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="Tnm-SR-ZEm" id="T3Y-Ln-Onl">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
@ -473,8 +485,8 @@
|
||||
</popUpButtonCell>
|
||||
</popUpButton>
|
||||
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="7fg-Ww-JjR">
|
||||
<rect key="frame" x="30" y="250" width="234" height="26"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="30" y="250" width="262" height="26"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Disabled" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="lxQ-4n-kEv" id="lvb-QF-0Ht">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
@ -500,12 +512,12 @@
|
||||
<point key="canvasLocation" x="-501" y="667.5"/>
|
||||
</customView>
|
||||
<customView id="Zn1-Y5-RbR">
|
||||
<rect key="frame" x="0.0" y="0.0" width="292" height="183"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="201"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Dzd-uB-B6l">
|
||||
<rect key="frame" x="18" y="146" width="252" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="18" y="164" width="280" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Volume:" id="IbP-7k-xsZ">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -513,16 +525,16 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<slider verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="LNs-v1-Eki">
|
||||
<rect key="frame" x="40" y="121" width="230" height="19"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="30" y="130" width="261" height="28"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<sliderCell key="cell" continuous="YES" state="on" alignment="left" maxValue="256" doubleValue="256" tickMarkPosition="below" sliderType="linear" id="Xvk-Vj-NHx"/>
|
||||
<connections>
|
||||
<action selector="volumeChanged:" target="QvC-M9-y7g" id="4g3-NK-ay4"/>
|
||||
</connections>
|
||||
</slider>
|
||||
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="T69-6N-dhT">
|
||||
<rect key="frame" x="30" y="65" width="234" height="26"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="30" y="74" width="262" height="26"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Disabled (Keep DC offset)" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="Fgo-0S-zUG" id="om2-Bn-43B">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
@ -541,8 +553,8 @@
|
||||
</connections>
|
||||
</popUpButton>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="WU3-oV-KHO">
|
||||
<rect key="frame" x="18" y="98" width="256" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="18" y="107" width="284" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="High-pass filter:" id="YLF-RL-b2D">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -550,8 +562,8 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="GPt-9I-QBh">
|
||||
<rect key="frame" x="18" y="43" width="252" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="18" y="52" width="280" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Interference volume:" id="I2Q-6U-uIx">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -559,18 +571,18 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<slider verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="FpE-5i-j5L">
|
||||
<rect key="frame" x="30" y="18" width="234" height="19"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="30" y="18" width="261" height="28"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<sliderCell key="cell" continuous="YES" state="on" alignment="left" maxValue="256" tickMarkPosition="below" sliderType="linear" id="Rbx-DU-xYf"/>
|
||||
<connections>
|
||||
<action selector="interferenceVolumeChanged:" target="QvC-M9-y7g" id="HFU-0q-hj1"/>
|
||||
</connections>
|
||||
</slider>
|
||||
</subviews>
|
||||
<point key="canvasLocation" x="-825" y="446.5"/>
|
||||
<point key="canvasLocation" x="-825" y="455.5"/>
|
||||
</customView>
|
||||
<customView id="8TU-6J-NCg">
|
||||
<rect key="frame" x="0.0" y="0.0" width="292" height="467"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="467"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Utu-t4-cLx">
|
||||
@ -583,8 +595,8 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DZu-ts-deW">
|
||||
<rect key="frame" x="18" y="87" width="105" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="18" y="87" width="133" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Enable rumble:" id="QMX-3p-s1Z">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -592,14 +604,14 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<scrollView focusRingType="none" fixedFrame="YES" autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" hasHorizontalScroller="NO" hasVerticalScroller="NO" usesPredominantAxisScrolling="NO" horizontalScrollElasticity="none" verticalScrollElasticity="none" translatesAutoresizingMaskIntoConstraints="NO" id="PBp-dj-EIa">
|
||||
<rect key="frame" x="32" y="208" width="229" height="211"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="32" y="208" width="257" height="211"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<clipView key="contentView" focusRingType="none" ambiguous="YES" drawsBackground="NO" id="AMs-PO-nid">
|
||||
<rect key="frame" x="1" y="1" width="227" height="209"/>
|
||||
<rect key="frame" x="1" y="1" width="255" height="209"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<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="227" height="209"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="255" height="209"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<size key="intercellSpacing" width="3" height="2"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -618,7 +630,7 @@
|
||||
</textFieldCell>
|
||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
</tableColumn>
|
||||
<tableColumn width="105" minWidth="40" maxWidth="1000" id="5VG-zV-WM6">
|
||||
<tableColumn width="133" minWidth="40" maxWidth="1000" id="5VG-zV-WM6">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -650,8 +662,8 @@
|
||||
</scroller>
|
||||
</scrollView>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="fcF-wc-KwM">
|
||||
<rect key="frame" x="30" y="183" width="203" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="30" y="183" width="231" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Controller for multiplayer games:" id="AJA-9b-VKI">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -659,8 +671,8 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="0Az-0R-oNw">
|
||||
<rect key="frame" x="42" y="150" width="222" height="26"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="42" y="150" width="250" height="26"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<popUpButtonCell key="cell" type="push" title="None" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingMiddle" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="hy8-cr-RrE" id="uEC-vN-8Jq">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
@ -675,8 +687,8 @@
|
||||
</connections>
|
||||
</popUpButton>
|
||||
<box verticalHuggingPriority="750" fixedFrame="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="VEc-Ed-Z6f">
|
||||
<rect key="frame" x="12" y="139" width="268" height="5"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="12" y="139" width="296" height="5"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
</box>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ReM-uo-H0r">
|
||||
<rect key="frame" x="227" y="431" width="8" height="17"/>
|
||||
@ -707,8 +719,8 @@
|
||||
</connections>
|
||||
</popUpButton>
|
||||
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Ogs-xG-b4b">
|
||||
<rect key="frame" x="30" y="58" width="234" height="22"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="30" y="58" width="262" height="22"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Never" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="jki-7x-bnM" id="o9b-MH-8kd">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
@ -725,8 +737,8 @@
|
||||
</connections>
|
||||
</popUpButton>
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="RuW-Db-dzW">
|
||||
<rect key="frame" x="18" y="110" width="264" height="25"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="18" y="110" width="292" height="25"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="check" title="Analog turbo and slow-motion controls" bezelStyle="regularSquare" imagePosition="left" lineBreakMode="charWrapping" inset="2" id="Mvp-oc-N3t">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -735,20 +747,9 @@
|
||||
<action selector="changeAnalogControls:" target="QvC-M9-y7g" id="1xR-gY-WKo"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="d2I-jU-sLb">
|
||||
<rect key="frame" x="198" y="13" width="67" height="32"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="push" title="Skip" bezelStyle="rounded" alignment="center" enabled="NO" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="sug-xy-tbw">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="skipButton:" target="QvC-M9-y7g" id="aw8-sw-yJw"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Qa7-Z7-yfO">
|
||||
<rect key="frame" x="26" y="13" width="173" height="32"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="26" y="13" width="204" height="32"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="push" title="Configure a controller" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="GdK-tQ-Wim">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -757,16 +758,27 @@
|
||||
<action selector="configureJoypad:" target="QvC-M9-y7g" id="IfY-Kc-PKU"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="d2I-jU-sLb">
|
||||
<rect key="frame" x="223" y="13" width="72" height="32"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="push" title="Skip" bezelStyle="rounded" alignment="center" enabled="NO" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="sug-xy-tbw">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="skipButton:" target="QvC-M9-y7g" id="aw8-sw-yJw"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<point key="canvasLocation" x="-159" y="1161.5"/>
|
||||
</customView>
|
||||
<customView id="ffn-ie-9C3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="292" height="95"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="95"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ZVh-ob-6wl">
|
||||
<rect key="frame" x="18" y="59" width="256" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="18" y="59" width="284" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="check" title="Check for updates on launch" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="euw-4z-Urd">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -776,12 +788,12 @@
|
||||
</connections>
|
||||
</button>
|
||||
<progressIndicator wantsLayer="YES" fixedFrame="YES" maxValue="100" displayedWhenStopped="NO" indeterminate="YES" controlSize="small" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="fB8-sd-zrh">
|
||||
<rect key="frame" x="257" y="23" width="16" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="285" y="23" width="16" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
|
||||
</progressIndicator>
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="KnI-UA-Nlj">
|
||||
<rect key="frame" x="14" y="13" width="240" height="32"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<rect key="frame" x="14" y="13" width="268" height="32"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="push" title="Check for updates now" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="j8a-EZ-Ef5">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
|
@ -289,6 +289,19 @@ static void update_square_sample(GB_gameboy_t *gb, unsigned index)
|
||||
0);
|
||||
}
|
||||
|
||||
static inline void update_wave_sample(GB_gameboy_t *gb, unsigned cycles)
|
||||
{
|
||||
if (gb->apu.wave_channel.current_sample_index & 1) {
|
||||
update_sample(gb, GB_WAVE,
|
||||
(gb->apu.wave_channel.current_sample_byte & 0xF) >> gb->apu.wave_channel.shift,
|
||||
cycles);
|
||||
}
|
||||
else {
|
||||
update_sample(gb, GB_WAVE,
|
||||
(gb->apu.wave_channel.current_sample_byte >> 4) >> gb->apu.wave_channel.shift,
|
||||
cycles);
|
||||
}
|
||||
}
|
||||
|
||||
/* the effects of NRX2 writes on current volume are not well documented and differ
|
||||
between models and variants. The exact behavior can only be verified on CGB as it
|
||||
@ -516,6 +529,16 @@ void GB_apu_div_event(GB_gameboy_t *gb)
|
||||
if (gb->apu.wave_channel.length_enabled) {
|
||||
if (gb->apu.wave_channel.pulse_length) {
|
||||
if (!--gb->apu.wave_channel.pulse_length) {
|
||||
if (gb->apu.is_active[GB_WAVE] && gb->model == GB_MODEL_AGB) {
|
||||
if (gb->apu.wave_channel.sample_countdown == 0) {
|
||||
gb->apu.wave_channel.current_sample_byte =
|
||||
gb->io_registers[GB_IO_WAV_START + (((gb->apu.wave_channel.current_sample_index + 1) & 0xF) >> 1)];
|
||||
}
|
||||
else if (gb->apu.wave_channel.sample_countdown == 9) {
|
||||
// TODO: wtf?
|
||||
gb->apu.wave_channel.current_sample_byte = gb->io_registers[GB_IO_WAV_START];
|
||||
}
|
||||
}
|
||||
gb->apu.is_active[GB_WAVE] = false;
|
||||
update_sample(gb, GB_WAVE, 0, 0);
|
||||
}
|
||||
@ -583,6 +606,12 @@ void GB_apu_run(GB_gameboy_t *gb)
|
||||
gb->apu.apu_cycles = 0;
|
||||
if (!cycles) return;
|
||||
|
||||
if (unlikely(gb->apu.channel_3_delayed_bugged_read)) {
|
||||
gb->apu.channel_3_delayed_bugged_read = false;
|
||||
gb->apu.wave_channel.current_sample_byte =
|
||||
gb->io_registers[GB_IO_WAV_START + (gb->address_bus & 0xF)];
|
||||
}
|
||||
|
||||
bool start_ch4 = false;
|
||||
if (likely(!gb->stopped || GB_is_cgb(gb))) {
|
||||
if (gb->apu.channel_4_dmg_delayed_start) {
|
||||
@ -665,11 +694,9 @@ void GB_apu_run(GB_gameboy_t *gb)
|
||||
gb->apu.wave_channel.sample_countdown = gb->apu.wave_channel.sample_length ^ 0x7FF;
|
||||
gb->apu.wave_channel.current_sample_index++;
|
||||
gb->apu.wave_channel.current_sample_index &= 0x1F;
|
||||
gb->apu.wave_channel.current_sample =
|
||||
gb->apu.wave_channel.wave_form[gb->apu.wave_channel.current_sample_index];
|
||||
update_sample(gb, GB_WAVE,
|
||||
gb->apu.wave_channel.current_sample >> gb->apu.wave_channel.shift,
|
||||
cycles - cycles_left);
|
||||
gb->apu.wave_channel.current_sample_byte =
|
||||
gb->io_registers[GB_IO_WAV_START + (gb->apu.wave_channel.current_sample_index >> 1)];
|
||||
update_wave_sample(gb, cycles - cycles_left);
|
||||
gb->apu.wave_channel.wave_form_just_read = true;
|
||||
}
|
||||
if (cycles_left) {
|
||||
@ -677,6 +704,23 @@ void GB_apu_run(GB_gameboy_t *gb)
|
||||
gb->apu.wave_channel.wave_form_just_read = false;
|
||||
}
|
||||
}
|
||||
else if (gb->apu.wave_channel.enable && gb->apu.channel_3_pulsed && gb->model < GB_MODEL_AGB) {
|
||||
uint8_t cycles_left = cycles;
|
||||
while (unlikely(cycles_left > gb->apu.wave_channel.sample_countdown)) {
|
||||
cycles_left -= gb->apu.wave_channel.sample_countdown + 1;
|
||||
gb->apu.wave_channel.sample_countdown = gb->apu.wave_channel.sample_length ^ 0x7FF;
|
||||
if (cycles_left) {
|
||||
gb->apu.wave_channel.current_sample_byte =
|
||||
gb->io_registers[GB_IO_WAV_START + (gb->address_bus & 0xF)];
|
||||
}
|
||||
else {
|
||||
gb->apu.channel_3_delayed_bugged_read = true;
|
||||
}
|
||||
}
|
||||
if (cycles_left) {
|
||||
gb->apu.wave_channel.sample_countdown -= cycles_left;
|
||||
}
|
||||
}
|
||||
|
||||
// The noise channel can step even if inactive on the DMG
|
||||
if (gb->apu.is_active[GB_NOISE] || !GB_is_cgb(gb)) {
|
||||
@ -729,12 +773,8 @@ void GB_apu_run(GB_gameboy_t *gb)
|
||||
void GB_apu_init(GB_gameboy_t *gb)
|
||||
{
|
||||
memset(&gb->apu, 0, sizeof(gb->apu));
|
||||
/* Restore the wave form */
|
||||
for (unsigned reg = GB_IO_WAV_START; reg <= GB_IO_WAV_END; reg++) {
|
||||
gb->apu.wave_channel.wave_form[(reg - GB_IO_WAV_START) * 2] = gb->io_registers[reg] >> 4;
|
||||
gb->apu.wave_channel.wave_form[(reg - GB_IO_WAV_START) * 2 + 1] = gb->io_registers[reg] & 0xF;
|
||||
}
|
||||
gb->apu.lf_div = 1;
|
||||
gb->apu.wave_channel.shift = 4;
|
||||
/* APU glitch: When turning the APU on while DIV's bit 4 (or 5 in double speed mode) is on,
|
||||
the first DIV/APU event is skipped. */
|
||||
if (gb->div_counter & (gb->cgb_double_speed? 0x2000 : 0x1000)) {
|
||||
@ -922,7 +962,7 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
|
||||
}
|
||||
|
||||
if (reg >= GB_IO_WAV_START && reg <= GB_IO_WAV_END && gb->apu.is_active[GB_WAVE]) {
|
||||
if (!GB_is_cgb(gb) && !gb->apu.wave_channel.wave_form_just_read) {
|
||||
if ((!GB_is_cgb(gb) && !gb->apu.wave_channel.wave_form_just_read) || gb->model == GB_MODEL_AGB) {
|
||||
return;
|
||||
}
|
||||
reg = GB_IO_WAV_START + gb->apu.wave_channel.current_sample_index / 2;
|
||||
@ -1059,7 +1099,6 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
|
||||
if (!(value & 4) && !(((gb->apu.square_channels[index].sample_countdown - 1) / 2) & 0x400)) {
|
||||
gb->apu.square_channels[index].current_sample_index++;
|
||||
gb->apu.square_channels[index].current_sample_index &= 0x7;
|
||||
gb->apu.is_active[index] = true;
|
||||
}
|
||||
/* Todo: verify with the schematics what's going on in here */
|
||||
else if (gb->apu.square_channels[index].sample_length == 0x7FF &&
|
||||
@ -1150,6 +1189,16 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
|
||||
case GB_IO_NR30:
|
||||
gb->apu.wave_channel.enable = value & 0x80;
|
||||
if (!gb->apu.wave_channel.enable) {
|
||||
gb->apu.channel_3_pulsed = false;
|
||||
if (gb->apu.is_active[GB_WAVE]) {
|
||||
// Todo: I assume this happens on pre-CGB models; test this with an audible test
|
||||
if (gb->apu.wave_channel.sample_countdown == 0 && gb->model < GB_MODEL_AGB) {
|
||||
gb->apu.wave_channel.current_sample_byte = gb->io_registers[GB_IO_WAV_START + (gb->pc & 0xF)];
|
||||
}
|
||||
else if (gb->apu.wave_channel.wave_form_just_read && gb->model <= GB_MODEL_CGB_C) {
|
||||
gb->apu.wave_channel.current_sample_byte = gb->io_registers[GB_IO_WAV_START + (GB_IO_NR30 & 0xF)];
|
||||
}
|
||||
}
|
||||
gb->apu.is_active[GB_WAVE] = false;
|
||||
update_sample(gb, GB_WAVE, 0, 0);
|
||||
}
|
||||
@ -1160,7 +1209,7 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
|
||||
case GB_IO_NR32:
|
||||
gb->apu.wave_channel.shift = (uint8_t[]){4, 0, 1, 2}[(value >> 5) & 3];
|
||||
if (gb->apu.is_active[GB_WAVE]) {
|
||||
update_sample(gb, GB_WAVE, gb->apu.wave_channel.current_sample >> gb->apu.wave_channel.shift, 0);
|
||||
update_wave_sample(gb, 0);
|
||||
}
|
||||
break;
|
||||
case GB_IO_NR33:
|
||||
@ -1170,13 +1219,13 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
|
||||
case GB_IO_NR34:
|
||||
gb->apu.wave_channel.sample_length &= 0xFF;
|
||||
gb->apu.wave_channel.sample_length |= (value & 7) << 8;
|
||||
if ((value & 0x80)) {
|
||||
if (value & 0x80) {
|
||||
gb->apu.channel_3_pulsed = true;
|
||||
/* DMG bug: wave RAM gets corrupted if the channel is retriggerred 1 cycle before the APU
|
||||
reads from it. */
|
||||
if (!GB_is_cgb(gb) &&
|
||||
gb->apu.is_active[GB_WAVE] &&
|
||||
gb->apu.wave_channel.sample_countdown == 0 &&
|
||||
gb->apu.wave_channel.enable) {
|
||||
gb->apu.wave_channel.sample_countdown == 0) {
|
||||
unsigned offset = ((gb->apu.wave_channel.current_sample_index + 1) >> 1) & 0xF;
|
||||
|
||||
/* This glitch varies between models and even specific instances:
|
||||
@ -1188,26 +1237,24 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
|
||||
are all deterministic. */
|
||||
if (offset < 4) {
|
||||
gb->io_registers[GB_IO_WAV_START] = gb->io_registers[GB_IO_WAV_START + offset];
|
||||
gb->apu.wave_channel.wave_form[0] = gb->apu.wave_channel.wave_form[offset / 2];
|
||||
gb->apu.wave_channel.wave_form[1] = gb->apu.wave_channel.wave_form[offset / 2 + 1];
|
||||
}
|
||||
else {
|
||||
memcpy(gb->io_registers + GB_IO_WAV_START,
|
||||
gb->io_registers + GB_IO_WAV_START + (offset & ~3),
|
||||
4);
|
||||
memcpy(gb->apu.wave_channel.wave_form,
|
||||
gb->apu.wave_channel.wave_form + (offset & ~3) * 2,
|
||||
8);
|
||||
}
|
||||
}
|
||||
if (!gb->apu.is_active[GB_WAVE]) {
|
||||
gb->apu.wave_channel.current_sample_index = 0;
|
||||
if (gb->apu.is_active[GB_WAVE] && gb->apu.wave_channel.sample_countdown == 0) {
|
||||
gb->apu.wave_channel.current_sample_byte = gb->io_registers[GB_IO_WAV_START];
|
||||
}
|
||||
if (gb->apu.wave_channel.enable) {
|
||||
gb->apu.is_active[GB_WAVE] = true;
|
||||
update_sample(gb, GB_WAVE,
|
||||
gb->apu.wave_channel.current_sample >> gb->apu.wave_channel.shift,
|
||||
(gb->apu.wave_channel.current_sample_byte >> 4) >> gb->apu.wave_channel.shift,
|
||||
0);
|
||||
}
|
||||
gb->apu.wave_channel.sample_countdown = (gb->apu.wave_channel.sample_length ^ 0x7FF) + 3;
|
||||
gb->apu.wave_channel.current_sample_index = 0;
|
||||
if (gb->apu.wave_channel.pulse_length == 0) {
|
||||
gb->apu.wave_channel.pulse_length = 0x100;
|
||||
gb->apu.wave_channel.length_enabled = false;
|
||||
@ -1232,10 +1279,6 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
|
||||
}
|
||||
}
|
||||
gb->apu.wave_channel.length_enabled = value & 0x40;
|
||||
if (gb->apu.is_active[GB_WAVE] && !gb->apu.wave_channel.enable) {
|
||||
gb->apu.is_active[GB_WAVE] = false;
|
||||
update_sample(gb, GB_WAVE, 0, 0);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@ -1401,12 +1444,6 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
|
||||
gb->apu.noise_channel.length_enabled = value & 0x40;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
if (reg >= GB_IO_WAV_START && reg <= GB_IO_WAV_END) {
|
||||
gb->apu.wave_channel.wave_form[(reg - GB_IO_WAV_START) * 2] = value >> 4;
|
||||
gb->apu.wave_channel.wave_form[(reg - GB_IO_WAV_START) * 2 + 1] = value & 0xF;
|
||||
}
|
||||
}
|
||||
gb->io_registers[reg] = value;
|
||||
}
|
||||
|
@ -99,9 +99,9 @@ typedef struct
|
||||
|
||||
uint16_t sample_countdown; // in APU ticks (Reloaded from sample_length, xorred $7FF)
|
||||
uint8_t current_sample_index;
|
||||
uint8_t current_sample; // Current sample before shifting.
|
||||
uint8_t current_sample_byte; // Current sample byte.
|
||||
|
||||
int8_t wave_form[32];
|
||||
GB_PADDING(int8_t, wave_form)[32];
|
||||
bool wave_form_just_read;
|
||||
} wave_channel;
|
||||
|
||||
@ -122,6 +122,7 @@ typedef struct
|
||||
|
||||
} noise_channel;
|
||||
|
||||
/* Todo: merge these into their structs when breaking save state compatibility */
|
||||
#define GB_SKIP_DIV_EVENT_INACTIVE 0
|
||||
#define GB_SKIP_DIV_EVENT_SKIPPED 1
|
||||
#define GB_SKIP_DIV_EVENT_SKIP 2
|
||||
@ -136,6 +137,8 @@ typedef struct
|
||||
|
||||
GB_envelope_clock_t square_envelope_clock[2];
|
||||
GB_envelope_clock_t noise_envelope_clock;
|
||||
bool channel_3_pulsed;
|
||||
bool channel_3_delayed_bugged_read;
|
||||
} GB_apu_t;
|
||||
|
||||
typedef enum {
|
||||
@ -184,7 +187,6 @@ void GB_apu_div_secondary_event(GB_gameboy_t *gb);
|
||||
void GB_apu_init(GB_gameboy_t *gb);
|
||||
void GB_apu_run(GB_gameboy_t *gb);
|
||||
void GB_apu_update_cycles_per_sample(GB_gameboy_t *gb);
|
||||
void GB_borrow_sgb_border(GB_gameboy_t *gb);
|
||||
#endif
|
||||
|
||||
#endif /* apu_h */
|
||||
|
@ -428,23 +428,23 @@ static lvalue_t debugger_evaluate_lvalue(GB_gameboy_t *gb, const char *string,
|
||||
if (string[0] != '$' && (string[0] < '0' || string[0] > '9')) {
|
||||
if (length == 1) {
|
||||
switch (string[0]) {
|
||||
case 'a': return (lvalue_t){LVALUE_REG_H, .register_address = &gb->registers[GB_REGISTER_AF]};
|
||||
case 'f': return (lvalue_t){LVALUE_REG_L, .register_address = &gb->registers[GB_REGISTER_AF]};
|
||||
case 'b': return (lvalue_t){LVALUE_REG_H, .register_address = &gb->registers[GB_REGISTER_BC]};
|
||||
case 'c': return (lvalue_t){LVALUE_REG_L, .register_address = &gb->registers[GB_REGISTER_BC]};
|
||||
case 'd': return (lvalue_t){LVALUE_REG_H, .register_address = &gb->registers[GB_REGISTER_DE]};
|
||||
case 'e': return (lvalue_t){LVALUE_REG_L, .register_address = &gb->registers[GB_REGISTER_DE]};
|
||||
case 'h': return (lvalue_t){LVALUE_REG_H, .register_address = &gb->registers[GB_REGISTER_HL]};
|
||||
case 'l': return (lvalue_t){LVALUE_REG_L, .register_address = &gb->registers[GB_REGISTER_HL]};
|
||||
case 'a': return (lvalue_t){LVALUE_REG_H, .register_address = &gb->af};
|
||||
case 'f': return (lvalue_t){LVALUE_REG_L, .register_address = &gb->af};
|
||||
case 'b': return (lvalue_t){LVALUE_REG_H, .register_address = &gb->bc};
|
||||
case 'c': return (lvalue_t){LVALUE_REG_L, .register_address = &gb->bc};
|
||||
case 'd': return (lvalue_t){LVALUE_REG_H, .register_address = &gb->de};
|
||||
case 'e': return (lvalue_t){LVALUE_REG_L, .register_address = &gb->de};
|
||||
case 'h': return (lvalue_t){LVALUE_REG_H, .register_address = &gb->hl};
|
||||
case 'l': return (lvalue_t){LVALUE_REG_L, .register_address = &gb->hl};
|
||||
}
|
||||
}
|
||||
else if (length == 2) {
|
||||
switch (string[0]) {
|
||||
case 'a': if (string[1] == 'f') return (lvalue_t){LVALUE_REG16, .register_address = &gb->registers[GB_REGISTER_AF]};
|
||||
case 'b': if (string[1] == 'c') return (lvalue_t){LVALUE_REG16, .register_address = &gb->registers[GB_REGISTER_BC]};
|
||||
case 'd': if (string[1] == 'e') return (lvalue_t){LVALUE_REG16, .register_address = &gb->registers[GB_REGISTER_DE]};
|
||||
case 'h': if (string[1] == 'l') return (lvalue_t){LVALUE_REG16, .register_address = &gb->registers[GB_REGISTER_HL]};
|
||||
case 's': if (string[1] == 'p') return (lvalue_t){LVALUE_REG16, .register_address = &gb->registers[GB_REGISTER_SP]};
|
||||
case 'a': if (string[1] == 'f') return (lvalue_t){LVALUE_REG16, .register_address = &gb->af};
|
||||
case 'b': if (string[1] == 'c') return (lvalue_t){LVALUE_REG16, .register_address = &gb->bc};
|
||||
case 'd': if (string[1] == 'e') return (lvalue_t){LVALUE_REG16, .register_address = &gb->de};
|
||||
case 'h': if (string[1] == 'l') return (lvalue_t){LVALUE_REG16, .register_address = &gb->hl};
|
||||
case 's': if (string[1] == 'p') return (lvalue_t){LVALUE_REG16, .register_address = &gb->sp};
|
||||
case 'p': if (string[1] == 'c') return (lvalue_t){LVALUE_REG16, .register_address = &gb->pc};
|
||||
}
|
||||
}
|
||||
@ -606,23 +606,23 @@ value_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
|
||||
if (string[0] != '$' && (string[0] < '0' || string[0] > '9')) {
|
||||
if (length == 1) {
|
||||
switch (string[0]) {
|
||||
case 'a': ret = VALUE_16(gb->registers[GB_REGISTER_AF] >> 8); goto exit;
|
||||
case 'f': ret = VALUE_16(gb->registers[GB_REGISTER_AF] & 0xFF); goto exit;
|
||||
case 'b': ret = VALUE_16(gb->registers[GB_REGISTER_BC] >> 8); goto exit;
|
||||
case 'c': ret = VALUE_16(gb->registers[GB_REGISTER_BC] & 0xFF); goto exit;
|
||||
case 'd': ret = VALUE_16(gb->registers[GB_REGISTER_DE] >> 8); goto exit;
|
||||
case 'e': ret = VALUE_16(gb->registers[GB_REGISTER_DE] & 0xFF); goto exit;
|
||||
case 'h': ret = VALUE_16(gb->registers[GB_REGISTER_HL] >> 8); goto exit;
|
||||
case 'l': ret = VALUE_16(gb->registers[GB_REGISTER_HL] & 0xFF); goto exit;
|
||||
case 'a': ret = VALUE_16(gb->af >> 8); goto exit;
|
||||
case 'f': ret = VALUE_16(gb->af & 0xFF); goto exit;
|
||||
case 'b': ret = VALUE_16(gb->bc >> 8); goto exit;
|
||||
case 'c': ret = VALUE_16(gb->bc & 0xFF); goto exit;
|
||||
case 'd': ret = VALUE_16(gb->de >> 8); goto exit;
|
||||
case 'e': ret = VALUE_16(gb->de & 0xFF); goto exit;
|
||||
case 'h': ret = VALUE_16(gb->hl >> 8); goto exit;
|
||||
case 'l': ret = VALUE_16(gb->hl & 0xFF); goto exit;
|
||||
}
|
||||
}
|
||||
else if (length == 2) {
|
||||
switch (string[0]) {
|
||||
case 'a': if (string[1] == 'f') {ret = VALUE_16(gb->registers[GB_REGISTER_AF]); goto exit;}
|
||||
case 'b': if (string[1] == 'c') {ret = VALUE_16(gb->registers[GB_REGISTER_BC]); goto exit;}
|
||||
case 'd': if (string[1] == 'e') {ret = VALUE_16(gb->registers[GB_REGISTER_DE]); goto exit;}
|
||||
case 'h': if (string[1] == 'l') {ret = VALUE_16(gb->registers[GB_REGISTER_HL]); goto exit;}
|
||||
case 's': if (string[1] == 'p') {ret = VALUE_16(gb->registers[GB_REGISTER_SP]); goto exit;}
|
||||
case 'a': if (string[1] == 'f') {ret = VALUE_16(gb->af); goto exit;}
|
||||
case 'b': if (string[1] == 'c') {ret = VALUE_16(gb->bc); goto exit;}
|
||||
case 'd': if (string[1] == 'e') {ret = VALUE_16(gb->de); goto exit;}
|
||||
case 'h': if (string[1] == 'l') {ret = VALUE_16(gb->hl); goto exit;}
|
||||
case 's': if (string[1] == 'p') {ret = VALUE_16(gb->sp); goto exit;}
|
||||
case 'p': if (string[1] == 'c') {ret = (value_t){true, bank_for_addr(gb, gb->pc), gb->pc}; goto exit;}
|
||||
}
|
||||
}
|
||||
@ -811,15 +811,15 @@ static bool registers(GB_gameboy_t *gb, char *arguments, char *modifiers, const
|
||||
}
|
||||
|
||||
|
||||
GB_log(gb, "AF = $%04x (%c%c%c%c)\n", gb->registers[GB_REGISTER_AF], /* AF can't really be an address */
|
||||
GB_log(gb, "AF = $%04x (%c%c%c%c)\n", gb->af, /* AF can't really be an address */
|
||||
(gb->f & GB_CARRY_FLAG)? 'C' : '-',
|
||||
(gb->f & GB_HALF_CARRY_FLAG)? 'H' : '-',
|
||||
(gb->f & GB_SUBTRACT_FLAG)? 'N' : '-',
|
||||
(gb->f & GB_ZERO_FLAG)? 'Z' : '-');
|
||||
GB_log(gb, "BC = %s\n", value_to_string(gb, gb->registers[GB_REGISTER_BC], false));
|
||||
GB_log(gb, "DE = %s\n", value_to_string(gb, gb->registers[GB_REGISTER_DE], false));
|
||||
GB_log(gb, "HL = %s\n", value_to_string(gb, gb->registers[GB_REGISTER_HL], false));
|
||||
GB_log(gb, "SP = %s\n", value_to_string(gb, gb->registers[GB_REGISTER_SP], false));
|
||||
GB_log(gb, "BC = %s\n", value_to_string(gb, gb->bc, false));
|
||||
GB_log(gb, "DE = %s\n", value_to_string(gb, gb->de, false));
|
||||
GB_log(gb, "HL = %s\n", value_to_string(gb, gb->hl, false));
|
||||
GB_log(gb, "SP = %s\n", value_to_string(gb, gb->sp, false));
|
||||
GB_log(gb, "PC = %s\n", value_to_string(gb, gb->pc, false));
|
||||
GB_log(gb, "IME = %s\n", gb->ime? "Enabled" : "Disabled");
|
||||
return true;
|
||||
@ -1550,7 +1550,7 @@ static bool mbc(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugg
|
||||
if (cartridge->has_ram) {
|
||||
GB_log(gb, "Current mapped RAM bank: %x\n", gb->mbc_ram_bank);
|
||||
if (gb->cartridge_type->mbc_type != GB_HUC1) {
|
||||
GB_log(gb, "RAM is curently %s\n", gb->mbc_ram_enable? "enabled" : "disabled");
|
||||
GB_log(gb, "RAM is currently %s\n", gb->mbc_ram_enable? "enabled" : "disabled");
|
||||
}
|
||||
}
|
||||
if (cartridge->mbc_type == GB_MBC1 && gb->mbc1_wiring == GB_STANDARD_MBC1_WIRING) {
|
||||
@ -1797,8 +1797,9 @@ static bool apu(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugg
|
||||
|
||||
GB_log(gb, "\nCH3:\n");
|
||||
GB_log(gb, " Wave:");
|
||||
for (uint8_t i = 0; i < 32; i++) {
|
||||
GB_log(gb, "%s%X", i%4?"":" ", gb->apu.wave_channel.wave_form[i]);
|
||||
for (uint8_t i = 0; i < 16; i++) {
|
||||
GB_log(gb, "%s%X", i % 2? "" : " ", gb->io_registers[GB_IO_WAV_START + i] >> 4);
|
||||
GB_log(gb, "%X", gb->io_registers[GB_IO_WAV_START + i] & 0xF);
|
||||
}
|
||||
GB_log(gb, "\n");
|
||||
GB_log(gb, " Current position: %u\n", gb->apu.wave_channel.current_sample_index);
|
||||
@ -1880,11 +1881,14 @@ static bool wave(GB_gameboy_t *gb, char *arguments, char *modifiers, const debug
|
||||
|
||||
for (int8_t cur_val = 0xf & mask; cur_val >= 0; cur_val -= shift_amount) {
|
||||
for (uint8_t i = 0; i < 32; i++) {
|
||||
if ((gb->apu.wave_channel.wave_form[i] & mask) == cur_val) {
|
||||
GB_log(gb, "%X", gb->apu.wave_channel.wave_form[i]);
|
||||
uint8_t sample = i & 1?
|
||||
(gb->io_registers[GB_IO_WAV_START + i / 2] & 0xF) :
|
||||
(gb->io_registers[GB_IO_WAV_START + i / 2] >> 4);
|
||||
if ((sample & mask) == cur_val) {
|
||||
GB_log(gb, "%X", sample);
|
||||
}
|
||||
else {
|
||||
GB_log(gb, "%c", i%4 == 2 ? '-' : ' ');
|
||||
GB_log(gb, "%c", i % 4 == 2 ? '-' : ' ');
|
||||
}
|
||||
}
|
||||
GB_log(gb, "\n");
|
||||
@ -2039,7 +2043,7 @@ void GB_debugger_call_hook(GB_gameboy_t *gb, uint16_t call_addr)
|
||||
gb->debug_stopped = true;
|
||||
}
|
||||
else {
|
||||
gb->sp_for_call_depth[gb->debug_call_depth] = gb->registers[GB_REGISTER_SP];
|
||||
gb->sp_for_call_depth[gb->debug_call_depth] = gb->sp;
|
||||
gb->addr_for_call_depth[gb->debug_call_depth] = gb->pc;
|
||||
}
|
||||
}
|
||||
@ -2047,7 +2051,7 @@ void GB_debugger_call_hook(GB_gameboy_t *gb, uint16_t call_addr)
|
||||
if (gb->backtrace_size < sizeof(gb->backtrace_sps) / sizeof(gb->backtrace_sps[0])) {
|
||||
|
||||
while (gb->backtrace_size) {
|
||||
if (gb->backtrace_sps[gb->backtrace_size - 1] < gb->registers[GB_REGISTER_SP]) {
|
||||
if (gb->backtrace_sps[gb->backtrace_size - 1] < gb->sp) {
|
||||
gb->backtrace_size--;
|
||||
}
|
||||
else {
|
||||
@ -2055,7 +2059,7 @@ void GB_debugger_call_hook(GB_gameboy_t *gb, uint16_t call_addr)
|
||||
}
|
||||
}
|
||||
|
||||
gb->backtrace_sps[gb->backtrace_size] = gb->registers[GB_REGISTER_SP];
|
||||
gb->backtrace_sps[gb->backtrace_size] = gb->sp;
|
||||
gb->backtrace_returns[gb->backtrace_size].bank = bank_for_addr(gb, call_addr);
|
||||
gb->backtrace_returns[gb->backtrace_size].addr = call_addr;
|
||||
gb->backtrace_size++;
|
||||
@ -2076,9 +2080,9 @@ void GB_debugger_ret_hook(GB_gameboy_t *gb)
|
||||
gb->debug_stopped = true;
|
||||
}
|
||||
else {
|
||||
if (gb->registers[GB_REGISTER_SP] != gb->sp_for_call_depth[gb->debug_call_depth]) {
|
||||
if (gb->sp != gb->sp_for_call_depth[gb->debug_call_depth]) {
|
||||
GB_log(gb, "Stack leak detected for function %s!\n", value_to_string(gb, gb->addr_for_call_depth[gb->debug_call_depth], true));
|
||||
GB_log(gb, "SP is $%04x, should be $%04x.\n", gb->registers[GB_REGISTER_SP],
|
||||
GB_log(gb, "SP is $%04x, should be $%04x.\n", gb->sp,
|
||||
gb->sp_for_call_depth[gb->debug_call_depth]);
|
||||
gb->debug_stopped = true;
|
||||
}
|
||||
@ -2086,7 +2090,7 @@ void GB_debugger_ret_hook(GB_gameboy_t *gb)
|
||||
}
|
||||
|
||||
while (gb->backtrace_size) {
|
||||
if (gb->backtrace_sps[gb->backtrace_size - 1] <= gb->registers[GB_REGISTER_SP]) {
|
||||
if (gb->backtrace_sps[gb->backtrace_size - 1] <= gb->sp) {
|
||||
gb->backtrace_size--;
|
||||
}
|
||||
else {
|
||||
@ -2243,7 +2247,6 @@ 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;
|
||||
@ -2564,13 +2567,13 @@ static bool condition_code(GB_gameboy_t *gb, uint8_t opcode)
|
||||
{
|
||||
switch ((opcode >> 3) & 0x3) {
|
||||
case 0:
|
||||
return !(gb->registers[GB_REGISTER_AF] & GB_ZERO_FLAG);
|
||||
return !(gb->af & GB_ZERO_FLAG);
|
||||
case 1:
|
||||
return (gb->registers[GB_REGISTER_AF] & GB_ZERO_FLAG);
|
||||
return (gb->af & GB_ZERO_FLAG);
|
||||
case 2:
|
||||
return !(gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG);
|
||||
return !(gb->af & GB_CARRY_FLAG);
|
||||
case 3:
|
||||
return (gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG);
|
||||
return (gb->af & GB_CARRY_FLAG);
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -2587,8 +2590,8 @@ static uint16_t jr_cc_r8(GB_gameboy_t *gb, uint8_t opcode)
|
||||
|
||||
static uint16_t ret(GB_gameboy_t *gb, uint8_t opcode)
|
||||
{
|
||||
return GB_read_memory(gb, gb->registers[GB_REGISTER_SP]) |
|
||||
(GB_read_memory(gb, gb->registers[GB_REGISTER_SP] + 1) << 8);
|
||||
return GB_read_memory(gb, gb->sp) |
|
||||
(GB_read_memory(gb, gb->sp + 1) << 8);
|
||||
}
|
||||
|
||||
|
||||
@ -2670,7 +2673,7 @@ static jump_to_return_t test_jump_to_breakpoints(GB_gameboy_t *gb, uint16_t *add
|
||||
if (!gb->has_jump_to_breakpoints) return JUMP_TO_NONE;
|
||||
|
||||
if (!is_in_trivial_memory(gb->pc) || !is_in_trivial_memory(gb->pc + 2) ||
|
||||
!is_in_trivial_memory(gb->registers[GB_REGISTER_SP]) || !is_in_trivial_memory(gb->registers[GB_REGISTER_SP] + 1)) {
|
||||
!is_in_trivial_memory(gb->sp) || !is_in_trivial_memory(gb->sp + 1)) {
|
||||
return JUMP_TO_NONTRIVIAL;
|
||||
}
|
||||
|
||||
|
@ -632,7 +632,7 @@ static void advance_fetcher_state_machine(GB_gameboy_t *gb)
|
||||
GB_FETCHER_SLEEP,
|
||||
} fetcher_step_t;
|
||||
|
||||
fetcher_step_t fetcher_state_machine [8] = {
|
||||
static const fetcher_step_t fetcher_state_machine [8] = {
|
||||
GB_FETCHER_SLEEP,
|
||||
GB_FETCHER_GET_TILE,
|
||||
GB_FETCHER_SLEEP,
|
||||
@ -666,7 +666,10 @@ static void advance_fetcher_state_machine(GB_gameboy_t *gb)
|
||||
x = gb->window_tile_x;
|
||||
}
|
||||
else {
|
||||
x = ((gb->io_registers[GB_IO_SCX] / 8) + gb->fetcher_x) & 0x1F;
|
||||
/* TODO: There is some CGB timing error around here.
|
||||
Adjusting SCX by 7 or less shouldn't have an effect on a CGB,
|
||||
but SameBoy is affected by a change of both 7 and 6 (but not less). */
|
||||
x = ((gb->io_registers[GB_IO_SCX] + gb->position_in_line + 8) / 8) & 0x1F;
|
||||
}
|
||||
if (gb->model > GB_MODEL_CGB_C) {
|
||||
/* This value is cached on the CGB-D and newer, so it cannot be used to mix tiles together */
|
||||
@ -777,12 +780,6 @@ static void advance_fetcher_state_machine(GB_gameboy_t *gb)
|
||||
|
||||
// fallthrough
|
||||
case GB_FETCHER_PUSH: {
|
||||
if (gb->fetcher_state == 6) {
|
||||
/* The background map index increase at this specific point. If this state is not reached,
|
||||
it will simply not increase. */
|
||||
gb->fetcher_x++;
|
||||
gb->fetcher_x &= 0x1f;
|
||||
}
|
||||
if (gb->fetcher_state < 7) {
|
||||
gb->fetcher_state++;
|
||||
}
|
||||
@ -1049,7 +1046,6 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
|
||||
gb->position_in_line = - (gb->io_registers[GB_IO_SCX] & 7) - 8;
|
||||
gb->lcd_x = 0;
|
||||
|
||||
gb->fetcher_x = 0;
|
||||
gb->extra_penalty_for_sprite_at_0 = (gb->io_registers[GB_IO_SCX] & 7);
|
||||
|
||||
|
||||
@ -1530,7 +1526,7 @@ uint8_t GB_get_oam_info(GB_gameboy_t *gb, GB_oam_info_t *dest, uint8_t *sprite_h
|
||||
uint8_t count = 0;
|
||||
*sprite_height = (gb->io_registers[GB_IO_LCDC] & 4) ? 16:8;
|
||||
uint8_t oam_to_dest_index[40] = {0,};
|
||||
for (unsigned y = 0; y < LINES; y++) {
|
||||
for (signed y = 0; y < LINES; y++) {
|
||||
GB_object_t *sprite = (GB_object_t *) &gb->oam;
|
||||
uint8_t sprites_in_line = 0;
|
||||
for (uint8_t i = 0; i < 40; i++, sprite++) {
|
||||
|
@ -284,7 +284,7 @@ int GB_load_rom(GB_gameboy_t *gb, const char *path)
|
||||
gb->rom_size |= gb->rom_size >> 1;
|
||||
gb->rom_size++;
|
||||
}
|
||||
if (gb->rom_size == 0) {
|
||||
if (gb->rom_size < 0x8000) {
|
||||
gb->rom_size = 0x8000;
|
||||
}
|
||||
fseek(f, 0, SEEK_SET);
|
||||
@ -396,7 +396,7 @@ int GB_load_gbs_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t size
|
||||
gb->rom_size++;
|
||||
}
|
||||
|
||||
if (gb->rom_size == 0) {
|
||||
if (gb->rom_size < 0x8000) {
|
||||
gb->rom_size = 0x8000;
|
||||
}
|
||||
|
||||
@ -412,6 +412,7 @@ int GB_load_gbs_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t size
|
||||
if (gb->mbc_ram) {
|
||||
free(gb->mbc_ram);
|
||||
gb->mbc_ram = NULL;
|
||||
gb->mbc_ram_size = 0;
|
||||
}
|
||||
|
||||
if (gb->cartridge_type->has_ram) {
|
||||
@ -1198,7 +1199,7 @@ uint8_t GB_run(GB_gameboy_t *gb)
|
||||
{
|
||||
gb->vblank_just_occured = false;
|
||||
|
||||
if (gb->sgb && gb->sgb->intro_animation < 140) {
|
||||
if (gb->sgb && gb->sgb->intro_animation < 96) {
|
||||
/* On the SGB, the GB is halted after finishing the boot ROM.
|
||||
Then, after the boot animation is almost done, it's reset.
|
||||
Since the SGB HLE does not perform any header validity checks,
|
||||
@ -1535,18 +1536,13 @@ static void reset_ram(GB_gameboy_t *gb)
|
||||
case GB_MODEL_SGB_PAL_NO_SFC: /* Unverified */
|
||||
case GB_MODEL_SGB2:
|
||||
case GB_MODEL_SGB2_NO_SFC: {
|
||||
uint8_t temp;
|
||||
for (unsigned i = 0; i < GB_IO_WAV_END - GB_IO_WAV_START; i++) {
|
||||
if (i & 1) {
|
||||
temp = GB_random() & GB_random() & GB_random();
|
||||
gb->io_registers[GB_IO_WAV_START + i] = GB_random() & GB_random() & GB_random();
|
||||
}
|
||||
else {
|
||||
temp = GB_random() | GB_random() | GB_random();
|
||||
gb->io_registers[GB_IO_WAV_START + i] = GB_random() | GB_random() | GB_random();
|
||||
}
|
||||
gb->apu.wave_channel.wave_form[i * 2] = temp >> 4;
|
||||
gb->apu.wave_channel.wave_form[i * 2 + 1] = temp & 0xF;
|
||||
gb->io_registers[GB_IO_WAV_START + i] = temp;
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -1602,7 +1598,10 @@ void GB_reset(GB_gameboy_t *gb)
|
||||
{
|
||||
uint32_t mbc_ram_size = gb->mbc_ram_size;
|
||||
GB_model_t model = gb->model;
|
||||
uint8_t rtc_section[GB_SECTION_SIZE(rtc)];
|
||||
memcpy(rtc_section, GB_GET_SECTION(gb, rtc), sizeof(rtc_section));
|
||||
memset(gb, 0, (size_t)GB_GET_SECTION((GB_gameboy_t *) 0, unsaved));
|
||||
memcpy(GB_GET_SECTION(gb, rtc), rtc_section, sizeof(rtc_section));
|
||||
gb->model = model;
|
||||
gb->version = GB_STRUCT_VERSION;
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "cheats.h"
|
||||
#include "rumble.h"
|
||||
#include "workboy.h"
|
||||
#include "random.h"
|
||||
|
||||
#define GB_STRUCT_VERSION 13
|
||||
|
||||
@ -434,6 +435,7 @@ struct GB_gameboy_internal_s {
|
||||
|
||||
int32_t ir_sensor;
|
||||
bool effective_ir_input;
|
||||
uint16_t address_bus;
|
||||
);
|
||||
|
||||
/* DMA and HDMA */
|
||||
@ -553,7 +555,7 @@ struct GB_gameboy_internal_s {
|
||||
/* Video Display */
|
||||
GB_SECTION(video,
|
||||
uint32_t vram_size; // Different between CGB and DMG
|
||||
uint8_t cgb_vram_bank;
|
||||
bool cgb_vram_bank;
|
||||
uint8_t oam[0xA0];
|
||||
uint8_t background_palettes_data[0x40];
|
||||
uint8_t sprite_palettes_data[0x40];
|
||||
@ -581,7 +583,7 @@ struct GB_gameboy_internal_s {
|
||||
uint8_t current_line;
|
||||
uint16_t ly_for_comparison;
|
||||
GB_fifo_t bg_fifo, oam_fifo;
|
||||
uint8_t fetcher_x;
|
||||
GB_PADDING(uint8_t, fetcher_x);
|
||||
uint8_t fetcher_y;
|
||||
uint16_t cycles_for_line;
|
||||
uint8_t current_tile;
|
||||
@ -892,5 +894,9 @@ unsigned GB_get_player_count(GB_gameboy_t *gb);
|
||||
// `title` must be at least 17 bytes in size
|
||||
void GB_get_rom_title(GB_gameboy_t *gb, char *title);
|
||||
uint32_t GB_get_rom_crc32(GB_gameboy_t *gb);
|
||||
|
||||
#ifdef GB_INTERNAL
|
||||
void GB_borrow_sgb_border(GB_gameboy_t *gb);
|
||||
#endif
|
||||
|
||||
#endif /* GB_h */
|
||||
|
@ -12,7 +12,7 @@ void GB_update_joyp(GB_gameboy_t *gb)
|
||||
previous_state = gb->io_registers[GB_IO_JOYP] & 0xF;
|
||||
key_selection = (gb->io_registers[GB_IO_JOYP] >> 4) & 3;
|
||||
gb->io_registers[GB_IO_JOYP] &= 0xF0;
|
||||
uint8_t current_player = gb->sgb? (gb->sgb->current_player & (gb->sgb->player_count - 1) & 3) : 0;
|
||||
uint8_t current_player = gb->sgb? gb->sgb->current_player : 0;
|
||||
switch (key_selection) {
|
||||
case 3:
|
||||
if (gb->sgb && gb->sgb->player_count > 1) {
|
||||
@ -52,6 +52,7 @@ void GB_update_joyp(GB_gameboy_t *gb)
|
||||
break;
|
||||
|
||||
default:
|
||||
__builtin_unreachable();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -141,6 +141,7 @@ void GB_configure_cart(GB_gameboy_t *gb)
|
||||
if (gb->mbc_ram) {
|
||||
free(gb->mbc_ram);
|
||||
gb->mbc_ram = NULL;
|
||||
gb->mbc_ram_size = 0;
|
||||
}
|
||||
|
||||
if (gb->cartridge_type->has_ram) {
|
||||
|
@ -291,7 +291,7 @@ static uint8_t read_vram(GB_gameboy_t *gb, uint16_t addr)
|
||||
addr = gb->last_tile_data_address;
|
||||
}
|
||||
}
|
||||
return gb->vram[(addr & 0x1FFF) + (uint16_t) gb->cgb_vram_bank * 0x2000];
|
||||
return gb->vram[(addr & 0x1FFF) + (gb->cgb_vram_bank? 0x2000 : 0)];
|
||||
}
|
||||
|
||||
static uint8_t read_mbc_ram(GB_gameboy_t *gb, uint16_t addr)
|
||||
@ -464,7 +464,7 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr)
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < 8; i++) {
|
||||
@ -635,8 +635,7 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr)
|
||||
}
|
||||
return 0xFF;
|
||||
}
|
||||
/* Hardware registers */
|
||||
return 0;
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
if (addr == 0xFFFF) {
|
||||
@ -815,7 +814,7 @@ static void write_vram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||
addr = gb->last_tile_data_address;
|
||||
}
|
||||
}
|
||||
gb->vram[(addr & 0x1FFF) + (uint16_t) gb->cgb_vram_bank * 0x2000] = value;
|
||||
gb->vram[(addr & 0x1FFF) + (gb->cgb_vram_bank? 0x2000 : 0)] = value;
|
||||
}
|
||||
|
||||
static bool huc3_write(GB_gameboy_t *gb, uint8_t value)
|
||||
@ -1234,12 +1233,11 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||
gb->io_registers[GB_IO_DMA] = value;
|
||||
return;
|
||||
case GB_IO_SVBK:
|
||||
if (!gb->cgb_mode) {
|
||||
return;
|
||||
}
|
||||
gb->cgb_ram_bank = value & 0x7;
|
||||
if (!gb->cgb_ram_bank) {
|
||||
gb->cgb_ram_bank++;
|
||||
if (gb->cgb_mode || (GB_is_cgb(gb) && !gb->boot_rom_finished)) {
|
||||
gb->cgb_ram_bank = value & 0x7;
|
||||
if (!gb->cgb_ram_bank) {
|
||||
gb->cgb_ram_bank++;
|
||||
}
|
||||
}
|
||||
return;
|
||||
case GB_IO_VBK:
|
||||
@ -1396,7 +1394,7 @@ static GB_write_function_t * const write_map[] =
|
||||
write_vram, write_vram, /* 8XXX, 9XXX */
|
||||
write_mbc_ram, write_mbc_ram, /* AXXX, BXXX */
|
||||
write_ram, write_banked_ram, /* CXXX, DXXX */
|
||||
write_ram, write_high_memory, /* EXXX FXXX */
|
||||
write_ram, write_high_memory, /* EXXX FXXX */
|
||||
};
|
||||
|
||||
void GB_write_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||
|
@ -9,6 +9,15 @@
|
||||
#define BESS_NAME "SameBoy v" GB_VERSION
|
||||
#endif
|
||||
|
||||
_Static_assert((GB_SECTION_OFFSET(core_state) & 7) == 0, "Section core_state is not aligned");
|
||||
_Static_assert((GB_SECTION_OFFSET(dma) & 7) == 0, "Section dma is not aligned");
|
||||
_Static_assert((GB_SECTION_OFFSET(mbc) & 7) == 0, "Section mbc is not aligned");
|
||||
_Static_assert((GB_SECTION_OFFSET(hram) & 7) == 0, "Section hram is not aligned");
|
||||
_Static_assert((GB_SECTION_OFFSET(timing) & 7) == 0, "Section timing is not aligned");
|
||||
_Static_assert((GB_SECTION_OFFSET(apu) & 7) == 0, "Section apu is not aligned");
|
||||
_Static_assert((GB_SECTION_OFFSET(rtc) & 7) == 0, "Section rtc is not aligned");
|
||||
_Static_assert((GB_SECTION_OFFSET(video) & 7) == 0, "Section video is not aligned");
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint32_t magic;
|
||||
uint32_t size;
|
||||
@ -298,6 +307,11 @@ static bool verify_and_update_state_compatibility(GB_gameboy_t *gb, GB_gameboy_t
|
||||
return false;
|
||||
}
|
||||
|
||||
if (GB_is_cgb(gb) != GB_is_cgb(save) || GB_is_hle_sgb(gb) != GB_is_hle_sgb(save)) {
|
||||
GB_log(gb, "The save state is for a different Game Boy model. Try changing the emulated model.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gb->mbc_ram_size < save->mbc_ram_size) {
|
||||
GB_log(gb, "The save state has non-matching MBC RAM size.\n");
|
||||
return false;
|
||||
@ -324,7 +338,24 @@ static bool verify_and_update_state_compatibility(GB_gameboy_t *gb, GB_gameboy_t
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
switch (save->model) {
|
||||
case GB_MODEL_DMG_B: return true;
|
||||
case GB_MODEL_SGB_NTSC: return true;
|
||||
case GB_MODEL_SGB_PAL: return true;
|
||||
case GB_MODEL_SGB_NTSC_NO_SFC: return true;
|
||||
case GB_MODEL_SGB_PAL_NO_SFC: return true;
|
||||
case GB_MODEL_SGB2: return true;
|
||||
case GB_MODEL_SGB2_NO_SFC: return true;
|
||||
case GB_MODEL_CGB_C: return true;
|
||||
case GB_MODEL_CGB_E: return true;
|
||||
case GB_MODEL_AGB: return true;
|
||||
}
|
||||
if ((gb->model & GB_MODEL_FAMILY_MASK) == (save->model & GB_MODEL_FAMILY_MASK)) {
|
||||
save->model = gb->model;
|
||||
return true;
|
||||
}
|
||||
GB_log(gb, "This save state is for an unknown Game Boy model\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
static void sanitize_state(GB_gameboy_t *gb)
|
||||
@ -338,8 +369,36 @@ static void sanitize_state(GB_gameboy_t *gb)
|
||||
gb->bg_fifo.write_end &= 0xF;
|
||||
gb->oam_fifo.read_end &= 0xF;
|
||||
gb->oam_fifo.write_end &= 0xF;
|
||||
gb->last_tile_index_address &= 0x1FFF;
|
||||
gb->window_tile_x &= 0x1F;
|
||||
|
||||
/* These are kind of DOS-ish if too large */
|
||||
if (abs(gb->display_cycles) > 0x8000) {
|
||||
gb->display_cycles = 0;
|
||||
}
|
||||
|
||||
if (abs(gb->div_cycles) > 0x8000) {
|
||||
gb->div_cycles = 0;
|
||||
}
|
||||
|
||||
if (!GB_is_cgb(gb)) {
|
||||
gb->cgb_mode = false;
|
||||
}
|
||||
|
||||
if (gb->ram_size == 0x8000) {
|
||||
gb->cgb_ram_bank &= 0x7;
|
||||
}
|
||||
else {
|
||||
gb->cgb_ram_bank = 1;
|
||||
}
|
||||
if (gb->vram_size != 0x4000) {
|
||||
gb->cgb_vram_bank = 0;
|
||||
}
|
||||
if (!GB_is_cgb(gb)) {
|
||||
gb->current_tile_attributes = 0;
|
||||
}
|
||||
|
||||
gb->object_low_line_address &= gb->vram_size & ~1;
|
||||
gb->fetcher_x &= 0x1f;
|
||||
if (gb->lcd_x > gb->position_in_line) {
|
||||
gb->lcd_x = gb->position_in_line;
|
||||
}
|
||||
@ -347,6 +406,12 @@ static void sanitize_state(GB_gameboy_t *gb)
|
||||
if (gb->object_priority == GB_OBJECT_PRIORITY_UNDEFINED) {
|
||||
gb->object_priority = gb->cgb_mode? GB_OBJECT_PRIORITY_INDEX : GB_OBJECT_PRIORITY_X;
|
||||
}
|
||||
if (gb->sgb) {
|
||||
if (gb->sgb->player_count != 1 && gb->sgb->player_count != 2 && gb->sgb->player_count != 4) {
|
||||
gb->sgb->player_count = 1;
|
||||
}
|
||||
gb->sgb->current_player &= gb->sgb->player_count - 1;
|
||||
}
|
||||
if (gb->sgb && !gb->sgb->v14_3) {
|
||||
#ifdef GB_BIG_ENDIAN
|
||||
for (unsigned i = 0; i < sizeof(gb->sgb->border.raw_data) / 2; i++) {
|
||||
@ -711,7 +776,7 @@ static int save_state_internal(GB_gameboy_t *gb, virtual_file_t *file, bool appe
|
||||
bess_sgb.attribute_files = (BESS_buffer_t){LE32(sizeof(gb->sgb->attribute_files)),
|
||||
LE32(sgb_offset + offsetof(GB_sgb_t, attribute_files))};
|
||||
|
||||
bess_sgb.multiplayer_state = (gb->sgb->player_count << 4) | (gb->sgb->current_player & (gb->sgb->player_count - 1));
|
||||
bess_sgb.multiplayer_state = (gb->sgb->player_count << 4) | gb->sgb->current_player;
|
||||
if (file->write(file, &bess_sgb, sizeof(bess_sgb)) != sizeof(bess_sgb)) {
|
||||
goto error;
|
||||
}
|
||||
@ -904,7 +969,9 @@ static int load_bess_save(GB_gameboy_t *gb, virtual_file_t *file, bool is_samebo
|
||||
|
||||
save.halted = core.execution_mode == 1;
|
||||
save.stopped = core.execution_mode == 2;
|
||||
|
||||
|
||||
// Done early for compatibility with 0.14.x
|
||||
GB_write_memory(&save, 0xFF00 + GB_IO_SVBK, core.io_registers[GB_IO_SVBK]);
|
||||
// CPU related
|
||||
|
||||
// Determines DMG mode
|
||||
@ -965,7 +1032,6 @@ static int load_bess_save(GB_gameboy_t *gb, virtual_file_t *file, bool is_samebo
|
||||
GB_write_memory(&save, 0xFF00 + GB_IO_BGPI, core.io_registers[GB_IO_BGPI]);
|
||||
GB_write_memory(&save, 0xFF00 + GB_IO_OBPI, core.io_registers[GB_IO_OBPI]);
|
||||
GB_write_memory(&save, 0xFF00 + GB_IO_OPRI, core.io_registers[GB_IO_OPRI]);
|
||||
GB_write_memory(&save, 0xFF00 + GB_IO_SVBK, core.io_registers[GB_IO_SVBK]);
|
||||
|
||||
// Interrupts
|
||||
GB_write_memory(&save, 0xFF00 + GB_IO_IF, core.io_registers[GB_IO_IF]);
|
||||
@ -1004,6 +1070,7 @@ static int load_bess_save(GB_gameboy_t *gb, virtual_file_t *file, bool is_samebo
|
||||
case BE32('MBC '):
|
||||
if (!found_core) goto parse_error;
|
||||
if (LE32(block.size) % 3 != 0) goto parse_error;
|
||||
if (LE32(block.size) > 0x1000) goto parse_error;
|
||||
for (unsigned i = LE32(block.size); i > 0; i -= 3) {
|
||||
BESS_MBC_pair_t pair;
|
||||
file->read(file, &pair, sizeof(pair));
|
||||
@ -1151,6 +1218,7 @@ error:
|
||||
GB_log(gb, "Attempted to import a save state from a different emulator or incompatible version, but the save state is invalid.\n");
|
||||
}
|
||||
GB_free(&save);
|
||||
sanitize_state(gb);
|
||||
return errno;
|
||||
}
|
||||
|
||||
@ -1258,7 +1326,7 @@ int GB_load_state_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t le
|
||||
}
|
||||
|
||||
|
||||
bool GB_is_stave_state(const char *path)
|
||||
bool GB_is_save_state(const char *path)
|
||||
{
|
||||
bool ret = false;
|
||||
FILE *f = fopen(path, "rb");
|
||||
|
@ -10,7 +10,7 @@
|
||||
as anonymous enums inside unions */
|
||||
#define GB_SECTION(name, ...) __attribute__ ((aligned (8))) __VA_ARGS__
|
||||
#else
|
||||
#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(name, ...) union __attribute__ ((aligned (8))) {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))
|
||||
@ -27,7 +27,7 @@ void GB_save_state_to_buffer(GB_gameboy_t *gb, uint8_t *buffer);
|
||||
|
||||
int GB_load_state(GB_gameboy_t *gb, const char *path);
|
||||
int GB_load_state_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t length);
|
||||
bool GB_is_stave_state(const char *path);
|
||||
bool GB_is_save_state(const char *path);
|
||||
#ifdef GB_INTERNAL
|
||||
static inline uint32_t state_magic(void)
|
||||
{
|
||||
|
@ -375,39 +375,36 @@ static void command_ready(GB_gameboy_t *gb)
|
||||
}
|
||||
break;
|
||||
case PAL_TRN:
|
||||
gb->sgb->vram_transfer_countdown = 2;
|
||||
gb->sgb->vram_transfer_countdown = 3;
|
||||
gb->sgb->transfer_dest = TRANSFER_PALETTES;
|
||||
break;
|
||||
case DATA_SND:
|
||||
// Not supported, but used by almost all SGB games for hot patching, so let's mute the warning for this
|
||||
break;
|
||||
case MLT_REQ:
|
||||
if (gb->sgb->player_count == 1) {
|
||||
gb->sgb->current_player = 0;
|
||||
}
|
||||
gb->sgb->player_count = (gb->sgb->command[1] & 3) + 1; /* Todo: When breaking save state comaptibility,
|
||||
fix this to be 0 based. */
|
||||
if (gb->sgb->player_count == 3) {
|
||||
gb->sgb->current_player++;
|
||||
gb->sgb->player_count++;
|
||||
}
|
||||
gb->sgb->mlt_lock = true;
|
||||
gb->sgb->current_player &= (gb->sgb->player_count - 1);
|
||||
break;
|
||||
case CHR_TRN:
|
||||
gb->sgb->vram_transfer_countdown = 2;
|
||||
gb->sgb->vram_transfer_countdown = 3;
|
||||
gb->sgb->transfer_dest = (gb->sgb->command[1] & 1)? TRANSFER_HIGH_TILES : TRANSFER_LOW_TILES;
|
||||
break;
|
||||
case PCT_TRN:
|
||||
gb->sgb->vram_transfer_countdown = 2;
|
||||
gb->sgb->vram_transfer_countdown = 3;
|
||||
gb->sgb->transfer_dest = TRANSFER_BORDER_DATA;
|
||||
break;
|
||||
case ATTR_TRN:
|
||||
gb->sgb->vram_transfer_countdown = 2;
|
||||
gb->sgb->vram_transfer_countdown = 3;
|
||||
gb->sgb->transfer_dest = TRANSFER_ATTRIBUTES;
|
||||
break;
|
||||
case ATTR_SET:
|
||||
load_attribute_file(gb, gb->sgb->command[0] & 0x3F);
|
||||
load_attribute_file(gb, gb->sgb->command[1] & 0x3F);
|
||||
|
||||
if (gb->sgb->command[0] & 0x40) {
|
||||
if (gb->sgb->command[1] & 0x40) {
|
||||
gb->sgb->mask_mode = MASK_DISABLED;
|
||||
}
|
||||
break;
|
||||
@ -437,27 +434,22 @@ void GB_sgb_write(GB_gameboy_t *gb, uint8_t value)
|
||||
return;
|
||||
}
|
||||
if (gb->sgb->disable_commands) return;
|
||||
if (gb->sgb->command_write_index >= sizeof(gb->sgb->command) * 8) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t command_size = (gb->sgb->command[0] & 7 ?: 1) * SGB_PACKET_SIZE * 8;
|
||||
if ((gb->sgb->command[0] & 0xF1) == 0xF1) {
|
||||
command_size = SGB_PACKET_SIZE * 8;
|
||||
}
|
||||
|
||||
if ((value & 0x20) == 0 && (gb->io_registers[GB_IO_JOYP] & 0x20) != 0) {
|
||||
gb->sgb->mlt_lock ^= true;
|
||||
if ((value & 0x20) != 0 && (gb->io_registers[GB_IO_JOYP] & 0x20) == 0) {
|
||||
if ((gb->sgb->player_count & 1) == 0) {
|
||||
gb->sgb->current_player++;
|
||||
gb->sgb->current_player &= (gb->sgb->player_count - 1);
|
||||
}
|
||||
}
|
||||
|
||||
switch ((value >> 4) & 3) {
|
||||
case 3:
|
||||
gb->sgb->ready_for_pulse = true;
|
||||
if ((gb->sgb->player_count & 1) == 0 && !gb->sgb->mlt_lock) {
|
||||
gb->sgb->current_player++;
|
||||
gb->sgb->current_player &= 3;
|
||||
gb->sgb->mlt_lock = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2: // Zero
|
||||
@ -473,10 +465,12 @@ void GB_sgb_write(GB_gameboy_t *gb, uint8_t value)
|
||||
gb->sgb->ready_for_stop = false;
|
||||
}
|
||||
else {
|
||||
gb->sgb->command_write_index++;
|
||||
gb->sgb->ready_for_pulse = false;
|
||||
if (((gb->sgb->command_write_index) & (SGB_PACKET_SIZE * 8 - 1)) == 0) {
|
||||
gb->sgb->ready_for_stop = true;
|
||||
if (gb->sgb->command_write_index < sizeof(gb->sgb->command) * 8) {
|
||||
gb->sgb->command_write_index++;
|
||||
gb->sgb->ready_for_pulse = false;
|
||||
if (((gb->sgb->command_write_index) & (SGB_PACKET_SIZE * 8 - 1)) == 0) {
|
||||
gb->sgb->ready_for_stop = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -490,11 +484,13 @@ void GB_sgb_write(GB_gameboy_t *gb, uint8_t value)
|
||||
memset(gb->sgb->command, 0, sizeof(gb->sgb->command));
|
||||
}
|
||||
else {
|
||||
gb->sgb->command[gb->sgb->command_write_index / 8] |= 1 << (gb->sgb->command_write_index & 7);
|
||||
gb->sgb->command_write_index++;
|
||||
gb->sgb->ready_for_pulse = false;
|
||||
if (((gb->sgb->command_write_index) & (SGB_PACKET_SIZE * 8 - 1)) == 0) {
|
||||
gb->sgb->ready_for_stop = true;
|
||||
if (gb->sgb->command_write_index < sizeof(gb->sgb->command) * 8) {
|
||||
gb->sgb->command[gb->sgb->command_write_index / 8] |= 1 << (gb->sgb->command_write_index & 7);
|
||||
gb->sgb->command_write_index++;
|
||||
gb->sgb->ready_for_pulse = false;
|
||||
if (((gb->sgb->command_write_index) & (SGB_PACKET_SIZE * 8 - 1)) == 0) {
|
||||
gb->sgb->ready_for_stop = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -537,7 +533,6 @@ static uint32_t convert_rgb15_with_fade(GB_gameboy_t *gb, uint16_t color, uint8_
|
||||
return GB_convert_rgb15(gb, color, false);
|
||||
}
|
||||
|
||||
#include <stdio.h>
|
||||
static void render_boot_animation (GB_gameboy_t *gb)
|
||||
{
|
||||
#include "graphics/sgb_animation_logo.inc"
|
||||
@ -653,7 +648,7 @@ void GB_sgb_render(GB_gameboy_t *gb)
|
||||
}
|
||||
}
|
||||
if (gb->sgb->transfer_dest == TRANSFER_BORDER_DATA) {
|
||||
gb->sgb->border_animation = 64;
|
||||
gb->sgb->border_animation = 105; // Measured on an SGB2, but might be off by ±2
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -734,7 +729,10 @@ void GB_sgb_render(GB_gameboy_t *gb)
|
||||
}
|
||||
|
||||
uint32_t border_colors[16 * 4];
|
||||
if (gb->sgb->border_animation == 0 || gb->sgb->intro_animation < GB_SGB_INTRO_ANIMATION_LENGTH) {
|
||||
if (gb->sgb->border_animation == 0 || gb->sgb->border_animation > 64 || gb->sgb->intro_animation < GB_SGB_INTRO_ANIMATION_LENGTH) {
|
||||
if (gb->sgb->border_animation != 0) {
|
||||
gb->sgb->border_animation--;
|
||||
}
|
||||
for (unsigned i = 0; i < 16 * 4; i++) {
|
||||
border_colors[i] = convert_rgb15(gb, LE16(gb->sgb->border.palette[i]));
|
||||
}
|
||||
@ -767,6 +765,7 @@ void GB_sgb_render(GB_gameboy_t *gb)
|
||||
continue;
|
||||
}
|
||||
uint16_t tile = LE16(gb->sgb->border.map[tile_x + tile_y * 32]);
|
||||
if (tile & 0x300) continue; // Unused tile
|
||||
uint8_t flip_x = (tile & 0x4000)? 0:7;
|
||||
uint8_t flip_y = (tile & 0x8000)? 7:0;
|
||||
uint8_t palette = (tile >> 10) & 3;
|
||||
|
@ -61,7 +61,7 @@ struct GB_sgb_s {
|
||||
uint8_t received_header[0x54];
|
||||
|
||||
/* Multiplayer (cont) */
|
||||
bool mlt_lock;
|
||||
GB_PADDING(bool, mlt_lock);
|
||||
|
||||
bool v14_3; // True on save states created on 0.14.3 or newer; Remove when breaking save state compatibility!
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -434,7 +434,7 @@ void GB_emulate_timer_glitch(GB_gameboy_t *gb, uint8_t old_tac, uint8_t new_tac)
|
||||
/* The bit used for overflow testing must have been 1 */
|
||||
if (gb->div_counter & old_clocks) {
|
||||
/* And now either the timer must be disabled, or the new bit used for overflow testing be 0. */
|
||||
if (!(new_tac & 4) || gb->div_counter & new_clocks) {
|
||||
if (!(new_tac & 4) || !(gb->div_counter & new_clocks)) {
|
||||
increase_tima(gb);
|
||||
}
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ static void HIDReport(void *context, IOReturn result, void *sender, IOHIDReportT
|
||||
uint32_t reportID, uint8_t *report, CFIndex reportLength)
|
||||
{
|
||||
if (reportLength) {
|
||||
[(__bridge JOYController *)context gotReport:[[NSData alloc] initWithBytesNoCopy:report length:reportLength freeWhenDone:NO]];
|
||||
[(__bridge JOYController *)context gotReport:[[NSData alloc] initWithBytesNoCopy:report length:reportLength freeWhenDone:false]];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -128,10 +128,10 @@ endif
|
||||
|
||||
ifeq (,$(PKG_CONFIG))
|
||||
SDL_CFLAGS := $(shell sdl2-config --cflags)
|
||||
SDL_LDFLAGS := $(shell sdl2-config --libs)
|
||||
SDL_LDFLAGS := $(shell sdl2-config --libs) -lpthread
|
||||
else
|
||||
SDL_CFLAGS := $(shell $(PKG_CONFIG) --cflags sdl2)
|
||||
SDL_LDFLAGS := $(shell $(PKG_CONFIG) --libs sdl2)
|
||||
SDL_LDFLAGS := $(shell $(PKG_CONFIG) --libs sdl2) -lpthread
|
||||
endif
|
||||
ifeq (,$(PKG_CONFIG))
|
||||
GL_LDFLAGS := -lGL
|
||||
|
@ -5,6 +5,8 @@
|
||||
char *do_open_rom_dialog(void)
|
||||
{
|
||||
@autoreleasepool {
|
||||
int stderr_fd = dup(STDERR_FILENO);
|
||||
close(STDERR_FILENO);
|
||||
NSWindow *key = [NSApp keyWindow];
|
||||
NSOpenPanel *dialog = [NSOpenPanel openPanel];
|
||||
dialog.title = @"Open ROM";
|
||||
@ -12,6 +14,7 @@ char *do_open_rom_dialog(void)
|
||||
[dialog runModal];
|
||||
[key makeKeyAndOrderFront:nil];
|
||||
NSString *ret = [[[dialog URLs] firstObject] path];
|
||||
dup2(stderr_fd, STDERR_FILENO);
|
||||
if (ret) {
|
||||
return strdup(ret.UTF8String);
|
||||
}
|
||||
@ -22,6 +25,8 @@ char *do_open_rom_dialog(void)
|
||||
char *do_open_folder_dialog(void)
|
||||
{
|
||||
@autoreleasepool {
|
||||
int stderr_fd = dup(STDERR_FILENO);
|
||||
close(STDERR_FILENO);
|
||||
NSWindow *key = [NSApp keyWindow];
|
||||
NSOpenPanel *dialog = [NSOpenPanel openPanel];
|
||||
dialog.title = @"Select Boot ROMs Folder";
|
||||
@ -30,6 +35,7 @@ char *do_open_folder_dialog(void)
|
||||
[dialog runModal];
|
||||
[key makeKeyAndOrderFront:nil];
|
||||
NSString *ret = [[[dialog URLs] firstObject] path];
|
||||
dup2(stderr_fd, STDERR_FILENO);
|
||||
if (ret) {
|
||||
return strdup(ret.UTF8String);
|
||||
}
|
||||
|
@ -43,10 +43,10 @@ static OSStatus render(CGContextRef cgContext, CFURLRef url, bool showBorder)
|
||||
bitmapInfo,
|
||||
provider,
|
||||
NULL,
|
||||
YES,
|
||||
true,
|
||||
renderingIntent);
|
||||
CGContextSetInterpolationQuality(cgContext, kCGInterpolationNone);
|
||||
NSGraphicsContext *context = [NSGraphicsContext graphicsContextWithGraphicsPort:(void *)cgContext flipped:NO];
|
||||
NSGraphicsContext *context = [NSGraphicsContext graphicsContextWithGraphicsPort:(void *)cgContext flipped:false];
|
||||
[NSGraphicsContext setCurrentContext:context];
|
||||
|
||||
|
||||
|
1020
bsnes/gb/SDL/console.c
Normal file
1020
bsnes/gb/SDL/console.c
Normal file
File diff suppressed because it is too large
Load Diff
49
bsnes/gb/SDL/console.h
Normal file
49
bsnes/gb/SDL/console.h
Normal file
@ -0,0 +1,49 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#define CON_EOF "\x04"
|
||||
bool CON_start(char *(*completer)(const char *substring, uintptr_t *context));
|
||||
char *CON_readline(const char *prompt);
|
||||
char *CON_readline_async(void);
|
||||
|
||||
typedef struct {
|
||||
enum {
|
||||
CON_COLOR_NONE = 0,
|
||||
CON_COLOR_BLACK,
|
||||
CON_COLOR_RED,
|
||||
CON_COLOR_GREEN,
|
||||
CON_COLOR_YELLOW,
|
||||
CON_COLOR_BLUE,
|
||||
CON_COLOR_MAGENTA,
|
||||
CON_COLOR_CYAN,
|
||||
CON_COLOR_LIGHT_GREY,
|
||||
|
||||
CON_COLOR_DARK_GREY = CON_COLOR_BLACK + 0x10,
|
||||
CON_COLOR_BRIGHT_RED = CON_COLOR_RED + 0x10,
|
||||
CON_COLOR_BRIGHT_GREEN = CON_COLOR_GREEN + 0x10,
|
||||
CON_COLOR_BRIGHT_YELLOW = CON_COLOR_YELLOW + 0x10,
|
||||
CON_COLOR_BRIGHT_BLUE = CON_COLOR_BLUE + 0x10,
|
||||
CON_COLOR_BRIGHT_MAGENTA = CON_COLOR_MAGENTA + 0x10,
|
||||
CON_COLOR_BRIGHT_CYAN = CON_COLOR_CYAN + 0x10,
|
||||
CON_COLOR_WHITE = CON_COLOR_LIGHT_GREY + 0x10,
|
||||
} color:8, background:8;
|
||||
bool bold;
|
||||
bool italic;
|
||||
bool underline;
|
||||
} CON_attributes_t;
|
||||
|
||||
#ifndef __printflike
|
||||
/* Missing from Linux headers. */
|
||||
#define __printflike(fmtarg, firstvararg) \
|
||||
__attribute__((__format__ (__printf__, fmtarg, firstvararg)))
|
||||
#endif
|
||||
|
||||
void CON_print(const char *string);
|
||||
void CON_attributed_print(const char *string, CON_attributes_t *attributes);
|
||||
void CON_vprintf(const char *fmt, va_list args);
|
||||
void CON_attributed_vprintf(const char *fmt, CON_attributes_t *attributes, va_list args);
|
||||
void CON_printf(const char *fmt, ...) __printflike(1, 2);
|
||||
void CON_attributed_printf(const char *fmt, CON_attributes_t *attributes,...) __printflike(1, 3);
|
||||
void CON_set_async_prompt(const char *string);
|
||||
void CON_set_repeat_empty(bool repeat);
|
@ -1160,6 +1160,12 @@ void run_gui(bool is_running)
|
||||
static SDL_Surface *converted_background = NULL;
|
||||
if (!converted_background) {
|
||||
SDL_Surface *background = SDL_LoadBMP(resource_path("background.bmp"));
|
||||
|
||||
/* Create a blank background if background.bmp could not be loaded */
|
||||
if (!background) {
|
||||
background = SDL_CreateRGBSurface(0, 160, 144, 8, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
SDL_SetPaletteColors(background->format->palette, gui_palette, 0, 4);
|
||||
converted_background = SDL_ConvertSurface(background, pixel_format, 0);
|
||||
SDL_LockSurface(converted_background);
|
||||
@ -1338,7 +1344,7 @@ void run_gui(bool is_running)
|
||||
break;
|
||||
}
|
||||
case SDL_DROPFILE: {
|
||||
if (GB_is_stave_state(event.drop.file)) {
|
||||
if (GB_is_save_state(event.drop.file)) {
|
||||
if (GB_is_inited(&gb)) {
|
||||
dropped_state_file = event.drop.file;
|
||||
pending_command = GB_SDL_LOAD_STATE_FROM_FILE_COMMAND;
|
||||
|
@ -49,8 +49,8 @@ extern unsigned command_parameter;
|
||||
extern char *dropped_state_file;
|
||||
|
||||
typedef enum {
|
||||
JOYPAD_BUTTON_LEFT,
|
||||
JOYPAD_BUTTON_RIGHT,
|
||||
JOYPAD_BUTTON_LEFT,
|
||||
JOYPAD_BUTTON_UP,
|
||||
JOYPAD_BUTTON_DOWN,
|
||||
JOYPAD_BUTTON_A,
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "gui.h"
|
||||
#include "shader.h"
|
||||
#include "audio/audio.h"
|
||||
#include "console.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
@ -29,6 +30,7 @@ static char *filename = NULL;
|
||||
static typeof(free) *free_function = NULL;
|
||||
static char *battery_save_path_ptr = NULL;
|
||||
static SDL_GLContext gl_context = NULL;
|
||||
static bool console_supported = false;
|
||||
|
||||
bool uses_gl(void)
|
||||
{
|
||||
@ -44,6 +46,80 @@ void set_filename(const char *new_filename, typeof(free) *new_free_function)
|
||||
free_function = new_free_function;
|
||||
}
|
||||
|
||||
static char *completer(const char *substring, uintptr_t *context)
|
||||
{
|
||||
if (!GB_is_inited(&gb)) return NULL;
|
||||
char *temp = strdup(substring);
|
||||
char *ret = GB_debugger_complete_substring(&gb, temp, context);
|
||||
free(temp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void log_callback(GB_gameboy_t *gb, const char *string, GB_log_attributes attributes)
|
||||
{
|
||||
CON_attributes_t con_attributes = {0,};
|
||||
con_attributes.bold = attributes & GB_LOG_BOLD;
|
||||
con_attributes.underline = attributes & GB_LOG_UNDERLINE;
|
||||
if (attributes & GB_LOG_DASHED_UNDERLINE) {
|
||||
while (*string) {
|
||||
con_attributes.underline ^= true;
|
||||
CON_attributed_printf("%c", &con_attributes, *string);
|
||||
string++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
CON_attributed_print(string, &con_attributes);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_eof(void)
|
||||
{
|
||||
CON_set_async_prompt("");
|
||||
char *line = CON_readline("Quit? [y]/n > ");
|
||||
if (line[0] == 'n' || line[0] == 'N') {
|
||||
free(line);
|
||||
CON_set_async_prompt("> ");
|
||||
}
|
||||
else {
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
static char *input_callback(GB_gameboy_t *gb)
|
||||
{
|
||||
retry: {
|
||||
char *ret = CON_readline("Stopped> ");
|
||||
if (strcmp(ret, CON_EOF) == 0) {
|
||||
handle_eof();
|
||||
free(ret);
|
||||
goto retry;
|
||||
}
|
||||
else {
|
||||
CON_attributes_t attributes = {.bold = true};
|
||||
CON_attributed_printf("> %s\n", &attributes, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
static char *asyc_input_callback(GB_gameboy_t *gb)
|
||||
{
|
||||
retry: {
|
||||
char *ret = CON_readline_async();
|
||||
if (ret && strcmp(ret, CON_EOF) == 0) {
|
||||
handle_eof();
|
||||
free(ret);
|
||||
goto retry;
|
||||
}
|
||||
else if (ret) {
|
||||
CON_attributes_t attributes = {.bold = true};
|
||||
CON_attributed_printf("> %s\n", &attributes, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static char *captured_log = NULL;
|
||||
|
||||
static void log_capture_callback(GB_gameboy_t *gb, const char *string, GB_log_attributes attributes)
|
||||
@ -67,7 +143,7 @@ static void start_capturing_logs(void)
|
||||
|
||||
static const char *end_capturing_logs(bool show_popup, bool should_exit, uint32_t popup_flags, const char *title)
|
||||
{
|
||||
GB_set_log_callback(&gb, NULL);
|
||||
GB_set_log_callback(&gb, console_supported? log_callback : NULL);
|
||||
if (captured_log[0] == 0) {
|
||||
free(captured_log);
|
||||
captured_log = NULL;
|
||||
@ -149,7 +225,7 @@ static void handle_events(GB_gameboy_t *gb)
|
||||
break;
|
||||
|
||||
case SDL_DROPFILE: {
|
||||
if (GB_is_stave_state(event.drop.file)) {
|
||||
if (GB_is_save_state(event.drop.file)) {
|
||||
dropped_state_file = event.drop.file;
|
||||
pending_command = GB_SDL_LOAD_STATE_FROM_FILE_COMMAND;
|
||||
}
|
||||
@ -256,6 +332,7 @@ static void handle_events(GB_gameboy_t *gb)
|
||||
}
|
||||
case SDL_SCANCODE_C:
|
||||
if (event.type == SDL_KEYDOWN && (event.key.keysym.mod & KMOD_CTRL)) {
|
||||
CON_print("^C\a\n");
|
||||
GB_debugger_break(gb);
|
||||
}
|
||||
break;
|
||||
@ -408,12 +485,15 @@ static void rumble(GB_gameboy_t *gb, double amp)
|
||||
|
||||
static void debugger_interrupt(int ignore)
|
||||
{
|
||||
if (!GB_is_inited(&gb)) return;
|
||||
if (!GB_is_inited(&gb)) exit(0);
|
||||
/* ^C twice to exit */
|
||||
if (GB_debugger_is_stopped(&gb)) {
|
||||
GB_save_battery(&gb, battery_save_path_ptr);
|
||||
exit(0);
|
||||
}
|
||||
if (console_supported) {
|
||||
CON_print("^C\n");
|
||||
}
|
||||
GB_debugger_break(&gb);
|
||||
}
|
||||
|
||||
@ -442,7 +522,6 @@ static void gb_audio_callback(GB_gameboy_t *gb, GB_sample_t *sample)
|
||||
GB_audio_queue_sample(sample);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static bool handle_pending_command(void)
|
||||
{
|
||||
@ -577,6 +656,13 @@ restart:
|
||||
GB_set_rtc_mode(&gb, configuration.rtc_mode);
|
||||
GB_set_update_input_hint_callback(&gb, handle_events);
|
||||
GB_apu_set_sample_callback(&gb, gb_audio_callback);
|
||||
|
||||
if (console_supported) {
|
||||
CON_set_async_prompt("> ");
|
||||
GB_set_log_callback(&gb, log_callback);
|
||||
GB_set_input_callback(&gb, input_callback);
|
||||
GB_set_async_input_callback(&gb, asyc_input_callback);
|
||||
}
|
||||
}
|
||||
if (stop_on_start) {
|
||||
stop_on_start = false;
|
||||
@ -682,18 +768,29 @@ static bool get_arg_flag(const char *flag, int *argc, char **argv)
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
static void enable_smooth_scrolling(void)
|
||||
{
|
||||
CFPreferencesSetAppValue(CFSTR("AppleMomentumScrollSupported"), kCFBooleanTrue, kCFPreferencesCurrentApplication);
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
SetProcessDPIAware();
|
||||
#endif
|
||||
fprintf(stderr, "SameBoy v" GB_VERSION "\n");
|
||||
|
||||
#ifdef __APPLE__
|
||||
enable_smooth_scrolling();
|
||||
#endif
|
||||
|
||||
bool fullscreen = get_arg_flag("--fullscreen", &argc, argv) || get_arg_flag("-f", &argc, argv);
|
||||
bool nogl = get_arg_flag("--nogl", &argc, argv);
|
||||
stop_on_start = get_arg_flag("--stop-debugger", &argc, argv) || get_arg_flag("-s", &argc, argv);
|
||||
|
||||
if (argc > 2 || (argc == 2 && argv[1][0] == '-')) {
|
||||
fprintf(stderr, "SameBoy v" GB_VERSION "\n");
|
||||
fprintf(stderr, "Usage: %s [--fullscreen|-f] [--nogl] [--stop-debugger|-s] [rom]\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
@ -705,6 +802,13 @@ int main(int argc, char **argv)
|
||||
signal(SIGINT, debugger_interrupt);
|
||||
|
||||
SDL_Init(SDL_INIT_EVERYTHING);
|
||||
if ((console_supported = CON_start(completer))) {
|
||||
CON_set_repeat_empty(true);
|
||||
CON_printf("SameBoy v" GB_VERSION "\n");
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "SameBoy v" GB_VERSION "\n");
|
||||
}
|
||||
|
||||
strcpy(prefs_path, resource_path("prefs.bin"));
|
||||
if (access(prefs_path, R_OK | W_OK) != 0) {
|
||||
|
@ -33,7 +33,7 @@ GB_gameboy_t gb;
|
||||
|
||||
static unsigned int frames = 0;
|
||||
static bool use_tga = false;
|
||||
static const uint8_t bmp_header[] = {
|
||||
static uint8_t bmp_header[] = {
|
||||
0x42, 0x4D, 0x48, 0x68, 0x01, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x38, 0x00,
|
||||
0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x70, 0xFF,
|
||||
@ -45,13 +45,13 @@ static const uint8_t bmp_header[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static const uint8_t tga_header[] = {
|
||||
static uint8_t tga_header[] = {
|
||||
0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x90, 0x00,
|
||||
0x20, 0x28,
|
||||
};
|
||||
|
||||
uint32_t bitmap[160*144];
|
||||
uint32_t bitmap[256*224];
|
||||
|
||||
static char *async_input_callback(GB_gameboy_t *gb)
|
||||
{
|
||||
@ -140,10 +140,22 @@ static void vblank(GB_gameboy_t *gb)
|
||||
|
||||
if (frames >= test_length && !gb->disable_rendering) {
|
||||
bool is_screen_blank = true;
|
||||
for (unsigned i = 160*144; i--;) {
|
||||
if (bitmap[i] != bitmap[0]) {
|
||||
is_screen_blank = false;
|
||||
break;
|
||||
if (!gb->sgb) {
|
||||
for (unsigned i = 160 * 144; i--;) {
|
||||
if (bitmap[i] != bitmap[0]) {
|
||||
is_screen_blank = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (gb->sgb->mask_mode == 0) {
|
||||
for (unsigned i = 160 * 144; i--;) {
|
||||
if (gb->sgb->screen_buffer[i] != gb->sgb->screen_buffer[0]) {
|
||||
is_screen_blank = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -151,12 +163,20 @@ static void vblank(GB_gameboy_t *gb)
|
||||
if (!is_screen_blank || frames >= test_length + 60 * 4) {
|
||||
FILE *f = fopen(bmp_filename, "wb");
|
||||
if (use_tga) {
|
||||
tga_header[0xC] = GB_get_screen_width(gb);
|
||||
tga_header[0xD] = GB_get_screen_width(gb) >> 8;
|
||||
tga_header[0xE] = GB_get_screen_height(gb);
|
||||
tga_header[0xF] = GB_get_screen_height(gb) >> 8;
|
||||
fwrite(&tga_header, 1, sizeof(tga_header), f);
|
||||
}
|
||||
else {
|
||||
(*(uint32_t *)&bmp_header[0x2]) = sizeof(bmp_header) + sizeof(bitmap[0]) * GB_get_screen_width(gb) * GB_get_screen_height(gb) + 2;
|
||||
(*(uint32_t *)&bmp_header[0x12]) = GB_get_screen_width(gb);
|
||||
(*(int32_t *)&bmp_header[0x16]) = -GB_get_screen_height(gb);
|
||||
(*(uint32_t *)&bmp_header[0x22]) = sizeof(bitmap[0]) * GB_get_screen_width(gb) * GB_get_screen_height(gb) + 2;
|
||||
fwrite(&bmp_header, 1, sizeof(bmp_header), f);
|
||||
}
|
||||
fwrite(&bitmap, 1, sizeof(bitmap), f);
|
||||
fwrite(&bitmap, 1, sizeof(bitmap[0]) * GB_get_screen_width(gb) * GB_get_screen_height(gb), f);
|
||||
fclose(f);
|
||||
if (!gb->boot_rom_finished) {
|
||||
GB_log(gb, "Boot ROM did not finish.\n");
|
||||
@ -271,7 +291,7 @@ int main(int argc, char **argv)
|
||||
fprintf(stderr, "SameBoy Tester v" GB_VERSION "\n");
|
||||
|
||||
if (argc == 1) {
|
||||
fprintf(stderr, "Usage: %s [--dmg] [--start] [--length seconds] [--sav] [--boot path to boot ROM]"
|
||||
fprintf(stderr, "Usage: %s [--dmg] [--sgb] [--cgb] [--start] [--length seconds] [--sav] [--boot path to boot ROM]"
|
||||
#ifndef _WIN32
|
||||
" [--jobs number of tests to run simultaneously]"
|
||||
#endif
|
||||
@ -285,6 +305,7 @@ int main(int argc, char **argv)
|
||||
#endif
|
||||
|
||||
bool dmg = false;
|
||||
bool sgb = false;
|
||||
bool sav = false;
|
||||
const char *boot_rom_path = NULL;
|
||||
|
||||
@ -294,6 +315,21 @@ int main(int argc, char **argv)
|
||||
if (strcmp(argv[i], "--dmg") == 0) {
|
||||
fprintf(stderr, "Using DMG mode\n");
|
||||
dmg = true;
|
||||
sgb = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcmp(argv[i], "--sgb") == 0) {
|
||||
fprintf(stderr, "Using SGB mode\n");
|
||||
sgb = true;
|
||||
dmg = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcmp(argv[i], "--cgb") == 0) {
|
||||
fprintf(stderr, "Using CGB mode\n");
|
||||
dmg = false;
|
||||
sgb = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -374,6 +410,13 @@ int main(int argc, char **argv)
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
else if (sgb) {
|
||||
GB_init(&gb, GB_MODEL_SGB2);
|
||||
if (GB_load_boot_rom(&gb, boot_rom_path ?: executable_relative_path("sgb2_boot.bin"))) {
|
||||
fprintf(stderr, "Failed to load boot ROM from '%s'\n", boot_rom_path ?: executable_relative_path("sgb2_boot.bin"));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
GB_init(&gb, GB_MODEL_CGB_E);
|
||||
if (GB_load_boot_rom(&gb, boot_rom_path ?: executable_relative_path("cgb_boot.bin"))) {
|
||||
|
86
bsnes/gb/Windows/pthread.h
Normal file
86
bsnes/gb/Windows/pthread.h
Normal file
@ -0,0 +1,86 @@
|
||||
/* Very minimal pthread implementation for Windows */
|
||||
#include <Windows.h>
|
||||
#include <assert.h>
|
||||
|
||||
typedef HANDLE pthread_t;
|
||||
typedef struct pthread_attr_s pthread_attr_t;
|
||||
|
||||
static inline int pthread_create(pthread_t *pthread,
|
||||
const pthread_attr_t *attrs,
|
||||
LPTHREAD_START_ROUTINE function,
|
||||
void *context)
|
||||
{
|
||||
assert(!attrs);
|
||||
*pthread = CreateThread(NULL, 0, function,
|
||||
context, 0, NULL);
|
||||
return *pthread? 0 : GetLastError();
|
||||
}
|
||||
|
||||
|
||||
typedef struct {
|
||||
unsigned status;
|
||||
CRITICAL_SECTION cs;
|
||||
} pthread_mutex_t;
|
||||
#define PTHREAD_MUTEX_INITIALIZER {0,}
|
||||
|
||||
static inline CRITICAL_SECTION *pthread_mutex_to_cs(pthread_mutex_t *mutex)
|
||||
{
|
||||
retry:
|
||||
if (mutex->status == 2) return &mutex->cs;
|
||||
|
||||
unsigned expected = 0;
|
||||
unsigned desired = 1;
|
||||
if (__atomic_compare_exchange(&mutex->status, &expected, &desired, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED)) {
|
||||
InitializeCriticalSection(&mutex->cs);
|
||||
mutex->status = 2;
|
||||
return &mutex->cs;
|
||||
}
|
||||
goto retry;
|
||||
}
|
||||
|
||||
static inline int pthread_mutex_lock(pthread_mutex_t *mutex)
|
||||
{
|
||||
EnterCriticalSection(pthread_mutex_to_cs(mutex));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int pthread_mutex_unlock(pthread_mutex_t *mutex)
|
||||
{
|
||||
LeaveCriticalSection(pthread_mutex_to_cs(mutex));
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
unsigned status;
|
||||
CONDITION_VARIABLE cond;
|
||||
} pthread_cond_t;
|
||||
#define PTHREAD_COND_INITIALIZER {0,}
|
||||
|
||||
static inline CONDITION_VARIABLE *pthread_cond_to_win(pthread_cond_t *cond)
|
||||
{
|
||||
retry:
|
||||
if (cond->status == 2) return &cond->cond;
|
||||
|
||||
unsigned expected = 0;
|
||||
unsigned desired = 1;
|
||||
if (__atomic_compare_exchange(&cond->status, &expected, &desired, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED)) {
|
||||
InitializeConditionVariable(&cond->cond);
|
||||
cond->status = 2;
|
||||
return &cond->cond;
|
||||
}
|
||||
goto retry;
|
||||
}
|
||||
|
||||
|
||||
static inline int pthread_cond_signal(pthread_cond_t *cond)
|
||||
{
|
||||
WakeConditionVariable(pthread_cond_to_win(cond));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int pthread_cond_wait(pthread_cond_t *cond,
|
||||
pthread_mutex_t *mutex)
|
||||
{
|
||||
// Mutex is locked therefore already initialized
|
||||
return SleepConditionVariableCS(pthread_cond_to_win(cond), &mutex->cs, INFINITE);
|
||||
}
|
@ -5,3 +5,4 @@
|
||||
|
||||
#define read(...) _read(__VA_ARGS__)
|
||||
#define write(...) _write(__VA_ARGS__)
|
||||
#define isatty(...) _isatty(__VA_ARGS__)
|
||||
|
@ -1 +1 @@
|
||||
VERSION := 0.14.4
|
||||
VERSION := 0.14.7
|
@ -183,7 +183,7 @@ auto Presentation::create() -> void {
|
||||
.setName("SameBoy")
|
||||
.setLogo(Resource::SameBoy)
|
||||
.setDescription("Super Game Boy emulator")
|
||||
.setVersion("0.14.4")
|
||||
.setVersion("0.14.7")
|
||||
.setCopyright("Lior Halphon")
|
||||
.setLicense("MIT")
|
||||
.setWebsite("https://sameboy.github.io")
|
||||
|
@ -24,6 +24,8 @@ obj/libretro.o: target-libretro/libretro.cpp
|
||||
all: $(objects)
|
||||
ifeq ($(platform),linux)
|
||||
$(strip $(compiler) -o out/bsnes_libretro.so -shared $(objects) -Wl,--no-undefined -Wl,--version-script=target-libretro/link.T -lgomp -Wl,-Bdynamic $(options))
|
||||
else ifeq ($(platform),haiku)
|
||||
$(strip $(compiler) -o out/bsnes_libretro.so -shared $(objects) -Wl,--no-undefined -Wl,--version-script=target-libretro/link.T -lgomp -Wl,-Bdynamic $(options))
|
||||
else ifeq ($(platform),windows)
|
||||
$(strip $(compiler) -o out/bsnes_libretro.dll -shared $(objects) -Wl,--no-undefined -Wl,--version-script=target-libretro/link.T -lgomp -Wl,-Bdynamic $(options))
|
||||
else ifeq ($(platform),libnx)
|
||||
|
@ -154,6 +154,11 @@ ifeq ($(platform),bsd)
|
||||
options += -lstdc++ -lm
|
||||
endif
|
||||
|
||||
# haiku settings
|
||||
ifeq ($(platform),haiku)
|
||||
options += -lroot
|
||||
endif
|
||||
|
||||
# threading support
|
||||
ifeq ($(threaded),true)
|
||||
ifneq ($(filter $(platform),linux bsd),)
|
||||
|
@ -267,11 +267,7 @@ inline auto directory::copy(const string& source, const string& target) -> bool
|
||||
}
|
||||
#else
|
||||
inline auto directoryIsFolder(DIR* dp, struct dirent* ep) -> bool {
|
||||
#if defined(PLATFORM_HAIKU)
|
||||
struct stat sp = {0};
|
||||
fstatat(dirfd(dp), ep->d_name, &sp, 0);
|
||||
return S_ISDIR(sp.st_mode);
|
||||
#else
|
||||
#if defined(PLATFORM_MACOS) || defined(PLATFORM_LINUX) || defined(PLATFORM_BSD)
|
||||
if(ep->d_type == DT_DIR) return true;
|
||||
if(ep->d_type == DT_LNK || ep->d_type == DT_UNKNOWN) {
|
||||
//symbolic links must be resolved to determine type
|
||||
@ -279,8 +275,16 @@ inline auto directory::copy(const string& source, const string& target) -> bool
|
||||
fstatat(dirfd(dp), ep->d_name, &sp, 0);
|
||||
return S_ISDIR(sp.st_mode);
|
||||
}
|
||||
return false;
|
||||
#else // strictly POSIX systems
|
||||
struct stat sp = {0};
|
||||
stat(ep->d_name, &sp);
|
||||
if S_ISDIR(sp.st_mode) return true;
|
||||
if (S_ISLNK(sp.st_mode) || not S_ISREG(sp.st_mode)) {
|
||||
fstatat(dirfd(dp), ep->d_name, &sp, 0);
|
||||
return S_ISDIR(sp.st_mode);
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
inline auto directory::create(const string& pathname, uint permissions) -> bool {
|
||||
|
@ -4,7 +4,7 @@ namespace nall {
|
||||
using uint = unsigned;
|
||||
|
||||
enum class Compiler : uint { Clang, GCC, Microsoft, Unknown };
|
||||
enum class Platform : uint { Windows, MacOS, Linux, BSD, BeAPI, Android, Horizon, Unknown };
|
||||
enum class Platform : uint { Windows, MacOS, Linux, BSD, Haiku, Android, Horizon, Unknown };
|
||||
enum class API : uint { Windows, Posix, Unknown };
|
||||
enum class DisplayServer : uint { Windows, Quartz, Xorg, Unknown };
|
||||
enum class Architecture : uint { x86, amd64, ARM32, ARM64, PPC32, PPC64, Unknown };
|
||||
@ -112,11 +112,11 @@ namespace nall {
|
||||
constexpr auto platform() -> Platform { return Platform::BSD; }
|
||||
constexpr auto api() -> API { return API::Posix; }
|
||||
constexpr auto display() -> DisplayServer { return DisplayServer::Xorg; }
|
||||
#elif defined(__HAIKU__)
|
||||
#elif defined(__HAIKU__)
|
||||
#define PLATFORM_HAIKU
|
||||
#define API_POSIX
|
||||
#define DISPLAY_UNKNOWN
|
||||
constexpr auto platform() -> Platform { return Platform::BeAPI; }
|
||||
constexpr auto platform() -> Platform { return Platform::Haiku; }
|
||||
constexpr auto api() -> API { return API::Posix; }
|
||||
constexpr auto display() -> DisplayServer { return DisplayServer::Unknown; }
|
||||
#else
|
||||
|
@ -252,18 +252,18 @@ auto Audio::optimalDriver() -> string {
|
||||
return "DirectSound 7.0";
|
||||
#elif defined(AUDIO_WAVEOUT)
|
||||
return "waveOut";
|
||||
#elif defined(AUDIO_ALSA)
|
||||
return "ALSA";
|
||||
#elif defined(AUDIO_OSS)
|
||||
return "OSS";
|
||||
#elif defined(AUDIO_OPENAL)
|
||||
return "OpenAL";
|
||||
#elif defined(AUDIO_PULSEAUDIO)
|
||||
return "PulseAudio";
|
||||
#elif defined(AUDIO_PULSEAUDIOSIMPLE)
|
||||
return "PulseAudio Simple";
|
||||
#elif defined(AUDIO_OPENAL)
|
||||
return "OpenAL";
|
||||
#elif defined(AUDIO_AO)
|
||||
return "libao";
|
||||
#elif defined(AUDIO_ALSA)
|
||||
return "ALSA";
|
||||
#elif defined(AUDIO_OSS)
|
||||
return "OSS";
|
||||
#else
|
||||
return "None";
|
||||
#endif
|
||||
|
82
ruby/input/mouse/quartz.cpp
Normal file
82
ruby/input/mouse/quartz.cpp
Normal file
@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
|
||||
struct InputMouseQuartz {
|
||||
Input& input;
|
||||
InputMouseQuartz(Input& input) : input(input) {}
|
||||
|
||||
shared_pointer<HID::Mouse> hid{new HID::Mouse};
|
||||
|
||||
bool mouseAcquired = false;
|
||||
|
||||
auto acquire() -> bool {
|
||||
if(!mouseAcquired) {
|
||||
CGError error = CGAssociateMouseAndMouseCursorPosition(NO);
|
||||
if(error != kCGErrorSuccess) return false;
|
||||
[NSCursor hide];
|
||||
|
||||
mouseAcquired = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
auto acquired() -> bool {
|
||||
return mouseAcquired;
|
||||
}
|
||||
|
||||
auto release() -> bool {
|
||||
if(mouseAcquired) {
|
||||
CGError error = CGAssociateMouseAndMouseCursorPosition(YES);
|
||||
if(error != kCGErrorSuccess) return false;
|
||||
[NSCursor unhide];
|
||||
|
||||
mouseAcquired = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
auto assign(uint groupID, uint inputID, int16_t value) -> void {
|
||||
auto& group = hid->group(groupID);
|
||||
if(group.input(inputID).value() == value) return;
|
||||
input.doChange(hid, groupID, inputID, group.input(inputID).value(), value);
|
||||
group.input(inputID).setValue(value);
|
||||
}
|
||||
|
||||
auto poll(vector<shared_pointer<HID::Device>>& devices) -> void {
|
||||
int deltaX, deltaY;
|
||||
CGGetLastMouseDelta(&deltaX, &deltaY);
|
||||
|
||||
assign(HID::Mouse::GroupID::Axis, 0, deltaX);
|
||||
assign(HID::Mouse::GroupID::Axis, 1, deltaY);
|
||||
|
||||
NSUInteger buttons = [NSEvent pressedMouseButtons];
|
||||
|
||||
assign(HID::Mouse::GroupID::Button, 0, !!(buttons & (1 << 0)));
|
||||
assign(HID::Mouse::GroupID::Button, 2, !!(buttons & (1 << 1)));
|
||||
assign(HID::Mouse::GroupID::Button, 1, !!(buttons & (1 << 2)));
|
||||
assign(HID::Mouse::GroupID::Button, 4, !!(buttons & (1 << 3)));
|
||||
assign(HID::Mouse::GroupID::Button, 3, !!(buttons & (1 << 4)));
|
||||
|
||||
devices.append(hid);
|
||||
}
|
||||
|
||||
auto initialize() -> bool {
|
||||
hid->setVendorID(HID::Mouse::GenericVendorID);
|
||||
hid->setProductID(HID::Mouse::GenericProductID);
|
||||
hid->setPathID(0);
|
||||
|
||||
hid->axes().append("X");
|
||||
hid->axes().append("Y");
|
||||
|
||||
hid->buttons().append("Left");
|
||||
hid->buttons().append("Middle");
|
||||
hid->buttons().append("Right");
|
||||
hid->buttons().append("Up");
|
||||
hid->buttons().append("Down");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto terminate() -> void {
|
||||
release();
|
||||
}
|
||||
};
|
@ -1,9 +1,10 @@
|
||||
#include "keyboard/quartz.cpp"
|
||||
#include "mouse/quartz.cpp"
|
||||
#include "joypad/iokit.cpp"
|
||||
|
||||
struct InputQuartz : InputDriver {
|
||||
InputQuartz& self = *this;
|
||||
InputQuartz(Input& super) : InputDriver(super), keyboard(super), joypad(super) {}
|
||||
InputQuartz(Input& super) : InputDriver(super), keyboard(super), mouse(super), joypad(super) {}
|
||||
~InputQuartz() { terminate(); }
|
||||
|
||||
auto create() -> bool override {
|
||||
@ -13,13 +14,14 @@ struct InputQuartz : InputDriver {
|
||||
auto driver() -> string override { return "Quartz"; }
|
||||
auto ready() -> bool override { return isReady; }
|
||||
|
||||
auto acquired() -> bool override { return false; }
|
||||
auto acquire() -> bool override { return false; }
|
||||
auto release() -> bool override { return false; }
|
||||
auto acquired() -> bool override { return mouse.acquired(); }
|
||||
auto acquire() -> bool override { return mouse.acquire(); }
|
||||
auto release() -> bool override { return mouse.release(); }
|
||||
|
||||
auto poll() -> vector<shared_pointer<HID::Device>> override {
|
||||
vector<shared_pointer<HID::Device>> devices;
|
||||
keyboard.poll(devices);
|
||||
mouse.poll(devices);
|
||||
joypad.poll(devices);
|
||||
return devices;
|
||||
}
|
||||
@ -32,6 +34,7 @@ private:
|
||||
auto initialize() -> bool {
|
||||
terminate();
|
||||
if(!keyboard.initialize()) return false;
|
||||
if(!mouse.initialize()) return false;
|
||||
if(!joypad.initialize()) return false;
|
||||
return isReady = true;
|
||||
}
|
||||
@ -39,10 +42,12 @@ private:
|
||||
auto terminate() -> void {
|
||||
isReady = false;
|
||||
keyboard.terminate();
|
||||
mouse.terminate();
|
||||
joypad.terminate();
|
||||
}
|
||||
|
||||
bool isReady = false;
|
||||
InputKeyboardQuartz keyboard;
|
||||
InputMouseQuartz mouse;
|
||||
InputJoypadIOKit joypad;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user