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