Compare commits
No commits in common. "1d0830160bb2b985a35bba8198ab13f0b8521bd4" and "4538cd182d05052f118f770a0e2f6344d41ce61b" have entirely different histories.
1d0830160b
...
4538cd182d
|
@ -10,9 +10,24 @@ if SERVER then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
----[[
|
||||||
|
testobj = LCPUNative.CreateTest()
|
||||||
|
print(testobj:Test())
|
||||||
|
print(testobj.Variable)
|
||||||
|
|
||||||
|
testobj.MemberVariable = 32.9
|
||||||
|
print(testobj.MemberVariable)
|
||||||
|
|
||||||
|
testobj.Variable = 32.1 -- this should fial
|
||||||
|
testobj.Test = nil;
|
||||||
|
|
||||||
|
print(testobj.Variable)
|
||||||
|
|
||||||
|
print(testobj.Name)
|
||||||
|
--]]
|
||||||
|
|
||||||
-- rapid iteration requires rapid solutions
|
-- rapid iteration requires rapid solutions
|
||||||
--[[
|
--[[
|
||||||
device = LCPUNative.CreateDevice(0x100000f0, 0x10)
|
device = LCPUNative.CreateDevice(0x100000f0, 0x10)
|
||||||
device.a = 12
|
device.a = 12
|
||||||
device.apple = {}
|
device.apple = {}
|
||||||
|
@ -21,12 +36,8 @@ if SERVER then
|
||||||
--print(device.a)
|
--print(device.a)
|
||||||
--print(device.apple)
|
--print(device.apple)
|
||||||
|
|
||||||
print("name property is " .. device.Name)
|
|
||||||
print("base property is " .. device.Base)
|
|
||||||
print("size property is " .. device.Size)
|
|
||||||
|
|
||||||
function device:Clock()
|
function device:Clock()
|
||||||
print(self.Base)
|
print("a")
|
||||||
end
|
end
|
||||||
|
|
||||||
function device:Peek(address)
|
function device:Peek(address)
|
||||||
|
@ -59,7 +70,6 @@ end
|
||||||
cpu:Cycle();cpu:Cycle();cpu:Cycle();cpu:Cycle();
|
cpu:Cycle();cpu:Cycle();cpu:Cycle();cpu:Cycle();
|
||||||
cpu:Cycle();cpu:Cycle();cpu:Cycle();cpu:Cycle();
|
cpu:Cycle();cpu:Cycle();cpu:Cycle();cpu:Cycle();
|
||||||
cpu:Cycle();cpu:Cycle();cpu:Cycle();cpu:Cycle();
|
cpu:Cycle();cpu:Cycle();cpu:Cycle();cpu:Cycle();
|
||||||
--]]
|
]]
|
||||||
|
|
||||||
AddCSLuaFile("entities/gmod_lcpu_cpu.lua")
|
AddCSLuaFile("entities/gmod_lcpu_cpu.lua")
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,46 +1,86 @@
|
||||||
#include "LcpuGlobals.hpp"
|
#include "LcpuGlobals.hpp"
|
||||||
|
|
||||||
#include "GarrysMod/Lua/Interface.h"
|
#include "GarrysMod/Lua/Interface.h"
|
||||||
|
#include "GarrysMod/Lua/LuaBase.h"
|
||||||
#include "LuaCpu.hpp"
|
#include "LuaCpu.hpp"
|
||||||
#include "LuaDevice.hpp"
|
#include "LuaDevice.hpp"
|
||||||
|
#include "LuaHelpers.hpp"
|
||||||
|
|
||||||
namespace lcpu {
|
/// test for the "new" lua object system
|
||||||
LUA_FUNCTION(LCPUNative_CreateCPU) {
|
struct TestLuaObject : public lcpu::lua::LuaObject<TestLuaObject> {
|
||||||
LUA->CheckType(1, GarrysMod::Lua::Type::Number);
|
static const char* Name() { return "TestLuaObject"; }
|
||||||
auto memorySize = static_cast<u32>(LUA->GetNumber(1));
|
|
||||||
|
|
||||||
// TODO: There's probably a way to like, ensure a per-player max.
|
static void RegisterClass(GarrysMod::Lua::ILuaBase* LUA) {
|
||||||
if(memorySize > (64 * 1024 * 1024))
|
RegisterClassStart(LUA);
|
||||||
LUA->ThrowError("Over RAM size limit.");
|
// Metamethods can be registered here; in this case, our test object doesn't need any
|
||||||
|
RegisterClassEnd(LUA);
|
||||||
|
|
||||||
LuaCpu::Create(LUA, memorySize);
|
// Register methods. Maybe later I'll do some crazy template stuff; for now this is pretty barebones.
|
||||||
return 1;
|
RegisterMethod("Test", &Test);
|
||||||
|
RegisterGetter("Variable", [](GarrysMod::Lua::ILuaBase* LUA) { LUA->PushNumber(32.6); });
|
||||||
|
RegisterGetter("MemberVariable", [](GarrysMod::Lua::ILuaBase* LUA) {
|
||||||
|
auto self = TestLuaObject::FromLua(LUA, 1);
|
||||||
|
LUA->PushNumber(self->n);
|
||||||
|
});
|
||||||
|
RegisterSetter("MemberVariable", [](GarrysMod::Lua::ILuaBase* LUA) {
|
||||||
|
// 3 will be the assignment stack position
|
||||||
|
auto self = TestLuaObject::FromLua(LUA, 1);
|
||||||
|
self->n = LUA->GetNumber(3);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
LUA_FUNCTION(LCPUNative_CreateDevice) {
|
LUA_MEMBER_FUNCTION(Test)
|
||||||
auto base = LUA->CheckNumber(1);
|
double n;
|
||||||
auto size = LUA->CheckNumber(2);
|
};
|
||||||
lucore::LogInfo("Creating Lua device object mapped @ 0x{:08x} with size 0x{:08x}", static_cast<riscv::Address>(base),
|
|
||||||
static_cast<riscv::Address>(size));
|
|
||||||
LuaDevice::Create(LUA, static_cast<riscv::Address>(base), static_cast<riscv::Address>(size));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GlobalsBind(GarrysMod::Lua::ILuaBase* LUA) {
|
LUA_MEMBER_FUNCTION_IMPLEMENT(TestLuaObject, Test) {
|
||||||
LuaCpu::RegisterClass(LUA);
|
LUA->PushString("hi :)");
|
||||||
LuaDevice::RegisterClass(LUA);
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
// clang-format off
|
LUA_FUNCTION(LCPUNative_CreateCPU) {
|
||||||
LUA->PushSpecial(GarrysMod::Lua::SPECIAL_GLOB);
|
LUA->CheckType(1, GarrysMod::Lua::Type::Number);
|
||||||
LUA->CreateTable();
|
auto memorySize = static_cast<u32>(LUA->GetNumber(1));
|
||||||
LUA->PushNumber(LCPU_MODULE_VERSION);
|
|
||||||
LUA->SetField(-2, "ModuleVersion");
|
|
||||||
|
|
||||||
LUA_SET_C_FUNCTION_NAME(LCPUNative_CreateCPU, "CreateCPU");
|
// TODO: There's probably a way to like, ensure a per-player max.
|
||||||
LUA_SET_C_FUNCTION_NAME(LCPUNative_CreateDevice, "CreateDevice");
|
if(memorySize > (64 * 1024 * 1024))
|
||||||
LUA->SetField(-2, "LCPUNative");
|
LUA->ThrowError("Over RAM size limit.");
|
||||||
LUA->Pop();
|
|
||||||
// clang-format on
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace lcpu
|
LuaCpu::Create(LUA, memorySize);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
LUA_FUNCTION(LCPUNative_CreateDevice) {
|
||||||
|
auto base = LUA->CheckNumber(1);
|
||||||
|
auto size = LUA->CheckNumber(2);
|
||||||
|
lucore::LogInfo("Creating Lua device object mapped @ 0x{:08x} with size 0x{:08x}", static_cast<riscv::Address>(base),
|
||||||
|
static_cast<riscv::Address>(size));
|
||||||
|
LuaDevice::Create(LUA, static_cast<riscv::Address>(base), static_cast<riscv::Address>(size));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
LUA_FUNCTION(LCPUNative_CreateTest) {
|
||||||
|
TestLuaObject::Create(LUA);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GlobalsBind(GarrysMod::Lua::ILuaBase* LUA) {
|
||||||
|
LuaCpu::Bind(LUA);
|
||||||
|
LuaDevice::Bind(LUA);
|
||||||
|
|
||||||
|
TestLuaObject::RegisterClass(LUA);
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
LUA->PushSpecial(GarrysMod::Lua::SPECIAL_GLOB);
|
||||||
|
LUA->CreateTable();
|
||||||
|
LUA->PushNumber(LCPU_MODULE_VERSION);
|
||||||
|
LUA->SetField(-2, "ModuleVersion");
|
||||||
|
|
||||||
|
LUA_SET_C_FUNCTION_NAME(LCPUNative_CreateCPU, "CreateCPU");
|
||||||
|
LUA_SET_C_FUNCTION_NAME(LCPUNative_CreateDevice, "CreateDevice");
|
||||||
|
|
||||||
|
LUA_SET_C_FUNCTION_NAME(LCPUNative_CreateTest, "CreateTest");
|
||||||
|
LUA->SetField(-2, "LCPUNative");
|
||||||
|
LUA->Pop();
|
||||||
|
// clang-format on
|
||||||
|
}
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
#include "LuaHelpers.hpp"
|
#include "LuaHelpers.hpp"
|
||||||
|
|
||||||
namespace lcpu {
|
|
||||||
/// This should be bumped on any incompatible change to the native bindings
|
/// This should be bumped on any incompatible change to the native bindings
|
||||||
/// that would break older Lua code, or requires newer Lua code to run.
|
/// that would break older Lua code, or requires newer Lua code to run.
|
||||||
#define LCPU_MODULE_VERSION 1
|
#define LCPU_MODULE_VERSION 1
|
||||||
|
|
||||||
void GlobalsBind(GarrysMod::Lua::ILuaBase* LUA);
|
void GlobalsBind(GarrysMod::Lua::ILuaBase* LUA);
|
||||||
|
|
||||||
} // namespace lcpu
|
|
||||||
|
|
|
@ -3,9 +3,10 @@
|
||||||
#include <lucore/Logger.hpp>
|
#include <lucore/Logger.hpp>
|
||||||
|
|
||||||
#include "LuaDevice.hpp"
|
#include "LuaDevice.hpp"
|
||||||
|
#include "LuaHelpers.hpp"
|
||||||
|
|
||||||
|
// this is temporary from the thing
|
||||||
|
|
||||||
// this is temporary from the test harness, and will be replaced
|
|
||||||
// at some point.
|
|
||||||
/// simple 16550 UART implementation
|
/// simple 16550 UART implementation
|
||||||
struct SimpleUartDevice : public riscv::Bus::MmioDevice {
|
struct SimpleUartDevice : public riscv::Bus::MmioDevice {
|
||||||
constexpr static riscv::Address BASE_ADDRESS = 0x10000000;
|
constexpr static riscv::Address BASE_ADDRESS = 0x10000000;
|
||||||
|
@ -30,90 +31,97 @@ struct SimpleUartDevice : public riscv::Bus::MmioDevice {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace lcpu {
|
LUA_CLASS_BIND_VARIABLES_IMPLEMENT(LuaCpu);
|
||||||
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaCpu, PoweredOn) {
|
|
||||||
auto self = LuaCpu::FromLua(LUA, 1);
|
|
||||||
LUA->PushBool(self->poweredOn);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaCpu, Cycle) {
|
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaCpu, PoweredOn) {
|
||||||
auto self = LuaCpu::FromLua(LUA, 1);
|
auto self = LUA_CLASS_GET(LuaCpu)(1);
|
||||||
if(!self->poweredOn)
|
LUA->PushBool(self->poweredOn);
|
||||||
return 0;
|
return 1;
|
||||||
self->system->Step();
|
}
|
||||||
|
|
||||||
|
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaCpu, Cycle) {
|
||||||
|
auto self = LUA_CLASS_GET(LuaCpu)(1);
|
||||||
|
if(!self->poweredOn)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
self->system->Step();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaCpu, PowerOff) {
|
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaCpu, PowerOff) {
|
||||||
auto self = LuaCpu::FromLua(LUA, 1);
|
auto self = LUA_CLASS_GET(LuaCpu)(1);
|
||||||
if(!self->poweredOn)
|
if(!self->poweredOn)
|
||||||
return 0;
|
|
||||||
|
|
||||||
self->poweredOn = false;
|
|
||||||
self->system->bus->Reset();
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaCpu, PowerOn) {
|
self->poweredOn = false;
|
||||||
auto self = LuaCpu::FromLua(LUA, 1);
|
self->system->bus->Reset();
|
||||||
if(self->poweredOn)
|
return 0;
|
||||||
return 0;
|
}
|
||||||
|
|
||||||
self->poweredOn = true;
|
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaCpu, PowerOn) {
|
||||||
self->system->bus->Reset();
|
auto self = LUA_CLASS_GET(LuaCpu)(1);
|
||||||
|
if(self->poweredOn)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
self->poweredOn = true;
|
||||||
|
self->system->bus->Reset();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaCpu, Reset) {
|
||||||
|
auto self = LUA_CLASS_GET(LuaCpu)(1);
|
||||||
|
self->system->bus->Reset();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaCpu, AttachDevice) {
|
||||||
|
auto self = LUA_CLASS_GET(LuaCpu)(1);
|
||||||
|
auto device = LUA_CLASS_GET(LuaDevice)(2);
|
||||||
|
|
||||||
|
// Attach it
|
||||||
|
LUA->PushBool(self->system->bus->AttachDevice(static_cast<riscv::Bus::Device*>(device)));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LuaCpu::Bind(GarrysMod::Lua::ILuaBase* LUA) {
|
||||||
|
// clang-format off
|
||||||
|
LUA_CLASS_BIND_BEGIN(LuaCpu);
|
||||||
|
LUA_SET_C_FUNCTION(PoweredOn);
|
||||||
|
LUA_SET_C_FUNCTION(Cycle);
|
||||||
|
LUA_SET_C_FUNCTION(PowerOff);
|
||||||
|
LUA_SET_C_FUNCTION(PowerOn);
|
||||||
|
LUA_SET_C_FUNCTION(Reset);
|
||||||
|
LUA_SET_C_FUNCTION(AttachDevice);
|
||||||
|
LUA_CLASS_BIND_END();
|
||||||
|
// clang-format on
|
||||||
|
}
|
||||||
|
|
||||||
|
void LuaCpu::Create(GarrysMod::Lua::ILuaBase* LUA, u32 memorySize) {
|
||||||
|
auto cpuWrapper = new LuaCpu(memorySize);
|
||||||
|
|
||||||
|
// lame test code. this WILL be removed, I just want this for a quick test
|
||||||
|
cpuWrapper->system->bus->AttachDevice(new SimpleUartDevice);
|
||||||
|
auto fp = std::fopen("/home/lily/test-gmod.bin", "rb");
|
||||||
|
if(fp) {
|
||||||
|
std::fseek(fp, 0, SEEK_END);
|
||||||
|
auto len = std::ftell(fp);
|
||||||
|
std::fseek(fp, 0, SEEK_SET);
|
||||||
|
|
||||||
|
std::fread(cpuWrapper->system->ram->Raw(), 1, len, fp);
|
||||||
|
std::fclose(fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaCpu, Reset) {
|
LUA->PushUserType(cpuWrapper, __lua_typeid);
|
||||||
auto self = LuaCpu::FromLua(LUA, 1);
|
}
|
||||||
self->system->bus->Reset();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaCpu, AttachDevice) {
|
LuaCpu::LuaCpu(u32 memorySize) {
|
||||||
auto self = LuaCpu::FromLua(LUA, 1);
|
poweredOn = true;
|
||||||
auto device = LuaDevice::FromLua(LUA, 2);
|
system = riscv::System::Create(memorySize);
|
||||||
|
system->OnPowerOff = [&]() {
|
||||||
|
poweredOn = false;
|
||||||
|
system->bus->Reset();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Attach it
|
LuaCpu::~LuaCpu() {
|
||||||
LUA->PushBool(self->system->bus->AttachDevice(static_cast<riscv::Bus::Device*>(device)));
|
delete system;
|
||||||
return 1;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void LuaCpu::RegisterClass(GarrysMod::Lua::ILuaBase* LUA) {
|
|
||||||
RegisterClassStart(LUA);
|
|
||||||
|
|
||||||
RegisterMethod("PoweredOn", PoweredOn);
|
|
||||||
RegisterMethod("Cycle", Cycle);
|
|
||||||
RegisterMethod("PowerOff", PowerOff);
|
|
||||||
RegisterMethod("PowerOn", PowerOn);
|
|
||||||
RegisterMethod("Reset", Reset);
|
|
||||||
RegisterMethod("AttachDevice", AttachDevice);
|
|
||||||
}
|
|
||||||
|
|
||||||
LuaCpu::LuaCpu(u32 memorySize) {
|
|
||||||
poweredOn = true;
|
|
||||||
system = riscv::System::Create(memorySize);
|
|
||||||
system->OnPowerOff = [&]() {
|
|
||||||
poweredOn = false;
|
|
||||||
system->bus->Reset();
|
|
||||||
};
|
|
||||||
|
|
||||||
// lame test code. this WILL be removed, I just want this for a quick test
|
|
||||||
system->bus->AttachDevice(new SimpleUartDevice);
|
|
||||||
auto fp = std::fopen("/home/lily/test-gmod.bin", "rb");
|
|
||||||
if(fp) {
|
|
||||||
std::fseek(fp, 0, SEEK_END);
|
|
||||||
auto len = std::ftell(fp);
|
|
||||||
std::fseek(fp, 0, SEEK_SET);
|
|
||||||
|
|
||||||
std::fread(system->ram->Raw(), 1, len, fp);
|
|
||||||
std::fclose(fp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LuaCpu::~LuaCpu() {
|
|
||||||
delete system;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace lcpu
|
|
||||||
|
|
|
@ -1,32 +1,31 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <riscv/System.hpp>
|
#include <riscv/System.hpp>
|
||||||
|
|
||||||
#include "LuaObject.hpp"
|
#include "LuaHelpers.hpp"
|
||||||
|
|
||||||
namespace lcpu {
|
/// Bindings of [riscv::System] to Lua.
|
||||||
/// Bindings of [riscv::System] to Lua.
|
struct LuaCpu {
|
||||||
struct LuaCpu : public lua::LuaObject<LuaCpu> {
|
/// Lua binding stuff
|
||||||
/// Lua binding stuff
|
static void Bind(GarrysMod::Lua::ILuaBase* LUA);
|
||||||
constexpr static const char* Name() { return "LuaCpu"; }
|
static void Create(GarrysMod::Lua::ILuaBase* LUA, u32 memorySize);
|
||||||
static void RegisterClass(GarrysMod::Lua::ILuaBase* LUA);
|
|
||||||
|
|
||||||
protected:
|
private:
|
||||||
friend struct lua::LuaObject<LuaCpu>;
|
|
||||||
LuaCpu(u32 memorySize);
|
|
||||||
~LuaCpu();
|
|
||||||
|
|
||||||
private:
|
LuaCpu(u32 memorySize);
|
||||||
LUA_MEMBER_FUNCTION(PoweredOn); // Check if the CPU is powered on
|
~LuaCpu();
|
||||||
LUA_MEMBER_FUNCTION(Cycle); // do a single cycle (called internally by LCPU entity)
|
|
||||||
LUA_MEMBER_FUNCTION(PowerOff); // power off and reset the LCPU
|
|
||||||
LUA_MEMBER_FUNCTION(PowerOn); // power on the LCPU
|
|
||||||
LUA_MEMBER_FUNCTION(Reset); // reset the LCPU
|
|
||||||
LUA_MEMBER_FUNCTION(AttachDevice); // attach a LuaDevice to this cpu
|
|
||||||
|
|
||||||
// member variables
|
LUA_MEMBER_FUNCTION(PoweredOn); // Check if the CPU is powered on
|
||||||
riscv::System* system;
|
LUA_MEMBER_FUNCTION(Cycle); // do a single cycle (called internally by LCPU entity)
|
||||||
bool poweredOn;
|
LUA_MEMBER_FUNCTION(PowerOff); // power off and reset the LCPU
|
||||||
};
|
LUA_MEMBER_FUNCTION(PowerOn); // power on the LCPU
|
||||||
|
LUA_MEMBER_FUNCTION(Reset); // reset the LCPU
|
||||||
|
LUA_MEMBER_FUNCTION(AttachDevice); // attach a LuaDevice to this cpu
|
||||||
|
|
||||||
} // namespace lcpu
|
// class binding stuff
|
||||||
|
LUA_CLASS_BIND_VARIABLES(private);
|
||||||
|
|
||||||
|
// member variables
|
||||||
|
riscv::System* system;
|
||||||
|
bool poweredOn;
|
||||||
|
};
|
||||||
|
|
|
@ -1,112 +1,157 @@
|
||||||
#include "LuaDevice.hpp"
|
#include "LuaDevice.hpp"
|
||||||
|
|
||||||
namespace lcpu {
|
LUA_CLASS_BIND_VARIABLES_IMPLEMENT(LuaDevice);
|
||||||
void LuaDevice::RegisterClass(GarrysMod::Lua::ILuaBase* LUA) {
|
|
||||||
RegisterClassStart(LUA);
|
|
||||||
|
|
||||||
RegisterGetter("Base", [](GarrysMod::Lua::ILuaBase* LUA) {
|
bool LuaDevice::Clocked() const {
|
||||||
auto self = LuaDevice::FromLua(LUA, 1);
|
// no real good rationale for checking here,
|
||||||
LUA->PushNumber(static_cast<double>(self->base));
|
// since function calling does bail out properly
|
||||||
});
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
RegisterGetter("Size", [](GarrysMod::Lua::ILuaBase* LUA) {
|
void LuaDevice::Clock() {
|
||||||
auto self = LuaDevice::FromLua(LUA, 1);
|
// clang-format off
|
||||||
LUA->PushNumber(static_cast<double>(self->size));
|
LuaState->ReferencePush(tableReference);
|
||||||
});
|
LuaState->GetField(-1,"Clock");
|
||||||
}
|
if(LuaState->GetType(-1) == GarrysMod::Lua::Type::Function) {
|
||||||
|
LuaState->Push(-2); // 'self' argument
|
||||||
bool LuaDevice::Clocked() const {
|
LuaState->Call(1, 0);
|
||||||
// no real good rationale for checking here,
|
|
||||||
// since function calling does bail out properly
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LuaDevice::Clock() {
|
|
||||||
// clang-format off
|
|
||||||
lua->ReferencePush(GetTableReference());
|
|
||||||
lua->GetField(-1,"Clock");
|
|
||||||
if(lua->GetType(-1) == GarrysMod::Lua::Type::Function) {
|
|
||||||
lua->Push(-2); // 'self' argument
|
|
||||||
lua->Call(1, 0);
|
|
||||||
} else {
|
|
||||||
lua->Pop(); // pop the Clock function off the stack
|
|
||||||
}
|
|
||||||
lua->Pop(); // pop the reference
|
|
||||||
// clang-format on
|
|
||||||
}
|
|
||||||
|
|
||||||
riscv::Address LuaDevice::Base() const {
|
|
||||||
return base;
|
|
||||||
}
|
|
||||||
|
|
||||||
riscv::Address LuaDevice::Size() const {
|
|
||||||
return base;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 LuaDevice::Peek(riscv::Address address) {
|
|
||||||
// clang-format off
|
|
||||||
lua->ReferencePush(GetTableReference());
|
|
||||||
lua->GetField(-1,"Peek");
|
|
||||||
if(lua->GetType(-1) == GarrysMod::Lua::Type::Function) {
|
|
||||||
lua->Push(-2); // 'self' argument
|
|
||||||
lua->PushNumber(static_cast<double>(address));
|
|
||||||
lua->Call(2, 1);
|
|
||||||
|
|
||||||
auto result = static_cast<u32>(lua->GetNumber(-1));
|
|
||||||
lua->Pop(2); // pop result and the table off
|
|
||||||
return result;
|
|
||||||
} else {
|
|
||||||
lua->Pop(); // pop whatever Peek is off the stack
|
|
||||||
}
|
|
||||||
lua->Pop(); // pop the table reference
|
|
||||||
// clang-format on
|
|
||||||
return 0xffffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LuaDevice::Poke(riscv::Address address, u32 value) {
|
|
||||||
// clang-format off
|
|
||||||
lua->ReferencePush(GetTableReference());
|
|
||||||
lua->GetField(-1,"Poke");
|
|
||||||
if(lua->GetType(-1) == GarrysMod::Lua::Type::Function) {
|
|
||||||
lua->Push(-2); // 'self' argument
|
|
||||||
lua->PushNumber(static_cast<double>(address));
|
|
||||||
lua->PushNumber(static_cast<double>(value));
|
|
||||||
lua->Call(3, 0);
|
|
||||||
} else {
|
|
||||||
lua->Pop(); // pop whatever Peek is
|
|
||||||
}
|
|
||||||
lua->Pop(); // pop the table reference
|
|
||||||
// clang-format on
|
|
||||||
}
|
|
||||||
|
|
||||||
void LuaDevice::Reset() {
|
|
||||||
// clang-format off
|
|
||||||
lua->ReferencePush(GetTableReference());
|
|
||||||
lua->GetField(-1,"Reset");
|
|
||||||
if(lua->GetType(-1) == GarrysMod::Lua::Type::Function) {
|
|
||||||
lua->Push(-2); // 'self' argument
|
|
||||||
lua->Call(1, 0);
|
|
||||||
} else {
|
} else {
|
||||||
lua->Pop(); // pop whatever reset is
|
LuaState->Pop(); // pop the Clock function off the stack
|
||||||
}
|
}
|
||||||
lua->Pop(); // pop the reference
|
LuaState->Pop(); // pop the reference
|
||||||
// clang-format on
|
// clang-format off
|
||||||
}
|
}
|
||||||
|
|
||||||
LuaDevice::LuaDevice(riscv::Address base, riscv::Address size) : base(base), size(size) {
|
riscv::Address LuaDevice::Base() const {
|
||||||
}
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
void LuaDevice::AfterLuaInit() {
|
riscv::Address LuaDevice::Size() const {
|
||||||
// Our Lua callbacks only get the table, not the actual userdata,
|
return base;
|
||||||
// ao we have to mirror things :( kinda sucks, but meh
|
}
|
||||||
|
|
||||||
// clang-format off
|
u32 LuaDevice::Peek(riscv::Address address) {
|
||||||
lua->ReferencePush(GetTableReference());
|
// clang-format off
|
||||||
lua->PushNumber(static_cast<double>(base));
|
LuaState->ReferencePush(tableReference);
|
||||||
lua->SetField(-2, "Base");
|
LuaState->GetField(-1,"Peek");
|
||||||
lua->PushNumber(static_cast<double>(base));
|
if(LuaState->GetType(-1) == GarrysMod::Lua::Type::Function) {
|
||||||
lua->SetField(-2, "Size");
|
LuaState->Push(-2); // 'self' argument
|
||||||
lua->Pop();
|
LuaState->PushNumber(static_cast<double>(address));
|
||||||
// clang-format on
|
LuaState->Call(2, 1);
|
||||||
}
|
|
||||||
} // namespace lcpu
|
auto result = static_cast<u32>(LuaState->GetNumber(-1));
|
||||||
|
LuaState->Pop(2); // pop result and the table off
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
LuaState->Pop(); // pop whatever Peek is off the stack
|
||||||
|
}
|
||||||
|
LuaState->Pop(); // pop the table reference
|
||||||
|
// clang-format on
|
||||||
|
return 0xffffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LuaDevice::Poke(riscv::Address address, u32 value) {
|
||||||
|
// clang-format off
|
||||||
|
LuaState->ReferencePush(tableReference);
|
||||||
|
LuaState->GetField(-1,"Poke");
|
||||||
|
if(LuaState->GetType(-1) == GarrysMod::Lua::Type::Function) {
|
||||||
|
LuaState->Push(-2); // 'self' argument
|
||||||
|
LuaState->PushNumber(static_cast<double>(address));
|
||||||
|
LuaState->PushNumber(static_cast<double>(value));
|
||||||
|
LuaState->Call(3, 0);
|
||||||
|
} else {
|
||||||
|
LuaState->Pop(); // pop whatever Peek is
|
||||||
|
}
|
||||||
|
LuaState->Pop(); // pop the table reference
|
||||||
|
// clang-format on
|
||||||
|
}
|
||||||
|
|
||||||
|
void LuaDevice::Reset() {
|
||||||
|
// clang-format off
|
||||||
|
LuaState->ReferencePush(tableReference);
|
||||||
|
LuaState->GetField(-1,"Reset");
|
||||||
|
if(LuaState->GetType(-1) == GarrysMod::Lua::Type::Function) {
|
||||||
|
LuaState->Push(-2); // 'self' argument
|
||||||
|
LuaState->Call(1, 0);
|
||||||
|
} else {
|
||||||
|
LuaState->Pop(); // pop whatever reset is
|
||||||
|
}
|
||||||
|
LuaState->Pop(); // pop the reference
|
||||||
|
// clang-format on
|
||||||
|
}
|
||||||
|
|
||||||
|
LuaDevice::LuaDevice(riscv::Address base, riscv::Address size) : base(base), size(size) {
|
||||||
|
}
|
||||||
|
|
||||||
|
LuaDevice::~LuaDevice() {
|
||||||
|
// Free all refererences
|
||||||
|
if(tableReference == -1)
|
||||||
|
LuaState->ReferenceFree(tableReference);
|
||||||
|
}
|
||||||
|
|
||||||
|
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaDevice, __index) {
|
||||||
|
auto self = LUA_CLASS_GET(LuaDevice)(1);
|
||||||
|
// lucore::LogInfo("metamethod __index call");
|
||||||
|
|
||||||
|
// TODO: before moving this to a shared lua object class thing
|
||||||
|
// and moving the CPU class to use this way of doing things
|
||||||
|
// I should probably try and like, add stuff to ensure native
|
||||||
|
// methods can be registered as well,,
|
||||||
|
LUA->ReferencePush(self->tableReference);
|
||||||
|
LUA->Push(2);
|
||||||
|
LUA->GetTable(-2);
|
||||||
|
return 1; // the value
|
||||||
|
}
|
||||||
|
|
||||||
|
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaDevice, __newindex) {
|
||||||
|
auto self = LUA_CLASS_GET(LuaDevice)(1);
|
||||||
|
// lucore::LogInfo("metamethod __newindex call");
|
||||||
|
|
||||||
|
// Always push onto the table.
|
||||||
|
// TODO: This function
|
||||||
|
// should error on attempt to __newindex any native methods
|
||||||
|
// (when moved to a shared place)
|
||||||
|
LUA->ReferencePush(self->tableReference);
|
||||||
|
LUA->Push(2);
|
||||||
|
LUA->Push(3);
|
||||||
|
LUA->SetTable(-3);
|
||||||
|
LUA->Pop();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LuaDevice::Bind(GarrysMod::Lua::ILuaBase* LUA) {
|
||||||
|
// clang-format off
|
||||||
|
//LUA_CLASS_BIND_BEGIN(LuaDevice)
|
||||||
|
__lua_typeid = LUA->CreateMetaTable("LuaDevice");
|
||||||
|
LUA->PushSpecial(GarrysMod::Lua::SPECIAL_REG);
|
||||||
|
LUA->PushNumber(__lua_typeid);
|
||||||
|
LUA->SetField(-2, "LuaDevice__typeid");
|
||||||
|
LUA->Pop(); /* pop registry */
|
||||||
|
|
||||||
|
LUA_SET_C_FUNCTION(__gc)
|
||||||
|
LUA_SET_C_FUNCTION(__index)
|
||||||
|
LUA_SET_C_FUNCTION(__newindex)
|
||||||
|
LUA_CLASS_BIND_END();
|
||||||
|
// clang-format on
|
||||||
|
}
|
||||||
|
|
||||||
|
void LuaDevice::Create(GarrysMod::Lua::ILuaBase* LUA, riscv::Address base, riscv::Address size) {
|
||||||
|
auto device = new LuaDevice(base, size);
|
||||||
|
device->LuaState = LUA;
|
||||||
|
|
||||||
|
LUA->CreateTable();
|
||||||
|
device->tableReference = LUA->ReferenceCreate();
|
||||||
|
LUA->Pop();
|
||||||
|
|
||||||
|
// push base/size properties for lua to have a looksee at !
|
||||||
|
// ideally these should be handled as metamethods in __index,
|
||||||
|
// but i don't quite feel like making gmod sol2 yet /shrug
|
||||||
|
LUA->ReferencePush(device->tableReference);
|
||||||
|
LUA->PushNumber(static_cast<double>(base));
|
||||||
|
LUA->SetField(-2, "Base");
|
||||||
|
LUA->PushNumber(static_cast<double>(base));
|
||||||
|
LUA->SetField(-2, "Size");
|
||||||
|
LUA->Pop();
|
||||||
|
|
||||||
|
LUA->PushUserType(device, __lua_typeid);
|
||||||
|
}
|
||||||
|
|
|
@ -3,35 +3,38 @@
|
||||||
#include <riscv/Bus.hpp>
|
#include <riscv/Bus.hpp>
|
||||||
|
|
||||||
#include "LuaHelpers.hpp"
|
#include "LuaHelpers.hpp"
|
||||||
#include "LuaObject.hpp"
|
|
||||||
|
|
||||||
namespace lcpu {
|
/// A work-in-progress binding of [riscv::Bus::MmioDevice] to lua
|
||||||
/// A work-in-progress binding of [riscv::Bus::MmioDevice] to lua
|
struct LuaDevice : public riscv::Bus::MmioDevice {
|
||||||
struct LuaDevice : public riscv::Bus::MmioDevice, lcpu::lua::LuaObject<LuaDevice> {
|
/// Lua binding stuff
|
||||||
/// Lua binding stuff
|
static void Bind(GarrysMod::Lua::ILuaBase* LUA);
|
||||||
constexpr static const char* Name() { return "LuaDevice"; }
|
static void Create(GarrysMod::Lua::ILuaBase* LUA, riscv::Address base, riscv::Address size);
|
||||||
static void RegisterClass(GarrysMod::Lua::ILuaBase* LUA);
|
|
||||||
|
|
||||||
// [riscv::Bus::MmioDevice] implementation
|
~LuaDevice();
|
||||||
bool Clocked() const override;
|
|
||||||
void Clock() override;
|
|
||||||
void Reset() override;
|
|
||||||
|
|
||||||
riscv::Address Base() const override;
|
bool Clocked() const override;
|
||||||
riscv::Address Size() const override;
|
void Clock() override;
|
||||||
|
void Reset() override;
|
||||||
|
|
||||||
u32 Peek(riscv::Address address) override;
|
riscv::Address Base() const override;
|
||||||
void Poke(riscv::Address address, u32 value) override;
|
riscv::Address Size() const override;
|
||||||
|
|
||||||
protected:
|
u32 Peek(riscv::Address address) override;
|
||||||
friend lcpu::lua::LuaObject<LuaDevice>;
|
void Poke(riscv::Address address, u32 value) override;
|
||||||
LuaDevice(riscv::Address base, riscv::Address size);
|
|
||||||
~LuaDevice() = default;
|
|
||||||
|
|
||||||
void AfterLuaInit() override;
|
private:
|
||||||
|
// class binding stuff
|
||||||
|
LUA_CLASS_BIND_VARIABLES(private);
|
||||||
|
|
||||||
private:
|
LUA_MEMBER_FUNCTION(__index);
|
||||||
riscv::Address base {};
|
LUA_MEMBER_FUNCTION(__newindex);
|
||||||
riscv::Address size {};
|
|
||||||
};
|
LuaDevice(riscv::Address base, riscv::Address size);
|
||||||
} // namespace lcpu
|
|
||||||
|
riscv::Address base {};
|
||||||
|
riscv::Address size {};
|
||||||
|
GarrysMod::Lua::ILuaBase* LuaState;
|
||||||
|
|
||||||
|
// this should be a common type tbh
|
||||||
|
int tableReference = -1;
|
||||||
|
};
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
//! Helpers for binding Lua and C++.
|
//! Helpers for lua binding
|
||||||
//! If you want to bind a C++ class to Lua, see the
|
|
||||||
//! [lcpu::lua::LuaObject<TImpl>] type in LuaObject.hpp
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <GarrysMod/Lua/Interface.h>
|
#include <GarrysMod/Lua/Interface.h>
|
||||||
|
|
||||||
#include <lucore/Logger.hpp>
|
#include <lucore/Logger.hpp>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "GarrysMod/Lua/LuaBase.h"
|
#include "GarrysMod/Lua/LuaBase.h"
|
||||||
|
|
||||||
|
@ -22,6 +21,52 @@
|
||||||
|
|
||||||
#define LUA_MEMBER_FUNCTION_IMPLEMENT(CLASS, FUNC) int CLASS::FUNC##__ImpStatic(GarrysMod::Lua::ILuaBase* LUA)
|
#define LUA_MEMBER_FUNCTION_IMPLEMENT(CLASS, FUNC) int CLASS::FUNC##__ImpStatic(GarrysMod::Lua::ILuaBase* LUA)
|
||||||
|
|
||||||
|
// this synthesizes a lambda which takes the stack argument to get. this can actually also be
|
||||||
|
// stored as a variable for later usage (... if you so desire?)
|
||||||
|
#define LUA_CLASS_GET(T) \
|
||||||
|
[LUA](int stackPos) { \
|
||||||
|
LUA->CheckType(stackPos, T::__lua_typeid); \
|
||||||
|
return LUA->GetUserType<T>(stackPos, T::__lua_typeid); \
|
||||||
|
}
|
||||||
|
|
||||||
|
// This class binding package always implements the __gc metamethod
|
||||||
|
// to free any C++ object bound to Lua.
|
||||||
|
|
||||||
|
// Declare required binding variables.
|
||||||
|
#define LUA_CLASS_BIND_VARIABLES(ACCESS_LEVEL) \
|
||||||
|
public: \
|
||||||
|
static int __lua_typeid; \
|
||||||
|
ACCESS_LEVEL: \
|
||||||
|
LUA_MEMBER_FUNCTION(__gc);
|
||||||
|
|
||||||
|
// Implement required binding variables (typically in a .cpp file).
|
||||||
|
#define LUA_CLASS_BIND_VARIABLES_IMPLEMENT(T) \
|
||||||
|
int T::__lua_typeid = 0; \
|
||||||
|
LUA_MEMBER_FUNCTION_IMPLEMENT(T, __gc) { \
|
||||||
|
LUA->CheckType(1, T::__lua_typeid); \
|
||||||
|
auto self = LUA->GetUserType<T>(1, T::__lua_typeid); \
|
||||||
|
if(self != nullptr) { /* GetUserType returns nullptr on failure */ \
|
||||||
|
lucore::LogInfo("GCing {} object @ {:p}", #T, static_cast<void*>(self)); \
|
||||||
|
delete self; \
|
||||||
|
} \
|
||||||
|
return 0; \
|
||||||
|
}
|
||||||
|
|
||||||
|
// Begin the Bind() method of a class. This just sets up boilerplate
|
||||||
|
// and required things to setup a class.
|
||||||
|
#define LUA_CLASS_BIND_BEGIN(T) \
|
||||||
|
T::__lua_typeid = LUA->CreateMetaTable(#T); \
|
||||||
|
LUA->PushSpecial(GarrysMod::Lua::SPECIAL_REG); \
|
||||||
|
LUA->PushNumber(T::__lua_typeid); \
|
||||||
|
LUA->SetField(-2, #T "__typeid"); \
|
||||||
|
LUA->Pop(); /* pop registry */ \
|
||||||
|
LUA->Push(-1); \
|
||||||
|
LUA->SetField(-2, "__index"); \
|
||||||
|
LUA_SET_C_FUNCTION(__gc)
|
||||||
|
|
||||||
|
// End the Bind() method.
|
||||||
|
#define LUA_CLASS_BIND_END() LUA->Pop();
|
||||||
|
|
||||||
// Set a C function as a field.
|
// Set a C function as a field.
|
||||||
#define LUA_SET_C_FUNCTION(name) \
|
#define LUA_SET_C_FUNCTION(name) \
|
||||||
LUA->PushCFunction(name); \
|
LUA->PushCFunction(name); \
|
||||||
|
@ -44,4 +89,184 @@ namespace lcpu::lua {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A CRTP-based class which allows binding C++ to Lua, in a
|
||||||
|
/// fairly sensible manner.
|
||||||
|
template <class TImpl>
|
||||||
|
struct LuaObject {
|
||||||
|
using CFunc = GarrysMod::Lua::CFunc;
|
||||||
|
using ILuaFunc = void (*)(GarrysMod::Lua::ILuaBase*);
|
||||||
|
|
||||||
|
/// Register a C++ method.
|
||||||
|
static void RegisterMethod(const std::string& name, CFunc func) { methods()[name] = func; }
|
||||||
|
|
||||||
|
/// Register a getter for a value to be read.
|
||||||
|
static void RegisterGetter(const std::string& name, ILuaFunc func) { getters()[name] = func; }
|
||||||
|
|
||||||
|
/// Register a setter. This can be used to make a
|
||||||
|
/// C++ registered value read-write.
|
||||||
|
static void RegisterSetter(const std::string& name, ILuaFunc func) { setters()[name] = func; }
|
||||||
|
|
||||||
|
// addl. arguments are forwarded to the C++ constructor
|
||||||
|
template <class... Args>
|
||||||
|
static void Create(GarrysMod::Lua::ILuaBase* LUA, Args&&... args) {
|
||||||
|
auto ptr = new TImpl(static_cast<Args&&>(args)...);
|
||||||
|
ptr->InitLuaStuff(LUA);
|
||||||
|
LUA->PushUserType(ptr, __lua_typeid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static TImpl* FromLua(GarrysMod::Lua::ILuaBase* LUA, int stackPos) {
|
||||||
|
LUA->CheckType(stackPos, __lua_typeid);
|
||||||
|
return LUA->GetUserType<TImpl>(stackPos, __lua_typeid);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// This should be called first in your RegisterClass static method.
|
||||||
|
/// This doesn't pop the metatable off so you can keep adding things to it
|
||||||
|
static void RegisterClassStart(GarrysMod::Lua::ILuaBase* LUA) {
|
||||||
|
auto typeid_name = std::format("{}__typeid", TImpl::Name());
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
__lua_typeid = LUA->CreateMetaTable(TImpl::Name());
|
||||||
|
LUA->PushSpecial(GarrysMod::Lua::SPECIAL_REG);
|
||||||
|
LUA->PushNumber(__lua_typeid);
|
||||||
|
LUA->SetField(-2, typeid_name.c_str());
|
||||||
|
LUA->Pop(); // pop registry
|
||||||
|
// add in required metamethods
|
||||||
|
LUA_SET_C_FUNCTION(__gc)
|
||||||
|
LUA_SET_C_FUNCTION(__index)
|
||||||
|
LUA_SET_C_FUNCTION(__newindex)
|
||||||
|
// clang-format on
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call in your RegisterClass() method when done registering metamethods.
|
||||||
|
static void RegisterClassEnd(GarrysMod::Lua::ILuaBase* LUA) { LUA->Pop(); }
|
||||||
|
|
||||||
|
/// A detail function used to setup some stuff in the Create() method.
|
||||||
|
void InitLuaStuff(GarrysMod::Lua::ILuaBase* LUA) {
|
||||||
|
lua = LUA;
|
||||||
|
|
||||||
|
// create the table used to store user properties
|
||||||
|
// from Lua
|
||||||
|
LUA->CreateTable();
|
||||||
|
tableReference = LUA->ReferenceCreate();
|
||||||
|
|
||||||
|
// register some convinence things
|
||||||
|
RegisterGetter("Name", [](GarrysMod::Lua::ILuaBase* LUA) { LUA->PushString(TImpl::Name()); });
|
||||||
|
}
|
||||||
|
|
||||||
|
LuaObject() = default;
|
||||||
|
~LuaObject() {
|
||||||
|
// free the table reference
|
||||||
|
if(tableReference != -1)
|
||||||
|
lua->ReferenceFree(tableReference);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// The LUA interface used to create this class.
|
||||||
|
GarrysMod::Lua::ILuaBase* lua;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// base metamethods
|
||||||
|
LUA_MEMBER_FUNCTION(__gc)
|
||||||
|
LUA_MEMBER_FUNCTION(__index)
|
||||||
|
LUA_MEMBER_FUNCTION(__newindex)
|
||||||
|
|
||||||
|
// static stuff
|
||||||
|
static int __lua_typeid;
|
||||||
|
|
||||||
|
static auto& methods() {
|
||||||
|
static std::unordered_map<std::string, GarrysMod::Lua::CFunc> methods__;
|
||||||
|
return methods__;
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto& getters() {
|
||||||
|
static std::unordered_map<std::string, ILuaFunc> getters__;
|
||||||
|
return getters__;
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto& setters() {
|
||||||
|
static std::unordered_map<std::string, ILuaFunc> setters__;
|
||||||
|
return setters__;
|
||||||
|
}
|
||||||
|
|
||||||
|
// instance stuff
|
||||||
|
int tableReference { -1 };
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class TImpl>
|
||||||
|
int LuaObject<TImpl>::__lua_typeid = 0;
|
||||||
|
|
||||||
|
template <class TImpl>
|
||||||
|
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaObject<TImpl>, __gc) {
|
||||||
|
auto self = FromLua(LUA, 1);
|
||||||
|
if(self != nullptr) {
|
||||||
|
lucore::LogInfo("GCing LuaObject-based object @ {:p}", static_cast<void*>(self));
|
||||||
|
delete self;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TImpl>
|
||||||
|
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaObject<TImpl>, __index) {
|
||||||
|
auto self = FromLua(LUA, 1);
|
||||||
|
|
||||||
|
if(LUA->GetType(2) == GarrysMod::Lua::Type::String) {
|
||||||
|
auto key = GetLuaString(LUA, 2);
|
||||||
|
|
||||||
|
if(methods().find(key) != methods().end()) {
|
||||||
|
LUA->PushCFunction(methods()[key]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(getters().find(key) != getters().end()) {
|
||||||
|
// getters explicitly push their return onto the stack
|
||||||
|
getters()[key](LUA);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// look up from the table
|
||||||
|
// clang-format off
|
||||||
|
LUA->ReferencePush(self->tableReference);
|
||||||
|
LUA->Push(2);
|
||||||
|
LUA->GetTable(-2);
|
||||||
|
// clang-format on
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TImpl>
|
||||||
|
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaObject<TImpl>, __newindex) {
|
||||||
|
auto self = FromLua(LUA, 1);
|
||||||
|
|
||||||
|
if(LUA->GetType(2) == GarrysMod::Lua::Type::String) {
|
||||||
|
auto key = GetLuaString(LUA, 2);
|
||||||
|
|
||||||
|
// don't allow overwriting methods
|
||||||
|
if(methods().find(key) != methods().end())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// or read-only values
|
||||||
|
if(getters().find(key) != getters().end() && setters().find(key) == setters().end())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if(setters().find(key) != setters().end()) {
|
||||||
|
// for ergonomic sake only I kind of want to make this like
|
||||||
|
// LUA->Push(3) so that -1 (top of stack) is the value
|
||||||
|
// a bit cleaner. idk
|
||||||
|
setters()[key](LUA);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// push onto the table
|
||||||
|
// clang-format off
|
||||||
|
LUA->ReferencePush(self->tableReference);
|
||||||
|
LUA->Push(2);
|
||||||
|
LUA->Push(3);
|
||||||
|
LUA->SetTable(-3);
|
||||||
|
LUA->Pop();
|
||||||
|
// clang-format on
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace lcpu::lua
|
} // namespace lcpu::lua
|
||||||
|
|
|
@ -1,221 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
#include "GarrysMod/Lua/Interface.h"
|
|
||||||
#include "GarrysMod/Lua/LuaBase.h"
|
|
||||||
#include "LuaHelpers.hpp"
|
|
||||||
|
|
||||||
namespace lcpu::lua {
|
|
||||||
|
|
||||||
/// A CRTP-based class which allows binding C++ to Lua, in a
|
|
||||||
/// fairly sensible manner.
|
|
||||||
///
|
|
||||||
/// Classes backed by this class can have arbitrary properties
|
|
||||||
/// created by Lua (using a backing table created by this object).
|
|
||||||
template <class TImpl>
|
|
||||||
struct LuaObject {
|
|
||||||
using CFunc = GarrysMod::Lua::CFunc;
|
|
||||||
using ILuaFunc = int (*)(GarrysMod::Lua::ILuaBase*);
|
|
||||||
using ILuaVoidFunc = void (*)(GarrysMod::Lua::ILuaBase*);
|
|
||||||
|
|
||||||
/// Register a C++ method.
|
|
||||||
static void RegisterMethod(const std::string& name, CFunc func) { methods()[name] = func; }
|
|
||||||
|
|
||||||
/// Register a getter for a value to be read.
|
|
||||||
static void RegisterGetter(const std::string& name, ILuaVoidFunc func) { getters()[name] = func; }
|
|
||||||
|
|
||||||
/// Register a setter. This can be used to make a
|
|
||||||
/// C++ registered value read-write.
|
|
||||||
static void RegisterSetter(const std::string& name, ILuaVoidFunc func) { setters()[name] = func; }
|
|
||||||
|
|
||||||
virtual void AfterLuaInit() {};
|
|
||||||
|
|
||||||
/// Create an instance of this type to give to Lua.
|
|
||||||
/// addl. arguments are forwarded to the C++ constructor
|
|
||||||
template <class... Args>
|
|
||||||
static void Create(GarrysMod::Lua::ILuaBase* LUA, Args&&... args) {
|
|
||||||
auto ptr = new TImpl(static_cast<Args&&>(args)...);
|
|
||||||
ptr->InitLuaStuff(LUA);
|
|
||||||
ptr->AfterLuaInit();
|
|
||||||
LUA->PushUserType(ptr, __lua_typeid);
|
|
||||||
}
|
|
||||||
|
|
||||||
static TImpl* FromLua(GarrysMod::Lua::ILuaBase* LUA, int stackPos) {
|
|
||||||
LUA->CheckType(stackPos, __lua_typeid);
|
|
||||||
return LUA->GetUserType<TImpl>(stackPos, __lua_typeid);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
/// This should be called first in your RegisterClass static method.
|
|
||||||
static void RegisterClassStart(GarrysMod::Lua::ILuaBase* LUA) {
|
|
||||||
auto typeid_name = std::format("{}__typeid", TImpl::Name());
|
|
||||||
|
|
||||||
// clang-format off
|
|
||||||
__lua_typeid = LUA->CreateMetaTable(TImpl::Name());
|
|
||||||
LUA->PushSpecial(GarrysMod::Lua::SPECIAL_REG);
|
|
||||||
LUA->PushNumber(__lua_typeid);
|
|
||||||
LUA->SetField(-2, typeid_name.c_str());
|
|
||||||
LUA->Pop(); // pop registry
|
|
||||||
// add in required metamethods
|
|
||||||
LUA_SET_C_FUNCTION(__gc)
|
|
||||||
LUA_SET_C_FUNCTION(__index)
|
|
||||||
LUA_SET_C_FUNCTION(__newindex)
|
|
||||||
LUA->Pop(); // pop metatable
|
|
||||||
// clang-format on
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Register a metafunction.
|
|
||||||
/// Note that the following metafunctions are reserved by the implementation
|
|
||||||
/// of this object, and should not be overwritten:
|
|
||||||
///
|
|
||||||
/// - __gc
|
|
||||||
/// - __index
|
|
||||||
/// - __newindex
|
|
||||||
///
|
|
||||||
static void RegisterMetaFunction(GarrysMod::Lua::ILuaBase* LUA, const std::string& name, CFunc func) {
|
|
||||||
// clang-format off
|
|
||||||
LUA->PushMetaTable(__lua_typeid);
|
|
||||||
LUA->PushCFunction(func);
|
|
||||||
LUA->SetField(-2, name.c_str());
|
|
||||||
LUA->Pop();
|
|
||||||
// clang-format on
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A detail function used to setup some stuff in the Create() method.
|
|
||||||
void InitLuaStuff(GarrysMod::Lua::ILuaBase* LUA) {
|
|
||||||
lua = LUA;
|
|
||||||
|
|
||||||
// create the table used to store user properties
|
|
||||||
// from Lua
|
|
||||||
LUA->CreateTable();
|
|
||||||
tableReference = LUA->ReferenceCreate();
|
|
||||||
|
|
||||||
// register some convinence getters
|
|
||||||
RegisterGetter("Name", [](GarrysMod::Lua::ILuaBase* LUA) { LUA->PushString(TImpl::Name()); });
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetTableReference() { return tableReference; }
|
|
||||||
|
|
||||||
LuaObject() = default;
|
|
||||||
virtual ~LuaObject() {
|
|
||||||
// free the table reference
|
|
||||||
if(tableReference != -1)
|
|
||||||
lua->ReferenceFree(tableReference);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The LUA interface used to create this class.
|
|
||||||
GarrysMod::Lua::ILuaBase* lua;
|
|
||||||
|
|
||||||
private:
|
|
||||||
// base metamethods
|
|
||||||
LUA_MEMBER_FUNCTION(__gc)
|
|
||||||
LUA_MEMBER_FUNCTION(__index)
|
|
||||||
LUA_MEMBER_FUNCTION(__newindex)
|
|
||||||
|
|
||||||
// static stuff
|
|
||||||
static int __lua_typeid;
|
|
||||||
|
|
||||||
static auto& methods() {
|
|
||||||
static std::unordered_map<std::string, CFunc> methods__;
|
|
||||||
return methods__;
|
|
||||||
}
|
|
||||||
|
|
||||||
static auto& getters() {
|
|
||||||
static std::unordered_map<std::string, ILuaVoidFunc> getters__;
|
|
||||||
return getters__;
|
|
||||||
}
|
|
||||||
|
|
||||||
static auto& setters() {
|
|
||||||
static std::unordered_map<std::string, ILuaVoidFunc> setters__;
|
|
||||||
return setters__;
|
|
||||||
}
|
|
||||||
|
|
||||||
// instance stuff
|
|
||||||
int tableReference { -1 };
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class TImpl>
|
|
||||||
int LuaObject<TImpl>::__lua_typeid = 0;
|
|
||||||
|
|
||||||
template <class TImpl>
|
|
||||||
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaObject<TImpl>, __gc) {
|
|
||||||
auto self = FromLua(LUA, 1);
|
|
||||||
if(self != nullptr) {
|
|
||||||
lucore::LogInfo("GCing LuaObject {} @ {:p}", TImpl::Name(), static_cast<void*>(self));
|
|
||||||
delete self;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class TImpl>
|
|
||||||
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaObject<TImpl>, __index) {
|
|
||||||
auto self = FromLua(LUA, 1);
|
|
||||||
|
|
||||||
if(LUA->GetType(2) == GarrysMod::Lua::Type::String) {
|
|
||||||
auto& methods = LuaObject::methods();
|
|
||||||
auto& getters = LuaObject::getters();
|
|
||||||
auto key = GetLuaString(LUA, 2);
|
|
||||||
|
|
||||||
if(methods.find(key) != methods.end()) {
|
|
||||||
LUA->PushCFunction(methods[key]);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(getters.find(key) != getters.end()) {
|
|
||||||
// getters explicitly push their return onto the stack
|
|
||||||
getters[key](LUA);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// look up from the table
|
|
||||||
// clang-format off
|
|
||||||
LUA->ReferencePush(self->tableReference);
|
|
||||||
LUA->Push(2);
|
|
||||||
LUA->GetTable(-2);
|
|
||||||
// clang-format on
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class TImpl>
|
|
||||||
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaObject<TImpl>, __newindex) {
|
|
||||||
auto self = FromLua(LUA, 1);
|
|
||||||
|
|
||||||
if(LUA->GetType(2) == GarrysMod::Lua::Type::String) {
|
|
||||||
auto& methods = LuaObject::methods();
|
|
||||||
auto& getters = LuaObject::getters();
|
|
||||||
auto& setters = LuaObject::setters();
|
|
||||||
auto key = GetLuaString(LUA, 2);
|
|
||||||
|
|
||||||
// don't allow overwriting methods
|
|
||||||
if(methods.find(key) != methods.end())
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
// or read-only C++ values
|
|
||||||
if(getters.find(key) != getters.end() && setters.find(key) == setters.end())
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if(setters.find(key) != setters.end()) {
|
|
||||||
// clang-format off
|
|
||||||
// Push the value to be written onto the top of the stack.
|
|
||||||
// This is mostly for ergonomic reasons.
|
|
||||||
LUA->Push(3);
|
|
||||||
setters[key](LUA);
|
|
||||||
LUA->Pop();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// push onto the table
|
|
||||||
// clang-format off
|
|
||||||
LUA->ReferencePush(self->tableReference);
|
|
||||||
LUA->Push(2);
|
|
||||||
LUA->Push(3);
|
|
||||||
LUA->SetTable(-3);
|
|
||||||
LUA->Pop();
|
|
||||||
// clang-format on
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace lcpu::lua
|
|
|
@ -11,7 +11,7 @@ GMOD_MODULE_OPEN() {
|
||||||
lucore::Logger::The().AttachSink(lcpu::SourceSink::The());
|
lucore::Logger::The().AttachSink(lcpu::SourceSink::The());
|
||||||
lucore::LogInfo("LCPU Native Module! (ModuleVersion {})", LCPU_MODULE_VERSION);
|
lucore::LogInfo("LCPU Native Module! (ModuleVersion {})", LCPU_MODULE_VERSION);
|
||||||
|
|
||||||
lcpu::GlobalsBind(LUA);
|
GlobalsBind(LUA);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue