Index: apps/codecs.c
===================================================================
--- apps/codecs.c	(revision 12574)
+++ apps/codecs.c	(working copy)
@@ -123,7 +123,7 @@
     &current_tick,
     default_event_handler,
     default_event_handler_ex,
-    create_thread_on_core,
+    create_thread,
     remove_thread,
     reset_poweroff_timer,
 #ifndef SIMULATOR
Index: apps/playlist.c
===================================================================
--- apps/playlist.c	(revision 12574)
+++ apps/playlist.c	(working copy)
@@ -1873,7 +1873,8 @@
     memset(playlist->filenames, 0,
            playlist->max_playlist_size * sizeof(int));
     create_thread(playlist_thread, playlist_stack, sizeof(playlist_stack),
-                  playlist_thread_name IF_PRIO(, PRIORITY_BACKGROUND));
+                  playlist_thread_name IF_PRIO(, PRIORITY_BACKGROUND)
+		  IF_COP(, CPU, false));
     queue_init(&playlist_queue, true);
 #endif
 }
Index: apps/codecs.h
===================================================================
--- apps/codecs.h	(revision 12574)
+++ apps/codecs.h	(working copy)
@@ -199,9 +199,10 @@
     long* current_tick;
     long (*default_event_handler)(long event);
     long (*default_event_handler_ex)(long event, void (*callback)(void *), void *parameter);
-    struct thread_entry* (*create_thread)(unsigned int core, void (*function)(void), 
+    struct thread_entry* (*create_thread)(void (*function)(void), 
                                           void* stack, int stack_size, const char *name
-                                          IF_PRIO(, int priority));
+                                          IF_PRIO(, int priority)
+					  IF_COP(, unsigned int core, bool fallback));
     void (*remove_thread)(struct thread_entry *thread);
     void (*reset_poweroff_timer)(void);
 #ifndef SIMULATOR
Index: apps/plugins/battery_bench.c
===================================================================
--- apps/plugins/battery_bench.c	(revision 12574)
+++ apps/plugins/battery_bench.c	(working copy)
@@ -479,7 +479,8 @@
     rb->queue_init(&thread_q, true); /* put the thread's queue in the bcast list */
     if(rb->create_thread(thread, thread_stack,
         sizeof(thread_stack), "Battery Benchmark" 
-        IF_PRIO(, PRIORITY_BACKGROUND)) == NULL)
+        IF_PRIO(, PRIORITY_BACKGROUND)
+	IF_COP(, CPU, false)) == NULL)
     {
         rb->splash(HZ,true,"Cannot create thread!");
         return PLUGIN_ERROR;
Index: apps/plugins/mpegplayer/mpegplayer.c
===================================================================
--- apps/plugins/mpegplayer/mpegplayer.c	(revision 12574)
+++ apps/plugins/mpegplayer/mpegplayer.c	(working copy)
@@ -909,19 +909,17 @@
     videostatus = STREAM_PLAYING;
 
     /* We put the video thread on the second processor for multi-core targets. */
-#if NUM_CORES > 1
-    if ((videothread_id = rb->create_thread_on_core(COP,decode_mpeg2,
-#else
     if ((videothread_id = rb->create_thread(decode_mpeg2,
-#endif
-        (uint8_t*)video_stack,VIDEO_STACKSIZE,"mpgvideo" IF_PRIO(,PRIORITY_PLAYBACK))) == NULL)
+        (uint8_t*)video_stack,VIDEO_STACKSIZE,"mpgvideo" IF_PRIO(,PRIORITY_PLAYBACK)
+	IF_COP(, COP, true))) == NULL)
     {
         rb->splash(HZ,true,"Cannot create video thread!");
         return PLUGIN_ERROR;
     }
 
     if ((audiothread_id = rb->create_thread(mad_decode,
-        (uint8_t*)audio_stack,AUDIO_STACKSIZE,"mpgaudio" IF_PRIO(,PRIORITY_PLAYBACK))) == NULL)
+        (uint8_t*)audio_stack,AUDIO_STACKSIZE,"mpgaudio" IF_PRIO(,PRIORITY_PLAYBACK)
+	IF_COP(, CPU, false))) == NULL)
     {
         rb->splash(HZ,true,"Cannot create audio thread!");
         rb->remove_thread(videothread_id);
Index: apps/plugins/alpine_cdc.c
===================================================================
--- apps/plugins/alpine_cdc.c	(revision 12574)
+++ apps/plugins/alpine_cdc.c	(working copy)
@@ -1171,7 +1171,8 @@
     rb->memset(&gTread, 0, sizeof(gTread));
     gTread.foreground = true;
     rb->create_thread(thread, stack, stacksize, "CDC"
-                      IF_PRIO(, PRIORITY_BACKGROUND));
+                      IF_PRIO(, PRIORITY_BACKGROUND)
+		      IF_COP(, CPU, false));
 
 #ifdef DEBUG
     do
Index: apps/tagcache.c
===================================================================
--- apps/tagcache.c	(revision 12574)
+++ apps/tagcache.c	(working copy)
@@ -3928,7 +3928,8 @@
     queue_init(&tagcache_queue, true);
     create_thread(tagcache_thread, tagcache_stack,
                   sizeof(tagcache_stack), tagcache_thread_name 
-                  IF_PRIO(, PRIORITY_BACKGROUND));
+                  IF_PRIO(, PRIORITY_BACKGROUND)
+		  IF_COP(, CPU, false));
 #else
     tc_stat.initialized = true;
     allocate_tempbuf();
Index: apps/playback.c
===================================================================
--- apps/playback.c	(revision 12574)
+++ apps/playback.c	(working copy)
@@ -198,9 +198,9 @@
 /* TBD: Split out "audio" and "playback" (ie. calling) threads */
 
 /* Main state control */
-static volatile bool audio_codec_loaded; /* Is codec loaded? (C/A-) */
-static volatile bool playing;            /* Is audio playing? (A) */
-static volatile bool paused;             /* Is audio paused? (A/C-) */
+static volatile bool audio_codec_loaded NOCACHEBSS_ATTR;/* Codec loaded? (C/A-) */
+static volatile bool playing NOCACHEBSS_ATTR;          /* Is audio playing? (A) */
+static volatile bool paused NOCACHEBSS_ATTR;         /* Is audio paused? (A/C-) */
 static volatile bool filling IDATA_ATTR; /* Is file buffer refilling? (A/C-) */
 
 /* Ring buffer where compressed audio and codecs are loaded */
@@ -294,7 +294,7 @@
 
 /* Codec thread */
 extern struct codec_api ci;
