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.
 
 
 
 
 

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