add LibretroBridge, c++ bits which facilitate c# interop through the hazards of libco (more refined counterpart of what was tested for libsneshawk)

This commit is contained in:
zeromus 2017-04-17 22:48:16 -05:00
parent f2c48a165a
commit ca90853a88
11 changed files with 4665 additions and 0 deletions

View File

@ -0,0 +1,4 @@
root = true
[*]
indent_style = tab

View File

@ -0,0 +1,849 @@
//derived from libsnes
//types of messages:
//cmd: frontend->core: "command to core" a command from the frontend which causes emulation to proceed. when sending a command, the frontend should wait for an eMessage::BRK_Complete before proceeding, although a debugger might proceed after any BRK
//query: frontend->core: "query to core" a query from the frontend which can (and should) be satisfied immediately by the core but which does not result in emulation processes (notably, nothing resembling a CMD and nothing which can trigger a BRK)
//sig: core->frontend: "core signal" a synchronous operation called from the emulation process which the frontend should handle immediately without issuing any calls into the core
//brk: core->frontend: "core break" the emulation process has suspended. the frontend is free to do whatever it wishes.
#define _CRT_NONSTDC_NO_DEPRECATE
#include <Windows.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdio.h>
#include <string>
#define bool unsigned char
#include "libretro.h"
#undef bool
extern "C" uint64_t cpu_features_get();
#include "libco/libco.h"
//can't use retroarch's dynamic.h, it's too full of weird stuff. don't need it anyway
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint64_t u64;
typedef uint32_t u32;
typedef u8 u8bool;
typedef int16_t s16;
typedef int32_t s32;
typedef int64_t s64;
typedef void(*Action)();
struct retro_core_t
{
void(*retro_init)(void);
void(*retro_deinit)(void);
unsigned(*retro_api_version)(void);
void(*retro_get_system_info)(struct retro_system_info*);
void(*retro_get_system_av_info)(struct retro_system_av_info*);
void(*retro_set_environment)(retro_environment_t);
void(*retro_set_video_refresh)(retro_video_refresh_t);
void(*retro_set_audio_sample)(retro_audio_sample_t);
void(*retro_set_audio_sample_batch)(retro_audio_sample_batch_t);
void(*retro_set_input_poll)(retro_input_poll_t);
void(*retro_set_input_state)(retro_input_state_t);
void(*retro_set_controller_port_device)(unsigned, unsigned);
void(*retro_reset)(void);
void(*retro_run)(void);
size_t(*retro_serialize_size)(void);
u8bool(*retro_serialize)(void*, size_t);
u8bool(*retro_unserialize)(const void*, size_t);
void(*retro_cheat_reset)(void);
void(*retro_cheat_set)(unsigned, u8bool, const char*);
u8bool(*retro_load_game)(const struct retro_game_info*);
u8bool(*retro_load_game_special)(unsigned,
const struct retro_game_info*, size_t);
void(*retro_unload_game)(void);
unsigned(*retro_get_region)(void);
void *(*retro_get_memory_data)(unsigned);
size_t(*retro_get_memory_size)(unsigned);
};
enum eMessage : s32
{
NotSet,
Resume,
QUERY_FIRST,
QUERY_LAST,
CMD_FIRST,
CMD_SetEnvironment,
CMD_LoadNoGame,
CMD_LoadData,
CMD_LoadPath,
CMD_Deinit,
CMD_Reset,
CMD_Run,
CMD_Serialize,
CMD_Unserialize,
CMD_LAST,
SIG_VideoUpdate,
BRK_InputState,
};
enum eStatus : s32
{
eStatus_Idle,
eStatus_CMD,
eStatus_BRK
};
enum BufId : s32 {
Param0 = 0,
Param1 = 1,
SystemDirectory = 2,
SaveDirectory = 3,
CoreDirectory = 4,
CoreAssetsDirectory = 5,
BufId_Num //excess sized by 1.. no big deal
};
//TODO: do any of these need to be volatile?
struct CommStruct
{
//the cmd being executed
eMessage cmd;
//the status of the core
eStatus status;
//the SIG or BRK that the core is halted in
eMessage reason;
//flexible in/out parameters
//these are all "overloaded" a little so it isn't clear what's used for what in for any particular message..
//but I think it will beat having to have some kind of extremely verbose custom layouts for every message
u32 id, addr, value, size;
u32 port, device, index, slot; //for input state
//variables meant for stateful communication (not parameters)
//may be in, out, or inout. it's pretty sloppy.
struct {
//set by the core
retro_system_info retro_system_info;
retro_system_av_info retro_system_av_info;
size_t retro_serialize_size;
u32 retro_region;
u32 retro_api_version;
retro_pixel_format pixel_format; //default is 0 -- RETRO_PIXEL_FORMAT_0RGB1555
s32 rotation_ccw;
bool support_no_game;
retro_get_proc_address_t core_get_proc_address;
retro_game_geometry retro_game_geometry;
u8bool retro_game_geometry_dirty; //c# can clear this when it's acknowledged (but I think we might handle it from here? not sure)
//defined by the core. values arent put here, this is just the variables defined by the core
//todo: shutdown tidy
s32 variable_count;
const char** variable_keys;
const char** variable_comments;
//c# sets these with thunked callbacks
retro_perf_callback retro_perf_callback;
//various stashed stuff solely for c# convenience
u64 processor_features;
s32 fb_width, fb_height; //core sets these; c# picks up, and..
s32* fb_bufptr; //..sets this for the core to spill its data nito
} env;
//always used in pairs
void* buf[BufId_Num];
s32 buf_size[BufId_Num];
//===========================================================
//private stuff
std::string *variables;
bool variables_dirty;
void* privbuf[BufId_Num]; //TODO remember to tidy this.. (needs to be done in snes too)
void SetString(int id, const char* str)
{
size_t len = strlen(str);
CopyBuffer(id, (void*)str, len+1);
}
void CopyBuffer(int id, void* ptr, s32 size)
{
if (privbuf[id]) free(privbuf[id]);
buf[id] = privbuf[id] = malloc(size);
memcpy(buf[id], ptr, size);
buf_size[id] = size;
}
void SetBuffer(int id, void* ptr, s32 size)
{
buf[id] = ptr;
buf_size[id] = size;
}
struct {
} strings;
HMODULE dllModule;
retro_core_t funs;
void LoadSymbols()
{
//retroarch would throw an error here if the FP ws null. maybe better than throwing an error later, but are all the functions required?
# define SYMBOL(x) { \
FARPROC func = GetProcAddress(dllModule, #x); \
memcpy(&funs.x, &func, sizeof(func)); \
}
SYMBOL(retro_init);
SYMBOL(retro_deinit);
SYMBOL(retro_api_version);
SYMBOL(retro_get_system_info);
SYMBOL(retro_get_system_av_info);
SYMBOL(retro_set_environment);
SYMBOL(retro_set_video_refresh);
SYMBOL(retro_set_audio_sample);
SYMBOL(retro_set_audio_sample_batch);
SYMBOL(retro_set_input_poll);
SYMBOL(retro_set_input_state);
SYMBOL(retro_set_controller_port_device);
SYMBOL(retro_reset);
SYMBOL(retro_run);
SYMBOL(retro_serialize_size);
SYMBOL(retro_serialize);
SYMBOL(retro_unserialize);
SYMBOL(retro_cheat_reset);
SYMBOL(retro_cheat_set);
SYMBOL(retro_load_game);
SYMBOL(retro_load_game_special);
SYMBOL(retro_unload_game);
SYMBOL(retro_get_region);
SYMBOL(retro_get_memory_data);
SYMBOL(retro_get_memory_size);
}
retro_core_t fn;
} comm;
//coroutines
cothread_t co_control, co_emu, co_emu_suspended;
//internal state
Action CMD_cb;
void BREAK(eMessage msg) {
comm.status = eStatus_BRK;
comm.reason = msg;
co_emu_suspended = co_active();
co_switch(co_control);
comm.status = eStatus_CMD;
}
//all this does is run commands on the emulation thread infinitely forever
//(I should probably make a mechanism for bailing...)
void new_emuthread()
{
for (;;)
{
//process the current CMD
CMD_cb();
//when that returned, we're definitely done with the CMD--so we're now IDLE
comm.status = eStatus_Idle;
co_switch(co_control);
}
}
void retro_log_printf(enum retro_log_level level, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vprintf(fmt,args);
va_end(args);
}
u8bool retro_environment(unsigned cmd, void *data)
{
switch (cmd)
{
case RETRO_ENVIRONMENT_SET_ROTATION:
comm.env.rotation_ccw = (int)*(const unsigned*)data * 90;
return true;
case RETRO_ENVIRONMENT_GET_OVERSCAN:
return false; //could return true to crop overscan
case RETRO_ENVIRONMENT_GET_CAN_DUPE:
return true;
case RETRO_ENVIRONMENT_SET_MESSAGE:
{
//TODO: try to respect design principle by forwarding to frontend with the timer
auto &msg = *(retro_message*)data;
printf("%s\n",msg.msg);
return true;
}
case RETRO_ENVIRONMENT_SHUTDOWN:
//TODO low priority
return false;
case RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL:
//unneeded
return false;
case RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY:
*(const char**)data = (const char*)comm.buf[SystemDirectory];
return true;
case RETRO_ENVIRONMENT_SET_PIXEL_FORMAT:
comm.env.pixel_format = *(const enum retro_pixel_format*)data;
return true;
case RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS:
//TODO medium priority
return false;
case RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK:
//TODO high priority (to support keyboard consoles, probably high value for us. but that may take a lot of infrastructure work)
return false;
case RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE:
//TODO high priority (to support disc systems)
return false;
case RETRO_ENVIRONMENT_SET_HW_RENDER:
//TODO high priority (to support 3d renderers
return false;
case RETRO_ENVIRONMENT_GET_VARIABLE:
{
//according to retroarch's `core_option_manager_get` this is what we should do
comm.variables_dirty = false;
auto req = (retro_variable *)data;
req->value = nullptr;
for(int i=0;i<comm.env.variable_count;i++)
{
if(!strcmp(comm.env.variable_keys[i],req->key))
{
req->value = comm.variables[i].c_str();
return true;
}
}
return true;
}
case RETRO_ENVIRONMENT_SET_VARIABLES:
{
auto var = (retro_variable *)data;
int nVars = 0;
while(var->key)
nVars++, var++;
comm.variables = new std::string[nVars];
comm.env.variable_count = nVars;
comm.env.variable_keys = new const char*[nVars];
comm.env.variable_comments = new const char*[nVars];
var = (retro_variable *)data;
for(int i=0;i<nVars;i++)
{
comm.env.variable_keys[i] = var[i].key;
comm.env.variable_comments[i] = var[i].value;
//analyze to find default and save it
std::string comment = var[i].value;
auto ofs = comment.find_first_of(';')+2;
auto pipe = comment.find('|',ofs);
if(pipe == std::string::npos)
comm.variables[i] = comment.substr(ofs);
else
comm.variables[i] = comment.substr(ofs,pipe-ofs);
}
return true;
}
case RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE:
*(u8bool*)data = comm.variables_dirty;
break;
case RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME:
comm.env.support_no_game = !!*(u8bool*)data;
break;
case RETRO_ENVIRONMENT_GET_LIBRETRO_PATH:
*(const char**)data = (const char*)comm.buf[CoreDirectory];
return true;
case RETRO_ENVIRONMENT_SET_AUDIO_CALLBACK:
//dont know what to do with this yet
return false;
case RETRO_ENVIRONMENT_SET_FRAME_TIME_CALLBACK:
//dont know what to do with this yet
return false;
case RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE:
//TODO low priority
return false;
case RETRO_ENVIRONMENT_GET_INPUT_DEVICE_CAPABILITIES:
//TODO medium priority - other input methods
*(u64*)data = (1<<RETRO_DEVICE_JOYPAD);
return true;
case RETRO_ENVIRONMENT_GET_LOG_INTERFACE:
((retro_log_callback*)data)->log = retro_log_printf;
return true;
case RETRO_ENVIRONMENT_GET_PERF_INTERFACE:
*((retro_perf_callback *)data) = comm.env.retro_perf_callback;
return true;
case RETRO_ENVIRONMENT_GET_LOCATION_INTERFACE:
//TODO low priority
return false;
case RETRO_ENVIRONMENT_GET_CORE_ASSETS_DIRECTORY:
*(const char**)data = (const char*)comm.buf[CoreAssetsDirectory];
return true;
case RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY:
*(const char**)data = (const char*)comm.buf[SaveDirectory];
return true;
case RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO:
printf("NEED RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO\n");
return false;
case RETRO_ENVIRONMENT_SET_PROC_ADDRESS_CALLBACK:
comm.env.core_get_proc_address = ((retro_get_proc_address_interface*)data)->get_proc_address;
return true;
case RETRO_ENVIRONMENT_SET_SUBSYSTEM_INFO:
//needs retro_load_game_special to be useful; not supported yet
return false;
case RETRO_ENVIRONMENT_SET_CONTROLLER_INFO:
//TODO medium priority probably
return false;
case RETRO_ENVIRONMENT_SET_GEOMETRY:
comm.env.retro_game_geometry = *((const retro_game_geometry *)data);
comm.env.retro_game_geometry_dirty = true;
return true;
case RETRO_ENVIRONMENT_GET_USERNAME:
//we definitely want to return false here so the core will do something deterministic
return false;
case RETRO_ENVIRONMENT_GET_LANGUAGE:
*((unsigned *)data) = RETRO_LANGUAGE_ENGLISH;
return true;
}
return false;
}
template<int ROT> static inline int* address(int width, int height, int pitch, int x, int y, int* dstbuf, int* optimize0dst)
{
switch (ROT)
{
case 0:
return optimize0dst;
case 90:
//TODO:
return optimize0dst;
case 180:
//TODO:
return optimize0dst;
case 270:
{
int dx = width - y - 1;
int dy = x;
return dstbuf + dy * width + dx;
}
default:
//impossible
return 0;
}
}
template<int ROT> void Blit555(short* srcbuf, s32* dstbuf, int width, int height, int pitch)
{
s32* dst = dstbuf;
for (int y = 0; y < height; y++)
{
short* row = srcbuf;
for (int x = 0; x < width; x++)
{
short ci = *row;
int r = ci & 0x001f;
int g = ci & 0x03e0;
int b = ci & 0x7c00;
r = (r << 3) | (r >> 2);
g = (g >> 2) | (g >> 7);
b = (b >> 7) | (b >> 12);
int co = r | g | b | 0xff000000;
*address<ROT>(width, height, pitch, x, y, dstbuf, dst) = co;
dst++;
row++;
}
srcbuf += pitch/2;
}
}
template<int ROT> void Blit565(short* srcbuf, s32* dstbuf, int width, int height, int pitch)
{
s32* dst = dstbuf;
for (int y = 0; y < height; y++)
{
short* row = srcbuf;
for (int x = 0; x < width; x++)
{
short ci = *row;
int r = ci & 0x001f;
int g = (ci & 0x07e0) >> 5;
int b = (ci & 0xf800) >> 11;
r = (r << 3) | (r >> 2);
g = (g << 2) | (g >> 4);
b = (b << 3) | (b >> 2);
int co = (b << 16) | (g << 8) | r;
*address<ROT>(width, height, pitch, x, y, dstbuf, dst) = co;
dst++;
row++;
}
srcbuf += pitch/2;
}
}
template<int ROT> void Blit888(int* srcbuf, s32* dstbuf, int width, int height, int pitch)
{
s32* dst = dstbuf;
for (int y = 0; y < height; y++)
{
int* row = srcbuf;
for (int x = 0; x < width; x++)
{
int ci = *row;
int co = ci | 0xff000000;
*address<ROT>(width,height,pitch,x,y,dstbuf,dst) = co;
dst++;
row++;
}
srcbuf += pitch/4;
}
}
void retro_video_refresh(const void *data, unsigned width, unsigned height, size_t pitch)
{
//handle a "dup frame" -- same as previous frame. so there isn't anything to be done here
if (!data)
return;
comm.env.fb_width = (s32)width;
comm.env.fb_height = (s32)height;
//notify c# of these new settings and let it allocate a buffer suitable for receiving the output (so we can work directly into c#'s int[])
//c# can read the settings right out of the comm env
//NOPE: not needed. for now, anyway. may want to notify later
//BREAK(eMessage::SIG_VideoUpdate);
////if (BufferWidth != width) BufferWidth = (int)width;
////if (BufferHeight != height) BufferHeight = (int)height;
////if (BufferWidth * BufferHeight != rawvidbuff.Length)
//// rawvidbuff = new int[BufferWidth * BufferHeight];
////if we have rotation, we might have a geometry mismatch and in any event we need a temp buffer to do the rotation from
////but that's a general problem, isnt it?
//if (comm.env.fb.raw == nullptr || comm.env.fb.raw_length != width * height)
//{
// if(comm.env.fb.raw)
// delete[] comm.env.fb.raw;
// comm.env.fb.raw = new u32[width * height];
// comm.env.fb.width = width;
// comm.env.fb.height = height;
//}
int w = (int)width;
int h = (int)height;
int p = (int)pitch;
switch(comm.env.pixel_format)
{
case RETRO_PIXEL_FORMAT_0RGB1555:
switch (comm.env.rotation_ccw)
{
case 0: Blit555<0>((short*)data, comm.env.fb_bufptr, w, h, p); break;
case 90: Blit555<90>((short*)data, comm.env.fb_bufptr, w, h, p); break;
case 180: Blit555<180>((short*)data, comm.env.fb_bufptr, w, h, p); break;
case 270: Blit555<270>((short*)data, comm.env.fb_bufptr, w, h, p); break;
}
break;
case RETRO_PIXEL_FORMAT_XRGB8888:
switch(comm.env.rotation_ccw)
{
case 0: Blit888<0>((int*)data, comm.env.fb_bufptr, w, h, p); break;
case 90: Blit888<90>((int*)data, comm.env.fb_bufptr, w, h, p); break;
case 180: Blit888<180>((int*)data, comm.env.fb_bufptr, w, h, p); break;
case 270: Blit888<270>((int*)data, comm.env.fb_bufptr, w, h, p); break;
}
break;
case RETRO_PIXEL_FORMAT_RGB565:
switch (comm.env.rotation_ccw)
{
case 0: Blit565<0>((short*)data, comm.env.fb_bufptr, w, h, p); break;
case 90: Blit565<90>((short*)data, comm.env.fb_bufptr, w, h, p); break;
case 180: Blit565<180>((short*)data, comm.env.fb_bufptr, w, h, p); break;
case 270: Blit565<270>((short*)data, comm.env.fb_bufptr, w, h, p); break;
}
break;
}
}
void retro_audio_sample(s16 left, s16 right)
{
}
size_t retro_audio_sample_batch(const s16 *data, size_t frames)
{
return 0;
}
void retro_input_poll()
{
}
s16 retro_input_state(unsigned port, unsigned device, unsigned index, unsigned id)
{
//we have to bail to c# for this, it's too complex.
comm.port = port;
comm.device = device;
comm.index = index;
comm.id = id;
BREAK(eMessage::BRK_InputState);
return (s16)comm.value;
}
//loads the game, too
//REQUIREMENTS:
//set SystemDirectory, SaveDirectory, CoreDirectory, CoreAssetsDirectory are set
//retro_perf_callback is set
static void LoadHandler(eMessage msg)
{
//retro_set_environment() is guaranteed to be called before retro_init().
comm.funs.retro_init();
retro_game_info rgi;
memset(&rgi,0,sizeof(rgi));
if (msg == eMessage::CMD_LoadNoGame) {}
else
{
rgi.path = (const char*)comm.buf[BufId::Param0];
if (msg == eMessage::CMD_LoadData)
{
rgi.data = comm.buf[BufId::Param1];
rgi.size = comm.buf_size[BufId::Param1];
}
}
comm.funs.retro_load_game(&rgi);
//Can be called only after retro_load_game() has successfully completed.
comm.funs.retro_get_system_av_info(&comm.env.retro_system_av_info);
//guaranteed to have been called before the first call to retro_run() is made.
//(I've put this after the retro_system_av_info runs, in case that's important
comm.funs.retro_set_video_refresh(retro_video_refresh);
comm.funs.retro_set_audio_sample(retro_audio_sample);
comm.funs.retro_set_audio_sample_batch(retro_audio_sample_batch);
comm.funs.retro_set_input_poll(retro_input_poll);
comm.funs.retro_set_input_state(retro_input_state);
//Between calls to retro_load_game() and retro_unload_game(), the returned size is never allowed to be larger than a previous returned
//value, to ensure that the frontend can allocate a save state buffer once.
comm.env.retro_serialize_size = comm.funs.retro_serialize_size();
//not sure when this can be called, but it's surely safe here
comm.env.retro_region = comm.funs.retro_get_region();
}
void cmd_LoadNoGame() { LoadHandler(eMessage::CMD_LoadNoGame); }
void cmd_LoadData() { LoadHandler(eMessage::CMD_LoadData); }
void cmd_LoadPath() { LoadHandler(eMessage::CMD_LoadPath); }
void cmd_Deinit()
{
//not sure if we need this
comm.funs.retro_unload_game();
comm.funs.retro_deinit();
//TODO: tidy
}
void cmd_Reset()
{
comm.funs.retro_reset();
}
void cmd_Run()
{
comm.funs.retro_run();
}
void cmd_Serialize()
{
comm.value = !!comm.funs.retro_serialize(comm.buf[BufId::Param0], comm.buf_size[BufId::Param0]);
}
void cmd_Unserialize()
{
comm.value = !!comm.funs.retro_unserialize(comm.buf[BufId::Param0], comm.buf_size[BufId::Param0]);
}
//TODO
//void(*retro_set_controller_port_device)(unsigned, unsigned);
//void *(*retro_get_memory_data)(unsigned);
//size_t(*retro_get_memory_size)(unsigned);
//TODO low priority
//void(*retro_cheat_reset)(void);
//void(*retro_cheat_set)(unsigned, bool, const char*);
//bool(*retro_load_game_special)(unsigned,
//TODO maybe not sensible though
//void(*retro_unload_game)(void);
void cmd_SetEnvironment()
{
//stuff that can't be done until our environment is setup (the core will immediately query the environment)
comm.funs.retro_set_environment(retro_environment);
}
const Action kHandlers_CMD[] = {
cmd_SetEnvironment,
cmd_LoadNoGame,
cmd_LoadData,
cmd_LoadPath,
cmd_Deinit,
cmd_Reset,
cmd_Run,
cmd_Serialize,
cmd_Unserialize,
};
const Action kHandlers_QUERY[] = {
nullptr
};
//------------------------------------------------
//DLL INTERFACE
BOOL WINAPI DllMain(_In_ HINSTANCE hinstDLL, _In_ DWORD fdwReason, _In_ LPVOID lpvReserved)
{
return TRUE;
}
extern "C" __declspec(dllexport) void* __cdecl DllInit(HMODULE dllModule)
{
memset(&comm,0,sizeof(comm));
//make a coroutine thread to run the emulation in. we'll switch back to this cothread when communicating with the frontend
co_control = co_active();
co_emu = co_create(128*1024 * sizeof(void*), new_emuthread);
//grab all the function pointers we need.
comm.dllModule = dllModule;
comm.LoadSymbols();
//libretro startup steps
//"Can be called at any time, even before retro_init()."
comm.funs.retro_get_system_info(&comm.env.retro_system_info);
comm.env.retro_api_version = (u32)comm.funs.retro_api_version();
//now after this we return to the c# side to let some more setup happen
return &comm;
}
extern "C" __declspec(dllexport) void __cdecl Message(eMessage msg)
{
if (msg == eMessage::Resume)
{
cothread_t temp = co_emu_suspended;
co_emu_suspended = NULL;
co_switch(temp);
}
if (msg >= eMessage::CMD_FIRST && msg <= eMessage::CMD_LAST)
{
//CMD is only valid if status is idle
if (comm.status != eStatus_Idle)
{
printf("ERROR: cmd during non-idle\n");
return;
}
comm.status = eStatus_CMD;
comm.cmd = msg;
CMD_cb = kHandlers_CMD[msg - eMessage::CMD_FIRST - 1];
co_switch(co_emu);
//we could be in ANY STATE when we return from here
}
//QUERY can run any time
//but... some of them might not be safe for re-entrancy.
//later, we should have metadata for messages that indicates that
if (msg >= eMessage::QUERY_FIRST && msg <= eMessage::QUERY_LAST)
{
Action cb = kHandlers_QUERY[msg - eMessage::QUERY_FIRST - 1];
if (cb) cb();
}
}
//receives the given buffer and COPIES it. use this for returning values from SIGs
extern "C" __declspec(dllexport) void __cdecl CopyBuffer(int id, void* ptr, s32 size)
{
comm.CopyBuffer(id, ptr, size);
}
//receives the given buffer and STASHES IT. use this (carefully) for sending params for CMDs
extern "C" __declspec(dllexport) void __cdecl SetBuffer(int id, void* ptr, s32 size)
{
comm.SetBuffer(id, ptr, size);
}
extern "C" __declspec(dllexport) void __cdecl SetVariable(const char* key, const char* val)
{
for(int i=0;i<comm.env.variable_count;i++)
if(!strcmp(key,comm.env.variable_keys[i]))
{
comm.variables[i] = val;
comm.variables_dirty = true;
}
}