-static struct event_queue codec_queue;
+static struct event_queue codec_queue NOCACHEBSS_ATTR;
 static long codec_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)]
 IBSS_ATTR;
 static const char codec_thread_name[] = "codec";
@@ -308,7 +308,7 @@
 extern struct codec_api ci_voice;
 
 static struct thread_entry *voice_thread_p = NULL;
-static struct event_queue voice_queue;
+static struct event_queue voice_queue NOCACHEBSS_ATTR;
 static long voice_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)]
 IBSS_ATTR_VOICE_STACK;
 static const char voice_thread_name[] = "voice codec";
@@ -328,12 +328,12 @@
 /* Pointer to DRAM buffers for normal/voice codecs */
 static unsigned char *dram_buf[2] = { NULL, NULL };
 /* Mutex to control which codec (normal/voice) is running */
-static struct mutex mutex_codecthread;
+static struct mutex mutex_codecthread NOCACHEBSS_ATTR; 
 
 /* Voice state */
 static volatile bool voice_thread_start; /* Triggers voice playback (A/V) */
-static volatile bool voice_is_playing;   /* Is voice currently playing? (V) */
-static volatile bool voice_codec_loaded; /* Is voice codec loaded (V/A-) */
+static volatile bool voice_is_playing NOCACHEBSS_ATTR; /* Is voice currently playing? (V) */
+static volatile bool voice_codec_loaded NOCACHEBSS_ATTR; /* Is voice codec loaded (V/A-) */
 static char *voicebuf;
 static size_t voice_remaining;
 
@@ -867,7 +867,8 @@
     queue_init(&codec_queue, true);
 
     create_thread(audio_thread, audio_stack, sizeof(audio_stack),
-                  audio_thread_name IF_PRIO(, PRIORITY_BUFFERING));
+                  audio_thread_name IF_PRIO(, PRIORITY_BUFFERING)
+		  IF_COP(, CPU, false));
 }
 
 void audio_init(void)
@@ -892,7 +893,7 @@
     queue_init(&voice_queue, true);
     voice_thread_p = create_thread(voice_thread, voice_stack,
             sizeof(voice_stack), voice_thread_name 
-            IF_PRIO(, PRIORITY_PLAYBACK));
+            IF_PRIO(, PRIORITY_PLAYBACK) IF_COP(, CPU, false));
     
     while (!voice_codec_loaded)
         yield();
@@ -3540,7 +3541,8 @@
 
     codec_thread_p = create_thread(
             codec_thread, codec_stack, sizeof(codec_stack),
-            codec_thread_name IF_PRIO(, PRIORITY_PLAYBACK));
+            codec_thread_name IF_PRIO(, PRIORITY_PLAYBACK)
+	    IF_COP(, COP, true));
 
     while (1)
     {
Index: apps/plugin.c
===================================================================
--- apps/plugin.c	(revision 12574)
+++ apps/plugin.c	(working copy)
@@ -481,7 +481,6 @@
     sound_default,
     pcm_record_more,
 #endif
-    create_thread_on_core,
 
 #ifdef IRIVER_H100_SERIES
     /* Routines for the iriver_flash -plugin. */
Index: apps/plugin.h
===================================================================
--- apps/plugin.h	(revision 12574)
+++ apps/plugin.h	(working copy)
@@ -327,7 +327,8 @@
     long (*default_event_handler_ex)(long event, void (*callback)(void *), void *parameter);
     struct thread_entry* (*create_thread)(void (*function)(void), void* stack,
                                           int stack_size, const char *name
-                                          IF_PRIO(, int priority));
+                                          IF_PRIO(, int priority)
+					  IF_COP(, unsigned int core, bool fallback));
     void (*remove_thread)(struct thread_entry *thread);
     void (*reset_poweroff_timer)(void);
 #ifndef SIMULATOR
@@ -595,11 +596,6 @@
     void (*pcm_record_more)(void *start, size_t size);
 #endif
     
-    struct thread_entry*(*create_thread_on_core)(
-                          unsigned int core, void (*function)(void), 
-                          void* stack, int stack_size,
-                          const char *name IF_PRIO(, int priority));
-    
 #ifdef IRIVER_H100_SERIES
     /* Routines for the iriver_flash -plugin. */
     bool (*detect_original_firmware)(void);
Index: apps/main.c
===================================================================
--- apps/main.c	(revision 12574)
+++ apps/main.c	(working copy)
@@ -297,6 +297,9 @@
     /* if nobody initialized ATA before, I consider this a cold start */
     bool coldstart = (PACR2 & 0x4000) != 0; /* starting from Flash */
 #endif
+#ifdef CPU_PP
+    COP_CTL = PROC_WAKE;
+#endif
     system_init();
     kernel_init();
 
@@ -549,19 +552,25 @@
    so it should not be assumed that the coprocessor be usable even on
    platforms which support it.
 
-   At present the COP sleeps unless it receives a message from the CPU telling
-   it that we are loading a new kernel, so must reboot */
+   A kernel thread runs on the coprocessor which waits for other threads to be
+   added, and gracefully handles RoLo */
 
-#if CONFIG_CPU == PP5002
+#if CONFIG_CPU != PP5002
 /* 3G doesn't have Rolo support yet */
+    system_init();
+    kernel_init();
+
     while(1) {
-        COP_CTL = PROC_SLEEP;
+        sleep(HZ);
     }
 #else
     extern volatile unsigned char cpu_message;
 
+    system_init();
+    kernel_init();
+
     while(cpu_message != COP_REBOOT) {
-        COP_CTL = PROC_SLEEP;
+        sleep(HZ);
     }
     rolo_restart_cop();
 #endif /* PP5002 */
Index: docs/PLUGIN_API
===================================================================
--- docs/PLUGIN_API	(revision 12574)
+++ docs/PLUGIN_API	(working copy)
@@ -431,7 +431,8 @@
      SYS_USB_CONNECTED. Else do nothing and return 0.
      
   int create_thread(void* function, void* stack, int stack_size,
-                    const char *name);
+                    const char *name IF_PRIO(int priority)
+		    IF_COP(, unsigned int core, bool fallback));
  
      Create a thread.
      ??? (see firmware/thread.c:145)
Index: firmware/export/config.h
===================================================================
--- firmware/export/config.h	(revision 12574)
+++ firmware/export/config.h	(working copy)
@@ -271,23 +271,8 @@
 /* define for all cpus from PP family */
 #if (CONFIG_CPU == PP5002) || (CONFIG_CPU == PP5020) || (CONFIG_CPU == PP5024)
 #define CPU_PP
