#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sat Oct 17 00:28:36 2020
label: asm val1,val2
symbol: equ val
org mem


@author: Kym

Adjusted Sat Jul 20 1&:27:32 2024
to be python3 compatible and directly executable
@author: Paul Warren

"""
import sys, os, time
version="Beta 0.4.3"
# Constants to use to make code more readable
FUNCTION=0
OPCODE=1
ASM_CODE=0
LABEL=0
HEX=1
ADDRESS=1

################
format1="{:04X}:   {:02X}       {:<5s}  {:<s}"                #NOP HALT MOVE ARITH
format2="{:04X}:            {:<5s}  {:<4s}          {:<s}"      #ORG 
format3="{:04X}:   {:02X} {:02X}    {:<5s}  {:<6s} {:<s}"     #LOAD BR
format4="{:04X}:   {:02X} {:02X} {:02X} {:<5s}  {:<6s} {:<s}" #LOAD BR
format5="                 {:5s}  {:<8s}  {:02X}H {:<s}"         #Const
format6="                 {:5s}  {:<8s}  {:<4s} {:<s}"         #Const

##########################
def isHex(s):
    if s[-1].upper()=="H" and all(c.upper() in "0123456789ABCDEF" for c in s[:-1]):
        return True
    else:
        return False
######################################
#String, decimal or hex, to binary int
def getValue(s):
    if s[-1].upper()=="H":
        return int(s[:-1],16)
    else:
        return int(s)
########################################################################        
#Alternative getValue that allows symbols constants and +/- Expressions
def gv(s):
    global errors
    #Given an arithmetic expression of Hex,decimal,symbols and constants 
    #separeted by "+" or "-" operator (No white spaces) compute the value
    #of the expression.
    tokens=[]   #list of [ Const|symbol|hex|dec , sign]
    val=0       #Holds final value of expression
    #Split string into items that begin with a +ve
    pTokens=s.split("+")
    for pToken in pTokens:
        #scan each +ve for a -ve, first item is +ve and following will be -ve
        if "-" in pToken:  #does this pToken contain a -ve inside it
            nTokens=pToken.split("-")
            #first is +ve or null anny others will bel -ve
            if len(nTokens[0])>0 : tokens.append([nTokens[0],1]) #ignore null
            #add in the -ve tokens
            for nToken in nTokens[1:]:
                if len(nToken)>0:  tokens.append([nToken,-1])
        else: #No -ve so its a single +ve token
            tokens.append([pToken,1])
    #loop through tokens find value from symbols, constants, hex, decimal 
    # t[0] is the value and t[1] is its sign (+/-1)
    for t in tokens:
        if t[0] in symbols:           #Check symbol
            val=val+t[1]*symbols[t[0]]
        elif t[0] in constants:       #Check constants
            val=val+t[1]*constants[t[0]]
        elif isHex(t[0]):             #hex value   
            val=val+t[1]*int(t[0][:-1],16)
        elif t[0].isdigit():          #decimal integer
            val=val+t[1]*int(t[0])
        elif t[0] in labels:          #Finaly see if it's a label
            val=val+t[1]*labels[t[0]]
        elif t[0]+":" in labels:      #Finaly see if it's a label without :
            val=val+t[1]*labels[t[0]+":"]
        else:
            print("ERROR>>>> cannot evaluate ->",s)
            lstFile.write("ERROR>>>> cannot evaluate ->"+s+"\n")
            errors = errors+1
    return val
            
         
##########################
#Set memory address        
def do_org(p1=False):
   global mem,line
   #mem=getValue(line[HEX])
   mem=gv(line[HEX])
#   mem=int(line[1],16)
   if not p1:
       print(format2.format(mem,line[ASM_CODE],line[HEX],line[-1]))
       lstFile.write(format2.format(mem,line[ASM_CODE],line[HEX],line[-1])+"\n")   
       make_hex(None)  #use None to start new address
   return
##########################
#define a symbolic constant
def do_const(p1=False):
    global line,constants,errors
    #val=getValue(line[2])
    val=gv(line[2])
    if val>255 or val<0:
        print("error>>>"+line[1]+" Constant value out of range 0:255")
        lstFile.write("Error>>>"+line[1]+" Constant value out of range 0:255"+"\n")
        errors = errors + 1
    if p1:   #definitions only occur on pass1
        if line[1] in constants:
            #Duplicate constant name. Error only occurs during pass1
            print("Error>>>  Duplicate constant name")
            lstFile.write("Error>>>  Duplicate constant name"+"\n")
            errors = errors + 1
        else:    
            constants[line[1]]=val  
    if not p1:
        print(format6.format(line[0],line[1],line[2],line[-1]))
        lstFile.write(format6.format(line[0],line[1],line[2],line[-1])+"\n") 
    else:
        print("Constant-> {:>8s} {:>02X}H ({:})".format(line[1],val,val))
        lstFile.write("Constant-> {:>8s} {:>02X}H ({:})".format(line[1],val,val)+"\n") 
        
##########################
#define a symbolic variable (16bit location)
def do_def(p1=False):
    global mem,line,symbols,errors
    #check that memoryy size is of the form *nnn
    if line[2][0]=="*" and line[2][1:].isdigit():
       numBytes=int(line[2][1:])
    else:
        if p1:
            print("Error>>>>> bad size",line[2])
            print(l)
            lstFile.write("Error>>>>> bad size",line[2]+"\n")
            lstFile.write(l+"\n")
            errors = errors + 1
        return
    val=mem
    if p1: #Symbol definition occur on pass1
        if line[1] in symbols:
            #Duplicate symbol name. Only occurs in pass1
            print("Error>>>"+line[1]+"  Duplicate constant name")
            lstFile.write("Error>>>  Duplicate constant name"+"\n")
            errors = errors + 1
        else:    
            symbols[line[1]]=val  
    if not p1:
        print(format6.format(line[0],line[1],line[2],line[-1]))
        lstFile.write(format6.format(line[0],line[1],line[2],line[-1])+"\n") 
    else:
        print("Symbol->   {:>8s} {:>04X}H ({:})".format(line[1],val,val))
        lstFile.write("Symbol->   {:>8s} {:>04X}H ({:})".format(line[1],val,val)+"\n")         
    mem=mem+numBytes
    
####################################    
# SYS commands NOP, HALT
def do_sys():
    global mem,line
    code=asm[line[ASM_CODE].upper()][OPCODE]
    make_hex(code)
    print(format1.format(mem,code,line[ASM_CODE],line[-1]))
    lstFile.write(format1.format(mem,code,line[ASM_CODE],line[-1])+"\n")
    mem = mem +1
##########################
#Write a full line comment to the lst file
def do_comment():
    global l
    print(l.strip("\n"))
    lstFile.write(l.strip("\n")+"\n")
##########################
# Load immediat commands
def do_loadI():
    global mem,line,constants,errors
    #LDIx
    code=asm[line[ASM_CODE].upper()][OPCODE]
    make_hex(code)
#    #Check to see if the immediate value is in the constants dictionary
#    if line[HEX] in constants:
#        val=constants[line[HEX]]
#    else:
#        val=getValue(line[HEX])
    #IF indirect arg ends in :L or :H then that indicates use Hi or Lo byte of val
    if line[HEX][-2:]==":H" or line[HEX][-2:]==":L":
        val=gv(line[HEX][:-2])
        if line[HEX][-1]=="H":
            val,lo=divmod(val,256)
        else:
            hi,val=divmod(val,256)
    else:
        val=gv(line[HEX])
    if val>255 or val<0:
        print("error>>> Immediate value out of range 0:255")
        lstFile.write("Error>>> Immediate value out of range 0:255"+"\n")
        errors = errors + 1
    # line[HEX] is the value for immediate addressing in ascii hex (8bits)
    print(format3.format(mem,code,val,line[ASM_CODE],line[HEX],line[-1]))
    lstFile.write(format3.format(mem,code,val,line[ASM_CODE],line[HEX],line[-1])+"\n")
    mem = mem + 1
    make_hex(val)
    mem = mem +1
##########################
# Load immediat commands
def do_loadA():
    global mem,line
    #LDIx
    code=asm[line[ASM_CODE].upper()][OPCODE]
    make_hex(code)
    #val=getValue(line[ADDRESS])
    # line[ADDRESS] is a 2 byte address or symbol
#    if line[ADDRESS] in symbols:
#        val=symbols[line[ADDRESS]]
#    elif line[ADDRESS] in labels:   #Check predefined sysmbols
#        val=labels[line[ADDRESS]]
#    elif (line[1][-1] == "H") or (line[1].isdigit()): #hex or decimal address
#        val=getValue(line[ADDRESS])  
#    else:
#        print "error>>>>>",l
#        lstFile.write("error>>>>>"+l+"\n")
#        return
    val=gv(line[ADDRESS])
        #Split high and low bytes
    hi,lo=divmod(val,256)
    print(format4.format(mem,code,hi,lo,line[ASM_CODE],line[HEX],line[-1]))
    lstFile.write(format4.format(mem,code,hi,lo,line[ASM_CODE],line[HEX],line[-1])+"\n")
    mem = mem + 1
    make_hex(hi)
    mem = mem + 1
    make_hex(lo)
    mem = mem +1
##########################
#Move Register commands
def do_move():
    global mem,line
    #MOVxy
    code=asm[line[ASM_CODE].upper()][OPCODE]
    make_hex(code)
    print(format1.format(mem,code,line[ASM_CODE],line[-1]))
    lstFile.write(format1.format(mem,code,line[ASM_CODE],line[-1])+"\n")
    mem = mem + 1
##########################
#ALU commands
def do_arith():
    global mem,line
    #MOVxy
    code=asm[line[ASM_CODE].upper()][OPCODE]
    make_hex(code)
    print(format1.format(mem,code,line[ASM_CODE],line[-1]))
    lstFile.write(format1.format(mem,code,line[ASM_CODE],line[-1])+"\n")
    mem = mem + 1
##########################
#Branch commands
def do_branch():
    global mem,line,l,errors
    #LDIx
    code=asm[line[ASM_CODE].upper()][OPCODE]
    make_hex(code)
    # line[ADDRESS] is a 2 byte address or symbol
    if line[1] in labels:   #Check predefined sysmbols
        val=labels[line[ADDRESS]]
    elif (line[1][-1] == "H") or (line[1].isdigit()): #hex or decimal address
        val=getValue(line[ADDRESS])  
    else:
        print("Error>>>>>",l)
        lstFile.write("Error>>>>>"+l+"\n")
        errors = errors + 1
        return
    #Split high and low bytes
    hi,lo=divmod(val,256)
    print(format4.format(mem,code,hi,lo,line[ASM_CODE],line[ADDRESS],line[-1]))
    lstFile.write(format4.format(mem,code,hi,lo,line[ASM_CODE],line[ADDRESS],line[-1])+"\n")
    mem = mem + 1
    make_hex(hi)
    mem = mem + 1
    make_hex(lo)
    mem = mem + 1
##########################
#Add hex code for h to hex list
"""
Hex list is of the form
[AAAA:, xx, xx, xx, xx, xx, xx, xx, xx,
 AAAA:, xx, xx, xx,
 BBBB:, xx, xx, xx, xx, xx, xx, xx, xx,
]
"""    
def make_hex(h):
    #h=None forces new address to hex list, use hexoffset to sync 8 bytes per line
    global hex,mem,hexOffset
    if ((len(hex)-hexOffset) % 9 == 0) or (h==None):
        hex.append("{:04X}: ".format(mem))
        if h==None:
            hexOffset = (len(hex) % 9)-1
            return
    hex.append("{:02X} ".format(h))
    return

################################################################
def pass1(srcFile):
    global mem,labels,l,line,errors
    print(">>>>> Starting pass1...")
    lstFile.write(">>>>> Starting pass1...\n")
    for l in srcFile:
        #Check for a line of white spaces
        if len(l.strip())==0:  #A line of white spaces
            continue
   
        #fudge whole line comment character so that it has a space after it
        #The comment character is treated as a Mnemonic and needs a space
        start=l.strip()
        if start[0] == Comment :
           continue    
        #break source line (l) into a list of tokens (words)
        #EG "    LDIA 05H" -> ["LDIA","05H"]
        line=l.split() 
    
        #check for lable or symbol definition
    #check for lable or symbol definition
        if ":" in line[LABEL]:
            if line[LABEL] in labels:
                #Duplicate label name
                print("Error>>>  Duplicate label name")
                lstFile.write("Error>>>  Duplicate label name"+"\n")
                errors = errors + 1
            else:    
                labels[line[LABEL]]=mem  
            print("Label->     {:>7s} {:04X}H".format(line[LABEL], mem))
            lstFile.write("Label->     {:>7s} {:04X}H".format(line[LABEL], mem)+"\n")
            continue
    
        #must be asm code
        if line[ASM_CODE].upper() in asm:
            if line[ASM_CODE].upper() in ["ORG","EQU","DEF"]:
                asm[line[ASM_CODE].upper()][FUNCTION](p1=True)
            else:
                mem=mem+asm[line[ASM_CODE].upper()][2]
    print("<<<<< end of pass1")  
    lstFile.write("<<<<< end of pass1\n")
    
####################################################################    
"""
Use the asm dictionary to store assembler directives
asm["ADD"] will return the list [do_arith,0b00100010]
asm["ADD"][0]() calls the function do_arith()
asm["ADD"][1] returns the opcode value 0b00100010 in decimal
asm["ADD"][2] returns the number of bytes used by the instruction
alternativly:
asm["ADD"][FUNCTION]() calls the function do_arith()
asm["ADD"][OPCODE] returns the opcode value 0b00100010 in decimal
"""   
# Mnemonics either use 0,1,2 3bytes of memory
Comment="\\" 
asm={"ORG":   [do_org,None,0],
     "EQU": [do_const,None,0],             #Define a symbolic constant (8bit)
     "DEF": [do_def,None,0],               #Define a symbolice address (16Bit)
      Comment:[do_comment,None,0],
     "NOP":   [do_sys,0b00000000,1],
     "HALT":  [do_sys,0b00001000,1],
     "WAIT":  [do_sys,0b00001100,1],
     "RESET":  [do_sys,0b00000100,1],
     "ADD":   [do_arith,0b00100010,1],   #A+B -> C
     "AND":   [do_arith,0b00100110,1],   #A&B -> C 
     "NOTA,B":  [do_arith,0b00101001,1],   #Not(A) -> B
     "NOTA,C":  [do_arith,0b00101010,1],   #Not(A) -> C
     "INCB,A":  [do_arith,0b00101100,1],   #Inc(B) -> A
     "INCB,C":  [do_arith,0b00101110,1],   #Inc(B) -> C
     "ROLB,A":  [do_arith,0b00110100,1],   #RoL(B) ->A
     "ROLB,C":  [do_arith,0b00110110,1],   #ROL(B) ->C
     "RORA,B":  [do_arith,0b00110001,1],   #ROR(A) ->B
     "RORA,C":  [do_arith,0b00110010,1],   #ROR(A) ->C
#     "LDIA":  [do_loadI,0b10011000],
#     "LDIB":  [do_loadI,0b10011001],
#     "LDIC":  [do_loadI,0b10011010],
#     "LDAA":  [do_loadA,0b10100000],
#     "LDAB":  [do_loadA,0b10100001],
#     "LDAC":  [do_loadA,0b10100010],
     "LDIA":  [do_loadI,0b10011000,2],
     "LDIB":  [do_loadI,0b10011001,2],
     "LDIC":  [do_loadI,0b10011010,2],
     "LDAA":  [do_loadA,0b10100000,3],
     "LDAB":  [do_loadA,0b10100001,3],
     "LDAC":  [do_loadA,0b10100010,3],
     "STAA":  [do_loadA,0b10000100,3],
     "STAB":  [do_loadA,0b10001100,3],
     "STAC":  [do_loadA,0b10010100,3],
     "STAZ":  [do_loadA,0b10111100,3],
     "LDDA":  [do_move,0b10101000,1],
     "LDDB":  [do_move,0b10101001,1],
     "LDDC":  [do_move,0b10101010,1],
     "STDA":  [do_move,0b10000101,1],
     "STDB":  [do_move,0b10001101,1],
     "STDC":  [do_move,0b10010101,1],
#     "CLRM":  [do_loadA,0b10100101,3],
#     "CLRM":  [do_loadA,0b10101010,3],
     "MOVA,ML":[do_move,0b11000100,1],
     "MOVB,ML":[do_move,0b11001100,1],
     "MOVC,ML":[do_move,0b11010100,1],
     "MOVA,MH":[do_move,0b11000011,1],
     "MOVB,MH":[do_move,0b11001011,1],
     "MOVC,MH":[do_move,0b11010011,1],
     "MOVM,PC":[do_move,0b11011110,1], 
     "MOVAD+,PC":[do_move,0b11101110,1],  #would result in M+1 into AD+
     "MOVPC,AD+":[do_move,0b11110101,1], #PC+1 -> AD+
     "MOVAD+,PC":[do_move,0b11101100,1],
#     "MOVAB": [do_move,0b11000001],
#     "MOVAC": [do_move,0b11000010],
#     "MOVBA": [do_move,0b11001000],
#     "MOVBC": [do_move,0b11001010],
#     "MOVCA": [do_move,0b11010000],
#     "MOVCB": [do_move,0b11010001],
     "MOVA,B": [do_move,0b11000001,1],
     "MOVA,C": [do_move,0b11000010,1],
     "MOVB,A": [do_move,0b11001000,1],
     "MOVB,C": [do_move,0b11001010,1],
     "MOVC,A": [do_move,0b11010000,1],
     "MOVC,B": [do_move,0b11010001,1],
     "MOVZ,A":  [do_move,0b11111000,1],
     "MOVZ,B":  [do_move,0b11111001,1],
     "MOVZ,C":  [do_move,0b11111010,1],
     "MOVZ,MH": [do_move,0b11111011,1],
     "MOVZ,ML": [do_move,0b11111100,1],
     "BRZ":   [do_branch,0b01000000,3],
     "BRP":   [do_branch,0b01000100,3],
     "BRN":   [do_branch,0b01001000,3],
     "BRNZ":  [do_branch,0b01001100,3],
     "BRNP":  [do_branch,0b01010000,3],
     "BRNN":  [do_branch,0b01010100,3],
     "BRO":   [do_branch,0b01011000,3],
     "BRNO":  [do_branch,0b01011100,3],
     "BRA":   [do_branch,0b01100000,3]
    }
#Initial memory address, updated using the "org" directive    
mem=0x0000
# list containing the hex output ofthe assembler
hex=[]
#used to sync 8 bytes of code per "line" after using the "org" directive
hexOffset=0 #used to get 8 bytes after address in Hex list after multiple uses or org
#Dictionary to store symbolic address labels/values
labels={}
#Dictionary to store symbolic address symbols/values
symbols={}
#Dictionary to store symbolic constants
constants={}
#Count errors
errors=0
#simple test sourcecode
prgm=r"""
\This is a comment
\Define a constant
    EQU START 05H
    EQU LENG  10
    EQU END   START+LENG

