From 163a5ea20c061fe66452017ef8f1b613af3f352f Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Wed, 29 Jan 2020 14:19:11 +0200 Subject: [PATCH] Add DMG color palettes (Cocoa) --- Cocoa/Document.m | 28 ++++++++++++++++++++ Cocoa/GBPreferencesWindow.h | 1 + Cocoa/GBPreferencesWindow.m | 21 +++++++++++++++ Cocoa/Preferences.xib | 43 ++++++++++++++++++++++++++----- Core/display.c | 14 +++++++--- Core/gb.c | 51 ++++++++++++++++++++++--------------- Core/gb.h | 15 ++++++++++- 7 files changed, 143 insertions(+), 30 deletions(-) diff --git a/Cocoa/Document.m b/Cocoa/Document.m index 85dd728..0c40776 100644 --- a/Cocoa/Document.m +++ b/Cocoa/Document.m @@ -184,6 +184,27 @@ 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]); @@ -193,6 +214,7 @@ static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample) 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); @@ -452,6 +474,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" diff --git a/Cocoa/GBPreferencesWindow.h b/Cocoa/GBPreferencesWindow.h index 90eee54..b34aafa 100644 --- a/Cocoa/GBPreferencesWindow.h +++ b/Cocoa/GBPreferencesWindow.h @@ -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; diff --git a/Cocoa/GBPreferencesWindow.m b/Cocoa/GBPreferencesWindow.m index ecf0311..8d3e73d 100644 --- a/Cocoa/GBPreferencesWindow.m +++ b/Cocoa/GBPreferencesWindow.m @@ -14,6 +14,7 @@ NSPopUpButton *_graphicsFilterPopupButton; NSPopUpButton *_highpassFilterPopupButton; NSPopUpButton *_colorCorrectionPopupButton; + NSPopUpButton *_colorPalettePopupButton; NSPopUpButton *_rewindPopupButton; NSButton *_aspectRatioCheckbox; NSEventModifierFlags previousModifiers; @@ -84,6 +85,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 +212,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]) diff --git a/Cocoa/Preferences.xib b/Cocoa/Preferences.xib index 8278ee1..2c05a64 100644 --- a/Cocoa/Preferences.xib +++ b/Cocoa/Preferences.xib @@ -63,6 +63,7 @@ + @@ -78,11 +79,11 @@ - + - + @@ -91,7 +92,7 @@ - + @@ -127,7 +128,7 @@ - + @@ -136,7 +137,7 @@ - + @@ -156,6 +157,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + diff --git a/Core/display.c b/Core/display.c index 5c1935c..f0e2ff2 100644 --- a/Core/display.c +++ b/Core/display.c @@ -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; } diff --git a/Core/gb.c b/Core/gb.c index f29d400..d019fc4 100644 --- a/Core/gb.c +++ b/Core/gb.c @@ -573,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); @@ -882,16 +902,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); diff --git a/Core/gb.h b/Core/gb.h index c27b6c6..7831cb7 100644 --- a/Core/gb.h +++ b/Core/gb.h @@ -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, @@ -513,6 +523,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]; @@ -696,6 +707,8 @@ 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); +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); void GB_set_serial_transfer_bit_end_callback(GB_gameboy_t *gb, GB_serial_transfer_bit_end_callback_t callback);