change libco to use an msvc-compiled dll so that it doesnt get wrecked whenever the .net GC process suspends a thread in a coroutine. cant figure out how to get that same code working compiled by mingw32.

This commit is contained in:
zeromus 2012-09-09 19:02:13 +00:00
parent 8cdcae462d
commit f3b6afa5ad
19 changed files with 765 additions and 19 deletions

View File

@ -51,6 +51,11 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
public class BGInfo
{
/// <summary>
/// Is the layer even enabled?
/// </summary>
public bool Enabled { get { return Bpp != 0; } }
/// <summary>
/// screen and tiledata register values
/// </summary>

View File

@ -112,21 +112,24 @@ namespace BizHawk.MultiClient
allocate(256, 256);
gd.RenderTilesToScreen(pixelptr, stride / 4, 8, 0);
}
if (selection == "BG1" || selection == "BG2" || selection == "BG3" /*|| selection == "BG4"*/)
if (selection == "BG1" || selection == "BG2" || selection == "BG3" || selection == "BG4")
{
int bgnum = int.Parse(selection.Substring(2));
var si = gd.ScanScreenInfo();
var bg = si.BG[bgnum];
var dims = bg.ScreenSizeInPixels;
allocate(dims.Width, dims.Height);
int numPixels = dims.Width * dims.Height;
System.Diagnostics.Debug.Assert(stride / 4 == dims.Width);
var map = gd.FetchTilemap(bg.ScreenAddr, bg.ScreenSize);
int paletteStart = 0;
gd.DecodeBG(pixelptr, stride / 4, map, bg.TiledataAddr, bg.ScreenSize, bg.Bpp, bg.TileSize, paletteStart);
gd.Paletteize(pixelptr, 0, 0, numPixels);
gd.Colorize(pixelptr, 0, numPixels);
if (bg.Enabled)
{
var dims = bg.ScreenSizeInPixels;
allocate(dims.Width, dims.Height);
int numPixels = dims.Width * dims.Height;
System.Diagnostics.Debug.Assert(stride / 4 == dims.Width);
var map = gd.FetchTilemap(bg.ScreenAddr, bg.ScreenSize);
int paletteStart = 0;
gd.DecodeBG(pixelptr, stride / 4, map, bg.TiledataAddr, bg.ScreenSize, bg.Bpp, bg.TileSize, paletteStart);
gd.Paletteize(pixelptr, 0, 0, numPixels);
gd.Colorize(pixelptr, 0, numPixels);
}
}
if (bmp != null)

Binary file not shown.

Binary file not shown.

1
libsnes/bizwinclean.sh Normal file
View File

@ -0,0 +1 @@
rm bsnes/obj/*.o

View File

@ -1,4 +1,6 @@
cd bsnes
export BIZWINCFLAGS="-I. -O3 -masm=intel -DLIBCO_IMPORT -DLIBCO_MSVC"
export TARGET_LIBSNES_LIBDEPS="-L ../libco_msvc_win32/release/ -llibco_msvc_win32"
profile=compatibility platform=win target=libsnes make -e -j
cd ..
cp bsnes/out/snes.dll ../BizHawk.MultiClient/output

View File

@ -9,12 +9,16 @@ target := ui
# options += console
# compiler
c := $(compiler) -std=gnu99
c := $(compiler) -std=gnu99 $(BIZWINCFLAGS)
cpp := $(subst cc,++,$(compiler)) -std=gnu++0x
flags := -I. -O3 -fomit-frame-pointer
link :=
objects := libco
ifneq ($(BIZWINCFLAGS),)
flags := $(BIZWINCFLAGS)
endif
# profile-guided optimization mode
# pgo := instrument
# pgo := optimize

View File

@ -4,8 +4,12 @@
license: public domain
*/
#if defined(__GNUC__) && defined(__i386__)
#include "x86.c"
#if (defined(__ARM_EABI__) || defined(__ARMCC_VERSION) )
#include "sjlj-multi.c"
#elif defined(__GNUC__) && defined(__i386__)
#if !defined(LIBCO_MSVC)
#include "x86.c"
#endif
#elif defined(__GNUC__) && defined(__amd64__)
#include "amd64.c"
#elif defined(__GNUC__) && defined(_ARCH_PPC)

View File

@ -20,11 +20,20 @@ extern "C" {
#endif
typedef void* cothread_t;
typedef void (*coentry_t)(void);
cothread_t co_active();
cothread_t co_create(unsigned int, void (*)(void));
void co_delete(cothread_t);
void co_switch(cothread_t);
#if defined(LIBCO_IMPORT)
#define LIBCO_IMPORTDECL __declspec(dllimport)
#elif defined(LIBCO_EXPORT)
#define LIBCO_IMPORTDECL __declspec(dllexport)
#else
#define LIBCO_IMPORTDECL
#endif
LIBCO_IMPORTDECL cothread_t co_active();
LIBCO_IMPORTDECL cothread_t co_create(unsigned int, coentry_t);
LIBCO_IMPORTDECL void co_delete(cothread_t);
LIBCO_IMPORTDECL void co_switch(cothread_t);
#ifdef __cplusplus
}