-
-/* PP family has dual cores */
-#if 0
-/* Keep it as single core until dual core support is ready */
-#define NUM_CORES 2
-#define CURRENT_CORE current_core()
 #endif
 
-#define NUM_CORES 1
-#define CURRENT_CORE 0
-
-#define COP_REBOOT 0x00000001
-#else
-#define NUM_CORES 1
-#define CURRENT_CORE 0
-#endif
-
 /* define for all cpus from ARM family */
 #if defined(CPU_PP) || (CONFIG_CPU == PNX0101) || (CONFIG_CPU == S3C2440) \
     || (CONFIG_CPU == TMS320DSC25)
@@ -346,4 +331,28 @@
 #define IRAM_LCDFRAMEBUFFER
 #endif
 
+/* Dual core support */
+#ifdef CPU_PP
+#define NUM_CORES 2
+#define CURRENT_CORE current_core()
+/* Hopefully at some point we will learn how to mark areas of main memory as
+ * not to be cached.  Until then, use IRAM for variables shared across cores */
+#define NOCACHEBSS_ATTR IBSS_ATTR
+#define NOCACHEDATA_ATTR IDATA_ATTR
+
+#define IF_COP(empty, x, y) , x, y
+
+/* Defines for inter-core messaging */
+#define COP_REBOOT 1
+
+#else
+#define NUM_CORES 1
+#define CURRENT_CORE CPU
+#define NOCACHEBSS_ATTR
+#define NOCACHEDATA_ATTR
+
+#define IF_COP(empty, x, y)
+
+#endif /* Processor specific */
+
 #endif
Index: firmware/export/pp5002.h
===================================================================
--- firmware/export/pp5002.h	(revision 12574)
+++ firmware/export/pp5002.h	(working copy)
@@ -70,9 +70,12 @@
 
 #define INIT_USB    0x80000000
 
-#define CPU_INT_STAT     (*(volatile unsigned long*)(0xcf001000))
-#define CPU_INT_EN       (*(volatile unsigned long*)(0xcf001024))
-#define CPU_INT_CLR      (*(volatile unsigned long*)(0xcf001028))
+#define COP_INT_STAT     (*(volatile unsigned long*)(0xcf001010)) /* A guess */
+#define COP_INT_EN       (*(volatile unsigned long*)(0xcf001034))
+#define COP_INT_CLR      (*(volatile unsigned long*)(0xcf001038))

 #define USB2D_IDENT         (*(volatile unsigned long*)(0xc5000000))
 #define USB_STATUS          (*(volatile unsigned long*)(0xc50001a4))
Index: firmware/export/pp5020.h
===================================================================
--- firmware/export/pp5020.h	(revision 12574)
+++ firmware/export/pp5020.h	(working copy)
@@ -37,7 +37,13 @@
 #define CPU_HI_INT_CLR   (*(volatile unsigned long*)(0x60004128))
 #define CPU_INT_STAT     (*(volatile unsigned long*)(0x64004000))
 #define CPU_HI_INT_STAT  (*(volatile unsigned long*)(0x64004100))
-       
+#define COP_INT_STAT     (*(volatile unsigned long*)(0x60004004)) /* Guessed */
+#define COP_HI_INT_STAT  (*(volatile unsigned long*)(0x60004104)) /* From IPL*/
+#define COP_INT_EN       (*(volatile unsigned long*)(0x60004034)) /* Guessed */
+#define COP_HI_INT_EN    (*(volatile unsigned long*)(0x60004134)) /* From IPL*/
+#define COP_INT_CLR      (*(volatile unsigned long*)(0x60004038)) /* Guessed */
+#define COP_HI_INT_CLR   (*(volatile unsigned long*)(0x60004138)) /* From IPL*/
+
 #define TIMER1_IRQ   0
 #define TIMER2_IRQ   1
 #define I2S_IRQ      10
@@ -78,6 +84,16 @@
 #define PROC_SLEEP   0x80000000
 #define PROC_WAKE    0x0
 
+/* Mailboxes */
+/* Each processor has two mailboxes it can write to and two which
+   it can read from.  We define the first to be for sending messages
+   and the second for replying to messages */
+#define CPU_MESSAGE      (*(volatile unsigned long *)(0x60001000))
+#define COP_MESSAGE      (*(volatile unsigned long *)(0x60001004))
+#define CPU_REPLY        (*(volatile unsigned long *)(0x60001008))
+#define COP_REPLY        (*(volatile unsigned long *)(0x6000100c))
+
+
 /* Cache Control */
 #define CACHE_CTL        (*(volatile unsigned long *)(0x6000c000))
 
Index: firmware/export/thread.h
===================================================================
--- firmware/export/thread.h	(revision 12574)
+++ firmware/export/thread.h	(working copy)
@@ -124,14 +124,9 @@
 
 struct thread_entry*
     create_thread(void (*function)(void), void* stack, int stack_size,
-                  const char *name IF_PRIO(, int priority));
+                  const char *name IF_PRIO(, int priority)
+		  IF_COP(, unsigned int core, bool fallback));
 
-struct thread_entry*
-    create_thread_on_core(unsigned int core, void (*function)(void), 
-                          void* stack, int stack_size,
-                          const char *name
-                          IF_PRIO(, int priority));
-
 #ifdef HAVE_SCHEDULER_BOOSTCTRL
 void trigger_cpu_boost(void);
 #else
Index: firmware/kernel.c
===================================================================
--- firmware/kernel.c	(revision 12574)
+++ firmware/kernel.c	(working copy)
@@ -26,7 +26,7 @@
 #include "panic.h"
 
 #if !defined(CPU_PP) || !defined(BOOTLOADER) 
-long current_tick = 0;
+long current_tick NOCACHEDATA_ATTR = 0;
 #endif
 
 static void (*tick_funcs[MAX_NUM_TICK_TASKS])(void);
@@ -45,10 +45,13 @@
     /* Init the threading API */
     init_threads();
     
-    memset(tick_funcs, 0, sizeof(tick_funcs));
+    if(CURRENT_CORE == CPU)
+    {
+        memset(tick_funcs, 0, sizeof(tick_funcs));
 
-    num_queues = 0;
-    memset(all_queues, 0, sizeof(all_queues));
+        num_queues = 0;
+        memset(all_queues, 0, sizeof(all_queues));
+    }
 
     tick_start(1000/HZ);
 }
@@ -496,28 +499,36 @@
     int i;
 
     TIMER1_VAL; /* Read value to ack IRQ */
