# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#
-# Authors: Gabe Black
+
+from __future__ import print_function
import os
import sys
import re
-import string
import traceback
# get type names
from types import *
-# Prepend the directory where the PLY lex & yacc modules are found
-# to the search path.
-sys.path[0:0] = [os.environ['M5_PLY']]
-
from ply import lex
from ply import yacc
self.micro_classes = {}
self.labels = {}
- def add_microop(self, microop):
+ def add_microop(self, mnemonic, microop):
self.microops.append(microop)
def __str__(self):
string += " %s\n" % microop
return string
-class Macroop(Micro_Container):
+class Combinational_Macroop(Micro_Container):
pass
+class Rom_Macroop(object):
+ def __init__(self, name, target):
+ self.name = name
+ self.target = target
+
+ def __str__(self):
+ return "%s: %s\n" % (self.name, self.target)
+
class Rom(Micro_Container):
def __init__(self, name):
super(Rom, self).__init__(name)
def __init__(self):
self.is_microop = False
self.is_directive = False
+ self.params = ""
class Microop(Statement):
def __init__(self):
self.mnemonic = ""
self.labels = []
self.is_microop = True
- self.params = ""
class Directive(Statement):
def __init__(self):
##########################################################################
def print_error(message):
- print
- print "*** %s" % message
- print
+ print()
+ print("*** %s" % message)
+ print()
def handle_statement(parser, container, statement):
if statement.is_microop:
+ if statement.mnemonic not in parser.microops.keys():
+ raise Exception, "Unrecognized mnemonic: %s" % statement.mnemonic
+ parser.symbols["__microopClassFromInsideTheAssembler"] = \
+ parser.microops[statement.mnemonic]
try:
- microop = eval('parser.microops[statement.mnemonic](%s)' %
- statement.params)
+ microop = eval('__microopClassFromInsideTheAssembler(%s)' %
+ statement.params, {}, parser.symbols)
except:
- print_error("Error creating microop object.")
+ print_error("Error creating microop object with mnemonic %s." % \
+ statement.mnemonic)
raise
try:
for label in statement.labels:
- container.labels[label.name] = microop
- if label.extern:
- container.externs[label.name] = microop
- container.add_microop(microop)
+ container.labels[label.text] = microop
+ if label.is_extern:
+ container.externs[label.text] = microop
+ container.add_microop(statement.mnemonic, microop)
except:
print_error("Error adding microop.")
raise
elif statement.is_directive:
+ if statement.name not in container.directives.keys():
+ raise Exception, "Unrecognized directive: %s" % statement.name
+ parser.symbols["__directiveFunctionFromInsideTheAssembler"] = \
+ container.directives[statement.name]
try:
- eval('container.%s()' % statement.name)
+ eval('__directiveFunctionFromInsideTheAssembler(%s)' %
+ statement.params, {}, parser.symbols)
except:
print_error("Error executing directive.")
- print container.directives
+ print(container.directives)
raise
else:
raise Exception, "Didn't recognize the type of statement", statement
'LPAREN', 'RPAREN',
'LBRACE', 'RBRACE',
- #'COMMA',
'COLON', 'SEMI', 'DOT',
'NEWLINE'
)
for r in reserved:
reserved_map[r.lower()] = r
+# Ignore comments
+def t_ANY_COMMENT(t):
+ r'\#[^\n]*(?=\n)'
+
+def t_ANY_MULTILINECOMMENT(t):
+ r'/\*([^/]|((?<!\*)/))*\*/'
+
+# A colon marks the end of a label. It should follow an ID which will
+# put the lexer in the "params" state. Seeing the colon will put it back
+# in the "asm" state since it knows it saw a label and not a mnemonic.
def t_params_COLON(t):
r':'
t.lexer.begin('asm')
return t
+# Parameters are a string of text which don't contain an unescaped statement
+# statement terminator, ie a newline or semi colon.
+def t_params_PARAMS(t):
+ r'([^\n;\\]|(\\[\n;\\]))+'
+ t.lineno += t.value.count('\n')
+ unescapeParamsRE = re.compile(r'(\\[\n;\\])')
+ def unescapeParams(mo):
+ val = mo.group(0)
+ return val[1]
+ t.value = unescapeParamsRE.sub(unescapeParams, t.value)
+ t.lexer.begin('asm')
+ return t
+
+# An "ID" in the micro assembler is either a label, directive, or mnemonic
+# If it's either a directive or a mnemonic, it will be optionally followed by
+# parameters. If it's a label, the following colon will make the lexer stop
+# looking for parameters.
def t_asm_ID(t):
r'[A-Za-z_]\w*'
t.type = reserved_map.get(t.value, 'ID')
- t.lexer.begin('params')
+ # If the ID is really "extern", we shouldn't start looking for parameters
+ # yet. The real ID, the label itself, is coming up.
+ if t.type != 'EXTERN':
+ t.lexer.begin('params')
return t
+# If there is a label and you're -not- in the assembler (which would be caught
+# above), don't start looking for parameters.
def t_ANY_ID(t):
r'[A-Za-z_]\w*'
t.type = reserved_map.get(t.value, 'ID')
return t
-def t_params_PARAMS(t):
- r'([^\n;]|((?<=\\)[\n;]))+'
- t.lineno += t.value.count('\n')
- t.lexer.begin('asm')
- return t
-
+# Braces enter and exit micro assembly
def t_INITIAL_LBRACE(t):
r'\{'
t.lexer.begin('asm')
t.lexer.begin('INITIAL')
return t
+# At the top level, keep track of newlines only for line counting.
def t_INITIAL_NEWLINE(t):
r'\n+'
t.lineno += t.value.count('\n')
+# In the micro assembler, do line counting but also return a token. The
+# token is needed by the parser to detect the end of a statement.
def t_asm_NEWLINE(t):
r'\n+'
t.lineno += t.value.count('\n')
return t
+# A newline or semi colon when looking for params signals that the statement
+# is over and the lexer should go back to looking for regular assembly.
def t_params_NEWLINE(t):
r'\n+'
t.lineno += t.value.count('\n')
# Basic regular expressions to pick out simple tokens
t_ANY_LPAREN = r'\('
t_ANY_RPAREN = r'\)'
-#t_COMMA = r','
t_ANY_SEMI = r';'
t_ANY_DOT = r'\.'
'rom_or_macros : rom_or_macros rom_or_macro'
def p_rom_or_macro_0(t):
- '''rom_or_macro : rom_block'''
-
-def p_rom_or_macro_1(t):
- '''rom_or_macro : macroop_def'''
-
-# A block of statements
-def p_block(t):
- 'block : LBRACE statements RBRACE'
- block = Block()
- block.statements = t[2]
- t[0] = block
+ '''rom_or_macro : rom_block
+ | macroop_def'''
# Defines a section of microcode that should go in the current ROM
def p_rom_block(t):
'rom_block : DEF ROM block SEMI'
+ if not t.parser.rom:
+ print_error("Rom block found, but no Rom object specified.")
+ raise TypeError, "Rom block found, but no Rom object was specified."
for statement in t[3].statements:
handle_statement(t.parser, t.parser.rom, statement)
t[0] = t.parser.rom
# Defines a macroop that jumps to an external label in the ROM
def p_macroop_def_0(t):
- 'macroop_def : DEF MACROOP LPAREN ID RPAREN SEMI'
- t[0] = t[4]
+ 'macroop_def : DEF MACROOP ID LPAREN ID RPAREN SEMI'
+ if not t.parser.rom_macroop_type:
+ print_error("ROM based macroop found, but no ROM macroop class was specified.")
+ raise TypeError, "ROM based macroop found, but no ROM macroop class was specified."
+ macroop = t.parser.rom_macroop_type(t[3], t[5])
+ t.parser.macroops[t[3]] = macroop
+
# Defines a macroop that is combinationally generated
def p_macroop_def_1(t):
raise
for statement in t[4].statements:
handle_statement(t.parser, curop, statement)
- t.parser.macroops.append(curop)
+ t.parser.macroops[t[3]] = curop
+
+# A block of statements
+def p_block(t):
+ 'block : LBRACE statements RBRACE'
+ block = Block()
+ block.statements = t[2]
+ t[0] = block
def p_statements_0(t):
'statements : statement'
| directive'''
t[0] = t[1]
+# Ignore empty statements
def p_content_of_statement_1(t):
'content_of_statement : '
pass
| SEMI'''
pass
+# Different flavors of microop to avoid shift/reduce errors
def p_microop_0(t):
'microop : labels ID'
microop = Microop()
microop.params = t[2]
t[0] = microop
+# Labels in the microcode
def p_labels_0(t):
'labels : label'
t[0] = [t[1]]
t[1].append(t[2])
t[0] = t[1]
+# labels on lines by themselves are attached to the following instruction.
+def p_labels_2(t):
+ 'labels : labels NEWLINE'
+ t[0] = t[1]
+
def p_label_0(t):
'label : ID COLON'
label = Label()
label.text = t[2]
t[0] = label
-def p_directive(t):
+# Directives for the macroop
+def p_directive_0(t):
'directive : DOT ID'
directive = Directive()
directive.name = t[2]
t[0] = directive
+def p_directive_1(t):
+ 'directive : DOT ID PARAMS'
+ directive = Directive()
+ directive.name = t[2]
+ directive.params = t[3]
+ t[0] = directive
+
# Parse error handler. Note that the argument here is the offending
# *token*, not a grammar symbol (hence the need to use t.value)
def p_error(t):
class MicroAssembler(object):
- def __init__(self, macro_type, microops, rom):
+ def __init__(self, macro_type, microops,
+ rom = None, rom_macroop_type = None):
self.lexer = lex.lex()
self.parser = yacc.yacc()
self.parser.macro_type = macro_type
- self.parser.macroops = []
+ self.parser.macroops = {}
self.parser.microops = microops
self.parser.rom = rom
+ self.parser.rom_macroop_type = rom_macroop_type
+ self.parser.symbols = {}
+ self.symbols = self.parser.symbols
def assemble(self, asm):
self.parser.parse(asm, lexer=self.lexer)
- for macroop in self.parser.macroops:
- print macroop
- print self.parser.rom
macroops = self.parser.macroops
- self.parser.macroops = []
+ self.parser.macroops = {}
return macroops