Kitomas

KIT-8 Assembler v1

Mar 24th, 2023 (edited)
217
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 30.43 KB | Software | 0 0
  1. #tbd:
  2. #allow expressions with spaces
  3. if __name__ != "__main__": exit()
  4. setProgCounter0AfterSaving=True
  5. deletePreprocessorOutput=True
  6. printEvalExpressionDebug=False
  7. overwriteOutputFileIfBytesNotEmpty=False
  8. DisallowProgCounterToGoPast16Bit=False
  9. from sys import argv
  10. from os import remove as deleteFile
  11. from os.path import exists
  12. from time import time,sleep
  13. import subprocess
  14. from pprint import pprint,pformat
  15.  
  16. def _clamp(n, mn,mx): return max(mn,min(mx,n))
  17. from math import floor as _floor, ceil as _ceil, sin as _sin, cos as _cos, pi as _pi
  18. def _l(n): return n&255 #get low byte of 16-bit number
  19. def _h(n): return (n>>8)&255 #get high byte of 16-bit number
  20.  
  21. #parse and format an assembly source file
  22. def getSeparated(fileName):
  23.     if not exists(fileName): return None
  24.     file=open(fileName,"r")
  25.     lines=file.read().split("\n")
  26.     file.close()
  27.     #remove comments
  28.     lines=[line.split(";")[0] for line in lines]
  29.     #split by assembler newline thingies (&&, usually used in macros)
  30.     lines=sum([line.split("&&") for line in lines],[])
  31.     #remove leading and trailing whitespace
  32.     lines=[line.strip() for line in lines]
  33.     #separate by commas
  34.     lines=[line.split(",") for line in lines]
  35.     #separate by spaces and labels
  36.     spaceSep,spaceSepI,spaceRem=[],[],0;
  37.     for ii in range(len(lines)):
  38.         i=ii-spaceRem #to account for number of [""]s removed
  39.         if lines[i] == [""]:
  40.             del lines[i]
  41.             spaceRem+=1
  42.             continue
  43.         if lines[i][0] == "":
  44.             return None,"Line {}: Found comma or blank string at start of action".format(i+1)
  45.         spaceSepI=[]
  46.         for e in lines[i]:
  47.             for s in e.strip().split(" "):
  48.                 if len(s)==0: continue #prevents 'index out of range' error
  49.                 if s[0] in [":","."] and s[-1] == ":": spaceSep.append([s])
  50.                 else: spaceSepI.append(s)
  51.         if spaceSepI != []: spaceSep.append(spaceSepI)
  52.     #lines=spaceSep
  53.     #return lines,""
  54.     if spaceSep == []: return None,"Parsing file results in empty list"
  55.     return spaceSep,""
  56.    
  57. def reverseSortDict(d):
  58.     new_d={}
  59.     for k in sorted(d,key=len,reverse=True): new_d[k]=d[k]
  60.     return new_d
  61.  
  62. def adjustAddress(pc):
  63.     if pc <= 0xffff: return pc
  64.     else: return (32768 + pc%32768)
  65.  
  66. outputFileName="out.bin"
  67. progCounter=0x0000
  68. outputBytes=[progCounter]
  69. charset=[i for i in range(256)] #x->y encoding conversion; just have 1:1 mapping initially
  70. #(labels are limited to locations dependant on its position in code)
  71. labels={"nil":[0,{}]}
  72. currentLabel="nil" #current label for which local labels are assigned and compared
  73. localLabels={}
  74.  
  75. #tbd: do charset stuff here possibly
  76. def evalExpression(exp,pc,relativeMode=False):
  77.     try:
  78.         #replace @ with current value of program counter
  79.         evalNum=exp.replace("@",str(adjustAddress(pc)))
  80.         #check for char literal if there's no " on either end of the expression,
  81.         #so you can use ' in strings without getting a mismatch error
  82.         if exp[0] != '"' and exp[-1] != '"':
  83.             #replace char literals (like 'a')
  84.             charLiteralTemp=""; evalNumMax=len(evalNum)-1; ci=0
  85.             while ci<len(evalNum):
  86.                 c=evalNum[ci]
  87.                 if c == "'":
  88.                     ci+=2 #will be +3 in total this iteration
  89.                     if ci>evalNumMax or evalNum[ci] != "'":
  90.                         return None,"Mismatched char literal in expression"
  91.                     charLiteralTemp+=str(ord(evalNum[ci-1]))
  92.                 else: charLiteralTemp+=c
  93.                 ci+=1
  94.             evalNum=charLiteralTemp
  95.         #replace local labels with their values
  96.         for lLabelNameR in localLabels: #first check if the local label exists at all
  97.             lLabelName="."+lLabelNameR.split(".")[-1] #"label.local" to just ".local"
  98.             if lLabelName in exp:
  99.                 if not lLabelName in labels[currentLabel][1]:
  100.                     return None,"local label \"{}{}\" doesn't exist".format(currentLabel,lLabelName)
  101.                 evalNum=evalNum.replace(lLabelName,str(labels[currentLabel][1][lLabelName]))
  102.         #replace labels with their values
  103.         for labelName in labels:
  104.             label=labels[labelName]
  105.             if labelName in exp: evalNum=evalNum.replace(labelName,str(label[0]))
  106.         #do the actual eval
  107.         evalNum=eval(evalNum)
  108.         if type(evalNum) == bool: evalNum=int(evalNum)
  109.         if relativeMode: evalNum-=pc #for bnz
  110.         if printEvalExpressionDebug: print("@="+hex(pc)+': "'+exp+'" = '+str(evalNum))
  111.         return evalNum,"No Error"
  112.     except SyntaxError as syntaxErr:
  113.         #hopefully this only triggers when eval() fails...
  114.         return None,"Failed expression eval for \"{}\" ({})".format(syntaxErr.text,syntaxErr.msg)
  115.     except Exception as uniErr:
  116.         return None,uniErr
  117.  
  118. #toNbitAll() does both signed and unsigned N-bit ints
  119. def to8bitAll(n0):
  120.     try: n=_floor(n0+.5) #round to nearest whole
  121.     except TypeError: return None,"to8bitAll can't accept \"{}\", which is of type \"{}\"".format(n0,type(n0))
  122.     except Exception as err: return None,"to8bitAll error: "+err
  123.     if n< -128: return None,"Can't convert number \"{}\" below -128 to 8 bits".format(n)
  124.     if n>  255: return None,"Can't convert number \"{}\" above 255 to 8 bits".format(n)
  125.     if n<0: n+=256 #two's complement representation
  126.     return n.to_bytes(1,"little"),""
  127. def to16bitAll(n0):
  128.     try: n=_floor(n0+.5) #round to nearest whole
  129.     except TypeError: return None,"to16bitAll can't accept \"{}\", which is of type \"{}\"".format(n0,type(n0))
  130.     except Exception as err: return None,"to16bitAll error: "+err
  131.     if n< -32768: return None,"Can't convert number \"{}\" below -32768 to 16 bits".format(n)
  132.     if n>  65535: return None,"Can't convert number \"{}\" above 65535 to 16 bits".format(n)
  133.     if n<0: n+=65536 #two's complement representation
  134.     return n.to_bytes(2,"little"),""
  135. def to24bitAll(n0):
  136.     try: n=_floor(n0+.5) #round to nearest whole
  137.     except TypeError: return None,"to24bitAll can't accept \"{}\", which is of type \"{}\"".format(n0,type(n0))
  138.     except Exception as err: return None,"to24bitAll error: "+err
  139.     if n< -8388608: return None,"Can't convert number \"{}\" below -8388608 to 24 bits".format(n)
  140.     if n> 16777215: return None,"Can't convert number \"{}\" above 16777215 to 24 bits".format(n)
  141.     if n<0: n+=16777216 #two's complement representation
  142.     return n.to_bytes(3,"little"),""
  143. def to32bitAll(n0):
  144.     try: n=_floor(n0+.5) #round to nearest whole
  145.     except TypeError: return None,"to32bitAll can't accept \"{}\", which is of type \"{}\"".format(n0,type(n0))
  146.     except Exception as err: return None,"to32bitAll error: "+err
  147.     if n< -2147483648: return None,"Can't convert number \"{}\" below -2147483648 to 32 bits".format(n)
  148.     if n>  4294967295: return None,"Can't convert number \"{}\" above 4294967295 to 32 bits".format(n)
  149.     if n<0: n+=4294967296 #two's complement representation
  150.     return n.to_bytes(4,"little"),""
  151.    
  152. EWAC="Error while assembling chunk \"{}\": "
  153.  
  154. def directive_str(s,numBytes):
  155.     evalNumBytes=[]
  156.     for c in s:
  157.         o=charset[ord(c)]
  158.         if   numBytes == 1: evalNum,err= to8bitAll(o)
  159.         elif numBytes == 2: evalNum,err=to16bitAll(o)
  160.         elif numBytes == 3: evalNum,err=to24bitAll(o)
  161.         elif numBytes == 4: evalNum,err=to32bitAll(o)
  162.         if evalNum == None: return None,err
  163.         evalNumBytes.append(evalNum)
  164.     return evalNumBytes,""
  165.  
  166. def directive_ui(line,numBytes,updateGlobals=True):
  167.     global outputBytes
  168.     global progCounter
  169.     evalNumBytes=[]
  170.     for e in line[1:]:
  171.         evalNum,err=evalExpression(e,progCounter)
  172.         if evalNum == None:
  173.             print((EWAC+"{}").format(line,err))
  174.             return False
  175.         if type(evalNum) == str:
  176.             stringBytes,err=directive_str(evalNum,numBytes)
  177.             if stringBytes == None:
  178.                 print((EWAC+"{}").format(line,err))
  179.                 return False
  180.             evalNumBytes+=stringBytes
  181.             continue
  182.         elif numBytes == 1: evalNum,err= to8bitAll(evalNum)
  183.         elif numBytes == 2: evalNum,err=to16bitAll(evalNum)
  184.         elif numBytes == 3: evalNum,err=to24bitAll(evalNum)
  185.         elif numBytes == 4: evalNum,err=to32bitAll(evalNum)
  186.         if evalNum == None:
  187.             print((EWAC+"{}").format(line,err))
  188.             return False
  189.         evalNumBytes.append(evalNum)
  190.     if updateGlobals:
  191.         outputBytes.append(b''.join(evalNumBytes))
  192.         progCounter+=len(outputBytes[-1]) #len of evalNumBytes
  193.         return True
  194.     else: return evalNumBytes
  195.  
  196. def directive_u_(line,numBytes,updateGlobals=True):
  197.     global outputBytes
  198.     global progCounter
  199.     evalNumBytes=[]
  200.     for e in line[1:]:
  201.         evalNum,err=evalExpression(e,progCounter)
  202.         if evalNum == None:
  203.             print((EWAC+"{}").format(line,err))
  204.             return False
  205.         if type(evalNum) == str:
  206.             stringBytes,err=directive_str(evalNum,numBytes)
  207.             if stringBytes == None:
  208.                 print((EWAC+"{}").format(line,err))
  209.                 return False
  210.             evalNumBytes+=stringBytes
  211.             continue
  212.         elif evalNum < 0:
  213.             print((EWAC+"Can't convert \"{}\" to unsigned {}-bit number").format(line,e,numBytes*8))
  214.             return False
  215.         elif numBytes == 1: evalNum,err= to8bitAll(evalNum)
  216.         elif numBytes == 2: evalNum,err=to16bitAll(evalNum)
  217.         elif numBytes == 3: evalNum,err=to24bitAll(evalNum)
  218.         elif numBytes == 4: evalNum,err=to32bitAll(evalNum)
  219.         if evalNum == None:
  220.             print((EWAC+"{}").format(line,err))
  221.             return False
  222.         evalNumBytes.append(evalNum)
  223.     if updateGlobals:
  224.         outputBytes.append(b''.join(evalNumBytes))
  225.         progCounter+=len(outputBytes[-1]) #len of evalNumBytes
  226.         return True
  227.     else: return evalNumBytes
  228.    
  229. def directive_i_(line,numBytes,updateGlobals=True):
  230.     global outputBytes
  231.     global progCounter
  232.     evalNumBytes=[]
  233.     for e in line[1:]:
  234.         evalNum,err=evalExpression(e,progCounter)
  235.         if evalNum == None:
  236.             print((EWAC+"{}").format(line,err))
  237.             return False
  238.         if type(evalNum) == str:
  239.             stringBytes,err=directive_str(evalNum,numBytes)
  240.             if stringBytes == None:
  241.                 print((EWAC+"{}").format(line,err))
  242.                 return False
  243.             evalNumBytes+=stringBytes
  244.             continue
  245.         elif evalNum > ((256**numBytes)-1):
  246.             print((EWAC+"Can't convert \"{}\" to signed {}-bit number").format(line,e,numBytes*8))
  247.             return False
  248.         elif numBytes == 1: evalNum,err= to8bitAll(evalNum)
  249.         elif numBytes == 2: evalNum,err=to16bitAll(evalNum)
  250.         elif numBytes == 3: evalNum,err=to24bitAll(evalNum)
  251.         elif numBytes == 4: evalNum,err=to32bitAll(evalNum)
  252.         if evalNum == None:
  253.             print((EWAC+"{}").format(line,err))
  254.             return False
  255.         evalNumBytes.append(evalNum)
  256.     if updateGlobals:
  257.         outputBytes.append(b''.join(evalNumBytes))
  258.         progCounter+=len(outputBytes[-1]) #len of evalNumBytes
  259.         return True
  260.     else: return evalNumBytes
  261.  
  262. def directive_fill(line):
  263.     global progCounter
  264.     global outputBytes
  265.     if len(line) < 2 or len(line) > 3:
  266.         print((EWAC+"Directive \"!fill\" requires 1-2 args, not {}").format(line,len(line)-1)); return False
  267.     fillCount,err=evalExpression(line[1],progCounter)
  268.     if fillCount == False: print((EWAC+"{}").format(line,err)); return False
  269.     fillValue=0
  270.     if len(line) == 3: fillValue,err=evalExpression(line[2],progCounter)
  271.     if fillValue == None: print((EWAC+"{}").format(line,err)); return False
  272.     if fillValue != _clamp(fillValue, 0,255): print((EWAC+"!fill value \"{}\" out of bounds (must be 0->255)").format(line,fillValue)); return False
  273.     progCounter+=fillCount; outputBytes.append([fillValue.to_bytes(1,"little"),]*fillCount)
  274.     return True
  275.  
  276. def directive_align(line):
  277.     global progCounter
  278.     global outputBytes
  279.     if len(line) < 2 or len(line) > 3:
  280.         print((EWAC+"Directive \"!align\" requires 1-2 args, not {}").format(line,len(line)-1)); return False
  281.     alignA,err=evalExpression(line[1],progCounter)
  282.     if alignA == None: print((EWAC+"{}").format(line,err)); return False
  283.     alignN=0
  284.     if len(line) == 3: alignN,err=evalExpression(line[2],progCounter)
  285.     if alignN != _clamp(alignN, 0,255): print((EWAC+"!align value \"{}\" out of bounds (must be 0->255)").format(line,alignN)); return False
  286.     alignC=alignA-progCounter%alignA
  287.     progCounter+=alignC; outputBytes.append([alignN.to_bytes(1,"little"),]*alignC)
  288.     return True
  289.  
  290. def directive_AT_EQ(line):
  291.     global progCounter
  292.     if len(line) != 2:
  293.         print((EWAC+"Directive \"{}\" requires 1 arg, not {}").format(line,line[0],len(line)-1))
  294.         return False
  295.     progCounter,err=evalExpression(line[1],progCounter)
  296.     if progCounter == None:
  297.         print((EWAC+"{}").format(line,err))
  298.         return False
  299.     if progCounter < 0:
  300.         print((EWAC+"{} value \"{}\" can't be < 0").format(line,line[0],progCounter))
  301.         return False
  302.     elif DisallowProgCounterToGoPast16Bit and progCounter > 0xffff:
  303.         print((EWAC+"{} value \"{}\" can't be > 0xffff (65535)").format(line,line[0],progCounter))
  304.         return False
  305.     outputBytes.append(progCounter)
  306.     return True
  307.  
  308. def directive_tofile(line):
  309.     global outputFileName
  310.     try: #check if file name is valid
  311.         if len(line) != 2:
  312.             print((EWAC+"{} requires 1 arg, not {}").format(line,line[0],len(line)-1))
  313.             return False
  314.         outputFileName=line[1]
  315.         if outputFileName[0] == '"' and outputFileName[-1] == '"':
  316.             outputFileName=outputFileName[1:-1]
  317.         existedPreviously=exists(outputFileName)
  318.         outputFile=open(outputFileName,"a")
  319.         outputFile.close()
  320.         if not existedPreviously:
  321.             deleteFile(outputFileName)
  322.     except Exception as err:
  323.         print((EWAC+"{}").format(line,err)); return False
  324.     return True
  325.  
  326. def directive_save(line):
  327.     #fileName,byteList
  328.     global outputFileName
  329.     global outputBytes
  330.     global progCounter
  331.     try:
  332.         if len(line) > 2:
  333.             print((EWAC+"{} requires 0-1 args, not {}").format(line,line[0],len(line)-1))
  334.             return False
  335.         errorOnOverwrite=False
  336.         if len(line) == 2: errorOnOverwrite=eval(line[1])
  337.         if errorOnOverwrite and exists(outputFileName):
  338.             print((EWAC+"File \"{}\" already exists").format(line,outputFileName))
  339.             return False
  340.         if len(outputBytes) == 0:
  341.             print((EWAC+"Length of output bytes is 0").format(line))
  342.             return False
  343.         outputFile=open(outputFileName,"wb")
  344.         for chunk in outputBytes:
  345.             chunkType=type(chunk)
  346.             if   chunkType == int: outputFile.seek(chunk,0)
  347.             elif chunkType == bytes: outputFile.write(chunk)
  348.             elif chunkType == list: outputFile.write(b''.join(chunk))
  349.         outputFile.close()
  350.         outputBytes=[]
  351.         if setProgCounter0AfterSaving: progCounter=0
  352.         return True
  353.     except Exception as err:
  354.         print((EWAC+"{}").format(line,err)); return False
  355.  
  356. #def directive_flush():
  357.  
  358. def directive_binary(line):
  359.     global progCounter
  360.     global outputBytes
  361.     if len(line) < 2 or len(line) > 3:
  362.         print((EWAC+"{} requires 1-2 args, not {}").format(line,line[0],len(line)-1))
  363.         return False
  364.     fileName=line[1]
  365.     if fileName[0] == '"' and fileName[-1] == '"': fileName=fileName[1:-1]
  366.     seekBytes=0
  367.     try:
  368.         if len(line) == 3: seekBytes=evalExpression(line[2],progCounter)
  369.         if type(seekBytes) != int:
  370.             print((EWAC+"eval of \"{}\" should be of type <class 'int'>, not {}").format(line,seekBytes,type(seekBytes)))
  371.             return False
  372.     except Exception as err:
  373.         print((EWAC+"{}").format(line,err))
  374.     if not exists(fileName):
  375.         print((EWAC+"Binary file \"{}\" doesn't exist").format(line,fileName))
  376.         return False
  377.     file=open(fileName,"rb")
  378.     file.seek(seekBytes,0)
  379.     fileBytes=file.read()
  380.     file.close()
  381.     if len(fileBytes) == 0:
  382.         print((EWAC+"Binary file \"{}\"'s byte length is 0 after seeking").format(line,fileName))
  383.     outputBytes.append(fileBytes)
  384.     progCounter+=len(fileBytes)
  385.     return True
  386.  
  387. def directive_chrset(line):
  388.     global charset
  389.     if len(line) != 2:
  390.         print((EWAC+"{} requires 1 arg, not {}").format(line,line[0],len(line)-1))
  391.         return False
  392.     fileName=line[1]; fileBytes=None
  393.     if not exists(fileName):
  394.         print((EWAC+"Charset encoding file \"{}\" doesn't exist").format(line,fileName))
  395.         return False
  396.     try:
  397.         file=open(fileName,"rb")
  398.         fileBytes=file.read()
  399.         file.close()
  400.     except Exception as err:
  401.         print((EWAC+"{}").format(line,err))
  402.         return False
  403.     if len(fileBytes) != 256:
  404.         print((EWAC+"Length of charset encoding file should be 256, not {}").format(line,fileName,len(fileBytes)))
  405.         return False
  406.     charset=[n for n in fileBytes]
  407.     return True
  408.    
  409. def directive_savlbl(line):
  410.     global progCounter
  411.     global labels
  412.     global localLabels
  413.     if len(line) < 2 or len(line) > 3:
  414.         print((EWAC+"{} requires 1-2 args, not {}").format(line,line[0],len(line)-1))
  415.         return False
  416.     fileName=line[1]
  417.     if fileName[0] == '"' and fileName[-1] == '"': fileName=fileName[1:-1]
  418.     errorOnOverwrite=False
  419.     try:
  420.         if len(line) == 3: errorOnOverwrite=evalExpression(line[2],progCounter)
  421.         if type(errorOnOverwrite) != bool:
  422.             print((EWAC+"eval of \"{}\" should be of type <class 'bool'>, not {}").format(line,errorOnOverwrite,type(errorOnOverwrite)))
  423.             return False
  424.         if errorOnOverwrite and exists(fileName):
  425.             print((EWAC+"File \"{}\" already exists").format(line,fileName))
  426.             return False
  427.         file=open(fileName,"w")
  428.         file.write("labels="+pformat(labels))
  429.         file.close()
  430.     except Exception as err:
  431.         print((EWAC+"{}").format(line,err))
  432.     return True
  433.  
  434. directives={
  435.     "!8"     :True,
  436.     "!16"    :True,
  437.     "!24"    :True,
  438.     "!32"    :True,
  439.    
  440.     "!u8"    :True,
  441.     "!u16"   :True,
  442.     "!u24"   :True,
  443.     "!u32"   :True,
  444.    
  445.     "!i8"    :True,
  446.     "!i16"   :True,
  447.     "!i24"   :True,
  448.     "!i32"   :True,
  449.  
  450.     "!fill"  :True,
  451.     "!align" :True,
  452.     "!@="    :True, "!pc=":True,
  453.     "!tofile":True, "!to":True,
  454.     "!save"  :True, "!savefile":True,
  455.     "!flush" :True, "!flushbytes":True,
  456.     "!binary":True, "!bin":True,
  457.     "!chrset":True, "!encoding":True,
  458.     "!savlbl":True, "!savelabels":True
  459. }
  460. parameters={
  461.     "i":"00",
  462.     "l":"01",
  463.     "h":"10",
  464.     "c":"11", "s":"11", "a":"11"
  465. }
  466. descriptors={ "^":"00", "#":"01", "@":"10", "*":"11" }
  467. instructions={
  468.     #MMM:   OOOO,MN,MX,    P,    D,    E,     ^,    #,    @,    *,
  469.     "hcf":("0000",1,1, False,False,False, False,False,False,False), #
  470.     "jpa":("0000",2,2, False,False, True, False,False, True,False),
  471.     "jpr":("0000",1,1, False,False,False,  True,False,False,False),
  472.     "bnz":("0000",2,2, False,False, True, False,False,False,False),
  473.     "ina":("0001",2,2,  True,False,False, False,False,False,False), #
  474.     "tar":("0010",2,2,  True,False,False, False,False,False,False), #
  475.     "tra":("0011",2,2,  True,False,False, False,False,False,False),
  476.     "ldr":("0100",3,4,  True, True, True,  True, True, True, True),
  477.     "str":("0101",3,4,  True, True, True,  True,False, True, True),
  478.    #"ina":("0001",2,2,  True,False,False, False,False,False,False), #
  479.     "adc":("0110",2,3, False, True, True,  True, True, True, True),
  480.     "sbc":("0111",2,3, False, True, True,  True, True, True, True),
  481.     "add":("1000",2,3, False, True, True,  True, True, True, True),
  482.     "ror":("1001",2,3, False, True, True,  True, True, True, True), #
  483.     "and":("1010",2,3, False, True, True,  True, True, True, True),
  484.     "ora":("1011",2,3, False, True, True,  True, True, True, True),
  485.     "xor":("1100",2,3, False, True, True,  True, True, True, True),
  486.    #"bnz":("0000",2,2, False,False, True, False,False,False,False), #
  487.     "cmp":("1101",2,3, False, True, True,  True, True, True, True),
  488.     "smb":("1110",2,3, False, True, True,  True, True, True, True),
  489.     "sys":("1111",2,3, False, True, True,  True, True, True, True),
  490. }
  491.  
  492. def stitchInstruction(line): #base 2 instruction,byte count,error
  493.     mnemonic=line[0].lower()
  494.     parameter,descriptor,expression=None,None,None
  495.     instruction=instructions.get(mnemonic)
  496.     if not instruction:
  497.         return None,None,"Malformed or nonexistent mnemonic \""+mnemonic+"\""
  498.     opcode=instruction[0]
  499.     lineArgc=len(line); byteMin,byteMax=instruction[1:3]
  500.     if _clamp(lineArgc, byteMin,byteMax) != lineArgc:
  501.         print((EWAC+"Invalid number of line arguments for instruction \"{}\"\
  502. (should be between {} and {}, not {})").format(line, mnemonic, byteMin,byteMax, lineArgc))
  503.         return None,None
  504.     uP,uD,uE=instruction[3:6] #use P,D,E
  505.     argIndex, byteCount,expression=1, 1,0
  506.     #parameter handling
  507.     if uP:
  508.         parameter=line[argIndex].lower()
  509.         if not parameters.get(parameter):
  510.             print((EWAC+"Invalid parameter \"{}\"").format(line,line[argIndex]))
  511.             return None,None
  512.         opcode=parameters[parameter]+opcode
  513.         argIndex+=1
  514.     elif mnemonic == "jpa": opcode="01"+opcode
  515.     elif mnemonic == "jpr": opcode="10"+opcode
  516.     elif mnemonic == "bnz": opcode="11"+opcode
  517.     else: opcode="00"+opcode
  518.     #address mode descriptor handling
  519.     if uD:
  520.         descriptor=line[argIndex].lower()
  521.         if not descriptors.get(descriptor):
  522.             print((EWAC+"Invalid address mode desriptor \"{}\"").format(line,line[argIndex]))
  523.             return None,None
  524.         opcode=descriptors[descriptor]+opcode
  525.         byteCount+=min(2, int(descriptors[descriptor],2) )
  526.         argIndex+=1
  527.     elif mnemonic == "bnz":
  528.         opcode="00"+opcode
  529.         byteCount+=1 #should =2 now
  530.     else: opcode="00"+opcode
  531.     #expression handling
  532.     expressionBytes=b''
  533.     if mnemonic == "jpa":
  534.         if lineArgc != 2:
  535.             print((EWAC+"Instruction \"{}\" requires an expression").format(line,line[0]))
  536.             return None,None
  537.         expression,err=evalExpression(line[argIndex],progCounter)
  538.         if expression == None: print((EWAC+"{}").format(line,err)); return None,None
  539.         elif type(expression) == str:
  540.             print((EWAC+"Instructions can't use strings for expressions").format(line))
  541.             return None,None
  542.         elif expression < 0:
  543.             print((EWAC+"Can't convert \"{}\" to unsigned 16-bit number").format(line,line[1]))
  544.             return None,None
  545.         else: expressionBytes,err=to16bitAll(expression)
  546.         if expressionBytes == None: print((EWAC+"{}").format(line,err)); return None,None
  547.     elif mnemonic == "bnz":
  548.         if lineArgc != 2:
  549.             print((EWAC+"Instruction \"{}\" requires an expression").format(line,line[0]))
  550.             return None,None
  551.         #tbd: put +2 of progCounter inside evalExpression if problems occur
  552.         expression,err=evalExpression(line[argIndex],progCounter+2,relativeMode=True)
  553.         if expression == None: print((EWAC+"{}").format(line,err)); return None,None
  554.         elif type(expression) == str:
  555.             print((EWAC+"Instructions can't use strings for expressions").format(line))
  556.             return None,None
  557.         elif expression > 127:
  558.             print((EWAC+"Can't convert \"{}\" so signed 8-bit number").format(line,line[1]))
  559.             return None,None
  560.         else: expressionBytes,err=to8bitAll(expression)
  561.         if expressionBytes == None: print((EWAC+"{}").format(line,err)); return None,None
  562.     elif byteCount>1 and lineArgc<(1+uP+uD+uE):
  563.         #is there no expression where there should be?
  564.         print((EWAC+"Address mode \"{}\" requires an expression argument").format(line,descriptor))
  565.         return None,None
  566.     elif uE and byteCount>1:
  567.         #for everything except jpa or bnz
  568.         expression,err=evalExpression(line[argIndex],progCounter)
  569.         if expression == None: print((EWAC+"{}").format(line,err)); return None,None
  570.         elif type(expression) == str:
  571.             print((EWAC+"Instructions can't use strings for expressions").format(line))
  572.             return None,None
  573.         elif byteCount == 2: expressionBytes,err=to8bitAll(expression)
  574.         elif byteCount == 3: expressionBytes,err=to16bitAll(expression)
  575.         else: print((EWAC+"This error shouldn't occur!").format(line)); return None,None
  576.         if expressionBytes == None: print((EWAC+"{}").format(line,err)); return None,None
  577.     elif byteCount==1 and lineArgc>(1+uP+uD):
  578.         #is there an expression where there shouldn't be?
  579.         print((EWAC+"Address mode \"{}\" does not take an expression argument").format(line,descriptor))
  580.         return None,None
  581.     #print(mnemonic, opcode,expression, int(opcode,2).to_bytes(1,"little"),expressionBytes)
  582.     return [int(opcode,2).to_bytes(1,"little")+expressionBytes],byteCount
  583.  
  584.  
  585. timeStart=time()
  586.  
  587. if len(argv) < 2: exit()
  588. #black box preprocessor magic
  589. proc=subprocess.Popen(["cpp","-x","assembler-with-cpp","-nostdinc","-CC","-undef","-P"]+argv[1:]+["_PreProcOutput",],
  590.                       stdout=subprocess.PIPE, shell=True)
  591. while proc.poll() == None: sleep(0.02)
  592. pExitCode=proc.poll()
  593. pOutput,pError = proc.communicate()
  594. if len(pOutput) != 0: print(pOutput.decode("utf-8"))
  595. if pExitCode != 0: exit()
  596.  
  597. #parse output of preprocessor
  598. lines,err=getSeparated("_PreProcOutput")
  599. if lines == None: print("Error while parsing: "+err); exit()
  600.  
  601. #(yes, it is basically assembling twice,
  602. #so labels are all defined before instruction handling begins)
  603. #assemble line by line
  604. for assembler_pass in range(2):
  605.     for line in lines:
  606.         lineCompleted=False
  607.         identifier=line[0][0]
  608.         if identifier == '!': #assembler directive
  609.             directiveName=line[0]
  610.             if not directives.get(directiveName):
  611.                 print((EWAC+"Directive \"{}\" doesn't exist").format(line,line[0])); break
  612.             if   directiveName == "!8":
  613.                 if not directive_ui(line,1): break
  614.             elif directiveName == "!16":
  615.                 if not directive_ui(line,2): break
  616.             elif directiveName == "!24":
  617.                 if not directive_ui(line,3): break
  618.             elif directiveName == "!32":
  619.                 if not directive_ui(line,4): break
  620.             elif directiveName == "!u8":
  621.                 if not directive_u_(line,1): break
  622.             elif directiveName == "!u16":
  623.                 if not directive_u_(line,2): break
  624.             elif directiveName == "!u24":
  625.                 if not directive_u_(line,3): break
  626.             elif directiveName == "!u32":
  627.                 if not directive_u_(line,4): break
  628.             elif directiveName == "!i8":
  629.                 if not directive_i_(line,1): break
  630.             elif directiveName == "!i16":
  631.                 if not directive_i_(line,2): break
  632.             elif directiveName == "!i24":
  633.                 if not directive_i_(line,3): break
  634.             elif directiveName == "!i32":
  635.                 if not directive_i_(line,4): break
  636.             elif directiveName == "!fill":
  637.                 if not directive_fill(line): break
  638.             elif directiveName == "!align":
  639.                 if not directive_align(line): break
  640.             elif directiveName in ["!@=","!pc=","!PC="]:
  641.                 if not directive_AT_EQ(line): break
  642.             elif directiveName in ["!tofile","!to"]:
  643.                 if not directive_tofile(line): break
  644.             elif directiveName in ["!save","!savefile"] and assembler_pass==1: #2nd pass only
  645.                 if not directive_save(line): break
  646.             elif directiveName in ["!flush","!flushbytes"]:
  647.                 outputBytes=[] #tbd: might need to put more stuff here at some point
  648.             elif directiveName in ["!binary","!bin"]:
  649.                 if not directive_binary(line): break
  650.             elif directiveName in ["!chrset","!encoding"]:
  651.                 if not directive_chrset(line): break
  652.             elif directiveName in ["!savlbl","!savelabels"] and assembler_pass==1: #2nd pass only
  653.                 if not directive_savlbl(line): break
  654.         elif identifier == ':': #define label
  655.             if assembler_pass == 0: #1st pass only
  656.                 newLabel=line[0][1:-1]
  657.                 if line[0][-1] != ":":
  658.                     print((EWAC+"Label definition missing a ':' as its last char").format(line)); break
  659.                 if labels.get(newLabel):
  660.                     print((EWAC+"Label \"{}\" is already defined").format(line,newLabel)); break
  661.                 labels[newLabel]=[progCounter,{}]
  662.                 currentLabel=newLabel
  663.                 labels=reverseSortDict(labels)
  664.         elif identifier == '.': #define local label
  665.             if assembler_pass == 0: #1st pass only
  666.                 if line[0][-1] != ":":
  667.                     print((EWAC+"Local label definition missing a ':' as its last char").format(line)); break
  668.                 newLocal=line[0][:-1]
  669.                 if newLocal in labels[currentLabel][1]:
  670.                     print((EWAC+"Label \"{}{}\" is already defined").format(line,currentLabel,line[0])); break
  671.                 localLabels[currentLabel+newLocal]=progCounter #["label.local"] = progCounter
  672.                 labels[currentLabel][1][newLocal]=progCounter
  673.                 localLabels=reverseSortDict(localLabels)
  674.         elif assembler_pass == 1: #instruction handling done on 2nd pass
  675.             instruction,byteCount=stitchInstruction(line)
  676.             if instruction == None: break
  677.             outputBytes+=instruction
  678.             progCounter+=byteCount
  679.         if DisallowProgCounterToGoPast16Bit and progCounter > 0xffff:
  680.             print((EWAC+"Program counter = {} ({}), which is past 0xffff").format(line,hex(progCounter),progCounter))
  681.             break
  682.         lineCompleted=True
  683.     if assembler_pass == 0: outputBytes=[0x0000]
  684.     if not lineCompleted: break;
  685.  
  686. if len(outputBytes) > 0:
  687.     if overwriteOutputFileIfBytesNotEmpty or not exists(outputFileName):
  688.         directive_save([])
  689.     elif exists(outputFileName):
  690.         pprint(outputBytes,width=40)
  691.         print("(Warning: output bytes is not empty post-assembly; the above data will be lost)")
  692.     else: directive_save([])
  693. if deletePreprocessorOutput: deleteFile("_PreProcOutput")
  694. print("Finished assembling in {}ms".format( int((time()-timeStart)*1000) ))
Tags: kit-8
Add Comment
Please, Sign In to add comment