From 6c3d7412ea9e675dc4901f4714ec0fe50065b785 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?L=C3=A9o=20Lam?= <leo@leolam.fr>
Date: Tue, 13 Jul 2021 13:15:00 +0200
Subject: [PATCH] Fix compatibility with IDA 7.x and Python 3

---
 gcdsp.py           | 139 +++++++++++++++++----------------------------
 gcdsp_generated.py |   2 +
 gcdsp_opcodes.py   |  31 ++++++++++
 gen_from_tables.py |  23 ++++----
 4 files changed, 98 insertions(+), 97 deletions(-)
 create mode 100644 gcdsp_opcodes.py

diff --git a/gcdsp.py b/gcdsp.py
index c329882..b75a13a 100644
--- a/gcdsp.py
+++ b/gcdsp.py
@@ -10,50 +10,18 @@
 repository.
 """
 from idaapi import *
-import os.path
+from pathlib import Path
+
+from gcdsp_opcodes import OpType
+from gcdsp_generated import *
 
 GREETINGS_STRING = """\
 GC/Wii DSP processor for IDA (C) 2011 delroth@dolphin-emu.org - \
 licensed under the GPLv2 license\
 """
 
-class OpType:
-    """Enumeration of the different operand encoding types which can be found
-    in the GC DSP ISA. From DSPTables.h in Dolphin source code."""
-
-    NONE = 0
-    VAL = 1
-    IMM = 2
-    MEM = 3
-    STR = 4
-    ADDR_I = 5
-    ADDR_D = 6
-
-    REG = 0x8000
-    REG04 = REG | 0x0400
-    REG08 = REG | 0x0800
-    REG18 = REG | 0x1800
-    REGM18 = REG18
-    REG19 = REG | 0x1900
-    REGM19 = REG19
-    REG1A = REG | 0x1a80
-    REG1C = REG | 0x1c00
-    ACCL = REG | 0x1c00
-    ACCM = REG | 0x1e00
-    ACCM_D = REG | 0x1e80
-    ACC = REG | 0x2000
-    ACC_D = REG | 0x2080
-    AX = REG | 0x2200
-    REGS_MASK = 0x3f80
-
-    REF = REG | 0x4000
-    PRG = REF | REG
-
-
-# Get autogenerated parts from another file
-execfile(os.path.join(os.path.dirname(__file__), 'gcdsp_generated.py'))
-
-class Operand(object):
+
+class Operand:
     def __init__(self, type, size, loc, rshift, mask):
         self.type = type
         self.size = size
@@ -115,7 +83,7 @@ def parse(self, res, byte1, byte2):
         else:
             raise ValueError("unhandled type: %04X" % type)
 
-class Instr(object):
+class Instr:
     def __init__(self, name, opcode, mask, size, operands=[], ext_operands=[],
                  stops=False, calls=False, jumps=False, shifts=False,
                  hll=False):
@@ -170,7 +138,7 @@ class GCDSPProcessor(processor_t):
     instruc_start = 0
 
     assembler = {
-        "flag" : ASH_HEXF3 | ASD_DECF0 | ASO_OCTF1 | ASB_BINF3 | AS_NOTAB
+        "flag" : ASH_HEXF3 | ASD_DECF0 | ASO_OCTF1 | ASB_BINF3
                | AS_ASCIIC | AS_ASCIIZ,
         "uflag": 0,
         "name": "GNU assembler",
@@ -291,40 +259,41 @@ def _init_registers(self):
             self.reg_ids[reg] = i
 
         # Simulate fake segment registers
-        self.regFirstSreg = self.regCodeSreg = self.reg_ids["$CS"]
-        self.regLastSreg = self.regDataSreg = self.reg_ids["$DS"]
+        self.reg_first_sreg = self.reg_code_sreg = self.reg_ids["$CS"]
+        self.reg_last_sreg = self.reg_data_sreg = self.reg_ids["$DS"]
+
 
     def notify_init(self, idp_file):
         """Called at module initialization."""
-        cvar.inf.mf = True  # set to big endian... wtf
-        cvar.inf.wide_high_byte_first = True  # big endian for 16b bytes too
+        cvar.inf.set_be(True)
+        cvar.inf.lflags |= LFLG_WIDE_HBF  # big endian for 16b bytes too
         return True
 
     def notify_endbinary(self, ok):
         """Called when the binary finished loading."""
         if ok:
-            print GREETINGS_STRING
+            print(GREETINGS_STRING)
 
-    def _read_cmd_byte(self):
-        ea = self.cmd.ea + self.cmd.size
-        byte = get_full_byte(ea)
-        self.cmd.size += 1
+    def _read_cmd_byte(self, cmd):
+        ea = cmd.ea + cmd.size
+        byte = get_wide_byte(ea)
+        cmd.size += 1
         return byte
 
-    def ana(self):
-        """Analyze one instruction and fill the "cmd" instance member."""
-        cmd = self.cmd
-        byte1 = self._read_cmd_byte()
+    def notify_ana(self, cmd):
+        """Analyze one instruction and fill "cmd"."""
+        self.cmd = cmd
+        byte1 = self._read_cmd_byte(cmd)
         instr = self.instrs_opcode[byte1]
         if instr is None:
             return 0
 
         if instr.size == 2:
-            byte2 = self._read_cmd_byte()
+            byte2 = self._read_cmd_byte(cmd)
         else:
             byte2 = 0
 
-        operands = [cmd[i] for i in xrange(6)]
+        operands = [cmd[i] for i in range(6)]
         for to_fill in operands:
             to_fill.type = o_void
 
@@ -334,80 +303,78 @@ def ana(self):
         cmd.itype = instr.id
         return cmd.size
 
-    def _emu_operand(self, op):
+    def _emu_operand(self, cmd, op):
         """Emulated using one operand from the instruction."""
         if op.type == o_mem:
-            ua_dodata2(0, op.addr, op.dtyp)
-            ua_add_dref(0, op.addr, dr_R)  # TODO: dr_W ?
+            cmd.create_op_data(op.addr, 0, op.dtype)
+            cmd.add_dref(op.addr, 0, dr_R)
         elif op.type == o_near:
-            if self.cmd.get_canon_feature() & CF_CALL:
+            if cmd.get_canon_feature() & CF_CALL:
                 fl = fl_CN
             else:
                 fl = fl_JN
-            ua_add_cref(0, op.addr, fl)
+            cmd.add_cref(op.addr, 0, fl)
 
-    def emu(self):
+    def notify_emu(self, cmd):
         """Emulate instruction behavior and create x-refs, interpret operand
         values, etc."""
-        instr = self.instrs_list[self.cmd.itype]
+        instr = self.instrs_list[cmd.itype]
 
-        for i in xrange(len(instr.all_operands)):
-            self._emu_operand(self.cmd[i])
+        for i in range(len(instr.all_operands)):
+            self._emu_operand(cmd, cmd[i])
 
         if not instr.stops:  # add a link to next instr if code continues
-            ua_add_cref(0, self.cmd.ea + self.cmd.size, fl_F)
+            cmd.add_cref(cmd.ea + cmd.size, 0, fl_F)
 
         return True
 
-    def outop(self, op):
+    def notify_out_operand(self, ctx, op):
         """Generates text representation of an instruction operand."""
         if op.type == o_reg:
-            out_register(self.reg_names[op.reg])
+            ctx.out_register(self.reg_names[op.reg])
         elif op.type == o_phrase:
-            out_symbol('@')
-            out_register(self.reg_names[op.reg])
+            ctx.out_symbol('@')
+            ctx.out_register(self.reg_names[op.reg])
         elif op.type == o_imm:
-            OutValue(op, OOFW_IMM)
+            ctx.out_value(op, OOFW_IMM)
         elif op.type in [o_near, o_mem]:
-            ok = out_name_expr(op, op.addr, BADADDR)
+            ok = ctx.out_name_expr(op, op.addr, BADADDR)
             if not ok:
-                out_tagon(COLOR_ERROR)
-                OutLong(op.addr, 16)
-                out_tagoff(COLOR_ERROR)
-                QueueMark(Q_noName, self.cmd.ea)
+                ctx.out_tagon(COLOR_ERROR)
+                ctx.out_long(op.addr, 16)
+                ctx.out_tagoff(COLOR_ERROR)
+                QueueMark(PR_NONAME, self.cmd.ea)
         else:
             return False
         return True
 
-    def out(self):
+    def notify_out_insn(self, ctx):
         """Generates text representation of an instruction in the "cmd" inst
         member."""
         cmd = self.cmd
 
-        buf = init_output_buffer(1024)
-        OutMnem(15)  # max width = 15
+        ctx.out_mnem(15)  # max width = 15
 
         instr = self.instrs_list[cmd.itype]
 
         in_extended = False
-        for i in xrange(0, 6):
+        for i in range(0, 6):
             if cmd[i].type == o_void:
                 break
 
             if i != 0:
                 if not in_extended and i >= len(instr.operands):
                     in_extended = True
-                    OutChar(' ')
-                    out_symbol(':')
+                    ctx.out_char(' ')
+                    ctx.out_symbol(':')
                 else:
-                    out_symbol(',')
-                OutChar(' ')
+                    ctx.out_symbol(',')
+                ctx.out_char(' ')
 
-            out_one_operand(i)
+            ctx.out_one_operand(i)
 
-        term_output_buffer()
         cvar.gl_comm = 1  # allow comments at end of line
-        MakeLine(buf)
+        ctx.flush_outbuf()
 
 def PROCESSOR_ENTRY():
     return GCDSPProcessor()
diff --git a/gcdsp_generated.py b/gcdsp_generated.py
index 155a341..070af51 100644
--- a/gcdsp_generated.py
+++ b/gcdsp_generated.py
@@ -1,4 +1,6 @@
 #### THIS CODE WAS AUTO-GENERATED BY gen_from_tables.py ####
+from gcdsp_opcodes import OpType
+
 opcodes = [
     ["NOP",0x0000,0xfffc,1,0,[],False,False],
     ["DAR",0x0004,0xfffc,1,1,[[OpType.REG,1,0,0,0x0003]],False,False],
diff --git a/gcdsp_opcodes.py b/gcdsp_opcodes.py
new file mode 100644
index 0000000..c0d916e
--- /dev/null
+++ b/gcdsp_opcodes.py
@@ -0,0 +1,31 @@
+class OpType:
+    """Enumeration of the different operand encoding types which can be found
+    in the GC DSP ISA. From DSPTables.h in Dolphin source code."""
+
+    NONE = 0
+    VAL = 1
+    IMM = 2
+    MEM = 3
+    STR = 4
+    ADDR_I = 5
+    ADDR_D = 6
+
+    REG = 0x8000
+    REG04 = REG | 0x0400
+    REG08 = REG | 0x0800
+    REG18 = REG | 0x1800
+    REGM18 = REG18
+    REG19 = REG | 0x1900
+    REGM19 = REG19
+    REG1A = REG | 0x1a80
+    REG1C = REG | 0x1c00
+    ACCL = REG | 0x1c00
+    ACCM = REG | 0x1e00
+    ACCM_D = REG | 0x1e80
+    ACC = REG | 0x2000
+    ACC_D = REG | 0x2080
+    AX = REG | 0x2200
+    REGS_MASK = 0x3f80
+
+    REF = REG | 0x4000
+    PRG = REF | REG
diff --git a/gen_from_tables.py b/gen_from_tables.py
index da625e0..44c22ca 100755
--- a/gen_from_tables.py
+++ b/gen_from_tables.py
@@ -50,7 +50,7 @@ def gen_from_text(text):
             else:
                 fields = line.replace('{', '[').replace('}', ']').split(',')
                 fields = fields[0:3] + fields[5:-5] + [fields[-4]]
-                fields = map(str.strip, fields)
+                fields = list(map(str.strip, fields))
                 fields = ','.join(fields) + ']'
                 fields = fields.replace('true', 'True')
                 fields = fields.replace('false', 'False')
@@ -60,21 +60,22 @@ def gen_from_text(text):
                 else:
                     opcodes_ext.append(fields)
 
-    print 'opcodes = ['
-    print '    ' + ',\n    '.join(opcodes)
-    print ']'
-    print
-    print 'opcodes_ext = ['
-    print '    ' + ',\n    '.join(opcodes_ext)
-    print ']'
+    print('opcodes = [')
+    print('    ' + ',\n    '.join(opcodes))
+    print(']')
+    print()
+    print('opcodes_ext = [')
+    print('    ' + ',\n    '.join(opcodes_ext))
+    print(']')
 
 if __name__ == '__main__':
     if len(sys.argv) != 2:
-        print 'usage: %s /path/to/DSPTables.cpp' % sys.argv[0]
+        print('usage: %s /path/to/DSPTables.cpp' % sys.argv[0])
         sys.exit(1)
 
     text = open(sys.argv[1]).read()
 
-    print "#### THIS CODE WAS AUTO-GENERATED BY gen_from_tables.py ####"
+    print("#### THIS CODE WAS AUTO-GENERATED BY gen_from_tables.py ####")
+    print("from gcdsp_opcodes import OpType")
     gen_from_text(text)
-    print "#### END AUTO-GENERATED CODE ####"
+    print("#### END AUTO-GENERATED CODE ####")