View File

@ -0,0 +1,22 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LibretroBridge", "LibretroBridge.vcxproj", "{AEACAA89-FDA2-40C6-910C-85AEB9726452}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x86 = Debug|x86
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{AEACAA89-FDA2-40C6-910C-85AEB9726452}.Debug|x86.ActiveCfg = Debug|Win32
{AEACAA89-FDA2-40C6-910C-85AEB9726452}.Debug|x86.Build.0 = Debug|Win32
{AEACAA89-FDA2-40C6-910C-85AEB9726452}.Release|x86.ActiveCfg = Release|Win32
{AEACAA89-FDA2-40C6-910C-85AEB9726452}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,187 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClCompile Include="features_cpu.c" />
<ClCompile Include="libco\sjlj-multi.c" />
<ClCompile Include="LibretroBridge.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="libco\libco.h" />
<ClInclude Include="libretro.h" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{AEACAA89-FDA2-40C6-910C-85AEB9726452}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>LibretroBridge</RootNamespace>
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140_xp</PlatformToolset>
<CharacterSet>NotSet</CharacterSet>
<WholeProgramOptimization>false</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140_xp</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140_xp</PlatformToolset>
<CharacterSet>NotSet</CharacterSet>
<WholeProgramOptimization>false</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140_xp</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>..\..\output\dll\</OutDir>
<IntDir>.obj\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>..\..\output\dll\</OutDir>
<IntDir>.obj\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>..\..\output\dll\</OutDir>
<IntDir>.obj\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>..\..\output\dll\</OutDir>
<IntDir>.obj\$(Configuration)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBRETRO_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
<WholeProgramOptimization>false</WholeProgramOptimization>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_WINDOWS;_USRDLL;LIBRETRO_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
<WholeProgramOptimization>false</WholeProgramOptimization>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>false</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBRETRO_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>false</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_WINDOWS;_USRDLL;LIBRETRO_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
<ProjectExtensions>
<VisualStudio>
<UserProperties />
</VisualStudio>
</ProjectExtensions>
</Project>

