changeset 381:6df89163e114

Fix section and ldr pseudo instruction
author Windel Bouwman
date Sat, 26 Apr 2014 17:41:56 +0200
parents 67a584582aee
children 0c44e494ef58
files doc/utils.rst kernel/arch/qemu_vexpress/archmem.c3 kernel/arch/qemu_vexpress/startup_a9.asm kernel/arch/qemu_vexpress/vexpressA9.mmap kernel/arch/vexpressA9.c3 kernel/build.xml kernel/make.sh kernel/src/kernel.c3 kernel/src/memory.c3 kernel/startup_a9.asm python/asm.py python/ppci/assembler.py python/ppci/buildfunctions.py python/ppci/buildtasks.py python/ppci/linker.py python/ppci/outstream.py python/ppci/target/arm/__init__.py python/ppci/target/arm/armv7.lidl python/ppci/target/arm/instructions.py python/ppci/target/basetarget.py python/ppci/target/msp430/msp430.py python/ppci/target/thumb/armtarget.py python/zcc.py readme.rst test/runtests.sh test/testarmasm.py test/testasm.py test/testbintools.py test/testmsp430asm.py test/testsamples.py test/testthumbasm.py util/test_patterns.txt
diffstat 32 files changed, 291 insertions(+), 276 deletions(-) [+]
line wrap: on
line diff
--- a/doc/utils.rst	Fri Apr 18 13:08:45 2014 +0200
+++ b/doc/utils.rst	Sat Apr 26 17:41:56 2014 +0200
@@ -29,3 +29,43 @@
 
 .. automodule:: pyburg
 
+
+Machine descriptions
+--------------------
+
+There are some attempts made already to describe machines in a Domain
+Specific Language (DSL). Examples of these are:
+- Tablegen (llvm)
+- cgen (gnu)
+- LISA (Aachen)
+- nML (Berlin)
+- SLED (Specifying representations of machine instructions (norman ramsey and Mary F. Fernandez))
+
+
+The goal of a machine description file is to describe a file and generate
+tools like assemblers, disassemblers, linkers, debuggers and simulators.
+
+Advantage of using this approach is that porting these tools is a semi automated
+process. Rewriting all of these tools from scratch is tedious and errorprone.
+
+Concepts to use in this language:
+- Single stream of instructions
+- State stored in memory
+- Pipelining
+- Instruction semantics
+
+Each instruction has the following properties:
+- Bit representation
+- Assembly language representation
+- Semantic action
+
+Optionally a description in terms of compiler code generation can be attached
+to this. But perhaps this clutters the description too much and we need to put
+it elsewhere.
+
+
+The description language can help to expand these descriptions by expanding
+the permutations.
+
+
+
--- a/kernel/arch/qemu_vexpress/archmem.c3	Fri Apr 18 13:08:45 2014 +0200
+++ b/kernel/arch/qemu_vexpress/archmem.c3	Sat Apr 26 17:41:56 2014 +0200
@@ -1,19 +1,4 @@
 module archmem;
 import io;
 
-function void init()
-{
-    // putc(65)
-    io.print2("PFR0 = ", pfr0());
-    io.print2("PFR1 = ", pfr1());
-    io.print2("MMFR0 = ", mmfr0());
 
-    // This below is not compatible with all qemu versions:
-    // io.print2("MPUIR = ", arch.mpuir());
-}
-
-function int pfr0();
-function int pfr1();
-function int mmfr0();
-// function int mpuir();
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kernel/arch/qemu_vexpress/startup_a9.asm	Sat Apr 26 17:41:56 2014 +0200
@@ -0,0 +1,73 @@
+
+section code
+
+interrupt_vector_table:
+ivt_reset: B start  ; 0x0 reset
+ivt_undef: B undef_handler  ; 0x4 undefined instruction
+ivt_svc: B undef_handler  ; 0x08 Supervisor call
+ivt_prefetch: B undef_handler ; 0x0C prefetch abort
+ivt_data: B undef_handler  ; 0x10 data abort
+ivt_hyptrap: B undef_handler ; 0x14 not used
+ivt_irq: B undef_handler  ; 0x18 IRQ
+ivt_fiq: B undef_handler  ; 0x18 FIQ
+
+
+start:
+
+; Setup TTBR1 (translation table base register)
+
+ldr r0, =kernel_table0    ; pseudo instruction which loads the value of the symbol
+; -KERNEL_BASE
+mcr p15, 0, r0, c2, c0, 1 ; TTBR1
+mcr p15, 0, r0, c2, c0, 0 ; TTBR0
+
+; Prepare the TTBCR (translation table base control register)
+mov r0, 0x1  ; TBD: why set this to 1?
+mcr p15, 0, r0, c2, c0, 2
+
+; Enable the VMSA (Virtual memory system architecture):
+mrc p15, 0, r0, c1, c0, 0
+; TODO:
+; orr r0, r0, 0x1
+mcr p15, 0, r0, c1, c0, 0
+
+; Setup stack:
+mov sp, 0x30000
+BL kernel_start          ; Branch to main (this is actually in the interrupt vector)
+local_loop:
+B local_loop
+
+
+; Interrupt handlers:
+
+undef_handler:
+B undef_handler
+
+
+; Assembly language helpers:
+; Called to identify the proc:
+arch_pfr0:
+mrc p15, 0, r0, c0, c1, 0
+mov pc, lr
+
+arch_pfr1:
+mrc p15, 0, r0, c0, c1, 1
+mov pc, lr
+
+arch_mmfr0:
+mrc p15, 0, r0, c0, c1, 4
+mov pc, lr
+
+
+arch_mpuir:
+mrc p15, 0, r0, c0, c0, 4
+mov pc, lr
+
+
+; Memory map tables:
+
+section mem_tables
+
+kernel_table0:
+ dcd 0x000402
+
--- a/kernel/arch/qemu_vexpress/vexpressA9.mmap	Fri Apr 18 13:08:45 2014 +0200
+++ b/kernel/arch/qemu_vexpress/vexpressA9.mmap	Sat Apr 26 17:41:56 2014 +0200
@@ -1,6 +1,7 @@
 
 {
     "code": "0x10000",
+    "mem_tables": "0x11000",
     "data": "0x20000"
 }
 