-    /* Run through the list of tick tasks */
-    for (i = 0;i < MAX_NUM_TICK_TASKS;i++)
+    /* Run through the list of tick tasks (using main core) */
+    if (CURRENT_CORE == CPU)
     {
-        if (tick_funcs[i])
+        for (i = 0;i < MAX_NUM_TICK_TASKS;i++)
         {
-            tick_funcs[i]();
+            if (tick_funcs[i])
+            {
+                tick_funcs[i]();
+            }
         }
+
+        current_tick++;
     }
-
-    current_tick++;
 }
 #endif
 
 void tick_start(unsigned int interval_in_ms)
 {
 #ifndef BOOTLOADER
-    TIMER1_CFG = 0x0;
-    TIMER1_VAL;
-    /* enable timer */
-    TIMER1_CFG = 0xc0000000 | (interval_in_ms*1000 - 1);
-    /* unmask interrupt source */
-    CPU_INT_EN = TIMER1_MASK;
+    if(CURRENT_CORE == CPU)
+    {
+        TIMER1_CFG = 0x0;
+        TIMER1_VAL;
+        /* enable timer */
+        TIMER1_CFG = 0xc0000000 | (interval_in_ms*1000 - 1);
+        /* unmask interrupt source */
+        CPU_INT_EN = TIMER1_MASK;
+    } else {
+        COP_INT_EN = TIMER1_MASK;
+    }
 #else
     /* We don't enable interrupts in the bootloader */
     (void)interval_in_ms;
@@ -645,8 +656,31 @@
     m->thread = NULL;
 }
 
