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.
 
 
 
 
 

430 lines
15 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. from itertools import product
  12. import re
  13. import time
  14. SOURCE = "src/assembler/instructions.yml"
  15. DEST = "src/assembler/instructions.inc.c"
  16. ENCODING = "utf8"
  17. TAB = " " * 4
  18. try:
  19. import yaml
  20. except ImportError:
  21. print("Error: PyYAML is required (https://pypi.python.org/pypi/PyYAML)\n"
  22. "If you don't want to rebuild {0}, do:\n`make -t {0}`".format(DEST))
  23. exit(1)
  24. re_date = re.compile(r"^(\s*@AUTOGEN_DATE\s*)(.*?)$", re.M)
  25. re_inst = re.compile(
  26. r"(/\* @AUTOGEN_INST_BLOCK_START \*/\n*)(.*?)"
  27. r"(\n*/\* @AUTOGEN_INST_BLOCK_END \*/)", re.S)
  28. re_lookup = re.compile(
  29. r"(/\* @AUTOGEN_LOOKUP_BLOCK_START \*/\n*)(.*?)"
  30. r"(\n*/\* @AUTOGEN_LOOKUP_BLOCK_END \*/)", re.S)
  31. def _atoi(value):
  32. """
  33. Try to convert a string to an integer, supporting decimal and hexadecimal.
  34. """
  35. try:
  36. return int(value)
  37. except ValueError:
  38. return int(value, 16)
  39. class Instruction(object):
  40. """
  41. Represent a single ASM instruction mnemonic.
  42. """
  43. ARG_TYPES = {
  44. "register": "AT_REGISTER",
  45. "immediate": "AT_IMMEDIATE",
  46. "indirect": "AT_INDIRECT",
  47. "indexed": "AT_INDEXED",
  48. "condition": "AT_CONDITION",
  49. "port": "AT_PORT"
  50. }
  51. PSEUDO_TYPES = {
  52. "indirect_hl_or_indexed": ["AT_INDIRECT", "AT_INDEXED"]
  53. }
  54. def __init__(self, name, data):
  55. self._name = name
  56. self._data = data
  57. self._has_optional_args = False
  58. self._step_state = {}
  59. def _get_arg_parse_mask(self, num):
  60. """
  61. Return the appropriate mask to parse_args() for the num-th argument.
  62. """
  63. types = set()
  64. optional = False
  65. for case in self._data["cases"]:
  66. if num < len(case["type"]):
  67. atype = case["type"][num]
  68. if atype in self.ARG_TYPES:
  69. types.add(self.ARG_TYPES[atype])
  70. else:
  71. types.update(self.PSEUDO_TYPES[atype])
  72. else:
  73. optional = True
  74. if not types:
  75. return "AT_NONE"
  76. if optional:
  77. types.add("AT_OPTIONAL")
  78. self._has_optional_args = True
  79. return "|".join(sorted(types))
  80. def _handle_return(self, ret, indent=1):
  81. """
  82. Return code to handle an instruction return statement.
  83. """
  84. data = ", ".join("0x%02X" % byte if isinstance(byte, int) else byte
  85. for byte in ret)
  86. return TAB * indent + "INST_RETURN({0}, {1})".format(len(ret), data)
  87. def _build_case_type_check(self, args):
  88. """
  89. Return the test part of an if statement for an instruction case.
  90. """
  91. conds = ["INST_TYPE({0}) == {1}".format(i, self.ARG_TYPES[cond])
  92. for i, cond in enumerate(args)]
  93. check = " && ".join(conds)
  94. if self._has_optional_args:
  95. return "INST_NARGS == {0} && ".format(len(args)) + check
  96. return check
  97. def _build_register_check(self, num, cond):
  98. """
  99. Return an expression to check for a particular register value.
  100. """
  101. return "INST_REG({0}) == REG_{1}".format(num, cond.upper())
  102. def _build_immediate_check(self, num, cond):
  103. """
  104. Return an expression to check for a particular immediate value.
  105. """
  106. if "." in cond:
  107. itype, value = cond.split(".", 1)
  108. vtype = "sval" if itype.upper() in ["S8", "REL"] else "uval"
  109. test1 = "INST_IMM({0}).mask & IMM_{1}".format(num, itype.upper())
  110. if (itype.upper() == "U16"):
  111. test1 += " && !INST_IMM({0}).is_label".format(num)
  112. test2 = "INST_IMM({0}).{1} == {2}".format(num, vtype, _atoi(value))
  113. return "({0} && {1})".format(test1, test2)
  114. return "INST_IMM({0}).mask & IMM_{1}".format(num, cond.upper())
  115. def _build_indirect_check(self, num, cond):
  116. """
  117. Return an expression to check for a particular indirect value.
  118. """
  119. if cond.startswith("reg."):
  120. test1 = "INST_INDIRECT({0}).type == AT_REGISTER".format(num)
  121. test2 = "INST_INDIRECT({0}).addr.reg == REG_{1}".format(
  122. num, cond[len("reg."):].upper())
  123. return "({0} && {1})".format(test1, test2)
  124. if cond == "imm" or cond == "immediate":
  125. return "INST_INDIRECT({0}).type == AT_IMMEDIATE".format(num)
  126. err = "Unknown condition for indirect argument: {0}"
  127. return RuntimeError(err.format(cond))
  128. def _build_indexed_check(self, num, cond):
  129. """
  130. Return an expression to check for a particular indexed value.
  131. """
  132. raise RuntimeError("The indexed arg type does not support conditions")
  133. def _build_condition_check(self, num, cond):
  134. """
  135. Return an expression to check for a particular condition value.
  136. """
  137. return "INST_COND({0}) == COND_{1}".format(num, cond.upper())
  138. def _build_port_check(self, num, cond):
  139. """
  140. Return an expression to check for a particular port value.
  141. """
  142. if cond == "reg" or cond == "reg.c":
  143. return "INST_PORT({0}).type == AT_REGISTER".format(num)
  144. if cond == "imm" or cond == "immediate":
  145. return "INST_PORT({0}).type == AT_IMMEDIATE".format(num)
  146. err = "Unknown condition for port argument: {0}"
  147. return RuntimeError(err.format(cond))
  148. _SUBCASE_LOOKUP_TABLE = {
  149. "register": _build_register_check,
  150. "immediate": _build_immediate_check,
  151. "indirect": _build_indirect_check,
  152. "indexed": _build_indexed_check,
  153. "condition": _build_condition_check,
  154. "port": _build_port_check
  155. }
  156. def _build_subcase_check(self, types, conds):
  157. """
  158. Return the test part of an if statement for an instruction subcase.
  159. """
  160. conds = [self._SUBCASE_LOOKUP_TABLE[types[i]](self, i, cond)
  161. for i, cond in enumerate(conds) if cond != "_"]
  162. return " && ".join(conds)
  163. def _iter_permutations(self, types, conds):
  164. """
  165. Iterate over all permutations of the given subcase conditions.
  166. """
  167. def split(typ, cond):
  168. if "|" in cond:
  169. splits = [split(typ, c) for c in cond.split("|")]
  170. merged = [choice for s in splits for choice in s]
  171. if len(merged) != len(set(merged)):
  172. msg = "Repeated conditions for {0}: {1}"
  173. raise RuntimeError(msg.format(typ, cond))
  174. return merged
  175. if typ == "register":
  176. if cond == "i":
  177. return ["ix", "iy"]
  178. if cond == "ih":
  179. return ["ixh", "iyh"]
  180. if cond == "il":
  181. return ["ixl", "iyl"]
  182. return [cond]
  183. splits = [split(typ, cond) for typ, cond in zip(types, conds)]
  184. num = max(len(cond) for cond in splits)
  185. if any(1 < len(cond) < num for cond in splits):
  186. msg = "Invalid condition permutations: {0}"
  187. raise RuntimeError(msg.format(conds))
  188. choices = [cond * num if len(cond) == 1 else cond for cond in splits]
  189. return zip(*choices)
  190. def _adapt_return(self, types, conds, ret):
  191. """
  192. Return a modified byte list to accomodate for prefixes and immediates.
  193. """
  194. ret = ret[:]
  195. for i, byte in enumerate(ret):
  196. if not isinstance(byte, int):
  197. if byte == "u8":
  198. try:
  199. index = types.index("immediate")
  200. imm = "INST_IMM({0})".format(index)
  201. except ValueError:
  202. index = types.index("port")
  203. imm = "INST_PORT({0}).port.imm".format(index)
  204. ret[i] = imm + ".uval"
  205. elif byte == "u16":
  206. if i < len(ret) - 1:
  207. raise RuntimeError("U16 return byte must be last")
  208. try:
  209. index = types.index("immediate")
  210. imm = "INST_IMM({0})".format(index)
  211. except ValueError:
  212. indir = types.index("indirect")
  213. if not conds[indir].startswith("imm"):
  214. msg = "Passing non-immediate indirect as immediate"
  215. raise RuntimeError(msg)
  216. imm = "INST_INDIRECT({0}).addr.imm".format(indir)
  217. ret[i] = "INST_IMM_U16_B1({0})".format(imm)
  218. ret.append("INST_IMM_U16_B2({0})".format(imm))
  219. break
  220. elif byte == "rel":
  221. index = types.index("immediate")
  222. ret[i] = "INST_IMM({0}).sval - 2".format(index)
  223. elif byte.startswith("bit(") and byte.endswith(")"):
  224. index = types.index("immediate")
  225. base = byte[4:-1]
  226. ret[i] = "{0} + 8 * INST_IMM({1}).uval".format(base, index)
  227. elif byte.startswith("step(") and byte.endswith(")"):
  228. arg = byte[5:-1]
  229. if " " in arg:
  230. base, stride = map(_atoi, arg.split(" "))
  231. else:
  232. base, stride = _atoi(arg), 1
  233. if base not in self._step_state:
  234. self._step_state[base] = 0
  235. ret[i] = base + self._step_state[base] * stride
  236. self._step_state[base] += 1
  237. else:
  238. msg = "Unsupported return byte: {0}"
  239. raise RuntimeError(msg.format(byte))
  240. for i, cond in enumerate(conds):
  241. if types[i] == "register" and cond[0] == "i":
  242. prefix = "INST_I{0}_PREFIX".format(cond[1].upper())
  243. if ret[0] != prefix:
  244. ret.insert(0, prefix)
  245. elif types[i] == "indexed":
  246. ret.insert(0, "INST_INDEX_PREFIX({0})".format(i))
  247. ret.insert(2, "INST_INDEX({0}).offset".format(i))
  248. return ret
  249. def _handle_null_case(self, case):
  250. """
  251. Return code to handle an instruction case that takes no arguments.
  252. """
  253. return [
  254. TAB + "if (INST_NARGS == 0) {",
  255. self._handle_return(case["return"], 2),
  256. TAB + "}"
  257. ]
  258. def _handle_pseudo_case(self, pseudo, case):
  259. """
  260. Return code to handle an instruction pseudo-case.
  261. Pseudo-cases are cases that have pseudo-types as arguments. This means
  262. they are expanded to cover multiple "real" argument types.
  263. """
  264. index = case["type"].index(pseudo)
  265. if pseudo == "indirect_hl_or_indexed":
  266. case["type"][index] = "indexed"
  267. indexed = self._handle_case(case)
  268. case["type"][index] = "indirect"
  269. indirect = self._handle_case(case)
  270. base_cond = self._build_case_type_check(case["type"])
  271. hl_reg = TAB * 3 + self._build_indirect_check(index, "reg.hl")
  272. indirect[0] = TAB + "if ({0} &&\n{1}) {{".format(base_cond, hl_reg)
  273. return indirect + indexed
  274. raise RuntimeError("Unknown pseudo-type: {0}".format(pseudo))
  275. def _handle_case(self, case):
  276. """
  277. Return code to handle an instruction case.
  278. """
  279. ctype = case["type"]
  280. if not ctype:
  281. return self._handle_null_case(case)
  282. for pseudo in self.PSEUDO_TYPES:
  283. if pseudo in ctype:
  284. return self._handle_pseudo_case(pseudo, case)
  285. lines = []
  286. cond = self._build_case_type_check(ctype)
  287. lines.append(TAB + "if ({0}) {{".format(cond))
  288. subcases = [(perm, sub["return"]) for sub in case["cases"]
  289. for perm in self._iter_permutations(ctype, sub["cond"])]
  290. for cond, ret in subcases:
  291. check = self._build_subcase_check(ctype, cond)
  292. ret = self._adapt_return(ctype, cond, ret)
  293. if check:
  294. lines.append(TAB * 2 + "if ({0})".format(check))
  295. lines.append(self._handle_return(ret, 3))
  296. else:
  297. lines.append(self._handle_return(ret, 2))
  298. break # Unconditional subcase
  299. else:
  300. lines.append(TAB * 2 + "INST_ERROR(ARG_VALUE)")
  301. lines.append(TAB + "}")
  302. return lines
  303. def render(self):
  304. """
  305. Convert data for an individual instruction into a C parse function.
  306. """
  307. lines = []
  308. if self._data["args"]:
  309. lines.append("{tab}INST_TAKES_ARGS(\n{tab2}{0},\n{tab2}{1},"
  310. "\n{tab2}{2}\n{tab})".format(
  311. self._get_arg_parse_mask(0), self._get_arg_parse_mask(1),
  312. self._get_arg_parse_mask(2), tab=TAB, tab2=TAB * 2))
  313. else:
  314. lines.append(TAB + "INST_TAKES_NO_ARGS")
  315. if "return" in self._data:
  316. lines.append(self._handle_return(self._data["return"]))
  317. elif "cases" in self._data:
  318. for case in self._data["cases"]:
  319. lines.extend(self._handle_case(case))
  320. lines.append(TAB + "INST_ERROR(ARG_TYPE)")
  321. else:
  322. msg = "Missing return or case block for {0} instruction"
  323. raise RuntimeError(msg.format(self._name))
  324. contents = "\n".join(lines)
  325. return "INST_FUNC({0})\n{{\n{1}\n}}".format(self._name, contents)
  326. def build_inst_block(data):
  327. """
  328. Return the instruction parser block, given instruction data.
  329. """
  330. return "\n\n".join(
  331. Instruction(k, v).render() for k, v in sorted(data.items()))
  332. def build_lookup_block(data):
  333. """
  334. Return the instruction lookup block, given instruction data.
  335. """
  336. macro = TAB + "HANDLE({0})"
  337. return "\n".join(macro.format(inst) for inst in sorted(data.keys()))
  338. def process(template, data):
  339. """
  340. Return C code generated from a source template and instruction data.
  341. """
  342. inst_block = build_inst_block(data)
  343. lookup_block = build_lookup_block(data)
  344. date = time.asctime(time.gmtime())
  345. result = re_date.sub(r"\1{0} UTC".format(date), template)
  346. result = re_inst.sub(r"\1{0}\3".format(inst_block), result)
  347. result = re_lookup.sub(r"\1{0}\3".format(lookup_block), result)
  348. return result
  349. def main():
  350. """
  351. Main script entry point.
  352. """
  353. with open(SOURCE, "r") as fp:
  354. text = fp.read().decode(ENCODING)
  355. with open(DEST, "r") as fp:
  356. template = fp.read().decode(ENCODING)
  357. data = yaml.load(text)
  358. result = process(template, data)
  359. with open(DEST, "w") as fp:
  360. fp.write(result.encode(ENCODING))
  361. if __name__ == "__main__":
  362. main()