diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
commit | f215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch) | |
tree | 6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Devices/EFI/Firmware/BaseTools/Scripts/ConvertMasmToNasm.py | |
parent | Initial commit. (diff) | |
download | virtualbox-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-x | src/VBox/Devices/EFI/Firmware/BaseTools/Scripts/ConvertMasmToNasm.py | 1005 |
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() |