# ---------------------------------------------------------------------- # Texas Instruments MSP430 processor module # Copyright (c) 2010-2021 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 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 ok = False si = switch_info_t() # 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 = SIGNEXT(op.addr, 20) + pcval else: op.addr = 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()