Add linux version of LibretroBridge (#2895)
* Add linux version of LibretroBridge * correct make variables or whatever * Revert Hawk-side changes (to avoid merge conflict with master) Co-authored-by: YoshiRulz <OSSYoshiRulz@gmail.com>
This commit is contained in:
parent
14dd3c1695
commit
158451a68e
|
@ -0,0 +1,168 @@
|
|||
#define LIBCO_C
|
||||
#include "libco.h"
|
||||
#include "settings.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 LIBCO_MPROTECT
|
||||
alignas(4096)
|
||||
#else
|
||||
#ifdef _MSC_VER // honestly fuck that compiler
|
||||
#undef section
|
||||
#pragma section(".text")
|
||||
__declspec(allocate(".text"))
|
||||
#else
|
||||
section(text)
|
||||
#endif
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
/* ABI: Win64 */
|
||||
static const unsigned char co_swap_function[4096] = {
|
||||
0x48, 0x89, 0x22, /* mov [rdx],rsp */
|
||||
0x48, 0x8b, 0x21, /* mov rsp,[rcx] */
|
||||
0x58, /* pop rax */
|
||||
0x48, 0x89, 0x6a, 0x08, /* mov [rdx+ 8],rbp */
|
||||
0x48, 0x89, 0x72, 0x10, /* mov [rdx+16],rsi */
|
||||
0x48, 0x89, 0x7a, 0x18, /* mov [rdx+24],rdi */
|
||||
0x48, 0x89, 0x5a, 0x20, /* mov [rdx+32],rbx */
|
||||
0x4c, 0x89, 0x62, 0x28, /* mov [rdx+40],r12 */
|
||||
0x4c, 0x89, 0x6a, 0x30, /* mov [rdx+48],r13 */
|
||||
0x4c, 0x89, 0x72, 0x38, /* mov [rdx+56],r14 */
|
||||
0x4c, 0x89, 0x7a, 0x40, /* mov [rdx+64],r15 */
|
||||
#if !defined(LIBCO_NO_SSE)
|
||||
0x0f, 0x29, 0x72, 0x50, /* movaps [rdx+ 80],xmm6 */
|
||||
0x0f, 0x29, 0x7a, 0x60, /* movaps [rdx+ 96],xmm7 */
|
||||
0x44, 0x0f, 0x29, 0x42, 0x70, /* movaps [rdx+112],xmm8 */
|
||||
0x48, 0x83, 0xc2, 0x70, /* add rdx,112 */
|
||||
0x44, 0x0f, 0x29, 0x4a, 0x10, /* movaps [rdx+ 16],xmm9 */
|
||||
0x44, 0x0f, 0x29, 0x52, 0x20, /* movaps [rdx+ 32],xmm10 */
|
||||
0x44, 0x0f, 0x29, 0x5a, 0x30, /* movaps [rdx+ 48],xmm11 */
|
||||
0x44, 0x0f, 0x29, 0x62, 0x40, /* movaps [rdx+ 64],xmm12 */
|
||||
0x44, 0x0f, 0x29, 0x6a, 0x50, /* movaps [rdx+ 80],xmm13 */
|
||||
0x44, 0x0f, 0x29, 0x72, 0x60, /* movaps [rdx+ 96],xmm14 */
|
||||
0x44, 0x0f, 0x29, 0x7a, 0x70, /* movaps [rdx+112],xmm15 */
|
||||
#endif
|
||||
0x48, 0x8b, 0x69, 0x08, /* mov rbp,[rcx+ 8] */
|
||||
0x48, 0x8b, 0x71, 0x10, /* mov rsi,[rcx+16] */
|
||||
0x48, 0x8b, 0x79, 0x18, /* mov rdi,[rcx+24] */
|
||||
0x48, 0x8b, 0x59, 0x20, /* mov rbx,[rcx+32] */
|
||||
0x4c, 0x8b, 0x61, 0x28, /* mov r12,[rcx+40] */
|
||||
0x4c, 0x8b, 0x69, 0x30, /* mov r13,[rcx+48] */
|
||||
0x4c, 0x8b, 0x71, 0x38, /* mov r14,[rcx+56] */
|
||||
0x4c, 0x8b, 0x79, 0x40, /* mov r15,[rcx+64] */
|
||||
#if !defined(LIBCO_NO_SSE)
|
||||
0x0f, 0x28, 0x71, 0x50, /* movaps xmm6, [rcx+ 80] */
|
||||
0x0f, 0x28, 0x79, 0x60, /* movaps xmm7, [rcx+ 96] */
|
||||
0x44, 0x0f, 0x28, 0x41, 0x70, /* movaps xmm8, [rcx+112] */
|
||||
0x48, 0x83, 0xc1, 0x70, /* add rcx,112 */
|
||||
0x44, 0x0f, 0x28, 0x49, 0x10, /* movaps xmm9, [rcx+ 16] */
|
||||
0x44, 0x0f, 0x28, 0x51, 0x20, /* movaps xmm10,[rcx+ 32] */
|
||||
0x44, 0x0f, 0x28, 0x59, 0x30, /* movaps xmm11,[rcx+ 48] */
|
||||
0x44, 0x0f, 0x28, 0x61, 0x40, /* movaps xmm12,[rcx+ 64] */
|
||||
0x44, 0x0f, 0x28, 0x69, 0x50, /* movaps xmm13,[rcx+ 80] */
|
||||
0x44, 0x0f, 0x28, 0x71, 0x60, /* movaps xmm14,[rcx+ 96] */
|
||||
0x44, 0x0f, 0x28, 0x79, 0x70, /* movaps xmm15,[rcx+112] */
|
||||
#endif
|
||||
0xff, 0xe0, /* jmp rax */
|
||||
};
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
static void co_init() {
|
||||
#ifdef LIBCO_MPROTECT
|
||||
DWORD old_privileges;
|
||||
VirtualProtect((void*)co_swap_function, sizeof co_swap_function, PAGE_EXECUTE_READ, &old_privileges);
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
/* ABI: SystemV */
|
||||
static const unsigned char co_swap_function[4096] = {
|
||||
0x48, 0x89, 0x26, /* mov [rsi],rsp */
|
||||
0x48, 0x8b, 0x27, /* mov rsp,[rdi] */
|
||||
0x58, /* pop rax */
|
||||
0x48, 0x89, 0x6e, 0x08, /* mov [rsi+ 8],rbp */
|
||||
0x48, 0x89, 0x5e, 0x10, /* mov [rsi+16],rbx */
|
||||
0x4c, 0x89, 0x66, 0x18, /* mov [rsi+24],r12 */
|
||||
0x4c, 0x89, 0x6e, 0x20, /* mov [rsi+32],r13 */
|
||||
0x4c, 0x89, 0x76, 0x28, /* mov [rsi+40],r14 */
|
||||
0x4c, 0x89, 0x7e, 0x30, /* mov [rsi+48],r15 */
|
||||
0x48, 0x8b, 0x6f, 0x08, /* mov rbp,[rdi+ 8] */
|
||||
0x48, 0x8b, 0x5f, 0x10, /* mov rbx,[rdi+16] */
|
||||
0x4c, 0x8b, 0x67, 0x18, /* mov r12,[rdi+24] */
|
||||
0x4c, 0x8b, 0x6f, 0x20, /* mov r13,[rdi+32] */
|
||||
0x4c, 0x8b, 0x77, 0x28, /* mov r14,[rdi+40] */
|
||||
0x4c, 0x8b, 0x7f, 0x30, /* mov r15,[rdi+48] */
|
||||
0xff, 0xe0, /* jmp rax */
|
||||
};
|
||||
|
||||
#ifdef LIBCO_MPROTECT
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
static void co_init() {
|
||||
#ifdef LIBCO_MPROTECT
|
||||
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_EXEC);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
static void crash() {
|
||||
LIBCO_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_derive(void* memory, 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;
|
||||
|
||||
if(handle = (cothread_t)memory) {
|
||||
unsigned int offset = (size & ~15) - 32;
|
||||
long long *p = (long long*)((char*)handle + offset); /* 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;
|
||||
}
|
||||
|
||||
cothread_t co_create(unsigned int size, void (*entrypoint)(void)) {
|
||||
void* memory = LIBCO_MALLOC(size);
|
||||
if(!memory) return (cothread_t)0;
|
||||
return co_derive(memory, size, entrypoint);
|
||||
}
|
||||
|
||||
void co_delete(cothread_t handle) {
|
||||
LIBCO_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);
|
||||
}
|
||||
|
||||
int co_serializable() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
libco v20 (2019-10-16)
|
||||
author: byuu
|
||||
license: ISC
|
||||
*/
|
||||
|
||||
#ifndef LIBCO_H
|
||||
#define LIBCO_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void* cothread_t;
|
||||
|
||||
cothread_t co_active(void);
|
||||
cothread_t co_derive(void*, unsigned int, void (*)(void));
|
||||
cothread_t co_create(unsigned int, void (*)(void));
|
||||
void co_delete(cothread_t);
|
||||
void co_switch(cothread_t);
|
||||
int co_serializable(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ifndef LIBCO_H */
|
||||
#endif
|
|
@ -0,0 +1,128 @@
|
|||
#if defined(LIBCO_C)
|
||||
|
||||
/*[amd64, arm, ppc, x86]:
|
||||
by default, co_swap_function is marked as a text (code) section
|
||||
if not supported, uncomment the below line to use mprotect instead */
|
||||
/* #define LIBCO_MPROTECT */
|
||||
|
||||
/*[amd64]:
|
||||
Win64 only: provides a substantial speed-up, but will thrash XMM regs
|
||||
do not use this unless you are certain your application won't use SSE */
|
||||
/* #define LIBCO_NO_SSE */
|
||||
|
||||
#if !defined(thread_local) /* User can override thread_local for obscure compilers */
|
||||
#if !defined(LIBCO_MP) /* Running in single-threaded environment */
|
||||
#define thread_local
|
||||
#else /* Running in multi-threaded environment */
|
||||
#if defined(__STDC__) /* Compiling as C Language */
|
||||
#if defined(_MSC_VER) /* Don't rely on MSVC's C11 support */
|
||||
#define thread_local __declspec(thread)
|
||||
#elif __STDC_VERSION__ < 201112L /* If we are on C90/99 */
|
||||
#if defined(__clang__) || defined(__GNUC__) /* Clang and GCC */
|
||||
#define thread_local __thread
|
||||
#else /* Otherwise, we ignore the directive (unless user provides their own) */
|
||||
#define thread_local
|
||||
#endif
|
||||
#else /* C11 and newer define thread_local in threads.h */
|
||||
#include <threads.h>
|
||||
#endif
|
||||
#elif defined(__cplusplus) /* Compiling as C++ Language */
|
||||
#if __cplusplus < 201103L /* thread_local is a C++11 feature */
|
||||
#if defined(_MSC_VER)
|
||||
#define thread_local __declspec(thread)
|
||||
#elif defined(__clang__) || defined(__GNUC__)
|
||||
#define thread_local __thread
|
||||
#else /* Otherwise, we ignore the directive (unless user provides their own) */
|
||||
#define thread_local
|
||||
#endif
|
||||
#else /* In C++ >= 11, thread_local in a builtin keyword */
|
||||
/* Don't do anything */
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* In alignas(a), 'a' should be a power of two that is at least the type's
|
||||
alignment and at most the implementation's alignment limit. This limit is
|
||||
2**13 on MSVC. To be portable to MSVC through at least version 10.0,
|
||||
'a' should be an integer constant, as MSVC does not support expressions
|
||||
such as 1 << 3.
|
||||
|
||||
The following C11 requirements are NOT supported on MSVC:
|
||||
|
||||
- If 'a' is zero, alignas has no effect.
|
||||
- alignas can be used multiple times; the strictest one wins.
|
||||
- alignas (TYPE) is equivalent to alignas (alignof (TYPE)).
|
||||
*/
|
||||
#if !defined(alignas)
|
||||
#if defined(__STDC__) /* C Language */
|
||||
#if defined(_MSC_VER) /* Don't rely on MSVC's C11 support */
|
||||
#define alignas(bytes) __declspec(align(bytes))
|
||||
#elif __STDC_VERSION__ >= 201112L /* C11 and above */
|
||||
#include <stdalign.h>
|
||||
#elif defined(__clang__) || defined(__GNUC__) /* C90/99 on Clang/GCC */
|
||||
#define alignas(bytes) __attribute__ ((aligned (bytes)))
|
||||
#else /* Otherwise, we ignore the directive (user should provide their own) */
|
||||
#define alignas(bytes)
|
||||
#endif
|
||||
#elif defined(__cplusplus) /* C++ Language */
|
||||
#if __cplusplus < 201103L
|
||||
#if defined(_MSC_VER)
|
||||
#define alignas(bytes) __declspec(align(bytes))
|
||||
#elif defined(__clang__) || defined(__GNUC__) /* C++98/03 on Clang/GCC */
|
||||
#define alignas(bytes) __attribute__ ((aligned (bytes)))
|
||||
#else /* Otherwise, we ignore the directive (unless user provides their own) */
|
||||
#define alignas(bytes)
|
||||
#endif
|
||||
#else /* C++ >= 11 has alignas keyword */
|
||||
/* Do nothing */
|
||||
#endif
|
||||
#endif /* = !defined(__STDC_VERSION__) && !defined(__cplusplus) */
|
||||
#endif
|
||||
|
||||
#if !defined(LIBCO_ASSERT)
|
||||
#include <assert.h>
|
||||
#define LIBCO_ASSERT assert
|
||||
#endif
|
||||
|
||||
#if defined (__OpenBSD__)
|
||||
#if !defined(LIBCO_MALLOC) || !defined(LIBCO_FREE)
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
static void* malloc_obsd(size_t size) {
|
||||
long pagesize = sysconf(_SC_PAGESIZE);
|
||||
char* memory = (char*)mmap(NULL, size + pagesize, PROT_READ|PROT_WRITE, MAP_STACK|MAP_PRIVATE|MAP_ANON, -1, 0);
|
||||
if (memory == MAP_FAILED) return NULL;
|
||||
*(size_t*)memory = size + pagesize;
|
||||
memory += pagesize;
|
||||
return (void*)memory;
|
||||
}
|
||||
|
||||
static void free_obsd(void *ptr) {
|
||||
char* memory = (char*)ptr - sysconf(_SC_PAGESIZE);
|
||||
munmap(memory, *(size_t*)memory);
|
||||
}
|
||||
|
||||
#define LIBCO_MALLOC malloc_obsd
|
||||
#define LIBCO_FREE free_obsd
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(LIBCO_MALLOC) || !defined(LIBCO_FREE)
|
||||
#include <stdlib.h>
|
||||
#define LIBCO_MALLOC malloc
|
||||
#define LIBCO_FREE free
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define section(name) __declspec(allocate("." #name))
|
||||
#elif defined(__APPLE__)
|
||||
#define section(name) __attribute__((section("__TEXT,__" #name)))
|
||||
#else
|
||||
#define section(name) __attribute__((section("." #name "#")))
|
||||
#endif
|
||||
|
||||
|
||||
/* if defined(LIBCO_C) */
|
||||
#endif
|
|
@ -19,12 +19,12 @@
|
|||
#include <string>
|
||||
|
||||
#define bool unsigned char
|
||||
#include "libretro.h"
|
||||
#include "../libretro.h"
|
||||
#undef bool
|
||||
|
||||
extern "C" uint64_t cpu_features_get();
|
||||
|
||||
#include "libco/libco.h"
|
||||
#include "../libco/libco.h"
|
||||
|
||||
//can't use retroarch's dynamic.h, it's too full of weird stuff. don't need it anyway
|
||||
|
||||
|
@ -209,11 +209,11 @@ struct CommStruct
|
|||
|
||||
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)); \
|
||||
}
|
||||
//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);
|
||||
|
@ -294,8 +294,8 @@ void retro_log_printf(enum retro_log_level level, const char *fmt, ...)
|
|||
|
||||
u8bool retro_environment(unsigned cmd, void *data)
|
||||
{
|
||||
switch (cmd)
|
||||
{
|
||||
switch (cmd)
|
||||
{
|
||||
case RETRO_ENVIRONMENT_SET_ROTATION:
|
||||
comm.env.rotation_ccw = (int)*(const unsigned*)data * 90;
|
||||
return true;
|
||||
|
@ -370,19 +370,19 @@ u8bool retro_environment(unsigned cmd, void *data)
|
|||
{
|
||||
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
|
||||
else
|
||||
comm.variables[i] = comment.substr(ofs,pipe-ofs);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
case RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE:
|
||||
*(u8bool*)data = comm.variables_dirty;
|
||||
break;
|
||||
|
@ -405,7 +405,7 @@ u8bool retro_environment(unsigned cmd, void *data)
|
|||
//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;
|
||||
|
@ -428,7 +428,7 @@ u8bool retro_environment(unsigned cmd, void *data)
|
|||
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;
|
||||
|
@ -450,113 +450,113 @@ u8bool retro_environment(unsigned cmd, void *data)
|
|||
*((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:
|
||||
{
|
||||
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;
|
||||
}
|
||||
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)
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
comm.env.fb_width = (s32)width;
|
||||
|
@ -571,9 +571,9 @@ void retro_video_refresh(const void *data, unsigned width, unsigned height, size
|
|||
////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
|
||||
//// 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)
|
||||
//{
|
||||
|
@ -587,58 +587,58 @@ void retro_video_refresh(const void *data, unsigned width, unsigned height, size
|
|||
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;
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
s16 samples[] = {left,right};
|
||||
comm.SetBuffer(BufId::Param0,(void*)&samples,4);
|
||||
BREAK(SIG_Sample);
|
||||
}
|
||||
size_t retro_audio_sample_batch(const s16 *data, size_t frames)
|
||||
{
|
||||
comm.SetBuffer(BufId::Param0, (void*)data, frames*4);
|
||||
BREAK(SIG_SampleBatch);
|
||||
return frames;
|
||||
}
|
||||
void retro_input_poll()
|
||||
{
|
||||
}
|
||||
void retro_audio_sample(s16 left, s16 right)
|
||||
{
|
||||
s16 samples[] = {left,right};
|
||||
comm.SetBuffer(BufId::Param0,(void*)&samples,4);
|
||||
BREAK(SIG_Sample);
|
||||
}
|
||||
size_t retro_audio_sample_batch(const s16 *data, size_t frames)
|
||||
{
|
||||
comm.SetBuffer(BufId::Param0, (void*)data, frames*4);
|
||||
BREAK(SIG_SampleBatch);
|
||||
return frames;
|
||||
}
|
||||
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.
|
||||
|
@ -646,7 +646,7 @@ s16 retro_input_state(unsigned port, unsigned device, unsigned index, unsigned i
|
|||
comm.device = device;
|
||||
comm.index = index;
|
||||
comm.id = id;
|
||||
|
||||
|
||||
BREAK(eMessage::SIG_InputState);
|
||||
|
||||
return (s16)comm.value;
|
||||
|
@ -659,28 +659,28 @@ s16 retro_input_state(unsigned port, unsigned device, unsigned index, unsigned i
|
|||
static void LoadHandler(eMessage msg)
|
||||
{
|
||||
//retro_set_environment() is guaranteed to be called before retro_init().
|
||||
|
||||
|
||||
comm.funs.retro_init();
|
||||
|
||||
retro_game_info rgi;
|
||||
retro_game_info* rgiptr = &rgi;
|
||||
memset(&rgi,0,sizeof(rgi));
|
||||
|
||||
if (msg == eMessage::CMD_LoadNoGame)
|
||||
{
|
||||
rgiptr = nullptr;
|
||||
}
|
||||
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(rgiptr);
|
||||
retro_game_info rgi;
|
||||
retro_game_info* rgiptr = &rgi;
|
||||
memset(&rgi,0,sizeof(rgi));
|
||||
|
||||
if (msg == eMessage::CMD_LoadNoGame)
|
||||
{
|
||||
rgiptr = nullptr;
|
||||
}
|
||||
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(rgiptr);
|
||||
|
||||
//Can be called only after retro_load_game() has successfully completed.
|
||||
comm.funs.retro_get_system_av_info(&comm.env.retro_system_av_info);
|
||||
|
@ -689,12 +689,12 @@ static void LoadHandler(eMessage msg)
|
|||
//(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_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
|
||||
//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_initial = comm.env.retro_serialize_size = comm.funs.retro_serialize_size();
|
||||
|
||||
|
@ -752,7 +752,7 @@ void cmd_Unserialize()
|
|||
//TODO maybe not sensible though
|
||||
//void(*retro_unload_game)(void);
|
||||
|
||||
void cmd_SetEnvironment()
|
||||
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);
|
||||
|
@ -795,7 +795,7 @@ 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_control = co_active();
|
||||
co_emu = co_create(128*1024 * sizeof(void*), new_emuthread);
|
||||
|
||||
//grab all the function pointers we need.
|
||||
|
@ -871,4 +871,4 @@ extern "C" __declspec(dllexport) void __cdecl SetVariable(const char* key, const
|
|||
comm.variables[i] = val;
|
||||
comm.variables_dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,12 +11,12 @@
|
|||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="features_cpu.c">
|
||||
<ClCompile Include="..\features_cpu.c">
|
||||
<EnableEnhancedInstructionSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
</EnableEnhancedInstructionSet>
|
||||
<EnableEnhancedInstructionSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotSet</EnableEnhancedInstructionSet>
|
||||
</ClCompile>
|
||||
<ClCompile Include="libco\amd64.c">
|
||||
<ClCompile Include="..\libco\amd64.c">
|
||||
<EnableEnhancedInstructionSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotSet</EnableEnhancedInstructionSet>
|
||||
<EnableEnhancedInstructionSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotSet</EnableEnhancedInstructionSet>
|
||||
</ClCompile>
|
||||
|
@ -26,17 +26,15 @@
|
|||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="libco\libco.h" />
|
||||
<ClInclude Include="libretro.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<MASM Include="libco\coswap.asm" />
|
||||
<ClInclude Include="..\libco\libco.h" />
|
||||
<ClInclude Include="..\libco\settings.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>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
|
@ -124,4 +122,4 @@
|
|||
<UserProperties />
|
||||
</VisualStudio>
|
||||
</ProjectExtensions>
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -1,168 +0,0 @@
|
|||
/* 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
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
/*
|
||||
libco.amd64 (2016-09-14)
|
||||
author: byuu
|
||||
license: public domain
|
||||
*/
|
||||
|
||||
#define LIBCO_C
|
||||
#include "libco.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static long long co_active_buffer[64];
|
||||
static cothread_t co_active_handle = 0;
|
||||
|
||||
static void* smalloc(size_t size)
|
||||
{
|
||||
char* ret = malloc(size + 16);
|
||||
if (ret)
|
||||
{
|
||||
*(size_t*)ret = size;
|
||||
return ret + 16;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void sfree(void* ptr)
|
||||
{
|
||||
char* original = (char*)ptr - 16;
|
||||
size_t size = *(size_t*)original + 16;
|
||||
memset(original, 0, size);
|
||||
free(original);
|
||||
}
|
||||
|
||||
extern void co_swap(cothread_t, cothread_t);
|
||||
|
||||
static void crash() {
|
||||
assert(0); /* called only if cothread_t entrypoint returns */
|
||||
}
|
||||
|
||||
void co_clean() {
|
||||
memset(co_active_buffer, 0, sizeof(co_active_buffer));
|
||||
}
|
||||
|
||||
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_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)smalloc(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) {
|
||||
sfree(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
|
|
@ -1,57 +0,0 @@
|
|||
_TEXT SEGMENT
|
||||
|
||||
PUBLIC co_swap
|
||||
co_swap PROC
|
||||
|
||||
mov [rdx],rsp
|
||||
mov rsp,[rcx]
|
||||
pop rax
|
||||
mov [rdx+ 8],rbp
|
||||
mov [rdx+16],rsi
|
||||
mov [rdx+24],rdi
|
||||
mov [rdx+32],rbx
|
||||
mov [rdx+40],r12
|
||||
mov [rdx+48],r13
|
||||
mov [rdx+56],r14
|
||||
mov [rdx+64],r15
|
||||
|
||||
movaps [rdx+ 80],xmm6
|
||||
movaps [rdx+ 96],xmm7
|
||||
movaps [rdx+112],xmm8
|
||||
add rdx,112
|
||||
movaps [rdx+ 16],xmm9
|
||||
movaps [rdx+ 32],xmm10
|
||||
movaps [rdx+ 48],xmm11
|
||||
movaps [rdx+ 64],xmm12
|
||||
movaps [rdx+ 80],xmm13
|
||||
movaps [rdx+ 96],xmm14
|
||||
movaps [rdx+112],xmm15
|
||||
|
||||
mov rbp,[rcx+ 8]
|
||||
mov rsi,[rcx+16]
|
||||
mov rdi,[rcx+24]
|
||||
mov rbx,[rcx+32]
|
||||
mov r12,[rcx+40]
|
||||
mov r13,[rcx+48]
|
||||
mov r14,[rcx+56]
|
||||
mov r15,[rcx+64]
|
||||
|
||||
movaps xmm6, [rcx+ 80]
|
||||
movaps xmm7, [rcx+ 96]
|
||||
movaps xmm8, [rcx+112]
|
||||
add rcx,112
|
||||
movaps xmm9, [rcx+ 16]
|
||||
movaps xmm10,[rcx+ 32]
|
||||
movaps xmm11,[rcx+ 48]
|
||||
movaps xmm12,[rcx+ 64]
|
||||
movaps xmm13,[rcx+ 80]
|
||||
movaps xmm14,[rcx+ 96]
|
||||
movaps xmm15,[rcx+112]
|
||||
|
||||
jmp rax
|
||||
|
||||
co_swap ENDP
|
||||
|
||||
_TEXT ENDS
|
||||
|
||||
END
|
|
@ -1,38 +0,0 @@
|
|||
/*
|
||||
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,873 @@
|
|||
//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 <dlfcn.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_GetMemory,
|
||||
QUERY_LAST,
|
||||
|
||||
CMD_FIRST,
|
||||
CMD_SetEnvironment,
|
||||
CMD_LoadNoGame,
|
||||
CMD_LoadData,
|
||||
CMD_LoadPath,
|
||||
CMD_Deinit,
|
||||
CMD_Reset,
|
||||
CMD_Run,
|
||||
CMD_UpdateSerializeSize,
|
||||
CMD_Serialize,
|
||||
CMD_Unserialize,
|
||||
CMD_LAST,
|
||||
|
||||
SIG_InputState,
|
||||
SIG_VideoUpdate,
|
||||
SIG_Sample,
|
||||
SIG_SampleBatch,
|
||||
};
|
||||
|
||||
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 system_info;
|
||||
retro_system_av_info system_av_info;
|
||||
size_t retro_serialize_size_initial;
|
||||
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 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 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];
|
||||
size_t 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, size_t 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, size_t size)
|
||||
{
|
||||
buf[id] = ptr;
|
||||
buf_size[id] = size;
|
||||
}
|
||||
|
||||
struct {
|
||||
} strings;
|
||||
|
||||
char* soFile;
|
||||
void* soFileHandle = nullptr;
|
||||
retro_core_t funs;
|
||||
|
||||
void LoadSymbols()
|
||||
{
|
||||
if (!soFileHandle) soFileHandle = dlopen(soFile, RTLD_NOW);
|
||||
//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) do { \
|
||||
void* addr = dlsym(soFileHandle, #x); \
|
||||
memcpy(&funs.x, &addr, sizeof(void*)); \
|
||||
} while(0)
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
bool 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.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.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;
|
||||
//stash pitch if needed
|
||||
|
||||
//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
|
||||
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)
|
||||
{
|
||||
s16 samples[] = {left,right};
|
||||
comm.SetBuffer(BufId::Param0,(void*)&samples,4);
|
||||
BREAK(SIG_Sample);
|
||||
}
|
||||
size_t retro_audio_sample_batch(const s16 *data, size_t frames)
|
||||
{
|
||||
comm.SetBuffer(BufId::Param0, (void*)data, frames*4);
|
||||
BREAK(SIG_SampleBatch);
|
||||
return frames;
|
||||
}
|
||||
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::SIG_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;
|
||||
retro_game_info* rgiptr = &rgi;
|
||||
memset(&rgi,0,sizeof(rgi));
|
||||
|
||||
if (msg == eMessage::CMD_LoadNoGame)
|
||||
{
|
||||
rgiptr = nullptr;
|
||||
}
|
||||
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(rgiptr);
|
||||
|
||||
//Can be called only after retro_load_game() has successfully completed.
|
||||
comm.funs.retro_get_system_av_info(&comm.env.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_initial = 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_UpdateSerializeSize()
|
||||
{
|
||||
comm.env.retro_serialize_size = comm.funs.retro_serialize_size();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void query_GetMemory()
|
||||
{
|
||||
comm.buf_size[BufId::Param0] = comm.funs.retro_get_memory_size(comm.value);
|
||||
comm.buf[BufId::Param0] = comm.funs.retro_get_memory_data(comm.value);
|
||||
}
|
||||
|
||||
const Action kHandlers_CMD[] = {
|
||||
cmd_SetEnvironment,
|
||||
cmd_LoadNoGame,
|
||||
cmd_LoadData,
|
||||
cmd_LoadPath,
|
||||
cmd_Deinit,
|
||||
cmd_Reset,
|
||||
cmd_Run,
|
||||
cmd_UpdateSerializeSize,
|
||||
cmd_Serialize,
|
||||
cmd_Unserialize,
|
||||
};
|
||||
|
||||
const Action kHandlers_QUERY[] = {
|
||||
query_GetMemory,
|
||||
};
|
||||
|
||||
//------------------------------------------------
|
||||
//DLL INTERFACE
|
||||
|
||||
#define EXPORT extern "C" __attribute__((visibility("default")))
|
||||
|
||||
EXPORT void* DllInit(const char* soFile)
|
||||
{
|
||||
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.soFile = strdup(soFile);
|
||||
comm.LoadSymbols();
|
||||
|
||||
//libretro startup steps
|
||||
//"Can be called at any time, even before retro_init()."
|
||||
comm.funs.retro_get_system_info(&comm.env.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;
|
||||
}
|
||||
|
||||
|
||||
EXPORT void 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
|
||||
EXPORT void 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
|
||||
EXPORT void SetBuffer(int id, void* ptr, s32 size)
|
||||
{
|
||||
comm.SetBuffer(id, ptr, size);
|
||||
}
|
||||
|
||||
EXPORT void 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,31 @@
|
|||
|
||||
LDLIBS := -ldl
|
||||
LDFLAGS := -shared
|
||||
CFLAGS := -fPIC -Os -fvisibility=hidden
|
||||
CXXFLAGS := -fPIC -Os -fvisibility=hidden
|
||||
|
||||
ROOT_DIR := ..
|
||||
OBJ_DIR := obj
|
||||
sources := $(ROOT_DIR)/features_cpu.c $(ROOT_DIR)/nix/LibretroBridge.cpp $(ROOT_DIR)/libco/amd64.c
|
||||
_OBJS := $(addsuffix .o,$(sources))
|
||||
OBJS := $(patsubst $(ROOT_DIR)%,$(OBJ_DIR)%,$(_OBJS))
|
||||
|
||||
target := $(OBJ_DIR)/LibretroBridge.so
|
||||
all: $(target)
|
||||
|
||||
$(OBJ_DIR)/%.c.o: $(ROOT_DIR)/%.c
|
||||
@mkdir -p $(@D)
|
||||
$(COMPILE.c) $< -o $@
|
||||
$(OBJ_DIR)/%.cpp.o: $(ROOT_DIR)/%.cpp
|
||||
@mkdir -p $(@D)
|
||||
$(COMPILE.cpp) $< -o $@
|
||||
|
||||
$(target): $(OBJS)
|
||||
$(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@
|
||||
cp $(target) ../../Assets/dll
|
||||
if [ -d ../../output/dll ]; then \
|
||||
cp $(target) ../../output/dll; \
|
||||
fi;
|
||||
|
||||
clean:
|
||||
rm -rf $(OBJ_DIR)
|
Loading…
Reference in New Issue