\Define some variables in RAM
ORG 4000H          \\Ram at 4000H
    DEF STACK  *10     \\Holds 5 addresses
    DEF stkPtr *2      \\Pointer into stack

\Beginning of code
\
  \Space prefixed string
  \Tab prefixed string
  \ Space after \ in string
org 0100H
    nop
    NOP
    STAZ stkPtr
    STAZ stkPtr+1
    MOVZ,A
    STDA
    LDDA
org 50          \\using decimal org
    LDIB FFH
    LDIB START+3    
    LDIA START
    LDIA STACK:H
    LDIA STACK:L
    LDIA STACK+3:L
    LDIA STACK+3:H
L1:
    ADD
    MOVC,A
    BRNZ L1:    \\ Test trailing comment
    nop
    nop
    LDAA 0002H  \\ Address in Hex
    LDAA L1:    \\ Address as a label
    LDAB stkPtr \\ Address as a symbol
    INCB,C
    STAC stkPtr
    STAC stkPtr+START-2
    LDAA stkPtr+1
    ROLB,C
    LDIA RET1:H
    MOVA,MH
    LDIA RET1:L
    MOVA,ML
    MOVM,PC
RET1:    
    halt
\ End of program
"""
#Set test true to run in spyder ide using the above prgm list to tst the code
test=False
#test=True

if not test:
    failed=False
    if len(sys.argv)<2:
        print("Assembler source file required (xxxx.asm)")
        failed=True
    else:
        if sys.argv[1]=="--help":
            sortedASM=sorted(list(asm.items()),key=lambda x: x[0], reverse=False)
            print("Relay Computer Assembler: Ver. "+version,"\n")
            print("Valid assembler codes:\n")
            for i,j in sortedASM:
                if j[1]==None:
                   print("{:<8s}:".format(i))
                else:
                   print("{:<8s}: {:02x}  {:08b}".format(i,j[1],j[1]))
#            sys.exit(0)
            test=True
            print("\n\n Example assembly \n\n")
            name,ext="test","asm"
            srcFile=prgm.split("\n")
        else: 
            fileName=sys.argv[1]
            name,ext = os.path.splitext(fileName)
            if ext != ".asm":
                print("Input file needs extension '.asm'")
                failed=True
    if failed:
        sys.exit()
else: #Running in test mode
    name,ext="test","asm"
    srcFile=prgm.split("\n")
hexFileName=name+".hex"
lstFileName=name+".lst"
#print name,ext,hexFileName,lstFileName
#time.sleep(10)
#exit()

if not test:
   srcFile=open(fileName,mode="r")
lstFile=open(lstFileName,mode="w")
if test:
    lstFile.write(">>>>> Running in test mode <<<<<<<\n")

pass1(srcFile)
if not test:
    srcFile.seek(0)
    
#loop through each line of the source code pass2
for l in srcFile:
    #Check for a line of white spaces
    if len(l.strip())==0:  #A line of white spaces
        print()
        lstFile.write("\n")
        continue

    #fudge whole line comment character so that it has a space after it
    #The comment character is treated as a Mnemonic and needs a space
    start=l.strip()
    if start[0] == Comment :
       if len(start)>1:
          if start[1] != " ":
             l=l.replace(Comment,Comment+" ",1)
       else:
          start = start + " "

    #break source line (l) into a list of tokens (words)
    #EG "    LDIA 05H" -> ["LDIA","05H"]
    line=l.split() 

    #check for lable or symbol definition
    if ":" in line[LABEL]:
        if line[LABEL] in labels:
            #Duplicate label name
            pass
            #print "Error>>>  Duplicate label name"
            #lstFile.write("Error>>>  Duplicate label name"+"\n")
        else:    
            labels[line[LABEL]]=mem  
        print("    "+line[LABEL])
        lstFile.write("    "+line[LABEL]+"\n")
        continue

    #Check for a trailing comment Append it to the line list
    if line[0][0] != Comment:    
        for C in line[1:]:  #Check each token to see if its the beginning of a comment
            if C[0]==Comment:
                line.append(l[l.rfind(Comment):].strip("\n"))
                break
        else:
            line.append("") #No trailing comment so make it Null
    #must be asm code
    if line[ASM_CODE].upper() in asm:
        asm[line[ASM_CODE].upper()][FUNCTION]()
    else:
        print(">>>>>>Error<<<<<< -- No Mnemonic:"+line[ASM_CODE]+"\n",l)
        lstFile.write(">>>>>>Error<<<<<< -- No Mnemonic:"+line[ASM_CODE]+"\n"+l+"\n")
        errors=errors+1


#Print listing of Labels
print("\n\nLabels\n======")
lstFile.write("\n\nLabels\n======\n")
sortedLabels = sorted(list(labels.items()), key=lambda x: x[1], reverse=False)
for l in sortedLabels:
    print("{:04X}: {:>9s}".format(l[1],l[0]))
    lstFile.write("{:04X}: {:>9s}".format(l[1],l[0])+"\n")

#Print listing of Symbols (Variables)
print("\nSymbols\n=======")
lstFile.write("\nSymbols\n======\n")
sortedSymbols = sorted(list(symbols.items()), key=lambda x: x[1], reverse=False)
for l in sortedSymbols:
    print("{:>9s}: {:04X}".format(l[0],l[1]))
    lstFile.write("{:>9s}: {:04X}".format(l[0],l[1])+"\n")

#Print listing of Constants
print("\nConstants\n=========")
lstFile.write("\n\nConstants\n=========\n")
sortedConstants = sorted(list(constants.items()), key=lambda x: x[1], reverse=False)
for l in sortedConstants:
    print("{:>9s}: {:02X}".format(l[0],l[1]))
    lstFile.write("{:>9s}: {:02X}".format(l[0],l[1])+"\n")

lstFile.write("\n{}:Errors\n".format(errors))   
lstFile.close()
if not test:
   srcFile.close()

print("\nHex output\n==========")
hexFile=open(hexFileName,mode="w")
skippedFirst=False
for l in hex:
    if (":" in l) and skippedFirst :
        print()
        hexFile.write("\n")
    print(l, end=' ')
    hexFile.write(l)
    skippedFirst=True
print()
print("\n{}:Errors\n".format(errors))
hexFile.write("\n")
hexFile.close()    

    
