Basic SGB support in the SDL port
This commit is contained in:
parent
3ee2c64899
commit
3e724afb0a
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
94
SDL/gui.c
94
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;
|
||||
}
|
||||
|
||||
|
@ -77,6 +77,7 @@ typedef struct {
|
||||
MODEL_DMG,
|
||||
MODEL_CGB,
|
||||
MODEL_AGB,
|
||||
MODEL_SGB,
|
||||
MODEL_MAX,
|
||||
} model;
|
||||
|
||||
|
16
SDL/main.c
16
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);
|
||||
|
||||
@ -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) {
|
||||
|
@ -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");
|
||||
|
@ -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 */
|
||||
|
@ -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))
|
||||
|
Loading…
Reference in New Issue
Block a user