An emulator, assembler, and disassembler for the Sega Game Gear
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 
 

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