lcpu: officially support legacy 32bit branch
32-bit builds using the development headers work on the legacy 32-bit branch so I don't really see a reason to not support it.. sure, it sucks that I now won't be able to get rid of a pretty gnarly codepath, but /shrug
This commit is contained in:
parent
6eada83eeb
commit
7ae6acfc3b
19
README.md
19
README.md
|
@ -9,7 +9,7 @@ It provides:
|
|||
(that and the more reasons to not use Lua for a part of this codebase, the better...)
|
||||
- Interoperation with the Wiremod addon (and addons which implement Wiremod integration)
|
||||
|
||||
This addon (for now) works with both the non-beta branch and the x86-64 beta branches of GMod.
|
||||
This addon works with both the non-beta branch and the x86-64 beta branches of GMod, however rigorous testing is usually only done with the x86-64 beta branch.
|
||||
|
||||
Note that the GitHub mirror is provided only for convinence, and is not used for active development.
|
||||
See [this link](https://git.crustywindo.ws/modeco80/gmod-lcpu) for the actual development repository.
|
||||
|
@ -20,28 +20,15 @@ You will need Wiremod installed, either from the Workshop or cloned as a filesys
|
|||
|
||||
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 (by hand):
|
||||
Preliminary installation steps (for Linux):
|
||||
|
||||
```
|
||||
garrysmod/addons$ git clone --recursive https://git.crustywindo.ws/modeco80/gmod-lcpu.git lcpu
|
||||
garrysmod/addons$ cd lcpu
|
||||
|
||||
# Build the LCPU native module. These steps build the linux64 version of the module
|
||||
# on linux; you'll need to alter it if you want to build for 32-bit.
|
||||
garrysmod/addons/lcpu$ cmake -Wno-dev -GNinja -S native -B build \
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
garrysmod/addons/lcpu$ ninja -C build
|
||||
|
||||
# Install the native module (Linux)
|
||||
# For Windows you can do the same thing, just replace this
|
||||
# with how you'd do it in batch (or use Explorer, I'm not your dad)
|
||||
garrysmod/addons/lcpu$ [[ ! -d '../../lua/bin']] && mkdir -p ../../lua/bin && cp build/projects/lcpu/*.dll ../../lua/bin
|
||||
|
||||
garrysmod/addons/lcpu$ ./build_module.sh
|
||||
# Tada!
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Build the LCPU addon for the reccomended environment
|
||||
# Build the LCPU native module for both linux32 and linux64
|
||||
# and install it into the proper directory gmod wants native modules to be.
|
||||
|
||||
set -x
|
||||
|
@ -8,11 +8,38 @@ set -x
|
|||
# where your game server is
|
||||
GS_PATH="/home/lily/gs/gmod"
|
||||
|
||||
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
|
||||
# make the module build directory
|
||||
[[ ! -d "module_build" ]] && {
|
||||
mkdir module_build
|
||||
}
|
||||
|
||||
cp -v module_build/projects/lcpu/*.dll $GS_PATH/garrysmod/lua/bin
|
||||
# $1 cmake build dir
|
||||
# $@ any other args to cmake
|
||||
cmake_gen() {
|
||||
local BD=$1
|
||||
shift
|
||||
cmake -Wno-dev -GNinja -S native -B module_build/$BD $@ -DCMAKE_BUILD_TYPE=Release
|
||||
}
|
||||
|
||||
# $1 cmake build dir
|
||||
cmake_build() {
|
||||
ninja -C module_build/$1
|
||||
}
|
||||
|
||||
build_and_place() {
|
||||
cmake_gen linux32 --toolchain $PWD/native/cmake/linux32-toolchain.cmake
|
||||
cmake_gen linux64
|
||||
cmake_build linux32
|
||||
cmake_build linux64
|
||||
|
||||
# GMod doesn't actually make the lua/bin directory on its own
|
||||
# so we have to check if it exists first and make if it doesn't
|
||||
[[ ! -d "$GS_PATH/garrysmod/lua/bin" ]] && {
|
||||
mkdir -p $GS_PATH/garrysmod/lua/bin
|
||||
}
|
||||
|
||||
cp -v module_build/linux32/projects/lcpu/*.dll $GS_PATH/garrysmod/lua/bin
|
||||
cp -v module_build/linux64/projects/lcpu/*.dll $GS_PATH/garrysmod/lua/bin
|
||||
}
|
||||
|
||||
build_and_place
|
||||
|
|
|
@ -9,8 +9,6 @@ add_subdirectory(projects/lucore)
|
|||
# RISC-V emulator library
|
||||
add_subdirectory(projects/riscv)
|
||||
|
||||
add_subdirectory(projects/riscv_test_harness)
|
||||
|
||||
# Garry's Mod native bindings to RISC-V emulator
|
||||
# Also lua device stuff
|
||||
add_subdirectory(projects/lcpu)
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
# toolchain file for linux multilib building
|
||||
set(CMAKE_C_FLAGS "-m32" CACHE STRING "C compiler flags" FORCE)
|
||||
set(CMAKE_CXX_FLAGS "-m32" CACHE STRING "C++ compiler flags" FORCE)
|
||||
|
||||
if(EXISTS /usr/lib32)
|
||||
set(LIB32 /usr/lib32)
|
||||
else()
|
||||
set(LIB32 /usr/lib)
|
||||
endif()
|
||||
|
||||
|
||||
set(CMAKE_SYSTEM_LIBRARY_PATH ${LIB32} CACHE STRING "system library search path" FORCE)
|
||||
set(CMAKE_LIBRARY_PATH ${LIB32} CACHE STRING "library search path" FORCE)
|
||||
|
||||
# this is probably unlikely to be needed, but just in case
|
||||
set(CMAKE_EXE_LINKER_FLAGS "-m32 -L${LIB32}" CACHE STRING "executable linker flags" FORCE)
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "-m32 -L${LIB32}" CACHE STRING "shared library linker flags" FORCE)
|
||||
set(CMAKE_MODULE_LINKER_FLAGS "-m32 -L${LIB32}" CACHE STRING "module linker flags" FORCE)
|
||||
|
||||
# point pkg-config to lib32
|
||||
if(EXISTS ${LIB32}/pkgconfig)
|
||||
set(ENV{PKG_CONFIG_LIBDIR} ${LIB32}/pkgconfig:/usr/share/pkgconfig:/usr/lib/pkgconfig:/usr/lib64/pkgconfig)
|
||||
endif()
|
|
@ -1,15 +1,9 @@
|
|||
#include "SourceSink.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <lucore/Assert.hpp>
|
||||
#include <lucore/Library.hpp>
|
||||
#include <lucore/Types.hpp>
|
||||
|
||||
// The old non-beta branch of GMod on Linux has multiple tier0 libraries for client and server.
|
||||
// This compatibility define allows to support that case (for now). Once this define is removed,
|
||||
// the old codepath can be totally removed.
|
||||
#define LCPU_SUPPORT_OLD_GMOD
|
||||
|
||||
namespace tier0 {
|
||||
lucore::Library* library = nullptr;
|
||||
|
||||
|
@ -17,11 +11,10 @@ namespace tier0 {
|
|||
Msg_t Msg {};
|
||||
|
||||
bool OpenLibrary() {
|
||||
#ifdef LCPU_SUPPORT_OLD_GMOD
|
||||
constexpr static std::string_view tier0_libraries[] {
|
||||
#ifdef __linux__
|
||||
#ifdef __linux__
|
||||
"tier0_srv",
|
||||
#endif
|
||||
#endif
|
||||
"tier0"
|
||||
};
|
||||
|
||||
|
@ -32,12 +25,6 @@ namespace tier0 {
|
|||
break;
|
||||
}
|
||||
}
|
||||
#else
|
||||
// The x86-64 branch of GMod, including the 32-bit binaries in the branch,
|
||||
// have a single tier0 library, which makes the codepath much simpler.
|
||||
// Hopefully I can switch to this path at some point.
|
||||
tier0::library = lucore::Library::OpenExisting("tier0");
|
||||
#endif
|
||||
|
||||
if(tier0::library == nullptr)
|
||||
return false;
|
||||
|
@ -65,14 +52,14 @@ namespace lcpu {
|
|||
|
||||
SourceSink::SourceSink() {
|
||||
if(!tier0::OpenLibrary()) {
|
||||
std::printf("Tier0 could not be opened\n");
|
||||
std::printf("Tier0 could not be opened - aborting\n");
|
||||
std::quick_exit(10);
|
||||
}
|
||||
|
||||
// TODO: A bit nicer of an error message?
|
||||
// Explain *what* to do if you see this message.
|
||||
if(!tier0::GrabSymbols()) {
|
||||
std::printf("Tier0 symbols could not be grabbed\n");
|
||||
std::printf("Tier0 symbols could not be grabbed - aborting\n");
|
||||
std::quick_exit(10);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace lucore::detail {
|
|||
}
|
||||
|
||||
void* OsLibrarySymbol(OsLibraryHandle dll, const char* symbolName) {
|
||||
return GetProcAddressA(reinterpret_cast<HMODULE*>(dll), symbolName);
|
||||
return GetProcAddressA(reinterpret_cast<HMODULE>(dll), symbolName);
|
||||
}
|
||||
|
||||
void OsFreeLibrary(OsLibraryHandle handle) {
|
||||
|
|
|
@ -12,4 +12,4 @@ Depends on lucore.
|
|||
|
||||
# Usage
|
||||
|
||||
TBD (if this is moved to another repo). See the riscv_test_harness project.
|
||||
TBD (if this is moved to another repo). See the lcpu project
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
add_executable(rvtest
|
||||
main.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(rvtest PUBLIC
|
||||
riscv::riscv
|
||||
)
|
|
@ -1,69 +0,0 @@
|
|||
//! A test harness for testing if the riscv library actually works.
|
||||
#include <cstdio> // I know, I know, but this is a test program. Yell later :)
|
||||
#include <lucore/Assert.hpp>
|
||||
#include <lucore/Logger.hpp>
|
||||
#include <lucore/StdoutSink.hpp>
|
||||
#include <riscv/System.hpp>
|
||||
|
||||
#include <thread>
|
||||
|
||||
/// 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 main(int argc, char** argv) {
|
||||
lucore::LoggerAttachStdout();
|
||||
|
||||
LUCORE_CHECK(argc == 2, "this test harness expects one argument (the file to load into riscv memory and execute). got {} arguments", argc);
|
||||
|
||||
// 128 KB of ram. Won't be enough to boot linux but should be good enough to test most baremetal apps
|
||||
auto system = riscv::System::Create(128 * 1024);
|
||||
LUCORE_CHECK(system, "could not create system for some reason.");
|
||||
|
||||
// Attach our UART device
|
||||
system->bus->AttachDevice(new SimpleUartDevice);
|
||||
|
||||
auto fp = std::fopen(argv[1], "rb");
|
||||
LUCORE_CHECK(fp, "could not open file \"{}\"", argv[1]);
|
||||
|
||||
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);
|
||||
|
||||
// This allows the host program running under the test
|
||||
// harness to tell us to shut down.
|
||||
bool shouldExit = false;
|
||||
system->OnPowerOff = [&shouldExit]() { shouldExit = true; };
|
||||
|
||||
while(!shouldExit) {
|
||||
system->Step();
|
||||
//std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
|
||||
delete system;
|
||||
return 0;
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
PROJECT = test
|
||||
|
||||
# where your rv32 toolchain is
|
||||
TCPATH = /home/lily/bin/riscv/bin
|
||||
PREFIX = $(TCPATH)/riscv32-unknown-elf
|
||||
|
||||
CC = $(PREFIX)-gcc
|
||||
CXX = $(PREFIX)-g++
|
||||
|
||||
ARCHFLAGS = -ffreestanding -fno-stack-protector -fdata-sections -ffunction-sections -march=rv32ima -mabi=ilp32
|
||||
CCFLAGS = -g -Os $(ARCHFLAGS) -std=c18
|
||||
CXXFLAGS = $(ARCHFLAGS) -g -Os -std=c++20 -fno-exceptions -fno-rtti
|
||||
LDFLAGS = -T binary.ld -nostdlib -Wl,--gc-sections
|
||||
|
||||
OBJS = start.o \
|
||||
main.o
|
||||
|
||||
.PHONY: all test clean
|
||||
|
||||
all: $(PROJECT).bin $(PROJECT).debug.txt
|
||||
|
||||
# this assumes the lcpu project build dir you're using is
|
||||
# [lcpu repo root]/build
|
||||
test: $(PROJECT).bin $(PROJECT).debug.txt
|
||||
../../../../build/projects/riscv_test_harness/rvtest $<
|
||||
|
||||
clean:
|
||||
rm $(PROJECT).elf $(PROJECT).bin $(PROJECT).debug.txt $(OBJS)
|
||||
|
||||
# Link rules
|
||||
|
||||
$(PROJECT).elf: $(OBJS)
|
||||
$(CC) $(CCFLAGS) $(LDFLAGS) -o $@ $(OBJS)
|
||||
|
||||
$(PROJECT).bin : $(PROJECT).elf
|
||||
$(PREFIX)-objcopy $^ -O binary $@
|
||||
|
||||
$(PROJECT).debug.txt : $(PROJECT).elf
|
||||
$(PREFIX)-objdump -t $^ > $@
|
||||
$(PREFIX)-objdump -S $^ >> $@
|
||||
|
||||
# Compile rules
|
||||
|
||||
%.o: %.cpp
|
||||
$(CXX) -c $(CXXFLAGS) $< -o $@
|
||||
|
||||
%.o: %.c
|
||||
$(CC) -c $(CCFLAGS) $< -o $@
|
||||
|
||||
%.o: %.S
|
||||
$(CC) -x assembler-with-cpp -march=rv32ima -mabi=ilp32 -c $< -o $@
|
||||
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
# what
|
||||
|
||||
This is a simple bare-metal riscv program that is able to run on the test harness that does some testing.
|
||||
|
||||
It is barebones, I know.
|
|
@ -1,73 +0,0 @@
|
|||
__heap_size = 0x1000;
|
||||
__stack_size = 0x1000;
|
||||
|
||||
ENTRY(_start)
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x80000000;
|
||||
.text : ALIGN(16) {
|
||||
__TEXT_BEGIN__ = .;
|
||||
*(.initial_jump)
|
||||
*(.entry.text)
|
||||
*(.init.literal)
|
||||
*(.init)
|
||||
*(.text)
|
||||
*(.literal .text .literal.* .text.* .stub)
|
||||
*(.out_jump.literal.*)
|
||||
*(.out_jump.*)
|
||||
__TEXT_END__ = .;
|
||||
}
|
||||
|
||||
/* If we're on a newer compiler */
|
||||
/DISCARD/ :
|
||||
{
|
||||
*(.interp)
|
||||
*(.dynsym)
|
||||
*(.dynstr)
|
||||
*(.header)
|
||||
} : phdr
|
||||
|
||||
.data : ALIGN(16) {
|
||||
__DATA_BEGIN__ = .;
|
||||
*(.rodata)
|
||||
*(.rodata.*)
|
||||
*(.gnu.linkonce.r.*)
|
||||
*(.rodata1)
|
||||
*(.dynsbss)
|
||||
*(.gnu.linkonce.sb.*)
|
||||
*(.scommon)
|
||||
*(.gnu.linkonce.sb2.*)
|
||||
*(.sbss)
|
||||
*(.sbss.*)
|
||||
*(.sbss2)
|
||||
*(.sbss2.*)
|
||||
*(.dynbss)
|
||||
*(.data)
|
||||
*(.data.*)
|
||||
*(.got)
|
||||
*(.got.*)
|
||||
__DATA_END__ = .;
|
||||
}
|
||||
|
||||
.bss : ALIGN( 16 ) {
|
||||
__BSS_BEGIN__ = .;
|
||||
*(.bss) /* Tricky: BSS needs to be allocated but not sent. GCC Will not populate these for calculating data size */
|
||||
*(.bss.*)
|
||||
__BSS_END__ = .;
|
||||
}
|
||||
|
||||
.heap : ALIGN( 16 ) {
|
||||
_sheap = .;
|
||||
. = . + __heap_size;
|
||||
_eheap = .;
|
||||
}
|
||||
|
||||
.stack : ALIGN( 16 ) {
|
||||
_estack = .;
|
||||
. = . + __stack_size;
|
||||
_sstack = .;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
// a simple test program
|
||||
#include <stdint.h>
|
||||
|
||||
uint32_t strlen(const char* str) {
|
||||
if(!str)
|
||||
return 0;
|
||||
const char* c = str;
|
||||
while(*c++)
|
||||
;
|
||||
return c - str;
|
||||
}
|
||||
|
||||
#define SYSCON *(volatile uint32_t*)0x11100000
|
||||
|
||||
#define UART_BASE 0x10000000
|
||||
#define UART_DATA *(volatile uint32_t*)UART_BASE
|
||||
#define UART_STATUS UART_DATA
|
||||
|
||||
void putc(char c) {
|
||||
UART_DATA = (uint32_t)c;
|
||||
}
|
||||
|
||||
__attribute__((noinline)) void puts(const char* str) {
|
||||
const uint32_t length = strlen(str);
|
||||
for(uint32_t i = 0; i < length; ++i)
|
||||
putc(str[i]);
|
||||
}
|
||||
|
||||
static uint32_t value = 0;
|
||||
static uint16_t shortvalue = 0;
|
||||
static uint8_t bytevalue = 0;
|
||||
|
||||
#define COUNTER_TEST(var, max) \
|
||||
for(int i = 0; i < max; ++i) { \
|
||||
puts(#var " is (before modification): "); \
|
||||
putc("0123456789"[var]); \
|
||||
putc('\n'); \
|
||||
\
|
||||
var = i; \
|
||||
puts(#var " is (after modification): "); \
|
||||
putc("0123456789"[var]); \
|
||||
putc('\n'); \
|
||||
}
|
||||
|
||||
void main() {
|
||||
puts("hello world I guess\n");
|
||||
|
||||
#if 1
|
||||
COUNTER_TEST(value, 9);
|
||||
COUNTER_TEST(shortvalue, 9);
|
||||
COUNTER_TEST(bytevalue, 9);
|
||||
#endif
|
||||
|
||||
// Shut down the test harness once we're done testing.
|
||||
puts("Tests done, shutting down test harness...\n");
|
||||
//SYSCON = 0x5555;
|
||||
|
||||
// loop forever
|
||||
for(;;);
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
# Simple bare-metal RISCV startup code.
|
||||
|
||||
.section .initial_jump
|
||||
.global _start
|
||||
|
||||
.extern main
|
||||
|
||||
.align 4
|
||||
_start:
|
||||
la sp, _sstack # set up C stack
|
||||
addi sp,sp,-16 # ...
|
||||
sw ra,12(sp) # ...
|
||||
jal ra, main # jump to C code!
|
Loading…
Reference in New Issue