#!/usr/bin/python

USAGE = """
Usage:
%s h8.lst <type> h8.inc (type = o/h/s20/s26)
"""
import sys

def name_to_type(name):
    if(name == "o"):
        return 0
    if(name == "h"):
        return 1
    if(name == "s20"):
        return 2
    if(name == "s26"):
        return 3
    print "Unknown chip type name %s" % name
    sys.exit(1)

def type_to_device(dtype):
    if(dtype == 0):
        return "h8_device"
    if(dtype == 1):
        return "h8h_device"
    if(dtype == 2):
        return "h8s2000_device"
    return "h8s2600_device"

def hexsplit(str):
    res = []
    for i in range(0, len(str), 2):
        res.append(int(str[i:i+2], 16))
    return res
        
def has_memory(ins):
    for s in ["read", "write", "sp_push", "sp_pop", "sp32_push", "sp32_pop", "fetch(", "prefetch_start(", "prefetch(", "prefetch_noirq("]:
        if s in ins:
            return True
    return False

def has_eat(ins):
    if "eat-all-cycles" in ins:
        return True
    return False

def save_full_one(f, t, name, source):
    print >>f, "void %s::%s_full()" % (t, name)
    print >>f, "{"
    substate = 1
    for line in source:
        if has_memory(line):
            print >>f, "\tif(icount <= bcount) { inst_substate = %d; return; }" % substate
            print >>f, line
            substate = substate + 1
        elif has_eat(line):
            print >>f, "\tif(icount) icount = bcount; inst_substate = %d; return;" % substate
            substate = substate + 1
        else:
            print >>f, line
    print >>f, "}"
    print >>f

def save_partial_one(f, t, name, source):
    print >>f, "void %s::%s_partial()" % (t, name)
    print >>f, "{"
    print >>f, "switch(inst_substate) {"
    print >>f, "case 0:"
    substate = 1
    for line in source:
        if has_memory(line):
            print >>f, "\tif(icount <= bcount) { inst_substate = %d; return; }" % substate
            print >>f, "case %d:;" % substate
            print >>f, line
            substate = substate + 1
        elif has_eat(line):
            print >>f, "\tif(icount) icount = bcount; inst_substate = %d; return;" % substate
            print >>f, "case %d:;" % substate
            substate = substate + 1
        else:
            print >>f, line
    print >>f, "\tbreak;"
    print >>f, "}"
    print >>f, "\tinst_substate = 0;"
    print >>f, "}"
    print >>f

class Hash:
    def __init__(self, premask):
        self.mask = 0x00
        self.enabled = False
        self.premask = premask
        self.d = {}

    def get(self, val, premask):
        if val in self.d:
            h = self.d[val]
            if h.premask != premask:
                print "Premask conflict"
                sys.exit(1)
            return h
        h = Hash(premask)
        self.d[val] = h
        return h
    
    def set(self, val, opc):
        if val in self.d:
            print "Collision on %s" % opc.description()
            sys.exit(1)
        self.d[val] = opc

