Merge remote-tracking branch 'origin/master' into gtk3

This commit is contained in:
Maximilian Mader 2020-01-30 16:41:59 +01:00
commit 5c9ff6f9e6
Signed by: Max
GPG Key ID: F71D56A3151C4FB3
29 changed files with 634 additions and 179 deletions

View File

@ -74,8 +74,15 @@ enum model {
topMargin:(unsigned) topMargin bottomMargin: (unsigned) bottomMargin
exposure:(unsigned) exposure;
- (void) gotNewSample:(GB_sample_t *)sample;
- (void) loadBootROM:(GB_boot_rom_t)type;
@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)
{
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
{
GB_init(&gb, [self internalModel]);
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_log_callback(&gb, (GB_log_callback_t) consoleLog);
GB_set_input_callback(&gb, (GB_input_callback_t) consoleInput);
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"]);
[self updatePalette];
GB_set_rgb_encode_callback(&gb, rgbEncode);
GB_set_camera_get_pixel_callback(&gb, cameraGetPixel);
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);
}
- (void) loadBootROM
- (void) loadBootROM: (GB_boot_rom_t)type
{
static NSString * const boot_names[] = {@"dmg_boot", @"cgb_boot", @"agb_boot", @"sgb_boot"};
if ([self internalModel] == GB_MODEL_SGB2) {
GB_load_boot_rom(&gb, [[self bootROMPathForName:@"sgb2_boot"] UTF8String]);
}
else {
GB_load_boot_rom(&gb, [[self bootROMPathForName:boot_names[current_model - 1]] UTF8String]);
}
static NSString *const names[] = {
[GB_BOOT_ROM_DMG0] = @"dmg0_boot",
[GB_BOOT_ROM_DMG] = @"dmg_boot",
[GB_BOOT_ROM_MGB] = @"mgb_boot",
[GB_BOOT_ROM_SGB] = @"sgb_boot",
[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
@ -338,8 +372,6 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
current_model = (enum model)[sender tag];
}
[self loadBootROM];
if (!modelsChanging && [sender tag] == MODEL_NONE) {
GB_reset(&gb);
}
@ -452,6 +484,12 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
name:@"GBColorCorrectionChanged"
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(updatePalette)
name:@"GBColorPaletteChanged"
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(updateRewindLength)
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), ^{
@try {
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;
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType: AVMediaTypeVideo];
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice: device error: &error];

View File

@ -7,6 +7,7 @@
@property (strong) IBOutlet NSButton *aspectRatioCheckbox;
@property (strong) IBOutlet NSPopUpButton *highpassFilterPopupButton;
@property (strong) IBOutlet NSPopUpButton *colorCorrectionPopupButton;
@property (nonatomic, strong) IBOutlet NSPopUpButton *colorPalettePopupButton;
@property (strong) IBOutlet NSPopUpButton *rewindPopupButton;
@property (strong) IBOutlet NSButton *configureJoypadButton;
@property (strong) IBOutlet NSButton *skipButton;

View File

@ -14,6 +14,7 @@
NSPopUpButton *_graphicsFilterPopupButton;
NSPopUpButton *_highpassFilterPopupButton;
NSPopUpButton *_colorCorrectionPopupButton;
NSPopUpButton *_colorPalettePopupButton;
NSPopUpButton *_rewindPopupButton;
NSButton *_aspectRatioCheckbox;
NSEventModifierFlags previousModifiers;
@ -31,6 +32,7 @@
@"NearestNeighbor",
@"Bilinear",
@"SmoothBilinear",
@"MonoLCD",
@"LCD",
@"CRT",
@"Scale2x",
@ -84,6 +86,18 @@
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
{
_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
{
[[NSUserDefaults standardUserDefaults] setObject:@([sender selectedTag])

View File

@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDisplayName</key>
<string>SameBoy</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDocumentTypes</key>
@ -70,7 +72,7 @@
<key>LSMinimumSystemVersion</key>
<string>10.9</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2015-2019 Lior Halphon</string>
<string>Copyright © 2015-2020 Lior Halphon</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
@ -116,6 +118,8 @@
</dict>
</dict>
</array>
<key>NSCameraUsageDescription</key>
<string>SameBoy needs to access your camera to emulate the Game Boy Camera</string>
<key>NSSupportsAutomaticGraphicsSwitching</key>
<true/>
</dict>

View File

@ -30,7 +30,7 @@
<h1>SameBoy</h1>
<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
of this software and associated documentation files (the "Software"), to deal

View File

@ -1,8 +1,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>
<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"/>
</dependencies>
<objects>
@ -17,7 +17,7 @@
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<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"/>
<windowCollectionBehavior key="collectionBehavior" fullScreenAuxiliary="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="cgbPopupButton" destination="dlD-sk-SHO" id="4tg-SR-e17"/>
<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="controlsTableView" destination="UDd-IJ-fxX" id="a1D-Md-yXv"/>
<outlet property="delegate" destination="-2" id="ASc-vN-Zbq"/>
@ -78,11 +79,11 @@
<point key="canvasLocation" x="183" y="354"/>
</window>
<customView id="sRK-wO-K6R">
<rect key="frame" x="0.0" y="0.0" width="292" height="166"/>
<rect key="frame" x="0.0" y="0.0" width="292" height="213"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="T91-rh-rRp">
<rect key="frame" x="18" y="129" width="256" height="17"/>
<rect key="frame" x="18" y="176" width="256" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Graphics Filter:" id="pXg-WY-8Q5">
<font key="font" metaFont="system"/>
@ -91,7 +92,7 @@
</textFieldCell>
</textField>
<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"/>
<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"/>
@ -103,7 +104,8 @@
</menuItem>
<menuItem title="Bilinear (Blurry)" id="iDe-si-atu"/>
<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="Scale2x" id="C1I-L2-Up1"/>
<menuItem title="Scale4x" id="uWA-Zp-JY9"/>
@ -127,7 +129,7 @@
</connections>
</popUpButton>
<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"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Color Correction:" id="5Si-hz-EK3">
<font key="font" metaFont="system"/>
@ -136,7 +138,7 @@
</textFieldCell>
</textField>
<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"/>
<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"/>
@ -156,6 +158,36 @@
<action selector="colorCorrectionChanged:" target="QvC-M9-y7g" id="Oq4-B5-nO6"/>
</connections>
</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">
<rect key="frame" x="18" y="18" width="256" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
@ -168,7 +200,7 @@
</connections>
</button>
</subviews>
<point key="canvasLocation" x="-176" y="589"/>
<point key="canvasLocation" x="-176" y="612.5"/>
</customView>
<customView id="ymk-46-SX7">
<rect key="frame" x="0.0" y="0.0" width="292" height="320"/>
@ -397,7 +429,7 @@
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<clipView key="contentView" focusRingType="none" ambiguous="YES" drawsBackground="NO" id="AMs-PO-nid">
<rect key="frame" x="1" y="1" width="238" height="209"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<autoresizingMask key="autoresizingMask"/>
<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">
<rect key="frame" x="0.0" y="0.0" width="238" height="209"/>
@ -441,11 +473,11 @@
</subviews>
<nil key="backgroundColor"/>
</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"/>
<autoresizingMask key="autoresizingMask"/>
</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"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>

View File

@ -48,6 +48,23 @@ bool GB_apu_is_DAC_enabled(GB_gameboy_t *gb, unsigned index)
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)
{
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;
uint8_t bias = agb_bias_for_channel(gb, 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 {
output.right = 0xf * right_volume;
}
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 {
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_NR24: {
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 |= (value & 7) << 8;
if (index == GB_SQUARE_1) {
@ -970,9 +1004,23 @@ void GB_set_sample_rate(GB_gameboy_t *gb, unsigned sample_rate)
if (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);
}
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)
{
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)
{
if (gb->apu_output.rate_set_in_clocks) return;
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 */
}

View File

@ -143,9 +143,12 @@ typedef struct {
GB_double_sample_t highpass_diff;
GB_sample_callback_t sample_callback;
bool rate_set_in_clocks;
} GB_apu_output_t;
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_apu_set_sample_callback(GB_gameboy_t *gb, GB_sample_callback_t callback);
#ifdef GB_INTERNAL

View File

@ -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->f & GB_CARRY_FLAG)? 'C' : '-',
(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_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));

View File

@ -142,9 +142,17 @@ static void display_vblank(GB_gameboy_t *gb)
}
}
else {
uint32_t color = (gb->io_registers[GB_IO_LCDC] & 0x80) && gb->stopped && GB_is_cgb(gb) ?
gb->rgb_encode_callback(gb, 0, 0, 0) :
gb->rgb_encode_callback(gb, 0xFF, 0xFF, 0xFF);
uint32_t color = 0;
if (GB_is_cgb(gb)) {
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++) {
gb->screen[i] = color;
}

119
Core/gb.c
View File

@ -18,6 +18,13 @@
#define GB_rewind_push(...)
#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)
{
char *string = NULL;
@ -566,21 +573,41 @@ void GB_set_async_input_callback(GB_gameboy_t *gb, GB_input_callback_t callback)
#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)
{
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;
update_dmg_palette(gb);
for (unsigned i = 0; i < 32; i++) {
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)
{
return gb->magic == 'SAME';
return gb->magic == state_magic();
}
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_SGB_NTSC: /* 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++) {
gb->ram[i] = GB_random();
if (i & 0x100) {
@ -758,7 +786,8 @@ static void reset_ram(GB_gameboy_t *gb)
case GB_MODEL_DMG_B:
case GB_MODEL_SGB_NTSC: /* 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_NO_SFC:
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_SGB_NTSC: /* 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_NO_SFC:
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_SGB_NTSC: /* 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_NO_SFC: {
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)
{
uint32_t mbc_ram_size = gb->mbc_ram_size;
@ -871,16 +932,7 @@ void GB_reset(GB_gameboy_t *gb)
gb->vram_size = 0x2000;
memset(gb->vram, 0, gb->vram_size);
if (gb->rgb_encode_callback) {
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);
}
update_dmg_palette(gb);
}
reset_ram(gb);
@ -925,7 +977,8 @@ void GB_reset(GB_gameboy_t *gb)
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)
@ -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)
{
if (gb->model == GB_MODEL_SGB_NTSC) {
return SGB_NTSC_FREQUENCY * gb->clock_multiplier;
}
if (gb->model == GB_MODEL_SGB_PAL) {
if (gb->model & GB_MODEL_PAL_BIT) {
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;
}
@ -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;
}
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);
}

View File

@ -50,6 +50,17 @@
#error Unable to detect endianess
#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 {
struct {
uint8_t seconds;
@ -61,7 +72,6 @@ typedef union {
uint8_t data[5];
} GB_rtc_time_t;
typedef enum {
// GB_MODEL_DMG_0 = 0x000,
// GB_MODEL_DMG_A = 0x001,
@ -70,7 +80,9 @@ typedef enum {
GB_MODEL_SGB = 0x004,
GB_MODEL_SGB_NTSC = GB_MODEL_SGB,
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_SGB2 = 0x101,
GB_MODEL_SGB2_NO_SFC = GB_MODEL_SGB2 | GB_MODEL_NO_SFC_BIT,
@ -96,7 +108,7 @@ enum {
enum {
GB_CARRY_FLAG = 16,
GB_HALF_CARRY_FLAG = 32,
GB_SUBSTRACT_FLAG = 64,
GB_SUBTRACT_FLAG = 64,
GB_ZERO_FLAG = 128,
};
@ -217,6 +229,17 @@ typedef enum {
GB_LOG_UNDERLINE_MASK = GB_LOG_DASHED_UNDERLINE | GB_LOG_UNDERLINE
} 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
#define LCDC_PERIOD 70224
#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_hreset_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 {
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
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
struct GB_gameboy_s {
#else
@ -515,6 +535,7 @@ struct GB_gameboy_internal_s {
uint32_t *screen;
uint32_t background_palettes_rgb[0x20];
uint32_t sprite_palettes_rgb[0x20];
const GB_palette_t *dmg_palette;
GB_color_correction_mode_t color_correction_mode;
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_vreset_callback;
GB_read_memory_callback_t read_memory_callback;
GB_boot_rom_load_callback_t boot_rom_load_callback;
/* IR */
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_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);
/* 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 */
void GB_set_serial_transfer_bit_start_callback(GB_gameboy_t *gb, GB_serial_transfer_bit_start_callback_t callback);

View File

@ -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_SGB_NTSC:
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_NO_SFC:
;
@ -421,6 +422,11 @@ static GB_read_function_t * const read_map[] =
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)
{
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_SGB_NTSC:
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_NO_SFC:
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;
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->io_registers[GB_IO_JOYP] = value & 0xF0;
GB_update_joyp(gb);

View File

@ -386,10 +386,6 @@ static void command_ready(GB_gameboy_t *gb)
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_hle_sgb(gb)) {
/* Notify via callback */

View File

@ -318,7 +318,7 @@ static void inc_hr(GB_gameboy_t *gb, uint8_t opcode)
uint8_t register_id;
register_id = ((opcode >> 4) + 1) & 0x03;
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) {
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;
gb->registers[register_id] -= 0x100;
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) {
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;
rr = gb->registers[register_id];
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 -_- */
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;
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) {
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[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) {
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);
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) {
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)
{
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)
{
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)
{
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)
@ -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;
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) {
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);
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) {
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;
value = get_src_value(gb, opcode);
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) {
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);
a = gb->registers[GB_REGISTER_AF] >> 8;
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) {
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);
a = gb->registers[GB_REGISTER_AF] >> 8;
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) {
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;
value = cycle_read_inc_oam_bug(gb, gb->pc++);
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) {
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++);
a = gb->registers[GB_REGISTER_AF] >> 8;
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) {
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++);
a = gb->registers[GB_REGISTER_AF] >> 8;
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) {
gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG;
}

