Hide keyboard shortcuts

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 

37 

38# 

39# Program Constants ----------------------------------------------------- 

40# 

41CAPASM_VERSION="Version 1.0.0" 

42CAPASM_VERSION_DATE="January 2021" 

43 

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 

52 

53# 

54# Class to generates Date/Time as BCD --------------------------------------- 

55# 

56class clsDateTime(object): 

57 

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) 

67 

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): 

77 

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, 

87 

88 0o10: 2, 

89 0o11: 1, 

90 0o12: 2, 

91 0o13: 1, 

92 0o14: 2, 

93 0o15: 1, 

94 0o16: 2, 

95 0o17: 1, 

96 

97 0o20: 2, 

98 0o21: 1, 

99 0o22: 2, 

100 0o23: 1, 

101 0o24: 2, 

102 0o25: 1, 

103 0o26: 2, 

104 0o27: 1, 

105 

106 0o30: 2, 

107 0o31: 1, 

108 0o32: 2, 

109 0o33: 1, 

110 0o34: 2, 

111 0o35: 1, 

112 0o36: 2, 

113 0o37: 1, 

114 

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, 

123 

124 0o50: 8, 

125 0o51: 7, 

126 0o52: 6, 

127 0o53: 5, 

128 0o54: 4, 

129 0o55: 3, 

130 0o56: 2, 

131 0o57: 1, 

132 

133 0o60: 8, 

134 0o61: 7, 

135 0o62: 6, 

136 0o63: 5, 

137 0o64: 4, 

138 0o65: 3, 

139 0o66: 2, 

140 0o67: 1, 

141 

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 } 

151 

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): 

162 

163 DELIMITER="" 

164 LABELMATCHSTRING="" 

165 

166 

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] 

176 

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 

217 

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 

260 

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 

284 

285# 

286# Basic Static class for the opcode dictionary ---------------------------------- 

287# 

288class OPCODES(object): 

289 

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 [] 

410 

411 

412 

413 

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 

465 

466 

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", 

515 

516 } 

517 

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) 

533 

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 

544 

545 def __init__(self,extendedChecks,globalSymbolFile,dictSymTypes): 

546 super().__init__() 

547 

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()) 

651 

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 

667 

668 

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): 

685 

686 def __init__(self,definedFlags): 

687 

688 super().__init__() 

689 self.__stack__= [] 

690 self.__flags__ = {} 

691 self.__activeConditionIndex__= -1 

692 

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) 

739 

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] 

781 

782# 

783# GlobVar data class, global variables of the assembler -------------------- 

784# 

785class clsGlobVar(object): 

786 

787 def __init__(self): 

788 

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): 

820 

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 

827 

828 def __repr__(self): # pragma: no cover 

829 return ("clsToken object '{:s}' {:d} '{:s}'".format(self.string, self.position,self.termChar)) 

830 

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): 

839 

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# 

846 

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] 

866 

867 return [oldch, oldposition,oldnxtch] 

868# 

869# Get Token, returns a token object 

870# 

871 

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): 

922 

923 scannedLineNumber=None 

924 scannedLabel=None 

925 scannedOpcode=None 

926 scannedOperand=[] 

927 

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] 

1001 

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): 

1053 

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]) 

1118 

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) 

1412 

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]] 

1437 

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 

1468 

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 

1483 

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 

1499 

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 

1517 

1518 def __init__(self,typ=OP_INVALID): 

1519 self.typ=typ 

1520 self.size=None 

1521 

1522 def __repr__(self): # pragma: no cover 

1523 return("clsParsedOperand (generic)") 

1524 

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): 

1531 

1532 def __init__(self): 

1533 super().__init__(clsParsedOperand.OP_INVALID) 

1534 

1535 def __repr__(self): # pragma: no cover 

1536 return("clsParsedOperand (invalid)") 

1537# 

1538# Parsed expression 

1539# 

1540class clsParsedExpression(clsParsedOperand): 

1541 

1542 def __init__(self,bytecode,size): 

1543 super().__init__(clsParsedOperand.OP_EXPRESSION) 

1544 self.byteCode=bytecode 

1545 self.size=size 

1546 

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): 

1557 

1558 def __init__(self,number,size=None): 

1559 super().__init__(clsParsedOperand.OP_NUMBER) 

1560 self.number=number 

1561 self.size=size 

1562 

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): 

1569 

1570 def __init__(self,string): 

1571 super().__init__(clsParsedOperand.OP_STRING) 

1572 self.string=string 

1573 

1574 def __repr__(self): # pragma: no cover 

1575 return ("clsParsedString string= {:s}".format(self.string)) 

1576 

1577# 

1578# Valid label operand (syntax checked) with optional size constraint 

1579# for symbols in literal data lists 

1580# 

1581class clsParsedLabel(clsParsedOperand): 

1582 

1583 def __init__(self,label,size=None): 

1584 super().__init__(clsParsedOperand.OP_LABEL) 

1585 self.label=label 

1586 self.size=size 

1587 

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): 

1594 

1595 R_HASH=-1 

1596 R_ILLEGAL=-2 

1597 

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 

1608 

1609 def __repr__(self): # pragma: no cover 

1610 return ("clsParsedRegister object '{:s}' {:s} '{:d}'".format(self.registerSign, self.registerTyp,self.registerNumber)) 

1611 

1612 

1613# 

1614# Code Info Data class ------------------------------------------------- 

1615# 

1616# An object of this class is returned by the code generator 

1617# 

1618class clsCodeInfo(object): 

1619 

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) 

1624 

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) 

1630 

1631 

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 

1650 

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 } 

1675 

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 

1736 

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 

1770 

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 

1821 

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 

1846 

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: 

1969 

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) 

2003 

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 

2042 

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): 

2062 

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 

2175 

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 

2249 

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 

2280 

2281 

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) 

2297 

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 

2319 

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 

2328 

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 

2337 

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 

2346 

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 

2357 

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 

2368 

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 

2376 

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] 

2400 

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 [] 

2417 

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) 

2439 

2440 

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 

2498 

2499 return [clsParsedLabel(label2,2)] 

2500 

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 

2561 

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 [] 

2640 

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 

2711 

2712 

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 

2724 

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# 

2761 

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 

2776 

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 

2826 

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)] 

2839 

2840# 

2841# Parse Push-/Pop- instructions 

2842# 

2843 def pStack(self): 

2844 

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] 

2861 

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 [ ] 

2881 

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] 

2924 

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() 

2932 

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__) 

2958 

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 

2991 

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__) 

3038