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.
 
 
 
 
 

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