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.
 
 
 
 
 

212 lines
5.4 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. uint32_t flags;
  36. if (fullscreen)
  37. flags = SDL_WINDOW_FULLSCREEN_DESKTOP;
  38. else
  39. flags = SDL_WINDOW_RESIZABLE;
  40. SDL_CreateWindowAndRenderer(
  41. scale * GG_SCREEN_WIDTH, scale * GG_SCREEN_HEIGHT,
  42. flags, &emu.window, &emu.renderer);
  43. if (!emu.window)
  44. FATAL("SDL failed to create a window: %s", SDL_GetError());
  45. if (!emu.renderer)
  46. FATAL("SDL failed to create a renderer: %s", SDL_GetError());
  47. emu.texture = SDL_CreateTexture(emu.renderer, SDL_PIXELFORMAT_ARGB8888,
  48. SDL_TEXTUREACCESS_STREAMING, GG_SCREEN_WIDTH, GG_SCREEN_HEIGHT);
  49. if (!emu.texture)
  50. FATAL("SDL failed to create a texture: %s", SDL_GetError());
  51. emu.pixels = cr_malloc(
  52. sizeof(uint32_t) * GG_SCREEN_WIDTH * GG_SCREEN_HEIGHT);
  53. SDL_RenderSetLogicalSize(emu.renderer, GG_SCREEN_WIDTH, GG_SCREEN_HEIGHT);
  54. SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest");
  55. SDL_SetWindowTitle(emu.window, "crater");
  56. SDL_ShowCursor(SDL_DISABLE);
  57. SDL_GL_SetSwapInterval(1); // Vsync
  58. SDL_SetRenderDrawColor(emu.renderer, 0x00, 0x00, 0x00, 0xFF);
  59. SDL_RenderClear(emu.renderer);
  60. SDL_RenderPresent(emu.renderer);
  61. }
  62. /*
  63. Actually send the pixel data to the screen.
  64. */
  65. static void draw_frame()
  66. {
  67. SDL_UpdateTexture(emu.texture, NULL, emu.pixels,
  68. GG_SCREEN_WIDTH * sizeof(uint32_t));
  69. SDL_SetRenderDrawColor(emu.renderer, 0x00, 0x00, 0x00, 0xFF);
  70. SDL_RenderClear(emu.renderer);
  71. SDL_RenderCopy(emu.renderer, emu.texture, NULL, NULL);
  72. SDL_RenderPresent(emu.renderer);
  73. }
  74. /*
  75. Handle a keyboard press; translate it into a Game Gear button press.
  76. */
  77. static void handle_keypress(GameGear *gg, SDL_Keycode key, bool state)
  78. {
  79. GGButton button;
  80. switch (key) {
  81. case SDLK_UP:
  82. case SDLK_w:
  83. button = BUTTON_UP; break;
  84. case SDLK_DOWN:
  85. case SDLK_s:
  86. button = BUTTON_DOWN; break;
  87. case SDLK_LEFT:
  88. case SDLK_a:
  89. button = BUTTON_LEFT; break;
  90. case SDLK_RIGHT:
  91. case SDLK_d:
  92. button = BUTTON_RIGHT; break;
  93. case SDLK_j:
  94. case SDLK_z:
  95. case SDLK_PERIOD:
  96. button = BUTTON_TRIGGER_1; break;
  97. case SDLK_k:
  98. case SDLK_x:
  99. case SDLK_SLASH:
  100. button = BUTTON_TRIGGER_2; break;
  101. case SDLK_RETURN:
  102. case SDLK_RETURN2:
  103. case SDLK_ESCAPE:
  104. button = BUTTON_START; break;
  105. default:
  106. return;
  107. }
  108. gamegear_input(gg, button, state);
  109. }
  110. /*
  111. Handle SDL events, mainly quit events and button presses.
  112. */
  113. static void handle_events(GameGear *gg)
  114. {
  115. SDL_Event event;
  116. while (SDL_PollEvent(&event)) {
  117. switch (event.type) {
  118. case SDL_QUIT:
  119. gamegear_power_off(gg);
  120. return;
  121. case SDL_KEYDOWN:
  122. handle_keypress(gg, event.key.keysym.sym, true);
  123. break;
  124. case SDL_KEYUP:
  125. handle_keypress(gg, event.key.keysym.sym, false);
  126. break;
  127. }
  128. }
  129. }
  130. /*
  131. GameGear callback: Draw the current frame and handle SDL event logic.
  132. */
  133. static void frame_callback(GameGear *gg)
  134. {
  135. draw_frame();
  136. handle_events(gg);
  137. }
  138. /*
  139. Clean up SDL stuff allocated in setup_graphics().
  140. */
  141. static void cleanup_graphics()
  142. {
  143. free(emu.pixels);
  144. SDL_DestroyTexture(emu.texture);
  145. SDL_DestroyRenderer(emu.renderer);
  146. SDL_DestroyWindow(emu.window);
  147. SDL_Quit();
  148. emu.window = NULL;
  149. emu.renderer = NULL;
  150. emu.texture = NULL;
  151. }
  152. /*
  153. Emulate a ROM in a Game Gear while handling I/O with the host computer.
  154. Block until emulation is finished.
  155. */
  156. void emulate(ROM *rom, Config *config)
  157. {
  158. Save save;
  159. if (!config->no_saving) {
  160. if (!save_init(&save, config->sav_path, rom))
  161. return;
  162. }
  163. emu.gg = gamegear_create();
  164. signal(SIGINT, handle_sigint);
  165. setup_graphics(config->fullscreen, config->scale);
  166. gamegear_attach_callback(emu.gg, frame_callback);
  167. gamegear_attach_display(emu.gg, emu.pixels);
  168. gamegear_load_rom(emu.gg, rom);
  169. if (!config->no_saving)
  170. gamegear_load_save(emu.gg, &save);
  171. gamegear_simulate(emu.gg);
  172. if (gamegear_get_exception(emu.gg))
  173. ERROR("caught exception: %s", gamegear_get_exception(emu.gg))
  174. else
  175. WARN("caught signal, stopping...")
  176. if (DEBUG_LEVEL)
  177. gamegear_print_state(emu.gg);
  178. cleanup_graphics();
  179. signal(SIGINT, SIG_DFL);
  180. gamegear_destroy(emu.gg);
  181. emu.gg = NULL;
  182. if (!config->no_saving)
  183. save_free(&save);
  184. }