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