From 77110ad1c07612a46d4ed8ace97349fa8b0f88e2 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Mon, 2 Dec 2024 16:41:30 +0000 Subject: [PATCH 1/7] Fix parsing of semicolons in DO with a statement: `do print(a);while(a--);` --- ChangeLog | 1 + src/jslex.c | 4 ++++ src/jslex.h | 1 + src/jsparse.c | 6 ++++-- 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 337003bd60..b06d35c98d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -64,6 +64,7 @@ STM32F4: Add SDIO support STM32: Ensure we kick the WDT if auto kicking is enabled and in deep sleep (avoids having to to it manually and wait 30ms for USB to wake up/shut down) Allow a 'file receive' packet which can request Espruino sends a file as binary packets (also fix files not being closed if transmission fails) + Fix parsing of semicolons in DO with a statement: `do print(a);while(a--);` 2v24 : Bangle.js2: Add 'Bangle.touchRd()', 'Bangle.touchWr()' Bangle.js2: After Bangle.showTestScreen, put Bangle.js into a hard off state (not soft off) diff --git a/src/jslex.c b/src/jslex.c index 7b63efc08f..181355ea19 100644 --- a/src/jslex.c +++ b/src/jslex.c @@ -41,6 +41,10 @@ void jslCharPosClone(JslCharPos *dstpos, JslCharPos *pos) { dstpos->currCh = pos->currCh; } +void jslCharPosClear(JslCharPos *pos) { + pos->it.var = 0; +} + void jslCharPosFromLex(JslCharPos *dstpos) { jsvStringIteratorClone(&dstpos->it, &lex->it); dstpos->currCh = lex->currCh; diff --git a/src/jslex.h b/src/jslex.h index e12cab1fe7..2ab5e61cdb 100644 --- a/src/jslex.h +++ b/src/jslex.h @@ -137,6 +137,7 @@ typedef struct JslCharPos { void jslCharPosFree(JslCharPos *pos); void jslCharPosClone(JslCharPos *dstpos, JslCharPos *pos); +void jslCharPosClear(JslCharPos *pos); ///< clear charpos (if was an undefined value) void jslCharPosFromLex(JslCharPos *dstpos); void jslCharPosNew(JslCharPos *dstpos, JsVar *src, size_t tokenStart); diff --git a/src/jsparse.c b/src/jsparse.c index a317cd6c65..63dc129943 100644 --- a/src/jsparse.c +++ b/src/jsparse.c @@ -2567,7 +2567,6 @@ NO_INLINE JsVar *jspeStatementDoOrWhile(bool isWhile) { JslCharPos whileCondStart; // We do repetition by pulling out the string representing our statement // there's definitely some opportunity for optimisation here - bool wasInLoop = (execInfo.execute&EXEC_IN_LOOP)!=0; JslCharPos whileBodyStart; if (isWhile) { // while loop @@ -2579,15 +2578,18 @@ NO_INLINE JsVar *jspeStatementDoOrWhile(bool isWhile) { jsvUnLock(cond); jslCharPosFromLex(&whileBodyStart); JSP_MATCH_WITH_CLEANUP_AND_RETURN(')',jslCharPosFree(&whileBodyStart);jslCharPosFree(&whileCondStart);,0); - } else { + } else { // do loop jslCharPosFromLex(&whileBodyStart); JSP_MATCH_WITH_CLEANUP_AND_RETURN(LEX_R_DO, jslCharPosFree(&whileBodyStart);,0); + jslCharPosClear(&whileCondStart); } JSP_SAVE_EXECUTE(); // actually try and execute first bit of while loop (we'll do the rest in the actual loop later) if (!loopCond) jspSetNoExecute(); execInfo.execute |= EXEC_IN_LOOP; + bool needSemiColon = (!isWhile) && lex->tk!='{'; jsvUnLock(jspeBlockOrStatement()); + if (needSemiColon) JSP_MATCH_WITH_CLEANUP_AND_RETURN(';',jslCharPosFree(&whileBodyStart);jslCharPosFree(&whileCondStart);,0); // do statement; while(a--); if (!wasInLoop) execInfo.execute &= (JsExecFlags)~EXEC_IN_LOOP; hasHadBreak |= jspeCheckBreakContinue(); From 6d6a82856b6c564595d3c7f2c09bbbc459670622 Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Tue, 3 Dec 2024 12:09:34 +0000 Subject: [PATCH 2/7] typescript: add `remove` to `MenuOptions` --- libs/pixljs/jswrap_pixljs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/pixljs/jswrap_pixljs.c b/libs/pixljs/jswrap_pixljs.c index 8609cd3a25..cd794be7f8 100644 --- a/libs/pixljs/jswrap_pixljs.c +++ b/libs/pixljs/jswrap_pixljs.c @@ -519,6 +519,7 @@ type MenuNumberItem = { type MenuOptions = { title?: string; back?: () => void; + remove?: () => void; selected?: number; fontHeight?: number; scroll?: number; @@ -733,4 +734,4 @@ To remove the window, call `E.showAlert()` with no arguments. }*/ void jswrap_pixljs_powerusage(JsVar *devices) { jsvObjectSetChildAndUnLock(devices, "LCD", jsvNewFromInteger(lcdIsOn ? 170 : 20)); -} \ No newline at end of file +} From 1d1707da1ecca8a2a89d0c25aff64b0271543250 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Tue, 3 Dec 2024 14:02:41 +0000 Subject: [PATCH 3/7] In SAVE_ON_FLASH builds (Microbit 1) remove getSerial, Math.LN*/LOG*SQRT* constants, passwords, Serial/I2C/SPI.find, Date.toUTCString --- ChangeLog | 1 + README_BuildProcess.md | 1 + libs/graphics/jswrap_graphics.c | 4 ++-- src/jsinteractive.c | 8 ++++++++ src/jsinteractive.h | 4 +++- src/jsutils.h | 1 + src/jswrap_date.c | 1 + src/jswrap_espruino.c | 7 +++++++ src/jswrap_interactive.c | 1 + src/jswrap_math.c | 6 ++++++ src/jswrap_number.c | 2 ++ src/jswrap_serial.c | 1 + src/jswrap_spi_i2c.c | 2 ++ 13 files changed, 36 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index b06d35c98d..b0c16aa22e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -65,6 +65,7 @@ STM32: Ensure we kick the WDT if auto kicking is enabled and in deep sleep (avoids having to to it manually and wait 30ms for USB to wake up/shut down) Allow a 'file receive' packet which can request Espruino sends a file as binary packets (also fix files not being closed if transmission fails) Fix parsing of semicolons in DO with a statement: `do print(a);while(a--);` + In SAVE_ON_FLASH builds (Microbit 1) remove getSerial, Math.LN*/LOG*SQRT* constants, passwords, Serial/I2C/SPI.find, Date.toUTCString 2v24 : Bangle.js2: Add 'Bangle.touchRd()', 'Bangle.touchWr()' Bangle.js2: After Bangle.showTestScreen, put Bangle.js into a hard off state (not soft off) diff --git a/README_BuildProcess.md b/README_BuildProcess.md index 2c7e55151e..f75ca93f46 100644 --- a/README_BuildProcess.md +++ b/README_BuildProcess.md @@ -179,6 +179,7 @@ These are set automatically when `SAVE_ON_FLASH` is set (see `jsutils.h`) * `ESPR_NO_LET_SCOPING` - don't create scopes for `let` (treat it like `var`, which was the 2v13 and earlier behaviour) * `ESPR_NO_PROMISES` - Don't include promise-handling functions * `ESPR_NO_PRETOKENISE` - Don't include code to pretokenise functions marked with `"ram"` - code pretokenised in the IDE can still be executed +* `ESPR_NO_PASSWORD` - Disable password protection on the console (E.setPassword/E.lockConsole) ### chip diff --git a/libs/graphics/jswrap_graphics.c b/libs/graphics/jswrap_graphics.c index 5c9245f01a..e230340c49 100644 --- a/libs/graphics/jswrap_graphics.c +++ b/libs/graphics/jswrap_graphics.c @@ -1720,7 +1720,7 @@ It is recommended that you use `Graphics.setFont("4x6")` for more flexibility. "type" : "method", "class" : "Graphics", "name" : "setFontVector", - "ifndef" : "SAVE_ON_FLASH", + "#if" : "!defined(SAVE_ON_FLASH) && !defined(NO_VECTOR_FONT)", "generate_full" : "jswrap_graphics_setFontSizeX(parent, size, true)", "params" : [ ["size","int32","The height of the font, as an integer"] @@ -2807,7 +2807,7 @@ void jswrap_graphics_drawCString(JsGraphics *gfx, int x, int y, char *str) { "type" : "method", "class" : "Graphics", "name" : "getVectorFontPolys", - "#if" : "!defined(SAVE_ON_FLASH) || !defined(NO_VECTOR_FONT)", + "#if" : "!defined(SAVE_ON_FLASH) && !defined(NO_VECTOR_FONT)", "generate" : "jswrap_graphics_getVectorFontPolys", "params" : [ ["str","JsVar","The string"], diff --git a/src/jsinteractive.c b/src/jsinteractive.c index 8f49f8ba91..c5082ce183 100644 --- a/src/jsinteractive.c +++ b/src/jsinteractive.c @@ -170,7 +170,11 @@ NO_INLINE bool jsiEcho() { } NO_INLINE bool jsiPasswordProtected() { +#ifndef ESPR_NO_PASSWORD return ((jsiStatus&JSIS_PASSWORD_PROTECTED)!=0); +#else + return 0; +#endif } static bool jsiShowInputLine() { @@ -892,11 +896,13 @@ void jsiSemiInit(bool autoLoad, JsfFileName *loadedFilename) { jspSoftInit(); } +#ifndef ESPR_NO_PASSWORD // If a password was set, apply the lock JsVar *pwd = jsvObjectGetChildIfExists(execInfo.hiddenRoot, PASSWORD_VARIABLE_NAME); if (pwd) jsiStatus |= JSIS_PASSWORD_PROTECTED; jsvUnLock(pwd); +#endif // Softinit may run initialisation code that will overwrite defaults jsiSoftInit(!autoLoad); @@ -1800,6 +1806,7 @@ void jsiHandleChar(char ch) { // 27 then 79 then 72 - end // 27 then 10 - alt enter +#ifndef ESPR_NO_PASSWORD if (jsiPasswordProtected()) { if (ch=='\r' || ch==10) { JsVar *pwd = jsvObjectGetChildIfExists(execInfo.hiddenRoot, PASSWORD_VARIABLE_NAME); @@ -1819,6 +1826,7 @@ void jsiHandleChar(char ch) { jsiAppendToInputLine(ch); return; } +#endif if (ch==3 && IS_PACKET_TRANSFER(inputState)) execInfo.execute &= ~EXEC_CTRL_C_MASK; // if we got Ctrl-C, ignore it diff --git a/src/jsinteractive.h b/src/jsinteractive.h index 4aff5f25d2..b1db593995 100644 --- a/src/jsinteractive.h +++ b/src/jsinteractive.h @@ -142,7 +142,9 @@ void jsiSetSleep(JsiSleepType isSleep); #define DEVICE_OPTIONS_NAME "_options" #define INIT_CALLBACK_NAME JS_EVENT_PREFIX"init" ///< Callback for `E.on('init'` #define KILL_CALLBACK_NAME JS_EVENT_PREFIX"kill" ///< Callback for `E.on('kill'` +#ifndef ESPR_NO_PASSWORD #define PASSWORD_VARIABLE_NAME "pwd" +#endif typedef enum { JSIS_NONE, @@ -159,7 +161,7 @@ typedef enum { JSIS_TODO_MASK = JSIS_TODO_FLASH_SAVE|JSIS_TODO_FLASH_LOAD|JSIS_TODO_RESET, JSIS_CONSOLE_FORCED = 1<<8, ///< see jsiSetConsoleDevice JSIS_WATCHDOG_AUTO = 1<<9, ///< Automatically kick the watchdog timer on idle - JSIS_PASSWORD_PROTECTED = 1<<10, ///< Password protected + JSIS_PASSWORD_PROTECTED = 1<<10, ///< Password protected (only ifndef ESPR_NO_PASSWORD) JSIS_COMPLETELY_RESET = 1<<11, ///< Has the board powered on *having not loaded anything from flash* JSIS_FIRST_BOOT = 1<<12, ///< Is this the first time we started, or has load/reset/etc been called? diff --git a/src/jsutils.h b/src/jsutils.h index 1319113d87..6e0550010c 100755 --- a/src/jsutils.h +++ b/src/jsutils.h @@ -58,6 +58,7 @@ #define ESPR_NO_SOFTWARE_I2C 1 #endif #define ESPR_NO_REGEX_OPTIMISE 1 +#define ESPR_NO_PASSWORD 1 #endif // SAVE_ON_FLASH #ifdef SAVE_ON_FLASH_EXTREME #define ESPR_NO_BLUETOOTH_MESSAGES 1 diff --git a/src/jswrap_date.c b/src/jswrap_date.c index 235757690e..fad337c4ea 100644 --- a/src/jswrap_date.c +++ b/src/jswrap_date.c @@ -736,6 +736,7 @@ JsVar *jswrap_date_toString(JsVar *parent) { "type" : "method", "class" : "Date", "name" : "toUTCString", + "ifndef" : "SAVE_ON_FLASH", "generate" : "jswrap_date_toUTCString", "return" : ["JsVar","A String"], "typescript" : "toUTCString(): string;" diff --git a/src/jswrap_espruino.c b/src/jswrap_espruino.c index 20fa1d03f7..d7ef62f23c 100644 --- a/src/jswrap_espruino.c +++ b/src/jswrap_espruino.c @@ -2072,6 +2072,7 @@ JsVar *jswrap_espruino_HSBtoRGB(JsVarFloat hue, JsVarFloat sat, JsVarFloat bri, "type" : "staticmethod", "class" : "E", "name" : "setPassword", + "ifndef" : "SAVE_ON_FLASH", "generate" : "jswrap_espruino_setPassword", "params" : [ ["password","JsVar","The password - max 20 chars"] @@ -2093,25 +2094,30 @@ from unknown sources) or read the device's firmware then they may be able to obtain it. */ void jswrap_espruino_setPassword(JsVar *pwd) { +#ifndef ESPR_NO_PASSWORD if (pwd) pwd = jsvAsString(pwd); jsvUnLock(jsvObjectSetChild(execInfo.hiddenRoot, PASSWORD_VARIABLE_NAME, pwd)); +#endif } /*JSON{ "type" : "staticmethod", "class" : "E", "name" : "lockConsole", + "ifndef" : "SAVE_ON_FLASH", "generate" : "jswrap_espruino_lockConsole" } If a password has been set with `E.setPassword()`, this will lock the console so the password needs to be entered to unlock it. */ void jswrap_espruino_lockConsole() { +#ifndef ESPR_NO_PASSWORD JsVar *pwd = jsvObjectGetChildIfExists(execInfo.hiddenRoot, PASSWORD_VARIABLE_NAME); if (pwd) jsiStatus |= JSIS_PASSWORD_PROTECTED; jsvUnLock(pwd); +#endif } /*JSON{ @@ -2601,6 +2607,7 @@ type PowerUsage = { "type" : "staticmethod", "class" : "E", "name" : "getPowerUsage", + "ifndef" : "SAVE_ON_FLASH", "generate" : "jswrap_espruino_getPowerUsage", "return" : ["JsVar","An object detailing power usage in microamps"], "typescript" : "getPowerUsage(): PowerUsage;" diff --git a/src/jswrap_interactive.c b/src/jswrap_interactive.c index dde3b1eb49..c3164ff7c4 100644 --- a/src/jswrap_interactive.c +++ b/src/jswrap_interactive.c @@ -347,6 +347,7 @@ void jswrap_interactive_setTime(JsVarFloat time) { /*JSON{ "type" : "function", "name" : "getSerial", + "ifndef" : "SAVE_ON_FLASH", "generate" : "jswrap_interface_getSerial", "return" : ["JsVar","The board's serial number"] } diff --git a/src/jswrap_math.c b/src/jswrap_math.c index 2652e3184f..af13f791ed 100644 --- a/src/jswrap_math.c +++ b/src/jswrap_math.c @@ -101,6 +101,7 @@ This is a standard JavaScript class that contains useful Maths routines "type" : "staticproperty", "class" : "Math", "name" : "LN2", + "ifndef" : "SAVE_ON_FLASH", "generate_full" : "0.6931471805599453", "return" : ["float","The natural logarithm of 2 - 0.6931471805599453"] }*/ @@ -108,6 +109,7 @@ This is a standard JavaScript class that contains useful Maths routines "type" : "staticproperty", "class" : "Math", "name" : "LN10", + "ifndef" : "SAVE_ON_FLASH", "generate_full" : "2.302585092994046", "return" : ["float","The natural logarithm of 10 - 2.302585092994046"] }*/ @@ -115,6 +117,7 @@ This is a standard JavaScript class that contains useful Maths routines "type" : "staticproperty", "class" : "Math", "name" : "LOG2E", + "ifndef" : "SAVE_ON_FLASH", "generate_full" : "1.4426950408889634", "return" : ["float","The base 2 logarithm of e - 1.4426950408889634"] }*/ @@ -122,6 +125,7 @@ This is a standard JavaScript class that contains useful Maths routines "type" : "staticproperty", "class" : "Math", "name" : "LOG10E", + "ifndef" : "SAVE_ON_FLASH", "generate_full" : "0.4342944819032518", "return" : ["float","The base 10 logarithm of e - 0.4342944819032518"] }*/ @@ -129,6 +133,7 @@ This is a standard JavaScript class that contains useful Maths routines "type" : "staticproperty", "class" : "Math", "name" : "SQRT2", + "ifndef" : "SAVE_ON_FLASH", "generate_full" : "1.4142135623730951", "return" : ["float","The square root of 2 - 1.4142135623730951"] }*/ @@ -136,6 +141,7 @@ This is a standard JavaScript class that contains useful Maths routines "type" : "staticproperty", "class" : "Math", "name" : "SQRT1_2", + "ifndef" : "SAVE_ON_FLASH", "generate_full" : "0.7071067811865476", "return" : ["float","The square root of 1/2 - 0.7071067811865476"] }*/ diff --git a/src/jswrap_number.c b/src/jswrap_number.c index f02290c659..0a03b638e6 100644 --- a/src/jswrap_number.c +++ b/src/jswrap_number.c @@ -144,6 +144,7 @@ JsVar *jswrap_number_toFixed(JsVar *parent, int decimals) { /*JSON{ "type" : "variable", "name" : "HIGH", + "ifndef" : "SAVE_ON_FLASH", "generate_full" : "1", "return" : ["int32","Logic 1 for Arduino compatibility - this is the same as just typing `1`"], "typescript" : "declare const HIGH: true;" @@ -152,6 +153,7 @@ JsVar *jswrap_number_toFixed(JsVar *parent, int decimals) { /*JSON{ "type" : "variable", "name" : "LOW", + "ifndef" : "SAVE_ON_FLASH", "generate_full" : "0", "return" : ["int32","Logic 0 for Arduino compatibility - this is the same as just typing `0`"], "typescript" : "declare const LOW: false;" diff --git a/src/jswrap_serial.c b/src/jswrap_serial.c index 29774b5d95..788f5b4050 100644 --- a/src/jswrap_serial.c +++ b/src/jswrap_serial.c @@ -104,6 +104,7 @@ Espruino boards) "type" : "staticmethod", "class" : "Serial", "name" : "find", + "ifndef" : "SAVE_ON_FLASH", "generate_full" : "jshGetDeviceObjectFor(JSH_USART1, JSH_USARTMAX, pin)", "params" : [ ["pin","pin","A pin to search with"] diff --git a/src/jswrap_spi_i2c.c b/src/jswrap_spi_i2c.c index dc1c417ad4..5d21538de1 100644 --- a/src/jswrap_spi_i2c.c +++ b/src/jswrap_spi_i2c.c @@ -73,6 +73,7 @@ JsVar *jswrap_spi_constructor() { "type" : "staticmethod", "class" : "SPI", "name" : "find", + "ifndef" : "SAVE_ON_FLASH", "generate_full" : "jshGetDeviceObjectFor(JSH_SPI1, JSH_SPIMAX, pin)", "params" : [ ["pin","pin","A pin to search with"] @@ -532,6 +533,7 @@ JsVar *jswrap_i2c_constructor() { "type" : "staticmethod", "class" : "I2C", "name" : "find", + "ifndef" : "SAVE_ON_FLASH", "generate_full" : "jshGetDeviceObjectFor(JSH_I2C1, JSH_I2CMAX, pin)", "params" : [ ["pin","pin","A pin to search with"] From 743af5ddd20c188241c1cd73697a6f36f97b4c68 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Tue, 3 Dec 2024 15:32:58 +0000 Subject: [PATCH 4/7] add deprecation docs --- libs/bluetooth/jswrap_bluetooth.c | 1 + libs/hexbadge/jswrap_hexbadge.c | 1 + libs/network/esp8266/jswrap_esp8266_network.c | 1 + libs/pixljs/jswrap_pixljs.c | 1 + libs/puckjs/jswrap_puck.c | 1 + scripts/common.py | 8 +++++++- src/jswrap_number.c | 2 ++ src/jswrap_serial.c | 1 + src/jswrap_spi_i2c.c | 2 ++ 9 files changed, 17 insertions(+), 1 deletion(-) diff --git a/libs/bluetooth/jswrap_bluetooth.c b/libs/bluetooth/jswrap_bluetooth.c index 8165c3d968..a455c38011 100644 --- a/libs/bluetooth/jswrap_bluetooth.c +++ b/libs/bluetooth/jswrap_bluetooth.c @@ -2365,6 +2365,7 @@ void jswrap_ble_setTxPower(JsVarInt pwr) { "type" : "staticmethod", "class" : "NRF", "name" : "setLowPowerConnection", + "deprecated" : true, "generate" : "jswrap_ble_setLowPowerConnection", "params" : [ ["lowPower","bool","Whether the connection is low power or not"] diff --git a/libs/hexbadge/jswrap_hexbadge.c b/libs/hexbadge/jswrap_hexbadge.c index b4d008274a..388627e757 100644 --- a/libs/hexbadge/jswrap_hexbadge.c +++ b/libs/hexbadge/jswrap_hexbadge.c @@ -192,6 +192,7 @@ int jswrap_badge_capSense(int corner) { "type" : "staticmethod", "class" : "Badge", "name" : "getBatteryPercentage", + "deprecated" : true, "generate" : "jswrap_badge_getBatteryPercentage", "return" : ["int", "A percentage between 0 and 100" ] } diff --git a/libs/network/esp8266/jswrap_esp8266_network.c b/libs/network/esp8266/jswrap_esp8266_network.c index 83451e132a..4b1e474933 100644 --- a/libs/network/esp8266/jswrap_esp8266_network.c +++ b/libs/network/esp8266/jswrap_esp8266_network.c @@ -1270,6 +1270,7 @@ void jswrap_ESP8266_wifi_soft_init() { "class" : "ESP8266", "ifdef" : "ESP8266", "name" : "ping", + "deprecated" : true, "generate" : "jswrap_wifi_ping", "params" : [ ["ipAddr", "JsVar", "A string representation of an IP address."], diff --git a/libs/pixljs/jswrap_pixljs.c b/libs/pixljs/jswrap_pixljs.c index cd794be7f8..8f8bdeff2a 100644 --- a/libs/pixljs/jswrap_pixljs.c +++ b/libs/pixljs/jswrap_pixljs.c @@ -47,6 +47,7 @@ Class containing utility functions for "type" : "staticmethod", "class" : "Pixl", "name" : "getBatteryPercentage", + "deprecated" : true, "generate" : "jswrap_espruino_getBattery", "return" : ["int", "A percentage between 0 and 100" ] } diff --git a/libs/puckjs/jswrap_puck.c b/libs/puckjs/jswrap_puck.c index acbcc0085e..b285d04223 100644 --- a/libs/puckjs/jswrap_puck.c +++ b/libs/puckjs/jswrap_puck.c @@ -1349,6 +1349,7 @@ JsVarFloat jswrap_puck_light() { "class" : "Puck", "ifdef" : "PUCKJS", "name" : "getBatteryPercentage", + "deprecated" : true, "generate" : "jswrap_espruino_getBattery", "return" : ["int", "A percentage between 0 and 100" ] } diff --git a/scripts/common.py b/scripts/common.py index 23739b9111..da2d5a80dd 100644 --- a/scripts/common.py +++ b/scripts/common.py @@ -71,7 +71,9 @@ def f(*popenargs, **kwargs): # // EV_CUSTOM = Called whenever an event of type EV_CUSTOM is received (jswOnCustomEvent(event)) # // EV_xxx = Something to be called with a character in an IRQ when it is received (eg. EV_SERIAL1) (jswOnCharEvent) # // powerusage = fn(JsVar*) called with an object, and should insert fields for deviec names and estimated power usage in uA (jswGetPowerUsage) -# "class" : "Double", "name" : "doubleToIntBits", +# "class" : "Double", +# "name" : "doubleToIntBits", +# "deprecated" : "2v24", // mark that this may be removed in the future (version=when it was deprecated). Adds a comment to description # "needs_parentName":true, // optional - if for a method, this makes the first 2 args parent+parentName (not just parent) # "generate_full|generate|wrap" : "*(JsVarInt*)&x", // if generate=false, it'll only be used for docs # "generate_js" : "full/file/path.js", // you can supply a JS file instead of 'generate' above. Should be of the form '(function(args) { ... })' @@ -206,11 +208,15 @@ def get_jsondata(is_for_document, parseArgs = True, boardObject = False): try: jsondata = json.loads(jsonstring) if len(description): jsondata["description"] = description; + else: jsondata["description"] = "" jsondata["filename"] = jswrap if jswrap[-2:]==".c": jsondata["include"] = jswrap[:-2]+".h" jsondata["githublink"] = "https://github.com/espruino/Espruino/blob/"+githash+"/"+jswrap+"#L"+str(linenumber) + if "deprecated" in jsondata and not "deprecated" in jsondata["description"].lower(): + jsondata["description"] = "**DEPRECATED** - this will be removed in subsequent versions of Espruino\n\n" + jsondata["description"]; + dropped_prefix = "Dropped " if "name" in jsondata: dropped_prefix += jsondata["name"]+" " elif "class" in jsondata: dropped_prefix += jsondata["class"]+" " diff --git a/src/jswrap_number.c b/src/jswrap_number.c index 0a03b638e6..106116b8d7 100644 --- a/src/jswrap_number.c +++ b/src/jswrap_number.c @@ -145,6 +145,7 @@ JsVar *jswrap_number_toFixed(JsVar *parent, int decimals) { "type" : "variable", "name" : "HIGH", "ifndef" : "SAVE_ON_FLASH", + "deprecated" : true, "generate_full" : "1", "return" : ["int32","Logic 1 for Arduino compatibility - this is the same as just typing `1`"], "typescript" : "declare const HIGH: true;" @@ -154,6 +155,7 @@ JsVar *jswrap_number_toFixed(JsVar *parent, int decimals) { "type" : "variable", "name" : "LOW", "ifndef" : "SAVE_ON_FLASH", + "deprecated" : true, "generate_full" : "0", "return" : ["int32","Logic 0 for Arduino compatibility - this is the same as just typing `0`"], "typescript" : "declare const LOW: false;" diff --git a/src/jswrap_serial.c b/src/jswrap_serial.c index 788f5b4050..da3690e00c 100644 --- a/src/jswrap_serial.c +++ b/src/jswrap_serial.c @@ -104,6 +104,7 @@ Espruino boards) "type" : "staticmethod", "class" : "Serial", "name" : "find", + "deprecated" : true, "ifndef" : "SAVE_ON_FLASH", "generate_full" : "jshGetDeviceObjectFor(JSH_USART1, JSH_USARTMAX, pin)", "params" : [ diff --git a/src/jswrap_spi_i2c.c b/src/jswrap_spi_i2c.c index 5d21538de1..cc627f939f 100644 --- a/src/jswrap_spi_i2c.c +++ b/src/jswrap_spi_i2c.c @@ -73,6 +73,7 @@ JsVar *jswrap_spi_constructor() { "type" : "staticmethod", "class" : "SPI", "name" : "find", + "deprecated" : true, "ifndef" : "SAVE_ON_FLASH", "generate_full" : "jshGetDeviceObjectFor(JSH_SPI1, JSH_SPIMAX, pin)", "params" : [ @@ -533,6 +534,7 @@ JsVar *jswrap_i2c_constructor() { "type" : "staticmethod", "class" : "I2C", "name" : "find", + "deprecated" : true, "ifndef" : "SAVE_ON_FLASH", "generate_full" : "jshGetDeviceObjectFor(JSH_I2C1, JSH_I2CMAX, pin)", "params" : [ From 1c3ae855737e068f9e176643e8dff4c17104306f Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Tue, 3 Dec 2024 12:26:57 +0000 Subject: [PATCH 5/7] Extracted graphicsBlendColorRGB565 to its own function --- libs/graphics/graphics.c | 29 ++++++++++++++++------------- libs/graphics/graphics.h | 2 ++ 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/libs/graphics/graphics.c b/libs/graphics/graphics.c index d5ace6863c..da2772dcdf 100644 --- a/libs/graphics/graphics.c +++ b/libs/graphics/graphics.c @@ -366,6 +366,20 @@ JsGraphicsSetPixelFn graphicsGetSetPixelUnclippedFn(JsGraphics *gfx, int x1, int return gfx->setPixel; // fast } +/// Merge one color into another based RGB565(amt is 0..256) +uint16_t graphicsBlendColorRGB565(uint16_t f, uint16_t b, int amt) { + unsigned int br = (b>>11)&0x1F; + unsigned int bg = (b>>5)&0x3F; + unsigned int bb = b&0x1F; + unsigned int fr = (f>>11)&0x1F; + unsigned int fg = (f>>5)&0x3F; + unsigned int fb = f&0x1F; + unsigned int ri = (br*(256-amt) + fr*amt) >> 8; + unsigned int gi = (bg*(256-amt) + fg*amt) >> 8; + unsigned int bi = (bb*(256-amt) + fb*amt) >> 8; + return (bi | gi<<5 | ri<<11); +} + /// Merge one color into another based on current bit depth (amt is 0..256) uint32_t graphicsBlendColor(JsGraphics *gfx, unsigned int fg, unsigned int bg, int iamt) { unsigned int amt = (iamt>0) ? (unsigned)iamt : 0; @@ -374,18 +388,7 @@ uint32_t graphicsBlendColor(JsGraphics *gfx, unsigned int fg, unsigned int bg, i // TODO: if our graphics instance is paletted this isn't correct! return (bg*(256-amt) + fg*amt + 127) >> 8; } else if (gfx->data.bpp==16) { // Blend from bg to fg - unsigned int b = bg; - unsigned int br = (b>>11)&0x1F; - unsigned int bg = (b>>5)&0x3F; - unsigned int bb = b&0x1F; - unsigned int f = fg; - unsigned int fr = (f>>11)&0x1F; - unsigned int fg = (f>>5)&0x3F; - unsigned int fb = f&0x1F; - unsigned int ri = (br*(256-amt) + fr*amt) >> 8; - unsigned int gi = (bg*(256-amt) + fg*amt) >> 8; - unsigned int bi = (bb*(256-amt) + fb*amt) >> 8; - return (bi | gi<<5 | ri<<11); + return graphicsBlendColorRGB565(fg,bg,iamt); #ifdef ESPR_GRAPHICS_12BIT } else if (gfx->data.bpp==12) { // Blend from bg to fg unsigned int b = bg; @@ -911,7 +914,7 @@ void graphicsScroll(JsGraphics *gfx, int xdir, int ydir) { static void graphicsDrawString(JsGraphics *gfx, int x1, int y1, const char *str) { // no need to modify coordinates as setPixel does that while (*str) { -#ifdef USE_FONT_6X8 +#ifdef USE_FONT_6X8 graphicsDrawChar6x8(gfx,x1,y1,*(str++),1,1,false); x1 = (int)(x1 + 6); #else diff --git a/libs/graphics/graphics.h b/libs/graphics/graphics.h index 75c4fa3ae4..189e70690f 100644 --- a/libs/graphics/graphics.h +++ b/libs/graphics/graphics.h @@ -201,6 +201,8 @@ void graphicsSetModified(JsGraphics *gfx, int x1, int y1, int x2, int y2); JsGraphicsSetPixelFn graphicsGetSetPixelFn(JsGraphics *gfx); /// Get a setPixel function and set modified area (assuming no clipping) (inclusive of x2,y2) - if all is ok it can choose a faster draw function JsGraphicsSetPixelFn graphicsGetSetPixelUnclippedFn(JsGraphics *gfx, int x1, int y1, int x2, int y2, bool coordsRotatedAlready); +/// Merge one color into another based RGB565(amt is 0..256) +uint16_t graphicsBlendColorRGB565(uint16_t fg, uint16_t bg, int iamt); /// Merge one color into another based on current bit depth (amt is 0..256) uint32_t graphicsBlendColor(JsGraphics *gfx, unsigned int fg, unsigned int bg, int iamt); /// Merge one color into another based on current bit depth (amt is 0..256) From a7466ef17e42b45da1dfe2eede912f5d53ec948d Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Tue, 3 Dec 2024 12:27:13 +0000 Subject: [PATCH 6/7] Added user-configurable palette --- libs/pipboy/jswrap_pipboy.c | 111 ++++++++++++++++++++++++++++++------ libs/pipboy/jswrap_pipboy.h | 1 + 2 files changed, 93 insertions(+), 19 deletions(-) diff --git a/libs/pipboy/jswrap_pipboy.c b/libs/pipboy/jswrap_pipboy.c index 3a230411b0..8f6fcf3627 100644 --- a/libs/pipboy/jswrap_pipboy.c +++ b/libs/pipboy/jswrap_pipboy.c @@ -49,6 +49,14 @@ uint8_t streamBuffer[STREAM_BUFFER_SIZE+4] __attribute__ ((aligned (8))); // we // Can't go in 64k CCM RAM because no DMA is allowed to CCM! // Maybe if we modified DMA to read to a buffer first? +/** Palette for scanlines +0: even (bright line) +1: odd (dark line) +0: even scan effect +1: odd scan effect + */ +uint16_t palette[4][16]; + typedef enum { ST_NONE, ST_AVI, @@ -201,6 +209,19 @@ void jswrap_pb_videoStart(JsVar *fn, JsVar *options) { f_lseek(&streamFile, (uint32_t)(videoInfo.streamOffset+8)); // go back to start of video data videoFrameTime = jshGetTimeFromMilliseconds(videoInfo.usPerFrame/1000.0); videoNextFrameTime = jshGetSystemTime() + videoFrameTime; + // set palette + for (int i=0;i<256;i++) { + uint16_t c = videoInfo.palette[i]; + uint16_t br = (c>>11)&0x1F; + uint16_t bg = (c>>6)&0x1F; + uint16_t bb = c&0x1F; + uint16_t lum = br; // lum = 0..31 + if (bg>lum) lum=bg; + if (bb>lum) lum=bb; + // We use the non-scan-effect palette here (not idx 2) as it's too bright otherwise + videoInfo.palette[i] = palette[0][lum>>1]; // Should blend so we don't lose 1 bit accuracy? + } + #ifndef LINUX // Set up Audio if (videoInfo.audioBufferSize <= I2S_RING_BUFFER_SIZE*2) { // IF we have audio @@ -1095,27 +1116,36 @@ void jswrap_pb_wake() { If `height` isn't specified the image height is used, otherwise only part of the image can be rendered. */ int scanlinePos = 0; -void getPaletteForLine2bpp(int y, uint16_t *palette) { +void getPaletteForLine2bpp(int y, uint16_t *pal) { int distfromScaline = y-scanlinePos; if (distfromScaline<0) distfromScaline=-distfromScaline; - int brightness = 220 + ((distfromScaline>64)?0:(63-distfromScaline)); - if (brightness>255) brightness=255; - if (y&1) brightness = (brightness*3) >> 2; - palette[3] = (uint16_t)(brightness>>2)<<5; - brightness = (brightness*2)/3; - palette[2] = (uint16_t)(brightness>>2)<<5; - brightness = brightness >> 1; - palette[1] = (uint16_t)(brightness>>2)<<5; -} -void getPaletteForLine4bpp(int y, uint16_t *palette) { + int n = y&1; + if (distfromScaline>64) { + // no overscan effect - too far away + pal[0] = palette[n][0]; + pal[1] = palette[n][5]; + pal[2] = palette[n][10]; + pal[3] = palette[n][15]; + } else { + int a = distfromScaline<<2; // 0..255 + pal[0] = graphicsBlendColorRGB565(palette[n][0],palette[n+2][0],a); + pal[1] = graphicsBlendColorRGB565(palette[n][5],palette[n+2][5],a); + pal[2] = graphicsBlendColorRGB565(palette[n][10],palette[n+2][10],a); + pal[3] = graphicsBlendColorRGB565(palette[n][15],palette[n+2][15],a); + } +} +void getPaletteForLine4bpp(int y, uint16_t *pal) { int distfromScaline = y-scanlinePos; if (distfromScaline<0) distfromScaline=-distfromScaline; - int brightness = 220 + ((distfromScaline>64)?0:(63-distfromScaline)); - if (brightness>255) brightness=255; - if (y&1) brightness = (brightness*3) >> 2; - for (int i=1;i<16;i++) { - int b = (brightness*i)>>4; - palette[i] = (uint16_t)(b>>2)<<5; + int n = y&1; + if (distfromScaline>64) { + // no overscan effect - too far away + for (int i=0;i<16;i++) + pal[i] = palette[n][i]; + } else { + int a = distfromScaline<<2; // 0..255 + for (int i=0;i<16;i++) + pal[i] = graphicsBlendColorRGB565(palette[n][i],palette[n+2][i],a); } } void jswrap_pb_blitImage(JsVar *image, int x, int y, JsVar *options) { @@ -1214,6 +1244,39 @@ JsVar *jswrap_pb_streamPlaying() { return 0; } +/*JSON{ + "type" : "staticmethod", + "class" : "Pip", + "name" : "setPalette", + "generate" : "jswrap_pb_setPalette", + "params" : [ + ["palette","JsVar","A 4 element array of 16 element arrays"] + ] +} +Set the colour palette used for rendering everything on PipBoy +*/ +void jswrap_pb_setPalette(JsVar *pal) { + if (!jsvIsArray(pal)) { + jsExceptionHere(JSET_ERROR, "Expecting array of arrays"); + return; + } + for (int i=0;i<4;i++) { + JsVar *a = jsvGetArrayItem(pal, i); + if (!jsvIsIterable(a)) { + jsExceptionHere(JSET_ERROR, "Expecting array of arrays, pal[%d] is %t", i, a); + jsvUnLock(a); + return; + } + JsvIterator it; + jsvIteratorNew(&it, a, JSIF_EVERY_ARRAY_ELEMENT); + for (int c=0;c<16;c++) { + palette[i][c] = (uint16_t)jsvIteratorGetIntegerValue(&it); + jsvIteratorNext(&it); + } + jsvIteratorFree(&it); + jsvUnLock(a); + } +} /*JSON{ "type" : "init", @@ -1222,11 +1285,21 @@ JsVar *jswrap_pb_streamPlaying() { void jswrap_pb_init() { // Enable watchdog jswrap_espruino_enableWatchdog(15, NULL); // init watchdog, auto mode + // Set up colour palette + for (uint16_t i=0;i<16;i++) { + // normally we're a bit more dim + uint16_t b = (i*220) >> 6; // 0..63 + palette[0][i] = b << 5; // even + palette[1][i] = ((b*3)>>2) << 5; // odd + // overscan - full range + palette[2][i] = i << 7; + palette[3][i] = (i*3) << 5; + } // splash screen const unsigned char img_raw[] = {199, 17, 2, 0, 0, 31, 255, 255, 255, 255, 255, 255, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 255, 255, 255, 255, 255, 255, 233, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 255, 255, 255, 255, 255, 255, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 91, 255, 213, 85, 85, 111, 255, 192, 11, 255, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 255, 249, 85, 85, 85, 255, 252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 253, 0, 0, 0, 191, 253, 0, 127, 255, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 255, 128, 0, 0, 7, 255, 208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 255, 224, 0, 0, 7, 255, 224, 127, 255, 224, 2, 255, 255, 255, 255, 255, 233, 0, 0, 0, 0, 0, 0, 0, 0, 255, 248, 0, 0, 0, 63, 254, 0, 107, 255, 255, 255, 232, 0, 191, 255, 255, 224, 127, 255, 255, 248, 0, 0, 63, 255, 255, 255, 255, 255, 254, 7, 255, 255, 192, 47, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 15, 255, 255, 255, 255, 255, 255, 224, 127, 255, 255, 255, 255, 253, 91, 255, 255, 255, 131, 255, 255, 255, 224, 0, 3, 255, 255, 255, 255, 255, 255, 224, 26, 255, 252, 0, 107, 255, 250, 170, 170, 255, 248, 0, 170, 170, 170, 170, 168, 0, 191, 255, 255, 255, 255, 255, 248, 7, 255, 250, 170, 171, 255, 255, 255, 255, 255, 252, 47, 255, 255, 254, 0, 0, 47, 255, 191, 255, 255, 234, 80, 0, 7, 255, 192, 0, 47, 255, 0, 0, 11, 255, 192, 11, 255, 255, 255, 255, 224, 11, 255, 234, 170, 170, 171, 255, 240, 63, 253, 0, 0, 31, 255, 255, 253, 191, 255, 0, 127, 255, 208, 0, 0, 2, 255, 244, 0, 0, 0, 0, 0, 0, 127, 253, 0, 1, 255, 244, 0, 0, 191, 252, 0, 21, 85, 85, 85, 85, 0, 127, 254, 0, 0, 0, 31, 255, 67, 255, 224, 0, 0, 255, 245, 85, 64, 255, 253, 31, 255, 244, 0, 0, 0, 31, 255, 128, 0, 0, 0, 0, 0, 3, 255, 208, 0, 31, 255, 64, 0, 7, 255, 208, 0, 0, 0, 0, 0, 0, 7, 255, 224, 0, 0, 0, 255, 248, 47, 255, 0, 0, 15, 255, 128, 0, 1, 255, 255, 255, 248, 0, 0, 0, 85, 255, 248, 0, 0, 0, 0, 0, 0, 127, 254, 81, 20, 255, 249, 64, 5, 127, 255, 213, 64, 0, 0, 0, 0, 17, 127, 255, 64, 0, 5, 95, 255, 130, 255, 245, 85, 85, 255, 248, 0, 0, 2, 255, 255, 254, 0, 0, 0, 15, 255, 255, 192, 0, 0, 0, 0, 1, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 64, 0, 0, 0, 3, 255, 255, 255, 255, 255, 255, 255, 248, 15, 255, 255, 255, 255, 255, 128, 0, 0, 3, 255, 255, 128, 0, 0, 0, 127, 255, 252, 0, 0, 0, 0, 0, 11, 255, 255, 255, 255, 255, 255, 255, 255, 255, 250, 255, 224, 0, 0, 0, 0, 31, 255, 255, 255, 255, 255, 255, 249, 0, 11, 255, 255, 255, 255, 144, 0, 0, 0, 127, 255, 208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 255, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 255, 244, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 255, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 255, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 255, 255, 208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 255, 254, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 170, 128, 0, 0, 0, 0, 0, 0}; JsVar *img = jsvNewNativeString((char*)&img_raw[0], sizeof(img_raw)); JsVar *g = jsvNewObject(); // fake object for rendering - graphicsInternal.data.fgColor = 63<<5; + graphicsInternal.data.fgColor = palette[0][15]; jsvUnLock(jswrap_graphics_clear(g, 0)); jsvUnLock(jswrap_graphics_drawImage(g, img, (LCD_WIDTH-200)/2, LCD_HEIGHT/2-16, NULL)); graphicsInternal.data.fontSize = JSGRAPHICS_FONTSIZE_6X8+1; @@ -1303,7 +1376,7 @@ void jswrap_pb_init() { if (res == FR_NO_FILE) msg = jsvNewFromString("NO VERSION FILE"); else if (res == FR_NOT_ENABLED) msg = jsvNewFromString("NO SD CARD"); else msg = jsvVarPrintf("SD CARD ERROR %d", res); - graphicsInternal.data.fgColor = 63<<5; // green + graphicsInternal.data.fgColor = palette[0][15]; // green graphicsInternal.data.fontSize = JSGRAPHICS_FONTSIZE_6X8+1; graphicsInternal.data.fontAlignX = 0; jsvUnLock(jswrap_graphics_drawString(g, msg, (LCD_WIDTH/2), LCD_HEIGHT/2+14, 0)); diff --git a/libs/pipboy/jswrap_pipboy.h b/libs/pipboy/jswrap_pipboy.h index 9d11b1138e..ae56d556d6 100644 --- a/libs/pipboy/jswrap_pipboy.h +++ b/libs/pipboy/jswrap_pipboy.h @@ -36,6 +36,7 @@ void jswrap_pb_blitImage(JsVar *image, int x, int y, JsVar *options); void jswrap_pb_getAudioWaveform(JsVar *dst, int y1, int y2); bool jswrap_pb_audioIsPlaying(); JsVar *jswrap_pb_streamPlaying(); +void jswrap_pb_setPalette(JsVar *pal); void jswrap_pb_init(); void jswrap_pb_kill(); From 610a0a92594b0d037edb88decb947d96ec7a3d19 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Tue, 3 Dec 2024 12:29:02 +0000 Subject: [PATCH 7/7] docs --- libs/pipboy/jswrap_pipboy.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/libs/pipboy/jswrap_pipboy.c b/libs/pipboy/jswrap_pipboy.c index 8f6fcf3627..bcfb1ad01f 100644 --- a/libs/pipboy/jswrap_pipboy.c +++ b/libs/pipboy/jswrap_pipboy.c @@ -1254,6 +1254,25 @@ JsVar *jswrap_pb_streamPlaying() { ] } Set the colour palette used for rendering everything on PipBoy + +eg: + +``` +var pal = [ + new Uint16Array(16), + new Uint16Array(16), + new Uint16Array(16), + new Uint16Array(16) +]; +// Orangey +for (var i=0;i<16;i++) { + pal[0][i] = g.toColor(i/15,i/30,0); // 0: even (bright line) + pal[1][i] = g.toColor(i/30,i/60,0); // 1: odd (dark line) + pal[2][i] = g.toColor(i/10,i/20,0); // 0: even scan effect + pal[3][i] = g.toColor(i/20,i/40,0); // 1: odd scan effect +} +Pip.setPalette(pal); +``` */ void jswrap_pb_setPalette(JsVar *pal) { if (!jsvIsArray(pal)) {