An emulator, assembler, and disassembler for the Sega Game Gear
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 
 

222 lignes
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. }