diff --git a/desmume/src/GPU_osd.cpp b/desmume/src/GPU_osd.cpp index a52ed1588..34ebb09f6 100644 --- a/desmume/src/GPU_osd.cpp +++ b/desmume/src/GPU_osd.cpp @@ -115,7 +115,7 @@ void HudStruct::reset() { FpsDisplay.x=0; FpsDisplay.y=5; - FpsDisplay.xsize=120; + FpsDisplay.xsize=166; FpsDisplay.ysize=10; FrameCounter.x=0; @@ -504,7 +504,7 @@ void DrawHUD() if (CommonSettings.hud.FpsDisplay) { - osd->addFixed(Hud.FpsDisplay.x, Hud.FpsDisplay.y, "Fps:%02d/%02d", Hud.fps, Hud.fps3d); + osd->addFixed(Hud.FpsDisplay.x, Hud.FpsDisplay.y, "Fps:%02d/%02d%s", Hud.fps, Hud.fps3d, driver->EMU_IsEmulationPaused() ? " (paused)" : ""); } if (CommonSettings.hud.FrameCounterDisplay) diff --git a/desmume/src/NDSSystem.cpp b/desmume/src/NDSSystem.cpp index 880837e8a..c9eb901d3 100644 --- a/desmume/src/NDSSystem.cpp +++ b/desmume/src/NDSSystem.cpp @@ -2638,7 +2638,7 @@ void NDS_setTouchPos(u16 x, u16 y) #ifndef WIN32 // FIXME: this code should be deleted from here, // other platforms should call NDS_beginProcessingInput,NDS_endProcessingInput once per frame instead - // (see the function called "run" in src/windows/main.cpp), + // (see the function called "StepRunLoop_Core" in src/windows/main.cpp), // but I'm leaving this here for now since I can't test those other platforms myself. nds.touchX = rawUserInput.touch.touchX; nds.touchY = rawUserInput.touch.touchY; @@ -2656,7 +2656,7 @@ void NDS_releaseTouch(void) #ifndef WIN32 // FIXME: this code should be deleted from here, // other platforms should call NDS_beginProcessingInput,NDS_endProcessingInput once per frame instead - // (see the function called "run" in src/windows/main.cpp), + // (see the function called "StepRunLoop_Core" in src/windows/main.cpp), // but I'm leaving this here for now since I can't test those other platforms myself. nds.touchX = 0; nds.touchY = 0; @@ -2793,6 +2793,30 @@ static void NDS_applyFinalInput() } +void NDS_suspendProcessingInput(bool suspend) +{ + static int suspendCount = 0; + if(suspend) + { + // enter non-processing block + assert(validToProcessInput); + validToProcessInput = false; + suspendCount++; + } + else if(suspendCount) + { + // exit non-processing block + validToProcessInput = true; + suspendCount--; + } + else + { + // unwound past first time -> not processing + validToProcessInput = false; + } +} + + void emu_halt() { //printf("halting emu: ARM9 PC=%08X/%08X, ARM7 PC=%08X/%08X\n", NDS_ARM9.R[15], NDS_ARM9.instruct_adr, NDS_ARM7.R[15], NDS_ARM7.instruct_adr); execute = false; diff --git a/desmume/src/NDSSystem.h b/desmume/src/NDSSystem.h index 32da9424f..198e59213 100644 --- a/desmume/src/NDSSystem.h +++ b/desmume/src/NDSSystem.h @@ -336,6 +336,10 @@ void NDS_beginProcessingInput(); // call once per frame to copy the processed input to the final input void NDS_endProcessingInput(); +// this is in case something needs reentrancy while processing input +void NDS_suspendProcessingInput(bool suspend); + + int NDS_LoadROM(const char *filename, const char* logicalFilename=0); void NDS_FreeROM(void); diff --git a/desmume/src/commandline.cpp b/desmume/src/commandline.cpp index 3e4e2b351..ddd0ae1cc 100644 --- a/desmume/src/commandline.cpp +++ b/desmume/src/commandline.cpp @@ -41,6 +41,7 @@ CommandLine::CommandLine() , _cflash_image(0) , _cflash_path(0) , _single_core(0) +, _multi_core(0) { load_slot = 0; arm9_gdb_port = arm7_gdb_port = 0; @@ -68,6 +69,7 @@ void CommandLine::loadCommonOptions() { "cflash-path", 0, 0, G_OPTION_ARG_FILENAME, &_cflash_path, "Requests cflash in gbaslot with filesystem rooted at this path", "CFLASH_PATH"}, #ifdef _MSC_VER { "single-core", 0, 0, G_OPTION_ARG_NONE, &_single_core, "Limit execution to use approximately only one core", "NUM_CORES"}, + { "multi-core", 0, 0, G_OPTION_ARG_NONE, &_multi_core, "Act as if multiple cores are present, even on a single-core machine", "MULTI_CORE"}, { "scanline-filter-a", 0, 0, G_OPTION_ARG_INT, &scanline_filter_a, "Intensity of fadeout for scanlines filter (edge) (default 2)", "SCANLINE_FILTER_A"}, { "scanline-filter-b", 0, 0, G_OPTION_ARG_INT, &scanline_filter_b, "Intensity of fadeout for scanlines filter (corner) (default 4)", "SCANLINE_FILTER_B"}, #endif @@ -95,7 +97,8 @@ bool CommandLine::parse(int argc,char **argv) if(_cflash_image) cflash_image = _cflash_image; if(_cflash_path) cflash_path = _cflash_path; - CommonSettings.single_core = _single_core!=0; + if(_single_core) CommonSettings.single_core = true; + if(_multi_core) CommonSettings.single_core = false; if (argc == 2) nds_file = argv[1]; diff --git a/desmume/src/commandline.h b/desmume/src/commandline.h index 689ac776d..2d0ecd2d3 100644 --- a/desmume/src/commandline.h +++ b/desmume/src/commandline.h @@ -74,6 +74,7 @@ private: char* _cflash_image; char* _cflash_path; int _single_core; + int _multi_core; }; #endif diff --git a/desmume/src/driver.h b/desmume/src/driver.h index 2e459e6ee..a6741065b 100644 --- a/desmume/src/driver.h +++ b/desmume/src/driver.h @@ -32,10 +32,27 @@ class BaseDriver { public: virtual bool WIFI_Host_InitSystem() { return FALSE; } virtual void WIFI_Host_ShutdownSystem() {} + + virtual void AVI_SoundUpdate(void* soundData, int soundLen) {} virtual bool AVI_IsRecording() { return FALSE; } virtual bool WAV_IsRecording() { return FALSE; } + virtual void USR_InfoMessage(const char *message) { LOG("%s\n", message); } - virtual void AVI_SoundUpdate(void* soundData, int soundLen) {} + virtual void USR_RefreshScreen() {} + virtual void USR_SetDisplayPostpone(int milliseconds, bool drawNextFrame) {} // -1 == indefinitely, 0 == don't pospone, 500 == don't draw for 0.5 seconds + + enum eStepMainLoopResult + { + ESTEP_NOT_IMPLEMENTED = -1, + ESTEP_CALL_AGAIN = 0, + ESTEP_DONE = 1, + }; + virtual eStepMainLoopResult EMU_StepMainLoop(bool allowSleep, bool allowPause, int frameSkip, bool disableUser, bool disableCore) { return ESTEP_NOT_IMPLEMENTED; } // -1 frameSkip == useCurrentDefault + virtual void EMU_PauseEmulation(bool pause) {} + virtual bool EMU_IsEmulationPaused() { return false; } + virtual bool EMU_IsFastForwarding() { return false; } + virtual bool EMU_HasEmulationStarted() { return true; } + virtual bool EMU_IsAtFrameBoundary() { return true; } }; extern BaseDriver* driver; diff --git a/desmume/src/lua-engine.cpp b/desmume/src/lua-engine.cpp index 67ef2e85a..4741a2382 100644 --- a/desmume/src/lua-engine.cpp +++ b/desmume/src/lua-engine.cpp @@ -14,12 +14,20 @@ #include "windows.h" #endif -// the emulator must provide these so that we can implement -// the various functions the user can call from their lua script -// (this interface with the emulator needs cleanup, I know) + +// a few functions that maybe aren't part of the Lua engine +// but didn't make sense to add to BaseDriver (at least not yet) +static void Clear_Sound_Buffer() { + if(SPU_user) SPU_user->ShutUp(); +} +static bool IsHardwareAddressValid(u32 address) { + // maybe TODO? let's say everything is valid. + return true; +} + + +// actual lua engine follows // adapted from gens-rr, nitsuja + upthorn -extern int (*Update_Frame)(); -extern int (*Update_Frame_Fast)(); extern "C" { #include "lua.h" @@ -50,9 +58,9 @@ struct LuaContextInfo { bool ranExit; // used to prevent a registered exit callback from ever getting called more than once bool guiFuncsNeedDeferring; // true whenever GUI drawing would be cleared by the next emulation update before it would be visible, and thus needs to be deferred until after the next emulation update int numDeferredFuncs; // number of deferred function calls accumulated, used to impose an arbitrary limit to avoid running out of memory - bool ranFrameAdvance; // false if gens.frameadvance() hasn't been called yet + bool ranFrameAdvance; // false if emu.frameadvance() hasn't been called yet int transparencyModifier; // values less than 255 will scale down the opacity of whatever the GUI renders, values greater than 255 will increase the opacity of anything transparent the GUI renders - SpeedMode speedMode; // determines how gens.frameadvance() acts + SpeedMode speedMode; // determines how emu.frameadvance() acts char panicMessage [72]; // a message to print if the script terminates due to panic being set std::string lastFilename; // path to where the script last ran from so that restart can work (note: storing the script in memory instead would not be useful because we always want the most up-to-date script from file) std::string nextFilename; // path to where the script should run from next, mainly used in case the restart flag is true @@ -220,7 +228,7 @@ static int memory_registerHook(lua_State* L, LuaMemHookType hookType, int defaul // re-cache regions of hooked memory across all scripts CalculateMemHookRegions(hookType); - StopScriptIfFinished(luaStateToUIDMap[L]); + StopScriptIfFinished(luaStateToUIDMap[L->l_G->mainthread]); return 0; } @@ -280,7 +288,7 @@ DEFINE_LUA_FUNCTION(emu_registerbefore, "func") lua_getfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFOREEMULATION]); lua_insert(L,1); lua_setfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFOREEMULATION]); - StopScriptIfFinished(luaStateToUIDMap[L]); + StopScriptIfFinished(luaStateToUIDMap[L->l_G->mainthread]); return 1; } DEFINE_LUA_FUNCTION(emu_registerafter, "func") @@ -291,7 +299,7 @@ DEFINE_LUA_FUNCTION(emu_registerafter, "func") lua_getfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_AFTEREMULATION]); lua_insert(L,1); lua_setfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_AFTEREMULATION]); - StopScriptIfFinished(luaStateToUIDMap[L]); + StopScriptIfFinished(luaStateToUIDMap[L->l_G->mainthread]); return 1; } DEFINE_LUA_FUNCTION(emu_registerexit, "func") @@ -302,10 +310,10 @@ DEFINE_LUA_FUNCTION(emu_registerexit, "func") lua_getfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFOREEXIT]); lua_insert(L,1); lua_setfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFOREEXIT]); - StopScriptIfFinished(luaStateToUIDMap[L]); + StopScriptIfFinished(luaStateToUIDMap[L->l_G->mainthread]); return 1; -}/* -DEFINE_LUA_FUNCTION(emu_registerstart, "func") +} +DEFINE_LUA_FUNCTION(emu_registerstart, "func") // TODO: use call registered LUACALL_ONSTART functions on reset { if (!lua_isnil(L,1)) luaL_checktype(L, 1, LUA_TFUNCTION); @@ -314,11 +322,11 @@ DEFINE_LUA_FUNCTION(emu_registerstart, "func") lua_insert(L,1); lua_pushvalue(L,-1); // copy the function so we can also call it lua_setfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_ONSTART]); - if (!lua_isnil(L,-1) && ((Genesis_Started)||(SegaCD_Started)||(_32X_Started))) + if (!lua_isnil(L,-1) && driver->EMU_HasEmulationStarted()) lua_call(L,0,0); // call the function now since the game has already started and this start function hasn't been called yet - StopScriptIfFinished(luaStateToUIDMap[L]); + StopScriptIfFinished(luaStateToUIDMap[L->l_G->mainthread]); return 1; -}*/ +} DEFINE_LUA_FUNCTION(gui_register, "func") { if (!lua_isnil(L,1)) @@ -327,7 +335,7 @@ DEFINE_LUA_FUNCTION(gui_register, "func") lua_getfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_AFTEREMULATIONGUI]); lua_insert(L,1); lua_setfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_AFTEREMULATIONGUI]); - StopScriptIfFinished(luaStateToUIDMap[L]); + StopScriptIfFinished(luaStateToUIDMap[L->l_G->mainthread]); return 1; } DEFINE_LUA_FUNCTION(state_registersave, "func[,savekey]") @@ -340,7 +348,7 @@ DEFINE_LUA_FUNCTION(state_registersave, "func[,savekey]") lua_getfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFORESAVE]); lua_insert(L,1); lua_setfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFORESAVE]); - StopScriptIfFinished(luaStateToUIDMap[L]); + StopScriptIfFinished(luaStateToUIDMap[L->l_G->mainthread]); return 1; } DEFINE_LUA_FUNCTION(state_registerload, "func[,loadkey]") @@ -353,7 +361,7 @@ DEFINE_LUA_FUNCTION(state_registerload, "func[,loadkey]") lua_getfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_AFTERLOAD]); lua_insert(L,1); lua_setfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_AFTERLOAD]); - StopScriptIfFinished(luaStateToUIDMap[L]); + StopScriptIfFinished(luaStateToUIDMap[L->l_G->mainthread]); return 1; } @@ -374,7 +382,7 @@ DEFINE_LUA_FUNCTION(input_registerhotkey, "keynum,func") luaL_checktype(L, 2, LUA_TFUNCTION); lua_settop(L,2); lua_setfield(L, LUA_REGISTRYINDEX, key); - StopScriptIfFinished(luaStateToUIDMap[L]); + StopScriptIfFinished(luaStateToUIDMap[L->l_G->mainthread]); return 1; } } @@ -416,7 +424,7 @@ static int doPopup(lua_State* L, const char* deftype, const char* deficon) static const int etypes [] = {MB_OK, MB_YESNO, MB_YESNOCANCEL, MB_OKCANCEL, MB_ABORTRETRYIGNORE}; static const int eicons [] = {MB_ICONINFORMATION, MB_ICONQUESTION, MB_ICONWARNING, MB_ICONERROR}; // DialogsOpen++; - int uid = luaStateToUIDMap[L]; + int uid = luaStateToUIDMap[L->l_G->mainthread]; EnableWindow(MainWindow->getHWnd(), false); // if (Full_Screen) // { @@ -516,7 +524,7 @@ static char* ConstructScriptSaveDataPath(char* output, int bufferSize, LuaContex return rv; } -// gens.persistglobalvariables({ +// emu.persistglobalvariables({ // variable1 = defaultvalue1, // variable2 = defaultvalue2, // etc @@ -531,7 +539,7 @@ static char* ConstructScriptSaveDataPath(char* output, int bufferSize, LuaContex // also, if you change the default value that will reset the variable to the new default. DEFINE_LUA_FUNCTION(emu_persistglobalvariables, "variabletable") { - int uid = luaStateToUIDMap[L]; + int uid = luaStateToUIDMap[L->l_G->mainthread]; LuaContextInfo& info = GetCurrentInfo(); // construct a path we can load the persistent variables from @@ -596,7 +604,7 @@ DEFINE_LUA_FUNCTION(emu_persistglobalvariables, "variabletable") } else { - luaL_error(L, "'%s' = '%s' entries are not allowed in the table passed to gens.persistglobalvariables()", lua_typename(L,keyType), lua_typename(L,valueType)); + luaL_error(L, "'%s' = '%s' entries are not allowed in the table passed to emu.persistglobalvariables()", lua_typename(L,keyType), lua_typename(L,valueType)); } int varNameIndex = valueIndex; @@ -677,47 +685,82 @@ void DeferFunctionCall(lua_State* L, const char* idstring) // clean the stack lua_settop(L, 0); } + +static const char* refStashString = "refstash"; + void CallDeferredFunctions(lua_State* L, const char* idstring) { - lua_settop(L, 0); lua_getfield(L, LUA_REGISTRYINDEX, idstring); - int numCalls = lua_objlen(L, 1); - for(int i = 1; i <= numCalls; i++) - { - lua_rawgeti(L, 1, i); // get the function+arguments list - int listSize = lua_objlen(L, 2); - - // push the arguments and the function - for(int j = 1; j <= listSize; j++) - lua_rawgeti(L, 2, j); - - // get and pop the function - lua_CFunction cf = lua_tocfunction(L, -1); - lua_pop(L, 1); - - // shift first argument to slot 1 and call the function - lua_remove(L, 2); - lua_remove(L, 1); - cf(L); - - // prepare for next iteration - lua_settop(L, 0); - lua_getfield(L, LUA_REGISTRYINDEX, idstring); - } - - // clear the list of deferred functions + int numCalls = lua_objlen(L, -1); if(numCalls > 0) { + // save and pop any extra things that were on the stack + int top = lua_gettop(L); + int stashRef = -1; + if(top > 1) + { + lua_insert(L, 1); + lua_getfield(L, LUA_REGISTRYINDEX, refStashString); + lua_insert(L, 2); + lua_createtable(L, top-1, 0); + lua_insert(L, 3); + for(int remaining = top; remaining-- > 1;) + lua_rawseti(L, 3, remaining); + assert(lua_gettop(L) == 3); + stashRef = luaL_ref(L, 2); + lua_pop(L, 1); + } + + // loop through all the queued function calls + for(int i = 1; i <= numCalls; i++) + { + lua_rawgeti(L, 1, i); // get the function+arguments list + int listSize = lua_objlen(L, 2); + + // push the arguments and the function + for(int j = 1; j <= listSize; j++) + lua_rawgeti(L, 2, j); + + // get and pop the function + lua_CFunction cf = lua_tocfunction(L, -1); + lua_pop(L, 1); + + // shift first argument to slot 1 and call the function + lua_remove(L, 2); + lua_remove(L, 1); + cf(L); + + // prepare for next iteration + lua_settop(L, 0); + lua_getfield(L, LUA_REGISTRYINDEX, idstring); + } + + // clear the list of deferred functions lua_newtable(L); lua_setfield(L, LUA_REGISTRYINDEX, idstring); LuaContextInfo& info = GetCurrentInfo(); info.numDeferredFuncs -= numCalls; if(info.numDeferredFuncs < 0) info.numDeferredFuncs = 0; - } - // clean the stack - lua_settop(L, 0); + // restore the stack + lua_settop(L, 0); + if(top > 1) + { + lua_getfield(L, LUA_REGISTRYINDEX, refStashString); + lua_rawgeti(L, 1, stashRef); + for(int i = 1; i <= top-1; i++) + lua_rawgeti(L, 2, i); + luaL_unref(L, 1, stashRef); + lua_remove(L, 2); + lua_remove(L, 1); + } + assert(lua_gettop(L) == top - 1); + } + else + { + lua_pop(L, 1); + } } bool DeferGUIFuncIfNeeded(lua_State* L) @@ -993,7 +1036,7 @@ DEFINE_LUA_FUNCTION(print, "...") { const char* str = toCString(L); - int uid = luaStateToUIDMap[L]; + int uid = luaStateToUIDMap[L->l_G->mainthread]; LuaContextInfo& info = GetCurrentInfo(); if(info.print) @@ -1120,7 +1163,8 @@ DEFINE_LUA_FUNCTION(bitbit, "whichbit") return 1; } -//int gens_wait(lua_State* L); +int emu_wait(lua_State* L); +int dontworry(LuaContextInfo& info); void indicateBusy(lua_State* L, bool busy) { @@ -1136,7 +1180,7 @@ void indicateBusy(lua_State* L, bool busy) va_end(argp); lua_concat(L, 2); LuaContextInfo& info = GetCurrentInfo(); - int uid = luaStateToUIDMap[L]; + int uid = luaStateToUIDMap[L->l_G->mainthread]; if(info.print) { info.print(uid, lua_tostring(L,-1)); @@ -1150,7 +1194,7 @@ void indicateBusy(lua_State* L, bool busy) } */ #ifdef _WIN32 - int uid = luaStateToUIDMap[L]; + int uid = luaStateToUIDMap[L->l_G->mainthread]; HWND hDlg = (HWND)uid; char str [1024]; GetWindowText(hDlg, str, 1000); @@ -1170,6 +1214,7 @@ void indicateBusy(lua_State* L, bool busy) #endif } + #define HOOKCOUNT 4096 #define MAX_WORRY_COUNT 6000 void LuaRescueHook(lua_State* L, lua_Debug *dbg) @@ -1186,7 +1231,7 @@ void LuaRescueHook(lua_State* L, lua_Debug *dbg) // but we don't trust their judgement completely, // so periodically update the main loop so they have a chance to manually stop it info.worryCount = 0; -// gens_wait(L); + emu_wait(L); info.stopWorrying = true; } return; @@ -1201,7 +1246,7 @@ void LuaRescueHook(lua_State* L, lua_Debug *dbg) bool stopworrying = true; if(!info.panic) { -// Clear_Sound_Buffer(); + Clear_Sound_Buffer(); #if defined(ASK_USER_ON_FREEZE) && defined(_WIN32) DialogsOpen++; int answer = MessageBox(HWnd, "A Lua script has been running for quite a while. Maybe it is in an infinite loop.\n\nWould you like to stop the script?\n\n(Yes to stop it now,\n No to keep running and not ask again,\n Cancel to keep running but ask again later)", "Lua Alert", MB_YESNOCANCEL | MB_DEFBUTTON3 | MB_ICONASTERISK); @@ -1244,7 +1289,7 @@ void printfToOutput(const char* fmt, ...) if(info.print) { lua_State* L = info.L; - int uid = luaStateToUIDMap[L]; + int uid = luaStateToUIDMap[L->l_G->mainthread]; info.print(uid, str); info.print(uid, "\r\n"); worry(L,300); @@ -1257,8 +1302,8 @@ void printfToOutput(const char* fmt, ...) } bool FailVerifyAtFrameBoundary(lua_State* L, const char* funcName, int unstartedSeverity=2, int inframeSeverity=2) -{//TODO -/* if (!((Genesis_Started)||(SegaCD_Started)||(_32X_Started))) +{ + if (!driver->EMU_HasEmulationStarted()) { static const char* msg = "cannot call %s() when emulation has not started."; switch(unstartedSeverity) @@ -1269,7 +1314,7 @@ bool FailVerifyAtFrameBoundary(lua_State* L, const char* funcName, int unstarted } return true; } - if(Inside_Frame) + if(!driver->EMU_IsAtFrameBoundary()) { static const char* msg = "cannot call %s() inside an emulation frame."; switch(inframeSeverity) @@ -1279,104 +1324,119 @@ bool FailVerifyAtFrameBoundary(lua_State* L, const char* funcName, int unstarted default: case 2: luaL_error(L, msg, funcName); break; } return true; - }*/ + } return false; } -/* -// acts similar to normal emulation update -// except without the user being able to activate emulator commands -DEFINE_LUA_FUNCTION(gens_emulateframe, "") + + +// wrapper for EMU_StepMainLoop that provides a default implementation if ESTEP_NOT_IMPLEMENTED is returned. +// which only works if called from a function whose return value will get returned to Lua directly. +// TODO: actually implement the default case by making the main thread we use into a coroutine and resuming it periodically +static bool stepped_emulation = false; // <-- this is the result of running the macro +#define StepEmulationOnce(allowSleep, allowPause, frameSkip, disableUser, disableCore) \ + switch(driver->EMU_StepMainLoop(allowSleep, allowPause, frameSkip, disableUser, disableCore)) \ + { default: \ + case BaseDriver::ESTEP_NOT_IMPLEMENTED: /*return lua_yield(L, 0);*/ luaL_error(L, "Lua frame advance functions are not yet implemented for this platform, and neither is the fallback implementation."); break;/*TODO*/ \ + case BaseDriver::ESTEP_CALL_AGAIN: stepped_emulation = !driver->EMU_HasEmulationStarted(); break; \ + case BaseDriver::ESTEP_DONE: stepped_emulation = true; break; \ + } + +// same as StepEmulationOnce, except calls EMU_StepMainLoop multiple times if it returns ESTEP_CALL_AGAIN +#define StepEmulation(allowSleep, allowPause, frameSkip, disableUser, disableCore) \ + do { \ + StepEmulationOnce(allowSleep, allowPause, frameSkip, disableUser, disableCore); \ + if(stepped_emulation || (info).panic) break; \ + } while(true) + + +// note: caller must return the value this returns to Lua (at least if nonzero) +int StepEmulationAtSpeed(lua_State* L, SpeedMode speedMode, bool allowPause) { - if(FailVerifyAtFrameBoundary(L, "gens.emulateframe", 0,1)) + int postponeTime; + bool drawNextFrame; + int worryIntensity; + bool allowSleep; + int frameSkip; + bool disableUserFeedback; + + LuaContextInfo& info = GetCurrentInfo(); + switch(speedMode) + { + default: + case SPEEDMODE_NORMAL: + postponeTime = 0, drawNextFrame = true, worryIntensity = 300; + allowSleep = true; + frameSkip = -1; + disableUserFeedback = false; + break; + case SPEEDMODE_NOTHROTTLE: + postponeTime = 250, drawNextFrame = true, worryIntensity = 200; + allowSleep = driver->EMU_IsEmulationPaused(); + frameSkip = driver->EMU_IsFastForwarding() ? -1 : 0; + disableUserFeedback = false; + break; + case SPEEDMODE_TURBO: + postponeTime = 500, drawNextFrame = true, worryIntensity = 150; + allowSleep = driver->EMU_IsEmulationPaused(); + frameSkip = 16; + disableUserFeedback = false; + break; + case SPEEDMODE_MAXIMUM: + postponeTime = 1000, drawNextFrame = false, worryIntensity = 100; + allowSleep = driver->EMU_IsEmulationPaused(); + frameSkip = 65535; + disableUserFeedback = true; + break; + } + + driver->USR_SetDisplayPostpone(postponeTime, drawNextFrame); + allowPause ? dontworry(info) : worry(L, worryIntensity); + + if(!allowPause && driver->EMU_IsEmulationPaused()) + driver->EMU_PauseEmulation(false); + + StepEmulation(allowSleep, allowPause, frameSkip, disableUserFeedback, false); + return 0; +} + +// acts similar to normal emulation update +DEFINE_LUA_FUNCTION(emu_emulateframe, "") +{ + if(FailVerifyAtFrameBoundary(L, "emu.emulateframe", 0,1)) return 0; - Update_Emulation_One(HWnd); - Prevent_Next_Frame_Skipping(); // so we don't skip a whole bunch of frames immediately after emulating many frames by this method - - worry(L,300); - return 0; + return StepEmulationAtSpeed(L, SPEEDMODE_NORMAL, false); } // acts as a fast-forward emulation update that still renders every frame -// and the user is unable to activate emulator commands during it -DEFINE_LUA_FUNCTION(gens_emulateframefastnoskipping, "") +DEFINE_LUA_FUNCTION(emu_emulateframefastnoskipping, "") { - if(FailVerifyAtFrameBoundary(L, "gens.emulateframefastnoskipping", 0,1)) + if(FailVerifyAtFrameBoundary(L, "emu.emulateframefastnoskipping", 0,1)) return 0; - Update_Emulation_One_Before(HWnd); - Update_Frame_Hook(); - Update_Emulation_After_Controlled(HWnd, true); - Prevent_Next_Frame_Skipping(); // so we don't skip a whole bunch of frames immediately after a bout of fast-forward frames - - worry(L,200); - return 0; + return StepEmulationAtSpeed(L, SPEEDMODE_NOTHROTTLE, false); } -// acts as a (very) fast-forward emulation update -// where the user is unable to activate emulator commands -DEFINE_LUA_FUNCTION(gens_emulateframefast, "") +// acts as a fast-forward emulation update +DEFINE_LUA_FUNCTION(emu_emulateframefast, "") { - if(FailVerifyAtFrameBoundary(L, "gens.emulateframefast", 0,1)) + if(FailVerifyAtFrameBoundary(L, "emu.emulateframefast", 0,1)) return 0; - disableVideoLatencyCompensationCount = VideoLatencyCompensation + 1; - - Update_Emulation_One_Before(HWnd); - - if(FrameCount%16 == 0) // skip rendering 15 out of 16 frames - { - // update once and render - Update_Frame_Hook(); - Update_Emulation_After_Controlled(HWnd, true); - } - else - { - // update once but skip rendering - Update_Frame_Fast_Hook(); - Update_Emulation_After_Controlled(HWnd, false); - } - - Prevent_Next_Frame_Skipping(); // so we don't skip a whole bunch of frames immediately AFTER a bout of fast-forward frames - - worry(L,150); - return 0; + return StepEmulationAtSpeed(L, SPEEDMODE_TURBO, false); } // acts as an extremely-fast-forward emulation update -// that also doesn't render any graphics or generate any sounds, -// and the user is unable to activate emulator commands during it. -// if you load a savestate after calling this function, -// it should leave no trace of having been called, -// so you can do things like generate future emulation states every frame -// while the user continues to see and hear normal emulation -DEFINE_LUA_FUNCTION(gens_emulateframeinvisible, "") +// that also doesn't render any graphics or generate any sounds +DEFINE_LUA_FUNCTION(emu_emulateframeinvisible, "") { - if(FailVerifyAtFrameBoundary(L, "gens.emulateframeinvisible", 0,1)) + if(FailVerifyAtFrameBoundary(L, "emu.emulateframeinvisible", 0,1)) return 0; - int oldDisableSound2 = disableSound2; - int oldDisableRamSearchUpdate = disableRamSearchUpdate; - disableSound2 = true; - disableRamSearchUpdate = true; - - Update_Emulation_One_Before_Minimal(); - Update_Frame_Fast(); - UpdateLagCount(); - - disableSound2 = oldDisableSound2; - disableRamSearchUpdate = oldDisableRamSearchUpdate; - - // disable video latency compensation for a few frames - // because it can get pretty slow if that's doing prediction updates every frame - // when the lua script is also doing prediction updates - disableVideoLatencyCompensationCount = VideoLatencyCompensation + 1; - - worry(L,100); - return 0; + return StepEmulationAtSpeed(L, SPEEDMODE_MAXIMUM, false); } -DEFINE_LUA_FUNCTION(gens_speedmode, "mode") +DEFINE_LUA_FUNCTION(emu_speedmode, "mode") { SpeedMode newSpeedMode = SPEEDMODE_NORMAL; if(lua_isnumber(L,1)) @@ -1400,106 +1460,72 @@ DEFINE_LUA_FUNCTION(gens_speedmode, "mode") return 0; } -// tells Gens to wait while the script is doing calculations -// can call this periodically instead of gens.frameadvance + +// tells the emulation to wait while the script is doing calculations +// can call this periodically instead of emu.frameadvance // note that the user can use hotkeys at this time -// (e.g. a savestate could possibly get loaded before gens.wait() returns) -DEFINE_LUA_FUNCTION(gens_wait, "") +// (e.g. a savestate could possibly get loaded before emu.wait() returns) +DEFINE_LUA_FUNCTION(emu_wait, "") { LuaContextInfo& info = GetCurrentInfo(); - - switch(info.speedMode) - { - default: - case SPEEDMODE_NORMAL: - Step_Gens_MainLoop(true, false); - break; - case SPEEDMODE_NOTHROTTLE: - case SPEEDMODE_TURBO: - case SPEEDMODE_MAXIMUM: - Step_Gens_MainLoop(Paused!=0, false); - break; - } - + StepEmulationOnce(false, false, -1, true, true); + dontworry(info); return 0; } -*/ -/* -DEFINE_LUA_FUNCTION(gens_frameadvance, "") + + +DEFINE_LUA_FUNCTION(emu_frameadvance, "") { - if(FailVerifyAtFrameBoundary(L, "gens.frameadvance", 0,1)) - return gens_wait(L); + if(FailVerifyAtFrameBoundary(L, "emu.frameadvance", 0,1)) + return emu_wait(L); - int uid = luaStateToUIDMap[L]; + int uid = luaStateToUIDMap[L->l_G->mainthread]; LuaContextInfo& info = GetCurrentInfo(); if(!info.ranFrameAdvance) { // otherwise we'll never see the first frame of GUI drawing if(info.speedMode != SPEEDMODE_MAXIMUM) - Show_Genesis_Screen(); + driver->USR_RefreshScreen(); info.ranFrameAdvance = true; } - switch(info.speedMode) - { - default: - case SPEEDMODE_NORMAL: - while(!Step_Gens_MainLoop(true, true) && !info.panic); - break; - case SPEEDMODE_NOTHROTTLE: - while(!Step_Gens_MainLoop(Paused!=0, false) && !info.panic); - if(!(FastForwardKeyDown && (GetActiveWindow()==HWnd || BackgroundInput))) - gens_emulateframefastnoskipping(L); - else - gens_emulateframefast(L); - break; - case SPEEDMODE_TURBO: - while(!Step_Gens_MainLoop(Paused!=0, false) && !info.panic); - gens_emulateframefast(L); - break; - case SPEEDMODE_MAXIMUM: - while(!Step_Gens_MainLoop(Paused!=0, false) && !info.panic); - gens_emulateframeinvisible(L); - break; - } - return 0; + return StepEmulationAtSpeed(L, info.speedMode, true); } -DEFINE_LUA_FUNCTION(gens_pause, "") +DEFINE_LUA_FUNCTION(emu_pause, "") { - LuaContextInfo& info = GetCurrentInfo(); + driver->EMU_PauseEmulation(true); - Paused = 1; - while(!Step_Gens_MainLoop(true, false) && !info.panic); + LuaContextInfo& info = GetCurrentInfo(); + StepEmulation(true, true, 0, true, true); // allow the user to not have to manually unpause - // after restarting a script that used gens.pause() + // after restarting a script that used emu.pause() if(info.panic) - Paused = 0; + driver->EMU_PauseEmulation(false); return 0; } -DEFINE_LUA_FUNCTION(gens_unpause, "") +DEFINE_LUA_FUNCTION(emu_unpause, "") { LuaContextInfo& info = GetCurrentInfo(); - - Paused = 0; + driver->EMU_PauseEmulation(false); return 0; } -DEFINE_LUA_FUNCTION(gens_redraw, "") +DEFINE_LUA_FUNCTION(emu_redraw, "") { - Show_Genesis_Screen(); + driver->USR_RefreshScreen(); worry(L,250); return 0; } -*/ + DEFINE_LUA_FUNCTION(memory_readbyte, "address") { @@ -1589,18 +1615,18 @@ DEFINE_LUA_FUNCTION(memory_readbyterange, "address,length") // put all the values into the (1-based) array for(int a = address, n = 1; n <= length; a++, n++) { -// if(IsHardwareAddressValid(a)) -// { + if(IsHardwareAddressValid(a)) + { unsigned char value = (unsigned char)(_MMU_read08(address) & 0xFF); lua_pushinteger(L, value); lua_rawseti(L, -2, n); -// } + } // else leave the value nil } return 1; } -/* + DEFINE_LUA_FUNCTION(memory_isvalid, "address") { int address = luaL_checkinteger(L,1); @@ -1608,7 +1634,7 @@ DEFINE_LUA_FUNCTION(memory_isvalid, "address") lua_pushboolean(L, IsHardwareAddressValid(address)); return 1; } -*/ + struct registerPointerMap { const char* registerName; @@ -1754,7 +1780,7 @@ DEFINE_LUA_FUNCTION(state_create, "[location]") int len = GENESIS_STATE_LENGTH; if (SegaCD_Started) len += SEGACD_LENGTH_EX; if (_32X_Started) len += G32X_LENGTH_EX; - if (!((Genesis_Started)||(SegaCD_Started)||(_32X_Started))) + if (!driver->EMU_HasEmulationStarted()) len += std::max(SEGACD_LENGTH_EX, G32X_LENGTH_EX); // allocate the in-memory/anonymous savestate @@ -1900,7 +1926,7 @@ DEFINE_LUA_FUNCTION(state_load, "location[,option]") // saveData.ImportRecords(luaSaveFile); // fclose(luaSaveFile); // -// int uid = luaStateToUIDMap[L]; +// int uid = luaStateToUIDMap[L->l_G->mainthread]; // LuaContextInfo& info = GetCurrentInfo(); // // lua_settop(L, 0); @@ -2199,8 +2225,8 @@ DEFINE_LUA_FUNCTION(gui_parsecolor, "color") DEFINE_LUA_FUNCTION(gui_text, "x,y,str[,color=\"white\"[,outline=\"black\"]]") { if(DeferGUIFuncIfNeeded(L)) - return 0; // we have to wait until later to call this function because gens hasn't emulated the next frame yet - // (the only way to avoid this deferring is to be in a gui.register or gens.registerafter callback) + return 0; // we have to wait until later to call this function because we haven't emulated the next frame yet + // (the only way to avoid this deferring is to be in a gui.register or emu.registerafter callback) int x = luaL_checkinteger(L,1) & 0xFFFF; int y = luaL_checkinteger(L,2) & 0xFFFF; @@ -2728,7 +2754,7 @@ DEFINE_LUA_FUNCTION(emu_openscript, "filename") // TODO /* -DEFINE_LUA_FUNCTION(gens_loadrom, "filename") +DEFINE_LUA_FUNCTION(emu_loadrom, "filename") { struct Temp { Temp() {EnableStopAllLuaScripts(false);} ~Temp() {EnableStopAllLuaScripts(true);}} dontStopScriptsHere; const char* filename = lua_isstring(L,1) ? lua_tostring(L,1) : NULL; @@ -2758,13 +2784,12 @@ DEFINE_LUA_FUNCTION(emu_lagged, "") } DEFINE_LUA_FUNCTION(emu_emulating, "") { - lua_pushboolean(L, romloaded); + lua_pushboolean(L, driver->EMU_HasEmulationStarted()); return 1; } DEFINE_LUA_FUNCTION(emu_atframeboundary, "") { - // TODO (actually this is a full implementation currently since registermemory callbacks are disabled) - lua_pushboolean(L, true); + lua_pushboolean(L, driver->EMU_IsAtFrameBoundary()); return 1; } DEFINE_LUA_FUNCTION(movie_getlength, "") @@ -2883,7 +2908,7 @@ DEFINE_LUA_FUNCTION(movie_close, "") DEFINE_LUA_FUNCTION(sound_clear, "") { - if(SPU_user) SPU_user->ShutUp(); + Clear_Sound_Buffer(); return 0; } @@ -3066,6 +3091,8 @@ DEFINE_LUA_FUNCTION(input_getcurrentinputstatus, "") lua_pushinteger(L, y); lua_setfield(L, -2, "ymouse"); } + + worry(L,10); #else // NYI (well, return an empty table) #endif @@ -3328,8 +3355,13 @@ static const struct luaL_reg aggcustom [] = // Displays the given text on the screen, using the same font and techniques as the // main HUD. // -// TODO: this incomplete... it should support color and outline color and a much smaller font size -static int gui_text(lua_State *L) { +// TODO: this is incomplete... it should support color and outline color and a much smaller font size +static int gui_text(lua_State *L) +{ + if(DeferGUIFuncIfNeeded(L)) + return 0; // we have to wait until later to call this function because we haven't emulated the next frame yet + // (the only way to avoid this deferring is to be in a gui.register or emu.registerafter callback) + const char *msg; int x, y; @@ -3378,17 +3410,16 @@ static const struct luaL_reg styluslib [] = static const struct luaL_reg emulib [] = { - // TODO! -// {"frameadvance", emu_frameadvance}, -// {"speedmode", emu_speedmode}, -// {"wait", emu_wait}, -// {"pause", emu_pause}, -// {"unpause", emu_unpause}, -// {"emulateframe", emu_emulateframe}, - //{"emulateframefastnoskipping", emu_emulateframefastnoskipping}, // removed from library because probably nobody would notice the difference from emu_emulateframe -// {"emulateframefast", emu_emulateframefast}, -// {"emulateframeinvisible", emu_emulateframeinvisible}, -// {"redraw", emu_redraw}, + {"frameadvance", emu_frameadvance}, + {"speedmode", emu_speedmode}, + {"wait", emu_wait}, + {"pause", emu_pause}, + {"unpause", emu_unpause}, + {"emulateframe", emu_emulateframe}, + {"emulateframefastnoskipping", emu_emulateframefastnoskipping}, + {"emulateframefast", emu_emulateframefast}, + {"emulateframeinvisible", emu_emulateframeinvisible}, + {"redraw", emu_redraw}, {"framecount", emu_getframecount}, {"lagcount", emu_getlagcount}, {"lagged", emu_lagged}, @@ -3396,7 +3427,7 @@ static const struct luaL_reg emulib [] = {"atframeboundary", emu_atframeboundary}, {"registerbefore", emu_registerbefore}, {"registerafter", emu_registerafter}, -// {"registerstart", emu_registerstart}, + {"registerstart", emu_registerstart}, {"registerexit", emu_registerexit}, {"persistglobalvariables", emu_persistglobalvariables}, {"message", emu_message}, @@ -3421,7 +3452,7 @@ static const struct luaL_reg guilib [] = {"parsecolor", gui_parsecolor}, // {"gdscreenshot", gui_gdscreenshot}, // {"gdoverlay", gui_gdoverlay}, -// {"redraw", emu_redraw}, // some people might think of this as more of a GUI function + {"redraw", emu_redraw}, // some people might think of this as more of a GUI function // alternative names {"drawtext", gui_text}, // {"drawbox", gui_box}, @@ -3459,7 +3490,7 @@ static const struct luaL_reg memorylib [] = {"writebyte", memory_writebyte}, {"writeword", memory_writeword}, {"writedword", memory_writedword}, -// {"isvalid", memory_isvalid}, + {"isvalid", memory_isvalid}, {"getregister", memory_getregister}, {"setregister", memory_setregister}, // alternate naming scheme for word and double-word and unsigned @@ -3873,6 +3904,8 @@ void RunLuaScriptFile(int uid, const char* filenameCStr) lua_setfield(L, LUA_REGISTRYINDEX, deferredGUIIDString); lua_newtable(L); lua_setfield(L, LUA_REGISTRYINDEX, deferredJoySetIDString); + lua_newtable(L); + lua_setfield(L, LUA_REGISTRYINDEX, refStashString); info.started = true; RefreshScriptStartedStatus(); @@ -3902,7 +3935,7 @@ void RunLuaScriptFile(int uid, const char* filenameCStr) } else { -// Show_Genesis_Screen(); + driver->USR_RefreshScreen(); StopScriptIfFinished(uid, true); } } while(info.restart); @@ -3941,7 +3974,7 @@ void StopScriptIfFinished(int uid, bool justReturned) if(info.print) info.print(uid, "script returned but is still running registered functions\r\n"); else - fprintf(stderr, "%s\n", "script returned but is still running registered functions"); + fprintf(stdout, "%s\n", "script returned but is still running registered functions"); } } else @@ -3949,7 +3982,7 @@ void StopScriptIfFinished(int uid, bool justReturned) if(info.print) info.print(uid, "script finished running\r\n"); else - fprintf(stderr, "%s\n", "script finished running"); + fprintf(stdout, "%s\n", "script finished running"); StopLuaScript(uid); } @@ -4050,7 +4083,7 @@ void CallExitFunction(int uid) struct Scope { ~Scope(){ infoStack.erase(infoStack.begin()); } } scope; #endif - lua_settop(L, 0); + //lua_settop(L, 0); lua_getfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFOREEXIT]); int errorcode = 0; @@ -4371,6 +4404,19 @@ void CallRegisteredLuaMemHook(unsigned int address, int size, unsigned int value } */ +bool AnyLuaActive() +{ + std::map::iterator iter = luaContextInfo.begin(); + std::map::iterator end = luaContextInfo.end(); + while(iter != end) + { + LuaContextInfo& info = *iter->second; + if(info.started) + return true; + ++iter; + } + return false; +} void CallRegisteredLuaFunctions(LuaCallID calltype) { @@ -4401,7 +4447,7 @@ void CallRegisteredLuaFunctions(LuaCallID calltype) CallDeferredFunctions(L, deferredJoySetIDString); } - lua_settop(L, 0); + int top = lua_gettop(L); lua_getfield(L, LUA_REGISTRYINDEX, idstring); if (lua_isfunction(L, -1)) @@ -4421,6 +4467,7 @@ void CallRegisteredLuaFunctions(LuaCallID calltype) } info.guiFuncsNeedDeferring = true; + lua_settop(L, top); } ++iter; @@ -4445,7 +4492,7 @@ void CallRegisteredLuaSaveFunctions(int savestateNumber, LuaSaveData& saveData) struct Scope { ~Scope(){ infoStack.erase(infoStack.begin()); } } scope; #endif - lua_settop(L, 0); + int top = lua_gettop(L); lua_getfield(L, LUA_REGISTRYINDEX, idstring); if (lua_isfunction(L, -1)) @@ -4465,6 +4512,7 @@ void CallRegisteredLuaSaveFunctions(int savestateNumber, LuaSaveData& saveData) { lua_pop(L, 1); } + lua_settop(L, top); } ++iter; @@ -4490,7 +4538,7 @@ void CallRegisteredLuaLoadFunctions(int savestateNumber, const LuaSaveData& save struct Scope { ~Scope(){ infoStack.erase(infoStack.begin()); } } scope; #endif - lua_settop(L, 0); + int top = lua_gettop(L); lua_getfield(L, LUA_REGISTRYINDEX, idstring); if (lua_isfunction(L, -1)) @@ -4532,6 +4580,7 @@ void CallRegisteredLuaLoadFunctions(int savestateNumber, const LuaSaveData& save { lua_pop(L, 1); } + lua_settop(L, top); } ++iter; @@ -4636,7 +4685,7 @@ static void LuaStackToBinaryConverter(lua_State* L, int i, std::vectorl_G->mainthread], errmsg); } else { @@ -4810,7 +4859,7 @@ void BinaryToLuaStackConverter(lua_State* L, const unsigned char*& data, unsigne sprintf(errmsg, "values of type \"%s\" are not allowed to be loaded into registered load functions. The save state's Lua save data file might be corrupted.\r\n", lua_typename(L,type)); else sprintf(errmsg, "The save state's Lua save data file seems to be corrupted.\r\n"); - info.print(luaStateToUIDMap[L], errmsg); + info.print(luaStateToUIDMap[L->l_G->mainthread], errmsg); } else { diff --git a/desmume/src/lua-engine.h b/desmume/src/lua-engine.h index 31846b6c3..cfddd827a 100644 --- a/desmume/src/lua-engine.h +++ b/desmume/src/lua-engine.h @@ -7,6 +7,7 @@ void RunLuaScriptFile(int uid, const char* filename); void StopLuaScript(int uid); void RequestAbortLuaScript(int uid, const char* message = 0); void CloseLuaContext(int uid); +bool AnyLuaActive(); enum LuaCallID { diff --git a/desmume/src/windows/DeSmuME_2005.vcproj b/desmume/src/windows/DeSmuME_2005.vcproj index 3377c1594..e2b909543 100644 --- a/desmume/src/windows/DeSmuME_2005.vcproj +++ b/desmume/src/windows/DeSmuME_2005.vcproj @@ -78,7 +78,7 @@ /> Default - lua-5.1.4-x86.lib;glib-2.20.1-x86.lib;vfw32.lib;winmm.lib;opengl32.lib;glu32.lib;ws2_32.lib;user32.lib;gdi32.lib;directx\dxguid.lib;shell32.lib;comdlg32.lib;directx\dxerr8.lib;directx\dsound.lib;directx\dinput8.lib;directx\ddraw.lib;zlib-2005-x32.lib;zziplib-2005-x32.lib;shlwapi.lib;winpcap\wpcap.lib;7zip.lib;%(AdditionalDependencies) + lua-5.1.4-x86d.lib;glib-2.20.1-x86.lib;vfw32.lib;winmm.lib;opengl32.lib;glu32.lib;ws2_32.lib;user32.lib;gdi32.lib;directx\dxguid.lib;shell32.lib;comdlg32.lib;directx\dxerr8.lib;directx\dsound.lib;directx\dinput8.lib;directx\ddraw.lib;zlib-2005-x32.lib;zziplib-2005-x32.lib;shlwapi.lib;winpcap\wpcap.lib;7zip.lib;%(AdditionalDependencies) .\zlib123;.\zziplib;glib-2.20.1\lib;lua\lib;.\7z;.\agg;%(AdditionalLibraryDirectories) wpcap.dll;%(DelayLoadDLLs) true diff --git a/desmume/src/windows/lua/lib/lua-5.1.4-x86.lib b/desmume/src/windows/lua/lib/lua-5.1.4-x86.lib index 8e5eae2f3..ba47bc663 100644 Binary files a/desmume/src/windows/lua/lib/lua-5.1.4-x86.lib and b/desmume/src/windows/lua/lib/lua-5.1.4-x86.lib differ diff --git a/desmume/src/windows/lua/lib/lua-5.1.4-x86d.lib b/desmume/src/windows/lua/lib/lua-5.1.4-x86d.lib new file mode 100644 index 000000000..3011cb9c1 Binary files /dev/null and b/desmume/src/windows/lua/lib/lua-5.1.4-x86d.lib differ diff --git a/desmume/src/windows/lua/lua-5.1.4/src/luaconf.h b/desmume/src/windows/lua/lua-5.1.4/src/luaconf.h index 0ccb4fe7d..c90f8a91d 100644 --- a/desmume/src/windows/lua/lua-5.1.4/src/luaconf.h +++ b/desmume/src/windows/lua/lua-5.1.4/src/luaconf.h @@ -560,13 +560,17 @@ (defined(__i386) || defined (_M_IX86) || defined(__i386__)) /* On a Microsoft compiler, use assembler */ -#if defined(_MSC_VER) +/*#if defined(_MSC_VER)*/ +#if 0 /* actually, don't. it doesn't work right. */ #define lua_number2int(i,d) __asm fld d __asm fistp i #define lua_number2integer(i,n) lua_number2int(i, n) /* the next trick should work on any Pentium, but sometimes clashes with a DirectX idiosyncrasy */ +/* note: we could specify D3DCREATE_FPU_PRESERVE to guarantee that won't happen. + or if we don't even use d3d which is currently true, it's definitely ok. + the above assembler version is quite broken, anyway, can't handle >= 0xFF000000. */ #else union luai_Cast { double l_d; long l_l; }; diff --git a/desmume/src/windows/lua/vs8/lua.vcproj b/desmume/src/windows/lua/vs8/lua.vcproj index e8492b8f5..b593d86a7 100644 --- a/desmume/src/windows/lua/vs8/lua.vcproj +++ b/desmume/src/windows/lua/vs8/lua.vcproj @@ -43,15 +43,16 @@ getHWnd(); GetClientRect(hwnd,&r); - SetCapture(hwnd); int defwidth = video.width, defheight = (video.height+video.screengap); int winwidth = (r.right-r.left), winheight = (r.bottom-r.top); @@ -901,6 +903,19 @@ volatile int newestDisplayBuffer=-2; GMutex *display_mutex = NULL; GThread *display_thread = NULL; volatile bool display_die = false; +HANDLE display_wakeup_event = INVALID_HANDLE_VALUE; + +int displayPostponeType = 0; +DWORD displayPostponeUntil = ~0; +bool displayNoPostponeNext = false; + +DWORD display_invoke_argument = 0; +void (*display_invoke_function)(DWORD) = NULL; +HANDLE display_invoke_ready_event = INVALID_HANDLE_VALUE; +HANDLE display_invoke_done_event = INVALID_HANDLE_VALUE; +DWORD display_invoke_timeout = 500; +CRITICAL_SECTION display_invoke_handler_cs; + static void DoDisplay_DrawHud() { @@ -914,6 +929,10 @@ static void DoDisplay(bool firstTime) { Lock lock (win_backbuffer_sync); + if(displayPostponeType && !displayNoPostponeNext && (displayPostponeType < 0 || timeGetTime() < displayPostponeUntil)) + return; + displayNoPostponeNext = false; + //convert pixel format to 32bpp for compositing //why do we do this over and over? well, we are compositing to //filteredbuffer32bpp, and it needs to get refreshed each frame.. @@ -934,6 +953,22 @@ static void DoDisplay(bool firstTime) } } + if(AnyLuaActive()) + { + if(g_thread_self() == display_thread) + { + ResetEvent(display_invoke_ready_event); + display_invoke_argument = LUACALL_AFTEREMULATIONGUI; + display_invoke_function = (void(*)(DWORD))CallRegisteredLuaFunctions; + SignalObjectAndWait(display_invoke_ready_event, display_invoke_done_event, display_invoke_timeout, FALSE); + display_invoke_function = NULL; + } + else + { + CallRegisteredLuaFunctions(LUACALL_AFTEREMULATIONGUI); + } + } + //apply user's filter video.filter(); @@ -979,20 +1014,20 @@ void displayThread(void*) for(;;) { if(display_die) return; displayProc(); - Sleep(10); //don't be greedy and use a whole cpu core, but leave room for 60fps + //Sleep(10); //don't be greedy and use a whole cpu core, but leave room for 60fps + WaitForSingleObject(display_wakeup_event, 10); // same as sleep but lets something wake us up early } } void KillDisplay() { display_die = true; + SetEvent(display_wakeup_event); g_thread_join(display_thread); } void Display() { - CallRegisteredLuaFunctions(LUACALL_AFTEREMULATIONGUI); - if(CommonSettings.single_core) { video.srcBuffer = (u8*)GPU_screen; @@ -1022,6 +1057,7 @@ void Display() } + void CheckMessages() { MSG msg; @@ -1053,24 +1089,239 @@ void CheckMessages() } } +static void _ServiceDisplayThreadInvocation() +{ + Lock lock (display_invoke_handler_cs); + DWORD res = WaitForSingleObject(display_invoke_ready_event, display_invoke_timeout); + if(res != WAIT_ABANDONED && display_invoke_function) + display_invoke_function(display_invoke_argument); + display_invoke_function = NULL; + SetEvent(display_invoke_done_event); +} +static FORCEINLINE void ServiceDisplayThreadInvocations() +{ + if(display_invoke_function) + _ServiceDisplayThreadInvocation(); +} + + +static struct MainLoopData +{ + u64 freq; + int framestoskip; + int framesskipped; + int skipnextframe; + u64 lastticks; + u64 curticks; + u64 diffticks; + u32 framecount; + u64 onesecondticks; + u64 fpsticks; + HWND hwnd; + int fps; + int fps3d; + int fpsframecount; +} mainLoopData = {0}; + + +static void StepRunLoop_Core() +{ + input_acquire(); + NDS_beginProcessingInput(); + { + input_process(); + FCEUMOV_HandlePlayback(); + CallRegisteredLuaFunctions(LUACALL_BEFOREEMULATION); + } + NDS_endProcessingInput(); + FCEUMOV_HandleRecording(); + + inFrameBoundary = false; + { + Lock lock; + NDS_exec(); + win_sound_samplecounter = 735; + } + inFrameBoundary = true; + DRV_AviVideoUpdate((u16*)GPU_screen); + + extern bool rewinding; + + if (staterewindingenabled) { + + if(rewinding) + dorewind(); + else + rewindsave(); + } + + CallRegisteredLuaFunctions(LUACALL_AFTEREMULATION); + ServiceDisplayThreadInvocations(); +} + +static void StepRunLoop_Paused() +{ + paused = TRUE; + Sleep(100); + + // periodically update single-core OSD when paused and in the foreground + if(CommonSettings.single_core && GetActiveWindow() == mainLoopData.hwnd) + { + video.srcBuffer = (u8*)GPU_screen; + DoDisplay(true); + } + + ServiceDisplayThreadInvocations(); +} + +static void StepRunLoop_User() +{ + Hud.fps = mainLoopData.fps; + Hud.fps3d = mainLoopData.fps3d; + + Display(); + + gfx3d.frameCtrRaw++; + if(gfx3d.frameCtrRaw == 60) { + mainLoopData.fps3d = gfx3d.frameCtr; + gfx3d.frameCtrRaw = 0; + gfx3d.frameCtr = 0; + } + + + // TODO: make that thing properly threaded + static DWORD tools_time_last = 0; + DWORD time_now = timeGetTime(); + if((time_now - tools_time_last) >= 50) + { + if(MemView_IsOpened(ARMCPU_ARM9)) MemView_Refresh(ARMCPU_ARM9); + if(MemView_IsOpened(ARMCPU_ARM7)) MemView_Refresh(ARMCPU_ARM7); + // if(IORegView_IsOpened()) IORegView_Refresh(); + + tools_time_last = time_now; + } + if(SoundView_IsOpened()) SoundView_Refresh(); + + Update_RAM_Watch(); + Update_RAM_Search(); + + mainLoopData.fpsframecount++; + QueryPerformanceCounter((LARGE_INTEGER *)&mainLoopData.curticks); + bool oneSecond = mainLoopData.curticks >= mainLoopData.fpsticks + mainLoopData.freq; + if(oneSecond) // TODO: print fps on screen in DDraw + { + mainLoopData.fps = mainLoopData.fpsframecount; + mainLoopData.fpsframecount = 0; + QueryPerformanceCounter((LARGE_INTEGER *)&mainLoopData.fpsticks); + } + + if(nds.idleFrameCounter==0 || oneSecond) + { + //calculate a 16 frame arm9 load average + int load = 0; + for(int i=0;i<16;i++) + load = load/8 + nds.runCycleCollector[(i+nds.idleFrameCounter)&15]*7/8; + load = std::min(100,std::max(0,(int)(load*100/1120380))); + //sprintf(txt,"(%02d%%) %s", load, DESMUME_NAME_AND_VERSION); + SetWindowText(mainLoopData.hwnd, DESMUME_NAME_AND_VERSION); + } +} + +static void StepRunLoop_Throttle(bool allowSleep = true, int forceFrameSkip = -1) +{ + int skipRate = (forceFrameSkip < 0) ? frameskiprate : forceFrameSkip; + int ffSkipRate = (forceFrameSkip < 0) ? 9 : forceFrameSkip; + + if(lastskiprate != skipRate) + { + lastskiprate = skipRate; + mainLoopData.framestoskip = 0; // otherwise switches to lower frameskip rates will lag behind + } + + if(!mainLoopData.skipnextframe || forceFrameSkip == 0) + { + mainLoopData.framesskipped = 0; + + if (mainLoopData.framestoskip > 0) + mainLoopData.skipnextframe = 1; + } + else + { + mainLoopData.framestoskip--; + + if (mainLoopData.framestoskip < 1) + mainLoopData.skipnextframe = 0; + else + mainLoopData.skipnextframe = 1; + + mainLoopData.framesskipped++; + + NDS_SkipNextFrame(); + } + + if(FastForward) + { + if(mainLoopData.framesskipped < ffSkipRate) + { + mainLoopData.skipnextframe = 1; + mainLoopData.framestoskip = 1; + } + if (mainLoopData.framestoskip < 1) + mainLoopData.framestoskip += ffSkipRate; + } + else if((autoframeskipenab || FrameLimit) && allowSleep) + { + while(SpeedThrottle()) + { + } + } + + if (autoframeskipenab) + { + mainLoopData.framecount++; + + if (mainLoopData.framecount > 60) + { + mainLoopData.framecount = 1; + mainLoopData.onesecondticks = 0; + } + + QueryPerformanceCounter((LARGE_INTEGER *)&mainLoopData.curticks); + mainLoopData.diffticks = mainLoopData.curticks - mainLoopData.lastticks; + + if(ThrottleIsBehind() && (mainLoopData.framesskipped < ffSkipRate)) + { + mainLoopData.skipnextframe = 1; + mainLoopData.framestoskip = 1; + } + + mainLoopData.onesecondticks += mainLoopData.diffticks; + mainLoopData.lastticks = mainLoopData.curticks; + } + else + { + if (mainLoopData.framestoskip < 1) + mainLoopData.framestoskip += skipRate; + } + if (frameAdvance && allowSleep) + { + frameAdvance = false; + emu_halt(); + SPU_Pause(1); + } + ServiceDisplayThreadInvocations(); +} + DWORD WINAPI run() { u32 cycles = 0; - int wait=0; - u64 freq; - u64 OneFrameTime; - int framestoskip=0; - int framesskipped=0; - int skipnextframe=0; - u64 lastticks=0; - u64 curticks=0; - u64 diffticks=0; - u32 framecount=0; - u64 onesecondticks=0; - int fps=0; - int fpsframecount=0; - u64 fpsticks=0; - HWND hwnd = MainWindow->getHWnd(); + int wait = 0; + u64 OneFrameTime = ~0; + + HWND hwnd = MainWindow->getHWnd(); + mainLoopData.hwnd = hwnd; + + inFrameBoundary = true; InitSpeedThrottle(); @@ -1094,173 +1345,21 @@ DWORD WINAPI run() return -1; } - QueryPerformanceFrequency((LARGE_INTEGER *)&freq); - QueryPerformanceCounter((LARGE_INTEGER *)&lastticks); - OneFrameTime = freq / 60; + QueryPerformanceFrequency((LARGE_INTEGER *)&mainLoopData.freq); + QueryPerformanceCounter((LARGE_INTEGER *)&mainLoopData.lastticks); + OneFrameTime = mainLoopData.freq / 60; while(!finished) { while(execute) { - // the order of these function calls is very important - input_acquire(); - NDS_beginProcessingInput(); - { - input_process(); - FCEUMOV_HandlePlayback(); - CallRegisteredLuaFunctions(LUACALL_BEFOREEMULATION); - } - NDS_endProcessingInput(); - FCEUMOV_HandleRecording(); - - { - Lock lock; - NDS_exec(); - win_sound_samplecounter = 735; - } - DRV_AviVideoUpdate((u16*)GPU_screen); - - extern bool rewinding; - - if (staterewindingenabled) { - - if(rewinding) - dorewind(); - else - rewindsave(); - } - - CallRegisteredLuaFunctions(LUACALL_AFTEREMULATION); - - static int fps3d = 0; - - Hud.fps = fps; - Hud.fps3d = fps3d; - - Display(); - - gfx3d.frameCtrRaw++; - if(gfx3d.frameCtrRaw == 60) { - fps3d = gfx3d.frameCtr; - gfx3d.frameCtrRaw = 0; - gfx3d.frameCtr = 0; - } - - - // TODO: make that thing properly threaded - static DWORD tools_time_last = 0; - DWORD time_now = timeGetTime(); - if((time_now - tools_time_last) >= 50) - { - if(MemView_IsOpened(ARMCPU_ARM9)) MemView_Refresh(ARMCPU_ARM9); - if(MemView_IsOpened(ARMCPU_ARM7)) MemView_Refresh(ARMCPU_ARM7); - // if(IORegView_IsOpened()) IORegView_Refresh(); - - tools_time_last = time_now; - } - if(SoundView_IsOpened()) SoundView_Refresh(); - - Update_RAM_Watch(); - Update_RAM_Search(); - - fpsframecount++; - QueryPerformanceCounter((LARGE_INTEGER *)&curticks); - bool oneSecond = curticks >= fpsticks + freq; - if(oneSecond) // TODO: print fps on screen in DDraw - { - fps = fpsframecount; - fpsframecount = 0; - QueryPerformanceCounter((LARGE_INTEGER *)&fpsticks); - } - - if(nds.idleFrameCounter==0 || oneSecond) - { - //calculate a 16 frame arm9 load average - int load = 0; - for(int i=0;i<16;i++) - load = load/8 + nds.runCycleCollector[(i+nds.idleFrameCounter)&15]*7/8; - load = std::min(100,std::max(0,(int)(load*100/1120380))); - //sprintf(txt,"(%02d%%) %s", load, DESMUME_NAME_AND_VERSION); - SetWindowText(hwnd, DESMUME_NAME_AND_VERSION); - } - - if(!skipnextframe) - { - framesskipped = 0; - - if (framestoskip > 0) - skipnextframe = 1; - } - else - { - framestoskip--; - - if (framestoskip < 1) - skipnextframe = 0; - else - skipnextframe = 1; - - framesskipped++; - - NDS_SkipNextFrame(); - } - - if(FastForward) { - if(framesskipped < 9) - { - skipnextframe = 1; - framestoskip = 1; - } - if (framestoskip < 1) - framestoskip += 9; - } - - else if(autoframeskipenab || FrameLimit) - while(SpeedThrottle()) - { - } - - if (autoframeskipenab) - { - framecount++; - - if (framecount > 60) - { - framecount = 1; - onesecondticks = 0; - } - - QueryPerformanceCounter((LARGE_INTEGER *)&curticks); - diffticks = curticks-lastticks; - - if(ThrottleIsBehind() && (framesskipped < 9)) - { - skipnextframe = 1; - framestoskip = 1; - } - - onesecondticks += diffticks; - lastticks = curticks; - } - else - { - if (framestoskip < 1) - framestoskip += frameskiprate; - } - if (frameAdvance) - { - frameAdvance = false; - emu_halt(); - SPU_Pause(1); - } -// DisplayMessage(); - CheckMessages(); - + StepRunLoop_Core(); + StepRunLoop_User(); + StepRunLoop_Throttle(); + CheckMessages(); } - - paused = TRUE; + StepRunLoop_Paused(); CheckMessages(); - Sleep(100); } return 1; @@ -1530,6 +1629,106 @@ class WinDriver : public BaseDriver virtual void AVI_SoundUpdate(void* soundData, int soundLen) { ::DRV_AviSoundUpdate(soundData, soundLen); } + + virtual void USR_SetDisplayPostpone(int milliseconds, bool drawNextFrame) + { + displayPostponeType = milliseconds; + displayPostponeUntil = timeGetTime() + milliseconds; + displayNoPostponeNext |= drawNextFrame; + } + + virtual void USR_RefreshScreen() + { + // postpone updates except this one for at least 0.5 seconds + displayNoPostponeNext = true; + if(displayPostponeType >= 0) + { + static const int minPostponeTime = 500; + DWORD time_now = timeGetTime(); + if(displayPostponeType == 0 || (int)displayPostponeUntil - (int)time_now < minPostponeTime) + displayPostponeUntil = time_now + minPostponeTime; + if(displayPostponeType == 0) + displayPostponeType = minPostponeTime; + } + + Display(); + + // in multi-core mode now the display thread will probably + // wait for an invocation in this thread to happen, + // so handle that ASAP + if(!CommonSettings.single_core) + { + ResetEvent(display_invoke_ready_event); + SetEvent(display_wakeup_event); + if(AnyLuaActive()) + _ServiceDisplayThreadInvocation(); + } + } + + virtual void EMU_PauseEmulation(bool pause) + { + if(pause) + { + void Pause(); Pause(); + } + else + { + void Unpause(); Unpause(); + } + } + + virtual bool EMU_IsEmulationPaused() + { + return emu_paused; + } + + virtual bool EMU_IsFastForwarding() + { + return FastForward; + } + + virtual bool EMU_HasEmulationStarted() + { + return romloaded; + } + + virtual bool EMU_IsAtFrameBoundary() + { + return inFrameBoundary; + } + + virtual eStepEmulationResult EMU_StepMainLoop(bool allowSleep, bool allowPause, int frameSkip, bool disableUser, bool disableCore) + { + // this bit is here to handle calls through LUACALL_BEFOREEMULATION and in case Lua errors out while we're processing input + struct Scope{ bool m_on; + Scope() : m_on(NDS_isProcessingUserInput()) { if(m_on) NDS_suspendProcessingInput(true); } + ~Scope() { if(m_on || NDS_isProcessingUserInput()) NDS_suspendProcessingInput(false); } + } scope; + + ServiceDisplayThreadInvocations(); + + CheckMessages(); + + if(!romloaded) + return ESTEP_DONE; + + if(!execute && allowPause) + { + StepRunLoop_Paused(); + return ESTEP_CALL_AGAIN; + } + + if(!disableCore) + { + StepRunLoop_Throttle(allowSleep, frameSkip); + StepRunLoop_Core(); + } + + if(!disableUser) + StepRunLoop_User(); + + return ESTEP_DONE; + } }; std::string GetPrivateProfileStdString(LPCSTR lpAppName,LPCSTR lpKeyName,LPCSTR lpDefault) @@ -1551,6 +1750,10 @@ int _main() InitializeCriticalSection(&win_execute_sync); InitializeCriticalSection(&win_backbuffer_sync); + InitializeCriticalSection(&display_invoke_handler_cs); + display_invoke_ready_event = CreateEvent(NULL, TRUE, FALSE, NULL); + display_invoke_done_event = CreateEvent(NULL, FALSE, FALSE, NULL); + display_wakeup_event = CreateEvent(NULL, FALSE, FALSE, NULL); #ifdef GDB_STUB gdbstub_handle_t arm9_gdb_stub; @@ -1566,6 +1769,17 @@ int _main() oglrender_init = windows_opengl_init; + //try and detect this for users who don't specify it on the commandline + //(can't say I really blame them) + //this helps give a substantial speedup for singlecore users + SYSTEM_INFO systemInfo; + GetSystemInfo(&systemInfo); + if(systemInfo.dwNumberOfProcessors==1) + CommonSettings.single_core = true; + else + CommonSettings.single_core = false; + + char text[80]; GetINIPath(); @@ -1651,14 +1865,6 @@ int _main() return 1; } - //try and detect this for users who didn't specify it on the commandline - //(can't say I really blame them) - //this helps give a substantial speedup for singlecore users - SYSTEM_INFO systemInfo; - GetSystemInfo(&systemInfo); - if(systemInfo.dwNumberOfProcessors==1) - CommonSettings.single_core = true; - Desmume_InitOnce(); //in case this isnt actually a singlecore system, but the user requested it @@ -2540,7 +2746,7 @@ int HandleKeyMessage(WPARAM wParam, LPARAM lParam, int modifiers) return 1; } -static void Unpause() +void Unpause() { lastPauseFromLostFocus = FALSE; if (emu_paused) NDS_UnPause(); @@ -2562,25 +2768,32 @@ void FrameAdvance(bool state) if(!romloaded) return; if(state) { - if(first) { + if(first) { + // frame advance button newly pressed + first = false; + if(!emu_paused) { + // if not paused, pause immediately and don't advance yet + Pause(); + } else { + // if already paused, execute for 1 frame execute = TRUE; - frameAdvance=true; - first=false; + frameAdvance = true; } - else { - execute = TRUE; - } - } - else { - first = true; - if(frameAdvance) - {} - else - { - emu_halt(); - SPU_Pause(1); - emu_paused = 1; + } else { + // frame advance button still held down, + // start or continue executing at normal speed + execute = TRUE; + frameAdvance = false; } + } else { + // frame advance button released + first = true; + frameAdvance = false; + + // pause immediately + emu_halt(); + SPU_Pause(1); + emu_paused = 1; } } @@ -2979,8 +3192,7 @@ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM break; case WM_KEYDOWN: - if(paused) - input_acquire(); + input_acquire(); if(wParam != VK_PAUSE) break; case WM_SYSKEYDOWN: @@ -2992,8 +3204,7 @@ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM break; } case WM_KEYUP: - if(paused) - input_acquire(); + input_acquire(); if(wParam != VK_PAUSE) break; case WM_SYSKEYUP: @@ -3033,7 +3244,18 @@ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM hdc = BeginPaint(hwnd, &ps); - Display(); + if(!romloaded) + { + Display(); + } + else + { + if(CommonSettings.single_core) + { + video.srcBuffer = (u8*)GPU_screen; + DoDisplay(true); + } + } EndPaint(hwnd, &ps); } @@ -3088,6 +3310,8 @@ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM case WM_LBUTTONDBLCLK: if (wParam & MK_LBUTTON) { + SetCapture(hwnd); + s32 x = (s32)((s16)LOWORD(lParam)); s32 y = (s32)((s16)HIWORD(lParam));