diff --git a/epicardium/main.c b/epicardium/main.c
index deda39b8bec170004d41f9f34d472a4a727ae205..929fdb677a89af1fd62af12b237a55ea4d786835 100644
--- a/epicardium/main.c
+++ b/epicardium/main.c
@@ -12,7 +12,7 @@
 #include "FreeRTOS.h"
 #include "task.h"
 
-static TaskHandle_t dispatcher_task_id;
+TaskHandle_t dispatcher_task_id;
 
 /* TODO: Move out of main.c */
 void epic_leds_set(int led, uint8_t r, uint8_t g, uint8_t b)
@@ -21,56 +21,6 @@ void epic_leds_set(int led, uint8_t r, uint8_t g, uint8_t b)
 	leds_update();
 }
 
-/*
- * This hook is called before FreeRTOS enters tickless idle.
- */
-void pre_idle_sleep(TickType_t xExpectedIdleTime)
-{
-	if (xExpectedIdleTime > 0) {
-		/*
-		 * WFE because the other core should be able to notify
-		 * epicardium if it wants to issue an API call.
-		 */
-
-		/*
-		 * TODO: Ensure this is actually correct and does not have any
-		 * race conditions.
-		 */
-		__asm volatile( "cpsie i" ::: "memory" );
-		__asm volatile( "dsb" ::: "memory" );
-		__asm volatile( "isb" );
-		__asm volatile( "wfe" );
-		__asm volatile( "dsb" ::: "memory" );
-		__asm volatile( "isb" );
-		__asm volatile( "cpsid i" ::: "memory" );
-		__asm volatile( "dsb" );
-		__asm volatile( "isb" );
-	}
-}
-
-/*
- * This hook is called after FreeRTOS exits tickless idle.
- */
-void post_idle_sleep(TickType_t xExpectedIdleTime)
-{
-	/* Check whether a new API call was issued. */
-	if (api_dispatcher_poll_once()) {
-		xTaskNotifyGive(dispatcher_task_id);
-	}
-}
-
-#if 0
-void vPortSuppressTicksAndSleep(TickType_t xExpectedIdleTime)
-{
-	if (xExpectedIdleTime > 0) {
-		__WFE();
-		if (api_dispatcher_poll()) {
-			xTaskNotifyGive(dispatcher_task_id);
-		}
-	}
-}
-#endif
-
 /*
  * API dispatcher task.  This task will sleep until an API call is issued and
  * then wake up to dispatch it.
@@ -85,36 +35,6 @@ void vApiDispatcher(void*pvParameters)
 	}
 }
 
-void vApplicationGetIdleTaskMemory(
-	StaticTask_t**ppxIdleTaskTCBBuffer,
-	StackType_t**ppxIdleTaskStackBuffer,
-	uint32_t *pulIdleTaskStackSize)
-{
-	/*
-	 * If the buffers to be provided to the Idle task are declared inside this
-	 * function then they must be declared static - otherwise they will be allocated on
-	 * the stack and so not exists after this function exits.
-	 */
-	static StaticTask_t xIdleTaskTCB;
-	static StackType_t uxIdleTaskStack[ configMINIMAL_STACK_SIZE ];
-
-	/*
-	 * Pass out a pointer to the StaticTask_t structure in which the Idle task's
-	 * ktate will be stored.
-	 */
-	*ppxIdleTaskTCBBuffer = &xIdleTaskTCB;
-
-	/* Pass out the array that will be used as the Idle task's stack. */
-	*ppxIdleTaskStackBuffer = uxIdleTaskStack;
-
-	/*
-	 * Pass out the size of the array pointed to by *ppxIdleTaskStackBuffer.
-	 * Note that, as the array is necessarily of type StackType_t,
-	 * configMINIMAL_STACK_SIZE is specified in words, not bytes.
-	 */
-	*pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
-}
-
 int main(void)
 {
 	card10_init();
diff --git a/epicardium/meson.build b/epicardium/meson.build
index 7aa214405ec1be33d586bf5f1cc2d6cb820bfd1d..88726887b7d67b9644ceff2a8b1f2c0d45d20002 100644
--- a/epicardium/meson.build
+++ b/epicardium/meson.build
@@ -63,9 +63,10 @@ freertos = static_library(
 
 elf = executable(
   name + '.elf',
-  'main.c',
   'cdcacm.c',
+  'main.c',
   'serial.c',
+  'support.c',
   dependencies: [libcard10, max32665_startup_core0, maxusb],
   link_with: [api_dispatcher_lib, freertos],
   link_whole: [max32665_startup_core0_lib, board_card10_lib],
diff --git a/epicardium/support.c b/epicardium/support.c
new file mode 100644
index 0000000000000000000000000000000000000000..19bab453d15898beb0b1c9d09a0965003f25f7fc
--- /dev/null
+++ b/epicardium/support.c
@@ -0,0 +1,78 @@
+/*
+ * FreeRTOS support functions
+ */
+
+#include "FreeRTOS.h"
+#include "task.h"
+
+#include "api/dispatcher.h"
+
+extern TaskHandle_t dispatcher_task_id;
+
+/*
+ * This hook is called before FreeRTOS enters tickless idle.
+ */
+void pre_idle_sleep(TickType_t xExpectedIdleTime)
+{
+	if (xExpectedIdleTime > 0) {
+		/*
+		 * WFE because the other core should be able to notify
+		 * epicardium if it wants to issue an API call.
+		 */
+
+		/*
+		 * TODO: Ensure this is actually correct and does not have any
+		 * race conditions.
+		 */
+		__asm volatile( "cpsie i" ::: "memory" );
+		__asm volatile( "dsb" ::: "memory" );
+		__asm volatile( "isb" );
+		__asm volatile( "wfe" );
+		__asm volatile( "dsb" ::: "memory" );
+		__asm volatile( "isb" );
+		__asm volatile( "cpsid i" ::: "memory" );
+		__asm volatile( "dsb" );
+		__asm volatile( "isb" );
+	}
+}
+
+/*
+ * This hook is called after FreeRTOS exits tickless idle.
+ */
+void post_idle_sleep(TickType_t xExpectedIdleTime)
+{
+	/* Check whether a new API call was issued. */
+	if (api_dispatcher_poll_once()) {
+		xTaskNotifyGive(dispatcher_task_id);
+	}
+}
+
+void vApplicationGetIdleTaskMemory(
+	StaticTask_t**ppxIdleTaskTCBBuffer,
+	StackType_t**ppxIdleTaskStackBuffer,
+	uint32_t *pulIdleTaskStackSize)
+{
+	/*
+	 * If the buffers to be provided to the Idle task are declared inside this
+	 * function then they must be declared static - otherwise they will be allocated on
+	 * the stack and so not exists after this function exits.
+	 */
+	static StaticTask_t xIdleTaskTCB;
+	static StackType_t uxIdleTaskStack[ configMINIMAL_STACK_SIZE ];
+
+	/*
+	 * Pass out a pointer to the StaticTask_t structure in which the Idle task's
+	 * ktate will be stored.
+	 */
+	*ppxIdleTaskTCBBuffer = &xIdleTaskTCB;
+
+	/* Pass out the array that will be used as the Idle task's stack. */
+	*ppxIdleTaskStackBuffer = uxIdleTaskStack;
+
+	/*
+	 * Pass out the size of the array pointed to by *ppxIdleTaskStackBuffer.
+	 * Note that, as the array is necessarily of type StackType_t,
+	 * configMINIMAL_STACK_SIZE is specified in words, not bytes.
+	 */
+	*pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
+}