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.
 
 
 
 
 

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