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.
 
 
 
 
 

164 lignes
4.8 KiB

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # Copyright (C) 2014-2015 Ben Kurtovic <ben.kurtovic@gmail.com>
  4. # Released under the terms of the MIT License. See LICENSE for details.
  5. """
  6. This script generates 'src/assembler/instructions.inc.c' from
  7. 'src/assembler/instructions.yml'. It should be run automatically by make
  8. when the latter is modified, but can also be run manually.
  9. """
  10. from __future__ import print_function
  11. import re
  12. import time
  13. SOURCE = "src/assembler/instructions.yml"
  14. DEST = "src/assembler/instructions.inc.c"
  15. ENCODING = "utf8"
  16. TAB = " " * 4
  17. try:
  18. import yaml
  19. except ImportError:
  20. print("Error: PyYAML is required (https://pypi.python.org/pypi/PyYAML)\n"
  21. "If you don't want to rebuild {0}, do:\n`make -t {0}`".format(DEST))
  22. exit(1)
  23. re_date = re.compile(r"^(\s*@AUTOGEN_DATE\s*)(.*?)$", re.M)
  24. re_inst = re.compile(
  25. r"(/\* @AUTOGEN_INST_BLOCK_START \*/\n*)(.*?)"
  26. r"(\n*/\* @AUTOGEN_INST_BLOCK_END \*/)", re.S)
  27. re_lookup = re.compile(
  28. r"(/\* @AUTOGEN_LOOKUP_BLOCK_START \*/\n*)(.*?)"
  29. r"(\n*/\* @AUTOGEN_LOOKUP_BLOCK_END \*/)", re.S)
  30. class Instruction(object):
  31. """
  32. Represent a single ASM instruction mnemonic.
  33. """
  34. ARG_TYPES = {
  35. "register": "AT_REGISTER",
  36. "immediate": "AT_IMMEDIATE",
  37. "indirect": "AT_INDIRECT",
  38. "indexed": "AT_INDEXED|AT_INDIRECT",
  39. "condition": "AT_CONDITION",
  40. "port": "AT_PORT"
  41. }
  42. def __init__(self, name, data):
  43. self._name = name
  44. self._data = data
  45. def _get_arg_parse_mask(self, num):
  46. """
  47. Return the appropriate mask to parse_args() for the num-th argument.
  48. """
  49. types = set()
  50. optional = False
  51. for case in self._data["cases"]:
  52. if num < len(case["type"]):
  53. types.add(self.ARG_TYPES[case["type"][num]])
  54. else:
  55. optional = True
  56. if not types:
  57. return "AT_NONE"
  58. if optional:
  59. types.add("AT_OPTIONAL")
  60. return "|".join(types)
  61. def _handle_return(self, arg, indent=1):
  62. """
  63. Return code to handle an instruction return statement.
  64. """
  65. tabs = TAB * indent
  66. if arg == "error":
  67. return tabs + "INST_ERROR(ARG_SYNTAX)"
  68. else:
  69. data = ", ".join("0x%02X" % byte for byte in arg)
  70. return tabs + "INST_RETURN({0}, {1})".format(len(arg), data)
  71. def _handle_case(self, case):
  72. """
  73. TODO
  74. """
  75. return [TAB + "// " + str(case)]
  76. def render(self):
  77. """
  78. Convert data for an individual instruction into a C parse function.
  79. """
  80. lines = []
  81. if self._data["args"]:
  82. lines.append("{tab}INST_TAKES_ARGS(\n{tab2}{0}, \n{tab2}{1}, "
  83. "\n{tab2}{2}\n{tab})".format(
  84. self._get_arg_parse_mask(0), self._get_arg_parse_mask(1),
  85. self._get_arg_parse_mask(2), tab=TAB, tab2=TAB * 2))
  86. else:
  87. lines.append(TAB + "INST_TAKES_NO_ARGS")
  88. if "return" in self._data:
  89. lines.append(self._handle_return(self._data["return"]))
  90. elif "cases" in self._data:
  91. for case in self._data["cases"]:
  92. lines.extend(self._handle_case(case))
  93. lines.append(TAB + "INST_ERROR(ARG_TYPE)")
  94. else:
  95. msg = "Missing return or case block for {0} instruction"
  96. raise RuntimeError(msg.format(self._name))
  97. contents = "\n".join(lines)
  98. return "INST_FUNC({0})\n{{\n{1}\n}}".format(self._name, contents)
  99. def build_inst_block(data):
  100. """
  101. Return the instruction parser block, given instruction data.
  102. """
  103. return "\n\n".join(
  104. Instruction(k, v).render() for k, v in sorted(data.items()))
  105. def build_lookup_block(data):
  106. """
  107. Return the instruction lookup block, given instruction data.
  108. """
  109. macro = TAB + "HANDLE({0})"
  110. return "\n".join(macro.format(inst) for inst in sorted(data.keys()))
  111. def process(template, data):
  112. """
  113. Return C code generated from a source template and instruction data.
  114. """
  115. inst_block = build_inst_block(data)
  116. lookup_block = build_lookup_block(data)
  117. date = time.asctime(time.gmtime())
  118. result = re_date.sub(r"\1{0} UTC".format(date), template)
  119. result = re_inst.sub(r"\1{0}\3".format(inst_block), result)
  120. result = re_lookup.sub(r"\1{0}\3".format(lookup_block), result)
  121. return result
  122. def main():
  123. """
  124. Main script entry point.
  125. """
  126. with open(SOURCE, "r") as fp:
  127. text = fp.read().decode(ENCODING)
  128. with open(DEST, "r") as fp:
  129. template = fp.read().decode(ENCODING)
  130. data = yaml.load(text)
  131. result = process(template, data)
  132. # with open(DEST, "w") as fp:
  133. # fp.write(result.encode(ENCODING))
  134. print(result) # TODO: remove me!
  135. if __name__ == "__main__":
  136. main()