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.
 
 
 
 
 

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