Basic SGB support in the SDL port

This commit is contained in:
Lior Halphon 2019-05-18 18:45:31 +03:00
parent 3ee2c64899
commit 3e724afb0a
8 changed files with 84 additions and 50 deletions

View File

@ -798,12 +798,12 @@ uint32_t GB_get_clock_rate(GB_gameboy_t *gb)
return CPU_FREQUENCY * gb->clock_multiplier; 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; 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; return GB_is_sgb(gb)? 224 : 144;
} }

View File

@ -689,8 +689,8 @@ uint32_t GB_get_clock_rate(GB_gameboy_t *gb);
#endif #endif
void GB_set_clock_multiplier(GB_gameboy_t *gb, double multiplier); void GB_set_clock_multiplier(GB_gameboy_t *gb, double multiplier);
size_t GB_get_screen_width(GB_gameboy_t *gb); unsigned GB_get_screen_width(GB_gameboy_t *gb);
size_t GB_get_screen_height(GB_gameboy_t *gb); unsigned GB_get_screen_height(GB_gameboy_t *gb);
unsigned GB_get_player_count(GB_gameboy_t *gb); unsigned GB_get_player_count(GB_gameboy_t *gb);

View File

@ -44,7 +44,9 @@ void render_texture(void *pixels, void *previous)
} }
glClearColor(0, 0, 0, 1); glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT); 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); SDL_GL_SwapWindow(window);
} }
} }
@ -116,8 +118,8 @@ void update_viewport(void)
{ {
int win_width, win_height; int win_width, win_height;
SDL_GL_GetDrawableSize(window, &win_width, &win_height); SDL_GL_GetDrawableSize(window, &win_width, &win_height);
double x_factor = win_width / 160.0; double x_factor = win_width / (double) GB_get_screen_width(&gb);
double y_factor = win_height / 144.0; double y_factor = win_height / (double) GB_get_screen_height(&gb);
if (configuration.scaling_mode == GB_SDL_SCALING_INTEGER_FACTOR) { if (configuration.scaling_mode == GB_SDL_SCALING_INTEGER_FACTOR) {
x_factor = (int)(x_factor); x_factor = (int)(x_factor);
@ -133,8 +135,8 @@ void update_viewport(void)
} }
} }
unsigned new_width = x_factor * 160; unsigned new_width = x_factor * GB_get_screen_width(&gb);
unsigned new_height = y_factor * 144; unsigned new_height = y_factor * GB_get_screen_height(&gb);
rect = (SDL_Rect){(win_width - new_width) / 2, (win_height - new_height) /2, rect = (SDL_Rect){(win_width - new_width) / 2, (win_height - new_height) /2,
new_width, new_height}; new_width, new_height};
@ -148,7 +150,7 @@ void update_viewport(void)
} }
/* Does NOT check for bounds! */ /* 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) { if (ch < ' ' || ch > font_max) {
ch = '?'; ch = '?';
@ -163,11 +165,11 @@ static void draw_char(uint32_t *buffer, unsigned char ch, uint32_t color)
} }
buffer++; 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; unsigned orig_x = x;
while (*string) { while (*string) {
@ -178,23 +180,23 @@ static void draw_unbordered_text(uint32_t *buffer, unsigned x, unsigned y, const
continue; continue;
} }
if (x > 160 - GLYPH_WIDTH || y == 0 || y > 144 - GLYPH_HEIGHT) { if (x > width - GLYPH_WIDTH || y == 0 || y > height - GLYPH_HEIGHT) {
break; break;
} }
draw_char(&buffer[x + 160 * y], *string, color); draw_char(&buffer[x + width * y], width, height, *string, color);
x += GLYPH_WIDTH; x += GLYPH_WIDTH;
string++; 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, width, height, x - 1, y, string, border);
draw_unbordered_text(buffer, x + 1, y, string, border); draw_unbordered_text(buffer, width, height, x + 1, y, string, border);
draw_unbordered_text(buffer, x, y - 1, string, border); draw_unbordered_text(buffer, width, height, x, y - 1, string, border);
draw_unbordered_text(buffer, x, y + 1, string, border); draw_unbordered_text(buffer, width, height, x, y + 1, string, border);
draw_unbordered_text(buffer, x, y, string, color); draw_unbordered_text(buffer, width, height, x, y, string, color);
} }
enum decoration { enum decoration {
@ -203,17 +205,17 @@ enum decoration {
DECORATION_ARROWS, 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; unsigned x = width / 2 - (unsigned) strlen(string) * GLYPH_WIDTH / 2;
draw_text(buffer, x, y, string, color, border); draw_text(buffer, width, height, x, y, string, color, border);
switch (decoration) { switch (decoration) {
case DECORATION_SELECTION: 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; break;
case DECORATION_ARROWS: case DECORATION_ARROWS:
draw_text(buffer, x - GLYPH_WIDTH, y, LEFT_ARROW_STRING, color, border); draw_text(buffer, width, height, 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, width - x, y, RIGHT_ARROW_STRING, color, border);
break; break;
case DECORATION_NONE: case DECORATION_NONE:
@ -301,7 +303,7 @@ static void cycle_model_backwards(unsigned index)
const char *current_model_string(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]; [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,}; SDL_Event event = {0,};
gui_state = is_running? SHOWING_MENU : SHOWING_DROP_MESSAGE; gui_state = is_running? SHOWING_MENU : SHOWING_DROP_MESSAGE;
bool should_render = true; bool should_render = true;
@ -994,32 +1007,39 @@ void run_gui(bool is_running)
if (should_render) { if (should_render) {
should_render = false; should_render = false;
if (width == 160 && height == 144) {
memcpy(pixels, converted_background->pixels, sizeof(pixels)); 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) { switch (gui_state) {
case SHOWING_DROP_MESSAGE: 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, width, height, 8 + y_offset, "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, width, height, 116 + y_offset, "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, 128 + y_offset, "file to play", gui_palette_native[3], gui_palette_native[0], false);
break; break;
case SHOWING_MENU: 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; unsigned i = 0, y = 24;
for (const struct menu_item *item = current_menu; item->string; item++, i++) { for (const struct menu_item *item = current_menu; item->string; item++, i++) {
if (item->value_getter && !item->backwards_handler) { if (item->value_getter && !item->backwards_handler) {
char line[25]; char line[25];
snprintf(line, sizeof(line), "%s%*s", item->string, 24 - (int)strlen(item->string), item->value_getter(i)); 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); i == current_selection ? DECORATION_SELECTION : DECORATION_NONE);
y += 12; y += 12;
} }
else { 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); i == current_selection && !item->value_getter ? DECORATION_SELECTION : DECORATION_NONE);
y += 12; y += 12;
if (item->value_getter) { 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); i == current_selection ? DECORATION_ARROWS : DECORATION_NONE);
y += 12; y += 12;
} }
@ -1027,16 +1047,16 @@ void run_gui(bool is_running)
} }
break; break;
case SHOWING_HELP: 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; break;
case WAITING_FOR_KEY: 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; break;
case WAITING_FOR_JBUTTON: 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", joypad_configuration_progress != JOYPAD_BUTTONS_MAX ? "Press button for" : "Move the Analog Stick",
gui_palette_native[3], gui_palette_native[0], DECORATION_NONE); 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 *[]) (const char *[])
{ {
"Right", "Right",
@ -1054,7 +1074,7 @@ void run_gui(bool is_running)
"", "",
} [joypad_configuration_progress], } [joypad_configuration_progress],
gui_palette_native[3], gui_palette_native[0], DECORATION_NONE); 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; break;
} }

View File

@ -77,6 +77,7 @@ typedef struct {
MODEL_DMG, MODEL_DMG,
MODEL_CGB, MODEL_CGB,
MODEL_AGB, MODEL_AGB,
MODEL_SGB,
MODEL_MAX, MODEL_MAX,
} model; } model;

View File

@ -24,7 +24,7 @@
GB_gameboy_t gb; GB_gameboy_t gb;
static bool paused = false; 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 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 bool underclock_down = false, rewind_down = false, do_rewind = false, rewind_paused = false, turbo_down = false;
static double clock_mutliplier = 1.0; 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_DMG] = GB_MODEL_DMG_B,
[MODEL_CGB] = GB_MODEL_CGB_E, [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) void set_filename(const char *new_filename, bool new_should_free)
@ -423,9 +424,16 @@ restart:
GB_set_rewind_length(&gb, configuration.rewind_length); 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; bool error = false;
start_capturing_logs(); 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])); error = GB_load_boot_rom(&gb, resource_path(boot_roms[configuration.model]));
end_capturing_logs(true, error); end_capturing_logs(true, error);
@ -448,6 +456,8 @@ restart:
replace_extension(filename, path_length, symbols_path, ".sym"); replace_extension(filename, path_length, symbols_path, ".sym");
GB_debugger_load_symbol_file(&gb, symbols_path); GB_debugger_load_symbol_file(&gb, symbols_path);
update_viewport();
/* Run emulation */ /* Run emulation */
while (true) { while (true) {
if (paused || rewind_paused) { if (paused || rewind_paused) {

View File

@ -162,20 +162,22 @@ bool init_shader_with_name(shader_t *shader, const char *name)
return true; 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); glUseProgram(shader->program);
glUniform2f(shader->origin_uniform, x, y); glUniform2f(shader->origin_uniform, x, y);
glUniform2f(shader->resolution_uniform, w, h); glUniform2f(shader->resolution_uniform, w, h);
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, shader->texture); 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->texture_uniform, 0);
glUniform1i(shader->mix_previous_uniform, previous != NULL); glUniform1i(shader->mix_previous_uniform, previous != NULL);
if (previous) { if (previous) {
glActiveTexture(GL_TEXTURE1); glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, shader->previous_texture); 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); glUniform1i(shader->previous_texture_uniform, 1);
} }
glBindFragDataLocation(shader->program, 0, "frag_color"); glBindFragDataLocation(shader->program, 0, "frag_color");

View File

@ -17,7 +17,9 @@ typedef struct shader_s {
} shader_t; } shader_t;
bool init_shader_with_name(shader_t *shader, const char *name); 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); void free_shader(struct shader_s *shader);
#endif /* shader_h */ #endif /* shader_h */

View File

@ -5,7 +5,6 @@ uniform bool mix_previous;
uniform vec2 output_resolution; uniform vec2 output_resolution;
uniform vec2 origin; uniform vec2 origin;
const vec2 input_resolution = vec2(160, 144);
#define equal(x, y) ((x) == (y)) #define equal(x, y) ((x) == (y))
#define inequal(x, y) ((x) != (y)) #define inequal(x, y) ((x) != (y))