--- a/kernel/arch/vexpressA9.c3	Fri Apr 18 13:08:45 2014 +0200
+++ b/kernel/arch/vexpressA9.c3	Sat Apr 26 17:41:56 2014 +0200
@@ -5,6 +5,12 @@
 function void init()
 {
     // putc(65)
+    io.print2("PFR0 = ", pfr0());
+    io.print2("PFR1 = ", pfr1());
+    io.print2("MMFR0 = ", mmfr0());
+
+    // This below is not compatible with all qemu versions:
+    // io.print2("MPUIR = ", arch.mpuir());
 }
 
 function void putc(int c)
@@ -16,5 +22,11 @@
 
 function void halt()
 {
+    while(true) {}
 }
 
+function int pfr0();
+function int pfr1();
+function int mmfr0();
+// function int mpuir();
+
--- a/kernel/build.xml	Fri Apr 18 13:08:45 2014 +0200
+++ b/kernel/build.xml	Sat Apr 26 17:41:56 2014 +0200
@@ -5,7 +5,7 @@
     <property name="arch" value="arm" />
 
     <target name="vexpress">
-        <assemble source="startup_a9.asm" target="arm" output="start.o" />
+        <assemble source="arch/qemu_vexpress/startup_a9.asm" target="arm" output="start.o" />
         <compile target="arm" sources='arch/vexpressA9.c3' includes="${src}/*.c3" output="vexp.o" />
         <compile target="arm" sources='${src}/*.c3' output="henkie.o" />
         <link output="kernel_arm.bin" layout="arch/qemu_vexpress/vexpressA9.mmap" objects="start.o;henkie.o;vexp.o" />
--- a/kernel/make.sh	Fri Apr 18 13:08:45 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,5 +0,0 @@
-#!/bin/bash
-
-# ../python/zcc.py recipe thumb.yaml
-
-../python/zcc.py --report log.txt recipe arm.yaml
--- a/kernel/src/kernel.c3	Fri Apr 18 13:08:45 2014 +0200
+++ b/kernel/src/kernel.c3	Sat Apr 26 17:41:56 2014 +0200
@@ -6,12 +6,13 @@
 import arch;
 import io;
 
+
 // Main entry point of the kernel:
 function void start()
 {
+    io.println("Welcome to lcfos!");
     arch.init();
 
-    io.println("Welcome to lcfos!");
 
     // process.init();
     memory.init();
@@ -19,7 +20,8 @@
     //Process proc = new process:Process();
 
     //scheduler:queue(proc);
-    while(true) {}
+    io.println("Kernel finished");
+    arch.halt();
 }
 
 // Called in total stress:
--- a/kernel/src/memory.c3	Fri Apr 18 13:08:45 2014 +0200
+++ b/kernel/src/memory.c3	Sat Apr 26 17:41:56 2014 +0200
@@ -1,12 +1,14 @@
 module memory;
 
 import arch;
+import io;
 
 var int ptr;
 
 function void init()
 {
-    arch.init();
+    ptr = 0;
+    io.print2("ptr = ", ptr);
     // TODO
 }
 
