From 555835549a6239fda6051873a5363a20ee9b93dc Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sat, 12 Dec 2020 20:35:18 +0200 Subject: [PATCH 01/21] More accurate pausing behavior, including revision differences --- Core/apu.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Core/apu.c b/Core/apu.c index fdd21db..e37b265 100644 --- a/Core/apu.c +++ b/Core/apu.c @@ -474,7 +474,8 @@ void GB_apu_run(GB_gameboy_t *gb) gb->apu.noise_channel.alignment += cycles; if (gb->apu.square_sweep_calculate_countdown && - ((gb->io_registers[GB_IO_NR10] & 7) || gb->apu.square_sweep_calculate_countdown <= 7)) { // Calculation is paused if the lower bits + ((gb->io_registers[GB_IO_NR10] & 7) || + gb->apu.square_sweep_calculate_countdown <= (gb->model > GB_MODEL_CGB_C? 3 : 1))) { // Calculation is paused if the lower bits if (gb->apu.square_sweep_calculate_countdown > cycles) { gb->apu.square_sweep_calculate_countdown -= cycles; } @@ -823,7 +824,10 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value) gb->apu.shadow_sweep_sample_length = 0; if (gb->io_registers[GB_IO_NR10] & 7) { /* APU bug: if shift is nonzero, overflow check also occurs on trigger */ - gb->apu.square_sweep_calculate_countdown = (gb->io_registers[GB_IO_NR10] & 0x7) * 2 + 7 - gb->apu.lf_div; + gb->apu.square_sweep_calculate_countdown = (gb->io_registers[GB_IO_NR10] & 0x7) * 2 + 5 - gb->apu.lf_div; + if (gb->model > GB_MODEL_CGB_C) { + gb->apu.square_sweep_calculate_countdown += 2; + } gb->apu.sweep_length_addend = gb->apu.square_channels[GB_SQUARE_1].sample_length; gb->apu.sweep_length_addend >>= (gb->io_registers[GB_IO_NR10] & 7); } From db483ce95f31655fec74ddc53495d78f33903c77 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sat, 12 Dec 2020 20:40:35 +0200 Subject: [PATCH 02/21] Warn about potential odd-mode triggers --- Core/sm83_cpu.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Core/sm83_cpu.c b/Core/sm83_cpu.c index 7107ed1..5631853 100644 --- a/Core/sm83_cpu.c +++ b/Core/sm83_cpu.c @@ -351,6 +351,9 @@ static void leave_stop_mode(GB_gameboy_t *gb) static void stop(GB_gameboy_t *gb, uint8_t opcode) { if (gb->io_registers[GB_IO_KEY1] & 0x1) { + if (gb->cgb_double_speed && gb->io_registers[GB_IO_LCDC] & 0x80) { + GB_log(gb, "Returning from double speed mode while the PPU is on may trigger odd-mode\n"); + } flush_pending_cycles(gb); bool needs_alignment = false; From 7a3ebb708c528f3e1c069f03bc1a185b1b02744b Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sat, 12 Dec 2020 22:55:14 +0200 Subject: [PATCH 03/21] LCDC write timing regression fix --- Core/sm83_cpu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Core/sm83_cpu.c b/Core/sm83_cpu.c index 5631853..c93de07 100644 --- a/Core/sm83_cpu.c +++ b/Core/sm83_cpu.c @@ -249,6 +249,7 @@ static void cycle_write(GB_gameboy_t *gb, uint16_t addr, uint8_t value) // Todo: This is difference is because my timing is off in one of the models if (gb->model > GB_MODEL_CGB_C) { GB_advance_cycles(gb, gb->pending_cycles); + GB_write_memory(gb, addr, value ^ 0x10); // Write with the old TILE_SET first gb->tile_sel_glitch = true; GB_advance_cycles(gb, 1); gb->tile_sel_glitch = false; @@ -257,6 +258,7 @@ static void cycle_write(GB_gameboy_t *gb, uint16_t addr, uint8_t value) } else { GB_advance_cycles(gb, gb->pending_cycles - 1); + GB_write_memory(gb, addr, value ^ 0x10); // Write with the old TILE_SET first gb->tile_sel_glitch = true; GB_advance_cycles(gb, 1); gb->tile_sel_glitch = false; From 8f64f49c3be597f57377e4926ca2a61f2848c8c9 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Wed, 23 Dec 2020 23:49:57 +0200 Subject: [PATCH 04/21] More accurate emulation of window timing, actual correct fix of #123 --- Core/display.c | 44 +++++++++++++++++++++----------------------- Core/sm83_cpu.c | 5 ++--- 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/Core/display.c b/Core/display.c index c876599..5627678 100644 --- a/Core/display.c +++ b/Core/display.c @@ -821,7 +821,7 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles) GB_STATE(gb, display, 28); GB_STATE(gb, display, 29); GB_STATE(gb, display, 30); - // GB_STATE(gb, display, 31); + GB_STATE(gb, display, 31); GB_STATE(gb, display, 32); GB_STATE(gb, display, 33); GB_STATE(gb, display, 34); @@ -853,13 +853,7 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles) /* Handle mode 2 on the very first line 0 */ gb->current_line = 0; gb->window_y = -1; - /* Todo: verify timings */ - if (gb->io_registers[GB_IO_WY] == 0) { - gb->wy_triggered = true; - } - else { - gb->wy_triggered = false; - } + gb->wy_triggered = false; gb->ly_for_comparison = 0; gb->io_registers[GB_IO_STAT] &= ~3; @@ -910,11 +904,6 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles) /* Lines 0 - 143 */ gb->window_y = -1; for (; gb->current_line < LINES; gb->current_line++) { - /* Todo: verify timings */ - if ((gb->io_registers[GB_IO_WY] == gb->current_line || - (gb->current_line != 0 && gb->io_registers[GB_IO_WY] == gb->current_line - 1))) { - gb->wy_triggered = true; - } gb->oam_write_blocked = GB_is_cgb(gb) && !gb->cgb_double_speed; gb->accessed_oam_row = 0; @@ -994,6 +983,12 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles) gb->cycles_for_line += 2; GB_SLEEP(gb, display, 32, 2); mode_3_start: + /* Todo: verify timings */ + if ((gb->io_registers[GB_IO_LCDC] & 0x20) && + (gb->io_registers[GB_IO_WY] == 0) && + gb->current_line == 0) { + gb->wy_triggered = true; + } fifo_clear(&gb->bg_fifo); fifo_clear(&gb->oam_fifo); @@ -1021,7 +1016,7 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles) bool should_activate_window = false; if (gb->io_registers[GB_IO_WX] == 0) { static const uint8_t scx_to_wx0_comparisons[] = {-7, -9, -10, -11, -12, -13, -14, -14}; - if (gb->position_in_line == scx_to_wx0_comparisons[gb->io_registers[GB_IO_SCX] & 7] && !GB_is_cgb(gb)) { + if (gb->position_in_line == scx_to_wx0_comparisons[gb->io_registers[GB_IO_SCX] & 7]) { should_activate_window = true; } } @@ -1243,7 +1238,17 @@ abort_fetching_object: if (gb->hdma_on_hblank) { gb->hdma_starting = true; } - GB_SLEEP(gb, display, 11, LINE_LENGTH - gb->cycles_for_line); + GB_SLEEP(gb, display, 11, LINE_LENGTH - gb->cycles_for_line - 2); + /* + TODO: Verify double speed timing + TODO: Line 0 behaves differently when enabling the window during the transition between mode 2 to 3 + */ + if ((gb->io_registers[GB_IO_LCDC] & 0x20) && + (gb->io_registers[GB_IO_WY] == gb->current_line || + gb->io_registers[GB_IO_WY] == gb->current_line + 1)) { + gb->wy_triggered = true; + } + GB_SLEEP(gb, display, 31, 2); gb->mode_for_interrupt = 2; // Todo: unverified timing @@ -1337,14 +1342,7 @@ abort_fetching_object: gb->current_line = 0; - /* Todo: verify timings */ - if ((gb->io_registers[GB_IO_LCDC] & 0x20) && - (gb->io_registers[GB_IO_WY] == 0)) { - gb->wy_triggered = true; - } - else { - gb->wy_triggered = false; - } + gb->wy_triggered = false; // TODO: not the correct timing gb->current_lcd_line = 0; diff --git a/Core/sm83_cpu.c b/Core/sm83_cpu.c index c93de07..e423fba 100644 --- a/Core/sm83_cpu.c +++ b/Core/sm83_cpu.c @@ -380,9 +380,8 @@ static void stop(GB_gameboy_t *gb, uint8_t opcode) else { GB_timing_sync(gb); if ((gb->io_registers[GB_IO_JOYP] & 0xF) != 0xF) { - /* HW Bug? When STOP is executed while a button is down, the CPU halts forever - yet the other hardware keeps running. */ - gb->interrupt_enable = 0; + /* TODO: HW Bug? When STOP is executed while a button is down, the CPU enters halt + mode instead. Fine details not confirmed yet. */ gb->halted = true; } else { From aa2bdf2a1c2e1e004703b6e8bb61cb1cd7e257fc Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Wed, 23 Dec 2020 23:50:19 +0200 Subject: [PATCH 05/21] Better support for non-QWERTY Latin layouts --- SDL/gui.c | 4 ++-- SDL/gui.h | 9 +++++++++ SDL/main.c | 3 +-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/SDL/gui.c b/SDL/gui.c index 62656e8..3848d15 100644 --- a/SDL/gui.c +++ b/SDL/gui.c @@ -1206,7 +1206,7 @@ void run_gui(bool is_running) } case SDL_KEYDOWN: - if (event.key.keysym.scancode == SDL_SCANCODE_F && event.key.keysym.mod & MODIFIER) { + if (event_hotkey_code(&event) == SDL_SCANCODE_F && event.key.keysym.mod & MODIFIER) { if ((SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN_DESKTOP) == false) { SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP); } @@ -1215,7 +1215,7 @@ void run_gui(bool is_running) } update_viewport(); } - if (event.key.keysym.scancode == SDL_SCANCODE_O) { + if (event_hotkey_code(&event) == SDL_SCANCODE_O) { if (event.key.keysym.mod & MODIFIER) { char *filename = do_open_rom_dialog(); if (filename) { diff --git a/SDL/gui.h b/SDL/gui.h index c950907..f55464d 100644 --- a/SDL/gui.h +++ b/SDL/gui.h @@ -122,4 +122,13 @@ void connect_joypad(void); joypad_button_t get_joypad_button(uint8_t physical_button); joypad_axis_t get_joypad_axis(uint8_t physical_axis); +static SDL_Scancode event_hotkey_code(SDL_Event *event) +{ + if (event->key.keysym.sym >= SDLK_a && event->key.keysym.sym < SDLK_z) { + return SDL_SCANCODE_A + event->key.keysym.sym - SDLK_a; + } + + return event->key.keysym.scancode; +} + #endif diff --git a/SDL/main.c b/SDL/main.c index e79d0b3..45d016d 100644 --- a/SDL/main.c +++ b/SDL/main.c @@ -233,7 +233,7 @@ static void handle_events(GB_gameboy_t *gb) }; case SDL_KEYDOWN: - switch (event.key.keysym.scancode) { + switch (event_hotkey_code(&event)) { case SDL_SCANCODE_ESCAPE: { open_menu(); break; @@ -241,7 +241,6 @@ static void handle_events(GB_gameboy_t *gb) case SDL_SCANCODE_C: if (event.type == SDL_KEYDOWN && (event.key.keysym.mod & KMOD_CTRL)) { GB_debugger_break(gb); - } break; From 66f62d696c9664121887de43c33d234c17f17e63 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Thu, 24 Dec 2020 20:50:47 +0200 Subject: [PATCH 06/21] More window fixes --- Core/display.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Core/display.c b/Core/display.c index 5627678..031b23d 100644 --- a/Core/display.c +++ b/Core/display.c @@ -983,10 +983,9 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles) gb->cycles_for_line += 2; GB_SLEEP(gb, display, 32, 2); mode_3_start: - /* Todo: verify timings */ + /* TODO: Timing seems incorrect, might need an access conflict handling. */ if ((gb->io_registers[GB_IO_LCDC] & 0x20) && - (gb->io_registers[GB_IO_WY] == 0) && - gb->current_line == 0) { + gb->io_registers[GB_IO_WY] == gb->current_line) { gb->wy_triggered = true; } @@ -1241,11 +1240,10 @@ abort_fetching_object: GB_SLEEP(gb, display, 11, LINE_LENGTH - gb->cycles_for_line - 2); /* TODO: Verify double speed timing - TODO: Line 0 behaves differently when enabling the window during the transition between mode 2 to 3 + TODO: Timing differs on a DMG */ if ((gb->io_registers[GB_IO_LCDC] & 0x20) && - (gb->io_registers[GB_IO_WY] == gb->current_line || - gb->io_registers[GB_IO_WY] == gb->current_line + 1)) { + (gb->io_registers[GB_IO_WY] == gb->current_line)) { gb->wy_triggered = true; } GB_SLEEP(gb, display, 31, 2); From b5a611c5db46d6a0649d04d24d8d6339200f9ca1 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Thu, 24 Dec 2020 23:17:20 +0200 Subject: [PATCH 07/21] More accurate color correction curves --- Core/display.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/display.c b/Core/display.c index 031b23d..f5e81a8 100644 --- a/Core/display.c +++ b/Core/display.c @@ -215,12 +215,12 @@ static inline uint8_t scale_channel(uint8_t x) static inline uint8_t scale_channel_with_curve(uint8_t x) { - return (uint8_t[]){0,5,8,11,16,22,28,36,43,51,59,67,77,87,97,107,119,130,141,153,166,177,188,200,209,221,230,238,245,249,252,255}[x]; + return (uint8_t[]){0,6,12,20,28,36,45,56,66,76,88,100,113,125,137,149,161,172,182,192,202,210,218,225,232,238,243,247,250,252,254,255}[x]; } static inline uint8_t scale_channel_with_curve_agb(uint8_t x) { - return (uint8_t[]){0,2,5,10,15,20,26,32,38,45,52,60,68,76,84,92,101,110,119,128,138,148,158,168,178,189,199,210,221,232,244,255}[x]; + return (uint8_t[]){0,3,8,14,20,26,33,40,47,54,62,70,78,86,94,103,112,120,129,138,147,157,166,176,185,195,205,215,225,235,245,255}[x]; } static inline uint8_t scale_channel_with_curve_sgb(uint8_t x) From 159d9d0348bbc14a71a59180f3c5199eb278dd07 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Fri, 25 Dec 2020 14:14:17 +0200 Subject: [PATCH 08/21] Color temperature control --- Cocoa/Document.m | 13 ++++++++++ Cocoa/GBPreferencesWindow.h | 2 +- Cocoa/GBPreferencesWindow.m | 20 +++++++++++++++ Cocoa/Preferences.xib | 5 ++-- Core/display.c | 51 ++++++++++++++++++++++++++++++++----- Core/display.h | 1 + Core/gb.h | 1 + SDL/gui.c | 43 +++++++++++++++++++++++++++++++ SDL/gui.h | 4 +++ SDL/main.c | 3 +++ 10 files changed, 134 insertions(+), 9 deletions(-) diff --git a/Cocoa/Document.m b/Cocoa/Document.m index ea7ef49..a354e03 100644 --- a/Cocoa/Document.m +++ b/Cocoa/Document.m @@ -689,6 +689,11 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency) name:@"GBColorCorrectionChanged" object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(updateLightTemperature) + name:@"GBLightTemperatureChanged" + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateFrameBlendingMode) name:@"GBFrameBlendingModeChanged" @@ -1835,6 +1840,14 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency) } } +- (void) updateLightTemperature +{ + if (GB_is_inited(&gb)) { + GB_set_light_temperature(&gb, [[NSUserDefaults standardUserDefaults] doubleForKey:@"GBLightTemperature"]); + } +} + + - (void) updateFrameBlendingMode { self.view.frameBlendingMode = (GB_frame_blending_mode_t) [[NSUserDefaults standardUserDefaults] integerForKey:@"GBFrameBlendingMode"]; diff --git a/Cocoa/GBPreferencesWindow.h b/Cocoa/GBPreferencesWindow.h index ee697a8..f0b7506 100644 --- a/Cocoa/GBPreferencesWindow.h +++ b/Cocoa/GBPreferencesWindow.h @@ -17,7 +17,7 @@ @property (strong) IBOutlet NSMenuItem *bootROMsFolderItem; @property (strong) IBOutlet NSPopUpButtonCell *bootROMsButton; @property (strong) IBOutlet NSPopUpButton *rumbleModePopupButton; - +@property (weak) IBOutlet NSSlider *temperatureSlider; @property (weak) IBOutlet NSPopUpButton *dmgPopupButton; @property (weak) IBOutlet NSPopUpButton *sgbPopupButton; @property (weak) IBOutlet NSPopUpButton *cgbPopupButton; diff --git a/Cocoa/GBPreferencesWindow.m b/Cocoa/GBPreferencesWindow.m index 491f0c0..dd13ca1 100644 --- a/Cocoa/GBPreferencesWindow.m +++ b/Cocoa/GBPreferencesWindow.m @@ -26,6 +26,7 @@ NSPopUpButton *_dmgPopupButton, *_sgbPopupButton, *_cgbPopupButton; NSPopUpButton *_preferredJoypadButton; NSPopUpButton *_rumbleModePopupButton; + NSSlider *_temperatureSlider; } + (NSArray *)filterList @@ -91,11 +92,23 @@ [_colorCorrectionPopupButton selectItemAtIndex:mode]; } + - (NSPopUpButton *)colorCorrectionPopupButton { return _colorCorrectionPopupButton; } +- (void)setTemperatureSlider:(NSSlider *)temperatureSlider +{ + _temperatureSlider = temperatureSlider; + [temperatureSlider setDoubleValue:[[NSUserDefaults standardUserDefaults] doubleForKey:@"GBLightTemperature"] * 256]; + temperatureSlider.continuous = YES; +} + +- (NSSlider *)temperatureSlider +{ + return _temperatureSlider; +} - (void)setFrameBlendingModePopupButton:(NSPopUpButton *)frameBlendingModePopupButton { _frameBlendingModePopupButton = frameBlendingModePopupButton; @@ -284,6 +297,13 @@ [[NSNotificationCenter defaultCenter] postNotificationName:@"GBColorCorrectionChanged" object:nil]; } +- (IBAction)lightTemperatureChanged:(id)sender +{ + [[NSUserDefaults standardUserDefaults] setObject:@([sender doubleValue] / 256.0) + forKey:@"GBLightTemperature"]; + [[NSNotificationCenter defaultCenter] postNotificationName:@"GBLightTemperatureChanged" object:nil]; +} + - (IBAction)franeBlendingModeChanged:(id)sender { [[NSUserDefaults standardUserDefaults] setObject:@([sender indexOfSelectedItem]) diff --git a/Cocoa/Preferences.xib b/Cocoa/Preferences.xib index 99c6543..73eb0ab 100644 --- a/Cocoa/Preferences.xib +++ b/Cocoa/Preferences.xib @@ -79,15 +79,16 @@ + - + - + diff --git a/Core/display.c b/Core/display.c index f5e81a8..6ac6be8 100644 --- a/Core/display.c +++ b/Core/display.c @@ -2,6 +2,7 @@ #include #include #include +#include #include "gb.h" /* FIFO functions */ @@ -208,6 +209,26 @@ static void display_vblank(GB_gameboy_t *gb) GB_timing_sync(gb); } +static inline void temperature_tint(double temperature, double *r, double *g, double *b) +{ + if (temperature >= 0) { + *r = 1; + *g = pow(1 - temperature, 0.375); + if (temperature >= 0.75) { + *b = 0; + } + else { + *b = sqrt(0.75 - temperature); + } + } + else { + *b = 1; + double squared = pow(temperature, 2); + *g = 0.125 * squared + 0.3 * temperature + 1.0; + *r = 0.21875 * squared + 0.5 * temperature + 1.0; + } +} + static inline uint8_t scale_channel(uint8_t x) { return (x << 3) | (x >> 2); @@ -240,13 +261,12 @@ uint32_t GB_convert_rgb15(GB_gameboy_t *gb, uint16_t color, bool for_border) g = scale_channel(g); b = scale_channel(b); } + else if (GB_is_sgb(gb) || for_border) { + r = scale_channel_with_curve_sgb(r); + g = scale_channel_with_curve_sgb(g); + b = scale_channel_with_curve_sgb(b); + } else { - if (GB_is_sgb(gb) || for_border) { - return gb->rgb_encode_callback(gb, - scale_channel_with_curve_sgb(r), - scale_channel_with_curve_sgb(g), - scale_channel_with_curve_sgb(b)); - } bool agb = gb->model == GB_MODEL_AGB; r = agb? scale_channel_with_curve_agb(r) : scale_channel_with_curve(r); g = agb? scale_channel_with_curve_agb(g) : scale_channel_with_curve(g); @@ -301,6 +321,14 @@ uint32_t GB_convert_rgb15(GB_gameboy_t *gb, uint16_t color, bool for_border) } } + if (gb->light_temperature) { + double light_r, light_g, light_b; + temperature_tint(gb->light_temperature, &light_r, &light_g, &light_b); + r = round(light_r * r); + g = round(light_g * g); + b = round(light_b * b); + } + return gb->rgb_encode_callback(gb, r, g, b); } @@ -324,6 +352,17 @@ void GB_set_color_correction_mode(GB_gameboy_t *gb, GB_color_correction_mode_t m } } +void GB_set_light_temperature(GB_gameboy_t *gb, double temperature) +{ + gb->light_temperature = temperature; + if (GB_is_cgb(gb)) { + for (unsigned i = 0; i < 32; i++) { + GB_palette_changed(gb, false, i * 2); + GB_palette_changed(gb, true, i * 2); + } + } +} + /* STAT interrupt is implemented based on this finding: http://board.byuu.org/phpbb3/viewtopic.php?p=25527#p25531 diff --git a/Core/display.h b/Core/display.h index 5bdeba8..fdaf172 100644 --- a/Core/display.h +++ b/Core/display.h @@ -58,5 +58,6 @@ void GB_draw_tilemap(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette uint8_t GB_get_oam_info(GB_gameboy_t *gb, GB_oam_info_t *dest, uint8_t *sprite_height); uint32_t GB_convert_rgb15(GB_gameboy_t *gb, uint16_t color, bool for_border); void GB_set_color_correction_mode(GB_gameboy_t *gb, GB_color_correction_mode_t mode); +void GB_set_light_temperature(GB_gameboy_t *gb, double temperature); bool GB_is_odd_frame(GB_gameboy_t *gb); #endif /* display_h */ diff --git a/Core/gb.h b/Core/gb.h index ed736e0..c2e96db 100644 --- a/Core/gb.h +++ b/Core/gb.h @@ -573,6 +573,7 @@ struct GB_gameboy_internal_s { uint32_t sprite_palettes_rgb[0x20]; const GB_palette_t *dmg_palette; GB_color_correction_mode_t color_correction_mode; + double light_temperature; bool keys[4][GB_KEY_MAX]; GB_border_mode_t border_mode; GB_sgb_border_t borrowed_border; diff --git a/SDL/gui.c b/SDL/gui.c index 3848d15..63e42d8 100644 --- a/SDL/gui.c +++ b/SDL/gui.c @@ -110,6 +110,7 @@ configuration_t configuration = .volume = 100, .rumble_mode = GB_RUMBLE_ALL_GAMES, .default_scale = 2, + .color_temperature = 10, }; @@ -453,6 +454,33 @@ const char *current_color_correction_mode(unsigned index) [configuration.color_correction_mode]; } +const char *current_color_temperature(unsigned index) +{ + return (const char *[]){"12000K", + "11450K", + "10900K", + "10350K", + "9800K", + "9250K", + "8700K", + "8150K", + "7600K", + "7050K", + "6500K (White)", + "5950K", + "5400K", + "4850K", + "4300K", + "3750K", + "3200K", + "2650K", + "2100K", + "1550K", + "1000K"} + [configuration.color_temperature]; +} + + const char *current_palette(unsigned index) { return (const char *[]){"Greyscale", "Lime (Game Boy)", "Olive (Pocket)", "Teal (Light)"} @@ -533,6 +561,20 @@ static void cycle_color_correction_backwards(unsigned index) } } +static void decrease_color_temperature(unsigned index) +{ + if (configuration.color_temperature < 20) { + configuration.color_temperature++; + } +} + +static void increase_color_temperature(unsigned index) +{ + if (configuration.color_temperature > 0) { + configuration.color_temperature--; + } +} + static void cycle_palette(unsigned index) { if (configuration.dmg_palette == 3) { @@ -684,6 +726,7 @@ static const struct menu_item graphics_menu[] = { {"Default Window Scale:", cycle_default_scale, current_default_scale, cycle_default_scale_backwards}, {"Scaling Filter:", cycle_filter, current_filter_name, cycle_filter_backwards}, {"Color Correction:", cycle_color_correction, current_color_correction_mode, cycle_color_correction_backwards}, + {"Ambient Light:", decrease_color_temperature, current_color_temperature, increase_color_temperature}, {"Frame Blending:", cycle_blending_mode, blending_mode_string, cycle_blending_mode_backwards}, {"Mono Palette:", cycle_palette, current_palette, cycle_palette_backwards}, {"Display Border:", cycle_border_mode, current_border_mode, cycle_border_mode_backwards}, diff --git a/SDL/gui.h b/SDL/gui.h index f55464d..84930e0 100644 --- a/SDL/gui.h +++ b/SDL/gui.h @@ -110,6 +110,10 @@ typedef struct { GB_rumble_mode_t rumble_mode; uint8_t default_scale; + + /* v0.14 */ + unsigned padding; + uint8_t color_temperature; } configuration_t; extern configuration_t configuration; diff --git a/SDL/main.c b/SDL/main.c index 45d016d..a20d644 100644 --- a/SDL/main.c +++ b/SDL/main.c @@ -120,6 +120,7 @@ static void open_menu(void) GB_audio_set_paused(false); } GB_set_color_correction_mode(&gb, configuration.color_correction_mode); + GB_set_light_temperature(&gb, (configuration.color_temperature - 10.0) / 10.0); GB_set_border_mode(&gb, configuration.border_mode); update_palette(); GB_set_highpass_filter_mode(&gb, configuration.highpass_mode); @@ -496,6 +497,7 @@ restart: GB_set_rumble_mode(&gb, configuration.rumble_mode); GB_set_sample_rate(&gb, GB_audio_get_frequency()); GB_set_color_correction_mode(&gb, configuration.color_correction_mode); + GB_set_light_temperature(&gb, (configuration.color_temperature - 10.0) / 10.0); update_palette(); if ((unsigned)configuration.border_mode <= GB_BORDER_ALWAYS) { GB_set_border_mode(&gb, configuration.border_mode); @@ -646,6 +648,7 @@ int main(int argc, char **argv) configuration.dmg_palette %= 3; configuration.border_mode %= GB_BORDER_ALWAYS + 1; configuration.rumble_mode %= GB_RUMBLE_ALL_GAMES + 1; + configuration.color_temperature %= 21; } if (configuration.model >= MODEL_MAX) { From 4bbd27735fb6d12ba95deb10437214b2172aeec3 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Fri, 25 Dec 2020 20:40:39 +0200 Subject: [PATCH 09/21] Fix a regression in speed switch timing, reset DIV on speed switch, better odd-mode detection and avoidance --- Core/memory.c | 1 + Core/sm83_cpu.c | 16 ++++++++-------- Core/timing.c | 8 ++++++-- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/Core/memory.c b/Core/memory.c index 40af0e5..cff31b6 100644 --- a/Core/memory.c +++ b/Core/memory.c @@ -895,6 +895,7 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value) if ((value & 0x80) && !(gb->io_registers[GB_IO_LCDC] & 0x80)) { gb->display_cycles = 0; gb->display_state = 0; + gb->double_speed_alignment = 0; if (GB_is_sgb(gb)) { gb->frame_skip_state = GB_FRAMESKIP_SECOND_FRAME_RENDERED; } diff --git a/Core/sm83_cpu.c b/Core/sm83_cpu.c index e423fba..e56040b 100644 --- a/Core/sm83_cpu.c +++ b/Core/sm83_cpu.c @@ -332,6 +332,7 @@ static void nop(GB_gameboy_t *gb, uint8_t opcode) static void enter_stop_mode(GB_gameboy_t *gb) { + GB_write_memory(gb, 0xFF00 + GB_IO_DIV, 0); gb->stopped = true; gb->oam_ppu_blocked = !gb->oam_read_blocked; gb->vram_ppu_blocked = !gb->vram_read_blocked; @@ -340,30 +341,30 @@ static void enter_stop_mode(GB_gameboy_t *gb) static void leave_stop_mode(GB_gameboy_t *gb) { - /* The CPU takes more time to wake up then the other components */ - for (unsigned i = 0x200; i--;) { - GB_advance_cycles(gb, 0x10); - } gb->stopped = false; gb->oam_ppu_blocked = false; gb->vram_ppu_blocked = false; gb->cgb_palettes_ppu_blocked = false; + /* The CPU takes more time to wake up then the other components */ + for (unsigned i = 0x2000; i--;) { + GB_advance_cycles(gb, 0x10); + } + GB_write_memory(gb, 0xFF00 + GB_IO_DIV, 0); } static void stop(GB_gameboy_t *gb, uint8_t opcode) { if (gb->io_registers[GB_IO_KEY1] & 0x1) { - if (gb->cgb_double_speed && gb->io_registers[GB_IO_LCDC] & 0x80) { - GB_log(gb, "Returning from double speed mode while the PPU is on may trigger odd-mode\n"); - } flush_pending_cycles(gb); bool needs_alignment = false; GB_advance_cycles(gb, 0x4); /* Make sure we keep the CPU ticks aligned correctly when returning from double speed mode */ + if (gb->double_speed_alignment & 7) { GB_advance_cycles(gb, 0x4); needs_alignment = true; + GB_log(gb, "ROM triggered PPU odd mode, which is currently not supported. Reverting to even-mode.\n"); } gb->cgb_double_speed ^= true; @@ -388,7 +389,6 @@ static void stop(GB_gameboy_t *gb, uint8_t opcode) enter_stop_mode(gb); } } - /* Todo: is PC being actually read? */ gb->pc++; } diff --git a/Core/timing.c b/Core/timing.c index 44ff8f7..7009d7b 100644 --- a/Core/timing.c +++ b/Core/timing.c @@ -157,7 +157,9 @@ static void GB_set_internal_div_counter(GB_gameboy_t *gb, uint32_t value) static void GB_timers_run(GB_gameboy_t *gb, uint8_t cycles) { if (gb->stopped) { - gb->apu.apu_cycles += 4 << !gb->cgb_double_speed; + if (GB_is_cgb(gb)) { + gb->apu.apu_cycles += 4 << !gb->cgb_double_speed; + } return; } @@ -248,7 +250,9 @@ void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles) } // Not affected by speed boost - gb->double_speed_alignment += cycles; + if (gb->io_registers[GB_IO_LCDC] & 0x80) { + gb->double_speed_alignment += cycles; + } gb->hdma_cycles += cycles; gb->apu_output.sample_cycles += cycles; gb->cycles_since_last_sync += cycles; From 544d39f19d3707af33c889afb02c9a59dbabdf8c Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sat, 26 Dec 2020 00:19:48 +0200 Subject: [PATCH 10/21] Further improvements to STOP timing --- Core/sm83_cpu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Core/sm83_cpu.c b/Core/sm83_cpu.c index e56040b..cf73b31 100644 --- a/Core/sm83_cpu.c +++ b/Core/sm83_cpu.c @@ -346,9 +346,10 @@ static void leave_stop_mode(GB_gameboy_t *gb) gb->vram_ppu_blocked = false; gb->cgb_palettes_ppu_blocked = false; /* The CPU takes more time to wake up then the other components */ - for (unsigned i = 0x2000; i--;) { + for (unsigned i = 0x1FFF; i--;) { GB_advance_cycles(gb, 0x10); } + GB_advance_cycles(gb, gb->cgb_double_speed? 0x10 : 0xF); GB_write_memory(gb, 0xFF00 + GB_IO_DIV, 0); } From 6d5ce6c54d0e6311de3eb1ef5c961e0d80d79488 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sat, 26 Dec 2020 01:45:03 +0200 Subject: [PATCH 11/21] Better scrolling a spacing in the SDL UI --- SDL/gui.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/SDL/gui.c b/SDL/gui.c index 63e42d8..84c13e9 100644 --- a/SDL/gui.c +++ b/SDL/gui.c @@ -211,7 +211,7 @@ static void draw_unbordered_text(uint32_t *buffer, unsigned width, unsigned heig continue; } - if (x > width - GLYPH_WIDTH || y == 0 || y - y_offset > 144 - GLYPH_HEIGHT) { + if (x > width - GLYPH_WIDTH || y == 0 || y - y_offset > 144 - GLYPH_HEIGHT || y <= y_offset) { break; } @@ -1384,9 +1384,17 @@ void run_gui(bool is_running) 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 == 0) { + if (y < scroll) { + scroll = (y - 4) / 12 * 12; + goto rerender; + } + } + else { + if (y < scroll + 24) { + scroll = (y - 24) / 12 * 12; + goto rerender; + } } } if (i == current_selection && i == 0 && scroll != 0) { @@ -1406,14 +1414,20 @@ void run_gui(bool is_running) i == current_selection && !item->value_getter ? DECORATION_SELECTION : DECORATION_NONE); y += 12; if (item->value_getter) { - draw_text_centered(pixels, width, height, y + y_offset, item->value_getter(i), gui_palette_native[3], gui_palette_native[0], + draw_text_centered(pixels, width, height, y + y_offset - 1, item->value_getter(i), gui_palette_native[3], gui_palette_native[0], i == current_selection ? DECORATION_ARROWS : DECORATION_NONE); y += 12; } } if (i == current_selection) { - if (y > scroll + 144) { - scroll = y - 144; + if (item[1].string) { + if (y > scroll + 120) { + scroll = (y - 120) / 12 * 12; + goto rerender; + } + } + else if (y > scroll + 144) { + scroll = (y - 144) / 12 * 12; goto rerender; } } From c471696fbb4efbb31caef6d599293ec5377349e6 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sat, 26 Dec 2020 03:20:53 +0200 Subject: [PATCH 12/21] Scrollbar and mouse wheel support --- SDL/gui.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 68 insertions(+), 3 deletions(-) diff --git a/SDL/gui.c b/SDL/gui.c index 84c13e9..900907c 100644 --- a/SDL/gui.c +++ b/SDL/gui.c @@ -197,7 +197,7 @@ static void draw_char(uint32_t *buffer, unsigned width, unsigned height, unsigne } } -static unsigned scroll = 0; +static signed 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; @@ -262,6 +262,10 @@ struct menu_item { }; static const struct menu_item *current_menu = NULL; static const struct menu_item *root_menu = NULL; +static unsigned menu_height; +static unsigned scrollbar_size; +static bool mouse_scroling = false; + static unsigned current_selection = 0; static enum { @@ -303,6 +307,23 @@ static void open_rom(unsigned index) } } +static void recalculate_menu_height(void) +{ + menu_height = 24; + scrollbar_size = 0; + if (gui_state == SHOWING_MENU) { + for (const struct menu_item *item = current_menu; item->string; item++) { + menu_height += 12; + if (item->backwards_handler) { + menu_height += 12; + } + } + } + if (menu_height > 144) { + scrollbar_size = 144 * 140 / menu_height; + } +} + static const struct menu_item paused_menu[] = { {"Resume", NULL}, {"Open ROM", open_rom}, @@ -323,6 +344,7 @@ static void return_to_root_menu(unsigned index) current_menu = root_menu; current_selection = 0; scroll = 0; + recalculate_menu_height(); } static void cycle_model(unsigned index) @@ -434,6 +456,7 @@ static void enter_emulation_menu(unsigned index) current_menu = emulation_menu; current_selection = 0; scroll = 0; + recalculate_menu_height(); } const char *current_scaling_mode(unsigned index) @@ -739,6 +762,7 @@ static void enter_graphics_menu(unsigned index) current_menu = graphics_menu; current_selection = 0; scroll = 0; + recalculate_menu_height(); } const char *highpass_filter_string(unsigned index) @@ -800,6 +824,7 @@ static void enter_audio_menu(unsigned index) current_menu = audio_menu; current_selection = 0; scroll = 0; + recalculate_menu_height(); } static void modify_key(unsigned index) @@ -841,6 +866,7 @@ static void enter_controls_menu(unsigned index) current_menu = controls_menu; current_selection = 0; scroll = 0; + recalculate_menu_height(); } static unsigned joypad_index = 0; @@ -976,6 +1002,7 @@ static void enter_joypad_menu(unsigned index) current_menu = joypad_menu; current_selection = 0; scroll = 0; + recalculate_menu_height(); } joypad_button_t get_joypad_button(uint8_t physical_button) @@ -1060,6 +1087,7 @@ void run_gui(bool is_running) gui_state = is_running? SHOWING_MENU : SHOWING_DROP_MESSAGE; bool should_render = true; current_menu = root_menu = is_running? paused_menu : nonpaused_menu; + recalculate_menu_height(); current_selection = 0; scroll = 0; do { @@ -1247,6 +1275,23 @@ void run_gui(bool is_running) } break; } + + case SDL_MOUSEWHEEL: { + if (menu_height > 144) { + scroll -= event.wheel.y; + if (scroll < 0) { + scroll = 0; + } + if (scroll >= menu_height - 144) { + scroll = menu_height - 144; + } + + mouse_scroling = true; + should_render = true; + } + break; + } + case SDL_KEYDOWN: if (event_hotkey_code(&event) == SDL_SCANCODE_F && event.key.keysym.mod & MODIFIER) { @@ -1295,18 +1340,22 @@ void run_gui(bool is_running) gui_state = SHOWING_DROP_MESSAGE; } current_selection = 0; + mouse_scroling = false; scroll = 0; current_menu = root_menu; + recalculate_menu_height(); should_render = true; } } else if (gui_state == SHOWING_MENU) { if (event.key.keysym.scancode == SDL_SCANCODE_DOWN && current_menu[current_selection + 1].string) { current_selection++; + mouse_scroling = false; should_render = true; } else if (event.key.keysym.scancode == SDL_SCANCODE_UP && current_selection) { current_selection--; + mouse_scroling = false; should_render = true; } else if (event.key.keysym.scancode == SDL_SCANCODE_RETURN && !current_menu[current_selection].backwards_handler) { @@ -1383,7 +1432,7 @@ 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 (i == current_selection && !mouse_scroling) { if (i == 0) { if (y < scroll) { scroll = (y - 4) / 12 * 12; @@ -1397,7 +1446,7 @@ void run_gui(bool is_running) } } } - if (i == current_selection && i == 0 && scroll != 0) { + if (i == current_selection && i == 0 && scroll != 0 && !mouse_scroling) { scroll = 0; goto rerender; } @@ -1433,6 +1482,22 @@ void run_gui(bool is_running) } } + if (scrollbar_size) { + unsigned scrollbar_offset = (140 - scrollbar_size) * scroll / (menu_height - 144); + if (scrollbar_offset + scrollbar_size > 140) { + scrollbar_offset = 140 - scrollbar_size; + } + for (unsigned y = 0; y < 140; y++) { + uint32_t *pixel = pixels + x_offset + 156 + width * (y + y_offset + 2); + if (y >= scrollbar_offset && y < scrollbar_offset + scrollbar_size) { + pixel[0] = pixel[1]= gui_palette_native[2]; + } + else { + pixel[0] = pixel[1]= gui_palette_native[1]; + } + + } + } break; case SHOWING_HELP: draw_text(pixels, width, height, 2 + x_offset, 2 + y_offset, help[current_help_page], gui_palette_native[3], gui_palette_native[0]); From e1f797c21251c86bf66b1570047eb9a9926d5464 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sat, 26 Dec 2020 13:13:43 +0200 Subject: [PATCH 13/21] Improved scrolling --- SDL/gui.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/SDL/gui.c b/SDL/gui.c index 900907c..5701495 100644 --- a/SDL/gui.c +++ b/SDL/gui.c @@ -177,8 +177,7 @@ static void rescale_window(void) SDL_SetWindowSize(window, GB_get_screen_width(&gb) * configuration.default_scale, GB_get_screen_height(&gb) * configuration.default_scale); } -/* Does NOT check for bounds! */ -static void draw_char(uint32_t *buffer, unsigned width, unsigned height, unsigned char ch, uint32_t color) +static void draw_char(uint32_t *buffer, unsigned width, unsigned height, unsigned char ch, uint32_t color, uint32_t *mask_top, uint32_t *mask_bottom) { if (ch < ' ' || ch > font_max) { ch = '?'; @@ -188,7 +187,7 @@ static void draw_char(uint32_t *buffer, unsigned width, unsigned height, unsigne for (unsigned y = GLYPH_HEIGHT; y--;) { for (unsigned x = GLYPH_WIDTH; x--;) { - if (*(data++)) { + if (*(data++) && buffer >= mask_top && buffer < mask_bottom) { (*buffer) = color; } buffer++; @@ -198,7 +197,7 @@ static void draw_char(uint32_t *buffer, unsigned width, unsigned height, unsigne } static signed 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, signed y, const char *string, uint32_t color) { y -= scroll; unsigned orig_x = x; @@ -211,17 +210,17 @@ static void draw_unbordered_text(uint32_t *buffer, unsigned width, unsigned heig continue; } - if (x > width - GLYPH_WIDTH || y == 0 || y - y_offset > 144 - GLYPH_HEIGHT || y <= y_offset) { + if (x > width - GLYPH_WIDTH) { break; } - draw_char(&buffer[x + width * y], width, height, *string, color); + draw_char(&buffer[(signed)(x + width * y)], width, height, *string, color, &buffer[width * y_offset], &buffer[width * (y_offset + 144)]); x += GLYPH_WIDTH; string++; } } -static void draw_text(uint32_t *buffer, unsigned width, unsigned height, unsigned x, unsigned y, const char *string, uint32_t color, uint32_t border) +static void draw_text(uint32_t *buffer, unsigned width, unsigned height, unsigned x, signed y, const char *string, uint32_t color, uint32_t border) { draw_unbordered_text(buffer, width, height, x - 1, y, string, border); draw_unbordered_text(buffer, width, height, x + 1, y, string, border); @@ -1468,15 +1467,12 @@ void run_gui(bool is_running) y += 12; } } - if (i == current_selection) { - if (item[1].string) { - if (y > scroll + 120) { - scroll = (y - 120) / 12 * 12; - goto rerender; - } - } - else if (y > scroll + 144) { + if (i == current_selection && !mouse_scroling) { + if (y > scroll + 144) { scroll = (y - 144) / 12 * 12; + if (scroll > menu_height - 144) { + scroll = menu_height - 144; + } goto rerender; } } From 7fc59b5cf4e27c05c239ce1d5c1ac083bf58e832 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sat, 26 Dec 2020 15:10:11 +0200 Subject: [PATCH 14/21] Let the SDL port choose a boot ROMs folder --- Makefile | 2 +- OpenDialog/cocoa.m | 18 +++++++++++ OpenDialog/gtk.c | 69 ++++++++++++++++++++++++++++++++++++++++ OpenDialog/open_dialog.h | 2 +- OpenDialog/windows.c | 32 ++++++++++++++++++- SDL/font.c | 20 ++++++++++++ SDL/font.h | 2 ++ SDL/gui.c | 43 ++++++++++++++++++++++++- SDL/gui.h | 1 + SDL/main.c | 16 +++++++--- 10 files changed, 197 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index c3d030a..9d3ca7c 100644 --- a/Makefile +++ b/Makefile @@ -130,7 +130,7 @@ GL_LDFLAGS := $(shell $(PKG_CONFIG) --libs gl || echo -lGL) endif ifeq ($(PLATFORM),windows32) CFLAGS += -IWindows -Drandom=rand --target=i386-pc-windows -LDFLAGS += -lmsvcrt -lcomdlg32 -luser32 -lshell32 -lSDL2main -Wl,/MANIFESTFILE:NUL --target=i386-pc-windows +LDFLAGS += -lmsvcrt -lcomdlg32 -luser32 -lshell32 -lole32 -lSDL2main -Wl,/MANIFESTFILE:NUL --target=i386-pc-windows SDL_LDFLAGS := -lSDL2 GL_LDFLAGS := -lopengl32 else diff --git a/OpenDialog/cocoa.m b/OpenDialog/cocoa.m index 76b9606..aeeb98a 100644 --- a/OpenDialog/cocoa.m +++ b/OpenDialog/cocoa.m @@ -18,3 +18,21 @@ char *do_open_rom_dialog(void) return NULL; } } + +char *do_open_folder_dialog(void) +{ + @autoreleasepool { + NSWindow *key = [NSApp keyWindow]; + NSOpenPanel *dialog = [NSOpenPanel openPanel]; + dialog.title = @"Select Boot ROMs Folder"; + dialog.canChooseDirectories = true; + dialog.canChooseFiles = false; + [dialog runModal]; + [key makeKeyAndOrderFront:nil]; + NSString *ret = [[[dialog URLs] firstObject] path]; + if (ret) { + return strdup(ret.UTF8String); + } + return NULL; + } +} diff --git a/OpenDialog/gtk.c b/OpenDialog/gtk.c index 5b1caa3..378dcb4 100644 --- a/OpenDialog/gtk.c +++ b/OpenDialog/gtk.c @@ -6,6 +6,7 @@ #include #define GTK_FILE_CHOOSER_ACTION_OPEN 0 +#define GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER 2 #define GTK_RESPONSE_ACCEPT -3 #define GTK_RESPONSE_CANCEL -6 @@ -111,3 +112,71 @@ lazy_error: fprintf(stderr, "Failed to display GTK dialog\n"); return NULL; } + +char *do_open_folder_dialog(void) +{ + static void *handle = NULL; + + TRY_DLOPEN("libgtk-3.so"); + TRY_DLOPEN("libgtk-3.so.0"); + TRY_DLOPEN("libgtk-2.so"); + TRY_DLOPEN("libgtk-2.so.0"); + + if (!handle) { + goto lazy_error; + } + + + LAZY(gtk_init_check); + LAZY(gtk_file_chooser_dialog_new); + LAZY(gtk_dialog_run); + LAZY(g_free); + LAZY(gtk_widget_destroy); + LAZY(gtk_file_chooser_get_filename); + LAZY(g_log_set_default_handler); + LAZY(gtk_file_filter_new); + LAZY(gtk_file_filter_add_pattern); + LAZY(gtk_file_filter_set_name); + LAZY(gtk_file_chooser_add_filter); + LAZY(gtk_events_pending); + LAZY(gtk_main_iteration); + + /* Shut up GTK */ + g_log_set_default_handler(nop, NULL); + + gtk_init_check(0, 0); + + + void *dialog = gtk_file_chooser_dialog_new("Select Boot ROMs Folder", + 0, + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, + "_Cancel", GTK_RESPONSE_CANCEL, + "_Open", GTK_RESPONSE_ACCEPT, + NULL ); + + + int res = gtk_dialog_run (dialog); + char *ret = NULL; + + if (res == GTK_RESPONSE_ACCEPT) { + char *filename; + filename = gtk_file_chooser_get_filename(dialog); + ret = strdup(filename); + g_free(filename); + } + + while (gtk_events_pending()) { + gtk_main_iteration(); + } + + gtk_widget_destroy(dialog); + + while (gtk_events_pending()) { + gtk_main_iteration(); + } + return ret; + +lazy_error: + fprintf(stderr, "Failed to display GTK dialog\n"); + return NULL; +} diff --git a/OpenDialog/open_dialog.h b/OpenDialog/open_dialog.h index 85e5721..6d7fb5b 100644 --- a/OpenDialog/open_dialog.h +++ b/OpenDialog/open_dialog.h @@ -2,5 +2,5 @@ #define open_rom_h char *do_open_rom_dialog(void); - +char *do_open_folder_dialog(void); #endif /* open_rom_h */ diff --git a/OpenDialog/windows.c b/OpenDialog/windows.c index 52e281d..e711032 100644 --- a/OpenDialog/windows.c +++ b/OpenDialog/windows.c @@ -1,10 +1,11 @@ #include +#include #include "open_dialog.h" char *do_open_rom_dialog(void) { OPENFILENAMEW dialog; - wchar_t filename[MAX_PATH] = {0}; + static wchar_t filename[MAX_PATH] = {0}; memset(&dialog, 0, sizeof(dialog)); dialog.lStructSize = sizeof(dialog); @@ -25,3 +26,32 @@ char *do_open_rom_dialog(void) return NULL; } + +char *do_open_folder_dialog(void) +{ + + BROWSEINFOW dialog; + memset(&dialog, 0, sizeof(dialog)); + + dialog.ulFlags = BIF_USENEWUI; + dialog.lpszTitle = L"Select Boot ROMs Folder"; + + OleInitialize(NULL); + + LPITEMIDLIST list = SHBrowseForFolderW(&dialog); + static wchar_t filename[MAX_PATH] = {0}; + + if (list) { + if (!SHGetPathFromIDListW(list, filename)) { + OleUninitialize(); + return NULL; + } + char *ret = malloc(MAX_PATH * 4); + WideCharToMultiByte(CP_UTF8, 0, filename, sizeof(filename), ret, MAX_PATH * 4, NULL, NULL); + CoTaskMemFree(list); + OleUninitialize(); + return ret; + } + OleUninitialize(); + return NULL; +} diff --git a/SDL/font.c b/SDL/font.c index 93f3fa9..eb6243e 100644 --- a/SDL/font.c +++ b/SDL/font.c @@ -1033,6 +1033,26 @@ uint8_t font[] = { _, _, _, X, X, _, _, _, _, _, X, _, _, _, _, _, _, _, + + /* Elipsis */ + _, _, _, _, _, _, + _, _, _, _, _, _, + _, _, _, _, _, _, + _, _, _, _, _, _, + _, _, _, _, _, _, + _, _, _, _, _, _, + X, _, X, _, X, _, + _, _, _, _, _, _, + + /* Mojibake */ + X, X, X, X, X, _, + X, _, _, _, X, _, + X, _, _, _, X, _, + X, _, _, _, X, _, + X, _, _, _, X, _, + X, _, _, _, X, _, + X, _, _, _, X, _, + X, X, X, X, X, _, }; const uint8_t font_max = sizeof(font) / GLYPH_HEIGHT / GLYPH_WIDTH + ' '; diff --git a/SDL/font.h b/SDL/font.h index 21753a8..06d7adf 100644 --- a/SDL/font.h +++ b/SDL/font.h @@ -12,5 +12,7 @@ extern const uint8_t font_max; #define CTRL_STRING "\x80\x81\x82" #define SHIFT_STRING "\x83" #define CMD_STRING "\x84\x85" +#define ELLIPSIS_STRING "\x87" +#define MOJIBAKE_STRING "\x88" #endif /* font_h */ diff --git a/SDL/gui.c b/SDL/gui.c index 5701495..c36dff4 100644 --- a/SDL/gui.c +++ b/SDL/gui.c @@ -442,9 +442,50 @@ const char *current_rewind_string(unsigned index) return "Custom"; } +const char *current_bootrom_string(unsigned index) +{ + if (!configuration.bootrom_path[0]) { + return "Built-in Boot ROMs"; + } + size_t length = strlen(configuration.bootrom_path); + static char ret[24] = {0,}; + if (length <= 23) { + strcpy(ret, configuration.bootrom_path); + } + else { + memcpy(ret, configuration.bootrom_path, 11); + memcpy(ret + 12, configuration.bootrom_path + length - 11, 11); + } + for (unsigned i = 0; i < 24; i++) { + if (ret[i] < 0) { + ret[i] = MOJIBAKE_STRING[0]; + } + } + if (length > 23) { + ret[11] = ELLIPSIS_STRING[0]; + } + return ret; +} + +static void toggle_bootrom(unsigned index) +{ + if (configuration.bootrom_path[0]) { + configuration.bootrom_path[0] = 0; + } + else { + char *folder = do_open_folder_dialog(); + if (!folder) return; + if (strlen(folder) < sizeof(configuration.bootrom_path) - 1) { + strcpy(configuration.bootrom_path, folder); + } + free(folder); + } +} + static const struct menu_item emulation_menu[] = { {"Emulated Model:", cycle_model, current_model_string, cycle_model_backwards}, {"SGB Revision:", cycle_sgb_revision, current_sgb_revision_string, cycle_sgb_revision_backwards}, + {"Boot ROMs Folder:", toggle_bootrom, current_bootrom_string, toggle_bootrom}, {"Rewind Length:", cycle_rewind, current_rewind_string, cycle_rewind_backwards}, {"Back", return_to_root_menu}, {NULL,} @@ -883,7 +924,7 @@ const char *current_joypad_name(unsigned index) // SDL returns a name with repeated and trailing spaces while (*orig_name && i < sizeof(name) - 2) { if (orig_name[0] != ' ' || orig_name[1] != ' ') { - name[i++] = *orig_name; + name[i++] = *orig_name > 0? *orig_name : MOJIBAKE_STRING[0]; } orig_name++; } diff --git a/SDL/gui.h b/SDL/gui.h index 84930e0..b507237 100644 --- a/SDL/gui.h +++ b/SDL/gui.h @@ -114,6 +114,7 @@ typedef struct { /* v0.14 */ unsigned padding; uint8_t color_temperature; + char bootrom_path[4096]; } configuration_t; extern configuration_t configuration; diff --git a/SDL/main.c b/SDL/main.c index a20d644..06cde73 100644 --- a/SDL/main.c +++ b/SDL/main.c @@ -448,8 +448,6 @@ static bool handle_pending_command(void) 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", @@ -460,8 +458,17 @@ static void load_boot_rom(GB_gameboy_t *gb, GB_boot_rom_t type) [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); + bool use_built_in = true; + if (configuration.bootrom_path[0]) { + static char path[4096]; + snprintf(path, sizeof(path), "%s/%s", configuration.bootrom_path, names[type]); + use_built_in = GB_load_boot_rom(gb, path); + } + if (use_built_in) { + start_capturing_logs(); + GB_load_boot_rom(gb, resource_path(names[type])); + end_capturing_logs(true, false); + } } static void run(void) @@ -649,6 +656,7 @@ int main(int argc, char **argv) configuration.border_mode %= GB_BORDER_ALWAYS + 1; configuration.rumble_mode %= GB_RUMBLE_ALL_GAMES + 1; configuration.color_temperature %= 21; + configuration.bootrom_path[sizeof(configuration.bootrom_path) - 1] = 0; } if (configuration.model >= MODEL_MAX) { From 3dbd2eac91daed6a89f9a9b54c023c161eb594eb Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sat, 26 Dec 2020 23:33:01 +0200 Subject: [PATCH 15/21] Something went wrong with the color temperature commit somehow --- Cocoa/Document.m | 1 + Cocoa/GBPreferencesWindow.m | 1 - Cocoa/Preferences.xib | 29 +++++++++++++++++++++++------ 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/Cocoa/Document.m b/Cocoa/Document.m index a354e03..ad443b6 100644 --- a/Cocoa/Document.m +++ b/Cocoa/Document.m @@ -286,6 +286,7 @@ static void infraredStateChanged(GB_gameboy_t *gb, bool on) 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"]); + GB_set_light_temperature(&gb, [[NSUserDefaults standardUserDefaults] doubleForKey:@"GBLightTemperature"]); GB_set_border_mode(&gb, (GB_border_mode_t) [[NSUserDefaults standardUserDefaults] integerForKey:@"GBBorderMode"]); [self updatePalette]; GB_set_rgb_encode_callback(&gb, rgbEncode); diff --git a/Cocoa/GBPreferencesWindow.m b/Cocoa/GBPreferencesWindow.m index dd13ca1..6edc54e 100644 --- a/Cocoa/GBPreferencesWindow.m +++ b/Cocoa/GBPreferencesWindow.m @@ -102,7 +102,6 @@ { _temperatureSlider = temperatureSlider; [temperatureSlider setDoubleValue:[[NSUserDefaults standardUserDefaults] doubleForKey:@"GBLightTemperature"] * 256]; - temperatureSlider.continuous = YES; } - (NSSlider *)temperatureSlider diff --git a/Cocoa/Preferences.xib b/Cocoa/Preferences.xib index 73eb0ab..e5494fb 100644 --- a/Cocoa/Preferences.xib +++ b/Cocoa/Preferences.xib @@ -79,7 +79,7 @@ - + @@ -97,7 +97,7 @@ - + @@ -134,7 +134,7 @@ - + @@ -143,7 +143,7 @@ - + @@ -263,8 +263,25 @@ + + + + + + + + + + + + + + + + + - + @@ -491,7 +508,7 @@ - + From 47ebc317331037fcf9b98ee10d66fa17cb953e66 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sat, 26 Dec 2020 23:52:41 +0200 Subject: [PATCH 16/21] Fixed a bug where the SDL and libretro frontend would not update the border when loading a new ROM --- Core/gb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Core/gb.c b/Core/gb.c index 4788ad9..83cf23b 100644 --- a/Core/gb.c +++ b/Core/gb.c @@ -305,6 +305,7 @@ int GB_load_rom(GB_gameboy_t *gb, const char *path) fread(gb->rom, 1, gb->rom_size, f); fclose(f); GB_configure_cart(gb); + gb->tried_loading_sgb_border = false; return 0; } @@ -537,6 +538,7 @@ error: gb->rom_size = old_size; } fclose(f); + gb->tried_loading_sgb_border = false; return -1; } @@ -557,6 +559,7 @@ void GB_load_rom_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t siz memset(gb->rom, 0xff, gb->rom_size); memcpy(gb->rom, buffer, size); GB_configure_cart(gb); + gb->tried_loading_sgb_border = false; } typedef struct { From 1d34637bdaf7848aa5f99ea2c89087f45420973a Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sat, 26 Dec 2020 23:56:26 +0200 Subject: [PATCH 17/21] Fix it harder --- Core/gb.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Core/gb.c b/Core/gb.c index 83cf23b..77ea144 100644 --- a/Core/gb.c +++ b/Core/gb.c @@ -306,6 +306,8 @@ int GB_load_rom(GB_gameboy_t *gb, const char *path) fclose(f); GB_configure_cart(gb); gb->tried_loading_sgb_border = false; + gb->has_sgb_border = false; + load_default_border(gb); return 0; } @@ -539,6 +541,8 @@ error: } fclose(f); gb->tried_loading_sgb_border = false; + gb->has_sgb_border = false; + load_default_border(gb); return -1; } @@ -560,6 +564,8 @@ void GB_load_rom_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t siz memcpy(gb->rom, buffer, size); GB_configure_cart(gb); gb->tried_loading_sgb_border = false; + gb->has_sgb_border = false; + load_default_border(gb); } typedef struct { From 9e808b255cbc07aea196067ab41c28f0d0854959 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sun, 27 Dec 2020 00:03:40 +0200 Subject: [PATCH 18/21] Escape now returns to the previous menu if used from a submenu in the SDL port --- SDL/gui.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/SDL/gui.c b/SDL/gui.c index c36dff4..a21526d 100644 --- a/SDL/gui.c +++ b/SDL/gui.c @@ -1369,7 +1369,11 @@ void run_gui(bool is_running) } } else if (event.key.keysym.scancode == SDL_SCANCODE_ESCAPE) { - if (is_running) { + if (gui_state == SHOWING_MENU && current_menu != root_menu) { + return_to_root_menu(0); + should_render = true; + } + else if (is_running) { return; } else { From e535d97e8430ab353b3b6de87d3f67afd9adad5e Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sun, 27 Dec 2020 00:23:16 +0200 Subject: [PATCH 19/21] Fix GCC9 build break --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 9d3ca7c..a03f417 100644 --- a/Makefile +++ b/Makefile @@ -99,7 +99,7 @@ endif # These must come before the -Wno- flags WARNINGS += -Werror -Wall -Wno-unknown-warning -Wno-unknown-warning-option -WARNINGS += -Wno-nonnull -Wno-unused-result -Wno-strict-aliasing -Wno-multichar -Wno-int-in-bool-context +WARNINGS += -Wno-nonnull -Wno-unused-result -Wno-strict-aliasing -Wno-multichar -Wno-int-in-bool-context -Wno-format-truncation # Only add this flag if the compiler supports it ifeq ($(shell $(CC) -x c -c $(NULL) -o $(NULL) -Werror -Wpartial-availability 2> $(NULL); echo $$?),0) From 8e858c1bf146f7f62f7048f7089b9572bc3b2b30 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sun, 27 Dec 2020 01:02:50 +0200 Subject: [PATCH 20/21] Capitalization --- Cocoa/Preferences.xib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cocoa/Preferences.xib b/Cocoa/Preferences.xib index e5494fb..7ca5f28 100644 --- a/Cocoa/Preferences.xib +++ b/Cocoa/Preferences.xib @@ -274,7 +274,7 @@ - + From 5c854dbdca808c0258cb34a3cb11b1f9d2dd67ca Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Thu, 31 Dec 2020 00:06:36 +0200 Subject: [PATCH 21/21] Interference emulation --- Cocoa/Document.m | 12 ++++++++ Cocoa/GBPreferencesWindow.h | 1 + Cocoa/GBPreferencesWindow.m | 21 ++++++++++++++ Cocoa/Preferences.xib | 30 ++++++++++++++++---- Core/apu.c | 55 +++++++++++++++++++++++++++++++++++++ Core/apu.h | 4 +++ SDL/gui.c | 26 +++++++++++++++++- SDL/gui.h | 1 + SDL/main.c | 2 ++ 9 files changed, 145 insertions(+), 7 deletions(-) diff --git a/Cocoa/Document.m b/Cocoa/Document.m index ad443b6..0beceab 100644 --- a/Cocoa/Document.m +++ b/Cocoa/Document.m @@ -287,6 +287,7 @@ static void infraredStateChanged(GB_gameboy_t *gb, bool on) 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_light_temperature(&gb, [[NSUserDefaults standardUserDefaults] doubleForKey:@"GBLightTemperature"]); + GB_set_interference_volume(&gb, [[NSUserDefaults standardUserDefaults] doubleForKey:@"GBInterferenceVolume"]); GB_set_border_mode(&gb, (GB_border_mode_t) [[NSUserDefaults standardUserDefaults] integerForKey:@"GBBorderMode"]); [self updatePalette]; GB_set_rgb_encode_callback(&gb, rgbEncode); @@ -695,6 +696,11 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency) name:@"GBLightTemperatureChanged" object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(updateInterferenceVolume) + name:@"GBInterferenceVolumeChanged" + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateFrameBlendingMode) name:@"GBFrameBlendingModeChanged" @@ -1848,6 +1854,12 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency) } } +- (void) updateInterferenceVolume +{ + if (GB_is_inited(&gb)) { + GB_set_interference_volume(&gb, [[NSUserDefaults standardUserDefaults] doubleForKey:@"GBInterferenceVolume"]); + } +} - (void) updateFrameBlendingMode { diff --git a/Cocoa/GBPreferencesWindow.h b/Cocoa/GBPreferencesWindow.h index f0b7506..43a8f1d 100644 --- a/Cocoa/GBPreferencesWindow.h +++ b/Cocoa/GBPreferencesWindow.h @@ -18,6 +18,7 @@ @property (strong) IBOutlet NSPopUpButtonCell *bootROMsButton; @property (strong) IBOutlet NSPopUpButton *rumbleModePopupButton; @property (weak) IBOutlet NSSlider *temperatureSlider; +@property (weak) IBOutlet NSSlider *interferenceSlider; @property (weak) IBOutlet NSPopUpButton *dmgPopupButton; @property (weak) IBOutlet NSPopUpButton *sgbPopupButton; @property (weak) IBOutlet NSPopUpButton *cgbPopupButton; diff --git a/Cocoa/GBPreferencesWindow.m b/Cocoa/GBPreferencesWindow.m index 6edc54e..bd3a4a8 100644 --- a/Cocoa/GBPreferencesWindow.m +++ b/Cocoa/GBPreferencesWindow.m @@ -27,6 +27,7 @@ NSPopUpButton *_preferredJoypadButton; NSPopUpButton *_rumbleModePopupButton; NSSlider *_temperatureSlider; + NSSlider *_interferenceSlider; } + (NSArray *)filterList @@ -108,6 +109,18 @@ { return _temperatureSlider; } + +- (void)setInterferenceSlider:(NSSlider *)interferenceSlider +{ + _interferenceSlider = interferenceSlider; + [interferenceSlider setDoubleValue:[[NSUserDefaults standardUserDefaults] doubleForKey:@"GBInterferenceVolume"] * 256]; +} + +- (NSSlider *)interferenceSlider +{ + return _interferenceSlider; +} + - (void)setFrameBlendingModePopupButton:(NSPopUpButton *)frameBlendingModePopupButton { _frameBlendingModePopupButton = frameBlendingModePopupButton; @@ -303,6 +316,14 @@ [[NSNotificationCenter defaultCenter] postNotificationName:@"GBLightTemperatureChanged" object:nil]; } +- (IBAction)volumeTemperatureChanged:(id)sender +{ + [[NSUserDefaults standardUserDefaults] setObject:@([sender doubleValue] / 256.0) + forKey:@"GBInterferenceVolume"]; + [[NSNotificationCenter defaultCenter] postNotificationName:@"GBInterferenceVolumeChanged" object:nil]; + +} + - (IBAction)franeBlendingModeChanged:(id)sender { [[NSUserDefaults standardUserDefaults] setObject:@([sender indexOfSelectedItem]) diff --git a/Cocoa/Preferences.xib b/Cocoa/Preferences.xib index 7ca5f28..248cfce 100644 --- a/Cocoa/Preferences.xib +++ b/Cocoa/Preferences.xib @@ -73,6 +73,7 @@ + @@ -174,7 +175,7 @@ - + @@ -264,7 +265,7 @@ - + @@ -446,11 +447,11 @@ - + - + @@ -470,7 +471,7 @@ - + @@ -478,8 +479,25 @@ + + + + + + + + + + + + + + + + + - + diff --git a/Core/apu.c b/Core/apu.c index e37b265..3abca7d 100644 --- a/Core/apu.c +++ b/Core/apu.c @@ -137,6 +137,45 @@ static double smooth(double x) return 3*x*x - 2*x*x*x; } +static signed interference(GB_gameboy_t *gb) +{ + /* These aren't scientifically measured, but based on ear based on several recordings */ + signed ret = 0; + if (gb->halted) { + if (gb->model != GB_MODEL_AGB) { + ret -= MAX_CH_AMP / 5; + } + else { + ret -= MAX_CH_AMP / 12; + } + } + if (gb->io_registers[GB_IO_LCDC] & 0x80) { + ret += MAX_CH_AMP / 7; + if ((gb->io_registers[GB_IO_STAT] & 3) == 3 && gb->model != GB_MODEL_AGB) { + ret += MAX_CH_AMP / 14; + } + else if ((gb->io_registers[GB_IO_STAT] & 3) == 1) { + ret -= MAX_CH_AMP / 7; + } + } + + if (gb->apu.global_enable) { + ret += MAX_CH_AMP / 10; + } + + if (GB_is_cgb(gb) && gb->model < GB_MODEL_AGB && (gb->io_registers[GB_IO_RP] & 1)) { + ret += MAX_CH_AMP / 10; + } + + if (!GB_is_cgb(gb)) { + ret /= 4; + } + + ret += rand() % (MAX_CH_AMP / 12); + + return ret; +} + static void render(GB_gameboy_t *gb) { GB_sample_t output = {0, 0}; @@ -226,6 +265,17 @@ static void render(GB_gameboy_t *gb) } + + if (gb->apu_output.interference_volume) { + signed interference_bias = interference(gb); + int16_t interference_sample = (interference_bias - gb->apu_output.interference_highpass); + gb->apu_output.interference_highpass = gb->apu_output.interference_highpass * gb->apu_output.highpass_rate + + (1 - gb->apu_output.highpass_rate) * interference_sample; + interference_bias *= gb->apu_output.interference_volume; + + filtered_output.left = MAX(MIN(filtered_output.left + interference_bias, 0x7FFF), -0x8000); + filtered_output.right = MAX(MIN(filtered_output.right + interference_bias, 0x7FFF), -0x8000); + } assert(gb->apu_output.sample_callback); gb->apu_output.sample_callback(gb, &filtered_output); } @@ -1122,3 +1172,8 @@ void GB_apu_update_cycles_per_sample(GB_gameboy_t *gb) gb->apu_output.cycles_per_sample = 2 * GB_get_clock_rate(gb) / (double)gb->apu_output.sample_rate; /* 2 * because we use 8MHz units */ } } + +void GB_set_interference_volume(GB_gameboy_t *gb, double volume) +{ + gb->apu_output.interference_volume = volume; +} diff --git a/Core/apu.h b/Core/apu.h index 9d5fc80..69cea16 100644 --- a/Core/apu.h +++ b/Core/apu.h @@ -154,12 +154,16 @@ typedef struct { GB_sample_callback_t sample_callback; bool rate_set_in_clocks; + double interference_volume; + double interference_highpass; } 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_set_interference_volume(GB_gameboy_t *gb, double volume); void GB_apu_set_sample_callback(GB_gameboy_t *gb, GB_sample_callback_t callback); + #ifdef GB_INTERNAL bool GB_apu_is_DAC_enabled(GB_gameboy_t *gb, unsigned index); void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value); diff --git a/SDL/gui.c b/SDL/gui.c index a21526d..0846664 100644 --- a/SDL/gui.c +++ b/SDL/gui.c @@ -789,7 +789,7 @@ static const struct menu_item graphics_menu[] = { {"Default Window Scale:", cycle_default_scale, current_default_scale, cycle_default_scale_backwards}, {"Scaling Filter:", cycle_filter, current_filter_name, cycle_filter_backwards}, {"Color Correction:", cycle_color_correction, current_color_correction_mode, cycle_color_correction_backwards}, - {"Ambient Light:", decrease_color_temperature, current_color_temperature, increase_color_temperature}, + {"Ambient Light Temp.:", decrease_color_temperature, current_color_temperature, increase_color_temperature}, {"Frame Blending:", cycle_blending_mode, blending_mode_string, cycle_blending_mode_backwards}, {"Mono Palette:", cycle_palette, current_palette, cycle_palette_backwards}, {"Display Border:", cycle_border_mode, current_border_mode, cycle_border_mode_backwards}, @@ -852,9 +852,33 @@ void decrease_volume(unsigned index) } } +const char *interference_volume_string(unsigned index) +{ + static char ret[5]; + sprintf(ret, "%d%%", configuration.interference_volume); + return ret; +} + +void increase_interference_volume(unsigned index) +{ + configuration.interference_volume += 5; + if (configuration.interference_volume > 100) { + configuration.interference_volume = 100; + } +} + +void decrease_interference_volume(unsigned index) +{ + configuration.interference_volume -= 5; + if (configuration.interference_volume > 100) { + configuration.interference_volume = 0; + } +} + static const struct menu_item audio_menu[] = { {"Highpass Filter:", cycle_highpass_filter, highpass_filter_string, cycle_highpass_filter_backwards}, {"Volume:", increase_volume, volume_string, decrease_volume}, + {"Interference Volume:", increase_interference_volume, interference_volume_string, decrease_interference_volume}, {"Back", return_to_root_menu}, {NULL,} }; diff --git a/SDL/gui.h b/SDL/gui.h index b507237..8d69ec3 100644 --- a/SDL/gui.h +++ b/SDL/gui.h @@ -115,6 +115,7 @@ typedef struct { unsigned padding; uint8_t color_temperature; char bootrom_path[4096]; + uint8_t interference_volume; } configuration_t; extern configuration_t configuration; diff --git a/SDL/main.c b/SDL/main.c index 06cde73..63a61c9 100644 --- a/SDL/main.c +++ b/SDL/main.c @@ -121,6 +121,7 @@ static void open_menu(void) } GB_set_color_correction_mode(&gb, configuration.color_correction_mode); GB_set_light_temperature(&gb, (configuration.color_temperature - 10.0) / 10.0); + GB_set_interference_volume(&gb, configuration.interference_volume / 100.0); GB_set_border_mode(&gb, configuration.border_mode); update_palette(); GB_set_highpass_filter_mode(&gb, configuration.highpass_mode); @@ -505,6 +506,7 @@ restart: GB_set_sample_rate(&gb, GB_audio_get_frequency()); GB_set_color_correction_mode(&gb, configuration.color_correction_mode); GB_set_light_temperature(&gb, (configuration.color_temperature - 10.0) / 10.0); + GB_set_interference_volume(&gb, configuration.interference_volume / 100.0); update_palette(); if ((unsigned)configuration.border_mode <= GB_BORDER_ALWAYS) { GB_set_border_mode(&gb, configuration.border_mode);