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 //---- 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) MemoryScriptObject::MemoryScriptObject(QObject* parent)
: QObject(parent) : QObject(parent)
{ {
@ -211,6 +263,12 @@ MemoryScriptObject::MemoryScriptObject(QObject* parent)
//---------------------------------------------------- //----------------------------------------------------
MemoryScriptObject::~MemoryScriptObject() MemoryScriptObject::~MemoryScriptObject()
{ {
unregisterAll();
}
//----------------------------------------------------
void MemoryScriptObject::reset()
{
unregisterAll();
} }
//---------------------------------------------------- //----------------------------------------------------
uint8_t MemoryScriptObject::readByte(int address) uint8_t MemoryScriptObject::readByte(int address)
@ -332,6 +390,207 @@ void MemoryScriptObject::setRegisterP(uint8_t v)
X.P = 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 //---- Qt Script Instance
//---------------------------------------------------- //----------------------------------------------------
QtScriptInstance::QtScriptInstance(QObject* parent) QtScriptInstance::QtScriptInstance(QObject* parent)
@ -387,6 +646,7 @@ void QtScriptInstance::resetEngine()
ui_rootWidget->deleteLater(); ui_rootWidget->deleteLater();
ui_rootWidget = nullptr; ui_rootWidget = nullptr;
} }
mem->reset();
configEngine(); configEngine();
} }
@ -434,7 +694,10 @@ int QtScriptInstance::loadScriptFile( QString filepath )
if (evalResult.isError()) if (evalResult.isError())
{ {
print(evalResult.toString()); QString msg;
msg += evalResult.property("lineNumber").toString() + ": ";
msg += evalResult.toString();
print(msg);
return -1; return -1;
} }
else else
@ -442,9 +705,6 @@ int QtScriptInstance::loadScriptFile( QString filepath )
running = true; running = true;
//printf("Script Evaluation Success!\n"); //printf("Script Evaluation Success!\n");
} }
//onFrameBeginCallback = engine->globalObject().property("onFrameBegin");
//onFrameFinishCallback = engine->globalObject().property("onFrameFinish");
//onScriptStopCallback = engine->globalObject().property("onScriptStop");
return 0; return 0;
} }
@ -459,7 +719,7 @@ void QtScriptInstance::loadObjectChildren(QJSValue& jsObject, QObject* obj)
if (!name.isEmpty()) 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); QJSValue newJsObj = engine->newQObject(child);
@ -581,6 +841,7 @@ int QtScriptInstance::call(const QString& funcName, const QJSValueList& args)
//---------------------------------------------------- //----------------------------------------------------
void QtScriptInstance::stopRunning() void QtScriptInstance::stopRunning()
{ {
FCEU_WRAPPER_LOCK();
if (running) if (running)
{ {
if (onScriptStopCallback.isCallable()) if (onScriptStopCallback.isCallable())
@ -588,7 +849,10 @@ void QtScriptInstance::stopRunning()
onScriptStopCallback.call(); onScriptStopCallback.call();
} }
running = false; running = false;
mem->reset();
} }
FCEU_WRAPPER_UNLOCK();
} }
//---------------------------------------------------- //----------------------------------------------------
void QtScriptInstance::onFrameBegin() void QtScriptInstance::onFrameBegin()

View File

