1#!/usr/bin/env python 2 3from __future__ import print_function 4import sys 5import bitstring 6from capstone import * 7from capstone.m68k import * 8 9# 10# Objdump with the same output as his binary cousin 11# 12 13TODO = """ 14TODO : 15 16 o need more testing on M68K_AM_*_DISP 17 o cleanup, etc ... 18 19""" 20 21objdump_cmd_example = 'm68k-atari-mint-objdump -b binary -D -mm68k --adjust-vma 0x30664 u/m68k.bin' 22objdump_dumpheader_fmt = """ 23%s: file format binary 24 25 26Disassembly of section .data: 27 28%08x <.data>:""" 29 30 31M68000_CODE = b"\x04\x40\x00\x40" 32 33all_tests = ( 34 (CS_ARCH_M68K, CS_MODE_BIG_ENDIAN | CS_MODE_M68K_060, M68000_CODE, "M68060-32 (Big-endian)"), 35) 36 37 38def dump_bytes(b, len): 39 str = '' 40 i = 0 41 while i < len: 42 str += format("%02x%02x " % (b[i], b[i+1])) 43 i += 2 44 return str[:-1] 45 46def dump_op_reg(insn, op_reg): 47 if op_reg == M68K_REG_A7: 48 return "%sp" 49 if op_reg == M68K_REG_A6: 50 return "%fp" 51 return '%' + insn.reg_name(op_reg) 52 53def s8(value): 54 return bitstring.Bits(uint=value, length=8).unpack('int')[0] 55 56def s16(value): 57 return bitstring.Bits(uint=value, length=16).unpack('int')[0] 58 59def extsign8(value): 60 if value & 0x80: 61 return 0xffffffffffffff00 + value 62 return value 63 64def extsign1616(value): 65 if value & 0x8000: 66 return 0xffff0000 + value 67 return value 68 69def extsign1632(value): 70 if value & 0x8000: 71 return 0xffffffffffff0000 + value 72 return value 73 74 75def printRegbitsRange(buffer, data, prefix): 76 str = '' 77 first = 0 78 run_length = 0 79 80 i = 0 81 while i < 8: 82 if (data & (1 << i)): 83 first = i 84 run_length = 0 85 86 while (i < 7 and (data & (1 << (i + 1)))): 87 i += 1 88 run_length += 1 89 90 if len(buffer) or len(str): 91 str += "/" 92 93 str += format("%%%s%d" % (prefix, first)) 94 if run_length > 0: 95 str += format("-%%%s%d" % (prefix, first + run_length)) 96 i += 1 97 return str 98 99def registerBits(op): 100 str = '' 101 data = op.register_bits 102 103 str += printRegbitsRange(str, data & 0xff, "d") 104 str += printRegbitsRange(str, (data >> 8) & 0xff, "a") 105 str += printRegbitsRange(str, (data >> 16) & 0xff, "fp") 106 return str 107 108def dump_op_ea(insn, op): 109 s_spacing = " " 110 map_index_size_str = { 0: 'w', 1 : 'l' } 111 str = '' 112 113 if op.address_mode == M68K_AM_NONE: 114 if op.type == M68K_OP_REG_BITS: 115 return registerBits(op) 116 if op.type == M68K_OP_REG_PAIR: 117 return registerPair(op) 118 if op.type == M68K_OP_REG: 119 return dump_op_reg(insn, op.reg) 120 121 if op.address_mode == M68K_AM_REG_DIRECT_DATA: 122 return dump_op_reg(insn, op.reg) 123 if op.address_mode == M68K_AM_REG_DIRECT_ADDR: 124 return dump_op_reg(insn, op.reg) + "@" 125 if op.address_mode == M68K_AM_REGI_ADDR: 126 return dump_op_reg(insn, op.reg) + "@" 127 if op.address_mode == M68K_AM_REGI_ADDR_POST_INC: 128 return dump_op_reg(insn, op.reg) + "@+" 129 if op.address_mode == M68K_AM_REGI_ADDR_PRE_DEC: 130 return dump_op_reg(insn, op.reg) + "@-" 131 if op.address_mode == M68K_AM_REGI_ADDR_DISP: 132# str = dump_op_reg(insn, op.mem.base_reg - M68K_REG_A0 + 1) #double check and fixme '+1' : 02af 899f 2622 133 str = dump_op_reg(insn, op.mem.base_reg) 134 if op.mem.disp: 135 str += format("@(%d)" % s16(op.mem.disp)) 136 return str 137 138 if op.address_mode == M68K_AM_PCI_DISP: 139 return format("%%pc@(0x%x)" % ( extsign1616(op.mem.disp + 2))) 140 if op.address_mode == M68K_AM_ABSOLUTE_DATA_SHORT: 141 return format("0x%x" % (extsign1616(op.imm & 0xffff))) 142 if op.address_mode == M68K_AM_ABSOLUTE_DATA_LONG: 143 return format("0x%x" % (op.imm & 0xffffffff)) 144 if op.address_mode == M68K_AM_IMMEDIATE: 145 if insn.op_size.type == M68K_SIZE_TYPE_FPU: 146 map_fpu_size_str = { M68K_FPU_SIZE_SINGLE : op.simm, M68K_FPU_SIZE_DOUBLE : op.dimm } 147 return format("#%f" % (insn.op_size.fpu_size[map_fpu_size_str])) 148 return format("#$%x" % (op.imm)) 149 150 if op.address_mode in [ M68K_AM_PCI_INDEX_8_BIT_DISP, M68K_AM_AREGI_INDEX_8_BIT_DISP ]: 151 disp = op.mem.disp 152 if op.register_bits == 2: 153 disp = extsign8(op.mem.disp) 154 if op.register_bits == 4: 155 disp = extsign1632(op.mem.disp) 156 157 str = dump_op_reg(insn, op.mem.base_reg) + "@(" + "{0:016x}".format(disp) + "," + dump_op_reg(insn, op.mem.index_reg) + ":" + map_index_size_str[op.mem.index_size] 158 if op.register_bits: 159 str += format(":%u" % (op.register_bits)) 160 return str + ")" 161 162 163 if op.address_mode in [ M68K_AM_PCI_INDEX_BASE_DISP, M68K_AM_AREGI_INDEX_BASE_DISP ]: 164 str += format("%s" % ( dump_op_reg(insn, op.mem.base_reg) )) 165 str += format("@(%016x)@(%016x" % (extsign1632(op.mem.in_disp), extsign1632(op.mem.out_disp))) 166 if op.mem.index_reg: 167 str += "," + dump_op_reg(insn, op.mem.index_reg) + ":" + map_index_size_str[op.mem.index_size] 168 if op.register_bits: 169 str += format(":%u" % (op.register_bits)) 170 str += ")" 171 return str 172 173 if op.mem.in_disp > 0: 174 str += format("$%x" % ( op.mem.in_disp)) 175 176 str += format("(") 177 178 if op.address_mode == M68K_AM_PCI_INDEX_BASE_DISP: 179 str_size = '' 180 if op.mem.index_size: 181 str_size = "l" 182 else: 183 str_size = "w" 184 str += format("pc,%s%s.%s" % ( dump_op_reg(insn, op.mem.index_reg)), s_spacing, str_size) 185 else: 186 if op.mem.base_reg != M68K_REG_INVALID: 187 str += format("a%d,%s" % ( op.mem.base_reg - M68K_REG_A0, s_spacing)) 188 str_size = '' 189 if op.mem.index_size: 190 str_size = "l" 191 else: 192 str_size = "w" 193 str += format("%s.%s" % ( dump_op_reg(insn, op.mem.index_reg), str_size)) 194 195 if op.mem.scale > 0: 196 str += format("%s*%s%d)" % ( s_spacing, s_spacing, op.mem.scale)) 197 else: 198 str += ")" 199 return str 200 201 # It's ok to just use PCMI here as is as we set base_reg to PC in the disassembler. 202 # While this is not strictly correct it makes the code 203 # easier and that is what actually happens when the code is executed anyway. 204 205 if op.address_mode in [ M68K_AM_PC_MEMI_POST_INDEX, M68K_AM_PC_MEMI_PRE_INDEX, M68K_AM_MEMI_PRE_INDEX, M68K_AM_MEMI_POST_INDEX]: 206 if op.mem.base_reg: 207 str += format("%s" % ( dump_op_reg(insn, op.mem.base_reg) )) 208 if op.mem.in_disp: 209 value = op.mem.in_disp 210 if op.mem.in_disp & 0x8000: 211 value = 0xffffffffffff0000 + op.mem.in_disp 212 str += format("@(%016x)@(%016x)" % (value, op.mem.out_disp)) 213 return str 214 215 str += format("([") 216 if op.mem.in_disp > 0: 217 str += format("$%x" % ( op.mem.in_disp)) 218 219 if op.mem.base_reg != M68K_REG_INVALID: 220 if op.mem.in_disp > 0: 221 str += format(",%s%s" % ( s_spacing, dump_op_reg(insn, op.mem.base_reg))) 222 else: 223 str += format("%s" % ( dump_op_reg(insn, op.mem.base_reg))) 224 225 if op.address_mode in [ M68K_AM_MEMI_POST_INDEX, M68K_AM_PC_MEMI_POST_INDEX]: 226 str += format("]") 227 228 if op.mem.index_reg != M68K_REG_INVALID: 229 str_size = '' 230 if op.mem.index_size: 231 str_size = "l" 232 else: 233 str_size = "w" 234 str += format(",%s%s.%s" % ( s_spacing, dump_op_reg(insn, op.mem.index_reg), str_size)) 235 if op.mem.scale > 0: 236 str += format("%s*%s%d" % ( s_spacing, s_spacing, op.mem.scale)) 237 if op.address_mode in [ M68K_AM_MEMI_PRE_INDEX, M68K_AM_PC_MEMI_PRE_INDEX]: 238 str += format("]") 239 if op.mem.out_disp > 0: 240 str += format(",%s$%x" % ( s_spacing, op.mem.out_disp)) 241 str += format(")") 242 return str 243 244 245 if op.mem.bitfield: 246 return format("%d:%d" % ( op.mem.offset, op.mem.width)) 247 248 ############# OK 249 if op.address_mode == M68K_AM_AREGI_INDEX_BASE_DISP: 250 if op.mem.index_size: 251 str_size = "l" 252 else: 253 str_size = "w" 254 bits = op.mem.disp 255 return dump_op_reg(insn, op.mem.base_reg) + "@(" + "{0:016b}".format(bits) + "," + dump_op_reg(insn, op.mem.index_reg) + ":" + str_size + ")" 256 return '' 257 258 259 260# M68K Addressing Modes 261 262map_address_mode_str = { 263 0 : "M68K_AM_NONE", 264 1 : "M68K_AM_REG_DIRECT_DATA", 265 2 : "M68K_AM_REG_DIRECT_ADDR", 266 3 : "M68K_AM_REGI_ADDR", 267 4 : "M68K_AM_REGI_ADDR_POST_INC", 268 5 : "M68K_AM_REGI_ADDR_PRE_DEC", 269 6 : "M68K_AM_REGI_ADDR_DISP", 270 7 : "M68K_AM_AREGI_INDEX_8_BIT_DISP", 271 8 : "M68K_AM_AREGI_INDEX_BASE_DISP", 272 9 : "M68K_AM_MEMI_POST_INDEX", 273 10 : "M68K_AM_MEMI_PRE_INDEX", 274 11 : "M68K_AM_PCI_DISP", 275 12 : "M68K_AM_PCI_INDEX_8_BIT_DISP", 276 13 : "M68K_AM_PCI_INDEX_BASE_DISP", 277 14 : "M68K_AM_PC_MEMI_POST_INDEX", 278 15 : "M68K_AM_PC_MEMI_PRE_INDEX", 279 16 : "M68K_AM_ABSOLUTE_DATA_SHORT", 280 17 : "M68K_AM_ABSOLUTE_DATA_LONG", 281 18 : "M68K_AM_IMMEDIATE", 282 } 283 284 285# Operand type for instruction's operands 286 287map_op_str = { 288 0 : "M68K_OP_INVALID", 289 1 : "M68K_OP_REG", 290 2 : "M68K_OP_IMM", 291 3 : "M68K_OP_MEM", 292 4 : "M68K_OP_FP", 293 5 : "M68K_OP_REG_BITS", 294 6 : "M68K_OP_REG_PAIR", 295} 296 297 298def debug(insn, op): 299 if len(sys.argv) > 3: 300 print("id %d type %s address_mode %s" % (insn.id, map_op_str[op.type], map_address_mode_str[op.address_mode])) 301 302 303def dump_ops(insn): 304 str = '' 305 mnemonic = insn.insn_name() 306 307 i = 0 308 while i < len(insn.operands): 309 if i > 0: 310 str += ',' 311 op = insn.operands[i] 312 debug(insn, op) 313 # "data" instruction generated by SKIPDATA option has no detail 314 if insn.id == M68K_INS_INVALID: 315 return format("0x%04x" % (op.imm)) 316 if op.type == M68K_OP_REG: 317 str_op_reg = dump_op_ea(insn, op) 318 if str_op_reg == '' or op.address_mode == M68K_AM_REG_DIRECT_ADDR: 319 str_op_reg = dump_op_reg(insn, op.reg) 320 str += str_op_reg 321 if op.type == M68K_OP_IMM: 322 str_op_imm = format("#%u" % (op.imm)) 323 if mnemonic in ["bkpt"]: 324 str_op_imm = format("%u" % (op.imm)) 325 signed_insn = [ "move", "moveq", "cmp", "cmpi", "ori", "bclr", "pack", "unpk", "sub", "add" ] 326 if mnemonic in signed_insn: 327 if insn.op_size.size == 1 or mnemonic == "moveq": 328 str_op_imm = format("#%d" % s8(op.imm)) 329 if insn.op_size.size == 2 or mnemonic == "pack": 330 str_op_imm = format("#%d" % s16(op.imm)) 331 if insn.op_size.size == 4: 332 str_op_imm = format("#%d" % (op.imm)) 333 334 dbxx_insn = [ "dbt", "dbf", "dbhi", "dbls", "dbcc", "dbcs", "dbne", "dbeq", "dbvc", "dbvs", "dbpl", "dbmi", "dbge", "dblt", "dbgt", "dble", "dbra" ] 335 if is_branch(insn) or mnemonic in dbxx_insn: 336 str_op_imm = format("0x%x" % (op.imm & 0xffffffff)) 337 str += str_op_imm 338 if op.type == M68K_OP_MEM: 339 str_op_mem = dump_op_ea(insn, op) 340 if str_op_mem == '': 341 str_op_mem = format("0x%x" % (op.imm)) 342 str += str_op_mem 343 if op.type in [ M68K_OP_REG_BITS, M68K_OP_REG_PAIR ]: 344 str += dump_op_ea(insn, op) 345 346# if insn.address == 0x3127c: 347# import pdb;pdb.set_trace() 348# print("type %u am %u\n" % (op.type, op.address_mode)) 349 i += 1 350 return str 351 352 353def is_branch(insn): 354 mnemonic = insn.insn_name() 355 branch_insn = [ "bsr", "bra", "bhi", "bls", "bcc", "bcs", "bne", "beq", "bvc", "bvs", "bpl", "bmi", "bge", "blt", "bgt", "ble" ]; 356 return mnemonic in branch_insn 357 358def dump_mnemonic(insn): 359 # "data" instruction generated by SKIPDATA option has no detail 360 if insn.id == M68K_INS_INVALID: 361 return ".short" 362 mnemonic = insn.insn_name() 363 ext = { 0: '', 1:'b', 2:'w', 4:'l' } 364 if is_branch(insn): 365 ext.update({ 1:'s', 2:'w', 4:'l' }) 366 367 no_size = [ "pea", "lea", "bset", "bclr", "bchg", "btst", "nbcd", "abcd", "sbcd", "exg", "scc", "sls", "scs", "shi" ] 368 sxx_insn = [ "st", "sf", "shi", "sls", "scc", "scs", "sne", "seq", "svc", "svs", "spl", "smi", "sge", "slt", "sgt", "sle", "stop" ] 369 no_size += sxx_insn 370 no_size += [ "tas" ] 371 if mnemonic in no_size: 372 ext.update({ 0:'', 1:'', 2:'', 4:'' }) 373 return mnemonic + ext[insn.op_size.size] 374 375def print_insn_detail_np(insn): 376 # objdump format hack 377 if insn.size == 2: 378 space = ' ' * 11 379 if insn.size == 4: 380 space = ' ' * 6 381 if insn.size >= 6: 382 space = ' ' 383 space_ops = '' 384 if len(insn.operands) > 0: 385 space_ops = ' ' 386 387 print(" %x:\t%s%s\t%s%s%s" % (insn.address, dump_bytes(insn._raw.bytes, min(insn.size, 6)), space, dump_mnemonic(insn), space_ops, dump_ops(insn))) 388 389 if insn.size > 6: 390 delta = min(insn.size, 6) 391 print(" %x:\t%s " % (insn.address+delta, dump_bytes(insn._raw.bytes[delta:], min(insn.size-delta, 6)))) 392 393 394def print_objdump_dumpheader(filename='', address=0): 395 print(objdump_dumpheader_fmt % (filename, address)) 396 397# ## Test class Cs 398def test_class(): 399 for (arch, mode, code, comment) in all_tests: 400 filename = "/dev/stdin" 401 address = 0 402 if len(sys.argv) > 1: 403 filename = sys.argv[1] 404 if len(sys.argv) > 2: 405 address = int(sys.argv[2],16) 406 if len(sys.argv) > 3: 407 debug_mode = True 408 409 with open(filename, "rb") as f: 410 code = f.read() 411 412 try: 413 md = Cs(arch, mode) 414 md.detail = True 415 416 print_objdump_dumpheader(filename, address) 417 418 for insn in md.disasm(code, address): 419 print_insn_detail_np(insn) 420 421 except CsError as e: 422 print("ERROR: %s" % e) 423 424 425if __name__ == '__main__': 426 test_class() 427