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