+#ifdef CPU_PP
+/* PortalPlayer chips have 2 cores, therefore need atomic mutexes */
+
+static inline bool test_and_set(bool *x, bool v)
+{
+    asm volatile (
+        "swpb %0, %0, [%1]\n"
+        : "+r"(v)
+        : "r"(x)
+    );
+    return v;
+}
+
 void mutex_lock(struct mutex *m)
 {
+    if (test_and_set(&m->locked,true))
+    {
+        /* Wait until the lock is open... */
+        block_thread(&m->thread);
+    }
+}
+
+#else
+void mutex_lock(struct mutex *m)
+{
     if (m->locked)
     {
         /* Wait until the lock is open... */
@@ -656,6 +690,7 @@
     /* ...and lock it */
     m->locked = true;
 }
+#endif
 
 void mutex_unlock(struct mutex *m)
 {
Index: firmware/system.c
===================================================================
--- firmware/system.c	(revision 12574)
+++ firmware/system.c	(working copy)
@@ -612,12 +612,22 @@
 
 void irq(void)
 {
-    if (CPU_INT_STAT & TIMER1_MASK)
-        TIMER1();
-    else if (CPU_INT_STAT & TIMER2_MASK)
-        TIMER2();
-    else if (CPU_HI_INT_STAT & GPIO_MASK)
-        ipod_mini_button_int();
+    if(CURRENT_CORE == CPU)
+    {
+        if (CPU_INT_STAT & TIMER1_MASK)
+            TIMER1();
+        else if (CPU_INT_STAT & TIMER2_MASK)
+            TIMER2();
+        else if (CPU_HI_INT_STAT & GPIO_MASK)
+            ipod_mini_button_int();
+    } else {
+        if (COP_INT_STAT & TIMER1_MASK)
+            TIMER1();
+        else if (COP_INT_STAT & TIMER2_MASK)
+            TIMER2();
+        else if (COP_HI_INT_STAT & GPIO_MASK)
+            ipod_mini_button_int();
+    }
 }
 #elif (defined IRIVER_H10) || (defined IRIVER_H10_5GB) || defined(ELIO_TPJ1022) \
     || (defined SANSA_E200)
@@ -626,22 +636,40 @@
 /* TODO: Even if it isn't in the target tree, this should be the default case */
 void irq(void)
 {
-    if (CPU_INT_STAT & TIMER1_MASK)
-        TIMER1();
-    else if (CPU_INT_STAT & TIMER2_MASK)
-        TIMER2();
+    if(CURRENT_CORE == CPU)
+    {
+        if (CPU_INT_STAT & TIMER1_MASK)
+            TIMER1();
+        else if (CPU_INT_STAT & TIMER2_MASK)
+            TIMER2();
+    } else {
+        if (COP_INT_STAT & TIMER1_MASK)
+            TIMER1();
+        else if (COP_INT_STAT & TIMER2_MASK)
+            TIMER2();
+    }
 }
 #else
 extern void ipod_4g_button_int(void);
 
 void irq(void)
 {
-    if (CPU_INT_STAT & TIMER1_MASK)
-        TIMER1();
-    else if (CPU_INT_STAT & TIMER2_MASK)
-        TIMER2();
-    else if (CPU_HI_INT_STAT & I2C_MASK)
-        ipod_4g_button_int();
+    if(CURRENT_CORE == CPU)
+    {
+        if (CPU_INT_STAT & TIMER1_MASK)
+            TIMER1();
+        else if (CPU_INT_STAT & TIMER2_MASK)
+            TIMER2();
+        else if (CPU_HI_INT_STAT & I2C_MASK)
+            ipod_4g_button_int();
+    } else {
+        if (COP_INT_STAT & TIMER1_MASK)
+            TIMER1();
+        else if (COP_INT_STAT & TIMER2_MASK)
+            TIMER2();
+        else if (COP_HI_INT_STAT & I2C_MASK)
+            ipod_4g_button_int();
+    }
 }
 #endif
 #endif /* BOOTLOADER */
@@ -694,43 +722,47 @@
 {
     unsigned long postmult;
 
-    if (frequency == CPUFREQ_NORMAL)
-        postmult = CPUFREQ_NORMAL_MULT;
-    else if (frequency == CPUFREQ_MAX)
-        postmult = CPUFREQ_MAX_MULT;
-    else
-        postmult = CPUFREQ_DEFAULT_MULT;
-    cpu_frequency = frequency;
+    if (CURRENT_CORE == CPU)
+    {
+        if (frequency == CPUFREQ_NORMAL)
+            postmult = CPUFREQ_NORMAL_MULT;
+        else if (frequency == CPUFREQ_MAX)
+            postmult = CPUFREQ_MAX_MULT;
+        else
+            postmult = CPUFREQ_DEFAULT_MULT;
+        cpu_frequency = frequency;
 
-    /* Enable PLL? */
-    outl(inl(0x70000020) | (1<<30), 0x70000020);
+        /* Enable PLL? */
+        outl(inl(0x70000020) | (1<<30), 0x70000020);
 
-    /* Select 24MHz crystal as clock source? */
-    outl((inl(0x60006020) & 0x0fffff0f) | 0x20000020, 0x60006020);
+        /* Select 24MHz crystal as clock source? */
+        outl((inl(0x60006020) & 0x0fffff0f) | 0x20000020, 0x60006020);
 
-    /* Clock frequency = (24/8)*postmult */
-    outl(0xaa020000 | 8 | (postmult << 8), 0x60006034);
+        /* Clock frequency = (24/8)*postmult */
+        outl(0xaa020000 | 8 | (postmult << 8), 0x60006034);
 
-    /* Wait for PLL relock? */
-    udelay(2000);
+        /* Wait for PLL relock? */
+        udelay(2000);
 
-    /* Select PLL as clock source? */
-    outl((inl(0x60006020) & 0x0fffff0f) | 0x20000070, 0x60006020);
+        /* Select PLL as clock source? */
+        outl((inl(0x60006020) & 0x0fffff0f) | 0x20000070, 0x60006020);
 
 #if defined(IPOD_COLOR) || defined(IPOD_4G) || defined(IPOD_MINI) || defined(IRIVER_H10) || defined(IRIVER_H10_5GB)
-    /* We don't know why the timer interrupt gets disabled on the PP5020
-       based ipods, but without the following line, the 4Gs will freeze
-       when CPU frequency changing is enabled.
+        /* We don't know why the timer interrupt gets disabled on the PP5020
+           based ipods, but without the following line, the 4Gs will freeze
+           when CPU frequency changing is enabled.
 
-       Note also that a simple "CPU_INT_EN = TIMER1_MASK;" (as used
-       elsewhere to enable interrupts) doesn't work, we need "|=".
+           Note also that a simple "CPU_INT_EN = TIMER1_MASK;" (as used
+           elsewhere to enable interrupts) doesn't work, we need "|=".
 
-       It's not needed on the PP5021 and PP5022 ipods.
-    */
+           It's not needed on the PP5021 and PP5022 ipods.
+        */
 
-    /* unmask interrupt source */
-    CPU_INT_EN |= TIMER1_MASK;
+        /* unmask interrupt source */
+        CPU_INT_EN |= TIMER1_MASK;
+        COP_INT_EN |= TIMER1_MASK;
 #endif
+    }
 }
 #elif !defined(BOOTLOADER)
 void ipod_set_cpu_frequency(void)
@@ -754,25 +786,34 @@
 void system_init(void)
 {
 #ifndef BOOTLOADER
-    /* Remap the flash ROM from 0x00000000 to 0x20000000. */
-    MMAP3_LOGICAL  = 0x20000000 | 0x3a00;
-    MMAP3_PHYSICAL = 0x00000000 | 0x3f84;
+    if (CURRENT_CORE == CPU)
+    {
+        /* Remap the flash ROM from 0x00000000 to 0x20000000. */
+        MMAP3_LOGICAL  = 0x20000000 | 0x3a00;
+        MMAP3_PHYSICAL = 0x00000000 | 0x3f84;
 
-    /* The hw revision is written to the last 4 bytes of SDRAM by the
-       bootloader - we save it before Rockbox overwrites it. */
-    ipod_hw_rev = (*((volatile unsigned long*)(0x01fffffc)));
+        /* The hw revision is written to the last 4 bytes of SDRAM by the
+           bootloader - we save it before Rockbox overwrites it. */
+        ipod_hw_rev = (*((volatile unsigned long*)(0x01fffffc)));
 
-    /* disable all irqs */
-    outl(-1, 0x60001138);
-    outl(-1, 0x60001128);
-    outl(-1, 0x6000111c);
+        /* disable all irqs */
+        outl(-1, 0x60001138);
+        outl(-1, 0x60001128);
+        outl(-1, 0x6000111c);
 
-    outl(-1, 0x60001038);
-    outl(-1, 0x60001028);
-    outl(-1, 0x6000101c);
-#ifndef HAVE_ADJUSTABLE_CPU_FREQ
-    ipod_set_cpu_frequency();
+        outl(-1, 0x60001038);
+        outl(-1, 0x60001028);
+        outl(-1, 0x6000101c);
+#if (!defined HAVE_ADJUSTABLE_CPU_FREQ) && (NUM_CORES == 1)
+        ipod_set_cpu_frequency();
 #endif
+    }
+#if (!defined HAVE_ADJUSTABLE_CPU_FREQ) && (NUM_CORES > 1)
+    else
+    {
+        ipod_set_cpu_frequency();
+    }
+#endif
     ipod_init_cache();
 #endif
 }
@@ -796,10 +837,18 @@
 
 void irq(void)
 {
-    if (CPU_INT_STAT & TIMER1_MASK)
-        TIMER1();
-    else if (CPU_INT_STAT & TIMER2_MASK)
-        TIMER2();
+    if(CURRENT_CORE == CPU)
+    {
+        if (CPU_INT_STAT & TIMER1_MASK)
+            TIMER1();
+        else if (CPU_INT_STAT & TIMER2_MASK)
+            TIMER2();
+    } else {
+        if (COP_INT_STAT & TIMER1_MASK)
+            TIMER1();
+        else if (COP_INT_STAT & TIMER2_MASK)
+            TIMER2();
+    }
 }
 
 #endif
@@ -848,29 +897,32 @@
 {
     unsigned long postmult;
 
-    if (frequency == CPUFREQ_NORMAL)
-        postmult = CPUFREQ_NORMAL_MULT;
-    else if (frequency == CPUFREQ_MAX)
-        postmult = CPUFREQ_MAX_MULT;
-    else
-        postmult = CPUFREQ_DEFAULT_MULT;
-    cpu_frequency = frequency;
+    if (CURRENT_CORE == CPU)
+    {
+        if (frequency == CPUFREQ_NORMAL)
+            postmult = CPUFREQ_NORMAL_MULT;
+        else if (frequency == CPUFREQ_MAX)
+            postmult = CPUFREQ_MAX_MULT;
+        else
+            postmult = CPUFREQ_DEFAULT_MULT;
+        cpu_frequency = frequency;
 
-    outl(0x02, 0xcf005008);
-    outl(0x55, 0xcf00500c);
-    outl(0x6000, 0xcf005010);
+        outl(0x02, 0xcf005008);
+        outl(0x55, 0xcf00500c);
+        outl(0x6000, 0xcf005010);
 
-    /* Clock frequency = (24/8)*postmult */
-    outl(8, 0xcf005018);
-    outl(postmult, 0xcf00501c);
+        /* Clock frequency = (24/8)*postmult */
+        outl(8, 0xcf005018);
+        outl(postmult, 0xcf00501c);
 
-    outl(0xe000, 0xcf005010);
+        outl(0xe000, 0xcf005010);
 
-    /* Wait for PLL relock? */
-    udelay(2000);
+        /* Wait for PLL relock? */
+        udelay(2000);
 
-    /* Select PLL as clock source? */
-    outl(0xa8, 0xcf00500c);
+        /* Select PLL as clock source? */
+        outl(0xa8, 0xcf00500c);
+    }
 }
 #elif !defined(BOOTLOADER)
 static void ipod_set_cpu_speed(void)
@@ -901,17 +953,20 @@
 void system_init(void)
 {
 #ifndef BOOTLOADER
-    /* Remap the flash ROM from 0x00000000 to 0x20000000. */
-    MMAP3_LOGICAL  = 0x20000000 | 0x3a00;
-    MMAP3_PHYSICAL = 0x00000000 | 0x3f84;
+    if (CURRENT_CORE == CPU)
+    {
+        /* Remap the flash ROM from 0x00000000 to 0x20000000. */
+        MMAP3_LOGICAL  = 0x20000000 | 0x3a00;
+        MMAP3_PHYSICAL = 0x00000000 | 0x3f84;
 
-    ipod_hw_rev = (*((volatile unsigned long*)(0x01fffffc)));
-    outl(-1, 0xcf00101c);
-    outl(-1, 0xcf001028);
-    outl(-1, 0xcf001038);
+        ipod_hw_rev = (*((volatile unsigned long*)(0x01fffffc)));
+        outl(-1, 0xcf00101c);
+        outl(-1, 0xcf001028);
+        outl(-1, 0xcf001038);
 #ifndef HAVE_ADJUSTABLE_CPU_FREQ
-    ipod_set_cpu_speed();
+        ipod_set_cpu_speed();
 #endif
+    }
     ipod_init_cache();
 #endif
 }
Index: firmware/backlight.c
===================================================================
--- firmware/backlight.c	(revision 12574)
+++ firmware/backlight.c	(working copy)
@@ -582,7 +582,8 @@
     
     create_thread(backlight_thread, backlight_stack,
                   sizeof(backlight_stack), backlight_thread_name 
-                  IF_PRIO(, PRIORITY_SYSTEM));
+                  IF_PRIO(, PRIORITY_SYSTEM)
+		  IF_COP(, CPU, false));
     tick_add_task(backlight_tick);
 }
 
