An emulator, assembler, and disassembler for the Sega Game Gear
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

222 lines
5.6 KiB

  1. /* Copyright (C) 2014-2017 Ben Kurtovic <ben.kurtovic@gmail.com>
  2. Released under the terms of the MIT License. See LICENSE for details. */
  3. #include <signal.h>
  4. #include <stdint.h>
  5. #include <SDL.h>
  6. #include "emulator.h"
  7. #include "gamegear.h"
  8. #include "logging.h"
  9. #include "save.h"
  10. #include "util.h"
  11. typedef struct {
  12. GameGear *gg;
  13. SDL_Window *window;
  14. SDL_Renderer *renderer;
  15. SDL_Texture *texture;
  16. uint32_t *pixels;
  17. } Emulator;
  18. static Emulator emu;
  19. /*
  20. Signal handler for SIGINT. Tells the GameGear to power off, if it exists.
  21. */
  22. static void handle_sigint(int sig)
  23. {
  24. (void) sig;
  25. if (emu.gg)
  26. gamegear_power_off(emu.gg); // Safe!
  27. }
  28. /*
  29. Set up SDL for drawing the game.
  30. */
  31. static void setup_graphics(bool fullscreen, unsigned scale)
  32. {
  33. if (SDL_Init(SDL_INIT_VIDEO) < 0)
  34. FATAL("SDL failed to initialize: %s", SDL_GetError());
  35. SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest");
  36. uint32_t flags;
  37. if (fullscreen)
  38. flags = SDL_WINDOW_FULLSCREEN_DESKTOP;
  39. else
  40. flags = SDL_WINDOW_RESIZABLE;
  41. SDL_CreateWindowAndRenderer(
  42. scale * GG_SCREEN_WIDTH, scale * GG_SCREEN_HEIGHT,
  43. flags, &emu.window, &emu.renderer);
  44. if (!emu.window)
  45. FATAL("SDL failed to create a window: %s", SDL_GetError());
  46. if (!emu.renderer)
  47. FATAL("SDL failed to create a renderer: %s", SDL_GetError());
  48. emu.texture = SDL_CreateTexture(emu.renderer, SDL_PIXELFORMAT_ARGB8888,
  49. SDL_TEXTUREACCESS_STREAMING, GG_SCREEN_WIDTH, GG_SCREEN_HEIGHT);
  50. if (!emu.texture)
  51. FATAL("SDL failed to create a texture: %s", SDL_GetError());
  52. emu.pixels = cr_malloc(
  53. sizeof(uint32_t) * GG_SCREEN_WIDTH * GG_SCREEN_HEIGHT);
  54. SDL_RenderSetLogicalSize(emu.renderer, GG_SCREEN_WIDTH, GG_SCREEN_HEIGHT);
  55. SDL_SetTextureBlendMode(emu.texture, SDL_BLENDMODE_BLEND);
  56. SDL_SetWindowTitle(emu.window, "crater");
  57. SDL_ShowCursor(SDL_DISABLE);
  58. SDL_GL_SetSwapInterval(1); // Vsync
  59. SDL_SetRenderDrawColor(emu.renderer, 0x00, 0x00, 0x00, 0xFF);
  60. SDL_RenderClear(emu.renderer);
  61. SDL_RenderPresent(emu.renderer);
  62. }
  63. /*
  64. Actually send the pixel data to the screen.
  65. */
  66. static void draw_frame()
  67. {
  68. SDL_UpdateTexture(emu.texture, NULL, emu.pixels,
  69. GG_SCREEN_WIDTH * sizeof(uint32_t));
  70. SDL_SetRenderDrawColor(emu.renderer, 0x00, 0x00, 0x00, 0xFF);
  71. SDL_RenderClear(emu.renderer);
  72. SDL_RenderCopy(emu.renderer, emu.texture, NULL, NULL);
  73. SDL_RenderPresent(emu.renderer);
  74. }
  75. /*
  76. Handle a keyboard press; translate it into a Game Gear button press.
  77. */
  78. static void handle_keypress(GameGear *gg, SDL_Keycode key, bool state)
  79. {
  80. GGButton button;
  81. switch (key) {
  82. case SDLK_UP:
  83. case SDLK_w:
  84. button = BUTTON_UP; break;
  85. case SDLK_DOWN:
  86. case SDLK_s:
  87. button = BUTTON_DOWN; break;
  88. case SDLK_LEFT:
  89. case SDLK_a:
  90. button = BUTTON_LEFT; break;
  91. case SDLK_RIGHT:
  92. case SDLK_d:
  93. button = BUTTON_RIGHT; break;
  94. case SDLK_j:
  95. case SDLK_z:
  96. case SDLK_PERIOD:
  97. button = BUTTON_TRIGGER_1; break;
  98. case SDLK_k:
  99. case SDLK_x:
  100. case SDLK_SLASH:
  101. button = BUTTON_TRIGGER_2; break;
  102. case SDLK_RETURN:
  103. case SDLK_RETURN2:
  104. case SDLK_ESCAPE:
  105. button = BUTTON_START; break;
  106. default:
  107. return;
  108. }
  109. gamegear_input(gg, button, state);
  110. }
  111. /*
  112. Handle SDL events, mainly quit events and button presses.
  113. */
  114. static void handle_events(GameGear *gg)
  115. {
  116. SDL_Event event;
  117. while (SDL_PollEvent(&event)) {
  118. switch (event.type) {
  119. case SDL_QUIT:
  120. gamegear_power_off(gg);
  121. return;
  122. case SDL_KEYDOWN:
  123. handle_keypress(gg, event.key.keysym.sym, true);
  124. break;
  125. case SDL_KEYUP:
  126. handle_keypress(gg, event.key.keysym.sym, false);
  127. break;
  128. }
  129. }
  130. }
  131. /*
  132. GameGear callback: Draw the current frame and handle SDL event logic.
  133. */
  134. static void frame_callback(GameGear *gg)
  135. {
  136. draw_frame();
  137. handle_events(gg);
  138. }
  139. /*
  140. Clean up SDL stuff allocated in setup_graphics().
  141. */
  142. static void cleanup_graphics()
  143. {
  144. free(emu.pixels);
  145. SDL_DestroyTexture(emu.texture);
  146. SDL_DestroyRenderer(emu.renderer);
  147. SDL_DestroyWindow(emu.window);
  148. SDL_Quit();
  149. emu.window = NULL;
  150. emu.renderer = NULL;
  151. emu.texture = NULL;
  152. }
  153. /*
  154. Emulate a ROM in a Game Gear while handling I/O with the host computer.
  155. Block until emulation is finished.
  156. */
  157. void emulate(ROM *rom, Config *config)
  158. {
  159. Save save;
  160. if (!config->no_saving) {
  161. if (!save_init(&save, config->sav_path, rom))
  162. return;
  163. }
  164. BIOS *bios = NULL;
  165. if (config->bios_path) {
  166. if (!(bios = bios_open(config->bios_path)))
  167. return;
  168. }
  169. emu.gg = gamegear_create();
  170. signal(SIGINT, handle_sigint);
  171. setup_graphics(config->fullscreen, config->scale);
  172. gamegear_attach_callback(emu.gg, frame_callback);
  173. gamegear_attach_display(emu.gg, emu.pixels);
  174. gamegear_load_rom(emu.gg, rom);
  175. if (bios)
  176. gamegear_load_bios(emu.gg, bios);
  177. if (!config->no_saving)
  178. gamegear_load_save(emu.gg, &save);
  179. gamegear_simulate(emu.gg);
  180. if (gamegear_get_exception(emu.gg))
  181. ERROR("caught exception: %s", gamegear_get_exception(emu.gg))
  182. else
  183. WARN("caught signal, stopping...")
  184. if (DEBUG_LEVEL)
  185. gamegear_print_state(emu.gg);
  186. cleanup_graphics();
  187. signal(SIGINT, SIG_DFL);
  188. gamegear_destroy(emu.gg);
  189. emu.gg = NULL;
  190. if (!config->no_saving)
  191. save_free(&save);
  192. }