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.
 
 
 
 
 

237 lines
6.3 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 <ctype.h>
  4. #include <errno.h>
  5. #include <stdbool.h>
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include <sys/stat.h>
  10. #include "rom.h"
  11. #include "logging.h"
  12. #include "util.h"
  13. #define NUM_LOCATIONS 3
  14. #define MAGIC_LEN 8
  15. #define HEADER_SIZE 16
  16. static size_t header_locations[NUM_LOCATIONS] = {0x7FF0, 0x1FF0, 0x3FF0};
  17. static const char header_magic[MAGIC_LEN + 1] = "TMR SEGA";
  18. /*
  19. Return whether or not the given ROM image size is valid.
  20. */
  21. static bool validate_size(off_t size)
  22. {
  23. if (size & (size - 1))
  24. return false; // Ensure size is a power of two
  25. off_t kbytes = size >> 10;
  26. return kbytes >= 8 && kbytes <= 1024;
  27. }
  28. #ifdef DEBUG_MODE
  29. /*
  30. DEBUG FUNCTION: Print out the header to stdout.
  31. */
  32. static void print_header(const uint8_t *header)
  33. {
  34. char header_hex[3 * HEADER_SIZE], header_chr[3 * HEADER_SIZE];
  35. for (int i = 0; i < HEADER_SIZE; i++) {
  36. snprintf(&header_hex[3 * i], 3, "%02X", header[i]);
  37. if (isprint(header[i]))
  38. snprintf(&header_chr[3 * i], 3, "%2c", header[i]);
  39. else {
  40. header_chr[3 * i] = ' ';
  41. header_chr[3 * i + 1] = '?';
  42. }
  43. header_hex[3 * i + 2] = header_chr[3 * i + 2] = ' ';
  44. }
  45. header_hex[3 * HEADER_SIZE - 1] = header_chr[3 * HEADER_SIZE - 1] = '\0';
  46. DEBUG("- header dump (hex): %s", header_hex)
  47. DEBUG("- header dump (chr): %s", header_chr)
  48. }
  49. #endif
  50. /*
  51. Parse a ROM image's header, and return whether or not it is valid.
  52. The header is 16 bytes long, consisting of:
  53. byte 0: magic ('T')
  54. byte 1: magic ('M')
  55. byte 2: magic ('R')
  56. byte 3: magic (' ')
  57. byte 4: magic ('S')
  58. byte 5: magic ('E')
  59. byte 6: magic ('G')
  60. byte 7: magic ('A')
  61. byte 8: unused
  62. byte 9: unused
  63. byte A: checksum (LSB)
  64. byte B: checksum (MSB)
  65. byte C: product code (LSB)
  66. byte D: product code (middle byte)
  67. byte E (hi nibble): product code (most-significant nibble)
  68. byte E (lo nibble): version
  69. byte F (hi nibble): region code
  70. byte F (lo nibble): ROM size
  71. (Based on: http://www.smspower.org/Development/ROMHeader)
  72. */
  73. static bool parse_header(ROM *rom, const uint8_t *header)
  74. {
  75. #ifdef DEBUG_MODE
  76. print_header(header);
  77. #endif
  78. rom->checksum = header[0xA] + (header[0xB] << 8);
  79. rom->product_code = bcd_decode(header[0xC]) +
  80. (bcd_decode(header[0xD]) * 100) + ((header[0xE] >> 4) * 10000);
  81. rom->version = header[0xE] & 0x0F;
  82. rom->region_code = header[0xF] >> 4;
  83. const char* region = rom_region(rom);
  84. DEBUG("- header info:")
  85. DEBUG(" - checksum: 0x%04X", rom->checksum)
  86. DEBUG(" - product code: %u", rom->product_code)
  87. DEBUG(" - version: %u", rom->version)
  88. DEBUG(" - region code: %u (%s)", rom->region_code, region ? region : "unknown")
  89. return true;
  90. }
  91. /*
  92. Find and read a ROM image's header, and return whether or not it is valid.
  93. */
  94. static bool find_and_read_header(ROM *rom)
  95. {
  96. size_t location, i;
  97. const uint8_t *header;
  98. DEBUG("- looking for header:")
  99. for (i = 0; i < NUM_LOCATIONS; i++) {
  100. location = header_locations[i];
  101. if (location + HEADER_SIZE > rom->size) {
  102. DEBUG(" - skipping location 0x%zX, out of range", location)
  103. continue;
  104. }
  105. DEBUG(" - trying location 0x%zX:", location)
  106. header = &rom->data[location];
  107. if (memcmp(header, header_magic, MAGIC_LEN)) {
  108. DEBUG(" - magic not present")
  109. }
  110. else {
  111. DEBUG(" - magic found")
  112. return parse_header(rom, header);
  113. }
  114. }
  115. DEBUG(" - could not find header")
  116. return false;
  117. }
  118. /*
  119. Create and load a ROM image located at the given path.
  120. rom_ptr will point to the new object if created successfully, and NULL will
  121. be returned. Otherwise, rom_ptr will not be modified and an error string
  122. will be returned. The error string should not be freed.
  123. */
  124. const char* rom_open(ROM **rom_ptr, const char *path)
  125. {
  126. ROM *rom;
  127. FILE* fp;
  128. struct stat st;
  129. if (!(fp = fopen(path, "r")))
  130. return strerror(errno);
  131. if (fstat(fileno(fp), &st)) {
  132. fclose(fp);
  133. return strerror(errno);
  134. }
  135. if (!(st.st_mode & S_IFREG)) {
  136. fclose(fp);
  137. return (st.st_mode & S_IFDIR) ? rom_err_isdir : rom_err_notfile;
  138. }
  139. if (!(rom = malloc(sizeof(ROM))))
  140. OUT_OF_MEMORY()
  141. // Set defaults:
  142. rom->name = NULL;
  143. rom->data = NULL;
  144. rom->size = 0;
  145. rom->checksum = 0;
  146. rom->product_code = 0;
  147. rom->version = 0;
  148. rom->region_code = 0;
  149. // Set rom->name:
  150. if (!(rom->name = malloc(sizeof(char) * (strlen(path) + 1))))
  151. OUT_OF_MEMORY()
  152. strcpy(rom->name, path);
  153. DEBUG("Loading ROM %s:", rom->name)
  154. // Set rom->size:
  155. DEBUG("- size: %lld", st.st_size)
  156. if (!validate_size(st.st_size)) {
  157. rom_close(rom);
  158. fclose(fp);
  159. return rom_err_badsize;
  160. }
  161. rom->size = st.st_size;
  162. // Set rom->data:
  163. if (!(rom->data = malloc(sizeof(uint8_t) * st.st_size)))
  164. OUT_OF_MEMORY()
  165. if (!(fread(rom->data, st.st_size, 1, fp))) {
  166. rom_close(rom);
  167. fclose(fp);
  168. return rom_err_badread;
  169. }
  170. fclose(fp);
  171. // Parse the header:
  172. if (!find_and_read_header(rom)) {
  173. rom_close(rom);
  174. return rom_err_badheader;
  175. }
  176. *rom_ptr = rom;
  177. return NULL;
  178. }
  179. /*
  180. Free a ROM object previously created with rom_open().
  181. */
  182. void rom_close(ROM *rom)
  183. {
  184. if (rom->name)
  185. free(rom->name);
  186. if (rom->data)
  187. free(rom->data);
  188. free(rom);
  189. }
  190. /*
  191. Return the region this ROM was intended for, based on header information.
  192. NULL is returned if the region code is invalid.
  193. Region code information is taken from:
  194. http://www.smspower.org/Development/ROMHeader
  195. */
  196. const char* rom_region(const ROM *rom)
  197. {
  198. switch (rom->region_code) {
  199. case 3: return "SMS Japan";
  200. case 4: return "SMS Export";
  201. case 5: return "GG Japan";
  202. case 6: return "GG Export";
  203. case 7: return "GG International";
  204. default: return NULL;
  205. }
  206. }