--- a/kernel/startup_a9.asm	Fri Apr 18 13:08:45 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-
-
-interrupt_vector_table:
-ivt_reset: B start  ; 0x0 reset
-ivt_undef: B undef_handler  ; 0x4 undefined instruction
-ivt_svc: B undef_handler  ; 0x08 Supervisor call
-ivt_prefetch: B undef_handler ; 0x0C prefetch abort
-ivt_data: B undef_handler  ; 0x10 data abort
-ivt_hyptrap: B undef_handler ; 0x14 not used
-ivt_irq: B undef_handler  ; 0x18 IRQ
-ivt_fiq: B undef_handler  ; 0x18 FIQ
-
-
-start:
-
-; Setup TTBR1 (translation table base register)
-
-; mov r0, =kernel_table0-KERNEL_BASE
-mcr p15, 0, r0, c2, c0, 1 ; TTBR1
-mcr p15, 0, r0, c2, c0, 0 ; TTBR0
-
-; Prepare the TTBCR (translation table base control register)
-mov r0, 0x1  ; TBD: why set this to 1?
-mcr p15, 0, r0, c2, c0, 2
-
-; Enable the VMSA (Virtual memory system architecture):
-mrc p15, 0, r0, c1, c0, 0
-; TODO:
-; orr r0, r0, 0x1
-mcr p15, 0, r0, c1, c0, 0
-
-; Setup stack:
-mov sp, 0x30000
-BL kernel_start          ; Branch to main (this is actually in the interrupt vector)
-local_loop:
-B local_loop
-
-
-; Interrupt handlers:
-
-undef_handler:
-B undef_handler
-
-; Called to identify the proc:
-archmem_pfr0:
-mrc p15, 0, r0, c0, c1, 0
-mov pc, lr
-
-archmem_pfr1:
-mrc p15, 0, r0, c0, c1, 1
-mov pc, lr
-
-archmem_mmfr0:
-mrc p15, 0, r0, c0, c1, 4
-mov pc, lr
-
-
-archmem_mpuir:
-mrc p15, 0, r0, c0, c0, 4
-mov pc, lr
--- a/python/asm.py	Fri Apr 18 13:08:45 2014 +0200
+++ b/python/asm.py	Sat Apr 26 17:41:56 2014 +0200
@@ -1,7 +1,8 @@
 #!/usr/bin/env python3
 
 import argparse
-from ppci.assembler import Assembler
+from ppci.target.target_list import targets
+from ppci.buildfunctions import assemble
 
 if __name__ == '__main__':
     # When run as main file, try to grab command line arguments:
@@ -9,5 +10,4 @@
     parser.add_argument('sourcefile', type=argparse.FileType('r'),
         help='the source file to assemble')
     args = parser.parse_args()
-    a = Assembler()
-    obj = a.assemble(args.sourcefile.read())
+    obj = assemble(args.sourcefile, targets['arm'])
--- a/python/ppci/assembler.py	Fri Apr 18 13:08:45 2014 +0200
+++ b/python/ppci/assembler.py	Sat Apr 26 17:41:56 2014 +0200
@@ -89,7 +89,8 @@
         if prod == 'instruction':
             def f_wrap(*args):
                 i = f(args)
-                self.emit(i)
+                if i:
+                    self.emit(i)
         else:
             def f_wrap(*rhs):
                 return f(rhs)
@@ -112,7 +113,6 @@
         g.add_production('asmline2', ['label'])
         g.add_production('asmline2', [])
         g.add_production('label', ['ID', ':'], self.p_label)
-        #g.add_production('label', [])
 
         # Add instruction rules for the target in question:
         for prod, rhs, f in instruction_rules:
@@ -134,11 +134,14 @@
         self.p.parse(lexer)
 
 
-class Assembler:
+class BaseAssembler:
+    """ Assembler base class, inherited by assemblers specific for a target """
     def __init__(self, target):
         self.target = target
         assert isinstance(target, Target)
-        self.parser = Parser(target.asm_keywords, target.assembler_rules, self.emit)
+
+    def make_parser(self):
+        self.parser = Parser(self.target.asm_keywords, self.target.assembler_rules, self.emit)
 
     def emit(self, *args):
         self.stream.emit(*args)
@@ -151,7 +154,9 @@
 
     def assemble(self, asmsrc, stream):
         """ Assemble this source snippet """
-        if hasattr(asmsrc, 'read'):
+        if type(asmsrc) is str:
+            pass
+        elif hasattr(asmsrc, 'read'):
             asmsrc2 = asmsrc.read()
             asmsrc.close()
             asmsrc = asmsrc2
@@ -160,5 +165,6 @@
         self.stream = stream
         for line in asmsrc.split('\n'):
             self.parse_line(line)
-        self.stream = None
 
+    def flush(self):
+        pass
--- a/python/ppci/buildfunctions.py	Fri Apr 18 13:08:45 2014 +0200
+++ b/python/ppci/buildfunctions.py	Sat Apr 26 17:41:56 2014 +0200
@@ -13,12 +13,11 @@
 from .transform import CleanPass, RemoveAddZero
 from .linker import Linker
 from .target.target_list import targets
-from .outstream import BinaryOutputStream, MasterOutputStream
-from .outstream import LoggerOutputStream
-from .assembler import Assembler
+from .outstream import BinaryAndLoggingStream
 from .objectfile import ObjectFile, load_object
 from . import DiagnosticsManager, CompilerError
 
+
 def fix_target(tg):
     """ Try to return an instance of the Target class """
     if isinstance(tg, Target):
@@ -28,6 +27,7 @@
             return targets[tg]
     raise TaskError('Invalid target {}'.format(tg))
 
+
 def fix_file(f):
     """ Determine if argument is a file like object or make it so! """
     if hasattr(f, 'read'):
@@ -38,6 +38,7 @@
     else:
         raise TaskError('cannot use {} as input'.format(f))
 
+
 def fix_object(o):
     if isinstance(o, ObjectFile):
         return o
@@ -49,22 +50,23 @@
 
 
 def assemble(source, target):
+    """ Invoke the assembler on the given source, returns an object containing
+        the output. """
     logger = logging.getLogger('assemble')
     target = fix_target(target)
     source = fix_file(source)
     output = ObjectFile()
-    assembler = Assembler(target)
+    assembler = target.assembler
     logger.debug('Assembling into code section')
