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.
 
 
 
 
 

343 regels
10 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. // TODO
  126. DEBUG("parse_data(): %.*s", (int) line->length, line->data)
  127. // return error_info_create(line, ET_PARSER, ED_PARSE_SYNTAX);
  128. ASMData *data = malloc(sizeof(ASMData));
  129. if (!data)
  130. OUT_OF_MEMORY()
  131. data->loc.offset = offset;
  132. data->loc.length = 6;
  133. data->bytes = (uint8_t*) strdup("foobar");
  134. data->next = NULL;
  135. *data_ptr = data;
  136. return NULL;
  137. }
  138. /*
  139. Parse an instruction encoded in a line into an ASMInstruction object.
  140. On success, return NULL and store the instruction in *inst_ptr. On failure,
  141. return an ErrorInfo object; *inst_ptr is not modified.
  142. */
  143. static ErrorInfo* parse_instruction(
  144. const ASMLine *line, ASMInstruction **inst_ptr, size_t offset)
  145. {
  146. // TODO
  147. DEBUG("parse_instruction(): %.*s", (int) line->length, line->data)
  148. // return error_info_create(line, ET_PARSER, ED_PARSE_SYNTAX);
  149. ASMInstruction *inst = malloc(sizeof(ASMInstruction));
  150. if (!inst)
  151. OUT_OF_MEMORY()
  152. inst->loc.offset = offset;
  153. inst->loc.length = 1;
  154. uint8_t tmp = 0x3C;
  155. inst->bytes = memcpy(malloc(1), &tmp, 1);
  156. inst->symbol = NULL;
  157. inst->line = line;
  158. inst->next = NULL;
  159. *inst_ptr = inst;
  160. return NULL;
  161. }
  162. /*
  163. Check if the given object location is legal.
  164. Checks include ROM size bounding, overlapping with existing objects, and
  165. block-crossing assuming the .cross_blocks directive has not been specified.
  166. On success, return NULL and add the location to the overlap table.
  167. On failure, return an ErrorInfo object.
  168. */
  169. static ErrorInfo* check_layout(
  170. ASMLayoutInfo *li, const ASMLocation *loc, const ASMLine *line)
  171. {
  172. const ASMLine *clash = NULL, *clash_origin;
  173. if (loc->offset + loc->length > li->size) {
  174. clash = &bounds_sentinel;
  175. } else {
  176. for (size_t i = 0; i < loc->length; i++) {
  177. if (li->overlap_table[loc->offset + i]) {
  178. clash = li->overlap_table[loc->offset + i];
  179. clash_origin = li->overlap_origins[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. if (clash_origin)
  193. error_info_append(ei, clash_origin);
  194. }
  195. return ei;
  196. }
  197. uint8_t bank = (loc->offset + loc->length - 1) / MMU_ROM_BANK_SIZE;
  198. if (bank != li->bank && !li->cross_blocks) {
  199. ErrorInfo *ei = error_info_create(line, ET_LAYOUT, ED_LYT_BLOCK_CROSS);
  200. if (li->origin)
  201. error_info_append(ei, li->origin);
  202. return ei;
  203. }
  204. for (size_t i = 0; i < loc->length; i++) {
  205. li->overlap_table[loc->offset + i] = line;
  206. li->overlap_origins[loc->offset + i] = li->origin;
  207. }
  208. return NULL;
  209. }
  210. /*
  211. Tokenize ASMLines into ASMInstructions and ASMData.
  212. NULL is returned on success and an ErrorInfo object is returned on failure.
  213. state->instructions, state->data, and state->symtable may or may not be
  214. modified regardless of success.
  215. */
  216. ErrorInfo* tokenize(AssemblerState *state)
  217. {
  218. ASMLayoutInfo li = {
  219. .size = state->rom_size ? state->rom_size : ROM_SIZE_MAX,
  220. .origin = NULL, .bank = 0, .cross_blocks = state->cross_blocks
  221. };
  222. li.overlap_table = calloc(li.size, sizeof(const ASMLine*));
  223. li.overlap_origins = calloc(li.size, sizeof(const ASMLine*));
  224. if (!li.overlap_table || !li.overlap_origins)
  225. OUT_OF_MEMORY()
  226. ErrorInfo *ei = NULL;
  227. ASMInstruction dummy_inst = {.next = NULL}, *inst, *prev_inst = &dummy_inst;
  228. ASMData dummy_data = {.next = NULL}, *data, *prev_data = &dummy_data;
  229. const ASMLine *line = state->lines;
  230. size_t offset = 0;
  231. ASMSlotInfo si = {.lines = {0}};
  232. for (size_t i = 0; i < HEADER_SIZE; i++)
  233. li.overlap_table[state->header.offset + i] = &header_sentinel;
  234. memset(si.slots, -1, MMU_NUM_ROM_BANKS);
  235. while (line) {
  236. if (line->is_label) {
  237. if (offset >= li.size) {
  238. ei = error_info_create(line, ET_LAYOUT, ED_LYT_BOUNDS);
  239. goto cleanup;
  240. }
  241. int8_t slot = si.slots[offset / MMU_NUM_ROM_BANKS];
  242. if ((ei = add_label_to_table(state->symtable, line, offset, slot)))
  243. goto cleanup;
  244. }
  245. else if (IS_LOCAL_DIRECTIVE(line)) {
  246. if (IS_DIRECTIVE(line, DIR_ORIGIN)) {
  247. if ((ei = handle_origin_directive(line, &offset)))
  248. goto cleanup;
  249. li.origin = line;
  250. li.bank = offset / MMU_ROM_BANK_SIZE;
  251. }
  252. else if (IS_DIRECTIVE(line, DIR_BLOCK)) {
  253. if ((ei = handle_block_directive(line, &offset, &si)))
  254. goto cleanup;
  255. li.origin = line;
  256. li.bank = offset / MMU_ROM_BANK_SIZE;
  257. }
  258. else {
  259. if ((ei = parse_data(line, &data, offset)))
  260. goto cleanup;
  261. offset += data->loc.length;
  262. prev_data->next = data;
  263. prev_data = data;
  264. if ((ei = check_layout(&li, &data->loc, line)))
  265. goto cleanup;
  266. }
  267. }
  268. else {
  269. if ((ei = parse_instruction(line, &inst, offset)))
  270. goto cleanup;
  271. offset += inst->loc.length;
  272. prev_inst->next = inst;
  273. prev_inst = inst;
  274. if ((ei = check_layout(&li, &inst->loc, line)))
  275. goto cleanup;
  276. }
  277. line = line->next;
  278. }
  279. cleanup:
  280. state->instructions = dummy_inst.next;
  281. state->data = dummy_data.next;
  282. free(li.overlap_table);
  283. free(li.overlap_origins);
  284. return ei;
  285. }