View File

@ -0,0 +1,168 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
* Copyright (C) 2011-2016 - Daniel De Matteis
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __DYNAMIC_H
#define __DYNAMIC_H
#include <boolean.h>
#include <retro_common_api.h>
#include <libretro.h>
#include "core_type.h"
RETRO_BEGIN_DECLS
/**
* libretro_get_environment_info:
* @func : Function pointer for get_environment_info.
* @load_no_content : If true, core should be able to auto-start
* without any content loaded.
*
* Sets environment callback in order to get statically known
* information from it.
*
* Fetched via environment callbacks instead of
* retro_get_system_info(), as this info is part of extensions.
*
* Should only be called once right after core load to
* avoid overwriting the "real" environ callback.
*
* For statically linked cores, pass retro_set_environment as argument.
*/
void libretro_get_environment_info(void(*)(retro_environment_t),
bool *load_no_content);
/**
* libretro_get_system_info:
* @path : Path to libretro library.
* @info : System info information.
* @load_no_content : If true, core should be able to auto-start
* without any content loaded.
*
* Gets system info from an arbitrary lib.
* The struct returned must be freed as strings are allocated dynamically.
*
* Returns: true (1) if successful, otherwise false (0).
**/
bool libretro_get_system_info(const char *path,
struct retro_system_info *info, bool *load_no_content);
/**
* libretro_free_system_info:
* @info : Pointer to system info information.
*
* Frees system information.
**/
void libretro_free_system_info(struct retro_system_info *info);
/**
* libretro_get_current_core_pathname:
* @name : Sanitized name of libretro core.
* @size : Size of @name
*
* Transforms a library id to a name suitable as a pathname.
**/
void libretro_get_current_core_pathname(char *name, size_t size);
const struct retro_subsystem_info *libretro_find_subsystem_info(
const struct retro_subsystem_info *info,
unsigned num_info, const char *ident);
/**
* libretro_find_controller_description:
* @info : Pointer to controller info handle.
* @id : Identifier of controller to search
* for.
*
* Search for a controller of type @id in @info.
*
* Returns: controller description of found controller on success,
* otherwise NULL.
**/
const struct retro_controller_description *
libretro_find_controller_description(
const struct retro_controller_info *info, unsigned id);
/**
* rarch_environment_cb:
* @cmd : Identifier of command.
* @data : Pointer to data.
*
* Environment callback function implementation.
*
* Returns: true (1) if environment callback command could
* be performed, otherwise false (0).
**/
bool rarch_environment_cb(unsigned cmd, void *data);
struct retro_core_t
{
void(*retro_init)(void);
void(*retro_deinit)(void);
unsigned(*retro_api_version)(void);
void(*retro_get_system_info)(struct retro_system_info*);
void(*retro_get_system_av_info)(struct retro_system_av_info*);
void(*retro_set_environment)(retro_environment_t);
void(*retro_set_video_refresh)(retro_video_refresh_t);
void(*retro_set_audio_sample)(retro_audio_sample_t);
void(*retro_set_audio_sample_batch)(retro_audio_sample_batch_t);
void(*retro_set_input_poll)(retro_input_poll_t);
void(*retro_set_input_state)(retro_input_state_t);
void(*retro_set_controller_port_device)(unsigned, unsigned);
void(*retro_reset)(void);
void(*retro_run)(void);
size_t(*retro_serialize_size)(void);
bool(*retro_serialize)(void*, size_t);
bool(*retro_unserialize)(const void*, size_t);
void(*retro_cheat_reset)(void);
void(*retro_cheat_set)(unsigned, bool, const char*);
bool(*retro_load_game)(const struct retro_game_info*);
bool(*retro_load_game_special)(unsigned,
const struct retro_game_info*, size_t);
void(*retro_unload_game)(void);
unsigned(*retro_get_region)(void);
void *(*retro_get_memory_data)(unsigned);
size_t(*retro_get_memory_size)(unsigned);
};
/**
* init_libretro_sym:
* @type : Type of core to be loaded.
* If CORE_TYPE_DUMMY, will
* load dummy symbols.
*
* Initializes libretro symbols and
* setups environment callback functions. Returns true on success,
* or false if symbols could not be loaded.
**/
bool init_libretro_sym(enum rarch_core_type type,
struct retro_core_t *core);
/**
* uninit_libretro_sym:
*
* Frees libretro core.
*
* Frees all core options,
* associated state, and
* unbind all libretro callback symbols.
**/
void uninit_libretro_sym(struct retro_core_t *core);
RETRO_END_DECLS
#endif