class Opcode:
    def __init__(self, val, mask, skip, name, am1, am2, otype, dtype):
        self.name = name
        self.val = hexsplit(val)
        self.mask = hexsplit(mask)
        self.skip = int(skip)
        self.am1 = am1
        self.am2 = am2
        self.source = []
        self.otype = otype
        self.enabled = otype == -1 or (otype == 0 and dtype == 0) or (otype != 0 and dtype >= otype)
        self.needed = self.enabled and (otype == dtype or (otype == -1 and dtype == 0))
        if dtype == 0 and (am1 == "r16l" or am2 == "r16l"):
            self.mask[len(self.mask)-1] = self.mask[len(self.mask)-1] | 0x08
        if dtype == 0 and (am1 == "r16h" or am2 == "r16h"):
            self.mask[len(self.mask)-1] = self.mask[len(self.mask)-1] | 0x80
        extra_words = 0
        if (am1 == "abs16" or am2 == "abs16" or am1 == "abs16e" or am1 == "abs24e") and self.skip == 0:
            extra_words = extra_words + 1
        if (am1 == "abs32" or am2 == "abs32") and self.skip == 0:
            extra_words = extra_words + 2
        if am1 == "imm16" or am1 == "rel16" or am1 == "r16d16h" or am2 == "r16d16h" or am1 == "r32d16h" or am2 == "r32d16h":
            extra_words = extra_words + 1
        if am1 == "imm32" or am1 == "r32d32hh" or am2 == "r32d32hh":
            extra_words = extra_words + 2
        self.extra_words = extra_words
        base_offset = len(self.val)/2 + self.skip
        for i in range(0, extra_words):
            self.source.append("\tfetch(%d);\n" % (i+base_offset));

    def description(self):
        return "%s %s %s" % (self.name, self.am1, self.am2)
    
    def add_source_line(self, line):
        self.source.append(line)
    
    def is_dispatch(self):
        return False
    
    def function_name(self):
        n = self.name.replace(".", "_")
        if self.am1 != "-":
            n = n + "_" + self.am1
        if self.am2 != "-":
            n = n + "_" + self.am2
        return n

    def save_dasm(self, f):
        if len(self.mask) == 2:
            mask  = (self.mask[0] << 8) | self.mask[1]
            val   = (self.val[0]  << 8) | self.val[1]
            mask2 = 0
            val2  = 0
            slot  = 0
        elif len(self.mask) == 4:
            mask  = (self.mask[0] << 24) | (self.mask[1] << 16) | (self.mask[2] << 8) | self.mask[3]
            val   = (self.val[0]  << 24) | (self.val[1]  << 16) | (self.val[2]  << 8) | self.val[3]
            mask2 = 0
            val2  = 0
            slot = self.skip + 1
        else:
            mask  = (self.mask[2] << 24) | (self.mask[3] << 16) | (self.mask[4] << 8) | self.mask[5]
            val   = (self.val[2]  << 24) | (self.val[3]  << 16) | (self.val[4]  << 8) | self.val[5]
            mask2 = (self.mask[0] << 8)  | self.mask[1]
            val2  = (self.val[0]  << 8)  | self.val[1]
            slot = 4
        
        size = len(self.val) + 2*self.skip + 2*self.extra_words
        
        if self.name == "jsr" or self.name == "bsr":
            flags = "%d | DASMFLAG_STEP_OVER" % size
        elif self.name == "rts" or self.name == "rte":
            flags = "%d | DASMFLAG_STEP_OUT" % size
        else:
            flags = "%d" % size
        
        print >>f, "\t{ %d, 0x%08x, 0x%08x, 0x%04x, 0x%04x, \"%s\", DASM_%s, DASM_%s, %s }, // %s" % ( slot, val, mask, val2, mask2, self.name, self.am1 if self.am1 != "-" else "none", self.am2 if self.am2 != "-" else "none", flags, "needed" if self.needed else "inherited")

class Special:
    def __init__(self, val, name, otype, dtype):
        self.name = name
        self.val = int(val, 16)
        self.enabled = otype == -1 or (otype == 0 and dtype == 0) or (otype != 0 and dtype >= otype)
        self.needed = otype == dtype or (otype == -1 and dtype == 0)
        self.source = []
    
    def add_source_line(self, line):
        self.source.append(line)

class Macro:
    def __init__(self, tokens):
        self.name = tokens[1]
        self.params = []
        for i in range(2, len(tokens)):
            self.params.append(tokens[i])
        self.source = []

    def add_source_line(self, line):
        self.source.append(line)

    def apply(self, target, tokens):
        values = []
        if len(self.params) > 1:
            for i in range(0, len(self.params)-1):
                values.append(tokens[i+1])
        lval = ""
        for i in range(len(self.params)-1, len(tokens)-1):
            if lval != "":
                lval = lval + " "
            lval = lval + tokens[i+1]
        values.append(lval)
        for i in range(0, len(self.source)):
            line = self.source[i]
            for i in range(0, len(self.params)):
                line = line.replace(self.params[i], values[i])
            target.add_source_line(line)

