xref: /aosp_15_r20/external/capstone/contrib/objdump/objdump-m68k.py (revision 9a0e4156d50a75a99ec4f1653a0e9602a5d45c18)
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