#
# Kimptp - converts a variety of file formats to the KIM-1 Papertape format
#
# Author: Carl Myerholtz W9OMS Nov 2020
#
# Usage: KIMPTP.py file.ext (starting address)
#        Outputs a text file: file.ptp
#
# If invoked without arguments it will ask for a filename and if the extension is .bin
#   it will ask for a starting address in hex.
#
# Optimized for use with the Charles Bond CBA65 6502 assembler
#
# Typical usage is to create a makeproject.bat file containing
#   CBA65 project.cba
#   KIMPTP.py project.h65
#
# Then execute the makefile from a CMD window
#
# Consists of four separate fuctions to process each file format
#   binhex(filename, startaddress) converts a binary file with a .bin extension.
#       Also needs a starting address for the binary
#   intelhex(filename) converts an Intel Hex format text file with a .hex extension
#   h65hex(filename) converts a h65 file generated by the Charles Bond CBA65 assembler
#       with a .h65 extension
#   moshex(filename) converts a Moshex file generated by the Charles Bond CBA65 assembler
#       with a .mos extension. The CBA moshex output does not match the KIM-1 papertape
#       exactly. It has an extra byte (00) after the address in each record and an
#       incorrect end record. I do not know if this is an error or if there is really
#       a difference bewteen the moshex format and the KIM-1 papertape format. 
#       This function strips out the extra 00 byte and appends the correct end record.
#       Use with caution with .mos files from other sources
#
# The print() statements that have been commented out will display the text
#   while also generating the output file.
#

#
#-------------------------------------------------------------------------------
# Binary format
#
def binhex(infile, address):
    try:
        fin = open(infile, "rb")
    except:
        print('File not found:')
        exit()
    outfname = infile[:infile.find('.')] + '.ptp'
    fout = open(outfname, 'w')

    binary = bytearray(fin.read())
    length = len(binary)
    fullrec = int(length/24)
    partrec = length - (fullrec *24)
#    address = 0x2000
#    print (length, fullrec, partrec, address)
#
# Generate mostly 24 byte records
#
    index = 0
    linecnt = 0
    while linecnt < fullrec:
        out = ';18' + format(address, '04X')
        bytecnt=0
        chksum=24+(address>>8&0xff)+(address&0xff)
        while bytecnt < 24:
            out = out + format(binary[index], '02X')
            chksum = chksum + binary[index]
            index = index + 1
            bytecnt = bytecnt +1
        out = out + format(chksum, '04X')
#        print(out)
        fout.write(out + '\n')
        address = address + 24
        linecnt = linecnt +1
#
# Generate final partial record if there is one
#
    if partrec > 0:
        out = ';' + format(partrec, '02X') + format(address, '04X')
        bytecnt=0
        chksum=partrec+(address>>8&0xff)+(address&0xff)
        while bytecnt < partrec:
            out = out + format(binary[index], '02X')
            chksum = chksum + binary[index]
            index = index + 1
            bytecnt = bytecnt +1
        out = out + format(chksum, '04X')
#        print(out)
        fout.write(out + '\n')
        linecnt = linecnt +1
#
# Generate end of file record with number of lines
#
    chksum = 0
    index = 0
    out = format(linecnt, '04X')
    data = bytearray.fromhex(out)
    while index < len(data):
        chksum = chksum + data[index]
        index = index + 1
    out = ';00' + out + format(chksum, '04X')  + '\n'
#    print(out)
    fout.write(out)
    fout.close()
#
#-------------------------------------------------------------------------------
#
# Intel Hex format
#
def intelhex(infile):
    try:
        fin = open(infile, "r")
    except:
        print('File not found:')
        exit()
    outfname = infile[:infile.find('.')] + '.ptp'
    fout = open(outfname, 'w')
    linecnt = 0
    for line in fin:
        if line[1:3] == '00':
            break
        line = line[1:]
        line = line[:6] + line[8:]
        linecnt=linecnt+1
        data = bytearray.fromhex(line)
        datalen = len(data) - 1
        out = ';' 
        chksum = 0
        index = 0
        while index < datalen:
            chksum = chksum + data[index]
            out = out + format(data[index], '02X')
            index = index + 1
        out = out + format(chksum, '04X') 
#        print(out)
        fout.write(out + '\n')
    chksum = 0
    index = 0
    out = format(linecnt, '04X')
    data = bytearray.fromhex(out)
    while index < len(data):
        chksum = chksum + data[index]
        index = index + 1
    out = ';00' + out + format(chksum, '04X')  + '\n'
#    print(out)
    fout.write(out)
    fout.close()
#
#------------------------------------------------------------------------------
#
#
def h65hex(infile):
    try:
        fin = open(infile, "r")
    except:
        print('File not found:')
        exit()
    outfname = infile[:infile.find('.')] + '.ptp'
    fout = open(outfname, 'w')
    linecnt = 0
    for line in fin:
        linecnt=linecnt+1
        data = bytearray.fromhex(line)
        datalen = len(data) - 2
        out = ';' + format(datalen, '02X')
        chksum = datalen
        index = 0
        while index < len(data):
            chksum = chksum + data[index]
            out = out + format(data[index], '02X')
            index = index + 1
        out = out + format(chksum, '04X') 
#        print(out)
        fout.write(out + '\n')
    chksum = 0
    index = 0
    out = format(linecnt, '04X')
    data = bytearray.fromhex(out)
    while index < len(data):
        chksum = chksum + data[index]
        index = index + 1
    out = ';00' + out + format(chksum, '04X')
#    print(out)
    fout.write(out + '\n')
    fout.close()
#
#-------------------------------------------------------------------------------
#
# Mos Hex format
#
def moshex(infile):
    try:
        fin = open(infile, "r")
    except:
        print('File not found:')
        exit()
    outfname = infile[:infile.find('.')] + '.ptp'
    fout = open(outfname, 'w')
    linecnt = 0
    for line in fin:
        if line[1:3] == '00':
            break
        out = line[:7] + line[9:] 
#        print(out)
        fout.write(out)
        linecnt = linecnt + 1
    chksum = 0
    index = 0
    out = format(linecnt, '04X')
    data = bytearray.fromhex(out)
    while index < len(data):
        chksum = chksum + data[index]
        index = index + 1
    out = ';00' + out + format(chksum, '04X')  + '\n'
#    print(out)
    fout.write(out)
    fout.close()
#
#------------------------------------------------------------------------------
#
# main code
#
#
import sys

numargs = len(sys.argv)

if numargs > 1:
    fname = sys.argv[1]
else:
    fname = input('Enter file name: ')
fext = fname[fname.find('.')+1:].lower()
if fext == "bin":
    if numargs == 3:
        start = sys.argv[2]
    else:
        start=input('Enter starting address (hex): ')
    address=int(start, 16)
    binhex(fname, address)
elif fext == "hex":
    intelhex(fname)
elif fext == "h65":
    h65hex(fname)
elif fext == "mos":
    moshex(fname)
else:
    print("Unrecognized file extension ", fext)




