diff --git a/README.md b/README.md index ce27288..6bb7fb1 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,11 @@ See [this link](https://git.crustywindo.ws/modeco80/gmod-lcpu) for the actual de # Installation +You will need Wiremod installed, either from the Workshop or cloned as a filesystem addon. + This repository is set up to be a Filesystem Addon; therefore, workflows which clone repositories from Git and put them in addons/ should be able to work with the LCPU addon just fine. -Preliminary installation steps: +Preliminary installation steps (by hand): ``` garrysmod/addons$ git clone --recursive https://git.crustywindo.ws/modeco80/gmod-lcpu.git lcpu @@ -40,7 +42,7 @@ garrysmod/addons/lcpu$ [[ ! -d '../../lua/bin']] && mkdir -p ../../lua/bin && cp On Linux you can alternatively use the `./build_module.sh` script that will do all the build and installation steps automatically, once cloning the repository in the garrysmod/addons folder. -Windows building is currently untested; I see no reason why it wouldn't work but it is not a platform I will focus on specifically. +Windows building is currently untested; I see no reason why it wouldn't work. # Special Thanks diff --git a/ideas.md b/ideas.md index 3b068d5..6d8ff5b 100644 --- a/ideas.md +++ b/ideas.md @@ -13,49 +13,68 @@ This is basically the working ideas for the LCPU project. - Upload a raw binary to execute, generated from any user tooling (goes into server data folder) - Yes, this means you can run Linux in GMod. No, I'm not sorry. - - Or even an ELF? Would require less linker hell? -## Integrated simple project workflow (WIP) +## Integrated simple project workflow - Uses official RISC-V GCC toolchain - - Server operator can control root path of where they have installed it. + - In a podman container for jailing reasons? - Write assembly/C/C++ code using a tiny project system (source for them would go in server data folder ?) - - At the root of a project, a `project.json` file exists, with something like: + - At the root of a project, a `project.json` file is expected to exist, with contents like: ```json { "project": { - "cCompileFlags": "-O2", - "cppCompileFlags": "-O2 -fno-exceptions -fno-rtti", + // All configurations for a project. + "configurations": { + "debug": { + "CCompileFlags": "-O0 -g ${BaseCCompileFlags}", + "CppCompileFlags": "-O0 -g ${BaseCppCompileFlags}", + "LinkerScript": "binary.ld", + }, + "release": { + "CCompileFlags": "-O2 ${BaseCCompileFlags}", + "CppCompileFlags": "-O2 ${BaseCppCompileFlags}", + "LinkerScript": "binary.ld", + "LinkerFlags": "-Wl,--gc-sections" + }, + }, - "sources": [ + // Obviously you can use separate subdirectories; + // this is just a very very simple baremetal program. + "Sources": [ "startup.S", "main.cpp" ] - } } ``` -- No conditional compilation - - All files in a project are built by that project + - `BaseCCompileFlags` and `BaseCppCompileFlags` are defaulted to sane values for each language. + + - This will be transpiled into a `Makefile` by the addon. + - A standalone tool will be provided and used for transpiling `project.json` to a `Makefile` (and maybe even passed into the container and transpiled there, to reduce the actions on the host to just the podman run?) + - which is then run with `make` in a temporary podman container which only has access to the source code for the project (and nothing else, besides riscv tools). + - Command line is probably something like `make CONFIG=${config}` + - the output binary will be stored alongside the source code on the server side, with a name like `${name}-${config}.bin` + - This file can then be selected for loading (without uploading from the client). + + +- There is no conditional compilation in the `project.json` system + - All files in a project are always built by that project. - Text editor used to edit project source files + - Use the Wire editor? (we need wiremod anyways, and the text editor is.. OK I suppose.) - Some example projects? - I joke about it, but an RTOS would be really nice and a good stress test of the project system (for usage in "real" projects.) ## Moderation/administration tools - Admin controlled per-user max RAM size (default 64mb) - - possibly override for "respectful" users and admins (admins probably wouldn't even count)? - Admin controlled per-user max LCPU entity count (default 8) - - Admins don't count to limits -- Admin controled global (affects all placed LCPUs) scheduler cycle rate. - - Couldn't be faster than tickrate though or we might block source (and.. well, i dont think i have to explain) - - I decided not to go with the cpu thread stuff just because its annoying, and would require more state tracking. just ticking in lua using `ENT:Think` should be more than good enough (even if theres a risk of hitching source, but I don't think it's that big of a problem...) - - Project compilations however will definitely end up in a different thread though. Running them in the engine thread would undoubtably cause issues. +- Admin controled global (affects all placed LCPUs) scheduler cycle rate/instruction count. + - default is 0.1/1024 instructions ## Addon interopability diff --git a/lua/autorun/lcpu_load.lua b/lua/autorun/lcpu_load.lua index 74d3791..97eb002 100644 --- a/lua/autorun/lcpu_load.lua +++ b/lua/autorun/lcpu_load.lua @@ -6,9 +6,10 @@ if SERVER then if LCPUNative.ModuleVersion ~= 1 then print("Your LCPU native module is somehow lagging behind the Lua code. Please rebuild it.") LCPUNative = nil - return end - + + --LCPUNative.EnableDebug() + AddCSLuaFile("entities/gmod_lcpu_cpu.lua") end diff --git a/lua/entities/gmod_lcpu_cpu.lua b/lua/entities/gmod_lcpu_cpu.lua index b08ebf3..6f8878e 100644 --- a/lua/entities/gmod_lcpu_cpu.lua +++ b/lua/entities/gmod_lcpu_cpu.lua @@ -1,28 +1,57 @@ AddCSLuaFile() -DEFINE_BASECLASS("base_wire_entity") -- for now? +DEFINE_BASECLASS("base_wire_entity") ENT.PrintName = "LCPU" ENT.Author = "Lily <3" + -- no more, this deeply uses native APIs if CLIENT then return end + +-- TODO: serverside convars to control execution rate & cycle count + function ENT:Initialize() self:PhysicsInit(SOLID_VPHYSICS) self:SetMoveType(MOVETYPE_VPHYSICS) self:SetSolid(SOLID_VPHYSICS) - -- 64 kb of ram for now. self.cpu = LCPUNative.CreateCPU(128 * 1024) + -- UART + self.uart = LCPUNative.CreateDevice(0x10000000, 0xc) + self.uart.buffer = "" + + function self.uart:Peek(address) + if address == self.Base then return 0 end + if address == self.Base + 5 then return 0x60 end -- + return 0xffffffff + end + + function self.uart:Poke(address, value) + if address == self.Base then + local c = bit.band(value, 0x000000ff) + if c == 0 then return end + + -- Newline, reset the buffer + if c == 0xa then + print(self.buffer) + self:Reset() + else + -- Not a newline so we can keep going with it + self.buffer = self.buffer .. string.char(c) + end + end + end + + function self.uart:Reset() + self.buffer = "" + end + -- todo: cpu callbacks? once they become a thing -- test device framework -- (for once something works out how I wanted it to..) self.test_device = LCPUNative.CreateDevice(0x11300000, 0x8) self.test_device.register = 0x0 - --function self.test_device:Clock() - --print("TestDevice Clock()") - --end - function self.test_device:Peek(address) - --print("peek @ " .. address) + --print(string.format("TestDevice:Peek @ 0x%08x", address)) if address == self.Base then return CurTime() end -- it a test! if address == self.Base + 4 then return self.register end @@ -30,26 +59,35 @@ function ENT:Initialize() end function self.test_device:Poke(address, value) - print("poke of address " .. address .. " -> " .. value) + --print(string.format("TestDevice:Poke @ 0x%08x -> 0x%08x", address, value)) if address == self.Base + 4 then - print("LUAREG write") + --print("LUAREG write") self.register = value end end function self.test_device:Reset() - print("device was reset") + --print("TestDevice:Reset") -- clear the register self.register = 0 end - self.cpu:AttachDevice(self.test_device) + + self.cpu:AttachDevice(self.uart); + self.cpu:AttachDevice(self.test_device); + + self:SetOverlayText("hi :)") end function ENT:Think() - -- + -- Avoid running if the cpu is not powered on if not self.cpu:PoweredOn() then return end - self.cpu:Cycle() -- Even though this is gated by tickrate I'm just trying to be nice here self:NextThink(CurTime() + 0.1) + self.cpu:Cycle() +end + +function ENT:PowerOn() + self.cpu:PowerOn() + self:NextThink(CurTime() + 0.1) end diff --git a/lua/wire/stools/lcpu.lua b/lua/wire/stools/lcpu.lua index 621359f..4aaf7be 100644 --- a/lua/wire/stools/lcpu.lua +++ b/lua/wire/stools/lcpu.lua @@ -12,13 +12,11 @@ if CLIENT then text = "Create/Update LCPU" }, } - --{ name = "right", text = "Open editor" }, - --{ name = "reload", text = "Attach debugger" }, - --{ name = "reload_shift", text = "Shift+Reload: Clear" }, end WireToolSetup.BaseLang() -WireToolSetup.SetupMax(7) +WireToolSetup.SetupMax(8) + TOOL.ClientConVar = { model = "models/cheeze/wires/cpu.mdl", } @@ -77,11 +75,22 @@ end if CLIENT then function TOOL.BuildCPanel(panel) - local modelPanel = WireDermaExts.ModelSelect(panel, "lcpu_cpu_model", list.Get("Wire_gate_Models"), 2) + local modelPanel = WireDermaExts.ModelSelect(panel, "wire_lcpu_model", list.Get("Wire_gate_Models"), 2) panel:AddControl( "Label", { - Text = "" + Text = "LCPU Options:" + } + ) + + panel:AddControl("CheckBox", { + Label = "Start powered on" + }) + + panel:AddControl( + "Label", + { + Text = "Wire Interface Device options:" } ) end diff --git a/native/projects/lcpu/src/LcpuGlobals.cpp b/native/projects/lcpu/src/LcpuGlobals.cpp index 922dbe7..0d1e09b 100644 --- a/native/projects/lcpu/src/LcpuGlobals.cpp +++ b/native/projects/lcpu/src/LcpuGlobals.cpp @@ -3,6 +3,7 @@ #include #include "LuaCpu.hpp" #include "LuaDevice.hpp" +#include "lucore/Logger.hpp" namespace lcpu { LUA_FUNCTION(LCPUNative_CreateCPU) { @@ -13,6 +14,7 @@ namespace lcpu { if(memorySize > (64 * 1024 * 1024)) LUA->ThrowError("Over RAM size limit."); + lucore::LogDebug("Creating Lua CPU object with 0x{:08x} memory size", memorySize); LuaCpu::Create(LUA, memorySize); return 1; } @@ -20,12 +22,24 @@ namespace lcpu { 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(base), + lucore::LogDebug("Creating Lua device object mapped @ 0x{:08x} with size 0x{:08x}", static_cast(base), static_cast(size)); LuaDevice::Create(LUA, static_cast(base), static_cast(size)); return 1; } + LUA_FUNCTION(LCPUNative_EnableDebug) { + lucore::LogInfo("Enabling debug logging"); + lucore::Logger::The().SetLogLevel(lucore::Logger::MessageSeverity::Debug); + return 0; + } + + LUA_FUNCTION(LCPUNative_DisableDebug) { + lucore::LogInfo("Disabling debug logging"); + lucore::Logger::The().SetLogLevel(lucore::Logger::MessageSeverity::Info); + return 0; + } + void GlobalsBind(GarrysMod::Lua::ILuaBase* LUA) { LuaCpu::RegisterClass(LUA); LuaDevice::RegisterClass(LUA); @@ -36,8 +50,10 @@ namespace lcpu { 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_CreateCPU, "CreateCPU"); /// Create a CPU + LUA_SET_C_FUNCTION_NAME(LCPUNative_CreateDevice, "CreateDevice"); /// Create a device implemented in Lua + LUA_SET_C_FUNCTION_NAME(LCPUNative_EnableDebug, "EnableDebug"); /// Enable native module debug logging + LUA_SET_C_FUNCTION_NAME(LCPUNative_DisableDebug, "DisableDebug"); /// Disable native module debug logging LUA->SetField(-2, "LCPUNative"); LUA->Pop(); // clang-format on diff --git a/native/projects/lcpu/src/LuaCpu.cpp b/native/projects/lcpu/src/LuaCpu.cpp index 9ae011d..ab5d961 100644 --- a/native/projects/lcpu/src/LuaCpu.cpp +++ b/native/projects/lcpu/src/LuaCpu.cpp @@ -4,40 +4,14 @@ #include "LuaDevice.hpp" -// this is temporary from the test harness, and will be replaced -// at some point. -/// simple 16550 UART implementation -struct SimpleUartDevice : public riscv::Bus::MmioDevice { - constexpr static riscv::Address BASE_ADDRESS = 0x10000000; - - riscv::Address Base() const override { return BASE_ADDRESS; } - riscv::Address Size() const override { return 12; } // for now - - u32 Peek(riscv::Address address) override { - switch(address) { - case BASE_ADDRESS: return '\0'; // just return 0 for the input register - case BASE_ADDRESS + 5: return 0x60; // active, but no keyboard input - } - - return 0; - } - - void Poke(riscv::Address address, u32 value) override { - if(address == BASE_ADDRESS) { - char c = value & 0x000000ff; - std::fputc(c, stderr); - } - } -}; - namespace lcpu { - LUA_MEMBER_FUNCTION_IMPLEMENT(LuaCpu, PoweredOn) { + LUA_CLASS_FUNCTION(LuaCpu, PoweredOn) { auto self = LuaCpu::FromLua(LUA, 1); LUA->PushBool(self->poweredOn); return 1; } - LUA_MEMBER_FUNCTION_IMPLEMENT(LuaCpu, Cycle) { + LUA_CLASS_FUNCTION(LuaCpu, Cycle) { auto self = LuaCpu::FromLua(LUA, 1); if(!self->poweredOn) return 0; @@ -45,7 +19,7 @@ namespace lcpu { return 0; } - LUA_MEMBER_FUNCTION_IMPLEMENT(LuaCpu, PowerOff) { + LUA_CLASS_FUNCTION(LuaCpu, PowerOff) { auto self = LuaCpu::FromLua(LUA, 1); if(!self->poweredOn) return 0; @@ -55,7 +29,7 @@ namespace lcpu { return 0; } - LUA_MEMBER_FUNCTION_IMPLEMENT(LuaCpu, PowerOn) { + LUA_CLASS_FUNCTION(LuaCpu, PowerOn) { auto self = LuaCpu::FromLua(LUA, 1); if(self->poweredOn) return 0; @@ -65,13 +39,13 @@ namespace lcpu { return 0; } - LUA_MEMBER_FUNCTION_IMPLEMENT(LuaCpu, Reset) { + LUA_CLASS_FUNCTION(LuaCpu, Reset) { auto self = LuaCpu::FromLua(LUA, 1); self->system->bus->Reset(); return 0; } - LUA_MEMBER_FUNCTION_IMPLEMENT(LuaCpu, AttachDevice) { + LUA_CLASS_FUNCTION(LuaCpu, AttachDevice) { auto self = LuaCpu::FromLua(LUA, 1); auto device = LuaDevice::FromLua(LUA, 2); @@ -100,7 +74,6 @@ namespace lcpu { }; // 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); diff --git a/native/projects/lcpu/src/LuaCpu.hpp b/native/projects/lcpu/src/LuaCpu.hpp index a5af3ce..2023d45 100644 --- a/native/projects/lcpu/src/LuaCpu.hpp +++ b/native/projects/lcpu/src/LuaCpu.hpp @@ -5,7 +5,7 @@ #include "LuaObject.hpp" namespace lcpu { - /// Bindings of [riscv::System] to Lua. + /// Binding of [riscv::System] to Lua. struct LuaCpu : public lua::LuaObject { /// Lua binding stuff constexpr static const char* Name() { return "LuaCpu"; } @@ -17,12 +17,12 @@ namespace lcpu { ~LuaCpu(); private: - LUA_MEMBER_FUNCTION(PoweredOn); // Check if the CPU is powered on - 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 + LUA_CLASS_FUNCTION_DECL(PoweredOn); // Check if the CPU is powered on + LUA_CLASS_FUNCTION_DECL(Cycle); // do a single cycle (called internally by LCPU entity) + LUA_CLASS_FUNCTION_DECL(PowerOff); // power off and reset the LCPU + LUA_CLASS_FUNCTION_DECL(PowerOn); // power on the LCPU + LUA_CLASS_FUNCTION_DECL(Reset); // reset the LCPU + LUA_CLASS_FUNCTION_DECL(AttachDevice); // attach a LuaDevice to this cpu // member variables riscv::System* system; diff --git a/native/projects/lcpu/src/LuaDevice.cpp b/native/projects/lcpu/src/LuaDevice.cpp index 38ddc2e..006e48b 100644 --- a/native/projects/lcpu/src/LuaDevice.cpp +++ b/native/projects/lcpu/src/LuaDevice.cpp @@ -40,7 +40,7 @@ namespace lcpu { } riscv::Address LuaDevice::Size() const { - return base; + return size; } u32 LuaDevice::Peek(riscv::Address address) { @@ -51,7 +51,6 @@ namespace lcpu { lua->Push(-2); // 'self' argument lua->PushNumber(static_cast(address)); lua->Call(2, 1); - auto result = static_cast(lua->GetNumber(-1)); lua->Pop(2); // pop result and the table off return result; diff --git a/native/projects/lcpu/src/LuaDevice.hpp b/native/projects/lcpu/src/LuaDevice.hpp index 1ddb13e..2466155 100644 --- a/native/projects/lcpu/src/LuaDevice.hpp +++ b/native/projects/lcpu/src/LuaDevice.hpp @@ -6,7 +6,7 @@ #include "LuaObject.hpp" namespace lcpu { - /// A work-in-progress binding of [riscv::Bus::MmioDevice] to lua + /// Binding of [riscv::Bus::MmioDevice] to Lua struct LuaDevice : public riscv::Bus::MmioDevice, lcpu::lua::LuaObject { /// Lua binding stuff constexpr static const char* Name() { return "LuaDevice"; } diff --git a/native/projects/lcpu/src/LuaHelpers.hpp b/native/projects/lcpu/src/LuaHelpers.hpp index b7c88bb..e01f90c 100644 --- a/native/projects/lcpu/src/LuaHelpers.hpp +++ b/native/projects/lcpu/src/LuaHelpers.hpp @@ -1,16 +1,21 @@ -//! Helpers for binding Lua and C++. +//! Helper macros and inlines for binding Lua and C++. +//! //! If you want to bind a C++ class to Lua, see the -//! [lcpu::lua::LuaObject] type in LuaObject.hpp +//! [lcpu::lua::LuaObject] type in LuaObject.hpp. #pragma once #include #include -// These are like the official GMOD header LUA_FUNCTION but allow forward declaration -// and implementation inside of classes, making writing class bindings that much less -// of a PITA. Nifty! -#define LUA_MEMBER_FUNCTION(FUNC) \ +/// Declare a Lua function inside of a class. +/// This is like the official GMOD header's LUA_FUNCTION but allows: +/// - forward declaration +/// - being put inside of a class +/// - an out-of-line implementation (in .cpp file) +/// +/// This makes writing class bindings a lot less annoying. +#define LUA_CLASS_FUNCTION_DECL(FUNC) \ static int FUNC##__ImpStatic(GarrysMod::Lua::ILuaBase* LUA); \ static int FUNC(lua_State* L) { \ GarrysMod::Lua::ILuaBase* LUA = L->luabase; \ @@ -18,20 +23,28 @@ return FUNC##__ImpStatic(LUA); \ } -#define LUA_MEMBER_FUNCTION_IMPLEMENT(CLASS, FUNC) int CLASS::FUNC##__ImpStatic(GarrysMod::Lua::ILuaBase* LUA) +/// Implement a previously declared (with [LUA_CLASS_FUNCTION_DECL]) Lua function +/// from a class. +#define LUA_CLASS_FUNCTION(CLASS, FUNC) int CLASS::FUNC##__ImpStatic(GarrysMod::Lua::ILuaBase* LUA) -// Set a C function as a field. +/// Set a C function as a field of a table. +/// The stack layout prior to this function should be: +/// -1 (top) -> table #define LUA_SET_C_FUNCTION(name) \ LUA->PushCFunction(name); \ LUA->SetField(-2, #name); -// Set a C function as a field with an alternative field name. + +/// Set a C function as a field of a table, with an alternative name. +/// The stack layout prior to this function should be: +/// -1 (top) -> table #define LUA_SET_C_FUNCTION_NAME(name, altName) \ LUA->PushCFunction(name); \ LUA->SetField(-2, altName); namespace lcpu::lua { + /// Get a string from Lua as a STL string. inline std::string GetLuaString(GarrysMod::Lua::ILuaBase* LUA, int stackPos) { unsigned len {}; auto ptr = LUA->GetString(stackPos, &len); diff --git a/native/projects/lcpu/src/LuaObject.hpp b/native/projects/lcpu/src/LuaObject.hpp index ee08cdb..28ad1d1 100644 --- a/native/projects/lcpu/src/LuaObject.hpp +++ b/native/projects/lcpu/src/LuaObject.hpp @@ -4,6 +4,7 @@ #include "GarrysMod/Lua/Interface.h" #include "GarrysMod/Lua/LuaBase.h" +#include "GarrysMod/Lua/Types.h" #include "LuaHelpers.hpp" namespace lcpu::lua { @@ -27,6 +28,8 @@ namespace lcpu::lua { /// Register a setter. This can be used to make a /// C++ registered value read-write. + /// Notes: + /// - Stack index 3 will always be the value to be set. static void RegisterSetter(const std::string& name, ILuaVoidFunc func) { setters()[name] = func; } virtual void AfterLuaInit() {}; @@ -42,7 +45,6 @@ namespace lcpu::lua { } static TImpl* FromLua(GarrysMod::Lua::ILuaBase* LUA, int stackPos) { - LUA->CheckType(stackPos, __lua_typeid); return LUA->GetUserType(stackPos, __lua_typeid); } @@ -95,11 +97,14 @@ namespace lcpu::lua { RegisterGetter("Name", [](GarrysMod::Lua::ILuaBase* LUA) { LUA->PushString(TImpl::Name()); }); } + /// Get the user property table reference. This can be used + /// to fetch user properties from native C++ code. int GetTableReference() { return tableReference; } LuaObject() = default; + virtual ~LuaObject() { - // free the table reference + // free the table reference so it gets garbage collected too if(tableReference != -1) lua->ReferenceFree(tableReference); } @@ -108,12 +113,11 @@ namespace lcpu::lua { GarrysMod::Lua::ILuaBase* lua; private: - // base metamethods - LUA_MEMBER_FUNCTION(__gc) - LUA_MEMBER_FUNCTION(__index) - LUA_MEMBER_FUNCTION(__newindex) + LUA_CLASS_FUNCTION_DECL(__gc) + LUA_CLASS_FUNCTION_DECL(__index) + LUA_CLASS_FUNCTION_DECL(__newindex) - // static stuff + /// Lua type ID for this wrapped class static int __lua_typeid; static auto& methods() { @@ -139,19 +143,20 @@ namespace lcpu::lua { int LuaObject::__lua_typeid = 0; template - LUA_MEMBER_FUNCTION_IMPLEMENT(LuaObject, __gc) { + LUA_CLASS_FUNCTION(LuaObject, __gc) { auto self = FromLua(LUA, 1); if(self != nullptr) { - lucore::LogInfo("GCing LuaObject {} @ {:p}", TImpl::Name(), static_cast(self)); + lucore::LogDebug("GCing LuaObject {} @ {:p}", TImpl::Name(), static_cast(self)); delete self; } return 0; } template - LUA_MEMBER_FUNCTION_IMPLEMENT(LuaObject, __index) { + LUA_CLASS_FUNCTION(LuaObject, __index) { auto self = FromLua(LUA, 1); + // If the key is something we support, if(LUA->GetType(2) == GarrysMod::Lua::Type::String) { auto& methods = LuaObject::methods(); auto& getters = LuaObject::getters(); @@ -167,19 +172,22 @@ namespace lcpu::lua { getters[key](LUA); return 1; } + + lucore::LogDebug("LuaObject::__index({}) going to table", key); } - // look up from the table + // Failing to look up an item is not fatal; + // we simply then look up the key in the backing table. // clang-format off - LUA->ReferencePush(self->tableReference); - LUA->Push(2); - LUA->GetTable(-2); + LUA->ReferencePush(self->tableReference); // push table reference + LUA->Push(2); // push key onto the stack + LUA->GetTable(-2); // push table[key] onto the top of stack // clang-format on return 1; } template - LUA_MEMBER_FUNCTION_IMPLEMENT(LuaObject, __newindex) { + LUA_CLASS_FUNCTION(LuaObject, __newindex) { auto self = FromLua(LUA, 1); if(LUA->GetType(2) == GarrysMod::Lua::Type::String) { @@ -197,22 +205,21 @@ namespace lcpu::lua { 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(); + setters[key](LUA); return 0; } + + + lucore::LogDebug("LuaObject::__newindex({}) going to table", key); } - // push onto the table + + // set the provided value onto the table // clang-format off - LUA->ReferencePush(self->tableReference); - LUA->Push(2); - LUA->Push(3); - LUA->SetTable(-3); + LUA->ReferencePush(self->tableReference); // table + LUA->Push(2); // key + LUA->Push(3); // value + LUA->SetTable(-3); // table[key] = value LUA->Pop(); // clang-format on return 0; diff --git a/native/projects/riscv/src/Bus.cpp b/native/projects/riscv/src/Bus.cpp index 819cfa9..66be132 100644 --- a/native/projects/riscv/src/Bus.cpp +++ b/native/projects/riscv/src/Bus.cpp @@ -1,6 +1,7 @@ #include #include #include +#include namespace riscv { @@ -34,10 +35,10 @@ namespace riscv { // Refuse to overlap a device at its base address.. if(FindDeviceForAddress(upcasted->Base())) return false; + // ... or have the end overlap the start of another device. else if(FindDeviceForAddress(upcasted->Base() + upcasted->Size())) return false; - mmio_devices[upcasted->Base()] = upcasted; } @@ -123,6 +124,8 @@ namespace riscv { Bus::Device* Bus::FindDeviceForAddress(Address address) const { auto try_find_device = [&](const auto& container, Address address) { return std::find_if(container.begin(), container.end(), [&](const auto& pair) { + //lucore::LogInfo("0x{:08x} base, 0x{:08x} size -> {}", pair.first, pair.second->Size(), static_cast(pair.second)); + return // We can shorcut region checking if the requested addess matches base address. pair.first == address || diff --git a/test-gmod/main.c b/test-gmod/main.c index a03fb08..207b6dc 100644 --- a/test-gmod/main.c +++ b/test-gmod/main.c @@ -1,5 +1,5 @@ -// a simple test program - this version would talk to a device -// written in GLua +// a simple test program - this version talks to a device +// that is implemented in GLua thanks to the LCPU native addon #include #include @@ -14,7 +14,7 @@ uint32_t strlen(const char* str) { #define GLUA_DEVICE_BASE 0x11300000 // base address of the lua test device #define GLUA_DEVICE_WORLDTIME *(volatile uint32_t*)GLUA_DEVICE_BASE // world time register (read only) -#define GLUA_DEVICE_LUAREG *(volatile uint32_t*)(GLUA_DEVICE_BASE + 4) // lua register (read/write) +#define GLUA_DEVICE_LUAREG *(volatile uint32_t*)(GLUA_DEVICE_BASE + 4) // lua number register (read/write) #define SYSCON *(volatile uint32_t*)0x11100000 @@ -74,6 +74,8 @@ void vprintf(const char* format, va_list val) { case '%': if(format[i + 1] == '%') putc('%'); + if(format[i+1] == '\0') + return; switch(format[i + 1]) { case 'i': case 'd': { @@ -98,6 +100,8 @@ void vprintf(const char* format, va_list val) { default: putc(' '); break; } break; + case '\0': // band-aid fix. + return; default: putc(format[i]); break; } } @@ -111,7 +115,7 @@ void printf(const char* format, ...) { } void main() { - puts("fuck you garry I win"); + puts("fuck you garry I win\n"); for(int i = 0; i < 8; ++i) printf("GLUA_DEVICE_WORLDTIME reading says -> %d\n", GLUA_DEVICE_WORLDTIME);