diff options
Diffstat (limited to 'src/devices/cpu/z80/z80make.py')
-rw-r--r-- | src/devices/cpu/z80/z80make.py | 351 |
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)) |