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:
parent
f2c48a165a
commit
ca90853a88
|
@ -0,0 +1,4 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = tab
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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>
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
Loading…
Reference in New Issue