diff options
| author | kx <kx@radix.pro> | 2024-06-04 06:43:08 +0300 |
|---|---|---|
| committer | kx <kx@radix.pro> | 2024-06-04 06:43:08 +0300 |
| commit | 7e2ccccace24636f29ddc210b94606abd4c7e42b (patch) | |
| tree | 545d43ea12671048956cafeb12303e84d601e571 /BaseTools/Scripts/ConvertMasmToNasm.py | |
| parent | 27b044605cd5f6b33a3d231576003850b3fe305b (diff) | |
| download | edk2-trunk.tar.xz | |
Renormalized end-of-lines from master@27b044605cd5f6b33a3d231576003850b3fe305btrunk
Diffstat (limited to 'BaseTools/Scripts/ConvertMasmToNasm.py')
| -rwxr-xr-x | BaseTools/Scripts/ConvertMasmToNasm.py | 2010 |
1 files changed, 1005 insertions, 1005 deletions
diff --git a/BaseTools/Scripts/ConvertMasmToNasm.py b/BaseTools/Scripts/ConvertMasmToNasm.py index 0ef5a8badb..7c286a0637 100755 --- a/BaseTools/Scripts/ConvertMasmToNasm.py +++ b/BaseTools/Scripts/ConvertMasmToNasm.py @@ -1,1005 +1,1005 @@ -# @file ConvertMasmToNasm.py
-# This script assists with conversion of MASM assembly syntax to NASM
-#
-# Copyright (c) 2007 - 2016, Intel Corporation. All rights reserved.<BR>
-#
-# SPDX-License-Identifier: BSD-2-Clause-Patent
-#
-
-from __future__ import print_function
-
-#
-# Import Modules
-#
-import argparse
-import io
-import os.path
-import re
-import subprocess
-import sys
-
-
-class UnsupportedConversion(Exception):
- pass
-
-
-class NoSourceFile(Exception):
- pass
-
-
-class UnsupportedArch(Exception):
- unsupported = ('aarch64', 'arm', 'ebc', 'ipf')
-
-
-class CommonUtils:
-
- # Version and Copyright
- VersionNumber = "0.01"
- __version__ = "%prog Version " + VersionNumber
- __copyright__ = "Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved."
- __usage__ = "%prog [options] source.asm [destination.nasm]"
-
- def __init__(self, clone=None):
- if clone is None:
- self.args = self.ProcessCommandLine()
- else:
- self.args = clone.args
-
- self.unsupportedSyntaxSeen = False
- self.src = self.args.source
- self.keep = self.args.keep
- assert(os.path.exists(self.src))
- self.dirmode = os.path.isdir(self.src)
- srcExt = os.path.splitext(self.src)[1]
- assert (self.dirmode or srcExt != '.nasm')
- self.infmode = not self.dirmode and srcExt == '.inf'
- self.diff = self.args.diff
- self.git = self.args.git
- self.force = self.args.force
-
- if clone is None:
- self.rootdir = os.getcwd()
- self.DetectGit()
- else:
- self.rootdir = clone.rootdir
- self.gitdir = clone.gitdir
- self.gitemail = clone.gitemail
-
- def ProcessCommandLine(self):
- parser = argparse.ArgumentParser(description=self.__copyright__)
- parser.add_argument('--version', action='version',
- version='%(prog)s ' + self.VersionNumber)
- parser.add_argument("-q", "--quiet", action="store_true",
- help="Disable all messages except FATAL ERRORS.")
- parser.add_argument("--git", action="store_true",
- help="Use git to create commits for each file converted")
- parser.add_argument("--keep", action="append", choices=('asm', 's'),
- default=[],
- help="Don't remove files with this extension")
- parser.add_argument("--diff", action="store_true",
- help="Show diff of conversion")
- parser.add_argument("-f", "--force", action="store_true",
- help="Force conversion even if unsupported")
- parser.add_argument('source', help='MASM input file')
- parser.add_argument('dest', nargs='?',
- help='NASM output file (default=input.nasm; - for stdout)')
-
- return parser.parse_args()
-
- def RootRelative(self, path):
- result = path
- if result.startswith(self.rootdir):
- result = result[len(self.rootdir):]
- while len(result) > 0 and result[0] in '/\\':
- result = result[1:]
- return result
-
- def MatchAndSetMo(self, regexp, string):
- self.mo = regexp.match(string)
- return self.mo is not None
-
- def SearchAndSetMo(self, regexp, string):
- self.mo = regexp.search(string)
- return self.mo is not None
-
- def ReplacePreserveSpacing(self, string, find, replace):
- if len(find) >= len(replace):
- padded = replace + (' ' * (len(find) - len(replace)))
- return string.replace(find, padded)
- elif find.find(replace) >= 0:
- return string.replace(find, replace)
- else:
- lenDiff = len(replace) - len(find)
- result = string
- for i in range(lenDiff, -1, -1):
- padded = find + (' ' * i)
- result = result.replace(padded, replace)
- return result
-
- def DetectGit(self):
- lastpath = os.path.realpath(self.src)
- self.gitdir = None
- while True:
- path = os.path.split(lastpath)[0]
- if path == lastpath:
- self.gitemail = None
- return
- candidate = os.path.join(path, '.git')
- if os.path.isdir(candidate):
- self.gitdir = candidate
- self.gitemail = self.FormatGitEmailAddress()
- return
- lastpath = path
-
- def FormatGitEmailAddress(self):
- if not self.git or not self.gitdir:
- return ''
-
- cmd = ('git', 'config', 'user.name')
- name = self.RunAndCaptureOutput(cmd).strip()
- cmd = ('git', 'config', 'user.email')
- email = self.RunAndCaptureOutput(cmd).strip()
- if name.find(',') >= 0:
- name = '"' + name + '"'
- return name + ' <' + email + '>'
-
- def RunAndCaptureOutput(self, cmd, checkExitCode=True, pipeIn=None):
- if pipeIn:
- subpStdin = subprocess.PIPE
- else:
- subpStdin = None
- p = subprocess.Popen(args=cmd, stdout=subprocess.PIPE, stdin=subpStdin)
- (stdout, stderr) = p.communicate(pipeIn)
- if checkExitCode:
- if p.returncode != 0:
- print('command:', ' '.join(cmd))
- print('stdout:', stdout)
- print('stderr:', stderr)
- print('return:', p.returncode)
- assert p.returncode == 0
- return stdout.decode('utf-8', 'ignore')
-
- def FileUpdated(self, path):
- if not self.git or not self.gitdir:
- return
-
- cmd = ('git', 'add', path)
- self.RunAndCaptureOutput(cmd)
-
- def FileAdded(self, path):
- self.FileUpdated(path)
-
- def RemoveFile(self, path):
- if not self.git or not self.gitdir:
- return
-
- if self.ShouldKeepFile(path):
- return
-
- cmd = ('git', 'rm', path)
- self.RunAndCaptureOutput(cmd)
-
- def ShouldKeepFile(self, path):
- ext = os.path.splitext(path)[1].lower()
- if ext.startswith('.'):
- ext = ext[1:]
- return ext in self.keep
-
- def FileConversionFinished(self, pkg, module, src, dst):
- if not self.git or not self.gitdir:
- return
-
- if not self.args.quiet:
- print('Committing: Conversion of', dst)
-
- prefix = ' '.join(filter(lambda a: a, [pkg, module]))
- message = ''
- if self.unsupportedSyntaxSeen:
- message += 'ERROR! '
- message += '%s: Convert %s to NASM\n' % (prefix, src)
- message += '\n'
- message += 'The %s script was used to convert\n' % sys.argv[0]
- message += '%s to %s\n' % (src, dst)
- message += '\n'
- message += 'Contributed-under: TianoCore Contribution Agreement 1.0\n'
- assert(self.gitemail is not None)
- message += 'Signed-off-by: %s\n' % self.gitemail
- message = message.encode('utf-8', 'ignore')
-
- cmd = ('git', 'commit', '-F', '-')
- self.RunAndCaptureOutput(cmd, pipeIn=message)
-
-
-class ConvertAsmFile(CommonUtils):
-
- def __init__(self, src, dst, clone):
- CommonUtils.__init__(self, clone)
- self.ConvertAsmFile(src, dst)
- self.FileAdded(dst)
- self.RemoveFile(src)
-
- def ConvertAsmFile(self, inputFile, outputFile=None):
- self.globals = set()
- self.unsupportedSyntaxSeen = False
- self.inputFilename = inputFile
- if not outputFile:
- outputFile = os.path.splitext(inputFile)[0] + '.nasm'
- self.outputFilename = outputFile
-
- fullSrc = os.path.realpath(inputFile)
- srcParentDir = os.path.basename(os.path.split(fullSrc)[0])
- maybeArch = srcParentDir.lower()
- if maybeArch in UnsupportedArch.unsupported:
- raise UnsupportedArch
- self.ia32 = maybeArch == 'ia32'
- self.x64 = maybeArch == 'x64'
-
- self.inputFileBase = os.path.basename(self.inputFilename)
- self.outputFileBase = os.path.basename(self.outputFilename)
- self.output = io.BytesIO()
- if not self.args.quiet:
- dirpath, src = os.path.split(self.inputFilename)
- dirpath = self.RootRelative(dirpath)
- dst = os.path.basename(self.outputFilename)
- print('Converting:', dirpath, src, '->', dst)
- lines = io.open(self.inputFilename).readlines()
- self.Convert(lines)
- if self.outputFilename == '-' and not self.diff:
- output_data = self.output.getvalue()
- if sys.version_info >= (3, 0):
- output_data = output_data.decode('utf-8', 'ignore')
- sys.stdout.write(output_data)
- self.output.close()
- else:
- f = io.open(self.outputFilename, 'wb')
- f.write(self.output.getvalue())
- f.close()
- self.output.close()
-
- endOfLineRe = re.compile(r'''
- \s* ( ; .* )? \n $
- ''',
- re.VERBOSE | re.MULTILINE
- )
- begOfLineRe = re.compile(r'''
- \s*
- ''',
- re.VERBOSE
- )
-
- def Convert(self, lines):
- self.proc = None
- self.anonLabelCount = -1
- output = self.output
- self.oldAsmEmptyLineCount = 0
- self.newAsmEmptyLineCount = 0
- for line in lines:
- mo = self.begOfLineRe.search(line)
- assert mo is not None
- self.indent = mo.group()
- lineWithoutBeginning = line[len(self.indent):]
- mo = self.endOfLineRe.search(lineWithoutBeginning)
- if mo is None:
- endOfLine = ''
- else:
- endOfLine = mo.group()
- oldAsm = line[len(self.indent):len(line) - len(endOfLine)]
- self.originalLine = line.rstrip()
- if line.strip() == '':
- self.oldAsmEmptyLineCount += 1
- self.TranslateAsm(oldAsm, endOfLine)
- if line.strip() != '':
- self.oldAsmEmptyLineCount = 0
-
- procDeclRe = re.compile(r'''
- (?: ASM_PFX \s* [(] \s* )?
- ([\w@][\w@0-9]*) \s*
- [)]? \s+
- PROC
- (?: \s+ NEAR | FAR )?
- (?: \s+ C )?
- (?: \s+ (PUBLIC | PRIVATE) )?
- (?: \s+ USES ( (?: \s+ \w[\w0-9]* )+ ) )?
- \s* $
- ''',
- re.VERBOSE | re.IGNORECASE
- )
-
- procEndRe = re.compile(r'''
- ([\w@][\w@0-9]*) \s+
- ENDP
- \s* $
- ''',
- re.VERBOSE | re.IGNORECASE
- )
-
- varAndTypeSubRe = r' (?: [\w@][\w@0-9]* ) (?: \s* : \s* \w+ )? '
- publicRe = re.compile(r'''
- PUBLIC \s+
- ( %s (?: \s* , \s* %s )* )
- \s* $
- ''' % (varAndTypeSubRe, varAndTypeSubRe),
- re.VERBOSE | re.IGNORECASE
- )
-
- varAndTypeSubRe = re.compile(varAndTypeSubRe, re.VERBOSE | re.IGNORECASE)
-
- macroDeclRe = re.compile(r'''
- ([\w@][\w@0-9]*) \s+
- MACRO
- \s* $
- ''',
- re.VERBOSE | re.IGNORECASE
- )
-
- sectionDeclRe = re.compile(r'''
- ([\w@][\w@0-9]*) \s+
- ( SECTION | ENDS )
- \s* $
- ''',
- re.VERBOSE | re.IGNORECASE
- )
-
- externRe = re.compile(r'''
- EXTE?RN \s+ (?: C \s+ )?
- ([\w@][\w@0-9]*) \s* : \s* (\w+)
- \s* $
- ''',
- re.VERBOSE | re.IGNORECASE
- )
-
- externdefRe = re.compile(r'''
- EXTERNDEF \s+ (?: C \s+ )?
- ([\w@][\w@0-9]*) \s* : \s* (\w+)
- \s* $
- ''',
- re.VERBOSE | re.IGNORECASE
- )
-
- protoRe = re.compile(r'''
- ([\w@][\w@0-9]*) \s+
- PROTO
- (?: \s+ .* )?
- \s* $
- ''',
- re.VERBOSE | re.IGNORECASE
- )
-
- defineDataRe = re.compile(r'''
- ([\w@][\w@0-9]*) \s+
- ( db | dw | dd | dq ) \s+
- ( .*? )
- \s* $
- ''',
- re.VERBOSE | re.IGNORECASE
- )
-
- equRe = re.compile(r'''
- ([\w@][\w@0-9]*) \s+ EQU \s+ (\S.*?)
- \s* $
- ''',
- re.VERBOSE | re.IGNORECASE
- )
-
- ignoreRe = re.compile(r'''
- \. (?: const |
- mmx |
- model |
- xmm |
- x?list |
- [3-6]86p?
- ) |
- page
- (?: \s+ .* )?
- \s* $
- ''',
- re.VERBOSE | re.IGNORECASE
- )
-
- whitespaceRe = re.compile(r'\s+', re.MULTILINE)
-
- def TranslateAsm(self, oldAsm, endOfLine):
- assert(oldAsm.strip() == oldAsm)
-
- endOfLine = endOfLine.replace(self.inputFileBase, self.outputFileBase)
-
- oldOp = oldAsm.split()
- if len(oldOp) >= 1:
- oldOp = oldOp[0]
- else:
- oldOp = ''
-
- if oldAsm == '':
- newAsm = oldAsm
- self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
- elif oldOp in ('#include', ):
- newAsm = oldAsm
- self.EmitLine(oldAsm + endOfLine)
- elif oldOp.lower() in ('end', 'title', 'text'):
- newAsm = ''
- self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
- elif oldAsm.lower() == '@@:':
- self.anonLabelCount += 1
- self.EmitLine(self.anonLabel(self.anonLabelCount) + ':')
- elif self.MatchAndSetMo(self.ignoreRe, oldAsm):
- newAsm = ''
- self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
- elif oldAsm.lower() == 'ret':
- for i in range(len(self.uses) - 1, -1, -1):
- register = self.uses[i]
- self.EmitNewContent('pop ' + register)
- newAsm = 'ret'
- self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
- self.uses = tuple()
- elif oldOp.lower() == 'lea':
- newAsm = self.ConvertLea(oldAsm)
- self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
- elif oldAsm.lower() == 'end':
- newAsm = ''
- self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
- self.uses = tuple()
- elif self.MatchAndSetMo(self.equRe, oldAsm):
- equ = self.mo.group(1)
- newAsm = '%%define %s %s' % (equ, self.mo.group(2))
- self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
- elif self.MatchAndSetMo(self.externRe, oldAsm) or \
- self.MatchAndSetMo(self.protoRe, oldAsm):
- extern = self.mo.group(1)
- self.NewGlobal(extern)
- newAsm = 'extern ' + extern
- self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
- elif self.MatchAndSetMo(self.externdefRe, oldAsm):
- newAsm = ''
- self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
- elif self.MatchAndSetMo(self.macroDeclRe, oldAsm):
- newAsm = '%%macro %s 0' % self.mo.group(1)
- self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
- elif oldOp.lower() == 'endm':
- newAsm = r'%endmacro'
- self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
- elif self.MatchAndSetMo(self.sectionDeclRe, oldAsm):
- name = self.mo.group(1)
- ty = self.mo.group(2)
- if ty.lower() == 'section':
- newAsm = '.' + name
- else:
- newAsm = ''
- self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
- elif self.MatchAndSetMo(self.procDeclRe, oldAsm):
- proc = self.proc = self.mo.group(1)
- visibility = self.mo.group(2)
- if visibility is None:
- visibility = ''
- else:
- visibility = visibility.lower()
- if visibility != 'private':
- self.NewGlobal(self.proc)
- proc = 'ASM_PFX(' + proc + ')'
- self.EmitNewContent('global ' + proc)
- newAsm = proc + ':'
- self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
- uses = self.mo.group(3)
- if uses is not None:
- uses = tuple(filter(None, uses.split()))
- else:
- uses = tuple()
- self.uses = uses
- for register in self.uses:
- self.EmitNewContent(' push ' + register)
- elif self.MatchAndSetMo(self.procEndRe, oldAsm):
- newAsm = ''
- self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
- elif self.MatchAndSetMo(self.publicRe, oldAsm):
- publics = re.findall(self.varAndTypeSubRe, self.mo.group(1))
- publics = tuple(map(lambda p: p.split(':')[0].strip(), publics))
- for i in range(len(publics) - 1):
- name = publics[i]
- self.EmitNewContent('global ASM_PFX(%s)' % publics[i])
- self.NewGlobal(name)
- name = publics[-1]
- self.NewGlobal(name)
- newAsm = 'global ASM_PFX(%s)' % name
- self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
- elif self.MatchAndSetMo(self.defineDataRe, oldAsm):
- name = self.mo.group(1)
- ty = self.mo.group(2)
- value = self.mo.group(3)
- if value == '?':
- value = 0
- newAsm = '%s: %s %s' % (name, ty, value)
- newAsm = self.CommonConversions(newAsm)
- self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
- else:
- newAsm = self.CommonConversions(oldAsm)
- self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
-
- def NewGlobal(self, name):
- regex = re.compile(r'(?<![_\w\d])(?<!ASM_PFX\()(' + re.escape(name) +
- r')(?![_\w\d])')
- self.globals.add(regex)
-
- def ConvertAnonymousLabels(self, oldAsm):
- newAsm = oldAsm
- anonLabel = self.anonLabel(self.anonLabelCount)
- newAsm = newAsm.replace('@b', anonLabel)
- newAsm = newAsm.replace('@B', anonLabel)
- anonLabel = self.anonLabel(self.anonLabelCount + 1)
- newAsm = newAsm.replace('@f', anonLabel)
- newAsm = newAsm.replace('@F', anonLabel)
- return newAsm
-
- def anonLabel(self, count):
- return '.%d' % count
-
- def EmitString(self, string):
- self.output.write(string.encode('utf-8', 'ignore'))
-
- def EmitLineWithDiff(self, old, new):
- newLine = (self.indent + new).rstrip()
- if self.diff:
- if old is None:
- print('+%s' % newLine)
- elif newLine != old:
- print('-%s' % old)
- print('+%s' % newLine)
- else:
- print('', newLine)
- if newLine != '':
- self.newAsmEmptyLineCount = 0
- self.EmitString(newLine + '\r\n')
-
- def EmitLine(self, string):
- self.EmitLineWithDiff(self.originalLine, string)
-
- def EmitNewContent(self, string):
- self.EmitLineWithDiff(None, string)
-
- def EmitAsmReplaceOp(self, oldAsm, oldOp, newOp, endOfLine):
- newAsm = oldAsm.replace(oldOp, newOp, 1)
- self.EmitAsmWithComment(oldAsm, newAsm, endOfLine)
-
- hexNumRe = re.compile(r'0*((?=[\da-f])\d*(?<=\d)[\da-f]*)h', re.IGNORECASE)
-
- def EmitAsmWithComment(self, oldAsm, newAsm, endOfLine):
- for glblRe in self.globals:
- newAsm = glblRe.sub(r'ASM_PFX(\1)', newAsm)
-
- newAsm = self.hexNumRe.sub(r'0x\1', newAsm)
-
- newLine = newAsm + endOfLine
- emitNewLine = ((newLine.strip() != '') or
- ((oldAsm + endOfLine).strip() == ''))
- if emitNewLine and newLine.strip() == '':
- self.newAsmEmptyLineCount += 1
- if self.newAsmEmptyLineCount > 1:
- emitNewLine = False
- if emitNewLine:
- self.EmitLine(newLine.rstrip())
- elif self.diff:
- print('-%s' % self.originalLine)
-
- leaRe = re.compile(r'''
- (lea \s+) ([\w@][\w@0-9]*) \s* , \s* (\S (?:.*\S)?)
- \s* $
- ''',
- re.VERBOSE | re.IGNORECASE
- )
-
- def ConvertLea(self, oldAsm):
- newAsm = oldAsm
- if self.MatchAndSetMo(self.leaRe, oldAsm):
- lea = self.mo.group(1)
- dst = self.mo.group(2)
- src = self.mo.group(3)
- if src.find('[') < 0:
- src = '[' + src + ']'
- newAsm = lea + dst + ', ' + src
- newAsm = self.CommonConversions(newAsm)
- return newAsm
-
- ptrRe = re.compile(r'''
- (?<! \S )
- ([dfq]?word|byte) \s+ (?: ptr ) (\s*)
- (?= [[\s] )
- ''',
- re.VERBOSE | re.IGNORECASE
- )
-
- def ConvertPtr(self, oldAsm):
- newAsm = oldAsm
- while self.SearchAndSetMo(self.ptrRe, newAsm):
- ty = self.mo.group(1)
- if ty.lower() == 'fword':
- ty = ''
- else:
- ty += self.mo.group(2)
- newAsm = newAsm[:self.mo.start(0)] + ty + newAsm[self.mo.end(0):]
- return newAsm
-
- labelByteRe = re.compile(r'''
- (?: \s+ label \s+ (?: [dfq]?word | byte ) )
- (?! \S )
- ''',
- re.VERBOSE | re.IGNORECASE
- )
-
- def ConvertLabelByte(self, oldAsm):
- newAsm = oldAsm
- if self.SearchAndSetMo(self.labelByteRe, newAsm):
- newAsm = newAsm[:self.mo.start(0)] + ':' + newAsm[self.mo.end(0):]
- return newAsm
-
- unaryBitwiseOpRe = re.compile(r'''
- ( NOT )
- (?= \s+ \S )
- ''',
- re.VERBOSE | re.IGNORECASE
- )
- binaryBitwiseOpRe = re.compile(r'''
- ( \S \s+ )
- ( AND | OR | SHL | SHR )
- (?= \s+ \S )
- ''',
- re.VERBOSE | re.IGNORECASE
- )
- bitwiseOpReplacements = {
- 'not': '~',
- 'and': '&',
- 'shl': '<<',
- 'shr': '>>',
- 'or': '|',
- }
-
- def ConvertBitwiseOp(self, oldAsm):
- newAsm = oldAsm
- while self.SearchAndSetMo(self.binaryBitwiseOpRe, newAsm):
- prefix = self.mo.group(1)
- op = self.bitwiseOpReplacements[self.mo.group(2).lower()]
- newAsm = newAsm[:self.mo.start(0)] + prefix + op + \
- newAsm[self.mo.end(0):]
- while self.SearchAndSetMo(self.unaryBitwiseOpRe, newAsm):
- op = self.bitwiseOpReplacements[self.mo.group(1).lower()]
- newAsm = newAsm[:self.mo.start(0)] + op + newAsm[self.mo.end(0):]
- return newAsm
-
- sectionRe = re.compile(r'''
- \. ( code |
- data
- )
- (?: \s+ .* )?
- \s* $
- ''',
- re.VERBOSE | re.IGNORECASE
- )
-
- segmentRe = re.compile(r'''
- ( code |
- data )
- (?: \s+ SEGMENT )
- (?: \s+ .* )?
- \s* $
- ''',
- re.VERBOSE | re.IGNORECASE
- )
-
- def ConvertSection(self, oldAsm):
- newAsm = oldAsm
- if self.MatchAndSetMo(self.sectionRe, newAsm) or \
- self.MatchAndSetMo(self.segmentRe, newAsm):
- name = self.mo.group(1).lower()
- if name == 'code':
- if self.x64:
- self.EmitLine('DEFAULT REL')
- name = 'text'
- newAsm = 'SECTION .' + name
- return newAsm
-
- fwordRe = re.compile(r'''
- (?<! \S )
- fword
- (?! \S )
- ''',
- re.VERBOSE | re.IGNORECASE
- )
-
- def FwordUnsupportedCheck(self, oldAsm):
- newAsm = oldAsm
- if self.SearchAndSetMo(self.fwordRe, newAsm):
- newAsm = self.Unsupported(newAsm, 'fword used')
- return newAsm
-
- __common_conversion_routines__ = (
- ConvertAnonymousLabels,
- ConvertPtr,
- FwordUnsupportedCheck,
- ConvertBitwiseOp,
- ConvertLabelByte,
- ConvertSection,
- )
-
- def CommonConversions(self, oldAsm):
- newAsm = oldAsm
- for conv in self.__common_conversion_routines__:
- newAsm = conv(self, newAsm)
- return newAsm
-
- def Unsupported(self, asm, message=None):
- if not self.force:
- raise UnsupportedConversion
-
- self.unsupportedSyntaxSeen = True
- newAsm = '%error conversion unsupported'
- if message:
- newAsm += '; ' + message
- newAsm += ': ' + asm
- return newAsm
-
-
-class ConvertInfFile(CommonUtils):
-
- def __init__(self, inf, clone):
- CommonUtils.__init__(self, clone)
- self.inf = inf
- self.ScanInfAsmFiles()
- if self.infmode:
- self.ConvertInfAsmFiles()
-
- infSrcRe = re.compile(r'''
- \s*
- ( [\w@][\w@0-9/]* \.(asm|s) )
- \s* (?: \| [^#]* )?
- \s* (?: \# .* )?
- $
- ''',
- re.VERBOSE | re.IGNORECASE
- )
-
- def GetInfAsmFileMapping(self):
- srcToDst = {'order': []}
- for line in self.lines:
- line = line.rstrip()
- if self.MatchAndSetMo(self.infSrcRe, line):
- src = self.mo.group(1)
- srcExt = self.mo.group(2)
- dst = os.path.splitext(src)[0] + '.nasm'
- fullDst = os.path.join(self.dir, dst)
- if src not in srcToDst and not os.path.exists(fullDst):
- srcToDst[src] = dst
- srcToDst['order'].append(src)
- return srcToDst
-
- def ScanInfAsmFiles(self):
- src = self.inf
- assert os.path.isfile(src)
- f = io.open(src, 'rt')
- self.lines = f.readlines()
- f.close()
-
- path = os.path.realpath(self.inf)
- (self.dir, inf) = os.path.split(path)
- parent = os.path.normpath(self.dir)
- (lastpath, self.moduleName) = os.path.split(parent)
- self.packageName = None
- while True:
- lastpath = os.path.normpath(lastpath)
- (parent, basename) = os.path.split(lastpath)
- if parent == lastpath:
- break
- if basename.endswith('Pkg'):
- self.packageName = basename
- break
- lastpath = parent
-
- self.srcToDst = self.GetInfAsmFileMapping()
-
- self.dstToSrc = {'order': []}
- for src in self.srcToDst['order']:
- srcExt = os.path.splitext(src)[1]
- dst = self.srcToDst[src]
- if dst not in self.dstToSrc:
- self.dstToSrc[dst] = [src]
- self.dstToSrc['order'].append(dst)
- else:
- self.dstToSrc[dst].append(src)
-
- def __len__(self):
- return len(self.dstToSrc['order'])
-
- def __iter__(self):
- return iter(self.dstToSrc['order'])
-
- def ConvertInfAsmFiles(self):
- notConverted = []
- unsupportedArchCount = 0
- for dst in self:
- didSomething = False
- try:
- self.UpdateInfAsmFile(dst)
- didSomething = True
- except UnsupportedConversion:
- if not self.args.quiet:
- print('MASM=>NASM conversion unsupported for', dst)
- notConverted.append(dst)
- except NoSourceFile:
- if not self.args.quiet:
- print('Source file missing for', reldst)
- notConverted.append(dst)
- except UnsupportedArch:
- unsupportedArchCount += 1
- else:
- if didSomething:
- self.ConversionFinished(dst)
- if len(notConverted) > 0 and not self.args.quiet:
- for dst in notConverted:
- reldst = self.RootRelative(dst)
- print('Unabled to convert', reldst)
- if unsupportedArchCount > 0 and not self.args.quiet:
- print('Skipped', unsupportedArchCount, 'files based on architecture')
-
- def UpdateInfAsmFile(self, dst, IgnoreMissingAsm=False):
- infPath = os.path.split(os.path.realpath(self.inf))[0]
- asmSrc = os.path.splitext(dst)[0] + '.asm'
- fullSrc = os.path.join(infPath, asmSrc)
- fullDst = os.path.join(infPath, dst)
- srcParentDir = os.path.basename(os.path.split(fullSrc)[0])
- if srcParentDir.lower() in UnsupportedArch.unsupported:
- raise UnsupportedArch
- elif not os.path.exists(fullSrc):
- if not IgnoreMissingAsm:
- raise NoSourceFile
- else: # not os.path.exists(fullDst):
- conv = ConvertAsmFile(fullSrc, fullDst, self)
- self.unsupportedSyntaxSeen = conv.unsupportedSyntaxSeen
-
- fileChanged = False
- recentSources = list()
- i = 0
- while i < len(self.lines):
- line = self.lines[i].rstrip()
- updatedLine = line
- lineChanged = False
- preserveOldSource = False
- for src in self.dstToSrc[dst]:
- assert self.srcToDst[src] == dst
- updatedLine = self.ReplacePreserveSpacing(
- updatedLine, src, dst)
- lineChanged = updatedLine != line
- if lineChanged:
- preserveOldSource = self.ShouldKeepFile(src)
- break
-
- if lineChanged:
- if preserveOldSource:
- if updatedLine.strip() not in recentSources:
- self.lines.insert(i, updatedLine + '\n')
- recentSources.append(updatedLine.strip())
- i += 1
- if self.diff:
- print('+%s' % updatedLine)
- if self.diff:
- print('', line)
- else:
- if self.diff:
- print('-%s' % line)
- if updatedLine.strip() in recentSources:
- self.lines[i] = None
- else:
- self.lines[i] = updatedLine + '\n'
- recentSources.append(updatedLine.strip())
- if self.diff:
- print('+%s' % updatedLine)
- else:
- if len(recentSources) > 0:
- recentSources = list()
- if self.diff:
- print('', line)
-
- fileChanged |= lineChanged
- i += 1
-
- if fileChanged:
- self.lines = list(filter(lambda l: l is not None, self.lines))
-
- for src in self.dstToSrc[dst]:
- if not src.endswith('.asm'):
- fullSrc = os.path.join(infPath, src)
- if os.path.exists(fullSrc):
- self.RemoveFile(fullSrc)
-
- if fileChanged:
- f = io.open(self.inf, 'w', newline='\r\n')
- f.writelines(self.lines)
- f.close()
- self.FileUpdated(self.inf)
-
- def ConversionFinished(self, dst):
- asmSrc = os.path.splitext(dst)[0] + '.asm'
- self.FileConversionFinished(
- self.packageName, self.moduleName, asmSrc, dst)
-
-
-class ConvertInfFiles(CommonUtils):
-
- def __init__(self, infs, clone):
- CommonUtils.__init__(self, clone)
- infs = map(lambda i: ConvertInfFile(i, self), infs)
- infs = filter(lambda i: len(i) > 0, infs)
- dstToInfs = {'order': []}
- for inf in infs:
- for dst in inf:
- fulldst = os.path.realpath(os.path.join(inf.dir, dst))
- pair = (inf, dst)
- if fulldst in dstToInfs:
- dstToInfs[fulldst].append(pair)
- else:
- dstToInfs['order'].append(fulldst)
- dstToInfs[fulldst] = [pair]
-
- notConverted = []
- unsupportedArchCount = 0
- for dst in dstToInfs['order']:
- didSomething = False
- try:
- for inf, reldst in dstToInfs[dst]:
- inf.UpdateInfAsmFile(reldst, IgnoreMissingAsm=didSomething)
- didSomething = True
- except UnsupportedConversion:
- if not self.args.quiet:
- print('MASM=>NASM conversion unsupported for', reldst)
- notConverted.append(dst)
- except NoSourceFile:
- if not self.args.quiet:
- print('Source file missing for', reldst)
- notConverted.append(dst)
- except UnsupportedArch:
- unsupportedArchCount += 1
- else:
- if didSomething:
- inf.ConversionFinished(reldst)
- if len(notConverted) > 0 and not self.args.quiet:
- for dst in notConverted:
- reldst = self.RootRelative(dst)
- print('Unabled to convert', reldst)
- if unsupportedArchCount > 0 and not self.args.quiet:
- print('Skipped', unsupportedArchCount, 'files based on architecture')
-
-
-class ConvertDirectories(CommonUtils):
-
- def __init__(self, paths, clone):
- CommonUtils.__init__(self, clone)
- self.paths = paths
- self.ConvertInfAndAsmFiles()
-
- def ConvertInfAndAsmFiles(self):
- infs = list()
- for path in self.paths:
- assert(os.path.exists(path))
- for path in self.paths:
- for root, dirs, files in os.walk(path):
- for d in ('.svn', '.git'):
- if d in dirs:
- dirs.remove(d)
- for f in files:
- if f.lower().endswith('.inf'):
- inf = os.path.realpath(os.path.join(root, f))
- infs.append(inf)
-
- ConvertInfFiles(infs, self)
-
-
-class ConvertAsmApp(CommonUtils):
-
- def __init__(self):
- CommonUtils.__init__(self)
-
- src = self.args.source
- dst = self.args.dest
- if self.infmode:
- ConvertInfFiles((src,), self)
- elif self.dirmode:
- ConvertDirectories((src,), self)
- elif not self.dirmode:
- ConvertAsmFile(src, dst, self)
-
-ConvertAsmApp()
+# @file ConvertMasmToNasm.py +# This script assists with conversion of MASM assembly syntax to NASM +# +# Copyright (c) 2007 - 2016, Intel Corporation. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# + +from __future__ import print_function + +# +# Import Modules +# +import argparse +import io +import os.path +import re +import subprocess +import sys + + +class UnsupportedConversion(Exception): + pass + + +class NoSourceFile(Exception): + pass + + +class UnsupportedArch(Exception): + unsupported = ('aarch64', 'arm', 'ebc', 'ipf') + + +class CommonUtils: + + # Version and Copyright + VersionNumber = "0.01" + __version__ = "%prog Version " + VersionNumber + __copyright__ = "Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved." + __usage__ = "%prog [options] source.asm [destination.nasm]" + + def __init__(self, clone=None): + if clone is None: + self.args = self.ProcessCommandLine() + else: + self.args = clone.args + + self.unsupportedSyntaxSeen = False + self.src = self.args.source + self.keep = self.args.keep + assert(os.path.exists(self.src)) + self.dirmode = os.path.isdir(self.src) + srcExt = os.path.splitext(self.src)[1] + assert (self.dirmode or srcExt != '.nasm') + self.infmode = not self.dirmode and srcExt == '.inf' + self.diff = self.args.diff + self.git = self.args.git + self.force = self.args.force + + if clone is None: + self.rootdir = os.getcwd() + self.DetectGit() + else: + self.rootdir = clone.rootdir + self.gitdir = clone.gitdir + self.gitemail = clone.gitemail + + def ProcessCommandLine(self): + parser = argparse.ArgumentParser(description=self.__copyright__) + parser.add_argument('--version', action='version', + version='%(prog)s ' + self.VersionNumber) + parser.add_argument("-q", "--quiet", action="store_true", + help="Disable all messages except FATAL ERRORS.") + parser.add_argument("--git", action="store_true", + help="Use git to create commits for each file converted") + parser.add_argument("--keep", action="append", choices=('asm', 's'), + default=[], + help="Don't remove files with this extension") + parser.add_argument("--diff", action="store_true", + help="Show diff of conversion") + parser.add_argument("-f", "--force", action="store_true", + help="Force conversion even if unsupported") + parser.add_argument('source', help='MASM input file') + parser.add_argument('dest', nargs='?', + help='NASM output file (default=input.nasm; - for stdout)') + + return parser.parse_args() + + def RootRelative(self, path): + result = path + if result.startswith(self.rootdir): + result = result[len(self.rootdir):] + while len(result) > 0 and result[0] in '/\\': + result = result[1:] + return result + + def MatchAndSetMo(self, regexp, string): + self.mo = regexp.match(string) + return self.mo is not None + + def SearchAndSetMo(self, regexp, string): + self.mo = regexp.search(string) + return self.mo is not None + + def ReplacePreserveSpacing(self, string, find, replace): + if len(find) >= len(replace): + padded = replace + (' ' * (len(find) - len(replace))) + return string.replace(find, padded) + elif find.find(replace) >= 0: + return string.replace(find, replace) + else: + lenDiff = len(replace) - len(find) + result = string + for i in range(lenDiff, -1, -1): + padded = find + (' ' * i) + result = result.replace(padded, replace) + return result + + def DetectGit(self): + lastpath = os.path.realpath(self.src) + self.gitdir = None + while True: + path = os.path.split(lastpath)[0] + if path == lastpath: + self.gitemail = None + return + candidate = os.path.join(path, '.git') + if os.path.isdir(candidate): + self.gitdir = candidate + self.gitemail = self.FormatGitEmailAddress() + return + lastpath = path + + def FormatGitEmailAddress(self): + if not self.git or not self.gitdir: + return '' + + cmd = ('git', 'config', 'user.name') + name = self.RunAndCaptureOutput(cmd).strip() + cmd = ('git', 'config', 'user.email') + email = self.RunAndCaptureOutput(cmd).strip() + if name.find(',') >= 0: + name = '"' + name + '"' + return name + ' <' + email + '>' + + def RunAndCaptureOutput(self, cmd, checkExitCode=True, pipeIn=None): + if pipeIn: + subpStdin = subprocess.PIPE + else: + subpStdin = None + p = subprocess.Popen(args=cmd, stdout=subprocess.PIPE, stdin=subpStdin) + (stdout, stderr) = p.communicate(pipeIn) + if checkExitCode: + if p.returncode != 0: + print('command:', ' '.join(cmd)) + print('stdout:', stdout) + print('stderr:', stderr) + print('return:', p.returncode) + assert p.returncode == 0 + return stdout.decode('utf-8', 'ignore') + + def FileUpdated(self, path): + if not self.git or not self.gitdir: + return + + cmd = ('git', 'add', path) + self.RunAndCaptureOutput(cmd) + + def FileAdded(self, path): + self.FileUpdated(path) + + def RemoveFile(self, path): + if not self.git or not self.gitdir: + return + + if self.ShouldKeepFile(path): + return + + cmd = ('git', 'rm', path) + self.RunAndCaptureOutput(cmd) + + def ShouldKeepFile(self, path): + ext = os.path.splitext(path)[1].lower() + if ext.startswith('.'): + ext = ext[1:] + return ext in self.keep + + def FileConversionFinished(self, pkg, module, src, dst): + if not self.git or not self.gitdir: + return + + if not self.args.quiet: + print('Committing: Conversion of', dst) + + prefix = ' '.join(filter(lambda a: a, [pkg, module])) + message = '' + if self.unsupportedSyntaxSeen: + message += 'ERROR! ' + message += '%s: Convert %s to NASM\n' % (prefix, src) + message += '\n' + message += 'The %s script was used to convert\n' % sys.argv[0] + message += '%s to %s\n' % (src, dst) + message += '\n' + message += 'Contributed-under: TianoCore Contribution Agreement 1.0\n' + assert(self.gitemail is not None) + message += 'Signed-off-by: %s\n' % self.gitemail + message = message.encode('utf-8', 'ignore') + + cmd = ('git', 'commit', '-F', '-') + self.RunAndCaptureOutput(cmd, pipeIn=message) + + +class ConvertAsmFile(CommonUtils): + + def __init__(self, src, dst, clone): + CommonUtils.__init__(self, clone) + self.ConvertAsmFile(src, dst) + self.FileAdded(dst) + self.RemoveFile(src) + + def ConvertAsmFile(self, inputFile, outputFile=None): + self.globals = set() + self.unsupportedSyntaxSeen = False + self.inputFilename = inputFile + if not outputFile: + outputFile = os.path.splitext(inputFile)[0] + '.nasm' + self.outputFilename = outputFile + + fullSrc = os.path.realpath(inputFile) + srcParentDir = os.path.basename(os.path.split(fullSrc)[0]) + maybeArch = srcParentDir.lower() + if maybeArch in UnsupportedArch.unsupported: + raise UnsupportedArch + self.ia32 = maybeArch == 'ia32' + self.x64 = maybeArch == 'x64' + + self.inputFileBase = os.path.basename(self.inputFilename) + self.outputFileBase = os.path.basename(self.outputFilename) + self.output = io.BytesIO() + if not self.args.quiet: + dirpath, src = os.path.split(self.inputFilename) + dirpath = self.RootRelative(dirpath) + dst = os.path.basename(self.outputFilename) + print('Converting:', dirpath, src, '->', dst) + lines = io.open(self.inputFilename).readlines() + self.Convert(lines) + if self.outputFilename == '-' and not self.diff: + output_data = self.output.getvalue() + if sys.version_info >= (3, 0): + output_data = output_data.decode('utf-8', 'ignore') + sys.stdout.write(output_data) + self.output.close() + else: + f = io.open(self.outputFilename, 'wb') + f.write(self.output.getvalue()) + f.close() + self.output.close() + + endOfLineRe = re.compile(r''' + \s* ( ; .* )? \n $ + ''', + re.VERBOSE | re.MULTILINE + ) + begOfLineRe = re.compile(r''' + \s* + ''', + re.VERBOSE + ) + + def Convert(self, lines): + self.proc = None + self.anonLabelCount = -1 + output = self.output + self.oldAsmEmptyLineCount = 0 + self.newAsmEmptyLineCount = 0 + for line in lines: + mo = self.begOfLineRe.search(line) + assert mo is not None + self.indent = mo.group() + lineWithoutBeginning = line[len(self.indent):] + mo = self.endOfLineRe.search(lineWithoutBeginning) + if mo is None: + endOfLine = '' + else: + endOfLine = mo.group() + oldAsm = line[len(self.indent):len(line) - len(endOfLine)] + self.originalLine = line.rstrip() + if line.strip() == '': + self.oldAsmEmptyLineCount += 1 + self.TranslateAsm(oldAsm, endOfLine) + if line.strip() != '': + self.oldAsmEmptyLineCount = 0 + + procDeclRe = re.compile(r''' + (?: ASM_PFX \s* [(] \s* )? + ([\w@][\w@0-9]*) \s* + [)]? \s+ + PROC + (?: \s+ NEAR | FAR )? + (?: \s+ C )? + (?: \s+ (PUBLIC | PRIVATE) )? + (?: \s+ USES ( (?: \s+ \w[\w0-9]* )+ ) )? + \s* $ + ''', + re.VERBOSE | re.IGNORECASE + ) + + procEndRe = re.compile(r''' + ([\w@][\w@0-9]*) \s+ + ENDP + \s* $ + ''', + re.VERBOSE | re.IGNORECASE + ) + + varAndTypeSubRe = r' (?: [\w@][\w@0-9]* ) (?: \s* : \s* \w+ )? ' + publicRe = re.compile(r''' + PUBLIC \s+ + ( %s (?: \s* , \s* %s )* ) + \s* $ + ''' % (varAndTypeSubRe, varAndTypeSubRe), + re.VERBOSE | re.IGNORECASE + ) + + varAndTypeSubRe = re.compile(varAndTypeSubRe, re.VERBOSE | re.IGNORECASE) + + macroDeclRe = re.compile(r''' + ([\w@][\w@0-9]*) \s+ + MACRO + \s* $ + ''', + re.VERBOSE | re.IGNORECASE + ) + + sectionDeclRe = re.compile(r''' + ([\w@][\w@0-9]*) \s+ + ( SECTION | ENDS ) + \s* $ + ''', + re.VERBOSE | re.IGNORECASE + ) + + externRe = re.compile(r''' + EXTE?RN \s+ (?: C \s+ )? + ([\w@][\w@0-9]*) \s* : \s* (\w+) + \s* $ + ''', + re.VERBOSE | re.IGNORECASE + ) + + externdefRe = re.compile(r''' + EXTERNDEF \s+ (?: C \s+ )? + ([\w@][\w@0-9]*) \s* : \s* (\w+) + \s* $ + ''', + re.VERBOSE | re.IGNORECASE + ) + + protoRe = re.compile(r''' + ([\w@][\w@0-9]*) \s+ + PROTO + (?: \s+ .* )? + \s* $ + ''', + re.VERBOSE | re.IGNORECASE + ) + + defineDataRe = re.compile(r''' + ([\w@][\w@0-9]*) \s+ + ( db | dw | dd | dq ) \s+ + ( .*? ) + \s* $ + ''', + re.VERBOSE | re.IGNORECASE + ) + + equRe = re.compile(r''' + ([\w@][\w@0-9]*) \s+ EQU \s+ (\S.*?) + \s* $ + ''', + re.VERBOSE | re.IGNORECASE + ) + + ignoreRe = re.compile(r''' + \. (?: const | + mmx | + model | + xmm | + x?list | + [3-6]86p? + ) | + page + (?: \s+ .* )? + \s* $ + ''', + re.VERBOSE | re.IGNORECASE + ) + + whitespaceRe = re.compile(r'\s+', re.MULTILINE) + + def TranslateAsm(self, oldAsm, endOfLine): + assert(oldAsm.strip() == oldAsm) + + endOfLine = endOfLine.replace(self.inputFileBase, self.outputFileBase) + + oldOp = oldAsm.split() + if len(oldOp) >= 1: + oldOp = oldOp[0] + else: + oldOp = '' + + if oldAsm == '': + newAsm = oldAsm + self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) + elif oldOp in ('#include', ): + newAsm = oldAsm + self.EmitLine(oldAsm + endOfLine) + elif oldOp.lower() in ('end', 'title', 'text'): + newAsm = '' + self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) + elif oldAsm.lower() == '@@:': + self.anonLabelCount += 1 + self.EmitLine(self.anonLabel(self.anonLabelCount) + ':') + elif self.MatchAndSetMo(self.ignoreRe, oldAsm): + newAsm = '' + self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) + elif oldAsm.lower() == 'ret': + for i in range(len(self.uses) - 1, -1, -1): + register = self.uses[i] + self.EmitNewContent('pop ' + register) + newAsm = 'ret' + self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) + self.uses = tuple() + elif oldOp.lower() == 'lea': + newAsm = self.ConvertLea(oldAsm) + self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) + elif oldAsm.lower() == 'end': + newAsm = '' + self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) + self.uses = tuple() + elif self.MatchAndSetMo(self.equRe, oldAsm): + equ = self.mo.group(1) + newAsm = '%%define %s %s' % (equ, self.mo.group(2)) + self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) + elif self.MatchAndSetMo(self.externRe, oldAsm) or \ + self.MatchAndSetMo(self.protoRe, oldAsm): + extern = self.mo.group(1) + self.NewGlobal(extern) + newAsm = 'extern ' + extern + self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) + elif self.MatchAndSetMo(self.externdefRe, oldAsm): + newAsm = '' + self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) + elif self.MatchAndSetMo(self.macroDeclRe, oldAsm): + newAsm = '%%macro %s 0' % self.mo.group(1) + self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) + elif oldOp.lower() == 'endm': + newAsm = r'%endmacro' + self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) + elif self.MatchAndSetMo(self.sectionDeclRe, oldAsm): + name = self.mo.group(1) + ty = self.mo.group(2) + if ty.lower() == 'section': + newAsm = '.' + name + else: + newAsm = '' + self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) + elif self.MatchAndSetMo(self.procDeclRe, oldAsm): + proc = self.proc = self.mo.group(1) + visibility = self.mo.group(2) + if visibility is None: + visibility = '' + else: + visibility = visibility.lower() + if visibility != 'private': + self.NewGlobal(self.proc) + proc = 'ASM_PFX(' + proc + ')' + self.EmitNewContent('global ' + proc) + newAsm = proc + ':' + self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) + uses = self.mo.group(3) + if uses is not None: + uses = tuple(filter(None, uses.split())) + else: + uses = tuple() + self.uses = uses + for register in self.uses: + self.EmitNewContent(' push ' + register) + elif self.MatchAndSetMo(self.procEndRe, oldAsm): + newAsm = '' + self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) + elif self.MatchAndSetMo(self.publicRe, oldAsm): + publics = re.findall(self.varAndTypeSubRe, self.mo.group(1)) + publics = tuple(map(lambda p: p.split(':')[0].strip(), publics)) + for i in range(len(publics) - 1): + name = publics[i] + self.EmitNewContent('global ASM_PFX(%s)' % publics[i]) + self.NewGlobal(name) + name = publics[-1] + self.NewGlobal(name) + newAsm = 'global ASM_PFX(%s)' % name + self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) + elif self.MatchAndSetMo(self.defineDataRe, oldAsm): + name = self.mo.group(1) + ty = self.mo.group(2) + value = self.mo.group(3) + if value == '?': + value = 0 + newAsm = '%s: %s %s' % (name, ty, value) + newAsm = self.CommonConversions(newAsm) + self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) + else: + newAsm = self.CommonConversions(oldAsm) + self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) + + def NewGlobal(self, name): + regex = re.compile(r'(?<![_\w\d])(?<!ASM_PFX\()(' + re.escape(name) + + r')(?![_\w\d])') + self.globals.add(regex) + + def ConvertAnonymousLabels(self, oldAsm): + newAsm = oldAsm + anonLabel = self.anonLabel(self.anonLabelCount) + newAsm = newAsm.replace('@b', anonLabel) + newAsm = newAsm.replace('@B', anonLabel) + anonLabel = self.anonLabel(self.anonLabelCount + 1) + newAsm = newAsm.replace('@f', anonLabel) + newAsm = newAsm.replace('@F', anonLabel) + return newAsm + + def anonLabel(self, count): + return '.%d' % count + + def EmitString(self, string): + self.output.write(string.encode('utf-8', 'ignore')) + + def EmitLineWithDiff(self, old, new): + newLine = (self.indent + new).rstrip() + if self.diff: + if old is None: + print('+%s' % newLine) + elif newLine != old: + print('-%s' % old) + print('+%s' % newLine) + else: + print('', newLine) + if newLine != '': + self.newAsmEmptyLineCount = 0 + self.EmitString(newLine + '\r\n') + + def EmitLine(self, string): + self.EmitLineWithDiff(self.originalLine, string) + + def EmitNewContent(self, string): + self.EmitLineWithDiff(None, string) + + def EmitAsmReplaceOp(self, oldAsm, oldOp, newOp, endOfLine): + newAsm = oldAsm.replace(oldOp, newOp, 1) + self.EmitAsmWithComment(oldAsm, newAsm, endOfLine) + + hexNumRe = re.compile(r'0*((?=[\da-f])\d*(?<=\d)[\da-f]*)h', re.IGNORECASE) + + def EmitAsmWithComment(self, oldAsm, newAsm, endOfLine): + for glblRe in self.globals: + newAsm = glblRe.sub(r'ASM_PFX(\1)', newAsm) + + newAsm = self.hexNumRe.sub(r'0x\1', newAsm) + + newLine = newAsm + endOfLine + emitNewLine = ((newLine.strip() != '') or + ((oldAsm + endOfLine).strip() == '')) + if emitNewLine and newLine.strip() == '': + self.newAsmEmptyLineCount += 1 + if self.newAsmEmptyLineCount > 1: + emitNewLine = False + if emitNewLine: + self.EmitLine(newLine.rstrip()) + elif self.diff: + print('-%s' % self.originalLine) + + leaRe = re.compile(r''' + (lea \s+) ([\w@][\w@0-9]*) \s* , \s* (\S (?:.*\S)?) + \s* $ + ''', + re.VERBOSE | re.IGNORECASE + ) + + def ConvertLea(self, oldAsm): + newAsm = oldAsm + if self.MatchAndSetMo(self.leaRe, oldAsm): + lea = self.mo.group(1) + dst = self.mo.group(2) + src = self.mo.group(3) + if src.find('[') < 0: + src = '[' + src + ']' + newAsm = lea + dst + ', ' + src + newAsm = self.CommonConversions(newAsm) + return newAsm + + ptrRe = re.compile(r''' + (?<! \S ) + ([dfq]?word|byte) \s+ (?: ptr ) (\s*) + (?= [[\s] ) + ''', + re.VERBOSE | re.IGNORECASE + ) + + def ConvertPtr(self, oldAsm): + newAsm = oldAsm + while self.SearchAndSetMo(self.ptrRe, newAsm): + ty = self.mo.group(1) + if ty.lower() == 'fword': + ty = '' + else: + ty += self.mo.group(2) + newAsm = newAsm[:self.mo.start(0)] + ty + newAsm[self.mo.end(0):] + return newAsm + + labelByteRe = re.compile(r''' + (?: \s+ label \s+ (?: [dfq]?word | byte ) ) + (?! \S ) + ''', + re.VERBOSE | re.IGNORECASE + ) + + def ConvertLabelByte(self, oldAsm): + newAsm = oldAsm + if self.SearchAndSetMo(self.labelByteRe, newAsm): + newAsm = newAsm[:self.mo.start(0)] + ':' + newAsm[self.mo.end(0):] + return newAsm + + unaryBitwiseOpRe = re.compile(r''' + ( NOT ) + (?= \s+ \S ) + ''', + re.VERBOSE | re.IGNORECASE + ) + binaryBitwiseOpRe = re.compile(r''' + ( \S \s+ ) + ( AND | OR | SHL | SHR ) + (?= \s+ \S ) + ''', + re.VERBOSE | re.IGNORECASE + ) + bitwiseOpReplacements = { + 'not': '~', + 'and': '&', + 'shl': '<<', + 'shr': '>>', + 'or': '|', + } + + def ConvertBitwiseOp(self, oldAsm): + newAsm = oldAsm + while self.SearchAndSetMo(self.binaryBitwiseOpRe, newAsm): + prefix = self.mo.group(1) + op = self.bitwiseOpReplacements[self.mo.group(2).lower()] + newAsm = newAsm[:self.mo.start(0)] + prefix + op + \ + newAsm[self.mo.end(0):] + while self.SearchAndSetMo(self.unaryBitwiseOpRe, newAsm): + op = self.bitwiseOpReplacements[self.mo.group(1).lower()] + newAsm = newAsm[:self.mo.start(0)] + op + newAsm[self.mo.end(0):] + return newAsm + + sectionRe = re.compile(r''' + \. ( code | + data + ) + (?: \s+ .* )? + \s* $ + ''', + re.VERBOSE | re.IGNORECASE + ) + + segmentRe = re.compile(r''' + ( code | + data ) + (?: \s+ SEGMENT ) + (?: \s+ .* )? + \s* $ + ''', + re.VERBOSE | re.IGNORECASE + ) + + def ConvertSection(self, oldAsm): + newAsm = oldAsm + if self.MatchAndSetMo(self.sectionRe, newAsm) or \ + self.MatchAndSetMo(self.segmentRe, newAsm): + name = self.mo.group(1).lower() + if name == 'code': + if self.x64: + self.EmitLine('DEFAULT REL') + name = 'text' + newAsm = 'SECTION .' + name + return newAsm + + fwordRe = re.compile(r''' + (?<! \S ) + fword + (?! \S ) + ''', + re.VERBOSE | re.IGNORECASE + ) + + def FwordUnsupportedCheck(self, oldAsm): + newAsm = oldAsm + if self.SearchAndSetMo(self.fwordRe, newAsm): + newAsm = self.Unsupported(newAsm, 'fword used') + return newAsm + + __common_conversion_routines__ = ( + ConvertAnonymousLabels, + ConvertPtr, + FwordUnsupportedCheck, + ConvertBitwiseOp, + ConvertLabelByte, + ConvertSection, + ) + + def CommonConversions(self, oldAsm): + newAsm = oldAsm + for conv in self.__common_conversion_routines__: + newAsm = conv(self, newAsm) + return newAsm + + def Unsupported(self, asm, message=None): + if not self.force: + raise UnsupportedConversion + + self.unsupportedSyntaxSeen = True + newAsm = '%error conversion unsupported' + if message: + newAsm += '; ' + message + newAsm += ': ' + asm + return newAsm + + +class ConvertInfFile(CommonUtils): + + def __init__(self, inf, clone): + CommonUtils.__init__(self, clone) + self.inf = inf + self.ScanInfAsmFiles() + if self.infmode: + self.ConvertInfAsmFiles() + + infSrcRe = re.compile(r''' + \s* + ( [\w@][\w@0-9/]* \.(asm|s) ) + \s* (?: \| [^#]* )? + \s* (?: \# .* )? + $ + ''', + re.VERBOSE | re.IGNORECASE + ) + + def GetInfAsmFileMapping(self): + srcToDst = {'order': []} + for line in self.lines: + line = line.rstrip() + if self.MatchAndSetMo(self.infSrcRe, line): + src = self.mo.group(1) + srcExt = self.mo.group(2) + dst = os.path.splitext(src)[0] + '.nasm' + fullDst = os.path.join(self.dir, dst) + if src not in srcToDst and not os.path.exists(fullDst): + srcToDst[src] = dst + srcToDst['order'].append(src) + return srcToDst + + def ScanInfAsmFiles(self): + src = self.inf + assert os.path.isfile(src) + f = io.open(src, 'rt') + self.lines = f.readlines() + f.close() + + path = os.path.realpath(self.inf) + (self.dir, inf) = os.path.split(path) + parent = os.path.normpath(self.dir) + (lastpath, self.moduleName) = os.path.split(parent) + self.packageName = None + while True: + lastpath = os.path.normpath(lastpath) + (parent, basename) = os.path.split(lastpath) + if parent == lastpath: + break + if basename.endswith('Pkg'): + self.packageName = basename + break + lastpath = parent + + self.srcToDst = self.GetInfAsmFileMapping() + + self.dstToSrc = {'order': []} + for src in self.srcToDst['order']: + srcExt = os.path.splitext(src)[1] + dst = self.srcToDst[src] + if dst not in self.dstToSrc: + self.dstToSrc[dst] = [src] + self.dstToSrc['order'].append(dst) + else: + self.dstToSrc[dst].append(src) + + def __len__(self): + return len(self.dstToSrc['order']) + + def __iter__(self): + return iter(self.dstToSrc['order']) + + def ConvertInfAsmFiles(self): + notConverted = [] + unsupportedArchCount = 0 + for dst in self: + didSomething = False + try: + self.UpdateInfAsmFile(dst) + didSomething = True + except UnsupportedConversion: + if not self.args.quiet: + print('MASM=>NASM conversion unsupported for', dst) + notConverted.append(dst) + except NoSourceFile: + if not self.args.quiet: + print('Source file missing for', reldst) + notConverted.append(dst) + except UnsupportedArch: + unsupportedArchCount += 1 + else: + if didSomething: + self.ConversionFinished(dst) + if len(notConverted) > 0 and not self.args.quiet: + for dst in notConverted: + reldst = self.RootRelative(dst) + print('Unabled to convert', reldst) + if unsupportedArchCount > 0 and not self.args.quiet: + print('Skipped', unsupportedArchCount, 'files based on architecture') + + def UpdateInfAsmFile(self, dst, IgnoreMissingAsm=False): + infPath = os.path.split(os.path.realpath(self.inf))[0] + asmSrc = os.path.splitext(dst)[0] + '.asm' + fullSrc = os.path.join(infPath, asmSrc) + fullDst = os.path.join(infPath, dst) + srcParentDir = os.path.basename(os.path.split(fullSrc)[0]) + if srcParentDir.lower() in UnsupportedArch.unsupported: + raise UnsupportedArch + elif not os.path.exists(fullSrc): + if not IgnoreMissingAsm: + raise NoSourceFile + else: # not os.path.exists(fullDst): + conv = ConvertAsmFile(fullSrc, fullDst, self) + self.unsupportedSyntaxSeen = conv.unsupportedSyntaxSeen + + fileChanged = False + recentSources = list() + i = 0 + while i < len(self.lines): + line = self.lines[i].rstrip() + updatedLine = line + lineChanged = False + preserveOldSource = False + for src in self.dstToSrc[dst]: + assert self.srcToDst[src] == dst + updatedLine = self.ReplacePreserveSpacing( + updatedLine, src, dst) + lineChanged = updatedLine != line + if lineChanged: + preserveOldSource = self.ShouldKeepFile(src) + break + + if lineChanged: + if preserveOldSource: + if updatedLine.strip() not in recentSources: + self.lines.insert(i, updatedLine + '\n') + recentSources.append(updatedLine.strip()) + i += 1 + if self.diff: + print('+%s' % updatedLine) + if self.diff: + print('', line) + else: + if self.diff: + print('-%s' % line) + if updatedLine.strip() in recentSources: + self.lines[i] = None + else: + self.lines[i] = updatedLine + '\n' + recentSources.append(updatedLine.strip()) + if self.diff: + print('+%s' % updatedLine) + else: + if len(recentSources) > 0: + recentSources = list() + if self.diff: + print('', line) + + fileChanged |= lineChanged + i += 1 + + if fileChanged: + self.lines = list(filter(lambda l: l is not None, self.lines)) + + for src in self.dstToSrc[dst]: + if not src.endswith('.asm'): + fullSrc = os.path.join(infPath, src) + if os.path.exists(fullSrc): + self.RemoveFile(fullSrc) + + if fileChanged: + f = io.open(self.inf, 'w', newline='\r\n') + f.writelines(self.lines) + f.close() + self.FileUpdated(self.inf) + + def ConversionFinished(self, dst): + asmSrc = os.path.splitext(dst)[0] + '.asm' + self.FileConversionFinished( + self.packageName, self.moduleName, asmSrc, dst) + + +class ConvertInfFiles(CommonUtils): + + def __init__(self, infs, clone): + CommonUtils.__init__(self, clone) + infs = map(lambda i: ConvertInfFile(i, self), infs) + infs = filter(lambda i: len(i) > 0, infs) + dstToInfs = {'order': []} + for inf in infs: + for dst in inf: + fulldst = os.path.realpath(os.path.join(inf.dir, dst)) + pair = (inf, dst) + if fulldst in dstToInfs: + dstToInfs[fulldst].append(pair) + else: + dstToInfs['order'].append(fulldst) + dstToInfs[fulldst] = [pair] + + notConverted = [] + unsupportedArchCount = 0 + for dst in dstToInfs['order']: + didSomething = False + try: + for inf, reldst in dstToInfs[dst]: + inf.UpdateInfAsmFile(reldst, IgnoreMissingAsm=didSomething) + didSomething = True + except UnsupportedConversion: + if not self.args.quiet: + print('MASM=>NASM conversion unsupported for', reldst) + notConverted.append(dst) + except NoSourceFile: + if not self.args.quiet: + print('Source file missing for', reldst) + notConverted.append(dst) + except UnsupportedArch: + unsupportedArchCount += 1 + else: + if didSomething: + inf.ConversionFinished(reldst) + if len(notConverted) > 0 and not self.args.quiet: + for dst in notConverted: + reldst = self.RootRelative(dst) + print('Unabled to convert', reldst) + if unsupportedArchCount > 0 and not self.args.quiet: + print('Skipped', unsupportedArchCount, 'files based on architecture') + + +class ConvertDirectories(CommonUtils): + + def __init__(self, paths, clone): + CommonUtils.__init__(self, clone) + self.paths = paths + self.ConvertInfAndAsmFiles() + + def ConvertInfAndAsmFiles(self): + infs = list() + for path in self.paths: + assert(os.path.exists(path)) + for path in self.paths: + for root, dirs, files in os.walk(path): + for d in ('.svn', '.git'): + if d in dirs: + dirs.remove(d) + for f in files: + if f.lower().endswith('.inf'): + inf = os.path.realpath(os.path.join(root, f)) + infs.append(inf) + + ConvertInfFiles(infs, self) + + +class ConvertAsmApp(CommonUtils): + + def __init__(self): + CommonUtils.__init__(self) + + src = self.args.source + dst = self.args.dest + if self.infmode: + ConvertInfFiles((src,), self) + elif self.dirmode: + ConvertDirectories((src,), self) + elif not self.dirmode: + ConvertAsmFile(src, dst, self) + +ConvertAsmApp() |
