changeset 200:5e391d9a3381

Split off asm nodes
author Windel Bouwman
date Sun, 09 Jun 2013 16:06:49 +0200
parents a690473b79e2
children d5debbfc0200
files python/asm.py python/asmnodes.py python/msp430.py python/ppci/errors.py python/target.py python/testasm.py
diffstat 6 files changed, 154 insertions(+), 85 deletions(-) [+]
line wrap: on
line diff
--- a/python/asm.py	Fri Jun 07 18:59:57 2013 +0200
+++ b/python/asm.py	Sun Jun 09 16:06:49 2013 +0200
@@ -2,6 +2,7 @@
 import pyyacc
 from ppci import Token, CompilerError, SourceLocation
 from target import Target
+from asmnodes import ALabel, AInstruction, ABinop, AUnop, ASymbol, ANumber
 
 def tokenize(s):
      """
@@ -65,64 +66,11 @@
    def Peak(self):
       return self.curTok
 
-class ANode:
-    def __eq__(self, other):
-        return self.__repr__() == other.__repr__()
-
-class ALabel(ANode):
-    def __init__(self, name):
-        self.name = name
-    def __repr__(self):
-        return '{0}:'.format(self.name)
-
-class AInstruction(ANode):
-    def __init__(self, opcode, operands):
-        self.opcode = opcode
-        self.operands = operands
-    def __repr__(self):
-        ops = ', '.join(map(str, self.operands))
-        return '{0} {1}'.format(self.opcode, ops)
-
-class AExpression(ANode):
-    def __add__(self, other):
-        assert isinstance(other, AExpression)
-        return ABinop('+', self, other)
-    def __mul__(self, other):
-        assert isinstance(other, AExpression)
-        return ABinop('*', self, other)
-
-class ABinop(AExpression):
-    def __init__(self, op, arg1, arg2):
-        self.op = op
-        self.arg1 = arg1
-        self.arg2 = arg2
-    def __repr__(self):
-        return '{0} {1} {2}'.format(self.op, self.arg1, self.arg2)
-
-class AUnop(AExpression):
-    def __init__(self, op, arg):
-        self.op = op
-        self.arg = arg
-    def __repr__(self):
-        return '{0} {1}'.format(self.op, self.arg)
-
-class ASymbol(AExpression):
-    def __init__(self, name):
-        self.name = name
-    def __repr__(self):
-        return self.name
-
-class ANumber(AExpression):
-    def __init__(self, n):
-        assert type(n) is int
-        self.n = n
-    def __repr__(self):
-        return '{0}'.format(self.n)
 
 class Assembler:
     def __init__(self, target=None):
         self.target = target
-        self.output = []
+        self.restart()
         # Construct a parser given a grammar:
         ident = lambda x: x   # Identity helper function
         g = pyyacc.Grammar(['ID', 'NUMBER', ',', '[', ']', ':', '+', '-', '*', pyyacc.EPS, 'COMMENT'])
@@ -149,8 +97,8 @@
         g.add_production('mulop', ['*'], ident)
         g.add_production('term', ['factor'], ident)
         g.add_production('term', ['term', 'mulop', 'factor'], self.p_binop)
-        g.add_production('factor', ['ID'], self.p_symbol)
-        g.add_production('factor', ['NUMBER'], self.p_number)
+        g.add_production('factor', ['ID'], lambda name: ASymbol(name))
+        g.add_production('factor', ['NUMBER'], lambda num: ANumber(int(num)))
         g.start_symbol = 'asmline'
         self.p = g.genParser()
 
@@ -173,15 +121,12 @@
         self.emit(lab)
     def p_binop(self, exp1, op, exp2):
         return ABinop(op, exp1, exp2)
-    def p_symbol(self, name):
-        return ASymbol(name)
-    def p_number(self, n):
-        n = int(n)
-        return ANumber(n)
 
     # Top level interface:
     def restart(self):
-        pass
+        self.output = []
+        self.binout = bytearray()
+        self.current_section = '.text'
 
     def emit(self, a):
         """ Emit a parsed instruction """
@@ -189,7 +134,6 @@
         # Determine the bit pattern from a lookup table:
         # TODO
 
-
     def parse_line(self, line):
         """ Parse line into asm AST """
         tokens = tokenize(line)
@@ -215,8 +159,11 @@
         if not self.target:
             raise CompilerError('Cannot assemble without target')
         while self.output:
-            i = self.output.pop(0)
-            self.target.createInstruction(i)
+            vi = self.output.pop(0)
+            ri = self.target.mapInstruction(vi)
+            b = ri.encode()
+            assert type(b) is bytes
+            self.binout.extend(b)
 
     def back_patch(self):
         """ Fix references to earlier labels """
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/python/asmnodes.py	Sun Jun 09 16:06:49 2013 +0200
@@ -0,0 +1,57 @@
+
+""" Assembler AST nodes """
+
+class ANode:
+    def __eq__(self, other):
+        return self.__repr__() == other.__repr__()
+
+class ALabel(ANode):
+    def __init__(self, name):
+        self.name = name
+    def __repr__(self):
+        return '{0}:'.format(self.name)
+
+class AInstruction(ANode):
+    def __init__(self, opcode, operands):
+        self.opcode = opcode
+        self.operands = operands
+    def __repr__(self):
+        ops = ', '.join(map(str, self.operands))
+        return '{0} {1}'.format(self.opcode, ops)
+
+class AExpression(ANode):
+    def __add__(self, other):
+        assert isinstance(other, AExpression)
+        return ABinop('+', self, other)
+    def __mul__(self, other):
+        assert isinstance(other, AExpression)
+        return ABinop('*', self, other)
+
+class ABinop(AExpression):
+    def __init__(self, op, arg1, arg2):
+        self.op = op
+        self.arg1 = arg1
+        self.arg2 = arg2
+    def __repr__(self):
+        return '{0} {1} {2}'.format(self.op, self.arg1, self.arg2)
+
+class AUnop(AExpression):
+    def __init__(self, op, arg):
+        self.op = op
+        self.arg = arg
+    def __repr__(self):
+        return '{0} {1}'.format(self.op, self.arg)
+
+class ASymbol(AExpression):
+    def __init__(self, name):
+        self.name = name
+    def __repr__(self):
+        return self.name
+
+class ANumber(AExpression):
+    def __init__(self, n):
+        assert type(n) is int
+        self.n = n
+    def __repr__(self):
+        return '{0}'.format(self.n)
+
--- a/python/msp430.py	Fri Jun 07 18:59:57 2013 +0200
+++ b/python/msp430.py	Sun Jun 09 16:06:49 2013 +0200
@@ -7,26 +7,43 @@
         super().__init__(name)
         self.num = num
 
-# 8 bit variants:
+# 8 bit registers:
 PCB = MSP430Reg(0, 'r0')
-R14B = MSP430Reg(14, 'r14')
-R15B = MSP430Reg(15, 'r15')
+r13 = MSP430Reg(13, 'r13')
+r14 = MSP430Reg(14, 'r14')
+r15 = MSP430Reg(15, 'r15')
 
 # .. etc
 
 #GR8 = RegisterClass((PCB, R15B))
 
+# Two operand arithmatic instructions:
+
 class TwoOpArith(Instruction):
-    def __init__(self, opc, name):
-        super().__init__(opc)
-        self.name = name
+    operands = (MSP430Reg, MSP430Reg)
+    def __init__(self, op1, op2):
+        self.op1 = op1
+        self.op2 = op2
+    def encode(self):
+        # TODO:
+        b1 = (self.opcode << 4)
+        b2 = 0
+        ba = bytearray([b1, b2])
+        return bytes(ba)
 
-mov_ins = TwoOpArith(4, 'mov')
-add_ins = TwoOpArith(5, 'add')
+class mov_ins(TwoOpArith):
+    # class variables:
+    mnemonic = 'mov'
+    opcode = 4
+
+class add_ins(TwoOpArith):
+    mnemonic = 'add'
+    opcode = 5
 
 class MSP430(Target):
     def __init__(self):
-        self.registers = [PCB, R14B, R15B]
+        self.registers = [PCB, r13, r14, r15]
         self.instructions = [mov_ins, add_ins]
 
+t = MSP430()
 
--- a/python/ppci/errors.py	Fri Jun 07 18:59:57 2013 +0200
+++ b/python/ppci/errors.py	Sun Jun 09 16:06:49 2013 +0200
@@ -6,12 +6,16 @@
 from . import SourceLocation
 
 class CompilerError(Exception):
-    def __init__(self, msg, loc):
+    def __init__(self, msg, loc=None):
         self.msg = msg
         self.loc = loc
-        assert type(loc) is SourceLocation, '{0} must be SourceLocation'.format(type(loc))
+        if loc:
+            assert type(loc) is SourceLocation, '{0} must be SourceLocation'.format(type(loc))
     def __repr__(self):
-        return 'Compilererror {0} at row {1}'.format(self.msg, self.loc.row)
+        if self.loc:
+            return 'Compilererror: "{0}" at row {1}'.format(self.msg, self.loc.row)
+        else:
+            return 'Compilererror: "{0}"'.format(self.msg)
 
 def printError(source, e):
      def printLine(row, txt):
--- a/python/target.py	Fri Jun 07 18:59:57 2013 +0200
+++ b/python/target.py	Sun Jun 09 16:06:49 2013 +0200
@@ -1,3 +1,5 @@
+from asmnodes import ASymbol, AInstruction
+from ppci import CompilerError
 
 """
   Base classes for defining a target
