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.
 
 
 
 
 

422 lines
12 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(Config *config)
  79. {
  80. SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest");
  81. uint32_t flags;
  82. if (config->fullscreen)
  83. flags = SDL_WINDOW_FULLSCREEN_DESKTOP;
  84. else
  85. flags = SDL_WINDOW_RESIZABLE;
  86. int width = config->scale * GG_SCREEN_WIDTH;
  87. int height = config->scale * GG_SCREEN_HEIGHT;
  88. if (!config->square_par)
  89. height = height * GG_PIXEL_HEIGHT / GG_PIXEL_WIDTH;
  90. emu.window = SDL_CreateWindow("crater", SDL_WINDOWPOS_UNDEFINED,
  91. SDL_WINDOWPOS_UNDEFINED, width, height, flags);
  92. if (!emu.window)
  93. FATAL("SDL failed to create a window: %s", SDL_GetError());
  94. emu.renderer = SDL_CreateRenderer(emu.window, -1,
  95. SDL_RENDERER_ACCELERATED|SDL_RENDERER_PRESENTVSYNC);
  96. if (!emu.renderer) {
  97. // Fall back to software renderer (or a hardware renderer
  98. // without vsync, if one exists?)
  99. emu.renderer = SDL_CreateRenderer(emu.window, -1, 0);
  100. if (!emu.renderer)
  101. FATAL("SDL failed to create a renderer: %s", SDL_GetError());
  102. }
  103. SDL_RendererInfo info;
  104. SDL_GetRendererInfo(emu.renderer, &info);
  105. DEBUG("Using %s renderer", info.name);
  106. emu.texture = SDL_CreateTexture(emu.renderer, SDL_PIXELFORMAT_ARGB8888,
  107. SDL_TEXTUREACCESS_STREAMING, GG_SCREEN_WIDTH, GG_SCREEN_HEIGHT);
  108. if (!emu.texture)
  109. FATAL("SDL failed to create a texture: %s", SDL_GetError());
  110. emu.pixels = cr_malloc(
  111. sizeof(uint32_t) * GG_SCREEN_WIDTH * GG_SCREEN_HEIGHT);
  112. SDL_RenderSetLogicalSize(emu.renderer,
  113. config->square_par ? GG_SCREEN_WIDTH : GG_LOGICAL_WIDTH,
  114. config->square_par ? GG_SCREEN_HEIGHT : GG_LOGICAL_HEIGHT);
  115. SDL_SetTextureBlendMode(emu.texture, SDL_BLENDMODE_BLEND);
  116. SDL_ShowCursor(SDL_DISABLE);
  117. SDL_SetRenderDrawColor(emu.renderer, 0x00, 0x00, 0x00, 0xFF);
  118. SDL_RenderClear(emu.renderer);
  119. SDL_RenderPresent(emu.renderer);
  120. }
  121. /*
  122. Set up SDL.
  123. */
  124. static void setup_sdl(Config *config)
  125. {
  126. if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_GAMECONTROLLER) < 0)
  127. FATAL("SDL failed to initialize: %s", SDL_GetError());
  128. setup_input();
  129. setup_graphics(config);
  130. }
  131. /*
  132. Actually send the pixel data to the screen.
  133. */
  134. static void draw_frame()
  135. {
  136. void *pixels;
  137. int pitch;
  138. if (SDL_LockTexture(emu.texture, NULL, &pixels, &pitch) < 0)
  139. FATAL("SDL failed to lock texture: %s", SDL_GetError());
  140. if (pitch != sizeof(uint32_t) * GG_SCREEN_WIDTH)
  141. FATAL("SDL returned an unexpected texture pitch: %d, expected %zu",
  142. pitch, sizeof(uint32_t) * GG_SCREEN_WIDTH);
  143. memcpy(pixels, emu.pixels, sizeof(uint32_t) * GG_SCREEN_WIDTH * GG_SCREEN_HEIGHT);
  144. SDL_UnlockTexture(emu.texture);
  145. SDL_SetRenderDrawColor(emu.renderer, 0x00, 0x00, 0x00, 0xFF);
  146. SDL_RenderClear(emu.renderer);
  147. SDL_RenderCopy(emu.renderer, emu.texture, NULL, NULL);
  148. SDL_RenderPresent(emu.renderer);
  149. }
  150. /*
  151. Handle a keyboard press; translate it into a Game Gear button press.
  152. */
  153. static void handle_keypress(GameGear *gg, SDL_Keycode key, bool state)
  154. {
  155. GGButton button;
  156. switch (key) {
  157. case SDLK_UP:
  158. case SDLK_w:
  159. button = BUTTON_UP; break;
  160. case SDLK_DOWN:
  161. case SDLK_s:
  162. button = BUTTON_DOWN; break;
  163. case SDLK_LEFT:
  164. case SDLK_a:
  165. button = BUTTON_LEFT; break;
  166. case SDLK_RIGHT:
  167. case SDLK_d:
  168. button = BUTTON_RIGHT; break;
  169. case SDLK_j:
  170. case SDLK_z:
  171. case SDLK_PERIOD:
  172. button = BUTTON_TRIGGER_1; break;
  173. case SDLK_k:
  174. case SDLK_x:
  175. case SDLK_SLASH:
  176. button = BUTTON_TRIGGER_2; break;
  177. case SDLK_RETURN:
  178. case SDLK_RETURN2:
  179. case SDLK_ESCAPE:
  180. button = BUTTON_START; break;
  181. default:
  182. return;
  183. }
  184. gamegear_input(gg, button, state);
  185. }
  186. /*
  187. Handle controller input.
  188. */
  189. static void handle_controller_input(
  190. GameGear *gg, SDL_GameControllerButton input, bool state)
  191. {
  192. GGButton button;
  193. switch (input) {
  194. case SDL_CONTROLLER_BUTTON_A:
  195. button = BUTTON_TRIGGER_1; break;
  196. case SDL_CONTROLLER_BUTTON_B:
  197. button = BUTTON_TRIGGER_2; break;
  198. case SDL_CONTROLLER_BUTTON_START:
  199. button = BUTTON_START; break;
  200. case SDL_CONTROLLER_BUTTON_DPAD_UP:
  201. button = BUTTON_UP; break;
  202. case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
  203. button = BUTTON_DOWN; break;
  204. case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
  205. button = BUTTON_LEFT; break;
  206. case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
  207. button = BUTTON_RIGHT; break;
  208. default:
  209. return;
  210. }
  211. gamegear_input(gg, button, state);
  212. }
  213. /*
  214. Handle a controller being added.
  215. */
  216. static void handle_controller_added(int i)
  217. {
  218. SDL_Joystick *joy = SDL_JoystickOpen(i);
  219. SDL_JoystickID id = SDL_JoystickInstanceID(joy);
  220. for (int i = 0; i < emu.controllers.num; i++) {
  221. SDL_Joystick *other = SDL_GameControllerGetJoystick(
  222. emu.controllers.items[i]);
  223. if (SDL_JoystickInstanceID(other) == id) {
  224. TRACE("SDL added duplicate joystick %i", i);
  225. SDL_JoystickClose(joy);
  226. return;
  227. }
  228. }
  229. SDL_JoystickClose(joy);
  230. if (!SDL_IsGameController(i)) {
  231. DEBUG("SDL added joystick %i: not a game controller", i);
  232. return;
  233. }
  234. SDL_GameController *controller = SDL_GameControllerOpen(i);
  235. if (!controller) {
  236. ERROR("SDL failed to open controller %i: %s", i, SDL_GetError());
  237. return;
  238. }
  239. DEBUG("Added controller %i: %s", i, get_controller_name(controller));
  240. if (emu.controllers.num == 0) {
  241. int n = 4;
  242. emu.controllers.items = cr_calloc(n, sizeof(SDL_GameController*));
  243. emu.controllers.capacity = n;
  244. }
  245. else if (emu.controllers.num >= emu.controllers.capacity) {
  246. emu.controllers.capacity *= 2;
  247. emu.controllers.items = cr_realloc(emu.controllers.items,
  248. emu.controllers.capacity * sizeof(SDL_GameController*));
  249. }
  250. emu.controllers.items[emu.controllers.num++] = controller;
  251. }
  252. /*
  253. Handle a controller being removed.
  254. */
  255. static void handle_controller_removed(int id)
  256. {
  257. for (int i = 0; i < emu.controllers.num; i++) {
  258. SDL_GameController *controller = emu.controllers.items[i];
  259. SDL_Joystick *joy = SDL_GameControllerGetJoystick(controller);
  260. if (!joy)
  261. continue;
  262. if (SDL_JoystickInstanceID(joy) == id) {
  263. DEBUG("Removed controller %i: %s", i,
  264. get_controller_name(controller));
  265. SDL_GameControllerClose(controller);
  266. for (; i < emu.controllers.num; i++)
  267. emu.controllers.items[i] = emu.controllers.items[i + 1];
  268. emu.controllers.num--;
  269. return;
  270. }
  271. }
  272. DEBUG("SDL removed unknown controller: %i", id)
  273. }
  274. /*
  275. Handle SDL events, mainly quit events and button presses.
  276. */
  277. static void handle_events(GameGear *gg)
  278. {
  279. SDL_Event event;
  280. while (SDL_PollEvent(&event)) {
  281. switch (event.type) {
  282. case SDL_QUIT:
  283. gamegear_power_off(gg);
  284. return;
  285. case SDL_KEYDOWN:
  286. handle_keypress(gg, event.key.keysym.sym, true);
  287. break;
  288. case SDL_KEYUP:
  289. handle_keypress(gg, event.key.keysym.sym, false);
  290. break;
  291. case SDL_CONTROLLERBUTTONDOWN:
  292. handle_controller_input(gg, event.cbutton.button, true);
  293. break;
  294. case SDL_CONTROLLERBUTTONUP:
  295. handle_controller_input(gg, event.cbutton.button, false);
  296. break;
  297. case SDL_CONTROLLERDEVICEADDED:
  298. handle_controller_added(event.cdevice.which);
  299. break;
  300. case SDL_CONTROLLERDEVICEREMOVED:
  301. handle_controller_removed(event.cdevice.which);
  302. break;
  303. }
  304. }
  305. }
  306. /*
  307. GameGear callback: Draw the current frame and handle SDL event logic.
  308. */
  309. static void frame_callback(GameGear *gg)
  310. {
  311. draw_frame();
  312. handle_events(gg);
  313. }
  314. /*
  315. Clean up SDL stuff allocated in setup_sdl().
  316. */
  317. static void cleanup_sdl()
  318. {
  319. free(emu.pixels);
  320. SDL_DestroyTexture(emu.texture);
  321. SDL_DestroyRenderer(emu.renderer);
  322. SDL_DestroyWindow(emu.window);
  323. for (int i = 0; i < emu.controllers.num; i++)
  324. SDL_GameControllerClose(emu.controllers.items[i]);
  325. if (emu.controllers.items)
  326. free(emu.controllers.items);
  327. SDL_Quit();
  328. emu.window = NULL;
  329. emu.renderer = NULL;
  330. emu.texture = NULL;
  331. emu.controllers.items = NULL;
  332. emu.controllers.num = emu.controllers.capacity = 0;
  333. }
  334. /*
  335. Emulate a ROM in a Game Gear while handling I/O with the host computer.
  336. Block until emulation is finished.
  337. */
  338. void emulate(ROM *rom, Config *config)
  339. {
  340. Save save;
  341. if (!config->no_saving) {
  342. if (!save_init(&save, config->sav_path, rom))
  343. return;
  344. }
  345. BIOS *bios = NULL;
  346. if (config->bios_path) {
  347. if (!(bios = bios_open(config->bios_path)))
  348. return;
  349. }
  350. emu.gg = gamegear_create();
  351. signal(SIGINT, handle_sigint);
  352. setup_sdl(config);
  353. gamegear_attach_callback(emu.gg, frame_callback);
  354. gamegear_attach_display(emu.gg, emu.pixels);
  355. gamegear_load_rom(emu.gg, rom);
  356. if (bios)
  357. gamegear_load_bios(emu.gg, bios);
  358. if (!config->no_saving)
  359. gamegear_load_save(emu.gg, &save);
  360. gamegear_simulate(emu.gg);
  361. if (gamegear_get_exception(emu.gg))
  362. ERROR("caught exception: %s", gamegear_get_exception(emu.gg))
  363. else
  364. WARN("caught signal, stopping...")
  365. if (DEBUG_LEVEL)
  366. gamegear_print_state(emu.gg);
  367. cleanup_sdl();
  368. signal(SIGINT, SIG_DFL);
  369. gamegear_destroy(emu.gg);
  370. emu.gg = NULL;
  371. if (!config->no_saving)
  372. save_free(&save);
  373. }