View File

@ -0,0 +1,776 @@
/* Copyright (C) 2010-2017 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (features_cpu.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
//modified for bizhawk's libretro shim
#define __i686__ 1
#include <stdio.h>
#include <stdlib.h>
#if defined(_WIN32)
#include <direct.h>
#else
#include <unistd.h>
#endif
//#include <compat/strl.h>
//#include <streams/file_stream.h>
#include "libretro.h"
//#include <features/features_cpu.h>
#if defined(_WIN32) && !defined(_XBOX)
#include <windows.h>
#endif
#if defined(__CELLOS_LV2__)
#ifndef _PPU_INTRINSICS_H
#include <ppu_intrinsics.h>
#endif
#elif defined(_XBOX360)
#include <PPCIntrinsics.h>
#elif defined(_POSIX_MONOTONIC_CLOCK) || defined(ANDROID) || defined(__QNX__) || defined(DJGPP)
/* POSIX_MONOTONIC_CLOCK is not being defined in Android headers despite support being present. */
#include <time.h>
#endif
#if defined(__QNX__) && !defined(CLOCK_MONOTONIC)
#define CLOCK_MONOTONIC 2
#endif
#if defined(PSP)
#include <sys/time.h>
#include <psprtc.h>
#endif
#if defined(VITA)
#include <psp2/kernel/processmgr.h>
#include <psp2/rtc.h>
#endif
#if defined(__PSL1GHT__)
#include <sys/time.h>
#elif defined(__CELLOS_LV2__)
#include <sys/sys_time.h>
#endif
#ifdef GEKKO
#include <ogc/lwp_watchdog.h>
#endif
#ifdef WIIU
#include <wiiu/os/time.h>
#endif
#if defined(_3DS)
#include <3ds/svc.h>
#include <3ds/os.h>
#endif
/* iOS/OSX specific. Lacks clock_gettime(), so implement it. */
#ifdef __MACH__
#include <sys/time.h>
#ifndef CLOCK_MONOTONIC
#define CLOCK_MONOTONIC 0
#endif
#ifndef CLOCK_REALTIME
#define CLOCK_REALTIME 0
#endif
/* this function is part of iOS 10 now */
static int ra_clock_gettime(int clk_ik, struct timespec *t)
{
struct timeval now;
int rv = gettimeofday(&now, NULL);
if (rv)
return rv;
t->tv_sec = now.tv_sec;
t->tv_nsec = now.tv_usec * 1000;
return 0;
}
#endif
#if defined(__MACH__) && __IPHONE_OS_VERSION_MIN_REQUIRED < 100000
#else
#define ra_clock_gettime clock_gettime
#endif
#ifdef EMSCRIPTEN
#include <emscripten.h>
#endif
#if defined(BSD) || defined(__APPLE__)
#include <sys/sysctl.h>
#endif
#include <string.h>
/**
* cpu_features_get_perf_counter:
*
* Gets performance counter.
*
* Returns: performance counter.
**/
retro_perf_tick_t cpu_features_get_perf_counter(void)
{
retro_perf_tick_t time_ticks = 0;
#if defined(_WIN32)
long tv_sec, tv_usec;
static const unsigned __int64 epoch = 11644473600000000ULL;
FILETIME file_time;
SYSTEMTIME system_time;
ULARGE_INTEGER ularge;
GetSystemTime(&system_time);
SystemTimeToFileTime(&system_time, &file_time);
ularge.LowPart = file_time.dwLowDateTime;
ularge.HighPart = file_time.dwHighDateTime;
tv_sec = (long)((ularge.QuadPart - epoch) / 10000000L);
tv_usec = (long)(system_time.wMilliseconds * 1000);
time_ticks = (1000000 * tv_sec + tv_usec);
#elif defined(__linux__) || defined(__QNX__) || defined(__MACH__)
struct timespec tv = {0};
if (ra_clock_gettime(CLOCK_MONOTONIC, &tv) == 0)
time_ticks = (retro_perf_tick_t)tv.tv_sec * 1000000000 +
(retro_perf_tick_t)tv.tv_nsec;
#elif defined(__GNUC__) && defined(__i386__) || defined(__i486__) || defined(__i686__)
__asm__ volatile ("rdtsc" : "=A" (time_ticks));
#elif defined(__GNUC__) && defined(__x86_64__)
unsigned a, d;
__asm__ volatile ("rdtsc" : "=a" (a), "=d" (d));
time_ticks = (retro_perf_tick_t)a | ((retro_perf_tick_t)d << 32);
#elif defined(__ARM_ARCH_6__)
__asm__ volatile( "mrc p15, 0, %0, c9, c13, 0" : "=r"(time_ticks) );
#elif defined(__CELLOS_LV2__) || defined(_XBOX360) || defined(__powerpc__) || defined(__ppc__) || defined(__POWERPC__)
time_ticks = __mftb();
#elif defined(GEKKO)
time_ticks = gettime();
#elif defined(PSP)
sceRtcGetCurrentTick((uint64_t*)&time_ticks);
#elif defined(VITA)
sceRtcGetCurrentTick((SceRtcTick*)&time_ticks);
#elif defined(_3DS)
time_ticks = svcGetSystemTick();
#elif defined(WIIU)
time_ticks = OSGetSystemTime();
#elif defined(__mips__)
struct timeval tv;
gettimeofday(&tv,NULL);
time_ticks = (1000000 * tv.tv_sec + tv.tv_usec);
#endif
return time_ticks;
}
/**
* cpu_features_get_time_usec:
*
* Gets time in microseconds.
*
* Returns: time in microseconds.
**/
retro_time_t cpu_features_get_time_usec(void)
{
#if defined(_WIN32)
static LARGE_INTEGER freq;
LARGE_INTEGER count;
/* Frequency is guaranteed to not change. */
if (!freq.QuadPart && !QueryPerformanceFrequency(&freq))
return 0;
if (!QueryPerformanceCounter(&count))
return 0;
return count.QuadPart * 1000000 / freq.QuadPart;
#elif defined(__CELLOS_LV2__)
return sys_time_get_system_time();
#elif defined(GEKKO)
return ticks_to_microsecs(gettime());
#elif defined(_POSIX_MONOTONIC_CLOCK) || defined(__QNX__) || defined(ANDROID) || defined(__MACH__)
struct timespec tv = {0};
if (ra_clock_gettime(CLOCK_MONOTONIC, &tv) < 0)
return 0;
return tv.tv_sec * INT64_C(1000000) + (tv.tv_nsec + 500) / 1000;
#elif defined(EMSCRIPTEN)
return emscripten_get_now() * 1000;
#elif defined(__mips__) || defined(DJGPP)
struct timeval tv;
gettimeofday(&tv,NULL);
return (1000000 * tv.tv_sec + tv.tv_usec);
#elif defined(_3DS)
return osGetTime() * 1000;
#elif defined(VITA)
return sceKernelGetProcessTimeWide();
#elif defined(WIIU)
return ticks_to_us(OSGetSystemTime());
#else
#error "Your platform does not have a timer function implemented in cpu_features_get_time_usec(). Cannot continue."
#endif
}
#if defined(__x86_64__) || defined(__i386__) || defined(__i486__) || defined(__i686__)
#define CPU_X86
#endif
#if defined(_MSC_VER) && !defined(_XBOX)
#if (_MSC_VER > 1310)
#include <intrin.h>
#endif
#endif
#if defined(CPU_X86) && !defined(__MACH__)
void x86_cpuid(int func, int flags[4])
{
/* On Android, we compile RetroArch with PIC, and we
* are not allowed to clobber the ebx register. */
#ifdef __x86_64__
#define REG_b "rbx"
#define REG_S "rsi"
#else
#define REG_b "ebx"
#define REG_S "esi"
#endif
#if defined(__GNUC__)
__asm__ volatile (
"mov %%" REG_b ", %%" REG_S "\n"
"cpuid\n"
"xchg %%" REG_b ", %%" REG_S "\n"
: "=a"(flags[0]), "=S"(flags[1]), "=c"(flags[2]), "=d"(flags[3])
: "a"(func));
#elif defined(_MSC_VER)
__cpuid(flags, func);
#else
printf("Unknown compiler. Cannot check CPUID with inline assembly.\n");
memset(flags, 0, 4 * sizeof(int));
#endif
}
/* Only runs on i686 and above. Needs to be conditionally run. */
static uint64_t xgetbv_x86(uint32_t idx)
{
#if defined(__GNUC__)
uint32_t eax, edx;
__asm__ volatile (
/* Older GCC versions (Apple's GCC for example) do
* not understand xgetbv instruction.
* Stamp out the machine code directly.
*/
".byte 0x0f, 0x01, 0xd0\n"
: "=a"(eax), "=d"(edx) : "c"(idx));
return ((uint64_t)edx << 32) | eax;
#elif _MSC_FULL_VER >= 160040219
/* Intrinsic only works on 2010 SP1 and above. */
return _xgetbv(idx);
#else
printf("Unknown compiler. Cannot check xgetbv bits.\n");
return 0;
#endif
}
#endif
#if defined(__ARM_NEON__)
static void arm_enable_runfast_mode(void)
{
/* RunFast mode. Enables flush-to-zero and some
* floating point optimizations. */
static const unsigned x = 0x04086060;
static const unsigned y = 0x03000000;
int r;
__asm__ volatile(
"fmrx %0, fpscr \n\t" /* r0 = FPSCR */
"and %0, %0, %1 \n\t" /* r0 = r0 & 0x04086060 */
"orr %0, %0, %2 \n\t" /* r0 = r0 | 0x03000000 */
"fmxr fpscr, %0 \n\t" /* FPSCR = r0 */
: "=r"(r)
: "r"(x), "r"(y)
);
}
#endif
#if defined(__linux__) && !defined(CPU_X86)
static unsigned char check_arm_cpu_feature(const char* feature)
{
char line[1024];
unsigned char status = 0;
RFILE *fp = filestream_open("/proc/cpuinfo", RFILE_MODE_READ_TEXT, -1);
if (!fp)
return 0;
while (filestream_gets(fp, line, sizeof(line)) != NULL)
{
if (strncmp(line, "Features\t: ", 11))
continue;
if (strstr(line + 11, feature) != NULL)
status = 1;
break;
}
filestream_close(fp);
return status;
}
#if !defined(_SC_NPROCESSORS_ONLN)
/* Parse an decimal integer starting from 'input', but not going further
* than 'limit'. Return the value into '*result'.
*
* NOTE: Does not skip over leading spaces, or deal with sign characters.
* NOTE: Ignores overflows.
*
* The function returns NULL in case of error (bad format), or the new
* position after the decimal number in case of success (which will always
* be <= 'limit').
*/
static const char *parse_decimal(const char* input,
const char* limit, int* result)
{
const char* p = input;
int val = 0;
while (p < limit)
{
int d = (*p - '0');
if ((unsigned)d >= 10U)
break;
val = val*10 + d;
p++;
}
if (p == input)
return NULL;
*result = val;
return p;
}
/* Parse a textual list of cpus and store the result inside a CpuList object.
* Input format is the following:
* - comma-separated list of items (no spaces)
* - each item is either a single decimal number (cpu index), or a range made
* of two numbers separated by a single dash (-). Ranges are inclusive.
*
* Examples: 0
* 2,4-127,128-143
* 0-1
*/
static void cpulist_parse(CpuList* list, char **buf, ssize_t length)
{
const char* p = (const char*)buf;
const char* end = p + length;
/* NOTE: the input line coming from sysfs typically contains a
* trailing newline, so take care of it in the code below
*/
while (p < end && *p != '\n')
{
int val, start_value, end_value;
/* Find the end of current item, and put it into 'q' */
const char *q = (const char*)memchr(p, ',', end-p);
if (!q)
q = end;
/* Get first value */
p = parse_decimal(p, q, &start_value);
if (p == NULL)
return;
end_value = start_value;
/* If we're not at the end of the item, expect a dash and
* and integer; extract end value.
*/
if (p < q && *p == '-')
{
p = parse_decimal(p+1, q, &end_value);
if (p == NULL)
return;
}
/* Set bits CPU list bits */
for (val = start_value; val <= end_value; val++)
{
if ((unsigned)val < 32)
list->mask |= (uint32_t)(1U << val);
}
/* Jump to next item */
p = q;
if (p < end)
p++;
}
}
/* Read a CPU list from one sysfs file */
static void cpulist_read_from(CpuList* list, const char* filename)
{
ssize_t length;
char *buf = NULL;
list->mask = 0;
if (filestream_read_file(filename, (void**)&buf, &length) != 1)
return;
cpulist_parse(list, &buf, length);
if (buf)
free(buf);
buf = NULL;
}
#endif
#endif
/**
* cpu_features_get_core_amount:
*
* Gets the amount of available CPU cores.
*
* Returns: amount of CPU cores available.
**/
unsigned cpu_features_get_core_amount(void)
{
#if defined(_WIN32) && !defined(_XBOX)
/* Win32 */
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
return sysinfo.dwNumberOfProcessors;
#elif defined(GEKKO)
return 1;
#elif defined(PSP)
return 1;
#elif defined(VITA)
return 4;
#elif defined(_3DS)
return 1;
#elif defined(WIIU)
return 3;
#elif defined(_SC_NPROCESSORS_ONLN)
/* Linux, most UNIX-likes. */
long ret = sysconf(_SC_NPROCESSORS_ONLN);
if (ret <= 0)
return (unsigned)1;
return (unsigned)ret;
#elif defined(BSD) || defined(__APPLE__)
/* BSD */
/* Copypasta from stackoverflow, dunno if it works. */
int num_cpu = 0;
int mib[4];
size_t len = sizeof(num_cpu);
mib[0] = CTL_HW;
mib[1] = HW_AVAILCPU;
sysctl(mib, 2, &num_cpu, &len, NULL, 0);
if (num_cpu < 1)
{
mib[1] = HW_NCPU;
sysctl(mib, 2, &num_cpu, &len, NULL, 0);
if (num_cpu < 1)
num_cpu = 1;
}
return num_cpu;
#elif defined(__linux__)
CpuList cpus_present[1];
CpuList cpus_possible[1];
int amount = 0;
cpulist_read_from(cpus_present, "/sys/devices/system/cpu/present");
cpulist_read_from(cpus_possible, "/sys/devices/system/cpu/possible");
/* Compute the intersection of both sets to get the actual number of
* CPU cores that can be used on this device by the kernel.
*/
cpus_present->mask &= cpus_possible->mask;
amount = __builtin_popcount(cpus_present->mask);
if (amount == 0)
return 1;
return amount;
#elif defined(_XBOX360)
return 3;
#else
/* No idea, assume single core. */
return 1;
#endif
}
/* According to http://en.wikipedia.org/wiki/CPUID */
#define VENDOR_INTEL_b 0x756e6547
#define VENDOR_INTEL_c 0x6c65746e
#define VENDOR_INTEL_d 0x49656e69
/**
* cpu_features_get:
*
* Gets CPU features..
*
* Returns: bitmask of all CPU features available.
**/
uint64_t cpu_features_get(void)
{
int flags[4];
int vendor_shuffle[3];
char vendor[13];
size_t len = 0;
uint64_t cpu_flags = 0;
uint64_t cpu = 0;
unsigned max_flag = 0;
#if defined(CPU_X86) && !defined(__MACH__)
int vendor_is_intel = 0;
const int avx_flags = (1 << 27) | (1 << 28);
#endif
char buf[sizeof(" MMX MMXEXT SSE SSE2 SSE3 SSSE3 SS4 SSE4.2 AES AVX AVX2 NEON VMX VMX128 VFPU PS")];
memset(buf, 0, sizeof(buf));
(void)len;
(void)cpu_flags;
(void)flags;
(void)max_flag;
(void)vendor;
(void)vendor_shuffle;
#if defined(__MACH__)
len = sizeof(size_t);
if (sysctlbyname("hw.optional.mmx", NULL, &len, NULL, 0) == 0)
{
cpu |= RETRO_SIMD_MMX;
cpu |= RETRO_SIMD_MMXEXT;
}
len = sizeof(size_t);
if (sysctlbyname("hw.optional.floatingpoint", NULL, &len, NULL, 0) == 0)
{
cpu |= RETRO_SIMD_CMOV;
}
len = sizeof(size_t);
if (sysctlbyname("hw.optional.sse", NULL, &len, NULL, 0) == 0)
cpu |= RETRO_SIMD_SSE;
len = sizeof(size_t);
if (sysctlbyname("hw.optional.sse2", NULL, &len, NULL, 0) == 0)
cpu |= RETRO_SIMD_SSE2;
len = sizeof(size_t);
if (sysctlbyname("hw.optional.sse3", NULL, &len, NULL, 0) == 0)
cpu |= RETRO_SIMD_SSE3;
len = sizeof(size_t);
if (sysctlbyname("hw.optional.supplementalsse3", NULL, &len, NULL, 0) == 0)
cpu |= RETRO_SIMD_SSSE3;
len = sizeof(size_t);
if (sysctlbyname("hw.optional.sse4_1", NULL, &len, NULL, 0) == 0)
cpu |= RETRO_SIMD_SSE4;
len = sizeof(size_t);
if (sysctlbyname("hw.optional.sse4_2", NULL, &len, NULL, 0) == 0)
cpu |= RETRO_SIMD_SSE42;
len = sizeof(size_t);
if (sysctlbyname("hw.optional.aes", NULL, &len, NULL, 0) == 0)
cpu |= RETRO_SIMD_AES;
len = sizeof(size_t);
if (sysctlbyname("hw.optional.avx1_0", NULL, &len, NULL, 0) == 0)
cpu |= RETRO_SIMD_AVX;
len = sizeof(size_t);
if (sysctlbyname("hw.optional.avx2_0", NULL, &len, NULL, 0) == 0)
cpu |= RETRO_SIMD_AVX2;
len = sizeof(size_t);
if (sysctlbyname("hw.optional.altivec", NULL, &len, NULL, 0) == 0)
cpu |= RETRO_SIMD_VMX;
len = sizeof(size_t);
if (sysctlbyname("hw.optional.neon", NULL, &len, NULL, 0) == 0)
cpu |= RETRO_SIMD_NEON;
#elif defined(CPU_X86)
(void)avx_flags;
x86_cpuid(0, flags);
vendor_shuffle[0] = flags[1];
vendor_shuffle[1] = flags[3];
vendor_shuffle[2] = flags[2];
vendor[0] = '\0';
memcpy(vendor, vendor_shuffle, sizeof(vendor_shuffle));
/* printf("[CPUID]: Vendor: %s\n", vendor); */
vendor_is_intel = (
flags[1] == VENDOR_INTEL_b &&
flags[2] == VENDOR_INTEL_c &&
flags[3] == VENDOR_INTEL_d);
max_flag = flags[0];
if (max_flag < 1) /* Does CPUID not support func = 1? (unlikely ...) */
return 0;
x86_cpuid(1, flags);
if (flags[3] & (1 << 15))
cpu |= RETRO_SIMD_CMOV;
if (flags[3] & (1 << 23))
cpu |= RETRO_SIMD_MMX;
if (flags[3] & (1 << 25))
{
/* SSE also implies MMXEXT (according to FFmpeg source). */
cpu |= RETRO_SIMD_SSE;
cpu |= RETRO_SIMD_MMXEXT;
}
if (flags[3] & (1 << 26))
cpu |= RETRO_SIMD_SSE2;
if (flags[2] & (1 << 0))
cpu |= RETRO_SIMD_SSE3;
if (flags[2] & (1 << 9))
cpu |= RETRO_SIMD_SSSE3;
if (flags[2] & (1 << 19))
cpu |= RETRO_SIMD_SSE4;
if (flags[2] & (1 << 20))
cpu |= RETRO_SIMD_SSE42;
if ((flags[2] & (1 << 23)))
cpu |= RETRO_SIMD_POPCNT;
if (vendor_is_intel && (flags[2] & (1 << 22)))
cpu |= RETRO_SIMD_MOVBE;
if (flags[2] & (1 << 25))
cpu |= RETRO_SIMD_AES;
/* Must only perform xgetbv check if we have
* AVX CPU support (guaranteed to have at least i686). */
if (((flags[2] & avx_flags) == avx_flags)
&& ((xgetbv_x86(0) & 0x6) == 0x6))
cpu |= RETRO_SIMD_AVX;
if (max_flag >= 7)
{
x86_cpuid(7, flags);
if (flags[1] & (1 << 5))
cpu |= RETRO_SIMD_AVX2;
}
x86_cpuid(0x80000000, flags);
max_flag = flags[0];
if (max_flag >= 0x80000001u)
{
x86_cpuid(0x80000001, flags);
if (flags[3] & (1 << 23))
cpu |= RETRO_SIMD_MMX;
if (flags[3] & (1 << 22))
cpu |= RETRO_SIMD_MMXEXT;
}
#elif defined(__linux__)
if (check_arm_cpu_feature("neon"))
{
cpu |= RETRO_SIMD_NEON;
#ifdef __ARM_NEON__
arm_enable_runfast_mode();
#endif
}
if (check_arm_cpu_feature("vfpv3"))
cpu |= RETRO_SIMD_VFPV3;
if (check_arm_cpu_feature("vfpv4"))
cpu |= RETRO_SIMD_VFPV4;
if (check_arm_cpu_feature("asimd"))
{
cpu |= RETRO_SIMD_ASIMD;
#ifdef __ARM_NEON__
cpu |= RETRO_SIMD_NEON;
arm_enable_runfast_mode();
#endif
}
#if 0
check_arm_cpu_feature("swp");
check_arm_cpu_feature("half");
check_arm_cpu_feature("thumb");
check_arm_cpu_feature("fastmult");
check_arm_cpu_feature("vfp");
check_arm_cpu_feature("edsp");
check_arm_cpu_feature("thumbee");
check_arm_cpu_feature("tls");
check_arm_cpu_feature("idiva");
check_arm_cpu_feature("idivt");
#endif
#elif defined(__ARM_NEON__)
cpu |= RETRO_SIMD_NEON;
arm_enable_runfast_mode();
#elif defined(__ALTIVEC__)
cpu |= RETRO_SIMD_VMX;
#elif defined(XBOX360)
cpu |= RETRO_SIMD_VMX128;
#elif defined(PSP)
cpu |= RETRO_SIMD_VFPU;
#elif defined(GEKKO)
cpu |= RETRO_SIMD_PS;
#endif
//if (cpu & RETRO_SIMD_MMX) strlcat(buf, " MMX", sizeof(buf));
//if (cpu & RETRO_SIMD_MMXEXT) strlcat(buf, " MMXEXT", sizeof(buf));
//if (cpu & RETRO_SIMD_SSE) strlcat(buf, " SSE", sizeof(buf));
//if (cpu & RETRO_SIMD_SSE2) strlcat(buf, " SSE2", sizeof(buf));
//if (cpu & RETRO_SIMD_SSE3) strlcat(buf, " SSE3", sizeof(buf));
//if (cpu & RETRO_SIMD_SSSE3) strlcat(buf, " SSSE3", sizeof(buf));
//if (cpu & RETRO_SIMD_SSE4) strlcat(buf, " SSE4", sizeof(buf));
//if (cpu & RETRO_SIMD_SSE42) strlcat(buf, " SSE4.2", sizeof(buf));
//if (cpu & RETRO_SIMD_AES) strlcat(buf, " AES", sizeof(buf));
//if (cpu & RETRO_SIMD_AVX) strlcat(buf, " AVX", sizeof(buf));
//if (cpu & RETRO_SIMD_AVX2) strlcat(buf, " AVX2", sizeof(buf));
//if (cpu & RETRO_SIMD_NEON) strlcat(buf, " NEON", sizeof(buf));
//if (cpu & RETRO_SIMD_VFPV3) strlcat(buf, " VFPv3", sizeof(buf));
//if (cpu & RETRO_SIMD_VFPV4) strlcat(buf, " VFPv4", sizeof(buf));
//if (cpu & RETRO_SIMD_VMX) strlcat(buf, " VMX", sizeof(buf));
//if (cpu & RETRO_SIMD_VMX128) strlcat(buf, " VMX128", sizeof(buf));
//if (cpu & RETRO_SIMD_VFPU) strlcat(buf, " VFPU", sizeof(buf));
//if (cpu & RETRO_SIMD_PS) strlcat(buf, " PS", sizeof(buf));
//if (cpu & RETRO_SIMD_ASIMD) strlcat(buf, " ASIMD", sizeof(buf));
return cpu;
}

