diff --git a/epicardium/l0der/l0der.c b/epicardium/l0der/l0der.c
index 93a0ecba597bcaa8cef0af29a0362fddb71cdf87..0051988c28fd0cfe9eecaa4eec82df8f6270b7c1 100644
--- a/epicardium/l0der/l0der.c
+++ b/epicardium/l0der/l0der.c
@@ -1,5 +1,6 @@
 #include "l0der/l0der.h"
 
+#include <alloca.h>
 #include <stdio.h>
 #include <string.h>
 #include <ff.h>
@@ -8,6 +9,25 @@
 #include "l0der/elf.h"
 #include "modules/log.h"
 
+/*
+ * l0der is, in reality, a boneless operating-system style ELF loader.
+ *
+ * To implement it, we parse an ELF file somewhat defensively, trying to
+ * not DoS ourselves by overallocating RAM (no heap allocations, no recursion).
+ * 
+ * Currently we support only relocatable, PIE binaries. Adding support for
+ * static ELFs would be trivial, however we want to keep the possibility to
+ * shuffle around memory areas in future versions of card10 (possibly giving
+ * l0dables more RAM than 256k) without having to recompile all l0dables. We
+ * are also keeping the opportunity to have separate loading schemes in the
+ * future, for instance:
+ *  - l0dables running next to pycardium, without unloading it
+ *  - multiple l0dables running next to each other (TSR-style)
+ *
+ * Thus, we use PIE l0dables to keep these possibilities open and not write down
+ * a memory map in stone.
+ */
+
 /*
  * Read an ELF header, check E_IDENT.
  */
@@ -59,20 +79,31 @@ static int _read_elf_header(FIL *fp, Elf32_Ehdr *hdr)
 }
 
 /*
- * Read an ELF program header header.
+ * Read bytes from file at a given offset.
+ *
+ * :param FIL* fp: file pointer to read from
+ * :param uint32_t address: address from which to read
+ * :param void *data: buffer into which to read
+ * :param size_t count: amount of bytes to read
+ * :returns: ``0`` on success or a negative value on error.  Possible errors:
+ * 
+ *    - ``-EIO``: Could not read from FAT - address out of bounds of not enough bytes available.
  */
