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.
 
 
 
 
 

329 lignes
8.6 KiB

  1. /* Copyright (C) 2014-2016 Ben Kurtovic <ben.kurtovic@gmail.com>
  2. Released under the terms of the MIT License. See LICENSE for details. */
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <time.h>
  6. #include "disassembler.h"
  7. #include "disassembler/arguments.h"
  8. #include "disassembler/mnemonics.h"
  9. #include "disassembler/sizes.h"
  10. #include "mmu.h"
  11. #include "rom.h"
  12. #include "util.h"
  13. #include "version.h"
  14. #define HRULE \
  15. "----------------------------------------------------------------------------"
  16. #define NUM_BANKS(rom) \
  17. (((rom)->size + MMU_ROM_BANK_SIZE - 1) / MMU_ROM_BANK_SIZE)
  18. /* Structs and things */
  19. typedef struct {
  20. size_t cap, len;
  21. char **lines;
  22. } Disassembly;
  23. typedef enum {
  24. DT_BINARY = 0,
  25. DT_CODE,
  26. DT_HEADER
  27. } DataType;
  28. typedef struct {
  29. const uint8_t *data;
  30. DataType *types;
  31. size_t size;
  32. int8_t slot;
  33. } ROMBank;
  34. /*
  35. Format a sequence of bytes of a certain length as a pretty string.
  36. The result must be freed by the caller.
  37. */
  38. static char* format_bytestring(const uint8_t *bytes, size_t size)
  39. {
  40. // TODO: smarter alignment; pad to full len (then remove pad from TRACE())
  41. if (!size)
  42. return NULL;
  43. char *str = cr_malloc(sizeof(char) * (3 * size));
  44. size_t i;
  45. for (i = 0; i < size; i++) {
  46. snprintf(&str[3 * i], 3, "%02X", bytes[i]);
  47. str[3 * i + 2] = ' ';
  48. }
  49. str[3 * size - 1] = '\0';
  50. return str;
  51. }
  52. /*
  53. Free the given DisasInstr struct.
  54. */
  55. void disas_instr_free(DisasInstr *instr)
  56. {
  57. free(instr->bytestr);
  58. free(instr->line);
  59. free(instr);
  60. }
  61. /*
  62. Disassemble a single instruction starting at the given address.
  63. Return a dynamically allocated structure containing various interesting
  64. fields. This must be freed by the user with disas_instr_free().
  65. */
  66. DisasInstr* disassemble_instruction(const uint8_t *bytes)
  67. {
  68. size_t size = get_instr_size(bytes);
  69. char *bytestr = format_bytestring(bytes, size);
  70. char *mnemonic = decode_mnemonic(bytes);
  71. char *args = decode_arguments(bytes);
  72. char *line;
  73. if (args) {
  74. line = cr_malloc(strlen(mnemonic) + strlen(args) + 2);
  75. sprintf(line, "%s\t%s", mnemonic, args);
  76. free(args);
  77. } else {
  78. line = cr_strdup(mnemonic);
  79. }
  80. DisasInstr *instr = cr_malloc(sizeof(DisasInstr));
  81. instr->size = size;
  82. instr->bytestr = bytestr;
  83. instr->line = line;
  84. return instr;
  85. }
  86. /*
  87. Append a line to the end of a disassembly.
  88. */
  89. static void write_line(Disassembly *dis, char *line)
  90. {
  91. dis->lines[dis->len++] = line;
  92. if (dis->len >= dis->cap) {
  93. dis->cap *= 2;
  94. dis->lines = cr_realloc(dis->lines, sizeof(char*) * dis->cap);
  95. }
  96. }
  97. /*
  98. Macro that wraps write_line() in a printf-like interface.
  99. */
  100. #define WRITE_LINE_(dis, fmt, ...) \
  101. do { \
  102. char *tmp_buffer_; \
  103. if (asprintf(&tmp_buffer_, fmt "\n", __VA_ARGS__) < 0) \
  104. OUT_OF_MEMORY() \
  105. write_line(dis, tmp_buffer_); \
  106. } while(0);
  107. #define WRITE_LINE(dis, ...) WRITE_LINE_(dis, __VA_ARGS__, NULL)
  108. /*
  109. Write some metadata comments to the top of the disassembly.
  110. */
  111. static void write_metadata(Disassembly *dis, const ROM *rom)
  112. {
  113. time_t t;
  114. struct tm *tm_info;
  115. char buf[64];
  116. time(&t);
  117. tm_info = localtime(&t);
  118. strftime(buf, sizeof buf, "on %a %b %d, %Y at %H:%M:%S", tm_info);
  119. WRITE_LINE(dis, ";; GAME GEAR ROM DISASSEMBLY")
  120. WRITE_LINE(dis, ";; File: %s", rom->name)
  121. WRITE_LINE(dis, ";; Generated %s by crater %s", buf, CRATER_VERSION)
  122. WRITE_LINE(dis, ";; " HRULE)
  123. WRITE_LINE(dis, "")
  124. }
  125. /*
  126. Given a size, fill 'output' with a pretty string. Modified from rom.c.
  127. */
  128. static char* size_to_string(char *output, size_t size)
  129. {
  130. if (size >= (1 << 20))
  131. sprintf(output, "%zu MB", size >> 20);
  132. else
  133. sprintf(output, "%zu KB", size >> 10);
  134. return output;
  135. }
  136. /*
  137. Extract appropriate assembler directives from a ROM's header.
  138. */
  139. static void disassemble_header(Disassembly *dis, const ROM *rom)
  140. {
  141. char buf[64];
  142. DEBUG("Disassembling header")
  143. WRITE_LINE(dis, ".rom_size\t\"%s\"\t\t; $%zX bytes in %zu banks",
  144. size_to_string(buf, rom->size), rom->size, NUM_BANKS(rom))
  145. WRITE_LINE(dis, ".rom_header\t$%04X",
  146. rom->header_location)
  147. WRITE_LINE(dis, ".rom_checksum\t%s",
  148. (rom->reported_checksum == rom->expected_checksum) ? "on" : "off")
  149. WRITE_LINE(dis, ".rom_product\t%u\t\t; %s",
  150. rom->product_code, rom_product(rom) ? rom_product(rom) : "(unknown)")
  151. WRITE_LINE(dis, ".rom_version\t%u",
  152. rom->version)
  153. WRITE_LINE(dis, ".rom_region\t%u\t\t; %s",
  154. rom->region_code, rom_region(rom) ? rom_region(rom) : "(unknown)")
  155. WRITE_LINE(dis, ".rom_declsize\t$%X\t\t; %s",
  156. rom->declared_size,
  157. size_to_string(buf, size_code_to_bytes(rom->declared_size)))
  158. }
  159. /*
  160. Initialize and return an array of ROMBank objects for the given ROM.
  161. */
  162. static ROMBank* init_banks(const ROM *rom)
  163. {
  164. size_t nbanks = NUM_BANKS(rom), i;
  165. ROMBank *banks = cr_malloc(sizeof(ROMBank) * (nbanks + 1));
  166. DataType *types = cr_calloc(sizeof(DataType), rom->size);
  167. for (i = 0; i < nbanks; i++) {
  168. if (i == nbanks - 1 && rom->size % MMU_ROM_BANK_SIZE)
  169. banks[i].size = rom->size % MMU_ROM_BANK_SIZE;
  170. else
  171. banks[i].size = MMU_ROM_BANK_SIZE;
  172. banks[i].data = rom->data + (i * MMU_ROM_BANK_SIZE);
  173. banks[i].types = types + (i * MMU_ROM_BANK_SIZE);
  174. banks[i].slot = -1;
  175. }
  176. banks[nbanks].data = NULL; // Sentinel
  177. return banks;
  178. }
  179. /*
  180. Deallocate the given array of ROM banks.
  181. */
  182. static void free_banks(ROMBank *banks)
  183. {
  184. free(banks[0].types);
  185. free(banks);
  186. }
  187. /*
  188. Mark the ROM's header as non-binary/non-code inside of the relevant bank.
  189. */
  190. static void mark_header(const ROM *rom, ROMBank *banks)
  191. {
  192. size_t i;
  193. for (i = 0; i < HEADER_SIZE; i++)
  194. banks[0].types[rom->header_location + i] = DT_HEADER;
  195. }
  196. /*
  197. Render fully analyzed banks into lines of disassembly.
  198. */
  199. static void render_banks(Disassembly *dis, ROMBank *banks)
  200. {
  201. size_t bn = 0, i;
  202. while (banks[bn].data) {
  203. WRITE_LINE(dis, "")
  204. WRITE_LINE(dis, ";; " HRULE)
  205. WRITE_LINE(dis, "")
  206. WRITE_LINE(dis, ".block $%02zX", bn)
  207. for (i = 0; i < banks[bn].size; i++) {
  208. if (banks[bn].types[i] == DT_BINARY)
  209. WRITE_LINE(dis, ".byte $%02X", banks[bn].data[i])
  210. }
  211. bn++;
  212. }
  213. }
  214. /*
  215. Disassemble a ROM into an array of strings, each storing one source line.
  216. Each line is newline-terminated. The array itself is terminated with a NULL
  217. element. Each line, and the overall array, must be free()d by the caller.
  218. */
  219. char** disassemble(const ROM *rom)
  220. {
  221. Disassembly dis = {.cap = 16, .len = 0};
  222. dis.lines = cr_malloc(sizeof(char*) * dis.cap);
  223. write_metadata(&dis, rom);
  224. disassemble_header(&dis, rom);
  225. ROMBank *banks = init_banks(rom);
  226. mark_header(rom, banks);
  227. // TODO: analyze(): set DT_CODE (future: make labels, slots) where appropriate
  228. render_banks(&dis, banks);
  229. free_banks(banks);
  230. write_line(&dis, NULL);
  231. return dis.lines;
  232. }
  233. /*
  234. Write a disassembly created by disassemble() to the given output file.
  235. Return whether the file was written successfully. This function frees the
  236. disassembly along the way.
  237. */
  238. static bool write_disassembly(const char *path, char **lines)
  239. {
  240. FILE *fp;
  241. char **itr = lines;
  242. if (!(fp = fopen(path, "w"))) {
  243. ERROR_ERRNO("couldn't open destination file")
  244. return false;
  245. }
  246. while (*itr) {
  247. if (!fwrite(*itr, strlen(*itr), 1, fp)) {
  248. fclose(fp);
  249. do free(*itr); while (*(++itr));
  250. ERROR_ERRNO("couldn't write to destination file")
  251. return false;
  252. }
  253. free(*itr);
  254. itr++;
  255. }
  256. fclose(fp);
  257. free(lines);
  258. return true;
  259. }
  260. /*
  261. Disassemble the binary file at the input path into z80 source code.
  262. Return true if the operation was a success and false if it was a failure.
  263. Errors are printed to STDOUT; if the operation was successful then nothing
  264. is printed.
  265. */
  266. bool disassemble_file(const char *src_path, const char *dst_path)
  267. {
  268. ROM *rom;
  269. const char *errmsg;
  270. char **lines;
  271. DEBUG("Disassembling: %s -> %s", src_path, dst_path)
  272. if ((errmsg = rom_open(&rom, src_path))) {
  273. ERROR("couldn't load ROM image '%s': %s", src_path, errmsg)
  274. return false;
  275. }
  276. lines = disassemble(rom);
  277. rom_close(rom);
  278. DEBUG("Writing output file")
  279. return write_disassembly(dst_path, lines);
  280. }