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