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.
 
 
 
 
 

248 lines
6.9 KiB

  1. /* Copyright (C) 2014-2016 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/errors.h"
  6. #include "assembler/io.h"
  7. #include "assembler/preprocessor.h"
  8. #include "assembler/state.h"
  9. #include "assembler/tokenizer.h"
  10. #include "logging.h"
  11. #include "rom.h"
  12. #include "util.h"
  13. /*
  14. Return the smallest ROM size that can contain the given address.
  15. This uses bit twiddling hacks up to the largest possible ROM size.
  16. */
  17. static size_t bounding_rom_size(size_t size)
  18. {
  19. size--;
  20. size |= size >> 1;
  21. size |= size >> 2;
  22. size |= size >> 4;
  23. size |= size >> 8;
  24. size |= size >> 16;
  25. size++;
  26. return size;
  27. }
  28. /*
  29. Resolve default placeholder values in assembler state, such as ROM size.
  30. On success, no new heap objects are allocated. On error, an ErrorInfo
  31. object is returned.
  32. */
  33. static ErrorInfo* resolve_defaults(AssemblerState *state)
  34. {
  35. DEBUG("Resolving defaults")
  36. if (!state->rom_size) {
  37. state->rom_size = ROM_SIZE_MIN;
  38. const ASMInstruction *inst = state->instructions;
  39. while (inst) {
  40. size_t bound = inst->loc.offset + inst->loc.length;
  41. if (bound > state->rom_size)
  42. state->rom_size = bounding_rom_size(bound);
  43. inst = inst->next;
  44. }
  45. const ASMData *data = state->data;
  46. while (data) {
  47. size_t bound = data->loc.offset + data->loc.length;
  48. if (bound > state->rom_size)
  49. state->rom_size = bounding_rom_size(bound);
  50. data = data->next;
  51. }
  52. if (state->header.rom_size != INVALID_SIZE_CODE) {
  53. size_t decl_size = size_code_to_bytes(state->header.rom_size);
  54. if (decl_size > state->rom_size)
  55. state->rom_size = decl_size;
  56. }
  57. }
  58. if (state->header.rom_size == INVALID_SIZE_CODE)
  59. state->header.rom_size = size_bytes_to_code(state->rom_size);
  60. return NULL;
  61. }
  62. /*
  63. Resolve symbol placeholders in instructions such as jumps and branches.
  64. On success, no new heap objects are allocated. On error, an ErrorInfo
  65. object is returned.
  66. */
  67. static ErrorInfo* resolve_symbols(AssemblerState *state)
  68. {
  69. ErrorInfo *ei;
  70. ASMInstruction *inst = state->instructions;
  71. const ASMSymbol *symbol;
  72. DEBUG("Resolving symbols")
  73. while (inst) {
  74. if (inst->symbol) {
  75. symbol = asm_symtable_find(state->symtable, inst->symbol);
  76. if (!symbol) {
  77. ei = error_info_create(inst->line, ET_SYMBOL, ED_SYM_NO_LABEL);
  78. return ei;
  79. }
  80. inst->bytes[inst->loc.length - 2] = symbol->offset & 0xFF;
  81. inst->bytes[inst->loc.length - 1] = symbol->offset >> 8;
  82. free(inst->symbol);
  83. inst->symbol = NULL;
  84. }
  85. inst = inst->next;
  86. }
  87. return NULL;
  88. }
  89. /*
  90. Write the ROM header to the binary. Header contents are explained in rom.c.
  91. */
  92. static void write_header(const ASMHeaderInfo *info, uint8_t *binary)
  93. {
  94. uint8_t *header = binary + info->offset;
  95. // Bytes 0-7: magic string
  96. memcpy(header, rom_header_magic, HEADER_MAGIC_LEN);
  97. header += HEADER_MAGIC_LEN;
  98. // Bytes 8, 9: unused
  99. *(header++) = 0x00;
  100. *(header++) = 0x00;
  101. // Bytes A, B: checksum
  102. if (info->checksum) {
  103. uint16_t checksum = compute_checksum(binary, 0, info->rom_size);
  104. *header = checksum & 0xFF;
  105. *(header + 1) = checksum >> 8;
  106. } else {
  107. *header = *(header + 1) = 0x00;
  108. }
  109. header += 2;
  110. // Bytes C, D: product code (least significant two bytes)
  111. *header = bcd_encode(info->product_code % 100);
  112. *(header + 1) = bcd_encode((info->product_code / 100) % 100);
  113. header += 2;
  114. // Byte E: product code (most significant nibble), version
  115. *header = (info->product_code / 10000) << 4 | (info->version & 0x0F);
  116. header++;
  117. // Byte F: region code, ROM size
  118. *header = (info->region << 4) | (info->rom_size & 0x0F);
  119. }
  120. /*
  121. Convert finalized ASMInstructions and ASMData into a binary data block.
  122. This function should never fail.
  123. */
  124. static void serialize_binary(const AssemblerState *state, uint8_t *binary)
  125. {
  126. DEBUG("Serializing binary data")
  127. memset(binary, 0xFF, state->rom_size);
  128. const ASMInstruction *inst = state->instructions;
  129. while (inst) {
  130. memcpy(binary + inst->loc.offset, inst->bytes, inst->loc.length);
  131. inst = inst->next;
  132. }
  133. const ASMData *data = state->data;
  134. while (data) {
  135. memcpy(binary + data->loc.offset, data->bytes, data->loc.length);
  136. data = data->next;
  137. }
  138. write_header(&state->header, binary);
  139. }
  140. /*
  141. Assemble the z80 source code in the source code buffer into binary data.
  142. If successful, return the size of the assembled binary data and change
  143. *binary_ptr to point to the assembled ROM data buffer. *binary_ptr must be
  144. free()'d when finished.
  145. If an error occurred, return 0 and update *ei_ptr to point to an ErrorInfo
  146. object which can be shown to the user with error_info_print(). The
  147. ErrorInfo object must be destroyed with error_info_destroy() when finished.
  148. In either case, only one of *binary_ptr and *ei_ptr is modified.
  149. */
  150. size_t assemble(const LineBuffer *source, uint8_t **binary_ptr, ErrorInfo **ei_ptr)
  151. {
  152. AssemblerState state;
  153. ErrorInfo *error_info;
  154. size_t retval = 0;
  155. state_init(&state);
  156. if ((error_info = preprocess(&state, source)))
  157. goto error;
  158. asm_symtable_init(&state.symtable);
  159. if (TRACE_LEVEL)
  160. asm_lines_print(state.lines);
  161. if ((error_info = tokenize(&state)))
  162. goto error;
  163. if ((error_info = resolve_defaults(&state)))
  164. goto error;
  165. if ((error_info = resolve_symbols(&state)))
  166. goto error;
  167. uint8_t *binary = cr_malloc(sizeof(uint8_t) * state.rom_size);
  168. serialize_binary(&state, binary);
  169. *binary_ptr = binary;
  170. retval = state.rom_size;
  171. goto cleanup;
  172. error:
  173. *ei_ptr = error_info;
  174. cleanup:
  175. state_free(&state);
  176. return retval;
  177. }
  178. /*
  179. Assemble the z80 source code at the input path into a binary file.
  180. Return true if the operation was a success and false if it was a failure.
  181. Errors are printed to STDOUT; if the operation was successful then nothing
  182. is printed.
  183. */
  184. bool assemble_file(const char *src_path, const char *dst_path)
  185. {
  186. DEBUG("Assembling: %s -> %s", src_path, dst_path)
  187. LineBuffer *source = read_source_file(src_path, true);
  188. if (!source)
  189. return false;
  190. uint8_t *binary;
  191. ErrorInfo *error_info;
  192. size_t size = assemble(source, &binary, &error_info);
  193. line_buffer_free(source);
  194. if (!size) {
  195. error_info_print(error_info, stderr);
  196. error_info_destroy(error_info);
  197. return false;
  198. }
  199. DEBUG("Writing output file")
  200. bool success = write_binary_file(dst_path, binary, size);
  201. free(binary);
  202. return success;
  203. }