diff --git a/.gitignore b/.gitignore index cb93901..678b4b1 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,8 @@ build/ module_build/ native/projects/riscv/ref +# for now +native/projects/projgen + .cache/ .vscode/ diff --git a/ideas.md b/ideas.md index 6d8ff5b..1482225 100644 --- a/ideas.md +++ b/ideas.md @@ -22,50 +22,56 @@ This is basically the working ideas for the LCPU project. - 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 is expected to exist, with contents like: ```json - { - "project": { - // 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" - }, - }, + { + "Project": { + "Name": "test", - // Obviously you can use separate subdirectories; - // this is just a very very simple baremetal program. - "Sources": [ - "startup.S", - "main.cpp" - ] - } + // Define all build configurations here + "Configurations": { + "Debug": { + "CCompileFlags": "-O0 ${BaseCCompileFlags}", + "CppCompileFlags": "-O0 ${BaseCppCompileFlags}" + }, + "Release": { + "CCompileFlags": "-O2 ${BaseCCompileFlags}", + "CppCompileFlags": "-O2 ${BaseCppCompileFlags}", + // If a variable is unset it will usually default to + // ${Base${VariableName}} + "LinkerFlags": "-Wl,--gc-sections ${BaseLinkerFlags}" + } + }, + + "Sources": [ + "binary.ld", + "start.S", + "main.c" + ] } + } ``` - `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). + - which, when a Build is done in GMod; 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). + - This file can then be selected for loading (without needing to be uploaded from the client). - There is no conditional compilation in the `project.json` system - All files in a project are always built by that project. +- No notion of subprojects/build dependencies other than GCC generated dependencies + - This is meant to be simple for easy development in GMod. If you want complex build features you can export the project onto your own computer and use `lcpu_projgen` to generate Makefiles (which you can then maintain) + - Text editor used to edit project source files - Use the Wire editor? (we need wiremod anyways, and the text editor is.. OK I suppose.) + - Or I guess I could try getting Monaco to play nicely with DHTML - Some example projects? + - A simple bare metal "Hello World" - 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 diff --git a/lua/autorun/lcpu_load.lua b/lua/autorun/lcpu_load.lua index 97eb002..669edc6 100644 --- a/lua/autorun/lcpu_load.lua +++ b/lua/autorun/lcpu_load.lua @@ -9,7 +9,14 @@ if SERVER then return end - --LCPUNative.EnableDebug() + LCPU = {}; + LCPU.Devices = {}; + --LCPUNative.EnableDebug() + AddCSLuaFile("entities/gmod_lcpu_cpu.lua") + + -- Serverside devices + include("lcpu/devices/uart.lua") + include("lcpu/devices/gmlua_test.lua") end diff --git a/lua/entities/gmod_lcpu_cpu.lua b/lua/entities/gmod_lcpu_cpu.lua index 6f8878e..f8144e8 100644 --- a/lua/entities/gmod_lcpu_cpu.lua +++ b/lua/entities/gmod_lcpu_cpu.lua @@ -12,69 +12,16 @@ function ENT:Initialize() self:PhysicsInit(SOLID_VPHYSICS) self:SetMoveType(MOVETYPE_VPHYSICS) self:SetSolid(SOLID_VPHYSICS) + + -- CPU callbacks? self.cpu = LCPUNative.CreateCPU(128 * 1024) - -- UART - self.uart = LCPUNative.CreateDevice(0x10000000, 0xc) - self.uart.buffer = "" + -- UART & GLua test device + self.uart = LCPU.Devices.UART(0x10000000) + self.test_device = LCPU.Devices.LuaTest() - 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: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 - - return 0xffffffff - end - - function self.test_device:Poke(address, value) - --print(string.format("TestDevice:Poke @ 0x%08x -> 0x%08x", address, value)) - if address == self.Base + 4 then - --print("LUAREG write") - self.register = value - end - end - - function self.test_device:Reset() - --print("TestDevice:Reset") - -- clear the register - self.register = 0 - end - - - self.cpu:AttachDevice(self.uart); - self.cpu:AttachDevice(self.test_device); + self.cpu:AttachDevice(self.uart) + self.cpu:AttachDevice(self.test_device) self:SetOverlayText("hi :)") end diff --git a/lua/lcpu/devices/gmlua_test.lua b/lua/lcpu/devices/gmlua_test.lua new file mode 100644 index 0000000..bff57d8 --- /dev/null +++ b/lua/lcpu/devices/gmlua_test.lua @@ -0,0 +1,31 @@ +-- Lua test device. This'll probably get removed soon, this is just for testing +-- that Lua->C++ interop actually works like it should + +function LCPU.Devices.LuaTest() + local test_device = LCPUNative.CreateDevice(0x11300000, 0x8) + test_device.register = 0x0 + + function test_device: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 + + return 0xffffffff + end + + function test_device:Poke(address, value) + --print(string.format("TestDevice:Poke @ 0x%08x -> 0x%08x", address, value)) + if address == self.Base + 4 then + --print("LUAREG write") + self.register = value + end + end + + function test_device:Reset() + --print("TestDevice:Reset") + -- clear the register + self.register = 0 + end + + return test_device +end diff --git a/lua/lcpu/devices/uart.lua b/lua/lcpu/devices/uart.lua new file mode 100644 index 0000000..0c38d99 --- /dev/null +++ b/lua/lcpu/devices/uart.lua @@ -0,0 +1,35 @@ +-- UART device +-- For now, this just prints to the server console. +function LCPU.Devices.UART(base) + local uart = LCPUNative.CreateDevice(base, 0xc) + uart.buffer = "" + + function uart:Peek(address) + if address == self.Base then return 0 end + if address == self.Base + 5 then return 0x60 end -- Active, but no keyboard input + return 0xffffffff + end + + function uart:Poke(address, value) + if address == self.Base then + local c = bit.band(value, 0x000000ff) + if c == 0 then return end + + -- On newline or reaching length limit + -- print the buffer and then reset it + if c == 0xa or #self.buffer >= 256 then + print(string.format("UART: %s", 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 uart:Reset() + self.buffer = "" + end + + return uart +end diff --git a/test-gmod/project.json b/test-gmod/project.json new file mode 100644 index 0000000..365f713 --- /dev/null +++ b/test-gmod/project.json @@ -0,0 +1,23 @@ +{ + "Project": { + "Name": "test", + + "Configurations": { + "Debug": { + "CCompileFlags": "-O0 ${BaseCCompileFlags}", + "CppCompileFlags": "-O0 ${BaseCppCompileFlags}" + }, + "Release": { + "CCompileFlags": "-O2 ${BaseCCompileFlags}", + "CppCompileFlags": "-O2 ${BaseCppCompileFlags}", + "LinkerFlags": "-Wl,--gc-sections ${BaseLinkerFlags}" + } + }, + + "Sources": [ + "binary.ld", + "start.S", + "main.c" + ] + } +}