Deleted Added
sdiff udiff text old ( 4483:1e62824dcc3d ) new ( 4502:766acd3fa962 )
full compact
1# Copyright (c) 2003-2005 The Regents of The University of Michigan
2# All rights reserved.
3#
4# Redistribution and use in source and binary forms, with or without
5# modification, are permitted provided that the following conditions are
6# met: redistributions of source code must retain the above copyright
7# notice, this list of conditions and the following disclaimer;
8# redistributions in binary form must reproduce the above copyright
9# notice, this list of conditions and the following disclaimer in the
10# documentation and/or other materials provided with the distribution;
11# neither the name of the copyright holders nor the names of its
12# contributors may be used to endorse or promote products derived from
13# this software without specific prior written permission.
14#
15# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26#
27# Authors: Gabe Black
28
29import os
30import sys
31import re
32import string
33import traceback
34# get type names
35from types import *
36
37# Prepend the directory where the PLY lex & yacc modules are found
38# to the search path.
39sys.path[0:0] = [os.environ['M5_PLY']]
40
41from ply import lex
42from ply import yacc
43
44##########################################################################
45#
46# Base classes for use outside of the assembler
47#
48##########################################################################
49
50class Micro_Container(object):
51 def __init__(self, name):
52 self.microops = []
53 self.name = name
54 self.directives = {}
55 self.micro_classes = {}
56 self.labels = {}
57
58 def add_microop(self, microop):
59 self.microops.append(microop)
60
61 def __str__(self):
62 string = "%s:\n" % self.name
63 for microop in self.microops:
64 string += " %s\n" % microop
65 return string
66
67class Macroop(Micro_Container):
68 pass
69
70class Rom(Micro_Container):
71 def __init__(self, name):
72 super(Rom, self).__init__(name)
73 self.externs = {}
74
75##########################################################################
76#
77# Support classes
78#
79##########################################################################
80
81class Label(object):
82 def __init__(self):
83 self.extern = False
84 self.name = ""
85
86class Block(object):
87 def __init__(self):
88 self.statements = []
89
90class Statement(object):
91 def __init__(self):
92 self.is_microop = False
93 self.is_directive = False
94
95class Microop(Statement):
96 def __init__(self):
97 super(Microop, self).__init__()
98 self.mnemonic = ""
99 self.labels = []
100 self.is_microop = True
101 self.params = ""
102
103class Directive(Statement):
104 def __init__(self):
105 super(Directive, self).__init__()
106 self.name = ""
107 self.is_directive = True
108
109##########################################################################
110#
111# Functions that handle common tasks
112#
113##########################################################################
114
115def print_error(message):
116 print
117 print "*** %s" % message
118 print
119
120def handle_statement(parser, container, statement):
121 if statement.is_microop:
122 try:
123 microop = eval('parser.microops[statement.mnemonic](%s)' %
124 statement.params)
125 except:
126 print_error("Error creating microop object.")
127 raise
128 try:
129 for label in statement.labels:
130 container.labels[label.name] = microop
131 if label.extern:
132 container.externs[label.name] = microop
133 container.add_microop(microop)
134 except:
135 print_error("Error adding microop.")
136 raise
137 elif statement.is_directive:
138 try:
139 eval('container.%s()' % statement.name)
140 except:
141 print_error("Error executing directive.")
142 print container.directives
143 raise
144 else:
145 raise Exception, "Didn't recognize the type of statement", statement
146
147##########################################################################
148#
149# Lexer specification
150#
151##########################################################################
152
153# Error handler. Just call exit. Output formatted to work under
154# Emacs compile-mode. Optional 'print_traceback' arg, if set to True,
155# prints a Python stack backtrace too (can be handy when trying to
156# debug the parser itself).
157def error(lineno, string, print_traceback = False):
158 # Print a Python stack backtrace if requested.
159 if (print_traceback):
160 traceback.print_exc()
161 if lineno != 0:
162 line_str = "%d:" % lineno
163 else:
164 line_str = ""
165 sys.exit("%s %s" % (line_str, string))
166
167reserved = ('DEF', 'MACROOP', 'ROM', 'EXTERN')
168
169tokens = reserved + (
170 # identifier
171 'ID',
172 # arguments for microops and directives
173 'PARAMS',
174
175 'LPAREN', 'RPAREN',
176 'LBRACE', 'RBRACE',
177 #'COMMA',
178 'COLON', 'SEMI', 'DOT',
179 'NEWLINE'
180 )
181
182# New lines are ignored at the top level, but they end statements in the
183# assembler
184states = (
185 ('asm', 'exclusive'),
186 ('params', 'exclusive'),
187)
188
189reserved_map = { }
190for r in reserved:
191 reserved_map[r.lower()] = r
192
193def t_params_COLON(t):
194 r':'
195 t.lexer.begin('asm')
196 return t
197
198def t_asm_ID(t):
199 r'[A-Za-z_]\w*'
200 t.type = reserved_map.get(t.value, 'ID')
201 t.lexer.begin('params')
202 return t
203
204def t_ANY_ID(t):
205 r'[A-Za-z_]\w*'
206 t.type = reserved_map.get(t.value, 'ID')
207 return t
208
209def t_params_PARAMS(t):
210 r'([^\n;]|((?<=\\)[\n;]))+'
211 t.lineno += t.value.count('\n')
212 t.lexer.begin('asm')
213 return t
214
215def t_INITIAL_LBRACE(t):
216 r'\{'
217 t.lexer.begin('asm')
218 return t
219
220def t_asm_RBRACE(t):
221 r'\}'
222 t.lexer.begin('INITIAL')
223 return t
224
225def t_INITIAL_NEWLINE(t):
226 r'\n+'
227 t.lineno += t.value.count('\n')
228
229def t_asm_NEWLINE(t):
230 r'\n+'
231 t.lineno += t.value.count('\n')
232 return t
233
234def t_params_NEWLINE(t):
235 r'\n+'
236 t.lineno += t.value.count('\n')
237 t.lexer.begin('asm')
238 return t
239
240def t_params_SEMI(t):
241 r';'
242 t.lexer.begin('asm')
243 return t
244
245# Basic regular expressions to pick out simple tokens
246t_ANY_LPAREN = r'\('
247t_ANY_RPAREN = r'\)'
248#t_COMMA = r','
249t_ANY_SEMI = r';'
250t_ANY_DOT = r'\.'
251
252t_ANY_ignore = ' \t\x0c'
253
254def t_ANY_error(t):
255 error(t.lineno, "illegal character '%s'" % t.value[0])
256 t.skip(1)
257
258##########################################################################
259#
260# Parser specification
261#
262##########################################################################
263
264# Start symbol for a file which may have more than one macroop or rom
265# specification.
266def p_file(t):
267 'file : opt_rom_or_macros'
268
269def p_opt_rom_or_macros_0(t):
270 'opt_rom_or_macros : '
271
272def p_opt_rom_or_macros_1(t):
273 'opt_rom_or_macros : rom_or_macros'
274
275def p_rom_or_macros_0(t):
276 'rom_or_macros : rom_or_macro'
277
278def p_rom_or_macros_1(t):
279 'rom_or_macros : rom_or_macros rom_or_macro'
280
281def p_rom_or_macro_0(t):
282 '''rom_or_macro : rom_block'''
283
284def p_rom_or_macro_1(t):
285 '''rom_or_macro : macroop_def'''
286
287# A block of statements
288def p_block(t):
289 'block : LBRACE statements RBRACE'
290 block = Block()
291 block.statements = t[2]
292 t[0] = block
293
294# Defines a section of microcode that should go in the current ROM
295def p_rom_block(t):
296 'rom_block : DEF ROM block SEMI'
297 for statement in t[3].statements:
298 handle_statement(t.parser, t.parser.rom, statement)
299 t[0] = t.parser.rom
300
301# Defines a macroop that jumps to an external label in the ROM
302def p_macroop_def_0(t):
303 'macroop_def : DEF MACROOP LPAREN ID RPAREN SEMI'
304 t[0] = t[4]
305
306# Defines a macroop that is combinationally generated
307def p_macroop_def_1(t):
308 'macroop_def : DEF MACROOP ID block SEMI'
309 try:
310 curop = t.parser.macro_type(t[3])
311 except TypeError:
312 print_error("Error creating macroop object.")
313 raise
314 for statement in t[4].statements:
315 handle_statement(t.parser, curop, statement)
316 t.parser.macroops.append(curop)
317
318def p_statements_0(t):
319 'statements : statement'
320 if t[1]:
321 t[0] = [t[1]]
322 else:
323 t[0] = []
324
325def p_statements_1(t):
326 'statements : statements statement'
327 if t[2]:
328 t[1].append(t[2])
329 t[0] = t[1]
330
331def p_statement(t):
332 'statement : content_of_statement end_of_statement'
333 t[0] = t[1]
334
335# A statement can be a microop or an assembler directive
336def p_content_of_statement_0(t):
337 '''content_of_statement : microop
338 | directive'''
339 t[0] = t[1]
340
341def p_content_of_statement_1(t):
342 'content_of_statement : '
343 pass
344
345# Statements are ended by newlines or a semi colon
346def p_end_of_statement(t):
347 '''end_of_statement : NEWLINE
348 | SEMI'''
349 pass
350
351def p_microop_0(t):
352 'microop : labels ID'
353 microop = Microop()
354 microop.labels = t[1]
355 microop.mnemonic = t[2]
356 t[0] = microop
357
358def p_microop_1(t):
359 'microop : ID'
360 microop = Microop()
361 microop.mnemonic = t[1]
362 t[0] = microop
363
364def p_microop_2(t):
365 'microop : labels ID PARAMS'
366 microop = Microop()
367 microop.labels = t[1]
368 microop.mnemonic = t[2]
369 microop.params = t[3]
370 t[0] = microop
371
372def p_microop_3(t):
373 'microop : ID PARAMS'
374 microop = Microop()
375 microop.mnemonic = t[1]
376 microop.params = t[2]
377 t[0] = microop
378
379def p_labels_0(t):
380 'labels : label'
381 t[0] = [t[1]]
382
383def p_labels_1(t):
384 'labels : labels label'
385 t[1].append(t[2])
386 t[0] = t[1]
387
388def p_label_0(t):
389 'label : ID COLON'
390 label = Label()
391 label.is_extern = False
392 label.text = t[1]
393 t[0] = label
394
395def p_label_1(t):
396 'label : EXTERN ID COLON'
397 label = Label()
398 label.is_extern = True
399 label.text = t[2]
400 t[0] = label
401
402def p_directive(t):
403 'directive : DOT ID'
404 directive = Directive()
405 directive.name = t[2]
406 t[0] = directive
407
408# Parse error handler. Note that the argument here is the offending
409# *token*, not a grammar symbol (hence the need to use t.value)
410def p_error(t):
411 if t:
412 error(t.lineno, "syntax error at '%s'" % t.value)
413 else:
414 error(0, "unknown syntax error", True)
415
416class MicroAssembler(object):
417
418 def __init__(self, macro_type, microops, rom):
419 self.lexer = lex.lex()
420 self.parser = yacc.yacc()
421 self.parser.macro_type = macro_type
422 self.parser.macroops = []
423 self.parser.microops = microops
424 self.parser.rom = rom
425
426 def assemble(self, asm):
427 self.parser.parse(asm, lexer=self.lexer)
428 for macroop in self.parser.macroops:
429 print macroop
430 print self.parser.rom
431 macroops = self.parser.macroops
432 self.parser.macroops = []
433 return macroops