diff --git a/libretro-common/libco/libco.c b/libretro-common/libco/libco.c index 63126d3c23..456e9ed374 100644 --- a/libretro-common/libco/libco.c +++ b/libretro-common/libco/libco.c @@ -10,6 +10,8 @@ #include "amd64.c" #elif defined(__GNUC__) && defined(_ARCH_PPC) #include "ppc.c" +#elif defined(__GNUC__) && defined(VITA) + #include "psp2.c" #elif defined(__GNUC__) && (defined(__ARM_EABI__) || defined(__arm__)) #include "armeabi.c" #elif defined(__GNUC__) diff --git a/libretro-common/libco/psp2.c b/libretro-common/libco/psp2.c new file mode 100644 index 0000000000..eaf9b7a58f --- /dev/null +++ b/libretro-common/libco/psp2.c @@ -0,0 +1,113 @@ +/* +libco.arm (2015-06-18) +license: public domain +*/ + +#define LIBCO_C +#include "libco.h" + +#include +#include +#include +#include +#include +#include + +#define FOUR_KB_ALIGN(x) align(x, 12) +#define MB_ALIGN(x) align(x, 20) + +#ifdef __cplusplus +extern "C" { +#endif + + static inline int align(int x, int n) + { + return (((x >> n) + 1) << n); + } + + static thread_local unsigned long co_active_buffer[64]; + static thread_local cothread_t co_active_handle = 0; + static void(*co_swap)(cothread_t, cothread_t) = 0; + static int block; + static uint32_t co_swap_function[] = { + 0xe8a16ff0, /* stmia r1!, {r4-r11,sp,lr} */ + 0xe8b0aff0, /* ldmia r0!, {r4-r11,sp,pc} */ + 0xe12fff1e, /* bx lr */ + }; + + void co_init() + { + int ret; + void *base; + + block = sceKernelAllocMemBlockForVM("libco", + MB_ALIGN(FOUR_KB_ALIGN(sizeof co_swap_function))); + if (block < 0) + return; + + /* get base address */ + ret = sceKernelGetMemBlockBase(block, &base); + if (ret < 0) + return; + + /* set domain to be writable by user */ + ret = sceKernelOpenVMDomain(); + if (ret < 0) + return; + + memcpy(base, co_swap_function, sizeof co_swap_function); + + /* set domain back to read-only */ + ret = sceKernelCloseVMDomain(); + if (ret < 0) + return; + + /* flush icache */ + ret = sceKernelSyncVMDomain(block, base, + MB_ALIGN(FOUR_KB_ALIGN(sizeof co_swap_function))); + if (ret < 0) + return; + + co_swap = (void(*)(cothread_t, cothread_t))base; + } + + cothread_t co_active(void) + { + if (!co_active_handle) co_active_handle = &co_active_buffer; + return co_active_handle; + } + + cothread_t co_create(unsigned int size, void(*entrypoint)(void)) + { + unsigned long* handle = 0; + if (!co_swap) + co_init(); + if (!co_active_handle) co_active_handle = &co_active_buffer; + size += 256; + size &= ~15; + + if ((handle = (unsigned long*)malloc(size))) + { + unsigned long* p = (unsigned long*)((unsigned char*)handle + size); + handle[8] = (unsigned long)p; + handle[9] = (unsigned long)entrypoint; + } + + return handle; + } + + void co_delete(cothread_t handle) + { + free(handle); + sceKernelFreeMemBlock(block); + } + + void co_switch(cothread_t handle) + { + cothread_t co_previous_handle = co_active_handle; + co_swap(co_active_handle = handle, co_previous_handle); + } + +#ifdef __cplusplus +} +#endif