View File

@ -0,0 +1,120 @@
#define LIBCO_C
#include "libco.h"
#include <stdlib.h>
#include <setjmp.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;
} cothread_struct;
static thread_local cothread_struct co_primary;
static thread_local cothread_struct *co_running = 0;
//-------------------
#if defined(_MSC_VER) || defined(MINGW32)
__declspec(dllimport) cothread_t os_co_create();
__declspec(dllimport) void os_pre_setjmp(cothread_t target);
__declspec(dllimport) void os_pre_longjmp(cothread_struct* 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;
}
cothread_t co_create(unsigned int size, void (*coentry)(void))
{
cothread_struct *thread;
if(!co_running) co_running = &co_primary;
thread = (cothread_struct*)malloc(sizeof(cothread_struct));
if(thread)
{
thread->coentry = coentry;
thread->stack = malloc(size);
{
setjmp(thread->context);
os_co_create(thread,size,coentry);
}
}
return (cothread_t)thread;
}
void co_delete(cothread_t cothread)
{
if(cothread)
{
if(((cothread_struct*)cothread)->stack)
{
free(((cothread_struct*)cothread)->stack);
}
free(cothread);
}
}
void co_switch(cothread_t cothread)
{
os_pre_setjmp(cothread);
if(!setjmp(co_running->context))
{
co_running = (cothread_struct*)cothread;
os_pre_longjmp(co_running);
longjmp(co_running->context,0);
}
}
#ifdef __cplusplus
}
#endif

View File

@ -24,7 +24,7 @@ else ifeq ($(platform),osx)
ar rcs out/libsnes.a $(objects)
$(cpp) -o out/libsnes.dylib -install_name @executable_path/../Libraries/libsnes.dylib -shared -dynamiclib $(objects)
else ifeq ($(platform),win)
$(cpp) -o out/snes.dll -shared -Wl,--out-implib,libsnes.a $(objects)
$(cpp) -o out/snes.dll -shared -Wl,--out-implib,libsnes.a $(objects) $(TARGET_LIBSNES_LIBDEPS)
endif
install:

View File

@ -126,7 +126,31 @@ void snes_set_cartridge_basename(const char *basename) {
interface.basename = basename;
}
#include <setjmp.h>
static jmp_buf buf;
void second(void) {
printf("second\n"); // prints
longjmp(buf,1); // jumps back to where setjmp was called - making setjmp now return 1
}
void first(void) {
second();
printf("first\n"); // does not print
}
void test() {
if ( ! setjmp(buf) ) {
first(); // when executed, setjmp returns 0
} else { // when longjmp jumps back, setjmp returns 1
printf("main\n"); // prints
}
}
void snes_init(void) {
test();
SNES::interface = &interface;
SNES::system.init();
SNES::input.connect(SNES::Controller::Port1, SNES::Input::Device::Joypad);

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,178 @@
/*
libco.sjlj (2008-01-28)
author: Nach
license: public domain
*/
/*
* Note this was designed for UNIX systems. Based on ideas expressed in a paper
* by Ralf Engelschall.
* For SJLJ on other systems, one would want to rewrite springboard() and
* co_create() and hack the jmb_buf stack pointer.
*/
#define LIBCO_C
#define LIBCO_EXPORT
#include "../bsnes/libco/libco.h"
#include <stdlib.h>
#include <setjmp.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;
} cothread_struct;
static thread_local cothread_struct co_primary;
static thread_local cothread_struct *co_running = 0;
//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);
}
__declspec(dllexport) cothread_t co_active()
{
if(!co_running) co_running = &co_primary;
return (cothread_t)co_running;
}
__declspec(dllexport) cothread_t co_create(unsigned int size, void (*coentry)(void))
{
cothread_struct *thread;
if(!co_running) co_running = &co_primary;
thread = (cothread_struct*)malloc(sizeof(cothread_struct));
if(thread)
{
thread->coentry = coentry;
thread->stack = malloc(size);
{
setjmp(thread->context);
os_co_create(thread,size,coentry);
}
}
return (cothread_t)thread;
}
__declspec(dllexport) void co_delete(cothread_t cothread)
{
if(cothread)
{
if(((cothread_struct*)cothread)->stack)
{
free(((cothread_struct*)cothread)->stack);
}
free(cothread);
}
}
__declspec(dllexport) void co_switch(cothread_t cothread)
{
os_pre_setjmp(cothread);
if(!setjmp(co_running->context))
{
co_running = (cothread_struct*)cothread;
os_pre_longjmp(co_running);
longjmp(co_running->context,0);
}
}
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,20 @@

