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.
 
 
 
 
 

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