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.
 
 
 
 
 

246 rindas
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. }