# ----------------------------------------------------------------------
# Texas Instruments MSP430 processor module
# Copyright (c) 2010-2025 Hex-Rays
#
# This module demonstrates:
#  - instruction decoding and printing
#  - simplification of decoded instructions
#  - creation of code and data cross-references
#  - auto-creation of data items from cross-references
#  - tracing of the stack pointer changes
#  - creation of the stack variables
#  - handling of switch constructs
#
# Please send fixes or improvements to support@hex-rays.com

import sys
import copy

from ida_bytes import *
from ida_ua import *
from ida_idp import *
from ida_auto import *
from ida_nalt import *
from ida_funcs import *
from ida_lines import *
from ida_problems import *
from ida_offset import *
from ida_segment import *
from ida_name import *
from ida_netnode import *
from ida_xref import *
from ida_idaapi import *
import ida_frame
import idc

if sys.version_info.major < 3:
  range = xrange

# extract bitfield occupying bits high..low from val (inclusive, start from 0)
def BITS(val, high, low):
  return (val>>low)&((1<<(high-low+1))-1)

# extract one bit
def BIT(val, bit):
  return (val>>bit) & 1

# sign extend b low bits in x
# from "Bit Twiddling Hacks"
def SIGNEXT(x, b):
  m = 1 << (b - 1)
  x = x & ((1 << b) - 1)
  return (x ^ m) - m

# values for specval field for o_phrase operands
FL_INDIRECT = 1  # indirect: @Rn
FL_AUTOINC  = 2  # auto-increment: @Rn+

# values for specval field for o_mem operands
FL_ABSOLUTE = 1  # absolute: &addr
FL_SYMBOLIC = 2  # symbolic: addr

# values for insn_t.auxpref
AUX_SIZEMASK = 0x0F
AUX_NOSUF   = 0x00  # no suffix (e.g. SWPB)
AUX_WORD    = 0x01  # word transfer, .W suffix
AUX_BYTE    = 0x02  # byte transfer, .B suffix
AUX_A       = 0x03  # 20-bit transfer, .A suffix
AUX_AX      = 0x04  # 20-bit immediate/address, no suffix
AUX_REPIMM  = 0x10  # immediate repeat count present (in insn_t.segpref)
AUX_REPREG  = 0x20  # register repeat count (reg no in in insn_t.segpref)
AUX_ZC      = 0x40  # zero carry flag is set

# addressing mode field in the opcode
AM_REGISTER = 0 # Rn
AM_INDEXED  = 1 # X(Rn), also includes symbolic and absolute
AM_INDIRECT = 2 # @Rn
AM_AUTOINC  = 3 # @Rn+, also includes immediate (#N = @PC+)
# extra formats
AM_IMM20    = 100 # Rn is imm19:16, imm15:0 follows
AM_ABS20    = 101 # Rn is &abs19:16, &abs15:0 follows
AM_SYM20    = 102 # as IMM20, plus PC value

# operand data length value
#                 A/L B/W
DLEN_WORD   = 0 #  1   0   16-bit word
DLEN_BYTE   = 1 #  1   1   8-bit byte
DLEN_AWORD  = 2 #  0   1   20-bit address-word
DLEN_LONG   = 3 #  0   0   Reserved

# check if operand is immediate value val
def is_imm_op(op, val):
    if op.type == o_imm:
        # workaround for difference between Python and native numbers
        op2 = op_t()
        op2.value = val
        return op.value == op2.value
    return False

# are operands equal?
def same_op(op1, op2):
    return op1.type  == op2.type  and \
           op1.reg   == op2.reg   and \
           op1.value == op2.value and \
           op1.addr  == op2.addr  and \
           op1.flags == op2.flags and \
           op1.specval == op2.specval and \
           op1.dtype == op2.dtype

# is operand auto-increment register reg?
def is_autoinc(op, reg):
    return op.type == o_phrase and op.reg == reg and op.specval == FL_AUTOINC

# is sp delta fixed by the user?
def is_fixed_spd(ea):
    return (get_aflags(ea) & AFL_FIXEDSPD) != 0

