Added memory hooks to JS engine.
This commit is contained in:
parent
4f604b7708
commit
a02ae8d3e0
|
@ -203,6 +203,58 @@ QString EmuScriptObject::getDir()
|
|||
//----------------------------------------------------
|
||||
//---- Memory Script Object
|
||||
//----------------------------------------------------
|
||||
//----------------------------------------------------
|
||||
static void addressReadCallback(unsigned int address, unsigned int value, void *userData)
|
||||
{
|
||||
const MemoryScriptObject* mem = static_cast<const MemoryScriptObject*>(userData);
|
||||
|
||||
if (mem != nullptr)
|
||||
{
|
||||
const QJSValue* func = mem->getReadFunc(address);
|
||||
|
||||
if (func != nullptr)
|
||||
{
|
||||
QJSValueList args = { address, value };
|
||||
|
||||
func->call(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------
|
||||
static void addressWriteCallback(unsigned int address, unsigned int value, void *userData)
|
||||
{
|
||||
const MemoryScriptObject* mem = static_cast<const MemoryScriptObject*>(userData);
|
||||
|
||||
if (mem != nullptr)
|
||||
{
|
||||
const QJSValue* func = mem->getWriteFunc(address);
|
||||
|
||||
if (func != nullptr)
|
||||
{
|
||||
QJSValueList args = { address, value };
|
||||
|
||||
func->call(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------
|
||||
static void addressExecCallback(unsigned int address, unsigned int value, void *userData)
|
||||
{
|
||||
const MemoryScriptObject* mem = static_cast<const MemoryScriptObject*>(userData);
|
||||
|
||||
if (mem != nullptr)
|
||||
{
|
||||
const QJSValue* func = mem->getExecFunc(address);
|
||||
|
||||
if (func != nullptr)
|
||||
{
|
||||
QJSValueList args = { address, value };
|
||||
|
||||
func->call(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------
|
||||
MemoryScriptObject::MemoryScriptObject(QObject* parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
|
@ -211,6 +263,12 @@ MemoryScriptObject::MemoryScriptObject(QObject* parent)
|
|||
//----------------------------------------------------
|
||||
MemoryScriptObject::~MemoryScriptObject()
|
||||
{
|
||||
unregisterAll();
|
||||
}
|
||||
//----------------------------------------------------
|
||||
void MemoryScriptObject::reset()
|
||||
{
|
||||
unregisterAll();
|
||||
}
|
||||
//----------------------------------------------------
|
||||
uint8_t MemoryScriptObject::readByte(int address)
|
||||
|
@ -332,6 +390,207 @@ void MemoryScriptObject::setRegisterP(uint8_t v)
|
|||
X.P = v;
|
||||
}
|
||||
//----------------------------------------------------
|
||||
void MemoryScriptObject::registerCallback(int type, const QJSValue& func, int address, int size)
|
||||
{
|
||||
int n=0;
|
||||
int *numFuncsRegistered = nullptr;
|
||||
QJSValue** funcArray = nullptr;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
default:
|
||||
case X6502_MemHook::Read:
|
||||
funcArray = readFunc;
|
||||
numFuncsRegistered = &numReadFuncsRegistered;
|
||||
X6502_MemHook::Add(X6502_MemHook::Read, addressReadCallback, this);
|
||||
break;
|
||||
case X6502_MemHook::Write:
|
||||
funcArray = writeFunc;
|
||||
numFuncsRegistered = &numWriteFuncsRegistered;
|
||||
X6502_MemHook::Add(X6502_MemHook::Write, addressWriteCallback, this);
|
||||
break;
|
||||
case X6502_MemHook::Exec:
|
||||
funcArray = execFunc;
|
||||
numFuncsRegistered = &numExecFuncsRegistered;
|
||||
X6502_MemHook::Add(X6502_MemHook::Exec, addressExecCallback, this);
|
||||
break;
|
||||
}
|
||||
n = *numFuncsRegistered;
|
||||
|
||||
if (func.isCallable())
|
||||
{
|
||||
for (int i=0; i<size; i++)
|
||||
{
|
||||
int addr = address + i;
|
||||
|
||||
if ( (addr >= 0) && (addr < AddressRange) )
|
||||
{
|
||||
if (funcArray[addr] != nullptr)
|
||||
{
|
||||
n--;
|
||||
delete funcArray[addr];
|
||||
}
|
||||
funcArray[addr] = new QJSValue(func);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i=0; i<size; i++)
|
||||
{
|
||||
int addr = address + i;
|
||||
|
||||
if ( (addr >= 0) && (addr < AddressRange) )
|
||||
{
|
||||
if (funcArray[addr] != nullptr)
|
||||
{
|
||||
n--;
|
||||
delete funcArray[addr];
|
||||
}
|
||||
funcArray[addr] = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
*numFuncsRegistered = n;
|
||||
}
|
||||
//----------------------------------------------------
|
||||
void MemoryScriptObject::registerRead(const QJSValue& func, int address, int size)
|
||||
{
|
||||
registerCallback(X6502_MemHook::Read, func, address, size);
|
||||
}
|
||||
//----------------------------------------------------
|
||||
void MemoryScriptObject::registerWrite(const QJSValue& func, int address, int size)
|
||||
{
|
||||
registerCallback(X6502_MemHook::Write, func, address, size);
|
||||
}
|
||||
//----------------------------------------------------
|
||||
void MemoryScriptObject::registerExec(const QJSValue& func, int address, int size)
|
||||
{
|
||||
registerCallback(X6502_MemHook::Exec, func, address, size);
|
||||
}
|
||||
//----------------------------------------------------
|
||||
void MemoryScriptObject::unregisterCallback(int type, const QJSValue& func, int address, int size)
|
||||
{
|
||||
int n=0;
|
||||
int *numFuncsRegistered = nullptr;
|
||||
QJSValue** funcArray = nullptr;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
default:
|
||||
case X6502_MemHook::Read:
|
||||
funcArray = readFunc;
|
||||
numFuncsRegistered = &numReadFuncsRegistered;
|
||||
break;
|
||||
case X6502_MemHook::Write:
|
||||
funcArray = writeFunc;
|
||||
numFuncsRegistered = &numWriteFuncsRegistered;
|
||||
break;
|
||||
case X6502_MemHook::Exec:
|
||||
funcArray = execFunc;
|
||||
numFuncsRegistered = &numExecFuncsRegistered;
|
||||
break;
|
||||
}
|
||||
n = *numFuncsRegistered;
|
||||
|
||||
if (func.isCallable())
|
||||
{
|
||||
for (int i=0; i<size; i++)
|
||||
{
|
||||
int addr = address + i;
|
||||
|
||||
if ( (addr >= 0) && (addr < AddressRange) )
|
||||
{
|
||||
if (funcArray[addr] != nullptr)
|
||||
{
|
||||
n--;
|
||||
delete funcArray[addr];
|
||||
}
|
||||
funcArray[addr] = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i=0; i<size; i++)
|
||||
{
|
||||
int addr = address + i;
|
||||
|
||||
if ( (addr >= 0) && (addr < AddressRange) )
|
||||
{
|
||||
if (funcArray[addr] != nullptr)
|
||||
{
|
||||
n--;
|
||||
delete funcArray[addr];
|
||||
}
|
||||
funcArray[addr] = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
*numFuncsRegistered = n;
|
||||
|
||||
if (0 <= numReadFuncsRegistered)
|
||||
{
|
||||
X6502_MemHook::Remove(X6502_MemHook::Read, addressReadCallback, this);
|
||||
}
|
||||
if (0 <= numWriteFuncsRegistered)
|
||||
{
|
||||
X6502_MemHook::Remove(X6502_MemHook::Write, addressWriteCallback, this);
|
||||
}
|
||||
if (0 <= numExecFuncsRegistered)
|
||||
{
|
||||
X6502_MemHook::Remove(X6502_MemHook::Exec, addressExecCallback, this);
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------
|
||||
void MemoryScriptObject::unregisterRead(const QJSValue& func, int address, int size)
|
||||
{
|
||||
unregisterCallback(X6502_MemHook::Read, func, address, size);
|
||||
}
|
||||
//----------------------------------------------------
|
||||
void MemoryScriptObject::unregisterWrite(const QJSValue& func, int address, int size)
|
||||
{
|
||||
unregisterCallback(X6502_MemHook::Write, func, address, size);
|
||||
}
|
||||
//----------------------------------------------------
|
||||
void MemoryScriptObject::unregisterExec(const QJSValue& func, int address, int size)
|
||||
{
|
||||
unregisterCallback(X6502_MemHook::Exec, func, address, size);
|
||||
}
|
||||
//----------------------------------------------------
|
||||
void MemoryScriptObject::unregisterAll()
|
||||
{
|
||||
X6502_MemHook::Remove(X6502_MemHook::Read, addressReadCallback, this);
|
||||
X6502_MemHook::Remove(X6502_MemHook::Write, addressWriteCallback, this);
|
||||
X6502_MemHook::Remove(X6502_MemHook::Exec, addressExecCallback, this);
|
||||
|
||||
for (int i=0; i<AddressRange; i++)
|
||||
{
|
||||
if (execFunc[i] != nullptr)
|
||||
{
|
||||
numExecFuncsRegistered--;
|
||||
delete execFunc[i];
|
||||
}
|
||||
if (readFunc[i] != nullptr)
|
||||
{
|
||||
numReadFuncsRegistered--;
|
||||
delete readFunc[i];
|
||||
}
|
||||
if (writeFunc[i] != nullptr)
|
||||
{
|
||||
numWriteFuncsRegistered--;
|
||||
delete writeFunc[i];
|
||||
}
|
||||
execFunc[i] = nullptr;
|
||||
readFunc[i] = nullptr;
|
||||
writeFunc[i] = nullptr;
|
||||
}
|
||||
numReadFuncsRegistered = 0;
|
||||
numWriteFuncsRegistered = 0;
|
||||
numExecFuncsRegistered = 0;
|
||||
}
|
||||
//----------------------------------------------------
|
||||
//---- Qt Script Instance
|
||||
//----------------------------------------------------
|
||||
QtScriptInstance::QtScriptInstance(QObject* parent)
|
||||
|
@ -387,6 +646,7 @@ void QtScriptInstance::resetEngine()
|
|||
ui_rootWidget->deleteLater();
|
||||
ui_rootWidget = nullptr;
|
||||
}
|
||||
mem->reset();
|
||||
|
||||
configEngine();
|
||||
}
|
||||
|
@ -434,7 +694,10 @@ int QtScriptInstance::loadScriptFile( QString filepath )
|
|||
|
||||
if (evalResult.isError())
|
||||
{
|
||||
print(evalResult.toString());
|
||||
QString msg;
|
||||
msg += evalResult.property("lineNumber").toString() + ": ";
|
||||
msg += evalResult.toString();
|
||||
print(msg);
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
|
@ -442,9 +705,6 @@ int QtScriptInstance::loadScriptFile( QString filepath )
|
|||
running = true;
|
||||
//printf("Script Evaluation Success!\n");
|
||||
}
|
||||
//onFrameBeginCallback = engine->globalObject().property("onFrameBegin");
|
||||
//onFrameFinishCallback = engine->globalObject().property("onFrameFinish");
|
||||
//onScriptStopCallback = engine->globalObject().property("onScriptStop");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -459,7 +719,7 @@ void QtScriptInstance::loadObjectChildren(QJSValue& jsObject, QObject* obj)
|
|||
|
||||
if (!name.isEmpty())
|
||||
{
|
||||
printf("Object: %s.%s\n", obj->objectName().toStdString().c_str(), child->objectName().toStdString().c_str());
|
||||
//printf("Object: %s.%s\n", obj->objectName().toStdString().c_str(), child->objectName().toStdString().c_str());
|
||||
|
||||
QJSValue newJsObj = engine->newQObject(child);
|
||||
|
||||
|
@ -581,6 +841,7 @@ int QtScriptInstance::call(const QString& funcName, const QJSValueList& args)
|
|||
//----------------------------------------------------
|
||||
void QtScriptInstance::stopRunning()
|
||||
{
|
||||
FCEU_WRAPPER_LOCK();
|
||||
if (running)
|
||||
{
|
||||
if (onScriptStopCallback.isCallable())
|
||||
|
@ -588,7 +849,10 @@ void QtScriptInstance::stopRunning()
|
|||
onScriptStopCallback.call();
|
||||
}
|
||||
running = false;
|
||||
|
||||
mem->reset();
|
||||
}
|
||||
FCEU_WRAPPER_UNLOCK();
|
||||
}
|
||||
//----------------------------------------------------
|
||||
void QtScriptInstance::onFrameBegin()
|
||||
|
|
|
@ -78,11 +78,25 @@ public:
|
|||
|
||||
void setEngine(QJSEngine* _engine){ engine = _engine; }
|
||||
void setDialog(QScriptDialog_t* _dialog){ dialog = _dialog; }
|
||||
void reset();
|
||||
|
||||
const QJSValue* getReadFunc(int address) const { return readFunc[address]; }
|
||||
const QJSValue* getWriteFunc(int address) const { return writeFunc[address]; }
|
||||
const QJSValue* getExecFunc(int address) const { return execFunc[address]; }
|
||||
private:
|
||||
static constexpr int AddressRange = 0x10000;
|
||||
QJSEngine* engine = nullptr;
|
||||
QScriptDialog_t* dialog = nullptr;
|
||||
QtScriptInstance* script = nullptr;
|
||||
QJSValue* readFunc[AddressRange] = { nullptr };
|
||||
QJSValue* writeFunc[AddressRange] = { nullptr };
|
||||
QJSValue* execFunc[AddressRange] = { nullptr };
|
||||
int numReadFuncsRegistered = 0;
|
||||
int numWriteFuncsRegistered = 0;
|
||||
int numExecFuncsRegistered = 0;
|
||||
|
||||
void registerCallback(int type, const QJSValue& func, int address, int size = 1);
|
||||
void unregisterCallback(int type, const QJSValue& func, int address, int size = 1);
|
||||
|
||||
public slots:
|
||||
Q_INVOKABLE uint8_t readByte(int address);
|
||||
|
@ -104,6 +118,13 @@ public slots:
|
|||
Q_INVOKABLE void setRegisterY(uint8_t v);
|
||||
Q_INVOKABLE void setRegisterS(uint8_t v);
|
||||
Q_INVOKABLE void setRegisterP(uint8_t v);
|
||||
Q_INVOKABLE void registerRead(const QJSValue& func, int address, int size = 1);
|
||||
Q_INVOKABLE void registerWrite(const QJSValue& func, int address, int size = 1);
|
||||
Q_INVOKABLE void registerExec(const QJSValue& func, int address, int size = 1);
|
||||
Q_INVOKABLE void unregisterRead(const QJSValue& func, int address, int size = 1);
|
||||
Q_INVOKABLE void unregisterWrite(const QJSValue& func, int address, int size = 1);
|
||||
Q_INVOKABLE void unregisterExec(const QJSValue& func, int address, int size = 1);
|
||||
Q_INVOKABLE void unregisterAll();
|
||||
};
|
||||
|
||||
class QtScriptInstance : public QObject
|
||||
|
|
|
@ -118,15 +118,15 @@ bool DemandLua()
|
|||
#endif
|
||||
}
|
||||
|
||||
static void luaReadMemHook(unsigned int address, unsigned int value)
|
||||
static void luaReadMemHook(unsigned int address, unsigned int value, void *userData)
|
||||
{
|
||||
CallRegisteredLuaMemHook(address, 1, value, LUAMEMHOOK_READ);
|
||||
}
|
||||
static void luaWriteMemHook(unsigned int address, unsigned int value)
|
||||
static void luaWriteMemHook(unsigned int address, unsigned int value, void *userData)
|
||||
{
|
||||
CallRegisteredLuaMemHook(address, 1, value, LUAMEMHOOK_WRITE);
|
||||
}
|
||||
static void luaExecMemHook(unsigned int address, unsigned int value)
|
||||
static void luaExecMemHook(unsigned int address, unsigned int value, void *userData)
|
||||
{
|
||||
CallRegisteredLuaMemHook(address, 1, value, LUAMEMHOOK_EXEC);
|
||||
}
|
||||
|
@ -6501,9 +6501,9 @@ int FCEU_LoadLuaCode(const char *filename, const char *arg)
|
|||
lua_setfield(L, LUA_REGISTRYINDEX, luaMemHookTypeStrings[i]);
|
||||
}
|
||||
|
||||
X6502_MemHook::Add( X6502_MemHook::Read , luaReadMemHook );
|
||||
X6502_MemHook::Add( X6502_MemHook::Write, luaWriteMemHook );
|
||||
X6502_MemHook::Add( X6502_MemHook::Exec , luaExecMemHook );
|
||||
X6502_MemHook::Add( X6502_MemHook::Read , luaReadMemHook , nullptr );
|
||||
X6502_MemHook::Add( X6502_MemHook::Write, luaWriteMemHook, nullptr );
|
||||
X6502_MemHook::Add( X6502_MemHook::Exec , luaExecMemHook , nullptr );
|
||||
}
|
||||
|
||||
// We make our thread NOW because we want it at the bottom of the stack.
|
||||
|
@ -6619,9 +6619,9 @@ void FCEU_LuaStop() {
|
|||
//already killed
|
||||
if (!L) return;
|
||||
|
||||
X6502_MemHook::Remove( X6502_MemHook::Read , luaReadMemHook );
|
||||
X6502_MemHook::Remove( X6502_MemHook::Write, luaWriteMemHook );
|
||||
X6502_MemHook::Remove( X6502_MemHook::Exec , luaExecMemHook );
|
||||
X6502_MemHook::Remove( X6502_MemHook::Read , luaReadMemHook , nullptr );
|
||||
X6502_MemHook::Remove( X6502_MemHook::Write, luaWriteMemHook, nullptr );
|
||||
X6502_MemHook::Remove( X6502_MemHook::Exec , luaExecMemHook , nullptr );
|
||||
|
||||
// Since the script is exiting, we want to prevent an infinite loop.
|
||||
// CallExitFunction() > HandleCallbackError() > FCEU_LuaStop() > CallExitFunction() ...
|
||||
|
|
|
@ -63,7 +63,7 @@ class timeStampModule
|
|||
public:
|
||||
timeStampModule(void)
|
||||
{
|
||||
printf("timeStampModuleInit\n");
|
||||
//printf("timeStampModuleInit\n");
|
||||
#if defined(WIN32)
|
||||
timeStampRecord::qpcCalibrate();
|
||||
#endif
|
||||
|
|
|
@ -48,7 +48,7 @@ static X6502_MemHook* readMemHook = nullptr;
|
|||
static X6502_MemHook* writeMemHook = nullptr;
|
||||
static X6502_MemHook* execMemHook = nullptr;
|
||||
|
||||
void X6502_MemHook::Add(enum X6502_MemHook::Type type, void (*func)(unsigned int address, unsigned int value) )
|
||||
void X6502_MemHook::Add(enum X6502_MemHook::Type type, void (*func)(unsigned int address, unsigned int value, void *userData), void *userData )
|
||||
{
|
||||
X6502_MemHook** hookStart = nullptr;
|
||||
|
||||
|
@ -75,10 +75,11 @@ void X6502_MemHook::Add(enum X6502_MemHook::Type type, void (*func)(unsigned int
|
|||
|
||||
while (hook->next != nullptr)
|
||||
{
|
||||
if (hook->func == func)
|
||||
if ((hook->func == func) && (hook->userData == userData))
|
||||
{
|
||||
// Already registered
|
||||
//printf("LUA MemHook Already Registered\n");
|
||||
hook->refCount++;
|
||||
return;
|
||||
}
|
||||
hook = hook->next;
|
||||
|
@ -86,6 +87,8 @@ void X6502_MemHook::Add(enum X6502_MemHook::Type type, void (*func)(unsigned int
|
|||
X6502_MemHook* newHook = new X6502_MemHook();
|
||||
newHook->type = type;
|
||||
newHook->func = func;
|
||||
newHook->userData = userData;
|
||||
newHook->refCount = 1;
|
||||
hook->next = newHook;
|
||||
}
|
||||
else
|
||||
|
@ -93,12 +96,14 @@ void X6502_MemHook::Add(enum X6502_MemHook::Type type, void (*func)(unsigned int
|
|||
X6502_MemHook* newHook = new X6502_MemHook();
|
||||
newHook->type = type;
|
||||
newHook->func = func;
|
||||
newHook->userData = userData;
|
||||
newHook->refCount = 1;
|
||||
*hookStart = newHook;
|
||||
}
|
||||
//printf("LUA MemHook Added: %p\n", func);
|
||||
}
|
||||
|
||||
void X6502_MemHook::Remove(enum X6502_MemHook::Type type, void (*func)(unsigned int address, unsigned int value) )
|
||||
void X6502_MemHook::Remove(enum X6502_MemHook::Type type, void (*func)(unsigned int address, unsigned int value, void *userData), void *userData )
|
||||
{
|
||||
X6502_MemHook** hookStart = nullptr;
|
||||
|
||||
|
@ -126,18 +131,23 @@ void X6502_MemHook::Remove(enum X6502_MemHook::Type type, void (*func)(unsigned
|
|||
|
||||
while (hook != nullptr)
|
||||
{
|
||||
if (hook->func == func)
|
||||
if ((hook->func == func) && (hook->userData == userData))
|
||||
{
|
||||
if (prev != nullptr)
|
||||
hook->refCount--;
|
||||
|
||||
if (hook->refCount <= 0)
|
||||
{
|
||||
prev->next = hook->next;
|
||||
if (prev != nullptr)
|
||||
{
|
||||
prev->next = hook->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
*hookStart = hook->next;
|
||||
}
|
||||
delete hook;
|
||||
//printf("LUA MemHook Removed: %p\n", func);
|
||||
}
|
||||
else
|
||||
{
|
||||
*hookStart = hook->next;
|
||||
}
|
||||
delete hook;
|
||||
//printf("LUA MemHook Removed: %p\n", func);
|
||||
return;
|
||||
}
|
||||
prev = hook;
|
||||
|
|
10
src/x6502.h
10
src/x6502.h
|
@ -97,12 +97,12 @@ class X6502_MemHook
|
|||
public:
|
||||
enum Type { Read = 0, Write, Exec } type;
|
||||
|
||||
static void Add(enum Type type, void (*func)(unsigned int address, unsigned int value) );
|
||||
static void Remove(enum Type type, void (*func)(unsigned int address, unsigned int value) );
|
||||
static void Add(enum Type type, void (*func)(unsigned int address, unsigned int value, void *userData), void *userData = nullptr );
|
||||
static void Remove(enum Type type, void (*func)(unsigned int address, unsigned int value, void *userData), void *userData = nullptr );
|
||||
|
||||
inline void call( unsigned int address, unsigned int value )
|
||||
{
|
||||
func(address, value);
|
||||
func(address, value, userData);
|
||||
|
||||
if (next != nullptr)
|
||||
{
|
||||
|
@ -110,8 +110,10 @@ class X6502_MemHook
|
|||
}
|
||||
}
|
||||
private:
|
||||
void (*func)(unsigned int address, unsigned int value) = nullptr;
|
||||
void (*func)(unsigned int address, unsigned int value, void *userData) = nullptr;
|
||||
void *userData = nullptr;
|
||||
X6502_MemHook* next = nullptr;
|
||||
int refCount = 0;
|
||||
};
|
||||
|
||||
#define _X6502H
|
||||
|
|
Loading…
Reference in New Issue