summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/EFI/Firmware/BaseTools/Scripts/ConvertMasmToNasm.py
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Devices/EFI/Firmware/BaseTools/Scripts/ConvertMasmToNasm.py
parentInitial commit. (diff)
downloadvirtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz
virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/BaseTools/Scripts/ConvertMasmToNasm.py')
-rwxr-xr-xsrc/VBox/Devices/EFI/Firmware/BaseTools/Scripts/ConvertMasmToNasm.py1005
1 files changed, 1005 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/BaseTools/Scripts/ConvertMasmToNasm.py b/src/VBox/Devices/EFI/Firmware/BaseTools/Scripts/ConvertMasmToNasm.py
new file mode 100755
index 00000000..7c286a06
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/BaseTools/Scripts/ConvertMasmToNasm.py
@@ -0,0 +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()