An emulator, assembler, and disassembler for the Sega Game Gear
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 
 
 
 

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