changeset 393:6ae782a085e0

Added init program
author Windel Bouwman
date Sat, 17 May 2014 21:17:40 +0200
parents bb4289c84907
children 988f3fb861e4
files kernel/build.xml kernel/src/kernel.c3 kernel/src/memory.c3 kernel/src/process.c3 kernel/src/schedule.c3 kernel/src/syscall.c3 python/ppci/buildfunctions.py python/ppci/buildtasks.py python/ppci/c3/astnodes.py python/ppci/c3/codegenerator.py python/ppci/c3/lexer.py python/ppci/c3/parser.py python/ppci/c3/visitor.py python/ppci/tasks.py python/zcc.py test/testc3.py user/build.xml user/hello.c3 user/init.c3 user/lib.c3
diffstat 20 files changed, 277 insertions(+), 78 deletions(-) [+]
line wrap: on
line diff
--- a/kernel/build.xml	Fri May 16 13:05:10 2014 +0200
+++ b/kernel/build.xml	Sat May 17 21:17:40 2014 +0200
@@ -4,18 +4,32 @@
     <property name="src" value="src" />
     <property name="arch" value="arm" />
 
+
     <target name="vexpress">
-        <assemble source="arch/qemu_vexpress/start.asm" target="arm" output="start.o" />
-        <compile target="arm" sources='arch/qemu_vexpress/vexpressA9.c3' includes="${src}/*.c3" output="vexp.o" />
-        <compile target="arm" sources='${src}/*.c3' output="henkie.o" />
+        <build file="../user/build.xml" />
+
+        <assemble source="arch/qemu_vexpress/start.asm"
+            target="arm" output="start.o" />
+
+        <compile target="arm"
+            sources='arch/qemu_vexpress/vexpressA9.c3'
+            includes="${src}/*.c3" output="vexp.o" />
+
+        <compile target="arm" sources='${src}/*.c3'
+            output="henkie.o" />
+
+        <!-- <script file="gen_table.py" /> -->
+
         <link output="kernel.o"
             target="arm"
             layout="arch/qemu_vexpress/layout.mmap"
             objects="henkie.o;vexp.o;start.o" />
+
         <objcopy
             objectfile="kernel.o"
             imagename="image"
             output="kernel_arm.bin" />
+
     </target>
 
 </project>
--- a/kernel/src/kernel.c3	Fri May 16 13:05:10 2014 +0200
+++ b/kernel/src/kernel.c3	Sat May 17 21:17:40 2014 +0200
@@ -6,6 +6,9 @@
 import arch;
 import io;
 
+// Globals:
+var process.process_t* init_proc;
+
 
 // Main entry point of the kernel:
 function void start()
@@ -13,15 +16,20 @@
     io.println("Welcome to lcfos!");
     arch.init();
 
-
-    // process.init();
+    process.init();
     memory.init();
 
-    //Process proc = new process:Process();
+    init_proc = process.Create();
+
+    // TODO: copy content into process??
+    // Create a second process:
+    process.Create();
+
+    io.print2("init address ", cast<int>(init_proc));
 
     //scheduler:queue(proc);
     io.println("Kernel finished");
-    arch.halt();
+    panic();
 }
 
 // Called in total stress:
--- a/kernel/src/memory.c3	Fri May 16 13:05:10 2014 +0200
+++ b/kernel/src/memory.c3	Sat May 17 21:17:40 2014 +0200
@@ -5,17 +5,33 @@
 
 var int ptr;
 
+// Let the heap grow upwards..
+
 function void init()
 {
-    ptr = 0;
-    // io.print2("ptr = ", ptr);
-    // TODO
+    ptr = 0x80000;
 }
 
-function u8* Alloc(int size)
+function u8* alloc(int size)
 {
+    var int ptr2;
+    ptr2 = ptr;
+
+    io.print2("alloc size ", size);
+    io.print2("alloc address ", ptr);
+
+    // Increment new free point:
     ptr = ptr + size;
-    return cast<u8*>(ptr);
+    return cast<u8*>(ptr2);
 }
 