@ -78,11 +78,25 @@ public:
void setEngine(QJSEngine* _engine){ engine = _engine; } void setEngine(QJSEngine* _engine){ engine = _engine; }
void setDialog(QScriptDialog_t* _dialog){ dialog = _dialog; } 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: private:
static constexpr int AddressRange = 0x10000;
QJSEngine* engine = nullptr; QJSEngine* engine = nullptr;
QScriptDialog_t* dialog = nullptr; QScriptDialog_t* dialog = nullptr;
QtScriptInstance* script = 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: public slots:
Q_INVOKABLE uint8_t readByte(int address); Q_INVOKABLE uint8_t readByte(int address);
@ -104,6 +118,13 @@ public slots:
Q_INVOKABLE void setRegisterY(uint8_t v); Q_INVOKABLE void setRegisterY(uint8_t v);
Q_INVOKABLE void setRegisterS(uint8_t v); Q_INVOKABLE void setRegisterS(uint8_t v);
Q_INVOKABLE void setRegisterP(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 class QtScriptInstance : public QObject

View File

@ -118,15 +118,15 @@ bool DemandLua()
#endif #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); 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); 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); 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]); lua_setfield(L, LUA_REGISTRYINDEX, luaMemHookTypeStrings[i]);
} }
X6502_MemHook::Add( X6502_MemHook::Read , luaReadMemHook ); X6502_MemHook::Add( X6502_MemHook::Read , luaReadMemHook , nullptr );
X6502_MemHook::Add( X6502_MemHook::Write, luaWriteMemHook ); X6502_MemHook::Add( X6502_MemHook::Write, luaWriteMemHook, nullptr );
X6502_MemHook::Add( X6502_MemHook::Exec , luaExecMemHook ); X6502_MemHook::Add( X6502_MemHook::Exec , luaExecMemHook , nullptr );
} }
// We make our thread NOW because we want it at the bottom of the stack. // We make our thread NOW because we want it at the bottom of the stack.
@ -6619,9 +6619,9 @@ void FCEU_LuaStop() {
//already killed //already killed
if (!L) return; if (!L) return;
X6502_MemHook::Remove( X6502_MemHook::Read , luaReadMemHook ); X6502_MemHook::Remove( X6502_MemHook::Read , luaReadMemHook , nullptr );
X6502_MemHook::Remove( X6502_MemHook::Write, luaWriteMemHook ); X6502_MemHook::Remove( X6502_MemHook::Write, luaWriteMemHook, nullptr );
X6502_MemHook::Remove( X6502_MemHook::Exec , luaExecMemHook ); X6502_MemHook::Remove( X6502_MemHook::Exec , luaExecMemHook , nullptr );
// Since the script is exiting, we want to prevent an infinite loop. // Since the script is exiting, we want to prevent an infinite loop.
// CallExitFunction() > HandleCallbackError() > FCEU_LuaStop() > CallExitFunction() ... // CallExitFunction() > HandleCallbackError() > FCEU_LuaStop() > CallExitFunction() ...

View File

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

View File

@ -48,7 +48,7 @@ static X6502_MemHook* readMemHook = nullptr;
static X6502_MemHook* writeMemHook = nullptr; static X6502_MemHook* writeMemHook = nullptr;
static X6502_MemHook* execMemHook = 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; 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) while (hook->next != nullptr)
{ {
if (hook->func == func) if ((hook->func == func) && (hook->userData == userData))
{ {
// Already registered // Already registered
//printf("LUA MemHook Already Registered\n"); //printf("LUA MemHook Already Registered\n");
hook->refCount++;
return; return;
} }
hook = hook->next; 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(); X6502_MemHook* newHook = new X6502_MemHook();
newHook->type = type; newHook->type = type;
newHook->func = func; newHook->func = func;
newHook->userData = userData;
newHook->refCount = 1;
hook->next = newHook; hook->next = newHook;
} }
else else
@ -93,12 +96,14 @@ void X6502_MemHook::Add(enum X6502_MemHook::Type type, void (*func)(unsigned int
X6502_MemHook* newHook = new X6502_MemHook(); X6502_MemHook* newHook = new X6502_MemHook();
newHook->type = type; newHook->type = type;
newHook->func = func; newHook->func = func;
newHook->userData = userData;
newHook->refCount = 1;
*hookStart = newHook; *hookStart = newHook;
} }
//printf("LUA MemHook Added: %p\n", func); //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; X6502_MemHook** hookStart = nullptr;
@ -126,18 +131,23 @@ void X6502_MemHook::Remove(enum X6502_MemHook::Type type, void (*func)(unsigned
while (hook != nullptr) 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; return;
} }
prev = hook; prev = hook;

View File

@ -97,12 +97,12 @@ class X6502_MemHook
public: public:
enum Type { Read = 0, Write, Exec } type; enum Type { Read = 0, Write, Exec } type;
static void Add(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) ); 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 ) inline void call( unsigned int address, unsigned int value )
{ {
func(address, value); func(address, value, userData);
if (next != nullptr) if (next != nullptr)
{ {
@ -110,8 +110,10 @@ class X6502_MemHook
} }
} }
private: 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; X6502_MemHook* next = nullptr;
int refCount = 0;
}; };
#define _X6502H #define _X6502H