From 3e724afb0a5e42c8520dce054483be0982967733 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sat, 18 May 2019 18:45:31 +0300 Subject: [PATCH] Basic SGB support in the SDL port --- Core/gb.c | 4 +- Core/gb.h | 4 +- SDL/gui.c | 94 ++++++++++++++++++++++++---------------- SDL/gui.h | 1 + SDL/main.c | 18 ++++++-- SDL/shader.c | 8 ++-- SDL/shader.h | 4 +- Shaders/MasterShader.fsh | 1 - 8 files changed, 84 insertions(+), 50 deletions(-) diff --git a/Core/gb.c b/Core/gb.c index 9456f106..cb99d4c8 100644 --- a/Core/gb.c +++ b/Core/gb.c @@ -798,12 +798,12 @@ uint32_t GB_get_clock_rate(GB_gameboy_t *gb) return CPU_FREQUENCY * gb->clock_multiplier; } -size_t GB_get_screen_width(GB_gameboy_t *gb) +unsigned GB_get_screen_width(GB_gameboy_t *gb) { return GB_is_sgb(gb)? 256 : 160; } -size_t GB_get_screen_height(GB_gameboy_t *gb) +unsigned GB_get_screen_height(GB_gameboy_t *gb) { return GB_is_sgb(gb)? 224 : 144; } diff --git a/Core/gb.h b/Core/gb.h index a4b7256e..30654d52 100644 --- a/Core/gb.h +++ b/Core/gb.h @@ -689,8 +689,8 @@ uint32_t GB_get_clock_rate(GB_gameboy_t *gb); #endif void GB_set_clock_multiplier(GB_gameboy_t *gb, double multiplier); -size_t GB_get_screen_width(GB_gameboy_t *gb); -size_t GB_get_screen_height(GB_gameboy_t *gb); +unsigned GB_get_screen_width(GB_gameboy_t *gb); +unsigned GB_get_screen_height(GB_gameboy_t *gb); unsigned GB_get_player_count(GB_gameboy_t *gb); diff --git a/SDL/gui.c b/SDL/gui.c index 4de76f51..80f15073 100644 --- a/SDL/gui.c +++ b/SDL/gui.c @@ -44,7 +44,9 @@ void render_texture(void *pixels, void *previous) } glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT); - render_bitmap_with_shader(&shader, _pixels, previous, rect.x, rect.y, rect.w, rect.h); + render_bitmap_with_shader(&shader, _pixels, previous, + GB_get_screen_width(&gb), GB_get_screen_height(&gb), + rect.x, rect.y, rect.w, rect.h); SDL_GL_SwapWindow(window); } } @@ -116,8 +118,8 @@ void update_viewport(void) { int win_width, win_height; SDL_GL_GetDrawableSize(window, &win_width, &win_height); - double x_factor = win_width / 160.0; - double y_factor = win_height / 144.0; + double x_factor = win_width / (double) GB_get_screen_width(&gb); + double y_factor = win_height / (double) GB_get_screen_height(&gb); if (configuration.scaling_mode == GB_SDL_SCALING_INTEGER_FACTOR) { x_factor = (int)(x_factor); @@ -133,8 +135,8 @@ void update_viewport(void) } } - unsigned new_width = x_factor * 160; - unsigned new_height = y_factor * 144; + unsigned new_width = x_factor * GB_get_screen_width(&gb); + unsigned new_height = y_factor * GB_get_screen_height(&gb); rect = (SDL_Rect){(win_width - new_width) / 2, (win_height - new_height) /2, new_width, new_height}; @@ -148,7 +150,7 @@ void update_viewport(void) } /* Does NOT check for bounds! */ -static void draw_char(uint32_t *buffer, unsigned char ch, uint32_t color) +static void draw_char(uint32_t *buffer, unsigned width, unsigned height, unsigned char ch, uint32_t color) { if (ch < ' ' || ch > font_max) { ch = '?'; @@ -163,11 +165,11 @@ static void draw_char(uint32_t *buffer, unsigned char ch, uint32_t color) } buffer++; } - buffer += 160 - GLYPH_WIDTH; + buffer += width - GLYPH_WIDTH; } } -static void draw_unbordered_text(uint32_t *buffer, unsigned x, unsigned y, const char *string, uint32_t color) +static void draw_unbordered_text(uint32_t *buffer, unsigned width, unsigned height, unsigned x, unsigned y, const char *string, uint32_t color) { unsigned orig_x = x; while (*string) { @@ -178,23 +180,23 @@ static void draw_unbordered_text(uint32_t *buffer, unsigned x, unsigned y, const continue; } - if (x > 160 - GLYPH_WIDTH || y == 0 || y > 144 - GLYPH_HEIGHT) { + if (x > width - GLYPH_WIDTH || y == 0 || y > height - GLYPH_HEIGHT) { break; } - draw_char(&buffer[x + 160 * y], *string, color); + draw_char(&buffer[x + width * y], width, height, *string, color); x += GLYPH_WIDTH; string++; } } -static void draw_text(uint32_t *buffer, 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, unsigned y, const char *string, uint32_t color, uint32_t border) { - draw_unbordered_text(buffer, x - 1, y, string, border); - draw_unbordered_text(buffer, x + 1, y, string, border); - draw_unbordered_text(buffer, x, y - 1, string, border); - draw_unbordered_text(buffer, x, y + 1, string, border); - draw_unbordered_text(buffer, x, y, string, color); + draw_unbordered_text(buffer, width, height, x - 1, y, string, border); + draw_unbordered_text(buffer, width, height, x + 1, y, string, border); + draw_unbordered_text(buffer, width, height, x, y - 1, string, border); + draw_unbordered_text(buffer, width, height, x, y + 1, string, border); + draw_unbordered_text(buffer, width, height, x, y, string, color); } enum decoration { @@ -203,17 +205,17 @@ enum decoration { DECORATION_ARROWS, }; -static void draw_text_centered(uint32_t *buffer, unsigned y, const char *string, uint32_t color, uint32_t border, enum decoration decoration) +static void draw_text_centered(uint32_t *buffer, unsigned width, unsigned height, unsigned y, const char *string, uint32_t color, uint32_t border, enum decoration decoration) { - unsigned x = 160 / 2 - (unsigned) strlen(string) * GLYPH_WIDTH / 2; - draw_text(buffer, x, y, string, color, border); + unsigned x = width / 2 - (unsigned) strlen(string) * GLYPH_WIDTH / 2; + draw_text(buffer, width, height, x, y, string, color, border); switch (decoration) { case DECORATION_SELECTION: - draw_text(buffer, x - GLYPH_WIDTH, y, SELECTION_STRING, color, border); + draw_text(buffer, width, height, x - GLYPH_WIDTH, y, SELECTION_STRING, color, border); break; case DECORATION_ARROWS: - draw_text(buffer, x - GLYPH_WIDTH, y, LEFT_ARROW_STRING, color, border); - draw_text(buffer, 160 - x, y, RIGHT_ARROW_STRING, color, border); + draw_text(buffer, width, height, x - GLYPH_WIDTH, y, LEFT_ARROW_STRING, color, border); + draw_text(buffer, width, height, width - x, y, RIGHT_ARROW_STRING, color, border); break; case DECORATION_NONE: @@ -301,7 +303,7 @@ static void cycle_model_backwards(unsigned index) const char *current_model_string(unsigned index) { - return (const char *[]){"Game Boy", "Game Boy Color", "Game Boy Advance"} + return (const char *[]){"Game Boy", "Game Boy Color", "Game Boy Advance" , "Super Game Boy"} [configuration.model]; } @@ -764,7 +766,18 @@ void run_gui(bool is_running) } } - uint32_t pixels[160 * 144]; + unsigned width = GB_get_screen_width(&gb); + unsigned height = GB_get_screen_height(&gb); + unsigned x_offset = (width - 160) / 2; + unsigned y_offset = (height - 144) / 2; + uint32_t pixels[width * height]; + + if (width != 160 || height != 144) { + for (unsigned i = 0; i < width * height; i++) { + pixels[i] = gui_palette_native[0]; + } + } + SDL_Event event = {0,}; gui_state = is_running? SHOWING_MENU : SHOWING_DROP_MESSAGE; bool should_render = true; @@ -994,32 +1007,39 @@ void run_gui(bool is_running) if (should_render) { should_render = false; - memcpy(pixels, converted_background->pixels, sizeof(pixels)); + if (width == 160 && height == 144) { + memcpy(pixels, converted_background->pixels, sizeof(pixels)); + } + else { + for (unsigned y = 0; y < 144; y++) { + memcpy(pixels + x_offset + width * (y + y_offset), ((uint32_t *)converted_background->pixels) + 160 * y, 160 * 4); + } + } switch (gui_state) { case SHOWING_DROP_MESSAGE: - draw_text_centered(pixels, 8, "Press ESC for menu", gui_palette_native[3], gui_palette_native[0], false); - draw_text_centered(pixels, 116, "Drop a GB or GBC", gui_palette_native[3], gui_palette_native[0], false); - draw_text_centered(pixels, 128, "file to play", gui_palette_native[3], gui_palette_native[0], false); + draw_text_centered(pixels, width, height, 8 + y_offset, "Press ESC for menu", gui_palette_native[3], gui_palette_native[0], false); + draw_text_centered(pixels, width, height, 116 + y_offset, "Drop a GB or GBC", gui_palette_native[3], gui_palette_native[0], false); + draw_text_centered(pixels, width, height, 128 + y_offset, "file to play", gui_palette_native[3], gui_palette_native[0], false); break; case SHOWING_MENU: - draw_text_centered(pixels, 8, "SameBoy", gui_palette_native[3], gui_palette_native[0], false); + draw_text_centered(pixels, width, height, 8 + y_offset, "SameBoy", gui_palette_native[3], gui_palette_native[0], false); unsigned i = 0, y = 24; for (const struct menu_item *item = current_menu; item->string; item++, i++) { 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)); - draw_text_centered(pixels, y, line, gui_palette_native[3], gui_palette_native[0], + draw_text_centered(pixels, width, height, y + y_offset, line, gui_palette_native[3], gui_palette_native[0], i == current_selection ? DECORATION_SELECTION : DECORATION_NONE); y += 12; } else { - draw_text_centered(pixels, y, item->string, gui_palette_native[3], gui_palette_native[0], + draw_text_centered(pixels, width, height, y + y_offset, item->string, gui_palette_native[3], gui_palette_native[0], i == current_selection && !item->value_getter ? DECORATION_SELECTION : DECORATION_NONE); y += 12; if (item->value_getter) { - draw_text_centered(pixels, y, item->value_getter(i), gui_palette_native[3], gui_palette_native[0], + draw_text_centered(pixels, width, height, y + y_offset, item->value_getter(i), gui_palette_native[3], gui_palette_native[0], i == current_selection ? DECORATION_ARROWS : DECORATION_NONE); y += 12; } @@ -1027,16 +1047,16 @@ void run_gui(bool is_running) } break; case SHOWING_HELP: - draw_text(pixels, 2, 2, help[current_help_page], gui_palette_native[3], gui_palette_native[0]); + draw_text(pixels, width, height, 2 + x_offset, 2 + y_offset, help[current_help_page], gui_palette_native[3], gui_palette_native[0]); break; case WAITING_FOR_KEY: - draw_text_centered(pixels, 68, "Press a Key", gui_palette_native[3], gui_palette_native[0], DECORATION_NONE); + draw_text_centered(pixels, width, height, 68 + y_offset, "Press a Key", gui_palette_native[3], gui_palette_native[0], DECORATION_NONE); break; case WAITING_FOR_JBUTTON: - draw_text_centered(pixels, 68, + draw_text_centered(pixels, width, height, 68 + y_offset, joypad_configuration_progress != JOYPAD_BUTTONS_MAX ? "Press button for" : "Move the Analog Stick", gui_palette_native[3], gui_palette_native[0], DECORATION_NONE); - draw_text_centered(pixels, 80, + draw_text_centered(pixels, width, height, 80 + y_offset, (const char *[]) { "Right", @@ -1054,7 +1074,7 @@ void run_gui(bool is_running) "", } [joypad_configuration_progress], gui_palette_native[3], gui_palette_native[0], DECORATION_NONE); - draw_text_centered(pixels, 104, "Press Enter to skip", gui_palette_native[3], gui_palette_native[0], DECORATION_NONE); + draw_text_centered(pixels, width, height, 104 + y_offset, "Press Enter to skip", gui_palette_native[3], gui_palette_native[0], DECORATION_NONE); break; } diff --git a/SDL/gui.h b/SDL/gui.h index 4d106143..9711e832 100644 --- a/SDL/gui.h +++ b/SDL/gui.h @@ -77,6 +77,7 @@ typedef struct { MODEL_DMG, MODEL_CGB, MODEL_AGB, + MODEL_SGB, MODEL_MAX, } model; diff --git a/SDL/main.c b/SDL/main.c index 99facf8d..8a65a742 100755 --- a/SDL/main.c +++ b/SDL/main.c @@ -24,7 +24,7 @@ GB_gameboy_t gb; static bool paused = false; -static uint32_t pixel_buffer_1[160*144], pixel_buffer_2[160*144]; +static uint32_t pixel_buffer_1[256 * 224], pixel_buffer_2[256 * 224]; static uint32_t *active_pixel_buffer = pixel_buffer_1, *previous_pixel_buffer = pixel_buffer_2; static bool underclock_down = false, rewind_down = false, do_rewind = false, rewind_paused = false, turbo_down = false; static double clock_mutliplier = 1.0; @@ -39,7 +39,8 @@ static const GB_model_t sdl_to_internal_model[] = { [MODEL_DMG] = GB_MODEL_DMG_B, [MODEL_CGB] = GB_MODEL_CGB_E, - [MODEL_AGB] = GB_MODEL_AGB + [MODEL_AGB] = GB_MODEL_AGB, + [MODEL_SGB] = GB_MODEL_SGB, }; void set_filename(const char *new_filename, bool new_should_free) @@ -423,9 +424,16 @@ restart: GB_set_rewind_length(&gb, configuration.rewind_length); } + SDL_DestroyTexture(texture); + texture = SDL_CreateTexture(renderer, SDL_GetWindowPixelFormat(window), SDL_TEXTUREACCESS_STREAMING, + GB_get_screen_width(&gb), GB_get_screen_height(&gb)); + + SDL_SetWindowMinimumSize(window, GB_get_screen_width(&gb), GB_get_screen_height(&gb)); + + bool error = false; start_capturing_logs(); - const char * const boot_roms[] = {"dmg_boot.bin", "cgb_boot.bin", "agb_boot.bin"}; + const char * const boot_roms[] = {"dmg_boot.bin", "cgb_boot.bin", "agb_boot.bin", "sgb_boot.bin"}; error = GB_load_boot_rom(&gb, resource_path(boot_roms[configuration.model])); end_capturing_logs(true, error); @@ -447,7 +455,9 @@ restart: char symbols_path[path_length + 5]; replace_extension(filename, path_length, symbols_path, ".sym"); GB_debugger_load_symbol_file(&gb, symbols_path); - + + update_viewport(); + /* Run emulation */ while (true) { if (paused || rewind_paused) { diff --git a/SDL/shader.c b/SDL/shader.c index ed45c42f..37e5be7e 100644 --- a/SDL/shader.c +++ b/SDL/shader.c @@ -162,20 +162,22 @@ bool init_shader_with_name(shader_t *shader, const char *name) return true; } -void render_bitmap_with_shader(shader_t *shader, void *bitmap, void *previous,unsigned x, unsigned y, unsigned w, unsigned h) +void render_bitmap_with_shader(shader_t *shader, void *bitmap, void *previous, + unsigned source_width, unsigned source_height, + unsigned x, unsigned y, unsigned w, unsigned h) { glUseProgram(shader->program); glUniform2f(shader->origin_uniform, x, y); glUniform2f(shader->resolution_uniform, w, h); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, shader->texture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 160, 144, 0, GL_RGBA, GL_UNSIGNED_BYTE, bitmap); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, source_width, source_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, bitmap); glUniform1i(shader->texture_uniform, 0); glUniform1i(shader->mix_previous_uniform, previous != NULL); if (previous) { glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, shader->previous_texture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 160, 144, 0, GL_RGBA, GL_UNSIGNED_BYTE, previous); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, source_width, source_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, previous); glUniform1i(shader->previous_texture_uniform, 1); } glBindFragDataLocation(shader->program, 0, "frag_color"); diff --git a/SDL/shader.h b/SDL/shader.h index 20baf765..3a1c3040 100644 --- a/SDL/shader.h +++ b/SDL/shader.h @@ -17,7 +17,9 @@ typedef struct shader_s { } shader_t; bool init_shader_with_name(shader_t *shader, const char *name); -void render_bitmap_with_shader(shader_t *shader, void *bitmap, void *previous,unsigned x, unsigned y, unsigned w, unsigned h); +void render_bitmap_with_shader(shader_t *shader, void *bitmap, void *previous, + unsigned source_width, unsigned source_height, + unsigned x, unsigned y, unsigned w, unsigned h); void free_shader(struct shader_s *shader); #endif /* shader_h */ diff --git a/Shaders/MasterShader.fsh b/Shaders/MasterShader.fsh index a489cf76..cd569c2d 100644 --- a/Shaders/MasterShader.fsh +++ b/Shaders/MasterShader.fsh @@ -5,7 +5,6 @@ uniform bool mix_previous; uniform vec2 output_resolution; uniform vec2 origin; -const vec2 input_resolution = vec2(160, 144); #define equal(x, y) ((x) == (y)) #define inequal(x, y) ((x) != (y))