lcpu/lua: Move Lua device implementations into seperate "LCPU.Devices" namespace

This commit is contained in:
Lily Tsuru 2023-07-28 18:12:27 -04:00
parent 8eb4b6ef41
commit d3cee95b14
7 changed files with 138 additions and 86 deletions

3
.gitignore vendored
View File

@ -3,5 +3,8 @@ build/
module_build/ module_build/
native/projects/riscv/ref native/projects/riscv/ref
# for now
native/projects/projgen
.cache/ .cache/
.vscode/ .vscode/

View File

@ -23,27 +23,28 @@ This is basically the working ideas for the LCPU project.
- At the root of a project, a `project.json` file is expected to exist, with contents like: - At the root of a project, a `project.json` file is expected to exist, with contents like:
```json ```json
{ {
"project": { "Project": {
// All configurations for a project. "Name": "test",
"configurations": {
"debug": { // Define all build configurations here
"CCompileFlags": "-O0 -g ${BaseCCompileFlags}", "Configurations": {
"CppCompileFlags": "-O0 -g ${BaseCppCompileFlags}", "Debug": {
"LinkerScript": "binary.ld", "CCompileFlags": "-O0 ${BaseCCompileFlags}",
"CppCompileFlags": "-O0 ${BaseCppCompileFlags}"
}, },
"release": { "Release": {
"CCompileFlags": "-O2 ${BaseCCompileFlags}", "CCompileFlags": "-O2 ${BaseCCompileFlags}",
"CppCompileFlags": "-O2 ${BaseCppCompileFlags}", "CppCompileFlags": "-O2 ${BaseCppCompileFlags}",
"LinkerScript": "binary.ld", // If a variable is unset it will usually default to
"LinkerFlags": "-Wl,--gc-sections" // ${Base${VariableName}}
}, "LinkerFlags": "-Wl,--gc-sections ${BaseLinkerFlags}"
}
}, },
// Obviously you can use separate subdirectories;
// this is just a very very simple baremetal program.
"Sources": [ "Sources": [
"startup.S", "binary.ld",
"main.cpp" "start.S",
"main.c"
] ]
} }
} }
@ -53,19 +54,24 @@ This is basically the working ideas for the LCPU project.
- This will be transpiled into a `Makefile` by the addon. - 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?) - 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}` - 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` - 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 - There is no conditional compilation in the `project.json` system
- All files in a project are always built by that project. - 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 - Text editor used to edit project source files
- Use the Wire editor? (we need wiremod anyways, and the text editor is.. OK I suppose.) - 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? - 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.) - 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 ## Moderation/administration tools

View File

@ -9,7 +9,14 @@ if SERVER then
return return
end end
LCPU = {};
LCPU.Devices = {};
--LCPUNative.EnableDebug() --LCPUNative.EnableDebug()
AddCSLuaFile("entities/gmod_lcpu_cpu.lua") AddCSLuaFile("entities/gmod_lcpu_cpu.lua")
-- Serverside devices
include("lcpu/devices/uart.lua")
include("lcpu/devices/gmlua_test.lua")
end end

View File

@ -12,69 +12,16 @@ function ENT:Initialize()
self:PhysicsInit(SOLID_VPHYSICS) self:PhysicsInit(SOLID_VPHYSICS)
self:SetMoveType(MOVETYPE_VPHYSICS) self:SetMoveType(MOVETYPE_VPHYSICS)
self:SetSolid(SOLID_VPHYSICS) self:SetSolid(SOLID_VPHYSICS)
-- CPU callbacks?
self.cpu = LCPUNative.CreateCPU(128 * 1024) self.cpu = LCPUNative.CreateCPU(128 * 1024)
-- UART -- UART & GLua test device
self.uart = LCPUNative.CreateDevice(0x10000000, 0xc) self.uart = LCPU.Devices.UART(0x10000000)
self.uart.buffer = "" self.test_device = LCPU.Devices.LuaTest()
function self.uart:Peek(address) self.cpu:AttachDevice(self.uart)
if address == self.Base then return 0 end self.cpu:AttachDevice(self.test_device)
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:SetOverlayText("hi :)") self:SetOverlayText("hi :)")
end end

View File

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

35
lua/lcpu/devices/uart.lua Normal file
View File

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

23
test-gmod/project.json Normal file
View File

@ -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"
]
}
}