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.
 
 
 
 
 

267 lines
6.9 KiB

  1. /* Copyright (C) 2014-2017 Ben Kurtovic <ben.kurtovic@gmail.com>
  2. Released under the terms of the MIT License. See LICENSE for details. */
  3. #include <stdlib.h>
  4. #include <unistd.h>
  5. #include "gamegear.h"
  6. #include "logging.h"
  7. #include "util.h"
  8. /* Clock speed in Hz was taken from the official Sega GG documentation */
  9. #define CPU_CLOCK_SPEED (3579545.)
  10. #define CYCLES_PER_FRAME (CPU_CLOCK_SPEED / GG_FPS)
  11. #define CYCLES_PER_LINE (CYCLES_PER_FRAME / VDP_LINES_PER_FRAME)
  12. #define NS_PER_FRAME (1000 * 1000 * 1000 / GG_FPS)
  13. #define SET_EXC(...) snprintf(gg->exc_buffer, GG_EXC_BUFF_SIZE, __VA_ARGS__);
  14. /*
  15. Create and return a pointer to a new GameGear object.
  16. The GameGear operates in headless mode by default (i.e., without any
  17. noticeable output). You'll probably want to attach a frame-completion
  18. callback with gamegear_attach_callback() and a display with
  19. gamegear_attach_display().
  20. */
  21. GameGear* gamegear_create()
  22. {
  23. GameGear *gg = cr_malloc(sizeof(GameGear));
  24. mmu_init(&gg->mmu);
  25. vdp_init(&gg->vdp);
  26. psg_init(&gg->psg);
  27. io_init(&gg->io, &gg->mmu, &gg->vdp, &gg->psg);
  28. z80_init(&gg->cpu, &gg->mmu, &gg->io);
  29. gg->powered = false;
  30. gg->callback = NULL;
  31. gg->exc_buffer[0] = '\0';
  32. return gg;
  33. }
  34. /*
  35. Destroy a previously-allocated GameGear object.
  36. Does *not* destroy any loaded ROM objects.
  37. */
  38. void gamegear_destroy(GameGear *gg)
  39. {
  40. mmu_free(&gg->mmu);
  41. vdp_free(&gg->vdp);
  42. psg_free(&gg->psg);
  43. free(gg);
  44. }
  45. /*
  46. Load a ROM image into the GameGear object.
  47. Does *not* steal a reference to the ROM object, so it must be kept alive
  48. until another ROM is loaded or the GameGear is destroyed. Calling this
  49. function while the GameGear is powered on has no effect.
  50. */
  51. void gamegear_load_rom(GameGear *gg, const ROM *rom)
  52. {
  53. if (gg->powered)
  54. return;
  55. mmu_load_rom(&gg->mmu, rom->data, rom->size);
  56. }
  57. /*
  58. Load BIOS into the GameGear object.
  59. The same rules with gamegear_load_rom() apply here.
  60. */
  61. void gamegear_load_bios(GameGear *gg, const BIOS *bios)
  62. {
  63. if (gg->powered)
  64. return;
  65. mmu_load_bios(&gg->mmu, bios->data);
  66. }
  67. /*
  68. Load a game save into the GameGear object.
  69. The same rules with gamegear_load_rom() apply here.
  70. */
  71. void gamegear_load_save(GameGear *gg, Save *save)
  72. {
  73. if (gg->powered)
  74. return;
  75. mmu_load_save(&gg->mmu, save);
  76. }
  77. /*
  78. Update the GameGear's button/joystick state.
  79. 'state' should be true when the button is pressed, and false when it is
  80. released.
  81. */
  82. void gamegear_input(GameGear *gg, GGButton button, bool state)
  83. {
  84. if (button == BUTTON_START)
  85. io_set_start(&gg->io, state);
  86. else
  87. io_set_button(&gg->io, button, state);
  88. }
  89. /*
  90. Power on the GameGear.
  91. This clears the exception buffer and executes boot code (e.g. clearing
  92. memory and setting initial register values).
  93. */
  94. static void power_on(GameGear *gg)
  95. {
  96. gg->exc_buffer[0] = '\0';
  97. gg->powered = true;
  98. mmu_power(&gg->mmu);
  99. vdp_power(&gg->vdp);
  100. io_power(&gg->io);
  101. z80_power(&gg->cpu);
  102. }
  103. /*
  104. Power off the GameGear.
  105. This function *may* be used while the GameGear is running to trigger a safe
  106. shutdown at the next opportunity. It is also reentrant. If the GameGear is
  107. already off, it will do nothing.
  108. */
  109. void gamegear_power_off(GameGear *gg)
  110. {
  111. gg->powered = false;
  112. }
  113. /*
  114. Set a callback to be triggered whenever the GameGear completes a frame.
  115. The callback is passed a reference to the GameGear object.
  116. */
  117. void gamegear_attach_callback(GameGear *gg, GGFrameCallback callback)
  118. {
  119. gg->callback = callback;
  120. }
  121. /*
  122. Set a display to written to whenever the GameGear draws a pixel.
  123. The array must be (GG_SCREEN_WIDTH * GG_SCREEN_HEIGHT) pixels large, where
  124. each pixel is a 32-bit integer in ARGB order (i.e., A is the top 8 bits).
  125. */
  126. void gamegear_attach_display(GameGear *gg, uint32_t *pixels)
  127. {
  128. gg->vdp.pixels = pixels;
  129. }
  130. /*
  131. Reset any callbacks or displays attached to the GameGear.
  132. This returns the GameGear to headless mode.
  133. */
  134. void gamegear_detach(GameGear *gg)
  135. {
  136. gg->callback = NULL;
  137. gg->vdp.pixels = NULL;
  138. }
  139. /*
  140. Simulate the GameGear for one frame.
  141. This function simulates the number of clock cycles corresponding to 1/60th
  142. of a second. The return value indicates whether an exception flag has been
  143. set somewhere. If true, emulation must be stopped.
  144. */
  145. static bool simulate_frame(GameGear *gg)
  146. {
  147. size_t line;
  148. bool except;
  149. for (line = 0; line < VDP_LINES_PER_FRAME; line++) {
  150. except = z80_do_cycles(&gg->cpu, CYCLES_PER_LINE);
  151. if (except)
  152. return true;
  153. vdp_simulate_line(&gg->vdp);
  154. }
  155. return false;
  156. }
  157. /*
  158. Simulate the GameGear.
  159. The GameGear must start out in an unpowered state; it will be powered only
  160. during the simulation. This function blocks until the simulation ends,
  161. either by an exception occurring or someone calling gamegear_power_off().
  162. If a callback has been set with gamegear_set_callback(), then we'll trigger
  163. it after every frame has been simulated (sixty times per second).
  164. Exceptions can be retrieved after this call with gamegear_get_exception().
  165. If the simulation ended normally, then that function will return NULL.
  166. */
  167. void gamegear_simulate(GameGear *gg)
  168. {
  169. if (gg->powered)
  170. return;
  171. DEBUG("GameGear: powering on")
  172. power_on(gg);
  173. while (gg->powered) {
  174. uint64_t start = get_time_ns(), delta;
  175. if (simulate_frame(gg) || !gg->powered)
  176. break;
  177. if (gg->callback)
  178. gg->callback(gg);
  179. delta = get_time_ns() - start;
  180. if (delta < NS_PER_FRAME)
  181. usleep((NS_PER_FRAME - delta) / 1000);
  182. }
  183. DEBUG("GameGear: powering off")
  184. gamegear_power_off(gg);
  185. }
  186. /*
  187. If an exception flag has been set in the GameGear, return the reason.
  188. This function returns a const pointer to a buffer holding a human-readable
  189. exception string (although it may be cryptic to an end-user). The buffer is
  190. owned by the GameGear object and should not be freed - its contents last
  191. until the GameGear is powered on. If no exception flag is set, this
  192. function returns NULL.
  193. */
  194. const char* gamegear_get_exception(GameGear *gg)
  195. {
  196. if (!gg->exc_buffer[0]) {
  197. if (gg->cpu.except) {
  198. switch (gg->cpu.exc_code) {
  199. case Z80_EXC_NOT_POWERED:
  200. SET_EXC("CPU not powered")
  201. break;
  202. case Z80_EXC_UNIMPLEMENTED_OPCODE:
  203. SET_EXC("unimplemented opcode: 0x%02X", gg->cpu.exc_data)
  204. break;
  205. default:
  206. SET_EXC("unknown exception")
  207. break;
  208. }
  209. } else {
  210. return NULL;
  211. }
  212. }
  213. return gg->exc_buffer;
  214. }
  215. /*
  216. @DEBUG_LEVEL
  217. Print out some state info to stdout: Z80 and VDP register values, etc.
  218. */
  219. void gamegear_print_state(const GameGear *gg)
  220. {
  221. z80_dump_registers(&gg->cpu);
  222. vdp_dump_registers(&gg->vdp);
  223. }