View File

@ -0,0 +1,104 @@
/*
libco.amd64 (2009-10-12)
author: byuu
license: public domain
*/
#define LIBCO_C
#include "libco.h"
#include <assert.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
static thread_local long long co_active_buffer[64];
static thread_local cothread_t co_active_handle = 0;
static void (*co_swap)(cothread_t, cothread_t) = 0;
#ifdef _WIN32
//ABI: Win64
static unsigned char co_swap_function[] = {
0x48, 0x89, 0x22, 0x48, 0x8B, 0x21, 0x58, 0x48, 0x89, 0x6A, 0x08, 0x48, 0x89, 0x72, 0x10, 0x48,
0x89, 0x7A, 0x18, 0x48, 0x89, 0x5A, 0x20, 0x4C, 0x89, 0x62, 0x28, 0x4C, 0x89, 0x6A, 0x30, 0x4C,
0x89, 0x72, 0x38, 0x4C, 0x89, 0x7A, 0x40, 0x48, 0x81, 0xC2, 0x80, 0x00, 0x00, 0x00, 0x48, 0x83,
0xE2, 0xF0, 0x0F, 0x29, 0x32, 0x0F, 0x29, 0x7A, 0x10, 0x44, 0x0F, 0x29, 0x42, 0x20, 0x44, 0x0F,
0x29, 0x4A, 0x30, 0x44, 0x0F, 0x29, 0x52, 0x40, 0x44, 0x0F, 0x29, 0x5A, 0x50, 0x44, 0x0F, 0x29,
0x62, 0x60, 0x44, 0x0F, 0x29, 0x6A, 0x70, 0x44, 0x0F, 0x29, 0xB2, 0x80, 0x00, 0x00, 0x00, 0x44,
0x0F, 0x29, 0xBA, 0x90, 0x00, 0x00, 0x00, 0x48, 0x8B, 0x69, 0x08, 0x48, 0x8B, 0x71, 0x10, 0x48,
0x8B, 0x79, 0x18, 0x48, 0x8B, 0x59, 0x20, 0x4C, 0x8B, 0x61, 0x28, 0x4C, 0x8B, 0x69, 0x30, 0x4C,
0x8B, 0x71, 0x38, 0x4C, 0x8B, 0x79, 0x40, 0x48, 0x81, 0xC1, 0x80, 0x00, 0x00, 0x00, 0x48, 0x83,
0xE1, 0xF0, 0x0F, 0x29, 0x31, 0x0F, 0x29, 0x79, 0x10, 0x44, 0x0F, 0x29, 0x41, 0x20, 0x44, 0x0F,
0x29, 0x49, 0x30, 0x44, 0x0F, 0x29, 0x51, 0x40, 0x44, 0x0F, 0x29, 0x59, 0x50, 0x44, 0x0F, 0x29,
0x61, 0x60, 0x44, 0x0F, 0x29, 0x69, 0x70, 0x44, 0x0F, 0x29, 0xB1, 0x80, 0x00, 0x00, 0x00, 0x44,
0x0F, 0x29, 0xB9, 0x90, 0x00, 0x00, 0x00, 0xFF, 0xE0,
};
#include <windows.h>
void co_init() {
DWORD old_privileges;
VirtualProtect(co_swap_function, sizeof co_swap_function, PAGE_EXECUTE_READWRITE, &old_privileges);
}
#else
//ABI: SystemV
static unsigned char co_swap_function[] = {
0x48, 0x89, 0x26, 0x48, 0x8B, 0x27, 0x58, 0x48, 0x89, 0x6E, 0x08, 0x48, 0x89, 0x5E, 0x10, 0x4C,
0x89, 0x66, 0x18, 0x4C, 0x89, 0x6E, 0x20, 0x4C, 0x89, 0x76, 0x28, 0x4C, 0x89, 0x7E, 0x30, 0x48,
0x8B, 0x6F, 0x08, 0x48, 0x8B, 0x5F, 0x10, 0x4C, 0x8B, 0x67, 0x18, 0x4C, 0x8B, 0x6F, 0x20, 0x4C,
0x8B, 0x77, 0x28, 0x4C, 0x8B, 0x7F, 0x30, 0xFF, 0xE0,
};
#include <unistd.h>
#include <sys/mman.h>
void co_init() {
unsigned long long addr = (unsigned long long)co_swap_function;
unsigned long long base = addr - (addr % sysconf(_SC_PAGESIZE));
unsigned long long size = (addr - base) + sizeof co_swap_function;
mprotect((void*)base, size, PROT_READ | PROT_WRITE | PROT_EXEC);
}
#endif
static void crash() {
assert(0); /* called only if cothread_t entrypoint returns */
}
cothread_t co_active() {
if(!co_active_handle) co_active_handle = &co_active_buffer;
return co_active_handle;
}
cothread_t co_create(unsigned int size, void (*entrypoint)(void)) {
cothread_t handle;
if(!co_swap) {
co_init();
co_swap = (void (*)(cothread_t, cothread_t))co_swap_function;
}
if(!co_active_handle) co_active_handle = &co_active_buffer;
size += 512; /* allocate additional space for storage */
size &= ~15; /* align stack to 16-byte boundary */
if(handle = (cothread_t)malloc(size)) {
long long *p = (long long*)((char*)handle + size); /* seek to top of stack */
*--p = (long long)crash; /* crash if entrypoint returns */
*--p = (long long)entrypoint; /* start of function */
*(long long*)handle = (long long)p; /* stack pointer */
}
return handle;
}
void co_delete(cothread_t handle) {
free(handle);
}
void co_switch(cothread_t handle) {
register cothread_t co_previous_handle = co_active_handle;
co_swap(co_active_handle = handle, co_previous_handle);
}
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,38 @@
/*
libco
version: 0.16 (2010-12-24)
license: public domain
*/
#ifndef LIBCO_H
#define LIBCO_H
#ifdef LIBCO_C
#ifdef LIBCO_MP
#define thread_local __thread
#else
#define thread_local
#endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef void* cothread_t;
typedef void (*coentry_t)(void);
void* co_getstack(cothread_t);
cothread_t co_active();
cothread_t co_create_withstack(void* stack, int stacksize, coentry_t);
cothread_t co_create(unsigned int, coentry_t);
void co_delete(cothread_t);
void co_switch(cothread_t);
cothread_t co_primary();
#ifdef __cplusplus
}
#endif
/* ifndef LIBCO_H */
#endif

