1# @file ConvertMasmToNasm.py 2# This script assists with conversion of MASM assembly syntax to NASM 3# 4# Copyright (c) 2007 - 2016, Intel Corporation. All rights reserved.<BR> 5# 6# This program and the accompanying materials 7# are licensed and made available under the terms and conditions of the BSD License 8# which accompanies this distribution. The full text of the license may be found at 9# http://opensource.org/licenses/bsd-license.php 10# 11# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 12# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 13# 14 15from __future__ import print_function 16 17# 18# Import Modules 19# 20import argparse 21import io 22import os.path 23import re 24import subprocess 25import sys 26 27 28class UnsupportedConversion(Exception): 29 pass 30 31 32class NoSourceFile(Exception): 33 pass 34 35 36class UnsupportedArch(Exception): 37 unsupported = ('aarch64', 'arm', 'ebc', 'ipf') 38 39 40class CommonUtils: 41 42 # Version and Copyright 43 VersionNumber = "0.01" 44 __version__ = "%prog Version " + VersionNumber 45 __copyright__ = "Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved." 46 __usage__ = "%prog [options] source.asm [destination.nasm]" 47 48 def __init__(self, clone=None): 49 if clone is None: 50 self.args = self.ProcessCommandLine() 51 else: 52 self.args = clone.args 53 54 self.unsupportedSyntaxSeen = False 55 self.src = self.args.source 56 self.keep = self.args.keep 57 assert(os.path.exists(self.src)) 58 self.dirmode = os.path.isdir(self.src) 59 srcExt = os.path.splitext(self.src)[1] 60 assert (self.dirmode or srcExt != '.nasm') 61 self.infmode = not self.dirmode and srcExt == '.inf' 62 self.diff = self.args.diff 63 self.git = self.args.git 64 self.force = self.args.force 65 66 if clone is None: 67 self.rootdir = os.getcwd() 68 self.DetectGit() 69 else: 70 self.rootdir = clone.rootdir 71 self.gitdir = clone.gitdir 72 self.gitemail = clone.gitemail 73 74 def ProcessCommandLine(self): 75 parser = argparse.ArgumentParser(description=self.__copyright__) 76 parser.add_argument('--version', action='version', 77 version='%(prog)s ' + self.VersionNumber) 78 parser.add_argument("-q", "--quiet", action="store_true", 79 help="Disable all messages except FATAL ERRORS.") 80 parser.add_argument("--git", action="store_true", 81 help="Use git to create commits for each file converted") 82 parser.add_argument("--keep", action="append", choices=('asm', 's'), 83 default=[], 84 help="Don't remove files with this extension") 85 parser.add_argument("--diff", action="store_true", 86 help="Show diff of conversion") 87 parser.add_argument("-f", "--force", action="store_true", 88 help="Force conversion even if unsupported") 89 parser.add_argument('source', help='MASM input file') 90 parser.add_argument('dest', nargs='?', 91 help='NASM output file (default=input.nasm; - for stdout)') 92 93 return parser.parse_args() 94 95 def RootRelative(self, path): 96 result = path 97 if result.startswith(self.rootdir): 98 result = result[len(self.rootdir):] 99 while len(result) > 0 and result[0] in '/\\': 100 result = result[1:] 101 return result 102 103 def MatchAndSetMo(self, regexp, string): 104 self.mo = regexp.match(string) 105 return self.mo is not None 106 107 def SearchAndSetMo(self, regexp, string): 108 self.mo = regexp.search(string) 109 return self.mo is not None 110 111 def ReplacePreserveSpacing(self, string, find, replace): 112 if len(find) >= len(replace): 113 padded = replace + (' ' * (len(find) - len(replace))) 114 return string.replace(find, padded) 115 elif find.find(replace) >= 0: 116 return string.replace(find, replace) 117 else: 118 lenDiff = len(replace) - len(find) 119 result = string 120 for i in range(lenDiff, -1, -1): 121 padded = find + (' ' * i) 122 result = result.replace(padded, replace) 123 return result 124 125 def DetectGit(self): 126 lastpath = os.path.realpath(self.src) 127 self.gitdir = None 128 while True: 129 path = os.path.split(lastpath)[0] 130 if path == lastpath: 131 self.gitemail = None 132 return 133 candidate = os.path.join(path, '.git') 134 if os.path.isdir(candidate): 135 self.gitdir = candidate 136 self.gitemail = self.FormatGitEmailAddress() 137 return 138 lastpath = path 139 140 def FormatGitEmailAddress(self): 141 if not self.git or not self.gitdir: 142 return '' 143 144 cmd = ('git', 'config', 'user.name') 145 name = self.RunAndCaptureOutput(cmd).strip() 146 cmd = ('git', 'config', 'user.email') 147 email = self.RunAndCaptureOutput(cmd).strip() 148 if name.find(',') >= 0: 149 name = '"' + name + '"' 150 return name + ' <' + email + '>' 151 152 def RunAndCaptureOutput(self, cmd, checkExitCode=True, pipeIn=None): 153 if pipeIn: 154 subpStdin = subprocess.PIPE 155 else: 156 subpStdin = None 157 p = subprocess.Popen(args=cmd, stdout=subprocess.PIPE, stdin=subpStdin) 158 (stdout, stderr) = p.communicate(pipeIn) 159 if checkExitCode: 160 if p.returncode != 0: 161 print('command:', ' '.join(cmd)) 162 print('stdout:', stdout) 163 print('stderr:', stderr) 164 print('return:', p.returncode) 165 assert p.returncode == 0 166 return stdout.decode('utf-8', 'ignore') 167 168 def FileUpdated(self, path): 169 if not self.git or not self.gitdir: 170 return 171 172 cmd = ('git', 'add', path) 173 self.RunAndCaptureOutput(cmd) 174 175 def FileAdded(self, path): 176 self.FileUpdated(path) 177 178 def RemoveFile(self, path): 179 if not self.git or not self.gitdir: 180 return 181 182 if self.ShouldKeepFile(path): 183 return 184 185 cmd = ('git', 'rm', path) 186 self.RunAndCaptureOutput(cmd) 187 188 def ShouldKeepFile(self, path): 189 ext = os.path.splitext(path)[1].lower() 190 if ext.startswith('.'): 191 ext = ext[1:] 192 return ext in self.keep 193 194 def FileConversionFinished(self, pkg, module, src, dst): 195 if not self.git or not self.gitdir: 196 return 197 198 if not self.args.quiet: 199 print('Committing: Conversion of', dst) 200 201 prefix = ' '.join(filter(lambda a: a, [pkg, module])) 202 message = '' 203 if self.unsupportedSyntaxSeen: 204 message += 'ERROR! ' 205 message += '%s: Convert %s to NASM\n' % (prefix, src) 206 message += '\n' 207 message += 'The %s script was used to convert\n' % sys.argv[0] 208 message += '%s to %s\n' % (src, dst) 209 message += '\n' 210 message += 'Contributed-under: TianoCore Contribution Agreement 1.0\n' 211 assert(self.gitemail is not None) 212 message += 'Signed-off-by: %s\n' % self.gitemail 213 message = message.encode('utf-8', 'ignore') 214 215 cmd = ('git', 'commit', '-F', '-') 216 self.RunAndCaptureOutput(cmd, pipeIn=message) 217 218 219class ConvertAsmFile(CommonUtils): 220 221 def __init__(self, src, dst, clone): 222 CommonUtils.__init__(self, clone) 223 self.ConvertAsmFile(src, dst) 224 self.FileAdded(dst) 225 self.RemoveFile(src) 226 227 def ConvertAsmFile(self, inputFile, outputFile=None): 228 self.globals = set() 229 self.unsupportedSyntaxSeen = False 230 self.inputFilename = inputFile 231 if not outputFile: 232 outputFile = os.path.splitext(inputFile)[0] + '.nasm' 233 self.outputFilename = outputFile 234 235 fullSrc = os.path.realpath(inputFile) 236 srcParentDir = os.path.basename(os.path.split(fullSrc)[0]) 237 maybeArch = srcParentDir.lower() 238 if maybeArch in UnsupportedArch.unsupported: 239 raise UnsupportedArch 240 self.ia32 = maybeArch == 'ia32' 241 self.x64 = maybeArch == 'x64' 242 243 self.inputFileBase = os.path.basename(self.inputFilename) 244 self.outputFileBase = os.path.basename(self.outputFilename) 245 self.output = io.BytesIO() 246 if not self.args.quiet: 247 dirpath, src = os.path.split(self.inputFilename) 248 dirpath = self.RootRelative(dirpath) 249 dst = os.path.basename(self.outputFilename) 250 print('Converting:', dirpath, src, '->', dst) 251 lines = io.open(self.inputFilename).readlines() 252 self.Convert(lines) 253 if self.outputFilename == '-' and not self.diff: 254 output_data = self.output.getvalue() 255 if sys.version_info >= (3, 0): 256 output_data = output_data.decode('utf-8', 'ignore') 257 sys.stdout.write(output_data) 258 self.output.close() 259 else: 260 f = io.open(self.outputFilename, 'wb') 261 f.write(self.output.getvalue()) 262 f.close() 263 self.output.close() 264 265 endOfLineRe = re.compile(r''' 266 \s* ( ; .* )? \n $ 267 ''', 268 re.VERBOSE | re.MULTILINE 269 ) 270 begOfLineRe = re.compile(r''' 271 \s* 272 ''', 273 re.VERBOSE 274 ) 275 276 def Convert(self, lines): 277 self.proc = None 278 self.anonLabelCount = -1 279 output = self.output 280 self.oldAsmEmptyLineCount = 0 281 self.newAsmEmptyLineCount = 0 282 for line in lines: 283 mo = self.begOfLineRe.search(line) 284 assert mo is not None 285 self.indent = mo.group() 286 lineWithoutBeginning = line[len(self.indent):] 287 mo = self.endOfLineRe.search(lineWithoutBeginning) 288 if mo is None: 289 endOfLine = '' 290 else: 291 endOfLine = mo.group() 292 oldAsm = line[len(self.indent):len(line) - len(endOfLine)] 293 self.originalLine = line.rstrip() 294 if line.strip() == '': 295 self.oldAsmEmptyLineCount += 1 296 self.TranslateAsm(oldAsm, endOfLine) 297 if line.strip() != '': 298 self.oldAsmEmptyLineCount = 0 299 300 procDeclRe = re.compile(r''' 301 (?: ASM_PFX \s* [(] \s* )? 302 ([\w@][\w@0-9]*) \s* 303 [)]? \s+ 304 PROC 305 (?: \s+ NEAR | FAR )? 306 (?: \s+ C )? 307 (?: \s+ (PUBLIC | PRIVATE) )? 308 (?: \s+ USES ( (?: \s+ \w[\w0-9]* )+ ) )? 309 \s* $ 310 ''', 311 re.VERBOSE | re.IGNORECASE 312 ) 313 314 procEndRe = re.compile(r''' 315 ([\w@][\w@0-9]*) \s+ 316 ENDP 317 \s* $ 318 ''', 319 re.VERBOSE | re.IGNORECASE 320 ) 321 322 varAndTypeSubRe = r' (?: [\w@][\w@0-9]* ) (?: \s* : \s* \w+ )? ' 323 publicRe = re.compile(r''' 324 PUBLIC \s+ 325 ( %s (?: \s* , \s* %s )* ) 326 \s* $ 327 ''' % (varAndTypeSubRe, varAndTypeSubRe), 328 re.VERBOSE | re.IGNORECASE 329 ) 330 331 varAndTypeSubRe = re.compile(varAndTypeSubRe, re.VERBOSE | re.IGNORECASE) 332 333 macroDeclRe = re.compile(r''' 334 ([\w@][\w@0-9]*) \s+ 335 MACRO 336 \s* $ 337 ''', 338 re.VERBOSE | re.IGNORECASE 339 ) 340 341 sectionDeclRe = re.compile(r''' 342 ([\w@][\w@0-9]*) \s+ 343 ( SECTION | ENDS ) 344 \s* $ 345 ''', 346 re.VERBOSE | re.IGNORECASE 347 ) 348 349 externRe = re.compile(r''' 350 EXTE?RN \s+ (?: C \s+ )? 351 ([\w@][\w@0-9]*) \s* : \s* (\w+) 352 \s* $ 353 ''', 354 re.VERBOSE | re.IGNORECASE 355 ) 356 357 externdefRe = re.compile(r''' 358 EXTERNDEF \s+ (?: C \s+ )? 359 ([\w@][\w@0-9]*) \s* : \s* (\w+) 360 \s* $ 361 ''', 362 re.VERBOSE | re.IGNORECASE 363 ) 364 365 protoRe = re.compile(r''' 366 ([\w@][\w@0-9]*) \s+ 367 PROTO 368 (?: \s+ .* )? 369 \s* $ 370 ''', 371 re.VERBOSE | re.IGNORECASE 372 ) 373 374 defineDataRe = re.compile(r''' 375 ([\w@][\w@0-9]*) \s+ 376 ( db | dw | dd | dq ) \s+ 377 ( .*? ) 378 \s* $ 379 ''', 380 re.VERBOSE | re.IGNORECASE 381 ) 382 383 equRe = re.compile(r''' 384 ([\w@][\w@0-9]*) \s+ EQU \s+ (\S.*?) 385 \s* $ 386 ''', 387 re.VERBOSE | re.IGNORECASE 388 ) 389 390 ignoreRe = re.compile(r''' 391 \. (?: const | 392 mmx | 393 model | 394 xmm | 395 x?list | 396 [3-6]86p? 397 ) | 398 page 399 (?: \s+ .* )? 400 \s* $ 401 ''', 402 re.VERBOSE | re.IGNORECASE 403 ) 404 405 whitespaceRe = re.compile(r'\s+', re.MULTILINE) 406 407 def TranslateAsm(self, oldAsm, endOfLine): 408 assert(oldAsm.strip() == oldAsm) 409 410 endOfLine = endOfLine.replace(self.inputFileBase, self.outputFileBase) 411 412 oldOp = oldAsm.split() 413 if len(oldOp) >= 1: 414 oldOp = oldOp[0] 415 else: 416 oldOp = '' 417 418 if oldAsm == '': 419 newAsm = oldAsm 420 self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) 421 elif oldOp in ('#include', ): 422 newAsm = oldAsm 423 self.EmitLine(oldAsm + endOfLine) 424 elif oldOp.lower() in ('end', 'title', 'text'): 425 newAsm = '' 426 self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) 427 elif oldAsm.lower() == '@@:': 428 self.anonLabelCount += 1 429 self.EmitLine(self.anonLabel(self.anonLabelCount) + ':') 430 elif self.MatchAndSetMo(self.ignoreRe, oldAsm): 431 newAsm = '' 432 self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) 433 elif oldAsm.lower() == 'ret': 434 for i in range(len(self.uses) - 1, -1, -1): 435 register = self.uses[i] 436 self.EmitNewContent('pop ' + register) 437 newAsm = 'ret' 438 self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) 439 self.uses = tuple() 440 elif oldOp.lower() == 'lea': 441 newAsm = self.ConvertLea(oldAsm) 442 self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) 443 elif oldAsm.lower() == 'end': 444 newAsm = '' 445 self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) 446 self.uses = tuple() 447 elif self.MatchAndSetMo(self.equRe, oldAsm): 448 equ = self.mo.group(1) 449 newAsm = '%%define %s %s' % (equ, self.mo.group(2)) 450 self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) 451 elif self.MatchAndSetMo(self.externRe, oldAsm) or \ 452 self.MatchAndSetMo(self.protoRe, oldAsm): 453 extern = self.mo.group(1) 454 self.NewGlobal(extern) 455 newAsm = 'extern ' + extern 456 self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) 457 elif self.MatchAndSetMo(self.externdefRe, oldAsm): 458 newAsm = '' 459 self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) 460 elif self.MatchAndSetMo(self.macroDeclRe, oldAsm): 461 newAsm = '%%macro %s 0' % self.mo.group(1) 462 self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) 463 elif oldOp.lower() == 'endm': 464 newAsm = r'%endmacro' 465 self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) 466 elif self.MatchAndSetMo(self.sectionDeclRe, oldAsm): 467 name = self.mo.group(1) 468 ty = self.mo.group(2) 469 if ty.lower() == 'section': 470 newAsm = '.' + name 471 else: 472 newAsm = '' 473 self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) 474 elif self.MatchAndSetMo(self.procDeclRe, oldAsm): 475 proc = self.proc = self.mo.group(1) 476 visibility = self.mo.group(2) 477 if visibility is None: 478 visibility = '' 479 else: 480 visibility = visibility.lower() 481 if visibility != 'private': 482 self.NewGlobal(self.proc) 483 proc = 'ASM_PFX(' + proc + ')' 484 self.EmitNewContent('global ' + proc) 485 newAsm = proc + ':' 486 self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) 487 uses = self.mo.group(3) 488 if uses is not None: 489 uses = tuple(filter(None, uses.split())) 490 else: 491 uses = tuple() 492 self.uses = uses 493 for register in self.uses: 494 self.EmitNewContent(' push ' + register) 495 elif self.MatchAndSetMo(self.procEndRe, oldAsm): 496 newAsm = '' 497 self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) 498 elif self.MatchAndSetMo(self.publicRe, oldAsm): 499 publics = re.findall(self.varAndTypeSubRe, self.mo.group(1)) 500 publics = tuple(map(lambda p: p.split(':')[0].strip(), publics)) 501 for i in range(len(publics) - 1): 502 name = publics[i] 503 self.EmitNewContent('global ASM_PFX(%s)' % publics[i]) 504 self.NewGlobal(name) 505 name = publics[-1] 506 self.NewGlobal(name) 507 newAsm = 'global ASM_PFX(%s)' % name 508 self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) 509 elif self.MatchAndSetMo(self.defineDataRe, oldAsm): 510 name = self.mo.group(1) 511 ty = self.mo.group(2) 512 value = self.mo.group(3) 513 if value == '?': 514 value = 0 515 newAsm = '%s: %s %s' % (name, ty, value) 516 newAsm = self.CommonConversions(newAsm) 517 self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) 518 else: 519 newAsm = self.CommonConversions(oldAsm) 520 self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) 521 522 def NewGlobal(self, name): 523 regex = re.compile(r'(?<![_\w\d])(?<!ASM_PFX\()(' + re.escape(name) + 524 r')(?![_\w\d])') 525 self.globals.add(regex) 526 527 def ConvertAnonymousLabels(self, oldAsm): 528 newAsm = oldAsm 529 anonLabel = self.anonLabel(self.anonLabelCount) 530 newAsm = newAsm.replace('@b', anonLabel) 531 newAsm = newAsm.replace('@B', anonLabel) 532 anonLabel = self.anonLabel(self.anonLabelCount + 1) 533 newAsm = newAsm.replace('@f', anonLabel) 534 newAsm = newAsm.replace('@F', anonLabel) 535 return newAsm 536 537 def anonLabel(self, count): 538 return '.%d' % count 539 540 def EmitString(self, string): 541 self.output.write(string.encode('utf-8', 'ignore')) 542 543 def EmitLineWithDiff(self, old, new): 544 newLine = (self.indent + new).rstrip() 545 if self.diff: 546 if old is None: 547 print('+%s' % newLine) 548 elif newLine != old: 549 print('-%s' % old) 550 print('+%s' % newLine) 551 else: 552 print('', newLine) 553 if newLine != '': 554 self.newAsmEmptyLineCount = 0 555 self.EmitString(newLine + '\r\n') 556 557 def EmitLine(self, string): 558 self.EmitLineWithDiff(self.originalLine, string) 559 560 def EmitNewContent(self, string): 561 self.EmitLineWithDiff(None, string) 562 563 def EmitAsmReplaceOp(self, oldAsm, oldOp, newOp, endOfLine): 564 newAsm = oldAsm.replace(oldOp, newOp, 1) 565 self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) 566 567 hexNumRe = re.compile(r'0*((?=[\da-f])\d*(?<=\d)[\da-f]*)h', re.IGNORECASE) 568 569 def EmitAsmWithComment(self, oldAsm, newAsm, endOfLine): 570 for glblRe in self.globals: 571 newAsm = glblRe.sub(r'ASM_PFX(\1)', newAsm) 572 573 newAsm = self.hexNumRe.sub(r'0x\1', newAsm) 574 575 newLine = newAsm + endOfLine 576 emitNewLine = ((newLine.strip() != '') or 577 ((oldAsm + endOfLine).strip() == '')) 578 if emitNewLine and newLine.strip() == '': 579 self.newAsmEmptyLineCount += 1 580 if self.newAsmEmptyLineCount > 1: 581 emitNewLine = False 582 if emitNewLine: 583 self.EmitLine(newLine.rstrip()) 584 elif self.diff: 585 print('-%s' % self.originalLine) 586 587 leaRe = re.compile(r''' 588 (lea \s+) ([\w@][\w@0-9]*) \s* , \s* (\S (?:.*\S)?) 589 \s* $ 590 ''', 591 re.VERBOSE | re.IGNORECASE 592 ) 593 594 def ConvertLea(self, oldAsm): 595 newAsm = oldAsm 596 if self.MatchAndSetMo(self.leaRe, oldAsm): 597 lea = self.mo.group(1) 598 dst = self.mo.group(2) 599 src = self.mo.group(3) 600 if src.find('[') < 0: 601 src = '[' + src + ']' 602 newAsm = lea + dst + ', ' + src 603 newAsm = self.CommonConversions(newAsm) 604 return newAsm 605 606 ptrRe = re.compile(r''' 607 (?<! \S ) 608 ([dfq]?word|byte) \s+ (?: ptr ) (\s*) 609 (?= [[\s] ) 610 ''', 611 re.VERBOSE | re.IGNORECASE 612 ) 613 614 def ConvertPtr(self, oldAsm): 615 newAsm = oldAsm 616 while self.SearchAndSetMo(self.ptrRe, newAsm): 617 ty = self.mo.group(1) 618 if ty.lower() == 'fword': 619 ty = '' 620 else: 621 ty += self.mo.group(2) 622 newAsm = newAsm[:self.mo.start(0)] + ty + newAsm[self.mo.end(0):] 623 return newAsm 624 625 labelByteRe = re.compile(r''' 626 (?: \s+ label \s+ (?: [dfq]?word | byte ) ) 627 (?! \S ) 628 ''', 629 re.VERBOSE | re.IGNORECASE 630 ) 631 632 def ConvertLabelByte(self, oldAsm): 633 newAsm = oldAsm 634 if self.SearchAndSetMo(self.labelByteRe, newAsm): 635 newAsm = newAsm[:self.mo.start(0)] + ':' + newAsm[self.mo.end(0):] 636 return newAsm 637 638 unaryBitwiseOpRe = re.compile(r''' 639 ( NOT ) 640 (?= \s+ \S ) 641 ''', 642 re.VERBOSE | re.IGNORECASE 643 ) 644 binaryBitwiseOpRe = re.compile(r''' 645 ( \S \s+ ) 646 ( AND | OR | SHL | SHR ) 647 (?= \s+ \S ) 648 ''', 649 re.VERBOSE | re.IGNORECASE 650 ) 651 bitwiseOpReplacements = { 652 'not': '~', 653 'and': '&', 654 'shl': '<<', 655 'shr': '>>', 656 'or': '|', 657 } 658 659 def ConvertBitwiseOp(self, oldAsm): 660 newAsm = oldAsm 661 while self.SearchAndSetMo(self.binaryBitwiseOpRe, newAsm): 662 prefix = self.mo.group(1) 663 op = self.bitwiseOpReplacements[self.mo.group(2).lower()] 664 newAsm = newAsm[:self.mo.start(0)] + prefix + op + \ 665 newAsm[self.mo.end(0):] 666 while self.SearchAndSetMo(self.unaryBitwiseOpRe, newAsm): 667 op = self.bitwiseOpReplacements[self.mo.group(1).lower()] 668 newAsm = newAsm[:self.mo.start(0)] + op + newAsm[self.mo.end(0):] 669 return newAsm 670 671 sectionRe = re.compile(r''' 672 \. ( code | 673 data 674 ) 675 (?: \s+ .* )? 676 \s* $ 677 ''', 678 re.VERBOSE | re.IGNORECASE 679 ) 680 681 segmentRe = re.compile(r''' 682 ( code | 683 data ) 684 (?: \s+ SEGMENT ) 685 (?: \s+ .* )? 686 \s* $ 687 ''', 688 re.VERBOSE | re.IGNORECASE 689 ) 690 691 def ConvertSection(self, oldAsm): 692 newAsm = oldAsm 693 if self.MatchAndSetMo(self.sectionRe, newAsm) or \ 694 self.MatchAndSetMo(self.segmentRe, newAsm): 695 name = self.mo.group(1).lower() 696 if name == 'code': 697 if self.x64: 698 self.EmitLine('DEFAULT REL') 699 name = 'text' 700 newAsm = 'SECTION .' + name 701 return newAsm 702 703 fwordRe = re.compile(r''' 704 (?<! \S ) 705 fword 706 (?! \S ) 707 ''', 708 re.VERBOSE | re.IGNORECASE 709 ) 710 711 def FwordUnsupportedCheck(self, oldAsm): 712 newAsm = oldAsm 713 if self.SearchAndSetMo(self.fwordRe, newAsm): 714 newAsm = self.Unsupported(newAsm, 'fword used') 715 return newAsm 716 717 __common_conversion_routines__ = ( 718 ConvertAnonymousLabels, 719 ConvertPtr, 720 FwordUnsupportedCheck, 721 ConvertBitwiseOp, 722 ConvertLabelByte, 723 ConvertSection, 724 ) 725 726 def CommonConversions(self, oldAsm): 727 newAsm = oldAsm 728 for conv in self.__common_conversion_routines__: 729 newAsm = conv(self, newAsm) 730 return newAsm 731 732 def Unsupported(self, asm, message=None): 733 if not self.force: 734 raise UnsupportedConversion 735 736 self.unsupportedSyntaxSeen = True 737 newAsm = '%error conversion unsupported' 738 if message: 739 newAsm += '; ' + message 740 newAsm += ': ' + asm 741 return newAsm 742 743 744class ConvertInfFile(CommonUtils): 745 746 def __init__(self, inf, clone): 747 CommonUtils.__init__(self, clone) 748 self.inf = inf 749 self.ScanInfAsmFiles() 750 if self.infmode: 751 self.ConvertInfAsmFiles() 752 753 infSrcRe = re.compile(r''' 754 \s* 755 ( [\w@][\w@0-9/]* \.(asm|s) ) 756 \s* (?: \| [^#]* )? 757 \s* (?: \# .* )? 758 $ 759 ''', 760 re.VERBOSE | re.IGNORECASE 761 ) 762 763 def GetInfAsmFileMapping(self): 764 srcToDst = {'order': []} 765 for line in self.lines: 766 line = line.rstrip() 767 if self.MatchAndSetMo(self.infSrcRe, line): 768 src = self.mo.group(1) 769 srcExt = self.mo.group(2) 770 dst = os.path.splitext(src)[0] + '.nasm' 771 fullDst = os.path.join(self.dir, dst) 772 if src not in srcToDst and not os.path.exists(fullDst): 773 srcToDst[src] = dst 774 srcToDst['order'].append(src) 775 return srcToDst 776 777 def ScanInfAsmFiles(self): 778 src = self.inf 779 assert os.path.isfile(src) 780 f = io.open(src, 'rt') 781 self.lines = f.readlines() 782 f.close() 783 784 path = os.path.realpath(self.inf) 785 (self.dir, inf) = os.path.split(path) 786 parent = os.path.normpath(self.dir) 787 (lastpath, self.moduleName) = os.path.split(parent) 788 self.packageName = None 789 while True: 790 lastpath = os.path.normpath(lastpath) 791 (parent, basename) = os.path.split(lastpath) 792 if parent == lastpath: 793 break 794 if basename.endswith('Pkg'): 795 self.packageName = basename 796 break 797 lastpath = parent 798 799 self.srcToDst = self.GetInfAsmFileMapping() 800 801 self.dstToSrc = {'order': []} 802 for src in self.srcToDst['order']: 803 srcExt = os.path.splitext(src)[1] 804 dst = self.srcToDst[src] 805 if dst not in self.dstToSrc: 806 self.dstToSrc[dst] = [src] 807 self.dstToSrc['order'].append(dst) 808 else: 809 self.dstToSrc[dst].append(src) 810 811 def __len__(self): 812 return len(self.dstToSrc['order']) 813 814 def __iter__(self): 815 return iter(self.dstToSrc['order']) 816 817 def ConvertInfAsmFiles(self): 818 notConverted = [] 819 unsupportedArchCount = 0 820 for dst in self: 821 didSomething = False 822 try: 823 self.UpdateInfAsmFile(dst) 824 didSomething = True 825 except UnsupportedConversion: 826 if not self.args.quiet: 827 print('MASM=>NASM conversion unsupported for', dst) 828 notConverted.append(dst) 829 except NoSourceFile: 830 if not self.args.quiet: 831 print('Source file missing for', reldst) 832 notConverted.append(dst) 833 except UnsupportedArch: 834 unsupportedArchCount += 1 835 else: 836 if didSomething: 837 self.ConversionFinished(dst) 838 if len(notConverted) > 0 and not self.args.quiet: 839 for dst in notConverted: 840 reldst = self.RootRelative(dst) 841 print('Unabled to convert', reldst) 842 if unsupportedArchCount > 0 and not self.args.quiet: 843 print('Skipped', unsupportedArchCount, 'files based on architecture') 844 845 def UpdateInfAsmFile(self, dst, IgnoreMissingAsm=False): 846 infPath = os.path.split(os.path.realpath(self.inf))[0] 847 asmSrc = os.path.splitext(dst)[0] + '.asm' 848 fullSrc = os.path.join(infPath, asmSrc) 849 fullDst = os.path.join(infPath, dst) 850 srcParentDir = os.path.basename(os.path.split(fullSrc)[0]) 851 if srcParentDir.lower() in UnsupportedArch.unsupported: 852 raise UnsupportedArch 853 elif not os.path.exists(fullSrc): 854 if not IgnoreMissingAsm: 855 raise NoSourceFile 856 else: # not os.path.exists(fullDst): 857 conv = ConvertAsmFile(fullSrc, fullDst, self) 858 self.unsupportedSyntaxSeen = conv.unsupportedSyntaxSeen 859 860 fileChanged = False 861 recentSources = list() 862 i = 0 863 while i < len(self.lines): 864 line = self.lines[i].rstrip() 865 updatedLine = line 866 lineChanged = False 867 preserveOldSource = False 868 for src in self.dstToSrc[dst]: 869 assert self.srcToDst[src] == dst 870 updatedLine = self.ReplacePreserveSpacing( 871 updatedLine, src, dst) 872 lineChanged = updatedLine != line 873 if lineChanged: 874 preserveOldSource = self.ShouldKeepFile(src) 875 break 876 877 if lineChanged: 878 if preserveOldSource: 879 if updatedLine.strip() not in recentSources: 880 self.lines.insert(i, updatedLine + '\n') 881 recentSources.append(updatedLine.strip()) 882 i += 1 883 if self.diff: 884 print('+%s' % updatedLine) 885 if self.diff: 886 print('', line) 887 else: 888 if self.diff: 889 print('-%s' % line) 890 if updatedLine.strip() in recentSources: 891 self.lines[i] = None 892 else: 893 self.lines[i] = updatedLine + '\n' 894 recentSources.append(updatedLine.strip()) 895 if self.diff: 896 print('+%s' % updatedLine) 897 else: 898 if len(recentSources) > 0: 899 recentSources = list() 900 if self.diff: 901 print('', line) 902 903 fileChanged |= lineChanged 904 i += 1 905 906 if fileChanged: 907 self.lines = list(filter(lambda l: l is not None, self.lines)) 908 909 for src in self.dstToSrc[dst]: 910 if not src.endswith('.asm'): 911 fullSrc = os.path.join(infPath, src) 912 if os.path.exists(fullSrc): 913 self.RemoveFile(fullSrc) 914 915 if fileChanged: 916 f = io.open(self.inf, 'w', newline='\r\n') 917 f.writelines(self.lines) 918 f.close() 919 self.FileUpdated(self.inf) 920 921 def ConversionFinished(self, dst): 922 asmSrc = os.path.splitext(dst)[0] + '.asm' 923 self.FileConversionFinished( 924 self.packageName, self.moduleName, asmSrc, dst) 925 926 927class ConvertInfFiles(CommonUtils): 928 929 def __init__(self, infs, clone): 930 CommonUtils.__init__(self, clone) 931 infs = map(lambda i: ConvertInfFile(i, self), infs) 932 infs = filter(lambda i: len(i) > 0, infs) 933 dstToInfs = {'order': []} 934 for inf in infs: 935 for dst in inf: 936 fulldst = os.path.realpath(os.path.join(inf.dir, dst)) 937 pair = (inf, dst) 938 if fulldst in dstToInfs: 939 dstToInfs[fulldst].append(pair) 940 else: 941 dstToInfs['order'].append(fulldst) 942 dstToInfs[fulldst] = [pair] 943 944 notConverted = [] 945 unsupportedArchCount = 0 946 for dst in dstToInfs['order']: 947 didSomething = False 948 try: 949 for inf, reldst in dstToInfs[dst]: 950 inf.UpdateInfAsmFile(reldst, IgnoreMissingAsm=didSomething) 951 didSomething = True 952 except UnsupportedConversion: 953 if not self.args.quiet: 954 print('MASM=>NASM conversion unsupported for', reldst) 955 notConverted.append(dst) 956 except NoSourceFile: 957 if not self.args.quiet: 958 print('Source file missing for', reldst) 959 notConverted.append(dst) 960 except UnsupportedArch: 961 unsupportedArchCount += 1 962 else: 963 if didSomething: 964 inf.ConversionFinished(reldst) 965 if len(notConverted) > 0 and not self.args.quiet: 966 for dst in notConverted: 967 reldst = self.RootRelative(dst) 968 print('Unabled to convert', reldst) 969 if unsupportedArchCount > 0 and not self.args.quiet: 970 print('Skipped', unsupportedArchCount, 'files based on architecture') 971 972 973class ConvertDirectories(CommonUtils): 974 975 def __init__(self, paths, clone): 976 CommonUtils.__init__(self, clone) 977 self.paths = paths 978 self.ConvertInfAndAsmFiles() 979 980 def ConvertInfAndAsmFiles(self): 981 infs = list() 982 for path in self.paths: 983 assert(os.path.exists(path)) 984 for path in self.paths: 985 for root, dirs, files in os.walk(path): 986 for d in ('.svn', '.git'): 987 if d in dirs: 988 dirs.remove(d) 989 for f in files: 990 if f.lower().endswith('.inf'): 991 inf = os.path.realpath(os.path.join(root, f)) 992 infs.append(inf) 993 994 ConvertInfFiles(infs, self) 995 996 997class ConvertAsmApp(CommonUtils): 998 999 def __init__(self): 1000 CommonUtils.__init__(self) 1001 1002 src = self.args.source 1003 dst = self.args.dest 1004 if self.infmode: 1005 ConvertInfFiles((src,), self) 1006 elif self.dirmode: 1007 ConvertDirectories((src,), self) 1008 elif not self.dirmode: 1009 ConvertAsmFile(src, dst, self) 1010 1011ConvertAsmApp() 1012