-    o2 = BinaryOutputStream(output)
-    o1 = LoggerOutputStream()
-    ostream = MasterOutputStream([o1, o2])
+    ostream = BinaryAndLoggingStream(output)
     ostream.select_section('code')
     assembler.assemble(source, ostream)
+    assembler.flush()
     return output
 
 
 def c3compile(sources, includes, target):
-    """ Compile a set of sources """
+    """ Compile a set of sources for the given target """
     logger = logging.getLogger('c3c')
     logger.debug('C3 compilation started')
     target = fix_target(target)
@@ -75,9 +77,7 @@
     c3b = Builder(diag, target)
     cg = CodeGenerator(target)
 
-    o2 = BinaryOutputStream(output)
-    o1 = LoggerOutputStream()
-    o = MasterOutputStream([o1, o2])
+    output_stream = BinaryAndLoggingStream(output)
 
     for ircode in c3b.build(sources, includes):
         if not ircode:
@@ -100,7 +100,7 @@
         d = {'ircode':ircode}
         logger.debug('Starting code generation for {}'.format(ircode), extra=d)
 
-        cg.generate(ircode, o)
+        cg.generate(ircode, output_stream)
 
     if not c3b.ok:
         diag.printErrors()
@@ -109,7 +109,12 @@
 
 
 def link(objects, layout):
+    """ Links the iterable of objects into one using the given layout """
     objects = list(map(fix_object, objects))
     linker = Linker()
     output_obj = linker.link(objects, layout)
     return output_obj
+
+
+def objcopy(obj):
+    return
--- a/python/ppci/buildtasks.py	Fri Apr 18 13:08:45 2014 +0200
+++ b/python/ppci/buildtasks.py	Sat Apr 26 17:41:56 2014 +0200
@@ -53,6 +53,8 @@
             raise TaskError('Error during assembly:' + str(e))
         except CompilerError as e:
             raise TaskError('Error during assembly:' + str(e))
+        except OSError as e:
+            raise TaskError('Error:' + str(e))
         with open(output_filename, 'w') as f:
             output.save(f)
         self.logger.debug('Assembling finished')
--- a/python/ppci/linker.py	Fri Apr 18 13:08:45 2014 +0200
+++ b/python/ppci/linker.py	Sat Apr 26 17:41:56 2014 +0200
@@ -99,7 +99,7 @@
     if offset < 0:
         offset = -offset
         U = 0
-    assert offset < 4096
+    assert offset < 4096, str(sym) + str(section) + str(reloc)
     section.data[reloc.offset+2] |= (U << 7)
     section.data[reloc.offset+1] |= (offset >> 8) & 0xF
     section.data[reloc.offset+0] = offset & 0xFF
@@ -130,16 +130,13 @@
     section.data[reloc.offset+0] = offset & 0xFF
 
 
-class Layout:
-    pass
-
 class Linker:
     """ Merges the sections of several object files and 
         performs relocation """
     def __init__(self):
         self.logger = logging.getLogger('Linker')
 
-    def link(self, objs, layout={}):
+    def link(self, objs, layout):
         assert type(objs) is list
         # Create new object file to store output:
         self.dst = ObjectFile()
@@ -175,6 +172,9 @@
                 offset = offsets[reloc.section] + reloc.offset
                 self.dst.add_relocation(reloc.sym, offset, reloc.typ, reloc.section)
 
+        # Apply layout rules:
+        # TODO
+
         # Perform relocations:
         for reloc in self.dst.relocations:
             # Lookup symbol:
--- a/python/ppci/outstream.py	Fri Apr 18 13:08:45 2014 +0200
+++ b/python/ppci/outstream.py	Sat Apr 26 17:41:56 2014 +0200
@@ -23,6 +23,7 @@
     def __init__(self, obj_file):
         super().__init__()
         self.obj_file = obj_file
+        self.literal_pool = []
 
     def emit(self, item):
         """ Encode instruction and add symbol and relocation information """
@@ -83,3 +84,11 @@
     def select_section(self, sname):
         for output_stream in self.substreams:
             output_stream.select_section(sname)
+
+
+def BinaryAndLoggingStream(output):
+    """ Create a stream object that both logs and writes to an object file """
+    o2 = BinaryOutputStream(output)
+    o1 = LoggerOutputStream()
+    ostream = MasterOutputStream([o1, o2])
+    return ostream
--- a/python/ppci/target/arm/__init__.py	Fri Apr 18 13:08:45 2014 +0200
+++ b/python/ppci/target/arm/__init__.py	Sat Apr 26 17:41:56 2014 +0200
@@ -12,6 +12,36 @@
 from .instructions import LdrPseudo
 from .selector import ArmInstructionSelector
 from .frame import ArmFrame
