diff --git a/Makefile b/Makefile index 1f41277f..019432e4 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,7 @@ LUAS=submodules/lua/src BOOTLOADER=submodules/dfu-stm32f7 BUILD_DIR := build PRJ_DIR=crow +LUAC_CROSS=util/luacc CC=arm-none-eabi-gcc LD=arm-none-eabi-gcc @@ -53,7 +54,8 @@ CFLAGS += $(MCFLAGS) CFLAGS += $(OPTIMIZE) CFLAGS += $(DEFS) -I. -I./ $(STM32_INCLUDES) CFLAGS += -fsingle-precision-constant -Wdouble-promotion -CFLAGS += -DLUA_32BITS -DLUA_COMPAT_5_2 +CFLAGS += -DLUA_32BITS +# CFLAGS += -DLUA_32BITS -DLUA_COMPAT_5_2 CFLAGS += -fno-common CFLAGS += -DVERSION=\"$(GIT_VERSION)\" CFLAGS += -ffunction-sections -fdata-sections # provides majority of LTO binary size reduction @@ -141,19 +143,19 @@ $(II_TARGET): util/ii_lua_module.lua $(BUILD_DIR)/ii_%.lua: $(II_SRCD)/%.lua util/ii_lua_module.lua | $(BUILD_DIR) @lua util/ii_lua_module.lua $< $@ - @echo lua $@ + @echo "ii-lua-module $< -> $@" $(BUILD_DIR)/iihelp.lua: $(II_SRC) util/ii_lua_help.lua | $(BUILD_DIR) @lua util/ii_lua_help.lua $(II_SRCD) $@ - @echo lua $@ + @echo "ii-lua-help $@" $(BUILD_DIR)/ii_c_layer.h: $(II_SRC) util/ii_c_layer.lua | $(BUILD_DIR) @lua util/ii_c_layer.lua $(II_SRCD) $@ - @echo lua $@ + @echo "ii-c-layer $@" $(BUILD_DIR)/ii_lualink.h: $(II_SRC) util/ii_lualinker.lua | $(BUILD_DIR) @lua util/ii_lualinker.lua $(II_SRCD) $@ - @echo lua $@ + @echo "ii-lualinker $@" ### destination sources @@ -180,6 +182,7 @@ LUA_SRC += $(II_TARGET) LUA_PP = $(LUA_SRC:%.lua=%.lua.h) LUA_PP: $(LUA_SRC) + @echo "pre-compiling lua sources to bytecode wrapped in c headers" LUACORE_OBJS= lapi.o lcode.o lctype.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o \ lmem.o lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o \ @@ -194,6 +197,8 @@ OBJS = $(SRC:%.c=$(OBJDIR)/%.o) OBJS += $(addprefix $(LUAS)/,$(LUACORE_OBJS) $(LUALIB_OBJS) ) OBJS += Startup.o +$(OBJS): $(LUA_PP) + # specific objects that require built dependencies (ii) $(OBJDIR)/lib/l_bootstrap.o: $(LUA_PP) $(BUILD_DIR)/ii_lualink.h # $(OBJDIR)/lib/lualink.o: $(LUA_PP) $(BUILD_DIR)/ii_lualink.h @@ -301,10 +306,19 @@ zip: $(BIN) $(TARGET).dfu @echo f2l $< "->" $@ @$(FENNEL) --compile $< > $@ -%.lua.h: %.lua util/l2h.lua - @luac -p $< - @echo l2h $< "->" $@ - @lua util/l2h.lua $< +# a bunch of gnarly make-functions to massage the intermediate stage filenames +# everything goes into /build now, and we have to save output of LUAC_CROSS into +# named files for (xxd -i) to build include files with valid names +# could be avoided by a more complicated pass in (sed), but this was easier +# 1. cross-compile all .lua files into .lc bytecode for stm32-arm-cortex-m7 format +# 2. wrap the .lc binary files into .h headers with auto-generated names +# 3. add const qualifiers to headers to satisfy C99 struct initializer requirement + +%.lua.h: %.lua $(BUILD_DIR) + @echo l2h $< "->" $(addprefix $(BUILD_DIR)/, $(notdir $(subst .lua.h,.h,$@))) + @$(LUAC_CROSS) -s -o $(addprefix $(BUILD_DIR)/, $(notdir $(subst .lua,.lc,$<))) $< + @xxd -i $(addprefix $(BUILD_DIR)/, $(notdir $(subst .lua,.lc,$<))) $(addprefix $(BUILD_DIR)/, $(notdir $(subst .lua.h,.h,$@))) + @sed -i 's/unsigned int/const unsigned int/g' $(addprefix $(BUILD_DIR)/, $(notdir $(subst .lua.h,.h,$@))) Startup.o: $(STARTUP) @$(CC) $(CFLAGS) -c $< -o $@ diff --git a/lib/l_bootstrap.c b/lib/l_bootstrap.c index 9b6168a0..9dee345f 100644 --- a/lib/l_bootstrap.c +++ b/lib/l_bootstrap.c @@ -4,46 +4,46 @@ #include "l_crowlib.h" -#include "lua/crowlib.lua.h" -#include "lua/asl.lua.h" -#include "lua/asllib.lua.h" -#include "lua/clock.lua.h" -#include "lua/metro.lua.h" -#include "lua/public.lua.h" -#include "lua/input.lua.h" -#include "lua/output.lua.h" -#include "lua/ii.lua.h" -#include "build/iihelp.lua.h" // generated lua stub for loading i2c modules -#include "lua/calibrate.lua.h" -#include "lua/sequins.lua.h" -#include "lua/quote.lua.h" -#include "lua/timeline.lua.h" -#include "lua/hotswap.lua.h" +// Lua libs wrapped in C-headers +#include "build/crowlib.h" +#include "build/asl.h" +#include "build/asllib.h" +#include "build/clock.h" +#include "build/metro.h" +#include "build/public.h" +#include "build/input.h" +#include "build/output.h" +#include "build/ii.h" +#include "build/iihelp.h" // generated lua stub for loading i2c modules +#include "build/calibrate.h" +#include "build/sequins.h" +#include "build/quote.h" +#include "build/timeline.h" +#include "build/hotswap.h" #include "build/ii_lualink.h" // generated C header for linking to lua -static int _writer(lua_State *L, const void *p, size_t sz, void *ud); -static int _load_chunk(lua_State* L, const char* code, int strip); static int _open_lib( lua_State *L, const struct lua_lib_locator* lib, const char* name ); +static void lua_full_gc(lua_State* L); // mark the 3rd arg 'false' if you need to debug that library const struct lua_lib_locator Lua_libs[] = - { { "lua_crowlib" , lua_crowlib , true} - , { "lua_asl" , lua_asl , true} - , { "lua_asllib" , lua_asllib , true} - , { "lua_clock" , lua_clock , true} - , { "lua_metro" , lua_metro , true} - , { "lua_input" , lua_input , true} - , { "lua_output" , lua_output , true} - , { "lua_public" , lua_public , true} - , { "lua_ii" , lua_ii , true} - , { "build_iihelp" , build_iihelp , true} - , { "lua_calibrate" , lua_calibrate , true} - , { "lua_sequins" , lua_sequins , true} - , { "lua_quote" , lua_quote , true} - , { "lua_timeline" , lua_timeline , true} - , { "lua_hotswap" , lua_hotswap , true} - , { NULL , NULL , true} + { { "lua_crowlib" , build_crowlib_lc , true, build_crowlib_lc_len} + , { "lua_asl" , build_asl_lc , true, build_asl_lc_len} + , { "lua_asllib" , build_asllib_lc , true, build_asllib_lc_len} + , { "lua_clock" , build_clock_lc , true, build_clock_lc_len} + , { "lua_metro" , build_metro_lc , true, build_metro_lc_len} + , { "lua_input" , build_input_lc , true, build_input_lc_len} + , { "lua_output" , build_output_lc , true, build_output_lc_len} + , { "lua_public" , build_public_lc , true, build_public_lc_len} + , { "lua_ii" , build_ii_lc , true, build_ii_lc_len} + , { "build_iihelp" , build_iihelp_lc , true, build_iihelp_lc_len} + , { "lua_calibrate" , build_calibrate_lc , true, build_calibrate_lc_len} + , { "lua_sequins" , build_sequins_lc , true, build_sequins_lc_len} + , { "lua_quote" , build_quote_lc , true, build_quote_lc_len} + , { "lua_timeline" , build_timeline_lc , true, build_timeline_lc_len} + , { "lua_hotswap" , build_hotswap_lc , true, build_hotswap_lc_len} + , { NULL , NULL , true, 0} }; @@ -69,9 +69,18 @@ void l_bootstrap_init(lua_State* L){ // crowlib C extensions l_crowlib_init(L); + // track all user-created globals + luaL_dostring(L, + "_user={}\n" + "local function trace(t,k,v)\n" + "_user[k]=true\n" + "rawset(t,k,v)\n" + "end\n" + "setmetatable(_G,{ __newindex = trace })\n" + ); + // perform two full garbage collection cycles for full cleanup - lua_gc(L, LUA_GCCOLLECT, 1); - lua_gc(L, LUA_GCCOLLECT, 1); + lua_full_gc(L); } @@ -92,19 +101,22 @@ int l_bootstrap_dofile(lua_State* L) default:{ cname[p++] = l_name[i]; } break; } } + // goto fail; // no match was found, so error out (silently?) + strcomplete: lua_pop( L, 1 ); switch( _open_lib( L, Lua_libs, cname ) ){ case -1: goto fail; - case 1: return 1; + case 1: lua_full_gc(L); return 1; default: break; } switch( _open_lib( L, Lua_ii_libs, cname ) ){ case -1: goto fail; - case 1: return 1; + case 1: lua_full_gc(L); return 1; default: break; } printf("can't open library: %s\n", (char*)cname); + fail: lua_pushnil(L); return 1; @@ -117,58 +129,14 @@ int l_bootstrap_dofile(lua_State* L) /////////// private defns -// this is a somewhat arbitrary size. must be big enough for the biggest library. 8kB was too small -#define SIZED_STRING_LEN 0x4000 // (16kB) -struct sized_string{ - char data[SIZED_STRING_LEN]; - int len; -}; - -#define UNUSED(x) (void)(sizeof(x)) -static int _writer(lua_State *L, const void *p, size_t sz, void *ud) -{ - UNUSED(L); - struct sized_string* chunkstr = ud; /// need explicit cast? - if(chunkstr->len + sz >= SIZED_STRING_LEN){ - printf("chunkstr too small.\n"); - return 1; - } - memcpy(&chunkstr->data[chunkstr->len], p, sz); - chunkstr->len += sz; - return 0; -} -#undef UNUSED - -static int _load_chunk(lua_State* L, const char* code, int strip) -{ - int retval = 0; - struct sized_string chunkstr = {.len = 0}; - { // scope lua_State to destroy it asap - lua_State* LL=luaL_newstate(); - if( !LL ){ printf("luaL_newstate failed\n"); return 1; } - if( luaL_loadstring(LL, code) ){ - printf("loadstring error\n"); - retval = 1; - goto close_LL; - } - if( lua_dump(LL, _writer, &chunkstr, strip) ){ - printf("dump error\n"); - retval = 1; - goto close_LL; - } -close_LL: - lua_close(LL); - } - luaL_loadbuffer(L, chunkstr.data, chunkstr.len, chunkstr.data); // load our compiled chunk - return retval; -} - static int _open_lib( lua_State *L, const struct lua_lib_locator* lib, const char* name ) { uint8_t i = 0; while( lib[i].addr_of_luacode != NULL ){ if( !strcmp( name, lib[i].name ) ){ // if the strings match - if( _load_chunk(L, lib[i].addr_of_luacode, lib[i].stripped) ){ + if( luaL_loadbuffer(L, (const char*)lib[i].addr_of_luacode + , lib[i].len + , lib[i].name) ){ printf("can't load library: %s\n", (char*)lib[i].name ); printf( "%s\n", (char*)lua_tostring( L, -1 ) ); lua_pop( L, 1 ); @@ -186,3 +154,8 @@ static int _open_lib( lua_State *L, const struct lua_lib_locator* lib, const cha } return 0; // not found } + +static void lua_full_gc(lua_State* L){ + lua_gc(L, LUA_GCCOLLECT, 1); + lua_gc(L, LUA_GCCOLLECT, 1); +} diff --git a/lib/lualink.c b/lib/lualink.c index f0a6aebe..1909fadc 100644 --- a/lib/lualink.c +++ b/lib/lualink.c @@ -39,8 +39,9 @@ #define WATCHDOG_FREQ 0x100000 // ~1s how often we run the watchdog #define WATCHDOG_COUNT 2 // how many watchdogs before 'frozen' + // Basic crow script -#include "lua/First.lua.h" +#include "build/First.h" // Private prototypes static void Lua_linkctolua( lua_State* L ); @@ -80,10 +81,17 @@ lua_State* Lua_Init(void) luaL_openlibs(L); Lua_linkctolua(L); l_bootstrap_init(L); // redefine dofile(), print(), load crowlib - // Lua_eval(L, lua_bootstrap - // , strlen(lua_bootstrap) - // , "=lib" - // ); + return L; +} + +lua_State* Lua_ReInit_Environment(lua_State* L){ + // clear user-created globals + luaL_dostring(L, "for k,_ in pairs(_user) do\n" + "_G[k] = nil\n" + "end\n" + "_G._user = {}\n"); + lua_gc(L, LUA_GCCOLLECT, 1); + lua_gc(L, LUA_GCCOLLECT, 1); return L; } @@ -99,14 +107,15 @@ lua_State* Lua_Reset( void ) } events_clear(); clock_cancel_coro_all(); - Lua_DeInit(); - return Lua_Init(); + return Lua_ReInit_Environment(L); + // Lua_DeInit(); + // return Lua_Init(); } void Lua_load_default_script( void ) { - Lua_eval(L, lua_First - , strlen(lua_First) + Lua_eval(L, (const char*)build_First_lc + , build_First_lc_len , "=First.lua" ); } diff --git a/lib/lualink.h b/lib/lualink.h index 0b55fb23..3365ff62 100644 --- a/lib/lualink.h +++ b/lib/lualink.h @@ -9,8 +9,9 @@ typedef void (*ErrorHandler_t)(char* error_message); struct lua_lib_locator{ const char* name; - const char* addr_of_luacode; + const unsigned char* addr_of_luacode; const bool stripped; + const unsigned int len; }; extern volatile int CPU_count; // count from main.c diff --git a/lib/repl.c b/lib/repl.c index f085b942..948ce2a6 100644 --- a/lib/repl.c +++ b/lib/repl.c @@ -18,7 +18,7 @@ typedef enum{ REPL_normal // global variables lua_State* Lua; L_repl_mode repl_mode = REPL_normal; -char* new_script; +char new_script[USER_SCRIPT_SIZE]; // static alloc avoids malloc failures uint16_t new_script_len; static bool running_from_mem; static char running_script_name[64]; @@ -48,7 +48,6 @@ void REPL_init( lua_State* lua ) printf("failed to load user script\n"); Caw_send_luachunk("failed to load user script"); } - free(new_script); break; } case USERSCRIPT_Clear: @@ -97,7 +96,6 @@ void REPL_upload( int flash ) } else { Caw_send_luachunk("User script evaluation failed."); } - free(new_script); } repl_mode = REPL_normal; } @@ -194,7 +192,6 @@ static void REPL_receive_script( char* buf, uint32_t len, ErrorHandler_t errfn ) if( new_script_len + len >= USER_SCRIPT_SIZE ){ Caw_send_luachunk("!ERROR! Script is too long."); repl_mode = REPL_discard; - free(new_script); } else { memcpy( &new_script[new_script_len], buf, len ); new_script_len += len; @@ -203,12 +200,6 @@ static void REPL_receive_script( char* buf, uint32_t len, ErrorHandler_t errfn ) static bool REPL_new_script_buffer( uint32_t len ) { - new_script = calloc( sizeof(char), len); - if( new_script == NULL ){ - printf("malloc failed. REPL\n"); - Caw_send_luachunk("!ERROR! Out of memory."); - return false; - } new_script_len = 0; // reset counter as the buffer is empty return true; } diff --git a/lua/hotswap.lua b/lua/hotswap.lua index 953ff8af..ce36e20a 100644 --- a/lua/hotswap.lua +++ b/lua/hotswap.lua @@ -1,6 +1,7 @@ --- hotswap library --- globals are available on crow, otherwise require for norns +local require = require or function() end local s = sequins or require 'lib/sequins' local tl = timeline or require 'lib/timeline' diff --git a/lua/timeline.lua b/lua/timeline.lua index 4e36210a..9f451ffc 100644 --- a/lua/timeline.lua +++ b/lua/timeline.lua @@ -2,6 +2,7 @@ -- hotrod some clock & sequins structures for rapid playability --- globals are available on crow, otherwise require for norns +local require = require or function() end local s = sequins or require 'lib/sequins' local clk = clock or require 'clock' diff --git a/submodules/lua b/submodules/lua index 12ab0c3f..32ffee21 160000 --- a/submodules/lua +++ b/submodules/lua @@ -1 +1 @@ -Subproject commit 12ab0c3f491a9bf008cacc46e8904dfd6ece6a06 +Subproject commit 32ffee2104c3a19ad2122dbfc5d4a018c273afc7 diff --git a/util/ii_lualinker.lua b/util/ii_lualinker.lua index 1b0f5328..5b8b2247 100644 --- a/util/ii_lualinker.lua +++ b/util/ii_lualinker.lua @@ -1,15 +1,18 @@ get_offset = 0x80 function make_lualink(files) - local ll = '#pragma once\n\n' + local ll = '// THIS FILE IS GENERATED BY util/ii_lualinker.lua.\n\n' + .. '// DO NOT EDIT BY HAND!!!\n\n' + .. '#pragma once\n\n' .. '#include \"lib/lualink.h\" // struct lua_lib_locator\n\n' for _,f in ipairs(files) do - ll = ll .. '#include \"build/ii_' .. f.lua_name .. '.lua.h\"\n' + ll = ll .. '#include \"build/ii_' .. f.lua_name .. '.h\"\n' end ll = ll .. '\n' .. 'const struct lua_lib_locator Lua_ii_libs[] = {\n' for _,f in ipairs(files) do - ll = ll .. '\t{ \"build_ii_' .. f.lua_name .. '\", build_ii_' .. f.lua_name .. ', true },\n' + local name = 'build_ii_' .. f.lua_name + ll = ll .. '\t{ \"' .. name .. '\", ' .. name .. '_lc, true, ' .. name .. '_lc_len' .. '},\n' end ll = ll .. '\t{ NULL, NULL } };\n' return ll diff --git a/util/luacc b/util/luacc new file mode 100755 index 00000000..42e6793c Binary files /dev/null and b/util/luacc differ