Microsoft Visual Studio Solution File, Format Version 9.00
# Visual Studio 2005
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libco_msvc", "libco_msvc_win32.vcproj", "{C5D03072-BBF9-4DED-8CE6-5467736251BF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C5D03072-BBF9-4DED-8CE6-5467736251BF}.Debug|Win32.ActiveCfg = Debug|Win32
{C5D03072-BBF9-4DED-8CE6-5467736251BF}.Debug|Win32.Build.0 = Debug|Win32
{C5D03072-BBF9-4DED-8CE6-5467736251BF}.Release|Win32.ActiveCfg = Release|Win32
{C5D03072-BBF9-4DED-8CE6-5467736251BF}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,187 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8.00"
Name="libco_msvc_win32"
ProjectGUID="{C5D03072-BBF9-4DED-8CE6-5467736251BF}"
RootNamespace="libco_msvc_win32"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="2"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
CommandLine=""
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBCO_MSVC_EXPORTS"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="true"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
CommandLine="copy $(TargetPath) ..\..\BizHawk.MultiClient\output"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="2"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
CommandLine=""
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBCO_MSVC_EXPORTS"
RuntimeLibrary="2"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="true"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="2"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
CommandLine="copy $(TargetPath) ..\..\BizHawk.MultiClient\output"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<File
RelativePath=".\libco_msvc_win32.c"
>
</File>
<File
RelativePath=".\readme.txt"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@ -0,0 +1,11 @@
* Why does this exist?
Because zeromus can't figure out how to successfully compile this code in mingw32.
My efforts are in trying-to-port-to-mingw-win32.c; I don't know why it isn't working.
* Why do we need this code?
Because libco needs to be a bit more properly win32 in order for it to get used from .net code.
.net throws exceptions in each thread when it needs to suspend them for GC.
Those exceptions get garbled without more proper win32 stack frame setup, and the process terminates.
Additionally, you wont be able to debug very well from callbacks out of a coroutine into c# without this.

View File

@ -0,0 +1,178 @@
//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 LIBCO_C
#define LIBCO_EXPORT
#include "../bsnes/libco/libco.h"
#include <stdlib.h>
#include <setjmp.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*coentry_t)(void);
typedef struct
{
jmp_buf context;
coentry_t coentry;
void *stack;
unsigned long seh_frame, stack_top, stack_bottom;
cothread_t caller;
} cothread_struct;
static thread_local cothread_struct co_primary;
static thread_local cothread_struct *co_running = 0;
#define WINVER 0x0400
#define _WIN32_WINNT 0x0400
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#ifndef _MSC_VER
typedef struct __JUMP_BUFFER {
unsigned long Ebp;
unsigned long Ebx;
unsigned long Edi;
unsigned long Esi;
unsigned long Esp;
unsigned long Eip;
unsigned long Registration;
unsigned long TryLevel;
unsigned long Cookie;
unsigned long UnwindFunc;
unsigned long UnwindData[6];
} _JUMP_BUFFER;
#endif
#pragma warning(disable:4733)
#pragma warning(disable:4311)
static void capture_fs(cothread_struct* rec)
{
asm(
"mov %0, dword ptr fs:[0];"
"mov %1, dword ptr fs:[4];"
"mov %2, dword ptr fs:[8];"
:"=r"(rec->seh_frame), "=r"(rec->stack_top), "=r"(rec->stack_bottom)
:
:
);
}
static void restore_fs(cothread_struct* rec)
{
asm(
"mov dword ptr fs:[0], %0;"
"mov dword ptr fs:[4], %1;"
"mov dword ptr fs:[8], %2;"
:
:"r"(rec->seh_frame), "r"(rec->stack_top), "r"(rec->stack_bottom)
:
);
}
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);
}
cothread_t co_active()
{
if(!co_running) co_running = &co_primary;
return (cothread_t)co_running;
}
cothread_t co_create(unsigned int size, void (*coentry)(void))
{
cothread_struct *thread;
if(!co_running) co_running = &co_primary;
thread = (cothread_struct*)malloc(sizeof(cothread_struct));
if(thread)
{
thread->coentry = coentry;
thread->stack = malloc(size);
{
setjmp(thread->context);
os_co_create(thread,size,coentry);
}
}
return (cothread_t)thread;
}
void co_delete(cothread_t cothread)
{
if(cothread)
{
if(((cothread_struct*)cothread)->stack)
{
free(((cothread_struct*)cothread)->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