#!/usr/bin/env python

# Copyright (c) 2020 queueRAM
# 
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# 
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

import argparse
import re
import sys

def read_file(filepath):
    with open(filepath) as f:
        lines = f.readlines()
        split_lines = [re.split(r'[ ,]+', l.strip().replace('$', '')) for l in lines]
        return split_lines

# jumps and branches with named targets
jumps = ['jal', 'j']
branches = ['beq', 'bgez', 'bgtz', 'blez', 'bltz', 'bne']
jump_branches = jumps + branches
# jumps and branches with delay slots
has_delay_slot = jump_branches + ['jr']

def decode_references(instructions):
    refs = []
    for ins in instructions:
        if ins[3] in jump_branches:
            target = int(ins[-1], 0)
            if target not in refs:
                refs.append(target)
    return refs

def reassemble(args, instructions, refs):
    print('.rsp')
    print('\n.create DATA_FILE, 0x%04X' % 0x0000)
    print('\n.close // DATA_FILE\n')
    print('.create CODE_FILE, 0x%08X\n' % args.base)
    delay_slot = False
    for ins in instructions:
        addr = int(ins[0], 0)
        if (addr & 0xFFFF) in refs:
            print('%s_%08x:' % (args.name, addr))
        sys.stdout.write(' ' * args.indent)
        if delay_slot:
            sys.stdout.write(' ')
            delay_slot = False
        if ins[3] in jumps:
            target = int(ins[-1], 0) | (args.base & 0xFFFF0000)
            ins[-1] = '%s_%08x' % (args.name, target)
        elif ins[3] in branches:
            if ins[3][-1] =='z' and ins[5] == 'zero':
                del ins[5] # remove 'zero' operand from branch
            target = (int(ins[-1], 0) & 0x1FFF) + (args.base & 0xFFFF0000)
            ins[-1] = '%s_%08x' % (args.name, target)
        elif ins[3] == 'vsar': # fixup last operand of vsar
            reg_map = {'ACC_H': 0, 'ACC_M': 1, 'ACC_L': 2}
            reg = ins[4].split(r'[')[0]
            num = reg_map[ins[-1]]
            ins[-1] = '%s[%d]' % (reg, num)
        if ins[3] in has_delay_slot:
            delay_slot = True
        if len(ins) > 4: # with args
            print('%-5s %s' % (ins[3], ', '.join(ins[4:])))
        else:
            print('%s' % ins[3])
    print('\n.close // CODE_FILE')

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('input_file', help="input assembly file generated from `rasm2 -D -e -a rsp -B -o 0x04001000 -f`")
    parser.add_argument('-b', type=int, help="base address of file", dest='base', default=0x04001000)
    parser.add_argument('-i', type=int, help="amount of indentation", dest='indent', default=4)
    parser.add_argument('-n', help="name to prefex labels with", dest='name', default='f3d')
    args = parser.parse_args()

    lines = read_file(args.input_file)
    refs = decode_references(lines)
    reassemble(args, lines, refs)

main()