Index: firmware/target/arm/crt0-pp.S
===================================================================
--- firmware/target/arm/crt0-pp.S	(revision 12574)
+++ firmware/target/arm/crt0-pp.S	(working copy)
@@ -222,6 +222,19 @@
     strhi  r4, [r2], #4
     bhi    2b
 
+    /* Set up stack for IRQ mode */
+    msr    cpsr_c, #0xd2
+    ldr    sp, =cop_irq_stack
+    /* Set up stack for FIQ mode */
+    msr    cpsr_c, #0xd1
+    ldr    sp, =fiq_stack
+
+    /* Let abort and undefined modes use IRQ stack */
+    msr    cpsr_c, #0xd7
+    ldr    sp, =cop_irq_stack
+    msr    cpsr_c, #0xdb
+    ldr    sp, =cop_irq_stack
+
     ldr    sp, =cop_stackend
     
     /* Run cop_main() in apps/main.c */
@@ -307,6 +320,10 @@
     .space 256*4
 irq_stack:
 
+/* 256 words of COP IRQ stack */
+    .space 256*4
+cop_irq_stack:
+
 /* 256 words of FIQ stack */
     .space 256*4
 fiq_stack:
Index: firmware/target/arm/sandisk/sansa-e200/ata-e200.c
===================================================================
--- firmware/target/arm/sandisk/sansa-e200/ata-e200.c	(revision 12574)
+++ firmware/target/arm/sandisk/sansa-e200/ata-e200.c	(working copy)
@@ -687,7 +687,8 @@
     {
         queue_init(&sd_queue, true);
         create_thread(sd_thread, sd_stack,
-                      sizeof(sd_stack), sd_thread_name IF_PRIO(, PRIORITY_SYSTEM));
+                      sizeof(sd_stack), sd_thread_name IF_PRIO(, PRIORITY_SYSTEM)
+		      IF_COP(, CPU, false));
         initialized = true;
     }
 
Index: firmware/common/dircache.c
===================================================================
--- firmware/common/dircache.c	(revision 12574)
+++ firmware/common/dircache.c	(working copy)
@@ -701,7 +701,8 @@
     
     queue_init(&dircache_queue, true);
     create_thread(dircache_thread, dircache_stack,
-                sizeof(dircache_stack), dircache_thread_name IF_PRIO(, PRIORITY_BACKGROUND));
+                sizeof(dircache_stack), dircache_thread_name IF_PRIO(, PRIORITY_BACKGROUND)
+		IF_COP(, CPU, false));
 }
 
 /**
Index: firmware/thread.c
===================================================================
--- firmware/thread.c	(revision 12574)
+++ firmware/thread.c	(working copy)
@@ -64,6 +64,10 @@
 #endif
 #endif
 
+#if (NUM_CORES > 1)
+bool IDATA_ATTR kernel_running_on_cop = false;
+#endif
+
 /* Conserve IRAM
 static void add_to_list(struct thread_entry **list,
                         struct thread_entry *thread) ICODE_ATTR;
@@ -316,10 +320,13 @@
 #elif CONFIG_CPU == SH7034
         and_b(0x7F, &SBYCR);
         asm volatile ("sleep");
-#elif CONFIG_CPU == PP5020
+#elif defined (CPU_PP)
         /* This should sleep the CPU. It appears to wake by itself on
            interrupts */
-        CPU_CTL = 0x80000000;
+        if (CURRENT_CORE == CPU)
+            CPU_CTL = PROC_SLEEP;
+        else
+            COP_CTL = PROC_SLEEP;
 #elif CONFIG_CPU == S3C2440
         CLKCON |= (1 << 2); /* set IDLE bit */
         for(i=0; i<10; i++); /* wait for IDLE */
