An emulator, assembler, and disassembler for the Sega Game Gear
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 
 
 
 

399 Zeilen
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. }