+from ...assembler import BaseAssembler
+
+
+class ArmAssembler(BaseAssembler):
+    def __init__(self, target):
+        super().__init__(target)
+        self.target.add_keyword('section')
+        self.target.add_instruction(['section', 'ID'],
+                lambda rhs: self.select_section(rhs[1].val))
+        self.make_parser()
+        self.lit_pool = []
+        self.lit_counter = 0
+
+    def select_section(self, name):
+        self.flush()
+        self.stream.select_section(name)
+
+    def flush(self):
+        while self.lit_pool:
+            i = self.lit_pool.pop(0)
+            self.emit(i)
+
+    def add_literal(self, v):
+        """ For use in the pseudo instruction LDR r0, =SOMESYM """
+        # Invent some label for the literal and store it.
+        self.lit_counter += 1
+        label_name = "_lit_{}".format(self.lit_counter)
+        self.lit_pool.append(Label(label_name))
+        self.lit_pool.append(Dcd(v))
+        return label_name
 
 class ArmTarget(Target):
     def __init__(self):
@@ -19,6 +49,7 @@
         self.make_parser()
         self.ins_sel = ArmInstructionSelector()
         self.FrameClass = ArmFrame
+        self.assembler = ArmAssembler(self)
 
         self.add_lowering(Ldr3, lambda im: Ldr3(im.dst[0], im.others[0]))
         self.add_lowering(Str1, lambda im: Str1(im.src[1], im.src[0], im.others[0]))
@@ -38,6 +69,7 @@
         outs.emit(Label(lname))
         outs.emit(Dcd(0))
 
+
     def make_parser(self):
         # Assembly grammar:
         self.add_keyword('r0')
