Advertisement
PikalaxALT

gbz80disasm.py-v1.6.0-298-g0e17989

Jun 4th, 2016
219
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 33.10 KB | None | 0 0
  1. # -*- coding: utf-8 -*-
  2. """
  3. GBC disassembler
  4. """
  5.  
  6. import os
  7. import argparse
  8. from ctypes import c_int8
  9.  
  10. import configuration
  11. from wram import read_constants
  12.  
  13. z80_table = [
  14.     ('nop', 0),                    # 00
  15.     ('ld bc, {}', 2),              # 01
  16.     ('ld [bc], a', 0),             # 02
  17.     ('inc bc', 0),                 # 03
  18.     ('inc b', 0),                  # 04
  19.     ('dec b', 0),                  # 05
  20.     ('ld b, ${:02x}', 1),          # 06
  21.     ('rlca', 0),                   # 07
  22.     ('ld [{}], sp', 2),            # 08
  23.     ('add hl, bc', 0),             # 09
  24.     ('ld a, [bc]', 0),             # 0a
  25.     ('dec bc', 0),                 # 0b
  26.     ('inc c', 0),                  # 0c
  27.     ('dec c', 0),                  # 0d
  28.     ('ld c, ${:02x}', 1),          # 0e
  29.     ('rrca', 0),                   # 0f
  30.     ('db $10', 0),                 # 10
  31.     ('ld de, {}', 2),              # 11
  32.     ('ld [de], a', 0),             # 12
  33.     ('inc de', 0),                 # 13
  34.     ('inc d', 0),                  # 14
  35.     ('dec d', 0),                  # 15
  36.     ('ld d, ${:02x}', 1),          # 16
  37.     ('rla', 0),                    # 17
  38.     ('jr {}', 1),                  # 18
  39.     ('add hl, de', 0),             # 19
  40.     ('ld a, [de]', 0),             # 1a
  41.     ('dec de', 0),                 # 1b
  42.     ('inc e', 0),                  # 1c
  43.     ('dec e', 0),                  # 1d
  44.     ('ld e, ${:02x}', 1),          # 1e
  45.     ('rra', 0),                    # 1f
  46.     ('jr nz, {}', 1),              # 20
  47.     ('ld hl, {}', 2),              # 21
  48.     ('ld [hli], a', 0),            # 22
  49.     ('inc hl', 0),                 # 23
  50.     ('inc h', 0),                  # 24
  51.     ('dec h', 0),                  # 25
  52.     ('ld h, ${:02x}', 1),          # 26
  53.     ('daa', 0),                    # 27
  54.     ('jr z, {}', 1),               # 28
  55.     ('add hl, hl', 0),             # 29
  56.     ('ld a, [hli]', 0),            # 2a
  57.     ('dec hl', 0),                 # 2b
  58.     ('inc l', 0),                  # 2c
  59.     ('dec l', 0),                  # 2d
  60.     ('ld l, ${:02x}', 1),          # 2e
  61.     ('cpl', 0),                    # 2f
  62.     ('jr nc, {}', 1),              # 30
  63.     ('ld sp, {}', 2),              # 31
  64.     ('ld [hld], a', 0),            # 32
  65.     ('inc sp', 0),                 # 33
  66.     ('inc [hl]', 0),               # 34
  67.     ('dec [hl]', 0),               # 35
  68.     ('ld [hl], ${:02x}', 1),       # 36
  69.     ('scf', 0),                    # 37
  70.     ('jr c, {}', 1),               # 38
  71.     ('add hl, sp', 0),             # 39
  72.     ('ld a, [hld]', 0),            # 3a
  73.     ('dec sp', 0),                 # 3b
  74.     ('inc a', 0),                  # 3c
  75.     ('dec a', 0),                  # 3d
  76.     ('ld a, ${:02x}', 1),          # 3e
  77.     ('ccf', 0),                    # 3f
  78.     ('ld b, b', 0),                # 40
  79.     ('ld b, c', 0),                # 41
  80.     ('ld b, d', 0),                # 42
  81.     ('ld b, e', 0),                # 43
  82.     ('ld b, h', 0),                # 44
  83.     ('ld b, l', 0),                # 45
  84.     ('ld b, [hl]', 0),             # 46
  85.     ('ld b, a', 0),                # 47
  86.     ('ld c, b', 0),                # 48
  87.     ('ld c, c', 0),                # 49
  88.     ('ld c, d', 0),                # 4a
  89.     ('ld c, e', 0),                # 4b
  90.     ('ld c, h', 0),                # 4c
  91.     ('ld c, l', 0),                # 4d
  92.     ('ld c, [hl]', 0),             # 4e
  93.     ('ld c, a', 0),                # 4f
  94.     ('ld d, b', 0),                # 50
  95.     ('ld d, c', 0),                # 51
  96.     ('ld d, d', 0),                # 52
  97.     ('ld d, e', 0),                # 53
  98.     ('ld d, h', 0),                # 54
  99.     ('ld d, l', 0),                # 55
  100.     ('ld d, [hl]', 0),             # 56
  101.     ('ld d, a', 0),                # 57
  102.     ('ld e, b', 0),                # 58
  103.     ('ld e, c', 0),                # 59
  104.     ('ld e, d', 0),                # 5a
  105.     ('ld e, e', 0),                # 5b
  106.     ('ld e, h', 0),                # 5c
  107.     ('ld e, l', 0),                # 5d
  108.     ('ld e, [hl]', 0),             # 5e
  109.     ('ld e, a', 0),                # 5f
  110.     ('ld h, b', 0),                # 60
  111.     ('ld h, c', 0),                # 61
  112.     ('ld h, d', 0),                # 62
  113.     ('ld h, e', 0),                # 63
  114.     ('ld h, h', 0),                # 64
  115.     ('ld h, l', 0),                # 65
  116.     ('ld h, [hl]', 0),             # 66
  117.     ('ld h, a', 0),                # 67
  118.     ('ld l, b', 0),                # 68
  119.     ('ld l, c', 0),                # 69
  120.     ('ld l, d', 0),                # 6a
  121.     ('ld l, e', 0),                # 6b
  122.     ('ld l, h', 0),                # 6c
  123.     ('ld l, l', 0),                # 6d
  124.     ('ld l, [hl]', 0),             # 6e
  125.     ('ld l, a', 0),                # 6f
  126.     ('ld [hl], b', 0),             # 70
  127.     ('ld [hl], c', 0),             # 71
  128.     ('ld [hl], d', 0),             # 72
  129.     ('ld [hl], e', 0),             # 73
  130.     ('ld [hl], h', 0),             # 74
  131.     ('ld [hl], l', 0),             # 75
  132.     ('halt', 0),                   # 76
  133.     ('ld [hl], a', 0),             # 77
  134.     ('ld a, b', 0),                # 78
  135.     ('ld a, c', 0),                # 79
  136.     ('ld a, d', 0),                # 7a
  137.     ('ld a, e', 0),                # 7b
  138.     ('ld a, h', 0),                # 7c
  139.     ('ld a, l', 0),                # 7d
  140.     ('ld a, [hl]', 0),             # 7e
  141.     ('ld a, a', 0),                # 7f
  142.     ('add b', 0),                  # 80
  143.     ('add c', 0),                  # 81
  144.     ('add d', 0),                  # 82
  145.     ('add e', 0),                  # 83
  146.     ('add h', 0),                  # 84
  147.     ('add l', 0),                  # 85
  148.     ('add [hl]', 0),               # 86
  149.     ('add a', 0),                  # 87
  150.     ('adc b', 0),                  # 88
  151.     ('adc c', 0),                  # 89
  152.     ('adc d', 0),                  # 8a
  153.     ('adc e', 0),                  # 8b
  154.     ('adc h', 0),                  # 8c
  155.     ('adc l', 0),                  # 8d
  156.     ('adc [hl]', 0),               # 8e
  157.     ('adc a', 0),                  # 8f
  158.     ('sub b', 0),                  # 90
  159.     ('sub c', 0),                  # 91
  160.     ('sub d', 0),                  # 92
  161.     ('sub e', 0),                  # 93
  162.     ('sub h', 0),                  # 94
  163.     ('sub l', 0),                  # 95
  164.     ('sub [hl]', 0),               # 96
  165.     ('sub a', 0),                  # 97
  166.     ('sbc b', 0),                  # 98
  167.     ('sbc c', 0),                  # 99
  168.     ('sbc d', 0),                  # 9a
  169.     ('sbc e', 0),                  # 9b
  170.     ('sbc h', 0),                  # 9c
  171.     ('sbc l', 0),                  # 9d
  172.     ('sbc [hl]', 0),               # 9e
  173.     ('sbc a', 0),                  # 9f
  174.     ('and b', 0),                  # a0
  175.     ('and c', 0),                  # a1
  176.     ('and d', 0),                  # a2
  177.     ('and e', 0),                  # a3
  178.     ('and h', 0),                  # a4
  179.     ('and l', 0),                  # a5
  180.     ('and [hl]', 0),               # a6
  181.     ('and a', 0),                  # a7
  182.     ('xor b', 0),                  # a8
  183.     ('xor c', 0),                  # a9
  184.     ('xor d', 0),                  # aa
  185.     ('xor e', 0),                  # ab
  186.     ('xor h', 0),                  # ac
  187.     ('xor l', 0),                  # ad
  188.     ('xor [hl]', 0),               # ae
  189.     ('xor a', 0),                  # af
  190.     ('or b', 0),                   # b0
  191.     ('or c', 0),                   # b1
  192.     ('or d', 0),                   # b2
  193.     ('or e', 0),                   # b3
  194.     ('or h', 0),                   # b4
  195.     ('or l', 0),                   # b5
  196.     ('or [hl]', 0),                # b6
  197.     ('or a', 0),                   # b7
  198.     ('cp b', 0),                   # b8
  199.     ('cp c', 0),                   # b9
  200.     ('cp d', 0),                   # ba
  201.     ('cp e', 0),                   # bb
  202.     ('cp h', 0),                   # bc
  203.     ('cp l', 0),                   # bd
  204.     ('cp [hl]', 0),                # be
  205.     ('cp a', 0),                   # bf
  206.     ('ret nz', 0),                 # c0
  207.     ('pop bc', 0),                 # c1
  208.     ('jp nz, {}', 2),              # c2
  209.     ('jp {}', 2),                  # c3
  210.     ('call nz, {}', 2),            # c4
  211.     ('push bc', 0),                # c5
  212.     ('add ${:02x}', 1),            # c6
  213.     ('rst $0', 0),                 # c7
  214.     ('ret z', 0),                  # c8
  215.     ('ret', 0),                    # c9
  216.     ('jp z, {}', 2),               # ca
  217.     ('bitops', 1),                 # cb
  218.     ('call z, {}', 2),             # cc
  219.     ('call {}', 2),                # cd
  220.     ('adc ${:02x}', 1),            # ce
  221.     ('rst $8', 0),                 # cf
  222.     ('ret nc', 0),                 # d0
  223.     ('pop de', 0),                 # d1
  224.     ('jp nc, ${:04x}', 2),         # d2
  225.     ('db $d3', 0),                 # d3
  226.     ('call nc, {}', 2),            # d4
  227.     ('push de', 0),                # d5
  228.     ('sub ${:02x}', 1),            # d6
  229.     ('rst $10', 0),                # d7
  230.     ('ret c', 0),                  # d8
  231.     ('reti', 0),                   # d9
  232.     ('jp c, ${:04x}', 2),          # da
  233.     ('db $db', 0),                 # db
  234.     ('call c, {}', 2),             # dc
  235.     ('db $dd', 2),                 # dd
  236.     ('sbc ${:02x}', 1),            # de
  237.     ('rst $18', 0),                # df
  238.     ('ld [{}], a', 1),             # e0
  239.     ('pop hl', 0),                 # e1
  240.     ('ld [$ff00+c], a', 0),        # e2
  241.     ('db $e3', 0),                 # e3
  242.     ('db $e4', 0),                 # e4
  243.     ('push hl', 0),                # e5
  244.     ('and ${:02x}', 1),            # e6
  245.     ('rst $20', 0),                # e7
  246.     ('add sp, ${:02x}', 1),        # e8
  247.     ('jp [hl]', 0),                # e9
  248.     ('ld [{}], a', 2),             # ea
  249.     ('db $eb', 0),                 # eb
  250.     ('db $ec', 2),                 # ec
  251.     ('db $ed', 2),                 # ed
  252.     ('xor ${:02x}', 1),            # ee
  253.     ('rst $28', 0),                # ef
  254.     ('ld a, [{}]', 1),             # f0
  255.     ('pop af', 0),                 # f1
  256.     ('db $f2', 0),                 # f2
  257.     ('di', 0),                     # f3
  258.     ('db $f4', 0),                 # f4
  259.     ('push af', 0),                # f5
  260.     ('or ${:02x}', 1),             # f6
  261.     ('rst $30', 0),                # f7
  262.     ('ld hl, sp+${:02x}', 1),      # f8
  263.     ('ld sp, [hl]', 0),            # f9
  264.     ('ld a, [{}]', 2),             # fa
  265.     ('ei', 0),                     # fb
  266.     ('db $fc', 2),                 # fc
  267.     ('db $fd', 2),                 # fd
  268.     ('cp ${:02x}', 1),             # fe
  269.     ('rst $38', 0),                # ff
  270. ]
  271.  
  272. bit_ops_table = [
  273.     "rlc b",     "rlc c",     "rlc d",     "rlc e",     "rlc h",     "rlc l",     "rlc [hl]",     "rlc a",       # $00 - $07
  274.     "rrc b",     "rrc c",     "rrc d",     "rrc e",     "rrc h",     "rrc l",     "rrc [hl]",     "rrc a",       # $08 - $0f
  275.     "rl b",      "rl c",      "rl d",      "rl e",      "rl h",      "rl l",      "rl [hl]",      "rl a",        # $10 - $17
  276.     "rr b",      "rr c",      "rr d",      "rr e",      "rr h",      "rr l",      "rr [hl]",      "rr a",        # $18 - $1f
  277.     "sla b",     "sla c",     "sla d",     "sla e",     "sla h",     "sla l",     "sla [hl]",     "sla a",       # $20 - $27
  278.     "sra b",     "sra c",     "sra d",     "sra e",     "sra h",     "sra l",     "sra [hl]",     "sra a",       # $28 - $2f
  279.     "swap b",    "swap c",    "swap d",    "swap e",    "swap h",    "swap l",    "swap [hl]",    "swap a",      # $30 - $37
  280.     "srl b",     "srl c",     "srl d",     "srl e",     "srl h",     "srl l",     "srl [hl]",     "srl a",       # $38 - $3f
  281.     "bit 0, b",  "bit 0, c",  "bit 0, d",  "bit 0, e",  "bit 0, h",  "bit 0, l",  "bit 0, [hl]",  "bit 0, a",    # $40 - $47
  282.     "bit 1, b",  "bit 1, c",  "bit 1, d",  "bit 1, e",  "bit 1, h",  "bit 1, l",  "bit 1, [hl]",  "bit 1, a",    # $48 - $4f
  283.     "bit 2, b",  "bit 2, c",  "bit 2, d",  "bit 2, e",  "bit 2, h",  "bit 2, l",  "bit 2, [hl]",  "bit 2, a",    # $50 - $57
  284.     "bit 3, b",  "bit 3, c",  "bit 3, d",  "bit 3, e",  "bit 3, h",  "bit 3, l",  "bit 3, [hl]",  "bit 3, a",    # $58 - $5f
  285.     "bit 4, b",  "bit 4, c",  "bit 4, d",  "bit 4, e",  "bit 4, h",  "bit 4, l",  "bit 4, [hl]",  "bit 4, a",    # $60 - $67
  286.     "bit 5, b",  "bit 5, c",  "bit 5, d",  "bit 5, e",  "bit 5, h",  "bit 5, l",  "bit 5, [hl]",  "bit 5, a",    # $68 - $6f
  287.     "bit 6, b",  "bit 6, c",  "bit 6, d",  "bit 6, e",  "bit 6, h",  "bit 6, l",  "bit 6, [hl]",  "bit 6, a",    # $70 - $77
  288.     "bit 7, b",  "bit 7, c",  "bit 7, d",  "bit 7, e",  "bit 7, h",  "bit 7, l",  "bit 7, [hl]",  "bit 7, a",    # $78 - $7f
  289.     "res 0, b",  "res 0, c",  "res 0, d",  "res 0, e",  "res 0, h",  "res 0, l",  "res 0, [hl]",  "res 0, a",    # $80 - $87
  290.     "res 1, b",  "res 1, c",  "res 1, d",  "res 1, e",  "res 1, h",  "res 1, l",  "res 1, [hl]",  "res 1, a",    # $88 - $8f
  291.     "res 2, b",  "res 2, c",  "res 2, d",  "res 2, e",  "res 2, h",  "res 2, l",  "res 2, [hl]",  "res 2, a",    # $90 - $97
  292.     "res 3, b",  "res 3, c",  "res 3, d",  "res 3, e",  "res 3, h",  "res 3, l",  "res 3, [hl]",  "res 3, a",    # $98 - $9f
  293.     "res 4, b",  "res 4, c",  "res 4, d",  "res 4, e",  "res 4, h",  "res 4, l",  "res 4, [hl]",  "res 4, a",    # $a0 - $a7
  294.     "res 5, b",  "res 5, c",  "res 5, d",  "res 5, e",  "res 5, h",  "res 5, l",  "res 5, [hl]",  "res 5, a",    # $a8 - $af
  295.     "res 6, b",  "res 6, c",  "res 6, d",  "res 6, e",  "res 6, h",  "res 6, l",  "res 6, [hl]",  "res 6, a",    # $b0 - $b7
  296.     "res 7, b",  "res 7, c",  "res 7, d",  "res 7, e",  "res 7, h",  "res 7, l",  "res 7, [hl]",  "res 7, a",    # $b8 - $bf
  297.     "set 0, b",  "set 0, c",  "set 0, d",  "set 0, e",  "set 0, h",  "set 0, l",  "set 0, [hl]",  "set 0, a",    # $c0 - $c7
  298.     "set 1, b",  "set 1, c",  "set 1, d",  "set 1, e",  "set 1, h",  "set 1, l",  "set 1, [hl]",  "set 1, a",    # $c8 - $cf
  299.     "set 2, b",  "set 2, c",  "set 2, d",  "set 2, e",  "set 2, h",  "set 2, l",  "set 2, [hl]",  "set 2, a",    # $d0 - $d7
  300.     "set 3, b",  "set 3, c",  "set 3, d",  "set 3, e",  "set 3, h",  "set 3, l",  "set 3, [hl]",  "set 3, a",    # $d8 - $df
  301.     "set 4, b",  "set 4, c",  "set 4, d",  "set 4, e",  "set 4, h",  "set 4, l",  "set 4, [hl]",  "set 4, a",    # $e0 - $e7
  302.     "set 5, b",  "set 5, c",  "set 5, d",  "set 5, e",  "set 5, h",  "set 5, l",  "set 5, [hl]",  "set 5, a",    # $e8 - $ef
  303.     "set 6, b",  "set 6, c",  "set 6, d",  "set 6, e",  "set 6, h",  "set 6, l",  "set 6, [hl]",  "set 6, a",    # $f0 - $f7
  304.     "set 7, b",  "set 7, c",  "set 7, d",  "set 7, e",  "set 7, h",  "set 7, l",  "set 7, [hl]",  "set 7, a"     # $f8 - $ff
  305. ]
  306.  
  307. unconditional_returns = [0xc9, 0xd9]
  308. absolute_jumps = [0xc3, 0xc2, 0xca, 0xd2, 0xda]
  309. call_commands = [0xcd, 0xc4, 0xcc, 0xd4, 0xdc]
  310. relative_jumps = [0x18, 0x20, 0x28, 0x30, 0x38]
  311. unconditional_jumps = [0xc3, 0x18]
  312.  
  313.  
  314. def asm_label(address):
  315.     """
  316.     Return a local label name for asm at <address>.
  317.     """
  318.     return '.asm_%x' % address
  319.  
  320. def data_label(address):
  321.     """
  322.     Return a local label name for data at <address>.
  323.     """
  324.     return '.data_%x' % address
  325.  
  326. def get_local_address(address):
  327.     """
  328.     Return the local address of a rom address.
  329.     """
  330.     bank = address / 0x4000
  331.     address &= 0x3fff
  332.     if bank:
  333.         return address + 0x4000
  334.     return address
  335.  
  336. def get_global_address(address, bank):
  337.     """
  338.     Return the rom address of a local address and bank.
  339.  
  340.     This accounts for a quirk in mbc3 where 0:4000-7fff resolves to 1:4000-7fff.
  341.     """
  342.     if address < 0x8000:
  343.         if address >= 0x4000 and bank > 0:
  344.             return address + (bank - 1) * 0x4000
  345.    
  346.     return address
  347.  
  348. def created_but_unused_labels_exist(byte_labels):
  349.     """
  350.     Check whether a label has been created but not used.
  351.  
  352.     If so, then that means it has to be called or specified later.
  353.     """
  354.     return (False in [label["definition"] for label in byte_labels.values()])
  355.  
  356. def all_byte_labels_are_defined(byte_labels):
  357.     """
  358.     Check whether all labels have already been defined.
  359.     """
  360.     return (False not in [label["definition"] for label in byte_labels.values()])
  361.  
  362. def load_rom(path='baserom.gbc'):
  363.     return bytearray(open(path, 'rb').read())
  364.  
  365. def read_symfile(path='baserom.sym'):
  366.     """
  367.     Return a list of dicts of label data from an rgbds .sym file.
  368.     """
  369.     symbols = []
  370.     for line in open(path):
  371.         line = line.strip().split(';')[0]
  372.         if line:
  373.             bank_address, label = line.split(' ')[:2]
  374.             bank, address = bank_address.split(':')
  375.             symbols += [{
  376.                 'label': label,
  377.                 'bank': int(bank, 16),
  378.                 'address': int(address, 16),
  379.             }]
  380.     return symbols
  381.  
  382. def load_symbols(path):
  383.     sym = {}
  384.     reverse_sym = {}
  385.     wram_sym = {}
  386.     sram_sym = {}
  387.    
  388.     symbols = read_symfile(path)
  389.     for symbol in symbols:
  390.         bank = symbol['bank']
  391.         address = symbol['address']
  392.         label = symbol['label']
  393.        
  394.         if 0x0000 <= address < 0x8000:
  395.             if not sym.has_key(bank):
  396.                 sym[bank] = {}
  397.            
  398.             sym[bank][address] = label
  399.             reverse_sym[label] = get_global_address(address, bank)
  400.            
  401.         elif 0xa000 <= address < 0xc000:
  402.             if not sram_sym.has_key(bank):
  403.                 sram_sym[bank] = {}
  404.                
  405.             sram_sym[bank][address] = label
  406.            
  407.         elif address < 0xe000:
  408.             if not wram_sym.has_key(bank):
  409.                 wram_sym[bank] = {}
  410.            
  411.             wram_sym[bank][address] = label
  412.         else:  
  413.             raise ValueError("Unsupported symfile label type.")
  414.        
  415.     return sym, reverse_sym, wram_sym, sram_sym
  416.  
  417. def get_symbol(sym, address, bank=0):
  418.     if sym:
  419.         if 0x0000 <= address < 0x4000:
  420.             return sym.get(0, {}).get(address)
  421.         else:
  422.             return sym.get(bank, {}).get(address)
  423.    
  424.     return None
  425.  
  426. def get_banked_ram_sym(sym, address):
  427.     #if sym:
  428.     #   if 0xc000 <= address < 0xd000:
  429.     #       return sym.get(0, {}).get(address)
  430.     #   else:
  431.     #       return sym.get(bank, {}).get(address)
  432.     if sym:
  433.         for bank in sym.keys():
  434.             temp_sym = sym.get(bank, {}).get(address)
  435.             if temp_sym:
  436.                 return temp_sym
  437.        
  438.     return None
  439.  
  440. def create_address_comment(offset):
  441.     comment_bank = offset / 0x4000
  442.     if comment_bank != 0:
  443.         comment_bank_addr = (offset % 0x4000) + 0x4000
  444.     else:
  445.         comment_bank_addr = offset
  446.        
  447.     return " ; %x (%x:%x)" % (offset, comment_bank, comment_bank_addr)
  448.            
  449. def offset_is_used(labels, offset):
  450.     if offset in labels.keys():
  451.         return 0 < labels[offset]["usage"]
  452.  
  453. class Disassembler(object):
  454.     """
  455.     GBC disassembler
  456.     """
  457.  
  458.     def __init__(self, config):
  459.         """
  460.         Setup the class instance.
  461.         """
  462.         self.config = config
  463.         self.spacing = '\t'
  464.         self.rom = None
  465.         self.sym = None
  466.         self.rsym = None
  467.         self.gbhw = None
  468.         self.vram = None
  469.         self.sram = None
  470.         self.hram = None
  471.         self.wram = None
  472.  
  473.     def initialize(self, rom, symfile):
  474.         """
  475.         Setup the disassembler.
  476.         """
  477.         path = os.path.join(self.config.path, rom)
  478.         self.rom = load_rom(path)
  479.        
  480.         path = os.path.join(self.config.path, symfile)
  481.         if os.path.exists(path):
  482.             self.sym, self.rsym, self.wram, self.sram = load_symbols(path)
  483.  
  484.         path = os.path.join(self.config.path, 'gbhw.asm')
  485.        
  486.         if os.path.exists(path):
  487.             self.gbhw = read_constants(path)
  488.         else:
  489.             path = os.path.join(self.config.path, "constants/hardware_constants.asm")
  490.             if os.path.exists(path):
  491.                 self.gbhw = read_constants(path)
  492.        
  493.         path = os.path.join(self.config.path, 'vram.asm')
  494.         if os.path.exists(path):
  495.             self.vram = read_constants(path)
  496.            
  497.         path = os.path.join(self.config.path, 'hram.asm')
  498.         if os.path.exists(path):
  499.             self.hram = read_constants(path)
  500.  
  501.     def find_label(self, address, bank=0):
  502.         if type(address) is str:
  503.             address = int(address.replace('$', '0x'), 16)
  504.         elif address is None:
  505.             return address
  506.        
  507.         if 0x0000 <= address < 0x8000:
  508.             label = self.get_symbol(address, bank)
  509.         elif address < 0xa000 and self.vram:
  510.             label = self.vram.get(address)
  511.         elif address < 0xc000:
  512.             label = self.get_sram(address)
  513.         elif address < 0xe000:
  514.             label = self.get_wram(address)
  515.         elif ((0xff00 <= address < 0xff80) or (address == 0xffff)) and self.gbhw:
  516.             label = self.gbhw.get(address)
  517.         elif (0xff80 <= address < 0xffff) and self.hram:
  518.             label = self.hram.get(address)
  519.         else:
  520.             label = None
  521.            
  522.         return label
  523.  
  524.     def get_symbol(self, address, bank):
  525.         symbol = get_symbol(self.sym, address, bank)
  526.         if symbol == 'NULL' and address == 0 and bank == 0:
  527.             return None
  528.         return symbol
  529.  
  530.     def get_wram(self, address):
  531.         symbol = get_banked_ram_sym(self.wram, address)
  532.         if symbol == 'NULL' and address == 0:
  533.             return None
  534.         return symbol
  535.        
  536.     def get_sram(self, address):
  537.         symbol = get_banked_ram_sym(self.sram, address)
  538.         if symbol == 'NULL' and address == 0:
  539.             return None
  540.         return symbol
  541.        
  542.     def find_address_from_label(self, label):
  543.         if self.rsym:
  544.             return self.rsym.get(label)
  545.        
  546.         return None
  547.  
  548.     def output_bank_opcodes(self, start_offset, stop_offset, hard_stop=False, parse_data=False, include_last_address=True):
  549.         """
  550.         Output bank opcodes.
  551.  
  552.         fs = current_address
  553.         b = bank_byte
  554.         in = input_data  -- rom
  555.         bank_size = byte_count
  556.         i = offset
  557.         ad = end_address
  558.         a, oa = current_byte_number
  559.  
  560.         stop_at can be used to supply a list of addresses to not disassemble
  561.         over. This is useful if you know in advance that there are a lot of
  562.         fall-throughs.
  563.         """
  564.        
  565.         debug = False
  566.                
  567.         bank_id = start_offset / 0x4000
  568.        
  569.         stop_offset_undefined = False
  570.        
  571.         # check if stop_offset isn't defined
  572.         if stop_offset is None:
  573.             stop_offset_undefined = True
  574.             # stop at the end of the current bank if stop_offset is not defined
  575.             stop_offset = (bank_id + 1) * 0x4000 - 1
  576.    
  577.         if debug:
  578.             print "bank id is: " + str(bank_id)
  579.  
  580.         rom = self.rom
  581.  
  582.         offset = start_offset
  583.         current_byte_number = 0 #start from the beginning      
  584.  
  585.         byte_labels = {}
  586.         data_tables = {}
  587.        
  588.         output = "Func_%x:%s\n" % (start_offset,create_address_comment(start_offset))
  589.         is_data = False
  590.        
  591.         while True:
  592.             #first check if this byte already has a label
  593.             #if it does, use the label
  594.             #if not, generate a new label
  595.            
  596.             local_offset = get_local_address(offset)
  597.            
  598.             data_label_used = offset_is_used(data_tables, local_offset)
  599.             byte_label_used = offset_is_used(byte_labels, local_offset)
  600.             data_label_created = local_offset in data_tables.keys()
  601.             byte_label_created = local_offset in byte_labels.keys()
  602.            
  603.             if byte_label_created:
  604.             # if a byte label exists, remove any significance if there is a data label that exists
  605.                 if data_label_created:
  606.                     data_line_label = data_tables[local_offset]["name"]
  607.                     data_tables[local_offset]["usage"] = 0
  608.                 else:
  609.                     data_line_label = data_label(offset)
  610.                     data_tables[local_offset] = {}
  611.                     data_tables[local_offset]["name"] = data_line_label
  612.                     data_tables[local_offset]["usage"] = 0
  613.                    
  614.                 line_label = byte_labels[local_offset]["name"]
  615.                 byte_labels[local_offset]["usage"] += 1
  616.                 output += "\n"
  617.             elif data_label_created and parse_data:
  618.             # go add usage to a data label if it exists
  619.                 data_line_label = data_tables[local_offset]["name"]
  620.                 data_tables[local_offset]["usage"] += 1
  621.                
  622.                 line_label = asm_label(offset)
  623.                 byte_labels[local_offset] = {}
  624.                 byte_labels[local_offset]["name"] = line_label
  625.                 byte_labels[local_offset]["usage"] = 0
  626.                 output += "\n"
  627.             else:
  628.             # create both a data and byte label if neither exist
  629.                 data_line_label = data_label(offset)
  630.                 data_tables[local_offset] = {}
  631.                 data_tables[local_offset]["name"] = data_line_label
  632.                 data_tables[local_offset]["usage"] = 0
  633.                    
  634.                 line_label = asm_label(offset)
  635.                 byte_labels[local_offset] = {}
  636.                 byte_labels[local_offset]["name"] = line_label
  637.                 byte_labels[local_offset]["usage"] = 0
  638.  
  639.             # any labels created not above are now used, so mark them as "defined"
  640.             byte_labels[local_offset]["definition"] = True
  641.             data_tables[local_offset]["definition"] = True
  642.            
  643.             # for now, output the byte and data labels (unused labels will be removed later
  644.             output += line_label + "\n" + data_line_label + "\n"
  645.            
  646.             # get the current byte
  647.             opcode_byte = rom[offset]
  648.            
  649.             # process the current byte if this is code or parse data has not been set
  650.             if not is_data or not parse_data:
  651.                 # fetch the opcode string from a predefined table
  652.                 opcode_str = z80_table[opcode_byte][0]
  653.                 # fetch the number of arguments
  654.                 opcode_nargs = z80_table[opcode_byte][1]
  655.                 # get opcode arguments in advance (may not be used)
  656.                 opcode_arg_1 = rom[offset+1]
  657.                 opcode_arg_2 = rom[offset+2]
  658.                
  659.                 if opcode_nargs == 0:
  660.                 # set output string simply as the opcode
  661.                     opcode_output_str = opcode_str
  662.                
  663.                 elif opcode_nargs == 1:
  664.                 # opcodes with 1 argument
  665.                     if opcode_byte != 0xcb: # bit opcodes are handled separately
  666.                        
  667.                         if opcode_byte in relative_jumps:
  668.                         # if the current opcode is a relative jump, generate a label for the address we're jumping to
  669.                             # get the address of the location to jump to
  670.                             target_address = offset + 2 + c_int8(opcode_arg_1).value
  671.                             # get the local address to use as a key for byte_labels and data_tables
  672.                             local_target_address = get_local_address(target_address)
  673.                            
  674.                             if local_target_address in byte_labels.keys():
  675.                             # if the label has already been created, increase the usage and set output to the already created label
  676.                                 byte_labels[local_target_address]["usage"] += 1
  677.                                 opcode_output_str = byte_labels[local_target_address]["name"]
  678.                             elif target_address < start_offset:
  679.                             # if we're jumping to an address that is located before the start offset, assume it is a function
  680.                                 opcode_output_str = "Func_%x" % target_address
  681.                             else:
  682.                             # create a new label
  683.                                 opcode_output_str = asm_label(target_address)
  684.                                 byte_labels[local_target_address] = {}
  685.                                 byte_labels[local_target_address]["name"] = opcode_output_str
  686.                                 # we know the label is used once, so set the usage to 1
  687.                                 byte_labels[local_target_address]["usage"] = 1
  688.                                 # since the label has not been output yet, mark it as "not defined"
  689.                                 byte_labels[local_target_address]["definition"] = False
  690.                            
  691.                             # check if the target address conflicts with any data labels
  692.                             if local_target_address in data_tables.keys():
  693.                                 # if so, remove any instances of it being used and set it as defined
  694.                                 data_tables[local_target_address]["usage"] = 0
  695.                                 data_tables[local_target_address]["definition"] = True
  696.                            
  697.                             # format the resulting argument into the output string
  698.                             opcode_output_str = opcode_str.format(opcode_output_str)
  699.                            
  700.                             # debug function
  701.                             if created_but_unused_labels_exist(byte_labels) and debug:
  702.                                 output += create_address_comment(offset)
  703.                        
  704.                         elif opcode_byte == 0xe0 or opcode_byte == 0xf0:
  705.                         # handle gameboy hram read/write opcodes
  706.                             # create the address
  707.                             high_ram_address = 0xff00 + opcode_arg_1
  708.                             # search for an hram constant if possible
  709.                             high_ram_label = self.find_label(high_ram_address, bank_id)
  710.                             # if we couldn't find one, default to the address
  711.                             if high_ram_label is None:
  712.                                 high_ram_label = "$%x" % high_ram_address
  713.                            
  714.                             # format the resulting argument into the output string
  715.                             opcode_output_str = opcode_str.format(high_ram_label)
  716.                        
  717.                         else:
  718.                         # if this isn't a relative jump or hram read/write, just format the byte into the opcode string
  719.                             opcode_output_str = opcode_str.format(opcode_arg_1)
  720.                    
  721.                     else:
  722.                     # handle bit opcodes by fetching the opcode from a separate table
  723.                         opcode_output_str = bit_ops_table[opcode_arg_1]
  724.                
  725.                 elif opcode_nargs == 2:
  726.                 # opcodes with a pointer as an argument
  727.                     # format the two arguments into a little endian 16-bit pointer
  728.                     local_target_offset = opcode_arg_2 << 8 | opcode_arg_1
  729.                     # get the global offset of the pointer
  730.                     target_offset = get_global_address(local_target_offset, bank_id)
  731.                     # attempt to look for a matching label
  732.                     target_label = self.find_label(target_offset, bank_id)
  733.                    
  734.                     if opcode_byte in call_commands + absolute_jumps:
  735.                         if target_label is None:
  736.                         # if this is a call or jump opcode and the target label is not defined, create an undocumented label descriptor
  737.                             target_label = "Func_%x" % target_offset
  738.  
  739.                     else:
  740.                     # anything that isn't a call or jump is a load-based command
  741.                         if target_label is None:
  742.                         # handle the case of a label for the current address not existing
  743.                        
  744.                             # first, check if this is a byte label
  745.                             if offset_is_used(byte_labels, local_target_offset):
  746.                                 # fetch the already created byte label
  747.                                 target_label = byte_labels[local_target_offset]["name"]
  748.                                 # prevent this address from being treated as a data label
  749.                                 if local_target_offset in data_tables.keys():
  750.                                     data_tables[local_target_offset]["usage"] = 0
  751.                                 else:
  752.                                     data_tables[local_target_offset] = {}
  753.                                     data_tables[local_target_offset]["name"] = target_label
  754.                                     data_tables[local_target_offset]["usage"] = 0
  755.                                     data_tables[local_target_offset]["definition"] = True
  756.                                    
  757.                             elif local_target_offset >= 0x8000 or not parse_data:
  758.                             # do not create a label if this is a wram label or parse_data is not set
  759.                                 target_label = "$%x" % local_target_offset
  760.                            
  761.                             elif local_target_offset in data_tables.keys():
  762.                             # if the target offset has been created as a data label, increase usage and use the already defined name
  763.                                 data_tables[local_target_offset]["usage"] += 1
  764.                                 target_label = data_tables[local_target_offset]["name"]
  765.                             else:
  766.                             # for now, treat this as a data label, but do not set it as used (will be replaced later if unused)
  767.                                 target_label = data_label(target_offset)
  768.                                 data_tables[local_target_offset] = {}
  769.                                 data_tables[local_target_offset]["name"] = target_label
  770.                                 data_tables[local_target_offset]["usage"] = 0
  771.                                 data_tables[local_target_offset]["definition"] = False
  772.                            
  773.                     # format the label that was created into the opcode string
  774.                     opcode_output_str = opcode_str.format(target_label)
  775.  
  776.                 else:
  777.                     # error checking
  778.                     raise ValueError("Invalid amount of args.")
  779.                
  780.                 # append the formatted opcode output string to the output
  781.                 output += self.spacing + opcode_output_str + "\n" #+ " ; " + hex(offset)
  782.                 # increase the current byte number and offset by the amount of arguments plus 1 (opcode itself)
  783.                 current_byte_number += opcode_nargs + 1
  784.                 offset += opcode_nargs + 1
  785.                
  786.             else:
  787.                 # output a single lined db, using the current byte
  788.                 output += self.spacing + "db ${:02x}\n".format(opcode_byte) #+ " ; " + hex(offset)
  789.                 # manually increment offset and current byte number
  790.                 offset += 1
  791.                 current_byte_number += 1
  792.                 # stop treating the current code as data if we're parsing over a byte label
  793.                 if get_local_address(offset) in byte_labels.keys():
  794.                     is_data = False
  795.            
  796.             # update the local offset
  797.             local_offset = get_local_address(offset)
  798.            
  799.             # stop processing regardless of function end if we've passed the stop offset and the hard stop (dry run) flag is set
  800.             if hard_stop and offset >= stop_offset:
  801.                 break
  802.             # check if this is the end of the function, or we're processing data
  803.             elif (opcode_byte in unconditional_jumps + unconditional_returns) or is_data:
  804.                 # define data if it is located at the current offset
  805.                 if local_offset not in byte_labels.keys() and local_offset in data_tables.keys() and created_but_unused_labels_exist(data_tables) and parse_data:
  806.                     is_data = True
  807.                 #stop reading at a jump, relative jump or return
  808.                 elif all_byte_labels_are_defined(byte_labels) and (offset >= stop_offset or stop_offset_undefined):
  809.                     break
  810.                 # otherwise, add some spacing
  811.                 output += "\n"
  812.        
  813.         # before returning output, we need to clean up some things
  814.        
  815.         # first, clean up on unused byte labels
  816.         for label_line in byte_labels.values():
  817.             if label_line["usage"] == 0:
  818.                 output = output.replace((label_line["name"] + "\n"), "")
  819.        
  820.         # clean up on unused data labels
  821.         # this is slightly trickier to do as arguments for two byte variables use data labels
  822.        
  823.         # create a list of the output lines including the newlines
  824.         output_lines = [e+"\n" for e in output.split("\n") if e != ""]
  825.        
  826.         # go through each label
  827.         for label_addr in data_tables.keys():
  828.             # get the label dict
  829.             label_line = data_tables[label_addr]
  830.             # check if this label is unused
  831.             if label_line["usage"] == 0:
  832.                 # get label name
  833.                 label_name = label_line["name"]
  834.                 # loop over all output lines
  835.                 for i, line in enumerate(output_lines):
  836.                     if line.startswith(label_name):
  837.                     # remove line if it starts with the current label
  838.                         output_lines.pop(i)
  839.                     elif label_name in line:
  840.                     # if the label is used in a load-based opcode, replace it with the raw hex reference
  841.                         output_lines[i] = output_lines[i].replace(label_name, "$%x" % get_local_address(label_addr))
  842.        
  843.         # convert the modified list of lines into a string
  844.         output = "".join(output_lines)
  845.        
  846.         # tone down excessive spacing
  847.         output = output.replace("\n\n\n","\n\n")
  848.  
  849.         # add the offset of the final location
  850.         if include_last_address:
  851.             output += "; " + hex(offset)
  852.        
  853.         return [output, offset, stop_offset, byte_labels, data_tables]
  854.  
  855. def get_raw_addr(addr):
  856.     if addr:
  857.         if ":" in addr:
  858.             addr = addr.split(":")
  859.             addr = int(addr[0], 16)*0x4000+(int(addr[1], 16)%0x4000)
  860.         else:
  861.             label_addr = disasm.find_address_from_label(addr)
  862.             if label_addr:
  863.                 addr = label_addr
  864.             else:
  865.                 addr = int(addr, 16)
  866.    
  867.     return addr
  868.  
  869. if __name__ == "__main__":
  870.     # argument parser
  871.     ap = argparse.ArgumentParser()
  872.     ap.add_argument("-r", dest="rom", default="baserom.gbc")
  873.     ap.add_argument("-o", dest="filename", default="gbz80disasm_output.asm")
  874.     ap.add_argument("-s", dest="symfile")
  875.     ap.add_argument("-dp", "--use-disasm-path", dest="use_disasm_path", action="store_true")
  876.     ap.add_argument("-q", "--quiet", dest="quiet", action="store_true")
  877.     ap.add_argument("-nw", "--no-write", dest="no_write", action="store_true")
  878.     ap.add_argument("-d", "--dry-run", dest="dry_run", action="store_true")
  879.     ap.add_argument("-pd", "--parse_data", dest="parse_data", action="store_true")
  880.     ap.add_argument('offset')
  881.     ap.add_argument('end', nargs='?')
  882.    
  883.     args = ap.parse_args()
  884.     # if symfile is unspecified, use the rom name as the symfile name
  885.     if args.symfile is None:
  886.         args.symfile = args.rom.split(".")[0] + ".sym"
  887.    
  888.     # flag to determine whether to use the extras submodule path for labels/constants or the disassembly path
  889.     if args.use_disasm_path:
  890.         conf = configuration.Config(path=os.path.abspath(os.path.join(os.getcwd(),"../..")))
  891.     else:
  892.         conf = configuration.Config()
  893.    
  894.     # initialize disassembler
  895.     disasm = Disassembler(conf)
  896.     disasm.initialize(args.rom, args.symfile)
  897.    
  898.     # get global address of the start and stop offsets
  899.     start_addr = get_raw_addr(args.offset)
  900.     stop_addr = get_raw_addr(args.end)
  901.    
  902.     # run the disassembler and return the output
  903.     output = disasm.output_bank_opcodes(start_addr,stop_addr,hard_stop=args.dry_run,parse_data=args.parse_data)[0]
  904.    
  905.     # suppress output if quiet flag is set
  906.     if not args.quiet:
  907.         print output
  908.    
  909.     # only write to the output file if the no write flag is unset
  910.     if not args.no_write:
  911.         with open(args.filename, "w") as f:
  912.             f.write(output)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement