An emulator, assembler, and disassembler for the Sega Game Gear
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.
 
 
 
 
 

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