more lua binding fun
This commit is contained in:
parent
c69dc8d753
commit
9c5415c061
|
@ -11,6 +11,33 @@ function ENT:Initialize()
|
|||
self:SetSolid(SOLID_VPHYSICS)
|
||||
-- 64 kb of ram for now.
|
||||
self.cpu = LCPUNative.CreateCPU(128 * 1024)
|
||||
|
||||
-- test device framework
|
||||
-- this is how I ideally want to do things,
|
||||
-- dunno if it's possible
|
||||
self.test_device = LCPUNative.CreateDevice()
|
||||
print(self.test_device)
|
||||
self.test_device:SetBase(0x12000000)
|
||||
self.test_device:SetSize(0x10)
|
||||
function self.test_device:Clock()
|
||||
print("TestDevice Clock()")
|
||||
end
|
||||
|
||||
function self.test_device:Peek(address)
|
||||
if address == 0x12000000 then
|
||||
-- it a test!
|
||||
return CurTime()
|
||||
end
|
||||
return 0x10000
|
||||
end
|
||||
|
||||
function self.test_device:Poke(address, value)
|
||||
if address == 0x12000000 then
|
||||
print("What you doing?")
|
||||
end
|
||||
end
|
||||
|
||||
self.cpu:AttachDevice(self.test_device)
|
||||
end
|
||||
|
||||
function ENT:Think()
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
-- upload library
|
||||
-- (TODO)
|
|
@ -4,8 +4,7 @@ add_library(lcpu_native SHARED
|
|||
src/main.cpp
|
||||
src/LcpuGlobals.cpp
|
||||
src/LuaCpu.cpp
|
||||
#src/LuaDevice.cpp
|
||||
|
||||
src/LuaDevice.cpp
|
||||
src/SourceSink.cpp
|
||||
)
|
||||
|
||||
|
|
|
@ -1,35 +1,30 @@
|
|||
#include "LcpuGlobals.hpp"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "LuaCpu.hpp"
|
||||
#include "LuaDevice.hpp"
|
||||
|
||||
/// This should be bumped on any incompatible change to the native bindings
|
||||
/// that would break older Lua code, or requires newer Lua code to run.
|
||||
#define LCPU_MODULE_VERSION 1
|
||||
|
||||
|
||||
LUA_FUNCTION(LCPUNative_CreateCPU) {
|
||||
LUA->CheckType(1, GarrysMod::Lua::Type::Number);
|
||||
auto memorySize = (u32)std::round(LUA->GetNumber(1));
|
||||
auto memorySize = static_cast<u32>(LUA->GetNumber(1));
|
||||
|
||||
// TODO: There's probably a way to like, ensure a per-player max.
|
||||
if(memorySize > (64 * 1024 * 1024)) {
|
||||
if(memorySize > (64 * 1024 * 1024))
|
||||
LUA->ThrowError("Over RAM size limit.");
|
||||
}
|
||||
|
||||
LuaCpu::Create(LUA, memorySize);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LUA_FUNCTION(LCPUNative_CreateDevice) {
|
||||
//LuaDevice::Create(LUA);
|
||||
return 0;//1;
|
||||
LuaDevice::Create(LUA);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void GlobalsBind(GarrysMod::Lua::ILuaBase* LUA) {
|
||||
LuaCpu::Bind(LUA);
|
||||
//LuaDevice::Bind(LUA);
|
||||
// LuaDevice::Bind(LUA);
|
||||
|
||||
// clang-format off
|
||||
LUA->PushSpecial(GarrysMod::Lua::SPECIAL_GLOB);
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
#include "LuaHelpers.hpp"
|
||||
|
||||
/// This should be bumped on any incompatible change to the native bindings
|
||||
/// that would break older Lua code, or requires newer Lua code to run.
|
||||
#define LCPU_MODULE_VERSION 1
|
||||
|
||||
void GlobalsBind(GarrysMod::Lua::ILuaBase* LUA);
|
||||
|
|
|
@ -34,67 +34,56 @@ struct SimpleUartDevice : public riscv::Bus::MmioDevice {
|
|||
LUA_CLASS_BIND_VARIABLES_IMPLEMENT(LuaCpu);
|
||||
|
||||
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaCpu, PoweredOn) {
|
||||
LUA_CLASS_GET(LuaCpu, self, 1);
|
||||
auto self = LUA_CLASS_GET(LuaCpu)(1);
|
||||
LUA->PushBool(self->poweredOn);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaCpu, Cycle) {
|
||||
LUA_CLASS_GET(LuaCpu, self, 1);
|
||||
[&self]() {
|
||||
if(!self->poweredOn)
|
||||
return;
|
||||
self->system->Step();
|
||||
}();
|
||||
auto self = LUA_CLASS_GET(LuaCpu)(1);
|
||||
if(!self->poweredOn)
|
||||
return 0;
|
||||
self->system->Step();
|
||||
return 0;
|
||||
}
|
||||
|
||||
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaCpu, PowerOff) {
|
||||
LUA_CLASS_GET(LuaCpu, self, 1);
|
||||
[&self]() {
|
||||
if(!self->poweredOn)
|
||||
return;
|
||||
|
||||
self->poweredOn = false;
|
||||
self->system->bus->Reset();
|
||||
}();
|
||||
auto self = LUA_CLASS_GET(LuaCpu)(1);
|
||||
if(!self->poweredOn)
|
||||
return 0;
|
||||
|
||||
self->poweredOn = false;
|
||||
self->system->bus->Reset();
|
||||
return 0;
|
||||
}
|
||||
|
||||
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaCpu, PowerOn) {
|
||||
LUA_CLASS_GET(LuaCpu, self, 1);
|
||||
[&self]() {
|
||||
if(self->poweredOn)
|
||||
return;
|
||||
auto self = LUA_CLASS_GET(LuaCpu)(1);
|
||||
if(self->poweredOn)
|
||||
return 0;
|
||||
|
||||
self->poweredOn = true;
|
||||
self->system->bus->Reset();
|
||||
}();
|
||||
self->poweredOn = true;
|
||||
self->system->bus->Reset();
|
||||
return 0;
|
||||
}
|
||||
|
||||
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaCpu, Reset) {
|
||||
LUA_CLASS_GET(LuaCpu, self, 1);
|
||||
[&self]() { self->system->bus->Reset(); }();
|
||||
auto self = LUA_CLASS_GET(LuaCpu)(1);
|
||||
self->system->bus->Reset();
|
||||
return 0;
|
||||
}
|
||||
|
||||
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaCpu, AttachDevice) {
|
||||
LUA_CLASS_GET(LuaCpu, self, 1);
|
||||
bool result = false;
|
||||
#if 0
|
||||
[&]() {
|
||||
LUA_CLASS_GET(LuaDevice, device, 2);
|
||||
if(!device)
|
||||
return; // the bus is safe against this possibility, but
|
||||
// I'd rather be doubly-safe tbh
|
||||
auto self = LUA_CLASS_GET(LuaCpu)(1);
|
||||
auto device = LUA_CLASS_GET(LuaDevice)(1);
|
||||
|
||||
// Attach it
|
||||
result = self->system->bus->AttachDevice(static_cast<riscv::Bus::Device*>(device));
|
||||
}();
|
||||
#endif
|
||||
LUA->PushBool(result);
|
||||
// the bus is safe against this possibility, but
|
||||
// I'd rather be doubly-safe tbh
|
||||
if(!device)
|
||||
LUA->ThrowError("Null device pointer");
|
||||
|
||||
// Attach it
|
||||
LUA->PushBool(self->system->bus->AttachDevice(static_cast<riscv::Bus::Device*>(device)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -116,7 +105,7 @@ void LuaCpu::Create(GarrysMod::Lua::ILuaBase* LUA, u32 memorySize) {
|
|||
|
||||
// lame test code. this WILL be removed, I just want this for a quick test
|
||||
cpuWrapper->system->bus->AttachDevice(new SimpleUartDevice);
|
||||
auto fp = std::fopen("/home/lily/test.bin", "rb");
|
||||
auto fp = std::fopen("/home/lily/test-gmod.bin", "rb");
|
||||
if(fp) {
|
||||
std::fseek(fp, 0, SEEK_END);
|
||||
auto len = std::ftell(fp);
|
||||
|
@ -130,8 +119,6 @@ void LuaCpu::Create(GarrysMod::Lua::ILuaBase* LUA, u32 memorySize) {
|
|||
}
|
||||
|
||||
LuaCpu::LuaCpu(u32 memorySize) {
|
||||
lucore::LogInfo("in LuaCpu::LuaCpu(0x{:08x})\n", memorySize);
|
||||
|
||||
poweredOn = true;
|
||||
system = riscv::System::Create(memorySize);
|
||||
system->OnPowerOff = [&]() {
|
||||
|
|
|
@ -1,15 +1,107 @@
|
|||
#include "LuaDevice.hpp"
|
||||
|
||||
#include "LuaHelpers.hpp"
|
||||
|
||||
LUA_CLASS_BIND_VARIABLES_IMPLEMENT(LuaDevice);
|
||||
|
||||
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaDevice, SetBase) {
|
||||
auto self = LUA_CLASS_GET(LuaDevice)(1);
|
||||
if(self->bus)
|
||||
LUA->ThrowError("Do not call this on an attached device");
|
||||
self->base = static_cast<u32>(LUA->CheckNumber(2));
|
||||
return 0;
|
||||
}
|
||||
|
||||
LUA_MEMBER_FUNCTION_IMPLEMENT(LuaDevice, SetSize) {
|
||||
auto self = LUA_CLASS_GET(LuaDevice)(1);
|
||||
if(self->bus)
|
||||
LUA->ThrowError("Do not call this on an attached device");
|
||||
self->size = static_cast<u32>(LUA->CheckNumber(2));
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool LuaDevice::Clocked() const {
|
||||
// Even if Lua devices may not have a clock handler
|
||||
// installed there's no real non-awful way to check,
|
||||
// so just lie and say yes always.
|
||||
return true;
|
||||
}
|
||||
|
||||
void LuaDevice::Clock() {
|
||||
LuaState->PushUserType(this, __lua_typeid);
|
||||
LuaState->GetField(-2, "Clock");
|
||||
if(!LuaState->IsType(-1, GarrysMod::Lua::Type::Function)) {
|
||||
LuaState->Pop(2);
|
||||
return;
|
||||
}
|
||||
LuaState->Push(-2); // "self"
|
||||
LuaState->Call(1, 0);
|
||||
LuaState->Pop();
|
||||
}
|
||||
|
||||
riscv::Address LuaDevice::Base() const {
|
||||
return base;
|
||||
}
|
||||
|
||||
riscv::Address LuaDevice::Size() const {
|
||||
return base;
|
||||
}
|
||||
|
||||
u32 LuaDevice::Peek(riscv::Address address) {
|
||||
LuaState->PushUserType(this, __lua_typeid);
|
||||
LuaState->GetField(-2, "Peek");
|
||||
if(!LuaState->IsType(-1, GarrysMod::Lua::Type::Function)) {
|
||||
LuaState->Pop(2);
|
||||
return -1;
|
||||
}
|
||||
LuaState->Push(-2); // "self"
|
||||
LuaState->PushNumber(address);
|
||||
LuaState->Call(2, 1);
|
||||
auto result = LuaState->GetNumber(-1);
|
||||
LuaState->Pop();
|
||||
return static_cast<u32>(result);
|
||||
}
|
||||
|
||||
void LuaDevice::Poke(riscv::Address address, u32 value) {
|
||||
LuaState->PushUserType(this, __lua_typeid);
|
||||
LuaState->GetField(-2, "Poke");
|
||||
if(!LuaState->IsType(-1, GarrysMod::Lua::Type::Function)) {
|
||||
LuaState->Pop(2);
|
||||
return;
|
||||
}
|
||||
LuaState->Push(-2); // "self"
|
||||
LuaState->PushNumber(address);
|
||||
LuaState->PushNumber(value);
|
||||
LuaState->Call(3, 0);
|
||||
LuaState->Pop();
|
||||
}
|
||||
|
||||
void LuaDevice::Reset() {
|
||||
LuaState->PushUserType(this, __lua_typeid);
|
||||
LuaState->GetField(-2, "Reset");
|
||||
if(!LuaState->IsType(-1, GarrysMod::Lua::Type::Function))
|
||||
return;
|
||||
|
||||
LuaState->Push(-2); // "self"
|
||||
LuaState->Call(1, 0);
|
||||
LuaState->Pop();
|
||||
}
|
||||
|
||||
LuaDevice::~LuaDevice() = default;
|
||||
|
||||
void LuaDevice::Bind(GarrysMod::Lua::ILuaBase* LUA) {
|
||||
// clang-format off
|
||||
// TODO: I need to figure out how to like, set up metamethod stuff
|
||||
// so it all properly works.
|
||||
LUA_CLASS_BIND_BEGIN(LuaDevice);
|
||||
// todo handlers
|
||||
LUA_SET_C_FUNCTION(SetBase)
|
||||
LUA_SET_C_FUNCTION(SetSize)
|
||||
LUA_CLASS_BIND_END();
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
void LuaDevice::Create(GarrysMod::Lua::ILuaBase* LUA) {
|
||||
LUA->PushUserType(new LuaDevice(), __lua_typeid);
|
||||
auto device = new LuaDevice();
|
||||
device->LuaState = LUA;
|
||||
LUA->PushUserType(device, __lua_typeid);
|
||||
}
|
||||
|
|
|
@ -10,8 +10,14 @@ struct LuaDevice : public riscv::Bus::MmioDevice {
|
|||
static void Bind(GarrysMod::Lua::ILuaBase* LUA);
|
||||
static void Create(GarrysMod::Lua::ILuaBase* LUA);
|
||||
|
||||
riscv::Address Base() const override { return base; }
|
||||
riscv::Address Size() const override { return size; } // I think this is right?
|
||||
~LuaDevice();
|
||||
|
||||
bool Clocked() const override;
|
||||
void Clock() override;
|
||||
void Reset() override;
|
||||
|
||||
riscv::Address Base() const override;
|
||||
riscv::Address Size() const override;
|
||||
|
||||
u32 Peek(riscv::Address address) override;
|
||||
void Poke(riscv::Address address, u32 value) override;
|
||||
|
@ -24,14 +30,10 @@ struct LuaDevice : public riscv::Bus::MmioDevice {
|
|||
LUA_MEMBER_FUNCTION(SetBase);
|
||||
LUA_MEMBER_FUNCTION(SetSize);
|
||||
|
||||
LUA_MEMBER_FUNCTION(SetClockHandler);
|
||||
LUA_MEMBER_FUNCTION(SetResetHandler);
|
||||
LUA_MEMBER_FUNCTION(SetPeekHandler);
|
||||
LUA_MEMBER_FUNCTION(SetPokeHandler);
|
||||
// GetBase/GetSize?
|
||||
|
||||
riscv::Address base {};
|
||||
riscv::Address size {};
|
||||
|
||||
// ...?
|
||||
GarrysMod::Lua::ILuaBase* LuaState;
|
||||
};
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <GarrysMod/Lua/Interface.h>
|
||||
|
||||
#include <lucore/Logger.hpp>
|
||||
|
||||
// These are like the official GMOD header LUA_FUNCTION but allow forward declaration
|
||||
|
@ -17,20 +18,25 @@
|
|||
|
||||
#define LUA_MEMBER_FUNCTION_IMPLEMENT(CLASS, FUNC) int CLASS::FUNC##__ImpStatic(GarrysMod::Lua::ILuaBase* LUA)
|
||||
|
||||
// will make a "self" variable with the class
|
||||
#define LUA_CLASS_GET(T, name, stackPos) \
|
||||
LUA->CheckType(stackPos, T::__lua_typeid); \
|
||||
auto name = LUA->GetUserType<T>(stackPos, T::__lua_typeid); \
|
||||
if(!name) { \
|
||||
LUA->ThrowError("nullptr " #T " passed to function which requires a valid instance"); \
|
||||
// this synthesizes a lambda which takes the stack argument to get. this can actually also be
|
||||
// stored as a variable for later usage (... if you so desire?)
|
||||
#define LUA_CLASS_GET(T) \
|
||||
[LUA](int stackPos) { \
|
||||
LUA->CheckType(stackPos, T::__lua_typeid); \
|
||||
return LUA->GetUserType<T>(stackPos, T::__lua_typeid); \
|
||||
}
|
||||
|
||||
// This class binding package always implements the __gc metamethod
|
||||
// to free any C++ object bound to Lua.
|
||||
|
||||
// Declare required binding variables.
|
||||
#define LUA_CLASS_BIND_VARIABLES(ACCESS_LEVEL) \
|
||||
public: \
|
||||
static int __lua_typeid; \
|
||||
ACCESS_LEVEL: \
|
||||
LUA_MEMBER_FUNCTION(__gc);
|
||||
|
||||
// Implement required binding variables (typically in a .cpp file).
|
||||
#define LUA_CLASS_BIND_VARIABLES_IMPLEMENT(T) \
|
||||
int T::__lua_typeid = 0; \
|
||||
LUA_MEMBER_FUNCTION_IMPLEMENT(T, __gc) { \
|
||||
|
@ -43,6 +49,8 @@
|
|||
return 0; \
|
||||
}
|
||||
|
||||
// Begin the Bind() method of a class. This just sets up boilerplate
|
||||
// and required things to setup a class.
|
||||
#define LUA_CLASS_BIND_BEGIN(T) \
|
||||
T::__lua_typeid = LUA->CreateMetaTable(#T); \
|
||||
LUA->PushSpecial(GarrysMod::Lua::SPECIAL_REG); \
|
||||
|
@ -53,13 +61,15 @@
|
|||
LUA->SetField(-2, "__index"); \
|
||||
LUA_SET_C_FUNCTION(__gc)
|
||||
|
||||
// End the Bind() method.
|
||||
#define LUA_CLASS_BIND_END() LUA->Pop();
|
||||
|
||||
// Helpers for lua functions
|
||||
// Set a C function as a field.
|
||||
#define LUA_SET_C_FUNCTION(name) \
|
||||
LUA->PushCFunction(name); \
|
||||
LUA->SetField(-2, #name);
|
||||
|
||||
// Set a C function as a field with an alternative field name.
|
||||
#define LUA_SET_C_FUNCTION_NAME(name, altName) \
|
||||
LUA->PushCFunction(name); \
|
||||
LUA->SetField(-2, altName);
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
GMOD_MODULE_OPEN() {
|
||||
lucore::Logger::The().AttachSink(lcpu::SourceSink::The());
|
||||
lucore::LogInfo("LCPU Native Module!");
|
||||
lucore::LogInfo("LCPU Native Module! (ModuleVersion {})", LCPU_MODULE_VERSION);
|
||||
GlobalsBind(LUA);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
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 $@
|
||||
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
__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 = .;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
// a simple test program - this version would talk to a device
|
||||
// written in GLua
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
uint32_t strlen(const char* str) {
|
||||
if(!str)
|
||||
return 0;
|
||||
const char* c = str;
|
||||
while(*c++)
|
||||
;
|
||||
return c - str;
|
||||
}
|
||||
|
||||
#define GLUA_DEVICE *(volatile uint32_t*)0x12000000
|
||||
|
||||
#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]);
|
||||
}
|
||||
|
||||
int itoa(int value, char* sp, int radix) {
|
||||
char tmp[16];
|
||||
char* tp = tmp;
|
||||
int i;
|
||||
unsigned v;
|
||||
|
||||
int sign = (radix == 10 && value < 0);
|
||||
if(sign)
|
||||
v = -value;
|
||||
else
|
||||
v = (unsigned)value;
|
||||
|
||||
while(v || tp == tmp) {
|
||||
i = v % radix;
|
||||
v /= radix;
|
||||
if(i < 10)
|
||||
*tp++ = i + '0';
|
||||
else
|
||||
*tp++ = i + 'a' - 10;
|
||||
}
|
||||
|
||||
int len = tp - tmp;
|
||||
|
||||
if(sign) {
|
||||
*sp++ = '-';
|
||||
len++;
|
||||
}
|
||||
|
||||
while(tp > tmp)
|
||||
*sp++ = *--tp;
|
||||
|
||||
*sp = '\0';
|
||||
return len;
|
||||
}
|
||||
|
||||
void vprintf(const char* format, va_list val) {
|
||||
const int fl = strlen(format);
|
||||
for(int i = 0; i < fl; ++i) {
|
||||
switch(format[i]) {
|
||||
case '%':
|
||||
if(format[i+1] == '%')
|
||||
putc('%');
|
||||
switch(format[i+1]) {
|
||||
case 'i':
|
||||
case 'd': {
|
||||
char a[32];
|
||||
itoa(va_arg(val, uint32_t), &a[0], 10);
|
||||
|
||||
const int al = strlen(a);
|
||||
for(int j = 0; j < al; ++j)
|
||||
putc(a[j]);
|
||||
} break;
|
||||
|
||||
case 's': {
|
||||
char* p = va_arg(val, char*);
|
||||
if(!p)
|
||||
puts("(null)");
|
||||
else
|
||||
puts(p);
|
||||
};
|
||||
|
||||
default:
|
||||
putc(' ');
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
putc(format[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void printf(const char* format, ...) {
|
||||
va_list val;
|
||||
va_start(val, format);
|
||||
vprintf(format, val);
|
||||
va_end(val);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void main() {
|
||||
puts("fuck you garry I win");
|
||||
|
||||
for(int i = 0; i < 256; ++i)
|
||||
printf("uhh %d\n", GLUA_DEVICE);
|
||||
|
||||
SYSCON = 0x5555;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
# 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