Coverage for capasm/capcommon.py : 95%

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/python3
2# -*- coding: utf-8 -*-
3#
4# This module contains the common code for all programs
5#
6# (c) 2020 Joachim Siebold
7#
8# This program is free software; you can redistribute it and/or
9# modify it under the terms of the GNU General Public License
10# as published by the Free Software Foundation; either version 2
11# of the License, or (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License
19# along with this program; if not, write to the Free Software
20# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21#
22#--------------------------------------------------------------------------
23#
24# Changelog
25#
26# 02.01.2021 jsi
27# - solved issue #2, incorrect range check of relative jumps
28# 08.01.2021 jsi
29# - extended check marks redefinition of global symbols if there is a type or
30# value mismatch
31# - suppress superfluous code output of BSZ pseudo-ops
32# - line numbers in list file
33# - parsing of conditional assembly pseudo-ops fixed
34#
35import re,os,sys,importlib,datetime
36from pathlib import Path
38#
39# Program Constants -----------------------------------------------------
40#
41CAPASM_VERSION="Version 1.0.0"
42CAPASM_VERSION_DATE="January 2021"
44#
45# CAPASM custom exception -----------------------------------------------
46# The assembler raises this exception, if a fatal error occurred
47#
48class capasmError(Exception):
49 def __init__(self,msg):
50 super().__init__()
51 self.msg= msg
53#
54# Class to generates Date/Time as BCD ---------------------------------------
55#
56class clsDateTime(object):
58 def __init__(self):
59 super().__init__()
60 now=datetime.datetime.now()
61 self.bcdYear= self.intToBcd(now.date().year-2000)
62 self.bcdMonth= self.intToBcd(now.date().month)
63 self.bcdDay= self.intToBcd(now.date().day)
64 self.bcdHour= self.intToBcd(now.time().hour)
65 self.bcdMin= self.intToBcd(now.time().minute)
66 self.bcdSec= self.intToBcd(now.time().second)
68 def intToBcd(self,value):
69 return (((int(value/10)%10)<<4)+(value%10))
70#
71# Static class for the bytes to store check --------------------------------
72#
73# Returns the number of bytes that can be stored according to the
74# data register and the design of the CPU register bank
75#
76class BYTESTOSTORE(object):
78 __dictBytesToStore__= {
79 0o0: 2, # R(*) ptr
80 0o1: 0,
81 0o2: 2, # X
82 0o3: 1, # X
83 0o4: 2, # PC
84 0o5: 1, # PC
85 0o6: 2, # begin of GP 2 byte sections
86 0o7: 1,
88 0o10: 2,
89 0o11: 1,
90 0o12: 2,
91 0o13: 1,
92 0o14: 2,
93 0o15: 1,
94 0o16: 2,
95 0o17: 1,
97 0o20: 2,
98 0o21: 1,
99 0o22: 2,
100 0o23: 1,
101 0o24: 2,
102 0o25: 1,
103 0o26: 2,
104 0o27: 1,
106 0o30: 2,
107 0o31: 1,
108 0o32: 2,
109 0o33: 1,
110 0o34: 2,
111 0o35: 1,
112 0o36: 2,
113 0o37: 1,
115 0o40: 8, # begin of GP 8 byte sections
116 0o41: 7,
117 0o42: 6,
118 0o43: 5,
119 0o44: 4,
120 0o45: 3,
121 0o46: 2,
122 0o47: 1,
124 0o50: 8,
125 0o51: 7,
126 0o52: 6,
127 0o53: 5,
128 0o54: 4,
129 0o55: 3,
130 0o56: 2,
131 0o57: 1,
133 0o60: 8,
134 0o61: 7,
135 0o62: 6,
136 0o63: 5,
137 0o64: 4,
138 0o65: 3,
139 0o66: 2,
140 0o67: 1,
142 0o70: 8,
143 0o71: 7,
144 0o72: 6,
145 0o73: 5,
146 0o74: 4,
147 0o75: 3,
148 0o76: 2,
149 0o77: 1
150 }
152 @classmethod
153 def numBytes(cls,reg):
154 if reg<0:
155 return None
156 else:
157 return BYTESTOSTORE.__dictBytesToStore__[reg]
158#
159# Static class for number and label parsing ----------------------------------
160#
161class parseFunc(object):
163 DELIMITER=""
164 LABELMATCHSTRING=""
167# Parse quoted string
168#
169 @staticmethod
170 def parseQuotedString(string):
171 if string[0] not in parseFunc.DELIMITER:
172 return None
173 if string[0]!=string[-1]:
174 return None
175 string=string[1:len(string)-1]
177 return string
178#
179# Parse quoted or unquoted string
180#
181 @staticmethod
182 def parseAnyString(string):
183 if string[0] in parseFunc.DELIMITER:
184 return parseFunc.parseQuotedString(string)
185 else:
186 return string
187#
188# Parse label
189#
190 @staticmethod
191 def parseLabel(string,length):
192 match=re.fullmatch(parseFunc.LABELMATCHSTRING+ str(length)+"}",string)
193 if match:
194 return string
195 else:
196 return None
197#
198# Parse decimal number (without D at the end, e.g. line numbers)
199#
200 @staticmethod
201 def parseDecimal(string):
202 try:
203 val=int(string,10)
204 return None if val < 0 else val
205 except ValueError:
206 return None
207#
208# Parse hex number
209#
210 @staticmethod
211 def parseHex(string):
212 try:
213 val=int(string,16)
214 return None if val < 0 else val
215 except ValueError:
216 return None
218#
219# Parse binary number
220#
221 @staticmethod
222 def parseBin(string):
223 try:
224 val=int(string,2)
225 return None if val < 0 else val
226 except ValueError:
227 return None
228#
229# Parse octal number
230#
231 @staticmethod
232 def parseOctal(string):
233 try:
234 val=int(string,8)
235 return None if val < 0 else val
236 except ValueError:
237 return None
238#
239# Parse BCD number (with a C at the end)
240#
241 @staticmethod
242 def parseBCD(string):
243 retVal=0
244 for c in string:
245 if c in "0123456789":
246 retVal=(retVal<<4) | ord(c)-ord("0")
247 else:
248 return None
249 return retVal
250#
251# Parse Kbyte number (with a K at the end)
252#
253 @staticmethod
254 def parseKB(string):
255 try:
256 val=int(string,10)*1024
257 return None if val < 0 else val
258 except ValueError:
259 return None
261#
262# Parse number, guess the type from the type attribute character at the end
263# If the number has no attribute, then it is an ocal number
264#
265 @staticmethod
266 def parseNumber(string):
267 retval=0
268 if string[-1] in "Dd":
269 return parseFunc.parseDecimal(string[:-1])
270 elif string[-1] in "Cc":
271 return parseFunc.parseBCD(string[:-1])
272 elif string[-1] in "Hh#":
273 return parseFunc.parseHex(string[:-1])
274 elif string[-1] in "Bb":
275 return parseFunc.parseBin(string[:-1])
276 elif string[-1] in "OoQq":
277 return parseFunc.parseOctal(string[:-1])
278 elif string[-1] in "Kk":
279 return parseFunc.parseKB(string[:-1])
280 elif string[-1] in "0123456789":
281 return parseFunc.parseOctal(string)
282 else:
283 return None
285#
286# Basic Static class for the opcode dictionary ----------------------------------
287#
288class OPCODES(object):
290 NUM_OPERANDS_ANY=-1
291#
292# Each opcode is associated to a list with the items:
293# - parse method
294# - code generator method
295# - instruction or instruction template which must be completed later
296# - number of operand parameters min
297# - number of operand parameters max
298# for ARP/DRP optimization in IF-ELSE-ENDIF clauses:
299# - flag if opcode is an unconditional jump (GTO, RTN, JSBN)
300# - flag if opcode does not executable code
301# for the processing of conditional assembly pseudo-ops
302# - flag if opcode is an IF/ELSE/ENDIF conditional pseudo op
303#
304 __opcodeDict__= {
305 "ARP" : ["pArp","gdarp",0o0,1,1,False,False,False],
306 "DRP" : ["pDrp","gdarp",0o100,1,1,False,False,False],
307 "ELB" : ["p1reg","gdirect",0o200,1,1,False,False,False],
308 "ELM" : ["p1reg","gdirect",0o201,1,1,False,False,False],
309 "ERB" : ["p1reg","gdirect",0o202,1,1,False,False,False],
310 "ERM" : ["p1reg","gdirect",0o203,1,1,False,False,False],
311 "LLB" : ["p1reg","gdirect",0o204,1,1,False,False,False],
312 "LLM" : ["p1reg","gdirect",0o205,1,1,False,False,False],
313 "LRB" : ["p1reg","gdirect",0o206,1,1,False,False,False],
314 "LRM" : ["p1reg","gdirect",0o207,1,1,False,False,False],
315 "ICB" : ["p1reg","gdirect",0o210,1,1,False,False,False],
316 "ICM" : ["p1reg","gdirect",0o211,1,1,False,False,False],
317 "DCB" : ["p1reg","gdirect",0o212,1,1,False,False,False],
318 "DCM" : ["p1reg","gdirect",0o213,1,1,False,False,False],
319 "TCB" : ["p1reg","gdirect",0o214,1,1,False,False,False],
320 "TCM" : ["p1reg","gdirect",0o215,1,1,False,False,False],
321 "NCB" : ["p1reg","gdirect",0o216,1,1,False,False,False],
322 "NCM" : ["p1reg","gdirect",0o217,1,1,False,False,False],
323 "TSB" : ["p1reg","gdirect",0o220,1,1,False,False,False],
324 "TSM" : ["p1reg","gdirect",0o221,1,1,False,False,False],
325 "CLB" : ["p1reg","gdirect",0o222,1,1,False,False,False],
326 "CLM" : ["p1reg","gdirect",0o223,1,1,False,False,False],
327 "ORB" : ["pOrXr","gdirect",0o224,2,2,False,False,False],
328 "ORM" : ["pOrXr","gdirect",0o225,2,2,False,False,False],
329 "XRB" : ["pOrXr","gdirect",0o226,2,2,False,False,False],
330 "XRM" : ["pOrXr","gdirect",0o227,2,2,False,False,False],
331 "BIN" : ["pNoPer","gdirect",0o230,0,0,False,False,False],
332 "BCD" : ["pNoPer","gdirect",0o231,0,0,False,False,False],
333 "SAD" : ["pNoPer","gdirect",0o232,0,0,False,False,False],
334 "DCE" : ["pNoPer","gdirect",0o233,0,0,False,False,False],
335 "ICE" : ["pNoPer","gdirect",0o234,0,0,False,False,False],
336 "CLE" : ["pNoPer","gdirect",0o235,0,0,False,False,False],
337 "PAD" : ["pNoPer","gdirect",0o237,0,0,False,False,False],
338 "LDB" : ["pLdSt","gLdSt",0o240,2,10,False,False,False],
339 "LDBI" : ["pLdSt","gLdSt",0o240,2,NUM_OPERANDS_ANY,False,False,False],
340 "LDBD" : ["pLdSt","gLdSt",0o240,2,NUM_OPERANDS_ANY,False,False,False],
341 "LDM" : ["pLdSt","gLdSt",0o241,2,NUM_OPERANDS_ANY,False,False,False],
342 "LDMI" : ["pLdSt","gLdSt",0o241,2,NUM_OPERANDS_ANY,False,False,False],
343 "LDMD" : ["pLdSt","gLdSt",0o241,2,NUM_OPERANDS_ANY,False,False,False],
344 "STB" : ["pLdSt","gLdSt",0o242,2,NUM_OPERANDS_ANY,False,False,False],
345 "STBI" : ["pLdSt","gLdSt",0o242,2,NUM_OPERANDS_ANY,False,False,False],
346 "STBD" : ["pLdSt","gLdSt",0o242,2,NUM_OPERANDS_ANY,False,False,False],
347 "STM" : ["pLdSt","gLdSt",0o243,2,NUM_OPERANDS_ANY,False,False,False],
348 "STMI" : ["pLdSt","gLdSt",0o243,2,NUM_OPERANDS_ANY,False,False,False],
349 "STMD" : ["pLdSt","gLdSt",0o243,2,NUM_OPERANDS_ANY,False,False,False],
350 "CMB" : ["pAri","gAri",0o300,2,NUM_OPERANDS_ANY,False,False,False],
351 "CMM" : ["pAri","gAri",0o301,2,NUM_OPERANDS_ANY,False,False,False],
352 "CMBD" : ["pAri","gAri",0o300,2,NUM_OPERANDS_ANY,False,False,False],
353 "CMMD" : ["pAri","gAri",0o301,2,NUM_OPERANDS_ANY,False,False,False],
354 "ADB" : ["pAri","gAri",0o302,2,NUM_OPERANDS_ANY,False,False,False],
355 "ADM" : ["pAri","gAri",0o303,2,NUM_OPERANDS_ANY,False,False,False],
356 "ADBD" : ["pAri","gAri",0o302,2,NUM_OPERANDS_ANY,False,False,False],
357 "ADMD" : ["pAri","gAri",0o303,2,NUM_OPERANDS_ANY,False,False,False],
358 "SBB" : ["pAri","gAri",0o304,2,NUM_OPERANDS_ANY,False,False,False],
359 "SBM" : ["pAri","gAri",0o305,2,NUM_OPERANDS_ANY,False,False,False],
360 "SBBD" : ["pAri","gAri",0o304,2,NUM_OPERANDS_ANY,False,False,False],
361 "SBMD" : ["pAri","gAri",0o305,2,NUM_OPERANDS_ANY,False,False,False],
362 "ANM" : ["pAri","gAri",0o307,2,NUM_OPERANDS_ANY,False,False,False],
363 "ANMD" : ["pAri","gAri",0o307,2,NUM_OPERANDS_ANY,False,False,False],
364 "JSB" : ["pJsb","gJsb",0o306,1,2,False,False,False],
365 "POBD" : ["pStack","gStack",0o340,2,2,False,False,False],
366 "POMD" : ["pStack","gStack",0o341,2,2,False,False,False],
367 "PUBD" : ["pStack","gStack",0o344,2,2,False,False,False],
368 "PUMD" : ["pStack","gStack",0o345,2,2,False,False,False],
369 "POBI" : ["pStack","gStack",0o350,2,2,False,False,False],
370 "POMI" : ["pStack","gStack",0o351,2,2,False,False,False],
371 "PUBI" : ["pStack","gStack",0o354,2,2,False,False,False],
372 "PUMI" : ["pStack","gStack",0o355,2,2,False,False,False],
373#
374# Jump and conditional jump Instructions
375#
376 "JMP" : ["pJrel","gJrel",0o360,1,1,True,False,False],
377 "JNO" : ["pJrel","gJrel",0o361,1,1,False,False,False],
378 "JOD" : ["pJrel","gJrel",0o362,1,1,False,False,False],
379 "JEV" : ["pJrel","gJrel",0o363,1,1,False,False,False],
380 "JNG" : ["pJrel","gJrel",0o364,1,1,False,False,False],
381 "JPS" : ["pJrel","gJrel",0o365,1,1,False,False,False],
382 "JNZ" : ["pJrel","gJrel",0o366,1,1,False,False,False],
383 "JZR" : ["pJrel","gJrel",0o367,1,1,False,False,False],
384 "JEN" : ["pJrel","gJrel",0o370,1,1,False,False,False],
385 "JEZ" : ["pJrel","gJrel",0o371,1,1,False,False,False],
386 "JNC" : ["pJrel","gJrel",0o372,1,1,False,False,False],
387 "JCY" : ["pJrel","gJrel",0o373,1,1,False,False,False],
388 "JLZ" : ["pJrel","gJrel",0o374,1,1,False,False,False],
389 "JLN" : ["pJrel","gJrel",0o375,1,1,False,False,False],
390 "JRZ" : ["pJrel","gJrel",0o376,1,1,False,False,False],
391 "JRN" : ["pJrel","gJrel",0o377,1,1,False,False,False],
392 }
393 __condAssemblyOpcodes__= []
394#
395# extend the basic opcodes above with the assembler pseudo ops
396#
397 @classmethod
398 def extendDict(cls,extendedOpcodes):
399 OPCODES.__opcodeDict__.update(extendedOpcodes)
400#
401# get opcode information
402#
403 @classmethod
404 def get(cls,opcode):
405 lookUp=opcode
406 if lookUp in OPCODES.__opcodeDict__.keys():
407 return OPCODES.__opcodeDict__[lookUp]
408 else:
409 return []
414#
415# Error Messages static class --------------------------------------------
416#
417class MESSAGE(object):
418#
419# Errors
420#
421 E_ILL_OPCODE = 0
422 E_ILL_REGISTER= 1
423 E_ILL_LABEL = 2
424 E_DUP_LABEL = 3
425 E_ILL_NUMOPERANDS= 4
426 E_REGISTERSIGN=5
427 E_XREGEXPECTED=6
428 E_ILLADDRESSMODE=7
429 E_SYMNOTFOUND=8
430 E_NUMBERTOOLARGE=9
431 E_OPEXCEEDSSECTION=10
432 E_SIGNEDREGISTER=11
433 E_ILLNUMBER=12
434 E_ILLSTRING=13
435 E_ILL_LABELOP=14
436 E_ILL_LINENUMBER=15
437 E_NOTALLOWED_HERE=16
438 E_ILL_PROGNAME=17
439 E_RELJUMP_TOOLARGE=19
440 E_ILL_LITOPERAND=20
441 E_ILL_LITERALLENGTH=21
442 E_MISSING_LABEL=23
443 E_FLAGNOTDEFINED=25
444 E_AIFEIFMISMATCH=26
445 E_ILLFLAGNAME=27
446 E_MISSING_FIN=28
447 E_ROM_EXPECTED=29
448 E_PCGREATERTHANADDRESS=30
449 E_ILL_ADDRESS= 31
450 E_MISSINGRPAREN=100
451 E_DIVBYZERO=101
452 E_ILLEXPRESSION=102
453 E_INVALIDSIZESPEC=103
454 E_VALTOOLARGE=104
455 E_ILLQUOTSTRING=105
456 E_UNSIZEDEXPRESSION=106
457 E_ILLVALUE=107
458 E_ILLSTRUCT=110
459#
460# Warnings
461#
462 W_LITDATADOESNOTFILL=1001
463 W_REDEF_DOES_NOT_MATCH=1002
464 W_RHASH_LITERAL=1003
467 messages= {
468#
469# Error messages
470#
471 E_ILL_OPCODE : "Illegal opcode or pseudo opcode",
472 E_ILL_REGISTER: "Illegal register",
473 E_ILL_LABEL: "Illegal label in label field",
474 E_DUP_LABEL: "Label in label field is already defined",
475 E_ILL_NUMOPERANDS: "Illegal number of operands",
476 E_REGISTERSIGN: "+/- not allowed in register definition",
477 E_XREGEXPECTED: "X register expected as second operand",
478 E_ILLADDRESSMODE: "Illegal address mode",
479 E_SYMNOTFOUND: "Symbol not found",
480 E_NUMBERTOOLARGE: "Number too large",
481 E_OPEXCEEDSSECTION: "Literal or label data exceed section boundary",
482 E_SIGNEDREGISTER: "+/- required for address register",
483 E_ILLNUMBER: "Illegal number",
484 E_ILLSTRING: "Illegal string",
485 E_ILL_LABELOP: "Illegal label in operand field",
486 E_ILL_LINENUMBER: "Illegal line number",
487 E_NOTALLOWED_HERE: "Pseudo opcode not allowed here",
488 E_ILL_PROGNAME: "Illegal program name",
489 E_RELJUMP_TOOLARGE: "Relative jump too large",
490 E_ILL_LITOPERAND: "Illegal literal operand",
491 E_ILL_LITERALLENGTH: "Illegal byte length of literal operand",
492 E_MISSING_LABEL: "Missing label in label field",
493 E_FLAGNOTDEFINED: "Flag not defined",
494 E_AIFEIFMISMATCH: "AIF/EIF mismatch",
495 E_ILLFLAGNAME: "Illegal flag name",
496 E_MISSING_FIN: "Missing FIN/END statement",
497 E_ROM_EXPECTED: "ROM expected",
498 E_ILL_ADDRESS: "Illegal Address",
499 E_MISSINGRPAREN: "Missing ) in expression",
500 E_DIVBYZERO: "Division by zero in expression",
501 E_ILLEXPRESSION: "Illegal expression",
502 E_INVALIDSIZESPEC: "Illegal size specified",
503 E_VALTOOLARGE: "Value too large for size specified",
504 E_ILLQUOTSTRING: "Illegal quoted string",
505 E_UNSIZEDEXPRESSION: "Unsized expression(s)",
506 E_ILLVALUE: "Illagal Value",
507 E_ILLSTRUCT: "Illegal IF/ELSE/ENDIF or LOOP/EX/WHILE",
508 E_PCGREATERTHANADDRESS: "PC greater than the specified address",
509#
510# Warning messages
511#
512 W_LITDATADOESNOTFILL: "Literal data list does not fill register section",
513 W_REDEF_DOES_NOT_MATCH: "Value/type mismatch of redefined global symbol",
514 W_RHASH_LITERAL: "Dangerous R#, cannot check section boundary",
516 }
518#
519# Get message text for a message number
520#
521 def getMsg(msgno):
522 if msgno < 1000:
523 sv="ERROR"
524 else:
525 sv="WARNING"
526 return sv,MESSAGE.messages[msgno]
527#
528# Fatal error handler (I/O errors etc.). Raise custom exception
529#
530 @staticmethod
531 def fatalError(msg):
532 raise capasmError(msg)
534#
535# Symbol dictionary class -------------------------------------
536#
537class clsSymDict(object):
538#
539# Symbol types
540#
541 SYM_DAD=0
542 SYM_EQU=1
543 SYM_LCL=2
545 def __init__(self,extendedChecks,globalSymbolFile,dictSymTypes):
546 super().__init__()
548 self.__extendedChecks__=extendedChecks
549 self.__symbols__= { }
550 self.__dictSymbolTypes__= dictSymTypes
551 self.__maxSymNameLength__=0
552#
553# Load global symbols
554#
555 if globalSymbolFile in ["85","87","75","none"]:
556 globalModuleName=".globals"+globalSymbolFile
557 try:
558 self.__globalSyms__=importlib.import_module(globalModuleName, \
559 package='capasm')
560 except :
561 MESSAGE.fatalError("Invalid global symbol file")
562 else:
563 globalSymbolFilePath=Path(globalSymbolFile)
564 suffix=globalSymbolFilePath.suffix.upper()
565 if suffix != ".PY":
566 MESSAGE.fatalError(\
567 "global symbol file does not have a .py suffix")
568 if not os.access(globalSymbolFile,os.R_OK):
569 MESSAGE.fatalError(\
570 "cannot open or read global symbol file")
571 try:
572 spec=importlib.util.spec_from_file_location(".globals",\
573 globalSymbolFile)
574 self.__globalSyms__=importlib.util.module_from_spec(spec)
575 spec.loader.exec_module(self.__globalSyms__)
576 except :
577 MESSAGE.fatalError("Invalid global symbol file")
578 return
579#
580# Enter new symbol, we have to check for duplicates in the global symbol
581# dictionary and this dictionary as well. Returns None if we have no
582# error or an error number otherwise
583#
584 def enter(self,name,typ,value,size,defLineInfo,refLineInfo= []):
585#
586# Diagnosic
587#
588 msg=None
589#
590# Check global dict, if global symbol was redefined
591#
592 ret=self.__globalSyms__.globalSymbols.get(name)
593 if ret is not None and self.__extendedChecks__:
594#
595# Extended check, warn if redefined global symbol does not match
596# with local symbol definition
597#
598 if ret[1]!=value or \
599 (ret[0]== 0 and (typ != 2 and typ !=0)) or \
600 (ret[0]== 1 and typ != 1):
601 msg=MESSAGE.W_REDEF_DOES_NOT_MATCH
602#
603# Check our own dict, return error if duplicate entry
604#
605 if name in self.__symbols__.keys():
606 msg= MESSAGE.E_DUP_LABEL
607#
608# Enter symbol, determine maximum length of symbol name (for reference list)
609#
610 else:
611 self.__symbols__[name]=[typ,value,size,defLineInfo,refLineInfo]
612 l=len(name)
613 if l > self.__maxSymNameLength__:
614 self.__maxSymNameLength__=l
615 return msg
616#
617# Get a symbol. We look first in our own symbol dictionary. If the
618# symbol is not found, try the Globals dictionary. If a symbol was
619# found in the Globals dictionary the insert it into the local dict.
620#
621 def get(self,name,lineInfo=None,noGlobStore=False):
622 try:
623 ret=self.__symbols__[name]
624 if lineInfo is not None:
625 if not self.__symbols__[name][4]:
626 self.__symbols__[name][4]=[lineInfo]
627 else:
628 self.__symbols__[name][4].append(lineInfo)
629 return ret
630 except KeyError:
631 ret=self.__globalSyms__.globalSymbols.get(name)
632 if ret:
633 typ=ret[0]
634 value=ret[1]
635 size=2
636 if typ==clsSymDict.SYM_EQU and value <= 0xFF:
637 size=1
638 if lineInfo is not None:
639 refLineInfo=[lineInfo]
640 else:
641 refLineInfo=[]
642 defLineInfo=None
643 if not noGlobStore:
644 self.enter(name,typ,value,size,defLineInfo,refLineInfo)
645 return typ,value,size,defLineInfo,refLineInfo
646#
647# Get a list of all symbols in the local dictionary
648#
649 def getList(self):
650 return list(self.__symbols__.keys())
652#
653# Get string for a symbol type
654#
655 def getSymTypeString(self,st):
656 return self.__dictSymbolTypes__[st]
657#
658# Get max leght of symbol names
659#
660 def getMaxSymNameLength(self):
661 return self.__maxSymNameLength__
662#
663# Extend the global symbol table
664#
665 def extendGlobalSymbols(self,key,value):
666 self.__globalSyms__.globalSymbols.symbols[key]=value
669#
670# Conditional assembling class ---------------------------------------------
671#
672# Conditional assembly controls with IF <condition> ENDIF or
673# IF <condition> ELSE ENDIF clauses whether a source file line is "active"
674# == has to be executed or "not active" == "has to be treated as comment".
675#
676# The conditional assembly class maintains the following data:
677# - flags : Dictionary of flag names and their conditions
678# The dictionary is populated by the set or the
679# clr method
680# - stack : The current state of the parsed conditions are pushed
681# on a stack, because conditions can be nested
682# - activeConditionIndex: Index of the "current" active condition.
683#
684class clsConditionalAssembly(object):
686 def __init__(self,definedFlags):
688 super().__init__()
689 self.__stack__= []
690 self.__flags__ = {}
691 self.__activeConditionIndex__= -1
693 for f in definedFlags:
694 self.__flags__[f]=True
695#
696# returns True, if the current condition is active
697#
698 def isActive(self):
699 return (len(self.__stack__)-1) == self.__activeConditionIndex__
700#
701# returns True, if we are within a condition
702#
703 def isOpen(self):
704 return len(self.__stack__) > 0
705#
706# return True, if lines are inactive and must be handeled as comment
707#
708 def isSuppressed(self):
709 if not self.isOpen():
710 return False
711 return self.__stack__[self.__activeConditionIndex__]
712#
713# endif, pop condition from stack
714#
715 def endif(self):
716 if self.isActive():
717 self.__activeConditionIndex__-=1
718 self.__stack__.pop()
719#
720# set flag
721#
722 def set(self,name):
723 self.__flags__[name]= True
724#
725# clear flag
726#
727 def clr(self,name):
728 self.__flags__[name]= False
729#
730# ifdef, push new condition on stack
731#
732 def ifdef(self,name):
733 if not self.isSuppressed():
734 self.__activeConditionIndex__+=1
735 if name in self.__flags__:
736 self.__stack__.append(False)
737 else:
738 self.__stack__.append(True)
740#
741# ifndef, push new condition on stack
742#
743 def ifndef(self,name):
744 if not self.isSuppressed():
745 self.__activeConditionIndex__+=1
746 if name in self.__flags__:
747 self.__stack__.append(True)
748 else:
749 self.__stack__.append(False)
750#
751# ifset, push new condition on stack
752#
753 def ifset(self,name):
754 if not self.isSuppressed():
755 self.__activeConditionIndex__+=1
756 try:
757 self.__stack__.append(self.__flags__[name]==False)
758 except KeyError:
759 self.__stack__.append(False)
760 if not self.isSuppressed():
761 return False
762 return True
763#
764# ifnset, push new condition on stack
765#
766 def ifnset(self,name):
767 if not self.isSuppressed():
768 self.__activeConditionIndex__+=1
769 try:
770 self.__stack__.append(self.__flags__[name]!=False)
771 except KeyError:
772 self.__stack__.append(False)
773 if not self.isSuppressed():
774 return False
775 return True
776#
777# else reverts the status of the topmost condition
778#
779 def els(self):
780 self.__stack__[-1]= not self.__stack__[-1]
782#
783# GlobVar data class, global variables of the assembler --------------------
784#
785class clsGlobVar(object):
787 def __init__(self):
789 super().__init__()
790 self.progName="" # program name
791 self.arpReg=-1 # current content of the ARP
792 self.drpReg=-1 # current content of the DRP
793 self.lastStmtWasPAD= False # PAD sets this to True
794 self.lastStmtWasJSB= False # JSB sets this to True
795 self.ORG=0 # ORG value set by ORG
796 self.PC=0 # Program counter
797 self.codeLen=0 # length of generated code
798 self.allowHashRLiteral=True # allow LD R#,=Literal
799 self.hasAbs=False # if ABS was used
800 self.hasNam=False # if NAM was used
801 self.hasIncludes=False # if any include files were processed
802 self.symNamLen=6 # symbol name length parameter
803 self.isRegressionTest=False # true to run assembler in regtest mode
804 self.isFin=False # FIN Statement encountered
805 self.useHex=False # output hex instead of oct
806 self.symDict=None # Symbol dictionary
807 self.errorCount=0 # Error counter
808 self.warningCount=0 # Warning counter
809 self.sourceFileDirectory="" # directory of source file if specified
810 self.condAssembly= None # conditional assembly object
811 self.symDict= None # global symbol dictionary object
812 self.title="" # title of print page
813 self.doPageBreak=False # do a page break
814 self.lastRtnAddr=-255 # address of last return
815 self.lastOpcodeWasJmp=False # flag, if last opcode was JMP or RTN
816#
817# Token data class, result of lexical scanner -----------------------------
818#
819class clsToken(object):
821 def __init__(self, string= "", position= 0, termChar=""):
822 self.string=string # this is the scanned token as string
823 self.position=position # the position of the scanned token in the
824 # source line
825 self.termChar=termChar # the char after the token that terminated
826 # scanning
828 def __repr__(self): # pragma: no cover
829 return ("clsToken object '{:s}' {:d} '{:s}'".format(self.string, self.position,self.termChar))
831#
832# Lexical Scanner class -------------------------------------------------
833#
834# The scanLine() method of the scanner takes a source line as input and
835# returns a list of clsToken objects:
836# [lineNumber,label,opcode,[scannedOperands]]
837#
838class clsLineScanner(object):
840#
841# init scanner:
842# commentLineChar: character(s) that indicate a line with a comment
843# commentTrailerChar: character(s) that idicate trailing comment
844# stringDelimiters: string delimiters
845#
847 def __init__(self,commentLineChar,commentTrailerChar,stringDelimiters):
848 super().__init__()
849 self.__commentLineChar__= commentLineChar
850 self.__commentTrailerChar__= commentTrailerChar
851 self.__stringDelimiters__= stringDelimiters
852#
853# Get one character, returns a tripel of [character, position, next character]
854#
855 def scanChar(self):
856 oldposition= self.__position__
857 oldch= self.__gch__
858 oldnxtch= self.__nxtChar__
859 if self.__gch__ != "":
860 self.__position__+=1
861 self.__gch__=self.__nxtChar__
862 if (self.__position__) >= len(self.__line__)-1:
863 self.__nxtChar__= ""
864 else:
865 self.__nxtChar__=self.__line__[self.__position__+1]
867 return [oldch, oldposition,oldnxtch]
868#
869# Get Token, returns a token object
870#
872 def scanTok(self,termSyms=None):
873#
874# Skip blanks
875#
876 while True:
877 char, pos, nxtChar=self.scanChar()
878 if char!=" ":
879 break
880 token=""
881 position= -1
882 termchar= ""
883 termString=" "
884 inString=False
885 if termSyms is not None:
886 termString+=termSyms
887 if char in termSyms:
888 return clsToken(char, pos, nxtChar)
889#
890# Loop until end of line, blank or termchar encountered
891#
892 while char!="":
893 if not inString and char in termString:
894 termchar= char
895 break
896#
897# String handling
898#
899 if char in self.__stringDelimiters__:
900 if not inString:
901 quote=char
902 inString=True
903 else:
904#
905# In string mode and we have a " or '
906#
907 if char==quote:
908 inString=False
909#
910# Accumulate token
911#
912 if len(token)==0:
913 position= pos
914 token+=char
915 char, pos, nxtChar=self.scanChar()
916 return clsToken(token, position, termchar)
917#
918# Scan input line and return scanned line number, label, opcode and a list
919# of operands. Missing items are None
920#
921 def scanLine(self,line):
923 scannedLineNumber=None
924 scannedLabel=None
925 scannedOpcode=None
926 scannedOperand=[]
928 self.__line__= line
929 self.__position__= -1
930 self.__gch__= ""
931 self.__nxtChar__= ""
932 if self.__line__!="":
933 self.__gch__=self.__line__[0]
934 self.__position__= 0
935 if len(line) > 1:
936 self.__nxtChar__= self.__line__[1]
937#
938# We have an empty line that contains nothing, return line count as token
939#
940 tok=self.scanTok()
941 if tok.string=="":
942 return [scannedLineNumber,scannedLabel,scannedOpcode,scannedOperand]
943#
944# Is the first token a line number?
945#
946 lineBegin=0
947 if tok.string[0] in "0123456789":
948 scannedLineNumber=tok
949 lineBegin=len(scannedLineNumber.string)+tok.position
950 tok=self.scanTok()
951#
952# No next token, leave ...
953#
954 if tok.string=="":
955 return [scannedLineNumber,scannedLabel,scannedOpcode,scannedOperand]
956#
957# Is the token a label?
958#
959 if tok.position <= lineBegin+2:
960 if tok.string[0] not in self.__commentLineChar__:
961 scannedLabel= tok
962 tok= self.scanTok()
963#
964# No next token, leave ...
965#
966 if tok.string=="":
967 return [scannedLineNumber,scannedLabel,scannedOpcode,scannedOperand]
968#
969# Do we have a comment?
970#
971 if tok.string[0] in self.__commentLineChar__ \
972 or tok.string[0] in self.__commentTrailerChar__:
973 return [scannedLineNumber,scannedLabel,scannedOpcode,scannedOperand]
974#
975# Opcode, convert it to upper case
976#
977 scannedOpcode= tok
978 scannedOpcode.string=scannedOpcode.string.upper()
979#
980# Operand, if any, scan it as a comma separated list
981#
982 tok= self.scanTok(",")
983 while True:
984#
985# End of line
986#
987 if tok.string=="":
988 break
989#
990# Comment
991#
992 if tok.string[0] in self.__commentTrailerChar__:
993 break
994#
995# Comma, continue loop
996#
997 if tok.string!=",":
998 scannedOperand.append(tok)
999 tok= self.scanTok(",")
1000 return [scannedLineNumber,scannedLabel,scannedOpcode,scannedOperand]
1002#
1003# object code writer class --------------------------------------------
1004#
1005# This object writer dumps the generated code to the binary output file
1006#
1007class clsObjWriter(object):
1008#
1009# Initialize, open binary output file
1010#
1011 def __init__(self,objectfilename):
1012 super().__init__()
1013 self.__codeLen__=0
1014 self.__objectfile__= None
1015 try:
1016 self.__objectfile__=open(objectfilename,"wb")
1017 except OSError:
1018 MESSAGE.fatalError("Error opening object file")
1019 return
1020#
1021# Dump code to file
1022#
1023 def writeCode(self,codeInfo,parsedLine):
1024 if codeInfo.code == []:
1025 return
1026 for c in codeInfo.code:
1027 try:
1028 self.__objectfile__.write(c.to_bytes(1,byteorder="big"))
1029 self.__codeLen__+=1
1030 except OSError:
1031 MESSAGE.fatalError("Error writing object file")
1032 except OverflowError:
1033 MESSAGE.fatalError("Internal error: code overflow")
1034 return
1035#
1036# Destructor, flush and close file
1037#
1038 def __del__(self):
1039 if self.__objectfile__ is not None:
1040# fill= 256 - (self.__codeLen__ % 256)
1041# c=0xFF
1042# for i in range(0,fill):
1043# self.__objectfile__.write(c.to_bytes(1,byteorder="big"))
1044 self.__objectfile__.flush()
1045 self.__objectfile__.close()
1046 return
1047#
1048# List file writer class -----------------------------------------------
1049#
1050# The list file writer creates the list file
1051#
1052class clsListWriter(object):
1054#
1055# Initialize and open list file
1056#
1057 def __init__(self,globVar,listFileName,maxLines,lineWidth):
1058 super().__init__()
1059 self.__globVar__=globVar
1060 self.__maxLines__=maxLines
1061 self.__lineWidth__= lineWidth
1062 self.__lineCount__=maxLines
1063 self.__listFile__= None
1064 self.__totalLines__=0
1065 self.__totalBytesOfCode__=0
1066 self.__pageCount__=1
1067 self.__noList__=False
1068 self.__sourceFileDict__={ }
1069 self.__sourceFileCount__=0
1070 try:
1071 if listFileName=="":
1072 self.__listFile__=sys.stdout
1073 self.__noList__=True
1074 else:
1075 self.__listFile__=open(listFileName,"w")
1076 except OSError:
1077 MESSAGE.fatalError("Error opening list file")
1078 self.writeHeader()
1079 return
1080#
1081# Format program code byte (either 3 digit octal or 2 digit hex)
1082#
1083 def formatCode(self,b):
1084 if self.__globVar__.useHex:
1085 if b is None:
1086 return " "
1087 else:
1088 return "{:02X}".format(b)
1089 else:
1090 if b is None:
1091 return " "
1092 else:
1093 return "{:03o}".format(b)
1094#
1095# Format address (either 6 digit ocal number of 4 digit hex number)
1096#
1097 def formatAddress(self,b):
1098 if self.__globVar__.useHex:
1099 if b is None:
1100 return " "
1101 else:
1102 return "{:04X}".format(b)
1103 else:
1104 if b is None:
1105 return " "
1106 else:
1107 return "{:06o}".format(b)
1108#
1109# Format symbol line reference
1110#
1111 def formatLineReference(self,lineInfo):
1112 fileName,lineNumber= lineInfo
1113 if not fileName in self.__sourceFileDict__:
1114 self.__sourceFileCount__+=1
1115 self.__sourceFileDict__[fileName]=self.__sourceFileCount__
1116 return " {:5d};{:d}".format(lineNumber, \
1117 self.__sourceFileDict__[fileName])
1119#
1120# Write full header. This information is always printed to standard output
1121# and the beginning of the first page of the list file
1122#
1123 def writeHeader(self):
1124 if self.__globVar__.isRegressionTest:
1125 headerString=" "+self.__globVar__.progName+"\n(c) Joachim Siebold\n\n"
1126 else:
1127 s1=self.__globVar__.progName+" "+CAPASM_VERSION+" "+\
1128 CAPASM_VERSION_DATE
1129 s2=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
1130 offset=self.__lineWidth__-len(s1)-len(s2)
1131 headerString=" "+s1+" "*offset+s2+"\n(c) Joachim Siebold 2020\n\n"
1132#
1133# Print header to terminal regardless if we have a list file or not
1134#
1135 print(headerString)
1136 if not self.__noList__:
1137 self.__listFile__.write(headerString)
1138 self.__lineCount__=3
1139 self.__pageCount__=1
1140#
1141# Do a page break, write small header with page numbers
1142#
1143 def pageBreak(self):
1144 if self.__noList__:
1145 return
1146 self.__listFile__.write(" ")
1147 self.__listFile__.write("Page {:4d} {:^60s}\n".format(\
1148 self.__pageCount__,self.__globVar__.title))
1149 self.__listFile__.write(self.__globVar__.progName+"\n")
1150 self.__listFile__.write("\n")
1151 self.__lineCount__=3
1152 self.__pageCount__+=1
1153#
1154# Output one line to list file, do page break if necessary
1155#
1156 def wrL(self,string):
1157 self.__lineCount__+=1
1158 try:
1159 if self.__lineCount__> self.__maxLines__:
1160 self.pageBreak()
1161 self.__listFile__.write(string+"\n")
1162 except OSError:
1163 MESSAGE.fatalError("Error writing list file")
1164#
1165# Write list file information for a source line including code, sourceline
1166# and diagnostics. If no list file is specified, this information is
1167# printed to standard output only if any diagnostics exist
1168#
1169 def writeLine(self,parsedLine,codeInfo):
1170 lineNumber="{:5d} ".format(parsedLine.lineInfo[1])
1171#
1172# check if we have a page break
1173#
1174 if self.__totalLines__ > 0 and self.__globVar__.doPageBreak:
1175 self.__lineCount__= self.__maxLines__
1176 self.__globVar__.doPageBreak= False
1177 self.__totalLines__+=1
1178 codeLen=len(codeInfo.code)
1179 self.__totalBytesOfCode__+= codeLen
1180#
1181# Do not output statement, if we output to terminal
1182#
1183 if self.__noList__ and parsedLine.messages== [ ] \
1184 and codeInfo.messages== [ ]:
1185 return
1186 pc=parsedLine.PC
1187 line=parsedLine.line
1188#
1189# PC
1190#
1191 s=lineNumber+self.formatAddress(pc)+" "
1192#
1193# Bytes of code (max 3 octal or 4 hex)
1194#
1195 if self.__globVar__.useHex:
1196 numCode=4
1197 else:
1198 numCode=3
1199 for i in range(0,numCode):
1200 if i>= codeLen:
1201 s+=self.formatCode(None)+" "
1202 else:
1203 s+=self.formatCode(codeInfo.code[i])+" "
1204#
1205# Source code line
1206#
1207 s+=line
1208 self.wrL(s)
1209#
1210# Continuation line(s) for code, if we have more than numCode bytes
1211# of code. i is the index of the current byte. If the shortList
1212# flag is set in the codeInfo object, then continuation lines are skipped
1213#
1214 j=0
1215 i=numCode
1216 s=""
1217 skippedCode=False
1218 while i < codeLen:
1219 if j==0:
1220 pc+=numCode
1221 s=self.formatAddress(pc)+" "
1222 s+=self.formatCode(codeInfo.code[i])+" "
1223 j+=1
1224 if j==numCode:
1225 if(not codeInfo.shortList):
1226 self.wrL(s)
1227 else:
1228 skippedCode=True
1229 j=0
1230 i+=1
1231 if (skippedCode):
1232 self.wrL("...")
1233 if j>0:
1234 self.wrL(s)
1235#
1236# Error messages of parser and code generator, if any
1237#
1238 for e in parsedLine.messages:
1239 sv,msg=MESSAGE.getMsg(e)
1240 s="*{:s}(P) at {:s}({:d}): {:s}".format(sv,parsedLine.lineInfo[0], \
1241 parsedLine.lineInfo[1],msg)
1242 self.wrL(s)
1243 for e in codeInfo.messages:
1244 sv,msg=MESSAGE.getMsg(e)
1245 s="*{:s}(C) at {:s}({:d}): {:s}".format(sv,parsedLine.lineInfo[0], \
1246 parsedLine.lineInfo[1],msg)
1247 self.wrL(s)
1248 return
1249#
1250# Write symbol table
1251#
1252 def writeSymbols(self,reference):
1253 SymDict=self.__globVar__.symDict
1254#
1255# No table, if either no list file is speficied or the reference table
1256# option is zero
1257#
1258 if reference==0 or self.__noList__:
1259 return
1260 symlist=SymDict.getList()
1261 self.wrL(" ")
1262 self.wrL("{:d} Symbols used:".format(len(symlist)))
1263#
1264# Sort symbol names and begin loop to output information
1265#
1266 symlist.sort()
1267 maxLen=SymDict.getMaxSymNameLength()
1268 formatString="{:"+str(maxLen)+"s} {:s} {:s}"
1269 for sn in symlist:
1270#
1271# do not output symbols beginning with a number (generated symbols)
1272#
1273 if sn[0].isdigit():
1274 continue
1275#
1276# Output dictionary entry
1277#
1278 typ,value,size,defLineInfo,refLineInfo=SymDict.get(sn)
1279 symAddr=self.formatAddress(value)
1280 s=formatString.format(sn,\
1281 SymDict.getSymTypeString(typ),\
1282 symAddr)
1283 nSkip=len(s)
1284#
1285# Output symbol dictionary
1286# first print the line number where the symbol was defined
1287# this value is None for global symbols
1288#
1289 if defLineInfo is None:
1290 s+=" GLOBAL"
1291 else:
1292 s+=self.formatLineReference(defLineInfo)
1293 j=0
1294#
1295# Now output line references, output a warning if we have none
1296#
1297 if reference==2:
1298 if len(refLineInfo)==0:
1299 s+=" ** Not referenced!"
1300 j+=1
1301 else:
1302 for ln in refLineInfo:
1303 if ln[1] == clsParserInfo.ILL_NUMBER:
1304 continue # dead code ??
1305 j+=1
1306 s+=self.formatLineReference(ln)
1307#
1308# We need a continuation line
1309#
1310 if len(s)+8>= self.__lineWidth__:
1311 self.wrL(s)
1312 s=" "*nSkip
1313 j=0
1314 if j!=0:
1315 self.wrL(s)
1316 else:
1317 self.wrL(s)
1318#
1319# output source file dictionary
1320#
1321 self.wrL("")
1322 self.wrL("Index of source files in symbol cross reference:")
1323 for filename in self.__sourceFileDict__:
1324 self.wrL("{:d}: {:s}".format(self.__sourceFileDict__[filename],\
1325 filename))
1326 return
1327#
1328# Write statistics: source code lines, generated code, number of errors
1329# This information is always printed to standard output
1330# and the end of the list file
1331#
1332 def writeStatistics(self):
1333 numberOfErrors=self.__globVar__.errorCount
1334 numberOfWarnings=self.__globVar__.warningCount
1335 s1="Assembly completed"
1336 s2=" {:d} lines processed, {:d} error(s) {:d} warning(s)".format(self.__totalLines__, numberOfErrors,numberOfWarnings)
1337 if numberOfErrors == 0:
1338 s3=" {:d} bytes of code written to object file".format(self.__totalBytesOfCode__)
1339 else:
1340 s3=" object file deleted"
1341#
1342# Output statistics to terminal regardless if we have a list file
1343#
1344 print(s1)
1345 print(s2)
1346 print(s3)
1347 if not self.__noList__:
1348 self.wrL(s1)
1349 self.wrL(s2)
1350 self.wrL(s3)
1351 return
1352#
1353# Destructor, flush and close file
1354#
1355 def __del__(self):
1356 if self.__listFile__ is not None:
1357 self.__listFile__.flush()
1358 if self.__listFile__ != sys.stdout:
1359 self.__listFile__.close()
1360 return
1361#
1362# Source file reader class ----------------------------------------------
1363#
1364class clsSourceReader(object):
1365#
1366# Initialize and open first source file
1367#
1368 def __init__(self,inputFileName):
1369 super().__init__()
1370 self.__inputFiles__= []
1371 self.__lineInfos__= []
1372 try:
1373 self.__inputFiles__.append(open(inputFileName,"r"))
1374 self.__lineInfos__.append([Path(inputFileName).name,0])
1375 except OSError:
1376 MESSAGE.fatalError("Error opening source file")
1377#
1378# build name of include or link file.
1379# If the source assembly file name has a path and
1380# the include file name has no path
1381# then put the directory of the source assembly file name in front of
1382# the include file name
1383#
1384 def buildFileName(self,inputFileName,sourceFileDirectory):
1385 ifPath=Path(inputFileName)
1386 if str(ifPath.parent)!=".":
1387 return inputFileName
1388 if sourceFileDirectory==".":
1389 return inputFileName
1390 return str(Path(sourceFileDirectory) / ifPath)
1391#
1392# open include file
1393#
1394 def openInclude(self,inputFileName,sourceFileDirectory):
1395 if len(self.__inputFiles__)> 3:
1396 MESSAGE.fatalError("Maximum include depth exceeded")
1397 fileName=self.buildFileName(inputFileName,sourceFileDirectory)
1398 try:
1399 self.__inputFiles__.append(open(fileName,"r"))
1400 self.__lineInfos__.append([Path(inputFileName).name,0])
1401 except OSError:
1402 MESSAGE.fatalError("Error opening include or link file "+\
1403 inputFileName+" ")
1404#
1405# open linked file
1406#
1407 def openLink(self,inputFileName,sourceFileDirectory):
1408 self.__inputFiles__[-1].close()
1409 self.__inputFiles__.pop()
1410 self.__lineInfos__.pop()
1411 self.openInclude(inputFileName,sourceFileDirectory)
1413#
1414# Read a line
1415#
1416 def read(self):
1417 while self.__inputFiles__:
1418 try:
1419 line=self.__inputFiles__[-1].readline()
1420 except OSError:
1421 MESSAGE.fatalError("Error reading source or include file")
1422 if line:
1423 self.__lineInfos__[-1][1]+=1
1424 return line.strip("\r\n")
1425#
1426# EOF, fall back to previous file, if none return None
1427#
1428 self.__inputFiles__[-1].close()
1429 self.__inputFiles__.pop()
1430 self.__lineInfos__.pop()
1431 return None
1432#
1433# Get current filename and line count
1434#
1435 def getLineInfo(self):
1436 return [self.__lineInfos__[-1][0],self.__lineInfos__[-1][1]]
1438#
1439# Destructor, close any open files
1440#
1441 def __del__(self):
1442 for f in self.__inputFiles__:
1443 f.close()
1444 return
1445#
1446# Parser Info data class ----------------------------------------------
1447#
1448# An object of this class is returned by the parser
1449#
1450class clsParserInfo(object):
1451#
1452# Address Modes
1453#
1454 AM_REGISTER_IMMEDIATE=0
1455 AM_REGISTER_DIRECT=1
1456 AM_REGISTER_INDIRECT=2
1457 AM_LITERAL_IMMEDIATE=3
1458 AM_LITERAL_DIRECT=4
1459 AM_LITERAL_INDIRECT=5
1460 AM_INDEX_DIRECT=6
1461 AM_INDEX_INDIRECT=7
1462#
1463# Single, Multibyte Modes
1464#
1465 BM_UNKNOWN=0
1466 BM_SINGLEBYTE=1
1467 BM_MULTIBYTE=2
1469#
1470# Gosub Modes
1471#
1472 JS_LITERAL_DIRECT=0
1473 JS_INDEXED=1
1474#
1475# Stack operands mode
1476#
1477 STACK_INCREMENT=0
1478 STACK_DECREMENT=1
1479#
1480# Illegal Number
1481#
1482 ILL_NUMBER=-1
1484 def __init__(self,PC,lineInfo,messages,line,opcode="",opcodeLen=0,parsedOperand= [],needsArp=-1,needsDrp=-1,addressMode=AM_REGISTER_IMMEDIATE):
1485 self.PC=PC # program counter
1486 self.lineInfo= lineInfo # input file name and line number
1487 self.messages=messages # list of error messages, an empty
1488 # list means: no errors
1489 self.line=line # the original source code line
1490 self.opcode=opcode # the opcode string
1491 self.opcodeLen=opcodeLen # length of opcode, this enables
1492 # the main program to compute the
1493 # PC of the next statement
1494 self.parsedOperand=parsedOperand # list of parsed Operand objects,
1495 # the list may be empty
1496 self.needsArp=needsArp # if > 0, generate a DRP instruction
1497 self.needsDrp=needsDrp # if > 0, generate an ARP instruction
1498 self.addressMode=addressMode # address mode
1500 def __repr__(self): # pragma: no cover
1501 return("clsParserInfo object:")
1502#
1503# Parsed Operand Data Class --------------------------------------------
1504#
1505# Note: the base class is used to indicate illegal operand items
1506#
1507class clsParsedOperand(object):
1508#
1509# Parsed Operand Types
1510#
1511 OP_INVALID=0
1512 OP_REGISTER=1
1513 OP_LABEL=2
1514 OP_NUMBER=3
1515 OP_STRING=4
1516 OP_EXPRESSION=5
1518 def __init__(self,typ=OP_INVALID):
1519 self.typ=typ
1520 self.size=None
1522 def __repr__(self): # pragma: no cover
1523 return("clsParsedOperand (generic)")
1525 def isInvalid(self):
1526 return self.typ==clsParsedOperand.OP_INVALID
1527#
1528# Invalid operand, operand that had issues during parsing
1529#
1530class clsInvalidOperand(clsParsedOperand):
1532 def __init__(self):
1533 super().__init__(clsParsedOperand.OP_INVALID)
1535 def __repr__(self): # pragma: no cover
1536 return("clsParsedOperand (invalid)")
1537#
1538# Parsed expression
1539#
1540class clsParsedExpression(clsParsedOperand):
1542 def __init__(self,bytecode,size):
1543 super().__init__(clsParsedOperand.OP_EXPRESSION)
1544 self.byteCode=bytecode
1545 self.size=size
1547 def __repr__(self): # pragma: no cover
1548 s="clsParsedExpression\n"
1549 for item in self.byteCode:
1550 s+=str(item)+" "
1551 s+=str(self.size)
1552 return(s)
1553#
1554# Valid number operand (syntax checked)
1555#
1556class clsParsedNumber(clsParsedOperand):
1558 def __init__(self,number,size=None):
1559 super().__init__(clsParsedOperand.OP_NUMBER)
1560 self.number=number
1561 self.size=size
1563 def __repr__(self): # pragma: no cover
1564 return ("clsParsedNumber number= {:o}".format(self.number))
1565#
1566# Valid string operand (syntax checked)
1567#
1568class clsParsedString(clsParsedOperand):
1570 def __init__(self,string):
1571 super().__init__(clsParsedOperand.OP_STRING)
1572 self.string=string
1574 def __repr__(self): # pragma: no cover
1575 return ("clsParsedString string= {:s}".format(self.string))
1577#
1578# Valid label operand (syntax checked) with optional size constraint
1579# for symbols in literal data lists
1580#
1581class clsParsedLabel(clsParsedOperand):
1583 def __init__(self,label,size=None):
1584 super().__init__(clsParsedOperand.OP_LABEL)
1585 self.label=label
1586 self.size=size
1588 def __repr__(self): # pragma: no cover
1589 return ("clsParsedLabel label= "+self.label+" "+str(self.size))
1590#
1591# Valid register operand (syntax checked)
1592#
1593class clsParsedRegister(clsParsedOperand):
1595 R_HASH=-1
1596 R_ILLEGAL=-2
1598 def __init__(self,registerSign="", registerTyp="", registerNumber=R_ILLEGAL):
1599 super().__init__(clsParsedOperand.OP_REGISTER)
1600 self.registerSign=registerSign # sign of the register "+", "-" or ""
1601 self.registerTyp=registerTyp # register typ "R" or "X" or "!"
1602 self.registerNumber=registerNumber # decimal register number
1603 # a * results in register number 1
1604 # a # results in register number
1605 # R_HASH
1606 # if the number is R_ILLEGAL then
1607 # we have an invalid register
1609 def __repr__(self): # pragma: no cover
1610 return ("clsParsedRegister object '{:s}' {:s} '{:d}'".format(self.registerSign, self.registerTyp,self.registerNumber))
1613#
1614# Code Info Data class -------------------------------------------------
1615#
1616# An object of this class is returned by the code generator
1617#
1618class clsCodeInfo(object):
1620 def __init__(self,code, messages, shortList=False):
1621 self.code= code # list of generated code (bytes)
1622 self.messages=messages # list of error messages
1623 self.shortList=shortList # do not list all generated code (BSS)
1625 def __repr__(self): # pragma: no cover
1626 s="clsCodeInfo object code= "
1627 for i in self.code:
1628 s+="{:o} ".format(i)
1629 return (s)
1632#
1633# Code Generator Base class --------------------------------------------
1634#
1635# Genertes code and returns an object of class clsCodeInfo
1636#
1637class clsCodeGeneratorBase(object):
1638#
1639# Completion for load-/store- instruction templates according to address mode
1640#
1641 LOADSTORE_COMPLETION = {
1642 clsParserInfo.AM_REGISTER_IMMEDIATE : 0b00000,
1643 clsParserInfo.AM_REGISTER_DIRECT : 0b00100,
1644 clsParserInfo.AM_REGISTER_INDIRECT : 0b01100,
1645 clsParserInfo.AM_LITERAL_IMMEDIATE : 0b01000,
1646 clsParserInfo.AM_LITERAL_DIRECT : 0b10000,
1647 clsParserInfo.AM_LITERAL_INDIRECT : 0b11000,
1648 clsParserInfo.AM_INDEX_DIRECT : 0b10100,
1649 clsParserInfo.AM_INDEX_INDIRECT : 0b11100
1651 }
1652#
1653# Completion for arithmetic instruction templates according to address mode
1654#
1655 ARI_COMPLETION = {
1656 clsParserInfo.AM_REGISTER_IMMEDIATE : 0b00000,
1657 clsParserInfo.AM_REGISTER_DIRECT : 0b11000,
1658 clsParserInfo.AM_LITERAL_IMMEDIATE : 0b01000,
1659 clsParserInfo.AM_LITERAL_DIRECT : 0b10000,
1660 }
1661#
1662# Completion for jsb instruction templates according to address mode
1663#
1664 JSB_COMPLETION = {
1665 clsParserInfo.JS_LITERAL_DIRECT : 0b01000,
1666 clsParserInfo.JS_INDEXED : 0b00000,
1667 }
1668#
1669# Completion for stack instruction templates according to address mode
1670#
1671 STACK_COMPLETION = {
1672 clsParserInfo.STACK_INCREMENT : 0b00000,
1673 clsParserInfo.STACK_DECREMENT : 0b00010,
1674 }
1676 _methodDict_= { }
1677#
1678# Initialize generator
1679#
1680 def __init__(self,globVar):
1681 super().__init__()
1682 self.__globVar__= globVar
1683 return
1684#
1685# Add error message to the code generator message list
1686#
1687 def addError(self,errno):
1688 if isinstance(errno,list):
1689 self.__messages__.extend(errno)
1690 for e in errno:
1691 if e < 1000:
1692 self.__globVar__.errorCount+=1
1693 else:
1694 self.__globVar__.warningCount+=1
1695 else:
1696 self.__messages__.append(errno)
1697 if errno < 1000:
1698 self.__globVar__.errorCount+=1
1699 else:
1700 self.__globVar__.warningCount+=1
1701 return
1702#
1703# Generate GTO
1704#
1705 def gGto(self):
1706 SymDict=self.__globVar__.symDict
1707 defCode= [0]* self.__opcodeLen__
1708 pLabel=self.__parsedOperand__[1]
1709 if pLabel.typ != clsParsedOperand.OP_LABEL:
1710 self.__code__.extend(defCode)
1711 return
1712 ret=SymDict.get(pLabel.label,self.__lineInfo__)
1713 if ret==None:
1714 self.addError(MESSAGE.E_SYMNOTFOUND)
1715 self.__code__.extend(defCode)
1716 else:
1717#
1718# relative jump, only local labels which are not abs
1719#
1720 typ=ret[0]
1721 value=ret[1]
1722 if typ==clsSymDict.SYM_LCL and not self.__globVar__.hasAbs:
1723 self.__code__.append(0o313) # ADMD
1724 offset= value -(self.__pc__+self.__opcodeLen__-1)
1725 if offset < 0:
1726 offset= 0xFFFF + offset
1727 self.__code__.extend([offset & 0xFF, (offset >>8)&0xFF])
1728 else:
1729#
1730# absolute jump
1731#
1732 value-=1
1733 self.__code__.append(0o251) # LDMD
1734 self.__code__.extend([value & 0xFF, (value >>8)&0xFF])
1735 return
1737#
1738# Generate DEF,VAL
1739#
1740 def gDef(self):
1741 SymDict=self.__globVar__.symDict
1742 defCode= [0]* self.__opcodeLen__
1743 pLabel=self.__parsedOperand__[0]
1744 if pLabel.typ != clsParsedOperand.OP_LABEL:
1745 self.__code__.extend(defCode)
1746 return
1747 ret=SymDict.get(pLabel.label,self.__lineInfo__)
1748 if ret==None:
1749 self.addError(MESSAGE.E_SYMNOTFOUND)
1750 self.__code__.extend(defCode)
1751 else:
1752 if self.__opcodeLen__==1:
1753 if ret[1] > 0xFF:
1754 self.addError(MESSAGE.E_NUMBERTOOLARGE)
1755 self.__code__.extend(defCode)
1756 else:
1757 self.__code__.append(ret[1])
1758 else:
1759 self.__code__.extend([ret[1] & 0xFF, ret[1] >>8])
1760#
1761# Generate zeros
1762#
1763 def gGenZ(self):
1764 for i in range(0,self.__opcodeLen__):
1765 self.__code__.append(0)
1766 self.__shortList__=True
1767 return
1768#
1769# Generate nothing
1771 def gNil(self):
1772 return
1773#
1774# Generate Data, we have only parsed numbers
1775#
1776 def gData(self):
1777 self.gOperands()
1778 return
1779#
1780# Generate relative jump instructions
1781#
1782 def gJrel(self):
1783#
1784# exit if relative jump of an ELSE was removed due to code optimization
1785#
1786 if len(self.__parsedOperand__)==0:
1787 return
1788 SymDict=self.__globVar__.symDict
1789 self.__code__.append(self.__opcodeInfo__[2])
1790 self.__bytesToGenerate__-=1
1791 pOperand=self.__parsedOperand__[0]
1792 if pOperand.typ == clsParsedOperand.OP_LABEL:
1793 ret=SymDict.get(pOperand.label,self.__lineInfo__)
1794 if ret==None:
1795 self.addError(MESSAGE.E_SYMNOTFOUND)
1796 self.__code__.append(0)
1797 else:
1798 value=ret[1]
1799 offset=value-(self.__pc__+2)
1800 if offset > 127 or offset < -128:
1801 offset=0
1802 self.addError(MESSAGE.E_RELJUMP_TOOLARGE)
1803 if offset < 0:
1804 offset=255 -abs(offset)+1
1805 self.__code__.append(offset)
1806 else:
1807 self.__code__.append(0)
1808 return
1809#
1810# Generate Stack instructions
1811#
1812 def gStack(self):
1813#
1814# Complete instruction template according to address mode
1815#
1816 self.__code__.append(self.__opcodeInfo__[2] | \
1817 clsCodeGeneratorBase.STACK_COMPLETION[self.__addressMode__ ] )
1818 self.__bytesToGenerate__-=1
1819 self.gOperands()
1820 return
1822#
1823# Generate JSB instructions
1824#
1825 def gJsb(self):
1826#
1827# Complete instruction template according to address mode
1828#
1829 self.__code__.append(self.__opcodeInfo__[2] | \
1830 clsCodeGeneratorBase.JSB_COMPLETION[self.__addressMode__ ] )
1831 self.__bytesToGenerate__-=1
1832 self.gOperands()
1833 return
1834#
1835# generate CM, AD, SB, ANM instructions
1836#
1837 def gAri(self):
1838#
1839# Complete instruction template according to address mode
1840#
1841 self.__code__.append(self.__opcodeInfo__[2] | \
1842 clsCodeGeneratorBase.ARI_COMPLETION[self.__addressMode__ ] )
1843 self.__bytesToGenerate__-=1
1844 self.gOperands()
1845 return
1847#
1848# Generate LD, ST instructions
1849#
1850 def gLdSt(self):
1851#
1852# Complete instruction template according to address mode
1853#
1854 self.__code__.append(self.__opcodeInfo__[2] | \
1855 clsCodeGeneratorBase.LOADSTORE_COMPLETION[self.__addressMode__ ] )
1856 self.__bytesToGenerate__-=1
1857 self.gOperands()
1858 return
1859#
1860# generate code for operands
1861#
1862 def gOperands(self):
1863 SymDict=self.__globVar__.symDict
1864 op=[]
1865 for pOperand in self.__parsedOperand__:
1866#
1867# Noting to to for a register
1868#
1869 if pOperand.typ== clsParsedOperand.OP_REGISTER:
1870 continue
1871#
1872# Process label, 1 or 2 bytes long
1873#
1874 elif pOperand.typ== clsParsedOperand.OP_LABEL:
1875 ret=SymDict.get(pOperand.label,self.__lineInfo__)
1876#
1877# apply the size constraint
1878#
1879 if ret==None:
1880 self.addError(MESSAGE.E_SYMNOTFOUND)
1881 op.append(0)
1882 else:
1883 value=ret[1]
1884 if pOperand.size==2:
1885 op.append(value & 0xFF)
1886 op.append(value >>8)
1887 else:
1888 op.append(value & 0xFF)
1889#
1890# Number, 1 bytes
1891#
1892 elif pOperand.typ==clsParsedOperand.OP_NUMBER:
1893 number=pOperand.number
1894 if number > 0xFF:
1895 self.addError(MESSAGE.E_NUMBERTOOLARGE)
1896 op.append(0)
1897 else:
1898 op.append(number)
1899 elif pOperand.typ==clsParsedOperand.OP_EXPRESSION:
1900 result,byteResult, errors=self.__expression__.execute( \
1901 pOperand, self.__lineInfo__)
1902 if len(errors)>0:
1903 self.addError(errors)
1904 else:
1905 for b in byteResult:
1906 op.append(b)
1907#
1908# Append to instructions, check if we have too many bytes
1909# and exceed section boundaries
1910#
1911 if len(op) > self.__bytesToGenerate__:
1912 self.addError(MESSAGE.E_OPEXCEEDSSECTION)
1913 else:
1914#
1915# fill missing code with zeros (only necessary for faulty statements)
1916#
1917 l=len(op)
1918 while l < self.__bytesToGenerate__:
1919 self.__code__.append(0)
1920 l+=1
1921 self.__code__.extend(op)
1922 return
1923#
1924# Generate ARP, DRP instructions. Do not generate any code if
1925# the parsedOperand is of type OP_INVALID
1926#
1927 def gdarp(self):
1928# if self.__opcodeLen__==0:
1929# return
1930 code=self.__opcodeInfo__[2]
1931 if not self.__parsedOperand__[0].isInvalid():
1932 code|=self.__parsedOperand__[0].registerNumber
1933 self.__code__.append(code)
1934 self.__bytesToGenerate__-=1
1935 return
1936#
1937# Generate all instructions, where the opcode is not modfied by operands
1938#
1939 def gdirect(self):
1940 self.__code__.append(self.__opcodeInfo__[2])
1941 self.__bytesToGenerate__-=1
1942 return
1943#
1944# Generate Control Block (capasm only)
1945#
1946 def gNam(self):
1947 if len(self.__parsedOperand__)==0:
1948 return
1949 progName=self.__parsedOperand__[0].string
1950#
1951# if we have no program number, create HP-85 style control block
1952#
1953 if len(self.__parsedOperand__)==1:
1954 progName=progName.ljust(6)
1955#
1956# Prog name (6 characters)
1957 for i in range(0,6):
1958 self.__code__.append(ord(progName[i]))
1959#
1960# Type (always 2)
1961#
1962 self.__code__.append(2)
1963#
1964# 19 zeros
1965#
1966 for i in range(0,19):
1967 self.__code__.append(0)
1968 else:
1970#
1971# generate HP-87 style control block
1972#
1973 progNumber=self.__parsedOperand__[1].number
1974 progName=progName.ljust(10)
1975#
1976# Prog name (4 characters)
1977#
1978 for i in range(0,4):
1979 self.__code__.append(ord(progName[i]))
1980#
1981# Length (2 bytes)
1982#
1983 self.__code__.append(self.__globVar__.codeLen & 0xFF)
1984 self.__code__.append((self.__globVar__.codeLen>>8) & 0xFF)
1985#
1986# Type (always 2)
1987#
1988 self.__code__.append(2)
1989#
1990# Program number
1991#
1992 self.__code__.append(progNumber)
1993#
1994# Full ascii name
1995#
1996 for i in range (0,10):
1997 self.__code__.append(ord(progName[i]))
1998#
1999# 8 zeros
2000#
2001 for i in range(0,8):
2002 self.__code__.append(0)
2004 return
2005#
2006# Generate HED pseudo op
2007#
2008 def gHed(self):
2009 self.__globVar__.doPageBreak=True
2010 self.__globVar__.title=self.__parsedOperand__[0].string
2011 return
2012#
2013# Generate STE instruction (ncas only)
2014#
2015 def gSte(self):
2016 self.__code__.append(0x9D)
2017 self.__code__.append(0x9C)
2018#
2019# Generate code, top level method
2020#
2021 def generate(self,parsedLine):
2022 self.__pc__= parsedLine.PC
2023 self.__opcode__=parsedLine.opcode
2024 self.__opcodeLen__=parsedLine.opcodeLen
2025 self.__bytesToGenerate__=self.__opcodeLen__
2026 self.__needsArp__= parsedLine.needsArp
2027 self.__needsDrp__= parsedLine.needsDrp
2028 self.__parsedOperand__= parsedLine.parsedOperand
2029 self.__addressMode__= parsedLine.addressMode
2030 self.__lineInfo__= parsedLine.lineInfo
2031 self.__code__=[]
2032 self.__messages__=[]
2033 self.__shortList__=False
2034 if self.__opcode__=="":
2035 return clsCodeInfo(self.__code__,self.__messages__)
2036#
2037# Generate DRP, ARP if needed
2038#
2039 if self.__needsDrp__>=0:
2040 self.__code__.append(0o100 | self.__needsDrp__)
2041 self.__bytesToGenerate__-=1
2043 if self.__needsArp__>=0:
2044 self.__code__.append(0o0 | self.__needsArp__)
2045 self.__bytesToGenerate__-=1
2046#
2047# Call the opcode specific generator method
2048#
2049 self.__opcodeInfo__=OPCODES.get(self.__opcode__)
2050 if self.__opcodeInfo__ !=[]:
2051 fname=self.__opcodeInfo__[1]
2052 getattr(self,fname)()
2053 return clsCodeInfo(self.__code__, self.__messages__,self.__shortList__)
2054#
2055# Parser Base class ----------------------------------------------------
2056#
2057# The parseLine method takes the Program Counter, the list of scanned token
2058# and the original source line as arguments and returns an object of type
2059# clsParserInfo
2060#
2061class clsParserBase(object):
2063#
2064# Initialize parser
2065#
2066 def __init__(self,globVar,infile):
2067 super().__init__()
2068 self.__globVar__= globVar
2069 self.__infile__= infile
2070 self.__hasLcl__=False
2071 return
2072#
2073# check if a scanned opcode is single- or multibyte
2074#
2075 def getByteMode(self):
2076 c=self.__scannedOpcode__.string[2]
2077 if c=="B":
2078 return clsParserInfo.BM_SINGLEBYTE
2079 elif c=="M":
2080 return clsParserInfo.BM_MULTIBYTE
2081 else:
2082 return clsParserInfo.BM_UNKNOWN # dead code?
2083#
2084# Add an error to the parser error list
2085#
2086 def addError(self,errno):
2087 if isinstance(errno,list):
2088 self.__messages__.extend(errno)
2089 for e in errno:
2090 if e < 1000:
2091 self.__globVar__.errorCount+=1
2092 else:
2093 self.__globVar__.warningCount+=1
2094 else:
2095 self.__messages__.append(errno)
2096 if errno < 1000:
2097 self.__globVar__.errorCount+=1
2098 else:
2099 self.__globVar__.warningCount+=1
2100 return
2101#
2102# Parse register [+|-] [R|Z] [OctalNumber | # | *]
2103# returns object of class clsParsedRegister
2104# If signRequired is True, then a missing sign throws an error
2105# If notAllowed is True, then we can have a !<RegisterNumber>
2106#
2107 def parseRegister(self,token,signRequired,notAllowed):
2108 string=token.string
2109 registerTypes="rRxX"
2110 if notAllowed:
2111 registerTypes+="!"
2112 i=0
2113 sign=""
2114 if string[i]=="+" or string[i]=="-":
2115 sign=string[i]
2116 i+=1
2117 if not signRequired:
2118 self.addError(MESSAGE.E_REGISTERSIGN)
2119 return clsInvalidOperand()
2120# typ="R"
2121 if signRequired and sign=="":
2122 self.addError(MESSAGE.E_SIGNEDREGISTER)
2123 return clsInvalidOperand()
2124 if string[i] in registerTypes:
2125 typ=string[i].upper()
2126 i+=1
2127 if string[i]=="*":
2128 return clsParsedRegister(sign, typ, 1)
2129 elif string[i]=="#":
2130 return clsParsedRegister(sign, typ, clsParsedRegister.R_HASH)
2131 else:
2132 self.addError(MESSAGE.E_ILL_REGISTER)
2133 return clsInvalidOperand()
2134 number=parseFunc.parseNumber(string[i:])
2135 if number is None or number > 0o77 or number==1:
2136 self.addError(MESSAGE.E_ILL_REGISTER)
2137 return clsInvalidOperand()
2138 else:
2139 return clsParsedRegister(sign, typ, number)
2140#
2141# Parse the Label field
2142#
2143 def parseLabelField(self):
2144 label= self.__scannedLabel__.string
2145 PC=self.__globVar__.PC
2146 SymDict=self.__globVar__.symDict
2147 isLcl=False
2148#
2149# Valid label?
2150#
2151 if parseFunc.parseLabel(label,self.__globVar__.symNamLen) is None:
2152 self.addError(MESSAGE.E_ILL_LABEL)
2153 else:
2154#
2155# check if we have a "real" LCL and not an EQU or DAD
2156#
2157 isLcl=True
2158 if self.__scannedOpcode__ is not None:
2159 if self.__scannedOpcode__.string=="EQU" or \
2160 self.__scannedOpcode__.string=="DAD" or \
2161 self.__scannedOpcode__.string=="ADDR":
2162 isLcl=False
2163#
2164# real label, enter it into symbol table and invalidate
2165# arp, drp context
2166#
2167 if isLcl:
2168 ret=SymDict.enter(label,clsSymDict.SYM_LCL,PC,2, \
2169 self.__lineInfo__)
2170 if ret is not None:
2171 self.addError(ret)
2172# self.__globVar__.arpReg= -1
2173# self.__globVar__.drpReg= -1
2174 return isLcl
2176#
2177# Parse Data register, which is the first operand. Handle drp elimination
2178#
2179 def parseDr(self):
2180 dRegister=self.parseRegister(self.__scannedOperand__[0],False,False)
2181 if not dRegister.isInvalid():
2182 if dRegister.registerNumber!= clsParsedRegister.R_HASH and \
2183 self.__globVar__.drpReg!= dRegister.registerNumber:
2184 self.__needsDrp__= dRegister.registerNumber
2185 self.__globVar__.drpReg = dRegister.registerNumber
2186 self.__opcodeLen__+=1
2187 return dRegister
2188#
2189# Parse Adress register, which is the second operand. Handle arp elimination
2190# Note: the push/pop opcodes require AR to have a sign
2191#
2192 def parseAr(self,signRequired=False):
2193 aRegister=self.parseRegister(self.__scannedOperand__[1],signRequired,\
2194 False)
2195 if not aRegister.isInvalid():
2196 if aRegister.registerNumber!= clsParsedRegister.R_HASH \
2197 and self.__globVar__.arpReg!= aRegister.registerNumber:
2198 self.__needsArp__= aRegister.registerNumber
2199 self.__globVar__.arpReg = aRegister.registerNumber
2200 self.__opcodeLen__+=1
2201 return aRegister
2202#
2203# Parse Index register. Handle arp elimination
2204#
2205 def parseXr(self,index):
2206 xRegister=self.parseRegister(self.__scannedOperand__[index],False,\
2207 False)
2208 if not xRegister.isInvalid():
2209 if xRegister.registerTyp != "X":
2210 xRegister.typ= clsParsedOperand.OP_INVALID
2211 self.addError(MESSAGE.E_XREGEXPECTED)
2212 if xRegister.registerNumber!= clsParsedRegister.R_HASH \
2213 and self.__globVar__.arpReg!= xRegister.registerNumber:
2214 self.__needsArp__= xRegister.registerNumber
2215 self.__globVar__.arpReg = xRegister.registerNumber
2216 self.__opcodeLen__+=1
2217 return(xRegister)
2218#
2219# Parse label as operand
2220#
2221 def parseLabelOp(self,opIndex,size=2):
2222 label=self.__scannedOperand__[opIndex].string
2223 if self.__scannedOperand__[opIndex].termChar == ",":
2224 label+=","
2225 if label[0]=="=":
2226 label=label[1:]
2227 if parseFunc.parseLabel(label,self.__globVar__.symNamLen) is None:
2228 self.addError(MESSAGE.E_ILL_LABELOP)
2229 return clsInvalidOperand()
2230 else:
2231 return clsParsedLabel(label,size)
2232#
2233# Parse expression list
2234#
2235 def parseExpressionList(self,idx,numberOfBytesToStore=None):
2236 parsedOp=[ ]
2237 opLen=0
2238 hasErrors=False
2239#
2240# we have at least one operand which is a "="
2241#
2242 for opIndex in range(idx,len(self.__scannedOperand__)):
2243 opString= self.__scannedOperand__[opIndex].string
2244#
2245# if there is no operand, then quit
2246#
2247 if opString=="":
2248 return opLen,parsedOp
2250 parsedExpression,errors=self.__expression__.parse(opString,None,\
2251 True)
2252 if parsedExpression.typ== clsParsedOperand.OP_INVALID:
2253 self.addError(errors)
2254 parsedOp.append(clsInvalidOperand())
2255 hasErrors=True
2256 continue
2257 parsedOp.append(parsedExpression)
2258 if hasErrors:
2259 opLen=None
2260 else:
2261 opLen+=parsedExpression.size
2262#
2263# check, if we exceed the section boundary
2264#
2265 if numberOfBytesToStore is not None and opLen is not None:
2266 if opLen > numberOfBytesToStore:
2267 self.addError(MESSAGE.E_OPEXCEEDSSECTION)
2268 opLen=None
2269 return [opLen,parsedOp]
2270#
2271# parse single operand expression
2272#
2273 def parseSingleExpression(self,opIndex,indicatedSize=None):
2274 opString= self.__scannedOperand__[opIndex].string
2275 parsedExpression,errors=self.__expression__.parse(opString, \
2276 indicatedSize, False)
2277 if parsedExpression.typ== clsParsedOperand.OP_INVALID:
2278 self.addError(errors)
2279 return parsedExpression
2282#
2283# Include parsing and processing
2284#
2285 def pInc(self):
2286 self.__globVar__.hasIncludes=True
2287 fileName=parseFunc.parseAnyString(self.__scannedOperand__[0].string)
2288 if fileName is None:
2289 self.addError(MESSAGE.E_ILLSTRING)
2290 else:
2291 if self.__scannedOpcode__.string=="LNK":
2292 self.__infile__.openLink(fileName, \
2293 self.__globVar__.sourceFileDirectory)
2294 else:
2295 self.__infile__.openInclude(fileName, \
2296 self.__globVar__.sourceFileDirectory)
2298#
2299# Parse the HED statement
2300#
2301 def pHed(self):
2302 title=parseFunc.parseQuotedString(self.__scannedOperand__[0].string)
2303 if title is None:
2304 self.addError(MESSAGE.E_ILLSTRING)
2305 return [clsParsedString("")]
2306 else:
2307 return [clsParsedString(title)]
2308#
2309# Parse the conditinal assembly pseudo ops
2310#
2311 def pCondSet(self):
2312 cond=self.__globVar__.condAssembly
2313 pLabel=self.parseLabelOp(0)
2314 if pLabel.isInvalid():
2315 self.addError(MESSAGE.E_ILLFLAGNAME)
2316 else:
2317 cond.set(pLabel.label)
2318 return
2320 def pCondClr(self):
2321 cond=self.__globVar__.condAssembly
2322 pLabel=self.parseLabelOp(0)
2323 if pLabel.isInvalid():
2324 self.addError(MESSAGE.E_ILLFLAGNAME)
2325 else:
2326 cond.clr(pLabel.label)
2327 return
2329 def pCondIfDef(self):
2330 cond=self.__globVar__.condAssembly
2331 pLabel=self.parseLabelOp(0)
2332 if pLabel.isInvalid():
2333 self.addError(MESSAGE.E_ILLFLAGNAME)
2334 else:
2335 cond.ifdef(pLabel.label)
2336 return
2338 def pCondIfNotDef(self):
2339 cond=self.__globVar__.condAssembly
2340 pLabel=self.parseLabelOp(0)
2341 if pLabel.isInvalid():
2342 self.addError(MESSAGE.E_ILLFLAGNAME)
2343 else:
2344 cond.ifndef(pLabel.label)
2345 return
2347 def pCondIfSet(self):
2348 cond=self.__globVar__.condAssembly
2349 pLabel=self.parseLabelOp(0)
2350 if pLabel.isInvalid():
2351 self.addError(MESSAGE.E_ILLFLAGNAME)
2352 else:
2353 ret=cond.ifset(pLabel.label)
2354 if not ret:
2355 self.addError(MESSAGE.E_FLAGNOTDEFINED)
2356 return
2358 def pCondIfNotSet(self):
2359 cond=self.__globVar__.condAssembly
2360 pLabel=self.parseLabelOp(0)
2361 if pLabel.isInvalid():
2362 self.addError(MESSAGE.E_ILLFLAGNAME)
2363 else:
2364 ret=cond.ifnset(pLabel.label)
2365 if not ret:
2366 self.addError(MESSAGE.E_FLAGNOTDEFINED)
2367 return
2369 def pCondElse(self):
2370 cond=self.__globVar__.condAssembly
2371 if not cond.isOpen():
2372 self.addError(MESSAGE.E_AIFEIFMISMATCH)
2373 else:
2374 cond.els()
2375 return
2377 def pCondEndif(self):
2378 cond=self.__globVar__.condAssembly
2379 if not cond.isOpen():
2380 self.addError(MESSAGE.E_AIFEIFMISMATCH)
2381 else:
2382 cond.endif()
2383 return
2384#
2385# Now the opcode specific parsing methods follow. They are specified
2386# in the opcode table.
2387#
2388# Parse GTO, we have to fake a LDM R4, DESTINATION_LABEL-1 (LIT DIRECT)
2389#
2390 def pGto(self):
2391#
2392# Rearrange the scanned Operand
2393#
2394 self.__scannedOperand__= [clsToken("R4",3,""),self.__scannedOperand__[0]]
2395 self.__opcodeLen__=1
2396 dRegister=self.parseDr()
2397 pLabel=self.parseLabelOp(1)
2398 self.__opcodeLen__+=2
2399 return[dRegister,pLabel]
2401#
2402# Parse BSS
2403#
2404 def pBss(self):
2405 self.__opcodeLen__=0
2406 opstring= self.__scannedOperand__[0].string
2407 result,byteResult,errors=self.__expression__.immediate(opstring, 2, \
2408 self.__lineInfo__)
2409 if result is not None:
2410 if result < 0:
2411 self.addError(MESSAGE.E_ILLVALUE)
2412 else:
2413 self.__opcodeLen__=result
2414 else:
2415 self.addError(errors)
2416 return []
2418#
2419# Parse ignored statements
2420#
2421 def pNil(self):
2422 self.__opcodeLen__=0
2423 return []
2424#
2425# Parse END statement
2426#
2427 def pEnd(self):
2428 self.__globVar__.isFin=True
2429#
2430# check, if we have any open conditionals
2431#
2432 if self.__globVar__.condAssembly.isOpen():
2433 self.addError(MESSAGE.E_AIFEIFMISMATCH)
2434#
2435# check, if we habe any open structural pseudo ops
2436#
2437 if not self.__structCtx__.isEmpty():
2438 self.addError(MESSAGE.E_ILLSTRUCT)
2441 self.__opcodeLen__=0
2442 return []
2443#
2444# Parse Data pseudo op
2445#
2446 def pData(self):
2447 opLen,parsedOperand=self.parseExpressionList(0,None)
2448 if opLen is None:
2449 return []
2450 self.__opcodeLen__=opLen
2451 return(parsedOperand)
2452#
2453# Parse STE pseudo op which expands to a CLE, STE
2454#
2455 def pSte(self):
2456 self.__opcodeLen__=2
2457 return
2458#
2459# Parse IFxx pseudo op
2460#
2461 def pIf(self):
2462 label=self.__structCtx__.structIf(self.__globVar__.arpReg, \
2463 self.__globVar__.drpReg)
2464 self.__opcodeLen__+=2
2465 return [clsParsedLabel(label,2)]
2466#
2467# Parse ELSE pseudo op
2468#
2469 def pElse(self):
2470 SymDict=self.__globVar__.symDict
2471 ret=self.__structCtx__.structElse(self.__globVar__.arpReg, \
2472 self.__globVar__.drpReg)
2473 if ret is None:
2474 self.addError(MESSAGE.E_ILLSTRUCT)
2475 return [clsInvalidOperand()]
2476 else:
2477 label1,label2,oldArpReg,oldDrpReg=ret
2478#
2479# if the last statement was an unconditional jump then eliminate the
2480# ELSE condition and insert the destination label of the IF statement
2481# here.
2482#
2483 if self.__globVar__.lastOpcodeWasJmp:
2484 self.__opcodeLen__=0
2485 ret=SymDict.enter(label1,clsSymDict.SYM_LCL,self.__globVar__.PC,\
2486 2,self.__lineInfo__)
2487 invalidateArp,invalidateDrp=self.__structCtx__.removeElse()
2488 if invalidateArp:
2489 self.__globVar__.arpReg= -1
2490 if invalidateDrp:
2491 self.__globVar__.drpReg= -1
2492 return []
2493 self.__opcodeLen__+=2
2494 ret=SymDict.enter(label1,clsSymDict.SYM_LCL,self.__globVar__.PC+2,\
2495 2,self.__lineInfo__)
2496 self.__globVar__.arpReg= oldArpReg
2497 self.__globVar__.drpReg= oldDrpReg
2499 return [clsParsedLabel(label2,2)]
2501#
2502# Parse ENDIF pseudo op
2503#
2504 def pEndif(self):
2505 SymDict=self.__globVar__.symDict
2506 ret=self.__structCtx__.structEndif(self.__globVar__.arpReg, \
2507 self.__globVar__.drpReg)
2508 if ret is None:
2509 self.addError(MESSAGE.E_ILLSTRUCT)
2510 else:
2511 label,oldArpReg, oldDrpReg, invalidateArp,invalidateDrp=ret
2512 if label=="":
2513 return
2514 ret=SymDict.enter(label,clsSymDict.SYM_LCL,self.__globVar__.PC,2, \
2515 self.__lineInfo__)
2516 if self.__globVar__.lastOpcodeWasJmp:
2517 self.__globVar__.arpReg= oldArpReg
2518 self.__globVar__.drpReg= oldDrpReg
2519 else:
2520 if invalidateArp:
2521 self.__globVar__.arpReg= -1
2522 if invalidateDrp:
2523 self.__globVar__.drpReg= -1
2524 return
2525#
2526# Parse LOOP pseudo op
2527#
2528 def pLoop(self):
2529 SymDict=self.__globVar__.symDict
2530 label=self.__structCtx__.structLoop()
2531 ret=SymDict.enter(label,clsSymDict.SYM_LCL,self.__globVar__.PC,2, \
2532 self.__lineInfo__)
2533 self.__globVar__.arpReg= -1
2534 self.__globVar__.drpReg= -1
2535 return
2536#
2537# Parse EXxx pseudo op
2538#
2539 def pEx(self):
2540 label=self.__structCtx__.structEx()
2541 if label is None:
2542 self.addError(MESSAGE.E_ILLSTRUCT)
2543 return [clsInvalidOperand()]
2544 self.__opcodeLen__+=2
2545 return [clsParsedLabel(label,2)]
2546#
2547# Parse WHxx pseudo op
2548#
2549 def pWh(self):
2550 SymDict=self.__globVar__.symDict
2551 ret=self.__structCtx__.structWhile()
2552 if ret is None:
2553 self.addError(MESSAGE.E_ILLSTRUCT)
2554 return [clsInvalidOperand()]
2555 label1,label2=ret
2556 if label2 is not None:
2557 ret=SymDict.enter(label2,clsSymDict.SYM_LCL,self.__globVar__.PC+3,\
2558 2, self.__lineInfo__)
2559 self.__globVar__.arpReg= -1
2560 self.__globVar__.drpReg= -1
2562 self.__opcodeLen__+=2
2563 return [clsParsedLabel(label1,2)]
2564#
2565# Parse Rxx pseudo op
2566#
2567 def pR(self):
2568 SymDict=self.__globVar__.symDict
2569#
2570# check distance to a previous label, if within range, then
2571# create a label for that location
2572#
2573 if self.__globVar__.PC +2 - self.__globVar__.lastRtnAddr <= 128:
2574 label=self.__structCtx__.newLabel()
2575 ret=SymDict.enter(label,clsSymDict.SYM_LCL,\
2576 self.__globVar__.lastRtnAddr, 2, self.__lineInfo__)
2577 else:
2578#
2579# otherwise try to create a label for a jump to the next rtn statement
2580#
2581 label=self.__structCtx__.structR()
2582 self.__opcodeLen__+=2
2583 return [clsParsedLabel(label,2)]
2584#
2585# Parse the LOC statement
2586#
2587 def pLoc(self):
2588 self.__opcodeLen__=0
2589 number=self.parseAddress(0)
2590 if number==clsParserInfo.ILL_NUMBER:
2591 return []
2592#
2593# statement only allowed in ABS programs
2594#
2595 if not self.__globVar__.hasAbs:
2596 self.addError(MESSAGE.E_NOTALLOWED_HERE)
2597 return []
2598#
2599# do nothing if PC is the specified address
2600#
2601 if self.__globVar__.PC== number:
2602 return []
2603#
2604# error if PC is greater than address
2605#
2606 if self.__globVar__.PC> number:
2607 self.addError(MESSAGE.E_PCGREATERTANADDRESS)
2608 else:
2609 self.__opcodeLen__= number- self.__globVar__.PC
2610 return []
2611#
2612# Parse BYT
2613#
2614 def pByt(self):
2615 err=False
2616 self.__opcodeLen__=0
2617 pOperand=[]
2618 for operand in self.__scannedOperand__:
2619 number=parseFunc.parseNumber(operand.string)
2620 if number is None or number > 0xFF:
2621 err=True
2622 pOperand.append(clsInvalidOperand())
2623 else:
2624 pOperand.append(clsParsedNumber(number))
2625 self.__opcodeLen__+=1
2626 if err:
2627 self.addError(MESSAGE.E_ILLNUMBER)
2628 pOperand=[clsInvalidOperand()]
2629 return pOperand
2630#
2631# Parse BSZ
2632#
2633 def pBsz(self):
2634 number=self.parseAddress(0)
2635 if number!=clsParserInfo.ILL_NUMBER:
2636 self.__opcodeLen__=number
2637 else:
2638 self.__opcodeLen__=0
2639 return []
2641#
2642# Parse ASP, ASC
2643#
2644 def pAsc(self):
2645 pOperand=[]
2646#
2647# check, if we have a number as first operand
2648#
2649 firstOperand=self.__scannedOperand__[0]
2650 if firstOperand.string[0] in "0123456789":
2651 numChars=parseFunc.parseNumber(firstOperand.string)
2652 if numChars is None:
2653 self.addError(MESSAGE.E_ILLNUMBER)
2654 return pOperand
2655#
2656# search for the comma
2657#
2658 if firstOperand.termChar!=",":
2659 self.addError(MESSAGE.ILLSTRING)
2660 return pOperand
2661 strIndex=self.__line__.find(",",firstOperand.position+
2662 len(firstOperand.string))+1
2663 string=self.__line__[strIndex:strIndex+numChars]
2664 if len(string)!= numChars:
2665 self.addError(MESSAGE.E_ILLSTRING)
2666 return pOperand
2667 else:
2668 string=parseFunc.parseQuotedString(firstOperand.string)
2669 if string is None:
2670 self.addError(MESSAGE.E_ILLSTRING)
2671 return pOperand
2672 i=0
2673 err=False
2674 for c in string:
2675 i+=1
2676 n=ord(c)
2677 if n > 0o174 or n == 0o173 or n < 0o40 :
2678 err=True
2679 n=0
2680 if i==len(string) and self.__scannedOpcode__.string=="ASP":
2681 n|=0o200
2682 pOperand.append(clsParsedNumber(n))
2683 if err or i==0:
2684 self.addError(MESSAGE.E_ILLSTRING)
2685 self.__opcodeLen__=len(pOperand)
2686 return pOperand
2687#
2688# Parse FIN statement
2689#
2690 def pFin(self):
2691 self.__globVar__.isFin=True
2692#
2693# check, if we have any open conditionals
2694#
2695 if self.__globVar__.condAssembly.isOpen():
2696 self.addError(MESSAGE.E_AIFEIFMISMATCH)
2697 self.__opcodeLen__=0
2698 return []
2699#
2700# Parse an address
2701#
2702 def parseAddress(self,idx):
2703 address=parseFunc.parseNumber(self.__scannedOperand__[idx].string)
2704 if address is None:
2705 self.addError(MESSAGE.E_ILLNUMBER)
2706 address=clsParserInfo.ILL_NUMBER
2707 elif address > 0xFFFF:
2708 self.addError(MESSAGE.E_NUMBERTOOLARGE)
2709 address=clsParserInfo.ILL_NUMBER
2710 return address
2713#
2714# Parse literal data lists
2715#
2716 def parseLiteralDataList(self,numberOfBytesToStore):
2717 parsedOp=[ ]
2718 opLen=0
2719#
2720# we have at least one operand which is a "="
2721#
2722 for opIndex in range(1,len(self.__scannedOperand__)):
2723 opString= self.__scannedOperand__[opIndex].string
2725#
2726# first operand, remove "="
2727#
2728 if opIndex==1:
2729 opString=opString[1:]
2730#
2731# if there is no operand, then quit
2732#
2733 if opString=="":
2734 return opLen,parsedOp
2735#
2736# check, if we have a label
2737#
2738 if not opString[0] in "0123456789":
2739#
2740# no more operands are allowed
2741#
2742 if len(self.__scannedOperand__) > 2:
2743 self.addError(MESSAGE.E_ILL_NUMOPERANDS)
2744 return opLen,parsedOp
2745#
2746# check, if we have to truncate the label value FIX
2747#
2748 if numberOfBytesToStore==1:
2749 parsedOp.append(self.parseLabelOp(opIndex,1))
2750 opLen+=1
2751 else:
2752 parsedOp.append(self.parseLabelOp(opIndex,2))
2753 opLen+=2
2754#
2755# exit, if label
2756#
2757 return opLen,parsedOp
2758#
2759# numbers, the code generator checks that they do not exceed 0xFF
2760#
2762 number=parseFunc.parseNumber(opString)
2763 if number is None:
2764 self.addError(MESSAGE.E_ILLNUMBER)
2765 parsedOp.append(clsInvalidOperand())
2766 continue
2767#
2768# check, if we exceed the section boundary
2769#
2770 if numberOfBytesToStore is not None:
2771 if opLen+1> numberOfBytesToStore:
2772 self.addError(MESSAGE.E_OPEXCEEDSSECTION)
2773 break
2774 parsedOp.append(clsParsedNumber(number))
2775 opLen+=1
2777 return opLen,parsedOp
2778#
2779# Parse NAM pseudoop
2780# Syntax is NAM unquotedString (HP83/85 only)
2781# or NAM octalNumber, unquotedString (HP86/87 only)
2782# not supported on HP-75
2783#
2784 def pNam(self):
2785 pOperand=[ ]
2786#
2787# throw error if HP-75
2788#
2789 if self.__globVar__.hasNam:
2790 self.addError(MESSAGE.E_NOTALLOWED_HERE)
2791 return pOperand
2792 self.__globVar__.hasNam=True
2793#
2794# ABS only allowed before, if PC >= 0o77777
2795#
2796 if self.__globVar__.hasAbs and self.__globVar__.PC<=0o77777:
2797 self.addError(MESSAGE.E_NOTALLOWED_HERE)
2798 return pOperand
2799#
2800#
2801# check if we have two parameters, then decode program number first
2802#
2803 pnIndex=0
2804 progNumber= -1
2805 allowedLen=6
2806 if len(self.__scannedOperand__)==2:
2807 pnIndex=1
2808 allowedLen=10
2809 if len(self.__scannedOperand__)==2:
2810 number=parseFunc.parseNumber(\
2811 self.__scannedOperand__[0].string)
2812 if number is None or number > 0o377:
2813 progNumber=0
2814 self.addError(MESSAGE.E_ILLNUMBER)
2815 return pOperand
2816 else:
2817 progNumber=number
2818#
2819# decode and check program name
2820#
2821 progName= self.__scannedOperand__[pnIndex].string
2822 match=re.fullmatch("[\x20-\x7A|\|]{1,"+str(allowedLen)+"}",progName)
2823 if not match:
2824 self.addError(MESSAGE.E_ILL_PROGNAME)
2825 return pOperand
2827 self.__opcodeLen__=26
2828#
2829 if progNumber >=0:
2830 return [clsParsedString(progName),clsParsedNumber(progNumber)]
2831 else:
2832 return [clsParsedString(progName)]
2833#
2834# Parse JMP relative instructions
2835#
2836 def pJrel(self):
2837 self.__opcodeLen__=2
2838 return [self.parseLabelOp(0)]
2840#
2841# Parse Push-/Pop- instructions
2842#
2843 def pStack(self):
2845 parsedOperand=[]
2846 self.__opcodeLen__=1
2847#
2848# parse DR
2849#
2850 dRegister=self.parseDr()
2851#
2852# parse AR (signed!)
2853#
2854 aRegister=self.parseAr(True)
2855 if not aRegister.isInvalid():
2856 if aRegister.registerSign=="+":
2857 self.__addressMode__= clsParserInfo.STACK_INCREMENT
2858 else:
2859 self.__addressMode__= clsParserInfo.STACK_DECREMENT
2860 return [dRegister,aRegister]
2862#
2863# Parse or/xor- instructions, they have two operands DR and AR
2864#
2865 def pOrXr(self):
2866 self.__opcodeLen__=1
2867 dRegister=self.parseDr()
2868 aRegister=self.parseAr()
2869 if dRegister.isInvalid() or aRegister.isInvalid():
2870 self.__opcodeLen__=1
2871 return [dRegister,aRegister]
2872#
2873# Parse instructions without operand. If PAD was encountered set a flag to
2874# ensure that the DRP/ARP conetext becomes disabled
2875#
2876 def pNoPer(self):
2877 self.__opcodeLen__=1
2878 if self.__opcode__== "PAD":
2879 self.__globVar__.lastStmtWasPAD=True
2880 return [ ]
2882#
2883# Parse p1reg instructions, the only operand is the data register
2884#
2885 def p1reg(self):
2886 self.__opcodeLen__=1
2887 dRegister=self.parseDr()
2888 if dRegister.isInvalid():
2889 self.__opcodeLen__=1
2890 return [dRegister]
2891#
2892# Parse arp instruction, the only operand is the data register
2893#
2894 def pArp(self):
2895 dRegister=self.parseRegister(self.__scannedOperand__[0],False,True)
2896 self.__opcodeLen__=1
2897 if not dRegister.isInvalid():
2898 self.__globVar__.arpReg= dRegister.registerNumber
2899 if dRegister.registerTyp=="!":
2900 self.__opcodeLen__=0
2901 return [dRegister]
2902#
2903# Parse drp instruction, the only operand is the data register
2904#
2905 def pDrp(self):
2906 dRegister=self.parseRegister(self.__scannedOperand__[0],False,True)
2907 self.__opcodeLen__=1
2908 if not dRegister.isInvalid():
2909 self.__globVar__.drpReg= dRegister.registerNumber
2910 if dRegister.registerTyp=="!":
2911 self.__opcodeLen__=0
2912 return [dRegister]
2913#
2914# Parse line, top level method
2915#
2916 def parseLine(self,scannedLine,line):
2917 self.__messages__= [ ]
2918 self.__scannedLine__=scannedLine
2919 self.__scannedLineNumber__= scannedLine[0]
2920 self.__scannedLabel__= scannedLine[1]
2921 self.__line__=line
2922 self.__scannedOpcode__=self.__scannedLine__[2]
2923 self.__scannedOperand__= self.__scannedLine__[3]
2925 self.__parsedOperand__= [ ]
2926 self.__opcodeLen__=0
2927 self.__needsArp__= -1
2928 self.__needsDrp__= -1
2929 self.__addressMode__= clsParserInfo.AM_REGISTER_IMMEDIATE
2930 PC=self.__globVar__.PC
2931 self.__lineInfo__=self.__infile__.getLineInfo()
2933 condAssemblyIsSuppressed=self.__globVar__.condAssembly.isSuppressed()
2934#
2935# Parse lineNumber, if we have one (may be not a valid integer)
2936#
2937 if self.__scannedLineNumber__ is not None:
2938 if parseFunc.parseDecimal( \
2939 self.__scannedLineNumber__.string) is None:
2940 self.addError(MESSAGE.E_ILL_LINENUMBER)
2941#
2942# If we have a label field, parse it and enter label into symbol table
2943#
2944 if self.__scannedLabel__ is not None and not condAssemblyIsSuppressed:
2945 self.__hasLcl__=self.parseLabelField()
2946#
2947# Return if we have no opcode nor operands
2948#
2949 if self.__scannedOpcode__ is None:
2950 return clsParserInfo(PC,self.__lineInfo__,self.__messages__, \
2951 self.__line__)
2952#
2953# Return if we have a comment ! in the opcode field
2954#
2955 if self.__scannedOpcode__.string=="!":
2956 return clsParserInfo(PC,self.__lineInfo__,self.__messages__, \
2957 self.__line__)
2959 self.__opcode__=self.__scannedOpcode__.string
2960#
2961# Get information how to parse the opcode
2962#
2963 self.__opcodeInfo__=OPCODES.get(self.__opcode__)
2964#
2965# return error information, if opcode not found
2966#
2967 if self.__opcodeInfo__ ==[]:
2968 self.__hasLcl__=False
2969 self.addError(MESSAGE.E_ILL_OPCODE)
2970 return clsParserInfo(PC,self.__lineInfo__,self.__messages__, \
2971 self.__line__)
2972#
2973# We have to check the conditional assembly status,
2974# treat the line as comment if we are in False state
2975# except we have an conditional assembly statement
2976#
2977 if self.__opcodeInfo__[7]:
2978 condAssemblyIsSuppressed=False
2979 if condAssemblyIsSuppressed:
2980 return clsParserInfo(PC,self.__lineInfo__,self.__messages__, \
2981 self.__line__)
2982#
2983# Invalidate arp, drp context if the previous statement was a subroutine
2984# call or PAD
2985#
2986 if self.__globVar__.lastStmtWasPAD or self.__globVar__.lastStmtWasJSB:
2987 self.__globVar__.arpReg= -1
2988 self.__globVar__.drpReg= -1
2989 self.__globVar__.lastStmtWasPAD=False
2990 self.__globVar__.lastStmtWasJSB=False
2992#
2993# Check number of params for the opcode
2994#
2995 if len(self.__scannedOperand__)< self.__opcodeInfo__[3]:
2996 self.addError(MESSAGE.E_ILL_NUMOPERANDS)
2997 return clsParserInfo(PC,self.__lineInfo__,self.__messages__, \
2998 self.__line__)
2999 if self.__opcodeInfo__[4] != OPCODES.NUM_OPERANDS_ANY:
3000 if len(self.__scannedOperand__)> self.__opcodeInfo__[4]:
3001 self.addError(MESSAGE.E_ILL_NUMOPERANDS)
3002 return clsParserInfo(PC,self.__lineInfo__,self.__messages__, \
3003 self.__line__)
3004#
3005# Invalidate arp/drp context if we the following conditions are met:
3006# - the opcode generates executable code
3007# - the opcode is no unconditional jump
3008# - a local label exists for that line
3009#
3010 if not self.__opcodeInfo__[6]:
3011 if self.__hasLcl__ and not self.__opcodeInfo__[5]:
3012 self.__globVar__.arpReg= -1
3013 self.__globVar__.drpReg= -1
3014 self.__hasLcl__=False
3015#
3016# Call operand parse method
3017#
3018 fname=self.__opcodeInfo__[0]
3019 self.__parsedOperand__= getattr(self,fname)()
3020#
3021# Set flag, if the parsed operand is an unconditional JMP
3022# This flag is needed for parsing an immediately following ELSE
3023# statement which will eliminate the jump instructions to the
3024# corresponding ENDIF
3025#
3026 if self.__opcodeInfo__[5]:
3027 self.__globVar__.lastOpcodeWasJmp=True
3028 else:
3029 if not self.__opcodeInfo__[6]:
3030 self.__globVar__.lastOpcodeWasJmp=False
3031#
3032# return parsed statement information
3033#
3034 return clsParserInfo(PC,self.__lineInfo__,self.__messages__, \
3035 self.__line__, \
3036 self.__opcode__,self.__opcodeLen__, self.__parsedOperand__, \
3037 self.__needsArp__,self.__needsDrp__,self.__addressMode__)