Merge remote-tracking branch 'origin/master' into gtk3
This commit is contained in:
commit
5c9ff6f9e6
@ -74,8 +74,15 @@ enum model {
|
|||||||
topMargin:(unsigned) topMargin bottomMargin: (unsigned) bottomMargin
|
topMargin:(unsigned) topMargin bottomMargin: (unsigned) bottomMargin
|
||||||
exposure:(unsigned) exposure;
|
exposure:(unsigned) exposure;
|
||||||
- (void) gotNewSample:(GB_sample_t *)sample;
|
- (void) gotNewSample:(GB_sample_t *)sample;
|
||||||
|
- (void) loadBootROM:(GB_boot_rom_t)type;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
static void boot_rom_load(GB_gameboy_t *gb, GB_boot_rom_t type)
|
||||||
|
{
|
||||||
|
Document *self = (__bridge Document *)GB_get_user_data(gb);
|
||||||
|
[self loadBootROM: type];
|
||||||
|
}
|
||||||
|
|
||||||
static void vblank(GB_gameboy_t *gb)
|
static void vblank(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
Document *self = (__bridge Document *)GB_get_user_data(gb);
|
Document *self = (__bridge Document *)GB_get_user_data(gb);
|
||||||
@ -184,15 +191,38 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void) updatePalette
|
||||||
|
{
|
||||||
|
switch ([[NSUserDefaults standardUserDefaults] integerForKey:@"GBColorPalette"]) {
|
||||||
|
case 1:
|
||||||
|
GB_set_palette(&gb, &GB_PALETTE_DMG);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
GB_set_palette(&gb, &GB_PALETTE_MGB);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
GB_set_palette(&gb, &GB_PALETTE_GBL);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
GB_set_palette(&gb, &GB_PALETTE_GREY);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
- (void) initCommon
|
- (void) initCommon
|
||||||
{
|
{
|
||||||
GB_init(&gb, [self internalModel]);
|
GB_init(&gb, [self internalModel]);
|
||||||
GB_set_user_data(&gb, (__bridge void *)(self));
|
GB_set_user_data(&gb, (__bridge void *)(self));
|
||||||
|
GB_set_boot_rom_load_callback(&gb, (GB_boot_rom_load_callback_t)boot_rom_load);
|
||||||
GB_set_vblank_callback(&gb, (GB_vblank_callback_t) vblank);
|
GB_set_vblank_callback(&gb, (GB_vblank_callback_t) vblank);
|
||||||
GB_set_log_callback(&gb, (GB_log_callback_t) consoleLog);
|
GB_set_log_callback(&gb, (GB_log_callback_t) consoleLog);
|
||||||
GB_set_input_callback(&gb, (GB_input_callback_t) consoleInput);
|
GB_set_input_callback(&gb, (GB_input_callback_t) consoleInput);
|
||||||
GB_set_async_input_callback(&gb, (GB_input_callback_t) asyncConsoleInput);
|
GB_set_async_input_callback(&gb, (GB_input_callback_t) asyncConsoleInput);
|
||||||
GB_set_color_correction_mode(&gb, (GB_color_correction_mode_t) [[NSUserDefaults standardUserDefaults] integerForKey:@"GBColorCorrection"]);
|
GB_set_color_correction_mode(&gb, (GB_color_correction_mode_t) [[NSUserDefaults standardUserDefaults] integerForKey:@"GBColorCorrection"]);
|
||||||
|
[self updatePalette];
|
||||||
GB_set_rgb_encode_callback(&gb, rgbEncode);
|
GB_set_rgb_encode_callback(&gb, rgbEncode);
|
||||||
GB_set_camera_get_pixel_callback(&gb, cameraGetPixel);
|
GB_set_camera_get_pixel_callback(&gb, cameraGetPixel);
|
||||||
GB_set_camera_update_request_callback(&gb, cameraRequestUpdate);
|
GB_set_camera_update_request_callback(&gb, cameraRequestUpdate);
|
||||||
@ -318,15 +348,19 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
GB_debugger_set_disabled(&gb, false);
|
GB_debugger_set_disabled(&gb, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) loadBootROM
|
- (void) loadBootROM: (GB_boot_rom_t)type
|
||||||
{
|
{
|
||||||
static NSString * const boot_names[] = {@"dmg_boot", @"cgb_boot", @"agb_boot", @"sgb_boot"};
|
static NSString *const names[] = {
|
||||||
if ([self internalModel] == GB_MODEL_SGB2) {
|
[GB_BOOT_ROM_DMG0] = @"dmg0_boot",
|
||||||
GB_load_boot_rom(&gb, [[self bootROMPathForName:@"sgb2_boot"] UTF8String]);
|
[GB_BOOT_ROM_DMG] = @"dmg_boot",
|
||||||
}
|
[GB_BOOT_ROM_MGB] = @"mgb_boot",
|
||||||
else {
|
[GB_BOOT_ROM_SGB] = @"sgb_boot",
|
||||||
GB_load_boot_rom(&gb, [[self bootROMPathForName:boot_names[current_model - 1]] UTF8String]);
|
[GB_BOOT_ROM_SGB2] = @"sgb2_boot",
|
||||||
}
|
[GB_BOOT_ROM_CGB0] = @"cgb0_boot",
|
||||||
|
[GB_BOOT_ROM_CGB] = @"cgb_boot",
|
||||||
|
[GB_BOOT_ROM_AGB] = @"agb_boot",
|
||||||
|
};
|
||||||
|
GB_load_boot_rom(&gb, [[self bootROMPathForName:names[type]] UTF8String]);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)reset:(id)sender
|
- (IBAction)reset:(id)sender
|
||||||
@ -338,8 +372,6 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
current_model = (enum model)[sender tag];
|
current_model = (enum model)[sender tag];
|
||||||
}
|
}
|
||||||
|
|
||||||
[self loadBootROM];
|
|
||||||
|
|
||||||
if (!modelsChanging && [sender tag] == MODEL_NONE) {
|
if (!modelsChanging && [sender tag] == MODEL_NONE) {
|
||||||
GB_reset(&gb);
|
GB_reset(&gb);
|
||||||
}
|
}
|
||||||
@ -452,6 +484,12 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
name:@"GBColorCorrectionChanged"
|
name:@"GBColorCorrectionChanged"
|
||||||
object:nil];
|
object:nil];
|
||||||
|
|
||||||
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||||
|
selector:@selector(updatePalette)
|
||||||
|
name:@"GBColorPaletteChanged"
|
||||||
|
object:nil];
|
||||||
|
|
||||||
|
|
||||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||||
selector:@selector(updateRewindLength)
|
selector:@selector(updateRewindLength)
|
||||||
name:@"GBRewindLengthChanged"
|
name:@"GBRewindLengthChanged"
|
||||||
@ -1171,6 +1209,23 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
|||||||
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||||
@try {
|
@try {
|
||||||
if (!cameraSession) {
|
if (!cameraSession) {
|
||||||
|
if (@available(macOS 10.14, *)) {
|
||||||
|
switch ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]) {
|
||||||
|
case AVAuthorizationStatusAuthorized:
|
||||||
|
break;
|
||||||
|
case AVAuthorizationStatusNotDetermined: {
|
||||||
|
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
|
||||||
|
[self cameraRequestUpdate];
|
||||||
|
}];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case AVAuthorizationStatusDenied:
|
||||||
|
case AVAuthorizationStatusRestricted:
|
||||||
|
GB_camera_updated(&gb);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
NSError *error;
|
NSError *error;
|
||||||
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType: AVMediaTypeVideo];
|
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType: AVMediaTypeVideo];
|
||||||
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice: device error: &error];
|
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice: device error: &error];
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
@property (strong) IBOutlet NSButton *aspectRatioCheckbox;
|
@property (strong) IBOutlet NSButton *aspectRatioCheckbox;
|
||||||
@property (strong) IBOutlet NSPopUpButton *highpassFilterPopupButton;
|
@property (strong) IBOutlet NSPopUpButton *highpassFilterPopupButton;
|
||||||
@property (strong) IBOutlet NSPopUpButton *colorCorrectionPopupButton;
|
@property (strong) IBOutlet NSPopUpButton *colorCorrectionPopupButton;
|
||||||
|
@property (nonatomic, strong) IBOutlet NSPopUpButton *colorPalettePopupButton;
|
||||||
@property (strong) IBOutlet NSPopUpButton *rewindPopupButton;
|
@property (strong) IBOutlet NSPopUpButton *rewindPopupButton;
|
||||||
@property (strong) IBOutlet NSButton *configureJoypadButton;
|
@property (strong) IBOutlet NSButton *configureJoypadButton;
|
||||||
@property (strong) IBOutlet NSButton *skipButton;
|
@property (strong) IBOutlet NSButton *skipButton;
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
NSPopUpButton *_graphicsFilterPopupButton;
|
NSPopUpButton *_graphicsFilterPopupButton;
|
||||||
NSPopUpButton *_highpassFilterPopupButton;
|
NSPopUpButton *_highpassFilterPopupButton;
|
||||||
NSPopUpButton *_colorCorrectionPopupButton;
|
NSPopUpButton *_colorCorrectionPopupButton;
|
||||||
|
NSPopUpButton *_colorPalettePopupButton;
|
||||||
NSPopUpButton *_rewindPopupButton;
|
NSPopUpButton *_rewindPopupButton;
|
||||||
NSButton *_aspectRatioCheckbox;
|
NSButton *_aspectRatioCheckbox;
|
||||||
NSEventModifierFlags previousModifiers;
|
NSEventModifierFlags previousModifiers;
|
||||||
@ -31,6 +32,7 @@
|
|||||||
@"NearestNeighbor",
|
@"NearestNeighbor",
|
||||||
@"Bilinear",
|
@"Bilinear",
|
||||||
@"SmoothBilinear",
|
@"SmoothBilinear",
|
||||||
|
@"MonoLCD",
|
||||||
@"LCD",
|
@"LCD",
|
||||||
@"CRT",
|
@"CRT",
|
||||||
@"Scale2x",
|
@"Scale2x",
|
||||||
@ -84,6 +86,18 @@
|
|||||||
return _colorCorrectionPopupButton;
|
return _colorCorrectionPopupButton;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)setColorPalettePopupButton:(NSPopUpButton *)colorPalettePopupButton
|
||||||
|
{
|
||||||
|
_colorPalettePopupButton = colorPalettePopupButton;
|
||||||
|
NSInteger mode = [[NSUserDefaults standardUserDefaults] integerForKey:@"GBColorPalette"];
|
||||||
|
[_colorPalettePopupButton selectItemAtIndex:mode];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSPopUpButton *)colorPalettePopupButton
|
||||||
|
{
|
||||||
|
return _colorPalettePopupButton;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)setRewindPopupButton:(NSPopUpButton *)rewindPopupButton
|
- (void)setRewindPopupButton:(NSPopUpButton *)rewindPopupButton
|
||||||
{
|
{
|
||||||
_rewindPopupButton = rewindPopupButton;
|
_rewindPopupButton = rewindPopupButton;
|
||||||
@ -199,6 +213,14 @@
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (IBAction)colorPaletteChanged:(id)sender
|
||||||
|
{
|
||||||
|
[[NSUserDefaults standardUserDefaults] setObject:@([sender indexOfSelectedItem])
|
||||||
|
forKey:@"GBColorPalette"];
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBColorPaletteChanged" object:nil];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
- (IBAction)rewindLengthChanged:(id)sender
|
- (IBAction)rewindLengthChanged:(id)sender
|
||||||
{
|
{
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:@([sender selectedTag])
|
[[NSUserDefaults standardUserDefaults] setObject:@([sender selectedTag])
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>SameBoy</string>
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>en</string>
|
<string>en</string>
|
||||||
<key>CFBundleDocumentTypes</key>
|
<key>CFBundleDocumentTypes</key>
|
||||||
@ -70,7 +72,7 @@
|
|||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
<string>10.9</string>
|
<string>10.9</string>
|
||||||
<key>NSHumanReadableCopyright</key>
|
<key>NSHumanReadableCopyright</key>
|
||||||
<string>Copyright © 2015-2019 Lior Halphon</string>
|
<string>Copyright © 2015-2020 Lior Halphon</string>
|
||||||
<key>NSMainNibFile</key>
|
<key>NSMainNibFile</key>
|
||||||
<string>MainMenu</string>
|
<string>MainMenu</string>
|
||||||
<key>NSPrincipalClass</key>
|
<key>NSPrincipalClass</key>
|
||||||
@ -116,6 +118,8 @@
|
|||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
|
<key>NSCameraUsageDescription</key>
|
||||||
|
<string>SameBoy needs to access your camera to emulate the Game Boy Camera</string>
|
||||||
<key>NSSupportsAutomaticGraphicsSwitching</key>
|
<key>NSSupportsAutomaticGraphicsSwitching</key>
|
||||||
<true/>
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
<h1>SameBoy</h1>
|
<h1>SameBoy</h1>
|
||||||
<h2>MIT License</h2>
|
<h2>MIT License</h2>
|
||||||
<h3>Copyright © 2015-2019 Lior Halphon</h3>
|
<h3>Copyright © 2015-2020 Lior Halphon</h3>
|
||||||
|
|
||||||
<p>Permission is hereby granted, free of charge, to any person obtaining a copy
|
<p>Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="13771" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14868" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="macosx"/>
|
<deployment identifier="macosx"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="13771"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14868"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<objects>
|
<objects>
|
||||||
@ -17,7 +17,7 @@
|
|||||||
</customObject>
|
</customObject>
|
||||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||||
<window title="Preferences" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="QvC-M9-y7g" customClass="GBPreferencesWindow">
|
<window title="Preferences" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="QvC-M9-y7g" customClass="GBPreferencesWindow">
|
||||||
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
|
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
|
||||||
<windowCollectionBehavior key="collectionBehavior" fullScreenAuxiliary="YES"/>
|
<windowCollectionBehavior key="collectionBehavior" fullScreenAuxiliary="YES"/>
|
||||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||||
@ -63,6 +63,7 @@
|
|||||||
<outlet property="bootROMsFolderItem" destination="Dzv-Gc-zoL" id="yhV-ZI-avD"/>
|
<outlet property="bootROMsFolderItem" destination="Dzv-Gc-zoL" id="yhV-ZI-avD"/>
|
||||||
<outlet property="cgbPopupButton" destination="dlD-sk-SHO" id="4tg-SR-e17"/>
|
<outlet property="cgbPopupButton" destination="dlD-sk-SHO" id="4tg-SR-e17"/>
|
||||||
<outlet property="colorCorrectionPopupButton" destination="VEz-N4-uP6" id="EO2-Vt-JFJ"/>
|
<outlet property="colorCorrectionPopupButton" destination="VEz-N4-uP6" id="EO2-Vt-JFJ"/>
|
||||||
|
<outlet property="colorPalettePopupButton" destination="Iwr-eI-SD1" id="Xzc-RZ-JtV"/>
|
||||||
<outlet property="configureJoypadButton" destination="Qa7-Z7-yfO" id="RaX-P3-oCX"/>
|
<outlet property="configureJoypadButton" destination="Qa7-Z7-yfO" id="RaX-P3-oCX"/>
|
||||||
<outlet property="controlsTableView" destination="UDd-IJ-fxX" id="a1D-Md-yXv"/>
|
<outlet property="controlsTableView" destination="UDd-IJ-fxX" id="a1D-Md-yXv"/>
|
||||||
<outlet property="delegate" destination="-2" id="ASc-vN-Zbq"/>
|
<outlet property="delegate" destination="-2" id="ASc-vN-Zbq"/>
|
||||||
@ -78,11 +79,11 @@
|
|||||||
<point key="canvasLocation" x="183" y="354"/>
|
<point key="canvasLocation" x="183" y="354"/>
|
||||||
</window>
|
</window>
|
||||||
<customView id="sRK-wO-K6R">
|
<customView id="sRK-wO-K6R">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="292" height="166"/>
|
<rect key="frame" x="0.0" y="0.0" width="292" height="213"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="T91-rh-rRp">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="T91-rh-rRp">
|
||||||
<rect key="frame" x="18" y="129" width="256" height="17"/>
|
<rect key="frame" x="18" y="176" width="256" height="17"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Graphics Filter:" id="pXg-WY-8Q5">
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Graphics Filter:" id="pXg-WY-8Q5">
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
@ -91,7 +92,7 @@
|
|||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
</textField>
|
</textField>
|
||||||
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6pP-kK-EEC">
|
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6pP-kK-EEC">
|
||||||
<rect key="frame" x="30" y="97" width="234" height="26"/>
|
<rect key="frame" x="30" y="144" width="234" height="26"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="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">
|
<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"/>
|
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||||
@ -103,7 +104,8 @@
|
|||||||
</menuItem>
|
</menuItem>
|
||||||
<menuItem title="Bilinear (Blurry)" id="iDe-si-atu"/>
|
<menuItem title="Bilinear (Blurry)" id="iDe-si-atu"/>
|
||||||
<menuItem title="Smooth Bilinear (Less blurry)" id="1jN-pO-1iD"/>
|
<menuItem title="Smooth Bilinear (Less blurry)" id="1jN-pO-1iD"/>
|
||||||
<menuItem title="LCD Display" id="b8u-LZ-UQf"/>
|
<menuItem title="Monochrome LCD Display" id="b8u-LZ-UQf"/>
|
||||||
|
<menuItem title="LCD Display" id="pj3-Jt-bNU"/>
|
||||||
<menuItem title="CRT Display" id="FT9-FT-RZu"/>
|
<menuItem title="CRT Display" id="FT9-FT-RZu"/>
|
||||||
<menuItem title="Scale2x" id="C1I-L2-Up1"/>
|
<menuItem title="Scale2x" id="C1I-L2-Up1"/>
|
||||||
<menuItem title="Scale4x" id="uWA-Zp-JY9"/>
|
<menuItem title="Scale4x" id="uWA-Zp-JY9"/>
|
||||||
@ -127,7 +129,7 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</popUpButton>
|
</popUpButton>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Wc3-2K-6CD">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Wc3-2K-6CD">
|
||||||
<rect key="frame" x="18" y="75" width="256" height="17"/>
|
<rect key="frame" x="18" y="122" width="256" height="17"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Color Correction:" id="5Si-hz-EK3">
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Color Correction:" id="5Si-hz-EK3">
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
@ -136,7 +138,7 @@
|
|||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
</textField>
|
</textField>
|
||||||
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="VEz-N4-uP6">
|
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="VEz-N4-uP6">
|
||||||
<rect key="frame" x="30" y="43" width="234" height="26"/>
|
<rect key="frame" x="30" y="90" width="234" height="26"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="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">
|
<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"/>
|
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||||
@ -156,6 +158,36 @@
|
|||||||
<action selector="colorCorrectionChanged:" target="QvC-M9-y7g" id="Oq4-B5-nO6"/>
|
<action selector="colorCorrectionChanged:" target="QvC-M9-y7g" id="Oq4-B5-nO6"/>
|
||||||
</connections>
|
</connections>
|
||||||
</popUpButton>
|
</popUpButton>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="8fG-zm-hpr">
|
||||||
|
<rect key="frame" x="18" y="68" width="252" height="17"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="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"/>
|
||||||
|
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Iwr-eI-SD1">
|
||||||
|
<rect key="frame" x="30" y="39" width="229" height="22"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="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"/>
|
||||||
|
<menu key="menu" id="dHJ-3R-Ora">
|
||||||
|
<items>
|
||||||
|
<menuItem title="Greyscale" state="on" id="Ajr-5r-iIk">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Lime (Game Boy)" id="snU-ht-fQq"/>
|
||||||
|
<menuItem title="Olive (Pocket)" id="MQi-yt-nsT"/>
|
||||||
|
<menuItem title="Teal (Light)" id="xlg-6i-Fhl"/>
|
||||||
|
</items>
|
||||||
|
</menu>
|
||||||
|
</popUpButtonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="colorPaletteChanged:" target="QvC-M9-y7g" id="ui3-rg-PTs"/>
|
||||||
|
</connections>
|
||||||
|
</popUpButton>
|
||||||
<button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Vfj-tg-7OP">
|
<button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Vfj-tg-7OP">
|
||||||
<rect key="frame" x="18" y="18" width="256" height="18"/>
|
<rect key="frame" x="18" y="18" width="256" height="18"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
@ -168,7 +200,7 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
</subviews>
|
</subviews>
|
||||||
<point key="canvasLocation" x="-176" y="589"/>
|
<point key="canvasLocation" x="-176" y="612.5"/>
|
||||||
</customView>
|
</customView>
|
||||||
<customView id="ymk-46-SX7">
|
<customView id="ymk-46-SX7">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="292" height="320"/>
|
<rect key="frame" x="0.0" y="0.0" width="292" height="320"/>
|
||||||
@ -397,7 +429,7 @@
|
|||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<clipView key="contentView" focusRingType="none" ambiguous="YES" drawsBackground="NO" id="AMs-PO-nid">
|
<clipView key="contentView" focusRingType="none" ambiguous="YES" drawsBackground="NO" id="AMs-PO-nid">
|
||||||
<rect key="frame" x="1" y="1" width="238" height="209"/>
|
<rect key="frame" x="1" y="1" width="238" height="209"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<tableView focusRingType="none" appearanceType="vibrantLight" verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnReordering="NO" columnResizing="NO" multipleSelection="NO" emptySelection="NO" autosaveColumns="NO" typeSelect="NO" id="UDd-IJ-fxX">
|
<tableView focusRingType="none" appearanceType="vibrantLight" verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnReordering="NO" columnResizing="NO" multipleSelection="NO" emptySelection="NO" autosaveColumns="NO" typeSelect="NO" id="UDd-IJ-fxX">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="238" height="209"/>
|
<rect key="frame" x="0.0" y="0.0" width="238" height="209"/>
|
||||||
@ -441,11 +473,11 @@
|
|||||||
</subviews>
|
</subviews>
|
||||||
<nil key="backgroundColor"/>
|
<nil key="backgroundColor"/>
|
||||||
</clipView>
|
</clipView>
|
||||||
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="31h-at-Znm">
|
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="31h-at-Znm">
|
||||||
<rect key="frame" x="-100" y="-100" width="210" height="16"/>
|
<rect key="frame" x="-100" y="-100" width="210" height="16"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
</scroller>
|
</scroller>
|
||||||
<scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="JkP-U1-jdy">
|
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="JkP-U1-jdy">
|
||||||
<rect key="frame" x="-100" y="-100" width="15" height="102"/>
|
<rect key="frame" x="-100" y="-100" width="15" height="102"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
</scroller>
|
</scroller>
|
||||||
|
53
Core/apu.c
53
Core/apu.c
@ -48,6 +48,23 @@ bool GB_apu_is_DAC_enabled(GB_gameboy_t *gb, unsigned index)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint8_t agb_bias_for_channel(GB_gameboy_t *gb, unsigned index)
|
||||||
|
{
|
||||||
|
if (!gb->apu.is_active[index]) return 0;
|
||||||
|
|
||||||
|
switch (index) {
|
||||||
|
case GB_SQUARE_1:
|
||||||
|
return gb->apu.square_channels[GB_SQUARE_1].current_volume;
|
||||||
|
case GB_SQUARE_2:
|
||||||
|
return gb->apu.square_channels[GB_SQUARE_2].current_volume;
|
||||||
|
case GB_WAVE:
|
||||||
|
return 0;
|
||||||
|
case GB_NOISE:
|
||||||
|
return gb->apu.noise_channel.current_volume;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void update_sample(GB_gameboy_t *gb, unsigned index, int8_t value, unsigned cycles_offset)
|
static void update_sample(GB_gameboy_t *gb, unsigned index, int8_t value, unsigned cycles_offset)
|
||||||
{
|
{
|
||||||
if (gb->model >= GB_MODEL_AGB) {
|
if (gb->model >= GB_MODEL_AGB) {
|
||||||
@ -66,15 +83,17 @@ static void update_sample(GB_gameboy_t *gb, unsigned index, int8_t value, unsign
|
|||||||
}
|
}
|
||||||
|
|
||||||
GB_sample_t output;
|
GB_sample_t output;
|
||||||
|
uint8_t bias = agb_bias_for_channel(gb, index);
|
||||||
|
|
||||||
if (gb->io_registers[GB_IO_NR51] & (1 << index)) {
|
if (gb->io_registers[GB_IO_NR51] & (1 << index)) {
|
||||||
output.right = (0xf - value * 2) * right_volume;
|
output.right = (0xf - value * 2 + bias) * right_volume;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
output.right = 0xf * right_volume;
|
output.right = 0xf * right_volume;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gb->io_registers[GB_IO_NR51] & (0x10 << index)) {
|
if (gb->io_registers[GB_IO_NR51] & (0x10 << index)) {
|
||||||
output.left = (0xf - value * 2) * left_volume;
|
output.left = (0xf - value * 2 + bias) * left_volume;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
output.left = 0xf * left_volume;
|
output.left = 0xf * left_volume;
|
||||||
@ -681,6 +700,21 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
|
|||||||
case GB_IO_NR14:
|
case GB_IO_NR14:
|
||||||
case GB_IO_NR24: {
|
case GB_IO_NR24: {
|
||||||
unsigned index = reg == GB_IO_NR24? GB_SQUARE_2: GB_SQUARE_1;
|
unsigned index = reg == GB_IO_NR24? GB_SQUARE_2: GB_SQUARE_1;
|
||||||
|
|
||||||
|
/* TODO: When the sample length changes right before being updated, the countdown should change to the
|
||||||
|
old length, but the current sample should not change. Because our write timing isn't accurate to
|
||||||
|
the T-cycle, we hack around it by stepping the sample index backwards. */
|
||||||
|
if ((value & 0x80) == 0 && gb->apu.is_active[index]) {
|
||||||
|
/* On an AGB, as well as on CGB C and earlier (TODO: Tested: 0, B and C), it behaves slightly different on
|
||||||
|
double speed. */
|
||||||
|
if (gb->model == GB_MODEL_CGB_E /* || gb->model == GB_MODEL_CGB_D */ || gb->apu.square_channels[index].sample_countdown & 1) {
|
||||||
|
if (gb->apu.square_channels[index].sample_countdown >> 1 == (gb->apu.square_channels[index].sample_length ^ 0x7FF)) {
|
||||||
|
gb->apu.square_channels[index].current_sample_index--;
|
||||||
|
gb->apu.square_channels[index].current_sample_index &= 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
gb->apu.square_channels[index].sample_length &= 0xFF;
|
gb->apu.square_channels[index].sample_length &= 0xFF;
|
||||||
gb->apu.square_channels[index].sample_length |= (value & 7) << 8;
|
gb->apu.square_channels[index].sample_length |= (value & 7) << 8;
|
||||||
if (index == GB_SQUARE_1) {
|
if (index == GB_SQUARE_1) {
|
||||||
@ -970,9 +1004,23 @@ void GB_set_sample_rate(GB_gameboy_t *gb, unsigned sample_rate)
|
|||||||
if (sample_rate) {
|
if (sample_rate) {
|
||||||
gb->apu_output.highpass_rate = pow(0.999958, GB_get_clock_rate(gb) / (double)sample_rate);
|
gb->apu_output.highpass_rate = pow(0.999958, GB_get_clock_rate(gb) / (double)sample_rate);
|
||||||
}
|
}
|
||||||
|
gb->apu_output.rate_set_in_clocks = false;
|
||||||
GB_apu_update_cycles_per_sample(gb);
|
GB_apu_update_cycles_per_sample(gb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GB_set_sample_rate_by_clocks(GB_gameboy_t *gb, double cycles_per_sample)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (cycles_per_sample == 0) {
|
||||||
|
GB_set_sample_rate(gb, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gb->apu_output.cycles_per_sample = cycles_per_sample;
|
||||||
|
gb->apu_output.sample_rate = GB_get_clock_rate(gb) / cycles_per_sample * 2;
|
||||||
|
gb->apu_output.highpass_rate = pow(0.999958, cycles_per_sample);
|
||||||
|
gb->apu_output.rate_set_in_clocks = true;
|
||||||
|
}
|
||||||
|
|
||||||
void GB_apu_set_sample_callback(GB_gameboy_t *gb, GB_sample_callback_t callback)
|
void GB_apu_set_sample_callback(GB_gameboy_t *gb, GB_sample_callback_t callback)
|
||||||
{
|
{
|
||||||
gb->apu_output.sample_callback = callback;
|
gb->apu_output.sample_callback = callback;
|
||||||
@ -985,6 +1033,7 @@ void GB_set_highpass_filter_mode(GB_gameboy_t *gb, GB_highpass_mode_t mode)
|
|||||||
|
|
||||||
void GB_apu_update_cycles_per_sample(GB_gameboy_t *gb)
|
void GB_apu_update_cycles_per_sample(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
|
if (gb->apu_output.rate_set_in_clocks) return;
|
||||||
if (gb->apu_output.sample_rate) {
|
if (gb->apu_output.sample_rate) {
|
||||||
gb->apu_output.cycles_per_sample = 2 * GB_get_clock_rate(gb) / (double)gb->apu_output.sample_rate; /* 2 * because we use 8MHz units */
|
gb->apu_output.cycles_per_sample = 2 * GB_get_clock_rate(gb) / (double)gb->apu_output.sample_rate; /* 2 * because we use 8MHz units */
|
||||||
}
|
}
|
||||||
|
@ -143,9 +143,12 @@ typedef struct {
|
|||||||
GB_double_sample_t highpass_diff;
|
GB_double_sample_t highpass_diff;
|
||||||
|
|
||||||
GB_sample_callback_t sample_callback;
|
GB_sample_callback_t sample_callback;
|
||||||
|
|
||||||
|
bool rate_set_in_clocks;
|
||||||
} GB_apu_output_t;
|
} GB_apu_output_t;
|
||||||
|
|
||||||
void GB_set_sample_rate(GB_gameboy_t *gb, unsigned sample_rate);
|
void GB_set_sample_rate(GB_gameboy_t *gb, unsigned sample_rate);
|
||||||
|
void GB_set_sample_rate_by_clocks(GB_gameboy_t *gb, double cycles_per_sample); /* Cycles are in 8MHz units */
|
||||||
void GB_set_highpass_filter_mode(GB_gameboy_t *gb, GB_highpass_mode_t mode);
|
void GB_set_highpass_filter_mode(GB_gameboy_t *gb, GB_highpass_mode_t mode);
|
||||||
void GB_apu_set_sample_callback(GB_gameboy_t *gb, GB_sample_callback_t callback);
|
void GB_apu_set_sample_callback(GB_gameboy_t *gb, GB_sample_callback_t callback);
|
||||||
#ifdef GB_INTERNAL
|
#ifdef GB_INTERNAL
|
||||||
|
@ -819,7 +819,7 @@ 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->registers[GB_REGISTER_AF], /* AF can't really be an address */
|
||||||
(gb->f & GB_CARRY_FLAG)? 'C' : '-',
|
(gb->f & GB_CARRY_FLAG)? 'C' : '-',
|
||||||
(gb->f & GB_HALF_CARRY_FLAG)? 'H' : '-',
|
(gb->f & GB_HALF_CARRY_FLAG)? 'H' : '-',
|
||||||
(gb->f & GB_SUBSTRACT_FLAG)? 'N' : '-',
|
(gb->f & GB_SUBTRACT_FLAG)? 'N' : '-',
|
||||||
(gb->f & GB_ZERO_FLAG)? 'Z' : '-');
|
(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, "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, "DE = %s\n", value_to_string(gb, gb->registers[GB_REGISTER_DE], false));
|
||||||
|
@ -142,9 +142,17 @@ static void display_vblank(GB_gameboy_t *gb)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
uint32_t color = (gb->io_registers[GB_IO_LCDC] & 0x80) && gb->stopped && GB_is_cgb(gb) ?
|
uint32_t color = 0;
|
||||||
gb->rgb_encode_callback(gb, 0, 0, 0) :
|
if (GB_is_cgb(gb)) {
|
||||||
gb->rgb_encode_callback(gb, 0xFF, 0xFF, 0xFF);
|
color = (gb->io_registers[GB_IO_LCDC] & 0x80) && gb->stopped ?
|
||||||
|
gb->rgb_encode_callback(gb, 0, 0, 0) :
|
||||||
|
gb->rgb_encode_callback(gb, 0xFF, 0xFF, 0xFF);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
color = (gb->io_registers[GB_IO_LCDC] & 0x80) && gb->stopped ?
|
||||||
|
gb->background_palettes_rgb[3] :
|
||||||
|
gb->background_palettes_rgb[4];
|
||||||
|
}
|
||||||
for (unsigned i = 0; i < WIDTH * LINES; i++) {
|
for (unsigned i = 0; i < WIDTH * LINES; i++) {
|
||||||
gb->screen[i] = color;
|
gb->screen[i] = color;
|
||||||
}
|
}
|
||||||
|
119
Core/gb.c
119
Core/gb.c
@ -18,6 +18,13 @@
|
|||||||
#define GB_rewind_push(...)
|
#define GB_rewind_push(...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
static inline uint32_t state_magic(void)
|
||||||
|
{
|
||||||
|
if (sizeof(bool) == 1) return 'SAME';
|
||||||
|
return 'S4ME';
|
||||||
|
}
|
||||||
|
|
||||||
void GB_attributed_logv(GB_gameboy_t *gb, GB_log_attributes attributes, const char *fmt, va_list args)
|
void GB_attributed_logv(GB_gameboy_t *gb, GB_log_attributes attributes, const char *fmt, va_list args)
|
||||||
{
|
{
|
||||||
char *string = NULL;
|
char *string = NULL;
|
||||||
@ -566,21 +573,41 @@ void GB_set_async_input_callback(GB_gameboy_t *gb, GB_input_callback_t callback)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const GB_palette_t GB_PALETTE_GREY = {{{0x00, 0x00, 0x00}, {0x55, 0x55, 0x55}, {0xaa, 0xaa, 0xaa}, {0xff ,0xff, 0xff}, {0xff ,0xff, 0xff}}};
|
||||||
|
const GB_palette_t GB_PALETTE_DMG = {{{0x08, 0x18, 0x10}, {0x39, 0x61, 0x39}, {0x84, 0xa5, 0x63}, {0xc6, 0xde, 0x8c}, {0xd2 ,0xe6 ,0xa6}}};
|
||||||
|
const GB_palette_t GB_PALETTE_MGB = {{{0x07, 0x10, 0x0e}, {0x3a, 0x4c, 0x3a}, {0x81, 0x8d, 0x66}, {0xc2, 0xce, 0x93}, {0xcf, 0xda, 0xac}}};
|
||||||
|
const GB_palette_t GB_PALETTE_GBL = {{{0x0a, 0x1c, 0x15}, {0x35, 0x78, 0x62}, {0x56, 0xb4, 0x95}, {0x7f, 0xe2, 0xc3}, {0x91, 0xea, 0xd0}}};
|
||||||
|
|
||||||
|
static void update_dmg_palette(GB_gameboy_t *gb)
|
||||||
|
{
|
||||||
|
const GB_palette_t *palette = gb->dmg_palette ?: &GB_PALETTE_GREY;
|
||||||
|
if (gb->rgb_encode_callback && !GB_is_cgb(gb)) {
|
||||||
|
gb->sprite_palettes_rgb[4] = gb->sprite_palettes_rgb[0] = gb->background_palettes_rgb[0] =
|
||||||
|
gb->rgb_encode_callback(gb, palette->colors[3].r, palette->colors[3].g, palette->colors[3].b);
|
||||||
|
gb->sprite_palettes_rgb[5] = gb->sprite_palettes_rgb[1] = gb->background_palettes_rgb[1] =
|
||||||
|
gb->rgb_encode_callback(gb, palette->colors[2].r, palette->colors[2].g, palette->colors[2].b);
|
||||||
|
gb->sprite_palettes_rgb[6] = gb->sprite_palettes_rgb[2] = gb->background_palettes_rgb[2] =
|
||||||
|
gb->rgb_encode_callback(gb, palette->colors[1].r, palette->colors[1].g, palette->colors[1].b);
|
||||||
|
gb->sprite_palettes_rgb[7] = gb->sprite_palettes_rgb[3] = gb->background_palettes_rgb[3] =
|
||||||
|
gb->rgb_encode_callback(gb, palette->colors[0].r, palette->colors[0].g, palette->colors[0].b);
|
||||||
|
|
||||||
|
// LCD off color
|
||||||
|
gb->background_palettes_rgb[4] =
|
||||||
|
gb->rgb_encode_callback(gb, palette->colors[4].r, palette->colors[4].g, palette->colors[4].b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GB_set_palette(GB_gameboy_t *gb, const GB_palette_t *palette)
|
||||||
|
{
|
||||||
|
gb->dmg_palette = palette;
|
||||||
|
update_dmg_palette(gb);
|
||||||
|
}
|
||||||
|
|
||||||
void GB_set_rgb_encode_callback(GB_gameboy_t *gb, GB_rgb_encode_callback_t callback)
|
void GB_set_rgb_encode_callback(GB_gameboy_t *gb, GB_rgb_encode_callback_t callback)
|
||||||
{
|
{
|
||||||
if (!gb->rgb_encode_callback && !GB_is_cgb(gb)) {
|
|
||||||
gb->sprite_palettes_rgb[4] = gb->sprite_palettes_rgb[0] = gb->background_palettes_rgb[0] =
|
|
||||||
callback(gb, 0xFF, 0xFF, 0xFF);
|
|
||||||
gb->sprite_palettes_rgb[5] = gb->sprite_palettes_rgb[1] = gb->background_palettes_rgb[1] =
|
|
||||||
callback(gb, 0xAA, 0xAA, 0xAA);
|
|
||||||
gb->sprite_palettes_rgb[6] = gb->sprite_palettes_rgb[2] = gb->background_palettes_rgb[2] =
|
|
||||||
callback(gb, 0x55, 0x55, 0x55);
|
|
||||||
gb->sprite_palettes_rgb[7] = gb->sprite_palettes_rgb[3] = gb->background_palettes_rgb[3] =
|
|
||||||
callback(gb, 0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
gb->rgb_encode_callback = callback;
|
gb->rgb_encode_callback = callback;
|
||||||
|
update_dmg_palette(gb);
|
||||||
|
|
||||||
for (unsigned i = 0; i < 32; i++) {
|
for (unsigned i = 0; i < 32; i++) {
|
||||||
GB_palette_changed(gb, true, i * 2);
|
GB_palette_changed(gb, true, i * 2);
|
||||||
@ -660,7 +687,7 @@ void GB_disconnect_serial(GB_gameboy_t *gb)
|
|||||||
|
|
||||||
bool GB_is_inited(GB_gameboy_t *gb)
|
bool GB_is_inited(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
return gb->magic == 'SAME';
|
return gb->magic == state_magic();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GB_is_cgb(GB_gameboy_t *gb)
|
bool GB_is_cgb(GB_gameboy_t *gb)
|
||||||
@ -712,7 +739,8 @@ static void reset_ram(GB_gameboy_t *gb)
|
|||||||
case GB_MODEL_DMG_B:
|
case GB_MODEL_DMG_B:
|
||||||
case GB_MODEL_SGB_NTSC: /* Unverified*/
|
case GB_MODEL_SGB_NTSC: /* Unverified*/
|
||||||
case GB_MODEL_SGB_PAL: /* Unverified */
|
case GB_MODEL_SGB_PAL: /* Unverified */
|
||||||
case GB_MODEL_SGB_NO_SFC:
|
case GB_MODEL_SGB_NTSC_NO_SFC: /* Unverified */
|
||||||
|
case GB_MODEL_SGB_PAL_NO_SFC: /* Unverified */
|
||||||
for (unsigned i = 0; i < gb->ram_size; i++) {
|
for (unsigned i = 0; i < gb->ram_size; i++) {
|
||||||
gb->ram[i] = GB_random();
|
gb->ram[i] = GB_random();
|
||||||
if (i & 0x100) {
|
if (i & 0x100) {
|
||||||
@ -758,7 +786,8 @@ static void reset_ram(GB_gameboy_t *gb)
|
|||||||
case GB_MODEL_DMG_B:
|
case GB_MODEL_DMG_B:
|
||||||
case GB_MODEL_SGB_NTSC: /* Unverified*/
|
case GB_MODEL_SGB_NTSC: /* Unverified*/
|
||||||
case GB_MODEL_SGB_PAL: /* Unverified */
|
case GB_MODEL_SGB_PAL: /* Unverified */
|
||||||
case GB_MODEL_SGB_NO_SFC:
|
case GB_MODEL_SGB_NTSC_NO_SFC: /* Unverified */
|
||||||
|
case GB_MODEL_SGB_PAL_NO_SFC: /* Unverified */
|
||||||
case GB_MODEL_SGB2:
|
case GB_MODEL_SGB2:
|
||||||
case GB_MODEL_SGB2_NO_SFC:
|
case GB_MODEL_SGB2_NO_SFC:
|
||||||
for (unsigned i = 0; i < sizeof(gb->hram); i++) {
|
for (unsigned i = 0; i < sizeof(gb->hram); i++) {
|
||||||
@ -783,7 +812,8 @@ static void reset_ram(GB_gameboy_t *gb)
|
|||||||
case GB_MODEL_DMG_B:
|
case GB_MODEL_DMG_B:
|
||||||
case GB_MODEL_SGB_NTSC: /* Unverified */
|
case GB_MODEL_SGB_NTSC: /* Unverified */
|
||||||
case GB_MODEL_SGB_PAL: /* Unverified */
|
case GB_MODEL_SGB_PAL: /* Unverified */
|
||||||
case GB_MODEL_SGB_NO_SFC: /* Unverified */
|
case GB_MODEL_SGB_NTSC_NO_SFC: /* Unverified */
|
||||||
|
case GB_MODEL_SGB_PAL_NO_SFC: /* Unverified */
|
||||||
case GB_MODEL_SGB2:
|
case GB_MODEL_SGB2:
|
||||||
case GB_MODEL_SGB2_NO_SFC:
|
case GB_MODEL_SGB2_NO_SFC:
|
||||||
for (unsigned i = 0; i < 8; i++) {
|
for (unsigned i = 0; i < 8; i++) {
|
||||||
@ -811,7 +841,8 @@ static void reset_ram(GB_gameboy_t *gb)
|
|||||||
case GB_MODEL_DMG_B:
|
case GB_MODEL_DMG_B:
|
||||||
case GB_MODEL_SGB_NTSC: /* Unverified*/
|
case GB_MODEL_SGB_NTSC: /* Unverified*/
|
||||||
case GB_MODEL_SGB_PAL: /* Unverified */
|
case GB_MODEL_SGB_PAL: /* Unverified */
|
||||||
case GB_MODEL_SGB_NO_SFC: /* Unverified */
|
case GB_MODEL_SGB_NTSC_NO_SFC: /* Unverified */
|
||||||
|
case GB_MODEL_SGB_PAL_NO_SFC: /* Unverified */
|
||||||
case GB_MODEL_SGB2:
|
case GB_MODEL_SGB2:
|
||||||
case GB_MODEL_SGB2_NO_SFC: {
|
case GB_MODEL_SGB2_NO_SFC: {
|
||||||
uint8_t temp;
|
uint8_t temp;
|
||||||
@ -847,6 +878,36 @@ static void reset_ram(GB_gameboy_t *gb)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void request_boot_rom(GB_gameboy_t *gb)
|
||||||
|
{
|
||||||
|
if (gb->boot_rom_load_callback) {
|
||||||
|
GB_boot_rom_t type = 0;
|
||||||
|
switch (gb->model) {
|
||||||
|
case GB_MODEL_DMG_B:
|
||||||
|
type = GB_BOOT_ROM_DMG;
|
||||||
|
break;
|
||||||
|
case GB_MODEL_SGB_NTSC:
|
||||||
|
case GB_MODEL_SGB_PAL:
|
||||||
|
case GB_MODEL_SGB_NTSC_NO_SFC:
|
||||||
|
case GB_MODEL_SGB_PAL_NO_SFC:
|
||||||
|
type = GB_BOOT_ROM_SGB;
|
||||||
|
break;
|
||||||
|
case GB_MODEL_SGB2:
|
||||||
|
case GB_MODEL_SGB2_NO_SFC:
|
||||||
|
type = GB_BOOT_ROM_SGB2;
|
||||||
|
break;
|
||||||
|
case GB_MODEL_CGB_C:
|
||||||
|
case GB_MODEL_CGB_E:
|
||||||
|
type = GB_BOOT_ROM_CGB;
|
||||||
|
break;
|
||||||
|
case GB_MODEL_AGB:
|
||||||
|
type = GB_BOOT_ROM_AGB;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
gb->boot_rom_load_callback(gb, type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GB_reset(GB_gameboy_t *gb)
|
void GB_reset(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
uint32_t mbc_ram_size = gb->mbc_ram_size;
|
uint32_t mbc_ram_size = gb->mbc_ram_size;
|
||||||
@ -871,16 +932,7 @@ void GB_reset(GB_gameboy_t *gb)
|
|||||||
gb->vram_size = 0x2000;
|
gb->vram_size = 0x2000;
|
||||||
memset(gb->vram, 0, gb->vram_size);
|
memset(gb->vram, 0, gb->vram_size);
|
||||||
|
|
||||||
if (gb->rgb_encode_callback) {
|
update_dmg_palette(gb);
|
||||||
gb->sprite_palettes_rgb[4] = gb->sprite_palettes_rgb[0] = gb->background_palettes_rgb[0] =
|
|
||||||
gb->rgb_encode_callback(gb, 0xFF, 0xFF, 0xFF);
|
|
||||||
gb->sprite_palettes_rgb[5] = gb->sprite_palettes_rgb[1] = gb->background_palettes_rgb[1] =
|
|
||||||
gb->rgb_encode_callback(gb, 0xAA, 0xAA, 0xAA);
|
|
||||||
gb->sprite_palettes_rgb[6] = gb->sprite_palettes_rgb[2] = gb->background_palettes_rgb[2] =
|
|
||||||
gb->rgb_encode_callback(gb, 0x55, 0x55, 0x55);
|
|
||||||
gb->sprite_palettes_rgb[7] = gb->sprite_palettes_rgb[3] = gb->background_palettes_rgb[3] =
|
|
||||||
gb->rgb_encode_callback(gb, 0, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
reset_ram(gb);
|
reset_ram(gb);
|
||||||
|
|
||||||
@ -925,7 +977,8 @@ void GB_reset(GB_gameboy_t *gb)
|
|||||||
gb->nontrivial_jump_state = NULL;
|
gb->nontrivial_jump_state = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
gb->magic = (uintptr_t)'SAME';
|
gb->magic = state_magic();
|
||||||
|
request_boot_rom(gb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GB_switch_model_and_reset(GB_gameboy_t *gb, GB_model_t model)
|
void GB_switch_model_and_reset(GB_gameboy_t *gb, GB_model_t model)
|
||||||
@ -1017,12 +1070,12 @@ void GB_set_clock_multiplier(GB_gameboy_t *gb, double multiplier)
|
|||||||
|
|
||||||
uint32_t GB_get_clock_rate(GB_gameboy_t *gb)
|
uint32_t GB_get_clock_rate(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
if (gb->model == GB_MODEL_SGB_NTSC) {
|
if (gb->model & GB_MODEL_PAL_BIT) {
|
||||||
return SGB_NTSC_FREQUENCY * gb->clock_multiplier;
|
|
||||||
}
|
|
||||||
if (gb->model == GB_MODEL_SGB_PAL) {
|
|
||||||
return SGB_PAL_FREQUENCY * gb->clock_multiplier;
|
return SGB_PAL_FREQUENCY * gb->clock_multiplier;
|
||||||
}
|
}
|
||||||
|
if ((gb->model & ~GB_MODEL_NO_SFC_BIT) == GB_MODEL_SGB) {
|
||||||
|
return SGB_NTSC_FREQUENCY * gb->clock_multiplier;
|
||||||
|
}
|
||||||
return CPU_FREQUENCY * gb->clock_multiplier;
|
return CPU_FREQUENCY * gb->clock_multiplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1071,3 +1124,9 @@ void GB_set_icd_vreset_callback(GB_gameboy_t *gb, GB_icd_vreset_callback_t callb
|
|||||||
{
|
{
|
||||||
gb->icd_vreset_callback = callback;
|
gb->icd_vreset_callback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GB_set_boot_rom_load_callback(GB_gameboy_t *gb, GB_boot_rom_load_callback_t callback)
|
||||||
|
{
|
||||||
|
gb->boot_rom_load_callback = callback;
|
||||||
|
request_boot_rom(gb);
|
||||||
|
}
|
||||||
|
40
Core/gb.h
40
Core/gb.h
@ -50,6 +50,17 @@
|
|||||||
#error Unable to detect endianess
|
#error Unable to detect endianess
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
struct {
|
||||||
|
uint8_t r,g,b;
|
||||||
|
} colors[5];
|
||||||
|
} GB_palette_t;
|
||||||
|
|
||||||
|
extern const GB_palette_t GB_PALETTE_GREY;
|
||||||
|
extern const GB_palette_t GB_PALETTE_DMG;
|
||||||
|
extern const GB_palette_t GB_PALETTE_MGB;
|
||||||
|
extern const GB_palette_t GB_PALETTE_GBL;
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
struct {
|
struct {
|
||||||
uint8_t seconds;
|
uint8_t seconds;
|
||||||
@ -61,7 +72,6 @@ typedef union {
|
|||||||
uint8_t data[5];
|
uint8_t data[5];
|
||||||
} GB_rtc_time_t;
|
} GB_rtc_time_t;
|
||||||
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
// GB_MODEL_DMG_0 = 0x000,
|
// GB_MODEL_DMG_0 = 0x000,
|
||||||
// GB_MODEL_DMG_A = 0x001,
|
// GB_MODEL_DMG_A = 0x001,
|
||||||
@ -70,7 +80,9 @@ typedef enum {
|
|||||||
GB_MODEL_SGB = 0x004,
|
GB_MODEL_SGB = 0x004,
|
||||||
GB_MODEL_SGB_NTSC = GB_MODEL_SGB,
|
GB_MODEL_SGB_NTSC = GB_MODEL_SGB,
|
||||||
GB_MODEL_SGB_PAL = GB_MODEL_SGB | GB_MODEL_PAL_BIT,
|
GB_MODEL_SGB_PAL = GB_MODEL_SGB | GB_MODEL_PAL_BIT,
|
||||||
GB_MODEL_SGB_NO_SFC = GB_MODEL_SGB | GB_MODEL_NO_SFC_BIT,
|
GB_MODEL_SGB_NTSC_NO_SFC = GB_MODEL_SGB | GB_MODEL_NO_SFC_BIT,
|
||||||
|
GB_MODEL_SGB_NO_SFC = GB_MODEL_SGB_NTSC_NO_SFC,
|
||||||
|
GB_MODEL_SGB_PAL_NO_SFC = GB_MODEL_SGB | GB_MODEL_NO_SFC_BIT | GB_MODEL_PAL_BIT,
|
||||||
// GB_MODEL_MGB = 0x100,
|
// GB_MODEL_MGB = 0x100,
|
||||||
GB_MODEL_SGB2 = 0x101,
|
GB_MODEL_SGB2 = 0x101,
|
||||||
GB_MODEL_SGB2_NO_SFC = GB_MODEL_SGB2 | GB_MODEL_NO_SFC_BIT,
|
GB_MODEL_SGB2_NO_SFC = GB_MODEL_SGB2 | GB_MODEL_NO_SFC_BIT,
|
||||||
@ -96,7 +108,7 @@ enum {
|
|||||||
enum {
|
enum {
|
||||||
GB_CARRY_FLAG = 16,
|
GB_CARRY_FLAG = 16,
|
||||||
GB_HALF_CARRY_FLAG = 32,
|
GB_HALF_CARRY_FLAG = 32,
|
||||||
GB_SUBSTRACT_FLAG = 64,
|
GB_SUBTRACT_FLAG = 64,
|
||||||
GB_ZERO_FLAG = 128,
|
GB_ZERO_FLAG = 128,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -217,6 +229,17 @@ typedef enum {
|
|||||||
GB_LOG_UNDERLINE_MASK = GB_LOG_DASHED_UNDERLINE | GB_LOG_UNDERLINE
|
GB_LOG_UNDERLINE_MASK = GB_LOG_DASHED_UNDERLINE | GB_LOG_UNDERLINE
|
||||||
} GB_log_attributes;
|
} GB_log_attributes;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GB_BOOT_ROM_DMG0,
|
||||||
|
GB_BOOT_ROM_DMG,
|
||||||
|
GB_BOOT_ROM_MGB,
|
||||||
|
GB_BOOT_ROM_SGB,
|
||||||
|
GB_BOOT_ROM_SGB2,
|
||||||
|
GB_BOOT_ROM_CGB0,
|
||||||
|
GB_BOOT_ROM_CGB,
|
||||||
|
GB_BOOT_ROM_AGB,
|
||||||
|
} GB_boot_rom_t;
|
||||||
|
|
||||||
#ifdef GB_INTERNAL
|
#ifdef GB_INTERNAL
|
||||||
#define LCDC_PERIOD 70224
|
#define LCDC_PERIOD 70224
|
||||||
#define CPU_FREQUENCY 0x400000
|
#define CPU_FREQUENCY 0x400000
|
||||||
@ -247,6 +270,7 @@ typedef void (*GB_joyp_write_callback_t)(GB_gameboy_t *gb, uint8_t value);
|
|||||||
typedef void (*GB_icd_pixel_callback_t)(GB_gameboy_t *gb, uint8_t row);
|
typedef void (*GB_icd_pixel_callback_t)(GB_gameboy_t *gb, uint8_t row);
|
||||||
typedef void (*GB_icd_hreset_callback_t)(GB_gameboy_t *gb);
|
typedef void (*GB_icd_hreset_callback_t)(GB_gameboy_t *gb);
|
||||||
typedef void (*GB_icd_vreset_callback_t)(GB_gameboy_t *gb);
|
typedef void (*GB_icd_vreset_callback_t)(GB_gameboy_t *gb);
|
||||||
|
typedef void (*GB_boot_rom_load_callback_t)(GB_gameboy_t *gb, GB_boot_rom_t type);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool state;
|
bool state;
|
||||||
@ -276,10 +300,6 @@ typedef struct {
|
|||||||
This struct is not packed, but dumped sections exclusively use types that have the same alignment in both 32 and 64
|
This struct is not packed, but dumped sections exclusively use types that have the same alignment in both 32 and 64
|
||||||
bit platforms. */
|
bit platforms. */
|
||||||
|
|
||||||
/* We make sure bool is 1 for cross-platform save state compatibility. */
|
|
||||||
/* Todo: We might want to typedef our own bool if this prevents SameBoy from working on specific platforms. */
|
|
||||||
_Static_assert(sizeof(bool) == 1, "sizeof(bool) != 1");
|
|
||||||
|
|
||||||
#ifdef GB_INTERNAL
|
#ifdef GB_INTERNAL
|
||||||
struct GB_gameboy_s {
|
struct GB_gameboy_s {
|
||||||
#else
|
#else
|
||||||
@ -515,6 +535,7 @@ struct GB_gameboy_internal_s {
|
|||||||
uint32_t *screen;
|
uint32_t *screen;
|
||||||
uint32_t background_palettes_rgb[0x20];
|
uint32_t background_palettes_rgb[0x20];
|
||||||
uint32_t sprite_palettes_rgb[0x20];
|
uint32_t sprite_palettes_rgb[0x20];
|
||||||
|
const GB_palette_t *dmg_palette;
|
||||||
GB_color_correction_mode_t color_correction_mode;
|
GB_color_correction_mode_t color_correction_mode;
|
||||||
bool keys[4][GB_KEY_MAX];
|
bool keys[4][GB_KEY_MAX];
|
||||||
|
|
||||||
@ -544,6 +565,7 @@ struct GB_gameboy_internal_s {
|
|||||||
GB_icd_vreset_callback_t icd_hreset_callback;
|
GB_icd_vreset_callback_t icd_hreset_callback;
|
||||||
GB_icd_vreset_callback_t icd_vreset_callback;
|
GB_icd_vreset_callback_t icd_vreset_callback;
|
||||||
GB_read_memory_callback_t read_memory_callback;
|
GB_read_memory_callback_t read_memory_callback;
|
||||||
|
GB_boot_rom_load_callback_t boot_rom_load_callback;
|
||||||
|
|
||||||
/* IR */
|
/* IR */
|
||||||
long cycles_since_ir_change; // In 8MHz units
|
long cycles_since_ir_change; // In 8MHz units
|
||||||
@ -697,6 +719,10 @@ void GB_set_rgb_encode_callback(GB_gameboy_t *gb, GB_rgb_encode_callback_t callb
|
|||||||
void GB_set_infrared_callback(GB_gameboy_t *gb, GB_infrared_callback_t callback);
|
void GB_set_infrared_callback(GB_gameboy_t *gb, GB_infrared_callback_t callback);
|
||||||
void GB_set_rumble_callback(GB_gameboy_t *gb, GB_rumble_callback_t callback);
|
void GB_set_rumble_callback(GB_gameboy_t *gb, GB_rumble_callback_t callback);
|
||||||
void GB_set_update_input_hint_callback(GB_gameboy_t *gb, GB_update_input_hint_callback_t callback);
|
void GB_set_update_input_hint_callback(GB_gameboy_t *gb, GB_update_input_hint_callback_t callback);
|
||||||
|
/* Called when a new boot ROM is needed. The callback should call GB_load_boot_rom or GB_load_boot_rom_from_buffer */
|
||||||
|
void GB_set_boot_rom_load_callback(GB_gameboy_t *gb, GB_boot_rom_load_callback_t callback);
|
||||||
|
|
||||||
|
void GB_set_palette(GB_gameboy_t *gb, const GB_palette_t *palette);
|
||||||
|
|
||||||
/* These APIs are used when using internal clock */
|
/* These APIs are used when using internal clock */
|
||||||
void GB_set_serial_transfer_bit_start_callback(GB_gameboy_t *gb, GB_serial_transfer_bit_start_callback_t callback);
|
void GB_set_serial_transfer_bit_start_callback(GB_gameboy_t *gb, GB_serial_transfer_bit_start_callback_t callback);
|
||||||
|
@ -273,7 +273,8 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr)
|
|||||||
case GB_MODEL_DMG_B:
|
case GB_MODEL_DMG_B:
|
||||||
case GB_MODEL_SGB_NTSC:
|
case GB_MODEL_SGB_NTSC:
|
||||||
case GB_MODEL_SGB_PAL:
|
case GB_MODEL_SGB_PAL:
|
||||||
case GB_MODEL_SGB_NO_SFC:
|
case GB_MODEL_SGB_NTSC_NO_SFC:
|
||||||
|
case GB_MODEL_SGB_PAL_NO_SFC:
|
||||||
case GB_MODEL_SGB2:
|
case GB_MODEL_SGB2:
|
||||||
case GB_MODEL_SGB2_NO_SFC:
|
case GB_MODEL_SGB2_NO_SFC:
|
||||||
;
|
;
|
||||||
@ -421,6 +422,11 @@ static GB_read_function_t * const read_map[] =
|
|||||||
read_ram, read_high_memory, /* EXXX FXXX */
|
read_ram, read_high_memory, /* EXXX FXXX */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void GB_set_read_memory_callback(GB_gameboy_t *gb, GB_read_memory_callback_t callback)
|
||||||
|
{
|
||||||
|
gb->read_memory_callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t GB_read_memory(GB_gameboy_t *gb, uint16_t addr)
|
uint8_t GB_read_memory(GB_gameboy_t *gb, uint16_t addr)
|
||||||
{
|
{
|
||||||
if (gb->n_watchpoints) {
|
if (gb->n_watchpoints) {
|
||||||
@ -591,7 +597,8 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
|||||||
case GB_MODEL_DMG_B:
|
case GB_MODEL_DMG_B:
|
||||||
case GB_MODEL_SGB_NTSC:
|
case GB_MODEL_SGB_NTSC:
|
||||||
case GB_MODEL_SGB_PAL:
|
case GB_MODEL_SGB_PAL:
|
||||||
case GB_MODEL_SGB_NO_SFC:
|
case GB_MODEL_SGB_NTSC_NO_SFC:
|
||||||
|
case GB_MODEL_SGB_PAL_NO_SFC:
|
||||||
case GB_MODEL_SGB2:
|
case GB_MODEL_SGB2:
|
||||||
case GB_MODEL_SGB2_NO_SFC:
|
case GB_MODEL_SGB2_NO_SFC:
|
||||||
case GB_MODEL_CGB_E:
|
case GB_MODEL_CGB_E:
|
||||||
@ -746,7 +753,11 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
case GB_IO_JOYP:
|
case GB_IO_JOYP:
|
||||||
if ((gb->io_registers[GB_IO_JOYP] & 0x30) != (value & 0x30)) {
|
if (gb->joyp_write_callback) {
|
||||||
|
gb->joyp_write_callback(gb, value);
|
||||||
|
GB_update_joyp(gb);
|
||||||
|
}
|
||||||
|
else if ((gb->io_registers[GB_IO_JOYP] & 0x30) != (value & 0x30)) {
|
||||||
GB_sgb_write(gb, value);
|
GB_sgb_write(gb, value);
|
||||||
gb->io_registers[GB_IO_JOYP] = value & 0xF0;
|
gb->io_registers[GB_IO_JOYP] = value & 0xF0;
|
||||||
GB_update_joyp(gb);
|
GB_update_joyp(gb);
|
||||||
|
@ -385,11 +385,7 @@ static void command_ready(GB_gameboy_t *gb)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GB_sgb_write(GB_gameboy_t *gb, uint8_t value)
|
void GB_sgb_write(GB_gameboy_t *gb, uint8_t value)
|
||||||
{
|
{
|
||||||
if (gb->joyp_write_callback) {
|
|
||||||
gb->joyp_write_callback(gb, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!GB_is_sgb(gb)) return;
|
if (!GB_is_sgb(gb)) return;
|
||||||
if (!GB_is_hle_sgb(gb)) {
|
if (!GB_is_hle_sgb(gb)) {
|
||||||
/* Notify via callback */
|
/* Notify via callback */
|
||||||
|
@ -318,7 +318,7 @@ static void inc_hr(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
uint8_t register_id;
|
uint8_t register_id;
|
||||||
register_id = ((opcode >> 4) + 1) & 0x03;
|
register_id = ((opcode >> 4) + 1) & 0x03;
|
||||||
gb->registers[register_id] += 0x100;
|
gb->registers[register_id] += 0x100;
|
||||||
gb->registers[GB_REGISTER_AF] &= ~(GB_SUBSTRACT_FLAG | GB_ZERO_FLAG | GB_HALF_CARRY_FLAG);
|
gb->registers[GB_REGISTER_AF] &= ~(GB_SUBTRACT_FLAG | GB_ZERO_FLAG | GB_HALF_CARRY_FLAG);
|
||||||
|
|
||||||
if ((gb->registers[register_id] & 0x0F00) == 0) {
|
if ((gb->registers[register_id] & 0x0F00) == 0) {
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG;
|
||||||
@ -334,7 +334,7 @@ static void dec_hr(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
register_id = ((opcode >> 4) + 1) & 0x03;
|
register_id = ((opcode >> 4) + 1) & 0x03;
|
||||||
gb->registers[register_id] -= 0x100;
|
gb->registers[register_id] -= 0x100;
|
||||||
gb->registers[GB_REGISTER_AF] &= ~(GB_ZERO_FLAG | GB_HALF_CARRY_FLAG);
|
gb->registers[GB_REGISTER_AF] &= ~(GB_ZERO_FLAG | GB_HALF_CARRY_FLAG);
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_SUBSTRACT_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_SUBTRACT_FLAG;
|
||||||
|
|
||||||
if ((gb->registers[register_id] & 0x0F00) == 0xF00) {
|
if ((gb->registers[register_id] & 0x0F00) == 0xF00) {
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG;
|
||||||
@ -396,7 +396,7 @@ static void add_hl_rr(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
register_id = (opcode >> 4) + 1;
|
register_id = (opcode >> 4) + 1;
|
||||||
rr = gb->registers[register_id];
|
rr = gb->registers[register_id];
|
||||||
gb->registers[GB_REGISTER_HL] = hl + rr;
|
gb->registers[GB_REGISTER_HL] = hl + rr;
|
||||||
gb->registers[GB_REGISTER_AF] &= ~(GB_SUBSTRACT_FLAG | GB_CARRY_FLAG | GB_HALF_CARRY_FLAG);
|
gb->registers[GB_REGISTER_AF] &= ~(GB_SUBTRACT_FLAG | GB_CARRY_FLAG | GB_HALF_CARRY_FLAG);
|
||||||
|
|
||||||
/* The meaning of the Half Carry flag is really hard to track -_- */
|
/* The meaning of the Half Carry flag is really hard to track -_- */
|
||||||
if (((hl & 0xFFF) + (rr & 0xFFF)) & 0x1000) {
|
if (((hl & 0xFFF) + (rr & 0xFFF)) & 0x1000) {
|
||||||
@ -432,7 +432,7 @@ static void inc_lr(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
value = (gb->registers[register_id] & 0xFF) + 1;
|
value = (gb->registers[register_id] & 0xFF) + 1;
|
||||||
gb->registers[register_id] = (gb->registers[register_id] & 0xFF00) | value;
|
gb->registers[register_id] = (gb->registers[register_id] & 0xFF00) | value;
|
||||||
|
|
||||||
gb->registers[GB_REGISTER_AF] &= ~(GB_SUBSTRACT_FLAG | GB_ZERO_FLAG | GB_HALF_CARRY_FLAG);
|
gb->registers[GB_REGISTER_AF] &= ~(GB_SUBTRACT_FLAG | GB_ZERO_FLAG | GB_HALF_CARRY_FLAG);
|
||||||
|
|
||||||
if ((gb->registers[register_id] & 0x0F) == 0) {
|
if ((gb->registers[register_id] & 0x0F) == 0) {
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG;
|
||||||
@ -452,7 +452,7 @@ static void dec_lr(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
gb->registers[register_id] = (gb->registers[register_id] & 0xFF00) | value;
|
gb->registers[register_id] = (gb->registers[register_id] & 0xFF00) | value;
|
||||||
|
|
||||||
gb->registers[GB_REGISTER_AF] &= ~(GB_ZERO_FLAG | GB_HALF_CARRY_FLAG);
|
gb->registers[GB_REGISTER_AF] &= ~(GB_ZERO_FLAG | GB_HALF_CARRY_FLAG);
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_SUBSTRACT_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_SUBTRACT_FLAG;
|
||||||
|
|
||||||
if ((gb->registers[register_id] & 0x0F) == 0xF) {
|
if ((gb->registers[register_id] & 0x0F) == 0xF) {
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG;
|
||||||
@ -533,7 +533,7 @@ static void daa(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
|
|
||||||
gb->registers[GB_REGISTER_AF] &= ~(0xFF00 | GB_ZERO_FLAG);
|
gb->registers[GB_REGISTER_AF] &= ~(0xFF00 | GB_ZERO_FLAG);
|
||||||
|
|
||||||
if (gb->registers[GB_REGISTER_AF] & GB_SUBSTRACT_FLAG) {
|
if (gb->registers[GB_REGISTER_AF] & GB_SUBTRACT_FLAG) {
|
||||||
if (gb->registers[GB_REGISTER_AF] & GB_HALF_CARRY_FLAG) {
|
if (gb->registers[GB_REGISTER_AF] & GB_HALF_CARRY_FLAG) {
|
||||||
result = (result - 0x06) & 0xFF;
|
result = (result - 0x06) & 0xFF;
|
||||||
}
|
}
|
||||||
@ -567,19 +567,19 @@ static void daa(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
static void cpl(GB_gameboy_t *gb, uint8_t opcode)
|
static void cpl(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
{
|
{
|
||||||
gb->registers[GB_REGISTER_AF] ^= 0xFF00;
|
gb->registers[GB_REGISTER_AF] ^= 0xFF00;
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG | GB_SUBSTRACT_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG | GB_SUBTRACT_FLAG;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void scf(GB_gameboy_t *gb, uint8_t opcode)
|
static void scf(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
{
|
{
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG;
|
||||||
gb->registers[GB_REGISTER_AF] &= ~(GB_HALF_CARRY_FLAG | GB_SUBSTRACT_FLAG);
|
gb->registers[GB_REGISTER_AF] &= ~(GB_HALF_CARRY_FLAG | GB_SUBTRACT_FLAG);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ccf(GB_gameboy_t *gb, uint8_t opcode)
|
static void ccf(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
{
|
{
|
||||||
gb->registers[GB_REGISTER_AF] ^= GB_CARRY_FLAG;
|
gb->registers[GB_REGISTER_AF] ^= GB_CARRY_FLAG;
|
||||||
gb->registers[GB_REGISTER_AF] &= ~(GB_HALF_CARRY_FLAG | GB_SUBSTRACT_FLAG);
|
gb->registers[GB_REGISTER_AF] &= ~(GB_HALF_CARRY_FLAG | GB_SUBTRACT_FLAG);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ld_dhli_a(GB_gameboy_t *gb, uint8_t opcode)
|
static void ld_dhli_a(GB_gameboy_t *gb, uint8_t opcode)
|
||||||
@ -610,7 +610,7 @@ static void inc_dhl(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
value = cycle_read(gb, gb->registers[GB_REGISTER_HL]) + 1;
|
value = cycle_read(gb, gb->registers[GB_REGISTER_HL]) + 1;
|
||||||
cycle_write(gb, gb->registers[GB_REGISTER_HL], value);
|
cycle_write(gb, gb->registers[GB_REGISTER_HL], value);
|
||||||
|
|
||||||
gb->registers[GB_REGISTER_AF] &= ~(GB_SUBSTRACT_FLAG | GB_ZERO_FLAG | GB_HALF_CARRY_FLAG);
|
gb->registers[GB_REGISTER_AF] &= ~(GB_SUBTRACT_FLAG | GB_ZERO_FLAG | GB_HALF_CARRY_FLAG);
|
||||||
if ((value & 0x0F) == 0) {
|
if ((value & 0x0F) == 0) {
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG;
|
||||||
}
|
}
|
||||||
@ -627,7 +627,7 @@ static void dec_dhl(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
cycle_write(gb, gb->registers[GB_REGISTER_HL], value);
|
cycle_write(gb, gb->registers[GB_REGISTER_HL], value);
|
||||||
|
|
||||||
gb->registers[GB_REGISTER_AF] &= ~( GB_ZERO_FLAG | GB_HALF_CARRY_FLAG);
|
gb->registers[GB_REGISTER_AF] &= ~( GB_ZERO_FLAG | GB_HALF_CARRY_FLAG);
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_SUBSTRACT_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_SUBTRACT_FLAG;
|
||||||
if ((value & 0x0F) == 0x0F) {
|
if ((value & 0x0F) == 0x0F) {
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG;
|
||||||
}
|
}
|
||||||
@ -763,7 +763,7 @@ static void sub_a_r(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
uint8_t value, a;
|
uint8_t value, a;
|
||||||
value = get_src_value(gb, opcode);
|
value = get_src_value(gb, opcode);
|
||||||
a = gb->registers[GB_REGISTER_AF] >> 8;
|
a = gb->registers[GB_REGISTER_AF] >> 8;
|
||||||
gb->registers[GB_REGISTER_AF] = ((a - value) << 8) | GB_SUBSTRACT_FLAG;
|
gb->registers[GB_REGISTER_AF] = ((a - value) << 8) | GB_SUBTRACT_FLAG;
|
||||||
if (a == value) {
|
if (a == value) {
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG;
|
||||||
}
|
}
|
||||||
@ -781,7 +781,7 @@ static void sbc_a_r(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
value = get_src_value(gb, opcode);
|
value = get_src_value(gb, opcode);
|
||||||
a = gb->registers[GB_REGISTER_AF] >> 8;
|
a = gb->registers[GB_REGISTER_AF] >> 8;
|
||||||
carry = (gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG) != 0;
|
carry = (gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG) != 0;
|
||||||
gb->registers[GB_REGISTER_AF] = ((a - value - carry) << 8) | GB_SUBSTRACT_FLAG;
|
gb->registers[GB_REGISTER_AF] = ((a - value - carry) << 8) | GB_SUBTRACT_FLAG;
|
||||||
|
|
||||||
if ((uint8_t) (a - value - carry) == 0) {
|
if ((uint8_t) (a - value - carry) == 0) {
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG;
|
||||||
@ -833,7 +833,7 @@ static void cp_a_r(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
value = get_src_value(gb, opcode);
|
value = get_src_value(gb, opcode);
|
||||||
a = gb->registers[GB_REGISTER_AF] >> 8;
|
a = gb->registers[GB_REGISTER_AF] >> 8;
|
||||||
gb->registers[GB_REGISTER_AF] &= 0xFF00;
|
gb->registers[GB_REGISTER_AF] &= 0xFF00;
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_SUBSTRACT_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_SUBTRACT_FLAG;
|
||||||
if (a == value) {
|
if (a == value) {
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG;
|
||||||
}
|
}
|
||||||
@ -962,7 +962,7 @@ static void sub_a_d8(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
uint8_t value, a;
|
uint8_t value, a;
|
||||||
value = cycle_read_inc_oam_bug(gb, gb->pc++);
|
value = cycle_read_inc_oam_bug(gb, gb->pc++);
|
||||||
a = gb->registers[GB_REGISTER_AF] >> 8;
|
a = gb->registers[GB_REGISTER_AF] >> 8;
|
||||||
gb->registers[GB_REGISTER_AF] = ((a - value) << 8) | GB_SUBSTRACT_FLAG;
|
gb->registers[GB_REGISTER_AF] = ((a - value) << 8) | GB_SUBTRACT_FLAG;
|
||||||
if (a == value) {
|
if (a == value) {
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG;
|
||||||
}
|
}
|
||||||
@ -980,7 +980,7 @@ static void sbc_a_d8(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
value = cycle_read_inc_oam_bug(gb, gb->pc++);
|
value = cycle_read_inc_oam_bug(gb, gb->pc++);
|
||||||
a = gb->registers[GB_REGISTER_AF] >> 8;
|
a = gb->registers[GB_REGISTER_AF] >> 8;
|
||||||
carry = (gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG) != 0;
|
carry = (gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG) != 0;
|
||||||
gb->registers[GB_REGISTER_AF] = ((a - value - carry) << 8) | GB_SUBSTRACT_FLAG;
|
gb->registers[GB_REGISTER_AF] = ((a - value - carry) << 8) | GB_SUBTRACT_FLAG;
|
||||||
|
|
||||||
if ((uint8_t) (a - value - carry) == 0) {
|
if ((uint8_t) (a - value - carry) == 0) {
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG;
|
||||||
@ -1032,7 +1032,7 @@ static void cp_a_d8(GB_gameboy_t *gb, uint8_t opcode)
|
|||||||
value = cycle_read_inc_oam_bug(gb, gb->pc++);
|
value = cycle_read_inc_oam_bug(gb, gb->pc++);
|
||||||
a = gb->registers[GB_REGISTER_AF] >> 8;
|
a = gb->registers[GB_REGISTER_AF] >> 8;
|
||||||
gb->registers[GB_REGISTER_AF] &= 0xFF00;
|
gb->registers[GB_REGISTER_AF] &= 0xFF00;
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_SUBSTRACT_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_SUBTRACT_FLAG;
|
||||||
if (a == value) {
|
if (a == value) {
|
||||||
gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG;
|
gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG;
|
||||||
}
|
}
|
||||||
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2015-2019 Lior Halphon
|
Copyright (c) 2015-2020 Lior Halphon
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
83
Makefile
83
Makefile
@ -34,7 +34,7 @@ ifeq ($(MAKECMDGOALS),)
|
|||||||
MAKECMDGOALS := $(DEFAULT)
|
MAKECMDGOALS := $(DEFAULT)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
VERSION := 0.12.2
|
VERSION := 0.12.3
|
||||||
export VERSION
|
export VERSION
|
||||||
CONF ?= debug
|
CONF ?= debug
|
||||||
|
|
||||||
@ -51,10 +51,15 @@ endif
|
|||||||
# Use clang if it's available.
|
# Use clang if it's available.
|
||||||
ifeq ($(origin CC),default)
|
ifeq ($(origin CC),default)
|
||||||
ifneq (, $(shell which clang))
|
ifneq (, $(shell which clang))
|
||||||
CC := clang
|
CC := clang
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
# Find libraries with pkg-config if available.
|
||||||
|
ifneq (, $(shell which pkg-config))
|
||||||
|
PKG_CONFIG := pkg-config
|
||||||
|
endif
|
||||||
|
|
||||||
ifeq ($(PLATFORM),windows32)
|
ifeq ($(PLATFORM),windows32)
|
||||||
# To force use of the Unix version instead of the Windows version
|
# To force use of the Unix version instead of the Windows version
|
||||||
MKDIR := $(shell which mkdir)
|
MKDIR := $(shell which mkdir)
|
||||||
@ -71,9 +76,11 @@ endif
|
|||||||
# Set compilation and linkage flags based on target, platform and configuration
|
# Set compilation and linkage flags based on target, platform and configuration
|
||||||
|
|
||||||
OPEN_DIALOG = OpenDialog/gtk.c
|
OPEN_DIALOG = OpenDialog/gtk.c
|
||||||
|
NULL := /dev/null
|
||||||
|
|
||||||
ifeq ($(PLATFORM),windows32)
|
ifeq ($(PLATFORM),windows32)
|
||||||
OPEN_DIALOG = OpenDialog/windows.c
|
OPEN_DIALOG = OpenDialog/windows.c
|
||||||
|
NULL := NUL
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(PLATFORM),Darwin)
|
ifeq ($(PLATFORM),Darwin)
|
||||||
@ -82,28 +89,47 @@ endif
|
|||||||
|
|
||||||
|
|
||||||
CFLAGS += -Werror -Wall -Wno-unused-result -Wno-strict-aliasing -Wno-unknown-warning -Wno-unknown-warning-option -Wno-multichar -Wno-int-in-bool-context -std=gnu11 -D_GNU_SOURCE -DVERSION="$(VERSION)" -I. -D_USE_MATH_DEFINES
|
CFLAGS += -Werror -Wall -Wno-unused-result -Wno-strict-aliasing -Wno-unknown-warning -Wno-unknown-warning-option -Wno-multichar -Wno-int-in-bool-context -std=gnu11 -D_GNU_SOURCE -DVERSION="$(VERSION)" -I. -D_USE_MATH_DEFINES
|
||||||
SDL_LDFLAGS := -lSDL2 -lGL
|
|
||||||
GTK3_CFLAGS := `pkg-config --cflags gio-2.0 gtk+-3.0 epoxy sdl2` -DGTK_DISABLE_DEPRECATED=1 -DG_DISABLE_DEPRECATED=1 -DG_LOG_DOMAIN=\"SameBoy\" -DRESOURCE_PREFIX=\"/io/github/sameboy/\" -DAPP_ID=\"io.github.sameboy\"
|
ifeq (,$(PKG_CONFIG))
|
||||||
GTK3_LDFLAGS := `pkg-config --libs gio-2.0 gtk+-3.0 epoxy sdl2`
|
SDL_CFLAGS := $(shell sdl2-config --cflags)
|
||||||
|
SDL_LDFLAGS := $(shell sdl2-config --libs)
|
||||||
|
else
|
||||||
|
SDL_CFLAGS := $(shell $(PKG_CONFIG) --cflags sdl2)
|
||||||
|
SDL_LDFLAGS := $(shell $(PKG_CONFIG) --libs sdl2)
|
||||||
|
endif
|
||||||
|
ifeq (,$(PKG_CONFIG))
|
||||||
|
GL_LDFLAGS := -lGL
|
||||||
|
else
|
||||||
|
GL_CFLAGS := $(shell $(PKG_CONFIG) --cflags gl)
|
||||||
|
GL_LDFLAGS := $(shell $(PKG_CONFIG) --libs gl || echo -lGL)
|
||||||
|
endif
|
||||||
|
|
||||||
|
# TODO: The GTK3 frontend required pkg-config
|
||||||
|
ifneq (,$(PKG_CONFIG))
|
||||||
|
GTK3_CFLAGS := $(shell $(PKG_CONFIG) --cflags gio-2.0 gtk+-3.0 epoxy sdl2) -DGTK_DISABLE_DEPRECATED=1 -DG_DISABLE_DEPRECATED=1 -DG_LOG_DOMAIN=\"SameBoy\" -DRESOURCE_PREFIX=\"/io/github/sameboy/\" -DAPP_ID=\"io.github.sameboy\"
|
||||||
|
GTK3_LDFLAGS := $(shell $(PKG_CONFIG) --libs gio-2.0 gtk+-3.0 epoxy sdl2)
|
||||||
|
|
||||||
# TODO: REMOVE DISABLE UNUSED WARNINGS
|
# TODO: REMOVE DISABLE UNUSED WARNINGS
|
||||||
GTK3_CFLAGS += -Wno-unused
|
GTK3_CFLAGS += -Wno-unused
|
||||||
|
endif
|
||||||
|
|
||||||
ifeq ($(PLATFORM),windows32)
|
ifeq ($(PLATFORM),windows32)
|
||||||
CFLAGS += -IWindows -Drandom=rand
|
CFLAGS += -IWindows -Drandom=rand
|
||||||
LDFLAGS += -lmsvcrt -lcomdlg32 -lSDL2main -Wl,/MANIFESTFILE:NUL
|
LDFLAGS += -lmsvcrt -lcomdlg32 -luser32 -lSDL2main -Wl,/MANIFESTFILE:NUL
|
||||||
SDL_LDFLAGS := -lSDL2 -lopengl32
|
SDL_LDFLAGS := -lSDL2
|
||||||
|
GL_LDFLAGS := -lopengl32
|
||||||
else
|
else
|
||||||
LDFLAGS += -lc -lm -ldl
|
LDFLAGS += -lc -lm -ldl
|
||||||
GTK3_LDFLAGS += -Wl -rdynamic
|
GTK3_LDFLAGS += -Wl -rdynamic
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(PLATFORM),Darwin)
|
ifeq ($(PLATFORM),Darwin)
|
||||||
SYSROOT := $(shell xcodebuild -sdk macosx -version Path 2> /dev/null)
|
SYSROOT := $(shell xcodebuild -sdk macosx -version Path 2> $(NULL))
|
||||||
CFLAGS += -F/Library/Frameworks
|
CFLAGS += -F/Library/Frameworks -mmacosx-version-min=10.9
|
||||||
OCFLAGS += -x objective-c -fobjc-arc -Wno-deprecated-declarations -isysroot $(SYSROOT) -mmacosx-version-min=10.9
|
OCFLAGS += -x objective-c -fobjc-arc -Wno-deprecated-declarations -isysroot $(SYSROOT)
|
||||||
LDFLAGS += -framework AppKit -framework PreferencePanes -framework Carbon -framework QuartzCore -weak_framework Metal -weak_framework MetalKit
|
LDFLAGS += -framework AppKit -framework PreferencePanes -framework Carbon -framework QuartzCore -weak_framework Metal -weak_framework MetalKit -mmacosx-version-min=10.9
|
||||||
SDL_LDFLAGS := -F/Library/Frameworks -framework SDL2 -framework OpenGL
|
SDL_LDFLAGS := -F/Library/Frameworks -framework SDL2
|
||||||
|
GL_LDFLAGS := -framework OpenGL
|
||||||
endif
|
endif
|
||||||
CFLAGS += -Wno-deprecated-declarations
|
CFLAGS += -Wno-deprecated-declarations
|
||||||
ifeq ($(PLATFORM),windows32)
|
ifeq ($(PLATFORM),windows32)
|
||||||
@ -115,10 +141,16 @@ ifeq ($(CONF),debug)
|
|||||||
CFLAGS += -g
|
CFLAGS += -g
|
||||||
else ifeq ($(CONF), release)
|
else ifeq ($(CONF), release)
|
||||||
CFLAGS += -O3 -DNDEBUG
|
CFLAGS += -O3 -DNDEBUG
|
||||||
|
STRIP := strip
|
||||||
|
ifeq ($(PLATFORM),Darwin)
|
||||||
|
LDFLAGS += -Wl,-exported_symbols_list,$(NULL)
|
||||||
|
STRIP := -@true
|
||||||
|
endif
|
||||||
ifneq ($(PLATFORM),windows32)
|
ifneq ($(PLATFORM),windows32)
|
||||||
LDFLAGS += -flto
|
LDFLAGS += -flto
|
||||||
CFLAGS += -flto
|
CFLAGS += -flto
|
||||||
endif
|
endif
|
||||||
|
|
||||||
else
|
else
|
||||||
$(error Invalid value for CONF: $(CONF). Use "debug", "release" or "native_release")
|
$(error Invalid value for CONF: $(CONF). Use "debug", "release" or "native_release")
|
||||||
endif
|
endif
|
||||||
@ -192,6 +224,10 @@ $(OBJ)/gtk3/%.gtk3dep: %
|
|||||||
-@$(MKDIR) -p $(dir $@)
|
-@$(MKDIR) -p $(dir $@)
|
||||||
$(CC) $(CFLAGS) $(GTK3_CFLAGS) -MT $(OBJ)/$^.o -M $^ -c -o $@
|
$(CC) $(CFLAGS) $(GTK3_CFLAGS) -MT $(OBJ)/$^.o -M $^ -c -o $@
|
||||||
|
|
||||||
|
$(OBJ)/SDL/%.dep: SDL/%
|
||||||
|
-@$(MKDIR) -p $(dir $@)
|
||||||
|
$(CC) $(CFLAGS) $(SDL_CFLAGS) $(GL_CFLAGS) -MT $(OBJ)/$^.o -M $^ -c -o $@
|
||||||
|
|
||||||
$(OBJ)/%.dep: %
|
$(OBJ)/%.dep: %
|
||||||
-@$(MKDIR) -p $(dir $@)
|
-@$(MKDIR) -p $(dir $@)
|
||||||
$(CC) $(CFLAGS) -MT $(OBJ)/$^.o -M $^ -c -o $@
|
$(CC) $(CFLAGS) -MT $(OBJ)/$^.o -M $^ -c -o $@
|
||||||
@ -207,6 +243,10 @@ $(OBJ)/gtk3/%.c.o: gtk3/%.c
|
|||||||
-@$(MKDIR) -p $(dir $@)
|
-@$(MKDIR) -p $(dir $@)
|
||||||
$(CC) $(CFLAGS) $(GTK3_CFLAGS) -c $< -o $@
|
$(CC) $(CFLAGS) $(GTK3_CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
$(OBJ)/SDL/%.c.o: SDL/%.c
|
||||||
|
-@$(MKDIR) -p $(dir $@)
|
||||||
|
$(CC) $(CFLAGS) $(SDL_CFLAGS) $(GL_CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
$(OBJ)/%.c.o: %.c
|
$(OBJ)/%.c.o: %.c
|
||||||
-@$(MKDIR) -p $(dir $@)
|
-@$(MKDIR) -p $(dir $@)
|
||||||
$(CC) $(CFLAGS) -c $< -o $@
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
@ -248,7 +288,7 @@ $(BIN)/SameBoy.app/Contents/MacOS/SameBoy: $(CORE_OBJECTS) $(COCOA_OBJECTS)
|
|||||||
-@$(MKDIR) -p $(dir $@)
|
-@$(MKDIR) -p $(dir $@)
|
||||||
$(CC) $^ -o $@ $(LDFLAGS) -framework OpenGL -framework AudioUnit -framework AVFoundation -framework CoreVideo -framework CoreMedia -framework IOKit
|
$(CC) $^ -o $@ $(LDFLAGS) -framework OpenGL -framework AudioUnit -framework AVFoundation -framework CoreVideo -framework CoreMedia -framework IOKit
|
||||||
ifeq ($(CONF), release)
|
ifeq ($(CONF), release)
|
||||||
strip $@
|
$(STRIP) $@
|
||||||
endif
|
endif
|
||||||
|
|
||||||
$(BIN)/SameBoy.app/Contents/Resources/Base.lproj/%.nib: Cocoa/%.xib
|
$(BIN)/SameBoy.app/Contents/Resources/Base.lproj/%.nib: Cocoa/%.xib
|
||||||
@ -268,10 +308,7 @@ $(BIN)/SameBoy.qlgenerator: $(BIN)/SameBoy.qlgenerator/Contents/MacOS/SameBoyQL
|
|||||||
# once in the QL Generator. It should probably become a dylib instead.
|
# once in the QL Generator. It should probably become a dylib instead.
|
||||||
$(BIN)/SameBoy.qlgenerator/Contents/MacOS/SameBoyQL: $(CORE_OBJECTS) $(QUICKLOOK_OBJECTS)
|
$(BIN)/SameBoy.qlgenerator/Contents/MacOS/SameBoyQL: $(CORE_OBJECTS) $(QUICKLOOK_OBJECTS)
|
||||||
-@$(MKDIR) -p $(dir $@)
|
-@$(MKDIR) -p $(dir $@)
|
||||||
$(CC) $^ -o $@ $(LDFLAGS) -bundle -framework Cocoa -framework Quicklook
|
$(CC) $^ -o $@ $(LDFLAGS) -Wl,-exported_symbols_list,QuickLook/exports.sym -bundle -framework Cocoa -framework Quicklook
|
||||||
ifeq ($(CONF), release)
|
|
||||||
strip -u -r -s QuickLook/exports.sym $@
|
|
||||||
endif
|
|
||||||
|
|
||||||
# cgb_boot_fast.bin is not a standard boot ROM, we don't expect it to exist in the user-provided
|
# cgb_boot_fast.bin is not a standard boot ROM, we don't expect it to exist in the user-provided
|
||||||
# boot ROM directory.
|
# boot ROM directory.
|
||||||
@ -284,19 +321,19 @@ $(BIN)/SameBoy.qlgenerator/Contents/Resources/cgb_boot_fast.bin: $(BIN)/BootROMs
|
|||||||
# Unix versions build only one binary
|
# Unix versions build only one binary
|
||||||
$(BIN)/SDL/sameboy: $(CORE_OBJECTS) $(SDL_OBJECTS)
|
$(BIN)/SDL/sameboy: $(CORE_OBJECTS) $(SDL_OBJECTS)
|
||||||
-@$(MKDIR) -p $(dir $@)
|
-@$(MKDIR) -p $(dir $@)
|
||||||
$(CC) $^ -o $@ $(LDFLAGS) $(SDL_LDFLAGS)
|
$(CC) $^ -o $@ $(LDFLAGS) $(SDL_LDFLAGS) $(GL_LDFLAGS)
|
||||||
ifeq ($(CONF), release)
|
ifeq ($(CONF), release)
|
||||||
strip $@
|
$(STRIP) $@
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Windows version builds two, one with a console and one without it
|
# Windows version builds two, one with a console and one without it
|
||||||
$(BIN)/SDL/sameboy.exe: $(CORE_OBJECTS) $(SDL_OBJECTS) $(OBJ)/Windows/resources.o
|
$(BIN)/SDL/sameboy.exe: $(CORE_OBJECTS) $(SDL_OBJECTS) $(OBJ)/Windows/resources.o
|
||||||
-@$(MKDIR) -p $(dir $@)
|
-@$(MKDIR) -p $(dir $@)
|
||||||
$(CC) $^ -o $@ $(LDFLAGS) $(SDL_LDFLAGS) -Wl,/subsystem:windows
|
$(CC) $^ -o $@ $(LDFLAGS) $(SDL_LDFLAGS) $(GL_LDFLAGS) -Wl,/subsystem:windows
|
||||||
|
|
||||||
$(BIN)/SDL/sameboy_debugger.exe: $(CORE_OBJECTS) $(SDL_OBJECTS) $(OBJ)/Windows/resources.o
|
$(BIN)/SDL/sameboy_debugger.exe: $(CORE_OBJECTS) $(SDL_OBJECTS) $(OBJ)/Windows/resources.o
|
||||||
-@$(MKDIR) -p $(dir $@)
|
-@$(MKDIR) -p $(dir $@)
|
||||||
$(CC) $^ -o $@ $(LDFLAGS) $(SDL_LDFLAGS) -Wl,/subsystem:console
|
$(CC) $^ -o $@ $(LDFLAGS) $(SDL_LDFLAGS) $(GL_LDFLAGS) -Wl,/subsystem:console
|
||||||
|
|
||||||
ifneq ($(USE_WINDRES),)
|
ifneq ($(USE_WINDRES),)
|
||||||
$(OBJ)/%.o: %.rc
|
$(OBJ)/%.o: %.rc
|
||||||
@ -331,7 +368,7 @@ $(BIN)/tester/sameboy_tester: $(CORE_OBJECTS) $(TESTER_OBJECTS)
|
|||||||
-@$(MKDIR) -p $(dir $@)
|
-@$(MKDIR) -p $(dir $@)
|
||||||
$(CC) $^ -o $@ $(LDFLAGS)
|
$(CC) $^ -o $@ $(LDFLAGS)
|
||||||
ifeq ($(CONF), release)
|
ifeq ($(CONF), release)
|
||||||
strip $@
|
$(STRIP) $@
|
||||||
endif
|
endif
|
||||||
|
|
||||||
$(BIN)/tester/sameboy_tester.exe: $(CORE_OBJECTS) $(SDL_OBJECTS)
|
$(BIN)/tester/sameboy_tester.exe: $(CORE_OBJECTS) $(SDL_OBJECTS)
|
||||||
@ -391,7 +428,7 @@ $(BIN)/BootROMs/%.bin: BootROMs/%.asm $(OBJ)/BootROMs/SameBoyLogo.pb8
|
|||||||
-@$(MKDIR) -p $(dir $@)
|
-@$(MKDIR) -p $(dir $@)
|
||||||
rgbasm -i $(OBJ)/BootROMs/ -i BootROMs/ -o $@.tmp $<
|
rgbasm -i $(OBJ)/BootROMs/ -i BootROMs/ -o $@.tmp $<
|
||||||
rgblink -o $@.tmp2 $@.tmp
|
rgblink -o $@.tmp2 $@.tmp
|
||||||
dd if=$@.tmp2 of=$@ count=1 bs=$(if $(findstring dmg,$@)$(findstring sgb,$@),256,2304)
|
dd if=$@.tmp2 of=$@ count=1 bs=$(if $(findstring dmg,$@)$(findstring sgb,$@),256,2304) 2> $(NULL)
|
||||||
@rm $@.tmp $@.tmp2
|
@rm $@.tmp $@.tmp2
|
||||||
|
|
||||||
# Libretro Core (uses its own build system)
|
# Libretro Core (uses its own build system)
|
||||||
|
@ -47,7 +47,7 @@
|
|||||||
<key>CFPlugInUnloadFunction</key>
|
<key>CFPlugInUnloadFunction</key>
|
||||||
<string></string>
|
<string></string>
|
||||||
<key>NSHumanReadableCopyright</key>
|
<key>NSHumanReadableCopyright</key>
|
||||||
<string>Copyright © 2015-2019 Lior Halphon</string>
|
<string>Copyright © 2015-2020 Lior Halphon</string>
|
||||||
<key>QLNeedsToBeRunInMainThread</key>
|
<key>QLNeedsToBeRunInMainThread</key>
|
||||||
<false/>
|
<false/>
|
||||||
<key>QLPreviewHeight</key>
|
<key>QLPreviewHeight</key>
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
_DeallocQuickLookGeneratorPluginType
|
_DeallocQuickLookGeneratorPluginType
|
||||||
_QuickLookGeneratorQueryInterface
|
_QuickLookGeneratorQueryInterface
|
||||||
_QuickLookGeneratorPluginFactory
|
_QuickLookGeneratorPluginFactory
|
||||||
|
99
SDL/gui.c
99
SDL/gui.c
@ -1,5 +1,5 @@
|
|||||||
#include <SDL2/SDL.h>
|
|
||||||
#include <OpenDialog/open_dialog.h>
|
#include <OpenDialog/open_dialog.h>
|
||||||
|
#include <SDL.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@ -33,7 +33,7 @@ void render_texture(void *pixels, void *previous)
|
|||||||
{
|
{
|
||||||
if (renderer) {
|
if (renderer) {
|
||||||
if (pixels) {
|
if (pixels) {
|
||||||
SDL_UpdateTexture(texture, NULL, pixels, 160 * sizeof (uint32_t));
|
SDL_UpdateTexture(texture, NULL, pixels, GB_get_screen_width(&gb) * sizeof (uint32_t));
|
||||||
}
|
}
|
||||||
SDL_RenderClear(renderer);
|
SDL_RenderClear(renderer);
|
||||||
SDL_RenderCopy(renderer, texture, NULL, NULL);
|
SDL_RenderCopy(renderer, texture, NULL, NULL);
|
||||||
@ -175,9 +175,12 @@ static void draw_char(uint32_t *buffer, unsigned width, unsigned height, unsigne
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unsigned scroll = 0;
|
||||||
static void draw_unbordered_text(uint32_t *buffer, unsigned width, unsigned height, unsigned x, unsigned y, const char *string, uint32_t color)
|
static void draw_unbordered_text(uint32_t *buffer, unsigned width, unsigned height, unsigned x, unsigned y, const char *string, uint32_t color)
|
||||||
{
|
{
|
||||||
|
y -= scroll;
|
||||||
unsigned orig_x = x;
|
unsigned orig_x = x;
|
||||||
|
unsigned y_offset = (GB_get_screen_height(&gb) - 144) / 2;
|
||||||
while (*string) {
|
while (*string) {
|
||||||
if (*string == '\n') {
|
if (*string == '\n') {
|
||||||
x = orig_x;
|
x = orig_x;
|
||||||
@ -186,7 +189,7 @@ static void draw_unbordered_text(uint32_t *buffer, unsigned width, unsigned heig
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (x > width - GLYPH_WIDTH || y == 0 || y > height - GLYPH_HEIGHT) {
|
if (x > width - GLYPH_WIDTH || y == 0 || y - y_offset > 144 - GLYPH_HEIGHT) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -297,6 +300,7 @@ static void return_to_root_menu(unsigned index)
|
|||||||
{
|
{
|
||||||
current_menu = root_menu;
|
current_menu = root_menu;
|
||||||
current_selection = 0;
|
current_selection = 0;
|
||||||
|
scroll = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cycle_model(unsigned index)
|
static void cycle_model(unsigned index)
|
||||||
@ -407,6 +411,7 @@ static void enter_emulation_menu(unsigned index)
|
|||||||
{
|
{
|
||||||
current_menu = emulation_menu;
|
current_menu = emulation_menu;
|
||||||
current_selection = 0;
|
current_selection = 0;
|
||||||
|
scroll = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *current_scaling_mode(unsigned index)
|
const char *current_scaling_mode(unsigned index)
|
||||||
@ -421,6 +426,12 @@ const char *current_color_correction_mode(unsigned index)
|
|||||||
[configuration.color_correction_mode];
|
[configuration.color_correction_mode];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *current_palette(unsigned index)
|
||||||
|
{
|
||||||
|
return (const char *[]){"Greyscale", "Lime (Game Boy)", "Olive (Pocket)", "Teal (Light)"}
|
||||||
|
[configuration.dmg_palette];
|
||||||
|
}
|
||||||
|
|
||||||
void cycle_scaling(unsigned index)
|
void cycle_scaling(unsigned index)
|
||||||
{
|
{
|
||||||
configuration.scaling_mode++;
|
configuration.scaling_mode++;
|
||||||
@ -463,6 +474,26 @@ static void cycle_color_correction_backwards(unsigned index)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void cycle_palette(unsigned index)
|
||||||
|
{
|
||||||
|
if (configuration.dmg_palette == 3) {
|
||||||
|
configuration.dmg_palette = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
configuration.dmg_palette++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cycle_palette_backwards(unsigned index)
|
||||||
|
{
|
||||||
|
if (configuration.dmg_palette == 0) {
|
||||||
|
configuration.dmg_palette = 3;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
configuration.dmg_palette--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct shader_name {
|
struct shader_name {
|
||||||
const char *file_name;
|
const char *file_name;
|
||||||
const char *display_name;
|
const char *display_name;
|
||||||
@ -471,6 +502,7 @@ struct shader_name {
|
|||||||
{"NearestNeighbor", "Nearest Neighbor"},
|
{"NearestNeighbor", "Nearest Neighbor"},
|
||||||
{"Bilinear", "Bilinear"},
|
{"Bilinear", "Bilinear"},
|
||||||
{"SmoothBilinear", "Smooth Bilinear"},
|
{"SmoothBilinear", "Smooth Bilinear"},
|
||||||
|
{"MonoLCD", "Monochrome LCD"},
|
||||||
{"LCD", "LCD Display"},
|
{"LCD", "LCD Display"},
|
||||||
{"CRT", "CRT Display"},
|
{"CRT", "CRT Display"},
|
||||||
{"Scale2x", "Scale2x"},
|
{"Scale2x", "Scale2x"},
|
||||||
@ -556,6 +588,7 @@ static const struct menu_item graphics_menu[] = {
|
|||||||
{"Scaling Mode:", cycle_scaling, current_scaling_mode, cycle_scaling_backwards},
|
{"Scaling Mode:", cycle_scaling, current_scaling_mode, cycle_scaling_backwards},
|
||||||
{"Scaling Filter:", cycle_filter, current_filter_name, cycle_filter_backwards},
|
{"Scaling Filter:", cycle_filter, current_filter_name, cycle_filter_backwards},
|
||||||
{"Color Correction:", cycle_color_correction, current_color_correction_mode, cycle_color_correction_backwards},
|
{"Color Correction:", cycle_color_correction, current_color_correction_mode, cycle_color_correction_backwards},
|
||||||
|
{"Mono Palette:", cycle_palette, current_palette, cycle_palette_backwards},
|
||||||
{"Blend Frames:", toggle_blend_frames, blend_frames_string, toggle_blend_frames},
|
{"Blend Frames:", toggle_blend_frames, blend_frames_string, toggle_blend_frames},
|
||||||
{"Back", return_to_root_menu},
|
{"Back", return_to_root_menu},
|
||||||
{NULL,}
|
{NULL,}
|
||||||
@ -565,6 +598,7 @@ static void enter_graphics_menu(unsigned index)
|
|||||||
{
|
{
|
||||||
current_menu = graphics_menu;
|
current_menu = graphics_menu;
|
||||||
current_selection = 0;
|
current_selection = 0;
|
||||||
|
scroll = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *highpass_filter_string(unsigned index)
|
const char *highpass_filter_string(unsigned index)
|
||||||
@ -601,6 +635,7 @@ static void enter_audio_menu(unsigned index)
|
|||||||
{
|
{
|
||||||
current_menu = audio_menu;
|
current_menu = audio_menu;
|
||||||
current_selection = 0;
|
current_selection = 0;
|
||||||
|
scroll = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void modify_key(unsigned index)
|
static void modify_key(unsigned index)
|
||||||
@ -608,7 +643,6 @@ static void modify_key(unsigned index)
|
|||||||
gui_state = WAITING_FOR_KEY;
|
gui_state = WAITING_FOR_KEY;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void enter_controls_menu_2(unsigned index);
|
|
||||||
static const char *key_name(unsigned index);
|
static const char *key_name(unsigned index);
|
||||||
|
|
||||||
static const struct menu_item controls_menu[] = {
|
static const struct menu_item controls_menu[] = {
|
||||||
@ -620,12 +654,6 @@ static const struct menu_item controls_menu[] = {
|
|||||||
{"B:", modify_key, key_name,},
|
{"B:", modify_key, key_name,},
|
||||||
{"Select:", modify_key, key_name,},
|
{"Select:", modify_key, key_name,},
|
||||||
{"Start:", modify_key, key_name,},
|
{"Start:", modify_key, key_name,},
|
||||||
{"Next Page", enter_controls_menu_2},
|
|
||||||
{"Back", return_to_root_menu},
|
|
||||||
{NULL,}
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct menu_item controls_menu_2[] = {
|
|
||||||
{"Turbo:", modify_key, key_name,},
|
{"Turbo:", modify_key, key_name,},
|
||||||
{"Rewind:", modify_key, key_name,},
|
{"Rewind:", modify_key, key_name,},
|
||||||
{"Slow-Motion:", modify_key, key_name,},
|
{"Slow-Motion:", modify_key, key_name,},
|
||||||
@ -635,11 +663,11 @@ static const struct menu_item controls_menu_2[] = {
|
|||||||
|
|
||||||
static const char *key_name(unsigned index)
|
static const char *key_name(unsigned index)
|
||||||
{
|
{
|
||||||
if (current_menu == controls_menu_2) {
|
if (index >= 8) {
|
||||||
if (index == 0) {
|
if (index == 8) {
|
||||||
return SDL_GetScancodeName(configuration.keys[8]);
|
return SDL_GetScancodeName(configuration.keys[8]);
|
||||||
}
|
}
|
||||||
return SDL_GetScancodeName(configuration.keys_2[index - 1]);
|
return SDL_GetScancodeName(configuration.keys_2[index - 9]);
|
||||||
}
|
}
|
||||||
return SDL_GetScancodeName(configuration.keys[index]);
|
return SDL_GetScancodeName(configuration.keys[index]);
|
||||||
}
|
}
|
||||||
@ -648,12 +676,7 @@ static void enter_controls_menu(unsigned index)
|
|||||||
{
|
{
|
||||||
current_menu = controls_menu;
|
current_menu = controls_menu;
|
||||||
current_selection = 0;
|
current_selection = 0;
|
||||||
}
|
scroll = 0;
|
||||||
|
|
||||||
static void enter_controls_menu_2(unsigned index)
|
|
||||||
{
|
|
||||||
current_menu = controls_menu_2;
|
|
||||||
current_selection = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned joypad_index = 0;
|
static unsigned joypad_index = 0;
|
||||||
@ -744,6 +767,7 @@ static void enter_joypad_menu(unsigned index)
|
|||||||
{
|
{
|
||||||
current_menu = joypad_menu;
|
current_menu = joypad_menu;
|
||||||
current_selection = 0;
|
current_selection = 0;
|
||||||
|
scroll = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
joypad_button_t get_joypad_button(uint8_t physical_button)
|
joypad_button_t get_joypad_button(uint8_t physical_button)
|
||||||
@ -792,6 +816,7 @@ void connect_joypad(void)
|
|||||||
|
|
||||||
void run_gui(bool is_running)
|
void run_gui(bool is_running)
|
||||||
{
|
{
|
||||||
|
SDL_ShowCursor(SDL_ENABLE);
|
||||||
connect_joypad();
|
connect_joypad();
|
||||||
|
|
||||||
/* Draw the background screen */
|
/* Draw the background screen */
|
||||||
@ -825,6 +850,7 @@ void run_gui(bool is_running)
|
|||||||
bool should_render = true;
|
bool should_render = true;
|
||||||
current_menu = root_menu = is_running? paused_menu : nonpaused_menu;
|
current_menu = root_menu = is_running? paused_menu : nonpaused_menu;
|
||||||
current_selection = 0;
|
current_selection = 0;
|
||||||
|
scroll = 0;
|
||||||
do {
|
do {
|
||||||
/* Convert Joypad and mouse events (We only generate down events) */
|
/* Convert Joypad and mouse events (We only generate down events) */
|
||||||
if (gui_state != WAITING_FOR_KEY && gui_state != WAITING_FOR_JBUTTON) {
|
if (gui_state != WAITING_FOR_KEY && gui_state != WAITING_FOR_JBUTTON) {
|
||||||
@ -849,6 +875,7 @@ void run_gui(bool is_running)
|
|||||||
y = y * 8 / 7;
|
y = y * 8 / 7;
|
||||||
y -= 144 / 16;
|
y -= 144 / 16;
|
||||||
}
|
}
|
||||||
|
y += scroll;
|
||||||
|
|
||||||
if (x < 0 || x >= 160 || y < 24) {
|
if (x < 0 || x >= 160 || y < 24) {
|
||||||
continue;
|
continue;
|
||||||
@ -1011,6 +1038,15 @@ void run_gui(bool is_running)
|
|||||||
}
|
}
|
||||||
|
|
||||||
case SDL_KEYDOWN:
|
case SDL_KEYDOWN:
|
||||||
|
if (event.key.keysym.scancode == SDL_SCANCODE_F && event.key.keysym.mod & MODIFIER) {
|
||||||
|
if ((SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN_DESKTOP) == false) {
|
||||||
|
SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
SDL_SetWindowFullscreen(window, 0);
|
||||||
|
}
|
||||||
|
update_viewport();
|
||||||
|
}
|
||||||
if (event.key.keysym.scancode == SDL_SCANCODE_O) {
|
if (event.key.keysym.scancode == SDL_SCANCODE_O) {
|
||||||
if (event.key.keysym.mod & MODIFIER) {
|
if (event.key.keysym.mod & MODIFIER) {
|
||||||
char *filename = do_open_rom_dialog();
|
char *filename = do_open_rom_dialog();
|
||||||
@ -1048,6 +1084,7 @@ void run_gui(bool is_running)
|
|||||||
gui_state = SHOWING_DROP_MESSAGE;
|
gui_state = SHOWING_DROP_MESSAGE;
|
||||||
}
|
}
|
||||||
current_selection = 0;
|
current_selection = 0;
|
||||||
|
scroll = 0;
|
||||||
current_menu = root_menu;
|
current_menu = root_menu;
|
||||||
should_render = true;
|
should_render = true;
|
||||||
}
|
}
|
||||||
@ -1096,12 +1133,12 @@ void run_gui(bool is_running)
|
|||||||
should_render = true;
|
should_render = true;
|
||||||
}
|
}
|
||||||
else if (gui_state == WAITING_FOR_KEY) {
|
else if (gui_state == WAITING_FOR_KEY) {
|
||||||
if (current_menu == controls_menu_2) {
|
if (current_selection >= 8) {
|
||||||
if (current_selection == 0) {
|
if (current_selection == 8) {
|
||||||
configuration.keys[8] = event.key.keysym.scancode;
|
configuration.keys[8] = event.key.keysym.scancode;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
configuration.keys_2[current_selection - 1] = event.key.keysym.scancode;
|
configuration.keys_2[current_selection - 9] = event.key.keysym.scancode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -1115,6 +1152,7 @@ void run_gui(bool is_running)
|
|||||||
|
|
||||||
if (should_render) {
|
if (should_render) {
|
||||||
should_render = false;
|
should_render = false;
|
||||||
|
rerender:
|
||||||
if (width == 160 && height == 144) {
|
if (width == 160 && height == 144) {
|
||||||
memcpy(pixels, converted_background->pixels, sizeof(pixels));
|
memcpy(pixels, converted_background->pixels, sizeof(pixels));
|
||||||
}
|
}
|
||||||
@ -1134,6 +1172,16 @@ void run_gui(bool is_running)
|
|||||||
draw_text_centered(pixels, width, height, 8 + y_offset, "SameBoy", gui_palette_native[3], gui_palette_native[0], false);
|
draw_text_centered(pixels, width, height, 8 + y_offset, "SameBoy", gui_palette_native[3], gui_palette_native[0], false);
|
||||||
unsigned i = 0, y = 24;
|
unsigned i = 0, y = 24;
|
||||||
for (const struct menu_item *item = current_menu; item->string; item++, i++) {
|
for (const struct menu_item *item = current_menu; item->string; item++, i++) {
|
||||||
|
if (i == current_selection) {
|
||||||
|
if (y < scroll) {
|
||||||
|
scroll = y - 4;
|
||||||
|
goto rerender;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i == current_selection && i == 0 && scroll != 0) {
|
||||||
|
scroll = 0;
|
||||||
|
goto rerender;
|
||||||
|
}
|
||||||
if (item->value_getter && !item->backwards_handler) {
|
if (item->value_getter && !item->backwards_handler) {
|
||||||
char line[25];
|
char line[25];
|
||||||
snprintf(line, sizeof(line), "%s%*s", item->string, 24 - (int)strlen(item->string), item->value_getter(i));
|
snprintf(line, sizeof(line), "%s%*s", item->string, 24 - (int)strlen(item->string), item->value_getter(i));
|
||||||
@ -1152,6 +1200,13 @@ void run_gui(bool is_running)
|
|||||||
y += 12;
|
y += 12;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (i == current_selection) {
|
||||||
|
if (y > scroll + 144) {
|
||||||
|
scroll = y - 144;
|
||||||
|
goto rerender;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SHOWING_HELP:
|
case SHOWING_HELP:
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#ifndef gui_h
|
#ifndef gui_h
|
||||||
#define gui_h
|
#define gui_h
|
||||||
|
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL.h>
|
||||||
#include <Core/gb.h>
|
#include <Core/gb.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include "shader.h"
|
#include "shader.h"
|
||||||
@ -100,6 +100,9 @@ typedef struct {
|
|||||||
SGB_2,
|
SGB_2,
|
||||||
SGB_MAX
|
SGB_MAX
|
||||||
} sgb_revision;
|
} sgb_revision;
|
||||||
|
|
||||||
|
/* v0.13 */
|
||||||
|
uint8_t dmg_palette;
|
||||||
} configuration_t;
|
} configuration_t;
|
||||||
|
|
||||||
extern configuration_t configuration;
|
extern configuration_t configuration;
|
||||||
|
67
SDL/main.c
Executable file → Normal file
67
SDL/main.c
Executable file → Normal file
@ -2,7 +2,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <OpenDialog/open_dialog.h>
|
#include <OpenDialog/open_dialog.h>
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL.h>
|
||||||
#include <Core/gb.h>
|
#include <Core/gb.h>
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "gui.h"
|
#include "gui.h"
|
||||||
@ -12,6 +12,7 @@
|
|||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
#define AUDIO_FREQUENCY 96000
|
#define AUDIO_FREQUENCY 96000
|
||||||
#else
|
#else
|
||||||
|
#include <Windows.h>
|
||||||
/* Windows (well, at least my VM) can't handle 96KHz sound well :( */
|
/* Windows (well, at least my VM) can't handle 96KHz sound well :( */
|
||||||
|
|
||||||
/* felsqualle says: For SDL 2.0.6+ using the WASAPI driver, the highest freq.
|
/* felsqualle says: For SDL 2.0.6+ using the WASAPI driver, the highest freq.
|
||||||
@ -91,6 +92,26 @@ static const char *end_capturing_logs(bool show_popup, bool should_exit)
|
|||||||
return captured_log;
|
return captured_log;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void update_palette(void)
|
||||||
|
{
|
||||||
|
switch (configuration.dmg_palette) {
|
||||||
|
case 1:
|
||||||
|
GB_set_palette(&gb, &GB_PALETTE_DMG);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
GB_set_palette(&gb, &GB_PALETTE_MGB);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
GB_set_palette(&gb, &GB_PALETTE_GBL);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
GB_set_palette(&gb, &GB_PALETTE_GREY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void open_menu(void)
|
static void open_menu(void)
|
||||||
{
|
{
|
||||||
bool audio_playing = SDL_GetAudioDeviceStatus(device_id) == SDL_AUDIO_PLAYING;
|
bool audio_playing = SDL_GetAudioDeviceStatus(device_id) == SDL_AUDIO_PLAYING;
|
||||||
@ -98,11 +119,13 @@ static void open_menu(void)
|
|||||||
SDL_PauseAudioDevice(device_id, 1);
|
SDL_PauseAudioDevice(device_id, 1);
|
||||||
}
|
}
|
||||||
run_gui(true);
|
run_gui(true);
|
||||||
|
SDL_ShowCursor(SDL_DISABLE);
|
||||||
if (audio_playing) {
|
if (audio_playing) {
|
||||||
SDL_ClearQueuedAudio(device_id);
|
SDL_ClearQueuedAudio(device_id);
|
||||||
SDL_PauseAudioDevice(device_id, 0);
|
SDL_PauseAudioDevice(device_id, 0);
|
||||||
}
|
}
|
||||||
GB_set_color_correction_mode(&gb, configuration.color_correction_mode);
|
GB_set_color_correction_mode(&gb, configuration.color_correction_mode);
|
||||||
|
update_palette();
|
||||||
GB_set_highpass_filter_mode(&gb, configuration.highpass_mode);
|
GB_set_highpass_filter_mode(&gb, configuration.highpass_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,7 +146,7 @@ static void handle_events(GB_gameboy_t *gb)
|
|||||||
}
|
}
|
||||||
|
|
||||||
case SDL_WINDOWEVENT: {
|
case SDL_WINDOWEVENT: {
|
||||||
if (event.window.event == SDL_WINDOWEVENT_RESIZED) {
|
if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
|
||||||
update_viewport();
|
update_viewport();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -273,6 +296,7 @@ static void handle_events(GB_gameboy_t *gb)
|
|||||||
else {
|
else {
|
||||||
SDL_SetWindowFullscreen(window, 0);
|
SDL_SetWindowFullscreen(window, 0);
|
||||||
}
|
}
|
||||||
|
update_viewport();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -406,14 +430,12 @@ static bool handle_pending_command(void)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
case GB_SDL_RESET_COMMAND:
|
|
||||||
GB_save_battery(&gb, battery_save_path_ptr);
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case GB_SDL_NO_COMMAND:
|
case GB_SDL_NO_COMMAND:
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
case GB_SDL_RESET_COMMAND:
|
||||||
case GB_SDL_NEW_FILE_COMMAND:
|
case GB_SDL_NEW_FILE_COMMAND:
|
||||||
|
GB_save_battery(&gb, battery_save_path_ptr);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case GB_SDL_QUIT_COMMAND:
|
case GB_SDL_QUIT_COMMAND:
|
||||||
@ -423,8 +445,27 @@ static bool handle_pending_command(void)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void load_boot_rom(GB_gameboy_t *gb, GB_boot_rom_t type)
|
||||||
|
{
|
||||||
|
bool error = false;
|
||||||
|
start_capturing_logs();
|
||||||
|
static const char *const names[] = {
|
||||||
|
[GB_BOOT_ROM_DMG0] = "dmg0_boot.bin",
|
||||||
|
[GB_BOOT_ROM_DMG] = "dmg_boot.bin",
|
||||||
|
[GB_BOOT_ROM_MGB] = "mgb_boot.bin",
|
||||||
|
[GB_BOOT_ROM_SGB] = "sgb_boot.bin",
|
||||||
|
[GB_BOOT_ROM_SGB2] = "sgb2_boot.bin",
|
||||||
|
[GB_BOOT_ROM_CGB0] = "cgb0_boot.bin",
|
||||||
|
[GB_BOOT_ROM_CGB] = "cgb_boot.bin",
|
||||||
|
[GB_BOOT_ROM_AGB] = "agb_boot.bin",
|
||||||
|
};
|
||||||
|
GB_load_boot_rom(gb, resource_path(names[type]));
|
||||||
|
end_capturing_logs(true, error);
|
||||||
|
}
|
||||||
|
|
||||||
static void run(void)
|
static void run(void)
|
||||||
{
|
{
|
||||||
|
SDL_ShowCursor(SDL_DISABLE);
|
||||||
GB_model_t model;
|
GB_model_t model;
|
||||||
pending_command = GB_SDL_NO_COMMAND;
|
pending_command = GB_SDL_NO_COMMAND;
|
||||||
restart:
|
restart:
|
||||||
@ -447,11 +488,13 @@ restart:
|
|||||||
else {
|
else {
|
||||||
GB_init(&gb, model);
|
GB_init(&gb, model);
|
||||||
|
|
||||||
|
GB_set_boot_rom_load_callback(&gb, load_boot_rom);
|
||||||
GB_set_vblank_callback(&gb, (GB_vblank_callback_t) vblank);
|
GB_set_vblank_callback(&gb, (GB_vblank_callback_t) vblank);
|
||||||
GB_set_pixels_output(&gb, active_pixel_buffer);
|
GB_set_pixels_output(&gb, active_pixel_buffer);
|
||||||
GB_set_rgb_encode_callback(&gb, rgb_encode);
|
GB_set_rgb_encode_callback(&gb, rgb_encode);
|
||||||
GB_set_sample_rate(&gb, have_aspec.freq);
|
GB_set_sample_rate(&gb, have_aspec.freq);
|
||||||
GB_set_color_correction_mode(&gb, configuration.color_correction_mode);
|
GB_set_color_correction_mode(&gb, configuration.color_correction_mode);
|
||||||
|
update_palette();
|
||||||
GB_set_highpass_filter_mode(&gb, configuration.highpass_mode);
|
GB_set_highpass_filter_mode(&gb, configuration.highpass_mode);
|
||||||
GB_set_rewind_length(&gb, configuration.rewind_length);
|
GB_set_rewind_length(&gb, configuration.rewind_length);
|
||||||
GB_set_update_input_hint_callback(&gb, handle_events);
|
GB_set_update_input_hint_callback(&gb, handle_events);
|
||||||
@ -466,15 +509,6 @@ restart:
|
|||||||
|
|
||||||
|
|
||||||
bool error = false;
|
bool error = false;
|
||||||
start_capturing_logs();
|
|
||||||
const char * const boot_roms[] = {"dmg_boot.bin", "cgb_boot.bin", "agb_boot.bin", "sgb_boot.bin"};
|
|
||||||
const char *boot_rom = boot_roms[configuration.model];
|
|
||||||
if (configuration.model == MODEL_SGB && configuration.sgb_revision == SGB_2) {
|
|
||||||
boot_rom = "sgb2_boot.bin";
|
|
||||||
}
|
|
||||||
error = GB_load_boot_rom(&gb, resource_path(boot_rom));
|
|
||||||
end_capturing_logs(true, error);
|
|
||||||
|
|
||||||
start_capturing_logs();
|
start_capturing_logs();
|
||||||
error = GB_load_rom(&gb, filename);
|
error = GB_load_rom(&gb, filename);
|
||||||
end_capturing_logs(true, error);
|
end_capturing_logs(true, error);
|
||||||
@ -550,6 +584,9 @@ static bool get_arg_flag(const char *flag, int *argc, char **argv)
|
|||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
SetProcessDPIAware();
|
||||||
|
#endif
|
||||||
#define str(x) #x
|
#define str(x) #x
|
||||||
#define xstr(x) str(x)
|
#define xstr(x) str(x)
|
||||||
fprintf(stderr, "SameBoy v" xstr(VERSION) "\n");
|
fprintf(stderr, "SameBoy v" xstr(VERSION) "\n");
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#define GL_GLEXT_PROTOTYPES
|
#define GL_GLEXT_PROTOTYPES
|
||||||
#include <SDL2/SDL_opengl.h>
|
#include <SDL_opengl.h>
|
||||||
|
|
||||||
#ifndef __APPLE__
|
#ifndef __APPLE__
|
||||||
#define GL_COMPAT_NAME(func) gl_compat_##func
|
#define GL_COMPAT_NAME(func) gl_compat_##func
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
#define opengl_compat_h
|
#define opengl_compat_h
|
||||||
|
|
||||||
#define GL_GLEXT_PROTOTYPES
|
#define GL_GLEXT_PROTOTYPES
|
||||||
#include <SDL2/SDL_opengl.h>
|
#include <SDL_opengl.h>
|
||||||
#include <SDL2/SDL_video.h>
|
#include <SDL_video.h>
|
||||||
|
|
||||||
#ifndef __APPLE__
|
#ifndef __APPLE__
|
||||||
#define GL_COMPAT_NAME(func) gl_compat_##func
|
#define GL_COMPAT_NAME(func) gl_compat_##func
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include <SDL2/SDL.h>
|
#include <SDL.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
38
Shaders/MonoLCD.fsh
Normal file
38
Shaders/MonoLCD.fsh
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#define SCANLINE_DEPTH 0.25
|
||||||
|
#define BLOOM 0.4
|
||||||
|
|
||||||
|
STATIC vec4 scale(sampler2D image, vec2 position, vec2 input_resolution, vec2 output_resolution)
|
||||||
|
{
|
||||||
|
vec2 pixel = position * input_resolution - vec2(0.5, 0.5);
|
||||||
|
|
||||||
|
vec4 q11 = texture(image, (floor(pixel) + 0.5) / input_resolution);
|
||||||
|
vec4 q12 = texture(image, (vec2(floor(pixel.x), ceil(pixel.y)) + 0.5) / input_resolution);
|
||||||
|
vec4 q21 = texture(image, (vec2(ceil(pixel.x), floor(pixel.y)) + 0.5) / input_resolution);
|
||||||
|
vec4 q22 = texture(image, (ceil(pixel) + 0.5) / input_resolution);
|
||||||
|
|
||||||
|
vec2 s = smoothstep(0., 1., fract(pixel));
|
||||||
|
|
||||||
|
vec4 r1 = mix(q11, q21, s.x);
|
||||||
|
vec4 r2 = mix(q12, q22, s.x);
|
||||||
|
|
||||||
|
vec2 pos = fract(position * input_resolution);
|
||||||
|
vec2 sub_pos = fract(position * input_resolution * 6);
|
||||||
|
|
||||||
|
float multiplier = 1.0;
|
||||||
|
|
||||||
|
if (pos.y < 1.0 / 6.0) {
|
||||||
|
multiplier *= sub_pos.y * SCANLINE_DEPTH + (1 - SCANLINE_DEPTH);
|
||||||
|
}
|
||||||
|
else if (pos.y > 5.0 / 6.0) {
|
||||||
|
multiplier *= (1.0 - sub_pos.y) * SCANLINE_DEPTH + (1 - SCANLINE_DEPTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos.x < 1.0 / 6.0) {
|
||||||
|
multiplier *= sub_pos.x * SCANLINE_DEPTH + (1 - SCANLINE_DEPTH);
|
||||||
|
}
|
||||||
|
else if (pos.x > 5.0 / 6.0) {
|
||||||
|
multiplier *= (1.0 - sub_pos.x) * SCANLINE_DEPTH + (1 - SCANLINE_DEPTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mix(texture(image, position) * multiplier, mix(r1, r2, s.y), BLOOM);
|
||||||
|
}
|
Binary file not shown.
@ -2098,6 +2098,7 @@ static void load_boot_rom(GuiData *gui_data) {
|
|||||||
case GB_MODEL_SGB:
|
case GB_MODEL_SGB:
|
||||||
case GB_MODEL_SGB_PAL:
|
case GB_MODEL_SGB_PAL:
|
||||||
case GB_MODEL_SGB_NO_SFC:
|
case GB_MODEL_SGB_NO_SFC:
|
||||||
|
case GB_MODEL_SGB_PAL_NO_SFC:
|
||||||
boot_rom_name = "sgb_boot.bin";
|
boot_rom_name = "sgb_boot.bin";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -92,6 +92,7 @@ static unsigned emulated_devices = 1;
|
|||||||
static bool initialized = false;
|
static bool initialized = false;
|
||||||
static unsigned screen_layout = 0;
|
static unsigned screen_layout = 0;
|
||||||
static unsigned audio_out = 0;
|
static unsigned audio_out = 0;
|
||||||
|
static unsigned sgb_border = 1;
|
||||||
|
|
||||||
static bool geometry_updated = false;
|
static bool geometry_updated = false;
|
||||||
static bool link_cable_emulation = false;
|
static bool link_cable_emulation = false;
|
||||||
@ -204,6 +205,7 @@ static const struct retro_variable vars_single[] = {
|
|||||||
{ "sameboy_color_correction_mode", "Color correction; off|correct curves|emulate hardware|preserve brightness" },
|
{ "sameboy_color_correction_mode", "Color correction; off|correct curves|emulate hardware|preserve brightness" },
|
||||||
{ "sameboy_high_pass_filter_mode", "High-pass filter; off|accurate|remove dc offset" },
|
{ "sameboy_high_pass_filter_mode", "High-pass filter; off|accurate|remove dc offset" },
|
||||||
{ "sameboy_model", "Emulated model; Auto|Game Boy|Game Boy Color|Game Boy Advance|Super Game Boy|Super Game Boy 2" },
|
{ "sameboy_model", "Emulated model; Auto|Game Boy|Game Boy Color|Game Boy Advance|Super Game Boy|Super Game Boy 2" },
|
||||||
|
{ "sameboy_border", "Super Game Boy border; enabled|disabled" },
|
||||||
{ NULL }
|
{ NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -423,21 +425,20 @@ static void init_for_current_model(unsigned id)
|
|||||||
descs[7].len = 0x4000;
|
descs[7].len = 0x4000;
|
||||||
descs[7].flags = RETRO_MEMDESC_CONST;
|
descs[7].flags = RETRO_MEMDESC_CONST;
|
||||||
|
|
||||||
descs[8].ptr = GB_get_direct_access(&gameboy[i], GB_DIRECT_ACCESS_OAM, &size, &bank);
|
descs[8].ptr = GB_get_direct_access(&gameboy[i], GB_DIRECT_ACCESS_OAM, &size, &bank);
|
||||||
descs[8].start = 0xFE00;
|
descs[8].start = 0xFE00;
|
||||||
descs[8].select = 0xFFFFFF00;
|
descs[8].len = 0x00A0;
|
||||||
descs[8].len = 0x00A0;
|
descs[8].select= 0xFFFFFF00;
|
||||||
|
|
||||||
descs[9].ptr = descs[2].ptr + 0x2000; /* GBC RAM bank 2 */
|
descs[9].ptr = descs[2].ptr + 0x2000; /* GBC RAM bank 2 */
|
||||||
descs[9].start = 0x10000;
|
descs[9].start = 0x10000;
|
||||||
descs[9].select = 0xFFFF0000;
|
descs[9].len = GB_is_cgb(&gameboy[i]) ? 0x6000 : 0; /* 0x1000 per bank (2-7), unmapped on GB */
|
||||||
descs[9].len = GB_is_cgb(&gameboy[i]) ? 0x6000 : 0; /* 0x1000 per bank (2-7), unmapped on GB */
|
descs[9].select= 0xFFFF0000;
|
||||||
|
|
||||||
descs[10].ptr = descs[8].ptr;
|
descs[10].ptr = GB_get_direct_access(&gameboy[i], GB_DIRECT_ACCESS_IO, &size, &bank);
|
||||||
descs[10].offset = 0x100;
|
descs[10].start = 0xFF00;
|
||||||
descs[10].start = 0xFF00;
|
descs[10].len = 0x0080;
|
||||||
descs[10].select = 0xFFFFFF00;
|
descs[10].select= 0xFFFFFF00;
|
||||||
descs[10].len = 0x0080;
|
|
||||||
|
|
||||||
struct retro_memory_map mmaps;
|
struct retro_memory_map mmaps;
|
||||||
mmaps.descriptors = descs;
|
mmaps.descriptors = descs;
|
||||||
@ -486,7 +487,7 @@ static void check_variables()
|
|||||||
{
|
{
|
||||||
var.key = "sameboy_color_correction_mode";
|
var.key = "sameboy_color_correction_mode";
|
||||||
var.value = NULL;
|
var.value = NULL;
|
||||||
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value && GB_is_cgb(&gameboy[0]))
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
||||||
{
|
{
|
||||||
if (strcmp(var.value, "off") == 0)
|
if (strcmp(var.value, "off") == 0)
|
||||||
GB_set_color_correction_mode(&gameboy[0], GB_COLOR_CORRECTION_DISABLED);
|
GB_set_color_correction_mode(&gameboy[0], GB_COLOR_CORRECTION_DISABLED);
|
||||||
@ -535,12 +536,22 @@ static void check_variables()
|
|||||||
init_for_current_model(0);
|
init_for_current_model(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var.key = "sameboy_border";
|
||||||
|
var.value = NULL;
|
||||||
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
||||||
|
{
|
||||||
|
if (strcmp(var.value, "enabled") == 0)
|
||||||
|
sgb_border = 1;
|
||||||
|
else if (strcmp(var.value, "disabled") == 0)
|
||||||
|
sgb_border = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var.key = "sameboy_color_correction_mode_1";
|
var.key = "sameboy_color_correction_mode_1";
|
||||||
var.value = NULL;
|
var.value = NULL;
|
||||||
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value && GB_is_cgb(&gameboy[0]))
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
||||||
{
|
{
|
||||||
if (strcmp(var.value, "off") == 0)
|
if (strcmp(var.value, "off") == 0)
|
||||||
GB_set_color_correction_mode(&gameboy[0], GB_COLOR_CORRECTION_DISABLED);
|
GB_set_color_correction_mode(&gameboy[0], GB_COLOR_CORRECTION_DISABLED);
|
||||||
@ -554,7 +565,7 @@ static void check_variables()
|
|||||||
|
|
||||||
var.key = "sameboy_color_correction_mode_2";
|
var.key = "sameboy_color_correction_mode_2";
|
||||||
var.value = NULL;
|
var.value = NULL;
|
||||||
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value && GB_is_cgb(&gameboy[1]))
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
||||||
{
|
{
|
||||||
if (strcmp(var.value, "off") == 0)
|
if (strcmp(var.value, "off") == 0)
|
||||||
GB_set_color_correction_mode(&gameboy[1], GB_COLOR_CORRECTION_DISABLED);
|
GB_set_color_correction_mode(&gameboy[1], GB_COLOR_CORRECTION_DISABLED);
|
||||||
@ -880,8 +891,15 @@ void retro_run(void)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (model[0] == MODEL_SGB || model[0] == MODEL_SGB2)
|
if (model[0] == MODEL_SGB || model[0] == MODEL_SGB2) {
|
||||||
video_cb(frame_buf, SGB_VIDEO_WIDTH, SGB_VIDEO_HEIGHT, SGB_VIDEO_WIDTH * sizeof(uint32_t));
|
if (sgb_border == 1)
|
||||||
|
video_cb(frame_buf, SGB_VIDEO_WIDTH, SGB_VIDEO_HEIGHT, SGB_VIDEO_WIDTH * sizeof(uint32_t));
|
||||||
|
else {
|
||||||
|
int crop = SGB_VIDEO_WIDTH * ((SGB_VIDEO_HEIGHT - VIDEO_HEIGHT) / 2) + ((SGB_VIDEO_WIDTH - VIDEO_WIDTH) / 2);
|
||||||
|
|
||||||
|
video_cb(frame_buf + crop, VIDEO_WIDTH, VIDEO_HEIGHT, SGB_VIDEO_WIDTH * sizeof(uint32_t));
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
video_cb(frame_buf, VIDEO_WIDTH, VIDEO_HEIGHT, VIDEO_WIDTH * sizeof(uint32_t));
|
video_cb(frame_buf, VIDEO_WIDTH, VIDEO_HEIGHT, VIDEO_WIDTH * sizeof(uint32_t));
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user