An emulator, assembler, and disassembler for the Sega Game Gear
25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.
 
 
 
 
 

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