diff --git a/CMakeLists.txt b/CMakeLists.txt index 7daf625..12e4f2b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.9) -project(TIC-80 C) +project(TIC-80 C CXX) message("Building for target : ${CMAKE_SYSTEM_NAME}") if(UNIX AND NOT APPLE AND NOT EMSCRIPTEN AND NOT ANDROID) @@ -133,6 +133,40 @@ target_include_directories(wren PRIVATE 3rd-party/wren-0.1.0/src/include) target_include_directories(wren PRIVATE 3rd-party/wren-0.1.0/src/optional) target_include_directories(wren PRIVATE 3rd-party/wren-0.1.0/src/vm) +################################ +# SQUIRREL +################################ + +set(SQUIRREL_DIR 3rd-party/squirrel3.1) +set(SQUIRREL_SRC + ${SQUIRREL_DIR}/squirrel/sqapi.cpp + ${SQUIRREL_DIR}/squirrel/sqbaselib.cpp + ${SQUIRREL_DIR}/squirrel/sqclass.cpp + ${SQUIRREL_DIR}/squirrel/sqcompiler.cpp + ${SQUIRREL_DIR}/squirrel/sqdebug.cpp + ${SQUIRREL_DIR}/squirrel/sqfuncstate.cpp + ${SQUIRREL_DIR}/squirrel/sqlexer.cpp + ${SQUIRREL_DIR}/squirrel/sqmem.cpp + ${SQUIRREL_DIR}/squirrel/sqobject.cpp + ${SQUIRREL_DIR}/squirrel/sqstate.cpp + ${SQUIRREL_DIR}/squirrel/sqtable.cpp + ${SQUIRREL_DIR}/squirrel/sqvm.cpp + ${SQUIRREL_DIR}/sqstdlib/sqstdaux.cpp + ${SQUIRREL_DIR}/sqstdlib/sqstdblob.cpp + ${SQUIRREL_DIR}/sqstdlib/sqstdio.cpp + ${SQUIRREL_DIR}/sqstdlib/sqstdmath.cpp + ${SQUIRREL_DIR}/sqstdlib/sqstdrex.cpp + ${SQUIRREL_DIR}/sqstdlib/sqstdstream.cpp + ${SQUIRREL_DIR}/sqstdlib/sqstdstring.cpp + ${SQUIRREL_DIR}/sqstdlib/sqstdsystem.cpp +) + +add_library(squirrel STATIC ${SQUIRREL_SRC}) +set_target_properties(squirrel PROPERTIES LINKER_LANGUAGE CXX) +target_include_directories(squirrel PRIVATE 3rd-party/squirrel3.1/include) +target_include_directories(squirrel PRIVATE 3rd-party/squirrel3.1/squirrel) +target_include_directories(squirrel PRIVATE 3rd-party/squirrel3.1/sqstdlib) + ################################ # GIFLIB ################################ @@ -162,6 +196,7 @@ set(TIC80CORE_SRC ${TIC80CORE_DIR}/jsapi.c ${TIC80CORE_DIR}/luaapi.c ${TIC80CORE_DIR}/wrenapi.c + ${TIC80CORE_DIR}/squirrelapi.c ${TIC80CORE_DIR}/ext/gif.c 3rd-party/blip-buf/blip_buf.c # TODO: link it as lib? 3rd-party/duktape-2.2.0/src/duktape.c # TODO: link it as lib? @@ -175,11 +210,12 @@ target_include_directories(tic80core PRIVATE 3rd-party/duktape-2.2.0/src) target_include_directories(tic80core PRIVATE 3rd-party/lua-5.3.1/src) target_include_directories(tic80core PRIVATE 3rd-party/giflib-5.1.4/lib) target_include_directories(tic80core PRIVATE 3rd-party/wren-0.1.0/src/include) +target_include_directories(tic80core PRIVATE 3rd-party/squirrel3.1/include) target_include_directories(tic80core PRIVATE 3rd-party/moonscript) target_include_directories(tic80core PRIVATE 3rd-party/fennel) -add_dependencies(tic80core lua lpeg wren giflib) -target_link_libraries(tic80core lua lpeg wren giflib) +add_dependencies(tic80core lua lpeg wren squirrel giflib) +target_link_libraries(tic80core lua lpeg wren squirrel giflib) ################################ # SDL2 diff --git a/bin/assets/squirreldemo.tic.dat b/bin/assets/squirreldemo.tic.dat new file mode 100644 index 0000000..378e04c --- /dev/null +++ b/bin/assets/squirreldemo.tic.dat @@ -0,0 +1 @@ +0x78, 0xda, 0x63, 0x5c, 0xc0, 0xc4, 0x40, 0x08, 0xfc, 0xfb, 0x0f, 0x04, 0x4a, 0x4a, 0x4a, 0xfd, 0x1d, 0x1d, 0x1d, 0xfd, 0x20, 0x1a, 0xc8, 0xed, 0xff, 0xff, 0x01, 0x8e, 0xff, 0xff, 0x7f, 0xff, 0x4e, 0x49, 0xe9, 0xff, 0xbb, 0x8e, 0x8e, 0x0f, 0xef, 0x94, 0x9a, 0x3e, 0xbc, 0xfb, 0xdf, 0xff, 0xe1, 0xff, 0x87, 0xfe, 0x0f, 0x1f, 0x60, 0x18, 0xbb, 0xfe, 0xff, 0x78, 0xf5, 0xff, 0x47, 0xd2, 0xcf, 0x30, 0x0a, 0x06, 0x14, 0x80, 0xe3, 0x0a, 0x14, 0x77, 0x60, 0xf8, 0x03, 0xcc, 0xd6, 0x07, 0xc6, 0xe3, 0x7f, 0x86, 0x0f, 0xff, 0x81, 0x71, 0xfb, 0xef, 0x7f, 0x3f, 0xc3, 0x7f, 0x60, 0xdc, 0xbd, 0xef, 0x07, 0xc6, 0x5f, 0x07, 0x14, 0x83, 0xe2, 0x93, 0xe1, 0xc3, 0xfb, 0x77, 0xff, 0xff, 0xbf, 0x7b, 0x47, 0xa9, 0x7e, 0x56, 0x1d, 0x46, 0x06, 0x7d, 0x7d, 0x85, 0x92, 0xcc, 0x92, 0x9c, 0x54, 0x2b, 0x05, 0x85, 0xf4, 0xc4, 0xdc, 0x54, 0x08, 0x87, 0x0b, 0x28, 0x9a, 0x58, 0x5a, 0x92, 0x91, 0x5f, 0x64, 0x05, 0x11, 0x4d, 0x49, 0x2d, 0x4b, 0xcd, 0xc9, 0x2f, 0x48, 0x2d, 0x02, 0xc9, 0xa4, 0xa4, 0x16, 0x27, 0x03, 0x95, 0x2b, 0x14, 0x03, 0xe5, 0x4b, 0xc0, 0xbc, 0xa2, 0xcc, 0x82, 0x92, 0xcc, 0xfc, 0x3c, 0x90, 0x24, 0x84, 0x63, 0xa5, 0x50, 0x5c, 0x58, 0x9a, 0x59, 0x54, 0x94, 0x9a, 0xc3, 0xc5, 0x55, 0x62, 0xa3, 0x6b, 0xc0, 0x55, 0x61, 0xa3, 0x6b, 0x69, 0xc6, 0x55, 0x69, 0xa3, 0x6b, 0x64, 0xc2, 0xc5, 0x95, 0x56, 0x9a, 0x97, 0x0c, 0x52, 0xaf, 0x10, 0xe2, 0xe9, 0xac, 0xa1, 0xc9, 0x55, 0xcd, 0xc5, 0x99, 0x99, 0xa6, 0xa0, 0x91, 0x54, 0x92, 0xa7, 0x61, 0xa0, 0xa9, 0xa9, 0x50, 0x69, 0x5b, 0xa9, 0x6b, 0x68, 0x8d, 0x10, 0x33, 0x84, 0x88, 0x69, 0x23, 0x8b, 0x19, 0x01, 0xc5, 0x2a, 0x6c, 0x2b, 0x50, 0xd4, 0x19, 0x43, 0xc4, 0x40, 0xea, 0xb8, 0x38, 0x93, 0x73, 0x8a, 0x35, 0x0c, 0x8d, 0x35, 0xb9, 0x38, 0x8b, 0x0b, 0x8a, 0x34, 0x0c, 0xb5, 0x35, 0x34, 0x4a, 0x54, 0xcd, 0x0c, 0x34, 0xf5, 0x8d, 0x0d, 0x34, 0xb5, 0x8c, 0x74, 0x2a, 0x74, 0x2a, 0x75, 0x0c, 0x4d, 0x74, 0x8c, 0x75, 0x0c, 0x80, 0xd0, 0x48, 0xc7, 0x08, 0xa8, 0xac, 0xa0, 0x28, 0x33, 0xaf, 0x44, 0x43, 0xc9, 0xc3, 0xd5, 0xc7, 0xc7, 0x5f, 0x21, 0xdc, 0x3f, 0xc8, 0xc7, 0x45, 0x51, 0x49, 0xc7, 0xc2, 0x04, 0x88, 0x80, 0x92, 0x25, 0xb6, 0x25, 0xda, 0x86, 0x5c, 0xb5, 0x5c, 0x9c, 0x76, 0x94, 0xc5, 0x37, 0x33, 0x0b, 0x97, 0x01, 0x84, 0x05, 0xca, 0xb8, 0x30, 0x5a, 0xc0, 0x28, 0xa4, 0x6c, 0xc6, 0xae, 0x3b, 0xff, 0xde, 0x9f, 0x5d, 0xdd, 0x99, 0xee, 0xaa, 0xcc, 0x08, 0xe3, 0xc3, 0x68, 0x1e, 0xa0, 0x1e, 0x11, 0x1e, 0x19, 0x17, 0x15, 0x13, 0x03, 0x93, 0x5c, 0x3f, 0x2f, 0xbf, 0x56, 0x1f, 0x03, 0x93, 0x54, 0x95, 0x0b, 0x6e, 0x1e, 0xa5, 0x85, 0x89, 0x91, 0xb5, 0xe7, 0x2e, 0xd5, 0xea, 0xb4, 0x4e, 0x5d, 0x98, 0xbb, 0x4a, 0xe7, 0xd2, 0xaa, 0x99, 0xb9, 0x87, 0x4e, 0xdd, 0xba, 0x12, 0x77, 0xef, 0xdd, 0x35, 0x00, 0x34, 0x53, 0x38, 0x01, \ No newline at end of file diff --git a/build/windows/studio/studio.vcxproj.user b/build/windows/studio/studio.vcxproj.user new file mode 100644 index 0000000..6e2aec7 --- /dev/null +++ b/build/windows/studio/studio.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/demos/squirreldemo.tic b/demos/squirreldemo.tic new file mode 100644 index 0000000..13110f4 Binary files /dev/null and b/demos/squirreldemo.tic differ diff --git a/include/tic80_config.h b/include/tic80_config.h index 750dc7e..b64c721 100644 --- a/include/tic80_config.h +++ b/include/tic80_config.h @@ -27,6 +27,7 @@ #define TIC_BUILD_WITH_FENNEL 1 #define TIC_BUILD_WITH_JS 1 #define TIC_BUILD_WITH_WREN 1 +#define TIC_BUILD_WITH_SQUIRREL 1 #if defined(__APPLE__) // TODO: this disables macos config diff --git a/src/machine.h b/src/machine.h index c2db864..243b37b 100644 --- a/src/machine.h +++ b/src/machine.h @@ -131,6 +131,11 @@ typedef struct #if defined(TIC_BUILD_WITH_WREN) struct WrenVM* wren; #endif + +#if defined(TIC_BUILD_WITH_SQUIRREL) + struct SQVM* squirrel; +#endif + }; struct @@ -171,6 +176,10 @@ s32 drawSpriteFont(tic_mem* memory, u8 symbol, s32 x, s32 y, s32 width, s32 heig s32 drawFixedSpriteFont(tic_mem* memory, u8 index, s32 x, s32 y, s32 width, s32 height, u8 chromakey, s32 scale, bool alt); void parseCode(const tic_script_config* config, const char* start, u8* color, const tic_code_theme* theme); +#if defined(TIC_BUILD_WITH_SQUIRREL) +const tic_script_config* getSquirrelScriptConfig(); +#endif + #if defined(TIC_BUILD_WITH_LUA) const tic_script_config* getLuaScriptConfig(); diff --git a/src/squirrelapi.c b/src/squirrelapi.c new file mode 100644 index 0000000..b5642bc --- /dev/null +++ b/src/squirrelapi.c @@ -0,0 +1,1606 @@ +// 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); + + if (machine->data) + 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); + + if (machine->data) + 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; + bool alt = 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 (top >= 10) + { + SQBool b = SQFalse; + sq_getbool(vm, 10, &b); + alt = (b != SQFalse); + } + + } + } + } + } + } + + if(scale == 0) + { + sq_pushinteger(vm, 0); + return 1; + } + + s32 size = drawText(memory, text, x, y, width, height, chromakey, scale, fixed ? drawSpriteFont : drawFixedSpriteFont, alt); + + 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; + bool alt = 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 (top >= 8) + { + SQBool b = SQFalse; + sq_getbool(vm, 8, &b); + alt = (b != SQFalse); + } + } + } + } + } + + if(scale == 0) + { + sq_pushinteger(vm, 0); + return 1; + } + + s32 size = memory->api.text_ex(memory, text ? text : "nil", x, y, color, fixed, scale, alt); + + 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); + } + + if (machine->data) + 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); + + if (machine->data) + 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 && 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); + + if (machine->data) + 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); + + if (machine->data) + machine->data->error(machine->data->data, errorString); + sq_pop(vm, 3); // remove string, error and root table. + } + } + else + { + sq_pop(vm, 1); + if (machine->data) + 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); + if (machine->data) + 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); + if (machine->data) + 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; + + // make sure that the Squirrel interpreter is initialized. + if (vm == NULL) + { + if (!initSquirrel(tic, "")) + return; + 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); + if (machine->data) + machine->data->error(machine->data->data, errorString); + } + + sq_settop(vm, 0); +} + +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) */ diff --git a/src/studio.c b/src/studio.c index dc67ec9..c4fdfd0 100644 --- a/src/studio.c +++ b/src/studio.c @@ -1164,7 +1164,7 @@ static void processGamepadMapping() for(s32 i = 0; i < KEYMAP_COUNT; i++) if(impl.keycodes[i] && tic->api.key(tic, impl.keycodes[i])) - tic->ram.input.gamepads.data |= 1 << i; + tic->ram.input.gamepads.data |= 1 << i; } static bool isGameMenu() diff --git a/src/studio.h b/src/studio.h index aecf933..da1ec50 100644 --- a/src/studio.h +++ b/src/studio.h @@ -59,6 +59,7 @@ #define PROJECT_MOON_EXT ".moon" #define PROJECT_JS_EXT ".js" #define PROJECT_WREN_EXT ".wren" +#define PROJECT_SQUIRREL_EXT ".nut" typedef enum { diff --git a/src/tic.c b/src/tic.c index bd78e7c..2c746c2 100644 --- a/src/tic.c +++ b/src/tic.c @@ -574,6 +574,10 @@ void tic_close(tic_mem* memory) machine->state.initialized = false; +#if defined(TIC_BUILD_WITH_SQUIRREL) + getSquirrelScriptConfig()->close(memory); +#endif + #if defined(TIC_BUILD_WITH_LUA) getLuaScriptConfig()->close(memory); @@ -1619,12 +1623,19 @@ static const tic_script_config* getScriptConfig(const char* code) return getWrenScriptConfig(); #endif +#if defined(TIC_BUILD_WITH_SQUIRREL) + if (compareMetatag(code, "script", "squirrel", getSquirrelScriptConfig()->singleComment)) + return getSquirrelScriptConfig(); +#endif + #if defined(TIC_BUILD_WITH_LUA) return getLuaScriptConfig(); #elif defined(TIC_BUILD_WITH_JS) return getJsScriptConfig(); #elif defined(TIC_BUILD_WITH_WREN) return getWrenScriptConfig(); +#elif defined(TIC_BUILD_WITH_SQUIRREL) + return getSquirrelScriptConfig(); #endif }