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