@@ -18,9 +20,32 @@
 
 class Target:
     def __init__(self):
+        self.registers = []
         self.instructions = []
-    def createInstruction(self, vi):
-        pass
-    pass
+
+    def mapOperand(self, operand):
+        """ Try to map an operand to a target type """
+        if type(operand) is ASymbol:
+            # Try to map to register:
+            regs = {}
+            for r in self.registers:
+                regs[r.name] = r
+            if operand.name in regs:
+                return regs[operand.name]
+        else:
+            return
 
+    def mapInstruction(self, vi):
+        """ Map ast tree to real instruction for this target """
 
+        # map to real operands:
+        rops = tuple(map(self.mapOperand, vi.operands))
+        optypes = tuple(map(type, rops))
+
+        # look for a suitable instruction
+        for ic in self.instructions:
+            if ic.mnemonic == vi.opcode and ic.operands == optypes:
+                ri = ic(*rops)
+                return ri
+        raise CompilerError('No suitable instruction found')
+
--- a/python/testasm.py	Fri Jun 07 18:59:57 2013 +0200
+++ b/python/testasm.py	Sun Jun 09 16:06:49 2013 +0200
@@ -1,7 +1,7 @@
 #!/usr/bin/python
 
 import unittest, cProfile
-import ppci
+from ppci import CompilerError
 from asm import AInstruction, ABinop, AUnop, ASymbol, ALabel, ANumber, tokenize, Assembler
 import msp430
 
