diff --git a/Sujet.pdf b/Sujet.pdf
index 35aab09ee25abd86410640e5dbd04397de626f09..3f1222d0362a2e74c1a5e13ded8a8368fb4d15e8 100644
Binary files a/Sujet.pdf and b/Sujet.pdf differ
diff --git a/tests/invader/cep_platform.h b/tests/invader/cep_platform.h
new file mode 100644
index 0000000000000000000000000000000000000000..2bf145ddeea72aacbbadca87ddd129a65dd7db0f
--- /dev/null
+++ b/tests/invader/cep_platform.h
@@ -0,0 +1,53 @@
+#ifndef __CEP_PLATFORM_H__
+#define __CEP_PLATFORM_H__ 
+
+// Peripherals Addresses
+#define PERIPHS_BASE_ADDRESS            0x30000000
+#define REG_LEDS_ADDR                   (PERIPHS_BASE_ADDRESS + 0x0)
+#define REG_LEDS_CTRL                   (PERIPHS_BASE_ADDRESS + 0x4)
+#define REG_PIN_ADDR                    (PERIPHS_BASE_ADDRESS + 0x8)
+#define REG_PUSHBUTTON_CTL_ADDR         (PERIPHS_BASE_ADDRESS + 0xC)
+
+// VRAM addresses
+#define FRAME_BUFFER_CTRL_BASE_ADDRESS  0x70000000
+#define FRAME_BUFFER_CTRL_MODE_REG      (FRAME_BUFFER_CTRL_BASE_ADDRESS + 0x0)
+#define FRAME_BUFFER_CTRL_ADDR_REG      (FRAME_BUFFER_CTRL_BASE_ADDRESS + 0x4)
+#define VRAM_OFFSET                     0x80000000
+
+// PLIC registers addresses
+#define PLIC_PENDING_1                  0x0C001000
+#define PLIC_ENABLE_1                   0x0c002000
+#define PLIC_IRQ_CLAIM                  0x0c200004
+
+// DEPRECATED: Gestion des priorités du PLIC a été retirée de la plateforme cep dans qemu
+//#define PLIC_THRESHOLD                  0x0c200000
+//#define PLIC_IRQ_PRIORITY_BASE          0x0c000000
+//#define PLIC_IRQ_PRIORITY_1             0x0c000004
+
+// CLINT registers addresses
+#define CLINT_MSIP                      0x02000000
+#define CLINT_TIMER_CMP                 0x02004000
+#define CLINT_TIMER_CMP_HI              0x02004004
+#define CLINT_TIMER_CMP_LO              0x02004000
+#define CLINT_TIMER                     0x0200bff8
+#define CLINT_TIMER_HI                  0x0200bffc
+#define CLINT_TIMER_LOW                 0x0200bff8
+
+// VRAM sizes
+#define VRAM_WIDTH                      1280
+#define VRAM_HEIGTH                     720
+#define PIXEL_SIZE                      4
+#define VRAM_SIZE                       (VRAM_WIDTH * VRAM_HEIGTH * PIXEL_SIZE)
+
+// Push button modes
+#define REG_PUSHBUTTON_MODE_POLL        0x0
+#define REG_PUSHBUTTON_MODE_INT         0x1
+
+// HDMI Modes
+#define HDMI_MODE_720p_60Hz             4
+#define HDMI_MODE_1080p_60Hz            19
+#define HDMI_MODE_1080p_30Hz_blk_0      32
+#define HDMI_MODE_1080p_30Hz_blk_1      33
+#define HDMI_MODE_1080p_30Hz_blk_2      34
+
+#endif // __CEP_PLATFORM_H__
diff --git a/tests/invader/constants.s b/tests/invader/constants.s
new file mode 100644
index 0000000000000000000000000000000000000000..d08387c2bc7b951a95ba0dc5e4147ee7fbdbf0f4
--- /dev/null
+++ b/tests/invader/constants.s
@@ -0,0 +1,18 @@
+# See LICENSE for license details.
+
+.equ MAX_HARTS,    4
+.equ SAVE_REGS,    16
+.equ STACK_SIZE,   1024
+.equ STACK_SHIFT,  10
+.equ CONTEXT_SIZE, (SAVE_REGS * REGBYTES)
+
+.globl _text_start
+.globl _text_end
+.globl _rodata_start
+.globl _rodata_end
+.globl _data_start
+.globl _data_end
+.globl _bss_start
+.globl _bss_end
+.global _memory_start;
+.global _memory_end;
diff --git a/tests/invader/crt.s b/tests/invader/crt.s
new file mode 100644
index 0000000000000000000000000000000000000000..dcdf1e15619b966fe805452be4aeaf56892fb577
--- /dev/null
+++ b/tests/invader/crt.s
@@ -0,0 +1 @@
+.include "crtm.s"
diff --git a/tests/invader/crtm.s b/tests/invader/crtm.s
new file mode 100644
index 0000000000000000000000000000000000000000..158b3ce37b6d63155898f5f40916b043408ac200
--- /dev/null
+++ b/tests/invader/crtm.s
@@ -0,0 +1,75 @@
+# See LICENSE for license details.
+
+.include "macros.s"
+.include "constants.s"
+
+#
+# start of trap handler
+#
+
+.section .text.init,"ax",@progbits
+.globl _start
+
+_start:
+    # setup default trap vector
+    la      t0, trap_vector
+    csrw    mtvec, t0
+
+    # set up stack pointer based on hartid
+    la      sp, stacks + STACK_SIZE
+    #li sp, 0x00008FF0
+
+    # jump to main
+    lw a0, 4(sp) # pass argc
+    addi a1, sp, 8 # pass argv
+    j       libfemto_start_main
+
+    .align 2
+trap_vector:
+    # Save registers.
+    addi    sp, sp, -CONTEXT_SIZE
+    sxsp    ra, 0
+    sxsp    a0, 1
+    sxsp    a1, 2
+    sxsp    a2, 3
+    sxsp    a3, 4
+    sxsp    a4, 5
+    sxsp    a5, 6
+    sxsp    a6, 7
+    sxsp    a7, 8
+    sxsp    t0, 9
+    sxsp    t1, 10
+    sxsp    t2, 11
+    sxsp    t3, 12
+    sxsp    t4, 13
+    sxsp    t5, 14
+    sxsp    t6, 15
+
+    # Invoke the handler.
+    mv      a0, sp
+    csrr    a1, mcause
+    csrr    a2, mepc
+    jal     trap_handler
+
+    # Restore registers.
+    lxsp    ra, 0
+    lxsp    a0, 1
+    lxsp    a1, 2
+    lxsp    a2, 3
+    lxsp    a3, 4
+    lxsp    a4, 5
+    lxsp    a5, 6
+    lxsp    a6, 7
+    lxsp    a7, 8
+    lxsp    t0, 9
+    lxsp    t1, 10
+    lxsp    t2, 11
+    lxsp    t3, 12
+    lxsp    t4, 13
+    lxsp    t5, 14
+    lxsp    t6, 15
+    addi sp, sp, CONTEXT_SIZE
+
+    # Return
+    mret
+
diff --git a/tests/invader/extended.lds b/tests/invader/extended.lds
new file mode 100644
index 0000000000000000000000000000000000000000..a5347f423fb73d89b8c80613381dd9c68bb6cf81
--- /dev/null
+++ b/tests/invader/extended.lds
@@ -0,0 +1,54 @@
+OUTPUT_ARCH( "riscv" )
+
+GROUP(-lgcc)
+
+ENTRY( _start )
+
+MEMORY
+{
+  ram   (wxa!ri) : ORIGIN = 0x1000, LENGTH = 32M
+}
+
+PHDRS
+{
+  text PT_LOAD;
+  data PT_LOAD;
+  bss PT_LOAD;
+}
+
+SECTIONS
+{
+  PROVIDE(_start = 0x1000);
+  .text : {
+    PROVIDE(_text_start = .);
+    *(.text.init) *(.text .text.*)
+    PROVIDE(_text_end = .);
+  } >ram AT>ram :text
+
+  .rodata : {
+    PROVIDE(_rodata_start = .);
+    *(.rodata .rodata.*)
+    PROVIDE(_rodata_end = .);
+  } >ram AT>ram :text
+
+  .data : {
+    . = ALIGN(4096);
+    PROVIDE(_data_start = .);
+    *(.sdata .sdata.*) *(.data .data.*)
+    PROVIDE(_data_end = .);
+  } >ram AT>ram :data
+
+  .bss 0xc00000:{
+    PROVIDE(stacks = .);
+    PROVIDE(_bss_start = .);
+    .  = . + 0x400;
+    . = . + 0x100;
+    *(.sbss .sbss.*) *(.bss .bss.*)
+    *(COMMON)
+    . = . + 0x100;
+    PROVIDE(_bss_end = .);
+  } >ram AT>ram :bss
+
+  PROVIDE(_memory_start = ORIGIN(ram));
+  PROVIDE(_memory_end = ORIGIN(ram) + LENGTH(ram));
+}
diff --git a/tests/invader/font.c b/tests/invader/font.c
new file mode 100644
index 0000000000000000000000000000000000000000..937f6703219eac0e0bb8bc1d29914062511f6557
--- /dev/null
+++ b/tests/invader/font.c
@@ -0,0 +1,260 @@
+#include "font.h"
+
+char font8x8_basic[256][8] = {
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0000 (nul)
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0001
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0002
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0003
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0004
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0005
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0006
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0007
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0008
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0009
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+000A
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+000B
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+000C
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+000D
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+000E
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+000F
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0010
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0011
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0012
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0013
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0014
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0015
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0016
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0017
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0018
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0019
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+001A
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+001B
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+001C
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+001D
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+001E
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+001F
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0020 (space)
+    { 0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00},   // U+0021 (!)
+    { 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0022 (")
+    { 0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00},   // U+0023 (#)
+    { 0x0C, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x0C, 0x00},   // U+0024 ($)
+    { 0x00, 0x63, 0x33, 0x18, 0x0C, 0x66, 0x63, 0x00},   // U+0025 (%)
+    { 0x1C, 0x36, 0x1C, 0x6E, 0x3B, 0x33, 0x6E, 0x00},   // U+0026 (&)
+    { 0x06, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0027 (')
+    { 0x18, 0x0C, 0x06, 0x06, 0x06, 0x0C, 0x18, 0x00},   // U+0028 (()
+    { 0x06, 0x0C, 0x18, 0x18, 0x18, 0x0C, 0x06, 0x00},   // U+0029 ())
+    { 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00},   // U+002A (*)
+    { 0x00, 0x0C, 0x0C, 0x3F, 0x0C, 0x0C, 0x00, 0x00},   // U+002B (+)
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x06},   // U+002C (,)
+    { 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00},   // U+002D (-)
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00},   // U+002E (.)
+    { 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00},   // U+002F (/)
+    { 0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00},   // U+0030 (0)
+    { 0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00},   // U+0031 (1)
+    { 0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00},   // U+0032 (2)
+    { 0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00},   // U+0033 (3)
+    { 0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00},   // U+0034 (4)
+    { 0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00},   // U+0035 (5)
+    { 0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00},   // U+0036 (6)
+    { 0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00},   // U+0037 (7)
+    { 0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00},   // U+0038 (8)
+    { 0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00},   // U+0039 (9)
+    { 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00},   // U+003A (:)
+    { 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x06},   // U+003B (;)
+    { 0x18, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x18, 0x00},   // U+003C (<)
+    { 0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00},   // U+003D (=)
+    { 0x06, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x06, 0x00},   // U+003E (>)
+    { 0x1E, 0x33, 0x30, 0x18, 0x0C, 0x00, 0x0C, 0x00},   // U+003F (?)
+    { 0x3E, 0x63, 0x7B, 0x7B, 0x7B, 0x03, 0x1E, 0x00},   // U+0040 (@)
+    { 0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00},   // U+0041 (A)
+    { 0x3F, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3F, 0x00},   // U+0042 (B)
+    { 0x3C, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3C, 0x00},   // U+0043 (C)
+    { 0x1F, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1F, 0x00},   // U+0044 (D)
+    { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x46, 0x7F, 0x00},   // U+0045 (E)
+    { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x06, 0x0F, 0x00},   // U+0046 (F)
+    { 0x3C, 0x66, 0x03, 0x03, 0x73, 0x66, 0x7C, 0x00},   // U+0047 (G)
+    { 0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33, 0x00},   // U+0048 (H)
+    { 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00},   // U+0049 (I)
+    { 0x78, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, 0x00},   // U+004A (J)
+    { 0x67, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x67, 0x00},   // U+004B (K)
+    { 0x0F, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7F, 0x00},   // U+004C (L)
+    { 0x63, 0x77, 0x7F, 0x7F, 0x6B, 0x63, 0x63, 0x00},   // U+004D (M)
+    { 0x63, 0x67, 0x6F, 0x7B, 0x73, 0x63, 0x63, 0x00},   // U+004E (N)
+    { 0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00},   // U+004F (O)
+    { 0x3F, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x0F, 0x00},   // U+0050 (P)
+    { 0x1E, 0x33, 0x33, 0x33, 0x3B, 0x1E, 0x38, 0x00},   // U+0051 (Q)
+    { 0x3F, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x67, 0x00},   // U+0052 (R)
+    { 0x1E, 0x33, 0x07, 0x0E, 0x38, 0x33, 0x1E, 0x00},   // U+0053 (S)
+    { 0x3F, 0x2D, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00},   // U+0054 (T)
+    { 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0x00},   // U+0055 (U)
+    { 0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00},   // U+0056 (V)
+    { 0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00},   // U+0057 (W)
+    { 0x63, 0x63, 0x36, 0x1C, 0x1C, 0x36, 0x63, 0x00},   // U+0058 (X)
+    { 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x1E, 0x00},   // U+0059 (Y)
+    { 0x7F, 0x63, 0x31, 0x18, 0x4C, 0x66, 0x7F, 0x00},   // U+005A (Z)
+    { 0x1E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x1E, 0x00},   // U+005B ([)
+    { 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00},   // U+005C (\)
+    { 0x1E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1E, 0x00},   // U+005D (])
+    { 0x08, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00},   // U+005E (^)
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF},   // U+005F (_)
+    { 0x0C, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0060 (`)
+    { 0x00, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x6E, 0x00},   // U+0061 (a)
+    { 0x07, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00},   // U+0062 (b)
+    { 0x00, 0x00, 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x00},   // U+0063 (c)
+    { 0x38, 0x30, 0x30, 0x3e, 0x33, 0x33, 0x6E, 0x00},   // U+0064 (d)
+    { 0x00, 0x00, 0x1E, 0x33, 0x3f, 0x03, 0x1E, 0x00},   // U+0065 (e)
+    { 0x1C, 0x36, 0x06, 0x0f, 0x06, 0x06, 0x0F, 0x00},   // U+0066 (f)
+    { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x1F},   // U+0067 (g)
+    { 0x07, 0x06, 0x36, 0x6E, 0x66, 0x66, 0x67, 0x00},   // U+0068 (h)
+    { 0x0C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00},   // U+0069 (i)
+    { 0x30, 0x00, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E},   // U+006A (j)
+    { 0x07, 0x06, 0x66, 0x36, 0x1E, 0x36, 0x67, 0x00},   // U+006B (k)
+    { 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00},   // U+006C (l)
+    { 0x00, 0x00, 0x33, 0x7F, 0x7F, 0x6B, 0x63, 0x00},   // U+006D (m)
+    { 0x00, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x33, 0x00},   // U+006E (n)
+    { 0x00, 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00},   // U+006F (o)
+    { 0x00, 0x00, 0x3B, 0x66, 0x66, 0x3E, 0x06, 0x0F},   // U+0070 (p)
+    { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x78},   // U+0071 (q)
+    { 0x00, 0x00, 0x3B, 0x6E, 0x66, 0x06, 0x0F, 0x00},   // U+0072 (r)
+    { 0x00, 0x00, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x00},   // U+0073 (s)
+    { 0x08, 0x0C, 0x3E, 0x0C, 0x0C, 0x2C, 0x18, 0x00},   // U+0074 (t)
+    { 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00},   // U+0075 (u)
+    { 0x00, 0x00, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00},   // U+0076 (v)
+    { 0x00, 0x00, 0x63, 0x6B, 0x7F, 0x7F, 0x36, 0x00},   // U+0077 (w)
+    { 0x00, 0x00, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x00},   // U+0078 (x)
+    { 0x00, 0x00, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x1F},   // U+0079 (y)
+    { 0x00, 0x00, 0x3F, 0x19, 0x0C, 0x26, 0x3F, 0x00},   // U+007A (z)
+    { 0x38, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, 0x38, 0x00},   // U+007B ({)
+    { 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00},   // U+007C (|)
+    { 0x07, 0x0C, 0x0C, 0x38, 0x0C, 0x0C, 0x07, 0x00},   // U+007D (})
+    { 0x6E, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+007E (~)
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},    // U+007F
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0080
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0081
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0082
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0083
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0084
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0085
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0086
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0087
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0088
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0089
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+008A
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+008B
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+008C
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+008D
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+008E
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+008F
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0090
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0091
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0092
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0093
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0094
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0095
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0096
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0097
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0098
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0099
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+009A
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+009B
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+009C
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+009D
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+009E
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},    // U+009F
+     { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+00A0 (no break space)
+    { 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00},   // U+00A1 (inverted !)
+    { 0x18, 0x18, 0x7E, 0x03, 0x03, 0x7E, 0x18, 0x18},   // U+00A2 (dollarcents)
+    { 0x1C, 0x36, 0x26, 0x0F, 0x06, 0x67, 0x3F, 0x00},   // U+00A3 (pound sterling)
+    { 0x00, 0x00, 0x63, 0x3E, 0x36, 0x3E, 0x63, 0x00},   // U+00A4 (currency mark)
+    { 0x33, 0x33, 0x1E, 0x3F, 0x0C, 0x3F, 0x0C, 0x0C},   // U+00A5 (yen)
+    { 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00},   // U+00A6 (broken pipe)
+    { 0x7C, 0xC6, 0x1C, 0x36, 0x36, 0x1C, 0x33, 0x1E},   // U+00A7 (paragraph)
+    { 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+00A8 (diaeresis)
+    { 0x3C, 0x42, 0x99, 0x85, 0x85, 0x99, 0x42, 0x3C},   // U+00A9 (copyright symbol)
+    { 0x3C, 0x36, 0x36, 0x7C, 0x00, 0x00, 0x00, 0x00},   // U+00AA (superscript a)
+    { 0x00, 0xCC, 0x66, 0x33, 0x66, 0xCC, 0x00, 0x00},   // U+00AB (<<)
+    { 0x00, 0x00, 0x00, 0x3F, 0x30, 0x30, 0x00, 0x00},   // U+00AC (gun pointing left)
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+00AD (soft hyphen)
+    { 0x3C, 0x42, 0x9D, 0xA5, 0x9D, 0xA5, 0x42, 0x3C},   // U+00AE (registered symbol)
+    { 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+00AF (macron)
+    { 0x1C, 0x36, 0x36, 0x1C, 0x00, 0x00, 0x00, 0x00},   // U+00B0 (degree)
+    { 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x7E, 0x00},   // U+00B1 (plusminus)
+    { 0x1C, 0x30, 0x18, 0x0C, 0x3C, 0x00, 0x00, 0x00},   // U+00B2 (superscript 2)
+    { 0x1C, 0x30, 0x18, 0x30, 0x1C, 0x00, 0x00, 0x00},   // U+00B2 (superscript 3)
+    { 0x18, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+00B2 (aigu)
+    { 0x00, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x06, 0x03},   // U+00B5 (mu)
+    { 0xFE, 0xDB, 0xDB, 0xDE, 0xD8, 0xD8, 0xD8, 0x00},   // U+00B6 (pilcrow)
+    { 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00},   // U+00B7 (central dot)
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x1E},   // U+00B8 (cedille)
+    { 0x08, 0x0C, 0x08, 0x1C, 0x00, 0x00, 0x00, 0x00},   // U+00B9 (superscript 1)
+    { 0x1C, 0x36, 0x36, 0x1C, 0x00, 0x00, 0x00, 0x00},   // U+00BA (superscript 0)
+    { 0x00, 0x33, 0x66, 0xCC, 0x66, 0x33, 0x00, 0x00},   // U+00BB (>>)
+    { 0xC3, 0x63, 0x33, 0xBD, 0xEC, 0xF6, 0xF3, 0x03},   // U+00BC (1/4)
+    { 0xC3, 0x63, 0x33, 0x7B, 0xCC, 0x66, 0x33, 0xF0},   // U+00BD (1/2)
+    { 0x03, 0xC4, 0x63, 0xB4, 0xDB, 0xAC, 0xE6, 0x80},   // U+00BE (3/4)
+    { 0x0C, 0x00, 0x0C, 0x06, 0x03, 0x33, 0x1E, 0x00},   // U+00BF (inverted ?)
+    { 0x07, 0x00, 0x1C, 0x36, 0x63, 0x7F, 0x63, 0x00},   // U+00C0 (A grave)
+    { 0x70, 0x00, 0x1C, 0x36, 0x63, 0x7F, 0x63, 0x00},   // U+00C1 (A aigu)
+    { 0x1C, 0x36, 0x00, 0x3E, 0x63, 0x7F, 0x63, 0x00},   // U+00C2 (A circumflex)
+    { 0x6E, 0x3B, 0x00, 0x3E, 0x63, 0x7F, 0x63, 0x00},   // U+00C3 (A ~)
+    { 0x63, 0x1C, 0x36, 0x63, 0x7F, 0x63, 0x63, 0x00},   // U+00C4 (A umlaut)
+    { 0x0C, 0x0C, 0x00, 0x1E, 0x33, 0x3F, 0x33, 0x00},   // U+00C5 (A ring)
+    { 0x7C, 0x36, 0x33, 0x7F, 0x33, 0x33, 0x73, 0x00},   // U+00C6 (AE)
+    { 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x18, 0x30, 0x1E},   // U+00C7 (C cedille)
+    { 0x07, 0x00, 0x3F, 0x06, 0x1E, 0x06, 0x3F, 0x00},   // U+00C8 (E grave)
+    { 0x38, 0x00, 0x3F, 0x06, 0x1E, 0x06, 0x3F, 0x00},   // U+00C9 (E aigu)
+    { 0x0C, 0x12, 0x3F, 0x06, 0x1E, 0x06, 0x3F, 0x00},   // U+00CA (E circumflex)
+    { 0x36, 0x00, 0x3F, 0x06, 0x1E, 0x06, 0x3F, 0x00},   // U+00CB (E umlaut)
+    { 0x07, 0x00, 0x1E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00},   // U+00CC (I grave)
+    { 0x38, 0x00, 0x1E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00},   // U+00CD (I aigu)
+    { 0x0C, 0x12, 0x00, 0x1E, 0x0C, 0x0C, 0x1E, 0x00},   // U+00CE (I circumflex)
+    { 0x33, 0x00, 0x1E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00},   // U+00CF (I umlaut)
+    { 0x3F, 0x66, 0x6F, 0x6F, 0x66, 0x66, 0x3F, 0x00},   // U+00D0 (Eth)
+    { 0x3F, 0x00, 0x33, 0x37, 0x3F, 0x3B, 0x33, 0x00},   // U+00D1 (N ~)
+    { 0x0E, 0x00, 0x18, 0x3C, 0x66, 0x3C, 0x18, 0x00},   // U+00D2 (O grave)
+    { 0x70, 0x00, 0x18, 0x3C, 0x66, 0x3C, 0x18, 0x00},   // U+00D3 (O aigu)
+    { 0x3C, 0x66, 0x18, 0x3C, 0x66, 0x3C, 0x18, 0x00},   // U+00D4 (O circumflex)
+    { 0x6E, 0x3B, 0x00, 0x3E, 0x63, 0x63, 0x3E, 0x00},   // U+00D5 (O ~)
+    { 0xC3, 0x18, 0x3C, 0x66, 0x66, 0x3C, 0x18, 0x00},   // U+00D6 (O umlaut)
+    { 0x00, 0x36, 0x1C, 0x08, 0x1C, 0x36, 0x00, 0x00},   // U+00D7 (multiplicative x)
+    { 0x5C, 0x36, 0x73, 0x7B, 0x6F, 0x36, 0x1D, 0x00},   // U+00D8 (O stroke)
+    { 0x0E, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00},   // U+00D9 (U grave)
+    { 0x70, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00},   // U+00DA (U aigu)
+    { 0x3C, 0x66, 0x00, 0x66, 0x66, 0x66, 0x3C, 0x00},   // U+00DB (U circumflex)
+    { 0x33, 0x00, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x00},   // U+00DC (U umlaut)
+    { 0x70, 0x00, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x00},   // U+00DD (Y aigu)
+    { 0x0F, 0x06, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x0F},   // U+00DE (Thorn)
+    { 0x00, 0x1E, 0x33, 0x1F, 0x33, 0x1F, 0x03, 0x03},   // U+00DF (beta)
+    { 0x07, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x7E, 0x00},   // U+00E0 (a grave)
+    { 0x38, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x7E, 0x00},   // U+00E1 (a aigu)
+    { 0x7E, 0xC3, 0x3C, 0x60, 0x7C, 0x66, 0xFC, 0x00},   // U+00E2 (a circumflex)
+    { 0x6E, 0x3B, 0x1E, 0x30, 0x3E, 0x33, 0x7E, 0x00},   // U+00E3 (a ~)
+    { 0x33, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x7E, 0x00},   // U+00E4 (a umlaut)
+    { 0x0C, 0x0C, 0x1E, 0x30, 0x3E, 0x33, 0x7E, 0x00},   // U+00E5 (a ring)
+    { 0x00, 0x00, 0xFE, 0x30, 0xFE, 0x33, 0xFE, 0x00},   // U+00E6 (ae)
+    { 0x00, 0x00, 0x1E, 0x03, 0x03, 0x1E, 0x30, 0x1C},   // U+00E7 (c cedille)
+    { 0x07, 0x00, 0x1E, 0x33, 0x3F, 0x03, 0x1E, 0x00},   // U+00E8 (e grave)
+    { 0x38, 0x00, 0x1E, 0x33, 0x3F, 0x03, 0x1E, 0x00},   // U+00E9 (e aigu)
+    { 0x7E, 0xC3, 0x3C, 0x66, 0x7E, 0x06, 0x3C, 0x00},   // U+00EA (e circumflex)
+    { 0x33, 0x00, 0x1E, 0x33, 0x3F, 0x03, 0x1E, 0x00},   // U+00EB (e umlaut)
+    { 0x07, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00},   // U+00EC (i grave)
+    { 0x1C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00},   // U+00ED (i augu)
+    { 0x3E, 0x63, 0x1C, 0x18, 0x18, 0x18, 0x3C, 0x00},   // U+00EE (i circumflex)
+    { 0x33, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00},   // U+00EF (i umlaut)
+    { 0x1B, 0x0E, 0x1B, 0x30, 0x3E, 0x33, 0x1E, 0x00},   // U+00F0 (eth)
+    { 0x00, 0x1F, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x00},   // U+00F1 (n ~)
+    { 0x00, 0x07, 0x00, 0x1E, 0x33, 0x33, 0x1E, 0x00},   // U+00F2 (o grave)
+    { 0x00, 0x38, 0x00, 0x1E, 0x33, 0x33, 0x1E, 0x00},   // U+00F3 (o aigu)
+    { 0x1E, 0x33, 0x00, 0x1E, 0x33, 0x33, 0x1E, 0x00},   // U+00F4 (o circumflex)
+    { 0x6E, 0x3B, 0x00, 0x1E, 0x33, 0x33, 0x1E, 0x00},   // U+00F5 (o ~)
+    { 0x00, 0x33, 0x00, 0x1E, 0x33, 0x33, 0x1E, 0x00},   // U+00F6 (o umlaut)
+    { 0x18, 0x18, 0x00, 0x7E, 0x00, 0x18, 0x18, 0x00},   // U+00F7 (division)
+    { 0x00, 0x60, 0x3C, 0x76, 0x7E, 0x6E, 0x3C, 0x06},   // U+00F8 (o stroke)
+    { 0x00, 0x07, 0x00, 0x33, 0x33, 0x33, 0x7E, 0x00},   // U+00F9 (u grave)
+    { 0x00, 0x38, 0x00, 0x33, 0x33, 0x33, 0x7E, 0x00},   // U+00FA (u aigu)
+    { 0x1E, 0x33, 0x00, 0x33, 0x33, 0x33, 0x7E, 0x00},   // U+00FB (u circumflex)
+    { 0x00, 0x33, 0x00, 0x33, 0x33, 0x33, 0x7E, 0x00},   // U+00FC (u umlaut)
+    { 0x00, 0x38, 0x00, 0x33, 0x33, 0x3E, 0x30, 0x1F},   // U+00FD (y aigu)
+    { 0x00, 0x00, 0x06, 0x3E, 0x66, 0x3E, 0x06, 0x00},   // U+00FE (thorn)
+    { 0x00, 0x33, 0x00, 0x33, 0x33, 0x3E, 0x30, 0x1F}    // U+00FF (y umlaut)
+};
diff --git a/tests/invader/include/alloca.h b/tests/invader/include/alloca.h
new file mode 100644
index 0000000000000000000000000000000000000000..2ae6a3e066639298d28c546c2acf906c4400964c
--- /dev/null
+++ b/tests/invader/include/alloca.h
@@ -0,0 +1,3 @@
+#pragma once
+
+#define alloca(size) __builtin_alloca (size)
diff --git a/tests/invader/include/arch/riscv/csr.h b/tests/invader/include/arch/riscv/csr.h
new file mode 100644
index 0000000000000000000000000000000000000000..8bbc5f4b3c031723f598f92809a3af1f336a929c
--- /dev/null
+++ b/tests/invader/include/arch/riscv/csr.h
@@ -0,0 +1,81 @@
+// See LICENSE for license details.
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int* csr_enum_array();
+const char** csr_name_array();
+long read_csr_enum(int csrenum);
+void write_csr_enum(int csrenum, long value);
+
+enum {
+    csr_none,
+    csr_fflags,
+    csr_frm,
+    csr_fcsr,
+    csr_mcycle,
+    csr_minstret,
+    csr_mcycleh,
+    csr_minstreth,
+    csr_cycle,
+    csr_time,
+    csr_instret,
+    csr_cycleh,
+    csr_timeh,
+    csr_instreth,
+    csr_mvendorid,
+    csr_marchid,
+    csr_mimpid,
+    csr_mhartid,
+    csr_mstatus,
+    csr_misa,
+    csr_medeleg,
+    csr_mideleg,
+    csr_mie,
+    csr_mtvec,
+    csr_mcounteren,
+    csr_mscratch,
+    csr_mepc,
+    csr_mcause,
+    csr_mtval,
+    csr_mip,
+    csr_sstatus,
+    csr_sedeleg,
+    csr_sideleg,
+    csr_sie,
+    csr_stvec,
+    csr_scounteren,
+    csr_sscratch,
+    csr_sepc,
+    csr_scause,
+    csr_stval,
+    csr_sip,
+    csr_satp,
+    csr_pmpcfg0,
+    csr_pmpcfg1,
+    csr_pmpcfg2,
+    csr_pmpcfg3,
+    csr_pmpaddr0,
+    csr_pmpaddr1,
+    csr_pmpaddr2,
+    csr_pmpaddr3,
+    csr_pmpaddr4,
+    csr_pmpaddr5,
+    csr_pmpaddr6,
+    csr_pmpaddr7,
+    csr_pmpaddr8,
+    csr_pmpaddr9,
+    csr_pmpaddr10,
+    csr_pmpaddr11,
+    csr_pmpaddr12,
+    csr_pmpaddr13,
+    csr_pmpaddr14,
+    csr_pmpaddr15
+};
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/tests/invader/include/arch/riscv/encoding.h b/tests/invader/include/arch/riscv/encoding.h
new file mode 100644
index 0000000000000000000000000000000000000000..d592bbc72217c570ced0add5964d91dbd96eb752
--- /dev/null
+++ b/tests/invader/include/arch/riscv/encoding.h
@@ -0,0 +1,197 @@
+// See LICENSE for license details.
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MSTATUS_UIE         0x00000001
+#define MSTATUS_SIE         0x00000002
+#define MSTATUS_HIE         0x00000004
+#define MSTATUS_MIE         0x00000008
+#define MSTATUS_UPIE        0x00000010
+#define MSTATUS_SPIE        0x00000020
+#define MSTATUS_HPIE        0x00000040
+#define MSTATUS_MPIE        0x00000080
+#define MSTATUS_SPP         0x00000100
+#define MSTATUS_HPP         0x00000600
+#define MSTATUS_MPP         0x00001800
+#define MSTATUS_FS          0x00006000
+#define MSTATUS_XS          0x00018000
+#define MSTATUS_MPRV        0x00020000
+#define MSTATUS_SUM         0x00040000
+#define MSTATUS_MXR         0x00080000
+#define MSTATUS_TVM         0x00100000
+#define MSTATUS_TW          0x00200000
+#define MSTATUS_TSR         0x00400000
+#define MSTATUS32_SD        0x80000000
+#define MSTATUS64_SD        0x8000000000000000
+
+#define SSTATUS_UIE         0x00000001
+#define SSTATUS_SIE         0x00000002
+#define SSTATUS_UPIE        0x00000010
+#define SSTATUS_SPIE        0x00000020
+#define SSTATUS_SPP         0x00000100
+#define SSTATUS_FS          0x00006000
+#define SSTATUS_XS          0x00018000
+#define SSTATUS_SUM         0x00040000
+#define SSTATUS_MXR         0x00080000
+#define SSTATUS32_SD        0x80000000
+#define SSTATUS64_SD        0x8000000000000000
+
+#define DCSR_XDEBUGVER      (3U<<30)
+#define DCSR_NDRESET        (1<<29)
+#define DCSR_FULLRESET      (1<<28)
+#define DCSR_EBREAKM        (1<<15)
+#define DCSR_EBREAKH        (1<<14)
+#define DCSR_EBREAKS        (1<<13)
+#define DCSR_EBREAKU        (1<<12)
+#define DCSR_STOPCYCLE      (1<<10)
+#define DCSR_STOPTIME       (1<<9)
+#define DCSR_CAUSE          (7<<6)
+#define DCSR_DEBUGINT       (1<<5)
+#define DCSR_HALT           (1<<3)
+#define DCSR_STEP           (1<<2)
+#define DCSR_PRV            (3<<0)
+
+#define DCSR_CAUSE_NONE     0
+#define DCSR_CAUSE_SWBP     1
+#define DCSR_CAUSE_HWBP     2
+#define DCSR_CAUSE_DEBUGINT 3
+#define DCSR_CAUSE_STEP     4
+#define DCSR_CAUSE_HALT     5
+
+#define MCONTROL_TYPE(xlen)    (0xfULL<<((xlen)-4))
+#define MCONTROL_DMODE(xlen)   (1ULL<<((xlen)-5))
+#define MCONTROL_MASKMAX(xlen) (0x3fULL<<((xlen)-11))
+
+#define MCONTROL_SELECT     (1<<19)
+#define MCONTROL_TIMING     (1<<18)
+#define MCONTROL_ACTION     (0x3f<<12)
+#define MCONTROL_CHAIN      (1<<11)
+#define MCONTROL_MATCH      (0xf<<7)
+#define MCONTROL_M          (1<<6)
+#define MCONTROL_H          (1<<5)
+#define MCONTROL_S          (1<<4)
+#define MCONTROL_U          (1<<3)
+#define MCONTROL_EXECUTE    (1<<2)
+#define MCONTROL_STORE      (1<<1)
+#define MCONTROL_LOAD       (1<<0)
+
+#define MCONTROL_TYPE_NONE      0
+#define MCONTROL_TYPE_MATCH     2
+
+#define MCONTROL_ACTION_DEBUG_EXCEPTION   0
+#define MCONTROL_ACTION_DEBUG_MODE        1
+#define MCONTROL_ACTION_TRACE_START       2
+#define MCONTROL_ACTION_TRACE_STOP        3
+#define MCONTROL_ACTION_TRACE_EMIT        4
+
+#define MCONTROL_MATCH_EQUAL     0
+#define MCONTROL_MATCH_NAPOT     1
+#define MCONTROL_MATCH_GE        2
+#define MCONTROL_MATCH_LT        3
+#define MCONTROL_MATCH_MASK_LOW  4
+#define MCONTROL_MATCH_MASK_HIGH 5
+
+#define MIP_SSIP            (1 << IRQ_S_SOFT)
+#define MIP_HSIP            (1 << IRQ_H_SOFT)
+#define MIP_MSIP            (1 << IRQ_M_SOFT)
+#define MIP_STIP            (1 << IRQ_S_TIMER)
+#define MIP_HTIP            (1 << IRQ_H_TIMER)
+#define MIP_MTIP            (1 << IRQ_M_TIMER)
+#define MIP_SEIP            (1 << IRQ_S_EXT)
+#define MIP_HEIP            (1 << IRQ_H_EXT)
+#define MIP_MEIP            (1 << IRQ_M_EXT)
+
+#define SIP_SSIP MIP_SSIP
+#define SIP_STIP MIP_STIP
+
+#define PRV_U 0
+#define PRV_S 1
+#define PRV_H 2
+#define PRV_M 3
+
+#define SPTBR32_MODE 0x80000000
+#define SPTBR32_ASID 0x7FC00000
+#define SPTBR32_PPN  0x003FFFFF
+#define SPTBR64_MODE 0xF000000000000000
+#define SPTBR64_ASID 0x0FFFF00000000000
+#define SPTBR64_PPN  0x00000FFFFFFFFFFF
+
+#define SPTBR_MODE_OFF  0
+#define SPTBR_MODE_SV32 1
+#define SPTBR_MODE_SV39 8
+#define SPTBR_MODE_SV48 9
+#define SPTBR_MODE_SV57 10
+#define SPTBR_MODE_SV64 11
+
+#define PMP_R     0x01
+#define PMP_W     0x02
+#define PMP_X     0x04
+#define PMP_A     0x18
+#define PMP_L     0x80
+#define PMP_SHIFT     2
+#define PMPCFG_COUNT  4
+#define PMPADDR_COUNT 16
+
+#define PMP_OFF   0x00
+#define PMP_TOR   0x08
+#define PMP_NA4   0x10
+#define PMP_NAPOT 0x18
+
+#define IRQ_S_SOFT   1
+#define IRQ_H_SOFT   2
+#define IRQ_M_SOFT   3
+#define IRQ_S_TIMER  5
+#define IRQ_H_TIMER  6
+#define IRQ_M_TIMER  7
+#define IRQ_S_EXT    9
+#define IRQ_H_EXT    10
+#define IRQ_M_EXT    11
+#define IRQ_COP      12
+#define IRQ_HOST     13
+
+#define DEFAULT_RSTVEC     0x00001000
+#define CLINT_BASE         0x02000000
+#define CLINT_SIZE         0x000c0000
+#define EXT_IO_BASE        0x40000000
+#define DRAM_BASE          0x80000000
+
+// page table entry (PTE) fields
+#define PTE_V     0x001 // Valid
+#define PTE_R     0x002 // Read
+#define PTE_W     0x004 // Write
+#define PTE_X     0x008 // Execute
+#define PTE_U     0x010 // User
+#define PTE_G     0x020 // Global
+#define PTE_A     0x040 // Accessed
+#define PTE_D     0x080 // Dirty
+#define PTE_SOFT  0x300 // Reserved for Software
+
+#define PTE_PPN_SHIFT 10
+
+#define PTE_TABLE(PTE) (((PTE) & (PTE_V | PTE_R | PTE_W | PTE_X)) == PTE_V)
+
+#ifdef __riscv
+
+#if __riscv_xlen == 64
+# define MSTATUS_SD MSTATUS64_SD
+# define SSTATUS_SD SSTATUS64_SD
+# define RISCV_PGLEVEL_BITS 9
+# define SPTBR_MODE SPTBR64_MODE
+#else
+# define MSTATUS_SD MSTATUS32_SD
+# define SSTATUS_SD SSTATUS32_SD
+# define RISCV_PGLEVEL_BITS 10
+# define SPTBR_MODE SPTBR32_MODE
+#endif
+#define RISCV_PGSHIFT 12
+#define RISCV_PGSIZE (1 << RISCV_PGSHIFT)
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/tests/invader/include/arch/riscv/machine.h b/tests/invader/include/arch/riscv/machine.h
new file mode 100644
index 0000000000000000000000000000000000000000..077a6c1e8a629fda3b5eefed0a24e1219c992c8b
--- /dev/null
+++ b/tests/invader/include/arch/riscv/machine.h
@@ -0,0 +1,183 @@
+// See LICENSE for license details.
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <stddef.h>
+
+void arch_setup();
+void exit(int status) __attribute__((noreturn));
+
+#define die(str, ...) ({ \
+  printf("%s:%d: " str "\n", __FILE__, __LINE__, ##__VA_ARGS__); exit(-1); })
+
+#define assert(x) ({ if (!(x)) die("assertion failed: %s", #x); })
+
+#define read_const_csr(reg) ({ unsigned long __tmp; \
+  asm ("csrr %0, " #reg : "=r"(__tmp)); __tmp; })
+
+#define read_csr(reg) ({ unsigned long __tmp; \
+  asm volatile ("csrr %0, " #reg : "=r"(__tmp)); __tmp; })
+
+#define write_csr(reg, val) ({ \
+  asm volatile ("csrw " #reg ", %0" :: "rK"(val)); })
+
+#define swap_csr(reg, val) ({ unsigned long __tmp; \
+  asm volatile ("csrrw %0, " #reg ", %1" : "=r"(__tmp) : "rK"(val)); __tmp; })
+
+#define set_csr(reg, bit) ({ unsigned long __tmp; \
+  asm volatile ("csrrs %0, " #reg ", %1" : "=r"(__tmp) : "rK"(bit)); __tmp; })
+
+#define clear_csr(reg, bit) ({ unsigned long __tmp; \
+  asm volatile ("csrrc %0, " #reg ", %1" : "=r"(__tmp) : "rK"(bit)); __tmp; })
+
+static inline uintptr_t get_field(uintptr_t reg, uintptr_t mask)
+{
+    return ((reg & mask) / (mask & ~(mask << 1)));
+}
+
+static inline uintptr_t set_field(uintptr_t reg, uintptr_t mask, uintptr_t val)
+{
+    return ((reg & ~mask) | ((val * (mask & ~(mask << 1))) & mask));
+}
+
+static inline unsigned long rdtime() { return read_csr(time); }
+static inline unsigned long rdcycle() { return read_csr(cycle); }
+static inline unsigned long rdinstret() { return read_csr(instret); }
+static inline int64_t misa() { return read_const_csr(misa); }
+static inline int has_ext(char ext) { return misa() & (1 << (ext - 'a')); }
+static inline int xlen() { return misa() < 0 ? 64 : 32; }
+static inline void wfi() { asm volatile ("wfi" ::: "memory"); }
+
+__attribute__((noreturn)) static inline void mret()
+{
+    asm volatile ("mret");
+    __builtin_unreachable();
+}
+
+
+/*
+ * Memory
+ *
+ * TODO - improve this API to return a list of memory segments
+ */
+
+typedef struct memory_info
+{
+    uintptr_t start;
+    uintptr_t end;
+} memory_info_t;
+
+
+/*
+ * memory_probe - return memory_info
+ */
+memory_info_t memory_probe();
+
+/*
+ * memory_probe_range - probe a memory address range
+ */
+uintptr_t memory_probe_range(uintptr_t start, uintptr_t end);
+
+
+/*
+ * Physical Memory Protection
+ *
+ * PMP is optional but if implememented, enforcement must be enabled by
+ * default, if no PMP entries are set. This means loads, stores or fetches
+ * from any mode besides M mode, will fail unless explicitly allowed.
+ * PMP must be configured irregardless of whether it is implemented.
+ */
+
+typedef struct pmp_info
+{
+    int width;
+    int granularity;
+    int count;
+} pmp_info_t;
+
+/*
+ * pmp_probe - return pmp_info
+ */
+pmp_info_t pmp_probe();
+
+/*
+ * pmp_entry_granularity - return PMP entry width (physical memory width)
+ */
+int pmp_entry_width();
+
+/*
+ * pmp_entry_granularity - return PMP entry granularity (smallest entry size)
+ */
+int pmp_entry_granularity();
+
+/*
+ * pmp_entry_count - return number of PMP entries
+ */
+int pmp_entry_count();
+
+/*
+ * pmp_clear_all - set PMP to disallow mode != PRV_M physical memory accesses
+ */
+void pmp_clear_all();
+
+/*
+ * pmp_allow_all - set PMP to allow mode != PRV_M physical memory accesses
+ */
+void pmp_allow_all();
+
+/*
+ * pmp_entry_set - set one PMP entry
+ *
+ * - n    : pmp entry number
+ * - prot : protection (PMP_R | PMP_W | PMP_X)
+ * - addr : start address
+ * - len  : power of two length
+ */
+int pmp_entry_set(unsigned n, uint8_t prot, uint64_t addr, uint64_t len);
+
+
+/*
+ * Privileged modes
+ */
+
+/*
+ * mode_set_and_jump
+ *
+ * Set mstatus.mpp, sets mepc to passed function pointer and then issues mret
+ * Note: the hart will continue running on the same stack
+ */
+static inline void mode_set_and_jump(unsigned mode, void (*fn)(void))
+{
+    assert(mode <= PRV_U);
+    write_csr(mstatus, set_field(read_csr(mstatus), MSTATUS_MPP, mode));
+    write_csr(mepc, fn);
+    mret();
+}
+
+/*
+ * mode_set_and_continue
+ *
+ * Set mstatus.mpp, sets mepc to instruction after mret and then issues mret
+ * Note: the hart will continue running on the same stack
+ */
+static inline void mode_set_and_continue(unsigned mode)
+{
+    assert(mode <= PRV_U);
+    write_csr(mstatus, set_field(read_csr(mstatus), MSTATUS_MPP, mode));
+    asm volatile (
+        "lla t0, 1f\n"
+        "csrw mepc, t0\n"
+        "mret\n"
+        "1:"
+        ::: "t0"
+    );
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/tests/invader/include/arch/riscv/pte.h b/tests/invader/include/arch/riscv/pte.h
new file mode 100644
index 0000000000000000000000000000000000000000..38d7b8d4ad7999e8d07a430be1b8e0c908a1ad8d
--- /dev/null
+++ b/tests/invader/include/arch/riscv/pte.h
@@ -0,0 +1,121 @@
+#pragma once
+
+enum {
+    PTE_SHIFT_V   = 0,
+    PTE_SHIFT_R   = 1,
+    PTE_SHIFT_W   = 2,
+    PTE_SHIFT_X   = 3,
+    PTE_SHIFT_U   = 4,
+    PTE_SHIFT_G   = 5,
+    PTE_SHIFT_A   = 6,
+    PTE_SHIFT_D   = 7,
+    PTE_SHIFT_SW1 = 8,
+    PTE_SHIFT_SW2 = 9,
+    PTE_V         = 1 << PTE_SHIFT_V,
+    PTE_R         = 1 << PTE_SHIFT_R,
+    PTE_W         = 1 << PTE_SHIFT_W,
+    PTE_X         = 1 << PTE_SHIFT_X,
+    PTE_U         = 1 << PTE_SHIFT_U,
+    PTE_G         = 1 << PTE_SHIFT_G,
+    PTE_A         = 1 << PTE_SHIFT_A,
+    PTE_D         = 1 << PTE_SHIFT_D,
+    PTE_SW1       = 1 << PTE_SHIFT_SW1,
+    PTE_SW2       = 1 << PTE_SHIFT_SW2
+};
+
+enum {
+    SV32_LEVELS = 2,
+    SV32_LEVEL_BITS = 10,
+    SV32_PTE_SIZE = 4
+};
+
+union sv32_va {
+    uint32_t val;
+    struct {
+        uint32_t pg_off : 12;
+        uint32_t vpn    : 20;
+    } va;
+};
+
+union sv32_pa {
+    uint64_t val;
+    struct {
+        uint64_t pg_off  : 12;
+        uint64_t ppn     : 22;
+        uint64_t rsrv    : 30;
+    } pa;
+};
+
+union sv32_pte {
+    uint32_t val;
+    struct {
+        uint32_t flags   : 10;
+        uint32_t ppn     : 22;
+    } pte;
+};
+
+enum {
+    SV39_LEVELS = 3,
+    SV39_LEVEL_BITS = 9,
+    SV39_PTE_SIZE = 8
+};
+
+union sv39_va {
+    uint64_t val;
+    struct {
+        uint64_t pg_off  : 12;
+        uint64_t vpn     : 27;
+        uint64_t rsrv    : 25;
+    } va;
+};
+
+union sv39_pa {
+    uint64_t val;
+    struct {
+        uint64_t pg_off  : 12;
+        uint64_t ppn     : 44;
+        uint64_t rsrv    : 8;
+    } pa;
+};
+
+union sv39_pte {
+    uint64_t val;
+    struct {
+        uint64_t flags   : 10;
+        uint64_t ppn     : 44;
+        uint64_t rsrv    : 10;
+    } pte;
+};
+
+enum {
+    SV48_LEVELS = 4,
+    SV48_LEVEL_BITS = 9,
+    SV48_PTE_SIZE = 8
+};
+
+union sv48_va {
+    uint64_t val;
+    struct {
+        uint64_t pg_off  : 12;
+        uint64_t vpn     : 36;
+        uint64_t rsrv    : 16;
+    } va;
+};
+
+union sv48_pa {
+    uint64_t val;
+    struct {
+        uint64_t pg_off  : 12;
+        uint64_t ppn     : 44;
+        uint64_t rsrv    : 8;
+    } pa;
+};
+
+union sv48_pte {
+    uint64_t val;
+    struct {
+        uint64_t flags   : 10;
+        uint64_t ppn     : 44;
+        uint64_t rsrv    : 10;
+    } pte;
+};
diff --git a/tests/invader/include/arch/riscv/trap.h b/tests/invader/include/arch/riscv/trap.h
new file mode 100644
index 0000000000000000000000000000000000000000..0b33ec587ae9529285cb1f8f8ceb17249ec49741
--- /dev/null
+++ b/tests/invader/include/arch/riscv/trap.h
@@ -0,0 +1,51 @@
+// See LICENSE for license details.
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void (*trap_fn)(uintptr_t* regs, uintptr_t mcause, uintptr_t mepc);
+trap_fn get_trap_fn();
+void set_trap_fn(trap_fn fn);
+
+const char * riscv_excp_names[16];
+const char * riscv_intr_names[16];
+
+enum {
+  cause_misaligned_fetch     = 0,
+  cause_fault_fetch          = 1,
+  cause_illegal_instruction  = 2,
+  cause_breakpoint           = 3,
+  cause_misaligned_load      = 4,
+  cause_fault_load           = 5,
+  cause_misaligned_store     = 6,
+  cause_fault_store          = 7,
+  cause_user_ecall           = 8,
+  cause_supervisor_ecall     = 9,
+  cause_hypervisor_ecall     = 10,
+  cause_machine_ecall        = 11,
+  cause_exec_page_fault      = 12,
+  cause_load_page_fault      = 13,
+  cause_store_page_fault     = 15
+};
+
+enum {
+  intr_u_software           = 0,
+  intr_s_software           = 1,
+  intr_h_software           = 2,
+  intr_m_software           = 3,
+  intr_u_timer              = 4,
+  intr_s_timer              = 5,
+  intr_h_timer              = 6,
+  intr_m_timer              = 7,
+  intr_u_external           = 8,
+  intr_s_external           = 9,
+  intr_h_external           = 10,
+  intr_m_external           = 11,
+};
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/tests/invader/include/auxval.h b/tests/invader/include/auxval.h
new file mode 100644
index 0000000000000000000000000000000000000000..de108243640d3a8179b85cffb1dd8a54b47f3b14
--- /dev/null
+++ b/tests/invader/include/auxval.h
@@ -0,0 +1,34 @@
+// See LICENSE for license details.
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum {
+    HART0_CLOCK_FREQ           = 0x00010000,
+    UART0_CLOCK_FREQ           = 0x00011000,
+    UART0_BAUD_RATE            = 0x00011100,
+    NS16550A_UART0_CTRL_ADDR   = 0x00030000,
+    RISCV_HTIF_BASE_ADDR       = 0x00050000,
+    SIFIVE_CLINT_CTRL_ADDR     = 0x55550000,
+    SIFIVE_CLIC_CRTL_ADDR      = 0x55550001,
+    SIFIVE_TEST_CTRL_ADDR      = 0x55550002,
+    SIFIVE_UART0_CTRL_ADDR     = 0x55550010,
+    SIFIVE_GPIO0_CTRL_ADDR     = 0x55550020,
+    SIFIVE_SPI0_CTRL_ADDR      = 0x55550030,
+};
+
+typedef struct auxval {
+    unsigned long key;
+    unsigned long val;
+} auxval_t;
+
+extern auxval_t __auxv[];
+
+unsigned long getauxval(unsigned long key);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/tests/invader/include/device.h b/tests/invader/include/device.h
new file mode 100644
index 0000000000000000000000000000000000000000..822ab8c3f25147bfe0bb704f32b26f1931c85957
--- /dev/null
+++ b/tests/invader/include/device.h
@@ -0,0 +1,39 @@
+// See LICENSE for license details.
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct console_device {
+    void (*init)();
+    int (*getchar)();
+    int (*putchar)(int);
+} console_device_t;
+
+typedef struct poweroff_device {
+    void (*init)();
+    void (*poweroff)(int);
+} poweroff_device_t;
+
+void register_console(console_device_t *dev);
+void register_poweroff(poweroff_device_t *dev);
+
+extern console_device_t *console_dev;
+extern poweroff_device_t *poweroff_dev;
+
+extern console_device_t console_none;
+extern console_device_t console_htif;
+extern console_device_t console_ns16550a;
+extern console_device_t console_sifive_uart;
+extern console_device_t console_semihost;
+
+extern poweroff_device_t poweroff_none;
+extern poweroff_device_t poweroff_htif;
+extern poweroff_device_t poweroff_sifive_test;
+extern poweroff_device_t poweroff_semihost;
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/tests/invader/include/elf.h b/tests/invader/include/elf.h
new file mode 100644
index 0000000000000000000000000000000000000000..abfb892a256f9a9be1b155c7ffb3ca1d7f3ee3ac
--- /dev/null
+++ b/tests/invader/include/elf.h
@@ -0,0 +1,545 @@
+#pragma once
+
+#include <stdint.h>
+
+typedef uint32_t Elf32_Addr;
+typedef uint16_t Elf32_Half;
+typedef uint32_t Elf32_Off;
+typedef int32_t  Elf32_Sword;
+typedef uint32_t Elf32_Word;
+typedef uint8_t  Elf32_Byte;
+
+typedef uint64_t Elf64_Addr;
+typedef uint16_t Elf64_Half;
+typedef uint64_t Elf64_Off;
+typedef int32_t  Elf64_Sword;
+typedef uint32_t Elf64_Word;
+typedef uint64_t Elf64_Xword;
+typedef int64_t  Elf64_Sxword;
+typedef uint8_t  Elf64_Byte;
+
+enum {
+	AT_NULL = 0,
+	AT_PHDR = 3,
+	AT_PHENT = 4,
+	AT_PHNUM = 5,
+	AT_PAGESZ = 6,
+	AT_BASE = 7,
+	AT_FLAGS = 8,
+	AT_ENTRY = 9,
+	AT_UID = 11,
+	AT_EUID = 12,
+	AT_GID = 13,
+	AT_EGID  = 14,
+	AT_CLKTCK = 17,
+	AT_SECURE = 23,
+	AT_RANDOM = 25
+};
+
+typedef struct {
+	Elf32_Word a_type;
+	Elf32_Word a_val;
+} Elf32_auxv;
+
+typedef struct {
+	Elf64_Word a_type;
+	Elf64_Word a_val;
+} Elf64_auxv;
+
+enum {
+	ET_NONE = 0,
+	ET_REL = 1,
+	ET_EXEC = 2,
+	ET_DYN = 3,
+	ET_CORE = 4,
+	ET_LOPROC = 0xff00,
+	ET_HIPROC = 0xffff
+};
+
+enum {
+	EM_NONE = 0,
+	EM_X86_64 = 62,
+	EM_RISCV = 243
+};
+
+enum {
+	EV_NONE = 0,
+	EV_CURRENT = 1
+};
+
+enum {
+	EI_MAG0 = 0,
+	EI_MAG1 = 1,
+	EI_MAG2 = 2,
+	EI_MAG3 = 3,
+	EI_CLASS = 4,
+	EI_DATA = 5,
+	EI_VERSION = 6,
+	EI_OSABI = 7,
+	EI_ABIVERSION = 8,
+	EI_PAD = 9,
+	EI_NIDENT = 16
+};
+
+enum {
+	ELFMAG0 = 0x7f,
+	ELFMAG1 = 'E',
+	ELFMAG2 = 'L',
+	ELFMAG3 = 'F'
+};
+
+enum {
+	ELFCLASSNONE = 0,
+	ELFCLASS32 = 1,
+	ELFCLASS64 = 2,
+	ELFCLASS128 = 3
+};
+
+enum {
+	ELFDATANONE = 0,
+	ELFDATA2LSB = 1,
+	ELFDATA2MSB = 2
+};
+
+enum {
+	ELFOSABI_SYSV = 0,
+	ELFOSABI_LINUX = 3,
+	ELFOSABI_SOLARIS = 6,
+	ELFOSABI_FREEBSD = 9
+};
+
+enum {
+	ELFABIVERSION_NONE = 0
+};
+
+typedef struct {
+	Elf32_Byte  e_ident[EI_NIDENT];
+	Elf32_Half  e_type;
+	Elf32_Half  e_machine;
+	Elf32_Word  e_version;
+	Elf32_Addr  e_entry;
+	Elf32_Off   e_phoff;
+	Elf32_Off   e_shoff;
+	Elf32_Word  e_flags;
+	Elf32_Half  e_ehsize;
+	Elf32_Half  e_phentsize;
+	Elf32_Half  e_phnum;
+	Elf32_Half  e_shentsize;
+	Elf32_Half  e_shnum;
+	Elf32_Half  e_shstrndx;
+} Elf32_Ehdr;
+
+typedef struct {
+	Elf64_Byte  e_ident[EI_NIDENT];
+	Elf64_Half  e_type;
+	Elf64_Half  e_machine;
+	Elf64_Word  e_version;
+	Elf64_Addr  e_entry;
+	Elf64_Off   e_phoff;
+	Elf64_Off   e_shoff;
+	Elf64_Word  e_flags;
+	Elf64_Half  e_ehsize;
+	Elf64_Half  e_phentsize;
+	Elf64_Half  e_phnum;
+	Elf64_Half  e_shentsize;
+	Elf64_Half  e_shnum;
+	Elf64_Half  e_shstrndx;
+} Elf64_Ehdr;
+
+enum {
+	PT_NULL = 0,
+	PT_LOAD = 1,
+	PT_DYNAMIC = 2,
+	PT_INTERP = 3,
+	PT_NOTE = 4,
+	PT_SHLIB = 5,
+	PT_PHDR = 6,
+	PT_TLS = 7,
+	PT_LOOS = 0x60000000,
+	PT_GNU_EH_FRAME = 0x6474e550,
+	PT_GNU_STACK = 0x6474e551,
+	PT_GNU_RELRO = 0x6474e552,
+	PT_HIOS = 0x6fffffff,
+	PT_LOPROC = 0x70000000,
+	PT_HIPROC = 0x7fffffff
+};
+
+enum {
+	PF_X = 0x1,
+	PF_W = 0x2,
+	PF_R = 0x4,
+	PF_MASKOS = 0x00FF0000,
+	PF_MASKPROC = 0xFF000000,
+};
+
+typedef struct {
+	Elf32_Word  p_type;
+	Elf32_Off   p_offset;
+	Elf32_Addr  p_vaddr;
+	Elf32_Addr  p_paddr;
+	Elf32_Word  p_filesz;
+	Elf32_Word  p_memsz;
+	Elf32_Word  p_flags;
+	Elf32_Word  p_align;
+} Elf32_Phdr;
+
+typedef struct {
+	Elf64_Word  p_type;
+	Elf64_Word  p_flags;
+	Elf64_Off   p_offset;
+	Elf64_Addr  p_vaddr;
+	Elf64_Addr  p_paddr;
+	Elf64_Xword p_filesz;
+	Elf64_Xword p_memsz;
+	Elf64_Xword p_align;
+} Elf64_Phdr;
+
+enum {
+	SHN_UNDEF = 0,
+	SHN_LOPROC = 0xff00,
+	SHN_HIPROC = 0xff1f,
+	SHN_LOOS = 0xff20,
+	SHN_HIOS = 0xff3f,
+	SHN_ABS = 0xfff1,
+	SHN_COMMON = 0xfff2,
+	SHN_XINDEX = 0xffff,
+	SHN_HIRESERVE = 0xffff,
+};
+
+enum {
+	SHT_NULL = 0,
+	SHT_PROGBITS = 1,
+	SHT_SYMTAB = 2,
+	SHT_STRTAB = 3,
+	SHT_RELA = 4,
+	SHT_HASH = 5,
+	SHT_DYNAMIC = 6,
+	SHT_NOTE = 7,
+	SHT_NOBITS = 8,
+	SHT_REL = 9,
+	SHT_SHLIB = 10,
+	SHT_DYNSYM = 11,
+	SHT_INIT_ARRAY = 14,
+	SHT_FINI_ARRAY = 15,
+	SHT_PREINIT_ARRAY = 16,
+	SHT_GROUP = 17,
+	SHT_SYMTAB_SHNDX = 18,
+	SHT_LOOS = 0x60000000,
+	SHT_GNU_VERDEF = 0x6ffffffd,
+	SHT_GNU_VERNEED = 0x6ffffffe,
+	SHT_GNU_VERSYM = 0x6fffffff,
+	SHT_HIOS = 0x6fffffff,
+	SHT_LOPROC = 0x70000000,
+	SHT_HIPROC = 0x7fffffff,
+	SHT_LOUSER = 0x80000000,
+	SHT_HIUSER = 0xffffffff
+};
+
+enum {
+	SHF_WRITE = 0x1,
+	SHF_ALLOC = 0x2,
+	SHF_EXECINSTR = 0x4,
+	SHF_MERGE = 0x10,
+	SHF_STRINGS = 0x20,
+	SHF_INFO_LINK = 0x40,
+	SHF_LINK_ORDER = 0x80,
+	SHF_GROUP = 0x200,
+	SHF_TLS = 0x400,
+	SHF_MASKOS = 0x0F000000,
+	SHF_MASKPROC = 0xf0000000
+};
+
+enum {
+  GRP_COMDAT = 0x1,
+  GRP_MASKOS = 0x0ff00000,
+  GRP_MASKPROC = 0xf0000000
+};
+
+typedef struct {
+	Elf32_Word  sh_name;
+	Elf32_Word  sh_type;
+	Elf32_Word  sh_flags;
+	Elf32_Addr  sh_addr;
+	Elf32_Off   sh_offset;
+	Elf32_Word  sh_size;
+	Elf32_Word  sh_link;
+	Elf32_Word  sh_info;
+	Elf32_Word  sh_addralign;
+	Elf32_Word  sh_entsize;
+} Elf32_Shdr;
+
+typedef struct {
+	Elf64_Word  sh_name;
+	Elf64_Word  sh_type;
+	Elf64_Xword sh_flags;
+	Elf64_Addr  sh_addr;
+	Elf64_Off   sh_offset;
+	Elf64_Xword sh_size;
+	Elf64_Word  sh_link;
+	Elf64_Word  sh_info;
+	Elf64_Xword sh_addralign;
+	Elf64_Xword sh_entsize;
+} Elf64_Shdr;
+
+
+typedef struct {
+	Elf32_Word n_namesz;
+	Elf32_Word n_descsz;
+	Elf32_Word n_type;
+} Elf32_Nhdr;
+
+typedef struct {
+	Elf64_Word n_namesz;
+	Elf64_Word n_descsz;
+	Elf64_Word n_type;
+} Elf64_Nhdr;
+
+static inline const Elf32_Byte ELF32_ST_BIND(Elf32_Word i) { return i >> 4; }
+static inline const Elf32_Byte ELF32_ST_TYPE(Elf32_Word i) { return i & 0xf; }
+static inline const Elf32_Byte ELF32_ST_INFO(Elf32_Word b, Elf32_Word t) { return (b << 4) | (t & 0xf); }
+
+static inline const Elf32_Byte ELF64_ST_BIND(Elf32_Word i) { return i >> 4; }
+static inline const Elf32_Byte ELF64_ST_TYPE(Elf32_Word i) { return i & 0xf; }
+static inline const Elf32_Byte ELF64_ST_INFO(Elf32_Word b, Elf32_Word t) { return (b << 4) | (t & 0xf); }
+
+enum {
+	STB_LOCAL = 0,
+	STB_GLOBAL = 1,
+	STB_WEAK = 2,
+	STB_LOOS = 10,
+	STB_HIOS = 12,
+	STB_LOPROC = 13,
+	STB_HIPROC = 15
+};
+
+enum {
+	STT_NOTYPE = 0,
+	STT_OBJECT = 1,
+	STT_FUNC = 2,
+	STT_SECTION = 3,
+	STT_FILE = 4,
+	STT_LOOS = 10,
+	STT_HIOS = 12,
+	STT_LOPROC = 13,
+	STT_HIPROC = 15
+};
+
+enum {
+	STV_DEFAULT = 0,
+	STV_INTERNAL = 1,
+	STV_HIDDEN = 2,
+	STV_PROTECTED = 3
+};
+
+typedef struct {
+	Elf32_Word  st_name;
+	Elf32_Addr  st_value;
+	Elf32_Word  st_size;
+	Elf32_Byte  st_info;
+	Elf32_Byte  st_other;
+	Elf32_Half  st_shndx;
+} Elf32_Sym;
+
+typedef struct {
+	Elf64_Word  st_name;
+	Elf64_Byte  st_info;
+	Elf64_Byte  st_other;
+	Elf64_Half  st_shndx;
+	Elf64_Addr  st_value;
+	Elf64_Xword st_size;
+} Elf64_Sym;
+
+static inline const Elf32_Word ELF32_R_SYM(Elf32_Word i) { return i >> 8; }
+static inline const Elf32_Word ELF32_R_TYPE(Elf32_Word i) { return i & 0xff; }
+static inline const Elf32_Word ELF32_R_INFO(Elf32_Word s, Elf32_Word t) { return (s << 8) | (t & 0xff); }
+
+static inline const Elf64_Xword ELF64_R_SYM(Elf64_Xword i) { return i >> 32; }
+static inline const Elf64_Xword ELF64_R_TYPE(Elf64_Xword i) { return i & 0xffffffffUL; }
+static inline const Elf64_Xword ELF64_R_INFO(Elf64_Xword s, Elf64_Xword t) { return (s << 32) | (t & 0xffffffffUL); }
+
+typedef struct {
+	Elf32_Addr  r_offset;
+	Elf32_Word  r_info;
+} Elf32_Rel;
+
+typedef struct {
+	Elf32_Addr  r_offset;
+	Elf32_Word  r_info;
+	Elf32_Sword r_addend;
+} Elf32_Rela;
+
+typedef struct
+{
+	Elf64_Addr  r_offset;
+	Elf64_Xword r_info;
+} Elf64_Rel;
+
+typedef struct
+{
+	Elf64_Addr r_offset;
+	Elf64_Xword r_info;
+	Elf64_Sxword r_addend;
+} Elf64_Rela;
+
+enum {
+	R_X86_64_NONE = 0,
+	R_X86_64_64 = 1,
+	R_X86_64_PC32 = 2,
+	R_X86_64_GOT32 = 3,
+	R_X86_64_PLT32 = 4,
+	R_X86_64_COPY = 5,
+	R_X86_64_GLOB_DAT = 6,
+	R_X86_64_JUMP_SLOT = 7,
+	R_X86_64_RELATIVE = 8,
+	R_X86_64_GOTPCREL = 9,
+	R_X86_64_32 = 10,
+	R_X86_64_32S = 11,
+	R_X86_64_16 = 12,
+	R_X86_64_PC16 = 13,
+	R_X86_64_8 = 14,
+	R_X86_64_PC8 = 15,
+	R_X86_64_DTPMOD64 = 16,
+	R_X86_64_DTPOFF64 = 17,
+	R_X86_64_TPOFF64  = 18,
+	R_X86_64_TLSGD = 19,
+	R_X86_64_TLSLD = 20,
+	R_X86_64_DTPOFF32 = 21,
+	R_X86_64_GOTTPOFF = 22,
+	R_X86_64_TPOFF32 = 23,
+	R_X86_64_PC64 = 24,
+	R_X86_64_GOTOFF64 = 25,
+	R_X86_64_GOTPC32 = 26,
+	R_X86_64_SIZE32 = 32,
+	R_X86_64_SIZE64 = 33,
+	R_X86_64_GOTPC32_TLSDESC = 34,
+	R_X86_64_TLSDESC_CALL = 35,
+	R_X86_64_TLSDESC = 36,
+	R_X86_64_IRELATIVE = 37,
+	R_X86_64_RELATIVE64 = 38
+};
+
+enum {
+	R_RISCV_NONE = 0,
+	R_RISCV_32 = 1,
+	R_RISCV_64 = 2,
+	R_RISCV_RELATIVE = 3,
+	R_RISCV_COPY = 4,
+	R_RISCV_JUMP_SLOT = 5,
+	R_RISCV_TLS_DTPMOD32 = 6,
+	R_RISCV_TLS_DTPMOD64 = 7,
+	R_RISCV_TLS_DTPREL32 = 8,
+	R_RISCV_TLS_DTPREL64 = 9,
+	R_RISCV_TLS_TPREL32 = 10,
+	R_RISCV_TLS_TPREL64 = 11,
+	R_RISCV_BRANCH = 16,
+	R_RISCV_JAL = 17,
+	R_RISCV_CALL = 18,
+	R_RISCV_CALL_PLT = 19,
+	R_RISCV_GOT_HI20 = 20,
+	R_RISCV_TLS_GOT_HI20 = 21,
+	R_RISCV_TLS_GD_HI20 = 22,
+	R_RISCV_PCREL_HI20 = 23,
+	R_RISCV_PCREL_LO12_I = 24,
+	R_RISCV_PCREL_LO12_S = 25,
+	R_RISCV_HI20 = 26,
+	R_RISCV_LO12_I = 27,
+	R_RISCV_LO12_S = 28,
+	R_RISCV_TPREL_HI20 = 29,
+	R_RISCV_TPREL_LO12_I = 30,
+	R_RISCV_TPREL_LO12_S = 31,
+	R_RISCV_TPREL_ADD = 32,
+	R_RISCV_ADD8 = 33,
+	R_RISCV_ADD16 = 34,
+	R_RISCV_ADD32 = 35,
+	R_RISCV_ADD64 = 36,
+	R_RISCV_SUB8 = 37,
+	R_RISCV_SUB16 = 38,
+	R_RISCV_SUB32 = 39,
+	R_RISCV_SUB64 = 40,
+	R_RISCV_GNU_VTINHERIT = 41,
+	R_RISCV_GNU_VTENTRY = 42,
+	R_RISCV_ALIGN = 43,
+	R_RISCV_RVC_BRANCH = 44,
+	R_RISCV_RVC_JUMP = 45,
+	R_RISCV_RVC_LUI = 46,
+	R_RISCV_GPREL_I = 47,
+	R_RISCV_GPREL_S = 48,
+	R_RISCV_TPREL_I = 49,
+	R_RISCV_TPREL_S = 50,
+	R_RISCV_RELAX = 51,
+	R_RISCV_SUB6 = 52,
+	R_RISCV_SET6 = 53,
+	R_RISCV_SET8 = 54,
+	R_RISCV_SET16 = 55,
+	R_RISCV_SET32 = 56,
+};
+
+enum {
+	EF_RISCV_RVC = 0x1,
+	EF_RISCV_FLOAT_ABI_SINGLE = 0x2,
+	EF_RISCV_FLOAT_ABI_DOUBLE = 0x4,
+	EF_RISCV_FLOAT_ABI_QUAD = 0x6,
+	EF_RISCV_RVE = 0x8
+};
+
+enum {
+	DF_ORIGIN = 1,
+	DF_SYMBOLIC = 2,
+	DF_TEXTREL = 4,
+	DF_BIND_NOW = 8,
+	DF_STATIC_TLS = 16
+};
+
+enum {
+	DT_NULL = 0,
+	DT_NEEDED = 1,
+	DT_PLTRELSZ = 2,
+	DT_PLTGOT = 3,
+	DT_HASH = 4,
+	DT_STRTAB = 5,
+	DT_SYMTAB = 6,
+	DT_RELA = 7,
+	DT_RELASZ = 8,
+	DT_RELAENT = 9,
+	DT_STRSZ = 10,
+	DT_SYMENT = 11,
+	DT_INIT = 12,
+	DT_FINI = 13,
+	DT_SONAME = 14,
+	DT_RPATH = 15,
+	DT_SYMBOLIC = 16,
+	DT_REL = 17,
+	DT_RELSZ = 18,
+	DT_RELENT = 19,
+	DT_PLTREL = 20,
+	DT_DEBUG = 21,
+	DT_TEXTREL = 22,
+	DT_JMPREL = 23,
+	DT_BIND_NOW = 24,
+	DT_INIT_ARRAY = 25,
+	DT_FINI_ARRAY = 26,
+	DT_INIT_ARRAYSZ = 27,
+	DT_FINI_ARRAYSZ = 28,
+	DT_RUNPATH = 29,
+	DT_FLAGS = 30,
+	DT_LOOS = 0x60000000,
+	DT_HIOS = 0x6fffffff,
+	DT_LOPROC = 0x70000000,
+	DT_HIPROC = 0x7fffffff
+};
+
+typedef struct {
+	Elf32_Sword  d_tag;
+	union {
+		Elf32_Word  d_val;
+		Elf32_Addr  d_ptr;
+	} d_un;
+} Elf32_Dyn;
+
+typedef struct {
+	Elf64_Sxword d_tag;
+	union {
+		Elf64_Xword d_val;
+		Elf64_Addr  d_ptr;
+	} d_un;
+} Elf64_Dyn;
diff --git a/tests/invader/include/endian.h b/tests/invader/include/endian.h
new file mode 100644
index 0000000000000000000000000000000000000000..8510f2939748b9b810ae07d8b9ef0bd62c88cd13
--- /dev/null
+++ b/tests/invader/include/endian.h
@@ -0,0 +1,95 @@
+#pragma once
+
+#define bswap16(x) __builtin_bswap16(x)
+#define bswap32(x) __builtin_bswap32(x)
+#define bswap64(x) __builtin_bswap64(x)
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+static inline uint16_t htobe16(uint16_t x) { return bswap16(x); }
+static inline uint16_t htole16(uint16_t x) { return x; }
+static inline uint16_t be16toh(uint16_t x) { return bswap16(x); }
+static inline uint16_t le16toh(uint16_t x) { return x; }
+
+static inline uint32_t htobe32(uint32_t x) { return bswap32(x); }
+static inline uint32_t htole32(uint32_t x) { return x; }
+static inline uint32_t be32toh(uint32_t x) { return bswap32(x); }
+static inline uint32_t le32toh(uint32_t x) { return x; }
+
+static inline uint64_t htobe64(uint64_t x) { return bswap64(x); }
+static inline uint64_t htole64(uint64_t x) { return x; }
+static inline uint64_t be64toh(uint64_t x) { return bswap64(x); }
+static inline uint64_t le64toh(uint64_t x) { return x; }
+#elif __BYTE_ORDER == __BIG_ENDIAN
+static inline uint16_t htobe16(uint16_t x) { return x; }
+static inline uint16_t htole16(uint16_t x) { return bswap16(x); }
+static inline uint16_t be16toh(uint16_t x) { return x; }
+static inline uint16_t le16toh(uint16_t x) { return bswap16(x); }
+
+static inline uint32_t htobe32(uint32_t x) { return x; }
+static inline uint32_t htole32(uint32_t x) { return bswap32(x); }
+static inline uint32_t be32toh(uint32_t x) { return x; }
+static inline uint32_t le32toh(uint16_t x) { return bswap64(x); }
+
+static inline uint64_t htobe64(uint64_t x) { return x; }
+static inline uint64_t htole64(uint64_t x) { return bswap64(x); }
+static inline uint64_t be64toh(uint64_t x) { return x; }
+static inline uint64_t le64toh(uint64_t x) { return bswap64(x); }
+#endif
+
+#if __SIZE_WIDTH__ == 32
+#define _htobel htobe32
+#define _beltoh be32toh
+#define _htolel htole32
+#define _leltoh le32toh
+#endif
+
+#if __SIZE_WIDTH__ == 64
+#define _htobel htobe64
+#define _beltoh be64toh
+#define _htolel htole64
+#define _leltoh le64toh
+#endif
+
+#define htobe(X) _Generic((X),                \
+                 short: htobe16,              \
+                 unsigned short: htobe16,     \
+                 int: htobe32,                \
+                 unsigned int: htobe32,       \
+                 long: _htobel,               \
+                 unsigned long: _htobel,      \
+                 long long: htobe64,          \
+                 unsigned long long: htobe64  \
+                 )(X)
+
+#define betoh(X) _Generic((X),                \
+                 short: be16toh,              \
+                 unsigned short: be16toh,     \
+                 int: be32toh,                \
+                 unsigned int: be32toh,       \
+                 long: _beltoh,               \
+                 unsigned long: _beltoh,      \
+                 long long: be64toh,          \
+                 unsigned long long: be64toh  \
+                 )(X)
+
+#define htole(X) _Generic((X),                \
+                 short: htole16,              \
+                 unsigned short: htole16,     \
+                 int: htole32,                \
+                 unsigned int: htole32,       \
+                 long: _htolel,               \
+                 unsigned long: _htolel,      \
+                 long long: htole64,          \
+                 unsigned long long: htole64  \
+                 )(X)
+
+#define letoh(X) _Generic((X),                \
+                 short: le16toh,              \
+                 unsigned short: le16toh,     \
+                 int: le32toh,                \
+                 unsigned int: le32toh,       \
+                 long: _leltoh,               \
+                 unsigned long: _leltoh,      \
+                 long long: le64toh,          \
+                 unsigned long long: le64toh  \
+                 )(X)
diff --git a/tests/invader/include/femto.h b/tests/invader/include/femto.h
new file mode 100644
index 0000000000000000000000000000000000000000..aa8b4cf00d380a0321f909c8971f249316b8ddfb
--- /dev/null
+++ b/tests/invader/include/femto.h
@@ -0,0 +1,14 @@
+// See LICENSE for license details.
+
+#pragma once
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdbits.h>
+#include <string.h>
+
+#include "auxval.h"
+#include "device.h"
diff --git a/tests/invader/include/font.h b/tests/invader/include/font.h
new file mode 100644
index 0000000000000000000000000000000000000000..f65d48d250503448fea06af0668b1b630b80bb0e
--- /dev/null
+++ b/tests/invader/include/font.h
@@ -0,0 +1,28 @@
+#ifndef FONT_H
+#define FONT_H
+/**
+ * 8x8 monochrome bitmap fonts for rendering
+ * Author: Daniel Hepper <daniel@hepper.net>
+ *
+ * License: Public Domain
+ *
+ * Based on:
+ * // Summary: font8x8.h
+ * // 8x8 monochrome bitmap fonts for rendering
+ * //
+ * // Author:
+ * //     Marcel Sondaar
+ * //     International Business Machines (public domain VGA fonts)
+ * //
+ * // License:
+ * //     Public Domain
+ *
+ * Fetched from: http://dimensionalrift.homelinux.net/combuster/mos3/?p=viewsource&file=/modules/gfx/font8_8.asm
+ * Fetched from github : https://github.com/dhepper/font8x8/blob/master/font8x8_basic.h
+ **/
+
+// Constant: font8x8_basic
+// Contains an 8x8 font map for unicode points U+0000 - U+007F (basic latin)
+char font8x8_basic[256][8];
+
+#endif
diff --git a/tests/invader/include/libscreen.h b/tests/invader/include/libscreen.h
new file mode 100644
index 0000000000000000000000000000000000000000..c222621680066fe7682cebfa7a37aa12e7964981
--- /dev/null
+++ b/tests/invader/include/libscreen.h
@@ -0,0 +1,69 @@
+#ifndef LIBSCREEN
+#define LIBSCREEN
+
+typedef unsigned int uint;
+#include "font.h"
+#include "femto.h"
+
+#define CLINT_MSIP                      0x02000000
+#define CLINT_TIMER_CMP                 0x02004000
+#define CLINT_TIMER_CMP_HI              0x02004004
+#define CLINT_TIMER_CMP_LO              0x02004000
+#define CLINT_TIMER                     0x0200bff8
+#define CLINT_TIMER_HI                  0x0200bffc
+#define CLINT_TIMER_LOW                 0x0200bff8
+
+
+
+volatile uint* IMG;
+volatile uint *push;
+volatile uint *timer;
+volatile uint *timer_cmp;
+
+/* function to get the state of push buttons */
+uint push_button_get(void);
+
+// Timer options
+#ifdef ENV_QEMU 
+#define TIMER_FREQ 10000000 // 10MHz
+#define RATIO 500  
+#else
+#define TIMER_FREQ 100000000 // 100MHz
+#define RATIO 200 
+#endif
+
+/* function to set the timer to be reached in period*time/100 in the future */
+void timer_set(uint period, uint time);
+
+/* function to wait for timer zero value */
+void timer_wait(void);
+
+void timer_set_and_wait(uint period, uint time);
+
+#define NBCOL 1920
+#define NBROW 1080
+
+uint fgcolor;
+uint bgcolor;
+
+void draw(uint color, uint x, uint y);
+
+void clear_screen(uint color);
+
+void draw_bitmap(char* bitmap);
+
+void newline();
+void tab();
+
+/* Counts the number of characters of current word. Will be used to break lines, if possible not in the middle of words. */
+uint num_characters_until_white(char* str);
+
+uint display_cur_x = 0;
+uint display_cur_y = 0;
+uint display_scale = 16;
+
+void display_uint(uint i);
+void display_string(char* str);
+void set_fg_color(uint color);
+void set_bg_color(uint color);
+#endif
diff --git a/tests/invader/include/list.h b/tests/invader/include/list.h
new file mode 100644
index 0000000000000000000000000000000000000000..5f4cc72eca0b0c2f81c58c78400d997b11b4aba4
--- /dev/null
+++ b/tests/invader/include/list.h
@@ -0,0 +1,495 @@
+/*
+ * Minimal Linux-like double-linked list helper functions
+ *
+ * Copyright (c) 2012-2016, Sven Eckelmann <sven@narfation.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+
+/**
+ * container_of() - Calculate address of object that contains address ptr
+ * @ptr: pointer to member variable
+ * @type: type of the structure containing ptr
+ * @member: name of the member variable in struct @type
+ *
+ * Return: @type pointer of object containing ptr
+ */
+
+#define container_of(ptr, type, member) __extension__ ({ \
+	const __typeof__(((type *)0)->member) *__pmember = (ptr); \
+	(type *)((char *)__pmember - offsetof(type, member)); })
+
+/**
+ * struct list_head - Head and node of a double-linked list
+ * @prev: pointer to the previous node in the list
+ * @next: pointer to the next node in the list
+ *
+ * The simple double-linked list consists of a head and nodes attached to
+ * this head. Both node and head share the same struct type. The list_*
+ * functions and macros can be used to access and modify this data structure.
+ *
+ * The @prev pointer of the list head points to the last list node of the
+ * list and @next points to the first list node of the list. For an empty list,
+ * both member variables point to the head.
+ *
+ * The list nodes are usually embedded in a container structure which holds the
+ * actual data. Such an container object is called entry. The helper list_entry
+ * can be used to calculate the object address from the address of the node.
+ */
+struct list_head {
+	struct list_head *prev;
+	struct list_head *next;
+};
+
+/**
+ * LIST_HEAD - Declare list head and initialize it
+ * @head: name of the new object
+ */
+#define LIST_HEAD(head) \
+	struct list_head head = { &(head), &(head) }
+
+/**
+ * INIT_LIST_HEAD() - Initialize empty list head
+ * @head: pointer to list head
+ *
+ * This can also be used to initialize a unlinked list node.
+ *
+ * A node is usually linked inside a list, will be added to a list in
+ * the near future or the entry containing the node will be free'd soon.
+ *
+ * But an unlinked node may be given to a function which uses list_del(_init)
+ * before it ends up in a previously mentioned state. The list_del(_init) on an
+ * initialized node is well defined and safe. But the result of a
+ * list_del(_init) on an uninitialized node is undefined (unrelated memory is
+ * modified, crashes, ...).
+ */
+static __inline__ void INIT_LIST_HEAD(struct list_head *head)
+{
+	head->next = head;
+	head->prev = head;
+}
+
+/**
+ * list_add() - Add a list node to the beginning of the list
+ * @node: pointer to the new node
+ * @head: pointer to the head of the list
+ */
+static __inline__ void list_add(struct list_head *node,
+				struct list_head *head)
+{
+	struct list_head *next = head->next;
+
+	next->prev = node;
+	node->next = next;
+	node->prev = head;
+	head->next = node;
+}
+
+/**
+ * list_add_tail() - Add a list node to the end of the list
+ * @node: pointer to the new node
+ * @head: pointer to the head of the list
+ */
+static __inline__ void list_add_tail(struct list_head *node,
+				     struct list_head *head)
+{
+	struct list_head *prev = head->prev;
+
+	prev->next = node;
+	node->next = head;
+	node->prev = prev;
+	head->prev = node;
+}
+
+/**
+ * list_add_before() - Add a list node before another node to the list
+ * @new_node: pointer to the new node
+ * @node: pointer to the reference node in the list
+ *
+ * WARNING this functionality is not available in the Linux list implementation
+ */
+#define list_add_before(new_node, node) \
+	list_add_tail(new_node, node)
+
+/**
+ * list_add_behind() - Add a list node behind another node to the list
+ * @new_node: pointer to the new node
+ * @node: pointer to the reference node in the list
+ *
+ * WARNING this functionality is not available in the Linux list implementation
+ */
+#define list_add_behind(new_node, node) \
+	list_add(new_node, node)
+
+/**
+ * list_del() - Remove a list node from the list
+ * @node: pointer to the node
+ *
+ * The node is only removed from the list. Neither the memory of the removed
+ * node nor the memory of the entry containing the node is free'd. The node
+ * has to be handled like an uninitialized node. Accessing the next or prev
+ * pointer of the node is not safe.
+ *
+ * Unlinked, initialized nodes are also uninitialized after list_del.
+ *
+ * LIST_POISONING can be enabled during build-time to provoke an invalid memory
+ * access when the memory behind the next/prev pointer is used after a list_del.
+ * This only works on systems which prohibit access to the predefined memory
+ * addresses.
+ */
+static __inline__ void list_del(struct list_head *node)
+{
+	struct list_head *next = node->next;
+	struct list_head *prev = node->prev;
+
+	next->prev = prev;
+	prev->next = next;
+
+#ifdef LIST_POISONING
+	node->prev = (struct list_head *)(0x00100100);
+	node->next = (struct list_head *)(0x00200200);
+#endif
+}
+
+/**
+ * list_del_init() - Remove a list node from the list and reinitialize it
+ * @node: pointer to the node
+ *
+ * The removed node will not end up in an uninitialized state like when using
+ * list_del. Instead the node is initialized again to the unlinked state.
+ */
+static __inline__ void list_del_init(struct list_head *node)
+{
+	list_del(node);
+	INIT_LIST_HEAD(node);
+}
+
+/**
+ * list_empty() - Check if list head has no nodes attached
+ * @head: pointer to the head of the list
+ *
+ * Return: 0 - list is not empty !0 - list is empty
+ */
+static __inline__ int list_empty(const struct list_head *head)
+{
+	return (head->next == head);
+}
+
+/**
+ * list_is_singular() - Check if list head has exactly one node attached
+ * @head: pointer to the head of the list
+ *
+ * Return: 0 - list is not singular !0 -list has exactly one entry
+ */
+static __inline__ int list_is_singular(const struct list_head *head)
+{
+	return (!list_empty(head) && head->prev == head->next);
+}
+
+/**
+ * list_splice() - Add list nodes from a list to beginning of another list
+ * @list: pointer to the head of the list with the node entries
+ * @head: pointer to the head of the list
+ *
+ * All nodes from @list are added to to the beginning of the list of @head.
+ * It is similar to list_add but for multiple nodes. The @list head is not
+ * modified and has to be initialized to be used as a valid list head/node
+ * again.
+ */
+static __inline__ void list_splice(struct list_head *list,
+				   struct list_head *head)
+{
+	struct list_head *head_first = head->next;
+	struct list_head *list_first = list->next;
+	struct list_head *list_last = list->prev;
+
+	if (list_empty(list))
+		return;
+
+	head->next = list_first;
+	list_first->prev = head;
+
+	list_last->next = head_first;
+	head_first->prev = list_last;
+}
+
+/**
+ * list_splice_tail() - Add list nodes from a list to end of another list
+ * @list: pointer to the head of the list with the node entries
+ * @head: pointer to the head of the list
+ *
+ * All nodes from @list are added to to the end of the list of @head.
+ * It is similar to list_add_tail but for multiple nodes. The @list head is not
+ * modified and has to be initialized to be used as a valid list head/node
+ * again.
+ */
+static __inline__ void list_splice_tail(struct list_head *list,
+					struct list_head *head)
+{
+	struct list_head *head_last = head->prev;
+	struct list_head *list_first = list->next;
+	struct list_head *list_last = list->prev;
+
+	if (list_empty(list))
+		return;
+
+	head->prev = list_last;
+	list_last->next = head;
+
+	list_first->prev = head_last;
+	head_last->next = list_first;
+}
+
+/**
+ * list_splice_init() - Move list nodes from a list to beginning of another list
+ * @list: pointer to the head of the list with the node entries
+ * @head: pointer to the head of the list
+ *
+ * All nodes from @list are added to to the beginning of the list of @head.
+ * It is similar to list_add but for multiple nodes.
+ *
+ * The @list head will not end up in an uninitialized state like when using
+ * list_splice. Instead the @list is initialized again to the an empty
+ * list/unlinked state.
+ */
+static __inline__ void list_splice_init(struct list_head *list,
+					struct list_head *head)
+{
+	list_splice(list, head);
+	INIT_LIST_HEAD(list);
+}
+
+/**
+ * list_splice_tail_init() - Move list nodes from a list to end of another list
+ * @list: pointer to the head of the list with the node entries
+ * @head: pointer to the head of the list
+ *
+ * All nodes from @list are added to to the end of the list of @head.
+ * It is similar to list_add_tail but for multiple nodes.
+ *
+ * The @list head will not end up in an uninitialized state like when using
+ * list_splice. Instead the @list is initialized again to the an empty
+ * list/unlinked state.
+ */
+static __inline__ void list_splice_tail_init(struct list_head *list,
+					     struct list_head *head)
+{
+	list_splice_tail(list, head);
+	INIT_LIST_HEAD(list);
+}
+
+/**
+ * list_cut_position() - Move beginning of a list to another list
+ * @head_to: pointer to the head of the list which receives nodes
+ * @head_from: pointer to the head of the list
+ * @node: pointer to the node in which defines the cutting point
+ *
+ * All entries from the beginning of the list @head_from to (including) the
+ * @node is moved to @head_from.
+ *
+ * @head_to is replaced when @head_from is not empty. @node must be a real
+ * list node from @head_from or the behavior is undefined.
+ */
+static __inline__ void list_cut_position(struct list_head *head_to,
+					 struct list_head *head_from,
+					 struct list_head *node)
+{
+	struct list_head *head_from_first = head_from->next;
+
+	if (list_empty(head_from))
+		return;
+
+	if (head_from == node) {
+		INIT_LIST_HEAD(head_to);
+		return;
+	}
+
+	head_from->next = node->next;
+	head_from->next->prev = head_from;
+
+	head_to->prev = node;
+	node->next = head_to;
+	head_to->next = head_from_first;
+	head_to->next->prev = head_to;
+}
+
+/**
+ * list_move() - Move a list node to the beginning of the list
+ * @node: pointer to the node
+ * @head: pointer to the head of the list
+ *
+ * The @node is removed from its old position/node and add to the beginning of
+ * @head
+ */
+static __inline__ void list_move(struct list_head *node, struct list_head *head)
+{
+	list_del(node);
+	list_add(node, head);
+}
+
+/**
+ * list_move_tail() - Move a list node to the end of the list
+ * @node: pointer to the node
+ * @head: pointer to the head of the list
+ *
+ * The @node is removed from its old position/node and add to the end of @head
+ */
+static __inline__ void list_move_tail(struct list_head *node,
+				      struct list_head *head)
+{
+	list_del(node);
+	list_add_tail(node, head);
+}
+
+/**
+ * list_entry() - Calculate address of entry that contains list node
+ * @node: pointer to list node
+ * @type: type of the entry containing the list node
+ * @member: name of the list_head member variable in struct @type
+ *
+ * Return: @type pointer of entry containing node
+ */
+#define list_entry(node, type, member) container_of(node, type, member)
+
+/**
+ * list_first_entry() - get first entry of the list
+ * @head: pointer to the head of the list
+ * @type: type of the entry containing the list node
+ * @member: name of the list_head member variable in struct @type
+ *
+ * Return: @type pointer of first entry in list
+ */
+#define list_first_entry(head, type, member) \
+	list_entry((head)->next, type, member)
+
+/**
+ * list_last_entry() - get last entry of the list
+ * @head: pointer to the head of the list
+ * @type: type of the entry containing the list node
+ * @member: name of the list_head member variable in struct @type
+ *
+ * Return: @type pointer of last entry in list
+ */
+#define list_last_entry(head, type, member) \
+	list_entry((head)->prev, type, member)
+
+/**
+ * list_for_each - iterate over list nodes
+ * @node: list_head pointer used as iterator
+ * @head: pointer to the head of the list
+ *
+ * The nodes and the head of the list must must be kept unmodified while
+ * iterating through it. Any modifications to the the list will cause undefined
+ * behavior.
+ */
+#define list_for_each(node, head) \
+	for (node = (head)->next; \
+	     node != (head); \
+	     node = node->next)
+
+/**
+ * list_for_each_entry_t - iterate over list entries
+ * @entry: @type pointer used as iterator
+ * @head: pointer to the head of the list
+ * @type: type of the entries containing the list nodes
+ * @member: name of the list_head member variable in struct @type
+ *
+ * The nodes and the head of the list must must be kept unmodified while
+ * iterating through it. Any modifications to the the list will cause undefined
+ * behavior.
+ *
+ * WARNING this functionality is not available in the Linux list implementation
+ */
+#define list_for_each_entry_t(entry, head, type, member) \
+	for (entry = list_entry((head)->next, type, member); \
+	     &entry->member != (head); \
+	     entry = list_entry(entry->member.next, type, member))
+
+/**
+ * list_for_each_entry - iterate over list entries
+ * @entry: pointer used as iterator
+ * @head: pointer to the head of the list
+ * @member: name of the list_head member variable in struct type of @entry
+ *
+ * The nodes and the head of the list must must be kept unmodified while
+ * iterating through it. Any modifications to the the list will cause undefined
+ * behavior.
+ */
+#define list_for_each_entry(entry, head, member) \
+	list_for_each_entry_t(entry, head, __typeof__(*entry), member)
+
+/**
+ * list_for_each_safe - iterate over list nodes and allow deletes
+ * @node: list_head pointer used as iterator
+ * @safe: list_head pointer used to store info for next entry in list
+ * @head: pointer to the head of the list
+ *
+ * The current node (iterator) is allowed to be removed from the list. Any
+ * other modifications to the the list will cause undefined behavior.
+ */
+#define list_for_each_safe(node, safe, head) \
+	for (node = (head)->next, safe = node->next; \
+	     node != (head); \
+	     node = safe, safe = node->next)
+
+/**
+ * list_for_each_entry_safe_t - iterate over list entries and allow deletes
+ * @entry: @type pointer used as iterator
+ * @safe: @type pointer used to store info for next entry in list
+ * @head: pointer to the head of the list
+ * @type: type of the entries containing the list nodes
+ * @member: name of the list_head member variable in struct @type
+ *
+ * The current node (iterator) is allowed to be removed from the list. Any
+ * other modifications to the the list will cause undefined behavior.
+ *
+ * WARNING this functionality is not available in the Linux list implementation
+ */
+#define list_for_each_entry_safe_t(entry, safe, head, type, member) \
+	for (entry = list_entry((head)->next, type, member), \
+	     safe = list_entry(entry->member.next, type, member); \
+	     &entry->member != (head); \
+	     entry = safe, \
+	     safe = list_entry(safe->member.next, type, member))
+
+/**
+ * list_for_each_entry_safe - iterate over list entries and allow deletes
+ * @entry: pointer used as iterator
+ * @safe: @type pointer used to store info for next entry in list
+ * @head: pointer to the head of the list
+ * @member: name of the list_head member variable in struct type of @entry
+ *
+ * The current node (iterator) is allowed to be removed from the list. Any
+ * other modifications to the the list will cause undefined behavior.
+ */
+#define list_for_each_entry_safe(entry, safe, head, member) \
+	list_for_each_entry_safe_t(entry, safe, head, __typeof__(*entry), \
+				   member)
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/tests/invader/include/stdarg.h b/tests/invader/include/stdarg.h
new file mode 100644
index 0000000000000000000000000000000000000000..7383c4ae3bf7e4134a7d7d0e7cf2328ea363b080
--- /dev/null
+++ b/tests/invader/include/stdarg.h
@@ -0,0 +1,8 @@
+#pragma once
+
+typedef __builtin_va_list va_list;
+
+#define va_start(v,l)   __builtin_va_start(v,l)
+#define va_end(v)       __builtin_va_end(v)
+#define va_arg(v,l)     __builtin_va_arg(v,l)
+#define va_copy(d,s)    __builtin_va_copy(d,s)
diff --git a/tests/invader/include/stdbits.h b/tests/invader/include/stdbits.h
new file mode 100644
index 0000000000000000000000000000000000000000..298538137a5267fb77690bc66496d06380edda22
--- /dev/null
+++ b/tests/invader/include/stdbits.h
@@ -0,0 +1,63 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+#define clz(val) ({                         \
+    int result;                             \
+    switch(sizeof(val)) {                   \
+    case 1: result = clz8(val); break;      \
+    case 2: result = clz16(val); break;     \
+    case 4: result = clz32(val); break;     \
+    case 8: result = clz64(val); break;     \
+    }                                       \
+    result;                                 \
+})
+
+#define ctz(val) ({                         \
+    int result;                             \
+    switch(sizeof(val)) {                   \
+    case 1: result = ctz8(val); break;      \
+    case 2: result = ctz16(val); break;     \
+    case 4: result = ctz32(val); break;     \
+    case 8: result = ctz64(val); break;     \
+    }                                       \
+    result;                                 \
+})
+
+int clz8(int8_t val);
+int clz16(int16_t val);
+int clz32(int32_t val);
+int clz64(int64_t val);
+
+int ctz8(int8_t val);
+int ctz16(int16_t val);
+int ctz32(int32_t val);
+int ctz64(int64_t val);
+
+static inline int ispow2(uintptr_t val)
+{
+    return val && !(val & (val-1));
+}
+
+static inline uintptr_t roundpow2(uintptr_t val)
+{
+    val--;
+    val |= val >> 1;
+    val |= val >> 2;
+    val |= val >> 4;
+    val |= val >> 8;
+    val |= val >> 16;
+#if __SIZE_WIDTH__ == 64
+    val |= val >> 32;
+#endif
+    val++;
+    return val;
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/tests/invader/include/stddef.h b/tests/invader/include/stddef.h
new file mode 100644
index 0000000000000000000000000000000000000000..3799f2a275a271442783bbc91e82a0caf24630dd
--- /dev/null
+++ b/tests/invader/include/stddef.h
@@ -0,0 +1,8 @@
+#pragma once
+
+typedef __PTRDIFF_TYPE__ ptrdiff_t;
+typedef __SIZE_TYPE__ size_t;
+typedef __WCHAR_TYPE__ wchar_t;
+
+#define NULL 0L
+#define offsetof(type, member) __builtin_offsetof(type, member)
diff --git a/tests/invader/include/stdint.h b/tests/invader/include/stdint.h
new file mode 100644
index 0000000000000000000000000000000000000000..bef5c2f2ed13727244f26a9b883e040a51c01534
--- /dev/null
+++ b/tests/invader/include/stdint.h
@@ -0,0 +1,52 @@
+#pragma once
+
+typedef __INT8_TYPE__ int8_t;
+typedef __INT16_TYPE__ int16_t;
+typedef __INT32_TYPE__ int32_t;
+typedef __INT64_TYPE__ int64_t;
+
+typedef __INT_FAST8_TYPE__ int_fast8_t;
+typedef __INT_FAST16_TYPE__ int_fast16_t;
+typedef __INT_FAST32_TYPE__ int_fast32_t;
+typedef __INT_FAST64_TYPE__ int_fast64_t;
+
+typedef __INT_LEAST16_TYPE__ int_least16_t;
+typedef __INT_LEAST32_TYPE__ int_least32_t;
+typedef __INT_LEAST64_TYPE__ int_least64_t;
+typedef __INT_LEAST8_TYPE__ int_least8_t;
+
+typedef __INTMAX_TYPE__ intmax_t;
+typedef __INTPTR_TYPE__ intptr_t;
+
+typedef __UINT8_TYPE__ uint8_t;
+typedef __UINT16_TYPE__ uint16_t;
+typedef __UINT32_TYPE__ uint32_t;
+typedef __UINT64_TYPE__ uint64_t;
+
+typedef __UINT_FAST8_TYPE__ uint_fast8_t;
+typedef __UINT_FAST16_TYPE__ uint_fast16_t;
+typedef __UINT_FAST32_TYPE__ uint_fast32_t;
+typedef __UINT_FAST64_TYPE__ uint_fast64_t;
+
+typedef __UINT_LEAST8_TYPE__ uint_least8_t;
+typedef __UINT_LEAST16_TYPE__ uint_least16_t;
+typedef __UINT_LEAST32_TYPE__ uint_least32_t;
+typedef __UINT_LEAST64_TYPE__ uint_least64_t;
+
+typedef __UINTMAX_TYPE__ uintmax_t;
+typedef __UINTPTR_TYPE__ uintptr_t;
+
+#define INT8_MAX __INT8_MAX__
+#define INT16_MAX __INT16_MAX__
+#define INT32_MAX __INT32_MAX__
+#define INT64_MAX __INT64_MAX__
+
+#define UINT8_MAX  __UINT8_MAX__
+#define UINT16_MAX __UINT16_MAX__
+#define UINT32_MAX __UINT32_MAX__
+#define UINT64_MAX __UINT64_MAX__
+
+#define INTPTR_MAX __INTPTR_MAX__
+#define UINTPTR_MAX __UINTPTR_MAX__
+#define PTRDIFF_MAX __PTRDIFF_MAX__
+#define SIZE_MAX __SIZE_MAX__
diff --git a/tests/invader/include/stdio.h b/tests/invader/include/stdio.h
new file mode 100644
index 0000000000000000000000000000000000000000..f97e6e14d655647a57e6bb310b4c9834207e05ef
--- /dev/null
+++ b/tests/invader/include/stdio.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdarg.h>
+#include <stddef.h>
+
+int getchar(void);
+int printf(const char *, ...);
+int putchar(int);
+int puts(const char *);
+int snprintf(char *, size_t, const char *, ...);
+int vprintf(const char *, va_list);
+int vsnprintf(char *, size_t, const char *, va_list);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/tests/invader/include/stdlib.h b/tests/invader/include/stdlib.h
new file mode 100644
index 0000000000000000000000000000000000000000..7552a1c4cf5440d09c2bbda02506a65e879d4bd6
--- /dev/null
+++ b/tests/invader/include/stdlib.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+
+__attribute__((noreturn)) void abort(void);
+__attribute__((noreturn)) void exit(int status);
+void* malloc(size_t size);
+void free(void* ptr);
+void _malloc_addblock(void* addr, size_t size);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/tests/invader/include/string.h b/tests/invader/include/string.h
new file mode 100644
index 0000000000000000000000000000000000000000..6572d23e59556b519a1299091766d44d50737bd7
--- /dev/null
+++ b/tests/invader/include/string.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+
+void *memchr(const void *s, int c, size_t n);
+int memcmp(const void *, const void *, size_t);
+void *memcpy(void *, const void *, size_t);
+void *memset(void *, int, size_t);
+char *strchr(const char *s, int c);
+int strcmp(const char *, const char *);
+size_t strlen(const char *);
+int strncmp(const char *, const char *, size_t);
+char *strncpy(char *, const char *, size_t);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/tests/invader/invader.e b/tests/invader/invader.e
new file mode 100644
index 0000000000000000000000000000000000000000..cf0fbb4dac7d64f2df35a81f42ac1bf304b57664
--- /dev/null
+++ b/tests/invader/invader.e
@@ -0,0 +1,475 @@
+int push_button_get();
+void timer_set_and_wait(int period, int time);
+void clear_screen(int color);
+int read_pixel(int x, int y, int scale);
+void write_pixel_scaling(int pixel, int x, int y, int scale);
+void set_bg_color(int color);
+void set_fg_color(int color);
+void set_display_cur_pos(int x, int y);
+void display_string(char *msg);
+void set_display_scale(int s);
+int scale = 4;
+int TIMER_FREQ = 10000000;
+
+void dirty_exit(){
+  int x;
+  *(&x+1000000000) = 42;
+}
+
+/* Etats */
+
+struct etat {
+	int dx;
+	int dy;
+	int etat_suivant;
+};
+
+struct etat etats[5];
+
+void init_etat(int index, int dx, int dy, int next){
+  (etats[index]).dx = dx;
+  (etats[index]).dy = dy;
+  (etats[index]).etat_suivant = next;
+}
+
+/* Objets */
+
+struct Object {
+	int alive;
+	int period;
+	int deadline;
+	int x;
+  int y;
+	int dx;
+  int dy;
+	char *pattern;
+	int color;
+	int bg[64]; /* background */
+	int ax;
+  int ay;
+};
+
+struct Object object[7];
+
+int NOBJ = 7;
+
+void init_object(int index, int alive, int period, int deadline,
+                 int x, int y, int dx, int dy, char* pattern, int color){
+  (object[index]).alive = alive;
+  (object[index]).period = period;
+  (object[index]).deadline = deadline;
+  (object[index]).x = x;
+  (object[index]).y = y;
+  (object[index]).dx = dx;
+  (object[index]).dy = dy;
+  (object[index]).pattern = pattern;
+  (object[index]).color = color;
+}
+
+/* Sprites */
+
+char sprite_sship[8];
+char sprite_laser[8];
+char sprite_alien1[8];
+char sprite_alien2[8];
+char sprite_alien3[8];
+char sprite_alien4[8];
+char sprite_alien5[8];
+
+void init_sprite(char* t,  char a0, char a1, char a2, char a3,
+                 char a4, char a5, char a6, char a7){
+  t[0] = a0;
+  t[1] = a1;
+  t[2] = a2;
+  t[3] = a3;
+  t[4] = a4;
+  t[5] = a5;
+  t[6] = a6;
+  t[7] = a7;
+}
+
+void initialize()
+{
+	int i;
+  int dx;
+  int dy;
+	clear_screen(0x333333);
+  i = 0;
+	while(i < NOBJ) {
+		if (i == 1) {
+			/* laser */
+			(object[i]).alive = 0;
+			(object[i]).period = 1;
+		} else {
+			/* spaceship or aliens */
+			(object[i]).alive = 1;
+			if (i == 0){
+				/* spaceship */
+				(object[i]).period = 3;
+      }
+			else{
+				/* aliens */
+				(object[i]).period = 4;
+      }
+		}
+		(object[i]).deadline = 1;
+		if (i > 1) {
+			/* aliens */
+			if (i > 4) {
+				/* alien4 or alien5 */
+				(object[i]).y = 3; /* 3rd line */
+				(object[i]).x = 6 + (i - 4) * 8 ;
+			} else {
+
+				/* alien1, alien2 or alien3 */
+				(object[i]).y = 1; /* 1st line */
+				(object[i]).x = 10 + (i - 2) * 8;
+			}
+			(object[i]).dx = -1;
+			(object[i]).dy = 0;
+		}
+		(object[i]).ax = -1;
+		(object[i]).ay = -1;
+
+		/* initialization of object background considering the last one */
+    int* ptr = (object[i]).bg;
+    dx = 0;
+		while (dx < 8){
+      dy = 0;
+			while (dy < 8){
+        int p = read_pixel((((object[i]).x) * 8) + dx,
+                           (((object[i]).y) * 8) + dy, scale);
+				ptr[dx*8+dy] = p;
+        dy = dy + 1;
+      }
+      dx = dx + 1;
+    }
+    i = i + 1;
+	}
+}
+
+/* function to display the 8 pixels of a pattern line */
+void display_pattern_line(int m, int x, int y, int color)
+{
+	int i = 0;
+
+	while (i < 8){
+    if ((m & 1) == 1){
+      write_pixel_scaling(color, x + i, y, scale);
+    }
+		m = m / 2;
+    i = i + 1;
+	}
+}
+
+/* function to display an 8x8 object considering the last background */
+void display_pattern(char* pattern, int x, int y, int color)
+{
+	int i = 0;
+
+  while(i < 8){
+		display_pattern_line(pattern[i], x, y + i, color);
+    i = i + 1;
+  }
+}
+
+/* function to display an 8x8 object (spaceship, laser or alien) */
+void display_sprite(struct Object *object)
+{
+	int dx; int dy;
+	if ((object->ax > -1 && object->ay > -1) &&
+      (object->x != object->ax || object->y != object->ay || !(*object).alive))
+    {
+      int* ptr = object->bg;
+      dx = 0;
+      while(dx < 8) {
+        dy = 0;
+        while(dy < 8) {
+          write_pixel_scaling(ptr[dx*8+dy],
+                              ((object->ax) *8) + dx, ((object->ay) *8 ) + dy, scale);
+          if (!object->alive){
+            ptr[dx*8 + dy] = read_pixel(((object->x) *8) + dx,
+                                        ((object->y) *8) + dy, scale);
+          }
+          dy = dy + 1;
+        }
+        dx = dx + 1;
+      }
+    }
+	object->ax = object->x;
+	object->ay = object->y;
+	if ((*object).alive){
+		display_pattern(object->pattern, (object->x) * 8, (object->y) * 8,
+		                object->color);
+  }
+}
+
+int main(int argc, char* argv)
+{
+	/* declaration of local variables */
+	int i;
+  int dx;
+  int dy;
+	int push_state;
+  int alien_state;
+  int edge_reached;
+	int n_aliens;
+  struct Object *spaceship;
+  struct Object *laser;
+
+  init_etat(0, 0, 1, 1);
+  init_etat(1, 0, 1, 2);
+  init_etat(2, 1, 0, 3);
+  init_etat(3, 0, 1, 4);
+  init_etat(4, -1, 0, 1);
+
+  init_sprite(sprite_sship,  0x00, 0x3c, 0x7e, 0xff, 0xff, 0xe7, 0xc3, 0xc3);
+  init_sprite(sprite_laser,  231, 231, 255, 255, 126, 60, 24, 24);
+  init_sprite(sprite_alien1, 0xc3, 0x3c, 0x5a, 0xff, 0xff, 0x81, 0x42, 0x24);
+  init_sprite(sprite_alien2, 0xc3, 0x3c, 0x5a, 0xff, 0xff, 0xa5, 0xa5, 0x5a);
+  init_sprite(sprite_alien3, 0x42, 0x24, 0x3c, 0x5a, 0xff, 0xbd, 0x81, 0x42);
+  init_sprite(sprite_alien4, 0x81, 0x42, 0x3c, 0x5a, 0x5a, 0x3c, 0x42, 0x81);
+  init_sprite(sprite_alien5, 0x41, 0x22, 0x3e, 0x6b, 0x49, 0x7f, 0x3e, 0x55);
+  /*
+    0x41  .#....#.
+    0x22  ..#...#.
+    0x3e  ..#####.
+    0x6b  .##.#.##
+    0x49  .#..#..#
+    0x7f  .#######
+    0x3e  ..#####.
+    0x55  .#.#.#.#
+   */
+  init_object(0, 1, 3, 1, 18, 32, 0, 0, sprite_sship,  0x0000FF); /* blue spaceship */
+  init_object(1, 0, 1, 1, 18, 0,  0, 0, sprite_laser,  0xffc0cb); /* white laser */
+  init_object(2, 1, 4, 1, 10, 1, -1, 0, sprite_alien1, 0x00FF00); /* green alien */
+  init_object(3, 1, 4, 1, 18, 1, -1, 0, sprite_alien2, 0xFF0000); /* red alien */
+  init_object(4, 1, 4, 1, 26, 1, -1, 0, sprite_alien3, 0xFF00FF); /* magenta alien */
+  init_object(5, 1, 4, 1, 14, 3, -1, 0, sprite_alien4, 0xFFFF00); /* yellow alien */
+  init_object(6, 1, 4, 1, 22, 3, -1, 0, sprite_alien5, 0x00FFFF);  /* cyan alien */
+
+	/* initialization stage */
+	push_state = 0;           /* no button pressed at beginning */
+	alien_state = 0;          /* state of alien in a line */
+	edge_reached = 0;         /* no edge reached at beginning */
+	n_aliens = NOBJ - 2; /* number of displayed aliens */
+	spaceship = object;   /* spaceship is the first declared object */
+	laser = object + 1;       /* laser is the second declared object */
+
+
+
+  clear_screen(0xcccccc);
+  set_display_scale(8);
+  set_bg_color(0xcccccc);
+  set_fg_color(0x000000);
+  int started = 0;
+  while(!started){
+    char msg[60];
+    i = 0;
+    msg[i] = 'P'; i = i + 1;
+    msg[i] = 'o'; i = i + 1;
+    msg[i] = 'u'; i = i + 1;
+    msg[i] = 'r'; i = i + 1;
+    msg[i] = ' '; i = i + 1;
+    msg[i] = 'c'; i = i + 1;
+    msg[i] = 'o'; i = i + 1;
+    msg[i] = 'm'; i = i + 1;
+    msg[i] = 'm'; i = i + 1;
+    msg[i] = 'e'; i = i + 1;
+    msg[i] = 'n'; i = i + 1;
+    msg[i] = 'c'; i = i + 1;
+    msg[i] = 'e'; i = i + 1;
+    msg[i] = 'r'; i = i + 1;
+    msg[i] = ','; i = i + 1;
+    msg[i] = ' '; i = i + 1;
+    msg[i] = 'a'; i = i + 1;
+    msg[i] = 'p'; i = i + 1;
+    msg[i] = 'p'; i = i + 1;
+    msg[i] = 'u'; i = i + 1;
+    msg[i] = 'y'; i = i + 1;
+    msg[i] = 'e'; i = i + 1;
+    msg[i] = 'z'; i = i + 1;
+    msg[i] = ' '; i = i + 1;
+    msg[i] = 's'; i = i + 1;
+    msg[i] = 'u'; i = i + 1;
+    msg[i] = 'r'; i = i + 1;
+    msg[i] = ' '; i = i + 1;
+    msg[i] = 'H'; i = i + 1;
+    msg[i] = 'A'; i = i + 1;
+    msg[i] = 'U'; i = i + 1;
+    msg[i] = 'T'; i = i + 1;
+    msg[i] = '\n'; i = i + 1;
+    msg[i] = '\n'; i = i + 1;
+    msg[i] = 'P'; i = i + 1;
+    msg[i] = 'o'; i = i + 1;
+    msg[i] = 'u'; i = i + 1;
+    msg[i] = 'r'; i = i + 1;
+    msg[i] = ' '; i = i + 1;
+    msg[i] = 'q'; i = i + 1;
+    msg[i] = 'u'; i = i + 1;
+    msg[i] = 'i'; i = i + 1;
+    msg[i] = 't'; i = i + 1;
+    msg[i] = 't'; i = i + 1;
+    msg[i] = 'e'; i = i + 1;
+    msg[i] = 'r'; i = i + 1;
+    msg[i] = ','; i = i + 1;
+    msg[i] = ' '; i = i + 1;
+    msg[i] = 'a'; i = i + 1;
+    msg[i] = 'p'; i = i + 1;
+    msg[i] = 'p'; i = i + 1;
+    msg[i] = 'u'; i = i + 1;
+    msg[i] = 'y'; i = i + 1;
+    msg[i] = 'e'; i = i + 1;
+    msg[i] = 'z'; i = i + 1;
+    msg[i] = ' '; i = i + 1;
+    msg[i] = 's'; i = i + 1;
+    msg[i] = 'u'; i = i + 1;
+    msg[i] = 'r'; i = i + 1;
+    msg[i] = ' '; i = i + 1;
+    msg[i] = 'B'; i = i + 1;
+    msg[i] = 'A'; i = i + 1;
+    msg[i] = 'S'; i = i + 1;
+    msg[i] = 0; i = i + 1;
+
+
+    set_display_cur_pos(10,10);
+    display_string(msg);
+
+    push_state = push_button_get();
+    if (push_state & 0x4) {
+      started = 1;
+    }
+    if (push_state & 0x8) {
+      dirty_exit();
+    }
+  }
+
+  clear_screen(0x333333);
+
+  initialize();
+
+	/* display stage */
+	while(1==1) {
+		edge_reached=0;
+
+    /* decrease deadline of alive objects */
+    i = 0;
+		while (i < NOBJ) {
+			if ((object[i]).alive == 1){
+				(object[i]).deadline = (object[i]).deadline - 1;
+      }
+      i = i + 1;
+		}
+
+		/* display all alive objects */
+    i = 0;
+		while (i < NOBJ) {
+			if ((object[i]).alive == 1){
+				display_sprite(object + i);
+      }
+      i = i + 1;
+		}
+
+		/* determine new positions of all alive objects */
+    i = 0;
+		while(i < NOBJ) {
+			/* update object state when deadline is reached */
+			if ((object[i]).alive == 1 && (object[i]).deadline == 0) {
+				/* reinitialize the object deadline to period */
+				(object[i]).deadline = (object[i]).period;
+				/* determine new position and manage screen edges */
+				(object[i]).x = (object[i]).x + (object[i]).dx;
+				if ((object[i]).x < 0){
+					(object[i]).x = 0;
+        }
+				if ((object[i]).x > 59){
+					(object[i]).x = 59;
+        }
+				(object[i]).y = (object[i]).y + (object[i]).dy;
+				/* test if an edge of the screen was reached by an alien */
+				if (i >= 2 && ((object[i]).x == 0 || (object[i]).x == 59)){
+					edge_reached = 1;
+        }
+				if (i > 1 && (object[i]).y >= spaceship->y){
+          // PERDU
+					clear_screen(0x0000FF); /* blue screen */
+          timer_set_and_wait(TIMER_FREQ, 1000);
+          dirty_exit();
+				}
+			}
+      i = i + 1;
+		}
+    /* test if alien is hit by an alive laser */
+		if (laser->alive) {
+      i = 2;
+			while(i < NOBJ) {
+				if ((object[i]).alive && !((laser->x > (object[i]).x + 1) || (laser->x + 1 < (object[i]).x)) &&
+            (laser->y) == (object[i]).y) {
+					n_aliens = n_aliens - 1;
+					(object[i]).alive = 0;
+					laser->alive = 0;
+					if (n_aliens == 0) {
+						/* no more aliens */
+						spaceship->alive = 0;
+						clear_screen(0xFF00); /* yellow screen */
+            timer_set_and_wait(TIMER_FREQ, 1000);
+            push_state = 0;           /* no button pressed at beginning */
+            alien_state = 0;          /* state of alien in a line */
+            edge_reached = 0;         /* no edge reached at beginning */
+            n_aliens = NOBJ - 2; /* number of displayed aliens */
+            spaceship = object;   /* spaceship is the first declared object */
+            laser = object + 1;       /* laser is the second declared object */
+            initialize();
+					} else {
+						display_sprite(object + i);
+						display_sprite(laser);
+					}
+				}
+        i = i + 1;
+			}
+		}
+    /* when an alien reaches a screen edge, the group of aliens is moved */
+		if (edge_reached) {
+      i = 2;
+			while (i < NOBJ) {
+				(object[i]).dx = (etats[alien_state]).dx;
+				(object[i]).dy = (etats[alien_state]).dy;
+        i = i + 1;
+			}
+			alien_state = (etats[alien_state]).etat_suivant;
+		}
+    /* laser disappears when it reaches the screen top */
+		if (laser->alive && laser->y == 0) {
+			laser->alive = 0;
+			display_sprite(laser);
+		}
+		/* manage push buttons */
+    push_state = push_button_get();
+
+		if (   (spaceship->deadline == 1)
+           || (n_aliens == 0)) {
+			spaceship->dx = 0;
+			if (push_state & 0x1) { // right
+        spaceship->dx = 1;
+      }
+			if (push_state & 0x2){ // left
+				spaceship->dx = -1;
+      }
+			if (push_state & 0x4) { // fire
+				if (!laser->alive) {
+					laser->alive = 1;
+					laser->dx = 0;
+					laser->dy = -1;
+					laser->x = spaceship->x;
+					laser->y = spaceship->y - 1;
+					laser->deadline = laser->period;
+				}
+			}
+      if (push_state & 0x8) {
+        dirty_exit();
+			}
+		}
+
+    timer_set_and_wait(TIMER_FREQ, 4);
+	}
+}
diff --git a/tests/invader/itoa.c b/tests/invader/itoa.c
new file mode 100644
index 0000000000000000000000000000000000000000..e172978434ea4bd52d4138baa98d45495b83273e
--- /dev/null
+++ b/tests/invader/itoa.c
@@ -0,0 +1,34 @@
+#include<stdio.h>
+
+char * itoa(signed int i, char* buf){
+  char *s = buf + 10;
+  *s = '\0';
+  char neg = 0;
+  if (i < 0) {
+	  neg = 1;
+	  i = - i;
+  }
+  if (i == 0){
+	  *--s = '0';
+	  return s;
+  }
+  while(i > 0){
+	
+	*--s = (i % 10) + '0';
+	i = i / 10;
+  }
+  if (neg){
+	  *--s = '-';
+  }
+  return s;
+}
+
+/* int main(){ */
+/* char buf[20] = "abcdefghijklmnopqrst"; */
+/*  printf("itoa(-23467) = %s\n", itoa(-23467,buf)); */
+/*  printf("itoa(1238) = %s\n", itoa(1238,buf)); */
+/*  printf("itoa(0) = %s\n", itoa(0,buf)); */
+/*  printf("itoa(-0) = %s\n", itoa(-0,buf)); */
+
+/* 	return 0; */
+/* } */
diff --git a/tests/invader/libfemto.a b/tests/invader/libfemto.a
new file mode 100644
index 0000000000000000000000000000000000000000..b444d6e284575d5cd280a9d8048f56eb1e91f985
Binary files /dev/null and b/tests/invader/libfemto.a differ
diff --git a/tests/invader/libscreen.c b/tests/invader/libscreen.c
new file mode 100644
index 0000000000000000000000000000000000000000..a0defd7b546ad0dd18590d419391e9266a7bbeac
--- /dev/null
+++ b/tests/invader/libscreen.c
@@ -0,0 +1,218 @@
+#include "femto.h"
+#include "libscreen.h"
+#include "cep_platform.h"
+
+typedef unsigned int uint;
+
+volatile uint* IMG  = (volatile uint*) 0x80000000;
+volatile uint *push = (volatile uint *)0x30000008;
+volatile uint* led = (volatile uint *)REG_LEDS_ADDR;
+volatile uint *timer     = (volatile uint *)CLINT_TIMER;
+volatile uint *timer_hi =  (volatile uint *)CLINT_TIMER_HI;
+volatile uint *timer_lo =  (volatile uint *)CLINT_TIMER_LOW;
+volatile uint *timer_cmp = (volatile uint *)CLINT_TIMER_CMP;
+volatile uint *timer_cmp_hi = (volatile uint *)CLINT_TIMER_CMP_HI;
+volatile uint *timer_cmp_lo = (volatile uint *)CLINT_TIMER_CMP_LO;
+
+
+
+/* function to get the state of push buttons */
+uint push_button_get(void)
+{
+  uint v = (*push) >> 16;
+  //printf("push_button_get: @ %p, v = %x, %x\n", push, *push, v);
+  return v;
+}
+
+
+/* function to set the value displayed on leds */
+void led_set(uint value)
+{
+
+	*led = value;
+}
+
+/* function to set the timer to be reached in period*time/100 in the future */
+void timer_set(uint period, uint time)
+{
+  uint now = *timer;
+  *timer_cmp = now + ((uint)period/RATIO * time);
+}
+
+/* function to wait for timer zero value */
+void timer_wait(void)
+{
+	while(*timer <=  *timer_cmp);
+}
+
+void timer_set_and_wait(uint period, uint time)
+{
+  timer_set(period, time);
+  timer_wait();
+}
+
+void draw(uint color, uint x, uint y){
+  uint pos = (y * NBCOL + x);
+  if (pos < 1920 * 1080){
+    *(IMG + pos) = color;
+  }
+}
+
+void clear_screen(uint color){
+  for(uint j = 0; j < NBROW; j++){
+    for(uint i = 0; i < NBCOL; i++){
+      draw(color, i, j);
+    }
+  }
+}
+
+void draw_bitmap(char* bitmap){
+
+  for(uint j = 0; j < 8; j++){
+    for(uint i = 0; i < 8; i++){
+      uint set = (bitmap[j] & (1 << i)) >> i;
+      //draw pixel bitmap[j][i]
+      for(uint jy = 0; jy < display_scale; jy++){
+        for(uint ix = 0; ix < display_scale; ix++){
+          uint realx = display_cur_x + i * display_scale + ix;
+          uint realy = display_cur_y + j * display_scale + jy;
+          /* pruintf("Writing at %d, %d, color = %x\n", realx, realy, color); */
+          if(set){
+            draw(fgcolor, realx, realy);
+          }
+          else{
+            draw(bgcolor, realx, realy);
+          }
+        }
+      }
+    }
+  }
+}
+
+extern char* itoa(uint, char*);
+
+void newline(){
+  display_cur_x = 0;
+  display_cur_y += display_scale*10;
+}
+void tab(){
+  display_cur_x += display_scale*8*4;
+}
+
+/* Counts the number of characters of current word. Will be used to break lines, if possible not in the middle of words. */
+uint num_characters_until_white(char* str){
+  uint i = 0;
+  char c;
+  while(c = *str++){
+    if (c == ' ' || c == '\t' || c == '\n'){
+      return i;
+    }
+    if (c == '.' || c == ','){
+      return i + 1;
+    }
+    i++;
+  }
+  return i;
+}
+
+
+
+void display_string(char* str){
+  while(*str){
+    uint n = num_characters_until_white(str) + 1;
+    // If there's not enough space on current line for whole current word, newline
+    if (display_cur_x + (n-1) * display_scale * 8 > 1920){
+      newline();
+    }
+    for(uint i = 0; i < n; i++){
+      char c = str[i];
+      if(c == '\n'){
+        newline();
+      } else if (c == '\t'){
+        tab();
+      } else {
+        draw_bitmap(font8x8_basic[c]);
+        display_cur_x+=display_scale*8;
+      }
+      // Still, if the next character wouldn't fit on the screen, break in the middle of the word.
+      if (display_cur_x + display_scale * 8 > 1920){
+        newline();
+      }
+    }
+    str = str + n;
+  }
+}
+
+void display_uint(uint i){
+  char buf[10], *bu;
+  bu = itoa(i, buf);
+  display_string(bu);
+}
+
+void set_display_scale(int s){
+  display_scale = s;
+}
+void set_display_cur_pos(int x, int y){
+  display_cur_x = x;
+  display_cur_y = y;
+}
+
+
+void set_fg_color(uint color){
+  fgcolor = color;
+}
+void set_bg_color(uint color){
+  bgcolor = color;
+}
+
+
+
+/* function to read a pixel from a (x,y) position of video framebuffer */
+uint read_pixel(uint x, uint y, uint scale)
+{
+  // #SCALING
+  //return IMG[y * DISPLAY_WIDTH + x];
+	const uint pos = y * scale * NBCOL + x * scale;
+  if (pos < 1920*1080)
+    return IMG[pos];
+  return -1;
+}
+
+/* function to write a pixel in a (x,y) position of video framebuffer */
+void write_pixel(uint pixel, uint x, uint y)
+{
+  const uint pos = y * NBCOL + x;
+  if (pos < 1920*1080)
+  IMG[pos] = pixel;
+}
+
+void write_pixel_scaling(uint pixel, uint x, uint y, uint scale)
+{
+  uint i, j;
+	for (i = 0; i < scale; ++i) {
+		for (j = 0; j < scale; ++j) {
+			const uint real_y = (y * scale + i);
+			const uint real_x = x * scale + j;
+
+      write_pixel(pixel, real_x, real_y);
+		}
+	}
+
+}
+
+void show_pos ( int i, int x, int y){
+  /* printf("show_pos %d: @ %p, v = %x\n",x,  ptr, *(int*)(ptr+20)); */
+  /* printf("alive = %x\n", *(int*)(ptr+ 0 )); */
+  /* printf("period = %x\n", *(int*)(ptr+ 4 )); */
+  /* printf("deadline = %x\n", *(int*)(ptr+ 8 )); */
+  /* printf("x = %x\n", *(int*)(ptr+ 12 )); */
+  /* printf("y = %x\n", *(int*)(ptr+ 16 )); */
+  /* printf("dx = %x\n", *(int*)(ptr+ 20 )); */
+  /* printf("dy = %x\n", *(int*)(ptr+ 24 )); */
+  /* int i; */
+  /* asm("\t mv %0, sp" : "=r"(i)); */
+  /* printf("sp = %x\n", i); */
+  /* asm("\t mv %0, s0" : "=r"(i)); */
+  /* printf("s0 = %x\n", i); */
+  printf("i = %d, x = %d; y = %d\n",i, x,y);
+}
diff --git a/tests/invader/macros.s b/tests/invader/macros.s
new file mode 100644
index 0000000000000000000000000000000000000000..d987d0c1ec596dc45e95cbf0e7a9c69d5ff614b9
--- /dev/null
+++ b/tests/invader/macros.s
@@ -0,0 +1,23 @@
+# See LICENSE for license details.
+
+.equ REGBYTES, 4
+
+.macro lx a, b
+lw \a, \b
+.endm
+
+.macro sx a, b
+sw \a, \b
+.endm
+
+.macro lxsp a, b
+lw \a, ((\b)*REGBYTES)(sp)
+.endm
+
+.macro sxsp a, b
+sw \a, ((\b)*REGBYTES)(sp)
+.endm
+
+.macro .ptr a
+.4byte \a
+.endm
diff --git a/tests/invader/setup.c b/tests/invader/setup.c
new file mode 100644
index 0000000000000000000000000000000000000000..8a684ad365780ff73c6df52c38cc3c7a1d0c1412
--- /dev/null
+++ b/tests/invader/setup.c
@@ -0,0 +1,19 @@
+// See LICENSE for license details.
+
+#include "femto.h"
+
+auxval_t __auxv[] = {
+    { UART0_CLOCK_FREQ,         32000000   },
+    { UART0_BAUD_RATE,          115200     },
+    { SIFIVE_UART0_CTRL_ADDR,   0x10013000 },
+    { SIFIVE_TEST_CTRL_ADDR,    0x100000   },
+    { 0, 0 }
+};
+
+void arch_setup()
+{
+#if defined(ENV_QEMU) 
+    register_console(&console_sifive_uart);
+    register_poweroff(&poweroff_sifive_test);
+#endif
+}