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