An emulator, assembler, and disassembler for the Sega Game Gear
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 
 

337 linhas
9.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 <stdlib.h>
  4. #include <string.h>
  5. #include "tokenizer.h"
  6. #include "directives.h"
  7. #include "parse_util.h"
  8. #include "../logging.h"
  9. #include "../mmu.h"
  10. #include "../rom.h"
  11. /* Internal structs */
  12. typedef struct {
  13. size_t size;
  14. const ASMLine **overlap_table;
  15. const ASMLine *origin;
  16. uint8_t bank;
  17. bool cross_blocks;
  18. } ASMLayoutInfo;
  19. typedef struct {
  20. int8_t slots[MMU_NUM_ROM_BANKS];
  21. const ASMLine *lines[MMU_NUM_ROM_BANKS];
  22. } ASMSlotInfo;
  23. /* Sentinel values for overlap table */
  24. const ASMLine header_sentinel, bounds_sentinel;
  25. /*
  26. Return the address of a given ROM offset when mapped into the given slot.
  27. */
  28. static inline uint16_t map_into_slot(size_t offset, int8_t slot)
  29. {
  30. return (slot * MMU_ROM_BANK_SIZE) + (offset & (MMU_ROM_BANK_SIZE - 1));
  31. }
  32. /*
  33. Return the default slot associated with a given memory bank.
  34. */
  35. static inline int8_t default_bank_slot(uint8_t bank)
  36. {
  37. return bank > 2 ? 2 : bank;
  38. }
  39. /*
  40. Add a given line, representing a label, to the symbol table.
  41. Return NULL on success and an ErrorInfo object on failure (in the case of
  42. duplicate labels).
  43. */
  44. static ErrorInfo* add_label_to_table(
  45. ASMSymbolTable *symtable, const ASMLine *line, size_t offset, int8_t slot)
  46. {
  47. char *symbol = strndup(line->data, line->length - 1);
  48. if (!symbol)
  49. OUT_OF_MEMORY()
  50. const ASMSymbol *current = asm_symtable_find(symtable, symbol);
  51. if (current) {
  52. ErrorInfo *ei = error_info_create(line, ET_SYMBOL, ED_SYM_DUPE_LABELS);
  53. error_info_append(ei, current->line);
  54. return ei;
  55. }
  56. ASMSymbol *label = malloc(sizeof(ASMSymbol));
  57. if (!label)
  58. OUT_OF_MEMORY()
  59. label->offset = map_into_slot(offset,
  60. (slot >= 0) ? slot : default_bank_slot(offset / MMU_ROM_BANK_SIZE));
  61. label->symbol = symbol;
  62. label->line = line;
  63. asm_symtable_insert(symtable, label);
  64. return NULL;
  65. }
  66. /*
  67. Handle an origin directive by updating the offset.
  68. Return NULL on success and an ErrorInfo object on failure.
  69. */
  70. static ErrorInfo* handle_origin_directive(const ASMLine *line, size_t *offset)
  71. {
  72. if (!DIRECTIVE_HAS_ARG(line, DIR_ORIGIN))
  73. return error_info_create(line, ET_PREPROC, ED_PP_NO_ARG);
  74. uint32_t arg;
  75. if (!dparse_uint32_t(&arg, line, DIR_ORIGIN))
  76. return error_info_create(line, ET_PREPROC, ED_PP_BAD_ARG);
  77. if (arg >= ROM_SIZE_MAX)
  78. return error_info_create(line, ET_PREPROC, ED_PP_ARG_RANGE);
  79. *offset = arg;
  80. return NULL;
  81. }
  82. /*
  83. Handle a block directive by updating the offset and slot.
  84. Return NULL on success and an ErrorInfo object on failure.
  85. */
  86. static ErrorInfo* handle_block_directive(
  87. const ASMLine *line, size_t *offset, ASMSlotInfo *si)
  88. {
  89. if (!DIRECTIVE_HAS_ARG(line, DIR_BLOCK))
  90. return error_info_create(line, ET_PREPROC, ED_PP_NO_ARG);
  91. uint8_t *args, bank, slot;
  92. size_t dir_offset = DIRECTIVE_OFFSET(line, DIR_BLOCK) + 1, nargs;
  93. if (!parse_bytes(&args, &nargs, line->data + dir_offset,
  94. line->length - dir_offset))
  95. return error_info_create(line, ET_PREPROC, ED_PP_BAD_ARG);
  96. if (nargs < 1 || nargs > 2)
  97. return free(args), error_info_create(line, ET_PREPROC, ED_PP_BAD_ARG);
  98. bank = args[0];
  99. slot = nargs == 2 ? args[1] : default_bank_slot(bank);
  100. free(args);
  101. if (bank >= MMU_NUM_ROM_BANKS || slot >= MMU_NUM_SLOTS)
  102. return error_info_create(line, ET_PREPROC, ED_PP_ARG_RANGE);
  103. if (nargs == 2) {
  104. if (bank == 0 && slot != 0)
  105. return error_info_create(line, ET_LAYOUT, ED_LYT_BLOCK0);
  106. if (si->slots[bank] >= 0 && si->slots[bank] != slot) {
  107. ErrorInfo *ei = error_info_create(line, ET_LAYOUT, ED_LYT_SLOTS);
  108. error_info_append(ei, si->lines[bank]);
  109. return ei;
  110. }
  111. }
  112. *offset = bank * MMU_ROM_BANK_SIZE;
  113. si->slots[bank] = slot;
  114. if (!si->lines[bank])
  115. si->lines[bank] = line;
  116. return NULL;
  117. }
  118. /*
  119. Parse data encoded in a line into an ASMData object.
  120. On success, return NULL and store the instruction in *data_ptr. On failure,
  121. return an ErrorInfo object; *data_ptr is not modified.
  122. */
  123. static ErrorInfo* parse_data(
  124. const ASMLine *line, ASMData **data_ptr, size_t offset)
  125. {
  126. // TODO
  127. DEBUG("parse_data(): %.*s", (int) line->length, line->data)
  128. // return error_info_create(line, ET_PARSER, ED_PARSE_SYNTAX);
  129. ASMData *data = malloc(sizeof(ASMData));
  130. if (!data)
  131. OUT_OF_MEMORY()
  132. data->loc.offset = offset;
  133. data->loc.length = 6;
  134. data->bytes = (uint8_t*) strdup("foobar");
  135. data->next = NULL;
  136. *data_ptr = data;
  137. return NULL;
  138. }
  139. /*
  140. Parse an instruction encoded in a line into an ASMInstruction object.
  141. On success, return NULL and store the instruction in *inst_ptr. On failure,
  142. return an ErrorInfo object; *inst_ptr is not modified.
  143. */
  144. static ErrorInfo* parse_instruction(
  145. const ASMLine *line, ASMInstruction **inst_ptr, size_t offset)
  146. {
  147. // TODO
  148. DEBUG("parse_instruction(): %.*s", (int) line->length, line->data)
  149. // return error_info_create(line, ET_PARSER, ED_PARSE_SYNTAX);
  150. ASMInstruction *inst = malloc(sizeof(ASMInstruction));
  151. if (!inst)
  152. OUT_OF_MEMORY()
  153. inst->loc.offset = offset;
  154. inst->loc.length = 1;
  155. uint8_t tmp = 0x3C;
  156. inst->bytes = memcpy(malloc(1), &tmp, 1);
  157. inst->symbol = NULL;
  158. inst->line = line;
  159. inst->next = NULL;
  160. *inst_ptr = inst;
  161. return NULL;
  162. }
  163. /*
  164. Check if the given object location is legal.
  165. Checks include ROM size bounding, overlapping with existing objects, and
  166. block-crossing assuming the .cross_blocks directive has not been specified.
  167. On success, return NULL and add the location to the overlap table.
  168. On failure, return an ErrorInfo object.
  169. */
  170. static ErrorInfo* check_layout(
  171. ASMLayoutInfo *li, const ASMLocation *loc, const ASMLine *line)
  172. {
  173. const ASMLine *clash = NULL;
  174. if (loc->offset + loc->length > li->size) {
  175. clash = &bounds_sentinel;
  176. } else {
  177. for (size_t i = 0; i < loc->length; i++) {
  178. if (li->overlap_table[loc->offset + i]) {
  179. clash = li->overlap_table[loc->offset + i];
  180. break;
  181. }
  182. }
  183. }
  184. if (clash) {
  185. ErrorInfo *ei = error_info_create(line, ET_LAYOUT,
  186. (clash == &header_sentinel) ? ED_LYT_OVERLAP_HEAD :
  187. (clash == &bounds_sentinel) ? ED_LYT_BOUNDS : ED_LYT_OVERLAP);
  188. if (li->origin)
  189. error_info_append(ei, li->origin);
  190. if (clash != &header_sentinel && clash != &bounds_sentinel)
  191. error_info_append(ei, clash);
  192. return ei;
  193. }
  194. uint8_t bank = (loc->offset + loc->length - 1) / MMU_ROM_BANK_SIZE;
  195. if (bank != li->bank && !li->cross_blocks) {
  196. ErrorInfo *ei = error_info_create(line, ET_LAYOUT, ED_LYT_BLOCK_CROSS);
  197. if (li->origin)
  198. error_info_append(ei, li->origin);
  199. return ei;
  200. }
  201. for (size_t i = 0; i < loc->length; i++)
  202. li->overlap_table[loc->offset + i] = line;
  203. return NULL;
  204. }
  205. /*
  206. Tokenize ASMLines into ASMInstructions and ASMData.
  207. NULL is returned on success and an ErrorInfo object is returned on failure.
  208. state->instructions, state->data, and state->symtable may or may not be
  209. modified regardless of success.
  210. */
  211. ErrorInfo* tokenize(AssemblerState *state)
  212. {
  213. ASMLayoutInfo li = {
  214. .size = state->rom_size ? state->rom_size : ROM_SIZE_MAX,
  215. .origin = NULL, .bank = 0, .cross_blocks = state->cross_blocks
  216. };
  217. li.overlap_table = calloc(li.size, sizeof(const ASMLine*));
  218. if (!li.overlap_table)
  219. OUT_OF_MEMORY()
  220. ErrorInfo *ei = NULL;
  221. ASMInstruction dummy_inst = {.next = NULL}, *inst, *prev_inst = &dummy_inst;
  222. ASMData dummy_data = {.next = NULL}, *data, *prev_data = &dummy_data;
  223. const ASMLine *line = state->lines;
  224. size_t offset = 0;
  225. ASMSlotInfo si = {.lines = {0}};
  226. for (size_t i = 0; i < HEADER_SIZE; i++)
  227. li.overlap_table[state->header.offset + i] = &header_sentinel;
  228. memset(si.slots, -1, MMU_NUM_ROM_BANKS);
  229. while (line) {
  230. if (line->is_label) {
  231. if (offset >= li.size) {
  232. ei = error_info_create(line, ET_LAYOUT, ED_LYT_BOUNDS);
  233. goto cleanup;
  234. }
  235. int8_t slot = si.slots[offset / MMU_NUM_ROM_BANKS];
  236. if ((ei = add_label_to_table(state->symtable, line, offset, slot)))
  237. goto cleanup;
  238. }
  239. else if (IS_LOCAL_DIRECTIVE(line)) {
  240. if (IS_DIRECTIVE(line, DIR_ORIGIN)) {
  241. if ((ei = handle_origin_directive(line, &offset)))
  242. goto cleanup;
  243. li.origin = line;
  244. li.bank = offset / MMU_ROM_BANK_SIZE;
  245. }
  246. else if (IS_DIRECTIVE(line, DIR_BLOCK)) {
  247. if ((ei = handle_block_directive(line, &offset, &si)))
  248. goto cleanup;
  249. li.origin = line;
  250. li.bank = offset / MMU_ROM_BANK_SIZE;
  251. }
  252. else {
  253. if ((ei = parse_data(line, &data, offset)))
  254. goto cleanup;
  255. offset += data->loc.length;
  256. prev_data->next = data;
  257. prev_data = data;
  258. if ((ei = check_layout(&li, &data->loc, line)))
  259. goto cleanup;
  260. }
  261. }
  262. else {
  263. if ((ei = parse_instruction(line, &inst, offset)))
  264. goto cleanup;
  265. offset += inst->loc.length;
  266. prev_inst->next = inst;
  267. prev_inst = inst;
  268. if ((ei = check_layout(&li, &inst->loc, line)))
  269. goto cleanup;
  270. }
  271. line = line->next;
  272. }
  273. cleanup:
  274. state->instructions = dummy_inst.next;
  275. state->data = dummy_data.next;
  276. free(li.overlap_table);
  277. return ei;
  278. }