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.
 
 
 
 
 

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