summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/z80/z80make.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/cpu/z80/z80make.py')
-rw-r--r--src/devices/cpu/z80/z80make.py351
1 files changed, 351 insertions, 0 deletions
diff --git a/src/devices/cpu/z80/z80make.py b/src/devices/cpu/z80/z80make.py
new file mode 100644
index 00000000000..023cdfd29ba
--- /dev/null
+++ b/src/devices/cpu/z80/z80make.py
@@ -0,0 +1,351 @@
+#!/usr/bin/python
+# license:BSD-3-Clause
+# copyright-holders:Andre I. Holub
+from __future__ import print_function
+
+USAGE = """
+Usage:
+%s [type] z80.lst z80.inc
+"""
+import sys
+
+class IndStr:
+ def __init__(self, src, indent = None):
+ self.str = src.strip()
+ self.indent = indent
+ if not indent:
+ self.indent = src[:len(src) - len(src.lstrip())]
+
+ def line(self):
+ return self.str
+
+ def get_indent(self):
+ return self.indent
+
+ def is_comment(self):
+ return self.str.startswith("#") and not self.str.startswith("#if") and not self.str.startswith("#endif")
+
+ def is_blank(self):
+ return not self.str
+
+ def has_indent(self):
+ return self.indent != ''
+
+ def strip_indent(self, n):
+ if self.indent != '':
+ self.indent = self.indent[n:]
+
+ def replace(self, new):
+ self.str = new
+
+ def replace(self, old, new):
+ return IndStr(self.str.replace(old, new), self.indent)
+
+ def with_str(self, new_str):
+ return IndStr(new_str, self.indent)
+
+ def split(self):
+ return self.str.split()
+
+ def print(self, str, f):
+ print("\t\t%s%s" % (self.indent, str), file=f)
+
+
+
+class Opcode:
+ def __init__(self, prefix, code):
+ self.prefix = prefix
+ self.code = code
+ self.source = []
+
+ def add_source_lines(self, lines):
+ self.source.extend(lines)
+
+ def with_steps(self):
+ for i in range(0, len(self.source)):
+ tokens = self.source[i].line().split()
+ if (tokens[0] == '+') or (len(tokens) > 2 and tokens[1] == "!!"):
+ return True
+ return False
+
+ def save_dasm(self, step_switch: bool, f):
+ has_steps = self.with_steps()
+ if has_steps:
+ if step_switch:
+ print("\t\tswitch (u8(m_ref))", file=f)
+ print("\t\t{", file=f)
+ print("\t\tcase 0x00:", file=f)
+ else:
+ print("\t\t//case 0x00:", file=f)
+ step = 0
+ for i in range(0, len(self.source)):
+ il = self.source[i]
+ if not has_steps or not step_switch:
+ il.strip_indent(1)
+ line = il.line()
+ tokens = line.split()
+ last_line = i + 1 == len(self.source)
+
+ if tokens[0] == '+':
+ il.print("m_icount -= %s;" % (" ".join(tokens[1:])), f)
+ step += 1
+ il.print("if (m_icount <= 0) {", f)
+ if not last_line:
+ to_step = hex(256 + step)[3:]
+ il.print(" m_ref = 0x%s%s%s;" % (self.prefix, self.code, to_step), f)
+ il.print(" return;", f)
+ il.print("}", f)
+ if step_switch:
+ il.print("[[fallthrough]];", f)
+ print("\t\tcase 0x%s:" % (to_step), file=f)
+ else:
+ print("\t\t//case 0x%s:" % (to_step), file=f)
+ else:
+ il.print(" m_ref = 0xffff00;", f)
+ il.print(" return;", f)
+ il.print("}", f)
+ elif (len(tokens) > 2 and tokens[1] == "!!"):
+ step += 1;
+ if step_switch:
+ il.print("[[fallthrough]];", f)
+ print("\t\tcase 0x%s:" % (hex(256 + step)[3:]), file=f)
+ else:
+ print("\t\t//case 0x%s:" % (hex(256 + step)[3:]), file=f)
+ il.print("%s" % " ".join(tokens[2:]), f)
+ il.print("m_icount -= %s;" % (tokens[0]), f)
+ il.print("if (m_icount <= 0) {", f)
+ il.print(" if (access_to_be_redone()) {", f)
+ il.print(" m_icount += %s;" % (tokens[0]), f)
+ il.print(" m_ref = 0x%s%s%s;" % (self.prefix, self.code, hex(256 + step)[3:]), f)
+ il.print(" } else {", f)
+ if not last_line:
+ step += 1
+ to_step = hex(256 + step)[3:]
+ il.print(" m_ref = 0x%s%s%s;" % (self.prefix, self.code, to_step), f)
+ il.print(" }", f)
+ il.print(" return;", f)
+ il.print("}", f)
+ if step_switch:
+ il.print("[[fallthrough]];", f)
+ print("\t\tcase 0x%s:" % (to_step), file=f)
+ else:
+ print("\t\t//case 0x%s:" % (to_step), file=f)
+ else:
+ il.print(" m_ref = 0xffff00;", f)
+ il.print(" }", f)
+ il.print(" return;", f)
+ il.print("}", f)
+ else:
+ il.print("%s" % line, f)
+ if has_steps and step_switch:
+ print("\t\t}", file=f)
+
+class Macro:
+ def __init__(self, name, arg_name = None):
+ self.name = name
+ self.source = []
+ self.arg_name = arg_name
+
+ def apply(self, arg):
+ if self.arg_name is not None:
+ return [ r.replace(self.arg_name, arg) for r in self.source ]
+ else:
+ return self.source
+
+ def add_source_lines(self, lines):
+ self.source.extend(lines)
+
+class OpcodeList:
+ def __init__(self, gen, fname):
+ self.gen = gen
+ self.opcode_info = {} # prefix -> [Opcode]
+ self.macros = {}
+
+ try:
+ f = open(fname, "r")
+ except Exception:
+ err = sys.exc_info()[1]
+ sys.stderr.write("Cannot read opcodes file %s [%s]\n" % (fname, err))
+ sys.exit(1)
+
+ inf = None
+ for ln in f:
+ line = IndStr(ln)
+ # Skip comments except macros
+ if line.is_comment() or line.is_blank():
+ continue
+ if line.has_indent():
+ if inf is not None:
+ if isinstance(inf, Macro):
+ inf.add_source_lines([line])
+ else:
+ inf.add_source_lines(self.pre_process(line))
+ else:
+ # New opcode
+ tokens = line.split()
+ if tokens[0] == "macro":
+ arg_name = None
+ if len(tokens) > 2:
+ arg_name = tokens[2]
+ nnames = tokens[1].split(":")
+ if len(nnames) == 2:
+ inf = Macro(nnames[1], arg_name)
+ if nnames[0] == self.gen:
+ self.macros[nnames[1]] = inf
+ else:
+ inf = Macro(nnames[0], arg_name)
+ if None == self.gen:
+ if nnames[0] in self.macros:
+ sys.stderr.write("Replacing macro: %s\n" % nnames[0])
+ self.macros[nnames[0]] = inf
+ else:
+ if not nnames[0] in self.macros:
+ self.macros[nnames[0]] = inf
+ else:
+ ntokens = tokens[0].split(":")
+ gen = None if len(ntokens) == 1 else ntokens[0]
+ prefix = ntokens[0][:2] if len(ntokens) == 1 else ntokens[1][:2]
+ opcode = ntokens[0][2:] if len(ntokens) == 1 else ntokens[1][2:]
+ if self.opcode_info.get(prefix) is None:
+ self.opcode_info[prefix] = []
+ opcodes = self.opcode_info[prefix]
+
+ if None == gen:
+ inf = Opcode(prefix, opcode)
+ opcodes.append(inf)
+ elif gen == self.gen:
+ # Replace for ext generator
+ found = False
+ found_index = 0
+ for i in range(len(opcodes)):
+ if opcodes[i].code == opcode:
+ found = True
+ found_index = i
+ if found:
+ inf = Opcode(prefix, opcode)
+ opcodes[found_index] = inf
+ else:
+ sys.stderr.write("[%s] Cannot find opcode: %s%s\n" % (gen, prefix, opcode))
+ sys.exit(1)
+ else:
+ inf = Opcode(prefix, '/dev/null')
+
+ def pre_process(self, iline):
+ out = []
+ line = iline.str
+ line_toc = line.split()
+ times = 1
+ if len(line_toc) > 2 and line_toc[1] == "*":
+ times = int(line_toc[0])
+ line_toc = line_toc[2:]
+ line = " ".join(line_toc)
+ for i in range(times):
+ if line_toc[0] == 'call':
+ name = line_toc[1]
+ arg = None
+ if len(line_toc) > 2:
+ arg = " ".join(line_toc[2:])
+ if name in self.macros:
+ macro = self.macros[name]
+ ([out.extend(self.pre_process(il)) for il in macro.apply(arg)])
+ else:
+ sys.stderr.write("Macro not found %s\n" % name)
+ out.append(iline.with_str("... %s" % name))
+ else:
+ out.append(iline.with_str(line))
+ return out
+
+ def switch_prefix(self, prefixes, reenter: bool, f):
+ prefix_switch = len(prefixes) > 1
+
+ if prefix_switch:
+ print("switch (u8(m_ref >> 16)) // prefix", file=f)
+ print("{", file=f)
+
+ for prefix in sorted(prefixes):
+ is_rop = prefix == 'ff'
+ opc_switch = not is_rop
+ if prefix_switch:
+ print("case 0x%s:" % (prefix), file=f)
+ print("{", file=f)
+ if opc_switch:
+ print("\tswitch (u8(m_ref >> 8)) // opcode", file=f)
+ print("\t{", file=f)
+ for opc in self.opcode_info[prefix]:
+ # reenter loop only process steps > 0
+ if not reenter or opc.with_steps():
+ if opc_switch:
+ print("\tcase 0x%s:" % (opc.code), file=f)
+ opc.save_dasm(step_switch=reenter, f=f)
+ print("\t\tcontinue;", file=f)
+ print("", file=f)
+ if opc_switch:
+ print("\t}", file=f)
+
+ if prefix_switch:
+ print("} break; // prefix: %s" % (prefix), file=f)
+ print("", file=f)
+ if prefix_switch:
+ print("} // switch prefix", file=f)
+
+ def save_exec(self, f):
+ print("if (m_wait_state)", file=f)
+ print("{", file=f)
+ print("\tm_icount = 0; // stalled", file=f)
+ print("\treturn;", file=f)
+ print("}", file=f)
+ print("", file=f)
+ print("const bool nomemrq_en = !m_nomreq_cb.isunset();", file=f)
+ print("[[maybe_unused]] const bool refresh_en = !m_refresh_cb.isunset();", file=f)
+ print("", file=f)
+ print("bool interrupted = true;", file=f)
+ print("while (u8(m_ref) != 0x00) {", file=f)
+ print("// slow re-enter", file=f)
+ print("\t// workaround to simulate main loop behavior where continue statement relays on having it set after", file=f)
+ print("\tif (!interrupted) {", file=f)
+ print("\t\tm_ref = 0xffff00;", file=f)
+ print("\t\tcontinue;", file=f)
+ print("\t}", file=f)
+ print("\tinterrupted = false;", file=f)
+ print("", file=f)
+ self.switch_prefix(self.opcode_info.keys(), reenter=True, f=f)
+ print("", file=f)
+ print('assert((void("switch statement above must cover all possible cases!"), false));', file=f)
+ print("} // end: slow", file=f)
+ print("if (m_ref != 0xffff00) goto process;", file=f)
+ print("", file=f)
+ print("while (true) { // fast process", file=f)
+ print("\t\t// rop: unwrapped ff prefix", file=f)
+ self.switch_prefix(['ff'], reenter=False, f=f)
+ print("", file=f)
+ print("process:", file=f)
+ self.switch_prefix(self.opcode_info.keys() - ['ff'], reenter=False, f=f)
+ print("} // end: fast", file=f)
+ print('assert((void("unreachable!"), false));', file=f)
+
+def main(argv):
+ if len(argv) != 3 and len(argv) != 4:
+ print(USAGE % argv[0])
+ return 1
+
+ fidx = 1
+ gen = None
+ if len(argv) == 4:
+ gen = argv[1]
+ fidx = 2
+
+ opcodes = OpcodeList(gen, argv[fidx])
+
+ try:
+ f = open(argv[fidx + 1], "w")
+ except Exception:
+ err = sys.exc_info()[fidx]
+ sys.stderr.write("cannot write file %s [%s]\n" % (argv[fidx + 1], err))
+ sys.exit(1)
+
+ opcodes.save_exec(f)
+ f.close()
+
+# ======================================================================
+if __name__ == "__main__":
+ sys.exit(main(sys.argv))