# ----------------------------------------------------------------------
class msp430_processor_t(processor_t):
    """
    Processor module classes must derive from processor_t
    """

    # IDP id ( Numbers above 0x8000 are reserved for the third-party modules)
    id = PLFM_MSP430

    # Processor features
    flag = PR_SEGS | PRN_HEX | PR_RNAMESOK | PR_WORD_INS \
         | PR_USE32 | PR_DEFSEG32

    # Number of bits in a byte for code segments (usually 8)
    # IDA supports values up to 32 bits (64 for IDA64)
    cnbits = 8

    # Number of bits in a byte for non-code segments (usually 8)
    # IDA supports values up to 32 bits (64 for IDA64)
    dnbits = 8

    # short processor names
    # Each name should be shorter than 9 characters
    psnames = ['msp430']

    # long processor names
    # No restriction on name lengthes.
    plnames = ['Texas Instruments:Texas Instruments MSP430']

    # size of a segment register in bytes
    segreg_size = 0

    # Array of typical code start sequences (optional)
    codestart = ['\x0B\x12']  # 120B: push R11

    # Array of 'return' instruction opcodes (optional)
    # retcodes = ['\x30\x41']   # 4130: ret (mov.w @SP+, PC)

    # Array of instructions
    instruc = [
        {'name': '',  'feature': 0},                                # placeholder for "not an instruction"

        # two-operand instructions
        {'name': 'mov',  'feature': CF_USE1 | CF_CHG2,             'cmt': "Move source to destination"},
        {'name': 'add',  'feature': CF_USE1 | CF_USE2 | CF_CHG2,   'cmt': "Add source to destination"},
        {'name': 'addc', 'feature': CF_USE1 | CF_USE2 | CF_CHG2,   'cmt': "Add source and carry to destination"},
        {'name': 'subc', 'feature': CF_USE1 | CF_USE2 | CF_CHG2,   'cmt': "Subtract source with carry from destination"},
        {'name': 'sub',  'feature': CF_USE1 | CF_USE2 | CF_CHG2,   'cmt': "Subtract source from destination"},
        {'name': 'cmp',  'feature': CF_USE1 | CF_USE2,             'cmt': "Compare source and destination"},
        {'name': 'dadd', 'feature': CF_USE1 | CF_USE2 | CF_CHG2,   'cmt': "Add source decimally to destination"},
        {'name': 'bit',  'feature': CF_USE1 | CF_USE2,             'cmt': "Test bits set in source in destination"},
        {'name': 'bic',  'feature': CF_USE1 | CF_USE2 | CF_CHG2,   'cmt': "Clear bits set in source in destination"},
        {'name': 'bis',  'feature': CF_USE1 | CF_USE2 | CF_CHG2,   'cmt': "Set bits set in source in destination"},
        {'name': 'xor',  'feature': CF_USE1 | CF_USE2 | CF_CHG2,   'cmt': "Exclusive OR source with destination"},
        {'name': 'and',  'feature': CF_USE1 | CF_USE2 | CF_CHG2,   'cmt': "Binary AND source and destination"},

        # MSP430X instructions
        {'name': 'movx',  'feature': CF_USE1 | CF_CHG2,            'cmt': "Move source to destination"},
        {'name': 'addx',  'feature': CF_USE1 | CF_USE2 | CF_CHG2,  'cmt': "Add source to destination"},
        {'name': 'addcx', 'feature': CF_USE1 | CF_USE2 | CF_CHG2,  'cmt': "Add source and carry to destination"},
        {'name': 'subcx', 'feature': CF_USE1 | CF_USE2 | CF_CHG2,  'cmt': "Subtract source with carry from destination"},
        {'name': 'subx',  'feature': CF_USE1 | CF_USE2 | CF_CHG2,  'cmt': "Subtract source from destination"},
        {'name': 'cmpx',  'feature': CF_USE1 | CF_USE2,            'cmt': "Compare source and destination"},
        {'name': 'daddx', 'feature': CF_USE1 | CF_USE2 | CF_CHG2,  'cmt': "Add source decimally to destination"},
        {'name': 'bitx',  'feature': CF_USE1 | CF_USE2,            'cmt': "Test bits set in source in destination"},
        {'name': 'bicx',  'feature': CF_USE1 | CF_USE2 | CF_CHG2,  'cmt': "Clear bits set in source in destination"},
        {'name': 'bisx',  'feature': CF_USE1 | CF_USE2 | CF_CHG2,  'cmt': "Set bits set in source in destination"},
        {'name': 'xorx',  'feature': CF_USE1 | CF_USE2 | CF_CHG2,  'cmt': "Exclusive OR source with destination"},
        {'name': 'andx',  'feature': CF_USE1 | CF_USE2 | CF_CHG2,  'cmt': "Binary AND source and destination"},
        {'name': 'rrcm',  'feature': CF_USE1 | CF_USE2 | CF_CHG2,  'cmt': "Rotate right through C"},
        {'name': 'rram',  'feature': CF_USE1 | CF_USE2 | CF_CHG2,  'cmt': "Rotate right arithmetically"},
        {'name': 'rlam',  'feature': CF_USE1 | CF_USE2 | CF_CHG2,  'cmt': "Rotate left arithmetically"},
        {'name': 'rrum',  'feature': CF_USE1 | CF_USE2 | CF_CHG2,  'cmt': "Rotate right unsigned"},
        {'name': 'pushm', 'feature': CF_USE1 | CF_USE2,            'cmt': "Push registers onto stack"},
        {'name': 'popm',  'feature': CF_USE1 | CF_CHG2,            'cmt': "Pop registers from the stack"},

        # MSP430X address instructions
        {'name': 'mova',  'feature': CF_USE1 | CF_CHG2,            'cmt': "Move source to destination"},
        {'name': 'cmpa',  'feature': CF_USE1 | CF_USE2,            'cmt': "Compare source and destination"},
        {'name': 'adda',  'feature': CF_USE1 | CF_USE2 | CF_CHG2,  'cmt': "Add source to destination"},
        {'name': 'suba',  'feature': CF_USE1 | CF_USE2 | CF_CHG2,  'cmt': "Subtract source from destination"},

        # one-operand instructions
        {'name': 'rrc',  'feature': CF_USE1 | CF_CHG1,   'cmt': "Rotate right through C"},
        {'name': 'swpb', 'feature': CF_USE1 | CF_CHG1,   'cmt': "Swap bytes"},
        {'name': 'rra',  'feature': CF_USE1 | CF_CHG1,   'cmt': "Rotate right arithmetically"},
        {'name': 'sxt',  'feature': CF_USE1 | CF_CHG1,   'cmt': "Extend sign (8 bits to 16)"},
        {'name': 'push', 'feature': CF_USE1          ,   'cmt': "Push onto stack"},
        {'name': 'call', 'feature': CF_USE1 | CF_CALL,   'cmt': "Call subroutine"},
        {'name': 'reti', 'feature': CF_STOP          ,   'cmt': "Return from interrupt"},

        # MSP430X instructions
        {'name': 'rrcx', 'feature': CF_USE1 | CF_CHG1,   'cmt': "Rotate right through carry"},
        {'name': 'swpbx','feature': CF_USE1 | CF_CHG1,   'cmt': "Exchange low byte with high byte"},
        {'name': 'rrax', 'feature': CF_USE1 | CF_CHG1,   'cmt': "Rotate right arithmetically"},
        {'name': 'sxtx', 'feature': CF_USE1 | CF_CHG1,   'cmt': "Extend sign of lower byte"},
        {'name': 'pushx','feature': CF_USE1          ,   'cmt': "Push onto stack"},
        {'name': 'calla','feature': CF_USE1          ,   'cmt': "Call subroutine (20-bit)"},
        {'name': 'rrux', 'feature': CF_USE1 | CF_CHG1,   'cmt': "Rotate right unsigned"},

        # jumps
        {'name': 'jnz',  'feature': CF_USE1          ,   'cmt': "Jump if not zero/not equal"},
        {'name': 'jz',   'feature': CF_USE1          ,   'cmt': "Jump if zero/equal"},
        {'name': 'jnc',  'feature': CF_USE1          ,   'cmt': "Jump if no carry/lower (unsigned)"},
        {'name': 'jc',   'feature': CF_USE1          ,   'cmt': "Jump if carry/higher or same (unsigned)"},
        {'name': 'jn',   'feature': CF_USE1          ,   'cmt': "Jump if negative"},
        {'name': 'jge',  'feature': CF_USE1          ,   'cmt': "Jump if greater or equal (signed)"},
        {'name': 'jl',   'feature': CF_USE1          ,   'cmt': "Jump if less (signed)"},
        {'name': 'jmp',  'feature': CF_USE1 | CF_STOP,   'cmt': "Jump unconditionally"},

        # emulated instructions
        {'name': 'adc',  'feature': CF_USE1 | CF_CHG1,   'cmt': "Add carry to destination"},
        {'name': 'br',   'feature': CF_USE1 | CF_STOP,   'cmt': "Branch to destination"},
        {'name': 'clr',  'feature': CF_CHG1          ,   'cmt': "Clear destination"},
        {'name': 'clrc', 'feature': 0                ,   'cmt': "Clear carry bit"},
        {'name': 'clrn', 'feature': 0                ,   'cmt': "Clear negative bit"},
        {'name': 'clrz', 'feature': 0                ,   'cmt': "Clear zero bit"},
        {'name': 'dadc', 'feature': CF_USE1 | CF_CHG1,   'cmt': "Add carry decimally to destination"},
        {'name': 'dec',  'feature': CF_USE1 | CF_CHG1,   'cmt': "Decrement destination"},
        {'name': 'decd', 'feature': CF_USE1 | CF_CHG1,   'cmt': "Double-decrement destination"},
        {'name': 'dint', 'feature': 0                ,   'cmt': "Disable general interrupts"},
        {'name': 'eint', 'feature': 0                ,   'cmt': "Enable general interrupts"},
        {'name': 'inc',  'feature': CF_USE1 | CF_CHG1,   'cmt': "Increment destination"},
        {'name': 'incd', 'feature': CF_USE1 | CF_CHG1,   'cmt': "Double-increment destination"},
        {'name': 'inv',  'feature': CF_USE1 | CF_CHG1,   'cmt': "Invert destination"},
        {'name': 'nop',  'feature': 0                ,   'cmt': "No operation"},
        {'name': 'pop',  'feature': CF_CHG1          ,   'cmt': "Pop from the stack"},
        {'name': 'ret',  'feature': CF_STOP          ,   'cmt': "Return from subroutine"},
        {'name': 'rla',  'feature': CF_USE1 | CF_CHG1,   'cmt': "Rotate left arithmetically"},
        {'name': 'rlc',  'feature': CF_USE1 | CF_CHG1,   'cmt': "Rotate left through carry"},
        {'name': 'sbc',  'feature': CF_USE1 | CF_CHG1,   'cmt': "Substract borrow (=NOT carry) from destination"},
        {'name': 'setc', 'feature': 0                ,   'cmt': "Set carry bit"},
        {'name': 'setn', 'feature': 0                ,   'cmt': "Set negative bit"},
        {'name': 'setz', 'feature': 0                ,   'cmt': "Set zero bit"},
        {'name': 'tst',  'feature': CF_USE1          ,   'cmt': "Test destination"},
        # MSP430X instructions
        {'name': 'adcx', 'feature': CF_USE1 | CF_CHG1,   'cmt': "Add carry to destination"},
        {'name': 'bra',  'feature': CF_USE1 | CF_STOP,   'cmt': "Branch indirect to destination"},
        {'name': 'reta', 'feature': CF_STOP          ,   'cmt': "Return from subroutine"},
        {'name': 'popa', 'feature': CF_CHG1          ,   'cmt': "Pop from the stack"},
        {'name': 'clra', 'feature': CF_CHG1          ,   'cmt': "Clear destination"},
        {'name': 'clrx', 'feature': CF_CHG1          ,   'cmt': "Clear destination"},
        {'name': 'dadcx','feature': CF_USE1 | CF_CHG1,   'cmt': "Add carry decimally to destination"},
        {'name': 'decx', 'feature': CF_USE1 | CF_CHG1,   'cmt': "Decrement destination"},
        {'name': 'decda','feature': CF_USE1 | CF_CHG1,   'cmt': "Double-decrement destination"},
        {'name': 'decdx','feature': CF_USE1 | CF_CHG1,   'cmt': "Double-decrement destination"},
        {'name': 'incx', 'feature': CF_USE1 | CF_CHG1,   'cmt': "Increment destination"},
        {'name': 'incda','feature': CF_USE1 | CF_CHG1,   'cmt': "Double-increment destination"},
        {'name': 'incdx','feature': CF_USE1 | CF_CHG1,   'cmt': "Double-increment destination"},
        {'name': 'invx', 'feature': CF_USE1 | CF_CHG1,   'cmt': "Invert destination"},
        {'name': 'rlax', 'feature': CF_USE1 | CF_CHG1,   'cmt': "Rotate left arithmetically"},
        {'name': 'rlcx', 'feature': CF_USE1 | CF_CHG1,   'cmt': "Rotate left through carry"},
        {'name': 'sbcx', 'feature': CF_USE1 | CF_CHG1,   'cmt': "Substract borrow (=NOT carry) from destination"},
        {'name': 'tsta', 'feature': CF_USE1          ,   'cmt': "Test destination"},
        {'name': 'tstx', 'feature': CF_USE1          ,   'cmt': "Test destination"},
        {'name': 'popx', 'feature': CF_CHG1          ,   'cmt': "Pop from the stack"},
    ]

    # icode of the first instruction
    instruc_start = 0

    # icode of the last instruction + 1
    instruc_end = len(instruc) + 1

    # Size of long double (tbyte) for this processor (meaningful only if ash.a_tbyte != NULL) (optional)
    # tbyte_size = 0

    #
    # Number of digits in floating numbers after the decimal point.
    # If an element of this array equals 0, then the corresponding
    # floating point data is not used for the processor.
    # This array is used to align numbers in the output.
    #      real_width[0] - number of digits for short floats (only PDP-11 has them)
    #      real_width[1] - number of digits for "float"
    #      real_width[2] - number of digits for "double"
    #      real_width[3] - number of digits for "long double"
    # Example: IBM PC module has { 0,7,15,19 }
    #
    # (optional)
    real_width = (0, 7, 15, 19)


    # only one assembler is supported
    assembler = {
        # flag
        'flag' : ASH_HEXF0 | ASD_DECF0 | ASO_OCTF5 | ASB_BINF0 | AS_N2CHR,

        # user defined flags (local only for IDP) (optional)
        'uflag' : 0,

        # Assembler name (displayed in menus)
        'name': "Generic MSP430 assembler",

        # array of automatically generated header lines they appear at the start of disassembled text (optional)
        'header': [".msp430"],

        # org directive
        'origin': ".org",

        # end directive
        'end': ".end",

        # comment string (see also cmnt2)
        'cmnt': ";",

        # ASCII string delimiter
        'ascsep': "\"",

        # ASCII char constant delimiter
        'accsep': "'",

        # ASCII special chars (they can't appear in character and ascii constants)
        'esccodes': "\"'",

        #
        #      Data representation (db,dw,...):
        #
        # ASCII string directive
        'a_ascii': ".char",

        # byte directive
        'a_byte': ".byte",

        # word directive
        'a_word': ".short",

        # dword (32 bits)
        'a_dword': ".long",

        # qword (64 bits)
        'a_qword': ".quad",

        # float;  4bytes; remove if not allowed
        'a_float': ".float",

        # double ; 8bytes; remove if not allowed
        'a_double': ".double",

        # uninitialized data directive (should include '%s' for the size of data)
        'a_bss': ".space %s",

        # 'equ' Used if AS_UNEQU is set (optional)
        'a_equ': ".equ",

        # 'seg ' prefix (example: push seg seg001)
        'a_seg': "seg",

        # current IP (instruction pointer) symbol in assembler
        'a_curip': "$",

        # "public" name keyword. NULL-gen default, ""-do not generate
        'a_public': ".def",

        # "weak"   name keyword. NULL-gen default, ""-do not generate
        'a_weak': "",

        # "extrn"  name keyword
        'a_extrn': ".ref",

        # "comm" (communal variable)
        'a_comdef': "",

        # "align" keyword
        'a_align': ".align",

        # Left and right braces used in complex expressions
        'lbrace': "(",
        'rbrace': ")",

        # %  mod     assembler time operation
        'a_mod': "%",

        # &  bit and assembler time operation
        'a_band': "&",

        # |  bit or  assembler time operation
        'a_bor': "|",

        # ^  bit xor assembler time operation
        'a_xor': "^",

        # ~  bit not assembler time operation
        'a_bnot': "~",

        # << shift left assembler time operation
        'a_shl': "<<",

        # >> shift right assembler time operation
        'a_shr': ">>",

        # size of type (format string) (optional)
        'a_sizeof_fmt': "size %s",

        'flag2': 0,

        # the include directive (format string) (optional)
        'a_include_fmt': '.include "%s"',
    } # Assembler

    def ev_get_frame_retsize(self, frsize, pfn):
        ida_pro.int_pointer.frompointer(frsize).assign(2)
        return 1

    def ev_get_autocmt(self, insn):
        if 'cmt' in self.instruc[insn.itype]:
          return self.instruc[insn.itype]['cmt']

    # ----------------------------------------------------------------------
    def ev_is_sane_insn(self, insn, no_crefs):
        w = get_wide_word(insn.ea)
        if w == 0 or w == 0xFFFF:
          return -1
        return 1

    # ----------------------------------------------------------------------
    def is_movpc(self, insn):
        # mov xxx, PC
        return insn.itype == self.itype_mov and insn.Op2.is_reg(self.ireg_PC) and insn.auxpref == AUX_WORD

    # ----------------------------------------------------------------------
    def changes_pc(self, insn):
        Feature = insn.get_canon_feature()
        if (Feature & CF_CHG2) and insn.Op2.is_reg(self.ireg_PC):
          return True
        if (Feature & CF_CHG1) and insn.Op1.is_reg(self.ireg_PC):
          return True
        return False

    # ----------------------------------------------------------------------
    def handle_operand(self, insn, op, isRead):
        flags     = get_flags(insn.ea)
        is_offs   = is_off(flags, op.n)
        dref_flag = dr_R if isRead else dr_W
        def_arg   = is_defarg(flags, op.n)
        optype    = op.type

        itype = insn.itype
        # create code xrefs
        if optype == o_imm:
            makeoff = False
            if itype in [self.itype_call, self.itype_calla]:
                # call #func
                insn.add_cref(op.value, op.offb, fl_CN)
                makeoff = True
            elif self.is_movpc(insn) or insn.itype in [self.itype_br, self.itype_bra]:
                # mov #addr, PC
                insn.add_cref(op.value, op.offb, fl_JN)
                makeoff = True
            if makeoff and not def_arg:
                op_plain_offset(insn.ea, op.n, insn.cs)
                is_offs = True
            if is_offs:
                insn.add_off_drefs(op, dr_O, 0)
        # create data xrefs
        elif optype == o_displ:
            # delta(reg)
            if is_offs:
                insn.add_off_drefs(op, dref_flag, OOF_ADDR)
            elif may_create_stkvars() and not def_arg and op.reg == self.ireg_SP:
                # var_x(SP)
                pfn = get_func(insn.ea)
                if pfn and insn.create_stkvar(op, op.addr, STKVAR_VALID_SIZE):
                    op_stkvar(insn.ea, op.n)
        elif optype == o_mem:
            insn.create_op_data(op.addr, op)
            insn.add_dref(op.addr, op.offb, dref_flag)
        elif optype == o_near:
            insn.add_cref(op.addr, op.offb, fl_JN)

    # ----------------------------------------------------------------------
    def add_stkpnt(self, pfn, insn, v):
        if pfn:
            end = insn.ea + insn.size
            if not is_fixed_spd(end):
                ida_frame.add_auto_stkpnt(pfn, end, v)

    # ----------------------------------------------------------------------
    def trace_sp(self, insn):
        """
        Trace the value of the SP and create an SP change point if the current
        instruction modifies the SP.
        """
        pfn = get_func(insn.ea)
        if not pfn:
            return
        spofs = 0
        if insn.itype in [self.itype_add, self.itype_addx, self.itype_adda, self.itype_addc, self.itype_addcx,
           self.itype_sub, self.itype_subx, self.itype_suba, self.itype_subc, self.itype_subcx] and \
           insn.Op2.is_reg(self.ireg_SP) and insn.auxpref in [AUX_WORD, AUX_A, AUX_AX] and \
           insn.Op1.type == o_imm:
            # add.w  #xxx, SP
            # subc.w #xxx, SP
            if insn.auxpref == AUX_WORD:
              spofs = SIGNEXT(insn.Op1.value, 16)
            else:
              spofs = SIGNEXT(insn.Op1.value, 20)
            if insn.itype in [self.itype_sub, self.itype_suba, self.itype_subc, self.itype_subx, self.itype_subcx]:
                spofs = -spofs
        elif insn.itype in [self.itype_incd, self.itype_decd, self.itype_incdx,
             self.itype_decdx, self.itype_incda, self.itype_decda] and \
             insn.Op1.is_reg(self.ireg_SP) and insn.auxpref in [AUX_WORD, AUX_A, AUX_AX]:
              spofs = 2 if insn.itype in [self.itype_incd, self.itype_incdx, self.itype_incda] else -2
              self.add_stkpnt(pfn, insn, spofs)
        elif insn.itype == self.itype_push:
            spofs = -2
        elif insn.itype in [self.itype_popm, self.itype_pushm, self.itype_popx, self.itype_pushx]:
            # popm.a #n, reg -> +n*4
            # popm.w #n, reg -> +n*2
            # popx.a reg     -> +4
            # popx.w reg     -> +2
            if insn.itype in [self.itype_popm, self.itype_pushm]:
              count = insn.Op1.value
            else:
              count = 1
            spofs = 1 if insn.itype == self.itype_popm else -1
            if insn.auxpref == AUX_A:
              spofs *= count * 4
            else:
              spofs *= count * 2
        elif insn.itype == self.itype_pop or is_autoinc(insn.Op1, self.ireg_SP):
            # pop R7 or mov.w @SP+, R7
            if insn.auxpref in [AUX_A, AUX_AX]:
              spofs = 4
            else:
              spofs = 2

        if spofs != 0:
          self.add_stkpnt(pfn, insn, spofs)

    # ----------------------------------------------------------------------
    def check_switch(self, insn):
        # detect switches and set switch info
        #
        #       cmp.w   #nn, Rx
        #       jc      default
        #       [mov.w   Rx, Ry]
        #       rla.w   Ry, Ry
        #       br      jtbl(Ry)
        # jtbl  .short case0, .short case1
        if get_switch_info(insn.ea):
            return
        si = switch_info_t()

        # ask plugins about a possible switch
        code = self.ev_is_switch(si, insn)
        if code == 1:
            set_switch_info(insn.ea, si)
            create_switch_table(insn.ea, si)
            return
        elif code == -1:
            return
        else:
            # this processor module does not handle ev_is_switch
            pass

        ok = False
        # mov.w   jtbl(Ry), PC
        if (self.is_movpc(insn) or insn.itype in [self.itype_br, self.itype_bra]) and insn.Op1.type == o_displ:
            si.jumps = insn.Op1.addr # jump table address
            Ry = insn.Op1.reg
            ok = True
            # add.w   Ry, Ry  | rla.w Ry
            prev = insn_t()
            if decode_prev_insn(prev, insn.ea) != BADADDR and prev.auxpref == AUX_WORD:
                ok = prev.itype == self.itype_add and prev.Op1.is_reg(Ry) and prev.Op2.is_reg(Ry) or \
                     prev.itype == self.itype_rla and prev.Op1.is_reg(Ry)
            else:
                ok = False
            if ok and decode_prev_insn(prev, prev.ea) != BADADDR:
               # mov.w   Rx, Ry
               if prev.itype == self.itype_mov and \
                  prev.Op2.is_reg(Ry) and \
                  prev.Op1.type == o_reg and \
                  prev.auxpref == AUX_WORD:
                   Rx = prev.Op1.reg
                   ok = decode_prev_insn(prev, prev.ea) != BADADDR
               else:
                   Rx = Ry
            else:
                ok = False

            # jc default
            if ok and prev.itype == self.itype_jc:
                si.defjump = prev.Op1.addr
            else:
                ok = False

            # cmp.w   #nn, Rx
            if ok and decode_prev_insn(prev, prev.ea) == BADADDR or \
               prev.itype != self.itype_cmp or \
               prev.Op1.type != o_imm or \
               not prev.Op2.is_reg(Rx) or \
               prev.auxpref != AUX_WORD:
                ok = False
            else:
                si.ncases = prev.Op1.value
                si.lowcase = 0
                si.startea = prev.ea
                si.set_expr(Rx, dt_word)

        if ok:
            # make offset to the jump table
            op_plain_offset(insn.ea, 0, insn.cs)
            set_switch_info(insn.ea, si)
            create_switch_table(insn.ea, si)

    # ----------------------------------------------------------------------
    # The following callbacks are mandatory
    #
    def ev_emu_insn(self, insn):
        aux = self.get_auxpref(insn)
        Feature = insn.get_canon_feature()

        if Feature & CF_USE1:
            self.handle_operand(insn, insn.Op1, 1)
        if Feature & CF_CHG1:
            self.handle_operand(insn, insn.Op1, 0)
        if Feature & CF_USE2:
            self.handle_operand(insn, insn.Op2, 1)
        if Feature & CF_CHG2:
            self.handle_operand(insn, insn.Op2, 0)
        if Feature & CF_JUMP:
            remember_problem(PR_JUMP, insn.ea)

        # is it an unconditional jump?
        uncond_jmp = insn.itype in [self.itype_jmp, self.itype_br, self.itype_bra] or self.changes_pc(insn)

        # add flow
        flow = (Feature & CF_STOP == 0) and not uncond_jmp
        if flow:
            add_cref(insn.ea, insn.ea + insn.size, fl_F)
        else:
            self.check_switch(insn)

        # trace the stack pointer if:
        #   - it is the second analysis pass
        #   - the stack pointer tracing is allowed
        if may_trace_sp():
            if flow:
                self.trace_sp(insn)     # trace modification of SP register
            else:
                idc.recalc_spd(insn.ea) # recalculate SP register for the next insn

        return True

    # ----------------------------------------------------------------------
    def ev_out_operand(self, ctx, op):
        optype = op.type
        fl     = op.specval
        signed = 0
        sz = ctx.insn.auxpref & AUX_SIZEMASK

        if optype == o_reg:
            ctx.out_register(self.reg_names[op.reg])
        elif optype == o_imm:
            ctx.out_symbol('#')
            op2 = copy.copy(op)
            if sz == AUX_BYTE:
                op2.value &= 0xFF
            elif sz == AUX_WORD:
                op2.value &= 0xFFFF
            else:
                op2.value &= 0xFFFFF
            ctx.out_value(op2, OOFW_IMM | signed )
        elif optype in [o_near, o_mem]:
            if optype == o_mem and fl == FL_ABSOLUTE:
                ctx.out_symbol('&')
            r = ctx.out_name_expr(op, op.addr, BADADDR)
            if not r:
                ctx.out_tagon(COLOR_ERROR)
                ctx.out_btoa(op.addr, 16)
                ctx.out_tagoff(COLOR_ERROR)
                remember_problem(PR_NONAME, ctx.insn.ea)
        elif optype == o_displ:
            # 16-bit index is signed
            width = OOFW_16
            sign = OOF_SIGNED
            if sz in [AUX_A, AUX_AX] or op.dtype == dt_dword:
                # 20-bit index is not signed
                width = OOFW_24
                sign = 0
            ctx.out_value(op, OOF_ADDR | signed | width )
            ctx.out_symbol('(')
            ctx.out_register(self.reg_names[op.reg])
            ctx.out_symbol(')')
        elif optype == o_phrase:
            ctx.out_symbol('@')
            ctx.out_register(self.reg_names[op.reg])
            if fl == FL_AUTOINC:
              ctx.out_symbol('+')
        else:
            return False

        return True

    # ----------------------------------------------------------------------
    def ev_out_mnem(self, ctx):

        postfix = ""

        # add postfix if necessary
        sz = ctx.insn.auxpref & AUX_SIZEMASK
        if sz == AUX_BYTE:
            postfix = ".b"
        elif sz == AUX_WORD:
            postfix = ".w"
        elif sz == AUX_A:
            postfix = ".a"

        # first argument (8) is the width of the mnemonic field
        ctx.out_mnem(8, postfix)
        return 1

    # ----------------------------------------------------------------------
    def ev_out_insn(self, ctx):
        """
        Generate text representation of an instruction in 'ctx.insn' structure.
        This function shouldn't change the database, flags or anything else.
        All these actions should be performed only by emu() function.
        Returns: nothing
        """
        # do we need to print a modifier line first?
        if ctx.insn.auxpref & (AUX_REPREG | AUX_REPIMM | AUX_ZC):
          segpref = ctx.insn.segpref
          if sys.version_info.major < 3:
            segpref = ord(segpref)
          if ctx.insn.auxpref & AUX_ZC:
            ctx.out_line(".zc", COLOR_INSN)
            ctx.flush_outbuf()
          if ctx.insn.auxpref & AUX_REPREG:
            ctx.out_line(".rpt", COLOR_INSN)
            ctx.out_char(' ')
            ctx.out_register(self.reg_names[segpref])
            ctx.flush_outbuf()
          if ctx.insn.auxpref & AUX_REPIMM:
            ctx.out_line(".rpt", COLOR_INSN)
            ctx.out_char(' ')
            ctx.out_symbol('#')
            ctx.out_long(segpref, 10)
            ctx.flush_outbuf()
          #ident next line
          ctx.out_char(' ')

        ctx.out_mnemonic()

        # output first operand
        # kernel will call out_operand()
        if ctx.insn.Op1.type != o_void:
            ctx.out_one_operand(0)

        # output the rest of operands separated by commas
        for i in range(1, 3):
            if ctx.insn[i].type == o_void:
                break
            ctx.out_symbol(',')
            ctx.out_char(' ')
            ctx.out_one_operand(i)

        ctx.set_gen_cmt() # generate comment at the next call to MakeLine()
        ctx.flush_outbuf()
        return True

    # ----------------------------------------------------------------------
    # fill operand fields from decoded instruction parts
    # op:  operand to be filled in
    # reg: register number
    # A:   adressing mode
    # BW:  value of the B/W (byte/word) field
    #      can be DLEN_AWORD for 20-bit instructions
    # is_source: True if filling source operand
    # is_cg: can use constant generator
    # extw: 20-bit extension word
    def fill_op(self, insn, op, reg, A, BW, is_source, is_cg = False, extw = None):
        op.reg = reg
        topaddr = 0  # top 4 bits of an address value
        if extw:
          AL = BIT(extw, 6)
          if AL == 0 and BW == 1:
            BW = DLEN_AWORD
          if is_source:
            topaddr = BITS(extw, 10, 7)
          else:
            topaddr = BITS(extw,  3, 0)
        if BW == DLEN_WORD:
          op.dtype = dt_word
        elif BW == DLEN_BYTE:
          op.dtype = dt_byte
        else:
          # 20-bit
          op.dtype = dt_dword
        if is_cg:
            # check for constant generators
            if reg == self.ireg_SR and A >= 2 and A <= 3:
                op.type = o_imm
                op.value = [4, 8] [A-2]
                return
            elif reg == self.ireg_R3:
                op.type = o_imm
                op.value = [0, 1, 2, -1] [A]
                return
        if A == AM_REGISTER:
            # register mode
            op.type = o_reg
            op.dtype = dt_word
        elif A == AM_INDEXED:
            # indexed mode
            if reg == self.ireg_SR:
                # absolute address mode
                op.type = o_mem
                op.specval = FL_ABSOLUTE
                op.offb = insn.size
                op.addr = insn.get_next_word() | (topaddr << 16)
            else:
                # map it to IDA's displacement
                op.type = o_displ
                op.offb = insn.size
                pcval   = insn.ip + insn.size
                op.addr = insn.get_next_word() | (topaddr << 16)
                if reg == self.ireg_PC:
                  # symbolic addressing mode: address = PC + simm16
                  # 1) if PC is below 64K, the result is wrapped to be below 64K
                  # 2) if PC is above 64K, the result is used as-is (can be below or above 64K)
                  # 3) for MSP430X instructions delta is simm20, result not wrapped
                  # apparently the PC value is the address of the index word, not next instruction!
                  if extw:
                      op.addr = TRUNC(SIGNEXT(op.addr, 20) + pcval)
                  else:
                      op.addr = TRUNC(SIGNEXT(op.addr, 16) + pcval)
                      if pcval < 0x10000:
                        op.addr &= 0xFFFF
                  op.type = o_mem
                  op.specval = FL_SYMBOLIC
            # slau208p.pdf: All addresses, indexes, and immediate numbers have
            #               20-bit values when preceded by the extension word.
            if extw:
                op.dtype = dt_dword
        elif A == AM_INDIRECT:
            # Indirect register mode
            # map it to o_phrase
            op.type = o_phrase
            op.specval = FL_INDIRECT
        elif A == AM_AUTOINC:
            # Indirect autoincrement
            # map it to o_phrase
            if reg == self.ireg_PC:
                #this is actually immediate mode
                op.dtype = dt_dword if extw else dt_word
                op.type = o_imm
                op.offb = insn.size
                op.value = insn.get_next_word() | (topaddr << 16)
            else:
                op.type = o_phrase
                op.specval = FL_AUTOINC
        elif A in [AM_IMM20, AM_ABS20, AM_SYM20]:
            # reg is the high 4 bits, low 16 bits follow
            val = (reg << 16) | insn.get_next_word()
            if A == AM_IMM20:
              op.dtype = dt_dword
              op.value = val
              op.type = o_imm
            else:
              # &abs20
              op.addr = val
              op.type = o_mem
              if A == AM_SYM20:
                # symbolic addressing mode: address = PC + imm20
                # no sign-extension or wrapping is done
                pcval = insn.ip + insn.size
                op.addr += pcval
                op.specval = FL_SYMBOLIC
              else:
                op.specval = FL_ABSOLUTE
        else:
            warning("bad A(%d) in fill_op" % A)

    # ----------------------------------------------------------------------
    def handle_reg_extw(self, insn, extw):
        #  Register Mode Extension Word
        #   15 ... 12 11  10 9   8   7    6   5    4   3  0
        #  +---------+--------+----+---+-----+---+---+------+
        #  |  0001   | 1 | 00 | ZC | # | A/L | 0 | 0 | R/n-1|
        #  +---------+--------+----+---+-----+---+---+------+
        ZC = BIT(extw, 8)
        repreg = BIT(extw, 7)
        rep = BITS(extw, 3, 0)
        if rep:
          if repreg:
            insn.auxpref |= AUX_REPREG
            insn.segpref = rep
          else:
            insn.auxpref |= AUX_REPIMM
            insn.segpref = rep + 1
        if ZC:
          if insn.itype == self.itype_rrcx:
            insn.itype = self.itype_rrux
          else:
            insn.auxpref |= AUX_ZC

    # ----------------------------------------------------------------------
    def decode_format_I(self, insn, w, extw):
        #  Double-Operand (Format I) Instructions
        #
        #   15 ... 12 11 ... 8   7    6   5  4 3    0
        #  +---------+--------+----+-----+----+------+
        #  | Op-code |  Rsrc  | Ad | B/W | As | Rdst |
        #  +---------+--------+----+-----+----+------+
        #  |       Source or destination 15:0        |
        #  +-----------------------------------------+
        #  |             Destination 15:0            |
        #  +-----------------------------------------+
        opc  = BITS(w, 15, 12)
        As   = BITS(w, 5, 4)
        Ad   = BIT(w, 7)
        Rsrc = BITS(w, 11, 8)
        Rdst = BITS(w,  3, 0)
        BW   = BIT(w, 6)
        if opc < 4:
            # something went wrong
            insn.size = 0
        else:
            if extw:
              AL = BIT(extw, 6)
              if AL == 0 and BW == 1:
                BW = DLEN_AWORD
              insn.itype = self.itype_movx + (opc-4)
            else:
              insn.itype = self.itype_mov + (opc-4)
        self.fill_op(insn, insn.Op1, Rsrc, As, BW, True,  True,  extw)
        self.fill_op(insn, insn.Op2, Rdst, Ad, BW, False, False, extw)
        insn.auxpref = BW + AUX_WORD
        if extw and As == AM_REGISTER and Ad == AM_REGISTER:
          self.handle_reg_extw(insn, extw)

    # ----------------------------------------------------------------------
    def decode_format_II(self, insn, w, extw):
        #  Single-Operand (Format II) Instructions
        #
        #   15         10 9       7   6   5  4 3    0
        #  +-------------+---------+-----+----+------+
        #  | 0 0 0 1 0 0 | Op-code | B/W | Ad | Rdst |
        #  +-------------+---------+-----+----+------+
        #  |             Destination 15:0            |
        #  +-----------------------------------------+
        opc  = BITS(w, 9, 7)
        Ad   = BITS(w, 5, 4)
        Rdst = BITS(w, 3, 0)
        BW   = BIT(w, 6)
        if opc in [6, 7]:
          if extw:
            return 0
          return self.decode_430x_calla(insn, w)
        if extw:
          AL = BIT(extw, 6)
          if AL == 0 and BW == 1:
            BW = DLEN_AWORD
          insn.itype = self.itype_rrcx + opc
        else:
          insn.itype = self.itype_rrc + opc
        self.fill_op(insn, insn.Op1, Rdst, Ad, BW, False, True, extw)
        insn.auxpref = BW + AUX_WORD
        if insn.itype in [self.itype_swpb, self.itype_sxt, self.itype_call, self.itype_reti]:
            # these commands have no suffix and should have BW set to 0
            if BW == 0:
                insn.auxpref = AUX_NOSUF
                if insn.itype == self.itype_reti:
                    # Ad and Rdst should be 0
                    if Ad == 0 and Rdst == 0:
                        insn.Op1.type = o_void
                    else:
                        # bad instruction
                        insn.itype = self.itype_null
            else:
                # bad instruction
                insn.itype = self.itype_null
        if extw and Ad == AM_REGISTER:
          self.handle_reg_extw(insn, extw)

    # ----------------------------------------------------------------------
    def decode_jump(self, insn, w):
        #  Jump Instructions
        #
        #   15     13 12   10 9                    0
        #  +---------+-------+----------------------+
        #  |  0 0 1  |   C   |  10-bit PC offset    |
        #  +---------+-------+----------------------+
        C    = BITS(w, 12, 10)
        offs = BITS(w,  9,  0)
        offs = SIGNEXT(offs, 10)
        insn.Op1.type = o_near
        insn.Op1.addr = insn.ea + 2 + offs*2
        insn.itype = self.itype_jnz + C

    # ----------------------------------------------------------------------
    def decode_430x_mova(self, insn, w):
        #  MSP430X Address Instructions
        # 15    12  11 8  7     4  3 0
        #  0 0 0 0  src   0 0 0 0  dst  MOVA @Rsrc,Rdst
        #  0 0 0 0  src   0 0 0 1  dst  MOVA @Rsrc+,Rdst
        #  0 0 0 0  abs   0 0 1 0  dst  MOVA &abs20,Rdst
        #  0 0 0 0  src   0 0 1 1  dst  MOVA x(Rsrc),Rdst

        #  0 0 0 0 n-1 00  0 1 0 0 dst RRCM.A #n,Rdst
        #  0 0 0 0 n-1 01  0 1 0 0 dst RRAM.A #n,Rdst
        #  0 0 0 0 n-1 10  0 1 0 0 dst RLAM.A #n,Rdst
        #  0 0 0 0 n-1 11  0 1 0 0 dst RRUM.A #n,Rdst

        #  0 0 0 0 n-1 00  0 1 0 1 dst RRCM.W #n,Rdst
        #  0 0 0 0 n-1 01  0 1 0 1 dst RRAM.W #n,Rdst
        #  0 0 0 0 n-1 10  0 1 0 1 dst RLAM.W #n,Rdst
        #  0 0 0 0 n-1 11  0 1 0 1 dst RRUM.W #n,Rdst

        #  0 0 0 0  src   0 1 1 0  abs  MOVA Rsrc,&abs20
        #  0 0 0 0  src   0 1 1 1  dst  MOVA Rsrc,X(Rdst)

        #  0 0 0 0  imm   1 0 0 0  dst  MOVA #imm20,Rdst
        #  0 0 0 0  imm   1 0 0 1  dst  CMPA #imm20,Rdst
        #  0 0 0 0  imm   1 0 1 0  dst  ADDA #imm20,Rdst
        #  0 0 0 0  imm   1 0 1 1  dst  SUBA #imm20,Rdst
        #  0 0 0 0  src   1 1 0 0  dst  MOVA Rsrc,Rdst
        #  0 0 0 0  src   1 1 0 1  dst  CMPA Rsrc,Rdst
        #  0 0 0 0  src   1 1 1 0  dst  ADDA Rsrc,Rdst
        #  0 0 0 0  src   1 1 1 1  dst  SUBA Rsrc,Rdst

        opc  = BITS(w, 7, 4)
        Rsrc = BITS(w, 11, 8)
        Rdst = BITS(w,  3, 0)
        tbl = [
          # itype, operands addressing modes
          # indexed by opcode[7:4]
          [self.itype_mova, AM_INDIRECT, AM_REGISTER], # 0000 MOVA @Rsrc,Rdst
          [self.itype_mova, AM_AUTOINC,  AM_REGISTER], # 0001 MOVA @Rsrc+,Rdst
          [self.itype_mova, AM_ABS20,    AM_REGISTER], # 0010 MOVA &abs20,Rdst
          [self.itype_mova, AM_INDEXED,  AM_REGISTER], # 0011 MOVA X(Rsrc),Rdst
          [-1,              -1,          -1         ], # 0100 Rxxx.A #n, Rdst
          [-1,              -1,          -1         ], # 0101 Rxxx.W #n, Rdst
          [self.itype_mova, AM_REGISTER, AM_ABS20   ], # 0110 MOVA Rsrc, &abs20
          [self.itype_mova, AM_REGISTER, AM_INDEXED ], # 0111 MOVA Rsrc, X(Rdst)
          [self.itype_mova, AM_IMM20,    AM_REGISTER], # 1000 MOVA #imm20, Rdst
          [self.itype_cmpa, AM_IMM20,    AM_REGISTER], # 1001 CMPA #imm20, Rdst
          [self.itype_adda, AM_IMM20,    AM_REGISTER], # 1010 ADDA #imm20, Rdst
          [self.itype_suba, AM_IMM20,    AM_REGISTER], # 1011 SUBA #imm20, Rdst
          [self.itype_mova, AM_REGISTER, AM_REGISTER], # 1100 MOVA Rsrc, Rdst
          [self.itype_cmpa, AM_REGISTER, AM_REGISTER], # 1101 CMPA Rsrc, Rdst
          [self.itype_adda, AM_REGISTER, AM_REGISTER], # 1110 ADDA Rsrc, Rdst
          [self.itype_suba, AM_REGISTER, AM_REGISTER], # 1111 SUBA Rsrc, Rdst
        ]
        row = tbl[opc]
        if row[0] != -1:
          insn.itype = row[0]
          self.fill_op(insn, insn.Op1, Rsrc, row[1], 2, True,  Rdst != 0)
          self.fill_op(insn, insn.Op2, Rdst, row[2], 2, False, False)
          insn.auxpref = AUX_AX
        else:
          # src[3:2] == n-1
          # src[1:0] == insn id
          insn.itype = self.itype_rrcm + (Rsrc & 0b11)
          insn.Op1.type = o_imm
          insn.Op1.dtype = dt_byte
          insn.Op1.value = (Rsrc>>2) + 1
          # opc[0]: 0=.A, 1=.W
          BW = 0 if (opc & 1) else 2
          self.fill_op(insn, insn.Op2, Rdst, AM_REGISTER, BW, False, False)
          if opc & 1:
            insn.auxpref = AUX_A
          else:
            insn.auxpref = AUX_WORD

    # ----------------------------------------------------------------------
    def decode_430x_calla(self, insn, w):
        #  MSP430X CALLA Instruction
        opc  = BITS(w, 7, 4)
        Rdst = BITS(w, 3, 0)
        tbl = [
          # itype, dest addressing mode
          # indexed by opcode[7:4]
          [self.itype_reti,  -1         ], # 0000 RETI
          [self.itype_null,  -1         ], # 0001 ----
          [self.itype_null,  -1         ], # 0010 ----
          [self.itype_null,  -1         ], # 0011 ----
          [self.itype_calla, AM_REGISTER], # 0100 CALLA Rdst
          [self.itype_calla, AM_INDEXED ], # 0101 CALLA X(Rdst)
          [self.itype_calla, AM_INDIRECT], # 0110 CALLA @Rdst
          [self.itype_calla, AM_AUTOINC ], # 0111 CALLA @Rdst+
          [self.itype_calla, AM_ABS20   ], # 1000 CALLA &abs20
          [self.itype_calla, AM_SYM20   ], # 1001 CALLA sym20 (imm20+PC)
          [self.itype_null,  -1         ], # 1010 ----
          [self.itype_calla, AM_IMM20   ], # 1011 CALLA #imm20
          [self.itype_null,  -1         ], # 1100 ----
          [self.itype_null,  -1         ], # 1101 ----
          [self.itype_null,  -1         ], # 1110 ----
          [self.itype_null,  -1         ], # 1111 ----
        ]
        row = tbl[opc]
        insn.itype = row[0]
        if row[1] != -1:
          self.fill_op(insn, insn.Op1, Rdst, row[1], DLEN_AWORD, True, False)
          insn.auxpref = AUX_AX

    # ----------------------------------------------------------------------
    def decode_430x_pushm(self, insn, w):
        #  MSP430X PUSHM/POPM Instructions
        #  15  10 98 7 4 3 0
        #  000101 00 n-1 dst      PUSHM.A #n,Rdst
        #  000101 01 n-1 dst      PUSHM.W #n,Rdst
        #  000101 10 n-1 dst-n+1  POPM.A  #n, Rdst
        #  000101 11 n-1 dst-n+1  POPM.W  #n, Rdst
        opc  = BITS(w, 7, 4)
        Rdst = BITS(w, 3, 0)

        ispop = BIT(w, 9)
        insn.itype   = [self.itype_pushm, self.itype_popm] [ispop]
        n    = BITS(w, 7, 4) + 1
        Rdst = BITS(w, 3, 0)
        if ispop:
          Rdst += n - 1
        insn.Op1.type = o_imm
        insn.Op1.dtype = dt_byte
        insn.Op1.value = n
        isw = BIT(w, 8)
        BW = 0 if isw else 2
        self.fill_op(insn, insn.Op2, Rdst, AM_REGISTER, BW, False, False)
        insn.auxpref = [AUX_A, AUX_WORD] [isw]

    # ----------------------------------------------------------------------
    # does operand match tuple m? (type, value)
    def match_op(self, op, m):
        if m == None:
            return True
        if op.type != m[0]:
            return False
        if op.type == o_imm:
            return op.value == m[1]
        elif op.type in [o_reg, o_phrase]:
            return op.reg == m[1]
        else:
            return false

    # ----------------------------------------------------------------------
    # replace some instructions by simplified mnemonics ("emulated" in TI terms)
    def simplify(self, insn):
        # source mnemonic mapped to a list of matches:
        #   match function, new mnemonic, new operand
        maptbl = {
            self.itype_addc: [
                # addc #0, dst -> adc dst
                [ lambda: is_imm_op(insn.Op1, 0), self.itype_adc, 2 ],
                # addc dst, dst -> rlc dst
                [ lambda: same_op(insn.Op1, insn.Op2), self.itype_rlc, 1 ],
            ],
            self.itype_addcx: [
                # addcx #0, dst -> adcx dst
                [ lambda: is_imm_op(insn.Op1, 0), self.itype_adcx, 2 ],
                # addcx dst, dst -> rlcx dst
                [ lambda: same_op(insn.Op1, insn.Op2), self.itype_rlcx, 1 ],
            ],
            self.itype_mov: [
                # mov #0, R3 -> nop
                [ lambda: is_imm_op(insn.Op1, 0) and insn.Op2.is_reg(self.ireg_R3), self.itype_nop, 0 ],
                # mov #0, dst -> clr dst
                [ lambda: is_imm_op(insn.Op1, 0), self.itype_clr, 2 ],
                # mov @SP+, PC -> ret
                [ lambda: is_autoinc(insn.Op1, self.ireg_SP) and insn.Op2.is_reg(self.ireg_PC), self.itype_ret, 0 ],
                # mov @SP+, dst -> pop dst
                [ lambda: is_autoinc(insn.Op1, self.ireg_SP), self.itype_pop, 2 ],
                # mov dst, PC -> br dst
                [ lambda: self.is_movpc(insn), self.itype_br, 1 ],
            ],
            self.itype_movx: [
                # movx #0, dst -> clrx dst
                [ lambda: is_imm_op(insn.Op1, 0), self.itype_clrx, 2 ],
                # movx @SP+, dst -> popx dst
                [ lambda: is_autoinc(insn.Op1, self.ireg_SP), self.itype_popx, 2 ],
            ],
            self.itype_mova: [
                # mova #0, dst -> clra dst
                [ lambda: is_imm_op(insn.Op1, 0), self.itype_clra, 2 ],
                # mova @SP+, PC -> reta
                [ lambda: is_autoinc(insn.Op1, self.ireg_SP) and insn.Op2.is_reg(self.ireg_PC), self.itype_reta, 0 ],
                # mova @SP+, dst -> popa dst
                [ lambda: is_autoinc(insn.Op1, self.ireg_SP), self.itype_popa, 2 ],
                # mova dst, PC -> bra dst
                [ lambda: insn.Op2.is_reg(self.ireg_PC), self.itype_bra, 1 ],
            ],
            self.itype_bic: [
                # bic #1, SR -> clrc
                [ lambda: is_imm_op(insn.Op1, 1) and insn.Op2.is_reg(self.ireg_SR), self.itype_clrc, 0 ],
                # bic #2, SR -> clrz
                [ lambda: is_imm_op(insn.Op1, 2) and insn.Op2.is_reg(self.ireg_SR), self.itype_clrz, 0 ],
                # bic #4, SR -> clrn
                [ lambda: is_imm_op(insn.Op1, 4) and insn.Op2.is_reg(self.ireg_SR), self.itype_clrn, 0 ],
                # bic #8, SR -> dint
                [ lambda: is_imm_op(insn.Op1, 8) and insn.Op2.is_reg(self.ireg_SR), self.itype_dint, 0 ],
            ],
            self.itype_bis: [
                # bis #1, SR -> setc
                [ lambda: is_imm_op(insn.Op1, 1) and insn.Op2.is_reg(self.ireg_SR), self.itype_setc, 0 ],
                # bis #2, SR -> setz
                [ lambda: is_imm_op(insn.Op1, 2) and insn.Op2.is_reg(self.ireg_SR), self.itype_setz, 0 ],
                # bis #4, SR -> setn
                [ lambda: is_imm_op(insn.Op1, 4) and insn.Op2.is_reg(self.ireg_SR), self.itype_setn, 0 ],
                # bis #8, SR -> eint
                [ lambda: is_imm_op(insn.Op1, 8) and insn.Op2.is_reg(self.ireg_SR), self.itype_eint, 0 ],
            ],
            self.itype_dadd: [
                # dadd #0, dst -> dadc dst
                [ lambda: is_imm_op(insn.Op1, 0), self.itype_dadc, 2 ],
            ],
            self.itype_daddx: [
                # daddx #0, dst -> dadcx dst
                [ lambda: is_imm_op(insn.Op1, 0), self.itype_dadcx, 2 ],
            ],
            self.itype_sub: [
                # sub #1, dst -> dec dst
                [ lambda: is_imm_op(insn.Op1, 1), self.itype_dec, 2 ],
                # sub #2, dst -> decd dst
                [ lambda: is_imm_op(insn.Op1, 2), self.itype_decd, 2 ],
            ],
            self.itype_subx: [
                # subx #1, dst -> decx dst
                [ lambda: is_imm_op(insn.Op1, 1), self.itype_decx, 2 ],
                # subx #2, dst -> decdx dst
                [ lambda: is_imm_op(insn.Op1, 2), self.itype_decdx, 2 ],
            ],
            self.itype_suba: [
                # suba #2, dst -> decda dst
                [ lambda: is_imm_op(insn.Op1, 2), self.itype_decda, 2 ],
            ],
            self.itype_subc: [
                # subc #0, dst -> sbc dst
                [ lambda: is_imm_op(insn.Op1, 0), self.itype_sbc, 2 ],
            ],
            self.itype_subcx: [
                # subcx #0, dst -> sbcx dst
                [ lambda: is_imm_op(insn.Op1, 0), self.itype_sbcx, 2 ],
            ],
            self.itype_add: [
                # add #1, dst -> inc dst
                [ lambda: is_imm_op(insn.Op1, 1), self.itype_inc, 2 ],
                # add #2, dst -> incd dst
                [ lambda: is_imm_op(insn.Op1, 2), self.itype_incd, 2 ],
                # add dst, dst -> rla dst
                [ lambda: same_op(insn.Op1, insn.Op2), self.itype_rla, 1 ],
            ],
            self.itype_adda: [
                # adda #2, dst -> incda dst
                [ lambda: is_imm_op(insn.Op1, 2), self.itype_incda, 2 ],
            ],
            self.itype_addx: [
                # addx #1, dst -> incx dst
                [ lambda: is_imm_op(insn.Op1, 1), self.itype_incx, 2 ],
                # addx #2, dst -> incdx dst
                [ lambda: is_imm_op(insn.Op1, 2), self.itype_incdx, 2 ],
                # addx dst, dst -> rlax dst
                [ lambda: same_op(insn.Op1, insn.Op2), self.itype_rlax, 1 ],
            ],
            self.itype_xor: [
                # xor #-1, dst -> inv dst
                [ lambda: is_imm_op(insn.Op1, -1), self.itype_inv, 2 ],
            ],
            self.itype_xorx: [
                # xorx #-1, dst -> invx dst
                [ lambda: is_imm_op(insn.Op1, -1), self.itype_invx, 2 ],
            ],
            self.itype_cmp: [
                # cmp #0, dst -> tst dst
                [ lambda: is_imm_op(insn.Op1, 0), self.itype_tst, 2 ],
            ],
            self.itype_cmpx: [
                # cmpx #0, dst -> tstx dst
                [ lambda: is_imm_op(insn.Op1, 0), self.itype_tstx, 2 ],
            ],
            self.itype_cmpa: [
                # cmpa #0, dst -> tsta dst
                [ lambda: is_imm_op(insn.Op1, 0), self.itype_tsta, 2 ],
            ],
        }

        # instructions which should have no suffix
        nosuff = [self.itype_ret,  self.itype_reta, self.itype_br, self.itype_bra,
                  self.itype_clrc, self.itype_clrn, self.itype_clrz, self.itype_dint,
                  self.itype_eint, self.itype_clrc, self.itype_nop, self.itype_pop,
                  self.itype_popa, self.itype_setc, self.itype_setn, self.itype_setz, ]

        if insn.itype in maptbl:
            for m in maptbl[insn.itype]:
                if m[0]():
                    # matched instruction; replace the itype
                    insn.itype = m[1]
                    if m[2] == 0:
                        # remove the operands
                        insn.Op1.type = o_void
                    elif m[2] == 2:
                        # replace first operand with the second
                        insn.Op1.assign(insn.Op2)
                    # remove the second operand, if any
                    insn.Op2.type = o_void
                    # remove suffix if necessary
                    if insn.itype in nosuff and insn.auxpref == AUX_WORD:
                        insn.auxpref = 0
                    break

    # ----------------------------------------------------------------------
    def ev_ana_insn(self, insn):
        """
        Decodes an instruction into 'insn'.
        Returns: insn.size (=the size of the decoded instruction) or zero
        """
        if (insn.ea & 1) != 0:
            return 0
        w = insn.get_next_word()
        extw = None
        if BITS(w, 15, 11) == 0b00011:
            # operand extension word
            extw = w
            w = insn.get_next_word()
        if BITS(w, 15, 10) == 0b000100:
            self.decode_format_II(insn, w, extw)
        elif BITS(w, 15, 10) == 0b000101:
            if extw: return 0
            self.decode_430x_pushm(insn, w)
        elif BITS(w, 15, 13) == 1: # 001
            if extw: return 0
            self.decode_jump(insn, w)
        elif BITS(w, 15, 12) == 0:
            if extw: return 0
            self.decode_430x_mova(insn, w)
        else:
            self.decode_format_I(insn, w, extw)

        self.simplify(insn)
        return insn.itype != self.itype_null

    # ----------------------------------------------------------------------
    def init_instructions(self):
        Instructions = []
        i = 0
        for x in self.instruc:
            if x['name'] != '':
                setattr(self, 'itype_' + x['name'], i)
            else:
                setattr(self, 'itype_null', i)
            i += 1

        # icode of the last instruction + 1
        self.instruc_end = len(self.instruc)

        # Icode of return instruction. It is ok to give any of possible return
        # instructions
        self.icode_return = self.itype_reti

    # ----------------------------------------------------------------------
    def init_registers(self):
        """This function parses the register table and creates corresponding ireg_XXX constants"""

        # Registers definition
        self.reg_names = [
            # General purpose registers
            "PC", # R0
            "SP", # R1
            "SR", # R2, CG1
            "R3", # CG2
            "R4",
            "R5",
            "R6",
            "R7",
            "R8",
            "R9",
            "R10",
            "R11",
            "R12",
            "R13",
            "R14",
            "R15",
            # Fake segment registers
            "CS",
            "DS"
        ]

        # Create the ireg_XXXX constants
        for i in range(len(self.reg_names)):
            setattr(self, 'ireg_' + self.reg_names[i], i)

        # Segment register information (use virtual CS and DS registers if your
        # processor doesn't have segment registers):
        self.reg_first_sreg = self.ireg_CS
        self.reg_last_sreg  = self.ireg_DS

        # number of CS register
        self.reg_code_sreg = self.ireg_CS

        # number of DS register
        self.reg_data_sreg = self.ireg_DS

    # ----------------------------------------------------------------------
    def __init__(self):
        processor_t.__init__(self)
        self.init_instructions()
        self.init_registers()

# ----------------------------------------------------------------------
# Every processor module script must provide this function.
# It should return a new instance of a class derived from processor_t
def PROCESSOR_ENTRY():
    return msp430_processor_t()
