diff --git a/libretro-common/libco/libco.c b/libretro-common/libco/libco.c
index 50b6442c4b..36cd7da8f3 100644
--- a/libretro-common/libco/libco.c
+++ b/libretro-common/libco/libco.c
@@ -29,6 +29,8 @@ void genode_free_secondary_stack(void *stack);
     #include "ppc.c"
   #elif defined(__aarch64__)
     #include "aarch64.c"
+  #elif defined(PSP)
+    #include "psp1.c"
   #elif defined VITA
     #include "scefiber.c"
   #elif defined(__ARM_EABI__) || defined(__arm__)
diff --git a/libretro-common/libco/psp1.c b/libretro-common/libco/psp1.c
new file mode 100644
index 0000000000..45850a4d83
--- /dev/null
+++ b/libretro-common/libco/psp1.c
@@ -0,0 +1,45 @@
+#define LIBCO_C
+#include "libco.h"
+
+#include <stdlib.h>
+#include <pspthreadman.h>
+
+/* Since cothread_t is a void pointer it must contain an address. We can't return a reference to a local variable
+ * because it would go out of scope, so we create a static variable instead so we can return a reference to it.
+ */
+static SceUID active_thread_id = 0;
+
+cothread_t co_active()
+{
+  active_thread_id = sceKernelGetThreadId();
+  return &active_thread_id;
+}
+
+cothread_t co_create(unsigned int size, void (*entrypoint)(void))
+{
+  /* Similar scenario as with active_thread_id except there will only be one active_thread_id while there could be many
+   * new threads each with their own handle, so we create them on the heap instead and delete them manually when they're
+   * no longer needed in co_delete().
+   */
+  cothread_t handle = malloc(sizeof(cothread_t));
+
+  /* SceKernelThreadEntry has a different signature than entrypoint, but in practice this seems to work */
+  SceUID new_thread_id = sceKernelCreateThread("cothread", (SceKernelThreadEntry)entrypoint, 0x12, size, 0, NULL);
+  sceKernelStartThread(new_thread_id, 0, NULL);
+
+  *(SceUID *)handle = new_thread_id;
+  return handle;
+}
+
+void co_delete(cothread_t handle)
+{
+  sceKernelTerminateDeleteThread(*(SceUID *)handle);
+  free(handle);
+}
+
+void co_switch(cothread_t handle)
+{
+  sceKernelWakeupThread(*(SceUID *)handle);
+  /* Sleep the currently active thread so the new thread can start */
+  sceKernelSleepThread();
+}
\ No newline at end of file