An emulator, assembler, and disassembler for the Sega Game Gear
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 
 

303 lignes
8.7 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 <stdio.h>
  6. #include <stdbool.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, 0x3FF0, 0x1FF0};
  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. Compute the correct ROM data checksum.
  52. If the summable region (as specified by the range parameter) is too large,
  53. we'll compute the checksum over the default range (0x0000-0x7FF0), or the
  54. largest possible range.
  55. */
  56. static uint16_t compute_checksum(const uint8_t *data, size_t size, uint8_t range)
  57. {
  58. size_t low_region, high_region;
  59. switch (range & 0xF) {
  60. case 0xA: low_region = 0x1FEF; high_region = 0; break;
  61. case 0xB: low_region = 0x3FEF; high_region = 0; break;
  62. case 0xC: low_region = 0x7FEF; high_region = 0; break;
  63. case 0xD: low_region = 0xBFEF; high_region = 0; break;
  64. case 0xE: low_region = 0x7FEF; high_region = 0x0FFFF; break;
  65. case 0xF: low_region = 0x7FEF; high_region = 0x1FFFF; break;
  66. case 0x0: low_region = 0x7FEF; high_region = 0x3FFFF; break;
  67. case 0x1: low_region = 0x7FEF; high_region = 0x7FFFF; break;
  68. case 0x2: low_region = 0x7FEF; high_region = 0xFFFFF; break;
  69. default: low_region = 0x7FEF; high_region = 0; break;
  70. }
  71. if (low_region >= size)
  72. low_region = (size >= 0x4000) ? 0x3FEF : 0x1FEF;
  73. if (high_region >= size)
  74. high_region = 0;
  75. uint16_t sum = 0;
  76. for (size_t index = 0; index <= low_region; index++)
  77. sum += data[index];
  78. if (high_region) {
  79. for (size_t index = 0x08000; index <= high_region; index++)
  80. sum += data[index];
  81. }
  82. return sum;
  83. }
  84. #ifdef DEBUG_MODE
  85. /*
  86. DEBUG FUNCTION: Return the ROM's size as a string, according to its header.
  87. */
  88. static const char* parse_reported_size(uint8_t value)
  89. {
  90. switch (value) {
  91. case 0xA: return "8 KB";
  92. case 0xB: return "16 KB";
  93. case 0xC: return "32 KB";
  94. case 0xD: return "48 KB";
  95. case 0xE: return "64 KB";
  96. case 0xF: return "128 KB";
  97. case 0x0: return "256 KB";
  98. case 0x1: return "512 KB";
  99. case 0x2: return "1 MB";
  100. default: return "Unknown";
  101. }
  102. }
  103. #endif
  104. /*
  105. Parse a ROM image's header, and return whether or not it is valid.
  106. The header is 16 bytes long, consisting of:
  107. byte 0: magic ('T')
  108. byte 1: magic ('M')
  109. byte 2: magic ('R')
  110. byte 3: magic (' ')
  111. byte 4: magic ('S')
  112. byte 5: magic ('E')
  113. byte 6: magic ('G')
  114. byte 7: magic ('A')
  115. byte 8: unused
  116. byte 9: unused
  117. byte A: checksum (LSB)
  118. byte B: checksum (MSB)
  119. byte C: product code (LSB)
  120. byte D: product code (middle byte)
  121. byte E (hi nibble): product code (most-significant nibble)
  122. byte E (lo nibble): version
  123. byte F (hi nibble): region code
  124. byte F (lo nibble): ROM size
  125. (Based on: http://www.smspower.org/Development/ROMHeader)
  126. */
  127. static bool parse_header(ROM *rom, const uint8_t *header)
  128. {
  129. #ifdef DEBUG_MODE
  130. print_header(header);
  131. #endif
  132. rom->reported_checksum = header[0xA] + (header[0xB] << 8);
  133. rom->expected_checksum = compute_checksum(rom->data, rom->size, header[0xF]);
  134. rom->product_code = bcd_decode(header[0xC]) +
  135. (bcd_decode(header[0xD]) * 100) + ((header[0xE] >> 4) * 10000);
  136. rom->version = header[0xE] & 0x0F;
  137. rom->region_code = header[0xF] >> 4;
  138. DEBUG("- header info:")
  139. if (rom->reported_checksum == rom->expected_checksum)
  140. DEBUG(" - checksum: 0x%04X (valid)", rom->reported_checksum)
  141. else
  142. DEBUG(" - checksum: 0x%04X (invalid, expected 0x%04X)",
  143. rom->reported_checksum, rom->expected_checksum)
  144. DEBUG(" - product code: %u", rom->product_code)
  145. DEBUG(" - version: %u", rom->version)
  146. DEBUG(" - region code: %u (%s)", rom->region_code,
  147. rom_region(rom) ? rom_region(rom) : "unknown")
  148. DEBUG(" - reported size: %s", parse_reported_size(header[0xF] & 0xF))
  149. return true;
  150. }
  151. /*
  152. Find and read a ROM image's header, and return whether or not it is valid.
  153. */
  154. static bool find_and_read_header(ROM *rom)
  155. {
  156. size_t location, i;
  157. const uint8_t *header;
  158. DEBUG("- looking for header:")
  159. for (i = 0; i < NUM_LOCATIONS; i++) {
  160. location = header_locations[i];
  161. if (location + HEADER_SIZE > rom->size) {
  162. DEBUG(" - skipping location 0x%zX, out of range", location)
  163. continue;
  164. }
  165. DEBUG(" - trying location 0x%zX:", location)
  166. header = &rom->data[location];
  167. if (memcmp(header, header_magic, MAGIC_LEN)) {
  168. DEBUG(" - magic not present")
  169. }
  170. else {
  171. DEBUG(" - magic found")
  172. return parse_header(rom, header);
  173. }
  174. }
  175. DEBUG(" - could not find header")
  176. return false;
  177. }
  178. /*
  179. Create and load a ROM image located at the given path.
  180. rom_ptr will point to the new object if created successfully, and NULL will
  181. be returned. Otherwise, rom_ptr will not be modified and an error string
  182. will be returned. The error string should not be freed.
  183. */
  184. const char* rom_open(ROM **rom_ptr, const char *path)
  185. {
  186. ROM *rom;
  187. FILE* fp;
  188. struct stat st;
  189. if (!(fp = fopen(path, "r")))
  190. return strerror(errno);
  191. if (fstat(fileno(fp), &st)) {
  192. fclose(fp);
  193. return strerror(errno);
  194. }
  195. if (!(st.st_mode & S_IFREG)) {
  196. fclose(fp);
  197. return (st.st_mode & S_IFDIR) ? rom_err_isdir : rom_err_notfile;
  198. }
  199. if (!(rom = malloc(sizeof(ROM))))
  200. OUT_OF_MEMORY()
  201. // Set defaults:
  202. rom->name = NULL;
  203. rom->data = NULL;
  204. rom->size = 0;
  205. rom->reported_checksum = 0;
  206. rom->expected_checksum = 0;
  207. rom->product_code = 0;
  208. rom->version = 0;
  209. rom->region_code = 0;
  210. // Set rom->name:
  211. if (!(rom->name = malloc(sizeof(char) * (strlen(path) + 1))))
  212. OUT_OF_MEMORY()
  213. strcpy(rom->name, path);
  214. DEBUG("Loading ROM %s:", rom->name)
  215. // Set rom->size:
  216. DEBUG("- size: %lld", st.st_size)
  217. if (!validate_size(st.st_size)) {
  218. rom_close(rom);
  219. fclose(fp);
  220. return rom_err_badsize;
  221. }
  222. rom->size = st.st_size;
  223. // Set rom->data:
  224. if (!(rom->data = malloc(sizeof(uint8_t) * st.st_size)))
  225. OUT_OF_MEMORY()
  226. if (!(fread(rom->data, st.st_size, 1, fp))) {
  227. rom_close(rom);
  228. fclose(fp);
  229. return rom_err_badread;
  230. }
  231. fclose(fp);
  232. // Parse the header:
  233. if (!find_and_read_header(rom)) {
  234. rom_close(rom);
  235. return rom_err_badheader;
  236. }
  237. *rom_ptr = rom;
  238. return NULL;
  239. }
  240. /*
  241. Free a ROM object previously created with rom_open().
  242. */
  243. void rom_close(ROM *rom)
  244. {
  245. if (rom->name)
  246. free(rom->name);
  247. if (rom->data)
  248. free(rom->data);
  249. free(rom);
  250. }
  251. /*
  252. Return the region this ROM was intended for, based on header information.
  253. NULL is returned if the region code is invalid.
  254. Region code information is taken from:
  255. http://www.smspower.org/Development/ROMHeader
  256. */
  257. const char* rom_region(const ROM *rom)
  258. {
  259. switch (rom->region_code) {
  260. case 3: return "SMS Japan";
  261. case 4: return "SMS Export";
  262. case 5: return "GG Japan";
  263. case 6: return "GG Export";
  264. case 7: return "GG International";
  265. default: return NULL;
  266. }
  267. }