#!/usr/bin/python3
# Signetics 8X300, 8X305 disassembler
# Copyright 2016 Eric Smith <spacewar@gmail.com>

# This program is free software: you can redistribute it and/or modify
# it under the terms of version 3 of the GNU General Public License
# as published by the Free Software Foundation.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import argparse
import sys

from s8x30x import S8X30x, CpuType
from intelhex import IntelHex
from memory import Memory
from wd1000 import WD1000
from wd1001 import WD1001



def pass1(s8x30x, fw, base):
    symtab_by_value = {}
    for pc in range(base, base+len(fw)):
        (dis, operands, fields) = s8x30x.disassemble_inst(fw, pc, disassemble_operands = False)
        if 'j' in fields:
            symtab_by_value[fields['j']] = 'x%04x' % fields['j']
    return symtab_by_value


def pass2(s8x30x, fw, base,
          symtab_by_value, show_obj = False, output_file = sys.stdout):
    for pc in range(base, base+len(fw)):
        s = ''
        (dis, operands, fields) = s8x30x.disassemble_inst(fw, pc, symtab_by_value)
        if show_obj:
            s += '%04x: '% pc
            for i in range(len(fw[pc])):
                s += '%02x ' % fw[pc][i]
        if pc in symtab_by_value:
            label = symtab_by_value[pc] + ':'
        else:
            label = ''

        if fast_io_decoder is not None and len(fw[pc]) > 2:
            operands = fast_io_decoder.fast_io_decode(fw[pc][2:], operands)
            
        s += '%-8s%-8s%s' % (label, dis, operands)
        output_file.write(s + '\n')
    

def disassemble(s8x30x, fw, show_obj = False, output_file = sys.stdout,
                base = 0):
    symtab_by_value = pass1(s8x30x, fw, base)
    #symtab_by_name = { v: k for k, v in symtab_by_value.items() }
    pass2(s8x30x, fw, base, symtab_by_value, show_obj = show_obj, output_file = output_file)


# type function for argparse to support numeric arguments in hexadecimal
# ("0x" prefix) as well as decimal (no prefix)
def auto_int(x):
    return int(x, 0)


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description = 'Disassembler for Signetics 8X300/8X305')

    parser.add_argument('-l', '--listing', action='store_true',
                        help = 'generate output in listing format')

    fmt_group = parser.add_mutually_exclusive_group()
    fmt_group.add_argument('--binary', action='store_const',
                           dest='inputformat',
                           const='binary',
                           help = 'input file format is raw binary (default)')
    fmt_group.add_argument('--hex', action='store_const',
                           dest='inputformat',
                           const='hex',
                           help = 'input file format is Intel hex')

    cpu_type_group = parser.add_mutually_exclusive_group()
    cpu_type_group.add_argument('-0', '--8x300',
                                action='store_const',
                                dest='cpu_type',
                                const=CpuType.s8x300,
                                help = '8X300 processor')
    cpu_type_group.add_argument('-5', '--8x305',
                                action='store_const',
                                dest='cpu_type',
                                const=CpuType.s8x305,
                                help = '8X305 processor')
    
    fastio_group = parser.add_mutually_exclusive_group()
    fmt_group.add_argument('--wd1000',
                           action='store_const',
                           dest='fastio',
                           const='wd1000',
                           help = 'decode WD1000 fast I/O select')
    fmt_group.add_argument('--wd1001',
                           action='store_const',
                           dest='fastio',
                           const='wd1001',
                           help = 'decode WD1001 fast I/O select')

    parser.add_argument('-o', '--output', type=argparse.FileType('w'),
                        default = sys.stdout,
                        help = 'disassembly output file')

    parser.add_argument('input',
                        type = argparse.FileType('rb'),
                        nargs = '*',
                        help = 'input file(s), multiple files will be interleaved (useful for separate even, odd files)')

    args = parser.parse_args()
    #print(args)

    if len(args.input) < 2:
        print('Minimum two object files required', file = sys.stderr)
        sys.exit(2)

    if 'cpu_type' not in args:
        args.cpu_type = CpuType.s8x300

    s8x30x = S8X30x(cpu_type = args.cpu_type)

    data = [f.read() for f in args.input]

    if args.inputformat is 'hex':
        data = [IntelHex().read(d) for d in data]

    fast_io_decoder = None
    if args.fastio == 'wd1000':
        fast_io_decoder = WD1000()
    elif args.fastio == 'wd1001':
        fast_io_decoder = WD1001()

    memory = Memory(data)

    disassemble(s8x30x, memory, show_obj = args.listing, output_file = args.output)
