An emulator, assembler, and disassembler for the Sega Game Gear
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
 
 
 
 
 

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