micro_asm.py revision 4503:0f812a876221
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 self.params = "" 95 96class Microop(Statement): 97 def __init__(self): 98 super(Microop, self).__init__() 99 self.mnemonic = "" 100 self.labels = [] 101 self.is_microop = True 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.directives[statement.name](%s)' % statement.params) 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 'COLON', 'SEMI', 'DOT', 178 'NEWLINE' 179 ) 180 181# New lines are ignored at the top level, but they end statements in the 182# assembler 183states = ( 184 ('asm', 'exclusive'), 185 ('params', 'exclusive'), 186) 187 188reserved_map = { } 189for r in reserved: 190 reserved_map[r.lower()] = r 191 192def t_ANY_COMMENT(t): 193 r'\#[^\n]*(?=\n)' 194 #print "t_ANY_COMMENT %s" % t.value 195 196def t_ANY_MULTILINECOMMENT(t): 197 r'/\*([^/]|((?<!\*)/))*\*/' 198 #print "t_ANY_MULTILINECOMMENT %s" % t.value 199 200def t_params_COLON(t): 201 r':' 202 t.lexer.begin('asm') 203 #print "t_params_COLON %s" % t.value 204 return t 205 206def t_asm_ID(t): 207 r'[A-Za-z_]\w*' 208 t.type = reserved_map.get(t.value, 'ID') 209 t.lexer.begin('params') 210 #print "t_asm_ID %s" % t.value 211 return t 212 213def t_ANY_ID(t): 214 r'[A-Za-z_]\w*' 215 t.type = reserved_map.get(t.value, 'ID') 216 #print "t_ANY_ID %s" % t.value 217 return t 218 219def t_params_PARAMS(t): 220 r'([^\n;]|((?<=\\)[\n;]))+' 221 t.lineno += t.value.count('\n') 222 t.lexer.begin('asm') 223 #print "t_params_PARAMS %s" % t.value 224 return t 225 226def t_INITIAL_LBRACE(t): 227 r'\{' 228 t.lexer.begin('asm') 229 #print "t_INITIAL_LBRACE %s" % t.value 230 return t 231 232def t_asm_RBRACE(t): 233 r'\}' 234 t.lexer.begin('INITIAL') 235 #print "t_asm_RBRACE %s" % t.value 236 return t 237 238def t_INITIAL_NEWLINE(t): 239 r'\n+' 240 t.lineno += t.value.count('\n') 241 #print "t_INITIAL_NEWLINE %s" % t.value 242 243def t_asm_NEWLINE(t): 244 r'\n+' 245 t.lineno += t.value.count('\n') 246 #print "t_asm_NEWLINE %s" % t.value 247 return t 248 249def t_params_NEWLINE(t): 250 r'\n+' 251 t.lineno += t.value.count('\n') 252 t.lexer.begin('asm') 253 #print "t_params_NEWLINE %s" % t.value 254 return t 255 256def t_params_SEMI(t): 257 r';' 258 t.lexer.begin('asm') 259 #print "t_params_SEMI %s" % t.value 260 return t 261 262# Basic regular expressions to pick out simple tokens 263t_ANY_LPAREN = r'\(' 264t_ANY_RPAREN = r'\)' 265t_ANY_SEMI = r';' 266t_ANY_DOT = r'\.' 267 268t_ANY_ignore = ' \t\x0c' 269 270def t_ANY_error(t): 271 error(t.lineno, "illegal character '%s'" % t.value[0]) 272 t.skip(1) 273 274########################################################################## 275# 276# Parser specification 277# 278########################################################################## 279 280# Start symbol for a file which may have more than one macroop or rom 281# specification. 282def p_file(t): 283 'file : opt_rom_or_macros' 284 285def p_opt_rom_or_macros_0(t): 286 'opt_rom_or_macros : ' 287 288def p_opt_rom_or_macros_1(t): 289 'opt_rom_or_macros : rom_or_macros' 290 291def p_rom_or_macros_0(t): 292 'rom_or_macros : rom_or_macro' 293 294def p_rom_or_macros_1(t): 295 'rom_or_macros : rom_or_macros rom_or_macro' 296 297def p_rom_or_macro_0(t): 298 '''rom_or_macro : rom_block''' 299 300def p_rom_or_macro_1(t): 301 '''rom_or_macro : macroop_def''' 302 303# A block of statements 304def p_block(t): 305 'block : LBRACE statements RBRACE' 306 block = Block() 307 block.statements = t[2] 308 t[0] = block 309 310# Defines a section of microcode that should go in the current ROM 311def p_rom_block(t): 312 'rom_block : DEF ROM block SEMI' 313 for statement in t[3].statements: 314 handle_statement(t.parser, t.parser.rom, statement) 315 t[0] = t.parser.rom 316 317# Defines a macroop that jumps to an external label in the ROM 318def p_macroop_def_0(t): 319 'macroop_def : DEF MACROOP ID LPAREN ID RPAREN SEMI' 320 t[0] = t[4] 321 322# Defines a macroop that is combinationally generated 323def p_macroop_def_1(t): 324 'macroop_def : DEF MACROOP ID block SEMI' 325 try: 326 curop = t.parser.macro_type(t[3]) 327 except TypeError: 328 print_error("Error creating macroop object.") 329 raise 330 for statement in t[4].statements: 331 handle_statement(t.parser, curop, statement) 332 t.parser.macroops[t[3]] = curop 333 334def p_statements_0(t): 335 'statements : statement' 336 if t[1]: 337 t[0] = [t[1]] 338 else: 339 t[0] = [] 340 341def p_statements_1(t): 342 'statements : statements statement' 343 if t[2]: 344 t[1].append(t[2]) 345 t[0] = t[1] 346 347def p_statement(t): 348 'statement : content_of_statement end_of_statement' 349 t[0] = t[1] 350 351# A statement can be a microop or an assembler directive 352def p_content_of_statement_0(t): 353 '''content_of_statement : microop 354 | directive''' 355 t[0] = t[1] 356 357def p_content_of_statement_1(t): 358 'content_of_statement : ' 359 pass 360 361# Statements are ended by newlines or a semi colon 362def p_end_of_statement(t): 363 '''end_of_statement : NEWLINE 364 | SEMI''' 365 pass 366 367def p_microop_0(t): 368 'microop : labels ID' 369 microop = Microop() 370 microop.labels = t[1] 371 microop.mnemonic = t[2] 372 t[0] = microop 373 374def p_microop_1(t): 375 'microop : ID' 376 microop = Microop() 377 microop.mnemonic = t[1] 378 t[0] = microop 379 380def p_microop_2(t): 381 'microop : labels ID PARAMS' 382 microop = Microop() 383 microop.labels = t[1] 384 microop.mnemonic = t[2] 385 microop.params = t[3] 386 t[0] = microop 387 388def p_microop_3(t): 389 'microop : ID PARAMS' 390 microop = Microop() 391 microop.mnemonic = t[1] 392 microop.params = t[2] 393 t[0] = microop 394 395def p_labels_0(t): 396 'labels : label' 397 t[0] = [t[1]] 398 399def p_labels_1(t): 400 'labels : labels label' 401 t[1].append(t[2]) 402 t[0] = t[1] 403 404def p_label_0(t): 405 'label : ID COLON' 406 label = Label() 407 label.is_extern = False 408 label.text = t[1] 409 t[0] = label 410 411def p_label_1(t): 412 'label : EXTERN ID COLON' 413 label = Label() 414 label.is_extern = True 415 label.text = t[2] 416 t[0] = label 417 418def p_directive_0(t): 419 'directive : DOT ID' 420 directive = Directive() 421 directive.name = t[2] 422 t[0] = directive 423 424def p_directive_1(t): 425 'directive : DOT ID PARAMS' 426 directive = Directive() 427 directive.name = t[2] 428 directive.params = t[3] 429 t[0] = directive 430 431# Parse error handler. Note that the argument here is the offending 432# *token*, not a grammar symbol (hence the need to use t.value) 433def p_error(t): 434 if t: 435 error(t.lineno, "syntax error at '%s'" % t.value) 436 else: 437 error(0, "unknown syntax error", True) 438 439class MicroAssembler(object): 440 441 def __init__(self, macro_type, microops, rom): 442 self.lexer = lex.lex() 443 self.parser = yacc.yacc() 444 self.parser.macro_type = macro_type 445 self.parser.macroops = {} 446 self.parser.microops = microops 447 self.parser.rom = rom 448 449 def assemble(self, asm): 450 self.parser.parse(asm, lexer=self.lexer) 451 # Begin debug printing 452 for macroop in self.parser.macroops.values(): 453 print macroop 454 print self.parser.rom 455 # End debug printing 456 macroops = self.parser.macroops 457 self.parser.macroops = {} 458 return macroops 459