more lua binding fun
This commit is contained in:
parent
c69dc8d753
commit
9c5415c061
|
@ -11,6 +11,33 @@ function ENT:Initialize()
|
||||||
self:SetSolid(SOLID_VPHYSICS)
|
self:SetSolid(SOLID_VPHYSICS)
|
||||||
-- 64 kb of ram for now.
|
-- 64 kb of ram for now.
|
||||||
self.cpu = LCPUNative.CreateCPU(128 * 1024)
|
self.cpu = LCPUNative.CreateCPU(128 * 1024)
|
||||||
|
|
||||||
|
-- test device framework
|
||||||
|
-- this is how I ideally want to do things,
|
||||||
|
-- dunno if it's possible
|
||||||
|
self.test_device = LCPUNative.CreateDevice()
|
||||||
|
print(self.test_device)
|
||||||
|
self.test_device:SetBase(0x12000000)
|
||||||
|
self.test_device:SetSize(0x10)
|
||||||
|
function self.test_device:Clock()
|
||||||
|
print("TestDevice Clock()")
|
||||||
|
end
|
||||||
|
|
||||||
|
function self.test_device:Peek(address)
|
||||||
|
if address == 0x12000000 then
|
||||||
|
-- it a test!
|
||||||
|
return CurTime()
|
||||||
|
end
|
||||||
|
return 0x10000
|
||||||
|
end
|
||||||
|
|
||||||
|
function self.test_device:Poke(address, value)
|
||||||
|
if address == 0x12000000 then
|
||||||
|
print("What you doing?")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self.cpu:AttachDevice(self.test_device)
|
||||||
end
|
end
|
||||||
|
|
||||||
function ENT:Think()
|
function ENT:Think()
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
-- upload library
|
||||||
|
-- (TODO)
|
|
@ -4,8 +4,7 @@ add_library(lcpu_native SHARED
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
src/LcpuGlobals.cpp
|
src/LcpuGlobals.cpp
|
||||||
src/LuaCpu.cpp
|
src/LuaCpu.cpp
|
||||||
#src/LuaDevice.cpp
|
src/LuaDevice.cpp
|
||||||
|
|
||||||
src/SourceSink.cpp
|
src/SourceSink.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,35 +1,30 @@
|
||||||
#include "LcpuGlobals.hpp"
|
#include "LcpuGlobals.hpp"
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
#include "LuaCpu.hpp"
|
#include "LuaCpu.hpp"
|
||||||
#include "LuaDevice.hpp"
|
#include "LuaDevice.hpp"
|
||||||
|
|
||||||
/// 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.
|
|
||||||
#define LCPU_MODULE_VERSION 1
|
|
||||||
|
|
||||||
LUA_FUNCTION(LCPUNative_CreateCPU) {
|
LUA_FUNCTION(LCPUNative_CreateCPU) {
|
||||||
LUA->CheckType(1, GarrysMod::Lua::Type::Number);
|
LUA->CheckType(1, GarrysMod::Lua::Type::Number);
|
||||||
auto memorySize = (u32)std::round(LUA->GetNumber(1));
|
auto memorySize = static_cast<u32>(LUA->GetNumber(1));
|
||||||
|
|
||||||
// TODO: There's probably a way to like, ensure a per-player max.
|
// TODO: There's probably a way to like, ensure a per-player max.
|
||||||
if(memorySize > (64 * 1024 * 1024)) {
|
if(memorySize > (64 * 1024 * 1024))
|
||||||
LUA->ThrowError("Over RAM size limit.");
|
LUA->ThrowError("Over RAM size limit.");
|
||||||
}
|
|
||||||
|
|
||||||
LuaCpu::Create(LUA, memorySize);
|
LuaCpu::Create(LUA, memorySize);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
LUA_FUNCTION(LCPUNative_CreateDevice) {
|
LUA_FUNCTION(LCPUNative_CreateDevice) {
|
||||||
//LuaDevice::Create(LUA);
|
LuaDevice::Create(LUA);
|
||||||
return 0;//1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlobalsBind(GarrysMod::Lua::ILuaBase* LUA) {
|
void GlobalsBind(GarrysMod::Lua::ILuaBase* LUA) {
|
||||||
LuaCpu::Bind(LUA);
|
LuaCpu::Bind(LUA);
|
||||||
//LuaDevice::Bind(LUA);
|
// LuaDevice::Bind(LUA);
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
LUA->PushSpecial(GarrysMod::Lua::SPECIAL_GLOB);
|
LUA->PushSpecial(GarrysMod::Lua::SPECIAL_GLOB);
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
#include "LuaHelpers.hpp"
|
#include "LuaHelpers.hpp"
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
#define LCPU_MODULE_VERSION 1
|
||||||
|
|
||||||
void GlobalsBind(GarrysMod::Lua::ILuaBase* LUA);
|
void GlobalsBind(GarrysMod::Lua::ILuaBase* LUA);
|
||||||
|
|
|
@ -34,67 +34,56 @@ struct SimpleUartDevice : public riscv::Bus::MmioDevice {
|
||||||
LUA_CLASS_BIND_VARIABLES_IMPLEMENT(LuaCpu);
|
LUA_CLASS_BIND_VARIABLES_IMPLEMENT(LuaCpu);
|
||||||
|
|
||||||
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaCpu, PoweredOn) {
|
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaCpu, PoweredOn) {
|
||||||
LUA_CLASS_GET(LuaCpu, self, 1);
|
auto self = LUA_CLASS_GET(LuaCpu)(1);
|
||||||
LUA->PushBool(self->poweredOn);
|
LUA->PushBool(self->poweredOn);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaCpu, Cycle) {
|
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaCpu, Cycle) {
|
||||||
LUA_CLASS_GET(LuaCpu, self, 1);
|
auto self = LUA_CLASS_GET(LuaCpu)(1);
|
||||||
[&self]() {
|
|
||||||
if(!self->poweredOn)
|
if(!self->poweredOn)
|
||||||
return;
|
return 0;
|
||||||
self->system->Step();
|
self->system->Step();
|
||||||
}();
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaCpu, PowerOff) {
|
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaCpu, PowerOff) {
|
||||||
LUA_CLASS_GET(LuaCpu, self, 1);
|
auto self = LUA_CLASS_GET(LuaCpu)(1);
|
||||||
[&self]() {
|
|
||||||
if(!self->poweredOn)
|
if(!self->poweredOn)
|
||||||
return;
|
return 0;
|
||||||
|
|
||||||
self->poweredOn = false;
|
self->poweredOn = false;
|
||||||
self->system->bus->Reset();
|
self->system->bus->Reset();
|
||||||
}();
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaCpu, PowerOn) {
|
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaCpu, PowerOn) {
|
||||||
LUA_CLASS_GET(LuaCpu, self, 1);
|
auto self = LUA_CLASS_GET(LuaCpu)(1);
|
||||||
[&self]() {
|
|
||||||
if(self->poweredOn)
|
if(self->poweredOn)
|
||||||
return;
|
return 0;
|
||||||
|
|
||||||
self->poweredOn = true;
|
self->poweredOn = true;
|
||||||
self->system->bus->Reset();
|
self->system->bus->Reset();
|
||||||
}();
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaCpu, Reset) {
|
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaCpu, Reset) {
|
||||||
LUA_CLASS_GET(LuaCpu, self, 1);
|
auto self = LUA_CLASS_GET(LuaCpu)(1);
|
||||||
[&self]() { self->system->bus->Reset(); }();
|
self->system->bus->Reset();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaCpu, AttachDevice) {
|
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaCpu, AttachDevice) {
|
||||||
LUA_CLASS_GET(LuaCpu, self, 1);
|
auto self = LUA_CLASS_GET(LuaCpu)(1);
|
||||||
bool result = false;
|
auto device = LUA_CLASS_GET(LuaDevice)(1);
|
||||||
#if 0
|
|
||||||
[&]() {
|
// the bus is safe against this possibility, but
|
||||||
LUA_CLASS_GET(LuaDevice, device, 2);
|
|
||||||
if(!device)
|
|
||||||
return; // the bus is safe against this possibility, but
|
|
||||||
// I'd rather be doubly-safe tbh
|
// I'd rather be doubly-safe tbh
|
||||||
|
if(!device)
|
||||||
|
LUA->ThrowError("Null device pointer");
|
||||||
|
|
||||||
// Attach it
|
// Attach it
|
||||||
result = self->system->bus->AttachDevice(static_cast<riscv::Bus::Device*>(device));
|
LUA->PushBool(self->system->bus->AttachDevice(static_cast<riscv::Bus::Device*>(device)));
|
||||||
}();
|
|
||||||
#endif
|
|
||||||
LUA->PushBool(result);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,7 +105,7 @@ void LuaCpu::Create(GarrysMod::Lua::ILuaBase* LUA, u32 memorySize) {
|
||||||
|
|
||||||
// lame test code. this WILL be removed, I just want this for a quick test
|
// lame test code. this WILL be removed, I just want this for a quick test
|
||||||
cpuWrapper->system->bus->AttachDevice(new SimpleUartDevice);
|
cpuWrapper->system->bus->AttachDevice(new SimpleUartDevice);
|
||||||
auto fp = std::fopen("/home/lily/test.bin", "rb");
|
auto fp = std::fopen("/home/lily/test-gmod.bin", "rb");
|
||||||
if(fp) {
|
if(fp) {
|
||||||
std::fseek(fp, 0, SEEK_END);
|
std::fseek(fp, 0, SEEK_END);
|
||||||
auto len = std::ftell(fp);
|
auto len = std::ftell(fp);
|
||||||
|
@ -130,8 +119,6 @@ void LuaCpu::Create(GarrysMod::Lua::ILuaBase* LUA, u32 memorySize) {
|
||||||
}
|
}
|
||||||
|
|
||||||
LuaCpu::LuaCpu(u32 memorySize) {
|
LuaCpu::LuaCpu(u32 memorySize) {
|
||||||
lucore::LogInfo("in LuaCpu::LuaCpu(0x{:08x})\n", memorySize);
|
|
||||||
|
|
||||||
poweredOn = true;
|
poweredOn = true;
|
||||||
system = riscv::System::Create(memorySize);
|
system = riscv::System::Create(memorySize);
|
||||||
system->OnPowerOff = [&]() {
|
system->OnPowerOff = [&]() {
|
||||||
|
|
|
@ -1,15 +1,107 @@
|
||||||
#include "LuaDevice.hpp"
|
#include "LuaDevice.hpp"
|
||||||
|
|
||||||
|
#include "LuaHelpers.hpp"
|
||||||
|
|
||||||
LUA_CLASS_BIND_VARIABLES_IMPLEMENT(LuaDevice);
|
LUA_CLASS_BIND_VARIABLES_IMPLEMENT(LuaDevice);
|
||||||
|
|
||||||
|
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaDevice, SetBase) {
|
||||||
|
auto self = LUA_CLASS_GET(LuaDevice)(1);
|
||||||
|
if(self->bus)
|
||||||
|
LUA->ThrowError("Do not call this on an attached device");
|
||||||
|
self->base = static_cast<u32>(LUA->CheckNumber(2));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaDevice, SetSize) {
|
||||||
|
auto self = LUA_CLASS_GET(LuaDevice)(1);
|
||||||
|
if(self->bus)
|
||||||
|
LUA->ThrowError("Do not call this on an attached device");
|
||||||
|
self->size = static_cast<u32>(LUA->CheckNumber(2));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LuaDevice::Clocked() const {
|
||||||
|
// Even if Lua devices may not have a clock handler
|
||||||
|
// installed there's no real non-awful way to check,
|
||||||
|
// so just lie and say yes always.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LuaDevice::Clock() {
|
||||||
|
LuaState->PushUserType(this, __lua_typeid);
|
||||||
|
LuaState->GetField(-2, "Clock");
|
||||||
|
if(!LuaState->IsType(-1, GarrysMod::Lua::Type::Function)) {
|
||||||
|
LuaState->Pop(2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LuaState->Push(-2); // "self"
|
||||||
|
LuaState->Call(1, 0);
|
||||||
|
LuaState->Pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
riscv::Address LuaDevice::Base() const {
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
|
riscv::Address LuaDevice::Size() const {
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 LuaDevice::Peek(riscv::Address address) {
|
||||||
|
LuaState->PushUserType(this, __lua_typeid);
|
||||||
|
LuaState->GetField(-2, "Peek");
|
||||||
|
if(!LuaState->IsType(-1, GarrysMod::Lua::Type::Function)) {
|
||||||
|
LuaState->Pop(2);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
LuaState->Push(-2); // "self"
|
||||||
|
LuaState->PushNumber(address);
|
||||||
|
LuaState->Call(2, 1);
|
||||||
|
auto result = LuaState->GetNumber(-1);
|
||||||
|
LuaState->Pop();
|
||||||
|
return static_cast<u32>(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LuaDevice::Poke(riscv::Address address, u32 value) {
|
||||||
|
LuaState->PushUserType(this, __lua_typeid);
|
||||||
|
LuaState->GetField(-2, "Poke");
|
||||||
|
if(!LuaState->IsType(-1, GarrysMod::Lua::Type::Function)) {
|
||||||
|
LuaState->Pop(2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LuaState->Push(-2); // "self"
|
||||||
|
LuaState->PushNumber(address);
|
||||||
|
LuaState->PushNumber(value);
|
||||||
|
LuaState->Call(3, 0);
|
||||||
|
LuaState->Pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LuaDevice::Reset() {
|
||||||
|
LuaState->PushUserType(this, __lua_typeid);
|
||||||
|
LuaState->GetField(-2, "Reset");
|
||||||
|
if(!LuaState->IsType(-1, GarrysMod::Lua::Type::Function))
|
||||||
|
return;
|
||||||
|
|
||||||
|
LuaState->Push(-2); // "self"
|
||||||
|
LuaState->Call(1, 0);
|
||||||
|
LuaState->Pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
LuaDevice::~LuaDevice() = default;
|
||||||
|
|
||||||
void LuaDevice::Bind(GarrysMod::Lua::ILuaBase* LUA) {
|
void LuaDevice::Bind(GarrysMod::Lua::ILuaBase* LUA) {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
|
// TODO: I need to figure out how to like, set up metamethod stuff
|
||||||
|
// so it all properly works.
|
||||||
LUA_CLASS_BIND_BEGIN(LuaDevice);
|
LUA_CLASS_BIND_BEGIN(LuaDevice);
|
||||||
// todo handlers
|
LUA_SET_C_FUNCTION(SetBase)
|
||||||
|
LUA_SET_C_FUNCTION(SetSize)
|
||||||
LUA_CLASS_BIND_END();
|
LUA_CLASS_BIND_END();
|
||||||
// clang-format on
|
// clang-format on
|
||||||
}
|
}
|
||||||
|
|
||||||
void LuaDevice::Create(GarrysMod::Lua::ILuaBase* LUA) {
|
void LuaDevice::Create(GarrysMod::Lua::ILuaBase* LUA) {
|
||||||
LUA->PushUserType(new LuaDevice(), __lua_typeid);
|
auto device = new LuaDevice();
|
||||||
|
device->LuaState = LUA;
|
||||||
|
LUA->PushUserType(device, __lua_typeid);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,14 @@ struct LuaDevice : public riscv::Bus::MmioDevice {
|
||||||
static void Bind(GarrysMod::Lua::ILuaBase* LUA);
|
static void Bind(GarrysMod::Lua::ILuaBase* LUA);
|
||||||
static void Create(GarrysMod::Lua::ILuaBase* LUA);
|
static void Create(GarrysMod::Lua::ILuaBase* LUA);
|
||||||
|
|
||||||
riscv::Address Base() const override { return base; }
|
~LuaDevice();
|
||||||
riscv::Address Size() const override { return size; } // I think this is right?
|
|
||||||
|
bool Clocked() const override;
|
||||||
|
void Clock() override;
|
||||||
|
void Reset() override;
|
||||||
|
|
||||||
|
riscv::Address Base() const override;
|
||||||
|
riscv::Address Size() const override;
|
||||||
|
|
||||||
u32 Peek(riscv::Address address) override;
|
u32 Peek(riscv::Address address) override;
|
||||||
void Poke(riscv::Address address, u32 value) override;
|
void Poke(riscv::Address address, u32 value) override;
|
||||||
|
@ -24,14 +30,10 @@ struct LuaDevice : public riscv::Bus::MmioDevice {
|
||||||
LUA_MEMBER_FUNCTION(SetBase);
|
LUA_MEMBER_FUNCTION(SetBase);
|
||||||
LUA_MEMBER_FUNCTION(SetSize);
|
LUA_MEMBER_FUNCTION(SetSize);
|
||||||
|
|
||||||
LUA_MEMBER_FUNCTION(SetClockHandler);
|
// GetBase/GetSize?
|
||||||
LUA_MEMBER_FUNCTION(SetResetHandler);
|
|
||||||
LUA_MEMBER_FUNCTION(SetPeekHandler);
|
|
||||||
LUA_MEMBER_FUNCTION(SetPokeHandler);
|
|
||||||
|
|
||||||
riscv::Address base {};
|
riscv::Address base {};
|
||||||
riscv::Address size {};
|
riscv::Address size {};
|
||||||
|
|
||||||
// ...?
|
|
||||||
GarrysMod::Lua::ILuaBase* LuaState;
|
GarrysMod::Lua::ILuaBase* LuaState;
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <GarrysMod/Lua/Interface.h>
|
#include <GarrysMod/Lua/Interface.h>
|
||||||
|
|
||||||
#include <lucore/Logger.hpp>
|
#include <lucore/Logger.hpp>
|
||||||
|
|
||||||
// These are like the official GMOD header LUA_FUNCTION but allow forward declaration
|
// These are like the official GMOD header LUA_FUNCTION but allow forward declaration
|
||||||
|
@ -17,20 +18,25 @@
|
||||||
|
|
||||||
#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)
|
||||||
|
|
||||||
// will make a "self" variable with the class
|
// this synthesizes a lambda which takes the stack argument to get. this can actually also be
|
||||||
#define LUA_CLASS_GET(T, name, stackPos) \
|
// 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); \
|
LUA->CheckType(stackPos, T::__lua_typeid); \
|
||||||
auto name = LUA->GetUserType<T>(stackPos, T::__lua_typeid); \
|
return LUA->GetUserType<T>(stackPos, T::__lua_typeid); \
|
||||||
if(!name) { \
|
|
||||||
LUA->ThrowError("nullptr " #T " passed to function which requires a valid instance"); \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) \
|
#define LUA_CLASS_BIND_VARIABLES(ACCESS_LEVEL) \
|
||||||
public: \
|
public: \
|
||||||
static int __lua_typeid; \
|
static int __lua_typeid; \
|
||||||
ACCESS_LEVEL: \
|
ACCESS_LEVEL: \
|
||||||
LUA_MEMBER_FUNCTION(__gc);
|
LUA_MEMBER_FUNCTION(__gc);
|
||||||
|
|
||||||
|
// Implement required binding variables (typically in a .cpp file).
|
||||||
#define LUA_CLASS_BIND_VARIABLES_IMPLEMENT(T) \
|
#define LUA_CLASS_BIND_VARIABLES_IMPLEMENT(T) \
|
||||||
int T::__lua_typeid = 0; \
|
int T::__lua_typeid = 0; \
|
||||||
LUA_MEMBER_FUNCTION_IMPLEMENT(T, __gc) { \
|
LUA_MEMBER_FUNCTION_IMPLEMENT(T, __gc) { \
|
||||||
|
@ -43,6 +49,8 @@
|
||||||
return 0; \
|
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) \
|
#define LUA_CLASS_BIND_BEGIN(T) \
|
||||||
T::__lua_typeid = LUA->CreateMetaTable(#T); \
|
T::__lua_typeid = LUA->CreateMetaTable(#T); \
|
||||||
LUA->PushSpecial(GarrysMod::Lua::SPECIAL_REG); \
|
LUA->PushSpecial(GarrysMod::Lua::SPECIAL_REG); \
|
||||||
|
@ -53,13 +61,15 @@
|
||||||
LUA->SetField(-2, "__index"); \
|
LUA->SetField(-2, "__index"); \
|
||||||
LUA_SET_C_FUNCTION(__gc)
|
LUA_SET_C_FUNCTION(__gc)
|
||||||
|
|
||||||
|
// End the Bind() method.
|
||||||
#define LUA_CLASS_BIND_END() LUA->Pop();
|
#define LUA_CLASS_BIND_END() LUA->Pop();
|
||||||
|
|
||||||
// Helpers for lua functions
|
// 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); \
|
||||||
LUA->SetField(-2, #name);
|
LUA->SetField(-2, #name);
|
||||||
|
|
||||||
|
// Set a C function as a field with an alternative field name.
|
||||||
#define LUA_SET_C_FUNCTION_NAME(name, altName) \
|
#define LUA_SET_C_FUNCTION_NAME(name, altName) \
|
||||||
LUA->PushCFunction(name); \
|
LUA->PushCFunction(name); \
|
||||||
LUA->SetField(-2, altName);
|
LUA->SetField(-2, altName);
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
GMOD_MODULE_OPEN() {
|
GMOD_MODULE_OPEN() {
|
||||||
lucore::Logger::The().AttachSink(lcpu::SourceSink::The());
|
lucore::Logger::The().AttachSink(lcpu::SourceSink::The());
|
||||||
lucore::LogInfo("LCPU Native Module!");
|
lucore::LogInfo("LCPU Native Module! (ModuleVersion {})", LCPU_MODULE_VERSION);
|
||||||
GlobalsBind(LUA);
|
GlobalsBind(LUA);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
PROJECT = test
|
||||||
|
|
||||||
|
# where your rv32 toolchain is
|
||||||
|
TCPATH = /home/lily/bin/riscv/bin
|
||||||
|
PREFIX = $(TCPATH)/riscv32-unknown-elf
|
||||||
|
|
||||||
|
CC = $(PREFIX)-gcc
|
||||||
|
CXX = $(PREFIX)-g++
|
||||||
|
|
||||||
|
ARCHFLAGS = -ffreestanding -fno-stack-protector -fdata-sections -ffunction-sections -march=rv32ima -mabi=ilp32
|
||||||
|
CCFLAGS = -g -Os $(ARCHFLAGS) -std=c18
|
||||||
|
CXXFLAGS = $(ARCHFLAGS) -g -Os -std=c++20 -fno-exceptions -fno-rtti
|
||||||
|
LDFLAGS = -T binary.ld -nostdlib -Wl,--gc-sections
|
||||||
|
|
||||||
|
OBJS = start.o \
|
||||||
|
main.o
|
||||||
|
|
||||||
|
.PHONY: all test clean
|
||||||
|
|
||||||
|
all: $(PROJECT).bin $(PROJECT).debug.txt
|
||||||
|
|
||||||
|
# this assumes the lcpu project build dir you're using is
|
||||||
|
# [lcpu repo root]/build
|
||||||
|
test: $(PROJECT).bin $(PROJECT).debug.txt
|
||||||
|
../../../../build/projects/riscv_test_harness/rvtest $<
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm $(PROJECT).elf $(PROJECT).bin $(PROJECT).debug.txt $(OBJS)
|
||||||
|
|
||||||
|
# Link rules
|
||||||
|
|
||||||
|
$(PROJECT).elf: $(OBJS)
|
||||||
|
$(CC) $(CCFLAGS) $(LDFLAGS) -o $@ $(OBJS)
|
||||||
|
|
||||||
|
$(PROJECT).bin : $(PROJECT).elf
|
||||||
|
$(PREFIX)-objcopy $^ -O binary $@
|
||||||
|
|
||||||
|
$(PROJECT).debug.txt : $(PROJECT).elf
|
||||||
|
$(PREFIX)-objdump -t $^ > $@
|
||||||
|
$(PREFIX)-objdump -S $^ >> $@
|
||||||
|
|
||||||
|
# Compile rules
|
||||||
|
|
||||||
|
%.o: %.cpp
|
||||||
|
$(CXX) -c $(CXXFLAGS) $< -o $@
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) -c $(CCFLAGS) $< -o $@
|
||||||
|
|
||||||
|
%.o: %.S
|
||||||
|
$(CC) -x assembler-with-cpp -march=rv32ima -mabi=ilp32 -c $< -o $@
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
__heap_size = 0x1000;
|
||||||
|
__stack_size = 0x1000;
|
||||||
|
|
||||||
|
ENTRY(_start)
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
. = 0x80000000;
|
||||||
|
.text : ALIGN(16) {
|
||||||
|
__TEXT_BEGIN__ = .;
|
||||||
|
*(.initial_jump)
|
||||||
|
*(.entry.text)
|
||||||
|
*(.init.literal)
|
||||||
|
*(.init)
|
||||||
|
*(.text)
|
||||||
|
*(.literal .text .literal.* .text.* .stub)
|
||||||
|
*(.out_jump.literal.*)
|
||||||
|
*(.out_jump.*)
|
||||||
|
__TEXT_END__ = .;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we're on a newer compiler */
|
||||||
|
/DISCARD/ :
|
||||||
|
{
|
||||||
|
*(.interp)
|
||||||
|
*(.dynsym)
|
||||||
|
*(.dynstr)
|
||||||
|
*(.header)
|
||||||
|
} : phdr
|
||||||
|
|
||||||
|
.data : ALIGN(16) {
|
||||||
|
__DATA_BEGIN__ = .;
|
||||||
|
*(.rodata)
|
||||||
|
*(.rodata.*)
|
||||||
|
*(.gnu.linkonce.r.*)
|
||||||
|
*(.rodata1)
|
||||||
|
*(.dynsbss)
|
||||||
|
*(.gnu.linkonce.sb.*)
|
||||||
|
*(.scommon)
|
||||||
|
*(.gnu.linkonce.sb2.*)
|
||||||
|
*(.sbss)
|
||||||
|
*(.sbss.*)
|
||||||
|
*(.sbss2)
|
||||||
|
*(.sbss2.*)
|
||||||
|
*(.dynbss)
|
||||||
|
*(.data)
|
||||||
|
*(.data.*)
|
||||||
|
*(.got)
|
||||||
|
*(.got.*)
|
||||||
|
__DATA_END__ = .;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bss : ALIGN( 16 ) {
|
||||||
|
__BSS_BEGIN__ = .;
|
||||||
|
*(.bss) /* Tricky: BSS needs to be allocated but not sent. GCC Will not populate these for calculating data size */
|
||||||
|
*(.bss.*)
|
||||||
|
__BSS_END__ = .;
|
||||||
|
}
|
||||||
|
|
||||||
|
.heap : ALIGN( 16 ) {
|
||||||
|
_sheap = .;
|
||||||
|
. = . + __heap_size;
|
||||||
|
_eheap = .;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stack : ALIGN( 16 ) {
|
||||||
|
_estack = .;
|
||||||
|
. = . + __stack_size;
|
||||||
|
_sstack = .;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,122 @@
|
||||||
|
// a simple test program - this version would talk to a device
|
||||||
|
// written in GLua
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
uint32_t strlen(const char* str) {
|
||||||
|
if(!str)
|
||||||
|
return 0;
|
||||||
|
const char* c = str;
|
||||||
|
while(*c++)
|
||||||
|
;
|
||||||
|
return c - str;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define GLUA_DEVICE *(volatile uint32_t*)0x12000000
|
||||||
|
|
||||||
|
#define SYSCON *(volatile uint32_t*)0x11100000
|
||||||
|
|
||||||
|
#define UART_BASE 0x10000000
|
||||||
|
#define UART_DATA *(volatile uint32_t*)UART_BASE
|
||||||
|
#define UART_STATUS UART_DATA
|
||||||
|
|
||||||
|
void putc(char c) {
|
||||||
|
UART_DATA = (uint32_t)c;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((noinline)) void puts(const char* str) {
|
||||||
|
const uint32_t length = strlen(str);
|
||||||
|
for(uint32_t i = 0; i < length; ++i)
|
||||||
|
putc(str[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
int itoa(int value, char* sp, int radix) {
|
||||||
|
char tmp[16];
|
||||||
|
char* tp = tmp;
|
||||||
|
int i;
|
||||||
|
unsigned v;
|
||||||
|
|
||||||
|
int sign = (radix == 10 && value < 0);
|
||||||
|
if(sign)
|
||||||
|
v = -value;
|
||||||
|
else
|
||||||
|
v = (unsigned)value;
|
||||||
|
|
||||||
|
while(v || tp == tmp) {
|
||||||
|
i = v % radix;
|
||||||
|
v /= radix;
|
||||||
|
if(i < 10)
|
||||||
|
*tp++ = i + '0';
|
||||||
|
else
|
||||||
|
*tp++ = i + 'a' - 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
int len = tp - tmp;
|
||||||
|
|
||||||
|
if(sign) {
|
||||||
|
*sp++ = '-';
|
||||||
|
len++;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(tp > tmp)
|
||||||
|
*sp++ = *--tp;
|
||||||
|
|
||||||
|
*sp = '\0';
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vprintf(const char* format, va_list val) {
|
||||||
|
const int fl = strlen(format);
|
||||||
|
for(int i = 0; i < fl; ++i) {
|
||||||
|
switch(format[i]) {
|
||||||
|
case '%':
|
||||||
|
if(format[i+1] == '%')
|
||||||
|
putc('%');
|
||||||
|
switch(format[i+1]) {
|
||||||
|
case 'i':
|
||||||
|
case 'd': {
|
||||||
|
char a[32];
|
||||||
|
itoa(va_arg(val, uint32_t), &a[0], 10);
|
||||||
|
|
||||||
|
const int al = strlen(a);
|
||||||
|
for(int j = 0; j < al; ++j)
|
||||||
|
putc(a[j]);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case 's': {
|
||||||
|
char* p = va_arg(val, char*);
|
||||||
|
if(!p)
|
||||||
|
puts("(null)");
|
||||||
|
else
|
||||||
|
puts(p);
|
||||||
|
};
|
||||||
|
|
||||||
|
default:
|
||||||
|
putc(' ');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
putc(format[i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void printf(const char* format, ...) {
|
||||||
|
va_list val;
|
||||||
|
va_start(val, format);
|
||||||
|
vprintf(format, val);
|
||||||
|
va_end(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
puts("fuck you garry I win");
|
||||||
|
|
||||||
|
for(int i = 0; i < 256; ++i)
|
||||||
|
printf("uhh %d\n", GLUA_DEVICE);
|
||||||
|
|
||||||
|
SYSCON = 0x5555;
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
# Simple bare-metal RISCV startup code.
|
||||||
|
|
||||||
|
.section .initial_jump
|
||||||
|
.global _start
|
||||||
|
|
||||||
|
.extern main
|
||||||
|
|
||||||
|
.align 4
|
||||||
|
_start:
|
||||||
|
la sp, _sstack # set up C stack
|
||||||
|
addi sp,sp,-16 # ...
|
||||||
|
sw ra,12(sp) # ...
|
||||||
|
jal ra, main # jump to C code!
|
Loading…
Reference in New Issue