View File

@ -0,0 +1,237 @@
/*
original author: Nach
license: public domain
additional work: zeromus
note: more ARM compilers are supported here (check the ifdefs in _JUMP_BUFFER)
and: work has been done to make this coexist more peaceably with .net
*/
#define LIBCO_C
#include "libco.h"
#include <stdlib.h>
#include <setjmp.h>
#include <string.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct
{
jmp_buf context;
coentry_t coentry;
void *stack;
unsigned long seh_frame, stack_top, stack_bottom;
cothread_t caller;
int ownstack;
} cothread_struct;
static thread_local cothread_struct _co_primary;
static thread_local cothread_struct *co_running = 0;
cothread_t co_primary() { return (cothread_t)&_co_primary; }
//-------------------
#ifdef _MSC_VER
//links of interest
//http://connect.microsoft.com/VisualStudio/feedback/details/100319/really-wierd-behaviour-in-crt-io-coupled-with-some-inline-assembly
//http://en.wikipedia.org/wiki/Thread_Information_Block
//http://social.msdn.microsoft.com/Forums/en-US/vclanguage/thread/72093e46-4524-4f54-9f36-c7e8a309d1db/ //FS warning
#define WINVER 0x0400
#define _WIN32_WINNT 0x0400
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#pragma warning(disable:4733)
#pragma warning(disable:4311)
static void capture_fs(cothread_struct* rec)
{
int temp;
__asm mov eax, dword ptr fs:[0];
__asm mov temp, eax;
rec->seh_frame = temp;
__asm mov eax, dword ptr fs:[4];
__asm mov temp, eax;
rec->stack_top = temp;
__asm mov eax, dword ptr fs:[8];
__asm mov temp, eax;
rec->stack_bottom = temp;
}
static void restore_fs(cothread_struct* rec)
{
int temp;
temp = rec->seh_frame;
__asm mov eax, temp;
__asm mov dword ptr fs:[0], eax
temp = rec->stack_top;
__asm mov eax, temp;
__asm mov dword ptr fs:[4], eax
temp = rec->stack_bottom;
__asm mov eax, temp;
__asm mov dword ptr fs:[8], eax
}
static void os_co_wrapper()
{
cothread_struct* rec = (cothread_struct*)co_active();
__try
{
rec->coentry();
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
//unhandled win32 exception in coroutine.
//this coroutine will now be suspended permanently and control will be yielded to caller, for lack of anything better to do.
//perhaps the process should just terminate.
for(;;)
{
//dead coroutine
co_switch(rec->caller);
}
}
}
static void os_co_create(cothread_struct* rec, unsigned int size, coentry_t coentry)
{
_JUMP_BUFFER* jb = (_JUMP_BUFFER*)&rec->context;
cothread_struct temp;
jb->Esp = (unsigned long)rec->stack + size - 4;
jb->Eip = (unsigned long)os_co_wrapper;
rec->stack_top = jb->Esp + 4;
rec->stack_bottom = (unsigned long)rec->stack;
//wild assumption about SEH frame.. seems to work
capture_fs(&temp);
rec->seh_frame = temp.seh_frame;
}
static void os_pre_setjmp(cothread_t target)
{
cothread_struct* rec = (cothread_struct*)target;
capture_fs(co_running);
rec->caller = co_running;
}
static void os_pre_longjmp(cothread_struct* rec)
{
restore_fs(rec);
}
#elif defined(__ARM_EABI__) || defined(__ARMCC_VERSION)
//http://sourceware.org/cgi-bin/cvsweb.cgi/src/newlib/libc/machine/arm/setjmp.S?rev=1.5&content-type=text/x-cvsweb-markup&cvsroot=src
typedef struct
{
#ifdef LIBCO_ARM_JUMBLED
int r8,r9,r10,r11,lr,r4,r5,r6,r7,sp;
#else
int r4,r5,r6,r7,r8,r9,r10,fp;
#ifndef LIBCO_ARM_NOIP
int ip;
#endif
int sp,lr;
#endif
} _JUMP_BUFFER;
static void os_co_create(cothread_struct* rec, unsigned int size, coentry_t coentry)
{
_JUMP_BUFFER* jb = (_JUMP_BUFFER*)&rec->context;
jb->sp = (unsigned long)rec->stack + size - 4;
jb->lr = (unsigned long)coentry;
}
static void os_pre_setjmp(cothread_t target)
{
cothread_struct* rec = (cothread_struct*)target;
rec->caller = co_running;
}
static void os_pre_longjmp(cothread_struct* rec)
{
}
#else
#error "sjlj-multi: unsupported processor, compiler or operating system"
#endif
//-------------------
cothread_t co_active()
{
if(!co_running) co_running = &_co_primary;
return (cothread_t)co_running;
}
void* co_getstack(cothread_t cothread)
{
return ((cothread_struct*)cothread)->stack;
}
cothread_t co_create(unsigned int stacksize, coentry_t coentry)
{
cothread_struct* ret = (cothread_struct*)co_create_withstack(malloc(stacksize), stacksize, coentry);
if(ret)
ret->ownstack = 1;
return (cothread_t)ret;
}
cothread_t co_create_withstack(void* stack, int stacksize, coentry_t coentry)
{
cothread_struct *thread;
if(!co_running) co_running = &_co_primary;
thread = (cothread_struct*)malloc(sizeof(cothread_struct));
if(thread)
{
thread->coentry = coentry;
thread->stack = stack;
{
setjmp(thread->context);
os_co_create(thread,stacksize,coentry);
}
thread->ownstack = 0;
}
return (cothread_t)thread;
}
void co_delete(cothread_t cothread)
{
if(cothread)
{
cothread_struct* thread = (cothread_struct*)cothread;
if (thread->ownstack)
free(thread->stack);
free(cothread);
}
}
void co_switch(cothread_t cothread)
{
os_pre_setjmp(cothread);
if(!setjmp(co_running->context))
{
co_running = (cothread_struct*)cothread;
os_pre_longjmp(co_running);
longjmp(co_running->context,0);
}
}
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,117 @@
/*
libco.x86 (2009-10-12)
author: byuu
license: public domain
*/
#define LIBCO_C
#include "libco.h"
#include <assert.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
#if defined(_MSC_VER)
#define fastcall __fastcall
#elif defined(__GNUC__)
#define fastcall __attribute__((fastcall))
#else
#error "libco: please define fastcall macro"
#endif
static thread_local long co_active_buffer[64];
static thread_local cothread_t co_active_handle = 0;
static void (fastcall *co_swap)(cothread_t, cothread_t) = 0;
//ABI: fastcall
static unsigned char co_swap_function[] = {
0x89, 0x22, /* mov [edx],esp */
0x8b, 0x21, /* mov esp,[ecx] */
0x58, /* pop eax */
0x89, 0x6a, 0x04, /* mov [edx+0x04],ebp */
0x89, 0x72, 0x08, /* mov [edx+0x08],esi */
0x89, 0x7a, 0x0c, /* mov [edx+0x0c],edi */
0x89, 0x5a, 0x10, /* mov [edx+0x10],ebx */
0x8b, 0x69, 0x04, /* mov ebp,[ecx+0x04] */
0x8b, 0x71, 0x08, /* mov esi,[ecx+0x08] */
0x8b, 0x79, 0x0c, /* mov edi,[ecx+0x0c] */
0x8b, 0x59, 0x10, /* mov ebx,[ecx+0x10] */
0xff, 0xe0, /* jmp eax */
};
#ifdef _WIN32
#include <windows.h>
void co_init(void)
{
DWORD old_privileges;
VirtualProtect(co_swap_function,
sizeof co_swap_function, PAGE_EXECUTE_READWRITE, &old_privileges);
}
#else
#include <unistd.h>
#include <sys/mman.h>
void co_init(void)
{
unsigned long addr = (unsigned long)co_swap_function;
unsigned long base = addr - (addr % sysconf(_SC_PAGESIZE));
unsigned long size = (addr - base) + sizeof co_swap_function;
mprotect((void*)base, size, PROT_READ | PROT_WRITE | PROT_EXEC);
}
#endif
static void crash(void)
{
assert(0); /* called only if cothread_t entrypoint returns */
}
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))
{
cothread_t handle;
if(!co_swap)
{
co_init();
co_swap = (void (fastcall*)(cothread_t, cothread_t))co_swap_function;
}
if(!co_active_handle)
co_active_handle = &co_active_buffer;
size += 256; /* allocate additional space for storage */
size &= ~15; /* align stack to 16-byte boundary */
if((handle = (cothread_t)malloc(size)))
{
long *p = (long*)((char*)handle + size); /* seek to top of stack */
*--p = (long)crash; /* crash if entrypoint returns */
*--p = (long)entrypoint; /* start of function */
*(long*)handle = (long)p; /* stack pointer */
}
return handle;
}
void co_delete(cothread_t handle)
{
free(handle);
}
void co_switch(cothread_t handle)
{
register cothread_t co_previous_handle = co_active_handle;
co_swap(co_active_handle = handle, co_previous_handle);
}
#ifdef __cplusplus
}
#endif

File diff suppressed because it is too large Load Diff