| @@ -62,9 +62,16 @@ entirely with `--no-save`. | |||||
| Add `--debug` (`-g`) to show logging information while running. Pass it twice | Add `--debug` (`-g`) to show logging information while running. Pass it twice | ||||
| (`-gg`) to show more detailed logs, including an emulator trace. | (`-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 | `./crater -h` gives (fairly basic) command-line usage, and `./crater -v` gives | ||||
| the current version. | the current version. | ||||
| [par]: https://pineight.com/mw/index.php?title=Dot_clock_rates | |||||
| ### Input | ### Input | ||||
| crater supports keyboard and joystick/controller input. | crater supports keyboard and joystick/controller input. | ||||
| @@ -44,6 +44,8 @@ static void print_help(const char *arg1) | |||||
| " the Game Gear does not usually require BIOS)\n" | " the Game Gear does not usually require BIOS)\n" | ||||
| " -x, --scale <n> scale the game screen by an integer factor\n" | " -x, --scale <n> scale the game screen by an integer factor\n" | ||||
| " (applies to windowed mode only; defaults to 4)\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 <in> [<out>]\n" | " -a, --assemble <in> [<out>]\n" | ||||
| " convert z80 assembly source code into a binary file that\n" | " convert z80 assembly source code into a binary file that\n" | ||||
| " can be run by crater\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; | config->scale = scale; | ||||
| } | } | ||||
| else if (arg_check(arg, "q", "square")) { | |||||
| config->square_par = true; | |||||
| } | |||||
| else if (arg_check(arg, "a", "assemble")) { | else if (arg_check(arg, "a", "assemble")) { | ||||
| if (args->paths_read >= 1) { | if (args->paths_read >= 1) { | ||||
| config->src_path = config->rom_path; | config->src_path = config->rom_path; | ||||
| @@ -367,7 +372,8 @@ static bool sanity_check(Config *config) | |||||
| } else if (config->assemble && config->disassemble) { | } else if (config->assemble && config->disassemble) { | ||||
| ERROR("cannot assemble and disassemble at the same time") | ERROR("cannot assemble and disassemble at the same time") | ||||
| return false; | 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") | ERROR("cannot specify emulator options in assembler mode") | ||||
| return false; | return false; | ||||
| } else if (assembler && !config->src_path) { | } else if (assembler && !config->src_path) { | ||||
| @@ -424,6 +430,7 @@ int config_create(Config** config_ptr, int argc, char* argv[]) | |||||
| config->fullscreen = false; | config->fullscreen = false; | ||||
| config->no_saving = false; | config->no_saving = false; | ||||
| config->scale = 0; | config->scale = 0; | ||||
| config->square_par = false; | |||||
| config->rom_path = NULL; | config->rom_path = NULL; | ||||
| config->sav_path = NULL; | config->sav_path = NULL; | ||||
| config->bios_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("- fullscreen: %s", config->fullscreen ? "true" : "false") | ||||
| DEBUG("- no_saving: %s", config->no_saving ? "true" : "false") | DEBUG("- no_saving: %s", config->no_saving ? "true" : "false") | ||||
| DEBUG("- scale: %d", config->scale) | 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("- rom_path: %s", config->rom_path ? config->rom_path : "(null)") | ||||
| DEBUG("- sav_path: %s", config->sav_path ? config->sav_path : "(null)") | DEBUG("- sav_path: %s", config->sav_path ? config->sav_path : "(null)") | ||||
| DEBUG("- bios_path: %s", config->bios_path ? config->bios_path : "(null)") | DEBUG("- bios_path: %s", config->bios_path ? config->bios_path : "(null)") | ||||
| @@ -14,10 +14,9 @@ | |||||
| /* | /* | ||||
| We need some sort of maximum scale - with a native resolution of 160 x 144, | 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 */ | /* Structs */ | ||||
| @@ -28,6 +27,7 @@ typedef struct { | |||||
| bool fullscreen; | bool fullscreen; | ||||
| bool no_saving; | bool no_saving; | ||||
| unsigned scale; | unsigned scale; | ||||
| bool square_par; | |||||
| char *rom_path; | char *rom_path; | ||||
| char *sav_path; | char *sav_path; | ||||
| char *bios_path; | char *bios_path; | ||||
| @@ -88,19 +88,22 @@ static void setup_input() | |||||
| /* | /* | ||||
| Set up SDL for drawing the game. | 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"); | SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest"); | ||||
| uint32_t flags; | uint32_t flags; | ||||
| if (fullscreen) | |||||
| if (config->fullscreen) | |||||
| flags = SDL_WINDOW_FULLSCREEN_DESKTOP; | flags = SDL_WINDOW_FULLSCREEN_DESKTOP; | ||||
| else | else | ||||
| flags = SDL_WINDOW_RESIZABLE; | 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) | if (!emu.window) | ||||
| FATAL("SDL failed to create a window: %s", SDL_GetError()); | 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( | emu.pixels = cr_malloc( | ||||
| sizeof(uint32_t) * GG_SCREEN_WIDTH * GG_SCREEN_HEIGHT); | 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_SetTextureBlendMode(emu.texture, SDL_BLENDMODE_BLEND); | ||||
| SDL_SetWindowTitle(emu.window, "crater"); | SDL_SetWindowTitle(emu.window, "crater"); | ||||
| SDL_ShowCursor(SDL_DISABLE); | SDL_ShowCursor(SDL_DISABLE); | ||||
| @@ -130,13 +135,13 @@ static void setup_graphics(bool fullscreen, unsigned scale) | |||||
| /* | /* | ||||
| Set up SDL. | 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) | if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_GAMECONTROLLER) < 0) | ||||
| FATAL("SDL failed to initialize: %s", SDL_GetError()); | FATAL("SDL failed to initialize: %s", SDL_GetError()); | ||||
| setup_input(); | setup_input(); | ||||
| setup_graphics(fullscreen, scale); | |||||
| setup_graphics(config); | |||||
| } | } | ||||
| /* | /* | ||||
| @@ -370,7 +375,7 @@ void emulate(ROM *rom, Config *config) | |||||
| emu.gg = gamegear_create(); | emu.gg = gamegear_create(); | ||||
| signal(SIGINT, handle_sigint); | signal(SIGINT, handle_sigint); | ||||
| setup_sdl(config->fullscreen, config->scale); | |||||
| setup_sdl(config); | |||||
| gamegear_attach_callback(emu.gg, frame_callback); | gamegear_attach_callback(emu.gg, frame_callback); | ||||
| gamegear_attach_display(emu.gg, emu.pixels); | gamegear_attach_display(emu.gg, emu.pixels); | ||||
| @@ -15,6 +15,10 @@ | |||||
| #define GG_SCREEN_WIDTH 160 | #define GG_SCREEN_WIDTH 160 | ||||
| #define GG_SCREEN_HEIGHT 144 | #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_FPS 60 | ||||
| #define GG_EXC_BUFF_SIZE 128 | #define GG_EXC_BUFF_SIZE 128 | ||||