@@ -157,7 +189,7 @@
 
         # This is a pseudo instruction:
         self.add_instruction(['ldr', 'reg', ',', '=', 'ID'],
-            lambda rhs: LdrPseudo(rhs[1], rhs[4].val))
+            lambda rhs: LdrPseudo(rhs[1], rhs[4].val, self.assembler.add_literal))
 
         self.add_keyword('str')
         self.add_instruction(['str', 'reg', ',', '[', 'reg', ',', 'imm8', ']'],
--- a/python/ppci/target/arm/armv7.lidl	Fri Apr 18 13:08:45 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-
-#  This file specifies the encoding of the arm instruction set.
-
-fields {
-    word16 16 {
-        opcode 15:12
-        top2 15:14
-        top6 15:10
-        data_opcode 9..6
-        opB 11:9
-        Rm 8:6
-        Rn 5:3
-        Rt 2:0
-    }
-}
-
-patterns {
- add = 0
- sub, mul = 1..2
- [r1, r2, r3, r4, r5] is todo = 1..5
- [STR, STRH, STRB, LDRSB, LDR, LDRH, LDRB, LDRSH] is opcode = 0b0101 & opB = {0 to 7}
-
- EQ, NE, CS, CC, MI, PL, VS, VC, HI, LS, GE, LT, GT, LE, AL = 0..14
- [AND, EOR, LSL, LSR, ASR, ADC, SBC, ROR, TST, RSB, CMP, CMN, ORR, MUL, BIC, MVN] is  0..15
-
- memop is STR | STRH | STRB | LDRSB | LDR | LDR | LDRH | LDRB | LDRSH
-}
-
-
-constructors
-{
- alu rs1, reg_or_imm, rd
- memop Rt, [Rn, Rm] is memop & Rt & Rn & Rm
-}
-
-
--- a/python/ppci/target/arm/instructions.py	Fri Apr 18 13:08:45 2014 +0200
+++ b/python/ppci/target/arm/instructions.py	Sat Apr 26 17:41:56 2014 +0200
@@ -406,9 +406,10 @@
     raise Exception()
 
 
-def LdrPseudo(rt, lab):
+def LdrPseudo(rt, lab, add_lit):
     """ Ldr rt, =lab ==> ldr rt, [pc, offset in litpool] ... dcd lab """
-    return Ldr(rt, R0)
+    lit_lbl = add_lit(LabelAddress(lab))
+    return Ldr(rt, lit_lbl)
 
 def Str(*args):
     if len(args) == 3 and isinstance(args[1], ArmRegister):
--- a/python/ppci/target/basetarget.py	Fri Apr 18 13:08:45 2014 +0200
+++ b/python/ppci/target/basetarget.py	Sat Apr 26 17:41:56 2014 +0200
@@ -16,6 +16,9 @@
     def symbols(self):
         return []
 
+    def literals(self, add_literal):
+        pass
+
 
 class Nop(Instruction):
     """ Instruction that does nothing and has zero size """
@@ -138,6 +141,7 @@
             if isinstance(im.assem, Instruction):
                 outs.emit(im.assem)
             else:
+                # TODO assert isinstance(Abs
                 ins = self.lower_functions[im.assem](im)
                 outs.emit(ins)
 
--- a/python/ppci/target/msp430/msp430.py	Fri Apr 18 13:08:45 2014 +0200
+++ b/python/ppci/target/msp430/msp430.py	Sat Apr 26 17:41:56 2014 +0200
@@ -4,9 +4,17 @@
 from ppci import CompilerError
 from .registers import r10, r11, r12, r13, r14, r15
 from .instructions import Reti, Mov, Add
+from ...assembler import BaseAssembler
+
 
 # Create the target class (singleton):
 
+class Msp430Assembler(BaseAssembler):
+    def __init__(self, target):
+        super().__init__(target)
+        self.make_parser()
+
+
 class Msp430Target(Target):
     def __init__(self):
         super().__init__('msp430')
@@ -39,6 +47,7 @@
         self.add_keyword('reti')
         self.add_instruction(['reti'], lambda rhs: Reti())
 
+        self.assembler = Msp430Assembler(self)
 
 
 msp430target = Msp430Target()
--- a/python/ppci/target/thumb/armtarget.py	Fri Apr 18 13:08:45 2014 +0200
+++ b/python/ppci/target/thumb/armtarget.py	Sat Apr 26 17:41:56 2014 +0200
@@ -9,6 +9,7 @@
 from .arminstructionselector import ArmInstructionSelector
 from ..arm.registers import R0, R1, R2, R3, R4, R5, R6, R7, SP, LR, PC
 from ..arm.registers import register_range
+from ...assembler import BaseAssembler
 
 
 """ ARM target description. """
@@ -17,9 +18,11 @@
 # TBD: is this required?
 # TODO: make a difference between armv7 and armv5?
 
-thumb_assembly_rules = []
-def add_rule(rhs, f):
-    thumb_assembly_rules.append(('instruction', rhs, f))
+
+class ThumbAssembler(BaseAssembler):
+    def __init__(self, target):
+        super().__init__(target)
+        self.make_parser()
 
 
 class ThumbTarget(Target):
@@ -45,6 +48,8 @@
         self.add_lowering(Lsl, lambda im: Lsl(im.src[0], im.src[1]))
         self.add_lowering(Cmp, lambda im: Cmp(im.src[0], im.src[1]))
 
+        self.assembler = ThumbAssembler(self)
+
     def add_rules(self):
 
         # Add instructions:
--- a/python/zcc.py	Fri Apr 18 13:08:45 2014 +0200
+++ b/python/zcc.py	Sat Apr 26 17:41:56 2014 +0200
@@ -1,16 +1,13 @@
 #!/usr/bin/env python
 
 import sys
-import os
 import argparse
 import logging
 
 from ppci.tasks import TaskRunner
-import ppci.buildtasks
 from ppci.report import RstFormatter
-from ppci.objectfile import ObjectFile
-from ppci.target.target_list import targets, targetnames
 from ppci.recipe import RecipeLoader
+import ppci.buildtasks # Include not used, but it registers build tasks.
 import ppci
 
 
--- a/readme.rst	Fri Apr 18 13:08:45 2014 +0200
+++ b/readme.rst	Sat Apr 26 17:41:56 2014 +0200
@@ -68,3 +68,8 @@
 http://www.ohloh.net/p/lcfos
 
 
+Live demo is at redhat openshift:
+
+http://lcfos-windel.rhcloud.com/
+
+
--- a/test/runtests.sh	Fri Apr 18 13:08:45 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,19 +0,0 @@
-#!/usr/bin/env bash
-
-export PYTHONPATH=$PYTHONPATH:`pwd`/../python:`pwd`/../python/ide
-
-if [ "$1" == "loop" ]
-then
-  DIR=..
-  while :; do
-    python -m unittest
-    #python -m unittest -v
-    echo "Awaiting changes in $DIR"
-    inotifywait -r -e modify $DIR
-  done
-else
-  set -e
-  python -m unittest
-fi
-
-
--- a/test/testarmasm.py	Fri Apr 18 13:08:45 2014 +0200
+++ b/test/testarmasm.py	Sat Apr 26 17:41:56 2014 +0200
@@ -1,13 +1,10 @@
 import unittest
 from ppci.outstream import BinaryOutputStream
 from ppci.objectfile import ObjectFile
-from asm import Assembler
 from testasm import AsmTestCaseBase
 from ppci.target.target_list import arm_target
 
 
-a = Assembler(arm_target)
-
 class ArmAssemblerTestCase(AsmTestCaseBase):
     """ ARM-mode (not thumb-mode) instruction assembly test case """
     def setUp(self):
@@ -15,7 +12,7 @@
         self.obj = ObjectFile()
         self.ostream = BinaryOutputStream(self.obj)
         self.ostream.select_section('.text')
-        self.a = a
+        self.assembler = arm_target.assembler
 
     def testMovImm(self):
         self.feed('mov r4, 100')
@@ -69,17 +66,17 @@
         self.check('3092a0e1 3846a0e1')
 
     def testBranches(self):
-        self.feed('b sjakie')
-        self.feed('ble sjakie')
-        self.feed('bgt sjakie')
-        self.feed('beq sjakie')
-        self.feed('bl sjakie')
-        self.feed('sjakie:')
-        self.feed('b sjakie')
-        self.feed('ble sjakie')
-        self.feed('bgt sjakie')
-        self.feed('beq sjakie')
-        self.feed('bl sjakie')
+        self.feed("""b sjakie
+        ble sjakie
+        bgt sjakie
+        beq sjakie
+        bl sjakie
+        sjakie:
+        b sjakie
+        ble sjakie
+        bgt sjakie
+        beq sjakie
+        bl sjakie""")
         self.check('030000ea 020000da 010000ca 0000000a ffffffeb feffffea fdffffda fcffffca fbffff0a faffffeb')
 
     def testPush(self):
@@ -116,12 +113,16 @@
         self.feed('adr r1, cval')
         self.check('04508fe2 00908fe2 04804fe2 08b04fe2 0cc04fe2 10104fe2')
 
-    @unittest.skip('Too hard')
     def testLdrLabelAddress(self):
         self.feed('ldr r8, =a')
         self.feed('a:')
-        self.feed('dcd 6677')
-        self.check('00801fe5 151a0000 04000000')
+        self.check('04801fe5 04000000')
+
+    def testLdrLabelAddressAt10000(self):
+        """ Link code at 0x10000 and check if symbol was correctly patched """
+        self.feed('ldr r8, =a')
+        self.feed('a:')
+        self.check('04801fe5 04000100', {'.text':0x10000})
 
     def testCmp(self):
         self.feed('cmp r4, r11')
--- a/test/testasm.py	Fri Apr 18 13:08:45 2014 +0200
+++ b/test/testasm.py	Sat Apr 26 17:41:56 2014 +0200
@@ -2,11 +2,11 @@
 
 import unittest
 from ppci import CompilerError
-from ppci.assembler import tokenize, Assembler, Lexer
+from ppci.assembler import tokenize
 from ppci.objectfile import ObjectFile
-from ppci.linker import Linker
 from ppci.outstream import BinaryOutputStream
 from ppci.target.basetarget import Label
+from ppci.buildfunctions import link
 
 
 class AssemblerLexingCase(unittest.TestCase):
@@ -34,64 +34,6 @@
             list(tokenize(asmline, []))
 
 
-class AssemblerParsingTestCase(unittest.TestCase):
-    """
-        Tests the assembler parts
-    """
-    def setUp(self):
-        self.skipTest('refactoring asm parser')
-        self.parser = asmParser
-        self.stack = []
-
-    def emit(self, x):
-        self.stack.append(x)
-
-    def parse_line(self, line):
-        self.parser.parse(Lexer(line), self.emit)
-
-    def testParse(self):
-        asmline = 'lab1: mov rax, rbx'
-        self.parse_line(asmline)
-
-    def expectTree(self, asmline, stack):
-        self.parse_line(asmline)
-        self.assertSequenceEqual(stack, self.stack)
-
-    def testParse2(self):
-        asmline = 'a: mov rax, [rbx + 2]'
-        output = []
-        output.append(ALabel('a'))
-        output.append(AInstruction('mov', [ASymbol('rax'), AUnop('[]', ASymbol('rbx') + ANumber(2))]))
-        self.expectTree(asmline, output)
-
-    def testParse3(self):
-        # A label must be optional:
-        asmline = 'mov rax, 1'
-        output = [AInstruction('mov', [ASymbol('rax'), ANumber(1)])]
-        self.expectTree(asmline, output)
-
-    def testParse4(self):
-        # Test 3 operands:
-        asmline = 'add rax, [4*rbx + 22], rcx'
-        ops = []
-        ops.append(ASymbol('rax'))
-        ops.append(AUnop('[]', ANumber(4) * ASymbol('rbx') + ANumber(22)))
-        ops.append(ASymbol('rcx'))
-        output = [AInstruction('add', ops)]
-        self.expectTree(asmline, output)
-
-    def testParse5(self):
-        # An instruction must be optional:
-        asmline = 'lab1:'
-        output = []
-        output.append(ALabel('lab1'))
-        self.expectTree(asmline, output)
-
-    def testParse6(self):
-        # A line can be empty
-        self.parse_line('')
-
-
 class OustreamTestCase(unittest.TestCase):
     def test1(self):
         obj = ObjectFile()
@@ -104,11 +46,11 @@
 class AsmTestCaseBase(unittest.TestCase):
     """ Base testcase for assembly """
     def feed(self, line):
-        self.a.assemble(line, self.ostream)
+        self.assembler.assemble(line, self.ostream)
 
-    def check(self, hexstr):
-        l = Linker()
-        self.obj = l.link([self.obj])
+    def check(self, hexstr, layout={}):
+        self.assembler.flush()
+        self.obj = link([self.obj], layout)
         data = bytes(self.obj.get_section('.text').data)
         self.assertSequenceEqual(bytes.fromhex(hexstr), data)
 
--- a/test/testbintools.py	Fri Apr 18 13:08:45 2014 +0200
+++ b/test/testbintools.py	Sat Apr 26 17:41:56 2014 +0200
@@ -7,6 +7,7 @@
 from ppci import CompilerError
 from ppci.tasks import TaskRunner, TaskError
 from ppci.buildtasks import EmptyTask
+from ppci.buildfunctions import link
 
 
 class TaskTestCase(unittest.TestCase):
@@ -53,16 +54,14 @@
 
 class LinkerTestCase(unittest.TestCase):
     def testUndefinedReference(self):
-        l = Linker()
         o1 = ObjectFile()
         o1.get_section('.text')
         o1.add_relocation('undefined_sym', 0, 'rel8', '.text')
         o2 = ObjectFile()
         with self.assertRaises(CompilerError):
-            o3 = l.link([o1, o2])
+            o3 = link([o1, o2], {})
 
     def testDuplicateSymbol(self):
-        l = Linker()
         o1 = ObjectFile()
         o1.get_section('.text')
         o1.add_symbol('a', 0, '.text')
@@ -70,33 +69,30 @@
         o2.get_section('.text')
         o2.add_symbol('a', 0, '.text')
         with self.assertRaises(CompilerError):
-            o3 = l.link([o1, o2])
+            o3 = link([o1, o2], {})
 
     def testRel8Relocation(self):
-        l = Linker()
         o1 = ObjectFile()
         o1.get_section('.text').add_data(bytes([0]*100))
         o1.add_relocation('a', 0, 'rel8', '.text')
         o2 = ObjectFile()
         o2.get_section('.text').add_data(bytes([0]*100))
         o2.add_symbol('a', 24, '.text')
-        o3 = l.link([o1, o2])
+        o3 = link([o1, o2], {})
 
     def testSymbolValues(self):
-        l = Linker()
         o1 = ObjectFile()
         o1.get_section('.text').add_data(bytes([0]*108))
         o1.add_symbol('b', 24, '.text')
         o2 = ObjectFile()
         o2.get_section('.text').add_data(bytes([0]*100))
         o2.add_symbol('a', 2, '.text')
-        o3 = l.link([o1, o2])
+        o3 = link([o1, o2], {})
         self.assertEqual(110, o3.find_symbol('a').value)
         self.assertEqual(24, o3.find_symbol('b').value)
         self.assertEqual(208, o3.get_section('.text').Size)
 
     def testMemoryLayout(self):
-        l = Linker()
         memory_layout = {'.text': 0x08000000, '.data':0x20000000}
         o1 = ObjectFile()
         o1.get_section('.text').add_data(bytes([0]*108))
@@ -106,7 +102,7 @@
         o2.get_section('.data').add_data(bytes([0]*100))
         o2.add_symbol('a', 2, '.data')
         o2.add_symbol('c', 2, '.text')
-        o3 = l.link([o1, o2], layout=memory_layout)
+        o3 = link([o1, o2], memory_layout)
         self.assertEqual(0x20000000+2, o3.find_symbol('a').value)
         self.assertEqual(0x08000000+24, o3.find_symbol('b').value)
         self.assertEqual(0x08000000+110, o3.find_symbol('c').value)
--- a/test/testmsp430asm.py	Fri Apr 18 13:08:45 2014 +0200
+++ b/test/testmsp430asm.py	Sat Apr 26 17:41:56 2014 +0200
@@ -1,13 +1,11 @@
 #!/usr/bin/python
 
 import unittest
-from ppci.assembler import tokenize, Assembler
 from ppci.objectfile import ObjectFile
 from ppci.outstream import BinaryOutputStream
 from ppci.target.target_list import msp430target
 from testasm import AsmTestCaseBase
 
-a = Assembler(msp430target)
 
 class Msp430AssemblerTestCase(AsmTestCaseBase):
     def setUp(self):
@@ -15,7 +13,7 @@
         self.obj = ObjectFile()
         self.ostream = BinaryOutputStream(self.obj)
         self.ostream.select_section('.text')
-        self.a = a
+        self.assembler = msp430target.assembler
 
     def testMov(self):
         self.feed("mov r14, r15")
--- a/test/testsamples.py	Fri Apr 18 13:08:45 2014 +0200
+++ b/test/testsamples.py	Sat Apr 26 17:41:56 2014 +0200
@@ -12,6 +12,18 @@
 B local_loop
 """
 
+modarchcode = """
+module arch;
+
+function void putc(int c)
+{
+    var int *UART0DR;
+    UART0DR = cast<int*>(0x10009000); // UART0 DR register
+    *UART0DR = c;
+}
+
+"""
+
 class Samples:
     def testPrint(self):
         snippet = """
@@ -122,7 +134,7 @@
         o1 = assemble(io.StringIO(startercode), 'arm')
         o2 = c3compile([
             relpath('..', 'kernel', 'src', 'io.c3'),
-            relpath('..', 'kernel', 'arch', 'vexpressA9.c3'),
+            io.StringIO(modarchcode),
             io.StringIO(src)], [], 'arm')
         layout = {'code': 0x10000, 'data': 0x20000}
         o3 = link([o1, o2], layout)
--- a/test/testthumbasm.py	Fri Apr 18 13:08:45 2014 +0200
+++ b/test/testthumbasm.py	Sat Apr 26 17:41:56 2014 +0200
@@ -1,11 +1,9 @@
 import unittest
 from ppci.outstream import BinaryOutputStream
 from ppci.objectfile import ObjectFile
-from asm import Assembler
 from testasm import AsmTestCaseBase
 from ppci.target.target_list import thumb_target
 
-a = Assembler(thumb_target)
 
 class ThumbAssemblerTestCase(AsmTestCaseBase):
     def setUp(self):
@@ -13,7 +11,7 @@
         self.obj = ObjectFile()
         self.ostream = BinaryOutputStream(self.obj)
         self.ostream.select_section('.text')
-        self.a = a #
+        self.assembler = thumb_target.assembler
 
     def testMovImm8(self):
         self.feed('mov r4, 100')
@@ -148,4 +146,3 @@
         self.feed('b henkie')
         self.check('32b41519 94420198 049332bc a340ab42 f6d0f5d1 f4e7')
 
-
--- a/util/test_patterns.txt	Fri Apr 18 13:08:45 2014 +0200
+++ b/util/test_patterns.txt	Sat Apr 26 17:41:56 2014 +0200
@@ -59,5 +59,4 @@
 ===
 ldr r8, =a
 a:
-.word 6677