ported memory hook codes from gens, then re-implemented memory.registerwrite, and add memory.registerexec; FIXME: CallRegisteredLuaMemHook in nsf.c, I couldn't get how it should be; there might be other issues possibly
This commit is contained in:
parent
d7b02d9549
commit
3ab41b359c
|
@ -325,7 +325,7 @@ static DECLFW(BRAML)
|
|||
{
|
||||
RAM[A]=V;
|
||||
#ifdef _S9XLUA_H
|
||||
FCEU_LuaWriteInform();
|
||||
CallRegisteredLuaMemHook(A, 1, V, LUAMEMHOOK_WRITE);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -333,7 +333,7 @@ static DECLFW(BRAMH)
|
|||
{
|
||||
RAM[A&0x7FF]=V;
|
||||
#ifdef _S9XLUA_H
|
||||
FCEU_LuaWriteInform();
|
||||
CallRegisteredLuaMemHook(A&0x7FF, 1, V, LUAMEMHOOK_WRITE);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,19 @@ enum LuaCallID
|
|||
};
|
||||
extern void CallRegisteredLuaFunctions(LuaCallID calltype);
|
||||
|
||||
enum LuaMemHookType
|
||||
{
|
||||
LUAMEMHOOK_WRITE,
|
||||
LUAMEMHOOK_READ,
|
||||
LUAMEMHOOK_EXEC,
|
||||
LUAMEMHOOK_WRITE_SUB,
|
||||
LUAMEMHOOK_READ_SUB,
|
||||
LUAMEMHOOK_EXEC_SUB,
|
||||
|
||||
LUAMEMHOOK_COUNT
|
||||
};
|
||||
void CallRegisteredLuaMemHook(unsigned int address, int size, unsigned int value, LuaMemHookType hookType);
|
||||
|
||||
// Just forward function declarations
|
||||
|
||||
//void FCEU_LuaWrite(uint32 addr);
|
||||
|
@ -33,7 +46,7 @@ char *FCEU_GetFreezeFilename(int slot);
|
|||
// Call this before writing into a buffer passed to FCEU_CheatAddRAM().
|
||||
// (That way, Lua-based memwatch will work as expected for somebody
|
||||
// used to FCEU's memwatch.)
|
||||
void FCEU_LuaWriteInform();
|
||||
//void FCEU_LuaWriteInform(); // DEADBEEF
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
@ -4,6 +4,11 @@
|
|||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <zlib.h>
|
||||
#include <assert.h>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef __linux
|
||||
#include <unistd.h>
|
||||
|
@ -96,8 +101,6 @@ static int skipRerecords = FALSE;
|
|||
|
||||
// Used by the registry to find our functions
|
||||
static const char *frameAdvanceThread = "FCEU.FrameAdvance";
|
||||
static const char *memoryWatchTable = "FCEU.Memory";
|
||||
static const char *memoryValueTable = "FCEU.MemValues";
|
||||
static const char *guiCallbackTable = "FCEU.GUI";
|
||||
|
||||
// True if there's a thread waiting to run after a run of frame-advance.
|
||||
|
@ -136,6 +139,8 @@ enum
|
|||
// over time. The script gets knifed once this reaches zero.
|
||||
static int numTries;
|
||||
|
||||
// number of registered memory functions (1 per hooked byte)
|
||||
static unsigned int numMemHooks;
|
||||
|
||||
// Look in fceu.h for macros named like JOY_UP to determine the order.
|
||||
static const char *button_mappings[] = {
|
||||
|
@ -148,9 +153,19 @@ static const char* luaCallIDStrings [] =
|
|||
"CALL_AFTEREMULATION",
|
||||
"CALL_BEFOREEXIT",
|
||||
};
|
||||
#ifdef WIN32 // uh... yeah
|
||||
static const int _makeSureWeHaveTheRightNumberOfStrings [sizeof(luaCallIDStrings)/sizeof(*luaCallIDStrings) == LUACALL_COUNT ? 1 : 0];
|
||||
#endif
|
||||
|
||||
static const char* luaMemHookTypeStrings [] =
|
||||
{
|
||||
"MEMHOOK_WRITE",
|
||||
"MEMHOOK_READ",
|
||||
"MEMHOOK_EXEC",
|
||||
|
||||
"MEMHOOK_WRITE_SUB",
|
||||
"MEMHOOK_READ_SUB",
|
||||
"MEMHOOK_EXEC_SUB",
|
||||
};
|
||||
static const int _makeSureWeHaveTheRightNumberOfStrings2 [sizeof(luaMemHookTypeStrings)/sizeof(*luaMemHookTypeStrings) == LUAMEMHOOK_COUNT ? 1 : 0];
|
||||
|
||||
/**
|
||||
* Resets emulator speed / pause states after script exit.
|
||||
|
@ -213,53 +228,6 @@ int FCEU_LuaFrameSkip() {
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* When code determines that a write has occurred
|
||||
* (not necessarily worth informing Lua), call this.
|
||||
*
|
||||
*/
|
||||
void FCEU_LuaWriteInform() {
|
||||
if (!L || !luaRunning) return;
|
||||
// Nuke the stack, just in case.
|
||||
lua_settop(L,0);
|
||||
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, memoryWatchTable);
|
||||
lua_pushnil(L);
|
||||
while (lua_next(L, 1) != 0)
|
||||
{
|
||||
unsigned int addr = luaL_checkinteger(L, 2);
|
||||
lua_Integer value;
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, memoryValueTable);
|
||||
lua_pushvalue(L, 2);
|
||||
lua_gettable(L, 4);
|
||||
value = luaL_checkinteger(L, 5);
|
||||
if (FCEU_CheatGetByte(addr) != value)
|
||||
{
|
||||
// Value changed; update & invoke the Lua callback
|
||||
lua_pushinteger(L, addr);
|
||||
lua_pushinteger(L, FCEU_CheatGetByte(addr));
|
||||
lua_settable(L, 4);
|
||||
lua_pop(L, 2);
|
||||
|
||||
numTries = 1000;
|
||||
int res = lua_pcall(L, 0, 0, 0);
|
||||
if (res) {
|
||||
const char *err = lua_tostring(L, -1);
|
||||
|
||||
#ifdef WIN32
|
||||
//StopSound(); //mbg merge 7/23/08
|
||||
MessageBox(hAppWnd, err, "Lua Engine", MB_OK);
|
||||
#else
|
||||
fprintf(stderr, "Lua error: %s\n", err);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
lua_settop(L, 2);
|
||||
}
|
||||
lua_settop(L, 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Toggle certain rendering planes
|
||||
* emu.setrenderingplanes(sprites, background)
|
||||
|
@ -463,6 +431,236 @@ static int memory_readbyterange(lua_State *L) {
|
|||
}
|
||||
|
||||
|
||||
void HandleCallbackError(lua_State* L)
|
||||
{
|
||||
//if(L->errfunc || L->errorJmp)
|
||||
// luaL_error(L, "%s", lua_tostring(L,-1));
|
||||
//else
|
||||
{
|
||||
lua_pushnil(L);
|
||||
lua_setfield(L, LUA_REGISTRYINDEX, guiCallbackTable);
|
||||
|
||||
// Error?
|
||||
#ifdef WIN32
|
||||
MessageBox( hAppWnd, lua_tostring(L,-1), "Lua run error", MB_OK | MB_ICONSTOP);
|
||||
#else
|
||||
fprintf(stderr, "Lua thread bombed out: %s\n", lua_tostring(L,-1));
|
||||
#endif
|
||||
|
||||
FCEU_LuaStop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// the purpose of this structure is to provide a way of
|
||||
// QUICKLY determining whether a memory address range has a hook associated with it,
|
||||
// with a bias toward fast rejection because the majority of addresses will not be hooked.
|
||||
// (it must not use any part of Lua or perform any per-script operations,
|
||||
// otherwise it would definitely be too slow.)
|
||||
// calculating the regions when a hook is added/removed may be slow,
|
||||
// but this is an intentional tradeoff to obtain a high speed of checking during later execution
|
||||
struct TieredRegion
|
||||
{
|
||||
template<unsigned int maxGap>
|
||||
struct Region
|
||||
{
|
||||
struct Island
|
||||
{
|
||||
unsigned int start;
|
||||
unsigned int end;
|
||||
__forceinline bool Contains(unsigned int address, int size) const { return address < end && address+size > start; }
|
||||
};
|
||||
std::vector<Island> islands;
|
||||
|
||||
void Calculate(const std::vector<unsigned int>& bytes)
|
||||
{
|
||||
islands.clear();
|
||||
|
||||
unsigned int lastEnd = ~0;
|
||||
|
||||
std::vector<unsigned int>::const_iterator iter = bytes.begin();
|
||||
std::vector<unsigned int>::const_iterator end = bytes.end();
|
||||
for(; iter != end; ++iter)
|
||||
{
|
||||
unsigned int addr = *iter;
|
||||
if(addr < lastEnd || addr > lastEnd + (long long)maxGap)
|
||||
{
|
||||
islands.push_back(Island());
|
||||
islands.back().start = addr;
|
||||
}
|
||||
islands.back().end = addr+1;
|
||||
lastEnd = addr+1;
|
||||
}
|
||||
}
|
||||
|
||||
bool Contains(unsigned int address, int size) const
|
||||
{
|
||||
std::vector<Island>::const_iterator iter = islands.begin();
|
||||
std::vector<Island>::const_iterator end = islands.end();
|
||||
for(; iter != end; ++iter)
|
||||
if(iter->Contains(address, size))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
Region<0xFFFFFFFF> broad;
|
||||
Region<0x1000> mid;
|
||||
Region<0> narrow;
|
||||
|
||||
void Calculate(std::vector<unsigned int>& bytes)
|
||||
{
|
||||
std::sort(bytes.begin(), bytes.end());
|
||||
|
||||
broad.Calculate(bytes);
|
||||
mid.Calculate(bytes);
|
||||
narrow.Calculate(bytes);
|
||||
}
|
||||
|
||||
TieredRegion()
|
||||
{
|
||||
Calculate(std::vector<unsigned int>());
|
||||
}
|
||||
|
||||
__forceinline int NotEmpty()
|
||||
{
|
||||
return broad.islands.size();
|
||||
}
|
||||
|
||||
// note: it is illegal to call this if NotEmpty() returns 0
|
||||
__forceinline bool Contains(unsigned int address, int size)
|
||||
{
|
||||
return broad.islands[0].Contains(address,size) &&
|
||||
mid.Contains(address,size) &&
|
||||
narrow.Contains(address,size);
|
||||
}
|
||||
};
|
||||
TieredRegion hookedRegions [LUAMEMHOOK_COUNT];
|
||||
|
||||
|
||||
static void CalculateMemHookRegions(LuaMemHookType hookType)
|
||||
{
|
||||
std::vector<unsigned int> hookedBytes;
|
||||
// std::map<int, LuaContextInfo*>::iterator iter = luaContextInfo.begin();
|
||||
// std::map<int, LuaContextInfo*>::iterator end = luaContextInfo.end();
|
||||
// while(iter != end)
|
||||
// {
|
||||
// LuaContextInfo& info = *iter->second;
|
||||
if(/*info.*/ numMemHooks)
|
||||
{
|
||||
// lua_State* L = info.L;
|
||||
if(L)
|
||||
{
|
||||
lua_settop(L, 0);
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, luaMemHookTypeStrings[hookType]);
|
||||
lua_pushnil(L);
|
||||
while(lua_next(L, -2))
|
||||
{
|
||||
if(lua_isfunction(L, -1))
|
||||
{
|
||||
unsigned int addr = lua_tointeger(L, -2);
|
||||
hookedBytes.push_back(addr);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
lua_settop(L, 0);
|
||||
}
|
||||
}
|
||||
// ++iter;
|
||||
// }
|
||||
hookedRegions[hookType].Calculate(hookedBytes);
|
||||
}
|
||||
|
||||
static void CallRegisteredLuaMemHook_LuaMatch(unsigned int address, int size, unsigned int value, LuaMemHookType hookType)
|
||||
{
|
||||
// std::map<int, LuaContextInfo*>::iterator iter = luaContextInfo.begin();
|
||||
// std::map<int, LuaContextInfo*>::iterator end = luaContextInfo.end();
|
||||
// while(iter != end)
|
||||
// {
|
||||
// LuaContextInfo& info = *iter->second;
|
||||
if(/*info.*/ numMemHooks)
|
||||
{
|
||||
// lua_State* L = info.L;
|
||||
if(L/* && !info.panic*/)
|
||||
{
|
||||
#ifdef USE_INFO_STACK
|
||||
infoStack.insert(infoStack.begin(), &info);
|
||||
struct Scope { ~Scope(){ infoStack.erase(infoStack.begin()); } } scope;
|
||||
#endif
|
||||
lua_settop(L, 0);
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, luaMemHookTypeStrings[hookType]);
|
||||
for(int i = address; i != address+size; i++)
|
||||
{
|
||||
lua_rawgeti(L, -1, i);
|
||||
if (lua_isfunction(L, -1))
|
||||
{
|
||||
bool wasRunning = (luaRunning!=0) /*info.running*/;
|
||||
luaRunning /*info.running*/ = true;
|
||||
//RefreshScriptSpeedStatus();
|
||||
lua_pushinteger(L, address);
|
||||
lua_pushinteger(L, size);
|
||||
int errorcode = lua_pcall(L, 2, 0, 0);
|
||||
luaRunning /*info.running*/ = wasRunning;
|
||||
//RefreshScriptSpeedStatus();
|
||||
if (errorcode)
|
||||
{
|
||||
HandleCallbackError(L);
|
||||
//int uid = iter->first;
|
||||
//HandleCallbackError(L,info,uid,true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
lua_pop(L,1);
|
||||
}
|
||||
}
|
||||
lua_settop(L, 0);
|
||||
}
|
||||
}
|
||||
// ++iter;
|
||||
// }
|
||||
}
|
||||
void CallRegisteredLuaMemHook(unsigned int address, int size, unsigned int value, LuaMemHookType hookType)
|
||||
{
|
||||
// performance critical! (called VERY frequently)
|
||||
// I suggest timing a large number of calls to this function in Release if you change anything in here,
|
||||
// before and after, because even the most innocent change can make it become 30% to 400% slower.
|
||||
// a good amount to test is: 100000000 calls with no hook set, and another 100000000 with a hook set.
|
||||
// (on my system that consistently took 200 ms total in the former case and 350 ms total in the latter case)
|
||||
if(hookedRegions[hookType].NotEmpty())
|
||||
{
|
||||
//if((hookType <= LUAMEMHOOK_EXEC) && (address >= 0xE00000))
|
||||
// address |= 0xFF0000; // account for mirroring of RAM
|
||||
if(hookedRegions[hookType].Contains(address, size))
|
||||
CallRegisteredLuaMemHook_LuaMatch(address, size, value, hookType); // something has hooked this specific address
|
||||
}
|
||||
}
|
||||
|
||||
void CallRegisteredLuaFunctions(LuaCallID calltype)
|
||||
{
|
||||
assert((unsigned int)calltype < (unsigned int)LUACALL_COUNT);
|
||||
const char* idstring = luaCallIDStrings[calltype];
|
||||
|
||||
if (!L)
|
||||
return;
|
||||
|
||||
lua_settop(L, 0);
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, idstring);
|
||||
|
||||
int errorcode = 0;
|
||||
if (lua_isfunction(L, -1))
|
||||
{
|
||||
errorcode = lua_pcall(L, 0, 0, 0);
|
||||
if (errorcode)
|
||||
HandleCallbackError(L);
|
||||
}
|
||||
else
|
||||
{
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Not for the signed versions though
|
||||
static int memory_readbytesigned(lua_State *L) {
|
||||
signed char c = (signed char) FCEU_CheatGetByte(luaL_checkinteger(L,1));
|
||||
|
@ -470,37 +668,112 @@ static int memory_readbytesigned(lua_State *L) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
// memory.registerwrite(int address, function func)
|
||||
//
|
||||
// Calls the given function when the indicated memory address is
|
||||
// written to. No args are given to the function. The write has already
|
||||
// occurred, so the new address is readable.
|
||||
static int memory_registerwrite(lua_State *L) {
|
||||
static int memory_registerHook(lua_State* L, LuaMemHookType hookType, int defaultSize)
|
||||
{
|
||||
// get first argument: address
|
||||
unsigned int addr = luaL_checkinteger(L,1);
|
||||
if((addr & ~0xFFFFFF) == ~0xFFFFFF)
|
||||
addr &= 0xFFFFFF;
|
||||
|
||||
// Check args
|
||||
unsigned int addr = luaL_checkinteger(L, 1);
|
||||
if (lua_type(L,2) != LUA_TNIL && lua_type(L,2) != LUA_TFUNCTION)
|
||||
luaL_error(L, "function or nil expected in arg 2 to memory.register");
|
||||
|
||||
|
||||
// Check the address range
|
||||
if (addr > 0xffff)
|
||||
luaL_error(L, "arg 1 should be between 0x0000 and 0x0fff");
|
||||
// get optional second argument: size
|
||||
int size = defaultSize;
|
||||
int funcIdx = 2;
|
||||
if(lua_isnumber(L,2))
|
||||
{
|
||||
size = luaL_checkinteger(L,2);
|
||||
if(size < 0)
|
||||
{
|
||||
size = -size;
|
||||
addr -= size;
|
||||
}
|
||||
funcIdx++;
|
||||
}
|
||||
|
||||
// Commit it to the registery
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, memoryWatchTable);
|
||||
lua_pushvalue(L,1);
|
||||
lua_pushvalue(L,2);
|
||||
lua_settable(L, -3);
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, memoryValueTable);
|
||||
lua_pushvalue(L,1);
|
||||
if (lua_isnil(L,2)) lua_pushnil(L);
|
||||
else lua_pushinteger(L, FCEU_CheatGetByte(addr));
|
||||
lua_settable(L, -3);
|
||||
|
||||
// check last argument: callback function
|
||||
bool clearing = lua_isnil(L,funcIdx);
|
||||
if(!clearing)
|
||||
luaL_checktype(L, funcIdx, LUA_TFUNCTION);
|
||||
lua_settop(L,funcIdx);
|
||||
|
||||
// get the address-to-callback table for this hook type of the current script
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, luaMemHookTypeStrings[hookType]);
|
||||
|
||||
// count how many callback functions we'll be displacing
|
||||
int numFuncsAfter = clearing ? 0 : size;
|
||||
int numFuncsBefore = 0;
|
||||
for(unsigned int i = addr; i != addr+size; i++)
|
||||
{
|
||||
lua_rawgeti(L, -1, i);
|
||||
if(lua_isfunction(L, -1))
|
||||
numFuncsBefore++;
|
||||
lua_pop(L,1);
|
||||
}
|
||||
|
||||
// put the callback function in the address slots
|
||||
for(unsigned int i = addr; i != addr+size; i++)
|
||||
{
|
||||
lua_pushvalue(L, -2);
|
||||
lua_rawseti(L, -2, i);
|
||||
}
|
||||
|
||||
// adjust the count of active hooks
|
||||
//LuaContextInfo& info = GetCurrentInfo();
|
||||
/*info.*/ numMemHooks += numFuncsAfter - numFuncsBefore;
|
||||
|
||||
// re-cache regions of hooked memory across all scripts
|
||||
CalculateMemHookRegions(hookType);
|
||||
|
||||
//StopScriptIfFinished(luaStateToUIDMap[L]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LuaMemHookType MatchHookTypeToCPU(lua_State* L, LuaMemHookType hookType)
|
||||
{
|
||||
int cpuID = 0;
|
||||
|
||||
int cpunameIndex = 0;
|
||||
if(lua_type(L,2) == LUA_TSTRING)
|
||||
cpunameIndex = 2;
|
||||
else if(lua_type(L,3) == LUA_TSTRING)
|
||||
cpunameIndex = 3;
|
||||
|
||||
if(cpunameIndex)
|
||||
{
|
||||
const char* cpuName = lua_tostring(L, cpunameIndex);
|
||||
if(!stricmp(cpuName, "sub"))
|
||||
cpuID = 1;
|
||||
lua_remove(L, cpunameIndex);
|
||||
}
|
||||
|
||||
switch(cpuID)
|
||||
{
|
||||
case 0:
|
||||
return hookType;
|
||||
|
||||
case 1:
|
||||
switch(hookType)
|
||||
{
|
||||
case LUAMEMHOOK_WRITE: return LUAMEMHOOK_WRITE_SUB;
|
||||
case LUAMEMHOOK_READ: return LUAMEMHOOK_READ_SUB;
|
||||
case LUAMEMHOOK_EXEC: return LUAMEMHOOK_EXEC_SUB;
|
||||
}
|
||||
}
|
||||
return hookType;
|
||||
}
|
||||
|
||||
static int memory_registerwrite(lua_State *L)
|
||||
{
|
||||
return memory_registerHook(L, MatchHookTypeToCPU(L,LUAMEMHOOK_WRITE), 1);
|
||||
}
|
||||
static int memory_registerread(lua_State *L)
|
||||
{
|
||||
return memory_registerHook(L, MatchHookTypeToCPU(L,LUAMEMHOOK_READ), 1);
|
||||
}
|
||||
static int memory_registerexec(lua_State *L)
|
||||
{
|
||||
return memory_registerHook(L, MatchHookTypeToCPU(L,LUAMEMHOOK_EXEC), 2);
|
||||
}
|
||||
|
||||
//adelikat: table pulled from GENS. credz nitsuja!
|
||||
|
||||
#ifdef _WIN32
|
||||
|
@ -2115,8 +2388,12 @@ static const struct luaL_reg memorylib [] = {
|
|||
|
||||
// memory hooks
|
||||
{"registerwrite", memory_registerwrite},
|
||||
//{"registerread", memory_registerread}, TODO
|
||||
{"registerexec", memory_registerexec},
|
||||
// alternate names
|
||||
{"register", memory_registerwrite},
|
||||
{"registerrun", memory_registerexec},
|
||||
{"registerexecute", memory_registerexec},
|
||||
|
||||
{NULL,NULL}
|
||||
};
|
||||
|
@ -2207,26 +2484,6 @@ static const struct luaL_reg guilib[] = {
|
|||
{NULL,NULL}
|
||||
};
|
||||
|
||||
void HandleCallbackError(lua_State* L)
|
||||
{
|
||||
//if(L->errfunc || L->errorJmp)
|
||||
// luaL_error(L, "%s", lua_tostring(L,-1));
|
||||
//else
|
||||
{
|
||||
lua_pushnil(L);
|
||||
lua_setfield(L, LUA_REGISTRYINDEX, guiCallbackTable);
|
||||
|
||||
// Error?
|
||||
#ifdef WIN32
|
||||
MessageBox( hAppWnd, lua_tostring(L,-1), "Lua run error", MB_OK | MB_ICONSTOP);
|
||||
#else
|
||||
fprintf(stderr, "Lua thread bombed out: %s\n", lua_tostring(L,-1));
|
||||
#endif
|
||||
|
||||
FCEU_LuaStop();
|
||||
}
|
||||
}
|
||||
|
||||
void CallExitFunction() {
|
||||
if (!L)
|
||||
return;
|
||||
|
@ -2246,30 +2503,6 @@ void CallExitFunction() {
|
|||
HandleCallbackError(L);
|
||||
}
|
||||
|
||||
void CallRegisteredLuaFunctions(LuaCallID calltype)
|
||||
{
|
||||
assert((unsigned int)calltype < (unsigned int)LUACALL_COUNT);
|
||||
const char* idstring = luaCallIDStrings[calltype];
|
||||
|
||||
if (!L)
|
||||
return;
|
||||
|
||||
lua_settop(L, 0);
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, idstring);
|
||||
|
||||
int errorcode = 0;
|
||||
if (lua_isfunction(L, -1))
|
||||
{
|
||||
errorcode = lua_pcall(L, 0, 0, 0);
|
||||
if (errorcode)
|
||||
HandleCallbackError(L);
|
||||
}
|
||||
else
|
||||
{
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void FCEU_LuaFrameBoundary() {
|
||||
|
||||
// printf("Lua Frame\n");
|
||||
|
@ -2375,10 +2608,12 @@ int FCEU_LoadLuaCode(const char *filename) {
|
|||
|
||||
luabitop_validate(L);
|
||||
|
||||
lua_newtable(L);
|
||||
lua_setfield(L, LUA_REGISTRYINDEX, memoryWatchTable);
|
||||
lua_newtable(L);
|
||||
lua_setfield(L, LUA_REGISTRYINDEX, memoryValueTable);
|
||||
// push arrays for storing hook functions in
|
||||
for(int i = 0; i < LUAMEMHOOK_COUNT; i++)
|
||||
{
|
||||
lua_newtable(L);
|
||||
lua_setfield(L, LUA_REGISTRYINDEX, luaMemHookTypeStrings[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// We make our thread NOW because we want it at the bottom of the stack.
|
||||
|
@ -2415,6 +2650,7 @@ int FCEU_LoadLuaCode(const char *filename) {
|
|||
// Initialize settings
|
||||
luaRunning = TRUE;
|
||||
skipRerecords = FALSE;
|
||||
numMemHooks = 0;
|
||||
|
||||
wasPaused = FCEUI_EmulationPaused();
|
||||
if (wasPaused) FCEUI_ToggleEmulationPause();
|
||||
|
@ -2455,6 +2691,10 @@ void FCEU_LuaStop() {
|
|||
//execute the user's shutdown callbacks
|
||||
CallExitFunction();
|
||||
|
||||
/*info.*/numMemHooks = 0;
|
||||
for(int i = 0; i < LUAMEMHOOK_COUNT; i++)
|
||||
CalculateMemHookRegions((LuaMemHookType)i);
|
||||
|
||||
//sometimes iup uninitializes com
|
||||
//MBG TODO - test whether this is really necessary. i dont think it is
|
||||
#ifdef WIN32
|
||||
|
|
|
@ -442,7 +442,7 @@ static DECLFR(NSF_read)
|
|||
BANKSET(0x8000+x*4096,NSFHeader.BankSwitch[x]);
|
||||
}
|
||||
#ifdef _S9XLUA_H
|
||||
FCEU_LuaWriteInform();
|
||||
//CallRegisteredLuaMemHook(A, 1, V, LUAMEMHOOK_WRITE); FIXME
|
||||
#endif
|
||||
return (CurrentSong-1);
|
||||
}
|
||||
|
|
|
@ -51,8 +51,8 @@ static INLINE uint8 RdMem(unsigned int A)
|
|||
static INLINE void WrMem(unsigned int A, uint8 V)
|
||||
{
|
||||
BWrite[A](A,V);
|
||||
#ifdef _S9XLUA_H
|
||||
FCEU_LuaWriteInform();
|
||||
#ifdef _S9XLUA_H
|
||||
CallRegisteredLuaMemHook(A, 1, V, LUAMEMHOOK_WRITE);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -67,7 +67,7 @@ static INLINE void WrRAM(unsigned int A, uint8 V)
|
|||
{
|
||||
RAM[A]=V;
|
||||
#ifdef _S9XLUA_H
|
||||
FCEU_LuaWriteInform();
|
||||
CallRegisteredLuaMemHook(A, 1, V, LUAMEMHOOK_WRITE);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -82,7 +82,7 @@ void X6502_DMW(uint32 A, uint8 V)
|
|||
ADDCYC(1);
|
||||
BWrite[A](A,V);
|
||||
#ifdef _S9XLUA_H
|
||||
FCEU_LuaWriteInform();
|
||||
CallRegisteredLuaMemHook(A, 1, V, LUAMEMHOOK_WRITE);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -490,6 +490,9 @@ extern int test; test++;
|
|||
_tcount=0;
|
||||
if(MapIRQHook) MapIRQHook(temp);
|
||||
FCEU_SoundCPUHook(temp);
|
||||
#ifdef _S9XLUA_H
|
||||
CallRegisteredLuaMemHook(_PC, 1, 0, LUAMEMHOOK_EXEC);
|
||||
#endif
|
||||
_PC++;
|
||||
switch(b1)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue