Mercurial > lcfOS
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)