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