An emulator, assembler, and disassembler for the Sega Game Gear
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 
 

388 строки
11 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 "assembler.h"
  5. #include "assembler/directives.h"
  6. #include "assembler/errors.h"
  7. #include "assembler/io.h"
  8. #include "assembler/parse_util.h"
  9. #include "assembler/preprocessor.h"
  10. #include "assembler/state.h"
  11. #include "logging.h"
  12. #include "rom.h"
  13. #include "util.h"
  14. /* Sentinel values for overlap table */
  15. const ASMLine header_sentinel, bounds_sentinel;
  16. /*
  17. Add a given line, representing a label, to the symbol table.
  18. Return NULL on success and an ErrorInfo object on failure (in the case of
  19. duplicate labels).
  20. */
  21. static ErrorInfo* add_label_to_table(
  22. ASMSymbolTable *symtable, const ASMLine *line, size_t offset)
  23. {
  24. char *symbol = strndup(line->data, line->length - 1);
  25. if (!symbol)
  26. OUT_OF_MEMORY()
  27. const ASMSymbol *current = asm_symtable_find(symtable, symbol);
  28. if (current) {
  29. ErrorInfo *ei = error_info_create(line, ET_SYMBOL, ED_SYM_DUPE_LABELS);
  30. error_info_append(ei, current->line);
  31. return ei;
  32. }
  33. ASMSymbol *label = malloc(sizeof(ASMSymbol));
  34. if (!label)
  35. OUT_OF_MEMORY()
  36. // TODO: don't assume all ROM gets mapped to slot 2
  37. label->offset = (offset >= 0xC000) ? ((offset & 0x3FFF) + 0x8000) : offset;
  38. label->symbol = symbol;
  39. label->line = line;
  40. asm_symtable_insert(symtable, label);
  41. return NULL;
  42. }
  43. /*
  44. Parse data encoded in a line into an ASMData object.
  45. On success, return NULL and store the instruction in *data_ptr. On failure,
  46. return an ErrorInfo object; *data_ptr is not modified.
  47. */
  48. static ErrorInfo* parse_data(
  49. const ASMLine *line, ASMData **data_ptr, size_t offset)
  50. {
  51. // TODO
  52. DEBUG("parse_data(): %.*s", (int) line->length, line->data)
  53. return error_info_create(line, ET_PARSER, ED_PARSE_SYNTAX);
  54. }
  55. /*
  56. Parse an instruction encoded in a line into an ASMInstruction object.
  57. On success, return NULL and store the instruction in *inst_ptr. On failure,
  58. return an ErrorInfo object; *inst_ptr is not modified.
  59. */
  60. static ErrorInfo* parse_instruction(
  61. const ASMLine *line, ASMInstruction **inst_ptr, size_t offset)
  62. {
  63. // TODO
  64. DEBUG("parse_instruction(): %.*s", (int) line->length, line->data)
  65. return error_info_create(line, ET_PARSER, ED_PARSE_SYNTAX);
  66. }
  67. /*
  68. Check if the given location overlaps with any existing objects.
  69. On success, return NULL and add the location to the overlap table.
  70. On failure, return an ErrorInfo object.
  71. */
  72. static ErrorInfo* check_layout(
  73. const ASMLine **overlap_table, size_t size, const ASMLocation *loc,
  74. const ASMLine *line, const ASMLine *origin)
  75. {
  76. const ASMLine *clash = NULL;
  77. if (loc->offset + loc->length >= size) {
  78. clash = &bounds_sentinel;
  79. } else {
  80. for (size_t i = 0; i < loc->length; i++) {
  81. if (overlap_table[loc->offset + i]) {
  82. clash = overlap_table[loc->offset + i];
  83. break;
  84. }
  85. }
  86. }
  87. if (clash) {
  88. ErrorInfo *ei = error_info_create(line, ET_LAYOUT,
  89. (clash == &header_sentinel) ? ED_LYT_OVERLAP_HEAD :
  90. (clash == &bounds_sentinel) ? ED_LYT_BOUNDS : ED_LYT_OVERLAP);
  91. if (origin)
  92. error_info_append(ei, origin);
  93. if (clash != &header_sentinel && clash != &bounds_sentinel)
  94. error_info_append(ei, clash);
  95. return ei;
  96. }
  97. for (size_t i = 0; i < loc->length; i++)
  98. overlap_table[loc->offset + i] = line;
  99. return NULL;
  100. }
  101. /*
  102. Tokenize ASMLines into ASMInstructions and ASMData.
  103. NULL is returned on success and an ErrorInfo object is returned on failure.
  104. state->instructions, state->data, and state->symtable may or may not be
  105. modified regardless of success.
  106. */
  107. static ErrorInfo* tokenize(AssemblerState *state)
  108. {
  109. size_t size = state->rom_size ? state->rom_size : ROM_SIZE_MAX;
  110. const ASMLine **overlap_table = calloc(size, sizeof(const ASMLine*));
  111. if (!overlap_table)
  112. OUT_OF_MEMORY()
  113. ErrorInfo *ei = NULL;
  114. ASMInstruction dummy_inst = {.next = NULL}, *inst, *prev_inst = &dummy_inst;
  115. ASMData dummy_data = {.next = NULL}, *data, *prev_data = &dummy_data;
  116. const ASMLine *line = state->lines, *origin = NULL;
  117. size_t offset = 0;
  118. for (size_t i = 0; i < HEADER_SIZE; i++)
  119. overlap_table[state->header.offset + i] = &header_sentinel;
  120. while (line) {
  121. if (line->is_label) {
  122. if ((ei = add_label_to_table(state->symtable, line, offset)))
  123. goto cleanup;
  124. }
  125. else if (IS_LOCAL_DIRECTIVE(line)) {
  126. if (IS_DIRECTIVE(line, DIR_ORIGIN)) {
  127. if (!DIRECTIVE_HAS_ARG(line, DIR_ORIGIN)) {
  128. ei = error_info_create(line, ET_PREPROC, ED_PP_NO_ARG);
  129. goto cleanup;
  130. }
  131. uint32_t arg;
  132. if (!dparse_uint32_t(&arg, line, DIR_ORIGIN)) {
  133. ei = error_info_create(line, ET_PREPROC, ED_PP_BAD_ARG);
  134. goto cleanup;
  135. }
  136. offset = arg;
  137. origin = line;
  138. }
  139. else {
  140. if ((ei = parse_data(line, &data, offset)))
  141. goto cleanup;
  142. offset += data->loc.length;
  143. prev_data->next = data;
  144. prev_data = data;
  145. if ((ei = check_layout(overlap_table, size, &data->loc, line, origin)))
  146. goto cleanup;
  147. }
  148. }
  149. else {
  150. if ((ei = parse_instruction(line, &inst, offset)))
  151. goto cleanup;
  152. offset += inst->loc.length;
  153. prev_inst->next = inst;
  154. prev_inst = inst;
  155. if ((ei = check_layout(overlap_table, size, &inst->loc, line, origin)))
  156. goto cleanup;
  157. }
  158. line = line->next;
  159. }
  160. cleanup:
  161. state->instructions = dummy_inst.next;
  162. state->data = dummy_data.next;
  163. free(overlap_table);
  164. return ei;
  165. }
  166. /*
  167. Return the smallest ROM size that can contain the given address.
  168. This uses bit twiddling hacks up to the largest possible ROM size.
  169. */
  170. static size_t bounding_rom_size(size_t size)
  171. {
  172. size |= size >> 1;
  173. size |= size >> 2;
  174. size |= size >> 4;
  175. size |= size >> 8;
  176. size |= size >> 16;
  177. size++;
  178. return size;
  179. }
  180. /*
  181. Resolve default placeholder values in assembler state, such as ROM size.
  182. On success, no new heap objects are allocated. On error, an ErrorInfo
  183. object is returned.
  184. */
  185. static ErrorInfo* resolve_defaults(AssemblerState *state)
  186. {
  187. if (!state->rom_size) {
  188. state->rom_size = ROM_SIZE_MIN;
  189. const ASMInstruction *inst = state->instructions;
  190. while (inst) {
  191. size_t bound = inst->loc.offset + inst->loc.length;
  192. if (bound >= state->rom_size)
  193. state->rom_size = bounding_rom_size(bound);
  194. inst = inst->next;
  195. }
  196. const ASMData *data = state->data;
  197. while (data) {
  198. size_t bound = data->loc.offset + data->loc.length;
  199. if (bound >= state->rom_size)
  200. state->rom_size = bounding_rom_size(bound);
  201. data = data->next;
  202. }
  203. if (state->header.rom_size != INVALID_SIZE_CODE) {
  204. size_t decl_size = size_code_to_bytes(state->header.rom_size);
  205. if (decl_size > state->rom_size)
  206. state->rom_size = decl_size;
  207. }
  208. }
  209. if (state->header.rom_size == INVALID_SIZE_CODE)
  210. state->header.rom_size = size_bytes_to_code(state->rom_size);
  211. return NULL;
  212. }
  213. /*
  214. Resolve symbol placeholders in instructions such as jumps and branches.
  215. On success, no new heap objects are allocated. On error, an ErrorInfo
  216. object is returned.
  217. */
  218. static ErrorInfo* resolve_symbols(AssemblerState *state)
  219. {
  220. ErrorInfo *ei;
  221. ASMInstruction *inst = state->instructions;
  222. const ASMSymbol *symbol;
  223. while (inst) {
  224. if (inst->symbol) {
  225. symbol = asm_symtable_find(state->symtable, inst->symbol);
  226. if (!symbol) {
  227. ei = error_info_create(inst->line, ET_SYMBOL, ED_SYM_NO_LABEL);
  228. return ei;
  229. }
  230. if (inst->loc.length == 3) {
  231. inst->b2 = symbol->offset & 0xFF;
  232. inst->b3 = symbol->offset >> 8;
  233. } else {
  234. inst->b3 = symbol->offset & 0xFF;
  235. inst->b4 = symbol->offset >> 8;
  236. }
  237. free(inst->symbol);
  238. inst->symbol = NULL;
  239. }
  240. inst = inst->next;
  241. }
  242. return NULL;
  243. }
  244. /*
  245. Convert finalized ASMInstructions and ASMData into a binary data block.
  246. This function should never fail.
  247. */
  248. static void serialize_binary(AssemblerState *state, uint8_t *binary)
  249. {
  250. // TODO
  251. for (size_t i = 0; i < state->rom_size; i++)
  252. binary[i] = 'X';
  253. }
  254. /*
  255. Assemble the z80 source code in the source code buffer into binary data.
  256. If successful, return the size of the assembled binary data and change
  257. *binary_ptr to point to the assembled ROM data buffer. *binary_ptr must be
  258. free()'d when finished.
  259. If an error occurred, return 0 and update *ei_ptr to point to an ErrorInfo
  260. object which can be shown to the user with error_info_print(). The
  261. ErrorInfo object must be destroyed with error_info_destroy() when finished.
  262. In either case, only one of *binary_ptr and *ei_ptr is modified.
  263. */
  264. size_t assemble(const LineBuffer *source, uint8_t **binary_ptr, ErrorInfo **ei_ptr)
  265. {
  266. AssemblerState state;
  267. ErrorInfo *error_info;
  268. size_t retval = 0;
  269. state_init(&state);
  270. if ((error_info = preprocess(&state, source)))
  271. goto error;
  272. asm_symtable_init(&state.symtable);
  273. #ifdef DEBUG_MODE
  274. asm_lines_print(state.lines);
  275. #endif
  276. if ((error_info = tokenize(&state)))
  277. goto error;
  278. if ((error_info = resolve_defaults(&state)))
  279. goto error;
  280. if ((error_info = resolve_symbols(&state)))
  281. goto error;
  282. uint8_t *binary = malloc(sizeof(uint8_t) * state.rom_size);
  283. if (!binary)
  284. OUT_OF_MEMORY()
  285. serialize_binary(&state, binary);
  286. *binary_ptr = binary;
  287. retval = state.rom_size;
  288. goto cleanup;
  289. error:
  290. *ei_ptr = error_info;
  291. cleanup:
  292. state_free(&state);
  293. return retval;
  294. }
  295. /*
  296. Assemble the z80 source code at the input path into a binary file.
  297. Return true if the operation was a success and false if it was a failure.
  298. Errors are printed to STDOUT; if the operation was successful then nothing
  299. is printed.
  300. */
  301. bool assemble_file(const char *src_path, const char *dst_path)
  302. {
  303. LineBuffer *source = read_source_file(src_path, true);
  304. if (!source)
  305. return false;
  306. uint8_t *binary;
  307. ErrorInfo *error_info;
  308. size_t size = assemble(source, &binary, &error_info);
  309. line_buffer_free(source);
  310. if (!size) {
  311. error_info_print(error_info, stderr);
  312. error_info_destroy(error_info);
  313. return false;
  314. }
  315. bool success = write_binary_file(dst_path, binary, size);
  316. free(binary);
  317. return success;
  318. }