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.
 
 
 
 
 

201 lines
5.1 KiB

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