+function void memcpy(u8* dst, u8* src, int size)
+{
+    //
+    var int i;
+    for (i = 0; i < size; i = i + 1)
+    {
+        *(dst + i) = *(src + i);
+    }
+}
 
--- a/kernel/src/process.c3	Fri May 16 13:05:10 2014 +0200
+++ b/kernel/src/process.c3	Sat May 17 21:17:40 2014 +0200
@@ -15,6 +15,7 @@
 
 // init is the root of all processes:
 var process_t* root_process;
+
 var int next_pid;
 
 function void init()
@@ -30,10 +31,10 @@
 function process_t* Create()
 {
     var process_t* p;
-    //TODO: implement alloc:
 
-    //= memory.Alloc(sizeof(process_t));
+    p = cast<process_t*>(memory.alloc(sizeof(process_t)));
     p->id = next_pid;
+    p->status = 0; // Ready!
     p->next = cast<process_t*>(0);
 
     // Increment PID:
@@ -52,14 +53,13 @@
         {
             parent = parent->next;
         }
+
         parent->next = p;
     }
 
     return p;
 }
 
-// function
-
 
 function void Kill(process_t* p)
 {
--- a/kernel/src/schedule.c3	Fri May 16 13:05:10 2014 +0200
+++ b/kernel/src/schedule.c3	Sat May 17 21:17:40 2014 +0200
@@ -2,6 +2,7 @@
 module scheduler;
 
 import process;
+import arch;
 
 var process.process_t *current;
 
--- a/kernel/src/syscall.c3	Fri May 16 13:05:10 2014 +0200
+++ b/kernel/src/syscall.c3	Sat May 17 21:17:40 2014 +0200
@@ -9,6 +9,8 @@
 import process;
 
 
+/* System call numbers:
+*/
 const int SendMsg = 1;
 const int ReceiveMsg = 2;
 const int Reboot = 3;
@@ -18,7 +20,7 @@
 function void handle_system_call(int callId, int a, int b)
 {
     // Main entry, check what to do here
-    if (callId == 1)
+    if (callId == SendMsg)
     {
         handle_send_msg();
         var process.process_t* proc;
--- a/python/ppci/buildfunctions.py	Fri May 16 13:05:10 2014 +0200
+++ b/python/ppci/buildfunctions.py	Sat May 17 21:17:40 2014 +0200
@@ -16,8 +16,9 @@
 from .target.target_list import targets
 from .outstream import BinaryAndLoggingStream
 from .objectfile import ObjectFile, load_object
-from . import DiagnosticsManager, CompilerError
-from .tasks import TaskError
+from . import DiagnosticsManager
+from .tasks import TaskError, TaskRunner
+from .recipe import RecipeLoader
 
 
 def fix_target(tg):
@@ -64,6 +65,23 @@
         raise TaskError('Cannot use {} as layout'.format(l))
 
 
+def construct(buildfile, targets=[]):
+    recipe_loader = RecipeLoader()
+    try:
+        project = recipe_loader.load_file(buildfile)
+    except OSError:
+        raise TaskError('Could not construct {}'.format(buildfile))
+        project = None
+
+    if project:
+        runner = TaskRunner()
+        res = runner.run(project, targets)
+    else:
+        res = 1
+
+    return res
+
+
 def assemble(source, target):
     """ Invoke the assembler on the given source, returns an object containing
         the output. """
@@ -100,7 +118,7 @@
             # Something went wrong, do not continue the code generation
             continue
 
-        d = {'ircode':ircode}
+        d = {'ircode': ircode}
         logger.debug('Verifying code {}'.format(ircode), extra=d)
         Verifier().verify(ircode)
 
@@ -113,7 +131,7 @@
         Verifier().verify(ircode)
 
         # Code generation:
-        d = {'ircode':ircode}
+        d = {'ircode': ircode}
         logger.debug('Starting code generation for {}'.format(ircode), extra=d)
 
         cg.generate(ircode, output_stream)
@@ -132,4 +150,3 @@
     linker = Linker(target)
     output_obj = linker.link(objects, layout)
     return output_obj
-
--- a/python/ppci/buildtasks.py	Fri May 16 13:05:10 2014 +0200
+++ b/python/ppci/buildtasks.py	Sat May 17 21:17:40 2014 +0200
@@ -4,10 +4,8 @@
 Task can depend upon one another.
 """
 
-import logging
-
 from .tasks import Task, TaskError, register_task
-from .buildfunctions import c3compile, link, assemble, fix_object
+from .buildfunctions import c3compile, link, assemble, fix_object, construct
 from pyyacc import ParserException
 from . import CompilerError
 
@@ -36,9 +34,17 @@
         self.target.project.set_property(name, value)
 
 
+@register_task("build")
+class ConstructTask(Task):
+    """ Builds another build description file (build.xml) """
+    def run(self):
+        project = self.get_argument('file')
+        construct(project)
+
+
 @register_task("assemble")
 class AssembleTask(Task):
-    """ Task that can runs the assembler over the source and enters the 
+    """ Task that can runs the assembler over the source and enters the
         output into an object file """
 
     def run(self):
@@ -73,8 +79,8 @@
 
         output = c3compile(sources, includes, target)
         # Store output:
-        with open(output_filename, 'w') as f:
-            output.save(f)
+        with open(output_filename, 'w') as output_file:
+            output.save(output_file)
 
 
 @register_task("link")
@@ -92,8 +98,8 @@
             raise TaskError(e.msg)
 
         # Store output:
-        with open(output_filename, 'w') as f:
-            output_obj.save(f)
+        with open(output_filename, 'w') as output_file:
+            output_obj.save(output_file)
 
 
 @register_task("objcopy")
@@ -105,6 +111,5 @@
 
         obj = fix_object(object_filename)
         image = obj.get_image(image_name)
-        with open(output_filename, 'wb') as f:
-            f.write(image)
-
+        with open(output_filename, 'wb') as output_file:
+            output_file.write(image)
--- a/python/ppci/c3/astnodes.py	Fri May 16 13:05:10 2014 +0200
+++ b/python/ppci/c3/astnodes.py	Sat May 17 21:17:40 2014 +0200
@@ -207,6 +207,12 @@
         self.loc = loc
 
 
+class Sizeof(Expression):
+    def __init__(self, typ, loc):
+        super().__init__(loc)
+        self.query_typ = typ
+
+
 class Deref(Expression):
     def __init__(self, ptr, loc):
         super().__init__(loc)
@@ -380,6 +386,15 @@
         return 'IF-statement'
 
 
+class Switch(Statement):
+    def __init__(self, condition, loc):
+        super().__init__(loc)
+        self.condition = condition
+
+    def __repr__(self):
+        return 'Switch on {}'.format(self.condition)
+
+
 class While(Statement):
     def __init__(self, condition, statement, loc):
         super().__init__(loc)
--- a/python/ppci/c3/codegenerator.py	Fri May 16 13:05:10 2014 +0200
+++ b/python/ppci/c3/codegenerator.py	Sat May 17 21:17:40 2014 +0200
@@ -113,8 +113,8 @@
         elif type(code) is ast.Empty:
             pass
         elif type(code) is ast.Assignment:
-            lval = self.genExprCode(code.lval)
-            rval = self.genExprCode(code.rval)
+            lval = self.gen_expr_code(code.lval)
+            rval = self.gen_expr_code(code.rval)
             if not self.equal_types(code.lval.typ, code.rval.typ):
                 raise SemanticError('Cannot assign {} to {}'
                                     .format(code.rval.typ, code.lval.typ),
@@ -124,7 +124,7 @@
                                     code.lval.loc)
             self.emit(ir.Move(lval, rval))
         elif type(code) is ast.ExpressionStatement:
-            self.emit(ir.Exp(self.genExprCode(code.ex)))
+            self.emit(ir.Exp(self.gen_expr_code(code.ex)))
         elif type(code) is ast.If:
             bbtrue = self.newBlock()
             bbfalse = self.newBlock()
@@ -138,7 +138,7 @@
             self.emit(ir.Jump(te))
             self.setBlock(te)
         elif type(code) is ast.Return:
-            re = self.genExprCode(code.expr)
+            re = self.gen_expr_code(code.expr)
             self.emit(ir.Move(self.fn.return_value, re))
             self.emit(ir.Jump(self.fn.epiloog))
             b = self.newBlock()
@@ -167,6 +167,8 @@
             self.genCode(code.final)
             self.emit(ir.Jump(bbtest))
             self.setBlock(te)
+        elif type(code) is ast.Switch:
+            raise NotImplementedError('Unknown stmt {}'.format(code))
         else:
             raise NotImplementedError('Unknown stmt {}'.format(code))
 
@@ -193,8 +195,8 @@
                 if not self.equal_types(expr.b.typ, self.boolType):
                     raise SemanticError('Must be boolean', expr.b.loc)
             elif expr.op in ['==', '>', '<', '!=', '<=', '>=']:
-                ta = self.genExprCode(expr.a)
-                tb = self.genExprCode(expr.b)
+                ta = self.gen_expr_code(expr.a)
+                tb = self.gen_expr_code(expr.b)
                 if not self.equal_types(expr.a.typ, expr.b.typ):
                     raise SemanticError('Types unequal {} != {}'
                                         .format(expr.a.typ, expr.b.typ),
@@ -204,7 +206,7 @@
                 raise SemanticError('non-bool: {}'.format(expr.op), expr.loc)
             expr.typ = self.boolType
         elif type(expr) is ast.Literal:
-            self.genExprCode(expr)
+            self.gen_expr_code(expr)
             if expr.val:
                 self.emit(ir.Jump(bbtrue))
             else:
@@ -216,17 +218,21 @@
         if not self.equal_types(expr.typ, self.boolType):
             self.error('Condition must be boolean', expr.loc)
 
-    def genExprCode(self, expr):
+    def gen_expr_code(self, expr):
         """ Generate code for an expression. Return the generated ir-value """
         assert isinstance(expr, ast.Expression)
         if type(expr) is ast.Binop:
             expr.lvalue = False
             if expr.op in ['+', '-', '*', '/', '<<', '>>', '|', '&']:
-                ra = self.genExprCode(expr.a)
-                rb = self.genExprCode(expr.b)
+                ra = self.gen_expr_code(expr.a)
+                rb = self.gen_expr_code(expr.b)
                 if self.equal_types(expr.a.typ, self.intType) and \
                         self.equal_types(expr.b.typ, self.intType):
                     expr.typ = expr.a.typ
+                elif self.equal_types(expr.b.typ, self.intType) and \
+                        type(expr.a.typ) is ast.PointerType:
+                    # Special case for pointer arithmatic TODO: coerce!
+                    expr.typ = expr.a.typ
                 else:
                     raise SemanticError('Can only add integers', expr.loc)
             else:
@@ -234,7 +240,7 @@
             return ir.Binop(ra, expr.op, rb)
         elif type(expr) is ast.Unop:
             if expr.op == '&':
-                ra = self.genExprCode(expr.a)
+                ra = self.gen_expr_code(expr.a)
                 expr.typ = ast.PointerType(expr.a.typ)
                 if not expr.a.lvalue:
                     raise SemanticError('No valid lvalue', expr.a.loc)
@@ -253,13 +259,13 @@
                 expr.lvalue = True
                 return ir.Mem(self.varMap[tg])
             elif isinstance(tg, ast.Constant):
-                c_val = self.genExprCode(tg.value)
+                c_val = self.gen_expr_code(tg.value)
                 return self.evalConst(c_val)
             else:
                 raise NotImplementedError(str(tg))
         elif type(expr) is ast.Deref:
             # dereference pointer type:
-            addr = self.genExprCode(expr.ptr)
+            addr = self.gen_expr_code(expr.ptr)
             ptr_typ = self.the_type(expr.ptr.typ)
             expr.lvalue = True
             if type(ptr_typ) is ast.PointerType:
@@ -268,7 +274,7 @@
             else:
                 raise SemanticError('Cannot deref non-pointer', expr.loc)
         elif type(expr) is ast.Member:
-            base = self.genExprCode(expr.base)
+            base = self.gen_expr_code(expr.base)
             expr.lvalue = expr.base.lvalue
             basetype = self.the_type(expr.base.typ)
             if type(basetype) is ast.StructureType:
@@ -288,8 +294,8 @@
             return ir.Mem(ir.Add(base.e, offset))
         elif type(expr) is ast.Index:
             """ Array indexing """
-            base = self.genExprCode(expr.base)
-            idx = self.genExprCode(expr.i)
+            base = self.gen_expr_code(expr.base)
+            idx = self.gen_expr_code(expr.i)
             base_typ = self.the_type(expr.base.typ)
             if not isinstance(base_typ, ast.ArrayType):
                 raise SemanticError('Cannot index non-array type {}'
@@ -325,6 +331,12 @@
                 return ir.Const(expr.val)
         elif type(expr) is ast.TypeCast:
             return self.gen_type_cast(expr)
+        elif type(expr) is ast.Sizeof:
+            # The type of this expression is int:
+            expr.typ = self.intType
+            self.check_type(expr.query_typ)
+            type_size = self.size_of(expr.query_typ)
+            return ir.Const(type_size)
         elif type(expr) is ast.FunctionCall:
             return self.gen_function_call(expr)
         else:
@@ -338,7 +350,7 @@
 
     def gen_type_cast(self, expr):
         """ Generate code for type casting """
-        ar = self.genExprCode(expr.a)
+        ar = self.gen_expr_code(expr.a)
         from_type = self.the_type(expr.a.typ)
         to_type = self.the_type(expr.to_type)
         if isinstance(from_type, ast.PointerType) and \
@@ -364,7 +376,7 @@
     def gen_function_call(self, expr):
         """ Generate code for a function call """
         # Evaluate the arguments:
-        args = [self.genExprCode(e) for e in expr.args]
+        args = [self.gen_expr_code(e) for e in expr.args]
         # Check arguments:
         tg = self.resolveSymbol(expr.proc)
         if type(tg) is not ast.Function:
--- a/python/ppci/c3/lexer.py	Fri May 16 13:05:10 2014 +0200
+++ b/python/ppci/c3/lexer.py	Sat May 17 21:17:40 2014 +0200
@@ -8,8 +8,9 @@
 
 keywords = ['and', 'or', 'not', 'true', 'false',
             'else', 'if', 'while', 'for', 'return',
+            'switch', 'case', 'default',
             'function', 'var', 'type', 'const',
-            'struct', 'cast',
+            'struct', 'cast', 'sizeof',
             'import', 'module']
 
 
--- a/python/ppci/c3/parser.py	Fri May 16 13:05:10 2014 +0200
+++ b/python/ppci/c3/parser.py	Sat May 17 21:17:40 2014 +0200
@@ -5,7 +5,7 @@
 from .astnodes import Return, While, If, Empty, For
 from .astnodes import FunctionType, Function, FormalParameter
 from .astnodes import StructureType, DefinedType, PointerType, ArrayType
-from .astnodes import Constant, Variable
+from .astnodes import Constant, Variable, Sizeof
 from .astnodes import StructField, Deref, Index
 from .astnodes import Package
 from .astnodes import Identifier
@@ -227,6 +227,13 @@
         no = self.Statement() if self.hasConsumed('else') else Empty()
         return If(condition, yes, no, loc)
 
+    def parse_switch(self):
+        loc = self.Consume('switch').loc
+        self.Consume('(')
+        condition = self.Expression()
+        self.Consume(')')
+        return Switch(condition, loc)
+
     def parseWhile(self):
         loc = self.Consume('while').loc
         self.Consume('(')
@@ -271,6 +278,8 @@
             return self.parseWhile()
         elif self.Peak == 'for':
             return self.parse_for()
+        elif self.Peak == 'switch':
+            return self.parse_switch()
         elif self.Peak == '{':
             return self.parseCompound()
         elif self.hasConsumed(';'):
@@ -376,9 +385,18 @@
             ce = self.Expression()
             self.Consume(')')
             return TypeCast(t, ce, loc)
+        elif self.Peak == 'sizeof':
+            return self.sizeof_expression()
         else:
             return self.UnaryExpression()
 
+    def sizeof_expression(self):
+        loc = self.Consume('sizeof').loc
+        self.Consume('(')
+        typ = self.parse_type_spec()
+        self.Consume(')')
+        return Sizeof(typ, loc)
+
     def UnaryExpression(self):
         if self.Peak in ['&', '*']:
             op = self.Consume(self.Peak)
--- a/python/ppci/c3/visitor.py	Fri May 16 13:05:10 2014 +0200
+++ b/python/ppci/c3/visitor.py	Sat May 17 21:17:40 2014 +0200
@@ -60,6 +60,8 @@
         elif type(node) is TypeCast:
             self.do(node.a)
             self.do(node.to_type)
+        elif type(node) is Sizeof:
+            self.do(node.query_typ)
         elif type(node) is Member:
             self.do(node.base)
         elif type(node) is Index:
--- a/python/ppci/tasks.py	Fri May 16 13:05:10 2014 +0200
+++ b/python/ppci/tasks.py	Sat May 17 21:17:40 2014 +0200
@@ -25,7 +25,7 @@
 
 
 class Project:
-    """ A project contains a set of named targets that can depend upon 
+    """ A project contains a set of named targets that can depend upon
         eachother """
     def __init__(self, name):
         self.name = name
@@ -67,7 +67,8 @@
         target = self.get_target(target_name)
         for dep in target.dependencies:
             if dep in state:
-                raise TaskError('Dependency loop detected {} -> {}'.format(target_name, dep))
+                raise TaskError('Dependency loop detected {} -> {}'
+                                .format(target_name, dep))
             self.dfs(dep, state)
 
     def check_target(self, target_name):
@@ -195,4 +196,3 @@
             self.logger.error(str(e.msg))
             return 1
         return 0
-
--- a/python/zcc.py	Fri May 16 13:05:10 2014 +0200
+++ b/python/zcc.py	Sat May 17 21:17:40 2014 +0200
@@ -4,10 +4,9 @@
 import argparse
 import logging
 
-from ppci.tasks import TaskRunner
 from ppci.report import RstFormatter
-from ppci.recipe import RecipeLoader
-import ppci.buildtasks # Include not used, but it registers build tasks.
+from ppci.buildfunctions import construct
+import ppci.buildtasks  # Include not used, but it registers build tasks.
 import ppci
 
 
@@ -48,14 +47,7 @@
         fh.setFormatter(RstFormatter())
         logging.getLogger().addHandler(fh)
 
-    recipe_loader = RecipeLoader()
-    try:
-        project = recipe_loader.load_file(args.buildfile)
-    except OSError as e:
-        res = 1
-
-    runner = TaskRunner()
-    res = runner.run(project, args.targets)
+    res = construct(args.buildfile, args.targets)
 
     if args.report:
         logging.getLogger().removeHandler(fh)
--- a/test/testc3.py	Fri May 16 13:05:10 2014 +0200
+++ b/test/testc3.py	Sat May 17 21:17:40 2014 +0200
@@ -392,7 +392,7 @@
          module testarray;
          function void t()
          {
-            int c[20];
+            var int c[20];
          }
         """
         self.expectErrors(snippet, [7])
@@ -418,12 +418,48 @@
             print(a);
             print("Moi");
          }
+
          function void print(string a)
          {
          }
         """
         self.expectOK(snippet)
 
+    def testSizeof1(self):
+        snippet = """
+         module testsizeof;
+
+         function void t()
+         {
+            var int a;
+            a = sizeof(int*);
+         }
+        """
+        self.expectOK(snippet)
+
+    def testSizeof2(self):
+        snippet = """
+         module testsizeof2;
+
+         function void t()
+         {
+            sizeof(int*) = 2;
+         }
+        """
+        self.expectErrors(snippet, [6])
+
+    @unittest.skip('TODO: Too hard')
+    def testWrongVarUse(self):
+        snippet = """
+         module testsizeof;
+
+         function void t()
+         {
+            int a = 1;
+         }
+        """
+        self.expectOK(snippet)
+
     def testPointerType1(self):
         snippet = """
          module testpointer1;
@@ -494,6 +530,17 @@
         """
         self.expectOK(snippet)
 
+    def testPointerArithmatic(self):
+        snippet = """
+         module testpointerarithmatic;
+         function void t()
+         {
+            var int* pa;
+            *(pa+2) = 2;
+         }
+        """
+        self.expectOK(snippet)
+
     def testWrongCast(self):
         snippet = """
          module testptr_ir;
--- a/user/build.xml	Fri May 16 13:05:10 2014 +0200
+++ b/user/build.xml	Sat May 17 21:17:40 2014 +0200
@@ -1,12 +1,32 @@
 
 <project name="Userspace" default="all">
-   <target name="all" depends="hello">
-   </target>
-   <target name="hello">
-     <compile sources="lib.c3;ipc.c3;hello.c3" target="arm" output="hello.o"/>
-     <link output="hello.bin" layout="app.mmap"
-        target="arm"
-        objects="hello.o"/>
+    <target name="all" depends="hello,init">
+    </target>
+
+    <target name="lib">
+        <compile sources="lib.c3;ipc.c3" target="arm" output="lib.o"/>
+    </target>
+
+    <target name="hello" depends="lib">
+        <compile sources="hello.c3"
+                 includes="lib.c3;ipc.c3"
+                 target="arm"
+                 output="hello.o"/>
+        <link output="hello" layout="app.mmap"
+            target="arm"
+            objects="hello.o;lib.o"/>
+    </target>
+
+    <target name="init">
+        <compile sources="init.c3"
+            includes="lib.c3;ipc.c3"
+            target="arm"
+            output="init.o"/>
+        <link
+            output="init"
+            layout="app.mmap"
+            target="arm"
+            objects="init.o;lib.o"/>
     </target>
 </project>
 
--- a/user/hello.c3	Fri May 16 13:05:10 2014 +0200
+++ b/user/hello.c3	Sat May 17 21:17:40 2014 +0200
@@ -7,6 +7,6 @@
 
 function void start()
 {
-    lib.print(9); // 'Hello space');
+    lib.print("Hello space");
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/user/init.c3	Sat May 17 21:17:40 2014 +0200
@@ -0,0 +1,12 @@
+module init;
+import lib;
+
+/*
+Initial program run by the system.
+*/
+
+function void start()
+{
+    lib.print("Welcome to lcfos");
+}
+
--- a/user/lib.c3	Fri May 16 13:05:10 2014 +0200
+++ b/user/lib.c3	Sat May 17 21:17:40 2014 +0200
@@ -3,14 +3,31 @@
 
 /*
 Runtime library.
-
 */
 
-function void print(int txt)
+// Hack until something better exists:
+function void putc(int c)
+{
+    var int *UART0DR;
+    UART0DR = cast<int*>(0x109000); // UART0 DR register when remapped at 1MB
+    *UART0DR = c;
+}
+
+function void print(string txt)
 {
     // TODO
     var ipc.Msg msg;
     ipc.SendMessage(&msg);
+
+    // TBD: send text to putc or via send message??
+    var int i;
+    i = 0;
+
+    while (i < txt->len)
+    {
+        putc(cast<int>(txt->txt[i]));
+        i = i + 1;
+    }
 }