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

View File

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

View File

@ -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;
}

View File

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

View File

@ -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);
@ -448,6 +456,8 @@ restart:
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) {

View File

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

View File

@ -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 */

View File

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