diff --git a/README.md b/README.md index 6bb7fb1..60e102c 100644 --- a/README.md +++ b/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. diff --git a/build_module.sh b/build_module.sh index 61effc0..7c346a2 100755 --- a/build_module.sh +++ b/build_module.sh @@ -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 diff --git a/native/CMakeLists.txt b/native/CMakeLists.txt index d09dcf9..a934e74 100644 --- a/native/CMakeLists.txt +++ b/native/CMakeLists.txt @@ -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) diff --git a/native/cmake/linux32-toolchain.cmake b/native/cmake/linux32-toolchain.cmake new file mode 100644 index 0000000..75c4d98 --- /dev/null +++ b/native/cmake/linux32-toolchain.cmake @@ -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() diff --git a/native/projects/lcpu/src/SourceSink.cpp b/native/projects/lcpu/src/SourceSink.cpp index b8af76e..50c53ed 100644 --- a/native/projects/lcpu/src/SourceSink.cpp +++ b/native/projects/lcpu/src/SourceSink.cpp @@ -1,15 +1,9 @@ #include "SourceSink.hpp" -#include #include #include #include -// 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); } } diff --git a/native/projects/lucore/src/OsLibrary.win32.cpp b/native/projects/lucore/src/OsLibrary.win32.cpp index 8818297..35d0c26 100644 --- a/native/projects/lucore/src/OsLibrary.win32.cpp +++ b/native/projects/lucore/src/OsLibrary.win32.cpp @@ -14,7 +14,7 @@ namespace lucore::detail { } void* OsLibrarySymbol(OsLibraryHandle dll, const char* symbolName) { - return GetProcAddressA(reinterpret_cast(dll), symbolName); + return GetProcAddressA(reinterpret_cast(dll), symbolName); } void OsFreeLibrary(OsLibraryHandle handle) { diff --git a/native/projects/riscv/README.md b/native/projects/riscv/README.md index 7ba875b..f04dfa3 100644 --- a/native/projects/riscv/README.md +++ b/native/projects/riscv/README.md @@ -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 diff --git a/native/projects/riscv_test_harness/CMakeLists.txt b/native/projects/riscv_test_harness/CMakeLists.txt deleted file mode 100644 index 205aacd..0000000 --- a/native/projects/riscv_test_harness/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -add_executable(rvtest - main.cpp -) - -target_link_libraries(rvtest PUBLIC - riscv::riscv -) diff --git a/native/projects/riscv_test_harness/main.cpp b/native/projects/riscv_test_harness/main.cpp deleted file mode 100644 index 056a222..0000000 --- a/native/projects/riscv_test_harness/main.cpp +++ /dev/null @@ -1,69 +0,0 @@ -//! A test harness for testing if the riscv library actually works. -#include // I know, I know, but this is a test program. Yell later :) -#include -#include -#include -#include - -#include - -/// 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; -} diff --git a/native/projects/riscv_test_harness/test/Makefile b/native/projects/riscv_test_harness/test/Makefile deleted file mode 100644 index 33e4f65..0000000 --- a/native/projects/riscv_test_harness/test/Makefile +++ /dev/null @@ -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 $@ - - diff --git a/native/projects/riscv_test_harness/test/README.md b/native/projects/riscv_test_harness/test/README.md deleted file mode 100644 index bd65657..0000000 --- a/native/projects/riscv_test_harness/test/README.md +++ /dev/null @@ -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. diff --git a/native/projects/riscv_test_harness/test/binary.ld b/native/projects/riscv_test_harness/test/binary.ld deleted file mode 100644 index 076520e..0000000 --- a/native/projects/riscv_test_harness/test/binary.ld +++ /dev/null @@ -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 = .; - } -} - - diff --git a/native/projects/riscv_test_harness/test/main.c b/native/projects/riscv_test_harness/test/main.c deleted file mode 100644 index fc305f7..0000000 --- a/native/projects/riscv_test_harness/test/main.c +++ /dev/null @@ -1,60 +0,0 @@ -// a simple test program -#include - -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(;;); -} diff --git a/native/projects/riscv_test_harness/test/start.S b/native/projects/riscv_test_harness/test/start.S deleted file mode 100644 index be687bc..0000000 --- a/native/projects/riscv_test_harness/test/start.S +++ /dev/null @@ -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!