-static int _read_program_header(FIL *fp, uint32_t phdr_addr, Elf32_Phdr *phdr)
-{
+static int _seek_and_read(FIL *fp, uint32_t address, void *data, size_t count) {
 	FRESULT fres;
 
-	if ((fres = f_lseek(fp, phdr_addr)) != FR_OK) {
-		LOG_ERR("l0der", "_read_program_header: could not seek to 0x%lx: %d", phdr_addr, fres);
+	if ((fres = f_lseek(fp, address)) != FR_OK) {
+		LOG_ERR("l0der", "_seek_and_read: could not seek to 0x%lx: %d", address, fres);
 		return -EIO;
 	}
 
 	unsigned int read;
-	if ((fres = f_read(fp, phdr, sizeof(Elf32_Phdr), &read)) != FR_OK || read < sizeof(Elf32_Phdr)) {
-		LOG_ERR("l0der", "_read_program_header: could not read phdr: %d", fres);
+	if ((fres = f_read(fp, data, count, &read)) != FR_OK || read < count) {
+		if (fres == FR_OK) {
+			LOG_ERR("l0der", "_seek_and_read: could not read: wanted %d bytes, got %d", count, read);
+		} else {
+			LOG_ERR("l0der", "_seek_and_read: could not read: %d", fres);
+		}
 		return -EIO;
 	}
 
@@ -82,23 +113,17 @@ static int _read_program_header(FIL *fp, uint32_t phdr_addr, Elf32_Phdr *phdr)
 /*
  * Read an ELF program header header.
  */
-static int _read_section_header(FIL *fp, uint32_t shdr_addr, Elf32_Shdr *shdr)
+static int _read_program_header(FIL *fp, uint32_t phdr_addr, Elf32_Phdr *phdr)
 {
-	FRESULT fres;
-
-	if ((fres = f_lseek(fp, shdr_addr)) != FR_OK) {
-		LOG_ERR("l0der", "_read_section_header: could not seek to 0x%lx: %d", shdr_addr, fres);
-		return -EIO;
-	}
-
-	unsigned int read;
-	if ((fres = f_read(fp, shdr, sizeof(Elf32_Shdr), &read)) != FR_OK || read < sizeof(Elf32_Shdr)) {
-		LOG_ERR("l0der", "_read_section_header: could not read shdr (0x%x bytes) at %08lx: %d, got 0x%x bytes",
-				sizeof(Elf32_Shdr), shdr_addr, fres, read);
-		return -EIO;
-	}
+	return _seek_and_read(fp, phdr_addr, phdr, sizeof(Elf32_Phdr));
+}
 
-	return 0;
+/*
+ * Read an ELF section header header.
+ */
+static int _read_section_header(FIL *fp, uint32_t shdr_addr, Elf32_Shdr *shdr)
+{
+	return _seek_and_read(fp, shdr_addr, shdr, sizeof(Elf32_Shdr));
 }
 
 /*
@@ -165,30 +190,25 @@ static int _check_section_header(FIL *fp, Elf32_Shdr *shdr) {
 	return 0;
 }
 
+/*
+ * Interpreter expected in l0dable PIE binaries.
+ */
 static const char *_interpreter = "card10-l0dable";
 
+/*
+ * Check that the given INTERP program header contains the correct interpreter string.
+ */
 static int _check_interp(FIL *fp, Elf32_Phdr *phdr)
 {
-	uint32_t buffer_size = 64;
-	char interp[buffer_size];
+	int res;
+	uint32_t buffer_size = strlen(_interpreter) + 1;
+	char *interp = alloca(buffer_size);
 	memset(interp, 0, buffer_size);
 
-	if (phdr->p_filesz > buffer_size) {
-		LOG_ERR("l0der", "_check_interp: interpreter size too large");
-		return -1;
-	}
-
-	FRESULT fres;
-	if ((fres = f_lseek(fp, phdr->p_offset)) != FR_OK) {
-		LOG_ERR("l0der", "_check_interp: could not seek to 0x%lx: %d", phdr->p_offset, fres);
-		return -1;
+	if ((res = _seek_and_read(fp, phdr->p_offset, interp, buffer_size)) != FR_OK) {
+		return res;
 	}
 
-	unsigned int read; // unused (we don't care if the read gets truncated)
-	if ((fres = f_read(fp, interp, buffer_size, &read)) != FR_OK) {
-		LOG_ERR("l0der", "_check_interp: could not read segment %d", fres);
-		return -1;
-	}
 
 	if (strncmp(interp, _interpreter, strlen(_interpreter)) != 0) {
 		LOG_ERR("l0der", "_check_interp: invalid interpreter, want card10-l0dable");
@@ -198,6 +218,11 @@ static int _check_interp(FIL *fp, Elf32_Phdr *phdr)
 	return 0;
 }
 
+/*
+ * Calculate address at which binary should be loaded.
+ *
+ * Currently this means trying to fit it into core1 RAM.
+ */
 static int _get_load_addr(uint32_t image_start, uint32_t image_limit, void **load)
 {
 	uint32_t image_size = image_limit - image_start;
@@ -218,6 +243,11 @@ static int _get_load_addr(uint32_t image_start, uint32_t image_limit, void **loa
 	return 0;
 }
 
+/*
+ * Load a program segment into memory.
+ *
+ * Segment must be a LOAD segment.
+ */
 static int _load_segment(FIL *fp, void *image_load_addr, Elf32_Phdr *phdr)
 {
 	uint32_t segment_start = (uint32_t)image_load_addr + phdr->p_vaddr;
@@ -227,23 +257,17 @@ static int _load_segment(FIL *fp, void *image_load_addr, Elf32_Phdr *phdr)
 			segment_start, segment_limit, phdr->p_filesz);
 	memset((void *)segment_start, 0, phdr->p_memsz);
 
-	FRESULT fres;
-	unsigned int read;
-
-	if ((fres = f_lseek(fp, phdr->p_offset)) != FR_OK) {
-		LOG_ERR("l0der", "_load_segment: seek failed: %d", fres);
-		return -EIO;
-	}
-
-	if ((fres = f_read(fp, (void *)segment_start, phdr->p_filesz, &read)) != FR_OK || read != phdr->p_filesz) {
-		LOG_ERR("l0der", "_load_segment: read failed");
-		return -EIO;
-	}
-
-	return 0;
+	return _seek_and_read(fp, phdr->p_offset, (void*)segment_start, phdr->p_filesz);
 }
 
-static int _run_relocations(FIL *fp, void *load_addr, Elf32_Ehdr *hdr) {
+/*
+ * Apply dynamic relocations from ELF.
+ *
+ * Currently, we only support R_ARM_RELATIVE relocations. These seem to be
+ * the only one used when making 'standard' PIE binaries on RAM. However, other
+ * kinds might have to be implemented in the future.
+ */
+static int _run_relocations(FIL *fp, void *load_addr, uint32_t image_start, uint32_t image_limit, Elf32_Ehdr *hdr) {
 	int res;
 	FRESULT fres;
 	Elf32_Shdr shdr;
@@ -296,8 +320,12 @@ static int _run_relocations(FIL *fp, void *load_addr, Elf32_Ehdr *hdr) {
 						LOG_ERR("l0der", "_run_relocations: R_ARM_RELATIVE address must be 4-byte aligned");
 						return -ENOEXEC;
 					}
-					// TODO(q3k): check whether offset is contained in binary.
 					volatile uint32_t *addr = (uint32_t *)(rel.r_offset + load_addr);
+					if ((uint32_t)addr < image_start || (uint32_t)addr >= image_limit) {
+						LOG_ERR("l0der", "_run_relocations: R_ARM_RELATIVE address is outside image boundaries");
+						return -ENOEXEC;
+					}
+
 					*addr += (uint32_t)load_addr;
 					break;
 				default:
@@ -310,6 +338,9 @@ static int _run_relocations(FIL *fp, void *load_addr, Elf32_Ehdr *hdr) {
 	return 0;
 }
 
+/*
+ * Load a l0dable PIE binary.
+ */
 static int _load_pie(FIL *fp, Elf32_Ehdr *hdr, struct l0dable_info *info)
 {
 	int res;
@@ -363,12 +394,14 @@ static int _load_pie(FIL *fp, Elf32_Ehdr *hdr, struct l0dable_info *info)
 	}
 
 	if (status_interp != 0) {
+		// Expected interpreter string was not found.
 		LOG_ERR("l0der", "_load_pie: not a card10 l0dable");
 		return -ENOEXEC;
 	}
 
 
 	if (image_limit < image_start) {
+		// We didn't find any LOAD segment.
 		LOG_ERR("l0der", "_load_pie: no loadable segments");
 		return -ENOEXEC;
 	}
@@ -401,7 +434,7 @@ static int _load_pie(FIL *fp, Elf32_Ehdr *hdr, struct l0dable_info *info)
 
 	// Run relocations.
 	
-	if ((res = _run_relocations(fp, load_addr, hdr)) != 0) {
+	if ((res = _run_relocations(fp, load_addr, image_start, image_limit, hdr)) != 0) {
 		return res;
 	}
 
diff --git a/epicardium/l0der/l0der.h b/epicardium/l0der/l0der.h
index 80ce3242dc1f239a7e9c5fa3eef50e53fac2868b..d7f6b462c80694bc4304fa4baab68a206b21760a 100644
--- a/epicardium/l0der/l0der.h
+++ b/epicardium/l0der/l0der.h
@@ -22,6 +22,11 @@ struct l0dable_info {
  *
  * :param const char *path: Path of l0dable on FAT filesystem.
  * :param l0dable_info l0dable: Information about loaded l0dable.
- * :returns: ``0`` on success or a negative value on error.
+ * :returns: ``0`` on success or a negative value on error.  Possible errors:
+ *
+ *    - ``-ENOENT``: l0dable not present at given path.
+ *    - ``-EIO``: Read failed: l0dable corrupted or truncated.
+ *    - ``-ENOEXEC``: Corrupted/invalid l0dable.
+ *    - ``-ENOMEM``: l0dable too large to fit in RAM.
  */
 int l0der_load_path(const char *path, struct l0dable_info *l0dable);
diff --git a/l0dables/blinky/README.md b/l0dables/blinky/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..9b13b5104a1d717acef4f3af5592deb7b182948b
--- /dev/null
+++ b/l0dables/blinky/README.md
@@ -0,0 +1,6 @@
+blinky
+======
+
+It works! Blink it!
+
+This is a battery-hungry l0dable that shows you how to blink some LEDs while burning CPU time and battery.
diff --git a/l0dables/blinky/main.c b/l0dables/blinky/main.c
index 7d9c1e10002bbc24496818321fe2f3021e40f905..1736e0c4935890becfa7eb26c320b17e12e94d21 100644
--- a/l0dables/blinky/main.c
+++ b/l0dables/blinky/main.c
@@ -1,80 +1,65 @@
-#include "max32665.h"
-#include "mxc_sys.h"
-#include "gcr_regs.h"
-#include "icc_regs.h"
-#include "pwrseq_regs.h"
-
 #include "epicardium.h"
 
-uint32_t SystemCoreClock = HIRC_FREQ >> 1; 
-volatile uint32_t tombstone = 0;
+#include <math.h>
 
-void SystemCoreClockUpdate(void)
-{
-    uint32_t base_freq, div, clk_src;
+int levels[11] = {0};
+int levels_display[11] = {0};
 
-    // Determine the clock source and frequency
-    clk_src = (MXC_GCR->clkcn & MXC_F_GCR_CLKCN_CLKSEL);
-    switch (clk_src)
-    {
-        case MXC_S_GCR_CLKCN_CLKSEL_HIRC:
-            base_freq = HIRC_FREQ;
-            break;
-        case MXC_S_GCR_CLKCN_CLKSEL_XTAL32M:
-            base_freq = XTAL32M_FREQ;
-            break;
-        case MXC_S_GCR_CLKCN_CLKSEL_LIRC8:
-            base_freq = LIRC8_FREQ;
-            break;
-        case MXC_S_GCR_CLKCN_CLKSEL_HIRC96:
-            base_freq = HIRC96_FREQ;
-            break;
-        case MXC_S_GCR_CLKCN_CLKSEL_HIRC8:
-            base_freq = HIRC8_FREQ;
-            break;
-        case MXC_S_GCR_CLKCN_CLKSEL_XTAL32K:
-            base_freq = XTAL32K_FREQ;
-            break;
-        default:
-	    // Values 001 and 111 are reserved, and should never be encountered.
-	    base_freq = HIRC_FREQ;
-            break;
-    }
-    // Clock divider is retrieved to compute system clock
-    div = (MXC_GCR->clkcn & MXC_F_GCR_CLKCN_PSC) >> MXC_F_GCR_CLKCN_PSC_POS;
+// From https://learn.adafruit.com/led-tricks-gamma-correction/the-quick-fix
+const uint8_t gamma8[] = {
+    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1,  1,  1,  1,
+    1,  1,  1,  1,  1,  1,  1,  1,  1,  2,  2,  2,  2,  2,  2,  2,
+    2,  3,  3,  3,  3,  3,  3,  3,  4,  4,  4,  4,  4,  5,  5,  5,
+    5,  6,  6,  6,  6,  7,  7,  7,  7,  8,  8,  8,  9,  9,  9, 10,
+   10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16,
+   17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
+   25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36,
+   37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50,
+   51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68,
+   69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89,
+   90, 92, 93, 95, 96, 98, 99,101,102,104,105,107,109,110,112,114,
+  115,117,119,120,122,124,126,127,129,131,133,135,137,138,140,142,
+  144,146,148,150,152,154,156,158,160,162,164,167,169,171,173,175,
+  177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213,
+  215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255 };
 
-    SystemCoreClock = base_freq >> div;
+void fade() {
+    for (int i = 0; i < 11; i++) {
+        int level = gamma8[levels[i]];
+        if (levels_display[i] > 0) {
+            epic_leds_set(i, level, 0, 0);
+            if (level == 0) {
+                levels_display[i] = 0;
+            }
+        }
+        if (levels[i] > 0) {
+            levels[i]--;
+        }
+    }
 }
 
+/* 
+ * main() is called when l0dable is loaded and executed.
+ */
 int main(void) {
-    tombstone = 0x10;
-    // Enable FPU.
-    SCB->CPACR |= SCB_CPACR_CP10_Msk | SCB_CPACR_CP11_Msk;
-    __DSB();
-    __ISB();
-    tombstone = 0x11;
-
-    // Enable ICache1 Clock
-    MXC_GCR->perckcn1 &= ~(1 << 22);
-    tombstone = 0x12;
-
-    // Invalidate cache and wait until ready
-    MXC_ICC1->invalidate = 1;
-    while (!(MXC_ICC1->cache_ctrl & MXC_F_ICC_CACHE_CTRL_CACHE_RDY));
-    tombstone = 0x13;
-
-    // Enable Cache
-    MXC_ICC1->cache_ctrl |= MXC_F_ICC_CACHE_CTRL_CACHE_EN;
-    tombstone = 0x14;
-
-    SystemCoreClockUpdate();
-    tombstone = 0x15;
-
-    /* TMR5 is used to notify on keyboard interrupt */
-    //NVIC_EnableIRQ(TMR5_IRQn);
-    tombstone = 0x16;
-
-    epic_leds_set(0, 255, 255, 255);
-    tombstone = 0x17;
-    for (;;) {}
+    // l0dables are running on a separate, exclusive-to-l0dables core.
+    // Busy-waiting will not block the main operating system on core0 from
+    // running - but it will drain batteries.
+    for (;;) {
+        for (int i = 0; i < 11; i++) {
+            levels[i] = 255;
+            levels_display[i] = 1;
+            for (int j = 0; j < 64; j++) {
+                fade();
+            }
+        }
+        for (int i = 9; i > 0; i--) {
+            levels[i] = 255;
+            levels_display[i] = 1;
+            for (int j = 0; j < 64; j++) {
+                fade();
+            }
+        }
+    }
 }
diff --git a/l0dables/blinky/meson.build b/l0dables/blinky/meson.build
index 9a5c199dcaf503aafb2b966256b56938ae22be4e..9a2fdb4c23bde11073b8e792dcb6f5b792aa6e9f 100644
--- a/l0dables/blinky/meson.build
+++ b/l0dables/blinky/meson.build
@@ -4,7 +4,7 @@ elf = executable(
   name + '.elf',
   'main.c',
   build_by_default: true,
-  dependencies: [l0dable_startup, api_caller],
+  dependencies: [l0dable_startup, api_caller, periphdriver],
   link_whole: [l0dable_startup_lib],
   link_args: [
     '-Wl,-Map=' + meson.current_build_dir() + '/' + name + '.map',
diff --git a/l0dables/crt.s b/l0dables/lib/crt.s
similarity index 84%
rename from l0dables/crt.s
rename to l0dables/lib/crt.s
index 732c9c988ceee5640348ba9d92a356ab6728f806..b2d1ba76af534f8621bcf7b452518aa85b4a09ba 100644
--- a/l0dables/crt.s
+++ b/l0dables/lib/crt.s
@@ -1,26 +1,44 @@
+		/*
+		 * C Runtime for l0dable.
+		 *
+		 * Also known as a startup file.
+		 *
+		 * We provide the following to l0dables:
+		 *  - a 8k stack.
+		 *  - calling GCC initializers.
+		 *  - an ISR vector.
+		 */
+
 		.syntax unified
 		.arch armv7-m
 
+		/*
+		 * ISR Vector.
+		 *
+		 * All of the following (apart from Reset_Handler, which calls main())
+		 * are backed by weak referenced symbols, which you can override just
+         * by defining them in C code.
+		 */
 		.section .data
 		.align 2
-		.globl isr_vector
-isr_vector:
+		.globl __isr_vector
+__isr_vector:
 		.long    CARD10_STACK_LIMIT            /* Top of Stack */
-		.long    Reset_Handler         /* Reset Handler */
-		.long    NMI_Handler           /* NMI Handler */
-		.long    HardFault_Handler     /* Hard Fault Handler */
-		.long    MemManage_Handler     /* MPU Fault Handler */
-		.long    BusFault_Handler      /* Bus Fault Handler */
-		.long    UsageFault_Handler    /* Usage Fault Handler */
-		.long    0                     /* Reserved */
-		.long    0                     /* Reserved */
-		.long    0                     /* Reserved */
-		.long    0                     /* Reserved */
-		.long    SVC_Handler           /* SVCall Handler */
-		.long    0                     /* Reserved */ /* @TODO: Is this the Debug Montior Interrupt? */
-		.long    0                     /* Reserved */
-		.long    PendSV_Handler        /* PendSV Handler */
-		.long    SysTick_Handler       /* SysTick Handler */
+		.long    Reset_Handler                 /* Reset Handler */
+		.long    NMI_Handler                   /* NMI Handler */
+		.long    HardFault_Handler             /* Hard Fault Handler */
+		.long    MemManage_Handler             /* MPU Fault Handler */
+		.long    BusFault_Handler              /* Bus Fault Handler */
+		.long    UsageFault_Handler            /* Usage Fault Handler */
+		.long    0                             /* Reserved */
+		.long    0                             /* Reserved */
+		.long    0                             /* Reserved */
+		.long    0                             /* Reserved */
+		.long    SVC_Handler                   /* SVCall Handler */
+		.long    0                             /* Reserved */ /* @TODO: Is this the Debug Montior Interrupt? */
+		.long    0                             /* Reserved */
+		.long    PendSV_Handler                /* PendSV Handler */
+		.long    SysTick_Handler               /* SysTick Handler */
 		
 		/* Device-specific Interrupts */
 		.long    PF_IRQHandler                 /* 0x10  0x0040  16: Power Fail */
@@ -46,7 +64,7 @@ isr_vector:
 		.long    ADC_IRQHandler                /* 0x24  0x0090  36: ADC */
 		.long    RSV21_IRQHandler              /* 0x25  0x0094  37: Reserved */
 		.long    RSV22_IRQHandler              /* 0x26  0x0098  38: Reserved */
-		.long    FLC0_IRQHandler                /* 0x27  0x009C  39: Flash Controller */
+		.long    FLC0_IRQHandler               /* 0x27  0x009C  39: Flash Controller */
 		.long    GPIO0_IRQHandler              /* 0x28  0x00A0  40: GPIO0 */
 		.long    GPIO1_IRQHandler              /* 0x29  0x00A4  41: GPIO2 */
 		.long    RSV26_IRQHandler              /* 0x2A  0x00A8  42: GPIO3 */
@@ -107,7 +125,7 @@ isr_vector:
 		.long    WDT2_IRQHandler               /* 0x61  0x0184  97: Watchdog Timer 2 */
 		.long    ECC_IRQHandler                /* 0x62  0x0188  98: Error Correction */
 		.long    DVS_IRQHandler                /* 0x63  0x018C  99: DVS Controller */
-		.long    SIMO_IRQHandler               /* 0x64 0x0190  100: SIMO Controller */
+		.long    SIMO_IRQHandler               /* 0x64  0x0190  100: SIMO Controller */
 		.long    RPU_IRQHandler                /* 0x65  0x0194  101: RPU */ /* @TODO: Is this correct? */
 		.long    AUDIO_IRQHandler              /* 0x66  0x0198  102: Audio subsystem */
 		.long    FLC1_IRQHandler               /* 0x67  0x019C  103: Flash Control 1 */
@@ -119,28 +137,43 @@ isr_vector:
 		.long    HTMR0_IRQHandler              /* 0x6D  0x01B4  109: HTmr */
 		.long    HTMR1_IRQHandler              /* 0x6E  0x01B8  109: HTmr */
 
-
+		/*
+		 * Reset_Handler, or, l0dable entrypoint.
+		 */
 		.text
 		.thumb
 		.thumb_func
 		.align 2
 Reset_Handler:
-		// Set stack according to limits from linker script.
+		/* Set stack according to limits from linker script. */
 		ldr r0, =CARD10_STACK_LIMIT
 		mov sp, r0
 
-		// Jump to C code
+		/* Call system initialization from l0dables/lib/hardware.c. */
+		blx SystemInit
+
+		/* Call GCC constructors. */
+		ldr r0, =__libc_init_array
+		blx r0
+
+		/* Jump to C code */
 		ldr r0, =main
 		blx r0
 
-		// Spin
-		// TODO(q3k): let epicardium know we're done
+		/*
+		 * Spin forever.
+		 * TODO(q3k): let epicardium know we're done.
+		 */
 .spin:
 		bl .spin
 
-		// Macro to define default handlers. Default handler
-		// will be weak symbol and just dead loops. They can be
-		// overwritten by other handlers.
+		.globl _init
+_init:
+		bx lr
+
+		/* Macro to define default handlers. Default handler
+		 * will be weak symbol and just dead loops. They can be
+		 * overwritten by other handlers. */
 		.macro    def_irq_handler    handler_name
 		.align 1
 		.thumb_func
@@ -151,9 +184,9 @@ Reset_Handler:
 		.size    \handler_name, . - \handler_name
 		.endm
 
-
-		// Default ISRs.
-
+		/*
+		 * Declare all default ISRs.
+		 */
 		def_irq_handler    NMI_Handler
 		def_irq_handler    HardFault_Handler
 		def_irq_handler    MemManage_Handler
diff --git a/l0dables/lib/hardware.c b/l0dables/lib/hardware.c
new file mode 100644
index 0000000000000000000000000000000000000000..1e08e5ed5c5381bc9eebf7567bc43e35b120fe5f
--- /dev/null
+++ b/l0dables/lib/hardware.c
@@ -0,0 +1,74 @@
+/*
+ * Hardware routines for l0dables.
+ *
+ * You shouldn't have to do much here. SystemInit/SystemCoreClockUpdate are
+ * called automatically before main(), and provide you with a sensible execution
+ * environment.
+ *
+ * However, if you wish, you can define your own SystemInit and take over the
+ * initialization before main() gets called.
+ */
+
+#include "max32665.h"
+#include "mxc_sys.h"
+#include "gcr_regs.h"
+#include "icc_regs.h"
+#include "pwrseq_regs.h"
+
+uint32_t SystemCoreClock = HIRC_FREQ >> 1; 
+
+void SystemCoreClockUpdate(void)
+{
+    uint32_t base_freq, div, clk_src;
+
+    // Determine the clock source and frequency
+    clk_src = (MXC_GCR->clkcn & MXC_F_GCR_CLKCN_CLKSEL);
+    switch (clk_src)
+    {
+        case MXC_S_GCR_CLKCN_CLKSEL_HIRC:
+            base_freq = HIRC_FREQ;
+            break;
+        case MXC_S_GCR_CLKCN_CLKSEL_XTAL32M:
+            base_freq = XTAL32M_FREQ;
+            break;
+        case MXC_S_GCR_CLKCN_CLKSEL_LIRC8:
+            base_freq = LIRC8_FREQ;
+            break;
+        case MXC_S_GCR_CLKCN_CLKSEL_HIRC96:
+            base_freq = HIRC96_FREQ;
+            break;
+        case MXC_S_GCR_CLKCN_CLKSEL_HIRC8:
+            base_freq = HIRC8_FREQ;
+            break;
+        case MXC_S_GCR_CLKCN_CLKSEL_XTAL32K:
+            base_freq = XTAL32K_FREQ;
+            break;
+        default:
+	    // Values 001 and 111 are reserved, and should never be encountered.
+	    base_freq = HIRC_FREQ;
+            break;
+    }
+    // Clock divider is retrieved to compute system clock
+    div = (MXC_GCR->clkcn & MXC_F_GCR_CLKCN_PSC) >> MXC_F_GCR_CLKCN_PSC_POS;
+
+    SystemCoreClock = base_freq >> div;
+}
+
+__weak void SystemInit() {
+    // Enable FPU.
+    SCB->CPACR |= SCB_CPACR_CP10_Msk | SCB_CPACR_CP11_Msk;
+    __DSB();
+    __ISB();
+
+    // Enable ICache1 Clock
+    MXC_GCR->perckcn1 &= ~(1 << 22);
+
+    // Invalidate cache and wait until ready
+    MXC_ICC1->invalidate = 1;
+    while (!(MXC_ICC1->cache_ctrl & MXC_F_ICC_CACHE_CTRL_CACHE_RDY));
+
+    // Enable Cache
+    MXC_ICC1->cache_ctrl |= MXC_F_ICC_CACHE_CTRL_CACHE_EN;
+
+    SystemCoreClockUpdate();
+}
diff --git a/l0dables/core1.ld b/l0dables/lib/l0dable.ld
similarity index 52%
rename from l0dables/core1.ld
rename to l0dables/lib/l0dable.ld
index 4a7ff28de2bc564c5840f8ef7041d8f095567bc0..8c96d669000692d7446555b604863daf555d29bf 100644
--- a/l0dables/core1.ld
+++ b/l0dables/lib/l0dable.ld
@@ -1,29 +1,31 @@
-/*MEMORY {
-    SPIX (rx)  : ORIGIN = 0x08000000, LENGTH = 128M
-    FLASH (rx) : ORIGIN = 0x10080000, LENGTH = 512k
-    SRAM (rwx) : ORIGIN = 0x20040000, LENGTH = 256k
-    SPID (r)   : ORIGIN = 0x80000000, LENGTH = 512M
-}*/
-
-ENTRY(isr_vector);
-
-CARD10_CORE1_START = 0x20040000;
-CARD10_CORE1_LIMIT = 0x20080000;
+ENTRY(__isr_vector);
 
+/*
+ * Segment in the output l0dable.
+ *
+ * They are mostly standard, but we define them explicitely so that we can
+ * target them in sections.
+ */
 PHDRS
 {
     header PT_PHDR PHDRS ;
     interp PT_INTERP ;
     text PT_LOAD FILEHDR PHDRS ;
     data PT_LOAD ;
-    dynamic PT_DYNAMIC ;
 }
 
+/*
+ * ELF sections.
+ */
 SECTIONS {
-    /* . = CARD10_CORE1_START; */
-
     . = SIZEOF_HEADERS;
 
+    /*
+     * Customer card10-l0dable INTERP/intepreter path.
+     *
+     * We nuke the original one (.interp) provided by gcc/ld, and inject out
+     * own. This section is populated in l0dable.ld.
+     */
     .cinterp :
     {
         *(.cinterp);
@@ -43,6 +45,12 @@ SECTIONS {
         . = ALIGN(4);
         *(.data*)
 
+		. = ALIGN(4);
+		PROVIDE_HIDDEN (__preinit_array_start = .);
+		KEEP(*(.preinit_array))
+		PROVIDE_HIDDEN (__preinit_array_end = .);
+
+
         . = ALIGN(4);
         PROVIDE_HIDDEN (__init_array_start = .);
         KEEP(*(SORT(.init_array.*)))
@@ -56,36 +64,33 @@ SECTIONS {
         PROVIDE_HIDDEN (__fini_array_end = .);
     } :data
 
-    .dynamic :
-    {
-        *(.dynamic)
-    } :data :dynamic
-
     .bss :
     {
         . = ALIGN(4);
-        CARD10_BSS_OFF_START = .;
         *(.bss*)
         *(COMMON)
-        CARD10_BSS_LIMIT = .;
     } :data
 
     . = ALIGN(4096);
     .stack : 
     {
         CARD10_STACK_START = .;
-        . += 4096;
+        . += 8192;
         CARD10_STACK_LIMIT = .;
     } :data
 
+	/* Limit based on current limitations of l0dable setup - only uses core1 RAM. */
+    ASSERT(. < 0x40000, "Exceeded available RAM")
+
     /DISCARD/ :
     {
-        /* *(*) */
-        /* *(.symtab) */
-        /* *(.strtab) */
-        /* *(.shstrtab) */
-        /**(.interp)*/
+        /* Compiler version - nuke. */
         *(.comment)
+        /* ARM attributes - nuke. */
+        *(.ARM.attributes)
+        /* Original interpreter path from gcc/ld - nuke. */
         *(.interp)
+		/* Dynamic linking section - nuke, we're not a .so and nothing is going to link against us. */
+		*(.dynamic)
     }
 }
diff --git a/l0dables/lib/meson.build b/l0dables/lib/meson.build
new file mode 100644
index 0000000000000000000000000000000000000000..c9d81c080b70cce7f445d65828717344ebaabfee
--- /dev/null
+++ b/l0dables/lib/meson.build
@@ -0,0 +1,18 @@
+l0dable_startup_lib = static_library(
+  'l0dable-startup',
+  'crt.s',
+  'hardware.c',
+  dependencies: periphdriver,
+  pic: true,
+)
+
+l0dable_startup = declare_dependency(
+  link_args: [
+    '-nostdlib', '-n',
+    '-T', meson.current_source_dir() + 'l0dable.ld',
+  ],
+  compile_args: [
+    '-fPIE', '-pie',
+  ],
+)
+
diff --git a/l0dables/meson.build b/l0dables/meson.build
index 12bb45b3db79d8f0952ce4b7ee7897a4f8051967..f4be10b507d2e028d204202e14fa68bdf195a6ac 100644
--- a/l0dables/meson.build
+++ b/l0dables/meson.build
@@ -1,17 +1,3 @@
-l0dable_startup_lib = static_library(
-  'l0dable-startup',
-  'crt.s',
-  pic: true,
-)
-
-l0dable_startup = declare_dependency(
-  link_args: [
-    '-nostdlib', '-n',
-    '-T', meson.current_source_dir() + 'core1.ld',
-  ],
-  compile_args: [
-    '-fPIE', '-pie',
-  ],
-)
+subdir('lib/')
 
 subdir('blinky/')