An emulator, assembler, and disassembler for the Sega Game Gear
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 
 

242 行
7.4 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 "../rom.h"
  10. /* Sentinel values for overlap table */
  11. const ASMLine header_sentinel, bounds_sentinel;
  12. /*
  13. Add a given line, representing a label, to the symbol table.
  14. Return NULL on success and an ErrorInfo object on failure (in the case of
  15. duplicate labels).
  16. */
  17. static ErrorInfo* add_label_to_table(
  18. ASMSymbolTable *symtable, const ASMLine *line, size_t offset, ssize_t slot)
  19. {
  20. char *symbol = strndup(line->data, line->length - 1);
  21. if (!symbol)
  22. OUT_OF_MEMORY()
  23. const ASMSymbol *current = asm_symtable_find(symtable, symbol);
  24. if (current) {
  25. ErrorInfo *ei = error_info_create(line, ET_SYMBOL, ED_SYM_DUPE_LABELS);
  26. error_info_append(ei, current->line);
  27. return ei;
  28. }
  29. ASMSymbol *label = malloc(sizeof(ASMSymbol));
  30. if (!label)
  31. OUT_OF_MEMORY()
  32. size_t block_offset = offset & 0x3FFF;
  33. label->offset = slot >= 0 ? (block_offset + slot * 0x4000) :
  34. (offset >= 0xC000 ? (block_offset + 0x8000) : offset);
  35. label->symbol = symbol;
  36. label->line = line;
  37. asm_symtable_insert(symtable, label);
  38. return NULL;
  39. }
  40. /*
  41. Handle an origin directive by updating the offset and (maybe) the slot.
  42. Return NULL on success and an ErrorInfo object on failure.
  43. */
  44. static ErrorInfo* handle_origin_directive(
  45. const ASMLine *line, size_t *offset, ssize_t *slot)
  46. {
  47. if (!DIRECTIVE_HAS_ARG(line, DIR_ORIGIN))
  48. return error_info_create(line, ET_PREPROC, ED_PP_NO_ARG);
  49. uint32_t arg;
  50. if (!dparse_uint32_t(&arg, line, DIR_ORIGIN))
  51. return error_info_create(line, ET_PREPROC, ED_PP_BAD_ARG);
  52. *offset = arg;
  53. // TODO: if different block, *slot <-- slot lookup table for this block
  54. return NULL;
  55. }
  56. /*
  57. Handle a block directive by updating the offset and slot.
  58. Return NULL on success and an ErrorInfo object on failure.
  59. */
  60. static ErrorInfo* handle_block_directive(
  61. const ASMLine *line, size_t *offset, ssize_t *slot)
  62. {
  63. if (!DIRECTIVE_HAS_ARG(line, DIR_BLOCK))
  64. return error_info_create(line, ET_PREPROC, ED_PP_NO_ARG);
  65. uint8_t *args;
  66. size_t dir_offset = DIRECTIVE_OFFSET(line, DIR_BLOCK) + 1, nargs;
  67. if (!parse_bytes(&args, &nargs, line->data + dir_offset,
  68. line->length - dir_offset))
  69. return error_info_create(line, ET_PREPROC, ED_PP_BAD_ARG);
  70. if (nargs < 1 || nargs > 2 || args[0] >= 64 || (nargs == 2 && args[1] > 2))
  71. return error_info_create(line, ET_PREPROC, ED_PP_BAD_ARG);
  72. if (nargs == 2 && args[0] == 0 && args[1] != 0)
  73. return error_info_create(line, ET_LAYOUT, ED_LYT_BLOCK0);
  74. *offset = args[0] * (16 << 10);
  75. *slot = nargs == 2 ? args[1] : -1;
  76. free(args);
  77. return NULL;
  78. }
  79. /*
  80. Parse data encoded in a line into an ASMData object.
  81. On success, return NULL and store the instruction in *data_ptr. On failure,
  82. return an ErrorInfo object; *data_ptr is not modified.
  83. */
  84. static ErrorInfo* parse_data(
  85. const ASMLine *line, ASMData **data_ptr, size_t offset)
  86. {
  87. // TODO
  88. DEBUG("parse_data(): %.*s", (int) line->length, line->data)
  89. return error_info_create(line, ET_PARSER, ED_PARSE_SYNTAX);
  90. }
  91. /*
  92. Parse an instruction encoded in a line into an ASMInstruction object.
  93. On success, return NULL and store the instruction in *inst_ptr. On failure,
  94. return an ErrorInfo object; *inst_ptr is not modified.
  95. */
  96. static ErrorInfo* parse_instruction(
  97. const ASMLine *line, ASMInstruction **inst_ptr, size_t offset)
  98. {
  99. // TODO
  100. DEBUG("parse_instruction(): %.*s", (int) line->length, line->data)
  101. return error_info_create(line, ET_PARSER, ED_PARSE_SYNTAX);
  102. }
  103. /*
  104. Check if the given location overlaps with any existing objects.
  105. On success, return NULL and add the location to the overlap table.
  106. On failure, return an ErrorInfo object.
  107. */
  108. static ErrorInfo* check_layout(
  109. const ASMLine **overlap_table, size_t size, const ASMLocation *loc,
  110. const ASMLine *line, const ASMLine *origin)
  111. {
  112. // TODO: never let boundaries cross without state->cross_blocks
  113. const ASMLine *clash = NULL;
  114. if (loc->offset + loc->length > size) {
  115. clash = &bounds_sentinel;
  116. } else {
  117. for (size_t i = 0; i < loc->length; i++) {
  118. if (overlap_table[loc->offset + i]) {
  119. clash = overlap_table[loc->offset + i];
  120. break;
  121. }
  122. }
  123. }
  124. if (clash) {
  125. ErrorInfo *ei = error_info_create(line, ET_LAYOUT,
  126. (clash == &header_sentinel) ? ED_LYT_OVERLAP_HEAD :
  127. (clash == &bounds_sentinel) ? ED_LYT_BOUNDS : ED_LYT_OVERLAP);
  128. if (origin)
  129. error_info_append(ei, origin);
  130. if (clash != &header_sentinel && clash != &bounds_sentinel)
  131. error_info_append(ei, clash);
  132. return ei;
  133. }
  134. for (size_t i = 0; i < loc->length; i++)
  135. overlap_table[loc->offset + i] = line;
  136. return NULL;
  137. }
  138. /*
  139. Tokenize ASMLines into ASMInstructions and ASMData.
  140. NULL is returned on success and an ErrorInfo object is returned on failure.
  141. state->instructions, state->data, and state->symtable may or may not be
  142. modified regardless of success.
  143. */
  144. ErrorInfo* tokenize(AssemblerState *state)
  145. {
  146. size_t size = state->rom_size ? state->rom_size : ROM_SIZE_MAX;
  147. const ASMLine **overlap_table = calloc(size, sizeof(const ASMLine*));
  148. if (!overlap_table)
  149. OUT_OF_MEMORY()
  150. ErrorInfo *ei = NULL;
  151. ASMInstruction dummy_inst = {.next = NULL}, *inst, *prev_inst = &dummy_inst;
  152. ASMData dummy_data = {.next = NULL}, *data, *prev_data = &dummy_data;
  153. const ASMLine *line = state->lines, *origin = NULL;
  154. size_t offset = 0;
  155. ssize_t slot = -1;
  156. for (size_t i = 0; i < HEADER_SIZE; i++)
  157. overlap_table[state->header.offset + i] = &header_sentinel;
  158. while (line) {
  159. if (line->is_label) {
  160. if ((ei = add_label_to_table(state->symtable, line, offset, slot)))
  161. goto cleanup;
  162. }
  163. else if (IS_LOCAL_DIRECTIVE(line)) {
  164. if (IS_DIRECTIVE(line, DIR_ORIGIN)) {
  165. if ((ei = handle_origin_directive(line, &offset, &slot)))
  166. goto cleanup;
  167. origin = line;
  168. }
  169. else if (IS_DIRECTIVE(line, DIR_BLOCK)) {
  170. if ((ei = handle_block_directive(line, &offset, &slot)))
  171. goto cleanup;
  172. origin = line;
  173. }
  174. else {
  175. if ((ei = parse_data(line, &data, offset)))
  176. goto cleanup;
  177. offset += data->loc.length;
  178. prev_data->next = data;
  179. prev_data = data;
  180. if ((ei = check_layout(overlap_table, size, &data->loc, line, origin)))
  181. goto cleanup;
  182. }
  183. }
  184. else {
  185. if ((ei = parse_instruction(line, &inst, offset)))
  186. goto cleanup;
  187. offset += inst->loc.length;
  188. prev_inst->next = inst;
  189. prev_inst = inst;
  190. if ((ei = check_layout(overlap_table, size, &inst->loc, line, origin)))
  191. goto cleanup;
  192. }
  193. line = line->next;
  194. }
  195. cleanup:
  196. state->instructions = dummy_inst.next;
  197. state->data = dummy_data.next;
  198. free(overlap_table);
  199. return ei;
  200. }