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.
 
 
 
 
 

299 lignes
7.9 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 *type;
  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);
  166. for (i = 0; i < nbanks; i++) {
  167. if (i == nbanks - 1 && rom->size % MMU_ROM_BANK_SIZE)
  168. banks[i].size = rom->size % MMU_ROM_BANK_SIZE;
  169. else
  170. banks[i].size = MMU_ROM_BANK_SIZE;
  171. banks[i].data = rom->data + (i * MMU_ROM_BANK_SIZE);
  172. banks[i].type = cr_calloc(sizeof(DataType), banks[i].size);
  173. banks[i].slot = -1;
  174. }
  175. return banks;
  176. }
  177. /*
  178. Deallocate the given array of ROM banks.
  179. */
  180. static void free_banks(const ROM *rom, ROMBank *banks)
  181. {
  182. size_t nbanks = NUM_BANKS(rom), i;
  183. for (i = 0; i < nbanks; i++)
  184. free(banks[i].type);
  185. free(banks);
  186. }
  187. /*
  188. Disassemble a ROM into an array of strings, each storing one source line.
  189. Each line is newline-terminated. The array itself is terminated with a NULL
  190. element. Each line, and the overall array, must be free()d by the caller.
  191. */
  192. char** disassemble(const ROM *rom)
  193. {
  194. Disassembly dis = {.cap = 16, .len = 0};
  195. dis.lines = cr_malloc(sizeof(char*) * dis.cap);
  196. write_metadata(&dis, rom);
  197. disassemble_header(&dis, rom);
  198. ROMBank *banks = init_banks(rom);
  199. // TODO
  200. // mark_header(): set DT_HEADER where appropriate
  201. // analyze(): set DT_CODE (future: make labels, slots) where appropriate
  202. // render(): WRITE_LINE a bunch of of times
  203. free_banks(rom, banks);
  204. write_line(&dis, NULL);
  205. return dis.lines;
  206. }
  207. /*
  208. Write a disassembly created by disassemble() to the given output file.
  209. Return whether the file was written successfully. This function frees the
  210. disassembly along the way.
  211. */
  212. static bool write_disassembly(const char *path, char **lines)
  213. {
  214. FILE *fp;
  215. char **itr = lines;
  216. if (!(fp = fopen(path, "w"))) {
  217. ERROR_ERRNO("couldn't open destination file")
  218. return false;
  219. }
  220. while (*itr) {
  221. if (!fwrite(*itr, strlen(*itr), 1, fp)) {
  222. fclose(fp);
  223. do free(*itr); while (*(++itr));
  224. ERROR_ERRNO("couldn't write to destination file")
  225. return false;
  226. }
  227. free(*itr);
  228. itr++;
  229. }
  230. fclose(fp);
  231. free(lines);
  232. return true;
  233. }
  234. /*
  235. Disassemble the binary file at the input path into z80 source code.
  236. Return true if the operation was a success and false if it was a failure.
  237. Errors are printed to STDOUT; if the operation was successful then nothing
  238. is printed.
  239. */
  240. bool disassemble_file(const char *src_path, const char *dst_path)
  241. {
  242. ROM *rom;
  243. const char *errmsg;
  244. char **lines;
  245. DEBUG("Disassembling: %s -> %s", src_path, dst_path)
  246. if ((errmsg = rom_open(&rom, src_path))) {
  247. ERROR("couldn't load ROM image '%s': %s", src_path, errmsg)
  248. return false;
  249. }
  250. lines = disassemble(rom);
  251. rom_close(rom);
  252. DEBUG("Writing output file")
  253. return write_disassembly(dst_path, lines);
  254. }