diff --git a/README.md b/README.md index cf52846..6a9b1c9 100644 --- a/README.md +++ b/README.md @@ -62,9 +62,16 @@ entirely with `--no-save`. Add `--debug` (`-g`) to show logging information while running. Pass it twice (`-gg`) to show more detailed logs, including an emulator trace. +crater tries to reproduce the Game Gear's native display resolution, which had +a pixel aspect ratio (PAR) of [8:7][par]; this means the pixels were slightly +wider than square, unlike modern LCD displays with a 1:1 PAR. Add `--square` +(`-q`) to force square pixels. + `./crater -h` gives (fairly basic) command-line usage, and `./crater -v` gives the current version. +[par]: https://pineight.com/mw/index.php?title=Dot_clock_rates + ### Input crater supports keyboard and joystick/controller input. diff --git a/src/config.c b/src/config.c index 98d6e5e..db6eed0 100644 --- a/src/config.c +++ b/src/config.c @@ -44,6 +44,8 @@ static void print_help(const char *arg1) " the Game Gear does not usually require BIOS)\n" " -x, --scale scale the game screen by an integer factor\n" " (applies to windowed mode only; defaults to 4)\n" +" -q, --square force a square pixel aspect ratio instead of the more\n" +" faithful 8:7 PAR\n" " -a, --assemble []\n" " convert z80 assembly source code into a binary file that\n" " can be run by crater\n" @@ -269,6 +271,9 @@ static int parse_opt_arg(Config *config, Arguments *args, const char *arg) } config->scale = scale; } + else if (arg_check(arg, "q", "square")) { + config->square_par = true; + } else if (arg_check(arg, "a", "assemble")) { if (args->paths_read >= 1) { config->src_path = config->rom_path; @@ -367,7 +372,8 @@ static bool sanity_check(Config *config) } else if (config->assemble && config->disassemble) { ERROR("cannot assemble and disassemble at the same time") return false; - } else if (assembler && (config->fullscreen || config->scale)) { + } else if (assembler && (config->fullscreen || config->scale || + config->square_par)) { ERROR("cannot specify emulator options in assembler mode") return false; } else if (assembler && !config->src_path) { @@ -424,6 +430,7 @@ int config_create(Config** config_ptr, int argc, char* argv[]) config->fullscreen = false; config->no_saving = false; config->scale = 0; + config->square_par = false; config->rom_path = NULL; config->sav_path = NULL; config->bios_path = NULL; @@ -469,6 +476,7 @@ void config_dump_args(const Config* config) DEBUG("- fullscreen: %s", config->fullscreen ? "true" : "false") DEBUG("- no_saving: %s", config->no_saving ? "true" : "false") DEBUG("- scale: %d", config->scale) + DEBUG("- square_par: %s", config->square_par ? "true" : "false") DEBUG("- rom_path: %s", config->rom_path ? config->rom_path : "(null)") DEBUG("- sav_path: %s", config->sav_path ? config->sav_path : "(null)") DEBUG("- bios_path: %s", config->bios_path ? config->bios_path : "(null)") diff --git a/src/config.h b/src/config.h index 18fdc1b..7fd236d 100644 --- a/src/config.h +++ b/src/config.h @@ -14,10 +14,9 @@ /* We need some sort of maximum scale - with a native resolution of 160 x 144, - a scale factor of 1024 will let us go up to 163,840 x 147,456 pixels. - No one has a screen this large. + a scale factor of 128 will let us go up to 20,480 x 18,432 pixels. */ -#define SCALE_MAX 1024 +#define SCALE_MAX 128 /* Structs */ @@ -28,6 +27,7 @@ typedef struct { bool fullscreen; bool no_saving; unsigned scale; + bool square_par; char *rom_path; char *sav_path; char *bios_path; diff --git a/src/emulator.c b/src/emulator.c index 4743396..facaa00 100644 --- a/src/emulator.c +++ b/src/emulator.c @@ -88,19 +88,22 @@ static void setup_input() /* Set up SDL for drawing the game. */ -static void setup_graphics(bool fullscreen, unsigned scale) +static void setup_graphics(Config *config) { SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest"); uint32_t flags; - if (fullscreen) + if (config->fullscreen) flags = SDL_WINDOW_FULLSCREEN_DESKTOP; else flags = SDL_WINDOW_RESIZABLE; - SDL_CreateWindowAndRenderer( - scale * GG_SCREEN_WIDTH, scale * GG_SCREEN_HEIGHT, - flags, &emu.window, &emu.renderer); + int width = config->scale * GG_SCREEN_WIDTH; + int height = config->scale * GG_SCREEN_HEIGHT; + if (!config->square_par) + height = height * GG_PIXEL_HEIGHT / GG_PIXEL_WIDTH; + SDL_CreateWindowAndRenderer(width, height, flags, + &emu.window, &emu.renderer); if (!emu.window) FATAL("SDL failed to create a window: %s", SDL_GetError()); @@ -116,7 +119,9 @@ static void setup_graphics(bool fullscreen, unsigned scale) emu.pixels = cr_malloc( sizeof(uint32_t) * GG_SCREEN_WIDTH * GG_SCREEN_HEIGHT); - SDL_RenderSetLogicalSize(emu.renderer, GG_SCREEN_WIDTH, GG_SCREEN_HEIGHT); + SDL_RenderSetLogicalSize(emu.renderer, + config->square_par ? GG_SCREEN_WIDTH : GG_LOGICAL_WIDTH, + config->square_par ? GG_SCREEN_HEIGHT : GG_LOGICAL_HEIGHT); SDL_SetTextureBlendMode(emu.texture, SDL_BLENDMODE_BLEND); SDL_SetWindowTitle(emu.window, "crater"); SDL_ShowCursor(SDL_DISABLE); @@ -130,13 +135,13 @@ static void setup_graphics(bool fullscreen, unsigned scale) /* Set up SDL. */ -static void setup_sdl(bool fullscreen, unsigned scale) +static void setup_sdl(Config *config) { if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_GAMECONTROLLER) < 0) FATAL("SDL failed to initialize: %s", SDL_GetError()); setup_input(); - setup_graphics(fullscreen, scale); + setup_graphics(config); } /* @@ -370,7 +375,7 @@ void emulate(ROM *rom, Config *config) emu.gg = gamegear_create(); signal(SIGINT, handle_sigint); - setup_sdl(config->fullscreen, config->scale); + setup_sdl(config); gamegear_attach_callback(emu.gg, frame_callback); gamegear_attach_display(emu.gg, emu.pixels); diff --git a/src/gamegear.h b/src/gamegear.h index 420c058..7d91cfb 100644 --- a/src/gamegear.h +++ b/src/gamegear.h @@ -15,6 +15,10 @@ #define GG_SCREEN_WIDTH 160 #define GG_SCREEN_HEIGHT 144 +#define GG_PIXEL_WIDTH 8 +#define GG_PIXEL_HEIGHT 7 +#define GG_LOGICAL_WIDTH (GG_SCREEN_WIDTH * GG_PIXEL_WIDTH) +#define GG_LOGICAL_HEIGHT (GG_SCREEN_HEIGHT * GG_PIXEL_HEIGHT) #define GG_FPS 60 #define GG_EXC_BUFF_SIZE 128