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);