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.
 
 
 
 
 

251 lines
6.8 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. if (!(rom = malloc(sizeof(ROM))))
  152. OUT_OF_MEMORY()
  153. // Set defaults:
  154. rom->name = NULL;
  155. rom->data = NULL;
  156. rom->size = 0;
  157. rom->reported_checksum = 0;
  158. rom->expected_checksum = 0;
  159. rom->product_code = 0;
  160. rom->version = 0;
  161. rom->region_code = 0;
  162. // Set rom->name:
  163. if (!(rom->name = malloc(sizeof(char) * (strlen(path) + 1))))
  164. OUT_OF_MEMORY()
  165. strcpy(rom->name, path);
  166. DEBUG("Loading ROM %s:", rom->name)
  167. // Set rom->size:
  168. DEBUG("- size: %lld bytes (%s)", st.st_size, size_to_string(st.st_size))
  169. if (size_bytes_to_code(st.st_size) == INVALID_SIZE_CODE) {
  170. rom_close(rom);
  171. fclose(fp);
  172. return rom_err_badsize;
  173. }
  174. rom->size = st.st_size;
  175. // Set rom->data:
  176. if (!(rom->data = malloc(sizeof(uint8_t) * st.st_size)))
  177. OUT_OF_MEMORY()
  178. if (!(fread(rom->data, st.st_size, 1, fp))) {
  179. rom_close(rom);
  180. fclose(fp);
  181. return rom_err_badread;
  182. }
  183. fclose(fp);
  184. // Parse the header:
  185. if (!find_and_read_header(rom)) {
  186. rom_close(rom);
  187. return rom_err_badheader;
  188. }
  189. *rom_ptr = rom;
  190. return NULL;
  191. }
  192. /*
  193. Free a ROM object previously created with rom_open().
  194. */
  195. void rom_close(ROM *rom)
  196. {
  197. free(rom->name);
  198. free(rom->data);
  199. free(rom);
  200. }
  201. /*
  202. Return a string explanation of this ROM's product code.
  203. NULL is returned if the product code is not known.
  204. */
  205. const char* rom_product(const ROM *rom)
  206. {
  207. // TODO
  208. return NULL;
  209. }
  210. /*
  211. Return the region this ROM was intended for, based on header information.
  212. NULL is returned if the region code is invalid.
  213. */
  214. const char* rom_region(const ROM *rom)
  215. {
  216. return region_code_to_string(rom->region_code);
  217. }