lcpu: Misc cleanup

LuaDevice now works fully, without any weird bugs. Yay!
This took quite a bit of bugstomping to arrive to; some of which ended up discovering I'm not particularly proud of doing. Oops!

The LuaCpu implementation no longer has a UART implementation in C++ (which actually leaked everytime a LuaCpu was garbage collected... Yeesh) - instead the Lua entity code actually implements the UART! Pretty cool to see the fruits of my labor actually working!

The LuaHelpers macros are renamed slightly to make them less of a pain (and also because I think having `_IMPLEMENT` in a function implementation is a bit stupid.)

I have updated the ideas document to better reflect my plans for the project system.
This commit is contained in:
Lily Tsuru 2023-07-28 06:05:58 -04:00
parent bdd6839fa2
commit 8eb4b6ef41
14 changed files with 209 additions and 125 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,6 +3,7 @@
#include <GarrysMod/Lua/Interface.h>
#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<riscv::Address>(base),
lucore::LogDebug("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_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

View File

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

View File

@ -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<LuaCpu> {
/// 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;

View File

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

View File

@ -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<LuaDevice> {
/// Lua binding stuff
constexpr static const char* Name() { return "LuaDevice"; }

View File

@ -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<TImpl>] type in LuaObject.hpp
//! [lcpu::lua::LuaObject<TImpl>] type in LuaObject.hpp.
#pragma once
#include <GarrysMod/Lua/Interface.h>
#include <lucore/Logger.hpp>
// 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);

View File

@ -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<TImpl>(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<TImpl>::__lua_typeid = 0;
template <class TImpl>
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaObject<TImpl>, __gc) {
LUA_CLASS_FUNCTION(LuaObject<TImpl>, __gc) {
auto self = FromLua(LUA, 1);
if(self != nullptr) {
lucore::LogInfo("GCing LuaObject {} @ {:p}", TImpl::Name(), static_cast<void*>(self));
lucore::LogDebug("GCing LuaObject {} @ {:p}", TImpl::Name(), static_cast<void*>(self));
delete self;
}
return 0;
}
template <class TImpl>
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaObject<TImpl>, __index) {
LUA_CLASS_FUNCTION(LuaObject<TImpl>, __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 <class TImpl>
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaObject<TImpl>, __newindex) {
LUA_CLASS_FUNCTION(LuaObject<TImpl>, __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();
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;

View File

@ -1,6 +1,7 @@
#include <algorithm>
#include <riscv/Bus.hpp>
#include <riscv/CPU.hpp>
#include <lucore/Logger.hpp>
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<void*>(pair.second));
return
// We can shorcut region checking if the requested addess matches base address.
pair.first == address ||

View File

@ -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 <stdarg.h>
#include <stdint.h>
@ -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);