Added memory hooks to JS engine.

This commit is contained in:
harry 2024-01-26 22:57:54 -05:00
parent 4f604b7708
commit a02ae8d3e0
6 changed files with 328 additions and 31 deletions

View File

@ -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()

View File

@ -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

View File

@ -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() ...

View File

@ -63,7 +63,7 @@ class timeStampModule
public:
timeStampModule(void)
{
printf("timeStampModuleInit\n");
//printf("timeStampModuleInit\n");
#if defined(WIN32)
timeStampRecord::qpcCalibrate();
#endif

View File

@ -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;

View File

@ -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