An emulator, assembler, and disassembler for the Sega Game Gear
Não pode escolher mais do que 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 
 

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