class DispatchStep:
    def __init__(self, id, pos, opc):
        self.id = id
        self.pos = pos
        self.name = ""
        self.enabled = False
        self.mask = opc.mask[pos-1]
        for i in range(0, pos):
            self.name = self.name + ("%02x" % opc.val[i])
        if pos == 2:
            self.skip = opc.skip
        else:
            self.skip = 0

    def is_dispatch(self):
        return True

    def source(self):
        start = self.pos / 2
        end = start + self.skip
        s = []
        for i in range(start, end+1):
            s.append("\tIR[%d] = fetch();" % i)
        s.append("\tinst_state = 0x%x0000 | IR[%d];" % (self.id, end))
        return s;

class OpcodeList:
    def __init__(self, fname, dtype):
        self.opcode_info = []
        self.dispatch_info = []
        self.states_info = []
        self.dispatch = {}
        self.macros = {}
        try:
            f = open(fname, "r")
        except Exception, err:
            print "Cannot read opcodes file %s [%s]" % (fname, err)
            sys.exit(1)
        
        opc = None
        for line in f:
            if line.startswith("#"):
                continue
            line = line.rstrip()
            if not line:
                continue
            if line.startswith(" ") or line.startswith("\t"):
                if inf != None:
                    # append instruction to last opcode, maybe expand a macro
                    tokens = line.split()
                    if tokens[0] in self.macros:
                        self.macros[tokens[0]].apply(inf, tokens)
                    else:
                        inf.add_source_line(line)
            else:
                # New opcode
                tokens = line.split()
                if tokens[0] == "macro":
                    inf = Macro(tokens)
                    self.macros[inf.name] = inf
                elif len(tokens) == 2 or len(tokens) == 3:
                    if len(tokens) >= 3:
                        otype = name_to_type(tokens[2])
                    else:
                        otype = -1
                    inf = Special(tokens[0], tokens[1], otype, dtype)
                    self.states_info.append(inf)
                else:
                    if len(tokens) >= 7:
                        otype = name_to_type(tokens[6])
                    else:
                        otype = -1
                    if otype == -1 or dtype == 0 or (otype != 0 and dtype != 0):
                        inf = Opcode(tokens[0], tokens[1], tokens[2], tokens[3], tokens[4], tokens[5], otype, dtype)
                        self.opcode_info.append(inf)
                    else:
                        inf = None

    def get(self, i):
        if i in self.dispatch:
            return self.dispatch[i]
        h = Hash(0)
        self.dispatch[i] = h
        return h
    
    def build_dispatch(self):
        for opc in self.opcode_info:
            for i in range(0, len(opc.val)):
                v = opc.val[i]
                if i == 0:
                    h = self.get(0)
                if opc.enabled:
                    h.mask = h.mask | opc.mask[i]
                    h.enabled = True
                if (i & 1) == 0:
                    h = h.get(v, opc.mask[i])
                elif i == len(opc.val)-1:
                    if opc.enabled:
                        h.set(v, opc)
                else:
                    if v in h.d:
                        d = h.d[v]
                        if not d.is_dispatch():
                            print "Collision on %s" % opc.description()
                            sys.exit(1)
                        if opc.enabled:
                            d.enabled = True
                        h = self.get(d.id)
                    else:
                        d = DispatchStep(len(self.dispatch_info)+2, i+1, opc)
                        self.dispatch_info.append(d)
                        if opc.enabled:
                            d.enabled = True
                        h.set(v, d)
                        h = self.get(d.id)
    
    def save_dasm(self, f, dname):
        print >>f, "const %s::disasm_entry %s::disasm_entries[] = {" % (dname, dname)
        for opc in self.opcode_info:
            if opc.enabled:
                opc.save_dasm(f)
        print >>f, "\t{ 0, 0, 0, 0, 0, \"illegal\", 0, 0, 2 },"
        print >>f, "};"
        print >>f
    
    def save_opcodes(self, f, t):
        for opc in self.opcode_info:
            if opc.needed:
                save_full_one(f, t, opc.function_name(), opc.source)
                save_partial_one(f, t, opc.function_name(), opc.source)
        
        for sta in self.states_info:
            if sta.needed:
                save_full_one(f, t, "state_" + sta.name, sta.source)
                save_partial_one(f, t, "state_" + sta.name, sta.source)
    
    def save_dispatch(self, f, t):
        for dsp in self.dispatch_info:
            save_full_one(f, t, "dispatch_" + dsp.name, dsp.source())
            save_partial_one(f, t, "dispatch_" + dsp.name, dsp.source())
    
    def save_exec(self, f, t, dtype, v):
        print >>f, "void %s::do_exec_%s()" % (t, v)
        print >>f, "{"
        print >>f, "\tswitch(inst_state >> 16) {"
        for i in range(0, len(self.dispatch_info)+2):
            if i == 1:
                print >>f, "\tcase 0x01: {"
                print >>f, "\t\tswitch(inst_state & 0xffff) {"
                for sta in self.states_info:
                    if sta.enabled:
                        print >>f, "\t\tcase 0x%02x: state_%s_%s(); break;" % (sta.val & 0xffff, sta.name, v)
                print >>f, "\t\t}"
                print >>f, "\t\tbreak;"
                print >>f, "\t}"
            else:
                if i == 0 or self.dispatch_info[i-2].enabled:
                    print >>f, "\tcase 0x%02x: {" % i
                    h = self.get(i)
                    print >>f, "\t\tswitch((inst_state >> 8) & 0x%02x) {" % h.mask
                    for val, h2 in sorted(h.d.items()):
                        if h2.enabled:
                            fmask = h2.premask | (h.mask ^ 0xff)
                            c = ""
                            s = 0
                            while(s < 0x100):
                                c = c + "case 0x%02x: " % (val | s)
                                s = s + 1
                                while(s & fmask):
                                    s = s + (s & fmask)                    
                            print >>f, "\t\t%s{" % c
                            if h2.mask == 0x00:
                                n = h2.d[0]
                                if n.is_dispatch():
                                    print >>f, "\t\t\tdispatch_%s_%s();" % (n.name, v)
                                else:
                                    print >>f, "\t\t\t%s_%s();" % (n.function_name(), v)
                                print >>f, "\t\t\tbreak;"
                            else:
                                print >>f, "\t\t\tswitch(inst_state & 0x%02x) {" % h2.mask
                                if i == 0:
                                    mpos = 1
                                else:
                                    mpos = self.dispatch_info[i-2].pos + 1
                                for val2, n in sorted(h2.d.items()):
                                    if n.enabled:
                                        fmask = h2.mask ^ 0xff
                                        if n.is_dispatch():
                                            fmask = fmask | n.mask
                                        else:
                                            fmask = fmask | n.mask[mpos]
                                        c = ""
                                        s = 0
                                        while(s < 0x100):
                                            c = c + "case 0x%02x: " % (val2 | s)
                                            s = s + 1
                                            while(s & fmask):
                                                s = s + (s & fmask)
                                        if n.is_dispatch():
                                            print >>f, "\t\t\t%sdispatch_%s_%s(); break;" % (c, n.name, v)
                                        else:
                                            print >>f, "\t\t\t%s%s_%s(); break;" % (c, n.function_name(), v)
                                print >>f, "\t\t\tdefault: illegal(); break;"
                                print >>f, "\t\t\t}"
                                print >>f, "\t\t\tbreak;"
                            print >>f, "\t\t}"
                    print >>f, "\t\tdefault: illegal(); break;"
                    print >>f, "\t\t}"
                    print >>f, "\t\tbreak;"
                    print >>f, "\t}"
        print >>f, "\t}"
        print >>f, "}"

def main(argv):
    if len(argv) != 4:
        print USAGE % argv[0]
        return 1

    dtype = name_to_type(argv[2])
    dname = type_to_device(dtype)
    opcodes = OpcodeList(argv[1], dtype)
    
    try:
        f = open(argv[3], "w")
    except Exception, err:
        sys.stderr.write("cannot write file %s [%s]\n" % (argv[3], err))
        sys.exit(1)

    opcodes.build_dispatch()
    opcodes.save_dasm(f, dname)
    opcodes.save_opcodes(f, dname)
    if dtype == 0:
        opcodes.save_dispatch(f, dname)
    opcodes.save_exec(f, dname, dtype, "full")
    opcodes.save_exec(f, dname, dtype, "partial")
    f.close()

# ======================================================================
if __name__ == "__main__":
    sys.exit(main(sys.argv))

