Coverage for capasm/assembler.py : 99%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3#
4# This module contains the assembler for the capricorn cpu.
5# (c) 2020 Joachim Siebold
6#
7# This program is free software; you can redistribute it and/or
8# modify it under the terms of the GNU General Public License
9# as published by the Free Software Foundation; either version 2
10# of the License, or (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program; if not, write to the Free Software
19# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20#
21#-------------------------------------------------------------------
22#
23# Changelog
24# 18.05.2020 jsi
25# - start changelog
26# 19.05.2020 jsi
27# - changed number parsing to static methods
28# 21.05.2020 jsi
29# - renamed parseLiteralOp to parseLiteralDataList
30# 22.05.2020 jsi
31# - raise custom exception on fatal error
32# - fixed output of short symbol table
33# - allow comment text to follow immediately the comment sign "!"
34# - development version 0.9.1
35# 24.05.2020 jsi
36# - do not invalidate arp, drp conext on EQU or DAD pseudo ops
37# 25.05.2020 jsi
38# - store default bin file in the local directory
39# 26.05.2020 jsi
40# - add return value for assemble method
41# 30.05.2020 jsi
42# - beta version 0.9.5
43# - improved help text
44# 30.06.2020 jsi
45# - development version 0.9.6
46# - HP86/87 compatible NAM statement
47# - jump relative GTO
48# - bug fixes
49# - conditional assembly support
50# - include and link file support
51# 04.07.2020 jsi
52# - allow quoted strings for INC and LNK files
53# - use path of assembler source files for INC and LNK files, if there
54# is only a file name specified
55# 08.07.2020 jsi
56# - development version 0.9.7
57# - number parsing refactored, allow hex and bin numbers
58# 20.07.2020 jsi
59# - literal data list error fixes
60# - support non octal numbers for registers
61# - support LOC and HED statements
62# 23.07.2020 jsi
63# - development version 0.9.8
64# - support for ASC/ASP statements with numeric length qualifiers
65# - removed "'" as alternate string delimiter
66# - do not issue page break triggered by "HED" if there is no list file
67# 27.07.2020 jsi
68# - removed "-m" option
69# - added "-g" option and enhanced global symbols capabilities
70# 31.07.2020 jsi
71# - refactoring
72# - allow more special characters in symbols
74import argparse,sys,os,importlib,re
75import importlib.util
76from pathlib import Path
77from .capcommon import capasmError,BYTESTOSTORE,parseFunc,OPCODES, \
78 MESSAGE,clsSymDict, CAPASM_VERSION, CAPASM_VERSION_DATE, \
79 clsConditionalAssembly, clsGlobVar, clsToken, clsLineScanner, \
80 clsObjWriter, clsListWriter, clsSourceReader, clsParserInfo, \
81 clsParsedOperand, clsCodeInfo, clsInvalidOperand, clsParsedNumber, \
82 clsParsedString, clsParsedLabel, clsParsedRegister, clsCodeGeneratorBase, \
83 clsParserBase
85#
86# Parser ---------------------------------------------------------------
87#
88# The parseLine method takes the Program Counter, the list of scanned token
89# and the original source line as arguments and returns an object of type
90# clsParserInfo
91#
92class clsParser(clsParserBase):
94#
95# Initialize parser
96#
97 def __init__(self,globVar,infile):
98 super().__init__(globVar,infile)
99 return
100#
101# Parse ABS pseudoop
102# Syntax is: ABS {ROM} nnnn
103# ROM does not matter here
104#
105 def pAbs(self):
106 self.__globVar__.hasAbs=True
107 if self.__globVar__.PC !=0 or self.__globVar__.hasNam:
108 self.addError(MESSAGE.E_NOTALLOWED_HERE)
109 addrIndex=0
110 if len(self.__scannedOperand__)==2:
111 if self.__scannedOperand__[0].string.upper()== "ROM":
112 addrIndex=1
113 else:
114 self.addError(MESSAGE.E_ROM_EXPECTED)
115 return []
116 address=self.parseAddress(addrIndex)
117 if address!=clsParserInfo.ILL_NUMBER:
118 self.__globVar__.PC=address
119 return []
120#
121# Parse EQU and DAD pseudoops
122#
123 def pEqu(self):
124#
125# The label in the self.__scannedLabel__ field has already been
126# parsed by the parseLine method
127#
128 isDAD=False
129 if self.__scannedOpcode__=="DAD":
130 isDAD=True
131 SymDict=self.__globVar__.symDict
132 if self.__scannedLabel__ is None:
133 self.addError(MESSAGE.E_MISSING_LABEL)
134 return []
136 label=self.__scannedLabel__.string
137 address=self.parseAddress(0)
139 if address!=clsParserInfo.ILL_NUMBER:
140 size=2
141 if self.__scannedOpcode__.string=="EQU":
142 if address < 256:
143 size=1
144 ret=SymDict.enter(label,clsSymDict.SYM_EQU,address, size,\
145 self.__lineInfo__)
146 else:
147 address+=self.__globVar__.ORG
148 ret=SymDict.enter(label,clsSymDict.SYM_DAD,address, size,\
149 self.__lineInfo__)
150 if ret is not None:
151 self.addError(ret)
152 return []
153#
154# Parse DEF and VAL
155#
156 def pDef(self):
157 if self.__scannedOpcode__.string== "DEF":
158 self.__opcodeLen__=2
159 else:
160 self.__opcodeLen__=1
161 return[self.parseLabelOp(0)]
164#
165# Parse ORG pseudoop
166#
167 def pOrg(self):
168 address=self.parseAddress(0)
169 if address!=clsParserInfo.ILL_NUMBER:
170 self.__globVar__.ORG=address
171 return []
172#
173# Parse JSB instruction
174#
175 def pJsb(self):
176 self.__opcodeLen__=1
177 numBytesToStore=2
178 parsedOperand=[]
179#
180# Invalidate Arp, Drp context
181#
182 self.__globVar__.lastStmtWasJSB=True
183#
184# Determine mode
185#
186 if self.__scannedOperand__[0].string[0]=="=":
187#
188# JSB literal direct
189#
190 self.__addressMode__=clsParserInfo.JS_LITERAL_DIRECT
191 self.__opcodeLen__+=numBytesToStore
192 if len(self.__scannedOperand__)!=1:
193 self.addError(MESSAGE.E_ILL_NUMOPERANDS)
194 parsedOperand.append(clsInvalidOperand())
195 else:
196 parsedOperand.append(self.parseLabelOp(0))
197 else:
198#
199# JSB indexed
200#
201 self.__addressMode__=clsParserInfo.JS_INDEXED
202 self.__opcodeLen__+=numBytesToStore
203 if len(self.__scannedOperand__)!=2:
204 self.addError(MESSAGE.E_ILL_NUMOPERANDS) # dead code ??
205 else:
206 parsedOperand.append(self.parseXr(0))
207 parsedOperand.append(self.parseLabelOp(1))
208 return parsedOperand
209#
210# Parse AD, AN, CM and SB instructions
211#
212 def pAri(self):
214 parsedOperand=[]
215 self.__opcodeLen__=1
216 byteMode=self.getByteMode()
217#
218# Parse DR, if we have a valid register then determine the number of
219# bytes to store for literals or labels
220#
221 dRegister=self.parseDr()
222 if dRegister.isInvalid():
223 self.__opcodeLen__=1
224 return [dRegister]
225#
226# Now determina Address Mode and check number of opderands
227# and parse opcodes
228#
229 if len(self.__opcode__)==3: # ADB, ADM, SBB, SBM, CMB, CMM, ANM
230 if self.__scannedOperand__[1].string[0]== "=":
231 self.__addressMode__=clsParserInfo.AM_LITERAL_IMMEDIATE
232 if byteMode== clsParserInfo.BM_SINGLEBYTE:
233 numberOfBytesToStore=1
234 ret=self.parseLiteralDataList(numberOfBytesToStore)
235 self.__opcodeLen__+= ret[0]
236 else:
237 numberOfBytesToStore= \
238 BYTESTOSTORE.numBytes(dRegister.registerNumber)
239 if numberOfBytesToStore is None:
240 if not self.__globVar__.allowHashRLiteral:
241 self.addError(MESSAGE.W_RHASH_LITERAL)
242 ret=self.parseLiteralDataList(numberOfBytesToStore)
243 self.__opcodeLen__+= ret[0]
244 parsedOperand.extend(ret[1])
246 else:
247 self.__addressMode__=clsParserInfo.AM_REGISTER_IMMEDIATE
248 if len(self.__scannedOperand__)!=2:
249 self.addError(MESSAGE.E_ILL_NUMOPERANDS)
250 else:
251 parsedOperand.append(self.parseAr())
252 else: # ADBD, ADMD, SBBD, ANMD
254 if self.__scannedOperand__[1].string[0]== "=":
255 self.__addressMode__=clsParserInfo.AM_LITERAL_DIRECT
256 self.__opcodeLen__+= 2
257 if len(self.__scannedOperand__)!=2:
258 self.addError(MESSAGE.E_ILL_NUMOPERANDS)
259 else:
260 parsedOperand.append(self.parseLabelOp(1))
261 else:
262 self.__addressMode__=clsParserInfo.AM_REGISTER_DIRECT
263 if len(self.__scannedOperand__)!=2:
264 self.addError(MESSAGE.E_ILL_NUMOPERANDS)
265 else:
266 parsedOperand.append(self.parseAr())
268 return parsedOperand
270#
271# Parse LD / ST instructions: full to bursting of address modes
272#
273 def pLdSt(self):
275 parsedOperand=[]
276 self.__opcodeLen__=1
277 byteMode=self.getByteMode()
278#
279# Parse DR, if we have a valid register then determine the number of
280# bytes to store for literals or labels
281#
282 dRegister=self.parseDr()
283 if dRegister.isInvalid():
284 self.__opcodeLen__=1
285 return [dRegister]
286#
287# Now determina Address Mode and check number of opderands
288# and parse opcodes
289#
290 if len(self.__opcode__)==3: # LDB, STB, LDM, STM
292 if self.__scannedOperand__[1].string[0]== "=":
293 self.__addressMode__=clsParserInfo.AM_LITERAL_IMMEDIATE
294 if byteMode== clsParserInfo.BM_SINGLEBYTE:
295 numberOfBytesToStore=1
296 ret=self.parseLiteralDataList(numberOfBytesToStore)
297 self.__opcodeLen__+= ret[0]
298 else:
299 numberOfBytesToStore= \
300 BYTESTOSTORE.numBytes(dRegister.registerNumber)
301 if numberOfBytesToStore is None:
302 if not self.__globVar__.allowHashRLiteral:
303 self.addError(MESSAGE.W_RHASH_LITERAL)
304 ret=self.parseLiteralDataList(numberOfBytesToStore)
305 self.__opcodeLen__+= ret[0]
306 parsedOperand.extend(ret[1])
308 elif self.__scannedOperand__[1].string[0] in "xX":
309 self.addError(MESSAGE.E_ILLADDRESSMODE)
311 else:
312 self.__addressMode__=clsParserInfo.AM_REGISTER_IMMEDIATE
313 if len(self.__scannedOperand__)!=2:
314 self.addError(MESSAGE.E_ILL_NUMOPERANDS)
315 else:
316 parsedOperand.append(self.parseAr())
318 elif self.__opcode__[-1]=="D": # LDBD, STBD, LDMD, STMD
320 if self.__scannedOperand__[1].string[0]== "=":
321 self.__addressMode__=clsParserInfo.AM_LITERAL_DIRECT
322 self.__opcodeLen__+= 2
323 if len(self.__scannedOperand__)!=2:
324 self.addError(MESSAGE.E_ILL_NUMOPERANDS)
325 else:
326 parsedOperand.append(self.parseLabelOp(1))
328 elif self.__scannedOperand__[1].string[0] in "xX":
329 self.__addressMode__=clsParserInfo.AM_INDEX_DIRECT
330 self.__opcodeLen__+= 2
331 if len(self.__scannedOperand__)!=3:
332 self.addError(MESSAGE.E_ILL_NUMOPERANDS)
333 else:
334 parsedOperand.append(self.parseXr(1))
335 parsedOperand.append(self.parseLabelOp(2))
337 else:
338 self.__addressMode__=clsParserInfo.AM_REGISTER_DIRECT
339 if len(self.__scannedOperand__)!=2:
340 self.addError(MESSAGE.E_ILL_NUMOPERANDS)
341 else:
342 parsedOperand.append(self.parseAr())
344 elif self.__opcode__[-1]=="I": # LDBI, STBI, LDMI, STMI
346 if self.__scannedOperand__[1].string[0]== "=":
347 self.__addressMode__=clsParserInfo.AM_LITERAL_INDIRECT
348 self.__opcodeLen__+= 2
349 if len(self.__scannedOperand__)!=2:
350 self.addError(MESSAGE.E_ILL_NUMOPERANDS)
351 else:
352 parsedOperand.append(self.parseLabelOp(1))
354 elif self.__scannedOperand__[1].string[0] in "xX":
355 self.__addressMode__=clsParserInfo.AM_INDEX_INDIRECT
356 self.__opcodeLen__+= 2
357 if len(self.__scannedOperand__)!=3:
358 self.addError(MESSAGE.E_ILL_NUMOPERANDS)
359 else:
360 parsedOperand.append(self.parseXr(1))
361 parsedOperand.append(self.parseLabelOp(2))
363 else:
364 self.__addressMode__=clsParserInfo.AM_REGISTER_INDIRECT
365 if len(self.__scannedOperand__)!=2:
366 self.addError(MESSAGE.E_ILL_NUMOPERANDS)
367 else:
368 parsedOperand.append(self.parseAr())
370 return parsedOperand
371#
372# Code Generator class -------------------------------------------------
373#
374# Genertes code and returns an object of class clsCodeInfo
375#
376class clsCodeGenerator(clsCodeGeneratorBase):
378#
379# Initialize generator
380#
381 def __init__(self,globVar):
382 super().__init__(globVar)
383 return
384#
385# Assembler class ---------------------------------------------------------
386#
387# This is the top level class for the entire assembler
388#
389class clsAssembler(object):
391 def __init__(self):
392 super().__init__()
393#
394# extend the OPCODES dictionary with the capasm specific OPS
395#
396 def extendOpcodes(self):
397 OPCODES.extendDict( {
398 "RTN" : ["pNoPer","gdirect",0o236,0,0,False,False,False],
399 "ABS" : ["pAbs","gNil",0,1,2,False,False,False],
400 "FIN" : ["pFin","gNil",0,0,0,False,False,False],
401 "LST" : ["pNil","gNil",0,0,0,False,False,False],
402 "UNL" : ["pNil","gNil",0,0,0,False,False,False],
403 "GLO" : ["pNil","gNil",0,1,1,False,False,False],
404 "ASC" : ["pAsc","gData",0,1,256,False,False,False],
405 "ASP" : ["pAsc","gData",0,1,256,False,False,False],
406 "NAM" : ["pNam","gNam",0,1,2,False,False,False],
407 "BSZ" : ["pBsz","gGenZ",0,1,1,False,False,False],
408 "BYT" : ["pByt","gData",0,1,256,False,False,False],
409 "OCT" : ["pByt","gData",0,1,256,False,False,False],
410 "DAD" : ["pEqu","gNil",0,1,1,False,False,False],
411 "DEF" : ["pDef","gDef",0,1,1,False,False,False],
412 "EQU" : ["pEqu","gNil",0,1,1,False,False,False],
413 "GTO" : ["pGto","gGto",0,1,1,False,False,False],
414 "VAL" : ["pDef","gDef",0,1,1,False,False,False],
415 "ORG" : ["pOrg","gNil",0,1,1,False,False,False],
416 "SET" : ["pCondSet","gNil",0,1,1,False,False,False],
417 "CLR" : ["pCondClr","gNil",0,1,1,False,False,False],
418 "AIF" : ["pCondIfSet","gNil",0,1,1,False,False,True],
419 "DIF" : ["pCondIfDef","gNil",0,1,1,False,False,True],
420 "EIF" : ["pCondEndif","gNil",0,0,0,False,False,True],
421 "ELS" : ["pCondElse","gNil",0,0,0,False,False,True],
422 "INC" : ["pInc","gNil",0,1,1,False,False,False],
423 "LNK" : ["pInc","gNil",0,1,1,False,False,False],
424 "HED" : ["pHed","gHed",0,1,1,False,False,False],
425 "LOC" : ["pLoc","gGenZ",0,1,1,False,False,False],
426 })
427#
428# Assemble method. The method takes the values of the command line
429# switches and parameters. This method may be called multiple times
430# with different parameters.
431# Returns:
432# False: everything o.k.
433# True: errors in assembly
434# Raises capasmError on I/O error
435#
436 def assemble(self,sourceFileName,binFileName="",listFileName="", \
437 referenceOpt=1, pageSize=66, pageWidth=80, \
438 extendedChecks=False, symNamLen=6,useHex=False, definedFlags=[], \
439 globalSymbolFile="none"):
440#
441# initialize opcodes
442#
443 self.extendOpcodes()
444#
445# initialize error condition
446#
447 hasError=False
448#
449# Create global variables data object
450#
451 self.__globVar__=clsGlobVar()
452 self.__globVar__.useHex=useHex
453 self.__sourceFileName__= sourceFileName
454 self.__globalSymbolFile__= globalSymbolFile
455 self.__globVar__.progName="CAPASM"
456#
457# Initalize basic parser functions
458#
459 parseFunc.DELIMITER='"'
460 parseFunc.LABELMATCHSTRING="[(^0-9)(\x20-\x7A|\|)][\x20-\x7A|\|]{0,"
461#
462# Build file name of object file if not specified
463#
464 if binFileName=="":
465 self.__binFileName__= \
466 Path(self.__sourceFileName__).with_suffix(".bin").name
467 else:
468 self.__binFileName__=binFileName
469 self.__listFileName__= listFileName
470 self.__referenceOpt__= referenceOpt
471 self.__pageSize__= pageSize
472 self.__pageWidth__= pageWidth
473 self.__extendedChecks__= extendedChecks
474 self.__symNamLen__= symNamLen
475#
476# Create symbol table object
477#
478 self.__globVar__.symDict=clsSymDict( self.__extendedChecks__, \
479 self.__globalSymbolFile__, \
480 { clsSymDict.SYM_DAD: "DAD", \
481 clsSymDict.SYM_EQU: "EQU", \
482 clsSymDict.SYM_LCL: "LCL" })
483#
484# Create conditional assembly object
485#
486 self.__globVar__.condAssembly=clsConditionalAssembly(definedFlags)
487#
488# Check if we run in regression test mode
489#
490 if os.getenv("CAPASMREGRESSIONTEST"):
491 self.__globVar__.isRegressionTest=True
492#
493# get directory of source file
494#
495 self.__globVar__.sourceFileDirectory=\
496 str(Path(self.__sourceFileName__).parent)
497#
498# Check extended checks mode
499#
500 if self.__extendedChecks__:
501 self.__globVar__.allowHashRLiteral=False
502#
503# Set symbol name length
504#
505 self.__globVar__.symNamLen=self.__symNamLen__
506#
507# Pass 1: scan and parse lines, accumulate results in the
508# pass1Info list
509#
510 pass1Info=[]
511 infile=clsSourceReader(self.__sourceFileName__)
512 lineScanner=clsLineScanner("!","!",'"')
513 lineParser=clsParser(self.__globVar__,infile)
515 while not self.__globVar__.isFin:
516 line=infile.read()
517 if line is None:
518 if pass1Info:
519 pass1Info[-1].messages.append(MESSAGE.E_MISSING_FIN)
520 break
521 else:
522 MESSAGE.fatalError("Empty source file")
523#
524# Scan line
525#
526 scannedLine=lineScanner.scanLine(line)
527#
528# Parse line
529#
530 parsedLine=lineParser.parseLine(scannedLine,line)
531 pass1Info.append(parsedLine)
532#
533# Increment PC and codeLen with length of instructions
534#
535 self.__globVar__.PC+=parsedLine.opcodeLen
536 self.__globVar__.codeLen+=parsedLine.opcodeLen
538 infile=None
539 lineScanner=None
540 lineParser=None
541#
542# Passe 2: process content of pass1Info list, generate code,
543# write code to binary output file and output information to
544# the list file
545#
546 objWriter=clsObjWriter(self.__binFileName__)
547 listWriter=clsListWriter(self.__globVar__,self.__listFileName__, \
548 self.__pageSize__, self.__pageWidth__)
549 codeGenerator=clsCodeGenerator(self.__globVar__)
551 for parsedLine in pass1Info:
552#
553# Generate code
554#
555 codeInfo=codeGenerator.generate(parsedLine)
556#
557# Write code
558#
559 objWriter.writeCode(codeInfo,parsedLine)
560#
561# Write listing
562#
563 listWriter.writeLine(parsedLine,codeInfo)
565 codeGenerator=None
566 objWriter=None
568 listWriter.writeSymbols(self.__referenceOpt__)
569 listWriter.writeStatistics()
570 listWriter=None
571#
572# delete objectfile if any errors
573#
574 if self.__globVar__.errorCount>0:
575 os.remove(self.__binFileName__)
576 hasError=True
577 self.__globVar__=None
578#
579# return error condition
580#
581 return hasError
582#
583# custom arg checks ----------------------------------------------------
584#
585# Helper function to check the range for the page size parameter
586#
587class argPageSizeCheck(argparse.Action): # pragma: no cover
588 def __call__(self, parser, namespace, values, option_string=None):
589 if values < 40 or values> 100:
590 parser.error("Valid range for {0} is 40 to 100".format(option_string))
591 setattr(namespace, self.dest, values)
593#
594# Helper function to check the range for the line width parameter
595#
596class argWidthCheck(argparse.Action): # pragma: no cover
597 def __call__(self, parser, namespace, values, option_string=None):
598 if values < 80 or values> 132:
599 parser.error("Valid range for {0} is 80 to 132".format(option_string))
600 setattr(namespace, self.dest, values)
604#
605# Entry point capasm ------------------------------------------------------
606#
607# This entry point parses the command line parameters, creates an
608# assembler object and executes it with the parsed command line parameters
609#
610def capasm(): # pragma: no cover
611#
612# Command line arguments processing
613#
614 argparser=argparse.ArgumentParser(description=\
615 "An assembler for the Hewlett Packard Capricorn CPU (Series 80 and HP-75)",\
616 epilog=\
617 "See https://github.com/bug400/capasm for details. "+CAPASM_VERSION)
620 argparser.add_argument("sourcefile",help="source code file (required)")
621 argparser.add_argument("-b","--binfile",\
622 help="binary object code file (default: sourcefilename with suffix .bin",\
623 default="")
624 argparser.add_argument("-l","--listfile",\
625 help="list file (default: no list file)",default="")
626 argparser.add_argument("-g","--globalsymbolfile",\
627 help="global symbol file. Use either the built-in symbol table names {\"85\",\"87\",\"75\",\"none\"} or specify a file name for a custom table (default: none)",default="none")
628 argparser.add_argument("-r","--reference",type=int,default=1,\
629 help="symbol reference 0:none, 1:short, 2:full (default:1)",\
630 choices=[0,1,2])
631 argparser.add_argument("-p","--pagesize",type=int,default=66, \
632 help="lines per page (default: 66)",action=argPageSizeCheck)
633 argparser.add_argument("-w","--width",type=int,default=80, \
634 help="page width (default:80)",action=argWidthCheck)
635 argparser.add_argument("-c","--check",help="activate additional checks", \
636 action='store_true')
637 argparser.add_argument("-d","--define",action='append',default=[],\
638 help="define conditional flag with value True")
639 argparser.add_argument("-x","--hex",help="use hex output", \
640 action='store_true')
641 argparser.add_argument("-s","--symnamelength",\
642 help="maximum length of symbol names (default:6)", \
643 type=int,default=6,choices=[6,7,8,9,10,11,12])
644 args= argparser.parse_args()
645#
646# Create assembler object and run it
647#
648 capasm= clsAssembler()
649 try:
650 ret=capasm.assemble(args.sourcefile,listFileName=args.listfile,\
651 binFileName=args.binfile, referenceOpt=args.reference, \
652 pageSize=args.pagesize,pageWidth=args.width, \
653 extendedChecks=args.check, \
654 symNamLen=args.symnamelength,useHex=args.hex,\
655 definedFlags=args.define, \
656 globalSymbolFile=args.globalsymbolfile)
657 except capasmError as e:
658 print(e.msg+" -- Assembler terminated")
659 ret=True
660 if ret:
661 sys.exit(1)
662#
663# Run the capasm procedure, if this file is called as top level script
664#
665if __name__ == '__main__': # pragma: no cover
666 capasm()