@@ -26,7 +26,7 @@
     def testLex2(self):
         """ Test if lexer fails on a token that is invalid """
         asmline = '0z4: mov rax, rbx $ '
-        with self.assertRaises(ppci.CompilerError):
+        with self.assertRaises(CompilerError):
             list(tokenize(asmline))
 
 class AssemblerParsingTestCase(unittest.TestCase):
@@ -79,6 +79,11 @@
         self.a.parse_line('')
     
 class AssemblerOtherTestCase(unittest.TestCase):
+    def testWithoutTarget(self):
+        a = Assembler()
+        with self.assertRaises(CompilerError):
+            a.assemble_line('')
+    @unittest.skip 
     def testX86(self):
         testsrc = """ ; tst
         begin:
@@ -91,16 +96,30 @@
         # Compare with nasm output:
         nasmbytes = [0x48, 0x89, 0xd8, 0x48, 0x31, 0xd9, 0x48, 0xff, 0xc1]
 
-class AssemblerOtherTestCase(unittest.TestCase):
+class AssemblerMSP430TestCase(unittest.TestCase):
     def setUp(self):
-        self.a = Assembler(target=msp430.MSP430())
+        self.t = msp430.MSP430()
+        self.a = Assembler(target=self.t)
+
+    def testMapInstruction(self):
+        i = AInstruction('mov', [ASymbol('r14'), ASymbol('r15')])
+        self.t.mapInstruction(i)
+
+    def testMapOperand(self):
+        o = ASymbol('r14')
+        mo = self.t.mapOperand(o)
+        self.assertEqual(mo, msp430.r14)
+
+    def testMapOperandIndirection(self):
+        o = AUnop('[]', ASymbol('r14'))
+        mo = self.t.mapOperand(o)
 
     def testMov(self):
         line1 = "mov r14, r15"
         self.a.assemble_line(line1)
 
     def testAdd(self):
-        line1 = "addw r14, r15"
+        line1 = "add r14, r15"
         self.a.assemble_line(line1)