View File

@ -1,6 +1,6 @@
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
of this software and associated documentation files (the "Software"), to deal

View File

@ -34,7 +34,7 @@ ifeq ($(MAKECMDGOALS),)
MAKECMDGOALS := $(DEFAULT)
endif
VERSION := 0.12.2
VERSION := 0.12.3
export VERSION
CONF ?= debug
@ -55,6 +55,11 @@ CC := clang
endif
endif
# Find libraries with pkg-config if available.
ifneq (, $(shell which pkg-config))
PKG_CONFIG := pkg-config
endif
ifeq ($(PLATFORM),windows32)
# To force use of the Unix version instead of the Windows version
MKDIR := $(shell which mkdir)
@ -71,9 +76,11 @@ endif
# Set compilation and linkage flags based on target, platform and configuration
OPEN_DIALOG = OpenDialog/gtk.c
NULL := /dev/null
ifeq ($(PLATFORM),windows32)
OPEN_DIALOG = OpenDialog/windows.c
NULL := NUL
endif
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
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\"
GTK3_LDFLAGS := `pkg-config --libs gio-2.0 gtk+-3.0 epoxy sdl2`
ifeq (,$(PKG_CONFIG))
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
GTK3_CFLAGS += -Wno-unused
endif
ifeq ($(PLATFORM),windows32)
CFLAGS += -IWindows -Drandom=rand
LDFLAGS += -lmsvcrt -lcomdlg32 -lSDL2main -Wl,/MANIFESTFILE:NUL
SDL_LDFLAGS := -lSDL2 -lopengl32
LDFLAGS += -lmsvcrt -lcomdlg32 -luser32 -lSDL2main -Wl,/MANIFESTFILE:NUL
SDL_LDFLAGS := -lSDL2
GL_LDFLAGS := -lopengl32
else
LDFLAGS += -lc -lm -ldl
GTK3_LDFLAGS += -Wl -rdynamic
endif
ifeq ($(PLATFORM),Darwin)
SYSROOT := $(shell xcodebuild -sdk macosx -version Path 2> /dev/null)
CFLAGS += -F/Library/Frameworks
OCFLAGS += -x objective-c -fobjc-arc -Wno-deprecated-declarations -isysroot $(SYSROOT) -mmacosx-version-min=10.9
LDFLAGS += -framework AppKit -framework PreferencePanes -framework Carbon -framework QuartzCore -weak_framework Metal -weak_framework MetalKit
SDL_LDFLAGS := -F/Library/Frameworks -framework SDL2 -framework OpenGL
SYSROOT := $(shell xcodebuild -sdk macosx -version Path 2> $(NULL))
CFLAGS += -F/Library/Frameworks -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 -mmacosx-version-min=10.9
SDL_LDFLAGS := -F/Library/Frameworks -framework SDL2
GL_LDFLAGS := -framework OpenGL
endif
CFLAGS += -Wno-deprecated-declarations
ifeq ($(PLATFORM),windows32)
@ -115,10 +141,16 @@ ifeq ($(CONF),debug)
CFLAGS += -g
else ifeq ($(CONF), release)
CFLAGS += -O3 -DNDEBUG
STRIP := strip
ifeq ($(PLATFORM),Darwin)
LDFLAGS += -Wl,-exported_symbols_list,$(NULL)
STRIP := -@true
endif
ifneq ($(PLATFORM),windows32)
LDFLAGS += -flto
CFLAGS += -flto
endif
else
$(error Invalid value for CONF: $(CONF). Use "debug", "release" or "native_release")
endif
@ -192,6 +224,10 @@ $(OBJ)/gtk3/%.gtk3dep: %
-@$(MKDIR) -p $(dir $@)
$(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: %
-@$(MKDIR) -p $(dir $@)
$(CC) $(CFLAGS) -MT $(OBJ)/$^.o -M $^ -c -o $@
@ -207,6 +243,10 @@ $(OBJ)/gtk3/%.c.o: gtk3/%.c
-@$(MKDIR) -p $(dir $@)
$(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
-@$(MKDIR) -p $(dir $@)
$(CC) $(CFLAGS) -c $< -o $@
@ -248,7 +288,7 @@ $(BIN)/SameBoy.app/Contents/MacOS/SameBoy: $(CORE_OBJECTS) $(COCOA_OBJECTS)
-@$(MKDIR) -p $(dir $@)
$(CC) $^ -o $@ $(LDFLAGS) -framework OpenGL -framework AudioUnit -framework AVFoundation -framework CoreVideo -framework CoreMedia -framework IOKit
ifeq ($(CONF), release)
strip $@
$(STRIP) $@
endif
$(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.
$(BIN)/SameBoy.qlgenerator/Contents/MacOS/SameBoyQL: $(CORE_OBJECTS) $(QUICKLOOK_OBJECTS)
-@$(MKDIR) -p $(dir $@)
$(CC) $^ -o $@ $(LDFLAGS) -bundle -framework Cocoa -framework Quicklook
ifeq ($(CONF), release)
strip -u -r -s QuickLook/exports.sym $@
endif
$(CC) $^ -o $@ $(LDFLAGS) -Wl,-exported_symbols_list,QuickLook/exports.sym -bundle -framework Cocoa -framework Quicklook
# cgb_boot_fast.bin is not a standard boot ROM, we don't expect it to exist in the user-provided
# boot ROM directory.
@ -284,19 +321,19 @@ $(BIN)/SameBoy.qlgenerator/Contents/Resources/cgb_boot_fast.bin: $(BIN)/BootROMs
# Unix versions build only one binary
$(BIN)/SDL/sameboy: $(CORE_OBJECTS) $(SDL_OBJECTS)
-@$(MKDIR) -p $(dir $@)
$(CC) $^ -o $@ $(LDFLAGS) $(SDL_LDFLAGS)
$(CC) $^ -o $@ $(LDFLAGS) $(SDL_LDFLAGS) $(GL_LDFLAGS)
ifeq ($(CONF), release)
strip $@
$(STRIP) $@
endif
# Windows version builds two, one with a console and one without it
$(BIN)/SDL/sameboy.exe: $(CORE_OBJECTS) $(SDL_OBJECTS) $(OBJ)/Windows/resources.o
-@$(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
-@$(MKDIR) -p $(dir $@)
$(CC) $^ -o $@ $(LDFLAGS) $(SDL_LDFLAGS) -Wl,/subsystem:console
$(CC) $^ -o $@ $(LDFLAGS) $(SDL_LDFLAGS) $(GL_LDFLAGS) -Wl,/subsystem:console
ifneq ($(USE_WINDRES),)
$(OBJ)/%.o: %.rc
@ -331,7 +368,7 @@ $(BIN)/tester/sameboy_tester: $(CORE_OBJECTS) $(TESTER_OBJECTS)
-@$(MKDIR) -p $(dir $@)
$(CC) $^ -o $@ $(LDFLAGS)
ifeq ($(CONF), release)
strip $@
$(STRIP) $@
endif
$(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 $@)
rgbasm -i $(OBJ)/BootROMs/ -i BootROMs/ -o $@.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
# Libretro Core (uses its own build system)

View File

@ -47,7 +47,7 @@
<key>CFPlugInUnloadFunction</key>
<string></string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2015-2019 Lior Halphon</string>
<string>Copyright © 2015-2020 Lior Halphon</string>
<key>QLNeedsToBeRunInMainThread</key>
<false/>
<key>QLPreviewHeight</key>

View File

@ -1,5 +1,5 @@
#include <SDL2/SDL.h>
#include <OpenDialog/open_dialog.h>
#include <SDL.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
@ -33,7 +33,7 @@ void render_texture(void *pixels, void *previous)
{
if (renderer) {
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_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)
{
y -= scroll;
unsigned orig_x = x;
unsigned y_offset = (GB_get_screen_height(&gb) - 144) / 2;
while (*string) {
if (*string == '\n') {
x = orig_x;
@ -186,7 +189,7 @@ static void draw_unbordered_text(uint32_t *buffer, unsigned width, unsigned heig
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;
}
@ -297,6 +300,7 @@ static void return_to_root_menu(unsigned index)
{
current_menu = root_menu;
current_selection = 0;
scroll = 0;
}
static void cycle_model(unsigned index)
@ -407,6 +411,7 @@ static void enter_emulation_menu(unsigned index)
{
current_menu = emulation_menu;
current_selection = 0;
scroll = 0;
}
const char *current_scaling_mode(unsigned index)
@ -421,6 +426,12 @@ const char *current_color_correction_mode(unsigned index)
[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)
{
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 {
const char *file_name;
const char *display_name;
@ -471,6 +502,7 @@ struct shader_name {
{"NearestNeighbor", "Nearest Neighbor"},
{"Bilinear", "Bilinear"},
{"SmoothBilinear", "Smooth Bilinear"},
{"MonoLCD", "Monochrome LCD"},
{"LCD", "LCD Display"},
{"CRT", "CRT Display"},
{"Scale2x", "Scale2x"},
@ -556,6 +588,7 @@ static const struct menu_item graphics_menu[] = {
{"Scaling Mode:", cycle_scaling, current_scaling_mode, cycle_scaling_backwards},
{"Scaling Filter:", cycle_filter, current_filter_name, cycle_filter_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},
{"Back", return_to_root_menu},
{NULL,}
@ -565,6 +598,7 @@ static void enter_graphics_menu(unsigned index)
{
current_menu = graphics_menu;
current_selection = 0;
scroll = 0;
}
const char *highpass_filter_string(unsigned index)
@ -601,6 +635,7 @@ static void enter_audio_menu(unsigned index)
{
current_menu = audio_menu;
current_selection = 0;
scroll = 0;
}
static void modify_key(unsigned index)
@ -608,7 +643,6 @@ static void modify_key(unsigned index)
gui_state = WAITING_FOR_KEY;
}
static void enter_controls_menu_2(unsigned index);
static const char *key_name(unsigned index);
static const struct menu_item controls_menu[] = {
@ -620,12 +654,6 @@ static const struct menu_item controls_menu[] = {
{"B:", modify_key, key_name,},
{"Select:", 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,},
{"Rewind:", 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)
{
if (current_menu == controls_menu_2) {
if (index == 0) {
if (index >= 8) {
if (index == 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]);
}
@ -648,12 +676,7 @@ static void enter_controls_menu(unsigned index)
{
current_menu = controls_menu;
current_selection = 0;
}
static void enter_controls_menu_2(unsigned index)
{
current_menu = controls_menu_2;
current_selection = 0;
scroll = 0;
}
static unsigned joypad_index = 0;
@ -744,6 +767,7 @@ static void enter_joypad_menu(unsigned index)
{
current_menu = joypad_menu;
current_selection = 0;
scroll = 0;
}
joypad_button_t get_joypad_button(uint8_t physical_button)
@ -792,6 +816,7 @@ void connect_joypad(void)
void run_gui(bool is_running)
{
SDL_ShowCursor(SDL_ENABLE);
connect_joypad();
/* Draw the background screen */
@ -825,6 +850,7 @@ void run_gui(bool is_running)
bool should_render = true;
current_menu = root_menu = is_running? paused_menu : nonpaused_menu;
current_selection = 0;
scroll = 0;
do {
/* Convert Joypad and mouse events (We only generate down events) */
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 -= 144 / 16;
}
y += scroll;
if (x < 0 || x >= 160 || y < 24) {
continue;
@ -1011,6 +1038,15 @@ void run_gui(bool is_running)
}
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.mod & MODIFIER) {
char *filename = do_open_rom_dialog();
@ -1048,6 +1084,7 @@ void run_gui(bool is_running)
gui_state = SHOWING_DROP_MESSAGE;
}
current_selection = 0;
scroll = 0;
current_menu = root_menu;
should_render = true;
}
@ -1096,12 +1133,12 @@ void run_gui(bool is_running)
should_render = true;
}
else if (gui_state == WAITING_FOR_KEY) {
if (current_menu == controls_menu_2) {
if (current_selection == 0) {
if (current_selection >= 8) {
if (current_selection == 8) {
configuration.keys[8] = event.key.keysym.scancode;
}
else {
configuration.keys_2[current_selection - 1] = event.key.keysym.scancode;
configuration.keys_2[current_selection - 9] = event.key.keysym.scancode;
}
}
else {
@ -1115,6 +1152,7 @@ void run_gui(bool is_running)
if (should_render) {
should_render = false;
rerender:
if (width == 160 && height == 144) {
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);
unsigned i = 0, y = 24;
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) {
char line[25];
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;
}
}
if (i == current_selection) {
if (y > scroll + 144) {
scroll = y - 144;
goto rerender;
}
}
}
break;
case SHOWING_HELP:

View File

@ -1,7 +1,7 @@
#ifndef gui_h
#define gui_h
#include <SDL2/SDL.h>
#include <SDL.h>
#include <Core/gb.h>
#include <stdbool.h>
#include "shader.h"
@ -100,6 +100,9 @@ typedef struct {
SGB_2,
SGB_MAX
} sgb_revision;
/* v0.13 */
uint8_t dmg_palette;
} configuration_t;
extern configuration_t configuration;

67
SDL/main.c Executable file → Normal file
View File

@ -2,7 +2,7 @@
#include <stdio.h>
#include <signal.h>
#include <OpenDialog/open_dialog.h>
#include <SDL2/SDL.h>
#include <SDL.h>
#include <Core/gb.h>
#include "utils.h"
#include "gui.h"
@ -12,6 +12,7 @@
#ifndef _WIN32
#define AUDIO_FREQUENCY 96000
#else
#include <Windows.h>
/* 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.
@ -91,6 +92,26 @@ static const char *end_capturing_logs(bool show_popup, bool should_exit)
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)
{
bool audio_playing = SDL_GetAudioDeviceStatus(device_id) == SDL_AUDIO_PLAYING;
@ -98,11 +119,13 @@ static void open_menu(void)
SDL_PauseAudioDevice(device_id, 1);
}
run_gui(true);
SDL_ShowCursor(SDL_DISABLE);
if (audio_playing) {
SDL_ClearQueuedAudio(device_id);
SDL_PauseAudioDevice(device_id, 0);
}
GB_set_color_correction_mode(&gb, configuration.color_correction_mode);
update_palette();
GB_set_highpass_filter_mode(&gb, configuration.highpass_mode);
}
@ -123,7 +146,7 @@ static void handle_events(GB_gameboy_t *gb)
}
case SDL_WINDOWEVENT: {
if (event.window.event == SDL_WINDOWEVENT_RESIZED) {
if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
update_viewport();
}
break;
@ -273,6 +296,7 @@ static void handle_events(GB_gameboy_t *gb)
else {
SDL_SetWindowFullscreen(window, 0);
}
update_viewport();
}
break;
@ -406,14 +430,12 @@ static bool handle_pending_command(void)
return false;
}
case GB_SDL_RESET_COMMAND:
GB_save_battery(&gb, battery_save_path_ptr);
return true;
case GB_SDL_NO_COMMAND:
return false;
case GB_SDL_RESET_COMMAND:
case GB_SDL_NEW_FILE_COMMAND:
GB_save_battery(&gb, battery_save_path_ptr);
return true;
case GB_SDL_QUIT_COMMAND:
@ -423,8 +445,27 @@ static bool handle_pending_command(void)
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)
{
SDL_ShowCursor(SDL_DISABLE);
GB_model_t model;
pending_command = GB_SDL_NO_COMMAND;
restart:
@ -447,11 +488,13 @@ restart:
else {
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_pixels_output(&gb, active_pixel_buffer);
GB_set_rgb_encode_callback(&gb, rgb_encode);
GB_set_sample_rate(&gb, have_aspec.freq);
GB_set_color_correction_mode(&gb, configuration.color_correction_mode);
update_palette();
GB_set_highpass_filter_mode(&gb, configuration.highpass_mode);
GB_set_rewind_length(&gb, configuration.rewind_length);
GB_set_update_input_hint_callback(&gb, handle_events);
@ -466,15 +509,6 @@ restart:
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();
error = GB_load_rom(&gb, filename);
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)
{
#ifdef _WIN32
SetProcessDPIAware();
#endif
#define str(x) #x
#define xstr(x) str(x)
fprintf(stderr, "SameBoy v" xstr(VERSION) "\n");

View File

@ -1,5 +1,5 @@
#define GL_GLEXT_PROTOTYPES
#include <SDL2/SDL_opengl.h>
#include <SDL_opengl.h>
#ifndef __APPLE__
#define GL_COMPAT_NAME(func) gl_compat_##func

View File

@ -2,8 +2,8 @@
#define opengl_compat_h
#define GL_GLEXT_PROTOTYPES
#include <SDL2/SDL_opengl.h>
#include <SDL2/SDL_video.h>
#include <SDL_opengl.h>
#include <SDL_video.h>
#ifndef __APPLE__
#define GL_COMPAT_NAME(func) gl_compat_##func

View File

@ -1,4 +1,4 @@
#include <SDL2/SDL.h>
#include <SDL.h>
#include <stdio.h>
#include <string.h>
#include "utils.h"

38
Shaders/MonoLCD.fsh Normal file
View 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.

View File

@ -2098,6 +2098,7 @@ static void load_boot_rom(GuiData *gui_data) {
case GB_MODEL_SGB:
case GB_MODEL_SGB_PAL:
case GB_MODEL_SGB_NO_SFC:
case GB_MODEL_SGB_PAL_NO_SFC:
boot_rom_name = "sgb_boot.bin";
break;

View File

@ -92,6 +92,7 @@ static unsigned emulated_devices = 1;
static bool initialized = false;
static unsigned screen_layout = 0;
static unsigned audio_out = 0;
static unsigned sgb_border = 1;
static bool geometry_updated = 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_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_border", "Super Game Boy border; enabled|disabled" },
{ NULL }
};
@ -423,21 +425,20 @@ static void init_for_current_model(unsigned id)
descs[7].len = 0x4000;
descs[7].flags = RETRO_MEMDESC_CONST;
descs[8].ptr = GB_get_direct_access(&gameboy[i], GB_DIRECT_ACCESS_OAM, &size, &bank);
descs[8].start = 0xFE00;
descs[8].select = 0xFFFFFF00;
descs[8].len = 0x00A0;
descs[8].ptr = GB_get_direct_access(&gameboy[i], GB_DIRECT_ACCESS_OAM, &size, &bank);
descs[8].start = 0xFE00;
descs[8].len = 0x00A0;
descs[8].select= 0xFFFFFF00;
descs[9].ptr = descs[2].ptr + 0x2000; /* GBC RAM bank 2 */
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].ptr = descs[2].ptr + 0x2000; /* GBC RAM bank 2 */
descs[9].start = 0x10000;
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].offset = 0x100;
descs[10].start = 0xFF00;
descs[10].select = 0xFFFFFF00;
descs[10].len = 0x0080;
descs[10].ptr = GB_get_direct_access(&gameboy[i], GB_DIRECT_ACCESS_IO, &size, &bank);
descs[10].start = 0xFF00;
descs[10].len = 0x0080;
descs[10].select= 0xFFFFFF00;
struct retro_memory_map mmaps;
mmaps.descriptors = descs;
@ -486,7 +487,7 @@ static void check_variables()
{
var.key = "sameboy_color_correction_mode";
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)
GB_set_color_correction_mode(&gameboy[0], GB_COLOR_CORRECTION_DISABLED);
@ -535,12 +536,22 @@ static void check_variables()
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
{
var.key = "sameboy_color_correction_mode_1";
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)
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.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)
GB_set_color_correction_mode(&gameboy[1], GB_COLOR_CORRECTION_DISABLED);
@ -880,8 +891,15 @@ void retro_run(void)
}
else
{
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 (model[0] == MODEL_SGB || model[0] == MODEL_SGB2) {
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
video_cb(frame_buf, VIDEO_WIDTH, VIDEO_HEIGHT, VIDEO_WIDTH * sizeof(uint32_t));
}