sm64

A Super Mario 64 decompilation
Log | Files | Refs | README | LICENSE

rasm2armips.py (3846B)


      1 #!/usr/bin/env python
      2 
      3 # Copyright (c) 2020 queueRAM
      4 # 
      5 # Permission is hereby granted, free of charge, to any person obtaining a copy
      6 # of this software and associated documentation files (the "Software"), to deal
      7 # in the Software without restriction, including without limitation the rights
      8 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      9 # copies of the Software, and to permit persons to whom the Software is
     10 # furnished to do so, subject to the following conditions:
     11 # 
     12 # The above copyright notice and this permission notice shall be included in
     13 # all copies or substantial portions of the Software.
     14 # 
     15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     16 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     17 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     18 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     19 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     20 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     21 # SOFTWARE.
     22 
     23 import argparse
     24 import re
     25 import sys
     26 
     27 def read_file(filepath):
     28     with open(filepath) as f:
     29         lines = f.readlines()
     30         split_lines = [re.split(r'[ ,]+', l.strip().replace('$', '')) for l in lines]
     31         return split_lines
     32 
     33 # jumps and branches with named targets
     34 jumps = ['jal', 'j']
     35 branches = ['beq', 'bgez', 'bgtz', 'blez', 'bltz', 'bne']
     36 jump_branches = jumps + branches
     37 # jumps and branches with delay slots
     38 has_delay_slot = jump_branches + ['jr']
     39 
     40 def decode_references(instructions):
     41     refs = []
     42     for ins in instructions:
     43         if ins[3] in jump_branches:
     44             target = int(ins[-1], 0)
     45             if target not in refs:
     46                 refs.append(target)
     47     return refs
     48 
     49 def reassemble(args, instructions, refs):
     50     print('.rsp')
     51     print('\n.create DATA_FILE, 0x%04X' % 0x0000)
     52     print('\n.close // DATA_FILE\n')
     53     print('.create CODE_FILE, 0x%08X\n' % args.base)
     54     delay_slot = False
     55     for ins in instructions:
     56         addr = int(ins[0], 0)
     57         if (addr & 0xFFFF) in refs:
     58             print('%s_%08x:' % (args.name, addr))
     59         sys.stdout.write(' ' * args.indent)
     60         if delay_slot:
     61             sys.stdout.write(' ')
     62             delay_slot = False
     63         if ins[3] in jumps:
     64             target = int(ins[-1], 0) | (args.base & 0xFFFF0000)
     65             ins[-1] = '%s_%08x' % (args.name, target)
     66         elif ins[3] in branches:
     67             if ins[3][-1] =='z' and ins[5] == 'zero':
     68                 del ins[5] # remove 'zero' operand from branch
     69             target = (int(ins[-1], 0) & 0x1FFF) + (args.base & 0xFFFF0000)
     70             ins[-1] = '%s_%08x' % (args.name, target)
     71         elif ins[3] == 'vsar': # fixup last operand of vsar
     72             reg_map = {'ACC_H': 0, 'ACC_M': 1, 'ACC_L': 2}
     73             reg = ins[4].split(r'[')[0]
     74             num = reg_map[ins[-1]]
     75             ins[-1] = '%s[%d]' % (reg, num)
     76         if ins[3] in has_delay_slot:
     77             delay_slot = True
     78         if len(ins) > 4: # with args
     79             print('%-5s %s' % (ins[3], ', '.join(ins[4:])))
     80         else:
     81             print('%s' % ins[3])
     82     print('\n.close // CODE_FILE')
     83 
     84 def main():
     85     parser = argparse.ArgumentParser()
     86     parser.add_argument('input_file', help="input assembly file generated from `rasm2 -D -e -a rsp -B -o 0x04001000 -f`")
     87     parser.add_argument('-b', type=int, help="base address of file", dest='base', default=0x04001000)
     88     parser.add_argument('-i', type=int, help="amount of indentation", dest='indent', default=4)
     89     parser.add_argument('-n', help="name to prefex labels with", dest='name', default='f3d')
     90     args = parser.parse_args()
     91 
     92     lines = read_file(args.input_file)
     93     refs = decode_references(lines)
     94     reassemble(args, lines, refs)
     95 
     96 main()