Coverage for capasm/ncas.py : 88%

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
25import argparse,sys,os,math,time
26import importlib.util
27from pathlib import Path
28from .capcommon import capasmError,BYTESTOSTORE,parseFunc,OPCODES, \
29 MESSAGE,clsSymDict,CAPASM_VERSION, CAPASM_VERSION_DATE, \
30 clsConditionalAssembly, clsGlobVar, clsToken, clsLineScanner, \
31 clsObjWriter, clsListWriter, clsSourceReader, clsParserInfo, \
32 clsParsedOperand, clsParsedExpression, clsInvalidOperand, \
33 clsParsedLabel,clsParsedString, clsParsedRegister, clsCodeInfo, \
34 clsCodeGeneratorBase, clsParserBase, clsDateTime
36#
37# Expression parser and execute class -----------------------------------
38#
39class clsExpression(object):
41 EX_NUM=0
42 EX_SYM=1
43 EX_OP=2
45 OP_PLUS=0
46 OP_MINUS=1
47 OP_DIV=2
48 OP_MULT=3
49 OP_OR=4
50 OP_AND=5
51 OP_NOT=6
52 OP_MOD=7
53 OP_RESIZE=8
54 OP_CHS=9
55 OP_NONE=10
57 CH_OP=["PLUS","MINUS","DIV","MULT","OR","AND","NOT","MOD","RESIZE","CHS","NONE"]
60 def __init__(self,globVar):
61 super().__init__()
62 self.__globVar__=globVar
64 def addError(self,errnum):
65 self.__errors__.append(errnum)
66#
67# generate Bytecode
68#
69# number
70#
71 def genNumber(self,value):
72 self.__bc__.append([clsExpression.EX_NUM,value])
73 self.__lastNumber__=value
74 self.__size__=self.byteLen(value)
75#
76# location counter
77#
78 def genLoc(self,value):
79 self.__bc__.append([clsExpression.EX_NUM,value])
80 self.__size__=2
82#
83# symbol
84#
85 def genSymbol(self,name):
86 self.__bc__.append([clsExpression.EX_SYM,name])
87 ret=self.__globVar__.symDict.get(name,noGlobStore=True)
88 if ret is None:
89 self.__size__= None
90 else:
91 self.__size__=ret[2]
92#
93# opcode
94#
95 def genOp(self,op):
96 self.__bc__.append([clsExpression.EX_OP,op])
97 if op== clsExpression.OP_RESIZE:
98 self.__size__=self.__bc__[-2][1]
99 else:
100 self.__size__= None
101#
102# returns the smallest number of bytes a integer value will occupy
103# Note: for negative number an additional bit will be needed for the
104# 2s representation. Therefore an additional byte could be needed.
105#
106 def byteLen(self,value):
107 if value==0:
108 return 1
109 else:
110 n=(1+math.floor(math.log(abs(value))/(0.69314718055994530942*8)))
111#
112# check if we need an additional byte for the 2's representation
113#
114 if (value < 0) :
115 maxValue=(2**(8*n))/2
116 if abs(value) > maxValue:
117 n+=1
118 return n
119#
120# convert the result into a list of byte values
121#
122 def makeBytes(self,value,size=None):
123 if size is None:
124 return None
125 else:
126 nBytes=size
127 b= [0]* nBytes
128 for i in range(0,nBytes):
129 b[i]= value & 0xFF
130 value=value>>8
131 return b
133#
134# resize a value to a given size
135# positve integers are returned unchanged
136# negative integers are padded with 0xFF
137# returns None if size is too small
138#
139 def resize(self,value,size):
140 nBytes= self.byteLen(value)
141 if size < nBytes:
142 return None
143#
144# 2s complement for negative numbers
145#
146 if value < 0 :
147 value= (2 ** (size*8)) + value
148 return value
149#
150# scan a character
151#
152 def getch(self):
153 self.__getchCount__+=1
154 if self.__getchCount__ == len (self.__exprString__):
155 self.__GCH__= "\n"
156 else:
157 self.__GCH__= self.__exprString__[self.__getchCount__]
158#
159# location counter symbol "$"
160#
161 def LOC(self):
162 self.getch()
163 self.genLoc(self.__globVar__.PC)
164 return
165#
166# number, which can be ocal, decimal, bcd or hex. Hex numbers must always
167# begin with a digit
168#
169 def number(self):
170 numString=""
171 while True:
172 numString+=self.__GCH__
173 self.getch()
174 if "01234567890abcdefABCDEFhHoOKkQq#".find(self.__GCH__)< 0:
175 break
176 value=parseFunc.parseNumber(numString)
177 if value is None:
178 self.addError(MESSAGE.E_ILLNUMBER)
179 bValue=0
180 else:
181 bValue=value
182 self.genNumber(bValue)
183 return
184#
185# ASCII string, which can be either enclosed in ",',` or ^
186#
187 def asc(self):
189 value=0
190 term=self.__GCH__
191 self.getch()
192 string=""
193 while self.__GCH__ != term:
194 string+= self.__GCH__
195 self.getch()
196 if self.__GCH__== "\n":
197 self.addError(MESSAGE.E_ILLSTRING)
198 return
199 self.getch()
200 i=0
201 for c in string[::-1]:
202 if i== 0 and term in "^`":
203 c=chr(ord(c)+128)
204 value= value*256 + ord(c)
205 i+=1
206 self.genNumber(value)
207 return
208#
209# symbol name which always begins with a letter
210#
211 def symbol(self):
212 symName=""
213 while True:
214 symName+=self.__GCH__
215 self.getch()
216 if " )\n".find(self.__GCH__)>=0:
217 break
218 if parseFunc.parseLabel(symName,self.__globVar__.symNamLen) is None:
219 self.addError(MESSAGE.E_ILL_LABELOP)
220 return
221 self.genSymbol(symName)
222 return
223#
224# expression base
225#
226 def base(self):
227 strTerm="'`^"+'"'
228#
229# location counter symbol
230#
231 if self.__GCH__== "$":
232 self.LOC()
233#
234# number
235#
236 elif "01234567890".find(self.__GCH__) >=0:
237 self.number()
238#
239# quoted string
240#
241 elif self.__GCH__ in strTerm:
242 self.asc()
243#
244# left paranthesis, begin of a new term
245#
246 elif self.__GCH__=="(":
247 self.getch()
248 self.term()
249 if self.__GCH__ != ")":
250 self.addError(MESSAGE.E_MISSINGRPAREN)
251 return
252 self.getch()
253#
254# returned from term, look for a size specifier
255#
256 if self.__GCH__==".":
257 self.getch()
258 if "01234567890".find(self.__GCH__) >=0:
259 self.number()
260 self.genOp(clsExpression.OP_RESIZE)
261 size=self.__lastNumber__
262 if size ==None:
263 self.addError(MESSAGE.E_INVALIDSIZESPEC)
264 return
265 else:
266 self.symbol()
267 return
268#
269# expression unary, operators are "-" or "~"
270#
271 def unary(self):
272 if self.__GCH__== "-":
273 self.getch()
274 self.base()
275 self.genOp(clsExpression.OP_CHS)
276# elif self.__GCH__=="~":
277# self.getch()
278# self.base()
279# self.genOp(clsExpression.OP_NOT)
280 else:
281 self.base()
282 return
283#
284# expression bool, operators are "&" or "|"
285#
286 def bool(self):
287 first=True
288 operator=clsExpression.OP_NONE
289 done=False
290 while not done:
291 self.unary()
292 if first:
293 first=False
294 else:
295 if operator== clsExpression.OP_AND:
296 self.genOp(operator)
297 elif operator== clsExpression.OP_OR:
298 self.genOp(operator)
299 done=True
300 if self.__GCH__=="&":
301 operator= clsExpression.OP_AND
302 done= False
303 self.getch()
304 if self.__GCH__=="|":
305 operator= clsExpression.OP_OR
306 done= False
307 self.getch()
309 return
310#
311# expression factor, operators are "*", "/", "%" (modulo)
312#
313 def factor(self):
315 operator=clsExpression.OP_NONE
316 first=True
317 done=False
318 while not done:
319 self.bool()
320 if first:
321 first=False
322 else:
323 if operator== clsExpression.OP_MULT:
324 self.genOp(operator)
325 elif operator== clsExpression.OP_DIV:
326 self.genOp(operator)
327 elif operator == clsExpression.OP_MOD:
328 self.genOp(operator)
329 done=True
330 if self.__GCH__=="*":
331 operator=clsExpression.OP_MULT
332 done= False
333 self.getch()
334 if self.__GCH__=="/":
335 operator=clsExpression.OP_DIV
336 done= False
337 self.getch()
338 if self.__GCH__=="%":
339 operator=clsExpression.OP_MOD
340 done= False
341 self.getch()
343 return
344#
345# expression term, operators are "+" and "-"
346#
347 def term(self):
349 operator=clsExpression.OP_NONE
350 first=True
351 done=False
352 while not done:
353 self.factor()
354 if first:
355 first=False
356 else:
357 if operator== clsExpression.OP_PLUS:
358 self.genOp(operator)
359 elif operator== clsExpression.OP_MINUS:
360 self.genOp(operator)
361 done=True
362 if self.__GCH__=="+":
363 operator=clsExpression.OP_PLUS
364 done= False
365 self.getch()
366 if self.__GCH__=="-":
367 operator=clsExpression.OP_MINUS
368 done= False
369 self.getch()
371 return
373#
374# parse expression string
375# expr : string with expression
376# indicatedSize: force size of result to this size if not None
377# sizeRequired : True if the size of the expression must be determined by
378# parsing
379#
380 def parse(self,expr,indicatedSize=None,sizeRequired=False):
381 self.__exprString__=expr
382 self.__getchCount__=-1
383 self.__GCH__=""
384 self.__errors__= []
385 self.__size__= None
386 self.__bc__= []
387 self.__lastNumber__=None
388 self.getch()
389#
390# parse expression
391#
392 self.term()
393#
394# no more characters?
395#
396 if self.__GCH__ != "\n":
397 self.addError(MESSAGE.E_ILLEXPRESSION)
398#
399# do we have to resize to a determined size?
400#
401 if indicatedSize is not None:
402 if self.__size__ is None or \
403 self.__size__ != indicatedSize:
404 self.genNumber(indicatedSize)
405 self.genOp(clsExpression.OP_RESIZE)
406 self.__size__= indicatedSize
407#
408# no determined size, check if a size is required
409#
410 else:
411 if self.__size__ is None and sizeRequired:
412 self.addError(MESSAGE.E_UNSIZEDEXPRESSION)
414 if len(self.__errors__)==0:
415 parsedExpression=clsParsedExpression(self.__bc__,self.__size__)
416 else:
417 parsedExpression=clsInvalidOperand()
418 return parsedExpression,self.__errors__
419#
420# execute the byte code of an expression
421#
422 def execute(self,parsedExpression,lineInfo):
424 stack=[]
425 self.__errors__= []
426 size=parsedExpression.size
429 for typ,op in parsedExpression.byteCode:
430 if typ== clsExpression.EX_NUM:
431 stack.append(op)
432 elif typ==clsExpression.EX_SYM:
433 ret=self.__globVar__.symDict.get(op,lineInfo)
434 if ret is None:
435 self.addError(MESSAGE.E_SYMNOTFOUND)
436 return None, None, self.__errors__
437 value=ret[1]
438 stack.append(value)
439 elif typ==clsExpression.EX_OP:
440 if op==clsExpression.OP_PLUS:
441 stack[-2]+=stack[-1]
442 stack.pop()
443 elif op==clsExpression.OP_MINUS:
444 stack[-2]-=stack[-1]
445 stack.pop()
446 elif op==clsExpression.OP_MULT:
447 stack[-2]*=stack[-1]
448 stack.pop()
449 elif op==clsExpression.OP_DIV:
450 if stack[-1]==0:
451 self.addError(MESSAGE.E_DIVBYZERO)
452 return None, None, self.__errors__
453 stack[-2]//=stack[-1]
454 stack.pop()
455 elif op==clsExpression.OP_MOD:
456 if stack[-1]==0:
457 self.addError(MESSAGE.E_DIVBYZERO)
458 return None, None, self.__errors__
459 stack[-2]%=stack[-1]
460 stack.pop()
461 elif op==clsExpression.OP_AND:
462 stack[-2]&=stack[-1]
463 stack.pop()
464 elif op==clsExpression.OP_OR:
465 stack[-2]|=stack[-1]
466 stack.pop()
467 elif op==clsExpression.OP_CHS:
468 stack[-1]=-stack[-1]
469# elif op==clsExpression.OP_NOT:
470# stack[-1]= ~ stack[-1]
471 elif op==clsExpression.OP_RESIZE:
472 value=self.resize(stack[-2],stack[-1])
473 if value== None:
474 self.addError(MESSAGE.E_VALTOOLARGE)
475 return None, None,self.__errors__
476 else:
477 stack[-2]=value
478 stack.pop()
480 result=stack [0]
481 byteResult=self.makeBytes(result,size)
482 return result,byteResult,self.__errors__
484 def immediate(self,expression,indicatedSize,lineInfo):
485 parsedExpression,errors=self.parse(expression,indicatedSize,False)
486 if len(errors)!=0:
487 return None, None,errors
488 result,byteResult, errors=self.execute(parsedExpression,lineInfo)
489 return result,byteResult,errors
490#
491# Struct context ------------------------------------------------------
492#
493class clsStructContext(object):
495 CTX_IF=0
496 CTX_LOOP=1
497 CTX_RTN=2
498 CTX_DISABLED=3
500 def __init__(self):
501 super().__init__()
502 self.__lblCount__=0
503 self.__ctxStack__= [ ]
504 self.__rtnDest__= None
506 def isEmpty(self):
507 return len(self.__ctxStack__)==0
509 def push(self,ctxInfo):
510 if not self.__ctxStack__:
511 self.__ctxStack__=[ctxInfo]
512 else:
513 self.__ctxStack__.append(ctxInfo)
515 def pop(self):
516 self.__ctxStack__.pop()
518 def newLabel(self):
519 self.__lblCount__+=1
520 return("{:06d}".format(self.__lblCount__))
522 def structR(self):
523 if self.__rtnDest__ is None:
524 self.__rtnDest__= self.newLabel()
525 return(self.__rtnDest__)
527 def getRtnDest(self):
528 d=self.__rtnDest__
529 self.__rtnDest__= None
530 return(d)
532 def structIf(self,arpReg,drpReg):
533 lbl=self.newLabel()
534 self.push([clsStructContext.CTX_IF,lbl,None,arpReg,drpReg,False,False,-1,-1])
535 return(lbl)
537 def structElse(self,arpReg,drpReg):
538 if not self.__ctxStack__:
539 return None
540 if self.__ctxStack__[-1][0]!= clsStructContext.CTX_IF:
541 return None
542 if self.__ctxStack__[-1][2]!= None:
543 return None
544 lbl1=self.__ctxStack__[-1][1]
545 lbl2=self.newLabel()
546 self.__ctxStack__[-1][2]=lbl2
547#
548# check, if arp or drp were changed in the if clause, if so then
549# set the invalidate flag to True
550#
551 oldArpReg=self.__ctxStack__[-1][3]
552 if oldArpReg != arpReg:
553 self.__ctxStack__[-1][5]=True
554 oldDrpReg=self.__ctxStack__[-1][4]
555 if oldDrpReg != drpReg:
556 self.__ctxStack__[-1][6]=True
557#
558# store last adp, drp if the IF part
559#
560 self.__ctxStack__[-1][7]=arpReg
561 self.__ctxStack__[-1][8]=drpReg
562 return (lbl1,lbl2,oldArpReg,oldDrpReg)
563#
564# handle removed ELSE clause, return arp/drp status and invalidate entry
565#
566 def removeElse(self):
568 self.__ctxStack__[-1][0]= clsStructContext.CTX_DISABLED
569 return self.__ctxStack__[-1][5],self.__ctxStack__[-1][6]
571 def structEndif(self,arpReg,drpReg):
572 if not self.__ctxStack__:
573 return None
574 if self.__ctxStack__[-1][0]== clsStructContext.CTX_DISABLED:
575 self.pop()
576 return "",-1,-1,False,False
577 if self.__ctxStack__[-1][0]!= clsStructContext.CTX_IF:
578 return None
579 if self.__ctxStack__[-1][2]!= None:
580 lbl=self.__ctxStack__[-1][2]
581 else:
582 lbl=self.__ctxStack__[-1][1]
583#
584# check, if arp or drp were changed in the if clause, if so then
585# set the invalidate flag to True
586#
587 oldArpReg=self.__ctxStack__[-1][3]
588 if oldArpReg != arpReg:
589 self.__ctxStack__[-1][5]=True
590#
591# but not if drp was the same in the if and the else clause
592#
593 if arpReg == self.__ctxStack__[-1][7]:
594 if arpReg != -1:
595 self.__ctxStack__[-1][5]=False
596 oldDrpReg=self.__ctxStack__[-1][4]
597 if oldDrpReg != drpReg:
598 self.__ctxStack__[-1][6]=True
599#
600# but not if drp was the same in the if and the else clause
601#
602 if drpReg == self.__ctxStack__[-1][8]:
603 if drpReg != -1:
604 self.__ctxStack__[-1][6]=False
605 invalidateArp= self.__ctxStack__[-1][5]
606 invalidateDrp= self.__ctxStack__[-1][6]
607 self.pop()
608 return(lbl,oldArpReg,oldDrpReg,invalidateArp,invalidateDrp)
610 def structLoop(self):
611 lbl=self.newLabel()
612 self.push([clsStructContext.CTX_LOOP,lbl,None])
613 return(lbl)
615 def structEx(self):
616 if not self.__ctxStack__:
617 return None
618 if self.__ctxStack__[-1][0]!= clsStructContext.CTX_LOOP:
619 return None
620 lbl2=self.newLabel()
621 self.__ctxStack__[-1][2]=lbl2
622 return(lbl2)
624 def structWhile(self):
625 if not self.__ctxStack__:
626 return None
627 if self.__ctxStack__[-1][0]!= clsStructContext.CTX_LOOP:
628 return None
629 lbl1=self.__ctxStack__[-1][1]
630 lbl2=self.__ctxStack__[-1][2]
631 self.pop()
632 return(lbl1,lbl2)
634#
635# Parser ---------------------------------------------------------------
636#
637# The parseLine method takes the Program Counter, the list of scanned token
638# and the original source line as arguments and returns an object of type
639# clsParserInfo
640#
641class clsParser(clsParserBase):
643#
644# Initialize parser
645#
646 def __init__(self,globVar,infile):
647 super().__init__(globVar,infile)
648 self.__expression__=clsExpression(globVar)
649 self.__structCtx__= clsStructContext()
650 return
651#
652# Parse EQU, Addr pseudoop
653#
654 def pEqu(self):
655#
656# The label in the self.__scannedLabel__ field has already been
657# parsed by the parseLine method
658#
659 isAddr=False
660 indicatedSize=None
661 if self.__scannedOpcode__.string=="ADDR":
662 isAddr=True
663 indicatedSize=2
664 SymDict=self.__globVar__.symDict
665 if self.__scannedLabel__ is None:
666 self.addError(MESSAGE.E_MISSING_LABEL)
667 return []
669 label=self.__scannedLabel__.string
670 opstring= self.__scannedOperand__[0].string
671#
672# evaluate expression immediately
673#
674 result,byteResult,errors=self.__expression__.immediate(opstring,\
675 indicatedSize, self.__lineInfo__)
677 if result is not None:
678 if isAddr:
679 if result < 0 or result > 0xFFFF:
680 self.addError(MESSAGE.E_ILL_ADDRESS)
681 return []
682 size=len(byteResult)
683 ret=SymDict.enter(label,clsSymDict.SYM_DAD,result,size, \
684 self.__lineInfo__)
685 else:
686 if byteResult is None:
687 size=None
688 else:
689 size=len(byteResult)
690 ret=SymDict.enter(label,clsSymDict.SYM_EQU,result,size, \
691 self.__lineInfo__)
692 if ret is not None:
693 self.addError(ret)
694 else:
695 self.addError(errors)
696 return []
697#
698# Parse ABS pseudoop
699#
700 def pAbs(self):
701 self.__globVar__.hasAbs=True
702 if self.__globVar__.PC !=0:
703 self.addError(MESSAGE.E_NOTALLOWED_HERE)
704 return []
705#
706# Parse ORG pseudoop
707#
708 def pOrg(self):
709 opstring= self.__scannedOperand__[0].string
710 result,byteResult,errors=self.__expression__.immediate(opstring,2, \
711 self.__lineInfo__)
712 if result is not None:
713 if result >= 0 and result <= 0xFFFF:
714 self.__globVar__.PC=result
715 else:
716 self.addError(MESSAGE.E_ILL_ADDRESS)
717 else:
718 self.addError(errors)
719 return []
720#
721# parse DEF, VAL
722#
723 def pDef(self):
724 if self.__scannedOpcode__.string== "DEF":
725 self.__opcodeLen__=2
726 return [self.parseSingleExpression(0,2)]
727 else:
728 self.__opcodeLen__=1
729 return [self.parseSingleExpression(0,1)]
730#
731# Parse RTN statement
732#
733 def pRtn(self):
734 self.__globVar__.lastRtnAddr=self.__globVar__.PC
735 self.__opcodeLen__=1
736 SymDict=self.__globVar__.symDict
737 label=self.__structCtx__.getRtnDest()
738 if label is not None:
739 ret=SymDict.enter(label,clsSymDict.SYM_LCL,self.__globVar__.PC,2, \
740 self.__lineInfo__)
741 return
743#
744# Parse JSB instruction
745#
746 def pJsb(self):
747 self.__opcodeLen__=1
748 numBytesToStore=2
749 parsedOperand=[]
750#
751# Invalidate Arp, Drp context
752#
753 if self.__scannedOpcode__.string!="JSBN":
754 self.__globVar__.lastStmtWasJSB=True
755#
756# Determine mode
757#
758 if self.__scannedOperand__[0].string[0]=="=":
759#
760# JSB literal direct
761#
762 self.__addressMode__=clsParserInfo.JS_LITERAL_DIRECT
763 self.__opcodeLen__+=numBytesToStore
764 if len(self.__scannedOperand__)!=1:
765 self.addError(MESSAGE.E_ILL_NUMOPERANDS)
766 parsedOperand.append(clsInvalidOperand())
767 else:
768 parsedOperand.append(self.parseLabelOp(0))
769 else:
770#
771# JSB indexed
772#
773 self.__addressMode__=clsParserInfo.JS_INDEXED
774 self.__opcodeLen__+=numBytesToStore
775 if len(self.__scannedOperand__)!=2:
776 self.addError(MESSAGE.E_ILL_NUMOPERANDS) # dead code ??
777 else:
778 parsedOperand.append(self.parseXr(0))
779 parsedOperand.append(self.parseSingleExpression(1,2))
780 return parsedOperand
782#
783# Parse AD, AN, CM and SB instructions
784#
785 def pAri(self):
787 parsedOperand=[]
788 self.__opcodeLen__=1
789 byteMode=self.getByteMode()
790#
791# Parse DR, if we have a valid register then determine the number of
792# bytes to store for literals or labels
793#
794 dRegister=self.parseDr()
795 if dRegister.isInvalid():
796 self.__opcodeLen__=1
797 return [dRegister]
798#
799# Now determina Address Mode and check number of opderands
800# and parse opcodes
801#
802 if len(self.__opcode__)==3: # ADB, ADM, SBB, SBM, CMB, CMM, ANM
803 if self.__scannedOperand__[1].string[0]== "=":
804 self.__addressMode__=clsParserInfo.AM_LITERAL_IMMEDIATE
805 self.__scannedOperand__[1].string= \
806 self.__scannedOperand__[1].string[1:]
807 if byteMode== clsParserInfo.BM_SINGLEBYTE:
808 if len(self.__scannedOperand__[1].string) > 0:
809 self.__opcodeLen__+= 1
810 parsedOperand.append(self.parseSingleExpression(1,1))
811 else:
812 self.addError(MESSAGE.W_LITDATADOESNOTFILL)
814 else:
815 numberOfBytesToStore= \
816 BYTESTOSTORE.numBytes(dRegister.registerNumber)
817 if numberOfBytesToStore is None:
818 if not self.__globVar__.allowHashRLiteral:
819 self.addError(MESSAGE.W_RHASH_LITERAL)
820 opLen,parsedExpressions=self.parseExpressionList(1,\
821 numberOfBytesToStore)
822 if opLen is not None:
823 if numberOfBytesToStore is not None:
824 if numberOfBytesToStore != opLen:
825 self.addError(MESSAGE.W_LITDATADOESNOTFILL)
826 self.__opcodeLen__+= opLen
827 parsedOperand.extend(parsedExpressions)
829 else:
830 self.__addressMode__=clsParserInfo.AM_REGISTER_IMMEDIATE
831 if len(self.__scannedOperand__)!=2:
832 self.addError(MESSAGE.E_ILL_NUMOPERANDS)
833 else:
834 parsedOperand.append(self.parseAr())
835 else: # ADBD, ADMD, SBBD, ANMD
837 if self.__scannedOperand__[1].string[0]== "=":
838 self.__addressMode__=clsParserInfo.AM_LITERAL_DIRECT
839 self.__scannedOperand__[1].string= \
840 self.__scannedOperand__[1].string[1:]
841 self.__opcodeLen__+= 2
842 if len(self.__scannedOperand__)!=2:
843 self.addError(MESSAGE.E_ILL_NUMOPERANDS)
844 else:
845 parsedOperand.append(self.parseSingleExpression(1,2))
846 else:
847 self.__addressMode__=clsParserInfo.AM_REGISTER_DIRECT
848 if len(self.__scannedOperand__)!=2:
849 self.addError(MESSAGE.E_ILL_NUMOPERANDS)
850 else:
851 parsedOperand.append(self.parseAr())
853 return parsedOperand
855#
856# Parse LD / ST instructions: full to bursting of address modes
857#
858 def pLdSt(self):
860 parsedOperand=[]
861 self.__opcodeLen__=1
862 byteMode=self.getByteMode()
863#
864# Parse DR, if we have a valid register then determine the number of
865# bytes to store for literals or labels
866#
867 dRegister=self.parseDr()
868 if dRegister.isInvalid():
869 self.__opcodeLen__=1
870 return [dRegister]
871#
872# Now determina Address Mode and check number of opderands
873# and parse opcodes
874#
875 if len(self.__opcode__)==3: # LDB, STB, LDM, STM
877 if self.__scannedOperand__[1].string[0]== "=":
878 self.__addressMode__=clsParserInfo.AM_LITERAL_IMMEDIATE
879 self.__scannedOperand__[1].string= \
880 self.__scannedOperand__[1].string[1:]
881 if byteMode== clsParserInfo.BM_SINGLEBYTE:
882 if len(self.__scannedOperand__[1].string) > 0:
883 self.__opcodeLen__+= 1
884 parsedOperand.append(self.parseSingleExpression(1,1))
885 else:
886 self.addError(MESSAGE.W_LITDATADOESNOTFILL)
887 else:
888 numberOfBytesToStore= \
889 BYTESTOSTORE.numBytes(dRegister.registerNumber)
890 if numberOfBytesToStore is None:
891 if not self.__globVar__.allowHashRLiteral:
892 self.addError(MESSAGE.W_RHASH_LITERAL)
893 opLen,parsedExpressions=self.parseExpressionList(1,\
894 numberOfBytesToStore)
895 if opLen is not None:
896 if numberOfBytesToStore is not None:
897 if numberOfBytesToStore != opLen:
898 self.addError(MESSAGE.W_LITDATADOESNOTFILL)
899 self.__opcodeLen__+= opLen
900 parsedOperand.extend(parsedExpressions)
902 elif self.__scannedOperand__[1].string[0] in "xX":
903 self.addError(MESSAGE.E_ILLADDRESSMODE)
905 else:
906 self.__addressMode__=clsParserInfo.AM_REGISTER_IMMEDIATE
907 if len(self.__scannedOperand__)!=2:
908 self.addError(MESSAGE.E_ILL_NUMOPERANDS)
909 else:
910 parsedOperand.append(self.parseAr())
912 elif self.__opcode__[-1]=="D": # LDBD, STBD, LDMD, STMD
914 if self.__scannedOperand__[1].string[0]== "=":
915 self.__addressMode__=clsParserInfo.AM_LITERAL_DIRECT
916 self.__scannedOperand__[1].string= \
917 self.__scannedOperand__[1].string[1:]
918 self.__opcodeLen__+= 2
919 if len(self.__scannedOperand__)!=2:
920 self.addError(MESSAGE.E_ILL_NUMOPERANDS)
921 else:
922 parsedOperand.append(self.parseSingleExpression(1,2))
924 elif self.__scannedOperand__[1].string[0] in "xX":
925 self.__addressMode__=clsParserInfo.AM_INDEX_DIRECT
926 self.__opcodeLen__+= 2
927 if len(self.__scannedOperand__)!=3:
928 self.addError(MESSAGE.E_ILL_NUMOPERANDS)
929 else:
930 parsedOperand.append(self.parseXr(1))
931 parsedOperand.append(self.parseSingleExpression(2,2))
933 else:
934 self.__addressMode__=clsParserInfo.AM_REGISTER_DIRECT
935 if len(self.__scannedOperand__)!=2:
936 self.addError(MESSAGE.E_ILL_NUMOPERANDS)
937 else:
938 parsedOperand.append(self.parseAr())
940 elif self.__opcode__[-1]=="I": # LDBI, STBI, LDMI, STMI
942 if self.__scannedOperand__[1].string[0]== "=":
943 self.__addressMode__=clsParserInfo.AM_LITERAL_INDIRECT
944 self.__scannedOperand__[1].string= \
945 self.__scannedOperand__[1].string[1:]
946 self.__opcodeLen__+= 2
947 if len(self.__scannedOperand__)!=2:
948 self.addError(MESSAGE.E_ILL_NUMOPERANDS)
949 else:
950 parsedOperand.append(self.parseSingleExpression(1,2))
952 elif self.__scannedOperand__[1].string[0] in "xX":
953 self.__addressMode__=clsParserInfo.AM_INDEX_INDIRECT
954 self.__opcodeLen__+= 2
955 if len(self.__scannedOperand__)!=3:
956 self.addError(MESSAGE.E_ILL_NUMOPERANDS)
957 else:
958 parsedOperand.append(self.parseXr(1))
959 parsedOperand.append(self.parseSingleExpression(2,2))
961 else:
962 self.__addressMode__=clsParserInfo.AM_REGISTER_INDIRECT
963 if len(self.__scannedOperand__)!=2:
964 self.addError(MESSAGE.E_ILL_NUMOPERANDS)
965 else:
966 parsedOperand.append(self.parseAr())
968 return parsedOperand
969#
970# Code Generator class -------------------------------------------------
971#
972# Genertes code and returns an object of class clsCodeInfo
973#
974class clsCodeGenerator(clsCodeGeneratorBase):
976#
977# Initialize generator
978#
979 def __init__(self,globVar):
980 super().__init__(globVar)
981 self.__expression__=clsExpression(self.__globVar__)
982 return
983#
984# Generate ARP, DRP instructions. Overwrite superclass method
985#
986 def gdarp(self):
987 if self.__opcodeLen__==0:
988 return
989 super().gdarp()
990 return
991#
992# NCAS Assembler class ---------------------------------------------------------
993#
994# This is the top level class for the entire assembler
995#
996class clsNcas(object):
998 def __init__(self):
999 super().__init__()
1000#
1001# extend the OPCODES dictionary with the ncas specific OPS
1002#
1003 def extendOpcodes(self):
1004 OPCODES.extendDict( {
1005#
1006# ncas specific ops
1007#
1008 "RTN" : ["pRtn","gdirect",0o236,0,0,True,False,False],
1009#
1010# This is a karma "special" and it means a JSB without return
1011# Therefore this instruction is handled the same way as a RTN or GTO
1012# at the end of IF-ELSE clauses in optimizing ARP/DRP elimination
1013#
1014 "JSBN" : ["pJsb","gJsb",0o306,1,2,True,False,False],
1015#
1016# conditional jump alias
1017#
1018 "JEQ" : ["pJrel","gJrel",0o367,1,1,False,False,False], # alias of JZR
1019 "JNE" : ["pJrel","gJrel",0o366,1,1,False,False,False], # alias of JNZ
1020 "JGE" : ["pJrel","gJrel",0o365,1,1,False,False,False], # alias of JPS
1021 "JLT" : ["pJrel","gJrel",0o364,1,1,False,False,False], # alias of JNG
1022 "JHS" : ["pJrel","gJrel",0o373,1,1,False,False,False], # alias of JCY
1023 "JLO" : ["pJrel","gJrel",0o372,1,1,False,False,False], # alias of JNC
1024#
1025# IF pseudo op (we have to take the opposite condition there)
1026#
1027 "IFOV" : ["pIf","gJrel",0o361,0,0,False,False,False], # is JNO
1028 "IFEV" : ["pIf","gJrel",0o362,0,0,False,False,False], # is JOD
1029 "IFOD" : ["pIf","gJrel",0o363,0,0,False,False,False], # is JEV
1030 "IFPS" : ["pIf","gJrel",0o364,0,0,False,False,False], # is JNG
1031 "IFNG" : ["pIf","gJrel",0o365,0,0,False,False,False], # is JPS
1032 "IFZR" : ["pIf","gJrel",0o366,0,0,False,False,False], # is JNZ
1033 "IFNZ" : ["pIf","gJrel",0o367,0,0,False,False,False], # is JZR
1034 "IFEZ" : ["pIf","gJrel",0o370,0,0,False,False,False], # is JEN
1035 "IFEN" : ["pIf","gJrel",0o371,0,0,False,False,False], # is JEZ
1036 "IFCY" : ["pIf","gJrel",0o372,0,0,False,False,False], # is JNC
1037 "IFNC" : ["pIf","gJrel",0o373,0,0,False,False,False], # is JCY
1038 "IFLN" : ["pIf","gJrel",0o374,0,0,False,False,False], # is JLZ
1039 "IFLZ" : ["pIf","gJrel",0o375,0,0,False,False,False], # IS JLN
1040 "IFRN" : ["pIf","gJrel",0o376,0,0,False,False,False], # is JRZ
1041 "IFRZ" : ["pIf","gJrel",0o377,0,0,False,False,False], # is JRN
1042 "IFNE" : ["pIf","gJrel",0o367,0,0,False,False,False], # alias of JZR
1043 "IFEQ" : ["pIf","gJrel",0o366,0,0,False,False,False], # alias of JNZ
1044 "IFLT" : ["pIf","gJrel",0o365,0,0,False,False,False], # alias of JPS
1045 "IFLE" : ["pIf","gJrel",0o364,0,0,False,False,False], # alias of JNG
1046 "IFLO" : ["pIf","gJrel",0o373,0,0,False,False,False], # alias of JCY
1047 "IFHS" : ["pIf","gJrel",0o372,0,0,False,False,False], # alias of JNC
1048#
1049# While pseudo op
1050#
1051 "WHMP" : ["pWh","gJrel",0o360,0,0,False,False,False],
1052 "WHNO" : ["pWh","gJrel",0o361,0,0,False,False,False],
1053 "WHOD" : ["pWh","gJrel",0o362,0,0,False,False,False],
1054 "WHEV" : ["pWh","gJrel",0o363,0,0,False,False,False],
1055 "WHNG" : ["pWh","gJrel",0o364,0,0,False,False,False],
1056 "WHPS" : ["pWh","gJrel",0o365,0,0,False,False,False],
1057 "WHNZ" : ["pWh","gJrel",0o366,0,0,False,False,False],
1058 "WHZR" : ["pWh","gJrel",0o367,0,0,False,False,False],
1059 "WHEN" : ["pWh","gJrel",0o370,0,0,False,False,False],
1060 "WHEZ" : ["pWh","gJrel",0o371,0,0,False,False,False],
1061 "WHNC" : ["pWh","gJrel",0o372,0,0,False,False,False],
1062 "WHCY" : ["pWh","gJrel",0o373,0,0,False,False,False],
1063 "WHLZ" : ["pWh","gJrel",0o374,0,0,False,False,False],
1064 "WHLN" : ["pWh","gJrel",0o375,0,0,False,False,False],
1065 "WHRZ" : ["pWh","gJrel",0o376,0,0,False,False,False],
1066 "WHRN" : ["pWh","gJrel",0o377,0,0,False,False,False],
1067 "WHEQ" : ["pWh","gJrel",0o367,0,0,False,False,False], # alias of WHZR
1068 "WHNE" : ["pWh","gJrel",0o366,0,0,False,False,False], # alias of WHNZ
1069 "WHGE" : ["pWh","gJrel",0o365,0,0,False,False,False], # alias of WHPS
1070 "WHLT" : ["pWh","gJrel",0o364,0,0,False,False,False], # alias of WHNG
1071 "WHHS" : ["pWh","gJrel",0o373,0,0,False,False,False], # alias of WHCY
1072 "WHLO" : ["pWh","gJrel",0o372,0,0,False,False,False], # alias of WHNC
1073#
1074# conditional return pseudo op
1075#
1076 "RNO" : ["pR","gJrel",0o361,0,0,False,False,False],
1077 "ROD" : ["pR","gJrel",0o362,0,0,False,False,False],
1078 "REV" : ["pR","gJrel",0o363,0,0,False,False,False],
1079 "RNG" : ["pR","gJrel",0o364,0,0,False,False,False],
1080 "RPS" : ["pR","gJrel",0o365,0,0,False,False,False],
1081 "RNZ" : ["pR","gJrel",0o366,0,0,False,False,False],
1082 "RZR" : ["pR","gJrel",0o367,0,0,False,False,False],
1083 "REN" : ["pR","gJrel",0o370,0,0,False,False,False],
1084 "REZ" : ["pR","gJrel",0o371,0,0,False,False,False],
1085 "RNC" : ["pR","gJrel",0o372,0,0,False,False,False],
1086 "RCY" : ["pR","gJrel",0o373,0,0,False,False,False],
1087 "RLZ" : ["pR","gJrel",0o374,0,0,False,False,False],
1088 "RLN" : ["pR","gJrel",0o375,0,0,False,False,False],
1089 "RRZ" : ["pR","gJrel",0o376,0,0,False,False,False],
1090 "RRN" : ["pR","gJrel",0o377,0,0,False,False,False],
1091 "REQ" : ["pR","gJrel",0o367,0,0,False,False,False], # alias of RZR
1092 "RNE" : ["pR","gJrel",0o366,0,0,False,False,False], # alias of RNZ
1093 "RGE" : ["pR","gJrel",0o365,0,0,False,False,False], # alias of RPS
1094 "RLT" : ["pR","gJrel",0o364,0,0,False,False,False], # alias of RNG
1095 "RHS" : ["pR","gJrel",0o373,0,0,False,False,False], # alias of RCY
1096 "RLO" : ["pR","gJrel",0o372,0,0,False,False,False], # alias of RNC
1097#
1098# conditional exit pseudo op
1099#
1100 "EXNO" : ["pEx","gJrel",0o361,0,0,False,False,False],
1101 "EXOD" : ["pEx","gJrel",0o362,0,0,False,False,False],
1102 "EXEV" : ["pEx","gJrel",0o363,0,0,False,False,False],
1103 "EXNG" : ["pEx","gJrel",0o364,0,0,False,False,False],
1104 "EXPS" : ["pEx","gJrel",0o365,0,0,False,False,False],
1105 "EXNZ" : ["pEx","gJrel",0o366,0,0,False,False,False],
1106 "EXZR" : ["pEx","gJrel",0o367,0,0,False,False,False],
1107 "EXEN" : ["pEx","gJrel",0o370,0,0,False,False,False],
1108 "EXEZ" : ["pEx","gJrel",0o371,0,0,False,False,False],
1109 "EXNC" : ["pEx","gJrel",0o372,0,0,False,False,False],
1110 "EXCY" : ["pEx","gJrel",0o373,0,0,False,False,False],
1111 "EXLZ" : ["pEx","gJrel",0o374,0,0,False,False,False],
1112 "EXLN" : ["pEx","gJrel",0o375,0,0,False,False,False],
1113 "EXRZ" : ["pEx","gJrel",0o376,0,0,False,False,False],
1114 "EXRN" : ["pEx","gJrel",0o377,0,0,False,False,False],
1115 "EXEQ" : ["pEx","gJrel",0o367,0,0,False,False,False], # alias of RZR
1116 "EXNE" : ["pEx","gJrel",0o366,0,0,False,False,False], # alias of RNZ
1117 "EXGE" : ["pEx","gJrel",0o365,0,0,False,False,False], # alias of RPS
1118 "EXLT" : ["pEx","gJrel",0o364,0,0,False,False,False], # alias of RNG
1119 "EXHS" : ["pEx","gJrel",0o373,0,0,False,False,False], # alias of RCY
1120 "EXLO" : ["pEx","gJrel",0o372,0,0,False,False,False], # alias of RNC
1121 "LOOP" : ["pLoop","gNil",0,0,0,False,False,False],
1122 "ENDIF" : ["pEndif","gNil",0,0,0,False,False,False],
1123 "ELSE" : ["pElse","gJrel",0o360,0,0,False,False,False],
1124#
1125# compatibility pseudo ops
1126#
1127 "DEF" : ["pDef","gData",0,1,1,False,True,False],
1128 "VAL" : ["pDef","gData",0,1,1,False,True,False],
1129#
1130# ncas pseudo ops
1131#
1132 "END" : ["pEnd","gNil",0,0,0,False,False,False],
1133 "BSS" : ["pBss","gGenZ",0,1,1,False,True,False],
1134 "ADDR" : ["pEqu","gNil",0,1,1,False,True,False],
1135 "EQU" : ["pEqu","gNil",0,1,1,False,True,False],
1136 "GTO" : ["pGto","gGto",0,1,1,True,False,False],
1137 "ORG" : ["pOrg","gNil",0,1,1,False,True,False],
1138 "ABS" : ["pAbs","gNil",0,0,0,False,True,False],
1139 ".SET" : ["pCondSet","gNil",0,1,1,False,True,False],
1140 ".CLR" : ["pCondClr","gNil",0,1,1,False,True,False],
1141 ".IFSET" : ["pCondIfSet","gNil",0,1,1,False,True,True],
1142 ".IFNSET" : ["pCondIfNotSet","gNil",0,1,1,False,True,True],
1143 ".IFDEF" : ["pCondIfDef","gNil",0,1,1,False,True,True],
1144 ".IFNDEF" : ["pCondIfNotDef","gNil",0,1,1,False,True,True],
1145 ".ENDIF" : ["pCondEndif","gNil",0,0,0,False,True,True],
1146 ".ELSE" : ["pCondElse","gNil",0,0,0,False,True,True],
1147 "INCLUDE" : ["pInc","gNil",0,1,1,False,True,False],
1148 "DATA" : ["pData","gData",0,1,OPCODES.NUM_OPERANDS_ANY,False,True,False],
1149 "TITLE" : ["pHed","gHed",0,1,1,False,True,False],
1150 "STE" : ["pSte","gSte",0o235,0,0,False,False,False],
1151# "NOP" : ["pNoPer","gdirect",0o235,0,0,False,False,False],
1152 "NOP" : ["pNoPer","gdirect",0o220,0,0,False,False,False], # Karma NOP
1153 "NOP1" : ["pNoPer","gdirect",0o336,0,0,False,False,False], # see Series 80 wiki
1154 })
1156 def addTimeDateSyms(self,isRegressiontest):
1157#
1158# Add predefined symbols to global dictionary
1159#
1160 if (isRegressiontest):
1161 bcdYear=0
1162 bcdMonth=0
1163 bcdDay=0
1164 bcdHour=0
1165 bcdMin=0
1166 bcdSec=0
1167 seconds1900=2208988800
1168 else:
1169 dt=clsDateTime()
1170 bcdYear=dt.bcdYear
1171 bcdMonth=dt.bcdMonth
1172 bcdDay=dt.bcdDay
1173 bcdHour=dt.bcdHour
1174 bcdMin=dt.bcdMin
1175 bcdSec=dt.bcdSec
1176 seconds1970=int(time.time())
1177 seconds1900=seconds1970+2208988800
1179 self.__globVar__.symDict.extendGlobalSymbols('BCD_YEAR',[1,bcdYear])
1180 self.__globVar__.symDict.extendGlobalSymbols('BCD_MONTH',[1,bcdMonth])
1181 self.__globVar__.symDict.extendGlobalSymbols('BCD_DAY',[1,bcdDay])
1182 self.__globVar__.symDict.extendGlobalSymbols('BCD_HOUR',[1,bcdHour])
1183 self.__globVar__.symDict.extendGlobalSymbols('BCD_MIN',[1,bcdMin])
1184 self.__globVar__.symDict.extendGlobalSymbols('BCD_SEC',[1,bcdSec])
1185 self.__globVar__.symDict.extendGlobalSymbols('SECONDS1900',[1,seconds1900])
1186 return
1188#
1189# Assemble method. The method takes the values of the command line
1190# switches and parameters. This method may be called multiple times
1191# with different parameters.
1192# Returns:
1193# False: everything o.k.
1194# True: errors in assembly
1195# Raises capasmError on I/O error
1196#
1197 def assemble(self,sourceFileName,binFileName="",listFileName="", \
1198 referenceOpt=1, pageSize=66, pageWidth=80, \
1199 extendedChecks=False,useOct=False, definedFlags=[], \
1200 globalSymbolFile="none"):
1201#
1202# initialize opcode
1203#
1204 self.extendOpcodes()
1205#
1206# initialize error condition
1207#
1208 hasError=False
1209#
1210# Create global variables data object
1211#
1212 self.__globVar__=clsGlobVar()
1213 self.__globVar__.useHex= not useOct
1214 self.__sourceFileName__= sourceFileName
1215 self.__globalSymbolFile__= globalSymbolFile
1216 self.__globVar__.progName="NCAS"
1217#
1218# initialize basic parser functions
1219#
1220 parseFunc.DELIMITER="'"+'"'
1221 parseFunc.LABELMATCHSTRING=\
1222 "[A-Za-z][A-Za-z0-9_$\+\-\.#/?\(\!\&)=:<>\|@*^]{0,"
1223#
1224# Build file name of object file if not specified
1225#
1226 if binFileName=="":
1227 self.__binFileName__= \
1228 Path(self.__sourceFileName__).with_suffix(".bin").name
1229 else:
1230 self.__binFileName__=binFileName
1231 self.__listFileName__= listFileName
1232 self.__referenceOpt__= referenceOpt
1233 self.__pageSize__= pageSize
1234 self.__pageWidth__= pageWidth
1235 self.__extendedChecks__= extendedChecks
1236 self.__symNamLen__= 32
1237#
1238# Check if we run in regression test mode
1239#
1240 if os.getenv("CAPASMREGRESSIONTEST"):
1241 self.__globVar__.isRegressionTest=True
1242#
1243# Create symbol table object
1244#
1245 self.__globVar__.symDict=clsSymDict( self.__extendedChecks__, \
1246 self.__globalSymbolFile__, \
1247 { clsSymDict.SYM_DAD: "ADR", \
1248 clsSymDict.SYM_EQU: "EQU", \
1249 clsSymDict.SYM_LCL: "LCL" })
1250#
1251# add time and date global symbols
1252#
1253 self.addTimeDateSyms(self.__globVar__.isRegressionTest)
1254#
1255# Create conditional assembly object
1256#
1257 self.__globVar__.condAssembly=clsConditionalAssembly(definedFlags)
1258#
1259# get directory of source file
1260#
1261 self.__globVar__.sourceFileDirectory=\
1262 str(Path(self.__sourceFileName__).parent)
1263#
1264# Check extended checks mode
1265#
1266 if self.__extendedChecks__:
1267 self.__globVar__.allowHashRLiteral=False
1268#
1269# Set symbol name length
1270#
1271 self.__globVar__.symNamLen=self.__symNamLen__
1272#
1273# Pass 1: scan and parse lines, accumulate results in the
1274# pass1Info list
1275#
1276 pass1Info=[]
1277 infile=clsSourceReader(self.__sourceFileName__)
1278 lineScanner=clsLineScanner("*",";","'`^"+'"')
1279 lineParser=clsParser(self.__globVar__,infile)
1281 while not self.__globVar__.isFin:
1282 line=infile.read()
1283 if line is None:
1284 if pass1Info:
1285 pass1Info[-1].messages.append(MESSAGE.E_MISSING_FIN)
1286 break
1287 else:
1288 MESSAGE.fatalError("Empty source file")
1289#
1290# Scan line
1291#
1292 scannedLine=lineScanner.scanLine(line)
1293#
1294# Parse line
1295#
1296 parsedLine=lineParser.parseLine(scannedLine,line)
1297 pass1Info.append(parsedLine)
1298#
1299# Increment PC and codeLen with length of instructions
1300#
1301 self.__globVar__.PC+=parsedLine.opcodeLen
1302 self.__globVar__.codeLen+=parsedLine.opcodeLen
1304 infile=None
1305 lineScanner=None
1306 lineParser=None
1307#
1308# Passe 2: process content of pass1Info list, generate code,
1309# write code to binary output file and output information to
1310# the list file
1311#
1312 objWriter=clsObjWriter(self.__binFileName__)
1313 listWriter=clsListWriter(self.__globVar__,self.__listFileName__, \
1314 self.__pageSize__, self.__pageWidth__)
1315 codeGenerator=clsCodeGenerator(self.__globVar__)
1317 for parsedLine in pass1Info:
1318#
1319# Generate code
1320#
1321 codeInfo=codeGenerator.generate(parsedLine)
1322#
1323# Write code
1324#
1325 objWriter.writeCode(codeInfo,parsedLine)
1326#
1327# Write listing
1328#
1329 listWriter.writeLine(parsedLine,codeInfo)
1331 codeGenerator=None
1332 objWriter=None
1334 listWriter.writeSymbols(self.__referenceOpt__)
1335 listWriter.writeStatistics()
1336 listWriter=None
1337#
1338# delete objectfile if any errors
1339#
1340 if self.__globVar__.errorCount>0:
1341 os.remove(self.__binFileName__)
1342 hasError=True
1343 self.__globVar__=None
1344#
1345# return error condition
1346#
1347 return hasError
1348#
1349# custom arg checks ----------------------------------------------------
1350#
1351# Helper function to check the range for the page size parameter
1352#
1353class argPageSizeCheck(argparse.Action): # pragma: no cover
1354 def __call__(self, parser, namespace, values, option_string=None):
1355 if values < 40 or values> 100:
1356 parser.error("Valid range for {0} is 40 to 100".format(option_string))
1357 setattr(namespace, self.dest, values)
1359#
1360# Helper function to check the range for the line width parameter
1361#
1362class argWidthCheck(argparse.Action): # pragma: no cover
1363 def __call__(self, parser, namespace, values, option_string=None):
1364 if values < 80 or values> 132:
1365 parser.error("Valid range for {0} is 80 to 132".format(option_string))
1366 setattr(namespace, self.dest, values)
1370#
1371# Entry point ncas --------------------------------------------------------
1372#
1373# This entry point parses the command line parameters, creates an
1374# assembler object and executes it with the parsed command line parameters
1375#
1376def ncas(): # pragma: no cover
1377#
1378# Command line arguments processing
1379#
1380 argparser=argparse.ArgumentParser(description=\
1381 "An assembler for the Hewlett Packard HP-75",\
1382 epilog=\
1383 "See https://github.com/bug400/capasm for details. "+CAPASM_VERSION)
1386 argparser.add_argument("sourcefile",help="source code file (required)")
1387 argparser.add_argument("-b","--binfile",\
1388 help="binary object code file (default: sourcefilename with suffix .bin",\
1389 default="")
1390 argparser.add_argument("-l","--listfile",\
1391 help="list file (default: no list file)",default="")
1392 argparser.add_argument("-g","--globalsymbolfile",\
1393 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: 75)",default="75")
1394 argparser.add_argument("-r","--reference",type=int,default=1,\
1395 help="symbol reference 0:none, 1:short, 2:full (default:1)",\
1396 choices=[0,1,2])
1397 argparser.add_argument("-p","--pagesize",type=int,default=66, \
1398 help="lines per page (default: 66)",action=argPageSizeCheck)
1399 argparser.add_argument("-w","--width",type=int,default=80, \
1400 help="page width (default:80)",action=argWidthCheck)
1401 argparser.add_argument("-c","--check",help="activate additional checks", \
1402 action='store_true')
1403 argparser.add_argument("-d","--define",action='append',default=[],\
1404 help="define conditional flag with value True")
1405 argparser.add_argument("-o","--oct",help="use octal output", \
1406 action='store_true')
1407 args= argparser.parse_args()
1408#
1409# Create assembler object and run it
1410#
1411 ncas= clsNcas()
1412 try:
1413 ret=ncas.assemble(args.sourcefile,listFileName=args.listfile,\
1414 binFileName=args.binfile, referenceOpt=args.reference, \
1415 pageSize=args.pagesize,pageWidth=args.width, \
1416 extendedChecks=args.check, \
1417 useOct=args.oct,\
1418 definedFlags=args.define, \
1419 globalSymbolFile=args.globalsymbolfile)
1420 except capasmError as e:
1421 print(e.msg+" -- Assembler terminated")
1422 ret=True
1423 if ret:
1424 sys.exit(1)
1425#
1426# Run the capasm procedure, if this file is called as top level script
1427#
1428if __name__ == '__main__': # pragma: no cover
1429 ncas()