From f2bc54235ada79e1536b75fbaad3502ac14e11a9 Mon Sep 17 00:00:00 2001 From: Graham Clemo Date: Thu, 2 Aug 2018 23:17:40 +0100 Subject: [PATCH] Updated Makefile to build with Squirrel under Linux Added better Squirrel compiler error handling. --- Makefile | 18 +- src/squirrelapi.c | 3118 +++++++++++++++++++++++---------------------- 2 files changed, 1580 insertions(+), 1556 deletions(-) diff --git a/Makefile b/Makefile index b0d01cc..3a1694c 100644 --- a/Makefile +++ b/Makefile @@ -21,6 +21,7 @@ INCLUDES= \ -I$(3RD_PARTY)/wren-0.1.0/src/include \ -I$(3RD_PARTY)/moonscript \ -I$(3RD_PARTY)/fennel \ + -I$(3RD_PARTY)/squirrel3.1/include \ -I$(BLIPBUF_LIB) \ -I$(DUKTAPE_LIB) \ -I$(SDL_NET_LIB) \ @@ -48,6 +49,7 @@ LINUX_LIBS= \ -L$(3RD_PARTY)/wren-0.1.0/lib \ -L$(3RD_PARTY)/sdl-gpu/build/linux \ -L$(3RD_PARTY)/lua-5.3.1/src \ + -L$(3RD_PARTY)/squirrel3.1/lib \ -L$(3RD_PARTY)/SDL2-2.0.7/build/.libs LINUX64_LIBS= \ @@ -66,6 +68,7 @@ LINUX_LINKER_LTO_FLAGS= \ -lsdlgpu \ -llua \ -lwren \ + -lsquirrel -lsqstdlib -lstdc++ \ -lgif \ -ldl \ -lm \ @@ -77,6 +80,7 @@ LINUX_LINKER_LTO_FLAGS= \ LINUX_LINKER_FLAGS= \ -llua \ -lwren \ + -lsquirrel -lsqstdlib -lstdc++ \ -ldl \ -lm \ -lpthread \ @@ -103,6 +107,7 @@ EMS_LINKER_FLAGS= \ -L$(PRE_BUILT)/emscripten \ -llua \ -lwren \ + -lsquirrel \ -lgif \ -lz \ -lsdlgpu @@ -309,14 +314,17 @@ bin/jsapi.o: src/jsapi.c $(TIC80_H) bin/luaapi.o: src/luaapi.c $(TIC80_H) $(CC) $< $(OPT) $(INCLUDES) -c -o $@ +bin/squirrelapi.o: src/squirrelapi.c $(TIC80_H) + $(CC) $< $(OPT) $(INCLUDES) -c -o $@ + bin/wrenapi.o: src/wrenapi.c $(TIC80_H) $(CC) $< $(OPT) $(INCLUDES) -c -o $@ bin/duktape.o: $(DUKTAPE_LIB)/duktape.c $(TIC80_H) $(CC) $< $(OPT) $(INCLUDES) -c -o $@ -TIC80_SRC = src/tic80.c src/tic.c $(BLIP_SRC) src/jsapi.c src/luaapi.c src/wrenapi.c $(DUKTAPE_LIB)/duktape.c -TIC80_O = bin/tic80.o bin/tic.o bin/tools.o bin/blip_buf.o bin/jsapi.o bin/luaapi.o bin/wrenapi.o bin/duktape.o bin/gif.o +TIC80_SRC = src/tic80.c src/tic.c $(BLIP_SRC) src/jsapi.c src/luaapi.c src/wrenapi.c src/squirrelapi.c $(DUKTAPE_LIB)/duktape.c +TIC80_O = bin/squirrelapi.o bin/tic80.o bin/tic.o bin/tools.o bin/blip_buf.o bin/jsapi.o bin/luaapi.o bin/wrenapi.o bin/duktape.o bin/gif.o TIC80_A = bin/libtic80.a TIC80_DLL = bin/tic80.dll @@ -370,6 +378,7 @@ WREN_A=$(3RD_PARTY)/wren-0.1.0/lib/libwren.a SDLGPU_A=$(3RD_PARTY)/sdl-gpu/build/linux/libsdlgpu.a LUA_A=$(3RD_PARTY)/lua-5.3.1/src/liblua.a SDL2_A=$(3RD_PARTY)/SDL2-2.0.7/build/.libs/libSDL2.a +SQUIRREL_A=$(3RD_PARTY)/squirrel3.1/lib/libsquirrel.a $(WREN_A): make static -C $(3RD_PARTY)/wren-0.1.0/ @@ -383,7 +392,10 @@ $(LUA_A): $(SDL2_A): cd $(3RD_PARTY)/SDL2-2.0.7/ && ./configure --enable-sndio=no && make && cd ../.. -linux: $(WREN_A) $(SDLGPU_A) $(LUA_A) $(SDL2_A) +$(SQUIRREL_A): + cd $(3RD_PARTY)/squirrel3.1/ && make && cd ../.. + +linux: $(WREN_A) $(SDLGPU_A) $(LUA_A) $(SDL2_A) $(SQUIRREL_A) $(CC) $(LINUX_INCLUDES) $(SOURCES) $(SYSTEM) $(LPEG_SRC) $(GIF_SRC) $(SOURCES_EXT) $(TIC80_SRC) $(OPT) $(INCLUDES) $(LINUX_LIBS) $(LINUX_LINKER_FLAGS) -o $(BIN_NAME) linux-pro: diff --git a/src/squirrelapi.c b/src/squirrelapi.c index c90fcd6..54af085 100644 --- a/src/squirrelapi.c +++ b/src/squirrelapi.c @@ -1,1558 +1,1570 @@ -// MIT License - -// Copyright (c) 2017 Vadim Grigoruk @nesbox // grigoruk@gmail.com - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "machine.h" - -#if defined(TIC_BUILD_WITH_SQUIRREL) - -//#define USE_FOREIGN_POINTER -//#define CHECK_FORCE_EXIT - -#include -#include -#include -#include -#include -#include -#include -#include - -static const char TicMachine[] = "_TIC80"; - - -// !TODO: get rid of this wrap -static s32 getSquirrelNumber(HSQUIRRELVM vm, s32 index) -{ - SQInteger i; - if (SQ_SUCCEEDED(sq_getinteger(vm, index, &i))) - return (s32)i; - - SQFloat f; - if (SQ_SUCCEEDED(sq_getfloat(vm, index, &f))) - return (s32)f; - - return 0; -} - -static void registerSquirrelFunction(tic_machine* machine, SQFUNCTION func, const char *name) -{ - sq_pushroottable(machine->squirrel); - sq_pushstring(machine->squirrel, name, -1); - sq_newclosure(machine->squirrel, func, 0); - sq_newslot(machine->squirrel, -3, SQTrue); - sq_poptop(machine->squirrel); // remove root table. -} - -static tic_machine* getSquirrelMachine(HSQUIRRELVM vm) -{ -#if USE_FOREIGN_POINTER - return (tic_machine*)sq_getforeignpointer(vm); -#else - sq_pushregistrytable(vm); - sq_pushstring(vm, TicMachine, -1); - if (SQ_FAILED(sq_get(vm, -2))) - { - fprintf(stderr, "FATAL ERROR: TicMachine not found!\n"); - abort(); - } - SQUserPointer ptr; - if (SQ_FAILED(sq_getuserpointer(vm, -1, &ptr))) - { - fprintf(stderr, "FATAL ERROR: Cannot get user pointer for TicMachine!\n"); - abort(); - } - tic_machine* machine = (tic_machine*)ptr; - sq_pop(vm, 2); // user pointer and registry table. - return machine; -#endif -} - -static SQInteger squirrel_errorHandler(HSQUIRRELVM vm) -{ - tic_machine* machine = getSquirrelMachine(vm); - - SQStackInfos si; - SQInteger level = 0; - while (SQ_SUCCEEDED(sq_stackinfos(vm, level, &si))) - { - char buffer[100]; - snprintf(buffer, 99, "%.40s %.40s %.6d\n", si.funcname, si.source, (int)si.line); - - machine->data->error(machine->data->data, buffer); - ++level; - } - - return 0; -} - - -static SQInteger squirrel_peek(HSQUIRRELVM vm) -{ - tic_machine* machine = getSquirrelMachine(vm); - - // check number of args - if (sq_gettop(vm) != 2) - return sq_throwerror(vm, "invalid parameters, peek(address)"); - s32 address = getSquirrelNumber(vm, 2); - - if(address >=0 && address < sizeof(tic_ram)) - { - sq_pushinteger(vm, *((u8*)&machine->memory.ram + address)); - return 1; - } - - return 0; -} - -static SQInteger squirrel_poke(HSQUIRRELVM vm) -{ - tic_machine* machine = getSquirrelMachine(vm); - - if (sq_gettop(vm) != 3) - return sq_throwerror( vm, "invalid parameters, poke(address,value)" ); - s32 address = getSquirrelNumber(vm, 2); - u8 value = getSquirrelNumber(vm, 3) & 0xff; - - if(address >=0 && address < sizeof(tic_ram)) - { - *((u8*)&machine->memory.ram + address) = value; - } - - return 0; -} - -static SQInteger squirrel_peek4(HSQUIRRELVM vm) -{ - SQInteger top = sq_gettop(vm); - - if(top == 2) - { - s32 address = getSquirrelNumber(vm, 2); - - if(address >= 0 && address < sizeof(tic_ram)*2) - { - sq_pushinteger(vm, tic_tool_peek4((u8*)&getSquirrelMachine(vm)->memory.ram, address)); - return 1; - } - } - else return sq_throwerror(vm, "invalid parameters, peek4(addr)\n"); - - return 0; -} - -static SQInteger squirrel_poke4(HSQUIRRELVM vm) -{ - SQInteger top = sq_gettop(vm); - - if(top == 3) - { - s32 address = getSquirrelNumber(vm, 2); - u8 value = getSquirrelNumber(vm, 3); - - if(address >= 0 && address < sizeof(tic_ram)*2) - { - tic_tool_poke4((u8*)&getSquirrelMachine(vm)->memory.ram, address, value); - } - } - else return sq_throwerror(vm, "invalid parameters, poke4(addr,value)\n"); - - return 0; -} - -static SQInteger squirrel_cls(HSQUIRRELVM vm) -{ - SQInteger top = sq_gettop(vm); - - tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); - - memory->api.clear(memory, top == 2 ? getSquirrelNumber(vm, 2) : 0); - - return 0; -} - -static SQInteger squirrel_pix(HSQUIRRELVM vm) -{ - SQInteger top = sq_gettop(vm); - - if(top >= 3) - { - s32 x = getSquirrelNumber(vm, 2); - s32 y = getSquirrelNumber(vm, 3); - - tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); - - if(top >= 4) - { - s32 color = getSquirrelNumber(vm, 4); - memory->api.pixel(memory, x, y, color); - } - else - { - sq_pushinteger(vm, memory->api.get_pixel(memory, x, y)); - return 1; - } - - } - else return sq_throwerror(vm, "invalid parameters, pix(x y [color])\n"); - - return 0; -} - - - -static SQInteger squirrel_line(HSQUIRRELVM vm) -{ - SQInteger top = sq_gettop(vm); - - if(top == 6) - { - s32 x0 = getSquirrelNumber(vm, 2); - s32 y0 = getSquirrelNumber(vm, 3); - s32 x1 = getSquirrelNumber(vm, 4); - s32 y1 = getSquirrelNumber(vm, 5); - s32 color = getSquirrelNumber(vm, 6); - - tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); - - memory->api.line(memory, x0, y0, x1, y1, color); - } - else return sq_throwerror(vm, "invalid parameters, line(x0,y0,x1,y1,color)\n"); - - return 0; -} - -static SQInteger squirrel_rect(HSQUIRRELVM vm) -{ - SQInteger top = sq_gettop(vm); - - if(top == 6) - { - s32 x = getSquirrelNumber(vm, 2); - s32 y = getSquirrelNumber(vm, 3); - s32 w = getSquirrelNumber(vm, 4); - s32 h = getSquirrelNumber(vm, 5); - s32 color = getSquirrelNumber(vm, 6); - - tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); - - memory->api.rect(memory, x, y, w, h, color); - } - else return sq_throwerror(vm, "invalid parameters, rect(x,y,w,h,color)\n"); - - return 0; -} - -static SQInteger squirrel_rectb(HSQUIRRELVM vm) -{ - SQInteger top = sq_gettop(vm); - - if(top == 6) - { - s32 x = getSquirrelNumber(vm, 2); - s32 y = getSquirrelNumber(vm, 3); - s32 w = getSquirrelNumber(vm, 4); - s32 h = getSquirrelNumber(vm, 5); - s32 color = getSquirrelNumber(vm, 6); - - tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); - - memory->api.rect_border(memory, x, y, w, h, color); - } - else return sq_throwerror(vm, "invalid parameters, rectb(x,y,w,h,color)\n"); - - return 0; -} - -static SQInteger squirrel_circ(HSQUIRRELVM vm) -{ - SQInteger top = sq_gettop(vm); - - if(top == 5) - { - s32 radius = getSquirrelNumber(vm, 4); - if(radius < 0) return 0; - - s32 x = getSquirrelNumber(vm, 2); - s32 y = getSquirrelNumber(vm, 3); - s32 color = getSquirrelNumber(vm, 5); - - tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); - - memory->api.circle(memory, x, y, radius, color); - } - else return sq_throwerror(vm, "invalid parameters, circ(x,y,radius,color)\n"); - - return 0; -} - -static SQInteger squirrel_circb(HSQUIRRELVM vm) -{ - SQInteger top = sq_gettop(vm); - - if(top == 5) - { - s32 radius = getSquirrelNumber(vm, 4); - if(radius < 0) return 0; - - s32 x = getSquirrelNumber(vm, 2); - s32 y = getSquirrelNumber(vm, 3); - s32 color = getSquirrelNumber(vm, 5); - - tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); - - memory->api.circle_border(memory, x, y, radius, color); - } - else return sq_throwerror(vm, "invalid parameters, circb(x,y,radius,color)\n"); - - return 0; -} - -static SQInteger squirrel_tri(HSQUIRRELVM vm) -{ - SQInteger top = sq_gettop(vm); - - if(top == 8) - { - s32 pt[6]; - - for(s32 i = 0; i < COUNT_OF(pt); i++) - pt[i] = getSquirrelNumber(vm, i+2); - - s32 color = getSquirrelNumber(vm, 8); - - tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); - - memory->api.tri(memory, pt[0], pt[1], pt[2], pt[3], pt[4], pt[5], color); - } - else return sq_throwerror(vm, "invalid parameters, tri(x1,y1,x2,y2,x3,y3,color)\n"); - - return 0; -} - -static SQInteger squirrel_textri(HSQUIRRELVM vm) -{ - SQInteger top = sq_gettop(vm); - - if (top >= 13) - { - float pt[12]; - - for (s32 i = 0; i < COUNT_OF(pt); i++) - { - SQFloat f = 0.0; - sq_getfloat(vm, i + 2, &f); - pt[i] = (float)f; - } - - tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); - u8 chroma = 0xff; - bool use_map = false; - - // check for use map - if (top >= 14) - { - SQBool b = SQFalse; - sq_getbool(vm, 14, &b); - use_map = (b != SQFalse); - } - // check for chroma - if (top >= 15) - chroma = (u8)getSquirrelNumber(vm, 15); - - memory->api.textri(memory, pt[0], pt[1], // xy 1 - pt[2], pt[3], // xy 2 - pt[4], pt[5], // xy 3 - pt[6], pt[7], // uv 1 - pt[8], pt[9], // uv 2 - pt[10], pt[11], // uv 3 - use_map, // use map - chroma); // chroma - } - else return sq_throwerror(vm, "invalid parameters, textri(x1,y1,x2,y2,x3,y3,u1,v1,u2,v2,u3,v3,[use_map=false],[chroma=off])\n"); - return 0; -} - - -static SQInteger squirrel_clip(HSQUIRRELVM vm) -{ - SQInteger top = sq_gettop(vm); - - if(top == 1) - { - tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); - - memory->api.clip(memory, 0, 0, TIC80_WIDTH, TIC80_HEIGHT); - } - else if(top == 5) - { - s32 x = getSquirrelNumber(vm, 2); - s32 y = getSquirrelNumber(vm, 3); - s32 w = getSquirrelNumber(vm, 4); - s32 h = getSquirrelNumber(vm, 5); - - tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); - - memory->api.clip((tic_mem*)getSquirrelMachine(vm), x, y, w, h); - } - else return sq_throwerror(vm, "invalid parameters, use clip(x,y,w,h) or clip()\n"); - - return 0; -} - -static SQInteger squirrel_btnp(HSQUIRRELVM vm) -{ - tic_machine* machine = getSquirrelMachine(vm); - tic_mem* memory = (tic_mem*)machine; - - SQInteger top = sq_gettop(vm); - - if (top == 1) - { - sq_pushinteger(vm, memory->api.btnp(memory, -1, -1, -1)); - } - else if(top == 2) - { - s32 index = getSquirrelNumber(vm, 2) & 0x1f; - - sq_pushbool(vm, (memory->api.btnp(memory, index, -1, -1) ? SQTrue : SQFalse)); - } - else if (top == 4) - { - s32 index = getSquirrelNumber(vm, 2) & 0x1f; - u32 hold = getSquirrelNumber(vm, 3); - u32 period = getSquirrelNumber(vm, 4); - - sq_pushbool(vm, (memory->api.btnp(memory, index, hold, period) ? SQTrue : SQFalse)); - } - else - { - return sq_throwerror(vm, "invalid params, btnp [ id [ hold period ] ]\n"); - } - - return 1; -} - -static SQInteger squirrel_btn(HSQUIRRELVM vm) -{ - tic_machine* machine = getSquirrelMachine(vm); - - SQInteger top = sq_gettop(vm); - - if (top == 1) - { - sq_pushinteger(vm, machine->memory.ram.input.gamepads.data); - } - else if (top == 2) - { - u32 index = getSquirrelNumber(vm, 2) & 0x1f; - bool pressed = (machine->memory.ram.input.gamepads.data & (1 << index)) != 0; - sq_pushbool(vm, pressed ? SQTrue : SQFalse); - } - else - { - return sq_throwerror(vm, "invalid params, btn [ id ]\n"); - } - - return 1; -} - -static SQInteger squirrel_spr(HSQUIRRELVM vm) -{ - SQInteger top = sq_gettop(vm); - - s32 index = 0; - s32 x = 0; - s32 y = 0; - s32 w = 1; - s32 h = 1; - s32 scale = 1; - tic_flip flip = tic_no_flip; - tic_rotate rotate = tic_no_rotate; - static u8 colors[TIC_PALETTE_SIZE]; - s32 count = 0; - - if(top >= 2) - { - index = getSquirrelNumber(vm, 2); - - if(top >= 4) - { - x = getSquirrelNumber(vm, 3); - y = getSquirrelNumber(vm, 4); - - if(top >= 5) - { - if(OT_ARRAY == sq_gettype(vm, 5)) - { - for(s32 i = 0; i < TIC_PALETTE_SIZE; i++) - { - sq_pushinteger(vm, (SQInteger)i); - sq_rawget(vm, 5); - if(sq_gettype(vm, -1) & (OT_FLOAT|OT_INTEGER)) - { - colors[i-1] = getSquirrelNumber(vm, -1); - count++; - sq_poptop(vm); - } - else - { - sq_poptop(vm); - break; - } - } - } - else - { - colors[0] = getSquirrelNumber(vm, 5); - count = 1; - } - - if(top >= 6) - { - scale = getSquirrelNumber(vm, 6); - - if(top >= 7) - { - flip = getSquirrelNumber(vm, 7); - - if(top >= 8) - { - rotate = getSquirrelNumber(vm, 8); - - if(top >= 10) - { - w = getSquirrelNumber(vm, 9); - h = getSquirrelNumber(vm, 10); - } - } - } - } - } - } - } - - tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); - - memory->api.sprite_ex(memory, &memory->ram.tiles, index, x, y, w, h, colors, count, scale, flip, rotate); - - return 0; -} - -static SQInteger squirrel_mget(HSQUIRRELVM vm) -{ - SQInteger top = sq_gettop(vm); - - if(top == 3) - { - s32 x = getSquirrelNumber(vm, 2); - s32 y = getSquirrelNumber(vm, 3); - - tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); - - u8 value = memory->api.map_get(memory, &memory->ram.map, x, y); - sq_pushinteger(vm, value); - return 1; - } - else return sq_throwerror(vm, "invalid params, mget(x,y)\n"); - - return 0; -} - -static SQInteger squirrel_mset(HSQUIRRELVM vm) -{ - SQInteger top = sq_gettop(vm); - - if(top == 4) - { - s32 x = getSquirrelNumber(vm, 2); - s32 y = getSquirrelNumber(vm, 3); - u8 val = getSquirrelNumber(vm, 4); - - tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); - - memory->api.map_set(memory, &memory->ram.map, x, y, val); - } - else return sq_throwerror(vm, "invalid params, mget(x,y)\n"); - - return 0; -} - -typedef struct -{ - HSQUIRRELVM vm; - HSQOBJECT reg; -} RemapData; - -static void remapCallback(void* data, s32 x, s32 y, RemapResult* result) -{ - RemapData* remap = (RemapData*)data; - HSQUIRRELVM vm = remap->vm; - - SQInteger top = sq_gettop(vm); - - sq_pushobject(vm, remap->reg); - sq_pushroottable(vm); - sq_pushinteger(vm, result->index); - sq_pushinteger(vm, x); - sq_pushinteger(vm, y); - //lua_pcall(lua, 3, 3, 0); - - if (SQ_SUCCEEDED(sq_call(vm, 4, SQTrue, SQTrue))) - { - sq_pushinteger(vm, 0); - if (SQ_SUCCEEDED(sq_get(vm, -2))) - { - result->index = getSquirrelNumber(vm, -1); - sq_poptop(vm); - sq_pushinteger(vm, 1); - if (SQ_SUCCEEDED(sq_get(vm, -2))) - { - result->flip = getSquirrelNumber(vm, -1); - sq_poptop(vm); - sq_pushinteger(vm, 2); - if (SQ_SUCCEEDED(sq_get(vm, -2))) - { - result->rotate = getSquirrelNumber(vm, -1); - sq_poptop(vm); - } - } - } - } - - sq_settop(vm, top); -} - -static SQInteger squirrel_map(HSQUIRRELVM vm) -{ - s32 x = 0; - s32 y = 0; - s32 w = TIC_MAP_SCREEN_WIDTH; - s32 h = TIC_MAP_SCREEN_HEIGHT; - s32 sx = 0; - s32 sy = 0; - u8 chromakey = -1; - s32 scale = 1; - - SQInteger top = sq_gettop(vm); - - if(top >= 3) - { - x = getSquirrelNumber(vm, 2); - y = getSquirrelNumber(vm, 3); - - if(top >= 5) - { - w = getSquirrelNumber(vm, 4); - h = getSquirrelNumber(vm, 5); - - if(top >= 7) - { - sx = getSquirrelNumber(vm, 6); - sy = getSquirrelNumber(vm, 7); - - if(top >= 8) - { - chromakey = getSquirrelNumber(vm, 8); - - if(top >= 9) - { - scale = getSquirrelNumber(vm, 9); - - if(top >= 10) - { - SQObjectType type = sq_gettype(vm, 10); - if (type & (OT_CLOSURE|OT_NATIVECLOSURE|OT_INSTANCE)) - { - RemapData data = {vm}; - sq_resetobject(&data.reg); - sq_getstackobj(vm, 10, &data.reg); - sq_addref(vm, &data.reg); - - tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); - - memory->api.remap(memory, &memory->ram.map, &memory->ram.tiles, x, y, w, h, sx, sy, chromakey, scale, remapCallback, &data); - - //luaL_unref(lua, LUA_REGISTRYINDEX, data.reg); - sq_release(vm, &data.reg); - - return 0; - } - } - } - } - } - } - } - - tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); - - memory->api.map((tic_mem*)getSquirrelMachine(vm), &memory->ram.map, &memory->ram.tiles, x, y, w, h, sx, sy, chromakey, scale); - - return 0; -} - -static SQInteger squirrel_music(HSQUIRRELVM vm) -{ - SQInteger top = sq_gettop(vm); - tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); - - if(top == 1) memory->api.music(memory, -1, 0, 0, false); - else if(top >= 2) - { - memory->api.music(memory, -1, 0, 0, false); - - s32 track = getSquirrelNumber(vm, 2); - s32 frame = -1; - s32 row = -1; - bool loop = true; - - if(top >= 3) - { - frame = getSquirrelNumber(vm, 3); - - if(top >= 4) - { - row = getSquirrelNumber(vm, 4); - - if(top >= 5) - { - SQBool b = SQFalse; - sq_getbool(vm, 5, &b); - loop = (b != SQFalse); - } - } - } - - memory->api.music(memory, track, frame, row, loop); - } - else return sq_throwerror(vm, "invalid params, use music(track)\n"); - - return 0; -} - -static SQInteger squirrel_sfx(HSQUIRRELVM vm) -{ - SQInteger top = sq_gettop(vm); - - if(top >= 2) - { - tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); - - s32 note = -1; - s32 octave = -1; - s32 duration = -1; - s32 channel = 0; - s32 volume = MAX_VOLUME; - s32 speed = SFX_DEF_SPEED; - - s32 index = getSquirrelNumber(vm, 2); - - if(index < SFX_COUNT) - { - if (index >= 0) - { - tic_sample* effect = memory->ram.sfx.samples.data + index; - - note = effect->note; - octave = effect->octave; - speed = effect->speed; - } - - if(top >= 3) - { - if(sq_gettype(vm, 3) & (OT_INTEGER|OT_FLOAT)) - { - s32 id = getSquirrelNumber(vm, 3); - note = id % NOTES; - octave = id / NOTES; - } - else if(sq_gettype(vm, 3) == OT_STRING) - { - const SQChar* str; - sq_getstring(vm, 3, &str); - const char* noteStr = (const char*)str; - - if(!tic_tool_parse_note(noteStr, ¬e, &octave)) - { - return sq_throwerror(vm, "invalid note, should be like C#4\n"); - } - } - - if(top >= 4) - { - duration = getSquirrelNumber(vm, 4); - - if(top >= 5) - { - channel = getSquirrelNumber(vm, 5); - - if(top >= 6) - { - volume = getSquirrelNumber(vm, 6); - - if(top >= 7) - { - speed = getSquirrelNumber(vm, 7); - } - } - } - } - } - - if (channel >= 0 && channel < TIC_SOUND_CHANNELS) - { - memory->api.sfx_stop(memory, channel); - memory->api.sfx_ex(memory, index, note, octave, duration, channel, volume & 0xf, speed); - } - else return sq_throwerror(vm, "unknown channel\n"); - } - else return sq_throwerror(vm, "unknown sfx index\n"); - } - else return sq_throwerror(vm, "invalid sfx params\n"); - - return 0; -} - -static SQInteger squirrel_sync(HSQUIRRELVM vm) -{ - tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); - - bool toCart = false; - u32 mask = 0; - s32 bank = 0; - - if(sq_gettop(vm) >= 2) - { - mask = getSquirrelNumber(vm, 2); - - if(sq_gettop(vm) >= 3) - { - bank = getSquirrelNumber(vm, 3); - - if(sq_gettop(vm) >= 4) - { - SQBool b = SQFalse; - sq_getbool(vm, 4, &b); - toCart = (b != SQFalse); - } - } - } - - if(bank >= 0 && bank < TIC_BANKS) - memory->api.sync(memory, mask, bank, toCart); - else - return sq_throwerror(vm, "sync() error, invalid bank"); - - return 0; -} - -static SQInteger squirrel_reset(HSQUIRRELVM vm) -{ - tic_machine* machine = getSquirrelMachine(vm); - - machine->state.initialized = false; - - return 0; -} - -static SQInteger squirrel_key(HSQUIRRELVM vm) -{ - tic_machine* machine = getSquirrelMachine(vm); - tic_mem* tic = &machine->memory; - - SQInteger top = sq_gettop(vm); - - if (top == 1) - { - sq_pushbool(vm, tic->api.key(tic, tic_key_unknown) ? SQTrue : SQFalse); - } - else if (top == 2) - { - tic_key key = getSquirrelNumber(vm, 2); - - if(key < tic_key_escape) - sq_pushbool(vm, tic->api.key(tic, key) ? SQTrue : SQFalse); - else - { - return sq_throwerror(vm, "unknown keyboard code\n"); - } - } - else - { - return sq_throwerror(vm, "invalid params, key [code]\n"); - } - - return 1; -} - -static SQInteger squirrel_keyp(HSQUIRRELVM vm) -{ - tic_machine* machine = getSquirrelMachine(vm); - tic_mem* tic = &machine->memory; - - SQInteger top = sq_gettop(vm); - - if (top == 1) - { - sq_pushbool(vm, tic->api.keyp(tic, tic_key_unknown, -1, -1) ? SQTrue : SQFalse); - } - else - { - tic_key key = getSquirrelNumber(vm, 2); - - if(key >= tic_key_escape) - { - return sq_throwerror(vm, "unknown keyboard code\n"); - } - else - { - if(top == 2) - { - sq_pushbool(vm, tic->api.keyp(tic, key, -1, -1) ? SQTrue : SQFalse); - } - else if(top == 4) - { - u32 hold = getSquirrelNumber(vm, 3); - u32 period = getSquirrelNumber(vm, 4); - - sq_pushbool(vm, tic->api.keyp(tic, key, hold, period) ? SQTrue : SQFalse); - } - else - { - return sq_throwerror(vm, "invalid params, keyp [ code [ hold period ] ]\n"); - } - } - } - - return 1; -} - -static SQInteger squirrel_memcpy(HSQUIRRELVM vm) -{ - SQInteger top = sq_gettop(vm); - - if(top == 4) - { - s32 dest = getSquirrelNumber(vm, 2); - s32 src = getSquirrelNumber(vm, 3); - s32 size = getSquirrelNumber(vm, 4); - s32 bound = sizeof(tic_ram) - size; - - if(size >= 0 && size <= sizeof(tic_ram) && dest >= 0 && src >= 0 && dest <= bound && src <= bound) - { - u8* base = (u8*)&getSquirrelMachine(vm)->memory; - memcpy(base + dest, base + src, size); - return 0; - } - } - - return sq_throwerror(vm, "invalid params, memcpy(dest,src,size)\n"); -} - -static SQInteger squirrel_memset(HSQUIRRELVM vm) -{ - SQInteger top = sq_gettop(vm); - - if(top == 4) - { - s32 dest = getSquirrelNumber(vm, 2); - u8 value = getSquirrelNumber(vm, 3); - s32 size = getSquirrelNumber(vm, 4); - s32 bound = sizeof(tic_ram) - size; - - if(size >= 0 && size <= sizeof(tic_ram) && dest >= 0 && dest <= bound) - { - u8* base = (u8*)&getSquirrelMachine(vm)->memory; - memset(base + dest, value, size); - return 0; - } - } - - return sq_throwerror(vm, "invalid params, memset(dest,val,size)\n"); -} - -// NB we leave the string on the stack so that the char* pointer remains valid. -static const char* printString(HSQUIRRELVM vm, s32 index) -{ - const SQChar* text = ""; - if (SQ_SUCCEEDED(sq_tostring(vm, index))) - { - sq_getstring(vm, -1, &text); - } - - return (const char*)text; -} - -static SQInteger squirrel_font(HSQUIRRELVM vm) -{ - tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); - SQInteger top = sq_gettop(vm); - - if(top >= 2) - { - const char* text = printString(vm, 2); - s32 x = 0; - s32 y = 0; - s32 width = TIC_SPRITESIZE; - s32 height = TIC_SPRITESIZE; - u8 chromakey = 0; - bool fixed = false; - s32 scale = 1; - - if(top >= 4) - { - x = getSquirrelNumber(vm, 3); - y = getSquirrelNumber(vm, 4); - - if(top >= 5) - { - chromakey = getSquirrelNumber(vm, 5); - - if(top >= 7) - { - width = getSquirrelNumber(vm, 6); - height = getSquirrelNumber(vm, 7); - - if(top >= 8) - { - SQBool b = SQFalse; - sq_getbool(vm, 8, &b); - fixed = (b != SQFalse); - - if(top >= 9) - { - scale = getSquirrelNumber(vm, 9); - } - } - } - } - } - - if(scale == 0) - { - sq_pushinteger(vm, 0); - return 1; - } - - s32 size = drawText(memory, text, x, y, width, height, chromakey, scale, fixed ? drawSpriteFont : drawFixedSpriteFont); - - sq_pushinteger(vm, size); - return 1; - } - - return 0; -} - -static SQInteger squirrel_print(HSQUIRRELVM vm) -{ - SQInteger top = sq_gettop(vm); - - if(top >= 2) - { - tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); - - s32 x = 0; - s32 y = 0; - s32 color = TIC_PALETTE_SIZE-1; - bool fixed = false; - s32 scale = 1; - - const char* text = printString(vm, 2); - - if(top >= 4) - { - x = getSquirrelNumber(vm, 3); - y = getSquirrelNumber(vm, 4); - - if(top >= 5) - { - color = getSquirrelNumber(vm, 5) % TIC_PALETTE_SIZE; - - if(top >= 6) - { - SQBool b = SQFalse; - sq_getbool(vm, 5, &b); - fixed = (b != SQFalse); - - if(top >= 7) - { - scale = getSquirrelNumber(vm, 7); - } - } - } - } - - if(scale == 0) - { - sq_pushinteger(vm, 0); - return 1; - } - - s32 size = memory->api.text_ex(memory, text ? text : "nil", x, y, color, fixed, scale); - - sq_pushinteger(vm, size); - - return 1; - } - - return 0; -} - -static SQInteger squirrel_trace(HSQUIRRELVM vm) -{ - SQInteger top = sq_gettop(vm); - tic_machine* machine = getSquirrelMachine(vm); - - if(top >= 2) - { - const char* text = printString(vm, 2); - u8 color = tic_color_white; - - if(top >= 3) - { - color = getSquirrelNumber(vm, 3); - } - - machine->data->trace(machine->data->data, text ? text : "nil", color); - } - - return 0; -} - -static SQInteger squirrel_pmem(HSQUIRRELVM vm) -{ - SQInteger top = sq_gettop(vm); - tic_machine* machine = getSquirrelMachine(vm); - tic_mem* memory = &machine->memory; - - if(top >= 2) - { - u32 index = getSquirrelNumber(vm, 2); - - if(index < TIC_PERSISTENT_SIZE) - { - u32 val = memory->persistent.data[index]; - - if(top >= 3) - { - SQInteger i = 0; - sq_getinteger(vm, 3, &i); - memory->persistent.data[index] = (u32)i; - machine->data->syncPMEM = true; - } - - sq_pushinteger(vm, val); - - return 1; - } - return sq_throwerror(vm, "invalid persistent memory index\n"); - } - else return sq_throwerror(vm, "invalid params, pmem(index [val]) -> val\n"); - - return 0; -} - -static SQInteger squirrel_time(HSQUIRRELVM vm) -{ - tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); - - sq_pushfloat(vm, (SQFloat)(memory->api.time(memory))); - - return 1; -} - -static SQInteger squirrel_exit(HSQUIRRELVM vm) -{ - tic_machine* machine = getSquirrelMachine(vm); - - machine->data->exit(machine->data->data); - - return 0; -} - -static SQInteger squirrel_mouse(HSQUIRRELVM vm) -{ - tic_machine* machine = getSquirrelMachine(vm); - - const tic80_mouse* mouse = &machine->memory.ram.input.mouse; - - sq_newarray(vm, 5); - sq_pushinteger(vm, mouse->x); - sq_arrayappend(vm, -2); - sq_pushinteger(vm, mouse->y); - sq_arrayappend(vm, -2); - sq_pushbool(vm, mouse->left ? SQTrue : SQFalse); - sq_arrayappend(vm, -2); - sq_pushbool(vm, mouse->middle ? SQTrue : SQFalse); - sq_arrayappend(vm, -2); - sq_pushbool(vm, mouse->right ? SQTrue : SQFalse); - sq_arrayappend(vm, -2); - - return 1; -} - -static SQInteger squirrel_dofile(HSQUIRRELVM vm) -{ - return sq_throwerror(vm, "unknown method: \"dofile\"\n"); -} - -static SQInteger squirrel_loadfile(HSQUIRRELVM vm) -{ - return sq_throwerror(vm, "unknown method: \"loadfile\"\n"); -} - -static void squirrel_open_builtins(HSQUIRRELVM vm) -{ - sq_pushroottable(vm); - sqstd_register_mathlib(vm); - sqstd_register_stringlib(vm); - sqstd_register_bloblib(vm); - sq_poptop(vm); -} - -static const char* const ApiKeywords[] = API_KEYWORDS; -static const SQFUNCTION ApiFunc[] = -{ - NULL, NULL, NULL, squirrel_print, squirrel_cls, squirrel_pix, squirrel_line, squirrel_rect, - squirrel_rectb, squirrel_spr, squirrel_btn, squirrel_btnp, squirrel_sfx, squirrel_map, squirrel_mget, - squirrel_mset, squirrel_peek, squirrel_poke, squirrel_peek4, squirrel_poke4, squirrel_memcpy, - squirrel_memset, squirrel_trace, squirrel_pmem, squirrel_time, squirrel_exit, squirrel_font, squirrel_mouse, - squirrel_circ, squirrel_circb, squirrel_tri, squirrel_textri, squirrel_clip, squirrel_music, squirrel_sync, squirrel_reset, - squirrel_key, squirrel_keyp -}; - -STATIC_ASSERT(api_func, COUNT_OF(ApiKeywords) == COUNT_OF(ApiFunc)); - -static void checkForceExit(HSQUIRRELVM vm, SQInteger type, const SQChar* sourceName, SQInteger line, const SQChar* functionName) -{ - tic_machine* machine = getSquirrelMachine(vm); - tic_tick_data* tick = machine->data; - - if(tick->forceExit && tick->forceExit(tick->data)) - sq_throwerror(vm, "script execution was interrupted"); -} - -static void initAPI(tic_machine* machine) -{ - //lua_pushlightuserdata(machine->lua, machine); - //lua_setglobal(machine->lua, TicMachine); - HSQUIRRELVM vm = machine->squirrel; - - sq_pushregistrytable(vm); - sq_pushstring(vm, TicMachine, -1); - sq_pushuserpointer(machine->squirrel, machine); - sq_newslot(vm, -3, SQTrue); - sq_poptop(vm); - -#if USE_FOREIGN_POINTER - sq_setforeignptr(vm, machine); -#endif - - for (s32 i = 0; i < COUNT_OF(ApiFunc); i++) - if (ApiFunc[i]) - registerSquirrelFunction(machine, ApiFunc[i], ApiKeywords[i]); - - registerSquirrelFunction(machine, squirrel_dofile, "dofile"); - registerSquirrelFunction(machine, squirrel_loadfile, "loadfile"); - -#if CHECK_FORCE_EXIT - sq_setnativedebughook(vm, checkForceExit); -#endif - sq_enabledebuginfo(vm, SQTrue); - -} - -static void closeSquirrel(tic_mem* tic) -{ - tic_machine* machine = (tic_machine*)tic; - - if(machine->squirrel) - { - sq_close(machine->squirrel); - machine->squirrel = NULL; - } -} - -static bool initSquirrel(tic_mem* tic, const char* code) -{ - tic_machine* machine = (tic_machine*)tic; - - closeSquirrel(tic); - - HSQUIRRELVM vm = machine->squirrel = sq_open(100); - squirrel_open_builtins(vm); - - sq_newclosure(vm, squirrel_errorHandler, 0); - sq_seterrorhandler(vm); - - initAPI(machine); - - { - HSQUIRRELVM vm = machine->squirrel; - - sq_settop(vm, 0); - - if((SQ_FAILED(sq_compilebuffer(vm, code, strlen(code), "squirrel", SQFalse))) || - (sq_pushroottable(vm), false) || - (SQ_FAILED(sq_call(vm, 1, SQFalse, SQTrue)))) - { - sq_getlasterror(vm); - sq_tostring(vm, -1); - const SQChar* errorString = "unknown error"; - sq_getstring(vm, -1, &errorString); - - machine->data->error(machine->data->data, errorString); - - sq_pop(vm, 2); // error and error string - - return false; - } - } - - return true; -} - -static void callSquirrelTick(tic_mem* tic) -{ - tic_machine* machine = (tic_machine*)tic; - - const char* TicFunc = ApiKeywords[0]; - - HSQUIRRELVM vm = machine->squirrel; - - if(vm) - { - //lua_getglobal(lua, TicFunc); - sq_pushroottable(vm); - sq_pushstring(vm, TicFunc, -1); - - if (SQ_SUCCEEDED(sq_get(vm, -2))) - { - sq_pushroottable(vm); - if(SQ_FAILED(sq_call(vm, 1, SQFalse, SQTrue))) - { - sq_getlasterror(vm); - sq_tostring(vm, -1); - const SQChar* errorString = "unknown error"; - sq_getstring(vm, -1, &errorString); - - machine->data->error(machine->data->data, errorString); - sq_pop(vm, 3); // remove string, error and root table. - } - } - else - { - sq_pop(vm, 1); - machine->data->error(machine->data->data, "'function TIC()...' isn't found :("); - } - } -} - -static void callSquirrelScanlineName(tic_mem* memory, s32 row, void* data, const char* name) -{ - tic_machine* machine = (tic_machine*)memory; - HSQUIRRELVM vm = machine->squirrel; - - if (vm) - { - sq_pushroottable(vm); - sq_pushstring(vm, name, -1); - if (SQ_SUCCEEDED(sq_get(vm, -2))) - { - sq_pushroottable(vm); - sq_pushinteger(vm, row); - - if(SQ_FAILED(sq_call(vm, 2, SQFalse, SQTrue))) - { - sq_getlasterror(vm); - sq_tostring(vm, -1); - - const SQChar* errorString = "unknown error"; - sq_getstring(vm, -1, &errorString); - machine->data->error(machine->data->data, errorString); - sq_pop(vm, 3); // error string, error and root table - } - } - else sq_poptop(vm); - } -} - -static void callSquirrelScanline(tic_mem* memory, s32 row, void* data) -{ - callSquirrelScanlineName(memory, row, data, ApiKeywords[1]); - - // try to call old scanline - callSquirrelScanlineName(memory, row, data, "scanline"); -} - -static void callSquirrelOverline(tic_mem* memory, void* data) -{ - tic_machine* machine = (tic_machine*)memory; - HSQUIRRELVM vm = machine->squirrel; - - if (vm) - { - const char* OvrFunc = ApiKeywords[2]; - - sq_pushroottable(vm); - sq_pushstring(vm, OvrFunc, -1); - - if(SQ_SUCCEEDED(sq_get(vm, -2))) - { - sq_pushroottable(vm); - if(SQ_FAILED(sq_call(vm, 1, SQFalse, SQTrue))) - { - sq_getlasterror(vm); - sq_tostring(vm, -1); - const SQChar* errorString = "unknown error"; - sq_getstring(vm, -1, &errorString); - machine->data->error(machine->data->data, errorString); - sq_pop(vm, 3); - } - } - else sq_poptop(vm); - } - -} - -static const char* const SquirrelKeywords [] = -{ +// MIT License + +// Copyright (c) 2017 Vadim Grigoruk @nesbox // grigoruk@gmail.com + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include "machine.h" + +#if defined(TIC_BUILD_WITH_SQUIRREL) + +//#define USE_FOREIGN_POINTER +//#define CHECK_FORCE_EXIT + +#include +#include +#include +#include +#include +#include +#include +#include + +static const char TicMachine[] = "_TIC80"; + + +// !TODO: get rid of this wrap +static s32 getSquirrelNumber(HSQUIRRELVM vm, s32 index) +{ + SQInteger i; + if (SQ_SUCCEEDED(sq_getinteger(vm, index, &i))) + return (s32)i; + + SQFloat f; + if (SQ_SUCCEEDED(sq_getfloat(vm, index, &f))) + return (s32)f; + + return 0; +} + +static void registerSquirrelFunction(tic_machine* machine, SQFUNCTION func, const char *name) +{ + sq_pushroottable(machine->squirrel); + sq_pushstring(machine->squirrel, name, -1); + sq_newclosure(machine->squirrel, func, 0); + sq_newslot(machine->squirrel, -3, SQTrue); + sq_poptop(machine->squirrel); // remove root table. +} + +static tic_machine* getSquirrelMachine(HSQUIRRELVM vm) +{ +#if USE_FOREIGN_POINTER + return (tic_machine*)sq_getforeignpointer(vm); +#else + sq_pushregistrytable(vm); + sq_pushstring(vm, TicMachine, -1); + if (SQ_FAILED(sq_get(vm, -2))) + { + fprintf(stderr, "FATAL ERROR: TicMachine not found!\n"); + abort(); + } + SQUserPointer ptr; + if (SQ_FAILED(sq_getuserpointer(vm, -1, &ptr))) + { + fprintf(stderr, "FATAL ERROR: Cannot get user pointer for TicMachine!\n"); + abort(); + } + tic_machine* machine = (tic_machine*)ptr; + sq_pop(vm, 2); // user pointer and registry table. + return machine; +#endif +} + +void squirrel_compilerError(HSQUIRRELVM vm, const SQChar* desc, const SQChar* source, + SQInteger line, SQInteger column) +{ + tic_machine* machine = getSquirrelMachine(vm); + char buffer[1024]; + snprintf(buffer, 1023, "%.40s line %.6d column %.6d: %s\n", source, (int)line, (int)column, desc); + + machine->data->error(machine->data->data, buffer); +} + +static SQInteger squirrel_errorHandler(HSQUIRRELVM vm) +{ + tic_machine* machine = getSquirrelMachine(vm); + + SQStackInfos si; + SQInteger level = 0; + while (SQ_SUCCEEDED(sq_stackinfos(vm, level, &si))) + { + char buffer[100]; + snprintf(buffer, 99, "%.40s %.40s %.6d\n", si.funcname, si.source, (int)si.line); + + machine->data->error(machine->data->data, buffer); + ++level; + } + + return 0; +} + + +static SQInteger squirrel_peek(HSQUIRRELVM vm) +{ + tic_machine* machine = getSquirrelMachine(vm); + + // check number of args + if (sq_gettop(vm) != 2) + return sq_throwerror(vm, "invalid parameters, peek(address)"); + s32 address = getSquirrelNumber(vm, 2); + + if(address >=0 && address < sizeof(tic_ram)) + { + sq_pushinteger(vm, *((u8*)&machine->memory.ram + address)); + return 1; + } + + return 0; +} + +static SQInteger squirrel_poke(HSQUIRRELVM vm) +{ + tic_machine* machine = getSquirrelMachine(vm); + + if (sq_gettop(vm) != 3) + return sq_throwerror( vm, "invalid parameters, poke(address,value)" ); + s32 address = getSquirrelNumber(vm, 2); + u8 value = getSquirrelNumber(vm, 3) & 0xff; + + if(address >=0 && address < sizeof(tic_ram)) + { + *((u8*)&machine->memory.ram + address) = value; + } + + return 0; +} + +static SQInteger squirrel_peek4(HSQUIRRELVM vm) +{ + SQInteger top = sq_gettop(vm); + + if(top == 2) + { + s32 address = getSquirrelNumber(vm, 2); + + if(address >= 0 && address < sizeof(tic_ram)*2) + { + sq_pushinteger(vm, tic_tool_peek4((u8*)&getSquirrelMachine(vm)->memory.ram, address)); + return 1; + } + } + else return sq_throwerror(vm, "invalid parameters, peek4(addr)\n"); + + return 0; +} + +static SQInteger squirrel_poke4(HSQUIRRELVM vm) +{ + SQInteger top = sq_gettop(vm); + + if(top == 3) + { + s32 address = getSquirrelNumber(vm, 2); + u8 value = getSquirrelNumber(vm, 3); + + if(address >= 0 && address < sizeof(tic_ram)*2) + { + tic_tool_poke4((u8*)&getSquirrelMachine(vm)->memory.ram, address, value); + } + } + else return sq_throwerror(vm, "invalid parameters, poke4(addr,value)\n"); + + return 0; +} + +static SQInteger squirrel_cls(HSQUIRRELVM vm) +{ + SQInteger top = sq_gettop(vm); + + tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); + + memory->api.clear(memory, top == 2 ? getSquirrelNumber(vm, 2) : 0); + + return 0; +} + +static SQInteger squirrel_pix(HSQUIRRELVM vm) +{ + SQInteger top = sq_gettop(vm); + + if(top >= 3) + { + s32 x = getSquirrelNumber(vm, 2); + s32 y = getSquirrelNumber(vm, 3); + + tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); + + if(top >= 4) + { + s32 color = getSquirrelNumber(vm, 4); + memory->api.pixel(memory, x, y, color); + } + else + { + sq_pushinteger(vm, memory->api.get_pixel(memory, x, y)); + return 1; + } + + } + else return sq_throwerror(vm, "invalid parameters, pix(x y [color])\n"); + + return 0; +} + + + +static SQInteger squirrel_line(HSQUIRRELVM vm) +{ + SQInteger top = sq_gettop(vm); + + if(top == 6) + { + s32 x0 = getSquirrelNumber(vm, 2); + s32 y0 = getSquirrelNumber(vm, 3); + s32 x1 = getSquirrelNumber(vm, 4); + s32 y1 = getSquirrelNumber(vm, 5); + s32 color = getSquirrelNumber(vm, 6); + + tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); + + memory->api.line(memory, x0, y0, x1, y1, color); + } + else return sq_throwerror(vm, "invalid parameters, line(x0,y0,x1,y1,color)\n"); + + return 0; +} + +static SQInteger squirrel_rect(HSQUIRRELVM vm) +{ + SQInteger top = sq_gettop(vm); + + if(top == 6) + { + s32 x = getSquirrelNumber(vm, 2); + s32 y = getSquirrelNumber(vm, 3); + s32 w = getSquirrelNumber(vm, 4); + s32 h = getSquirrelNumber(vm, 5); + s32 color = getSquirrelNumber(vm, 6); + + tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); + + memory->api.rect(memory, x, y, w, h, color); + } + else return sq_throwerror(vm, "invalid parameters, rect(x,y,w,h,color)\n"); + + return 0; +} + +static SQInteger squirrel_rectb(HSQUIRRELVM vm) +{ + SQInteger top = sq_gettop(vm); + + if(top == 6) + { + s32 x = getSquirrelNumber(vm, 2); + s32 y = getSquirrelNumber(vm, 3); + s32 w = getSquirrelNumber(vm, 4); + s32 h = getSquirrelNumber(vm, 5); + s32 color = getSquirrelNumber(vm, 6); + + tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); + + memory->api.rect_border(memory, x, y, w, h, color); + } + else return sq_throwerror(vm, "invalid parameters, rectb(x,y,w,h,color)\n"); + + return 0; +} + +static SQInteger squirrel_circ(HSQUIRRELVM vm) +{ + SQInteger top = sq_gettop(vm); + + if(top == 5) + { + s32 radius = getSquirrelNumber(vm, 4); + if(radius < 0) return 0; + + s32 x = getSquirrelNumber(vm, 2); + s32 y = getSquirrelNumber(vm, 3); + s32 color = getSquirrelNumber(vm, 5); + + tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); + + memory->api.circle(memory, x, y, radius, color); + } + else return sq_throwerror(vm, "invalid parameters, circ(x,y,radius,color)\n"); + + return 0; +} + +static SQInteger squirrel_circb(HSQUIRRELVM vm) +{ + SQInteger top = sq_gettop(vm); + + if(top == 5) + { + s32 radius = getSquirrelNumber(vm, 4); + if(radius < 0) return 0; + + s32 x = getSquirrelNumber(vm, 2); + s32 y = getSquirrelNumber(vm, 3); + s32 color = getSquirrelNumber(vm, 5); + + tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); + + memory->api.circle_border(memory, x, y, radius, color); + } + else return sq_throwerror(vm, "invalid parameters, circb(x,y,radius,color)\n"); + + return 0; +} + +static SQInteger squirrel_tri(HSQUIRRELVM vm) +{ + SQInteger top = sq_gettop(vm); + + if(top == 8) + { + s32 pt[6]; + + for(s32 i = 0; i < COUNT_OF(pt); i++) + pt[i] = getSquirrelNumber(vm, i+2); + + s32 color = getSquirrelNumber(vm, 8); + + tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); + + memory->api.tri(memory, pt[0], pt[1], pt[2], pt[3], pt[4], pt[5], color); + } + else return sq_throwerror(vm, "invalid parameters, tri(x1,y1,x2,y2,x3,y3,color)\n"); + + return 0; +} + +static SQInteger squirrel_textri(HSQUIRRELVM vm) +{ + SQInteger top = sq_gettop(vm); + + if (top >= 13) + { + float pt[12]; + + for (s32 i = 0; i < COUNT_OF(pt); i++) + { + SQFloat f = 0.0; + sq_getfloat(vm, i + 2, &f); + pt[i] = (float)f; + } + + tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); + u8 chroma = 0xff; + bool use_map = false; + + // check for use map + if (top >= 14) + { + SQBool b = SQFalse; + sq_getbool(vm, 14, &b); + use_map = (b != SQFalse); + } + // check for chroma + if (top >= 15) + chroma = (u8)getSquirrelNumber(vm, 15); + + memory->api.textri(memory, pt[0], pt[1], // xy 1 + pt[2], pt[3], // xy 2 + pt[4], pt[5], // xy 3 + pt[6], pt[7], // uv 1 + pt[8], pt[9], // uv 2 + pt[10], pt[11], // uv 3 + use_map, // use map + chroma); // chroma + } + else return sq_throwerror(vm, "invalid parameters, textri(x1,y1,x2,y2,x3,y3,u1,v1,u2,v2,u3,v3,[use_map=false],[chroma=off])\n"); + return 0; +} + + +static SQInteger squirrel_clip(HSQUIRRELVM vm) +{ + SQInteger top = sq_gettop(vm); + + if(top == 1) + { + tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); + + memory->api.clip(memory, 0, 0, TIC80_WIDTH, TIC80_HEIGHT); + } + else if(top == 5) + { + s32 x = getSquirrelNumber(vm, 2); + s32 y = getSquirrelNumber(vm, 3); + s32 w = getSquirrelNumber(vm, 4); + s32 h = getSquirrelNumber(vm, 5); + + tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); + + memory->api.clip((tic_mem*)getSquirrelMachine(vm), x, y, w, h); + } + else return sq_throwerror(vm, "invalid parameters, use clip(x,y,w,h) or clip()\n"); + + return 0; +} + +static SQInteger squirrel_btnp(HSQUIRRELVM vm) +{ + tic_machine* machine = getSquirrelMachine(vm); + tic_mem* memory = (tic_mem*)machine; + + SQInteger top = sq_gettop(vm); + + if (top == 1) + { + sq_pushinteger(vm, memory->api.btnp(memory, -1, -1, -1)); + } + else if(top == 2) + { + s32 index = getSquirrelNumber(vm, 2) & 0x1f; + + sq_pushbool(vm, (memory->api.btnp(memory, index, -1, -1) ? SQTrue : SQFalse)); + } + else if (top == 4) + { + s32 index = getSquirrelNumber(vm, 2) & 0x1f; + u32 hold = getSquirrelNumber(vm, 3); + u32 period = getSquirrelNumber(vm, 4); + + sq_pushbool(vm, (memory->api.btnp(memory, index, hold, period) ? SQTrue : SQFalse)); + } + else + { + return sq_throwerror(vm, "invalid params, btnp [ id [ hold period ] ]\n"); + } + + return 1; +} + +static SQInteger squirrel_btn(HSQUIRRELVM vm) +{ + tic_machine* machine = getSquirrelMachine(vm); + + SQInteger top = sq_gettop(vm); + + if (top == 1) + { + sq_pushinteger(vm, machine->memory.ram.input.gamepads.data); + } + else if (top == 2) + { + u32 index = getSquirrelNumber(vm, 2) & 0x1f; + bool pressed = (machine->memory.ram.input.gamepads.data & (1 << index)) != 0; + sq_pushbool(vm, pressed ? SQTrue : SQFalse); + } + else + { + return sq_throwerror(vm, "invalid params, btn [ id ]\n"); + } + + return 1; +} + +static SQInteger squirrel_spr(HSQUIRRELVM vm) +{ + SQInteger top = sq_gettop(vm); + + s32 index = 0; + s32 x = 0; + s32 y = 0; + s32 w = 1; + s32 h = 1; + s32 scale = 1; + tic_flip flip = tic_no_flip; + tic_rotate rotate = tic_no_rotate; + static u8 colors[TIC_PALETTE_SIZE]; + s32 count = 0; + + if(top >= 2) + { + index = getSquirrelNumber(vm, 2); + + if(top >= 4) + { + x = getSquirrelNumber(vm, 3); + y = getSquirrelNumber(vm, 4); + + if(top >= 5) + { + if(OT_ARRAY == sq_gettype(vm, 5)) + { + for(s32 i = 0; i < TIC_PALETTE_SIZE; i++) + { + sq_pushinteger(vm, (SQInteger)i); + sq_rawget(vm, 5); + if(sq_gettype(vm, -1) & (OT_FLOAT|OT_INTEGER)) + { + colors[i-1] = getSquirrelNumber(vm, -1); + count++; + sq_poptop(vm); + } + else + { + sq_poptop(vm); + break; + } + } + } + else + { + colors[0] = getSquirrelNumber(vm, 5); + count = 1; + } + + if(top >= 6) + { + scale = getSquirrelNumber(vm, 6); + + if(top >= 7) + { + flip = getSquirrelNumber(vm, 7); + + if(top >= 8) + { + rotate = getSquirrelNumber(vm, 8); + + if(top >= 10) + { + w = getSquirrelNumber(vm, 9); + h = getSquirrelNumber(vm, 10); + } + } + } + } + } + } + } + + tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); + + memory->api.sprite_ex(memory, &memory->ram.tiles, index, x, y, w, h, colors, count, scale, flip, rotate); + + return 0; +} + +static SQInteger squirrel_mget(HSQUIRRELVM vm) +{ + SQInteger top = sq_gettop(vm); + + if(top == 3) + { + s32 x = getSquirrelNumber(vm, 2); + s32 y = getSquirrelNumber(vm, 3); + + tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); + + u8 value = memory->api.map_get(memory, &memory->ram.map, x, y); + sq_pushinteger(vm, value); + return 1; + } + else return sq_throwerror(vm, "invalid params, mget(x,y)\n"); + + return 0; +} + +static SQInteger squirrel_mset(HSQUIRRELVM vm) +{ + SQInteger top = sq_gettop(vm); + + if(top == 4) + { + s32 x = getSquirrelNumber(vm, 2); + s32 y = getSquirrelNumber(vm, 3); + u8 val = getSquirrelNumber(vm, 4); + + tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); + + memory->api.map_set(memory, &memory->ram.map, x, y, val); + } + else return sq_throwerror(vm, "invalid params, mget(x,y)\n"); + + return 0; +} + +typedef struct +{ + HSQUIRRELVM vm; + HSQOBJECT reg; +} RemapData; + +static void remapCallback(void* data, s32 x, s32 y, RemapResult* result) +{ + RemapData* remap = (RemapData*)data; + HSQUIRRELVM vm = remap->vm; + + SQInteger top = sq_gettop(vm); + + sq_pushobject(vm, remap->reg); + sq_pushroottable(vm); + sq_pushinteger(vm, result->index); + sq_pushinteger(vm, x); + sq_pushinteger(vm, y); + //lua_pcall(lua, 3, 3, 0); + + if (SQ_SUCCEEDED(sq_call(vm, 4, SQTrue, SQTrue))) + { + sq_pushinteger(vm, 0); + if (SQ_SUCCEEDED(sq_get(vm, -2))) + { + result->index = getSquirrelNumber(vm, -1); + sq_poptop(vm); + sq_pushinteger(vm, 1); + if (SQ_SUCCEEDED(sq_get(vm, -2))) + { + result->flip = getSquirrelNumber(vm, -1); + sq_poptop(vm); + sq_pushinteger(vm, 2); + if (SQ_SUCCEEDED(sq_get(vm, -2))) + { + result->rotate = getSquirrelNumber(vm, -1); + sq_poptop(vm); + } + } + } + } + + sq_settop(vm, top); +} + +static SQInteger squirrel_map(HSQUIRRELVM vm) +{ + s32 x = 0; + s32 y = 0; + s32 w = TIC_MAP_SCREEN_WIDTH; + s32 h = TIC_MAP_SCREEN_HEIGHT; + s32 sx = 0; + s32 sy = 0; + u8 chromakey = -1; + s32 scale = 1; + + SQInteger top = sq_gettop(vm); + + if(top >= 3) + { + x = getSquirrelNumber(vm, 2); + y = getSquirrelNumber(vm, 3); + + if(top >= 5) + { + w = getSquirrelNumber(vm, 4); + h = getSquirrelNumber(vm, 5); + + if(top >= 7) + { + sx = getSquirrelNumber(vm, 6); + sy = getSquirrelNumber(vm, 7); + + if(top >= 8) + { + chromakey = getSquirrelNumber(vm, 8); + + if(top >= 9) + { + scale = getSquirrelNumber(vm, 9); + + if(top >= 10) + { + SQObjectType type = sq_gettype(vm, 10); + if (type & (OT_CLOSURE|OT_NATIVECLOSURE|OT_INSTANCE)) + { + RemapData data = {vm}; + sq_resetobject(&data.reg); + sq_getstackobj(vm, 10, &data.reg); + sq_addref(vm, &data.reg); + + tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); + + memory->api.remap(memory, &memory->ram.map, &memory->ram.tiles, x, y, w, h, sx, sy, chromakey, scale, remapCallback, &data); + + //luaL_unref(lua, LUA_REGISTRYINDEX, data.reg); + sq_release(vm, &data.reg); + + return 0; + } + } + } + } + } + } + } + + tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); + + memory->api.map((tic_mem*)getSquirrelMachine(vm), &memory->ram.map, &memory->ram.tiles, x, y, w, h, sx, sy, chromakey, scale); + + return 0; +} + +static SQInteger squirrel_music(HSQUIRRELVM vm) +{ + SQInteger top = sq_gettop(vm); + tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); + + if(top == 1) memory->api.music(memory, -1, 0, 0, false); + else if(top >= 2) + { + memory->api.music(memory, -1, 0, 0, false); + + s32 track = getSquirrelNumber(vm, 2); + s32 frame = -1; + s32 row = -1; + bool loop = true; + + if(top >= 3) + { + frame = getSquirrelNumber(vm, 3); + + if(top >= 4) + { + row = getSquirrelNumber(vm, 4); + + if(top >= 5) + { + SQBool b = SQFalse; + sq_getbool(vm, 5, &b); + loop = (b != SQFalse); + } + } + } + + memory->api.music(memory, track, frame, row, loop); + } + else return sq_throwerror(vm, "invalid params, use music(track)\n"); + + return 0; +} + +static SQInteger squirrel_sfx(HSQUIRRELVM vm) +{ + SQInteger top = sq_gettop(vm); + + if(top >= 2) + { + tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); + + s32 note = -1; + s32 octave = -1; + s32 duration = -1; + s32 channel = 0; + s32 volume = MAX_VOLUME; + s32 speed = SFX_DEF_SPEED; + + s32 index = getSquirrelNumber(vm, 2); + + if(index < SFX_COUNT) + { + if (index >= 0) + { + tic_sample* effect = memory->ram.sfx.samples.data + index; + + note = effect->note; + octave = effect->octave; + speed = effect->speed; + } + + if(top >= 3) + { + if(sq_gettype(vm, 3) & (OT_INTEGER|OT_FLOAT)) + { + s32 id = getSquirrelNumber(vm, 3); + note = id % NOTES; + octave = id / NOTES; + } + else if(sq_gettype(vm, 3) == OT_STRING) + { + const SQChar* str; + sq_getstring(vm, 3, &str); + const char* noteStr = (const char*)str; + + if(!tic_tool_parse_note(noteStr, ¬e, &octave)) + { + return sq_throwerror(vm, "invalid note, should be like C#4\n"); + } + } + + if(top >= 4) + { + duration = getSquirrelNumber(vm, 4); + + if(top >= 5) + { + channel = getSquirrelNumber(vm, 5); + + if(top >= 6) + { + volume = getSquirrelNumber(vm, 6); + + if(top >= 7) + { + speed = getSquirrelNumber(vm, 7); + } + } + } + } + } + + if (channel >= 0 && channel < TIC_SOUND_CHANNELS) + { + memory->api.sfx_stop(memory, channel); + memory->api.sfx_ex(memory, index, note, octave, duration, channel, volume & 0xf, speed); + } + else return sq_throwerror(vm, "unknown channel\n"); + } + else return sq_throwerror(vm, "unknown sfx index\n"); + } + else return sq_throwerror(vm, "invalid sfx params\n"); + + return 0; +} + +static SQInteger squirrel_sync(HSQUIRRELVM vm) +{ + tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); + + bool toCart = false; + u32 mask = 0; + s32 bank = 0; + + if(sq_gettop(vm) >= 2) + { + mask = getSquirrelNumber(vm, 2); + + if(sq_gettop(vm) >= 3) + { + bank = getSquirrelNumber(vm, 3); + + if(sq_gettop(vm) >= 4) + { + SQBool b = SQFalse; + sq_getbool(vm, 4, &b); + toCart = (b != SQFalse); + } + } + } + + if(bank >= 0 && bank < TIC_BANKS) + memory->api.sync(memory, mask, bank, toCart); + else + return sq_throwerror(vm, "sync() error, invalid bank"); + + return 0; +} + +static SQInteger squirrel_reset(HSQUIRRELVM vm) +{ + tic_machine* machine = getSquirrelMachine(vm); + + machine->state.initialized = false; + + return 0; +} + +static SQInteger squirrel_key(HSQUIRRELVM vm) +{ + tic_machine* machine = getSquirrelMachine(vm); + tic_mem* tic = &machine->memory; + + SQInteger top = sq_gettop(vm); + + if (top == 1) + { + sq_pushbool(vm, tic->api.key(tic, tic_key_unknown) ? SQTrue : SQFalse); + } + else if (top == 2) + { + tic_key key = getSquirrelNumber(vm, 2); + + if(key < tic_key_escape) + sq_pushbool(vm, tic->api.key(tic, key) ? SQTrue : SQFalse); + else + { + return sq_throwerror(vm, "unknown keyboard code\n"); + } + } + else + { + return sq_throwerror(vm, "invalid params, key [code]\n"); + } + + return 1; +} + +static SQInteger squirrel_keyp(HSQUIRRELVM vm) +{ + tic_machine* machine = getSquirrelMachine(vm); + tic_mem* tic = &machine->memory; + + SQInteger top = sq_gettop(vm); + + if (top == 1) + { + sq_pushbool(vm, tic->api.keyp(tic, tic_key_unknown, -1, -1) ? SQTrue : SQFalse); + } + else + { + tic_key key = getSquirrelNumber(vm, 2); + + if(key >= tic_key_escape) + { + return sq_throwerror(vm, "unknown keyboard code\n"); + } + else + { + if(top == 2) + { + sq_pushbool(vm, tic->api.keyp(tic, key, -1, -1) ? SQTrue : SQFalse); + } + else if(top == 4) + { + u32 hold = getSquirrelNumber(vm, 3); + u32 period = getSquirrelNumber(vm, 4); + + sq_pushbool(vm, tic->api.keyp(tic, key, hold, period) ? SQTrue : SQFalse); + } + else + { + return sq_throwerror(vm, "invalid params, keyp [ code [ hold period ] ]\n"); + } + } + } + + return 1; +} + +static SQInteger squirrel_memcpy(HSQUIRRELVM vm) +{ + SQInteger top = sq_gettop(vm); + + if(top == 4) + { + s32 dest = getSquirrelNumber(vm, 2); + s32 src = getSquirrelNumber(vm, 3); + s32 size = getSquirrelNumber(vm, 4); + s32 bound = sizeof(tic_ram) - size; + + if(size >= 0 && size <= sizeof(tic_ram) && dest >= 0 && src >= 0 && dest <= bound && src <= bound) + { + u8* base = (u8*)&getSquirrelMachine(vm)->memory; + memcpy(base + dest, base + src, size); + return 0; + } + } + + return sq_throwerror(vm, "invalid params, memcpy(dest,src,size)\n"); +} + +static SQInteger squirrel_memset(HSQUIRRELVM vm) +{ + SQInteger top = sq_gettop(vm); + + if(top == 4) + { + s32 dest = getSquirrelNumber(vm, 2); + u8 value = getSquirrelNumber(vm, 3); + s32 size = getSquirrelNumber(vm, 4); + s32 bound = sizeof(tic_ram) - size; + + if(size >= 0 && size <= sizeof(tic_ram) && dest >= 0 && dest <= bound) + { + u8* base = (u8*)&getSquirrelMachine(vm)->memory; + memset(base + dest, value, size); + return 0; + } + } + + return sq_throwerror(vm, "invalid params, memset(dest,val,size)\n"); +} + +// NB we leave the string on the stack so that the char* pointer remains valid. +static const char* printString(HSQUIRRELVM vm, s32 index) +{ + const SQChar* text = ""; + if (SQ_SUCCEEDED(sq_tostring(vm, index))) + { + sq_getstring(vm, -1, &text); + } + + return (const char*)text; +} + +static SQInteger squirrel_font(HSQUIRRELVM vm) +{ + tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); + SQInteger top = sq_gettop(vm); + + if(top >= 2) + { + const char* text = printString(vm, 2); + s32 x = 0; + s32 y = 0; + s32 width = TIC_SPRITESIZE; + s32 height = TIC_SPRITESIZE; + u8 chromakey = 0; + bool fixed = false; + s32 scale = 1; + + if(top >= 4) + { + x = getSquirrelNumber(vm, 3); + y = getSquirrelNumber(vm, 4); + + if(top >= 5) + { + chromakey = getSquirrelNumber(vm, 5); + + if(top >= 7) + { + width = getSquirrelNumber(vm, 6); + height = getSquirrelNumber(vm, 7); + + if(top >= 8) + { + SQBool b = SQFalse; + sq_getbool(vm, 8, &b); + fixed = (b != SQFalse); + + if(top >= 9) + { + scale = getSquirrelNumber(vm, 9); + } + } + } + } + } + + if(scale == 0) + { + sq_pushinteger(vm, 0); + return 1; + } + + s32 size = drawText(memory, text, x, y, width, height, chromakey, scale, fixed ? drawSpriteFont : drawFixedSpriteFont); + + sq_pushinteger(vm, size); + return 1; + } + + return 0; +} + +static SQInteger squirrel_print(HSQUIRRELVM vm) +{ + SQInteger top = sq_gettop(vm); + + if(top >= 2) + { + tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); + + s32 x = 0; + s32 y = 0; + s32 color = TIC_PALETTE_SIZE-1; + bool fixed = false; + s32 scale = 1; + + const char* text = printString(vm, 2); + + if(top >= 4) + { + x = getSquirrelNumber(vm, 3); + y = getSquirrelNumber(vm, 4); + + if(top >= 5) + { + color = getSquirrelNumber(vm, 5) % TIC_PALETTE_SIZE; + + if(top >= 6) + { + SQBool b = SQFalse; + sq_getbool(vm, 5, &b); + fixed = (b != SQFalse); + + if(top >= 7) + { + scale = getSquirrelNumber(vm, 7); + } + } + } + } + + if(scale == 0) + { + sq_pushinteger(vm, 0); + return 1; + } + + s32 size = memory->api.text_ex(memory, text ? text : "nil", x, y, color, fixed, scale); + + sq_pushinteger(vm, size); + + return 1; + } + + return 0; +} + +static SQInteger squirrel_trace(HSQUIRRELVM vm) +{ + SQInteger top = sq_gettop(vm); + tic_machine* machine = getSquirrelMachine(vm); + + if(top >= 2) + { + const char* text = printString(vm, 2); + u8 color = tic_color_white; + + if(top >= 3) + { + color = getSquirrelNumber(vm, 3); + } + + machine->data->trace(machine->data->data, text ? text : "nil", color); + } + + return 0; +} + +static SQInteger squirrel_pmem(HSQUIRRELVM vm) +{ + SQInteger top = sq_gettop(vm); + tic_machine* machine = getSquirrelMachine(vm); + tic_mem* memory = &machine->memory; + + if(top >= 2) + { + u32 index = getSquirrelNumber(vm, 2); + + if(index < TIC_PERSISTENT_SIZE) + { + u32 val = memory->persistent.data[index]; + + if(top >= 3) + { + SQInteger i = 0; + sq_getinteger(vm, 3, &i); + memory->persistent.data[index] = (u32)i; + machine->data->syncPMEM = true; + } + + sq_pushinteger(vm, val); + + return 1; + } + return sq_throwerror(vm, "invalid persistent memory index\n"); + } + else return sq_throwerror(vm, "invalid params, pmem(index [val]) -> val\n"); + + return 0; +} + +static SQInteger squirrel_time(HSQUIRRELVM vm) +{ + tic_mem* memory = (tic_mem*)getSquirrelMachine(vm); + + sq_pushfloat(vm, (SQFloat)(memory->api.time(memory))); + + return 1; +} + +static SQInteger squirrel_exit(HSQUIRRELVM vm) +{ + tic_machine* machine = getSquirrelMachine(vm); + + machine->data->exit(machine->data->data); + + return 0; +} + +static SQInteger squirrel_mouse(HSQUIRRELVM vm) +{ + tic_machine* machine = getSquirrelMachine(vm); + + const tic80_mouse* mouse = &machine->memory.ram.input.mouse; + + sq_newarray(vm, 5); + sq_pushinteger(vm, mouse->x); + sq_arrayappend(vm, -2); + sq_pushinteger(vm, mouse->y); + sq_arrayappend(vm, -2); + sq_pushbool(vm, mouse->left ? SQTrue : SQFalse); + sq_arrayappend(vm, -2); + sq_pushbool(vm, mouse->middle ? SQTrue : SQFalse); + sq_arrayappend(vm, -2); + sq_pushbool(vm, mouse->right ? SQTrue : SQFalse); + sq_arrayappend(vm, -2); + + return 1; +} + +static SQInteger squirrel_dofile(HSQUIRRELVM vm) +{ + return sq_throwerror(vm, "unknown method: \"dofile\"\n"); +} + +static SQInteger squirrel_loadfile(HSQUIRRELVM vm) +{ + return sq_throwerror(vm, "unknown method: \"loadfile\"\n"); +} + +static void squirrel_open_builtins(HSQUIRRELVM vm) +{ + sq_pushroottable(vm); + sqstd_register_mathlib(vm); + sqstd_register_stringlib(vm); + sqstd_register_bloblib(vm); + sq_poptop(vm); +} + +static const char* const ApiKeywords[] = API_KEYWORDS; +static const SQFUNCTION ApiFunc[] = +{ + NULL, NULL, NULL, squirrel_print, squirrel_cls, squirrel_pix, squirrel_line, squirrel_rect, + squirrel_rectb, squirrel_spr, squirrel_btn, squirrel_btnp, squirrel_sfx, squirrel_map, squirrel_mget, + squirrel_mset, squirrel_peek, squirrel_poke, squirrel_peek4, squirrel_poke4, squirrel_memcpy, + squirrel_memset, squirrel_trace, squirrel_pmem, squirrel_time, squirrel_exit, squirrel_font, squirrel_mouse, + squirrel_circ, squirrel_circb, squirrel_tri, squirrel_textri, squirrel_clip, squirrel_music, squirrel_sync, squirrel_reset, + squirrel_key, squirrel_keyp +}; + +STATIC_ASSERT(api_func, COUNT_OF(ApiKeywords) == COUNT_OF(ApiFunc)); + +static void checkForceExit(HSQUIRRELVM vm, SQInteger type, const SQChar* sourceName, SQInteger line, const SQChar* functionName) +{ + tic_machine* machine = getSquirrelMachine(vm); + tic_tick_data* tick = machine->data; + + if(tick->forceExit && tick->forceExit(tick->data)) + sq_throwerror(vm, "script execution was interrupted"); +} + +static void initAPI(tic_machine* machine) +{ + //lua_pushlightuserdata(machine->lua, machine); + //lua_setglobal(machine->lua, TicMachine); + HSQUIRRELVM vm = machine->squirrel; + + sq_setcompilererrorhandler(vm, squirrel_compilerError); + + sq_pushregistrytable(vm); + sq_pushstring(vm, TicMachine, -1); + sq_pushuserpointer(machine->squirrel, machine); + sq_newslot(vm, -3, SQTrue); + sq_poptop(vm); + +#if USE_FOREIGN_POINTER + sq_setforeignptr(vm, machine); +#endif + + for (s32 i = 0; i < COUNT_OF(ApiFunc); i++) + if (ApiFunc[i]) + registerSquirrelFunction(machine, ApiFunc[i], ApiKeywords[i]); + + registerSquirrelFunction(machine, squirrel_dofile, "dofile"); + registerSquirrelFunction(machine, squirrel_loadfile, "loadfile"); + +#if CHECK_FORCE_EXIT + sq_setnativedebughook(vm, checkForceExit); +#endif + sq_enabledebuginfo(vm, SQTrue); + +} + +static void closeSquirrel(tic_mem* tic) +{ + tic_machine* machine = (tic_machine*)tic; + + if(machine->squirrel) + { + sq_close(machine->squirrel); + machine->squirrel = NULL; + } +} + +static bool initSquirrel(tic_mem* tic, const char* code) +{ + tic_machine* machine = (tic_machine*)tic; + + closeSquirrel(tic); + + HSQUIRRELVM vm = machine->squirrel = sq_open(100); + squirrel_open_builtins(vm); + + sq_newclosure(vm, squirrel_errorHandler, 0); + sq_seterrorhandler(vm); + + initAPI(machine); + + { + HSQUIRRELVM vm = machine->squirrel; + + sq_settop(vm, 0); + + if((SQ_FAILED(sq_compilebuffer(vm, code, strlen(code), "squirrel", SQTrue))) || + (sq_pushroottable(vm), false) || + (SQ_FAILED(sq_call(vm, 1, SQFalse, SQTrue)))) + { + sq_getlasterror(vm); + sq_tostring(vm, -1); + const SQChar* errorString = "unknown error"; + sq_getstring(vm, -1, &errorString); + + machine->data->error(machine->data->data, errorString); + + sq_pop(vm, 2); // error and error string + + return false; + } + } + + return true; +} + +static void callSquirrelTick(tic_mem* tic) +{ + tic_machine* machine = (tic_machine*)tic; + + const char* TicFunc = ApiKeywords[0]; + + HSQUIRRELVM vm = machine->squirrel; + + if(vm) + { + //lua_getglobal(lua, TicFunc); + sq_pushroottable(vm); + sq_pushstring(vm, TicFunc, -1); + + if (SQ_SUCCEEDED(sq_get(vm, -2))) + { + sq_pushroottable(vm); + if(SQ_FAILED(sq_call(vm, 1, SQFalse, SQTrue))) + { + sq_getlasterror(vm); + sq_tostring(vm, -1); + const SQChar* errorString = "unknown error"; + sq_getstring(vm, -1, &errorString); + + machine->data->error(machine->data->data, errorString); + sq_pop(vm, 3); // remove string, error and root table. + } + } + else + { + sq_pop(vm, 1); + machine->data->error(machine->data->data, "'function TIC()...' isn't found :("); + } + } +} + +static void callSquirrelScanlineName(tic_mem* memory, s32 row, void* data, const char* name) +{ + tic_machine* machine = (tic_machine*)memory; + HSQUIRRELVM vm = machine->squirrel; + + if (vm) + { + sq_pushroottable(vm); + sq_pushstring(vm, name, -1); + if (SQ_SUCCEEDED(sq_get(vm, -2))) + { + sq_pushroottable(vm); + sq_pushinteger(vm, row); + + if(SQ_FAILED(sq_call(vm, 2, SQFalse, SQTrue))) + { + sq_getlasterror(vm); + sq_tostring(vm, -1); + + const SQChar* errorString = "unknown error"; + sq_getstring(vm, -1, &errorString); + machine->data->error(machine->data->data, errorString); + sq_pop(vm, 3); // error string, error and root table + } + } + else sq_poptop(vm); + } +} + +static void callSquirrelScanline(tic_mem* memory, s32 row, void* data) +{ + callSquirrelScanlineName(memory, row, data, ApiKeywords[1]); + + // try to call old scanline + callSquirrelScanlineName(memory, row, data, "scanline"); +} + +static void callSquirrelOverline(tic_mem* memory, void* data) +{ + tic_machine* machine = (tic_machine*)memory; + HSQUIRRELVM vm = machine->squirrel; + + if (vm) + { + const char* OvrFunc = ApiKeywords[2]; + + sq_pushroottable(vm); + sq_pushstring(vm, OvrFunc, -1); + + if(SQ_SUCCEEDED(sq_get(vm, -2))) + { + sq_pushroottable(vm); + if(SQ_FAILED(sq_call(vm, 1, SQFalse, SQTrue))) + { + sq_getlasterror(vm); + sq_tostring(vm, -1); + const SQChar* errorString = "unknown error"; + sq_getstring(vm, -1, &errorString); + machine->data->error(machine->data->data, errorString); + sq_pop(vm, 3); + } + } + else sq_poptop(vm); + } + +} + +static const char* const SquirrelKeywords [] = +{ "base", "break", "case", "catch", "class", "clone", "continue", "const", "default", "delete", "else", "enum", "extends", "for", "foreach", "function", "if", "in", "local", "null", "resume", "return", "switch", "this", "throw", "try", "typeof", "while", "yield", "constructor", - "instanceof", "true", "false", "static", "__LINE__", "__FILE__" -}; - -static inline bool isalnum_(char c) {return isalnum(c) || c == '_';} - -static const tic_outline_item* getSquirrelOutline(const char* code, s32* size) -{ - enum{Size = sizeof(tic_outline_item)}; - - *size = 0; - - static tic_outline_item* items = NULL; - - if(items) - { - free(items); - items = NULL; - } - - const char* ptr = code; - - while(true) - { - static const char FuncString[] = "function "; - - ptr = strstr(ptr, FuncString); - - if(ptr) - { - ptr += sizeof FuncString - 1; - - const char* start = ptr; - const char* end = start; - - while(*ptr) - { - char c = *ptr; - - if(isalnum_(c) || c == ':'); - else if(c == '(') - { - end = ptr; - break; - } - else break; - - ptr++; - } - - if(end > start) - { - items = items ? realloc(items, (*size + 1) * Size) : malloc(Size); - - items[*size].pos = (s32)(start - code); - items[*size].size = (s32)(end - start); - - (*size)++; - } - } - else break; - } - - return items; -} - -void evalSquirrel(tic_mem* tic, const char* code) { - tic_machine* machine = (tic_machine*)tic; - HSQUIRRELVM vm = machine->squirrel; - - sq_settop(vm, 0); - - if((SQ_FAILED(sq_compilebuffer(vm, code, strlen(code), "squirrel", SQFalse))) || - (sq_pushroottable(vm), false) || - (SQ_FAILED(sq_call(vm, 1, SQFalse, SQTrue)))) - { - sq_getlasterror(vm); - sq_tostring(vm, -1); - const SQChar* errorString = "unknown error"; - sq_getstring(vm, -1, &errorString); - machine->data->error(machine->data->data, errorString); - sq_pop(vm, 3); - } -} - -static const tic_script_config SquirrelSyntaxConfig = -{ - .init = initSquirrel, - .close = closeSquirrel, - .tick = callSquirrelTick, - .scanline = callSquirrelScanline, - .overline = callSquirrelOverline, - - .getOutline = getSquirrelOutline, - .parse = parseCode, - .eval = evalSquirrel, - - .blockCommentStart = "/*", - .blockCommentEnd = "*/", - .singleComment = "//", - .blockStringStart = "@\"", - .blockStringEnd = "\"", - - .keywords = SquirrelKeywords, - .keywordsCount = COUNT_OF(SquirrelKeywords), - - .api = ApiKeywords, - .apiCount = COUNT_OF(ApiKeywords), -}; - -const tic_script_config* getSquirrelScriptConfig() -{ - return &SquirrelSyntaxConfig; -} - -#endif /* defined(TIC_BUILD_WITH_SQUIRREL) */ + "instanceof", "true", "false", "static", "__LINE__", "__FILE__" +}; + +static inline bool isalnum_(char c) {return isalnum(c) || c == '_';} + +static const tic_outline_item* getSquirrelOutline(const char* code, s32* size) +{ + enum{Size = sizeof(tic_outline_item)}; + + *size = 0; + + static tic_outline_item* items = NULL; + + if(items) + { + free(items); + items = NULL; + } + + const char* ptr = code; + + while(true) + { + static const char FuncString[] = "function "; + + ptr = strstr(ptr, FuncString); + + if(ptr) + { + ptr += sizeof FuncString - 1; + + const char* start = ptr; + const char* end = start; + + while(*ptr) + { + char c = *ptr; + + if(isalnum_(c) || c == ':'); + else if(c == '(') + { + end = ptr; + break; + } + else break; + + ptr++; + } + + if(end > start) + { + items = items ? realloc(items, (*size + 1) * Size) : malloc(Size); + + items[*size].pos = (s32)(start - code); + items[*size].size = (s32)(end - start); + + (*size)++; + } + } + else break; + } + + return items; +} + +void evalSquirrel(tic_mem* tic, const char* code) { + tic_machine* machine = (tic_machine*)tic; + HSQUIRRELVM vm = machine->squirrel; + + sq_settop(vm, 0); + + if((SQ_FAILED(sq_compilebuffer(vm, code, strlen(code), "squirrel", SQTrue))) || + (sq_pushroottable(vm), false) || + (SQ_FAILED(sq_call(vm, 1, SQFalse, SQTrue)))) + { + sq_getlasterror(vm); + sq_tostring(vm, -1); + const SQChar* errorString = "unknown error"; + sq_getstring(vm, -1, &errorString); + machine->data->error(machine->data->data, errorString); + sq_pop(vm, 3); + } +} + +static const tic_script_config SquirrelSyntaxConfig = +{ + .init = initSquirrel, + .close = closeSquirrel, + .tick = callSquirrelTick, + .scanline = callSquirrelScanline, + .overline = callSquirrelOverline, + + .getOutline = getSquirrelOutline, + .parse = parseCode, + .eval = evalSquirrel, + + .blockCommentStart = "/*", + .blockCommentEnd = "*/", + .singleComment = "//", + .blockStringStart = "@\"", + .blockStringEnd = "\"", + + .keywords = SquirrelKeywords, + .keywordsCount = COUNT_OF(SquirrelKeywords), + + .api = ApiKeywords, + .apiCount = COUNT_OF(ApiKeywords), +}; + +const tic_script_config* getSquirrelScriptConfig() +{ + return &SquirrelSyntaxConfig; +} + +#endif /* defined(TIC_BUILD_WITH_SQUIRREL) */