@@ -608,28 +615,17 @@
 }
 
 /*---------------------------------------------------------------------------
- * Create thread on the current core.
- * Return ID if context area could be allocated, else -1.
+ * Create a thread
+ * If using a dual core architecture, specify which core to start the thread
+ * on, and whether to fall back to the other core if it can't be created
+ * Return ID if context area could be allocated, else NULL.
  *---------------------------------------------------------------------------
  */
 struct thread_entry* 
     create_thread(void (*function)(void), void* stack, int stack_size,
-                  const char *name IF_PRIO(, int priority))
+                  const char *name IF_PRIO(, int priority)
+		  IF_COP(, unsigned int core, bool fallback))
 {
-    return create_thread_on_core(CURRENT_CORE, function, stack, stack_size,
-                  name IF_PRIO(, priority));
-}
-
-/*---------------------------------------------------------------------------
- * Create thread on a specific core.
- * Return ID if context area could be allocated, else -1.
- *---------------------------------------------------------------------------
- */
-struct thread_entry* 
-    create_thread_on_core(unsigned int core, void (*function)(void), 
-                          void* stack, int stack_size,
-                          const char *name IF_PRIO(, int priority))
-{
     unsigned int i;
     unsigned int stacklen;
     unsigned int *stackptr;
@@ -637,6 +633,29 @@
     struct regs *regs;
     struct thread_entry *thread;
 
+/*****
+ * Ugly code alert!
+ * To prevent ifdef hell while keeping the binary size down, we define
+ * core here if it hasn't been passed as a parameter
+ *****/
+#if NUM_CORES == 1
+#define core CPU
+#endif
+
+#if NUM_CORES > 1
+/* If the kernel hasn't initialised on the COP (most likely due to an old
+ * bootloader) then refuse to start threads on the COP
+ */
+    if((core == COP) && !kernel_running_on_cop)
+    {
+        if (fallback)
+            return create_thread(function, stack, stack_size, name
+	                         IF_PRIO(, priority) IF_COP(, CPU, false));
+	else
+            return NULL;
+    }
+#endif
+
     for (n = 0; n < MAXTHREADS; n++)
     {
         if (cores[core].threads[n].name == NULL)
@@ -644,9 +663,16 @@
     }
     
     if (n == MAXTHREADS)
-        return NULL;
+    {
+#if NUM_CORES > 1
+        if (fallback)
+	    return create_thread(function, stack, stack_size, name
+	                         IF_PRIO(, priority) IF_COP(, 1 - core, fallback));
+	else
+#endif
+            return NULL;
+    }
     
-    
     /* Munge the stack to make it easy to spot stack overflows */
     stacklen = stack_size / sizeof(int);
     stackptr = stack;
@@ -677,6 +703,9 @@
     THREAD_CPU_INIT(core, thread);
 
     return thread;
+#if NUM_CORES == 1
+#undef core
+#endif
 }
 
 #ifdef HAVE_SCHEDULER_BOOSTCTRL
@@ -751,7 +780,8 @@
 {
     unsigned int core = CURRENT_CORE;
 
-    memset(cores, 0, sizeof cores);
+    if (core == CPU)
+        memset(cores, 0, sizeof cores);
     cores[core].sleeping = NULL;
     cores[core].running = NULL;
     cores[core].threads[0].name = main_thread_name;
@@ -779,6 +809,10 @@
 #endif
     }
     cores[core].threads[0].context.start = 0; /* thread 0 already running */
+#if NUM_CORES > 1
+    if(core == COP)
+        kernel_running_on_cop = true; /* can we use context.start for this? */
+#endif
 }
 
 int thread_stack_usage(const struct thread_entry *thread)
Index: firmware/pcm_record.c
===================================================================
--- firmware/pcm_record.c	(revision 12574)
+++ firmware/pcm_record.c	(working copy)
@@ -418,7 +418,8 @@
     queue_enable_queue_send(&pcmrec_queue, &pcmrec_queue_send);
     pcmrec_thread_p =
         create_thread(pcmrec_thread, pcmrec_stack, sizeof(pcmrec_stack),
-                      pcmrec_thread_name IF_PRIO(, PRIORITY_RECORDING));
+                      pcmrec_thread_name IF_PRIO(, PRIORITY_RECORDING)
+		      IF_COP(, CPU, false));
 } /* pcm_rec_init */
 
 /** audio_* group **/
Index: firmware/powermgmt.c
===================================================================
--- firmware/powermgmt.c	(revision 12574)
+++ firmware/powermgmt.c	(working copy)
@@ -1245,7 +1245,8 @@
     /* init history to 0 */
     memset(power_history, 0x00, sizeof(power_history));
     create_thread(power_thread, power_stack, sizeof(power_stack),
-                  power_thread_name IF_PRIO(, PRIORITY_SYSTEM));
+                  power_thread_name IF_PRIO(, PRIORITY_SYSTEM)
+		  IF_COP(, CPU, false));
 }
 
 #endif /* SIMULATOR */
Index: firmware/mpeg.c
===================================================================
--- firmware/mpeg.c	(revision 12574)
+++ firmware/mpeg.c	(working copy)
@@ -2905,7 +2905,8 @@
     queue_init(&mpeg_queue, true);
 #endif /* !SIMULATOR */
     create_thread(mpeg_thread, mpeg_stack,
-                  sizeof(mpeg_stack), mpeg_thread_name IF_PRIO(, PRIORITY_SYSTEM));
+                  sizeof(mpeg_stack), mpeg_thread_name IF_PRIO(, PRIORITY_SYSTEM)
+		  IF_COP(, CPU, false));
 
     memset(trackdata, sizeof(trackdata), 0);
 
Index: firmware/usb.c
===================================================================
--- firmware/usb.c	(revision 12574)
+++ firmware/usb.c	(working copy)
@@ -428,7 +428,8 @@
 #ifndef BOOTLOADER
     queue_init(&usb_queue, true);
     create_thread(usb_thread, usb_stack, sizeof(usb_stack), 
-                  usb_thread_name IF_PRIO(, PRIORITY_SYSTEM));
+                  usb_thread_name IF_PRIO(, PRIORITY_SYSTEM)
+		  IF_COP(, CPU, false));
 
     tick_add_task(usb_tick);
 #endif
Index: firmware/drivers/ata.c
===================================================================
--- firmware/drivers/ata.c	(revision 12574)
+++ firmware/drivers/ata.c	(working copy)
@@ -991,7 +991,8 @@
         last_disk_activity = current_tick;
         create_thread(ata_thread, ata_stack,
                       sizeof(ata_stack), ata_thread_name
-                      IF_PRIO(, PRIORITY_SYSTEM));
+                      IF_PRIO(, PRIORITY_SYSTEM)
+		      IF_COP(, CPU, false));
         initialized = true;
 
     }
