diff --git a/.gitignore b/.gitignore index 18168a7..cb93901 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ build/ -lua/bin +# this one is created by the build script +module_build/ native/projects/riscv/ref .cache/ diff --git a/build_module.sh b/build_module.sh index 27b654a..61effc0 100755 --- a/build_module.sh +++ b/build_module.sh @@ -2,15 +2,17 @@ # Build the LCPU addon for the reccomended environment # and install it into the proper directory gmod wants native modules to be. -# This expects to be ran in [gmod]/addons/[addon folder]. set -x -cmake -Wno-dev -GNinja -S native -B build -DCMAKE_BUILD_TYPE=Release -ninja -C build +# where your game server is +GS_PATH="/home/lily/gs/gmod" -[[ ! -d '../../lua/bin' ]] && { - mkdir -p ../../lua/bin +cmake -Wno-dev -GNinja -S native -B module_build -DCMAKE_BUILD_TYPE=Release +ninja -C module_build + +[[ ! -d "$GS_PATH/garrysmod/lua/bin" ]] && { + mkdir -p $GS_PATH/garrysmod/lua/bin } -cp -v build/projects/lcpu/*.dll ../../lua/bin +cp -v module_build/projects/lcpu/*.dll $GS_PATH/garrysmod/lua/bin diff --git a/lua/autorun/lcpu_load.lua b/lua/autorun/lcpu_load.lua index be069d4..4b95770 100644 --- a/lua/autorun/lcpu_load.lua +++ b/lua/autorun/lcpu_load.lua @@ -1,3 +1,15 @@ --- skeleton load file to get gmod to recognize this as an addon --- this will contain files later on in life. AddCSLuaFile() + +-- prime the native lua module if running on the server +if SERVER then + require("lcpu_native") + + if LCPUNative.ModuleVersion != 1 then + print("Your LCPU native module is somehow lagging behind the Lua code. Please rebuild it.") + LCPUNative = nil + return + end + + AddCSLuaFile("entities/gmod_lcpu_cpu.lua") + AddCSLuaFile("lcpu/stool_helper.lua") +end diff --git a/lua/entities/gmod_lcpu_cpu.lua b/lua/entities/gmod_lcpu_cpu.lua new file mode 100644 index 0000000..685b838 --- /dev/null +++ b/lua/entities/gmod_lcpu_cpu.lua @@ -0,0 +1,20 @@ +AddCSLuaFile() +DEFINE_BASECLASS("base_wire_entity") -- for now? +ENT.PrintName = "LCPU" +ENT.Author = "Lily <3" +-- no more, this deeply uses native APIs +if CLIENT then return end + +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(64 * 1024) +end + +function ENT:Think() + self.cpu:Cycle() + -- Even though this is gated by tickrate I'm just trying to be nice here + self:NextThink(CurTime() + 0.1) +end diff --git a/lua/weapons/gmod_tool/stools/lcpu.lua b/lua/weapons/gmod_tool/stools/lcpu.lua new file mode 100644 index 0000000..4452945 --- /dev/null +++ b/lua/weapons/gmod_tool/stools/lcpu.lua @@ -0,0 +1,98 @@ +-- default vars +TOOL.Mode = "lcpu_lcpu" +TOOL.short_name = "lcpu" +TOOL.Category = "Lily <3" +TOOL.Name = "#tool.lcpu.name" + +if CLIENT then + language.Add("tool.lcpu.name", "LCPU Tool") + language.Add("tool.lcpu.desc", "Spawns a LCPU") + language.Add("tool.lcpu.model", "LCPU Model:") + TOOL.Information = { + { + name = "left", + text = "Spawn or update a LCPU" + }, + } + -- { name = "right", text = "Open editor" }, + -- { name = "reload", text = "Attach debugger" }, + -- { name = "reload_shift", text = "Shift+Reload: Clear" }, +end + +TOOL.ClientConVar = { + model = "models/cheeze/wires/cpu.mdl", +} + +if CLIENT then + ------------------------------------------------------------------------------ + -- Make sure firing animation is displayed clientside + ------------------------------------------------------------------------------ + function TOOL:LeftClick() + return true + end + + function TOOL:Reload() + return true + end + + function TOOL:RightClick() + return false + end +end + +if SERVER then + --function TOOL:Reload(trace) + -- if trace.Entity:IsPlayer() then return false end + -- local player = self:GetOwner() + + -- return true + --end + + + + function TOOL:LeftClick() + return true + end + + -- Right click: open editor + --function TOOL:RightClick(trace) + -- return true + --end +end + +if CLIENT then + ------------------------------------------------------------------------------ + -- Build tool control panel + ------------------------------------------------------------------------------ + function TOOL.BuildCPanel(panel) + print("bruh") + panel:AddControl( "Header", { Description = "#tool.lcpu.desc" } ) + panel:AddControl( + "Label", + { + Text = "LCPU settings:" + } + ) + + panel:AddControl( + "PropSelect", + { + Label = "#tool.lcpu.model", + ConVar = "model", + Height = 0, + Models = list.Get("Wire_gate_Models") + } + ) + + panel:AddControl( + "Label", + { + Text = "" + } + ) + end + -- function TOOL:DrawToolScreen(width, height) + -- local currentTime = os.date("*t") + -- CPULib.RenderCPUTool(currentTime.yday % 4,"CPU") + -- end +end diff --git a/native/projects/lcpu/CMakeLists.txt b/native/projects/lcpu/CMakeLists.txt index 7b60104..222783b 100644 --- a/native/projects/lcpu/CMakeLists.txt +++ b/native/projects/lcpu/CMakeLists.txt @@ -2,8 +2,11 @@ include(./gmod_headers.cmake) add_library(lcpu_native SHARED - src/main.cpp + src/SourceSink.cpp + + src/main.cpp + src/LuaCpu.cpp ) if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") diff --git a/native/projects/lcpu/src/LuaCpu.cpp b/native/projects/lcpu/src/LuaCpu.cpp new file mode 100644 index 0000000..1850075 --- /dev/null +++ b/native/projects/lcpu/src/LuaCpu.cpp @@ -0,0 +1,108 @@ +#include "LuaCpu.hpp" + +#include + +#include "LuaMember.hpp" + +// this is temporary from the thing + +/// 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; + fputc(c, stderr); + } + } +}; + +int LuaCpu::__lua_typeid; + +void LuaCpu::Bind(GarrysMod::Lua::ILuaBase* LUA) { + lucore::LogInfo("In LuaCpu::Bind()"); + __lua_typeid = LUA->CreateMetaTable("LuaCpu"); + LUA->PushSpecial(GarrysMod::Lua::SPECIAL_REG); + LUA->PushNumber(__lua_typeid); + LUA->SetField(-2, "LuaCpu__typeid"); + LUA->Pop(); // Pop the registry + + LUA->Push(-1); + // This method is called when the GC is done with our stuff + LUA->PushCFunction(__gc); + LUA->SetField(-1, "__gc"); + + LUA->PushCFunction(Cycle); + LUA->SetField(-1, "Cycle"); + LUA->Pop(); +} + +void LuaCpu::Create(GarrysMod::Lua::ILuaBase* LUA, u32 memorySize) { + LUA->PushUserType(new LuaCpu(memorySize), __lua_typeid); +} + +LuaCpu::LuaCpu(u32 memorySize) { + system = riscv::System::Create(memorySize); + system->OnPowerOff = [&]() { this->OnSysconShutdown(); }; + + system->bus->AttachDevice(new SimpleUartDevice); + + // lame test code. this WILL be removed, I just want this for a quick test + auto fp = std::fopen("/home/lily/gs/gmod/garrysmod/addons/lcpu/native/projects/riscv_test_harness/test/test.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; +} + +void LuaCpu::CycleImpl() { + if(!poweredOn) + return; + + system->Step(); +} + +void LuaCpu::OnSysconShutdown() { + poweredOn = false; +} + +LUA_MEMBER_FUNCTION(LuaCpu, __gc) { + auto self = LUA->GetUserType(1, __lua_typeid); + if(self != nullptr) { // GetUserType returns nullptr on failure + delete self; + } + + return 0; +} + +LUA_MEMBER_FUNCTION(LuaCpu, Cycle) { + auto self = LUA->GetUserType(1, __lua_typeid); + if(!self) { + LUA->ThrowError("invalid self argument for LuaCpu:Cycle()"); + } + + self->CycleImpl(); + return 0; +} diff --git a/native/projects/lcpu/src/LuaCpu.hpp b/native/projects/lcpu/src/LuaCpu.hpp new file mode 100644 index 0000000..bd08e6a --- /dev/null +++ b/native/projects/lcpu/src/LuaCpu.hpp @@ -0,0 +1,33 @@ +#include + +#include "LuaMember.hpp" + +// A work-in-progress +struct LuaCpu { + /// Lua binding stuff + static void Bind(GarrysMod::Lua::ILuaBase* LUA); + static void Create(GarrysMod::Lua::ILuaBase* LUA, u32 memorySize); + + LuaCpu(u32 memorySize); + ~LuaCpu(); + + private: + void CycleImpl(); + + void OnSysconShutdown(); + + // LUA user type id. + static int __lua_typeid; + + // Metafunctions + LUA_MEMBER_FUNCTION_DECLARE(__gc) + LUA_MEMBER_FUNCTION_DECLARE(__tostring) + + // Called by the LCPU entity for specific tasks: + LUA_MEMBER_FUNCTION_DECLARE(Cycle) + LUA_MEMBER_FUNCTION_DECLARE(SetMemorySize) + + // member variables + riscv::System* system; + bool poweredOn = false; +}; diff --git a/native/projects/lcpu/src/LuaMember.hpp b/native/projects/lcpu/src/LuaMember.hpp new file mode 100644 index 0000000..8e6cc94 --- /dev/null +++ b/native/projects/lcpu/src/LuaMember.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include + +// These are like the official GMOD header LUA_FUNCTION but allow forward declaration +// and implementation inside of classes. Pretty nifty! +#define LUA_MEMBER_FUNCTION_DECLARE(FUNC) \ + static int FUNC##__ImpStatic(GarrysMod::Lua::ILuaBase* LUA); \ + static int FUNC(lua_State* L) { \ + GarrysMod::Lua::ILuaBase* LUA = L->luabase; \ + LUA->SetState(L); \ + return FUNC##__ImpStatic(LUA); \ + } + +#define LUA_MEMBER_FUNCTION(CLASS, FUNC) int CLASS::FUNC##__ImpStatic(GarrysMod::Lua::ILuaBase* LUA) diff --git a/native/projects/lcpu/src/main.cpp b/native/projects/lcpu/src/main.cpp index bb6918c..8ca1d73 100644 --- a/native/projects/lcpu/src/main.cpp +++ b/native/projects/lcpu/src/main.cpp @@ -1,16 +1,60 @@ #include -#include +#include +#include +#include #include "SourceSink.hpp" -LUA_FUNCTION(lcpu_native_test) { +#include "LuaCpu.hpp" + + +LUA_FUNCTION(LCPUNative_CreateCPU) { + LUA->CheckType(1, GarrysMod::Lua::Type::Number); + auto memorySize = (u32)std::round(LUA->GetNumber(1)); + + if (memorySize > (64 * 1024 * 1024)) { + LUA->ThrowError("Over current RAM size limit."); + } + + //LuaCpu::Create(LUA, memorySize); + return 0; } +// Create a device object. This is internally used by the CPU to create Wire and other interface things. +LUA_FUNCTION(LCPUNative_CreateDevice) { + return 0; // for now, while LuaDevice doesn't exist? +} + +void LCPUNative_Bind(GarrysMod::Lua::ILuaBase* LUA) { + LUA->PushSpecial( GarrysMod::Lua::SPECIAL_GLOB ); + LUA->CreateTable(); + LUA->PushNumber(1); + LUA->SetField(-2, "ModuleVersion"); + + LUA->PushCFunction(LCPUNative_CreateCPU); + LUA->SetField(-2, "CreateCPU"); + + LUA->PushCFunction(LCPUNative_CreateDevice); + LUA->SetField(-2, "CreateDevice"); + LUA->SetField(-2, "LCPUNative"); + LUA->Pop(); +} + + + GMOD_MODULE_OPEN() { lucore::Logger::The().AttachSink(lcpu::SourceSink::The()); - lucore::LogInfo("LCPU Native Module loading"); + lucore::LogInfo("LCPU Native Module!"); + + + // Let lua types bind + //LuaCpu::Bind(LUA); + + // Bind the global namespace + LCPUNative_Bind(LUA); + return 0; } diff --git a/native/projects/riscv/include/riscv/Devices/RamDevice.hpp b/native/projects/riscv/include/riscv/Devices/RamDevice.hpp index 56e3ea4..4e19800 100644 --- a/native/projects/riscv/include/riscv/Devices/RamDevice.hpp +++ b/native/projects/riscv/include/riscv/Devices/RamDevice.hpp @@ -9,6 +9,10 @@ namespace riscv::devices { RamDevice(Address base, Address size); virtual ~RamDevice(); + // Resize. DO NOT call this while actively using the memory, + // or you WILL crash. + void Resize(Address newSize); + // Implementation of Device interface Address Base() const override; diff --git a/native/projects/riscv/src/CPU.cpp b/native/projects/riscv/src/CPU.cpp index fb2a8e6..b38e556 100644 --- a/native/projects/riscv/src/CPU.cpp +++ b/native/projects/riscv/src/CPU.cpp @@ -279,9 +279,7 @@ namespace riscv { // case 0xf12: rval = 0x00000000; break; //marchid // case 0xf13: rval = 0x00000000; break; //mimpid // case 0xf14: rval = 0x00000000; break; //mhartid - default: - // MINIRV32_OTHERCSR_READ(csrno, rval); - break; + default: break; } switch(microop) { @@ -309,9 +307,7 @@ namespace riscv { // case 0xf13: break; //mimpid // case 0xf14: break; //mhartid // case 0x301: break; //misa - default: - // MINIRV32_OTHERCSR_WRITE(csrno, writeval); - break; + default: break; } } else if(microop == 0x0) { // "SYSTEM" 0b000 rdid = 0; diff --git a/native/projects/riscv/src/Devices/RamDevice.cpp b/native/projects/riscv/src/Devices/RamDevice.cpp index 3ce512c..33f6ccb 100644 --- a/native/projects/riscv/src/Devices/RamDevice.cpp +++ b/native/projects/riscv/src/Devices/RamDevice.cpp @@ -1,7 +1,5 @@ #include -#include "riscv/Types.hpp" - namespace riscv::devices { RamDevice::RamDevice(Address base, Address size) : memoryBase(base), memorySize(size) { @@ -13,6 +11,15 @@ namespace riscv::devices { delete[] memory; } + void RamDevice::Resize(Address newSize) { + if(memory) { + delete[] memory; + } + + memory = new u8[newSize]; + memorySize = newSize; + } + Address RamDevice::Base() const { return memoryBase; } diff --git a/native/projects/riscv_test_harness/main.cpp b/native/projects/riscv_test_harness/main.cpp index 1cb65fc..2aa35ec 100644 --- a/native/projects/riscv_test_harness/main.cpp +++ b/native/projects/riscv_test_harness/main.cpp @@ -15,8 +15,8 @@ struct SimpleUartDevice : public riscv::Bus::MmioDevice { u32 Peek(riscv::Address address) override { switch(address) { - case BASE_ADDRESS: return 0x60; // active, but no keyboard input - case BASE_ADDRESS + 5: return '\0'; + 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; diff --git a/native/projects/riscv_test_harness/test/main.c b/native/projects/riscv_test_harness/test/main.c index f47208e..fc305f7 100644 --- a/native/projects/riscv_test_harness/test/main.c +++ b/native/projects/riscv_test_harness/test/main.c @@ -53,8 +53,8 @@ void main() { // Shut down the test harness once we're done testing. puts("Tests done, shutting down test harness...\n"); - SYSCON = 0x5555; + //SYSCON = 0x5555; // loop forever - // for(;;); + for(;;); }