diff --git a/CMakeLists.txt b/CMakeLists.txt index 0a32da55a..30174de0f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ include(cmake/version.cmake) project(TIC-80 VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REVISION} LANGUAGES C CXX) if(APPLE) - set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version") + set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version") endif() message("Building for target: ${CMAKE_SYSTEM_NAME}") @@ -15,9 +15,9 @@ message("VERSION_HASH: ${VERSION_HASH}") configure_file("${PROJECT_SOURCE_DIR}/version.h.in" "${CMAKE_CURRENT_BINARY_DIR}/version.h") if(ANDROID OR EMSCRIPTEN OR N3DS OR BAREMETALPI) - set(BUILD_STATIC_DEFAULT ON) + set(BUILD_STATIC_DEFAULT ON) else() - set(BUILD_STATIC_DEFAULT OFF) + set(BUILD_STATIC_DEFAULT OFF) endif() set(BUILD_PLAYER_DEFAULT OFF) @@ -38,23 +38,30 @@ option(BUILD_TOUCH_INPUT "Build with touch input support" ${BUILD_TOUCH_INPUT_DE option(BUILD_NO_OPTIMIZATION "Build without optimizations for debugging" OFF) option(BUILD_ASAN_DEBUG "Build with AddressSanitizer" OFF) option(BUILD_WITH_ZLIB "Build with zlib linked" ON) +option(EBUG "Debug" OFF) option(TIC80_TARGET "Target binary suffix") +if(EBUG) + set(CMAKE_BUILD_TYPE Debug) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -ggdb -O0") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -ggdb -O0") +endif() + if(NOT TIC80_TARGET) - set(TIC80_TARGET tic80) + set(TIC80_TARGET tic80) endif() if(NOT BUILD_SDL) - set(BUILD_SDLGPU OFF) + set(BUILD_SDLGPU OFF) endif() add_library(runtime INTERFACE) if(BUILD_STATIC) - set(TIC_RUNTIME STATIC) - target_compile_definitions(runtime INTERFACE TIC_RUNTIME_STATIC) + set(TIC_RUNTIME STATIC) + target_compile_definitions(runtime INTERFACE TIC_RUNTIME_STATIC) else() - set(TIC_RUNTIME SHARED) + set(TIC_RUNTIME SHARED) endif() target_compile_definitions(runtime INTERFACE BUILD_DEPRECATED) @@ -65,33 +72,33 @@ message("BUILD_TOUCH_INPUT: ${BUILD_TOUCH_INPUT}") message("BUILD_WITH_ALL: ${BUILD_WITH_ALL}") if (N3DS) - set(BUILD_SDL OFF) + set(BUILD_SDL OFF) endif() if (BAREMETALPI) - set(BUILD_SDL OFF) + set(BUILD_SDL OFF) endif() if(UNIX AND NOT APPLE AND NOT EMSCRIPTEN AND NOT ANDROID AND NOT N3DS) - set(LINUX TRUE) + set(LINUX TRUE) - if(CMAKE_HOST_SYSTEM_NAME STREQUAL "FreeBSD") - set(FREEBSD TRUE) - endif() + if(CMAKE_HOST_SYSTEM_NAME STREQUAL "FreeBSD") + set(FREEBSD TRUE) + endif() endif() if (BUILD_NO_OPTIMIZATION) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0") endif() if (BUILD_ASAN_DEBUG) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") - if (LINUX AND CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -shared-libasan") - endif() + if (LINUX AND CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -shared-libasan") + endif() endif() set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) @@ -103,44 +110,50 @@ endif() if(MSVC) - add_definitions("/D\"_CRT_SECURE_NO_WARNINGS\"") - add_definitions("/D\"_CRT_NONSTDC_NO_DEPRECATE\"") + add_definitions("/D\"_CRT_SECURE_NO_WARNINGS\"") + add_definitions("/D\"_CRT_NONSTDC_NO_DEPRECATE\"") - foreach( OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES} ) - string( TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG ) - set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${CMAKE_BINARY_DIR}/lib ) - set( CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${CMAKE_BINARY_DIR}/lib ) - set( CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${CMAKE_BINARY_DIR}/bin ) + foreach( OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES} ) + string( TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG ) + set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${CMAKE_BINARY_DIR}/lib ) + set( CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${CMAKE_BINARY_DIR}/lib ) + set( CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${CMAKE_BINARY_DIR}/bin ) - # use static runtime - # !TODO: use set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") from cmake 3.15 instead - if(CMAKE_C_FLAGS_${OUTPUTCONFIG} MATCHES "/MD") - string(REGEX REPLACE "/MD" "/MT" CMAKE_C_FLAGS_${OUTPUTCONFIG} "${CMAKE_C_FLAGS_${OUTPUTCONFIG}}") - endif() + # use static runtime + # !TODO: use set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") from cmake 3.15 instead + if(CMAKE_C_FLAGS_${OUTPUTCONFIG} MATCHES "/MD") + string(REGEX REPLACE "/MD" "/MT" CMAKE_C_FLAGS_${OUTPUTCONFIG} "${CMAKE_C_FLAGS_${OUTPUTCONFIG}}") + endif() - if(CMAKE_CXX_FLAGS_${OUTPUTCONFIG} MATCHES "/MD") - string(REGEX REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_${OUTPUTCONFIG} "${CMAKE_CXX_FLAGS_${OUTPUTCONFIG}}") - endif() - endforeach() + if(CMAKE_CXX_FLAGS_${OUTPUTCONFIG} MATCHES "/MD") + string(REGEX REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_${OUTPUTCONFIG} "${CMAKE_CXX_FLAGS_${OUTPUTCONFIG}}") + endif() + endforeach() else() - set(CMAKE_C_STANDARD 11) + set(CMAKE_C_STANDARD 11) endif() if(RPI) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu11") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu11") endif() set(THIRDPARTY_DIR ${CMAKE_SOURCE_DIR}/vendor) +# TODO: use this variable (containing a path) to get a list of all demos that +# need to be converted using tools. It is simplest to always build the tools and +# rebuild all the demos, then letting CMake itself decide if these are +# up-to-date afterwards. set(DEMO_CARTS_IN ${CMAKE_SOURCE_DIR}/demos) +# Tools and utilities used in some cases include(cmake/gif.cmake) include(cmake/blipbuf.cmake) include(cmake/zlib.cmake) include(cmake/zip.cmake) include(cmake/tools.cmake) +# Languages include(cmake/lua.cmake) include(cmake/moon.cmake) include(cmake/fennel.cmake) @@ -154,6 +167,7 @@ include(cmake/quickjs.cmake) include(cmake/janet.cmake) include(cmake/r.cmake) +# Core and editors include(cmake/core.cmake) include(cmake/wave.cmake) include(cmake/argparse.cmake) @@ -161,8 +175,10 @@ include(cmake/naett.cmake) include(cmake/png.cmake) include(cmake/studio.cmake) +# Graphics libraries include(cmake/sdl.cmake) include(cmake/libretro.cmake) include(cmake/n3ds.cmake) +# Installation, obviously. include(cmake/install.cmake) diff --git a/cmake/renv.cmake b/cmake/renv.cmake new file mode 100644 index 000000000..1ac61bbe0 --- /dev/null +++ b/cmake/renv.cmake @@ -0,0 +1,27 @@ +if(NOT DEFINED ENV{R_HOME} AND DEFINED PREFIX) + set(R_HOME "${PREFIX}/lib64/R") +endif() + +if(NOT DEFINED ENV{LD_LIBRARY_PATH} AND NOT LD_LIBRARY_PATH) + set(LD_LIBRARY_PATH "/usr/include/R:${R_HOME}/lib:${PREFIX}/lib/jvm/jre/lib/server") +else() + # Add these entries to the this *PATH variable + set(LD_LIBRARY_PATH "${LD_LIBRARY_PATH}:${PREFIX}/lib64/R/lib:${PREFIX}/lib/jvm/jre/lib/server") +endif() + +if(NOT DEFINED ENV{R_SHARE_DIR} AND DEFINED ENV{PREFIX}) + set(R_SHARE_DIR "${PREFIX}/share/R") +else() + set(R_SHARE_DIR ENV{R_SHARE_DIR}) +endif() + +if(NOT DEFINED ENV{R_DOC_DIR} AND DEFINED ENV{PREFIX}) + set(R_DOC_DIR "${PREFIX}/share/doc/R") +else() + set(R_DOC_DIR ENV{R_DOC_DIR}) +endif() + +if(DEFINED ENV{PREFIX} OR DEFINED PREFIX) + set(R_SRC "${R_HOME}/lib") + list(APPEND R_SRC "${PREFIX}/lib/jvm/jre/lib/server" R_SHARE_DIR R_DOC_DIR) +endif() diff --git a/demos/bunny/rbenchmark.r b/demos/bunny/rbenchmark.r new file mode 100644 index 000000000..c0abd1c59 --- /dev/null +++ b/demos/bunny/rbenchmark.r @@ -0,0 +1,91 @@ +## title: Bunnymark in Python +## author: Bryce Carson +## desc: Benchmarking tool to see how many bunnies can fly around the screen, using R +## input: gamepad +## script: r +## version: 0.0.1 + +screenWidth <- 240 +screenHeight <- 136 +toolbarHeight <- 6 +t <- 0 + +new_bunny <- function() { + velocityRUnif <- \() runif(1, -100.0, 100.0) / 60.0 + xV <- velocityRUnif() + yV <- velocityRUnif() + + newBunny <- + structure(sqrt(xV^2 + yV^2), + width = 26, + height = 32, + x = sample(0:(screenWidth - width), 1), + y = sample(0:(screenHeight - height), 1), + speed_x = xV, + speed_y = yV, + sprite = 1, + class = "Bunny" + ) + newBunny +} + +draw_bunny <- function(bunny) { + ## stopifnot(is(bunny, "Bunny")) + with(attributes(bunny), + t80.spr(sprite, x, y, 1, 1, 0, 0, 4, 4)) +} + +update_bunny <- function(bunny) { + ## stopifnot(is(bunny, "Bunny")) + attr(bunny, "x") <- attr(bunny, "x") + attr(bunny, "speed_x") + attr(bunny, "y") <- attr(bunny, "y") + attr(bunny, "speed_y") + + if (attr(bunny, "x") + attr(bunny, "width") > screenWidth) { + attr(bunny, "x") <- screenWidth - attr(bunny, "width") + attr(bunny, "speed_x") <- attr(bunny, "speed_x") * -1 + } + + if (attr(bunny, "y") + attr(bunny, "height") > screenHeight) { + attr(bunny, "y") <- screenHeight - attr(bunny, "height") + attr(bunny, "speed_y") <- attr(bunny, "speed_y") * -1 + } + + if (attr(bunny, "x") < 0) { + attr(bunny, "x") <- 0 + attr(bunny, "speed_x") <- attr(bunny, "speed_x") * -1.0 + } + + if (attr(bunny, "y") < toolbarHeight) { + attr(bunny, "y") <- toolbarHeight + attr(bunny, "speed_y") <- attr(bunny, "speed_y") * -1.0 + } +} + +## FIXME: this removes the attributes. S3 classes need to define special +## methods and generics to work with various primitive classes and generics +## like list. Consider data.frame, which is a list, but which does not lose +## its attributes when you make a list of data.frames. +bunnies <- list(new_bunny()) + +## +## 001:11111100111110dd111110dc111110dc111110dc111110dc111110dd111110dd +## 002:00011110ddd0110dccd0110dccd0110dccd0110dccd0110dcddd00dddddddddd +## 003:00001111dddd0111cccd0111cccd0111cccd0111cccd0111dcdd0111dddd0111 +## 004:1111111111111111111111111111111111111111111111111111111111111111 +## 017:111110dd111110dd111110dd111110dd10000ddd1eeeeddd1eeeeedd10000eed +## 018:d0ddddddd0ddddddddddddddddd0000dddddccddddddccdddddddddddddddddd +## 019:0ddd01110ddd0111dddd0111dddd0111ddddd000ddddddddddddddddddddd000 +## 020:1111111111111111111111111111111101111111d0111111d011111101111111 +## 033:111110ee111110ee111110ee111110ee111110ee111110ee111110ee111110ee +## 034:dddcccccddccccccddccccccddccccccddccccccdddcccccdddddddddddddddd +## 035:dddd0111cddd0111cddd0111cddd0111cddd0111dddd0111dddd0111dddd0111 +## 036:1111111111111111111111111111111111111111111111111111111111111111 +## 049:111110ee111110ee111110ee111110ee111110ee111110ee111110ee11111100 +## 050:dddeeeeeddeeeeeed00000000111111101111111011111110111111111111111 +## 051:eddd0111eedd01110eed011110ee011110ee011110ee011110ee011111001111 +## 052:1111111111111111111111111111111111111111111111111111111111111111 +## + +## +## 000:1a1c2c5d275db13e53ef7d57ffcd75a7f07038b76425717929366f3b5dc941a6f673eff7f4f4f494b0c2566c86333c57 +## diff --git a/src/api/r.c b/src/api/r.c index 4ec706270..7d26ba748 100644 --- a/src/api/r.c +++ b/src/api/r.c @@ -39,11 +39,11 @@ void evalR(tic_mem *memory, const char *code) { SEXP result = Rf_eval(Rf_mkString(code), R_GlobalEnv); } -#define killer \ - tic_core *core; \ - if ((core = (((tic_core *) tic))->currentVM) != NULL) { \ - Rf_endEmbeddedR(0); \ - core->currentVM = NULL; \ +#define killer \ + tic_core *core; \ + if ((core = (((tic_core *) tic))->currentVM) != NULL) { \ + Rf_endEmbeddedR(0); \ + core->currentVM = NULL; \ } tic_core *getTICCore(tic_mem* tic, const char* code); @@ -51,6 +51,10 @@ tic_core *getTICCore(tic_mem* tic, const char* code); static bool initR(tic_mem *tic, const char *code) { killer; +#define dbgmsg initR##__LINE__ + fprintf(stderr, "%s\n", dbgmsg); +#undef dbgmsg + int tries = 1; core = getTICCore(tic, code); @@ -194,15 +198,20 @@ static const u8 MarkRom[] = #include "../build/assets/rmark.tic.dat" }; +/* DEFAULT visibility*/ +/* EXPORT_SCRIPT -> RScriptConfig if static, else ScriptConfig */ TIC_EXPORT const tic_script EXPORT_SCRIPT(R) = { /* The first five members of the struct have the sum total following * size. */ /* sizeof(u8) + 3 * sizeof(char *) */ - .id = 42, + /* R's id is determined by counting up from 10, beginning with Lua, all of + the other languages TIC-80 supports. Python was the 10th langauge supported, + with .id 20. */ + .id = 21, .name = "r", .fileExtension = ".r", - .projectComment = "##", + .projectComment = "#", { .init = initR, .close = closeR, @@ -226,7 +235,7 @@ TIC_EXPORT const tic_script EXPORT_SCRIPT(R) = .blockCommentEnd = NULL, .blockCommentStart2 = NULL, .blockCommentEnd2 = NULL, - .singleComment = "##", + .singleComment = "#", .blockStringStart = "\"", .blockStringEnd = "\"", .stdStringStartEnd = "\"", diff --git a/src/api/r.org b/src/api/r.org index be6835665..3b8b1ecd1 100644 --- a/src/api/r.org +++ b/src/api/r.org @@ -247,7 +247,7 @@ definition of the function in either soure tree. #+name: define a function to get a pointer to the tic_core #+begin_src C - bool initR(tic_mem *tic, const char *code); + bool initR(tic_mem *tic, const char *code); tic_core *getTICCore(tic_mem* tic, const char* code) { tic_core *core; @@ -269,21 +269,21 @@ review Narain Gehani's /Advanced Introduction to C/. #+name: a quotation from the *TIC-80* ~core.h~ #+begin_src C - tic_mem memory; // it should be first - tic80_pixel_color_format screen_format; + tic_mem memory; // it should be first + tic80_pixel_color_format screen_format; - void* currentVM; + void* currentVM; #+end_src Now quoted is the first four members of the =tic_mem= struct. #+name: a quotation from the *TIC-80* ~api.h~ #+begin_src C - tic80 product; - tic_ram* ram; - tic_cartridge cart; + tic80 product; + tic_ram* ram; + tic_cartridge cart; - tic_ram* base_ram; + tic_ram* base_ram; #+end_src The sizes of these types are definitely not equal, so there is no direct @@ -302,28 +302,28 @@ define multiple Scheme functions within the bounds of the Scheme interpeter. #+name: a quotation from the *TIC-80* =scheme.c= #+begin_src c - static void initAPI(tic_core* core) - { - s7_scheme* sc = core->currentVM; - - #define API_FUNC_DEF(name, desc, helpstr, count, reqcount, ...) \ - {scheme_ ## name, desc "\n" helpstr, count, reqcount, "t80::" #name}, - - static const struct{s7_function func; const char* helpstr; int count; int reqcount; const char* name;} ApiItems[] = - {TIC_API_LIST(API_FUNC_DEF)}; - - #undef API_FUNC_DEF - - for (s32 i = 0; i < COUNT_OF(ApiItems); i++) { - s7_define_function(sc, - ApiItems[i].name, - ApiItems[i].func, - ApiItems[i].reqcount, - ApiItems[i].count - ApiItems[i].reqcount, // opt count - false, // rest_arg - ApiItems[i].helpstr); - } - } + static void initAPI(tic_core* core) + { + s7_scheme* sc = core->currentVM; + + #define API_FUNC_DEF(name, desc, helpstr, count, reqcount, ...) \ + {scheme_ ## name, desc "\n" helpstr, count, reqcount, "t80::" #name}, + + static const struct{s7_function func; const char* helpstr; int count; int reqcount; const char* name;} ApiItems[] = + {TIC_API_LIST(API_FUNC_DEF)}; + + #undef API_FUNC_DEF + + for (s32 i = 0; i < COUNT_OF(ApiItems); i++) { + s7_define_function(sc, + ApiItems[i].name, + ApiItems[i].func, + ApiItems[i].reqcount, + ApiItems[i].count - ApiItems[i].reqcount, // opt count + false, // rest_arg + ApiItems[i].helpstr); + } + } #+end_src @@ -342,36 +342,36 @@ and not undefine it. #+name: a modified quotation from the *TIC-80* =scheme.c= #+begin_src c - #define API_FUNC_DEF(name, desc, helpstr, count, reqcount, ...) { \ - scheme_##name, \ - desc "\n" helpstr, \ - count, \ - reqcount, \ - "t80::"##name \ - }, /* DONT delete this trailing comma! It's intentional! */ - - static void initAPI(tic_core* core) - { - s7_scheme* sc = core->currentVM; - - static const struct { - s7_function func; - const char* helpstr; - int count; - int reqcount; - const char* name; - } ApiItems[] = { TIC_API_LIST(API_FUNC_DEF); }; - - for (s32 i = 0; i < COUNT_OF(ApiItems); i++) { - s7_define_function(sc, - ApiItems[i].name, - ApiItems[i].func, - ApiItems[i].reqcount, - ApiItems[i].count - ApiItems[i].reqcount, // opt count - false, // rest_arg - ApiItems[i].helpstr); - } - } + #define API_FUNC_DEF(name, desc, helpstr, count, reqcount, ...) { \ + scheme_##name, \ + desc "\n" helpstr, \ + count, \ + reqcount, \ + "t80::"##name \ + }, /* DONT delete this trailing comma! It's intentional! */ + + static void initAPI(tic_core* core) + { + s7_scheme* sc = core->currentVM; + + static const struct { + s7_function func; + const char* helpstr; + int count; + int reqcount; + const char* name; + } ApiItems[] = { TIC_API_LIST(API_FUNC_DEF); }; + + for (s32 i = 0; i < COUNT_OF(ApiItems); i++) { + s7_define_function(sc, + ApiItems[i].name, + ApiItems[i].func, + ApiItems[i].reqcount, + ApiItems[i].count - ApiItems[i].reqcount, // opt count + false, // rest_arg + ApiItems[i].helpstr); + } + } #+end_src * Writing the R language integration for TIC-80 @@ -447,19 +447,19 @@ none of it is Scheme any more. Before a final public release this will be changed so that no references to "Scheme" occur in the code. #+begin_src C :noweb no-export :tangle r.c - <> + <> - <> + <> - <> + <> <> - <> + <> - <> + <> - <> + <> #+end_src *** Procedures to initialize, close, and re-initialize R @@ -501,11 +501,11 @@ restarting R as necessary and tracking the current interpreter (there can be only one). #+begin_src C :noweb-ref cartridge commands - #define killer \ - tic_core *core; \ - if ((core = (((tic_core *) tic))->currentVM) != NULL) { \ - Rf_endEmbeddedR(0); \ - core->currentVM = NULL; \ + #define killer \ + tic_core *core; \ + if ((core = (((tic_core *) tic))->currentVM) != NULL) { \ + Rf_endEmbeddedR(0); \ + core->currentVM = NULL; \ } tic_core *getTICCore(tic_mem* tic, const char* code); @@ -513,6 +513,10 @@ only one). static bool initR(tic_mem *tic, const char *code) { killer; + #define dbgmsg initR##__LINE__ + fprintf(stderr, "%s\n", dbgmsg); + #undef dbgmsg + int tries = 1; core = getTICCore(tic, code); @@ -566,58 +570,144 @@ These four TIC-80 API functions or commands are defined using a macro. *** Exporting a =tic_script= for *TIC-80* to use at compile-time This constant is used by TIC-80 to setup the cartridge, both for editing in the -"studio" and the runtime evaluation of the script. +"studio" and the runtime evaluat (use-package emacsql-sqlite :after 'emacsql) +ion of the script. #+name: TIC EXPORT #+begin_src c - TIC_EXPORT const tic_script EXPORT_SCRIPT(R) = - { - /* The first five members of the struct have the sum total following - * size. */ - /* sizeof(u8) + 3 * sizeof(char *) */ - .id = 42, - .name = "r", - .fileExtension = ".r", - .projectComment = "##", - { - .init = initR, - .close = closeR, - .tick = callRFn_TIC80, - .boot = callRFn_BOOT, - - /* In the Scheme integration these have additional argument types s32 and - * void * (row and data, respectively). */ - .callback = - { - .scanline = callRFn_SCN, - .border = callRFn_BDR, - .menu = callRFn_MENU, - }, - }, - - .getOutline = getROutline, - .eval = evalR, - - .blockCommentStart = NULL, - .blockCommentEnd = NULL, - .blockCommentStart2 = NULL, - .blockCommentEnd2 = NULL, - .singleComment = "##", - .blockStringStart = "\"", - .blockStringEnd = "\"", - .stdStringStartEnd = "\"", - .blockEnd = NULL, - .lang_isalnum = r_isalnum, - .api_keywords = RAPIKeywords, - .api_keywordsCount = COUNT_OF(RAPIKeywords), - .useStructuredEdition = false, - - .keywords = RKeywords, - .keywordsCount = COUNT_OF(RKeywords), - - .demo = {DemoRom, sizeof DemoRom}, - .mark = {MarkRom, sizeof MarkRom, "rmark.tic"}, - }; + /* DEFAULT visibility*/ + /* EXPORT_SCRIPT -> RScriptConfig if static, else ScriptConfig */ + TIC_EXPORT const tic_script EXPORT_SCRIPT(R) = + { + /* The first five members of the struct have the sum total following + ,* size. */ + /* sizeof(u8) + 3 * sizeof(char *) */ + /* R's id is determined by counting up from 10, beginning with Lua, all of + the other languages TIC-80 supports. Python was the 10th langauge supported, + with .id 20. */ + .id = 21, + .name = "r", + .fileExtension = ".r", + .projectComment = "#", + { + .init = initR, + .close = closeR, + .tick = callRFn_TIC80, + .boot = callRFn_BOOT, + + /* In the Scheme integration these have additional argument types s32 and + ,* void * (row and data, respectively). */ + .callback = + { + .scanline = callRFn_SCN, + .border = callRFn_BDR, + .menu = callRFn_MENU, + }, + }, + + .getOutline = getROutline, + .eval = evalR, + + .blockCommentStart = NULL, + .blockCommentEnd = NULL, + .blockCommentStart2 = NULL, + .blockCommentEnd2 = NULL, + .singleComment = "#", + .blockStringStart = "\"", + .blockStringEnd = "\"", + .stdStringStartEnd = "\"", + .blockEnd = NULL, + .lang_isalnum = r_isalnum, + .api_keywords = RAPIKeywords, + .api_keywordsCount = COUNT_OF(RAPIKeywords), + .useStructuredEdition = false, + + .keywords = RKeywords, + .keywordsCount = COUNT_OF(RKeywords), + + .demo = {DemoRom, sizeof DemoRom}, + .mark = {MarkRom, sizeof MarkRom, "rmark.tic"}, + }; +#+end_src + +On line three of the current source file---/exempli gratia/---if =MACROVAR(it)= +was invoked its argument would expand to =it3=. When used in the first define +below, =it3= will be a pointer to an array of tic_scripts, which is iterated +over (explaning the =it= argument). ~*script~ is modified, but the type +specifier is ~const~, so what's going on with the syntax that I don't remember? +Does it only apply to the first declared variable in the identifier list? + +~Scripts~ is an array of =tic_script *=-typed objects, that is it is an array of +pointers to =tic_script= objects. + +#+name: FOREACH_LANG +#+begin_src C + #define FOREACH_LANG(script) \ + for(const tic_script **MACROVAR(it) = tic_scripts(), *script = *MACROVAR(it); \ + ,*MACROVAR(it); \ + script = *++MACROVAR(it)) + + #define CONCAT2(a, b) a ## b + #define CONCAT(a, b) CONCAT2(a, b) + #define MACROVAR(name) CONCAT(name, __LINE__) + + + const tic_script** tic_scripts() + { + return Scripts; + } + + static const tic_script *Scripts[MAX_SUPPORTED_LANGS + 1] = + { + #if defined(TIC_RUNTIME_STATIC) + #if defined (TIC_BUILD_WITH_LUA) + &EXPORT_SCRIPT(Lua), + #endif + + #if defined(TIC_BUILD_WITH_RUBY) + &EXPORT_SCRIPT(Ruby), + #endif + + #if defined(TIC_BUILD_WITH_JS) + &EXPORT_SCRIPT(Js), + #endif + + #if defined(TIC_BUILD_WITH_MOON) + &EXPORT_SCRIPT(Moon), + #endif + + #if defined(TIC_BUILD_WITH_FENNEL) + &EXPORT_SCRIPT(Fennel), + #endif + + #if defined(TIC_BUILD_WITH_SCHEME) + &EXPORT_SCRIPT(Scheme), + #endif + + #if defined(TIC_BUILD_WITH_SQUIRREL) + &EXPORT_SCRIPT(Squirrel), + #endif + + #if defined(TIC_BUILD_WITH_WREN) + &EXPORT_SCRIPT(Wren), + #endif + + #if defined(TIC_BUILD_WITH_WASM) + &EXPORT_SCRIPT(Wasm), + #endif + + #if defined(TIC_BUILD_WITH_JANET) + &EXPORT_SCRIPT(Janet), + #endif + + #if defined(TIC_BUILD_WITH_PYTHON) + &EXPORT_SCRIPT(Python), + #endif + + #endif + + NULL, + }; #+end_src *** Providing lists of syntax elements for highlighting and outline generation @@ -1073,6 +1163,7 @@ successfully built the DAT files myself). local TIC2DAT="${HOME}/src/c/build/bin/bin2txt" command $TIC2DAT ${1} ${BLDA_DIR}/$(basename $1).dat -z rm ${BLDA_DIR}/$(basename $1) + # mv ${BLDA_DIR}/$(basename $1) -t ~/src/c/build/ } for SRC in $R_DEMO $R_MARK; do @@ -1081,6 +1172,8 @@ successfully built the DAT files myself). done #+end_src +#+RESULTS: convert the R demo and benchmark source files to .tic.dat format + #+RESULTS: I'm now confident that the DAT files are derived from my R-based source files, @@ -1093,6 +1186,87 @@ to work out why that is with a more involved search of the sources and then some debugging. *** Second attempt at debugging the bugged ~new r~ functionality +Looking at the Scheme language integration it seems my mistake may be calling +the command with a lower-case ~r~, when I should call it with an upper-case ~R~, +if and only if the ~.name~ member of the ~const tic_script~ object in my +language integration has that casing. + +#+begin_src C + TIC_EXPORT const tic_script EXPORT_SCRIPT(R) = + { + /* The first five members of the struct have the sum total following + ,* size. */ + /* sizeof(u8) + 3 * sizeof(char *) */ + .id = 42, + .name = "r", + .fileExtension = ".r", + .projectComment = "##" +#+end_src + +Unfortunatley, as shown by the quotation, that is not the case. + +Perhaps it is the arbitrary ~.id~ I chose. How are the other IDs determined? + +- lua :: 10 +- ruby :: 11 +- js :: 12 +- moonscript :: 13, which is alike my R integration in that it uses ~moon~ for its name and then ~.moon~ for its extension; no problems for me here. +- fennel :: 14 +- squirrel :: 15 +- wren :: 16 +- wasm :: 17 +- janet :: 18 +- scheme :: 19 +- python :: 20 +- r :: 21 + +It looks like R is the eleventh language added to TIC-80. There are room for +five more, given I saw a "maximum number of langauges" elsewhere in the codebase +set to sixteen. + +*** Third attempt +This procedure is used at runtime to add scripts to the ~Scripts~ array. + +#+begin_src C + void tic_add_script(const tic_script* script) + { + s32 index = 0; + FOREACH_LANG(it) + { + if(it->id == script->id || strcmp(it->name, script->name) == 0) + { + // script already exists + return; + } + + index++; + } + + if(index < MAX_SUPPORTED_LANGS) + { + Scripts[index] = script; + qsort(Scripts, index + 1, sizeof Scripts[0], compareScripts); + } + } +#+end_src + +- How is the ~Scripts~ array used? +- [X] How is ~it~ defined at runtime? ~it~ likely means "iterator", the current one. + +It doesn't appear that ~it~ is defined in /any way/. It is a literal macro +argument used in concatenation of an identifier, and no more. + +~Scripts~ is only used within =script.c=; it is only used within the functions +~tic_add_script~, ~tic_get_script~, and ~tic_scripts~. Collectively, =script.c= +defines functionality for compile-time and run-time population of the =Scripts= +array, which is used for autocompletion of certain command-line commands within +the TIC-80 console (~new~ in particular). + +~tic_get_script~ checks if the argument ~memory~ (of type =tic_mem *=) has the +same id as any of the =tic_scripts= in =Scripts=. If none of the scripts match, +either an uninitialized =tic_script= called ~empty~ is returned or the address +of the whole array is returned (effectively returning the first member of the +array). ~tic_scripts~ returns the address of the whole array forthright. * TODO Debugging R programs in TIC-80 Integrate the R debugger, browser, etc. into TIC-80. @@ -1133,36 +1307,36 @@ to a pointer to another type (=tic_core=) as well. It's not simple code (the whole application is an emulator for a computer that doesn't exist, after all!). #+begin_src C - struct tic_mem -{ - tic80 product; - tic_ram* ram; - tic_cartridge cart; - - tic_ram* base_ram; - - char saveid[TIC_SAVEID_SIZE]; - - union - { - struct - { -#if RETRO_IS_BIG_ENDIAN - u8 padded:5; - u8 keyboard:1; - u8 mouse:1; - u8 gamepad:1; -#else - u8 gamepad:1; - u8 mouse:1; - u8 keyboard:1; - u8 padded:5; -#endif - }; - - u8 data; - } input; -}; + struct tic_mem + { + tic80 product; + tic_ram* ram; + tic_cartridge cart; + + tic_ram* base_ram; + + char saveid[TIC_SAVEID_SIZE]; + + union + { + struct + { + #if RETRO_IS_BIG_ENDIAN + u8 padded:5; + u8 keyboard:1; + u8 mouse:1; + u8 gamepad:1; + #else + u8 gamepad:1; + u8 mouse:1; + u8 keyboard:1; + u8 padded:5; + #endif + }; + + u8 data; + } input; + }; #+end_src This is the fifth member of the struct =tic_script=, which is predictably offset @@ -1175,38 +1349,38 @@ I wrote this small, not-so-good program to learn the size (in bytes) of the offset of the functions in the =tic_script= type. #+begin_src C - #include - #include - - int main(void) { - typedef struct { - unsigned int x; - char *a[3]; - } memory; - - memory x; - memory y; - - printf("y (%p) - x (%p) = %p address\n", &y, &x, &y - &x); - printf("0x.....20 -> int = %d\n", (int) 0x20); - printf("0x.....20 -> int = %d\n", (unsigned long int) ( &y - &x )); - } + #include + #include + + int main(void) { + typedef struct { + unsigned int x; + char *a[3]; + } memory; + + memory x; + memory y; + + printf("y (%p) - x (%p) = %p address\n", &y, &x, &y - &x); + printf("0x.....20 -> int = %d\n", (int) 0x20); + printf("0x.....20 -> int = %d\n", (unsigned long int) ( &y - &x )); + } #+end_src #+begin_src C - #include - #include + #include + #include - int main(void) { - typedef struct { - unsigned int x; - char *a[3]; - } memory; + int main(void) { + typedef struct { + unsigned int x; + char *a[3]; + } memory; - memory x, y; + memory x, y; - printf("%zu\n", sizeof(x)); - } + printf("%zu\n", sizeof(x)); + } #+end_src ** Learning how to work with this stuff @@ -1246,68 +1420,68 @@ for R from C. #+name: a (annotated) quotation of =embeddedRCall.h= #+begin_src c - #ifndef EMBEDDED_R_CALL_H - #define EMBEDDED_R_CALL_H + #ifndef EMBEDDED_R_CALL_H + #define EMBEDDED_R_CALL_H - #include - #include + #include + #include - /* Predeclarations */ - int eval_R_command(const char *funcName, int argc, char *argv[]); - void init_R(int argc, char **argv); - void end_R(); + /* Predeclarations */ + int eval_R_command(const char *funcName, int argc, char *argv[]); + void init_R(int argc, char **argv); + void end_R(); - #endif + #endif #+end_src #+name: a quotation of =embeddedRCall.c= #+begin_src C - #include - #include "embeddedRCall.h" + #include + #include "embeddedRCall.h" - int - eval_R_command(const char *funcName, int argc, char *argv[]) - { - SEXP e; a /* MAYBE FIXME: Possibly missing semi-colon. Copy-paste error? */ - SEXP arg; + int + eval_R_command(const char *funcName, int argc, char *argv[]) + { + SEXP e; a /* MAYBE FIXME: Possibly missing semi-colon. Copy-paste error? */ + SEXP arg; - int i; - int errorOccurred; - init_R(argc, argv); + int i; + int errorOccurred; + init_R(argc, argv); - PROTECT(arg = allocVector(INTSXP, 10)); - for(i = 0; i < LENGTH(arg); i++) INTEGER(arg)[i] = i + 1; + PROTECT(arg = allocVector(INTSXP, 10)); + for(i = 0; i < LENGTH(arg); i++) INTEGER(arg)[i] = i + 1; - PROTECT(e = lang2(install(funcName), arg)); + PROTECT(e = lang2(install(funcName), arg)); - /* Evaluate the call to the R function. - Ignore the return value. - ,*/ - R_tryEval(e, R_GlobalEnv, &errorOccurred); + /* Evaluate the call to the R function. + Ignore the return value. + ,*/ + R_tryEval(e, R_GlobalEnv, &errorOccurred); - Rf_endEmbeddedR(0); - UNPROTECT(2); - return(0); - } + Rf_endEmbeddedR(0); + UNPROTECT(2); + return(0); + } - void - init_R(int argc, char **argv) - { - int defaultArgc = 1; - char *defaultArgv[] = {"Rtest"}; - - if(argc == 0 || argv == NULL) { - argc = defaultArgc; - argv = defaultArgv; - } - Rf_initEmbeddedR(argc, argv); - } - - void - end_R() - { - Rf_endEmbeddedR(0); - } + void + init_R(int argc, char **argv) + { + int defaultArgc = 1; + char *defaultArgv[] = {"Rtest"}; + + if(argc == 0 || argv == NULL) { + argc = defaultArgc; + argv = defaultArgv; + } + Rf_initEmbeddedR(argc, argv); + } + + void + end_R() + { + Rf_endEmbeddedR(0); + } #+end_src *** Registering symbols in R from C diff --git a/src/script.c b/src/script.c index ddce20ebb..1a3b0e7ed 100644 --- a/src/script.c +++ b/src/script.c @@ -73,6 +73,10 @@ extern tic_script EXPORT_SCRIPT(Janet); extern tic_script EXPORT_SCRIPT(Python); #endif +#if defined(TIC_BUILD_WITH_R) +extern tic_script EXPORT_SCRIPT(R); +#endif + #endif static const tic_script *Scripts[MAX_SUPPORTED_LANGS + 1] = @@ -122,6 +126,10 @@ static const tic_script *Scripts[MAX_SUPPORTED_LANGS + 1] = &EXPORT_SCRIPT(Python), #endif + #if defined(TIC_BUILD_WITH_R) + &EXPORT_SCRIPT(R), + #endif + #endif NULL,