Index: firmware/drivers/lcd-1bit-vert.c
===================================================================
--- firmware/drivers/lcd-1bit-vert.c	(revision 12574)
+++ firmware/drivers/lcd-1bit-vert.c	(working copy)
@@ -68,7 +68,8 @@
     /* Call device specific init */
     lcd_init_device();
     create_thread(scroll_thread, scroll_stack,
-                  sizeof(scroll_stack), scroll_name IF_PRIO(, PRIORITY_USER_INTERFACE));
+                  sizeof(scroll_stack), scroll_name IF_PRIO(, PRIORITY_USER_INTERFACE)
+		  IF_COP(, CPU, false));
 }
 
 /*** parameter handling ***/
Index: firmware/drivers/lcd-2bit-vert.c
===================================================================
--- firmware/drivers/lcd-2bit-vert.c	(revision 12574)
+++ firmware/drivers/lcd-2bit-vert.c	(working copy)
@@ -82,7 +82,8 @@
     /* Call device specific init */
     lcd_init_device();
     create_thread(scroll_thread, scroll_stack,
-                  sizeof(scroll_stack), scroll_name IF_PRIO(, PRIORITY_USER_INTERFACE));
+                  sizeof(scroll_stack), scroll_name IF_PRIO(, PRIORITY_USER_INTERFACE)
+		  IF_COP(, CPU, false));
 }
 
 /*** parameter handling ***/
Index: firmware/drivers/lcd-2bit-horz.c
===================================================================
--- firmware/drivers/lcd-2bit-horz.c	(revision 12574)
+++ firmware/drivers/lcd-2bit-horz.c	(working copy)
@@ -79,7 +79,8 @@
     /* Call device specific init */
     lcd_init_device();
     create_thread(scroll_thread, scroll_stack,
-                  sizeof(scroll_stack), scroll_name IF_PRIO(, PRIORITY_USER_INTERFACE));
+                  sizeof(scroll_stack), scroll_name IF_PRIO(, PRIORITY_USER_INTERFACE)
+		  IF_COP(, CPU, false));
 }
 
 /*** parameter handling ***/
Index: firmware/drivers/ata_mmc.c
===================================================================
--- firmware/drivers/ata_mmc.c	(revision 12574)
+++ firmware/drivers/ata_mmc.c	(working copy)
@@ -1173,7 +1173,8 @@
 
         queue_init(&mmc_queue, true);
         create_thread(mmc_thread, mmc_stack,
-                      sizeof(mmc_stack), mmc_thread_name IF_PRIO(, PRIORITY_SYSTEM));
+                      sizeof(mmc_stack), mmc_thread_name IF_PRIO(, PRIORITY_SYSTEM)
+		      IF_COP(, CPU, false));
         tick_add_task(mmc_tick);
         initialized = true;
     }
Index: firmware/drivers/lcd-remote-2bit-vi.c
===================================================================
--- firmware/drivers/lcd-remote-2bit-vi.c	(revision 12574)
+++ firmware/drivers/lcd-remote-2bit-vi.c	(working copy)
@@ -1241,5 +1241,6 @@
     queue_init(&remote_scroll_queue, false);
 #endif
     create_thread(scroll_thread, scroll_stack,
-                  sizeof(scroll_stack), scroll_name IF_PRIO(, PRIORITY_USER_INTERFACE));
+                  sizeof(scroll_stack), scroll_name IF_PRIO(, PRIORITY_USER_INTERFACE)
+		  IF_COP(, CPU, false));
 }
Index: firmware/drivers/lcd-16bit.c
===================================================================
--- firmware/drivers/lcd-16bit.c	(revision 12574)
+++ firmware/drivers/lcd-16bit.c	(working copy)
@@ -89,7 +89,8 @@
 
     create_thread(scroll_thread, scroll_stack,
                   sizeof(scroll_stack), scroll_name
-                  IF_PRIO(, PRIORITY_USER_INTERFACE));
+                  IF_PRIO(, PRIORITY_USER_INTERFACE)
+		  IF_COP(, CPU, false));
 }
 
 /*** parameter handling ***/
Index: firmware/drivers/lcd-remote-1bit-v.c
===================================================================
--- firmware/drivers/lcd-remote-1bit-v.c	(revision 12574)
+++ firmware/drivers/lcd-remote-1bit-v.c	(working copy)
@@ -903,5 +903,6 @@
     queue_init(&remote_scroll_queue, false);
 #endif
     create_thread(scroll_thread, scroll_stack,
-                  sizeof(scroll_stack), scroll_name IF_PRIO(, PRIORITY_USER_INTERFACE));
+                  sizeof(scroll_stack), scroll_name IF_PRIO(, PRIORITY_USER_INTERFACE)
+		  IF_COP(, CPU, false));
 }
Index: firmware/drivers/lcd-player.c
===================================================================
--- firmware/drivers/lcd-player.c	(revision 12574)
+++ firmware/drivers/lcd-player.c	(working copy)
@@ -610,7 +610,8 @@
     lcd_set_contrast(lcd_default_contrast());
 
     create_thread(scroll_thread, scroll_stack,
-                  sizeof(scroll_stack), scroll_name IF_PRIO(, PRIORITY_USER_INTERFACE));
+                  sizeof(scroll_stack), scroll_name IF_PRIO(, PRIORITY_USER_INTERFACE)
+		  IF_COP(, CPU, false));
 }
 
 void lcd_jump_scroll (int mode) /* 0=off, 1=once, ..., JUMP_SCROLL_ALWAYS */
Index: uisimulator/sdl/thread-sdl.c
===================================================================
--- uisimulator/sdl/thread-sdl.c	(revision 12574)
+++ uisimulator/sdl/thread-sdl.c	(working copy)
@@ -75,12 +75,6 @@
     return 0;
 }
 
-int create_thread_on_core(void (*core)(void), void (*fp)(void), void* sp, int stk_size)
-{
-    (void)core;
-    return create_thread(fp, sp, stk_size);
-}
-
 void init_threads(void)
 {
     m = SDL_CreateMutex();
Index: uisimulator/common/stubs.c
===================================================================
--- uisimulator/common/stubs.c	(revision 12574)
+++ uisimulator/common/stubs.c	(working copy)
@@ -258,12 +258,6 @@
     (void)threadnum;
 }
 
-void remove_thread_on_core(unsigned int core, int threadnum)
-{
-    (void)core;
-    (void)threadnum;
-}
-
 /* assure an unused place to direct virtual pointers to */
 #define VIRT_SIZE 0xFFFF /* more than enough for our string ID range */
 unsigned char vp_dummy[VIRT_SIZE];
