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.

update_asm_instructions.py 16 KiB

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