An emulator, assembler, and disassembler for the Sega Game Gear
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 
 

246 lignes
6.6 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. #define IS_LABEL(line) (line->data[line->length - 1] == ':')
  15. /*
  16. Parse an instruction encoded in line into an ASMInstruction object.
  17. On success, return NULL and store the instruction in *inst_ptr. On failure,
  18. return an ErrorInfo object; *inst_ptr is not modified.
  19. */
  20. static ErrorInfo* parse_instruction(
  21. const ASMLine *line, ASMInstruction **inst_ptr, size_t offset)
  22. {
  23. // TODO
  24. return error_info_create(line, ET_PARSER, ED_PARSE_SYNTAX);
  25. }
  26. /*
  27. Tokenize ASMLines into ASMInstructions.
  28. On success, state->instructions is modified and NULL is returned. On error,
  29. an ErrorInfo object is returned and state->instructions is not modified.
  30. state->symtable may or may not be modified regardless of success.
  31. */
  32. static ErrorInfo* tokenize(AssemblerState *state)
  33. {
  34. size_t size = state->rom_size ? state->rom_size : ROM_SIZE_MAX;
  35. const ASMLine **overlap_table = calloc(size, sizeof(const ASMLine*));
  36. if (!overlap_table)
  37. OUT_OF_MEMORY()
  38. // TODO: fill overlap table for header with pointers to a dummy object
  39. ErrorInfo *ei = NULL;
  40. ASMInstruction dummy = {.next = NULL}, *inst, *prev = &dummy;
  41. const ASMLine *line = state->lines, *origin = NULL;
  42. size_t offset = 0;
  43. while (line) {
  44. if (IS_LOCAL_DIRECTIVE(line)) {
  45. if (!IS_DIRECTIVE(line, DIR_ORIGIN)) {
  46. // TODO
  47. ei = error_info_create(line, ET_PREPROC, ED_PP_UNKNOWN);
  48. goto error;
  49. }
  50. if (!DIRECTIVE_HAS_ARG(line, DIR_ORIGIN)) {
  51. ei = error_info_create(line, ET_PREPROC, ED_PP_NO_ARG);
  52. goto error;
  53. }
  54. uint32_t arg;
  55. if (!parse_uint32_t(&arg, line, DIR_ORIGIN)) {
  56. ei = error_info_create(line, ET_PREPROC, ED_PP_BAD_ARG);
  57. goto error;
  58. }
  59. offset = arg;
  60. origin = line;
  61. }
  62. else if (IS_LABEL(line)) {
  63. // TODO: add to symbol table
  64. }
  65. else {
  66. if ((ei = parse_instruction(line, &inst, offset)))
  67. goto error;
  68. // TODO: bounded check on range [offset, offset + inst->length) against overlap table
  69. // if clash, use error with current line,
  70. // then table line (if not header),
  71. // then origin line (if non-null)
  72. offset += inst->length;
  73. prev->next = inst;
  74. prev = inst;
  75. }
  76. line = line->next;
  77. }
  78. state->instructions = dummy.next;
  79. goto cleanup;
  80. error:
  81. asm_instructions_free(dummy.next);
  82. cleanup:
  83. free(overlap_table);
  84. return ei;
  85. }
  86. /*
  87. Resolve default placeholder values in assembler state, such as ROM size.
  88. On success, no new heap objects are allocated. On error, an ErrorInfo
  89. object is returned.
  90. */
  91. static ErrorInfo* resolve_defaults(AssemblerState *state)
  92. {
  93. if (!state->rom_size) {
  94. state->rom_size = ROM_SIZE_MIN;
  95. // TODO: use highest instruction too
  96. if (state->header.rom_size != INVALID_SIZE_CODE) {
  97. size_t decl_size = size_code_to_bytes(state->header.rom_size);
  98. if (decl_size > state->rom_size)
  99. state->rom_size = decl_size;
  100. }
  101. }
  102. if (state->header.rom_size == INVALID_SIZE_CODE)
  103. state->header.rom_size = size_bytes_to_code(state->rom_size);
  104. return NULL;
  105. }
  106. /*
  107. Resolve symbol placeholders in instructions such as jumps and branches.
  108. On success, no new heap objects are allocated. On error, an ErrorInfo
  109. object is returned.
  110. */
  111. static ErrorInfo* resolve_symbols(AssemblerState *state)
  112. {
  113. // TODO
  114. (void) state;
  115. return NULL;
  116. }
  117. /*
  118. Convert finalized ASMInstructions into a binary data block.
  119. This function should never fail.
  120. */
  121. static void serialize_binary(AssemblerState *state, uint8_t *binary)
  122. {
  123. // TODO
  124. for (size_t i = 0; i < state->rom_size; i++)
  125. binary[i] = 'X';
  126. }
  127. /*
  128. Assemble the z80 source code in the source code buffer into binary data.
  129. If successful, return the size of the assembled binary data and change
  130. *binary_ptr to point to the assembled ROM data buffer. *binary_ptr must be
  131. free()'d when finished.
  132. If an error occurred, return 0 and update *ei_ptr to point to an ErrorInfo
  133. object which can be shown to the user with error_info_print(). The
  134. ErrorInfo object must be destroyed with error_info_destroy() when finished.
  135. In either case, only one of *binary_ptr and *ei_ptr is modified.
  136. */
  137. size_t assemble(const LineBuffer *source, uint8_t **binary_ptr, ErrorInfo **ei_ptr)
  138. {
  139. AssemblerState state;
  140. ErrorInfo *error_info;
  141. size_t retval = 0;
  142. state_init(&state);
  143. if ((error_info = preprocess(&state, source)))
  144. goto error;
  145. asm_symtable_init(&state.symtable);
  146. #ifdef DEBUG_MODE
  147. asm_lines_print(state.lines);
  148. #endif
  149. if ((error_info = tokenize(&state)))
  150. goto error;
  151. if ((error_info = resolve_defaults(&state)))
  152. goto error;
  153. if ((error_info = resolve_symbols(&state)))
  154. goto error;
  155. uint8_t *binary = malloc(sizeof(uint8_t) * state.rom_size);
  156. if (!binary)
  157. OUT_OF_MEMORY()
  158. serialize_binary(&state, binary);
  159. *binary_ptr = binary;
  160. retval = state.rom_size;
  161. goto cleanup;
  162. error:
  163. *ei_ptr = error_info;
  164. cleanup:
  165. asm_lines_free(state.lines);
  166. asm_includes_free(state.includes);
  167. asm_instructions_free(state.instructions);
  168. asm_symtable_free(state.symtable);
  169. return retval;
  170. }
  171. /*
  172. Assemble the z80 source code at the input path into a binary file.
  173. Return true if the operation was a success and false if it was a failure.
  174. Errors are printed to STDOUT; if the operation was successful then nothing
  175. is printed.
  176. */
  177. bool assemble_file(const char *src_path, const char *dst_path)
  178. {
  179. LineBuffer *source = read_source_file(src_path, true);
  180. if (!source)
  181. return false;
  182. uint8_t *binary;
  183. ErrorInfo *error_info;
  184. size_t size = assemble(source, &binary, &error_info);
  185. line_buffer_free(source);
  186. if (!size) {
  187. error_info_print(error_info, stderr);
  188. error_info_destroy(error_info);
  189. return false;
  190. }
  191. bool success = write_binary_file(dst_path, binary, size);
  192. free(binary);
  193. return success;
  194. }