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.
 
 
 
 
 

399 lignes
11 KiB

  1. /* Copyright (C) 2014-2019 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 <unistd.h>
  6. #include <SDL.h>
  7. #include "emulator.h"
  8. #include "config.h"
  9. #include "gamegear.h"
  10. #include "logging.h"
  11. #include "save.h"
  12. #include "util.h"
  13. typedef struct {
  14. SDL_GameController **items;
  15. int num, capacity;
  16. } Controllers;
  17. typedef struct {
  18. GameGear *gg;
  19. SDL_Window *window;
  20. SDL_Renderer *renderer;
  21. SDL_Texture *texture;
  22. uint32_t *pixels;
  23. Controllers controllers;
  24. } Emulator;
  25. static Emulator emu;
  26. /*
  27. Signal handler for SIGINT. Tells the GameGear to power off, if it exists.
  28. */
  29. static void handle_sigint(int sig)
  30. {
  31. (void) sig;
  32. if (emu.gg)
  33. gamegear_power_off(emu.gg); // Safe!
  34. }
  35. /*
  36. Get the name of a SDL game controller.
  37. */
  38. static const char *get_controller_name(SDL_GameController *controller)
  39. {
  40. const char *name = SDL_GameControllerName(controller);
  41. if (name)
  42. return name;
  43. return "(UNKNOWN)";
  44. }
  45. /*
  46. Set up SDL for accepting controller input.
  47. */
  48. static void setup_input()
  49. {
  50. if (!access(CONTROLLER_DB_PATH, R_OK)) {
  51. if (SDL_GameControllerAddMappingsFromFile(CONTROLLER_DB_PATH) < 0)
  52. ERROR("SDL failed to load controller mappings: %s", SDL_GetError());
  53. }
  54. int i, c = 0, n = SDL_NumJoysticks();
  55. if (n <= 0)
  56. return;
  57. SDL_GameController **items = cr_calloc(n, sizeof(SDL_GameController*));
  58. for (i = 0; i < n; i++) {
  59. if (!SDL_IsGameController(i)) {
  60. DEBUG("Ignoring joystick %i: not a game controller", i);
  61. continue;
  62. }
  63. SDL_GameController *controller = SDL_GameControllerOpen(i);
  64. if (!controller) {
  65. ERROR("SDL failed to open controller %i: %s", i, SDL_GetError());
  66. continue;
  67. }
  68. DEBUG("Loaded controller %i: %s", i, get_controller_name(controller));
  69. items[c++] = controller;
  70. }
  71. emu.controllers.items = items;
  72. emu.controllers.num = c;
  73. emu.controllers.capacity = n;
  74. }
  75. /*
  76. Set up SDL for drawing the game.
  77. */
  78. static void setup_graphics(bool fullscreen, unsigned scale)
  79. {
  80. SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest");
  81. uint32_t flags;
  82. if (fullscreen)
  83. flags = SDL_WINDOW_FULLSCREEN_DESKTOP;
  84. else
  85. flags = SDL_WINDOW_RESIZABLE;
  86. SDL_CreateWindowAndRenderer(
  87. scale * GG_SCREEN_WIDTH, scale * GG_SCREEN_HEIGHT,
  88. flags, &emu.window, &emu.renderer);
  89. if (!emu.window)
  90. FATAL("SDL failed to create a window: %s", SDL_GetError());
  91. if (!emu.renderer)
  92. FATAL("SDL failed to create a renderer: %s", SDL_GetError());
  93. emu.texture = SDL_CreateTexture(emu.renderer, SDL_PIXELFORMAT_ARGB8888,
  94. SDL_TEXTUREACCESS_STREAMING, GG_SCREEN_WIDTH, GG_SCREEN_HEIGHT);
  95. if (!emu.texture)
  96. FATAL("SDL failed to create a texture: %s", SDL_GetError());
  97. emu.pixels = cr_malloc(
  98. sizeof(uint32_t) * GG_SCREEN_WIDTH * GG_SCREEN_HEIGHT);
  99. SDL_RenderSetLogicalSize(emu.renderer, GG_SCREEN_WIDTH, GG_SCREEN_HEIGHT);
  100. SDL_SetTextureBlendMode(emu.texture, SDL_BLENDMODE_BLEND);
  101. SDL_SetWindowTitle(emu.window, "crater");
  102. SDL_ShowCursor(SDL_DISABLE);
  103. SDL_GL_SetSwapInterval(1); // Vsync
  104. SDL_SetRenderDrawColor(emu.renderer, 0x00, 0x00, 0x00, 0xFF);
  105. SDL_RenderClear(emu.renderer);
  106. SDL_RenderPresent(emu.renderer);
  107. }
  108. /*
  109. Set up SDL.
  110. */
  111. static void setup_sdl(bool fullscreen, unsigned scale)
  112. {
  113. if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_GAMECONTROLLER) < 0)
  114. FATAL("SDL failed to initialize: %s", SDL_GetError());
  115. setup_input();
  116. setup_graphics(fullscreen, scale);
  117. }
  118. /*
  119. Actually send the pixel data to the screen.
  120. */
  121. static void draw_frame()
  122. {
  123. SDL_UpdateTexture(emu.texture, NULL, emu.pixels,
  124. GG_SCREEN_WIDTH * sizeof(uint32_t));
  125. SDL_SetRenderDrawColor(emu.renderer, 0x00, 0x00, 0x00, 0xFF);
  126. SDL_RenderClear(emu.renderer);
  127. SDL_RenderCopy(emu.renderer, emu.texture, NULL, NULL);
  128. SDL_RenderPresent(emu.renderer);
  129. }
  130. /*
  131. Handle a keyboard press; translate it into a Game Gear button press.
  132. */
  133. static void handle_keypress(GameGear *gg, SDL_Keycode key, bool state)
  134. {
  135. GGButton button;
  136. switch (key) {
  137. case SDLK_UP:
  138. case SDLK_w:
  139. button = BUTTON_UP; break;
  140. case SDLK_DOWN:
  141. case SDLK_s:
  142. button = BUTTON_DOWN; break;
  143. case SDLK_LEFT:
  144. case SDLK_a:
  145. button = BUTTON_LEFT; break;
  146. case SDLK_RIGHT:
  147. case SDLK_d:
  148. button = BUTTON_RIGHT; break;
  149. case SDLK_j:
  150. case SDLK_z:
  151. case SDLK_PERIOD:
  152. button = BUTTON_TRIGGER_1; break;
  153. case SDLK_k:
  154. case SDLK_x:
  155. case SDLK_SLASH:
  156. button = BUTTON_TRIGGER_2; break;
  157. case SDLK_RETURN:
  158. case SDLK_RETURN2:
  159. case SDLK_ESCAPE:
  160. button = BUTTON_START; break;
  161. default:
  162. return;
  163. }
  164. gamegear_input(gg, button, state);
  165. }
  166. /*
  167. Handle controller input.
  168. */
  169. static void handle_controller_input(
  170. GameGear *gg, SDL_GameControllerButton input, bool state)
  171. {
  172. GGButton button;
  173. switch (input) {
  174. case SDL_CONTROLLER_BUTTON_A:
  175. button = BUTTON_TRIGGER_1; break;
  176. case SDL_CONTROLLER_BUTTON_B:
  177. button = BUTTON_TRIGGER_2; break;
  178. case SDL_CONTROLLER_BUTTON_START:
  179. button = BUTTON_START; break;
  180. case SDL_CONTROLLER_BUTTON_DPAD_UP:
  181. button = BUTTON_UP; break;
  182. case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
  183. button = BUTTON_DOWN; break;
  184. case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
  185. button = BUTTON_LEFT; break;
  186. case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
  187. button = BUTTON_RIGHT; break;
  188. default:
  189. return;
  190. }
  191. gamegear_input(gg, button, state);
  192. }
  193. /*
  194. Handle a controller being added.
  195. */
  196. static void handle_controller_added(int i)
  197. {
  198. SDL_Joystick *joy = SDL_JoystickOpen(i);
  199. SDL_JoystickID id = SDL_JoystickInstanceID(joy);
  200. for (int i = 0; i < emu.controllers.num; i++) {
  201. SDL_Joystick *other = SDL_GameControllerGetJoystick(
  202. emu.controllers.items[i]);
  203. if (SDL_JoystickInstanceID(other) == id) {
  204. TRACE("SDL added duplicate joystick %i", i);
  205. SDL_JoystickClose(joy);
  206. return;
  207. }
  208. }
  209. SDL_JoystickClose(joy);
  210. if (!SDL_IsGameController(i)) {
  211. DEBUG("SDL added joystick %i: not a game controller", i);
  212. return;
  213. }
  214. SDL_GameController *controller = SDL_GameControllerOpen(i);
  215. if (!controller) {
  216. ERROR("SDL failed to open controller %i: %s", i, SDL_GetError());
  217. return;
  218. }
  219. DEBUG("Added controller %i: %s", i, get_controller_name(controller));
  220. if (emu.controllers.num == 0) {
  221. int n = 4;
  222. emu.controllers.items = cr_calloc(n, sizeof(SDL_GameController*));
  223. emu.controllers.capacity = n;
  224. }
  225. else if (emu.controllers.num >= emu.controllers.capacity) {
  226. emu.controllers.capacity *= 2;
  227. emu.controllers.items = cr_realloc(emu.controllers.items,
  228. emu.controllers.capacity * sizeof(SDL_GameController*));
  229. }
  230. emu.controllers.items[emu.controllers.num++] = controller;
  231. }
  232. /*
  233. Handle a controller being removed.
  234. */
  235. static void handle_controller_removed(int id)
  236. {
  237. for (int i = 0; i < emu.controllers.num; i++) {
  238. SDL_GameController *controller = emu.controllers.items[i];
  239. SDL_Joystick *joy = SDL_GameControllerGetJoystick(controller);
  240. if (!joy)
  241. continue;
  242. if (SDL_JoystickInstanceID(joy) == id) {
  243. DEBUG("Removed controller %i: %s", i,
  244. get_controller_name(controller));
  245. SDL_GameControllerClose(controller);
  246. for (; i < emu.controllers.num; i++)
  247. emu.controllers.items[i] = emu.controllers.items[i + 1];
  248. emu.controllers.num--;
  249. return;
  250. }
  251. }
  252. DEBUG("SDL removed unknown controller: %i", id)
  253. }
  254. /*
  255. Handle SDL events, mainly quit events and button presses.
  256. */
  257. static void handle_events(GameGear *gg)
  258. {
  259. SDL_Event event;
  260. while (SDL_PollEvent(&event)) {
  261. switch (event.type) {
  262. case SDL_QUIT:
  263. gamegear_power_off(gg);
  264. return;
  265. case SDL_KEYDOWN:
  266. handle_keypress(gg, event.key.keysym.sym, true);
  267. break;
  268. case SDL_KEYUP:
  269. handle_keypress(gg, event.key.keysym.sym, false);
  270. break;
  271. case SDL_CONTROLLERBUTTONDOWN:
  272. handle_controller_input(gg, event.cbutton.button, true);
  273. break;
  274. case SDL_CONTROLLERBUTTONUP:
  275. handle_controller_input(gg, event.cbutton.button, false);
  276. break;
  277. case SDL_CONTROLLERDEVICEADDED:
  278. handle_controller_added(event.cdevice.which);
  279. break;
  280. case SDL_CONTROLLERDEVICEREMOVED:
  281. handle_controller_removed(event.cdevice.which);
  282. break;
  283. }
  284. }
  285. }
  286. /*
  287. GameGear callback: Draw the current frame and handle SDL event logic.
  288. */
  289. static void frame_callback(GameGear *gg)
  290. {
  291. draw_frame();
  292. handle_events(gg);
  293. }
  294. /*
  295. Clean up SDL stuff allocated in setup_sdl().
  296. */
  297. static void cleanup_sdl()
  298. {
  299. free(emu.pixels);
  300. SDL_DestroyTexture(emu.texture);
  301. SDL_DestroyRenderer(emu.renderer);
  302. SDL_DestroyWindow(emu.window);
  303. for (int i = 0; i < emu.controllers.num; i++)
  304. SDL_GameControllerClose(emu.controllers.items[i]);
  305. if (emu.controllers.items)
  306. free(emu.controllers.items);
  307. SDL_Quit();
  308. emu.window = NULL;
  309. emu.renderer = NULL;
  310. emu.texture = NULL;
  311. emu.controllers.items = NULL;
  312. emu.controllers.num = emu.controllers.capacity = 0;
  313. }
  314. /*
  315. Emulate a ROM in a Game Gear while handling I/O with the host computer.
  316. Block until emulation is finished.
  317. */
  318. void emulate(ROM *rom, Config *config)
  319. {
  320. Save save;
  321. if (!config->no_saving) {
  322. if (!save_init(&save, config->sav_path, rom))
  323. return;
  324. }
  325. BIOS *bios = NULL;
  326. if (config->bios_path) {
  327. if (!(bios = bios_open(config->bios_path)))
  328. return;
  329. }
  330. emu.gg = gamegear_create();
  331. signal(SIGINT, handle_sigint);
  332. setup_sdl(config->fullscreen, config->scale);
  333. gamegear_attach_callback(emu.gg, frame_callback);
  334. gamegear_attach_display(emu.gg, emu.pixels);
  335. gamegear_load_rom(emu.gg, rom);
  336. if (bios)
  337. gamegear_load_bios(emu.gg, bios);
  338. if (!config->no_saving)
  339. gamegear_load_save(emu.gg, &save);
  340. gamegear_simulate(emu.gg);
  341. if (gamegear_get_exception(emu.gg))
  342. ERROR("caught exception: %s", gamegear_get_exception(emu.gg))
  343. else
  344. WARN("caught signal, stopping...")
  345. if (DEBUG_LEVEL)
  346. gamegear_print_state(emu.gg);
  347. cleanup_sdl();
  348. signal(SIGINT, SIG_DFL);
  349. gamegear_destroy(emu.gg);
  350. emu.gg = NULL;
  351. if (!config->no_saving)
  352. save_free(&save);
  353. }