micro_asm.py (4512:cfa340f9d12a) micro_asm.py (4566:a0ec2dee1a1b)
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 Combinational_Macroop(Micro_Container):
68 pass
69
70class Rom_Macroop(object):
71 def __init__(self, name, target):
72 self.name = name
73 self.target = target
74
75 def __str__(self):
76 return "%s: %s\n" % (self.name, self.target)
77
78class Rom(Micro_Container):
79 def __init__(self, name):
80 super(Rom, self).__init__(name)
81 self.externs = {}
82
83##########################################################################
84#
85# Support classes
86#
87##########################################################################
88
89class Label(object):
90 def __init__(self):
91 self.extern = False
92 self.name = ""
93
94class Block(object):
95 def __init__(self):
96 self.statements = []
97
98class Statement(object):
99 def __init__(self):
100 self.is_microop = False
101 self.is_directive = False
102 self.params = ""
103
104class Microop(Statement):
105 def __init__(self):
106 super(Microop, self).__init__()
107 self.mnemonic = ""
108 self.labels = []
109 self.is_microop = True
110
111class Directive(Statement):
112 def __init__(self):
113 super(Directive, self).__init__()
114 self.name = ""
115 self.is_directive = True
116
117##########################################################################
118#
119# Functions that handle common tasks
120#
121##########################################################################
122
123def print_error(message):
124 print
125 print "*** %s" % message
126 print
127
128def handle_statement(parser, container, statement):
129 if statement.is_microop:
130 try:
131 microop = eval('parser.microops[statement.mnemonic](%s)' %
132 statement.params)
133 except:
134 print_error("Error creating microop object.")
135 raise
136 try:
137 for label in statement.labels:
138 container.labels[label.name] = microop
139 if label.extern:
140 container.externs[label.name] = microop
141 container.add_microop(microop)
142 except:
143 print_error("Error adding microop.")
144 raise
145 elif statement.is_directive:
146 try:
147 eval('container.directives[statement.name](%s)' % statement.params)
148 except:
149 print_error("Error executing directive.")
150 print container.directives
151 raise
152 else:
153 raise Exception, "Didn't recognize the type of statement", statement
154
155##########################################################################
156#
157# Lexer specification
158#
159##########################################################################
160
161# Error handler. Just call exit. Output formatted to work under
162# Emacs compile-mode. Optional 'print_traceback' arg, if set to True,
163# prints a Python stack backtrace too (can be handy when trying to
164# debug the parser itself).
165def error(lineno, string, print_traceback = False):
166 # Print a Python stack backtrace if requested.
167 if (print_traceback):
168 traceback.print_exc()
169 if lineno != 0:
170 line_str = "%d:" % lineno
171 else:
172 line_str = ""
173 sys.exit("%s %s" % (line_str, string))
174
175reserved = ('DEF', 'MACROOP', 'ROM', 'EXTERN')
176
177tokens = reserved + (
178 # identifier
179 'ID',
180 # arguments for microops and directives
181 'PARAMS',
182
183 'LPAREN', 'RPAREN',
184 'LBRACE', 'RBRACE',
185 'COLON', 'SEMI', 'DOT',
186 'NEWLINE'
187 )
188
189# New lines are ignored at the top level, but they end statements in the
190# assembler
191states = (
192 ('asm', 'exclusive'),
193 ('params', 'exclusive'),
194)
195
196reserved_map = { }
197for r in reserved:
198 reserved_map[r.lower()] = r
199
200# Ignore comments
201def t_ANY_COMMENT(t):
202 r'\#[^\n]*(?=\n)'
203
204def t_ANY_MULTILINECOMMENT(t):
205 r'/\*([^/]|((?<!\*)/))*\*/'
206
207# A colon marks the end of a label. It should follow an ID which will
208# put the lexer in the "params" state. Seeing the colon will put it back
209# in the "asm" state since it knows it saw a label and not a mnemonic.
210def t_params_COLON(t):
211 r':'
212 t.lexer.begin('asm')
213 return t
214
215# An "ID" in the micro assembler is either a label, directive, or mnemonic
216# If it's either a directive or a mnemonic, it will be optionally followed by
217# parameters. If it's a label, the following colon will make the lexer stop
218# looking for parameters.
219def t_asm_ID(t):
220 r'[A-Za-z_]\w*'
221 t.type = reserved_map.get(t.value, 'ID')
222 t.lexer.begin('params')
223 return t
224
225# If there is a label and you're -not- in the assember (which would be caught
226# above), don't start looking for parameters.
227def t_ANY_ID(t):
228 r'[A-Za-z_]\w*'
229 t.type = reserved_map.get(t.value, 'ID')
230 return t
231
232# Parameters are a string of text which don't contain an unescaped statement
233# statement terminator, ie a newline or semi colon.
234def t_params_PARAMS(t):
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 Combinational_Macroop(Micro_Container):
68 pass
69
70class Rom_Macroop(object):
71 def __init__(self, name, target):
72 self.name = name
73 self.target = target
74
75 def __str__(self):
76 return "%s: %s\n" % (self.name, self.target)
77
78class Rom(Micro_Container):
79 def __init__(self, name):
80 super(Rom, self).__init__(name)
81 self.externs = {}
82
83##########################################################################
84#
85# Support classes
86#
87##########################################################################
88
89class Label(object):
90 def __init__(self):
91 self.extern = False
92 self.name = ""
93
94class Block(object):
95 def __init__(self):
96 self.statements = []
97
98class Statement(object):
99 def __init__(self):
100 self.is_microop = False
101 self.is_directive = False
102 self.params = ""
103
104class Microop(Statement):
105 def __init__(self):
106 super(Microop, self).__init__()
107 self.mnemonic = ""
108 self.labels = []
109 self.is_microop = True
110
111class Directive(Statement):
112 def __init__(self):
113 super(Directive, self).__init__()
114 self.name = ""
115 self.is_directive = True
116
117##########################################################################
118#
119# Functions that handle common tasks
120#
121##########################################################################
122
123def print_error(message):
124 print
125 print "*** %s" % message
126 print
127
128def handle_statement(parser, container, statement):
129 if statement.is_microop:
130 try:
131 microop = eval('parser.microops[statement.mnemonic](%s)' %
132 statement.params)
133 except:
134 print_error("Error creating microop object.")
135 raise
136 try:
137 for label in statement.labels:
138 container.labels[label.name] = microop
139 if label.extern:
140 container.externs[label.name] = microop
141 container.add_microop(microop)
142 except:
143 print_error("Error adding microop.")
144 raise
145 elif statement.is_directive:
146 try:
147 eval('container.directives[statement.name](%s)' % statement.params)
148 except:
149 print_error("Error executing directive.")
150 print container.directives
151 raise
152 else:
153 raise Exception, "Didn't recognize the type of statement", statement
154
155##########################################################################
156#
157# Lexer specification
158#
159##########################################################################
160
161# Error handler. Just call exit. Output formatted to work under
162# Emacs compile-mode. Optional 'print_traceback' arg, if set to True,
163# prints a Python stack backtrace too (can be handy when trying to
164# debug the parser itself).
165def error(lineno, string, print_traceback = False):
166 # Print a Python stack backtrace if requested.
167 if (print_traceback):
168 traceback.print_exc()
169 if lineno != 0:
170 line_str = "%d:" % lineno
171 else:
172 line_str = ""
173 sys.exit("%s %s" % (line_str, string))
174
175reserved = ('DEF', 'MACROOP', 'ROM', 'EXTERN')
176
177tokens = reserved + (
178 # identifier
179 'ID',
180 # arguments for microops and directives
181 'PARAMS',
182
183 'LPAREN', 'RPAREN',
184 'LBRACE', 'RBRACE',
185 'COLON', 'SEMI', 'DOT',
186 'NEWLINE'
187 )
188
189# New lines are ignored at the top level, but they end statements in the
190# assembler
191states = (
192 ('asm', 'exclusive'),
193 ('params', 'exclusive'),
194)
195
196reserved_map = { }
197for r in reserved:
198 reserved_map[r.lower()] = r
199
200# Ignore comments
201def t_ANY_COMMENT(t):
202 r'\#[^\n]*(?=\n)'
203
204def t_ANY_MULTILINECOMMENT(t):
205 r'/\*([^/]|((?<!\*)/))*\*/'
206
207# A colon marks the end of a label. It should follow an ID which will
208# put the lexer in the "params" state. Seeing the colon will put it back
209# in the "asm" state since it knows it saw a label and not a mnemonic.
210def t_params_COLON(t):
211 r':'
212 t.lexer.begin('asm')
213 return t
214
215# An "ID" in the micro assembler is either a label, directive, or mnemonic
216# If it's either a directive or a mnemonic, it will be optionally followed by
217# parameters. If it's a label, the following colon will make the lexer stop
218# looking for parameters.
219def t_asm_ID(t):
220 r'[A-Za-z_]\w*'
221 t.type = reserved_map.get(t.value, 'ID')
222 t.lexer.begin('params')
223 return t
224
225# If there is a label and you're -not- in the assember (which would be caught
226# above), don't start looking for parameters.
227def t_ANY_ID(t):
228 r'[A-Za-z_]\w*'
229 t.type = reserved_map.get(t.value, 'ID')
230 return t
231
232# Parameters are a string of text which don't contain an unescaped statement
233# statement terminator, ie a newline or semi colon.
234def t_params_PARAMS(t):
235 r'([^\n;]|((?<=\\)[\n;]))+'
235 r'([^\n;\\]|(\\[\n;\\]))+'
236 t.lineno += t.value.count('\n')
236 t.lineno += t.value.count('\n')
237 unescapeParamsRE = re.compile(r'(\\[\n;\\])')
238 def unescapeParams(mo):
239 val = mo.group(0)
240 print "About to sub %s for %s" % (val[1], val)
241 return val[1]
242 print "Looking for matches in %s" % t.value
243 t.value = unescapeParamsRE.sub(unescapeParams, t.value)
237 t.lexer.begin('asm')
238 return t
239
240# Braces enter and exit micro assembly
241def t_INITIAL_LBRACE(t):
242 r'\{'
243 t.lexer.begin('asm')
244 return t
245
246def t_asm_RBRACE(t):
247 r'\}'
248 t.lexer.begin('INITIAL')
249 return t
250
251# At the top level, keep track of newlines only for line counting.
252def t_INITIAL_NEWLINE(t):
253 r'\n+'
254 t.lineno += t.value.count('\n')
255
256# In the micro assembler, do line counting but also return a token. The
257# token is needed by the parser to detect the end of a statement.
258def t_asm_NEWLINE(t):
259 r'\n+'
260 t.lineno += t.value.count('\n')
261 return t
262
263# A newline or semi colon when looking for params signals that the statement
264# is over and the lexer should go back to looking for regular assembly.
265def t_params_NEWLINE(t):
266 r'\n+'
267 t.lineno += t.value.count('\n')
268 t.lexer.begin('asm')
269 return t
270
271def t_params_SEMI(t):
272 r';'
273 t.lexer.begin('asm')
274 return t
275
276# Basic regular expressions to pick out simple tokens
277t_ANY_LPAREN = r'\('
278t_ANY_RPAREN = r'\)'
279t_ANY_SEMI = r';'
280t_ANY_DOT = r'\.'
281
282t_ANY_ignore = ' \t\x0c'
283
284def t_ANY_error(t):
285 error(t.lineno, "illegal character '%s'" % t.value[0])
286 t.skip(1)
287
288##########################################################################
289#
290# Parser specification
291#
292##########################################################################
293
294# Start symbol for a file which may have more than one macroop or rom
295# specification.
296def p_file(t):
297 'file : opt_rom_or_macros'
298
299def p_opt_rom_or_macros_0(t):
300 'opt_rom_or_macros : '
301
302def p_opt_rom_or_macros_1(t):
303 'opt_rom_or_macros : rom_or_macros'
304
305def p_rom_or_macros_0(t):
306 'rom_or_macros : rom_or_macro'
307
308def p_rom_or_macros_1(t):
309 'rom_or_macros : rom_or_macros rom_or_macro'
310
311def p_rom_or_macro_0(t):
312 '''rom_or_macro : rom_block
313 | macroop_def'''
314
315# Defines a section of microcode that should go in the current ROM
316def p_rom_block(t):
317 'rom_block : DEF ROM block SEMI'
318 if not t.parser.rom:
319 print_error("Rom block found, but no Rom object specified.")
320 raise TypeError, "Rom block found, but no Rom object was specified."
321 for statement in t[3].statements:
322 handle_statement(t.parser, t.parser.rom, statement)
323 t[0] = t.parser.rom
324
325# Defines a macroop that jumps to an external label in the ROM
326def p_macroop_def_0(t):
327 'macroop_def : DEF MACROOP ID LPAREN ID RPAREN SEMI'
328 if not t.parser.rom_macroop_type:
329 print_error("ROM based macroop found, but no ROM macroop class was specified.")
330 raise TypeError, "ROM based macroop found, but no ROM macroop class was specified."
331 macroop = t.parser.rom_macroop_type(t[3], t[5])
332 t.parser.macroops[t[3]] = macroop
333
334
335# Defines a macroop that is combinationally generated
336def p_macroop_def_1(t):
337 'macroop_def : DEF MACROOP ID block SEMI'
338 try:
339 curop = t.parser.macro_type(t[3])
340 except TypeError:
341 print_error("Error creating macroop object.")
342 raise
343 for statement in t[4].statements:
344 handle_statement(t.parser, curop, statement)
345 t.parser.macroops[t[3]] = curop
346
347# A block of statements
348def p_block(t):
349 'block : LBRACE statements RBRACE'
350 block = Block()
351 block.statements = t[2]
352 t[0] = block
353
354def p_statements_0(t):
355 'statements : statement'
356 if t[1]:
357 t[0] = [t[1]]
358 else:
359 t[0] = []
360
361def p_statements_1(t):
362 'statements : statements statement'
363 if t[2]:
364 t[1].append(t[2])
365 t[0] = t[1]
366
367def p_statement(t):
368 'statement : content_of_statement end_of_statement'
369 t[0] = t[1]
370
371# A statement can be a microop or an assembler directive
372def p_content_of_statement_0(t):
373 '''content_of_statement : microop
374 | directive'''
375 t[0] = t[1]
376
377# Ignore empty statements
378def p_content_of_statement_1(t):
379 'content_of_statement : '
380 pass
381
382# Statements are ended by newlines or a semi colon
383def p_end_of_statement(t):
384 '''end_of_statement : NEWLINE
385 | SEMI'''
386 pass
387
388# Different flavors of microop to avoid shift/reduce errors
389def p_microop_0(t):
390 'microop : labels ID'
391 microop = Microop()
392 microop.labels = t[1]
393 microop.mnemonic = t[2]
394 t[0] = microop
395
396def p_microop_1(t):
397 'microop : ID'
398 microop = Microop()
399 microop.mnemonic = t[1]
400 t[0] = microop
401
402def p_microop_2(t):
403 'microop : labels ID PARAMS'
404 microop = Microop()
405 microop.labels = t[1]
406 microop.mnemonic = t[2]
407 microop.params = t[3]
408 t[0] = microop
409
410def p_microop_3(t):
411 'microop : ID PARAMS'
412 microop = Microop()
413 microop.mnemonic = t[1]
414 microop.params = t[2]
415 t[0] = microop
416
417# Labels in the microcode
418def p_labels_0(t):
419 'labels : label'
420 t[0] = [t[1]]
421
422def p_labels_1(t):
423 'labels : labels label'
424 t[1].append(t[2])
425 t[0] = t[1]
426
427def p_label_0(t):
428 'label : ID COLON'
429 label = Label()
430 label.is_extern = False
431 label.text = t[1]
432 t[0] = label
433
434def p_label_1(t):
435 'label : EXTERN ID COLON'
436 label = Label()
437 label.is_extern = True
438 label.text = t[2]
439 t[0] = label
440
441# Directives for the macroop
442def p_directive_0(t):
443 'directive : DOT ID'
444 directive = Directive()
445 directive.name = t[2]
446 t[0] = directive
447
448def p_directive_1(t):
449 'directive : DOT ID PARAMS'
450 directive = Directive()
451 directive.name = t[2]
452 directive.params = t[3]
453 t[0] = directive
454
455# Parse error handler. Note that the argument here is the offending
456# *token*, not a grammar symbol (hence the need to use t.value)
457def p_error(t):
458 if t:
459 error(t.lineno, "syntax error at '%s'" % t.value)
460 else:
461 error(0, "unknown syntax error", True)
462
463class MicroAssembler(object):
464
465 def __init__(self, macro_type, microops,
466 rom = None, rom_macroop_type = None):
467 self.lexer = lex.lex()
468 self.parser = yacc.yacc()
469 self.parser.macro_type = macro_type
470 self.parser.macroops = {}
471 self.parser.microops = microops
472 self.parser.rom = rom
473 self.parser.rom_macroop_type = rom_macroop_type
474
475 def assemble(self, asm):
476 self.parser.parse(asm, lexer=self.lexer)
477 # Begin debug printing
478 for macroop in self.parser.macroops.values():
479 print macroop
480 print self.parser.rom
481 # End debug printing
482 macroops = self.parser.macroops
483 self.parser.macroops = {}
484 return macroops
244 t.lexer.begin('asm')
245 return t
246
247# Braces enter and exit micro assembly
248def t_INITIAL_LBRACE(t):
249 r'\{'
250 t.lexer.begin('asm')
251 return t
252
253def t_asm_RBRACE(t):
254 r'\}'
255 t.lexer.begin('INITIAL')
256 return t
257
258# At the top level, keep track of newlines only for line counting.
259def t_INITIAL_NEWLINE(t):
260 r'\n+'
261 t.lineno += t.value.count('\n')
262
263# In the micro assembler, do line counting but also return a token. The
264# token is needed by the parser to detect the end of a statement.
265def t_asm_NEWLINE(t):
266 r'\n+'
267 t.lineno += t.value.count('\n')
268 return t
269
270# A newline or semi colon when looking for params signals that the statement
271# is over and the lexer should go back to looking for regular assembly.
272def t_params_NEWLINE(t):
273 r'\n+'
274 t.lineno += t.value.count('\n')
275 t.lexer.begin('asm')
276 return t
277
278def t_params_SEMI(t):
279 r';'
280 t.lexer.begin('asm')
281 return t
282
283# Basic regular expressions to pick out simple tokens
284t_ANY_LPAREN = r'\('
285t_ANY_RPAREN = r'\)'
286t_ANY_SEMI = r';'
287t_ANY_DOT = r'\.'
288
289t_ANY_ignore = ' \t\x0c'
290
291def t_ANY_error(t):
292 error(t.lineno, "illegal character '%s'" % t.value[0])
293 t.skip(1)
294
295##########################################################################
296#
297# Parser specification
298#
299##########################################################################
300
301# Start symbol for a file which may have more than one macroop or rom
302# specification.
303def p_file(t):
304 'file : opt_rom_or_macros'
305
306def p_opt_rom_or_macros_0(t):
307 'opt_rom_or_macros : '
308
309def p_opt_rom_or_macros_1(t):
310 'opt_rom_or_macros : rom_or_macros'
311
312def p_rom_or_macros_0(t):
313 'rom_or_macros : rom_or_macro'
314
315def p_rom_or_macros_1(t):
316 'rom_or_macros : rom_or_macros rom_or_macro'
317
318def p_rom_or_macro_0(t):
319 '''rom_or_macro : rom_block
320 | macroop_def'''
321
322# Defines a section of microcode that should go in the current ROM
323def p_rom_block(t):
324 'rom_block : DEF ROM block SEMI'
325 if not t.parser.rom:
326 print_error("Rom block found, but no Rom object specified.")
327 raise TypeError, "Rom block found, but no Rom object was specified."
328 for statement in t[3].statements:
329 handle_statement(t.parser, t.parser.rom, statement)
330 t[0] = t.parser.rom
331
332# Defines a macroop that jumps to an external label in the ROM
333def p_macroop_def_0(t):
334 'macroop_def : DEF MACROOP ID LPAREN ID RPAREN SEMI'
335 if not t.parser.rom_macroop_type:
336 print_error("ROM based macroop found, but no ROM macroop class was specified.")
337 raise TypeError, "ROM based macroop found, but no ROM macroop class was specified."
338 macroop = t.parser.rom_macroop_type(t[3], t[5])
339 t.parser.macroops[t[3]] = macroop
340
341
342# Defines a macroop that is combinationally generated
343def p_macroop_def_1(t):
344 'macroop_def : DEF MACROOP ID block SEMI'
345 try:
346 curop = t.parser.macro_type(t[3])
347 except TypeError:
348 print_error("Error creating macroop object.")
349 raise
350 for statement in t[4].statements:
351 handle_statement(t.parser, curop, statement)
352 t.parser.macroops[t[3]] = curop
353
354# A block of statements
355def p_block(t):
356 'block : LBRACE statements RBRACE'
357 block = Block()
358 block.statements = t[2]
359 t[0] = block
360
361def p_statements_0(t):
362 'statements : statement'
363 if t[1]:
364 t[0] = [t[1]]
365 else:
366 t[0] = []
367
368def p_statements_1(t):
369 'statements : statements statement'
370 if t[2]:
371 t[1].append(t[2])
372 t[0] = t[1]
373
374def p_statement(t):
375 'statement : content_of_statement end_of_statement'
376 t[0] = t[1]
377
378# A statement can be a microop or an assembler directive
379def p_content_of_statement_0(t):
380 '''content_of_statement : microop
381 | directive'''
382 t[0] = t[1]
383
384# Ignore empty statements
385def p_content_of_statement_1(t):
386 'content_of_statement : '
387 pass
388
389# Statements are ended by newlines or a semi colon
390def p_end_of_statement(t):
391 '''end_of_statement : NEWLINE
392 | SEMI'''
393 pass
394
395# Different flavors of microop to avoid shift/reduce errors
396def p_microop_0(t):
397 'microop : labels ID'
398 microop = Microop()
399 microop.labels = t[1]
400 microop.mnemonic = t[2]
401 t[0] = microop
402
403def p_microop_1(t):
404 'microop : ID'
405 microop = Microop()
406 microop.mnemonic = t[1]
407 t[0] = microop
408
409def p_microop_2(t):
410 'microop : labels ID PARAMS'
411 microop = Microop()
412 microop.labels = t[1]
413 microop.mnemonic = t[2]
414 microop.params = t[3]
415 t[0] = microop
416
417def p_microop_3(t):
418 'microop : ID PARAMS'
419 microop = Microop()
420 microop.mnemonic = t[1]
421 microop.params = t[2]
422 t[0] = microop
423
424# Labels in the microcode
425def p_labels_0(t):
426 'labels : label'
427 t[0] = [t[1]]
428
429def p_labels_1(t):
430 'labels : labels label'
431 t[1].append(t[2])
432 t[0] = t[1]
433
434def p_label_0(t):
435 'label : ID COLON'
436 label = Label()
437 label.is_extern = False
438 label.text = t[1]
439 t[0] = label
440
441def p_label_1(t):
442 'label : EXTERN ID COLON'
443 label = Label()
444 label.is_extern = True
445 label.text = t[2]
446 t[0] = label
447
448# Directives for the macroop
449def p_directive_0(t):
450 'directive : DOT ID'
451 directive = Directive()
452 directive.name = t[2]
453 t[0] = directive
454
455def p_directive_1(t):
456 'directive : DOT ID PARAMS'
457 directive = Directive()
458 directive.name = t[2]
459 directive.params = t[3]
460 t[0] = directive
461
462# Parse error handler. Note that the argument here is the offending
463# *token*, not a grammar symbol (hence the need to use t.value)
464def p_error(t):
465 if t:
466 error(t.lineno, "syntax error at '%s'" % t.value)
467 else:
468 error(0, "unknown syntax error", True)
469
470class MicroAssembler(object):
471
472 def __init__(self, macro_type, microops,
473 rom = None, rom_macroop_type = None):
474 self.lexer = lex.lex()
475 self.parser = yacc.yacc()
476 self.parser.macro_type = macro_type
477 self.parser.macroops = {}
478 self.parser.microops = microops
479 self.parser.rom = rom
480 self.parser.rom_macroop_type = rom_macroop_type
481
482 def assemble(self, asm):
483 self.parser.parse(asm, lexer=self.lexer)
484 # Begin debug printing
485 for macroop in self.parser.macroops.values():
486 print macroop
487 print self.parser.rom
488 # End debug printing
489 macroops = self.parser.macroops
490 self.parser.macroops = {}
491 return macroops