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.
 
 
 
 
 

150 lines
4.2 KiB

  1. /* Copyright (C) 2014-2015 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 "gamegear.h"
  5. #include "logging.h"
  6. #include "util.h"
  7. /* Clock speed in Hz was taken from the official Sega GG documentation */
  8. #define CPU_CLOCK_SPEED (3579545.)
  9. #define CYCLES_PER_FRAME (CPU_CLOCK_SPEED / 60)
  10. #define CYCLES_PER_LINE (CYCLES_PER_FRAME / VDP_LINES_PER_FRAME)
  11. /*
  12. Create and return a pointer to a new GameGear object.
  13. */
  14. GameGear* gamegear_create()
  15. {
  16. GameGear *gg = cr_malloc(sizeof(GameGear));
  17. mmu_init(&gg->mmu);
  18. vdp_init(&gg->vdp);
  19. io_init(&gg->io, &gg->vdp);
  20. z80_init(&gg->cpu, &gg->mmu, &gg->io);
  21. gg->powered = false;
  22. gg->exc_buffer[0] = '\0';
  23. return gg;
  24. }
  25. /*
  26. Destroy a previously-allocated GameGear object.
  27. Does *not* destroy any loaded ROM objects.
  28. */
  29. void gamegear_destroy(GameGear *gg)
  30. {
  31. mmu_free(&gg->mmu);
  32. vdp_free(&gg->vdp);
  33. free(gg);
  34. }
  35. /*
  36. Load a ROM image into the GameGear object.
  37. Does *not* steal a reference to the ROM object, so it must be kept alive
  38. until another ROM is loaded or the GameGear is destroyed. Calling this
  39. function while the GameGear is powered on has no effect.
  40. */
  41. void gamegear_load(GameGear *gg, const ROM *rom)
  42. {
  43. if (gg->powered)
  44. return;
  45. mmu_load_rom(&gg->mmu, rom->data, rom->size);
  46. }
  47. /*
  48. Set the GameGear object's power state (true = on; false = off).
  49. Powering on the GameGear executes boot code (e.g. clearing memory and
  50. setting initial register values) and starts the clock. Powering it off
  51. stops the clock and clears any exception data.
  52. Setting the power state to its current value has no effect.
  53. */
  54. void gamegear_power(GameGear *gg, bool state)
  55. {
  56. if (gg->powered == state)
  57. return;
  58. if (state) {
  59. mmu_power(&gg->mmu);
  60. vdp_power(&gg->vdp);
  61. io_power(&gg->io);
  62. z80_power(&gg->cpu);
  63. } else {
  64. gg->exc_buffer[0] = '\0';
  65. }
  66. gg->powered = state;
  67. }
  68. /*
  69. Simulate the GameGear for one frame.
  70. This function simulates the number of clock cycles corresponding to 1/60th
  71. of a second. If the system is powered off, this function does nothing.
  72. The return value indicates whether an exception flag has been set
  73. somewhere. If true, emulation must be stopped. gamegear_get_exception() can
  74. be used to fetch exception information. Power-cycling the GameGear with
  75. gamegear_power(gg, false) followed by gamegear_power(gg, true) will reset
  76. the exception flag and allow emulation to restart normally.
  77. */
  78. bool gamegear_simulate_frame(GameGear *gg)
  79. {
  80. if (!gg->powered)
  81. return false;
  82. size_t line;
  83. bool except;
  84. for (line = 0; line < VDP_LINES_PER_FRAME; line++) {
  85. vdp_simulate_line(&gg->vdp);
  86. except = z80_do_cycles(&gg->cpu, CYCLES_PER_LINE);
  87. if (except)
  88. return true;
  89. }
  90. return false;
  91. }
  92. /*
  93. If an exception flag has been set in the GameGear, return the reason.
  94. This function returns a const pointer to a buffer holding a human-readable
  95. exception string (although it may be cryptic to an end-user). The buffer is
  96. owned by the GameGear object and should not be freed - its contents last
  97. until the GameGear's power state is changed. If no exception flag is set,
  98. this function returns NULL.
  99. */
  100. const char* gamegear_get_exception(GameGear *gg)
  101. {
  102. #define SET_EXC(...) snprintf(gg->exc_buffer, GG_EXC_BUFF_SIZE, __VA_ARGS__);
  103. if (!gg->powered)
  104. return NULL;
  105. if (!gg->exc_buffer[0]) {
  106. if (gg->cpu.except) {
  107. switch (gg->cpu.exc_code) {
  108. case Z80_EXC_NOT_POWERED:
  109. SET_EXC("CPU not powered")
  110. break;
  111. case Z80_EXC_UNIMPLEMENTED_OPCODE:
  112. SET_EXC("unimplemented opcode: 0x%02X", gg->cpu.exc_data)
  113. break;
  114. case Z80_EXC_IO_ERROR:
  115. SET_EXC("I/O error on port 0x%02X", gg->cpu.exc_data)
  116. break;
  117. default:
  118. SET_EXC("unknown exception")
  119. break;
  120. }
  121. } else {
  122. return NULL;
  123. }
  124. }
  125. return gg->exc_buffer;
  126. #undef SET_EXC
  127. }