An emulator, assembler, and disassembler for the Sega Game Gear
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 
 

248 lignes
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. }