diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..3715edfd --- /dev/null +++ b/.clang-format @@ -0,0 +1,6 @@ +BasedOnStyle: LLVM +IndentWidth: 4 +UseTab: Never +ColumnLimit: 100 +AllowShortFunctionsOnASingleLine: Inline +SortIncludes: false diff --git a/libs/bluetooth/bluetooth.cpp b/libs/bluetooth/bluetooth.cpp index effd4f2a..2e300cf8 100644 --- a/libs/bluetooth/bluetooth.cpp +++ b/libs/bluetooth/bluetooth.cpp @@ -87,15 +87,15 @@ namespace bluetooth { } //% - void uartWriteString(StringData *data) { + void uartWriteString(String data) { startUartService(); - uart->send(ManagedString(data)); + uart->send(MSTR(data)); } //% - StringData* uartReadUntil(StringData *del) { + String uartReadUntil(String del) { startUartService(); - return uart->readUntil(ManagedString(del)).leakData(); + return PSTR(uart->readUntil(MSTR(del))); } /** @@ -104,9 +104,9 @@ namespace bluetooth { */ //% help=bluetooth/on-uart-data-received //% weight=18 blockId=bluetooth_on_data_received block="bluetooth|on data received %delimiters=serial_delimiter_conv" - void onUartDataReceived(StringData* delimiters, Action body) { + void onUartDataReceived(String delimiters, Action body) { startUartService(); - uart->eventOn(ManagedString(delimiters)); + uart->eventOn(MSTR(delimiters)); registerWithDal(MICROBIT_ID_BLE_UART, MICROBIT_UART_S_EVT_DELIM_MATCH, body); } @@ -142,10 +142,10 @@ namespace bluetooth { //% blockId=eddystone_advertise_url block="bluetooth advertise url %url|with power %power|connectable %connectable" //% parts=bluetooth weight=11 blockGap=8 //% help=bluetooth/advertise-url blockExternalInputs=1 - void advertiseUrl(StringData* url, int power, bool connectable) { + void advertiseUrl(String url, int power, bool connectable) { power = min(MICROBIT_BLE_POWER_LEVELS-1, max(0, power)); int8_t level = CALIBRATED_POWERS[power]; - uBit.bleManager.advertiseEddystoneUrl(ManagedString(url), level, connectable); + uBit.bleManager.advertiseEddystoneUrl(MSTR(url), level, connectable); uBit.bleManager.setTransmitPower(power); } @@ -157,14 +157,12 @@ namespace bluetooth { */ //% parts=bluetooth weight=12 advanced=true void advertiseUidBuffer(Buffer nsAndInstance, int power, bool connectable) { - ManagedBuffer buf(nsAndInstance); - if (buf.length() != 16) return; + auto buf = nsAndInstance; + if (buf->length != 16) return; power = min(MICROBIT_BLE_POWER_LEVELS-1, max(0, power)); int8_t level = CALIBRATED_POWERS[power]; - uint8_t uidNs[10]; buf.readBytes(uidNs, 0, 10); - uint8_t uidInst[6]; buf.readBytes(uidInst, 10, 6); - uBit.bleManager.advertiseEddystoneUid((const char*)uidNs, (const char*)uidInst, level, connectable); + uBit.bleManager.advertiseEddystoneUid((const char*)buf->data, (const char*)buf->data + 10, level, connectable); } /** diff --git a/libs/core/ManagedBuffer.cpp b/libs/core/ManagedBuffer.cpp deleted file mode 100644 index 25b9cb26..00000000 --- a/libs/core/ManagedBuffer.cpp +++ /dev/null @@ -1,373 +0,0 @@ -#include "MicroBit.h" -#include "ManagedBuffer.h" -#include - -static const char empty[] __attribute__ ((aligned (4))) = "\xff\xff\0\0\0"; - -/** - * Internal constructor helper. - * Configures this ManagedBuffer to refer to the static empty buffer. - */ -void ManagedBuffer::initEmpty() -{ - ptr = (BufferData*)(void*)empty; -} - -/** - * Default Constructor. - * Creates an empty ManagedBuffer. - * - * Example: - * @code - * ManagedBuffer p(); - * @endcode - */ -ManagedBuffer::ManagedBuffer() -{ - initEmpty(); -} - -/** - * Constructor. - * Creates an empty ManagedBuffer of the given size. - * - * @param length The length of the buffer to create. - * - * Example: - * @code - * ManagedBuffer p(16); // Creates a ManagedBuffer 16 bytes long. - * @endcode - */ -ManagedBuffer::ManagedBuffer(int length) -{ - this->init(NULL, length); -} - -/** - * Constructor. - * Creates a new ManagedBuffer of the given size, - * and fills it with the data provided. - * - * @param data The data with which to fill the buffer. - * @param length The length of the buffer to create. - * - * Example: - * @code - * uint8_t buf = {13,5,2}; - * ManagedBuffer p(buf, 3); // Creates a ManagedBuffer 3 bytes long. - * @endcode - */ -ManagedBuffer::ManagedBuffer(uint8_t *data, int length) -{ - this->init(data, length); -} - -/** - * Copy Constructor. - * Add ourselves as a reference to an existing ManagedBuffer. - * - * @param buffer The ManagedBuffer to reference. - * - * Example: - * @code - * ManagedBuffer p(); - * ManagedBuffer p2(i); // Refers to the same buffer as p. - * @endcode - */ -ManagedBuffer::ManagedBuffer(const ManagedBuffer &buffer) -{ - ptr = buffer.ptr; - ptr->incr(); -} - -/** - * Constructor. - * Create a buffer from a raw BufferData pointer. It will ptr->incr(). This is to be used by specialized runtimes. - * - * @param p The pointer to use. - */ -ManagedBuffer::ManagedBuffer(BufferData *p) -{ - ptr = p; - ptr->incr(); -} - -/** - * Internal constructor-initialiser. - * - * @param data The data with which to fill the buffer. - * @param length The length of the buffer to create. - * - */ -void ManagedBuffer::init(uint8_t *data, int length) -{ - if (length <= 0) { - initEmpty(); - return; - } - - ptr = (BufferData *) malloc(sizeof(BufferData) + length); - ptr->init(); - - ptr->length = length; - - // Copy in the data buffer, if provided. - if (data) - memcpy(ptr->payload, data, length); - else - memset(ptr->payload, 0, length); -} - -/** - * Destructor. - * Removes buffer resources held by the instance. - */ -ManagedBuffer::~ManagedBuffer() -{ - ptr->decr(); -} - -/** - * Copy assign operation. - * - * Called when one ManagedBuffer is assigned the value of another using the '=' operator. - * Decrements our reference count and free up the buffer as necessary. - * Then, update our buffer to refer to that of the supplied ManagedBuffer, - * and increase its reference count. - * - * @param p The ManagedBuffer to reference. - * - * Example: - * @code - * uint8_t buf = {13,5,2}; - * ManagedBuffer p1(16); - * ManagedBuffer p2(buf, 3); - * - * p1 = p2; - * @endcode - */ -ManagedBuffer& ManagedBuffer::operator = (const ManagedBuffer &p) -{ - if(ptr == p.ptr) - return *this; - - ptr->decr(); - ptr = p.ptr; - ptr->incr(); - - return *this; -} - -/** - * Equality operation. - * - * Called when one ManagedBuffer is tested to be equal to another using the '==' operator. - * - * @param p The ManagedBuffer to test ourselves against. - * @return true if this ManagedBuffer is identical to the one supplied, false otherwise. - * - * Example: - * @code - * - * uint8_t buf = {13,5,2}; - * ManagedBuffer p1(16); - * ManagedBuffer p2(buf, 3); - * - * if(p1 == p2) // will be true - * uBit.display.scroll("same!"); - * @endcode - */ -bool ManagedBuffer::operator== (const ManagedBuffer& p) -{ - if (ptr == p.ptr) - return true; - else - return (ptr->length == p.ptr->length && (memcmp(ptr->payload, p.ptr->payload, ptr->length)==0)); -} - -/** - * Sets the byte at the given index to value provided. - * @param position The index of the byte to change. - * @param value The new value of the byte (0-255). - * @return MICROBIT_OK, or MICROBIT_INVALID_PARAMETER. - * - * Example: - * @code - * ManagedBuffer p1(16); - * p1.setByte(0,255); // Sets the firts byte in the buffer to the value 255. - * @endcode - */ -int ManagedBuffer::setByte(int position, uint8_t value) -{ - if (0 <= position && position < ptr->length) - { - ptr->payload[position] = value; - return MICROBIT_OK; - } - else - { - return MICROBIT_INVALID_PARAMETER; - } -} - -/** - * Determines the value of the given byte in the buffer. - * - * @param position The index of the byte to read. - * @return The value of the byte at the given position, or MICROBIT_INVALID_PARAMETER. - * - * Example: - * @code - * ManagedBuffer p1(16); - * p1.setByte(0,255); // Sets the firts byte in the buffer to the value 255. - * p1.getByte(0); // Returns 255. - * @endcode - */ -int ManagedBuffer::getByte(int position) -{ - if (0 <= position && position < ptr->length) - return ptr->payload[position]; - else - return MICROBIT_INVALID_PARAMETER; -} - -/** - * Get current ptr, do not decr() it, and set the current instance to an empty buffer. - * This is to be used by specialized runtimes which pass BufferData around. - */ -BufferData *ManagedBuffer::leakData() -{ - BufferData* res = ptr; - initEmpty(); - return res; -} - - -int ManagedBuffer::fill(uint8_t value, int offset, int length) -{ - if (offset < 0 || offset > ptr->length) - return MICROBIT_INVALID_PARAMETER; - if (length < 0) - length = ptr->length; - length = min(length, ptr->length - offset); - - memset(ptr->payload + offset, value, length); - - return MICROBIT_OK; -} - -ManagedBuffer ManagedBuffer::slice(int offset, int length) const -{ - offset = min(ptr->length, offset); - if (length < 0) - length = ptr->length; - length = min(length, ptr->length - offset); - return ManagedBuffer(ptr->payload + offset, length); -} - -void ManagedBuffer::shift(int offset, int start, int len) -{ - if (len < 0) len = ptr->length - start; - if (start < 0 || start + len > ptr->length || start + len < start - || len == 0 || offset == 0 || offset == INT_MIN) return; - if (offset <= -len || offset >= len) { - fill(0); - return; - } - - uint8_t *data = ptr->payload + start; - if (offset < 0) { - offset = -offset; - memmove(data + offset, data, len - offset); - memset(data, 0, offset); - } else { - len = len - offset; - memmove(data, data + offset, len); - memset(data + len, 0, offset); - } -} - -void ManagedBuffer::rotate(int offset, int start, int len) -{ - if (len < 0) len = ptr->length - start; - if (start < 0 || start + len > ptr-> length || start + len < start - || len == 0 || offset == 0 || offset == INT_MIN) return; - - if (offset < 0) - offset += len << 8; // try to make it positive - offset %= len; - if (offset < 0) - offset += len; - - uint8_t *data = ptr->payload + start; - - uint8_t *n_first = data + offset; - uint8_t *first = data; - uint8_t *next = n_first; - uint8_t *last = data + len; - - while (first != next) { - uint8_t tmp = *first; - *first++ = *next; - *next++ = tmp; - if (next == last) { - next = n_first; - } else if (first == n_first) { - n_first = next; - } - } -} - -int ManagedBuffer::writeBuffer(int dstOffset, const ManagedBuffer &src, int srcOffset, int length) -{ - if (length < 0) - length = src.length(); - - if (srcOffset < 0 || dstOffset < 0 || dstOffset > ptr->length) - return MICROBIT_INVALID_PARAMETER; - - length = min(src.length() - srcOffset, ptr->length - dstOffset); - - if (length < 0) - return MICROBIT_INVALID_PARAMETER; - - if (ptr == src.ptr) { - memmove(getBytes() + dstOffset, src.ptr->payload + srcOffset, length); - } else { - memcpy(getBytes() + dstOffset, src.ptr->payload + srcOffset, length); - } - - return MICROBIT_OK; -} - -int ManagedBuffer::writeBytes(int offset, uint8_t *src, int length, bool swapBytes) -{ - if (offset < 0 || length < 0 || offset + length > ptr->length) - return MICROBIT_INVALID_PARAMETER; - - if (swapBytes) { - uint8_t *p = ptr->payload + offset + length; - for (int i = 0; i < length; ++i) - *--p = src[i]; - } else { - memcpy(ptr->payload + offset, src, length); - } - - return MICROBIT_OK; -} - -int ManagedBuffer::readBytes(uint8_t *dst, int offset, int length, bool swapBytes) const -{ - if (offset < 0 || length < 0 || offset + length > ptr->length) - return MICROBIT_INVALID_PARAMETER; - - if (swapBytes) { - uint8_t *p = ptr->payload + offset + length; - for (int i = 0; i < length; ++i) - dst[i] = *--p; - } else { - memcpy(dst, ptr->payload + offset, length); - } - - return MICROBIT_OK; -} diff --git a/libs/core/ManagedBuffer.h b/libs/core/ManagedBuffer.h deleted file mode 100644 index 991dd8d7..00000000 --- a/libs/core/ManagedBuffer.h +++ /dev/null @@ -1,257 +0,0 @@ -#ifndef MICROBIT_MANAGED_BUFFER_H -#define MICROBIT_MANAGED_BUFFER_H - -#include "mbed.h" -#include "RefCounted.h" - -struct BufferData : RefCounted -{ - uint16_t length; // The length of the payload in bytes - uint8_t payload[0]; // ManagedBuffer data -}; - -/** - * Class definition for a ManagedBuffer. - * A ManagedBuffer holds a series of bytes, used with MicroBitRadio channels and in other places. - * n.b. This is a mutable, managed type. - */ -class ManagedBuffer -{ - BufferData *ptr; // Pointer to payload data - - public: - - /** - * Default Constructor. - * Creates an empty ManagedBuffer. The 'ptr' field in all empty buffers is shared. - * - * Example: - * @code - * ManagedBuffer p(); - * @endcode - */ - ManagedBuffer(); - - /** - * Constructor. - * Creates a new ManagedBuffer of the given size. - * - * @param length The length of the buffer to create. - * - * Example: - * @code - * ManagedBuffer p(16); // Creates a ManagedBuffer 16 bytes long. - * @endcode - */ - ManagedBuffer(int length); - - /** - * Constructor. - * Creates an empty ManagedBuffer of the given size, - * and fills it with the data provided. - * - * @param data The data with which to fill the buffer. - * @param length The length of the buffer to create. - * - * Example: - * @code - * uint8_t buf[] = {13,5,2}; - * ManagedBuffer p(buf, 3); // Creates a ManagedBuffer 3 bytes long. - * @endcode - */ - ManagedBuffer(uint8_t *data, int length); - - /** - * Copy Constructor. - * Add ourselves as a reference to an existing ManagedBuffer. - * - * @param buffer The ManagedBuffer to reference. - * - * Example: - * @code - * ManagedBuffer p(); - * ManagedBuffer p2(i); // Refers to the same buffer as p. - * @endcode - */ - ManagedBuffer(const ManagedBuffer &buffer); - - /** - * Constructor. - * Create a buffer from a raw BufferData pointer. It will ptr->incr(). This is to be used by specialized runtimes. - * - * @param p The pointer to use. - */ - ManagedBuffer(BufferData *p); - - /** - * Internal constructor helper. - * Configures this ManagedBuffer to refer to the static empty buffer. - */ - void initEmpty(); - - /** - * Internal constructor-initialiser. - * - * @param data The data with which to fill the buffer. - * @param length The length of the buffer to create. - * - */ - void init(uint8_t *data, int length); - - /** - * Destructor. - * Removes buffer resources held by the instance. - */ - ~ManagedBuffer(); - - /** - * Provide an array containing the buffer data. - * @return The contents of this buffer, as an array of bytes. - */ - uint8_t *getBytes() - { - return ptr->payload; - } - - /** - * Get current ptr, do not decr() it, and set the current instance to an empty buffer. - * This is to be used by specialized runtimes which pass BufferData around. - */ - BufferData *leakData(); - - /** - * Copy assign operation. - * - * Called when one ManagedBuffer is assigned the value of another using the '=' operator. - * Decrements our reference count and free up the buffer as necessary. - * Then, update our buffer to refer to that of the supplied ManagedBuffer, - * and increase its reference count. - * - * @param p The ManagedBuffer to reference. - * - * Example: - * @code - * uint8_t buf = {13,5,2}; - * ManagedBuffer p1(16); - * ManagedBuffer p2(buf, 3); - * - * p1 = p2; - * @endcode - */ - ManagedBuffer& operator = (const ManagedBuffer& p); - - /** - * Array access operation (read). - * - * Called when a ManagedBuffer is dereferenced with a [] operation. - * Transparently map this through to the underlying payload for elegance of programming. - * - * Example: - * @code - * ManagedBuffer p1(16); - * uint8_t data = p1[0]; - * @endcode - */ - uint8_t operator [] (int i) const - { - return ptr->payload[i]; - } - - /** - * Array access operation (modify). - * - * Called when a ManagedBuffer is dereferenced with a [] operation. - * Transparently map this through to the underlying payload for elegance of programming. - * - * Example: - * @code - * ManagedBuffer p1(16); - * p1[0] = 42; - * @endcode - */ - uint8_t& operator [] (int i) - { - return ptr->payload[i]; - } - - /** - * Equality operation. - * - * Called when one ManagedBuffer is tested to be equal to another using the '==' operator. - * - * @param p The ManagedBuffer to test ourselves against. - * @return true if this ManagedBuffer is identical to the one supplied, false otherwise. - * - * Example: - * @code - * - * uint8_t buf = {13,5,2}; - * ManagedBuffer p1(16); - * ManagedBuffer p2(buf, 3); - * - * if(p1 == p2) // will be true - * uBit.display.scroll("same!"); - * @endcode - */ - bool operator== (const ManagedBuffer& p); - - /** - * Sets the byte at the given index to value provided. - * @param position The index of the byte to change. - * @param value The new value of the byte (0-255). - * @return MICROBIT_OK, or MICROBIT_INVALID_PARAMETER. - * - * Example: - * @code - * ManagedBuffer p1(16); - * p1.setByte(0,255); // Sets the first byte in the buffer to the value 255. - * @endcode - */ - int setByte(int position, uint8_t value); - - /** - * Determines the value of the given byte in the buffer. - * - * @param position The index of the byte to read. - * @return The value of the byte at the given position, or MICROBIT_INVALID_PARAMETER. - * - * Example: - * @code - * ManagedBuffer p1(16); - * p1.setByte(0,255); // Sets the first byte in the buffer to the value 255. - * p1.getByte(0); // Returns 255. - * @endcode - */ - int getByte(int position); - - /** - * Gets number of bytes in this buffer - * @return The size of the buffer in bytes. - * - * Example: - * @code - * ManagedBuffer p1(16); - * p1.length(); // Returns 16. - * @endcode - */ - int length() const { return ptr->length; } - - int fill(uint8_t value, int offset = 0, int length = -1); - - ManagedBuffer slice(int offset = 0, int length = -1) const; - - void shift(int offset, int start = 0, int length = -1); - - void rotate(int offset, int start = 0, int length = -1); - - int readBytes(uint8_t *dst, int offset, int length, bool swapBytes = false) const; - - int writeBytes(int dstOffset, uint8_t *src, int length, bool swapBytes = false); - - int writeBuffer(int dstOffset, const ManagedBuffer &src, int srcOffset = 0, int length = -1); - - bool isReadOnly() const { return ptr->isReadOnly(); } -}; - -#endif - diff --git a/libs/core/_locales/core-jsdoc-strings.json b/libs/core/_locales/core-jsdoc-strings.json index 75286358..1d46c098 100644 --- a/libs/core/_locales/core-jsdoc-strings.json +++ b/libs/core/_locales/core-jsdoc-strings.json @@ -47,16 +47,17 @@ "Buffer.fill": "Fill (a fragment) of the buffer with given value.", "Buffer.getNumber": "Read a number in specified format from the buffer.", "Buffer.length": "Returns the length of a Buffer object.", - "Buffer.rotate": "Rotate buffer left in place.", - "Buffer.rotate|param|length": "number of elements in buffer. If negative, length is set as the buffer length minus start. eg: -1", + "Buffer.rotate": "Rotate buffer left in place.\n\n\n\nstart. eg: -1", + "Buffer.rotate|param|length": "number of elements in buffer. If negative, length is set as the buffer length minus", "Buffer.rotate|param|offset": "number of bytes to shift; use negative value to shift right", "Buffer.rotate|param|start": "start offset in buffer. Default is 0.", "Buffer.setNumber": "Write a number in specified format in the buffer.", - "Buffer.shift": "Shift buffer left in place, with zero padding.", - "Buffer.shift|param|length": "number of elements in buffer. If negative, length is set as the buffer length minus start. eg: -1", + "Buffer.shift": "Shift buffer left in place, with zero padding.\n\n\n\nstart. eg: -1", + "Buffer.shift|param|length": "number of elements in buffer. If negative, length is set as the buffer length minus", "Buffer.shift|param|offset": "number of bytes to shift; use negative value to shift right", "Buffer.shift|param|start": "start offset in buffer. Default is 0.", "Buffer.slice": "Return a copy of a fragment of a buffer.", + "Buffer.toHex": "Convert a buffer to its hexadecimal representation.", "Buffer.write": "Write contents of `src` at `dstOffset` in current buffer.", "EventCreationMode": "How to create the event.", "EventCreationMode.CreateAndFire": "MicroBitEvent is initialised, and its event handlers are immediately fired (not suitable for use in interrupts!).", @@ -189,6 +190,8 @@ "basic.showString|param|text": "the text to scroll on the screen, eg: \"Hello!\"", "control": "Runtime and event utilities.", "control.assert": "If the condition is false, display msg on serial console, and panic with code 098.", + "control.createBuffer": "Create a new zero-initialized buffer.", + "control.createBuffer|param|size": "number of bytes in the buffer", "control.deviceName": "Make a friendly name for the device based on its serial number", "control.deviceSerialNumber": "Derive a unique, consistent serial number of this device from internal data.", "control.eventSourceId": "Returns the value of a C++ runtime constant", diff --git a/libs/core/basic.cpp b/libs/core/basic.cpp index 6224a1e7..0fae3c66 100644 --- a/libs/core/basic.cpp +++ b/libs/core/basic.cpp @@ -38,7 +38,7 @@ namespace basic { //% blockId=device_show_leds //% block="show leds" icon="\uf00a" //% parts="ledmatrix" - void showLeds(ImageLiteral leds, int interval = 400) { + void showLeds(ImageLiteral_ leds, int interval = 400) { uBit.display.print(MicroBitImage(imageBytes(leds)), 0, 0, 0, interval); } @@ -53,18 +53,17 @@ namespace basic { //% async //% blockId=device_print_message //% parts="ledmatrix" - void showString(StringData *text, int interval = 150) { + void showString(String text, int interval = 150) { if (interval <= 0) interval = 1; - ManagedString s(text); - int l = s.length(); + int l = text ? text->length : 0; if (l == 0) { uBit.display.clear(); fiber_sleep(interval * 5); } else if (l > 1) { - uBit.display.scroll(s, interval); + uBit.display.scroll(MSTR(text), interval); } else { - uBit.display.print(s.charAt(0), interval * 5); + uBit.display.print(text->data[0], interval * 5); } } @@ -86,7 +85,7 @@ namespace basic { */ //% help=basic/show-animation imageLiteral=1 async //% parts="ledmatrix" - void showAnimation(ImageLiteral leds, int interval = 400) { + void showAnimation(ImageLiteral_ leds, int interval = 400) { uBit.display.animate(MicroBitImage(imageBytes(leds)), interval, 5, 0, 0); } @@ -96,18 +95,11 @@ namespace basic { */ //% help=basic/plot-leds weight=80 //% parts="ledmatrix" - void plotLeds(ImageLiteral leds) { + void plotLeds(ImageLiteral_ leds) { MicroBitImage i(imageBytes(leds)); uBit.display.print(i, 0, 0, 0, 0); } - void forever_stub(void *a) { - while (true) { - runAction0((Action)a); - fiber_sleep(20); - } - } - /** * Repeats the code forever in the background. On each iteration, allows other codes to run. * @param body code to execute @@ -115,10 +107,7 @@ namespace basic { //% help=basic/forever weight=55 blockGap=8 blockAllowMultiple=1 afterOnStart=true //% blockId=device_forever block="forever" icon="\uf01e" void forever(Action a) { - if (a != 0) { - incr(a); - create_fiber(forever_stub, (void*)a); - } + runForever(a); } /** diff --git a/libs/core/buffer.cpp b/libs/core/buffer.cpp index 1ccb5813..e315065d 100644 --- a/libs/core/buffer.cpp +++ b/libs/core/buffer.cpp @@ -1,160 +1,356 @@ -#include "pxt.h" +#include "pxtbase.h" +#include -// keep in sync with github/pxt/pxtsim/libgeneric.ts -enum class NumberFormat { - Int8LE = 1, - UInt8LE, - Int16LE, - UInt16LE, - Int32LE, - Int8BE, - UInt8BE, - Int16BE, - UInt16BE, - Int32BE, - // UInt32, -}; +using namespace std; //% indexerGet=BufferMethods::getByte indexerSet=BufferMethods::setByte namespace BufferMethods { - //% - int getByte(Buffer buf, int off) { - return max(ManagedBuffer(buf).getByte(off), 0); +//% +uint8_t *getBytes(Buffer buf) { + return buf->data; +} + +//% +int getByte(Buffer buf, int off) { + if (buf && 0 <= off && off < buf->length) + return buf->data[off]; + return 0; +} + +//% +void setByte(Buffer buf, int off, int v) { + if (buf && 0 <= off && off < buf->length) + buf->data[off] = v; +} + +int writeBuffer(Buffer buf, int dstOffset, Buffer src, int srcOffset = 0, int length = -1) { + if (length < 0) + length = src->length; + + if (srcOffset < 0 || dstOffset < 0 || dstOffset > buf->length) + return -1; + + length = min(src->length - srcOffset, buf->length - dstOffset); + + if (length < 0) + return -1; + + if (buf == src) { + memmove(buf->data + dstOffset, src->data + srcOffset, length); + } else { + memcpy(buf->data + dstOffset, src->data + srcOffset, length); } - //% - void setByte(Buffer buf, int off, int v) { - ManagedBuffer(buf).setByte(off, v); + return 0; +} + +/** + * Write a number in specified format in the buffer. + */ +//% +void setNumber(Buffer buf, NumberFormat format, int offset, TNumber value) { + if (offset < 0) + return; + setNumberCore(buf->data + offset, buf->length - offset, format, value); +} + +/** + * Read a number in specified format from the buffer. + */ +//% +TNumber getNumber(Buffer buf, NumberFormat format, int offset) { + if (offset < 0) + return fromInt(0); + return getNumberCore(buf->data + offset, buf->length - offset, format); +} + +/** Returns the length of a Buffer object. */ +//% property +int length(Buffer s) { + return s->length; +} + +/** + * Fill (a fragment) of the buffer with given value. + */ +//% +void fill(Buffer buf, int value, int offset = 0, int length = -1) { + if (offset < 0 || offset > buf->length) + return; // DEVICE_INVALID_PARAMETER; + if (length < 0) + length = buf->length; + length = min(length, buf->length - offset); + memset(buf->data + offset, value, length); +} + +/** + * Return a copy of a fragment of a buffer. + */ +//% +Buffer slice(Buffer buf, int offset = 0, int length = -1) { + offset = min((int)buf->length, offset); + if (length < 0) + length = buf->length; + length = min(length, buf->length - offset); + return mkBuffer(buf->data + offset, length); +} + +/** + * Shift buffer left in place, with zero padding. + * @param offset number of bytes to shift; use negative value to shift right + * @param start start offset in buffer. Default is 0. + * @param length number of elements in buffer. If negative, length is set as the buffer length minus + * start. eg: -1 + */ +//% +void shift(Buffer buf, int offset, int start = 0, int length = -1) { + if (length < 0) + length = buf->length - start; + if (start < 0 || start + length > buf->length || start + length < start || length == 0 || + offset == 0 || offset == INT_MIN) + return; + if (offset <= -length || offset >= length) { + fill(buf, 0); + return; } - //% - uint8_t *getBytes(Buffer buf) { - return buf->payload; - } - - /** - * Write a number in specified format in the buffer. - */ - //% - void setNumber(Buffer buf, NumberFormat format, int offset, int value) - { - int8_t i8; - uint8_t u8; - int16_t i16; - uint16_t u16; - int32_t i32; - - ManagedBuffer b(buf); - - // Assume little endian - #define WRITEBYTES(isz, swap) isz = value; b.writeBytes(offset, (uint8_t*)&isz, sizeof(isz), swap); break - - switch (format) { - case NumberFormat::Int8LE: WRITEBYTES(i8, false); - case NumberFormat::UInt8LE: WRITEBYTES(u8, false); - case NumberFormat::Int16LE: WRITEBYTES(i16, false); - case NumberFormat::UInt16LE: WRITEBYTES(u16, false); - case NumberFormat::Int32LE: WRITEBYTES(i32, false); - case NumberFormat::Int8BE: WRITEBYTES(i8, true); - case NumberFormat::UInt8BE: WRITEBYTES(u8, true); - case NumberFormat::Int16BE: WRITEBYTES(i16, true); - case NumberFormat::UInt16BE: WRITEBYTES(u16, true); - case NumberFormat::Int32BE: WRITEBYTES(i32, true); - } - } - - /** - * Read a number in specified format from the buffer. - */ - //% - int getNumber(Buffer buf, NumberFormat format, int offset) - { - int8_t i8; - uint8_t u8; - int16_t i16; - uint16_t u16; - int32_t i32; - - ManagedBuffer b(buf); - - // Assume little endian - #define READBYTES(isz, swap) b.readBytes((uint8_t*)&isz, offset, sizeof(isz), swap); return isz - - switch (format) { - case NumberFormat::Int8LE: READBYTES(i8, false); - case NumberFormat::UInt8LE: READBYTES(u8, false); - case NumberFormat::Int16LE: READBYTES(i16, false); - case NumberFormat::UInt16LE: READBYTES(u16, false); - case NumberFormat::Int32LE: READBYTES(i32, false); - case NumberFormat::Int8BE: READBYTES(i8, true); - case NumberFormat::UInt8BE: READBYTES(u8, true); - case NumberFormat::Int16BE: READBYTES(i16, true); - case NumberFormat::UInt16BE: READBYTES(u16, true); - case NumberFormat::Int32BE: READBYTES(i32, true); - } - - return 0; - } - - /** Returns the length of a Buffer object. */ - //% property - int length(Buffer s) { - return s->length; - } - - /** - * Fill (a fragment) of the buffer with given value. - */ - //% - void fill(Buffer buf, int value, int offset = 0, int length = -1) - { - ManagedBuffer(buf).fill(value, offset, length); - } - - /** - * Return a copy of a fragment of a buffer. - */ - //% - Buffer slice(Buffer buf, int offset = 0, int length = -1) - { - return ManagedBuffer(buf).slice(offset, length).leakData(); - } - - /** - * Shift buffer left in place, with zero padding. - * @param offset number of bytes to shift; use negative value to shift right - * @param start start offset in buffer. Default is 0. - * @param length number of elements in buffer. If negative, length is set as the buffer length minus start. eg: -1 - */ - //% - void shift(Buffer buf, int offset, int start = 0, int length = -1) - { - ManagedBuffer(buf).shift(offset, start, length); - } - - /** - * Rotate buffer left in place. - * @param offset number of bytes to shift; use negative value to shift right - * @param start start offset in buffer. Default is 0. - * @param length number of elements in buffer. If negative, length is set as the buffer length minus start. eg: -1 - */ - //% - void rotate(Buffer buf, int offset, int start = 0, int length = -1) - { - ManagedBuffer(buf).rotate(offset, start, length); - } - - // int readBytes(uint8_t *dst, int offset, int length, bool swapBytes = false) const; - // int writeBytes(int dstOffset, uint8_t *src, int length, bool swapBytes = false); - - /** - * Write contents of `src` at `dstOffset` in current buffer. - */ - //% - void write(Buffer buf, int dstOffset, Buffer src) - { - //Not supported, we only do up to 4 args :/ - //void write(Buffer buf, int dstOffset, Buffer src, int srcOffset = 0, int length = -1) - ManagedBuffer(buf).writeBuffer(dstOffset, ManagedBuffer(src), 0, -1); + uint8_t *data = buf->data + start; + if (offset < 0) { + offset = -offset; + memmove(data + offset, data, length - offset); + memset(data, 0, offset); + } else { + length = length - offset; + memmove(data, data + offset, length); + memset(data + length, 0, offset); } } + +/** + * Convert a buffer to its hexadecimal representation. + */ +//% +String toHex(Buffer buf) { + const char *hex = "0123456789abcdef"; + auto res = mkString(NULL, buf->length * 2); + for (int i = 0; i < buf->length; ++i) { + res->data[i << 1] = hex[buf->data[i] >> 4]; + res->data[(i << 1) + 1] = hex[buf->data[i] & 0xf]; + } + return res; +} + +/** + * Rotate buffer left in place. + * @param offset number of bytes to shift; use negative value to shift right + * @param start start offset in buffer. Default is 0. + * @param length number of elements in buffer. If negative, length is set as the buffer length minus + * start. eg: -1 + */ +//% +void rotate(Buffer buf, int offset, int start = 0, int length = -1) { + if (length < 0) + length = buf->length - start; + if (start < 0 || start + length > buf->length || start + length < start || length == 0 || + offset == 0 || offset == INT_MIN) + return; + + if (offset < 0) + offset += length << 8; // try to make it positive + offset %= length; + if (offset < 0) + offset += length; + + uint8_t *data = buf->data + start; + + uint8_t *n_first = data + offset; + uint8_t *first = data; + uint8_t *next = n_first; + uint8_t *last = data + length; + + while (first != next) { + uint8_t tmp = *first; + *first++ = *next; + *next++ = tmp; + if (next == last) { + next = n_first; + } else if (first == n_first) { + n_first = next; + } + } +} + +/** + * Write contents of `src` at `dstOffset` in current buffer. + */ +//% +void write(Buffer buf, int dstOffset, Buffer src) { + // srcOff and length not supported, we only do up to 4 args :/ + writeBuffer(buf, dstOffset, src, 0, -1); +} +} + +namespace control { +/** + * Create a new zero-initialized buffer. + * @param size number of bytes in the buffer + */ +//% +Buffer createBuffer(int size) { + return mkBuffer(NULL, size); +} +} + +namespace pxt { +static int writeBytes(uint8_t *dst, uint8_t *src, int length, bool swapBytes, int szLeft) { + if (szLeft < length) { + return -1; + } + + if (swapBytes) { + uint8_t *p = dst + length; + for (int i = 0; i < length; ++i) + *--p = src[i]; + } else { + if (length == 4 && ((uint32_t)dst & 3) == 0) + *(uint32_t *)dst = *(uint32_t *)src; + else if (length == 2 && ((uint32_t)dst & 1) == 0) + *(uint16_t *)dst = *(uint16_t *)src; + else + memcpy(dst, src, length); + } + + return 0; +} + +static int readBytes(uint8_t *src, uint8_t *dst, int length, bool swapBytes, int szLeft) { + if (szLeft < length) { + memset(dst, 0, length); + return -1; + } + + if (swapBytes) { + uint8_t *p = src + length; + for (int i = 0; i < length; ++i) + dst[i] = *--p; + } else { + if (length == 4 && ((uint32_t)src & 3) == 0) + *(uint32_t *)dst = *(uint32_t *)src; + else if (length == 2 && ((uint32_t)src & 1) == 0) + *(uint16_t *)dst = *(uint16_t *)src; + else + memcpy(dst, src, length); + } + + return 0; +} + +void setNumberCore(uint8_t *buf, int szLeft, NumberFormat format, TNumber value) { + int8_t i8; + uint8_t u8; + int16_t i16; + uint16_t u16; + int32_t i32; + uint32_t u32; + float f32; + double f64; + +// Assume little endian +#define WRITEBYTES(isz, swap, toInt) \ + isz = toInt(value); \ + writeBytes(buf, (uint8_t *)&isz, sizeof(isz), swap, szLeft); \ + break + + switch (format) { + case NumberFormat::Int8LE: + WRITEBYTES(i8, false, toInt); + case NumberFormat::UInt8LE: + WRITEBYTES(u8, false, toInt); + case NumberFormat::Int16LE: + WRITEBYTES(i16, false, toInt); + case NumberFormat::UInt16LE: + WRITEBYTES(u16, false, toInt); + case NumberFormat::Int32LE: + WRITEBYTES(i32, false, toInt); + case NumberFormat::UInt32LE: + WRITEBYTES(u32, false, toUInt); + + case NumberFormat::Int8BE: + WRITEBYTES(i8, true, toInt); + case NumberFormat::UInt8BE: + WRITEBYTES(u8, true, toInt); + case NumberFormat::Int16BE: + WRITEBYTES(i16, true, toInt); + case NumberFormat::UInt16BE: + WRITEBYTES(u16, true, toInt); + case NumberFormat::Int32BE: + WRITEBYTES(i32, true, toInt); + case NumberFormat::UInt32BE: + WRITEBYTES(u32, true, toUInt); + + case NumberFormat::Float32LE: + WRITEBYTES(f32, false, toFloat); + case NumberFormat::Float32BE: + WRITEBYTES(f32, true, toFloat); + case NumberFormat::Float64LE: + WRITEBYTES(f64, false, toDouble); + case NumberFormat::Float64BE: + WRITEBYTES(f64, true, toDouble); + } +} + +TNumber getNumberCore(uint8_t *buf, int szLeft, NumberFormat format) { + int8_t i8; + uint8_t u8; + int16_t i16; + uint16_t u16; + int32_t i32; + uint32_t u32; + float f32; + double f64; + +// Assume little endian +#define READBYTES(isz, swap, conv) \ + readBytes(buf, (uint8_t *)&isz, sizeof(isz), swap, szLeft); \ + return conv(isz) + + switch (format) { + case NumberFormat::Int8LE: + READBYTES(i8, false, fromInt); + case NumberFormat::UInt8LE: + READBYTES(u8, false, fromInt); + case NumberFormat::Int16LE: + READBYTES(i16, false, fromInt); + case NumberFormat::UInt16LE: + READBYTES(u16, false, fromInt); + case NumberFormat::Int32LE: + READBYTES(i32, false, fromInt); + case NumberFormat::UInt32LE: + READBYTES(u32, false, fromUInt); + + case NumberFormat::Int8BE: + READBYTES(i8, true, fromInt); + case NumberFormat::UInt8BE: + READBYTES(u8, true, fromInt); + case NumberFormat::Int16BE: + READBYTES(i16, true, fromInt); + case NumberFormat::UInt16BE: + READBYTES(u16, true, fromInt); + case NumberFormat::Int32BE: + READBYTES(i32, true, fromInt); + case NumberFormat::UInt32BE: + READBYTES(u32, true, fromUInt); + + case NumberFormat::Float32LE: + READBYTES(f32, false, fromFloat); + case NumberFormat::Float32BE: + READBYTES(f32, true, fromFloat); + case NumberFormat::Float64LE: + READBYTES(f64, false, fromDouble); + case NumberFormat::Float64BE: + READBYTES(f64, true, fromDouble); + } + + return 0; +} +} \ No newline at end of file diff --git a/libs/core/codal.cpp b/libs/core/codal.cpp new file mode 100644 index 00000000..463bc864 --- /dev/null +++ b/libs/core/codal.cpp @@ -0,0 +1,234 @@ +#include "pxt.h" +#include + +PXT_ABI(__aeabi_dadd) +PXT_ABI(__aeabi_dcmplt) +PXT_ABI(__aeabi_dcmpgt) +PXT_ABI(__aeabi_dsub) +PXT_ABI(__aeabi_ddiv) +PXT_ABI(__aeabi_dmul) + +extern "C" void target_panic(int error_code) +{ + // wait for serial to flush + wait_us(300000); + microbit_panic(error_code); +} + +extern "C" void target_reset() +{ + microbit_reset(); +} + +namespace pxt { + +MicroBit uBit; +MicroBitEvent lastEvent; + +void platform_init() {} + +void platform_init(); +void usb_init(); + +struct FreeList { + FreeList *next; +}; + +static void initCodal() { + + uBit.init(); + + // repeat error 4 times and restart as needed + microbit_panic_timeout(4); +} + +void dumpDmesg() {} + +// --------------------------------------------------------------------------- +// An adapter for the API expected by the run-time. +// --------------------------------------------------------------------------- + +// We have the invariant that if [dispatchEvent] is registered against the DAL +// for a given event, then [handlersMap] contains a valid entry for that +// event. +void dispatchEvent(MicroBitEvent e) { + lastEvent = e; + + auto curr = findBinding(e.source, e.value); + auto value = fromInt(e.value); + if (curr) + runAction1(curr->action, value); + + curr = findBinding(e.source, DEVICE_EVT_ANY); + if (curr) + runAction1(curr->action, value); +} + +void registerWithDal(int id, int event, Action a, int flags) { + // first time? + if (!findBinding(id, event)) + uBit.messageBus.listen(id, event, dispatchEvent, flags); + setBinding(id, event, a); +} + +void fiberDone(void *a) { + decr((Action)a); + release_fiber(); +} + +void releaseFiber() { + release_fiber(); +} + +void sleep_ms(unsigned ms) { + fiber_sleep(ms); +} + +void sleep_us(uint64_t us) { + wait_us(us); +} + +void forever_stub(void *a) { + while (true) { + runAction0((Action)a); + fiber_sleep(20); + } +} + +void runForever(Action a) { + if (a != 0) { + incr(a); + create_fiber(forever_stub, (void *)a); + } +} + +void runInParallel(Action a) { + if (a != 0) { + incr(a); + create_fiber((void (*)(void *))runAction0, (void *)a, fiberDone); + } +} + +void waitForEvent(int id, int event) { + fiber_wait_for_event(id, event); +} + +void initRuntime() { + initCodal(); + platform_init(); +} + +//% +unsigned afterProgramPage() { + unsigned ptr = (unsigned)&bytecode[0]; + ptr += programSize(); + ptr = (ptr + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1); + return ptr; +} + + +int current_time_ms() { + return system_timer_current_time(); +} + +static void logwriten(const char *msg, int l) +{ + uBit.serial.send((uint8_t*)msg, l); +} + +static void logwrite(const char *msg) +{ + logwriten(msg, strlen(msg)); +} + + +static void writeNum(char *buf, uint32_t n, bool full) +{ + int i = 0; + int sh = 28; + while (sh >= 0) + { + int d = (n >> sh) & 0xf; + if (full || d || sh == 0 || i) + { + buf[i++] = d > 9 ? 'A' + d - 10 : '0' + d; + } + sh -= 4; + } + buf[i] = 0; +} + +static void logwritenum(uint32_t n, bool full, bool hex) +{ + char buff[20]; + + if (hex) + { + writeNum(buff, n, full); + logwrite("0x"); + } + else + { + itoa(n, buff); + } + + logwrite(buff); +} + +void vdebuglog(const char *format, va_list ap) +{ + const char *end = format; + + while (*end) + { + if (*end++ == '%') + { + logwriten(format, end - format - 1); + uint32_t val = va_arg(ap, uint32_t); + switch (*end++) + { + case 'c': + logwriten((const char *)&val, 1); + break; + case 'd': + logwritenum(val, false, false); + break; + case 'x': + logwritenum(val, false, true); + break; + case 'p': + case 'X': + logwritenum(val, true, true); + break; + case 's': + logwrite((char *)(void *)val); + break; + case '%': + logwrite("%"); + break; + default: + logwrite("???"); + break; + } + format = end; + } + } + logwriten(format, end - format); + logwrite("\n"); +} + +void debuglog(const char *format, ...) +{ + va_list arg; + va_start(arg, format); + vdebuglog(format, arg); + va_end(arg); +} + + + + +} // namespace pxt + + + diff --git a/libs/core/control.cpp b/libs/core/control.cpp index be4ce53c..55756e39 100644 --- a/libs/core/control.cpp +++ b/libs/core/control.cpp @@ -220,7 +220,7 @@ namespace control { //% help=control/in-background blockAllowMultiple=1 afterOnStart=true //% blockId="control_in_background" block="run in background" blockGap=8 void inBackground(Action a) { - runInBackground(a); + runInParallel(a); } /** @@ -290,8 +290,8 @@ namespace control { */ //% blockId="control_device_name" block="device name" weight=10 blockGap=8 //% advanced=true - StringData* deviceName() { - return ManagedString(microbit_friendly_name()).leakData(); + String deviceName() { + return mkString(microbit_friendly_name(), -1); } /** diff --git a/libs/core/core.cpp b/libs/core/core.cpp index 23860df0..73c6fe91 100644 --- a/libs/core/core.cpp +++ b/libs/core/core.cpp @@ -1,468 +1,1112 @@ -#include "pxt.h" +#include "pxtbase.h" #include +#include +using namespace std; + + +#define p10(v) __builtin_powi(10, v) + + +namespace pxt { + +static HandlerBinding *handlerBindings; + +HandlerBinding *findBinding(int source, int value) { + for (auto p = handlerBindings; p; p = p->next) { + if (p->source == source && p->value == value) { + return p; + } + } + return 0; +} + +void setBinding(int source, int value, Action act) { + auto curr = findBinding(source, value); + incr(act); + if (curr) { + decr(curr->action); + curr->action = act; + return; + } + curr = new HandlerBinding(); + curr->next = handlerBindings; + curr->source = source; + curr->value = value; + curr->action = act; + handlerBindings = curr; +} + +static const uint16_t emptyString[] + __attribute__((aligned(4))) = {0xffff, PXT_REF_TAG_STRING, 0, 0}; + +static const uint16_t emptyBuffer[] + __attribute__((aligned(4))) = {0xffff, PXT_REF_TAG_BUFFER, 0, 0}; + +String mkString(const char *data, int len) { + if (len < 0) + len = strlen(data); + if (len == 0) + return (String)emptyString; + String r = new (::operator new(sizeof(BoxedString) + len + 1)) BoxedString(); + r->length = len; + if (data) + memcpy(r->data, data, len); + r->data[len] = 0; + MEMDBG("mkString: len=%d => %p", len, r); + return r; +} + +Buffer mkBuffer(const uint8_t *data, int len) { + if (len <= 0) + return (Buffer)emptyBuffer; + Buffer r = new (::operator new(sizeof(BoxedBuffer) + len)) BoxedBuffer(); + r->length = len; + if (data) + memcpy(r->data, data, len); + else + memset(r->data, 0, len); + MEMDBG("mkBuffer: len=%d => %p", len, r); + return r; +} + +#ifndef X86_64 +TNumber mkNaN() { + // TODO optimize + return fromDouble(NAN); +} +#endif + +static unsigned random_value = 0xC0DA1; + +void seedRandom(unsigned seed) { + random_value = seed; +} + +unsigned getRandom(unsigned max) { + unsigned m, result; + + do { + m = (unsigned)max; + result = 0; + + do { + // Cycle the LFSR (Linear Feedback Shift Register). + // We use an optimal sequence with a period of 2^32-1, as defined by Bruce Schneier here + // (a true legend in the field!), + // For those interested, it's documented in his paper: + // "Pseudo-Random Sequence Generator for 32-Bit CPUs: A fast, machine-independent + // generator for 32-bit Microprocessors" + // https://www.schneier.com/paper-pseudorandom-sequence.html + unsigned r = random_value; + + r = ((((r >> 31) ^ (r >> 6) ^ (r >> 4) ^ (r >> 2) ^ (r >> 1) ^ r) & 1) << 31) | + (r >> 1); + + random_value = r; + + result = ((result << 1) | (r & 0x00000001)); + } while (m >>= 1); + } while (result > (unsigned)max); + + return result; +} + +PXT_DEF_STRING(sTrue, "\x04\x00true") +PXT_DEF_STRING(sFalse, "\x05\x00false") +PXT_DEF_STRING(sUndefined, "\x09\x00undefined") +PXT_DEF_STRING(sNull, "\x04\x00null") +PXT_DEF_STRING(sObject, "\x08\x00[Object]") +PXT_DEF_STRING(sFunction, "\x0A\x00[Function]") +PXT_DEF_STRING(sNaN, "\x03\x00NaN") +PXT_DEF_STRING(sInf, "\x08\x00Infinity") +PXT_DEF_STRING(sMInf, "\x09\x00-Infinity") +} // namespace pxt + +#ifndef X86_64 namespace String_ { - //% - StringData *charAt(StringData *s, int pos) { - return ManagedString((char)ManagedString(s).charAt(pos)).leakData(); - } - //% - int charCodeAt(StringData *s, int index) { - return ManagedString(s).charAt(index); - } +//% +String mkEmpty() { + return mkString("", 0); +} - //% - StringData *concat(StringData *s, StringData *other) { - ManagedString a(s), b(other); - return (a + b).leakData(); - } +//% +String fromCharCode(int code) { + char buf[] = {(char)code, 0}; + return mkString(buf, 1); +} - //% - int compare(StringData *s, StringData *that) { - int compareResult = strcmp(s->data, that->data); - if (compareResult < 0) - return -1; - else if (compareResult > 0) - return 1; - return 0; - } - - //% - int compareDecr(StringData *s, StringData *that) { - int r = compare(s, that); - if (r == 0) - decr((uint32_t)that); - return r; - } - - - //% - int length(StringData *s) { return s->len; } - - //% - StringData *fromCharCode(int code) - { - return ManagedString((char)code).leakData(); - } - - //% - int toNumber(StringData *s) { - return atoi(s->data); - } - - //% - StringData *mkEmpty() - { - return ManagedString::EmptyString.leakData(); - } - - //% - StringData *substr(StringData *s, int start, int length) - { - if (length <= 0) - return mkEmpty(); - if (start < 0) - start = max(s->len + start, 0); - length = min(length, s->len - start); - ManagedString x(s); - return x.substring(start, length).leakData(); +//% +String charAt(String s, int pos) { + if (s && 0 <= pos && pos < s->length) { + return fromCharCode(s->data[pos]); + } else { + return mkEmpty(); } } +//% +TNumber charCodeAt(String s, int pos) { + if (s && 0 <= pos && pos < s->length) { + return fromInt(s->data[pos]); + } else { + return mkNaN(); + } +} + +//% +String concat(String s, String other) { + if (!s) + s = (String)sNull; + if (!other) + other = (String)sNull; + if (s->length == 0) + return (String)incrRC(other); + if (other->length == 0) + return (String)incrRC(s); + String r = mkString(NULL, s->length + other->length); + memcpy(r->data, s->data, s->length); + memcpy(r->data + s->length, other->data, other->length); + return r; +} + +//% +int compare(String s, String that) { + if (s == that) + return 0; + // TODO this isn't quite right, in JS both `null < "foo"` and `null > "foo"` are false + if (!s) + return -1; + if (!that) + return 1; + int compareResult = strcmp(s->data, that->data); + if (compareResult < 0) + return -1; + else if (compareResult > 0) + return 1; + return 0; +} + +//% +int length(String s) { + return s->length; +} + +#define isspace(c) ((c) == ' ') + +double mystrtod(const char *p, char **endp) { + while (isspace(*p)) + p++; + double m = 1; + double v = 0; + int dot = 0; + if (*p == '+') + p++; + if (*p == '-') { + m = -1; + p++; + } + if (*p == '0' && (p[1] | 0x20) == 'x') { + return m * strtol(p, endp, 16); + } + while (*p) { + int c = *p - '0'; + if (0 <= c && c <= 9) { + v *= 10; + v += c; + if (dot) + m /= 10; + } else if (!dot && *p == '.') { + dot = 1; + } else if (*p == 'e' || *p == 'E') { + break; + } else { + while (isspace(*p)) + p++; + if (*p) + return NAN; + break; + } + p++; + } + + v *= m; + + if (*p) { + p++; + int pw = strtol(p, endp, 10); + v *= p10(pw); + } + + return v; +} + +//% +TNumber toNumber(String s) { + // JSCHECK + char *endptr; + double v = mystrtod(s->data, &endptr); + if (endptr != s->data + s->length) + v = NAN; + else if (v == 0.0 || v == -0.0) + v = v; + else if (!isnormal(v)) + v = NAN; + return fromDouble(v); +} + +//% +String substr(String s, int start, int length) { + if (length <= 0) + return mkEmpty(); + if (start < 0) + start = max(s->length + start, 0); + length = min(length, s->length - start); + return mkString(s->data + start, length); +} +} // namespace String_ namespace Boolean_ { - // Cache the string literals "true" and "false" when used. - // Note that the representation of booleans stays the usual C-one. - - static const char sTrue[] __attribute__ ((aligned (4))) = "\xff\xff\x04\x00" "true\0"; - static const char sFalse[] __attribute__ ((aligned (4))) = "\xff\xff\x05\x00" "false\0"; +//% +bool bang(int v) { + return v == 0; +} +} // namespace Boolean_ - //% - StringData* toString(bool v) - { - if (v) { - return (StringData*)(void*)sTrue; - } else { - return (StringData*)(void*)sFalse; - } +namespace pxt { + +// ES5 9.5, 9.6 +unsigned toUInt(TNumber v) { + if (isNumber(v)) + return numValue(v); + if (isSpecial(v)) { + if ((intptr_t)v >> 6) + return 1; + else + return 0; } + if (!v) + return 0; - //% - bool bang(int v) { return v == 0; } + double num = toDouble(v); + if (!isnormal(num)) + return 0; + double rem = fmod(trunc(num), 4294967296.0); + if (rem < 0.0) + rem += 4294967296.0; + return (unsigned)rem; +} +int toInt(TNumber v) { + return (int)toUInt(v); } -namespace Number_ { - //% - StringData* toString(int n) - { - return ManagedString(n).leakData(); +// only support double in tagged mode +double toDouble(TNumber v) { + if (isTagged(v)) + return toInt(v); + + // JSCHECK + ValType t = valType(v); + if (t == ValType::Number) { + BoxedNumber *p = (BoxedNumber *)v; + return p->num; + } else if (t == ValType::String) { + return toDouble(String_::toNumber((String)v)); + } else { + return NAN; } +} - // +, - and friends are handled directly by assembly instructions - // The comparisons are here as they are more code-size efficient - - //% - bool lt(int x, int y) { return x < y; } - //% - bool le(int x, int y) { return x <= y; } - //% - bool neq(int x, int y) { return x != y; } - //% - bool eq(int x, int y) { return x == y; } - //% - bool gt(int x, int y) { return x > y; } - //% - bool ge(int x, int y) { return x >= y; } +float toFloat(TNumber v) { + // TODO optimize? + return (float)toDouble(v); +} - // These in fact call into C runtime on Cortex-M0 - //% - int div(int x, int y) { return x / y; } - //% - int mod(int x, int y) { return x % y; } +TNumber fromDouble(double r) { +#ifndef PXT_BOX_DEBUG + int ri = ((int)r) << 1; + if ((ri >> 1) == r) + return (TNumber)(ri | 1); +#endif + BoxedNumber *p = new BoxedNumber(); + p->num = r; + MEMDBG("mkNum: %p", p); + return (TNumber)p; +} - //% - bool eqDecr(int x, int y) { - if(x == y) { - decr(y); +TNumber fromFloat(float r) { + // TODO optimize + return fromDouble(r); +} + +TNumber fromInt(int v) { + if (canBeTagged(v)) + return TAG_NUMBER(v); + return fromDouble(v); +} + +TNumber fromUInt(unsigned v) { +#ifndef PXT_BOX_DEBUG + if (v <= 0x3fffffff) + return TAG_NUMBER(v); +#endif + return fromDouble(v); +} + +TValue fromBool(bool v) { + if (v) + return TAG_TRUE; + else + return TAG_FALSE; +} + +TNumber eqFixup(TNumber v) { + if (v == TAG_NULL) + return TAG_UNDEFINED; + if (v == TAG_TRUE) + return TAG_NUMBER(1); + if (v == TAG_FALSE) + return TAG_NUMBER(0); + return v; +} + +bool eqq_bool(TValue a, TValue b) { + // TODO improve this + + if (a == b) return true; - } - return false; + + ValType ta = valType(a); + ValType tb = valType(b); + + if (ta != tb) + return false; + + if (ta == ValType::String) + return String_::compare((String)a, (String)b) == 0; + + int aa = (int)a; + int bb = (int)b; + + // if at least one of the values is tagged, they are not equal + if ((aa | bb) & 3) + return false; + + if (ta == ValType::Number) + return toDouble(a) == toDouble(b); + else + return a == b; +} + +bool eq_bool(TValue a, TValue b) { + return eqq_bool(eqFixup(a), eqFixup(b)); +} + +//% +bool switch_eq(TValue a, TValue b) { + if (eqq_bool(eqFixup(a), eqFixup(b))) { + decr(b); + return true; + } + return false; +} + +} // namespace pxt + +namespace langsupp { +//% +TValue ptreq(TValue a, TValue b) { + return eq_bool(a, b) ? TAG_TRUE : TAG_FALSE; +} + +//% +TValue ptreqq(TValue a, TValue b) { + return eqq_bool(a, b) ? TAG_TRUE : TAG_FALSE; +} + +//% +TValue ptrneq(TValue a, TValue b) { + return !eq_bool(a, b) ? TAG_TRUE : TAG_FALSE; +} + +//% +TValue ptrneqq(TValue a, TValue b) { + return !eqq_bool(a, b) ? TAG_TRUE : TAG_FALSE; +} +} // namespace langsupp + +#define NUMOP(op) return fromDouble(toDouble(a) op toDouble(b)); +#define BITOP(op) return fromInt(toInt(a) op toInt(b)); +namespace numops { + +//% +int toBool(TValue v) { + if (isTagged(v)) { + if (v == TAG_UNDEFINED || v == TAG_NULL || v == TAG_FALSE || v == TAG_NUMBER(0)) + return 0; + else + return 1; + } + + ValType t = valType(v); + if (t == ValType::String) { + String s = (String)v; + if (s->length == 0) + return 0; + } else if (t == ValType::Number) { + double x = toDouble(v); + if (isnan(x) || x == 0.0 || x == -0.0) + return 0; + else + return 1; + } + + return 1; +} + +//% +int toBoolDecr(TValue v) { + if (v == TAG_TRUE) + return 1; + if (v == TAG_FALSE) + return 0; + int r = toBool(v); + decr(v); + return r; +} + +// TODO +// The integer, non-overflow case for add/sub/bit opts is handled in assembly + +//% +TNumber adds(TNumber a, TNumber b){NUMOP(+)} + +//% +TNumber subs(TNumber a, TNumber b){NUMOP(-)} + +//% +TNumber muls(TNumber a, TNumber b) { + if (bothNumbers(a, b)) { + int aa = (int)a; + int bb = (int)b; + // if both operands fit 15 bits, the result will not overflow int + if ((aa >> 15 == 0 || aa >> 15 == -1) && (bb >> 15 == 0 || bb >> 15 == -1)) { + // it may overflow 31 bit int though - use fromInt to convert properly + return fromInt((aa >> 1) * (bb >> 1)); + } + } + NUMOP(*) +} + +//% +TNumber div(TNumber a, TNumber b){NUMOP(/)} + +//% +TNumber mod(TNumber a, TNumber b) { + if (isNumber(a) && isNumber(b) && numValue(b)) + BITOP(%) + return fromDouble(fmod(toDouble(a), toDouble(b))); +} + +//% +TNumber lsls(TNumber a, TNumber b){BITOP(<<)} + +//% +TNumber lsrs(TNumber a, TNumber b) { + return fromUInt(toUInt(a) >> toUInt(b)); +} + +//% +TNumber asrs(TNumber a, TNumber b){BITOP(>>)} + +//% +TNumber eors(TNumber a, TNumber b){BITOP (^)} + +//% +TNumber orrs(TNumber a, TNumber b){BITOP(|)} + +//% +TNumber bnot(TNumber a) { + return fromInt(~toInt(a)); +} + +//% +TNumber ands(TNumber a, TNumber b) { + BITOP(&) +} + +#define CMPOP_RAW(op) \ + if (bothNumbers(a, b)) \ + return (int)a op((int)b); \ + return toDouble(a) op toDouble(b); + +#define CMPOP(op) \ + if (bothNumbers(a, b)) \ + return ((int)a op((int)b)) ? TAG_TRUE : TAG_FALSE; \ + return toDouble(a) op toDouble(b) ? TAG_TRUE : TAG_FALSE; + +//% +bool lt_bool(TNumber a, TNumber b){CMPOP_RAW(<)} + +//% +TNumber le(TNumber a, TNumber b){CMPOP(<=)} + +//% +TNumber lt(TNumber a, TNumber b){CMPOP(<)} + +//% +TNumber ge(TNumber a, TNumber b){CMPOP(>=)} + +//% +TNumber gt(TNumber a, TNumber b){CMPOP(>)} + +//% +TNumber eq(TNumber a, TNumber b) { + return pxt::eq_bool(a, b) ? TAG_TRUE : TAG_FALSE; +} + +//% +TNumber neq(TNumber a, TNumber b) { + return !pxt::eq_bool(a, b) ? TAG_TRUE : TAG_FALSE; +} + +//% +TNumber eqq(TNumber a, TNumber b) { + return pxt::eqq_bool(a, b) ? TAG_TRUE : TAG_FALSE; +} + +//% +TNumber neqq(TNumber a, TNumber b) { + return !pxt::eqq_bool(a, b) ? TAG_TRUE : TAG_FALSE; +} + +void mycvt(double d, char *buf) { + if (d < 0) { + *buf++ = '-'; + d = -d; + } + + if (!d) { + *buf++ = '0'; + *buf++ = 0; + return; + } + + int pw = (int)log10(d); + int e = 1; + int beforeDot = 1; + + if (0.000001 <= d && d < 1e21) { + if (pw > 0) { + d /= p10(pw); + beforeDot = 1 + pw; + } + } else { + d /= p10(pw); + e = pw; + } + + int sig = 0; + while (sig < 17 || beforeDot > 0) { + //printf("%f sig=%d bd=%d\n", d, sig, beforeDot); + int c = (int)d; + *buf++ = '0' + c; + d = (d - c) * 10; + if (--beforeDot == 0) + *buf++ = '.'; + if (sig || c) + sig++; + } + + buf--; + while (*buf == '0') + buf--; + if (*buf == '.') + buf--; + buf++; + + if (e != 1) { + *buf++ = 'e'; + itoa(e, buf); + } else { + *buf = 0; } } + +//% +String toString(TValue v) { + + if (v == TAG_UNDEFINED) + return (String)(void *)sUndefined; + else if (v == TAG_FALSE) + return (String)(void *)sFalse; + else if (v == TAG_TRUE) + return (String)(void *)sTrue; + else if (v == TAG_NULL) + return (String)(void *)sNull; + ValType t = valType(v); + + if (t == ValType::String) { + return (String)(void *)incr(v); + } else if (t == ValType::Number) { + char buf[64]; + + double x = toDouble(v); + + if (isnan(x)) + return (String)(void *)sNaN; + if (isinf(x)) { + if (x < 0) + return (String)(void *)sMInf; + else + return (String)(void *)sInf; + } + mycvt(x, buf); + + return mkString(buf); + } else if (t == ValType::Function) { + return (String)(void *)sFunction; + } else { + return (String)(void *)sObject; + } +} +} // namespace numops + namespace Math_ { - //% - int pow(int x, int y) - { - if (y < 0) - return 0; - int r = 1; - while (y) { - if (y & 1) - r *= x; - y >>= 1; - x *= x; - } - return r; - } - - //% - int random(int max) { - if (max == INT_MIN) - return -microbit_random(INT_MAX); - else if (max < 0) - return -microbit_random(-max); - else if (max == 0) - return 0; - else - return microbit_random(max); - } - - //% - int sqrt(int x) - { - return ::sqrt(x); - } - - // TODO: update signature when enabling FP - //% - int floor(int x){ - return x; - } - - //% - int ceil(int x){ - return x; - } - - //% - int trunc(int x){ - return x; - } - - //% - int round(int x){ - return x; - } - - //% - int imul(int x, int y) { - return x * y; - } - - //% - int idiv(int x, int y) { - return x / y; - } - +//% +TNumber pow(TNumber x, TNumber y) { + // regular pow() from math.h is 4k of code + return fromDouble(__builtin_powi(toDouble(x), toInt(y))); } +//% +TNumber atan2(TNumber y, TNumber x) { + return fromDouble(::atan2(toDouble(y), toDouble(y))); +} + +double randomDouble() { + return getRandom(UINT_MAX) / ((double)UINT_MAX + 1) + + getRandom(0xffffff) / ((double)UINT_MAX * 0xffffff); +} + +//% +TNumber random() { + return fromDouble(randomDouble()); +} + +//% +TNumber randomRange(TNumber min, TNumber max) { + if (isNumber(min) && isNumber(max)) { + int mini = numValue(min); + int maxi = numValue(max); + if (mini > maxi) { + int temp = mini; + mini = maxi; + maxi = temp; + } + if (maxi == mini) + return fromInt(mini); + else + return fromInt(mini + getRandom(maxi - mini)); + } else { + double mind = toDouble(min); + double maxd = toDouble(max); + if (mind > maxd) { + double temp = mind; + mind = maxd; + maxd = temp; + } + if (maxd == mind) + return fromDouble(mind); + else { + return fromDouble(mind + randomDouble() * (maxd - mind)); + } + } +} + +#define SINGLE(op) return fromDouble(::op(toDouble(x))); + +//% +TNumber log(TNumber x){SINGLE(log)} + +//% +TNumber log10(TNumber x){SINGLE(log10)} + +//% +TNumber sqrt(TNumber x){SINGLE(sqrt)} + +//% +TNumber floor(TNumber x){SINGLE(floor)} + +//% +TNumber ceil(TNumber x){SINGLE(ceil)} + +//% +TNumber trunc(TNumber x){SINGLE(trunc)} + +//% +TNumber round(TNumber x) { + SINGLE(round) +} + +//% +int imul(int x, int y) { + return x * y; +} + +//% +int idiv(int x, int y) { + return x / y; +} +} // namespace Math_ + namespace Array_ { - //% - RefCollection *mk(uint32_t flags) - { - return new RefCollection(flags); - } - //% - int length(RefCollection *c) { return c->length(); } - //% - void setLength(RefCollection *c, int newLength) { c->setLength(newLength); } - //% - void push(RefCollection *c, uint32_t x) { c->push(x); } - //% - uint32_t pop(RefCollection *c) { return c->pop(); } - //% - uint32_t getAt(RefCollection *c, int x) { return c->getAt(x); } - //% - void setAt(RefCollection *c, int x, uint32_t y) { c->setAt(x, y); } - //% - uint32_t removeAt(RefCollection *c, int x) { return c->removeAt(x); } - //% - void insertAt(RefCollection *c, int x, uint32_t value) { c->insertAt(x, value); } - //% - int indexOf(RefCollection *c, uint32_t x, int start) { return c->indexOf(x, start); } - //% - int removeElement(RefCollection *c, uint32_t x) { return c->removeElement(x); } +//% +RefCollection *mk(unsigned flags) { + auto r = new RefCollection(); + MEMDBG("mkColl: %p", r); + return r; } +//% +int length(RefCollection *c) { + return c->length(); +} +//% +void setLength(RefCollection *c, int newLength) { + c->setLength(newLength); +} +//% +void push(RefCollection *c, TValue x) { + c->push(x); +} +//% +TValue pop(RefCollection *c) { + return c->pop(); +} +//% +TValue getAt(RefCollection *c, int x) { + return c->getAt(x); +} +//% +void setAt(RefCollection *c, int x, TValue y) { + c->setAt(x, y); +} +//% +TValue removeAt(RefCollection *c, int x) { + return c->removeAt(x); +} +//% +void insertAt(RefCollection *c, int x, TValue value) { + c->insertAt(x, value); +} +//% +int indexOf(RefCollection *c, TValue x, int start) { + return c->indexOf(x, start); +} +//% +bool removeElement(RefCollection *c, TValue x) { + return c->removeElement(x); +} +} // namespace Array_ - -// Import some stuff directly namespace pxt { - //% - void registerWithDal(int id, int event, Action a); - //% - uint32_t runAction3(Action a, int arg0, int arg1, int arg2); - //% - uint32_t runAction2(Action a, int arg0, int arg1); - //% - uint32_t runAction1(Action a, int arg0); - //% - uint32_t runAction0(Action a); - //% - Action mkAction(int reflen, int totallen, int startptr); - //% - RefRecord* mkClassInstance(int offset); - //% - void RefRecord_destroy(RefRecord *r); - //% - void RefRecord_print(RefRecord *r); - //% - void debugMemLeaks(); - //% - int incr(uint32_t e); - //% - void decr(uint32_t e); - //% - uint32_t *allocate(uint16_t sz); - //% - int templateHash(); - //% - int programHash(); - //% - void *ptrOfLiteral(int offset); - //% - int getNumGlobals(); +//% +void *ptrOfLiteral(int offset); - //% - uint32_t programSize() { +//% +unsigned programSize() { return bytecode[17] * 2; - } - - //% - uint32_t afterProgramPage() { - uint32_t ptr = (uint32_t)&bytecode[0]; - ptr += programSize(); - if (ptr % PAGE_SIZE != 0) - ptr = (ptr & ~(PAGE_SIZE-1)) + PAGE_SIZE; - return ptr; - } } +//% +int getConfig(int key, int defl) { + int *cfgData = *(int **)&bytecode[18]; + for (int i = 0;; i += 2) { + if (cfgData[i] == key) + return cfgData[i + 1]; + if (cfgData[i] == 0) + return defl; + } +} + +} // namespace pxt + namespace pxtrt { - //% - uint32_t ldloc(RefLocal *r) { +//% +TValue ldloc(RefLocal *r) { return r->v; - } +} - //% - uint32_t ldlocRef(RefRefLocal *r) { - uint32_t tmp = r->v; +//% +TValue ldlocRef(RefRefLocal *r) { + TValue tmp = r->v; incr(tmp); return tmp; - } +} - //% - void stloc(RefLocal *r, uint32_t v) { +//% +void stloc(RefLocal *r, TValue v) { r->v = v; - } +} - //% - void stlocRef(RefRefLocal *r, uint32_t v) { +//% +void stlocRef(RefRefLocal *r, TValue v) { decr(r->v); r->v = v; - } +} - //% - RefLocal *mkloc() { - return new RefLocal(); - } +//% +RefLocal *mkloc() { + auto r = new RefLocal(); + MEMDBG("mkloc: %p", r); + return r; +} - //% - RefRefLocal *mklocRef() { - return new RefRefLocal(); - } +//% +RefRefLocal *mklocRef() { + auto r = new RefRefLocal(); + MEMDBG("mklocRef: %p", r); + return r; +} - // All of the functions below unref() self. This is for performance reasons - - // the code emitter will not emit the unrefs for them. - - //% - uint32_t ldfld(RefRecord *r, int idx) { - auto tmp = r->ld(idx); +// All of the functions below unref() self. This is for performance reasons - +// the code emitter will not emit the unrefs for them. + +//% +TValue ldfld(RefRecord *r, int idx) { + TValue tmp = r->ld(idx); r->unref(); return tmp; - } +} - //% - uint32_t ldfldRef(RefRecord *r, int idx) { - auto tmp = r->ldref(idx); +//% +TValue ldfldRef(RefRecord *r, int idx) { + TValue tmp = r->ldref(idx); r->unref(); return tmp; - } +} - //% - void stfld(RefRecord *r, int idx, uint32_t val) { +//% +void stfld(RefRecord *r, int idx, TValue val) { r->st(idx, val); r->unref(); - } +} - //% - void stfldRef(RefRecord *r, int idx, uint32_t val) { +//% +void stfldRef(RefRecord *r, int idx, TValue val) { r->stref(idx, val); r->unref(); - } +} - // Store a captured local in a closure. It returns the action, so it can be chained. - //% - RefAction *stclo(RefAction *a, int idx, uint32_t v) - { - //DBG("STCLO "); a->print(); DBG("@%d = %p\n", idx, (void*)v); +// Store a captured local in a closure. It returns the action, so it can be chained. +//% +RefAction *stclo(RefAction *a, int idx, TValue v) { + // DBG("STCLO "); a->print(); DBG("@%d = %p\n", idx, (void*)v); a->stCore(idx, v); return a; - } - - //% - void panic(int code) - { - microbit_panic(code); - } - - //% - int stringToBool(StringData *s) { - if (s == NULL) return 0; - if (s->len == 0) { - s->decr(); - return 0; - } - s->decr(); - return 1; - } - - //% - StringData* emptyToNull(StringData *s) { - if (!s || s->len == 0) - return NULL; - return s; - } - - //% - int ptrToBool(uint32_t p) { - if (p) { - decr(p); - return 1; - } else { - return 0; - } - } - - //% - RefMap *mkMap() { - return new RefMap(); - } - - //% - uint32_t mapGet(RefMap *map, uint32_t key) { - int i = map->findIdx(key); - if (i < 0) { - map->unref(); - return 0; - } - uint32_t r = map->data[i].val; - map->unref(); - return r; - } - - //% - uint32_t mapGetRef(RefMap *map, uint32_t key) { - int i = map->findIdx(key); - if (i < 0) { - map->unref(); - return 0; - } - uint32_t r = incr(map->data[i].val); - map->unref(); - return r; - } - - //% - void mapSet(RefMap *map, uint32_t key, uint32_t val) { - int i = map->findIdx(key); - if (i < 0) { - map->data.push_back({ - key << 1, - val - }); - } else { - if (map->data[i].key & 1) { - decr(map->data[i].val); - map->data[i].key = key << 1; - } - map->data[i].val = val; - } - map->unref(); - } - - //% - void mapSetRef(RefMap *map, uint32_t key, uint32_t val) { - int i = map->findIdx(key); - if (i < 0) { - map->data.push_back({ - (key << 1) | 1, - val - }); - } else { - if (map->data[i].key & 1) { - decr(map->data[i].val); - } else { - map->data[i].key = (key << 1) | 1; - } - map->data[i].val = val; - } - map->unref(); - } - - // - // Debugger - // - - //% - void* getGlobalsPtr() { - return globals; - } - - //% - void runtimeWarning(StringData *s) { - // noop for now - } } + +//% +void panic(int code) { + target_panic(code); +} + +//% +String emptyToNull(String s) { + if (!s || s->length == 0) + return NULL; + return s; +} + +//% +int ptrToBool(TValue p) { + if (p) { + decr(p); + return 1; + } else { + return 0; + } +} + +//% +RefMap *mkMap() { + auto r = new RefMap(); + MEMDBG("mkMap: %p", r); + return r; +} + +//% +TValue mapGet(RefMap *map, unsigned key) { + int i = map->findIdx(key); + if (i < 0) { + map->unref(); + return 0; + } + TValue r = incr(map->values.get(i)); + map->unref(); + return r; +} + +//% +TValue mapGetRef(RefMap *map, unsigned key) { + return mapGet(map, key); +} + +//% +void mapSet(RefMap *map, unsigned key, TValue val) { + int i = map->findIdx(key); + if (i < 0) { + map->keys.push((TValue)key); + map->values.push(val); + } else { + map->values.setRef(i, val); + } + map->unref(); +} + +//% +void mapSetRef(RefMap *map, unsigned key, TValue val) { + mapSet(map, key, val); +} + +// +// Debugger +// + +// This is only to be called once at the beginning of lambda function +//% +void *getGlobalsPtr() { +#ifdef DEVICE_GROUP_ID_USER + fiber_set_group(DEVICE_GROUP_ID_USER); +#endif + + return globals; +} + +//% +void runtimeWarning(String s) { + // noop for now +} +} // namespace pxtrt +#endif + +namespace pxt { + +//% +ValType valType(TValue v) { + if (isTagged(v)) { + if (!v) + return ValType::Undefined; + + if (isNumber(v)) + return ValType::Number; + if (v == TAG_TRUE || v == TAG_FALSE) + return ValType::Boolean; + else if (v == TAG_NULL) + return ValType::Object; + else { + oops(); + return ValType::Object; + } + } else { + int tag = ((RefObject *)v)->vtable; + + if (tag == PXT_REF_TAG_STRING) + return ValType::String; + else if (tag == PXT_REF_TAG_NUMBER) + return ValType::Number; + else if (tag == PXT_REF_TAG_ACTION || getVTable((RefObject *)v) == &RefAction_vtable) + return ValType::Function; + + return ValType::Object; + } +} + +PXT_DEF_STRING(sObjectTp, "\x06\x00object") +PXT_DEF_STRING(sBooleanTp, "\x07\x00boolean") +PXT_DEF_STRING(sStringTp, "\x06\x00string") +PXT_DEF_STRING(sNumberTp, "\x06\x00number") +PXT_DEF_STRING(sFunctionTp, "\x08\x00function") +PXT_DEF_STRING(sUndefinedTp, "\x09\x00undefined") + +//% +String typeOf(TValue v) { + switch (valType(v)) { + case ValType::Undefined: + return (String)sUndefinedTp; + case ValType::Boolean: + return (String)sBooleanTp; + case ValType::Number: + return (String)sNumberTp; + case ValType::String: + return (String)sStringTp; + case ValType::Object: + return (String)sObjectTp; + case ValType::Function: + return (String)sFunctionTp; + default: + oops(); + return 0; + } +} + +// Maybe in future we will want separate print methods; for now ignore +void anyPrint(TValue v) { + if (valType(v) == ValType::Object) { + if (isRefCounted(v)) { + auto o = (RefObject *)v; + auto meth = ((RefObjectMethod)getVTable(o)->methods[1]); + if ((void *)meth == (void *)&anyPrint) + DMESG("[RefObject refs=%d vt=%p]", o->refcnt, o->vtable); + else + meth(o); + } else { + DMESG("[Native %p]", v); + } + } else { +#ifndef X86_64 + String s = numops::toString(v); + DMESG("[%s %p = %s]", pxt::typeOf(v)->data, v, s->data); + decr((TValue)s); +#endif + } +} + +void dtorDoNothing() {} + +#define PRIM_VTABLE(name, sz) \ + const VTable name = {sz, \ + 0, \ + 0, \ + { \ + (void *)&dtorDoNothing, \ + (void *)&anyPrint, \ + }}; +PRIM_VTABLE(string_vt, 0) +PRIM_VTABLE(image_vt, 0) +PRIM_VTABLE(buffer_vt, 0) +PRIM_VTABLE(number_vt, 12) +PRIM_VTABLE(action_vt, 0) + +static const VTable *primVtables[] = {0, // 0 + &string_vt, // 1 + &buffer_vt, // 2 + &image_vt, // 3 + // filler: + 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, + &number_vt, // 32 + &action_vt, // 33 + 0}; + +VTable *getVTable(RefObject *r) { + if (r->vtable >= 34) + return (VTable *)((uintptr_t)r->vtable << vtableShift); + if (r->vtable == 0) + target_panic(100); + return (VTable *)primVtables[r->vtable]; +} +} // namespace pxt diff --git a/libs/core/enums.d.ts b/libs/core/enums.d.ts index 431629c3..4c9197d3 100644 --- a/libs/core/enums.d.ts +++ b/libs/core/enums.d.ts @@ -1,4 +1,35 @@ // Auto-generated. Do not edit. + + + declare const enum NumberFormat { + Int8LE = 1, + UInt8LE = 2, + Int16LE = 3, + UInt16LE = 4, + Int32LE = 5, + Int8BE = 6, + UInt8BE = 7, + Int16BE = 8, + UInt16BE = 9, + Int32BE = 10, + + UInt32LE = 11, + UInt32BE = 12, + Float32LE = 13, + Float64LE = 14, + Float32BE = 15, + Float64BE = 16, + } + + + declare const enum ValType { + Undefined = 0, + Boolean = 1, + Number = 2, + String = 3, + Object = 4, + Function = 5, + } declare namespace images { } declare namespace basic { @@ -536,19 +567,4 @@ declare namespace led { declare namespace serial { } - - declare const enum NumberFormat { - Int8LE = 1, - UInt8LE = 2, - Int16LE = 3, - UInt16LE = 4, - Int32LE = 5, - Int8BE = 6, - UInt8BE = 7, - Int16BE = 8, - UInt16BE = 9, - Int32BE = 10, - // UInt32, - } - // Auto-generated. Do not edit. Really. diff --git a/libs/core/images.cpp b/libs/core/images.cpp index 82203664..7becf1b2 100644 --- a/libs/core/images.cpp +++ b/libs/core/images.cpp @@ -1,156 +1,178 @@ #include "pxt.h" -/** -* Creation, manipulation and display of LED images. -*/ -//% color=#5C2D91 weight=31 icon="\uf03e" -//% advanced=true -namespace images { - /** - * Creates an image that fits on the LED screen. - */ - //% weight=75 help=images/create-image - //% blockId=device_build_image block="create image" - //% parts="ledmatrix" - Image createImage(ImageLiteral leds) { - return MicroBitImage(imageBytes(leds)).clone().leakData(); - } +PXT_VTABLE_BEGIN(RefMImage, 0, 0) +PXT_VTABLE_END - /** - * Creates an image with 2 frames. - */ - //% weight=74 help=images/create-big-image - //% blockId=device_build_big_image block="create big image" imageLiteral=2 - //% parts="ledmatrix" - Image createBigImage(ImageLiteral leds) { - return createImage(leds); +RefMImage::RefMImage(ImageData *d) : PXT_VTABLE_INIT(RefMImage), img(d) { + img->incr(); +} + +void RefMImage::destroy(RefMImage *t) { + t->img->decr(); +} + +void RefMImage::print(RefMImage *t) { + DMESG("RefMImage %p r=%d size=%d x %d", t, t->refcnt, img->width, img->height); +} + +void RefMImage::makeWritable() { + if (img->isReadOnly()) { + MicroBitImage i(img); + img = i.clone().leakData(); } } +/** + * Creation, manipulation and display of LED images. + */ +//% color=#5C2D91 weight=31 icon="\uf03e" +//% advanced=true +namespace images { +/** + * Creates an image that fits on the LED screen. + */ +//% weight=75 help=images/create-image +//% blockId=device_build_image block="create image" +//% parts="ledmatrix" +Image createImage(ImageLiteral_ leds) { + return new RefMImage(imageBytes(leds)); +} + +/** + * Creates an image with 2 frames. + */ +//% weight=74 help=images/create-big-image +//% blockId=device_build_big_image block="create big image" imageLiteral=2 +//% parts="ledmatrix" +Image createBigImage(ImageLiteral_ leds) { + return createImage(leds); +} +} // namespace images + namespace ImageMethods { - /** - * Plots the image at a given column to the screen - */ - //% help=images/plot-image - //% parts="ledmatrix" - void plotImage(Image i, int xOffset = 0) { - uBit.display.print(MicroBitImage(i), -xOffset, 0, 0, 0); - } +/** + * Plots the image at a given column to the screen + */ +//% help=images/plot-image +//% parts="ledmatrix" +void plotImage(Image i, int xOffset = 0) { + uBit.display.print(MicroBitImage(i->img), -xOffset, 0, 0, 0); +} - /** - * Shows an frame from the image at offset ``x offset``. - * @param xOffset column index to start displaying the image - */ - //% help=images/show-image weight=80 blockNamespace=images - //% blockId=device_show_image_offset block="show image %sprite|at offset %offset" blockGap=8 - //% parts="ledmatrix" async - void showImage(Image sprite, int xOffset, int interval = 400) { - uBit.display.print(MicroBitImage(sprite), -xOffset, 0, 0, interval); - } +/** + * Shows an frame from the image at offset ``x offset``. + * @param xOffset column index to start displaying the image + */ +//% help=images/show-image weight=80 blockNamespace=images +//% blockId=device_show_image_offset block="show image %sprite|at offset %offset" blockGap=8 +//% parts="ledmatrix" async +void showImage(Image sprite, int xOffset, int interval = 400) { + uBit.display.print(MicroBitImage(sprite->img), -xOffset, 0, 0, interval); +} - /** - * Draws the ``index``-th frame of the image on the screen. - * @param xOffset column index to start displaying the image - */ - //% help=images/plot-frame weight=80 - //% parts="ledmatrix" - void plotFrame(Image i, int xOffset) { - // TODO showImage() used in original implementation - plotImage(i, xOffset * 5); - } +/** + * Draws the ``index``-th frame of the image on the screen. + * @param xOffset column index to start displaying the image + */ +//% help=images/plot-frame weight=80 +//% parts="ledmatrix" +void plotFrame(Image i, int xOffset) { + // TODO showImage() used in original implementation + plotImage(i, xOffset * 5); +} - /** - * Scrolls an image . - * @param frameOffset x offset moved on each animation step, eg: 1, 2, 5 - * @param interval time between each animation step in milli seconds, eg: 200 - */ - //% help=images/scroll-image weight=79 async blockNamespace=images - //% blockId=device_scroll_image block="scroll image %sprite|with offset %frameoffset|and interval (ms) %delay" blockGap=8 - //% parts="ledmatrix" - void scrollImage(Image id, int frameOffset, int interval) { - MicroBitImage i(id); - uBit.display.animate(i, interval, frameOffset, MICROBIT_DISPLAY_ANIMATE_DEFAULT_POS, 0); - } +/** + * Scrolls an image . + * @param frameOffset x offset moved on each animation step, eg: 1, 2, 5 + * @param interval time between each animation step in milli seconds, eg: 200 + */ +//% help=images/scroll-image weight=79 async blockNamespace=images +//% blockId=device_scroll_image +//% block="scroll image %sprite|with offset %frameoffset|and interval (ms) %delay" +//% blockGap=8 parts="ledmatrix" +void scrollImage(Image id, int frameOffset, int interval) { + MicroBitImage i(id->img); + uBit.display.animate(i, interval, frameOffset, MICROBIT_DISPLAY_ANIMATE_DEFAULT_POS, 0); +} +/** + * Sets all pixels off. + */ +//% help=images/clear +//% parts="ledmatrix" +void clear(Image i) { + i->makeWritable(); + MicroBitImage(i->img).clear(); +} - /** - * Sets all pixels off. - */ - //% help=images/clear - //% parts="ledmatrix" - void clear(Image i) { - MicroBitImage(i).clear(); - } +/** + * Sets a specific pixel brightness at a given position + */ +//% +//% parts="ledmatrix" +void setPixelBrightness(Image i, int x, int y, int value) { + i->makeWritable(); + MicroBitImage(i->img).setPixelValue(x, y, value); +} - /** - * Sets a specific pixel brightness at a given position - */ - //% - //% parts="ledmatrix" - void setPixelBrightness(Image i, int x, int y, int value) { - MicroBitImage(i).setPixelValue(x, y, value); - } +/** + * Gets the pixel brightness ([0..255]) at a given position + */ +//% +//% parts="ledmatrix" +int pixelBrightness(Image i, int x, int y) { + int pix = MicroBitImage(i->img).getPixelValue(x, y); + if (pix < 0) + return 0; + return pix; +} +/** + * Gets the width in columns + */ +//% help=functions/width +int width(Image i) { + return i->img->width; +} - /** - * Gets the pixel brightness ([0..255]) at a given position - */ - //% - //% parts="ledmatrix" - int pixelBrightness(Image i, int x, int y) { - int pix = MicroBitImage(i).getPixelValue(x, y); - if (pix < 0) return 0; - return pix; - } +/** + * Gets the height in rows (always 5) + */ +//% +int height(Image i) { + return i->img->height; +} +/** + * Set a pixel state at position ``(x,y)`` + * @param x TODO + * @param y TODO + * @param value TODO + */ +//% help=images/set-pixel +//% parts="ledmatrix" +void setPixel(Image i, int x, int y, bool value) { + setPixelBrightness(i, x, y, value ? 255 : 0); +} - /** - * Gets the width in columns - */ - //% help=functions/width - int width(Image i) { - return i->width; - } +/** + * Get the pixel state at position ``(x,y)`` + * @param x TODO + * @param y TODO + */ +//% help=images/pixel +//% parts="ledmatrix" +bool pixel(Image i, int x, int y) { + return pixelBrightness(i, x, y) > 0; +} - /** - * Gets the height in rows (always 5) - */ - //% - int height(Image i) { - return i->height; - } - - /** - * Set a pixel state at position ``(x,y)`` - * @param x TODO - * @param y TODO - * @param value TODO - */ - //% help=images/set-pixel - //% parts="ledmatrix" - void setPixel(Image i, int x, int y, bool value) { - setPixelBrightness(i, x, y, value ? 255 : 0); - } - - /** - * Get the pixel state at position ``(x,y)`` - * @param x TODO - * @param y TODO - */ - //% help=images/pixel - //% parts="ledmatrix" - bool pixel(Image i, int x, int y) { - return pixelBrightness(i, x, y) > 0; - } - - - /** - * Shows a particular frame of the image strip. - * @param frame TODO - */ - //% weight=70 help=images/show-frame - //% parts="ledmatrix" - void showFrame(Image i, int frame, int interval = 400) { - showImage(i, frame * 5, interval); - } -} \ No newline at end of file +/** + * Shows a particular frame of the image strip. + * @param frame TODO + */ +//% weight=70 help=images/show-frame +//% parts="ledmatrix" +void showFrame(Image i, int frame, int interval = 400) { + showImage(i, frame * 5, interval); +} +} // namespace ImageMethods \ No newline at end of file diff --git a/libs/core/led.cpp b/libs/core/led.cpp index 8c4bcaf9..e0f0fbf8 100644 --- a/libs/core/led.cpp +++ b/libs/core/led.cpp @@ -139,7 +139,10 @@ namespace led { //% help=led/screenshot //% parts="ledmatrix" Image screenshot() { - return uBit.display.screenShot().leakData(); + auto d = uBit.display.screenShot().leakData(); + auto r = new RefMImage(d); + d->decr(); + return r; /* let Image img; img = image.createImage(""); diff --git a/libs/core/pins.cpp b/libs/core/pins.cpp index 1ada7a94..c596f315 100644 --- a/libs/core/pins.cpp +++ b/libs/core/pins.cpp @@ -84,6 +84,9 @@ enum class PinEventType { None = MICROBIT_PIN_EVENT_NONE }; + +namespace pxt +{ MicroBitPin *getPin(int id) { switch (id) { case MICROBIT_ID_IO_P0: return &uBit.io.P0; @@ -109,6 +112,7 @@ MicroBitPin *getPin(int id) { } } +} // pxt namespace pins { #define PINOP(op) \ @@ -359,7 +363,7 @@ namespace pins { //% Buffer createBuffer(int size) { - return ManagedBuffer(size).leakData(); + return mkBuffer(NULL, size); } /** @@ -369,7 +373,7 @@ namespace pins { Buffer i2cReadBuffer(int address, int size, bool repeat = false) { Buffer buf = createBuffer(size); - uBit.i2c.read(address << 1, (char*)buf->payload, size, repeat); + uBit.i2c.read(address << 1, (char*)buf->data, size, repeat); return buf; } @@ -379,7 +383,7 @@ namespace pins { //% void i2cWriteBuffer(int address, Buffer buf, bool repeat = false) { - uBit.i2c.write(address << 1, (char*)buf->payload, buf->length, repeat); + uBit.i2c.write(address << 1, (char*)buf->data, buf->length, repeat); } SPI* spi = NULL; diff --git a/libs/core/pxt.cpp b/libs/core/pxt.cpp index bdea3518..22720878 100644 --- a/libs/core/pxt.cpp +++ b/libs/core/pxt.cpp @@ -1,708 +1,527 @@ -#include "pxt.h" -#include +#include "pxtbase.h" -MicroBit uBit; +using namespace std; namespace pxt { - int incr(uint32_t e) - { - if (e) { - if (hasVTable(e)) - ((RefObject*)e)->ref(); - else - ((RefCounted*)e)->incr(); - } - return e; + +TValue incr(TValue e) { + if (isRefCounted(e)) { + getVTable((RefObject *)e); +#if MEMDBG_ENABLED + if (((RefObject *)e)->refcnt != 0xffff) + MEMDBG("INCR: %p refs=%d", e, ((RefObject *)e)->refcnt); +#endif + ((RefObject *)e)->ref(); + } + return e; +} + +void decr(TValue e) { + if (isRefCounted(e)) { +#if MEMDBG_ENABLED + if (((RefObject *)e)->refcnt != 0xffff) + MEMDBG("DECR: %p refs=%d", e, ((RefObject *)e)->refcnt); +#endif + ((RefObject *)e)->unref(); + } +} + +// TODO +Action mkAction(int reflen, int totallen, int startptr) { + check(0 <= reflen && reflen <= totallen, ERR_SIZE, 1); + check(reflen <= totallen && totallen <= 255, ERR_SIZE, 2); + check(bytecode[startptr] == 0xffff, ERR_INVALID_BINARY_HEADER, 3); + check(bytecode[startptr + 1] == PXT_REF_TAG_ACTION, ERR_INVALID_BINARY_HEADER, 4); + + uintptr_t tmp = (uintptr_t)&bytecode[startptr]; + + if (totallen == 0) { + return (TValue)tmp; // no closure needed } - void decr(uint32_t e) - { - if (e) { - if (hasVTable(e)) - ((RefObject*)e)->unref(); - else - ((RefCounted*)e)->decr(); - } + void *ptr = ::operator new(sizeof(RefAction) + totallen * sizeof(unsigned)); + RefAction *r = new (ptr) RefAction(); + r->len = totallen; + r->reflen = reflen; + r->func = (ActionCB)((tmp + 4) | 1); + memset(r->fields, 0, r->len * sizeof(unsigned)); + + MEMDBG("mkAction: start=%p => %p", startptr, r); + + return (Action)r; +} + +// TODO +TValue runAction3(Action a, TValue arg0, TValue arg1, TValue arg2) { + auto aa = (RefAction *)a; + if (aa->vtable == PXT_REF_TAG_ACTION) { + check(aa->refcnt == 0xffff, ERR_INVALID_BINARY_HEADER, 4); + return ((ActionCB)(((uintptr_t)a + 4) | 1))(NULL, arg0, arg1, arg2); + } else { + check(aa->refcnt != 0xffff, ERR_INVALID_BINARY_HEADER, 4); + return aa->runCore(arg0, arg1, arg2); } +} - Action mkAction(int reflen, int totallen, int startptr) - { - check(0 <= reflen && reflen <= totallen, ERR_SIZE, 1); - check(reflen <= totallen && totallen <= 255, ERR_SIZE, 2); - check(bytecode[startptr] == 0xffff, ERR_INVALID_BINARY_HEADER, 3); - check(bytecode[startptr + 1] == 0, ERR_INVALID_BINARY_HEADER, 4); +TValue runAction2(Action a, TValue arg0, TValue arg1) { + return runAction3(a, arg0, arg1, 0); +} - uint32_t tmp = (uint32_t)&bytecode[startptr]; +TValue runAction1(Action a, TValue arg0) { + return runAction3(a, arg0, 0, 0); +} - if (totallen == 0) { - return tmp; // no closure needed - } +TValue runAction0(Action a) { + return runAction3(a, 0, 0, 0); +} - void *ptr = ::operator new(sizeof(RefAction) + totallen * sizeof(uint32_t)); - RefAction *r = new (ptr) RefAction(); - r->len = totallen; - r->reflen = reflen; - r->func = (ActionCB)((tmp + 4) | 1); - memset(r->fields, 0, r->len * sizeof(uint32_t)); +RefRecord *mkClassInstance(int vtableOffset) { + VTable *vtable = (VTable *)&bytecode[vtableOffset]; - return (Action)r; + intcheck(vtable->methods[0] == &RefRecord_destroy, ERR_SIZE, 3); + intcheck(vtable->methods[1] == &RefRecord_print, ERR_SIZE, 4); + + void *ptr = ::operator new(vtable->numbytes); + RefRecord *r = new (ptr) RefRecord(PXT_VTABLE_TO_INT(vtable)); + memset(r->fields, 0, vtable->numbytes - sizeof(RefRecord)); + MEMDBG("mkClass: vt=%p => %p", vtable, r); + return r; +} + +TValue RefRecord::ld(int idx) { + // intcheck((reflen == 255 ? 0 : reflen) <= idx && idx < len, ERR_OUT_OF_BOUNDS, 1); + return fields[idx]; +} + +TValue RefRecord::ldref(int idx) { + // DMESG("LD %p len=%d reflen=%d idx=%d", this, len, reflen, idx); + // intcheck(0 <= idx && idx < reflen, ERR_OUT_OF_BOUNDS, 2); + TValue tmp = fields[idx]; + incr(tmp); + return tmp; +} + +void RefRecord::st(int idx, TValue v) { + // intcheck((reflen == 255 ? 0 : reflen) <= idx && idx < len, ERR_OUT_OF_BOUNDS, 3); + fields[idx] = v; +} + +void RefRecord::stref(int idx, TValue v) { + // DMESG("ST %p len=%d reflen=%d idx=%d", this, len, reflen, idx); + // intcheck(0 <= idx && idx < reflen, ERR_OUT_OF_BOUNDS, 4); + decr(fields[idx]); + fields[idx] = v; +} + +void RefObject::destroyVT() { + ((RefObjectMethod)getVTable(this)->methods[0])(this); + ::operator delete(this); +} + +void RefObject::printVT() { + ((RefObjectMethod)getVTable(this)->methods[1])(this); +} + +void RefRecord_destroy(RefRecord *r) { + VTable *tbl = getVTable(r); + uint8_t *refmask = (uint8_t *)&tbl->methods[tbl->userdata & 0xff]; + int len = (tbl->numbytes >> 2) - 1; + for (int i = 0; i < len; ++i) { + if (refmask[i]) + decr(r->fields[i]); + r->fields[i] = 0; } +} - uint32_t runAction3(Action a, int arg0, int arg1, int arg2) - { - if (hasVTable(a)) - return ((RefAction*)a)->runCore(arg0, arg1, arg2); - else { - check(*(uint16_t*)a == 0xffff, ERR_INVALID_BINARY_HEADER, 4); - return ((ActionCB)((a + 4) | 1))(NULL, arg0, arg1, arg2); - } - } +void RefRecord_print(RefRecord *r) { + DMESG("RefRecord %p r=%d size=%d bytes", r, r->refcnt, getVTable(r)->numbytes); +} - uint32_t runAction2(Action a, int arg0, int arg1) - { - return runAction3(a, arg0, arg1, 0); - } - - uint32_t runAction1(Action a, int arg0) - { - return runAction3(a, arg0, 0, 0); - } - - uint32_t runAction0(Action a) - { - return runAction3(a, 0, 0, 0); - } - - RefRecord* mkClassInstance(int vtableOffset) - { - VTable *vtable = (VTable*)&bytecode[vtableOffset]; - - intcheck(vtable->methods[0] == &RefRecord_destroy, ERR_SIZE, 3); - intcheck(vtable->methods[1] == &RefRecord_print, ERR_SIZE, 4); - - void *ptr = ::operator new(vtable->numbytes); - RefRecord *r = new (ptr) RefRecord(PXT_VTABLE_TO_INT(vtable)); - memset(r->fields, 0, vtable->numbytes - sizeof(RefRecord)); - return r; - } - - uint32_t RefRecord::ld(int idx) - { - //intcheck((reflen == 255 ? 0 : reflen) <= idx && idx < len, ERR_OUT_OF_BOUNDS, 1); - return fields[idx]; - } - - uint32_t RefRecord::ldref(int idx) - { - //printf("LD %p len=%d reflen=%d idx=%d\n", this, len, reflen, idx); - //intcheck(0 <= idx && idx < reflen, ERR_OUT_OF_BOUNDS, 2); - uint32_t tmp = fields[idx]; - incr(tmp); - return tmp; - } - - void RefRecord::st(int idx, uint32_t v) - { - //intcheck((reflen == 255 ? 0 : reflen) <= idx && idx < len, ERR_OUT_OF_BOUNDS, 3); - fields[idx] = v; - } - - void RefRecord::stref(int idx, uint32_t v) - { - //printf("ST %p len=%d reflen=%d idx=%d\n", this, len, reflen, idx); - //intcheck(0 <= idx && idx < reflen, ERR_OUT_OF_BOUNDS, 4); - decr(fields[idx]); - fields[idx] = v; - } - - void RefObject::destroy() { - ((RefObjectMethod)getVTable()->methods[0])(this); - } - - void RefObject::print() { - ((RefObjectMethod)getVTable()->methods[1])(this); - } - - void RefRecord_destroy(RefRecord *r) { - auto tbl = r->getVTable(); - uint8_t *refmask = (uint8_t*)&tbl->methods[tbl->userdata & 0xff]; - int len = (tbl->numbytes >> 2) - 1; - for (int i = 0; i < len; ++i) { - if (refmask[i]) decr(r->fields[i]); - r->fields[i] = 0; - } - //RefRecord is allocated using placement new - r->~RefRecord(); - ::operator delete(r); - } - - void RefRecord_print(RefRecord *r) - { - printf("RefRecord %p r=%d size=%d bytes\n", r, r->refcnt, r->getVTable()->numbytes); - } - - uint32_t Segment::get(uint32_t i) - { +TValue Segment::get(unsigned i) { #ifdef DEBUG_BUILD - printf("In Segment::get index:%u\n", i); - this->print(); + DMESG("In Segment::get index:%d", i); + this->print(); #endif - if (i < length) - { - return data[i]; - } - return Segment::DefaultValue; + if (i < length) { + return data[i]; + } + return Segment::DefaultValue; +} + +void Segment::setRef(unsigned i, TValue value) { + decr(get(i)); + set(i, value); +} + +void Segment::set(unsigned i, TValue value) { + if (i < size) { + data[i] = value; + } else if (i < Segment::MaxSize) { + growByMin(i + 1); + data[i] = value; + } else { + return; + } + if (length <= i) { + length = i + 1; } - void Segment::set(uint32_t i, uint32_t value) - { - if (i < size) - { - data[i] = value; - } - else if (i < Segment::MaxSize) - { - growByMin(i + 1); - data[i] = value; - } - if (length <= i) - { - length = i + 1; +#ifdef DEBUG_BUILD + DMESG("In Segment::set"); + this->print(); +#endif + + return; +} + +ramint_t Segment::growthFactor(ramint_t size) { + if (size == 0) { + return 4; + } + if (size < 64) { + return size * 2; // Double + } + if (size < 512) { + return size * 5 / 3; // Grow by 1.66 rate + } + // Grow by constant rate + if ((unsigned)size + 256 < MaxSize) + return size + 256; + else + return MaxSize; +} + +void Segment::growByMin(ramint_t minSize) { + growBy(max(minSize, growthFactor(size))); +} + +void Segment::growBy(ramint_t newSize) { +#ifdef DEBUG_BUILD + DMESG("growBy: %d", newSize); + this->print(); +#endif + if (size < newSize) { + // this will throw if unable to allocate + TValue *tmp = (TValue *)(::operator new(newSize * sizeof(TValue))); + + // Copy existing data + if (size) { + memcpy(tmp, data, size * sizeof(TValue)); } + // fill the rest with default value + memset(tmp + size, 0, (newSize - size) * sizeof(TValue)); + + // free older segment; + ::operator delete(data); + + data = tmp; + size = newSize; #ifdef DEBUG_BUILD - printf("In Segment::set\n"); + DMESG("growBy - after reallocation"); this->print(); #endif + } + // else { no shrinking yet; } + return; +} +void Segment::ensure(ramint_t newSize) { + if (newSize < size) { return; } + growByMin(newSize); +} - uint16_t Segment::growthFactor(uint16_t size) - { - if (size == 0) - { - return 4; - } - if (size < 64) - { - return size * 2; // Double - } - if (size < 512) - { - return size * 5/3; //Grow by 1.66 rate - } - return size + 256; //Grow by constant rate - } - - void Segment::growByMin(uint16_t minSize) - { - growBy(max(minSize, growthFactor(size))); - } - - void Segment::growBy(uint16_t newSize) - { -#ifdef DEBUG_BUILD - printf("growBy: %d\n", newSize); - this->print(); -#endif - if (size < newSize) - { - //this will throw if unable to allocate - uint32_t *tmp = (uint32_t*)(::operator new(newSize * sizeof(uint32_t))); - - //Copy existing data - if (size) - { - memcpy(tmp, data, size * sizeof(uint32_t)); - } - //fill the rest with default value - memset(tmp + size, Segment::DefaultValue, (newSize - size) * sizeof(uint32_t)); - - //free older segment; - ::operator delete(data); - - data = tmp; - size = newSize; - -#ifdef DEBUG_BUILD - printf("growBy - after reallocation\n"); - this->print(); -#endif - - } - //else { no shrinking yet; } - return; - } - - void Segment::ensure(uint16_t newSize) - { - if (newSize < size) - { - return; - } - growByMin(newSize); - } - - void Segment::setLength(uint32_t newLength) - { - if (newLength > size) - { +void Segment::setLength(unsigned newLength) { + if (newLength > size) { ensure(length); - } - length = newLength; - return; } + length = newLength; + return; +} - void Segment::push(uint32_t value) - { - this->set(length, value); - } +void Segment::push(TValue value) { + this->set(length, value); +} - uint32_t Segment::pop() - { +TValue Segment::pop() { #ifdef DEBUG_BUILD - printf("In Segment::pop\n"); - this->print(); + DMESG("In Segment::pop"); + this->print(); #endif - if (length > 0) - { + if (length > 0) { --length; - uint32_t value = data[length]; + TValue value = data[length]; data[length] = Segment::DefaultValue; return value; - } - return Segment::DefaultValue; } + return Segment::DefaultValue; +} - //this function removes an element at index i and shifts the rest of the elements to - //left to fill the gap - uint32_t Segment::remove(uint32_t i) - { +// this function removes an element at index i and shifts the rest of the elements to +// left to fill the gap +TValue Segment::remove(unsigned i) { #ifdef DEBUG_BUILD - printf("In Segment::remove index:%u\n", i); - this->print(); + DMESG("In Segment::remove index:%d", i); + this->print(); #endif - if (i < length) - { - //value to return - uint32_t ret = data[i]; - if (i + 1 < length) - { - //Move the rest of the elements to fill in the gap. - memmove(data + i, data + i + 1, (length - i - 1) * sizeof(uint32_t)); + if (i < length) { + // value to return + TValue ret = data[i]; + if (i + 1 < length) { + // Move the rest of the elements to fill in the gap. + memmove(data + i, data + i + 1, (length - i - 1) * sizeof(unsigned)); } length--; data[length] = Segment::DefaultValue; #ifdef DEBUG_BUILD - printf("After Segment::remove index:%u\n", i); + DMESG("After Segment::remove index:%d", i); this->print(); #endif return ret; - } - return Segment::DefaultValue; } + return Segment::DefaultValue; +} - //this function inserts element value at index i by shifting the rest of the elements right. - void Segment::insert(uint32_t i, uint32_t value) - { +// this function inserts element value at index i by shifting the rest of the elements right. +void Segment::insert(unsigned i, TValue value) { #ifdef DEBUG_BUILD - printf("In Segment::insert index:%u value:%u\n", i, value); - this->print(); + DMESG("In Segment::insert index:%d value:%d", i, value); + this->print(); #endif - if (i < length) - { + if (i < length) { ensure(length + 1); - if (i + 1 < length) - { - //Move the rest of the elements to fill in the gap. - memmove(data + i + 1, data + i, (length - i) * sizeof(uint32_t)); - } + + // Move the rest of the elements to fill in the gap. + memmove(data + i + 1, data + i, (length - i) * sizeof(unsigned)); data[i] = value; length++; - } - else - { - //This is insert beyond the length, just call set which will adjust the length + } else { + // This is insert beyond the length, just call set which will adjust the length set(i, value); - } + } #ifdef DEBUG_BUILD - printf("After Segment::insert index:%u\n", i); - this->print(); + DMESG("After Segment::insert index:%d", i); + this->print(); #endif - } +} - void Segment::print() - { - printf("Segment: %x, length: %u, size: %u\n", data, (uint32_t)length, (uint32_t)size); - for(uint32_t i = 0; i < size; i++) - { - printf("%d ",(uint32_t)data[i]); - } - printf("\n"); +void Segment::print() { + DMESG("Segment: %p, length: %d, size: %d", data, (unsigned)length, (unsigned)size); + for (unsigned i = 0; i < size; i++) { + DMESG("-> %d", (unsigned)(uintptr_t)data[i]); } +} - bool Segment::isValidIndex(uint32_t i) - { - if (i > length) - { +bool Segment::isValidIndex(unsigned i) { + if (i > length) { return false; - } - return true; } + return true; +} - void Segment::destroy() - { +void Segment::destroy() { #ifdef DEBUG_BUILD - printf("In Segment::destroy\n"); - this->print(); + DMESG("In Segment::destroy"); + this->print(); #endif - length = size = 0; - ::operator delete(data); - data = nullptr; - } + length = size = 0; + ::operator delete(data); + data = nullptr; +} - void RefCollection::push(uint32_t x) - { - if (isRef()) incr(x); - head.push(x); - } +void RefCollection::push(TValue x) { + incr(x); + head.push(x); +} - uint32_t RefCollection::pop() - { - uint32_t ret = head.pop(); - if (isRef()) - { - incr(ret); - } - return ret; - } +TValue RefCollection::pop() { + TValue ret = head.pop(); + incr(ret); + return ret; +} - uint32_t RefCollection::getAt(int i) - { - uint32_t tmp = head.get(i); - if (isRef()) - { - incr(tmp); - } - return tmp; - } +TValue RefCollection::getAt(int i) { + TValue tmp = head.get(i); + incr(tmp); + return tmp; +} - uint32_t RefCollection::removeAt(int i) - { - // no decr() - we return the result - return head.remove(i); - } +TValue RefCollection::removeAt(int i) { + return head.remove(i); +} - void RefCollection::insertAt(int i, uint32_t value) - { - head.insert(i, value); - if (isRef()) - { - incr(value); - } - } +void RefCollection::insertAt(int i, TValue value) { + head.insert(i, value); + incr(value); +} - void RefCollection::setAt(int i, uint32_t value) - { - if (isRef()) - { - if (head.isValidIndex((uint32_t)i)) - { - decr(head.get(i)); - } - incr(value); - } - head.set(i, value); - } +void RefCollection::setAt(int i, TValue value) { + incr(value); + head.setRef(i, value); +} - int RefCollection::indexOf(uint32_t x, int start) - { - if (isString()) - { - StringData *xx = (StringData*)x; - uint32_t i = start; - while(head.isValidIndex(i)) - { - StringData *ee = (StringData*)head.get(i); - if (ee == xx) - { - //handles ee being null - return (int) i; - } - if (ee && xx->len == ee->len && memcmp(xx->data, ee->data, xx->len) == 0) - { +int RefCollection::indexOf(TValue x, int start) { +#ifndef X86_64 + unsigned i = start; + while (head.isValidIndex(i)) { + if (pxt::eq_bool(head.get(i), x)) { return (int)i; - } - i++; } - } - else - { - uint32_t i = start; - while(head.isValidIndex(i)) - { - if (head.get(i) == x) - { - return (int)i; - } - i++; - } - } - - return -1; + i++; } +#endif + return -1; +} - int RefCollection::removeElement(uint32_t x) - { - int idx = indexOf(x, 0); - if (idx >= 0) { - uint32_t elt = removeAt(idx); - if (isRef()) decr(elt); +bool RefCollection::removeElement(TValue x) { + int idx = indexOf(x, 0); + if (idx >= 0) { + decr(removeAt(idx)); return 1; - } - return 0; } + return 0; +} - namespace Coll0 { - PXT_VTABLE_BEGIN(RefCollection, 0, 0) - PXT_VTABLE_END +namespace Coll0 { +PXT_VTABLE_BEGIN(RefCollection, 0, 0) +PXT_VTABLE_END +} // namespace Coll0 + +RefCollection::RefCollection() : RefObject(0) { + vtable = PXT_VTABLE_TO_INT(&Coll0::RefCollection_vtable); +} + +void RefCollection::destroy(RefCollection *t) { + for (unsigned i = 0; i < t->head.getLength(); i++) { + decr(t->head.get(i)); } - namespace Coll1 { - PXT_VTABLE_BEGIN(RefCollection, 1, 0) - PXT_VTABLE_END + t->head.destroy(); +} + +void RefCollection::print(RefCollection *t) { + DMESG("RefCollection %p r=%d size=%d", t, t->refcnt, t->head.getLength()); + t->head.print(); +} + +PXT_VTABLE_CTOR(RefAction) {} + +// fields[] contain captured locals +void RefAction::destroy(RefAction *t) { + for (int i = 0; i < t->reflen; ++i) { + decr(t->fields[i]); + t->fields[i] = 0; } - namespace Coll3 { - PXT_VTABLE_BEGIN(RefCollection, 3, 0) - PXT_VTABLE_END +} + +void RefAction::print(RefAction *t) { + DMESG("RefAction %p r=%d pc=%X size=%d (%d refs)", t, t->refcnt, + (const uint8_t *)t->func - (const uint8_t *)bytecode, t->len, t->reflen); +} + +void RefLocal::print(RefLocal *t) { + DMESG("RefLocal %p r=%d v=%d", t, t->refcnt, t->v); +} + +void RefLocal::destroy(RefLocal *) {} + +PXT_VTABLE_CTOR(RefLocal) { + v = 0; +} + +PXT_VTABLE_CTOR(RefRefLocal) { + v = 0; +} + +void RefRefLocal::print(RefRefLocal *t) { + DMESG("RefRefLocal %p r=%d v=%p", t, t->refcnt, (void *)t->v); +} + +void RefRefLocal::destroy(RefRefLocal *t) { + decr(t->v); +} + +PXT_VTABLE_BEGIN(RefMap, 0, RefMapMarker) +PXT_VTABLE_END +RefMap::RefMap() : PXT_VTABLE_INIT(RefMap) {} + +void RefMap::destroy(RefMap *t) { + for (unsigned i = 0; i < t->values.getLength(); ++i) { + decr(t->values.get(i)); + t->values.set(i, 0); } + t->keys.destroy(); + t->values.destroy(); +} - RefCollection::RefCollection(uint16_t flags) : RefObject(0) { - switch (flags) { - case 0: - vtable = PXT_VTABLE_TO_INT(&Coll0::RefCollection_vtable); - break; - case 1: - vtable = PXT_VTABLE_TO_INT(&Coll1::RefCollection_vtable); - break; - case 3: - vtable = PXT_VTABLE_TO_INT(&Coll3::RefCollection_vtable); - break; - default: - error(ERR_SIZE); - break; - } +int RefMap::findIdx(unsigned key) { + for (unsigned i = 0; i < keys.getLength(); ++i) { + if ((uintptr_t)keys.get(i) == key) + return i; } + return -1; +} - void RefCollection::destroy() - { - if (this->isRef()) - { - for(uint32_t i = 0; i < this->head.getLength(); i++) - { - decr(this->head.get(i)); - } - } - this->head.destroy(); - delete this; +void RefMap::print(RefMap *t) { + DMESG("RefMap %p r=%d size=%d", t, t->refcnt, t->keys.getLength()); +} + +#ifdef PXT_MEMLEAK_DEBUG +std::set allptrs; +void debugMemLeaks() { + DMESG("LIVE POINTERS:"); + for (std::set::iterator itr = allptrs.begin(); itr != allptrs.end(); itr++) { + anyPrint(*itr); } - - void RefCollection::print() - { - printf("RefCollection %p r=%d flags=%d size=%d\n", this, refcnt, getFlags(), head.getLength()); - head.print(); - } - - PXT_VTABLE_CTOR(RefAction) {} - - // fields[] contain captured locals - void RefAction::destroy() - { - for (int i = 0; i < this->reflen; ++i) { - decr(fields[i]); - fields[i] = 0; - } - //RefAction is allocated using placement new - this->~RefAction(); - ::operator delete(this); - } - - void RefAction::print() - { - printf("RefAction %p r=%d pc=0x%lx size=%d (%d refs)\n", this, refcnt, (const uint8_t*)func - (const uint8_t*)bytecode, len, reflen); - } - - void RefLocal::print() - { - printf("RefLocal %p r=%d v=%d\n", this, refcnt, v); - } - - void RefLocal::destroy() - { - delete this; - } - - PXT_VTABLE_CTOR(RefLocal) { - v = 0; - } - - PXT_VTABLE_CTOR(RefRefLocal) { - v = 0; - } - - void RefRefLocal::print() - { - printf("RefRefLocal %p r=%d v=%p\n", this, refcnt, (void*)v); - } - - void RefRefLocal::destroy() - { - decr(v); - delete this; - } - - PXT_VTABLE_BEGIN(RefMap, 0, RefMapMarker) - PXT_VTABLE_END - RefMap::RefMap() : PXT_VTABLE_INIT(RefMap) {} - - void RefMap::destroy() { - for (unsigned i = 0; i < data.size(); ++i) { - if (data[i].key & 1) { - decr(data[i].val); - } - data[i].val = 0; - } - data.resize(0); - delete this; - } - - int RefMap::findIdx(uint32_t key) { - for (unsigned i = 0; i < data.size(); ++i) { - if (data[i].key >> 1 == key) - return i; - } - return -1; - } - - void RefMap::print() - { - printf("RefMap %p r=%d size=%d\n", this, refcnt, data.size()); - } - - -#ifdef DEBUG_MEMLEAKS - std::set allptrs; - void debugMemLeaks() - { - printf("LIVE POINTERS:\n"); - for(std::set::iterator itr = allptrs.begin();itr!=allptrs.end();itr++) - { - (*itr)->print(); - } - printf("\n"); - } + DMESG("LIVE POINTERS END."); + dumpDmesg(); +} #else - void debugMemLeaks() {} +void debugMemLeaks() {} #endif +void error(PXT_ERROR code, int subcode) { + DMESG("Error: %d [%d]", code, subcode); + target_panic(42); +} - // --------------------------------------------------------------------------- - // An adapter for the API expected by the run-time. - // --------------------------------------------------------------------------- +uint16_t *bytecode; +TValue *globals; - map, Action> handlersMap; - - MicroBitEvent lastEvent; - - // We have the invariant that if [dispatchEvent] is registered against the DAL - // for a given event, then [handlersMap] contains a valid entry for that - // event. - void dispatchEvent(MicroBitEvent e) { - - lastEvent = e; - - Action curr = handlersMap[{ e.source, e.value }]; - if (curr) - runAction1(curr, e.value); - - curr = handlersMap[{ e.source, MICROBIT_EVT_ANY }]; - if (curr) - runAction1(curr, e.value); - } - - void registerWithDal(int id, int event, Action a) { - Action prev = handlersMap[{ id, event }]; - if (prev) - decr(prev); - else - uBit.messageBus.listen(id, event, dispatchEvent); - incr(a); - handlersMap[{ id, event }] = a; - } - - void fiberDone(void *a) - { - decr((Action)a); - release_fiber(); - } - - - void runInBackground(Action a) { - if (a != 0) { - incr(a); - create_fiber((void(*)(void*))runAction0, (void*)a, fiberDone); - } - } - - - void error(ERROR code, int subcode) - { - printf("Error: %d [%d]\n", code, subcode); - uBit.panic(42); - } - - uint16_t *bytecode; - uint32_t *globals; - int numGlobals; - - uint32_t *allocate(uint16_t sz) - { - uint32_t *arr = new uint32_t[sz]; - memset(arr, 0, sz * 4); +unsigned *allocate(ramint_t sz) { + unsigned *arr = new unsigned[sz]; + memset(arr, 0, sz * sizeof(unsigned)); return arr; - } +} - void checkStr(bool cond, const char *msg) - { +void checkStr(bool cond, const char *msg) { if (!cond) { - while (true) { - uBit.display.scroll(msg, 100); - uBit.sleep(100); - } + while (true) { + // uBit.display.scroll(msg, 100); + // uBit.sleep(100); + } } - } +} - int templateHash() - { - return ((int*)bytecode)[4]; - } +int templateHash() { + return ((int *)bytecode)[4]; +} - int programHash() - { - return ((int*)bytecode)[6]; - } +int programHash() { + return ((int *)bytecode)[6]; +} - int getNumGlobals() - { +int getNumGlobals() { return bytecode[16]; - } +} - void exec_binary(int32_t *pc) - { +#ifndef X86_64 +void exec_binary(unsigned *pc) { // XXX re-enable once the calibration code is fixed and [editor/embedded.ts] // properly prepends a call to [internal_main]. // ::touch_develop::internal_main(); @@ -710,37 +529,39 @@ namespace pxt { // unique group for radio based on source hash // ::touch_develop::micro_bit::radioDefaultGroup = programHash(); - // repeat error 4 times and restart as needed - microbit_panic_timeout(4); - - int32_t ver = *pc++; + unsigned ver = *pc++; checkStr(ver == 0x4209, ":( Bad runtime version"); - bytecode = *((uint16_t**)pc++); // the actual bytecode is here - globals = allocate(getNumGlobals()); + bytecode = *((uint16_t **)pc++); // the actual bytecode is here + globals = (TValue *)allocate(getNumGlobals()); + + // can be any valid address, best in RAM for speed + globals[0] = (TValue)&globals; // just compare the first word - checkStr(((uint32_t*)bytecode)[0] == 0x923B8E70 && - templateHash() == *pc, + // TODO + checkStr(((uint32_t *)bytecode)[0] == 0x923B8E70 && (unsigned)templateHash() == *pc, ":( Failed partial flash"); - uint32_t startptr = (uint32_t)bytecode; + uintptr_t startptr = (uintptr_t)bytecode; + startptr += 48; // header - startptr |= 1; // Thumb state + startptr |= 1; // Thumb state - ((uint32_t (*)())startptr)(); + initRuntime(); -#ifdef DEBUG_MEMLEAKS + ((unsigned (*)())startptr)(); + +#ifdef PXT_MEMLEAK_DEBUG pxt::debugMemLeaks(); #endif - return; - } - - void start() - { - exec_binary((int32_t*)functionsAndBytecode); - } + pxt::releaseFiber(); } -// vim: ts=2 sw=2 expandtab +void start() { + exec_binary((unsigned *)functionsAndBytecode); +} +#endif + +} // namespace pxt diff --git a/libs/core/pxt.h b/libs/core/pxt.h index 39482b28..3aea0be0 100644 --- a/libs/core/pxt.h +++ b/libs/core/pxt.h @@ -5,375 +5,54 @@ #pragma GCC diagnostic ignored "-Wunused-parameter" -#include "MicroBit.h" -#include "MicroBitImage.h" -#include "ManagedString.h" -#include "ManagedType.h" -#include "ManagedBuffer.h" - -#define printf(...) uBit.serial.printf(__VA_ARGS__) -// #define printf(...) - -#define intcheck(...) check(__VA_ARGS__) -//#define intcheck(...) do {} while (0) - -#include -#include -#include -#include - -#ifdef DEBUG_MEMLEAKS -#include -#endif - -extern MicroBit uBit; +#include "pxtbase.h" namespace pxt { - typedef uint32_t Action; - typedef uint32_t ImageLiteral; - - typedef enum { - ERR_INVALID_BINARY_HEADER = 5, - ERR_OUT_OF_BOUNDS = 8, - ERR_REF_DELETED = 7, - ERR_SIZE = 9, - } ERROR; - - extern const uint32_t functionsAndBytecode[]; - extern uint32_t *globals; - extern uint16_t *bytecode; - class RefRecord; - - // Utility functions - extern MicroBitEvent lastEvent; - void registerWithDal(int id, int event, Action a); - void runInBackground(Action a); - uint32_t runAction3(Action a, int arg0, int arg1, int arg2); - uint32_t runAction2(Action a, int arg0, int arg1); - uint32_t runAction1(Action a, int arg0); - uint32_t runAction0(Action a); - Action mkAction(int reflen, int totallen, int startptr); - void error(ERROR code, int subcode = 0); - void exec_binary(uint16_t *pc); - void start(); - void debugMemLeaks(); - // allocate [sz] words and clear them - uint32_t *allocate(uint16_t sz); - int templateHash(); - int programHash(); - uint32_t programSize(); - uint32_t afterProgramPage(); - int getNumGlobals(); - RefRecord* mkClassInstance(int vtableOffset); - - // The standard calling convention is: - // - when a pointer is loaded from a local/global/field etc, and incr()ed - // (in other words, its presence on stack counts as a reference) - // - after a function call, all pointers are popped off the stack and decr()ed - // This does not apply to the RefRecord and st/ld(ref) methods - they unref() - // the RefRecord* this. - int incr(uint32_t e); - void decr(uint32_t e); - - inline void *ptrOfLiteral(int offset) - { - return &bytecode[offset]; - } - - inline ImageData* imageBytes(int offset) - { - return (ImageData*)(void*)&bytecode[offset]; - } - - // Checks if object has a VTable, or if its RefCounted* from the runtime. - inline bool hasVTable(uint32_t e) - { - return (*((uint32_t*)e) & 1) == 0; - } - - inline void check(int cond, ERROR code, int subcode = 0) - { - if (!cond) error(code, subcode); - } - - - class RefObject; -#ifdef DEBUG_MEMLEAKS - extern std::set allptrs; -#endif - - typedef void (*RefObjectMethod)(RefObject *self); - typedef void *PVoid; - typedef void **PPVoid; - - const PPVoid RefMapMarker = (PPVoid)(void*)43; - - struct VTable { - uint16_t numbytes; // in the entire object, including the vtable pointer - uint16_t userdata; - PVoid *ifaceTable; - PVoid methods[2]; // we only use up to two methods here; pxt will generate more - // refmask sits at &methods[nummethods] - }; - - const int vtableShift = 2; - - // A base abstract class for ref-counted objects. - class RefObject - { +class RefMImage : public RefObject { public: - uint16_t refcnt; - uint16_t vtable; + ImageData *img; - RefObject(uint16_t vt) - { - refcnt = 2; - vtable = vt; -#ifdef DEBUG_MEMLEAKS - allptrs.insert(this); -#endif - } + RefMImage(ImageData *d); + void makeWritable(); + static void destroy(RefMImage *map); + static void print(RefMImage *map); +}; - inline VTable *getVTable() { - return (VTable*)(vtable << vtableShift); - } +#define MSTR(s) ManagedString((s)->data, (s)->length) - void destroy(); - void print(); +static inline String PSTR(ManagedString s) { + return mkString(s.toCharArray(), s.length()); +} - // Call to disable pointer tracking on the current instance (in destructor or some other hack) - inline void untrack() { -#ifdef DEBUG_MEMLEAKS - allptrs.erase(this); -#endif - } +typedef uint32_t ImageLiteral_; - // Increment/decrement the ref-count. Decrementing to zero deletes the current object. - inline void ref() - { - check(refcnt > 0, ERR_REF_DELETED); - //printf("INCR "); this->print(); - refcnt += 2; - } +static inline ImageData *imageBytes(ImageLiteral_ lit) { + return (ImageData*)ptrOfLiteral(lit); +} - inline void unref() - { - //printf("DECR "); this->print(); - check(refcnt > 0, ERR_REF_DELETED); - refcnt -= 2; - if (refcnt == 0) { - destroy(); - } - } - }; +typedef RefMImage *Image; - class Segment { - private: - uint32_t* data; - uint16_t length; - uint16_t size; +extern MicroBit uBit; +extern MicroBitEvent lastEvent; - static const uint16_t MaxSize = 0xFFFF; - static const uint32_t DefaultValue = 0x0; +MicroBitPin *getPin(int id); - static uint16_t growthFactor(uint16_t size); - void growByMin(uint16_t minSize); - void growBy(uint16_t newSize); - void ensure(uint16_t newSize); +static inline int min_(int a, int b) { + if (a < b) return a; + else return b; +} - public: - Segment() : data (nullptr), length(0), size(0) {}; +static inline int max_(int a, int b) { + if (a > b) return a; + else return b; +} - uint32_t get(uint32_t i); - void set(uint32_t i, uint32_t value); - - uint32_t getLength() { return length;}; - void setLength(uint32_t newLength); - - void push(uint32_t value); - uint32_t pop(); - - uint32_t remove(uint32_t i); - void insert(uint32_t i, uint32_t value); - - bool isValidIndex(uint32_t i); - - void destroy(); - - void print(); - }; - - // A ref-counted collection of either primitive or ref-counted objects (String, Image, - // user-defined record, another collection) - class RefCollection - : public RefObject - { - private: - Segment head; - public: - // 1 - collection of refs (need decr) - // 2 - collection of strings (in fact we always have 3, never 2 alone) - inline uint32_t getFlags() { return getVTable()->userdata; } - inline bool isRef() { return getFlags() & 1; } - inline bool isString() { return getFlags() & 2; } - - RefCollection(uint16_t f); - - void destroy(); - void print(); - - uint32_t length() { return head.getLength();} - void setLength(uint32_t newLength) { head.setLength(newLength); } - - void push(uint32_t x); - uint32_t pop(); - uint32_t getAt(int i); - void setAt(int i, uint32_t x); - //removes the element at index i and shifts the other elements left - uint32_t removeAt(int i); - //inserts the element at index i and moves the other elements right. - void insertAt(int i, uint32_t x); - - int indexOf(uint32_t x, int start); - int removeElement(uint32_t x); - }; - - struct MapEntry { - uint32_t key; - uint32_t val; - }; - - class RefMap - : public RefObject - { - public: - std::vector data; - - RefMap(); - void destroy(); - void print(); - int findIdx(uint32_t key); - }; - - // A ref-counted, user-defined JS object. - class RefRecord - : public RefObject - { - public: - // The object is allocated, so that there is space at the end for the fields. - uint32_t fields[]; - - RefRecord(uint16_t v) : RefObject(v) {} - - uint32_t ld(int idx); - uint32_t ldref(int idx); - void st(int idx, uint32_t v); - void stref(int idx, uint32_t v); - }; - - // these are needed when constructing vtables for user-defined classes - void RefRecord_destroy(RefRecord *r); - void RefRecord_print(RefRecord *r); - - class RefAction; - typedef uint32_t (*ActionCB)(uint32_t *captured, uint32_t arg0, uint32_t arg1, uint32_t arg2); - - // Ref-counted function pointer. It's currently always a ()=>void procedure pointer. - class RefAction - : public RefObject - { - public: - // This is the same as for RefRecord. - uint8_t len; - uint8_t reflen; - ActionCB func; // The function pointer - // fields[] contain captured locals - uint32_t fields[]; - - void destroy(); - void print(); - - RefAction(); - - inline void stCore(int idx, uint32_t v) - { - //printf("ST [%d] = %d ", idx, v); this->print(); - intcheck(0 <= idx && idx < len, ERR_OUT_OF_BOUNDS, 10); - intcheck(fields[idx] == 0, ERR_OUT_OF_BOUNDS, 11); // only one assignment permitted - fields[idx] = v; - } - - inline uint32_t runCore(int arg0, int arg1, int arg2) // internal; use runAction*() functions - { - this->ref(); - uint32_t r = this->func(&this->fields[0], arg0, arg1, arg2); - this->unref(); - return r; - } - }; - - // These two are used to represent locals written from inside inline functions - class RefLocal - : public RefObject - { - public: - uint32_t v; - void destroy(); - void print(); - RefLocal(); - }; - - class RefRefLocal - : public RefObject - { - public: - uint32_t v; - void destroy(); - void print(); - RefRefLocal(); - }; } using namespace pxt; -MicroBitPin *getPin(int id); -typedef ImageData* Image; -typedef BufferData* Buffer; -// The ARM Thumb generator in the JavaScript code is parsing -// the hex file and looks for the magic numbers as present here. -// -// Then it fetches function pointer addresses from there. - -#define PXT_SHIMS_BEGIN \ -namespace pxt { \ - const uint32_t functionsAndBytecode[] __attribute__((aligned(0x20))) = { \ - 0x08010801, 0x42424242, 0x08010801, 0x8de9d83e, - -#define PXT_SHIMS_END }; } - -#pragma GCC diagnostic ignored "-Wpmf-conversions" - -#define PXT_VTABLE_TO_INT(vt) ((uint32_t)(vt) >> vtableShift) -#define PXT_VTABLE_BEGIN(classname, flags, iface) \ -const VTable classname ## _vtable \ - __attribute__((aligned(1 << vtableShift))) \ - = { \ - sizeof(classname), \ - flags, \ - iface, \ - { \ - (void*)&classname::destroy, \ - (void*)&classname::print, - -#define PXT_VTABLE_END } }; - -#define PXT_VTABLE_INIT(classname) \ - RefObject(PXT_VTABLE_TO_INT(&classname ## _vtable)) - -#define PXT_VTABLE_CTOR(classname) \ - PXT_VTABLE_BEGIN(classname, 0, 0) PXT_VTABLE_END \ - classname::classname() : PXT_VTABLE_INIT(classname) +#define DEVICE_EVT_ANY 0 #endif diff --git a/libs/core/pxt.json b/libs/core/pxt.json index 2c275ddf..c6ef8634 100644 --- a/libs/core/pxt.json +++ b/libs/core/pxt.json @@ -4,10 +4,10 @@ "installedVersion": "tsmdvf", "files": [ "README.md", - "ManagedBuffer.cpp", - "ManagedBuffer.h", "pxt.cpp", "pxt.h", + "pxtbase.h", + "pxtcore.h", "dal.d.ts", "enums.d.ts", "shims.d.ts", @@ -15,6 +15,7 @@ "core.cpp", "pxt-helpers.ts", "helpers.ts", + "codal.cpp", "images.cpp", "basic.cpp", "icons.ts", diff --git a/libs/core/pxtbase.h b/libs/core/pxtbase.h new file mode 100644 index 00000000..7e18872c --- /dev/null +++ b/libs/core/pxtbase.h @@ -0,0 +1,701 @@ +#ifndef __PXTBASE_H +#define __PXTBASE_H + +//#define PXT_MEMLEAK_DEBUG 1 + +#pragma GCC diagnostic ignored "-Wunused-parameter" +#pragma GCC diagnostic ignored "-Wformat" +#pragma GCC diagnostic ignored "-Warray-bounds" + +// needed for gcc6; not sure why +#undef min +#undef max + +#define NOLOG(...) \ + do { \ + } while (0) + +#define MEMDBG_ENABLED 0 +#define MEMDBG NOLOG + +#include "pxtconfig.h" + +#define intcheck(...) check(__VA_ARGS__) +//#define intcheck(...) do {} while (0) + +#include +#include +#include + +#include + +#ifdef PXT_MEMLEAK_DEBUG +#include +#endif + +#include "pxtcore.h" + +#ifndef PXT_VTABLE_SHIFT +#define PXT_VTABLE_SHIFT 2 +#endif + +#define CONCAT_1(a, b) a##b +#define CONCAT_0(a, b) CONCAT_1(a, b) +#define STATIC_ASSERT(e) enum { CONCAT_0(_static_assert_, __LINE__) = 1 / ((e) ? 1 : 0) }; + +#ifndef ramint_t +// this type limits size of arrays +#ifdef __linux__ +#define ramint_t uint32_t +#else +#define ramint_t uint16_t +#endif +#endif + +#if 0 +inline void *operator new(size_t, void *p) { + return p; +} +inline void *operator new[](size_t, void *p) { + return p; +} +#endif + +namespace pxt { + +template inline const T &max(const T &a, const T &b) { + if (a < b) + return b; + return a; +} + +template inline const T &min(const T &a, const T &b) { + if (a < b) + return a; + return b; +} + +template inline void swap(T &a, T &b) { + T tmp = a; + a = b; + b = tmp; +} + +// +// Tagged values (assume 4 bytes for now, Cortex-M0) +// +struct TValueStruct {}; +typedef TValueStruct *TValue; + +typedef TValue TNumber; +typedef TValue Action; +typedef TValue ImageLiteral; + +// To be implemented by the target +extern "C" void target_panic(int error_code); +extern "C" void target_reset(); +void sleep_ms(unsigned ms); +void sleep_us(uint64_t us); +void releaseFiber(); +int current_time_ms(); +void initRuntime(); +void sendSerial(const char *data, int len); +int getSerialNumber(); +void registerWithDal(int id, int event, Action a, int flags = 16); // EVENT_LISTENER_DEFAULT_FLAGS +void runInParallel(Action a); +void runForever(Action a); +void waitForEvent(int id, int event); +//% +unsigned afterProgramPage(); +//% +void dumpDmesg(); + +// also defined DMESG macro +// end + +#define TAGGED_SPECIAL(n) (TValue)(void *)((n << 2) | 2) +#define TAG_FALSE TAGGED_SPECIAL(2) +#define TAG_TRUE TAGGED_SPECIAL(16) +#define TAG_UNDEFINED (TValue)0 +#define TAG_NULL TAGGED_SPECIAL(1) +#define TAG_NUMBER(n) (TNumber)(void *)((n << 1) | 1) + +inline bool isTagged(TValue v) { + return ((intptr_t)v & 3) || !v; +} + +inline bool isNumber(TValue v) { + return (intptr_t)v & 1; +} + +inline bool isSpecial(TValue v) { + return (intptr_t)v & 2; +} + +inline bool bothNumbers(TValue a, TValue b) { + return (intptr_t)a & (intptr_t)b & 1; +} + +inline int numValue(TValue n) { + return (intptr_t)n >> 1; +} + +#ifdef PXT_BOX_DEBUG +inline bool canBeTagged(int) { + return false; +} +#else +inline bool canBeTagged(int v) { + return (v << 1) >> 1 == v; +} +#endif + +typedef enum { + ERR_INVALID_BINARY_HEADER = 5, + ERR_OUT_OF_BOUNDS = 8, + ERR_REF_DELETED = 7, + ERR_SIZE = 9, +} PXT_ERROR; + +extern const unsigned functionsAndBytecode[]; +extern TValue *globals; +extern uint16_t *bytecode; +class RefRecord; + +// Utility functions + +//% +TValue runAction3(Action a, TValue arg0, TValue arg1, TValue arg2); +//% +TValue runAction2(Action a, TValue arg0, TValue arg1); +//% +TValue runAction1(Action a, TValue arg0); +//% +TValue runAction0(Action a); +//% +Action mkAction(int reflen, int totallen, int startptr); +// allocate [sz] words and clear them +//% +unsigned *allocate(ramint_t sz); +//% +int templateHash(); +//% +int programHash(); +//% +unsigned programSize(); +//% +int getNumGlobals(); +//% +RefRecord *mkClassInstance(int vtableOffset); +//% +void debugMemLeaks(); +//% +void anyPrint(TValue v); + +int getConfig(int key, int defl = -1); + +//% +int toInt(TNumber v); +//% +unsigned toUInt(TNumber v); +//% +double toDouble(TNumber v); +//% +float toFloat(TNumber v); +//% +TNumber fromDouble(double r); +//% +TNumber fromFloat(float r); + +//% +TNumber fromInt(int v); +//% +TNumber fromUInt(unsigned v); +//% +TValue fromBool(bool v); +//% +bool eq_bool(TValue a, TValue b); +//% +bool eqq_bool(TValue a, TValue b); + +void error(PXT_ERROR code, int subcode = 0); +void exec_binary(unsigned *pc); +void start(); + +struct HandlerBinding { + HandlerBinding *next; + int source; + int value; + Action action; +}; +HandlerBinding *findBinding(int source, int value); +void setBinding(int source, int value, Action act); + +// The standard calling convention is: +// - when a pointer is loaded from a local/global/field etc, and incr()ed +// (in other words, its presence on stack counts as a reference) +// - after a function call, all pointers are popped off the stack and decr()ed +// This does not apply to the RefRecord and st/ld(ref) methods - they unref() +// the RefRecord* this. +//% +TValue incr(TValue e); +//% +void decr(TValue e); + +class RefObject; + +static inline RefObject *incrRC(RefObject *r) { + return (RefObject *)incr((TValue)r); +} +static inline void decrRC(RefObject *r) { + decr((TValue)r); +} + +inline void *ptrOfLiteral(int offset) { + return &bytecode[offset]; +} + +// Checks if object is ref-counted, and has a custom PXT vtable in front +// TODO +inline bool isRefCounted(TValue e) { + return !isTagged(e) && (*((unsigned *)e) & 1) == 1; +} + +inline void check(int cond, PXT_ERROR code, int subcode = 0) { + if (!cond) + error(code, subcode); +} + +inline void oops() { + target_panic(47); +} + +class RefObject; +#ifdef PXT_MEMLEAK_DEBUG +extern std::set allptrs; +#endif + +typedef void (*RefObjectMethod)(RefObject *self); +typedef void *PVoid; +typedef void **PPVoid; + +typedef void *Object_; + +const PPVoid RefMapMarker = (PPVoid)(void *)43; + +struct VTable { + uint16_t numbytes; // in the entire object, including the vtable pointer + uint16_t userdata; + PVoid *ifaceTable; + PVoid methods[2]; // we only use up to two methods here; pxt will generate more + // refmask sits at &methods[nummethods] +}; + +const int vtableShift = PXT_VTABLE_SHIFT; + +// A base abstract class for ref-counted objects. +class RefObject { + public: + uint16_t refcnt; + uint16_t vtable; + + RefObject(uint16_t vt) { + refcnt = 3; + vtable = vt; +#ifdef PXT_MEMLEAK_DEBUG + allptrs.insert((TValue)this); +#endif + } + + void destroyVT(); + void printVT(); + + // Call to disable pointer tracking on the current instance (in destructor or some other hack) + inline void untrack() { +#ifdef PXT_MEMLEAK_DEBUG + allptrs.erase((TValue)this); +#endif + } + + inline bool isReadOnly() { return refcnt == 0xffff; } + + // Increment/decrement the ref-count. Decrementing to zero deletes the current object. + inline void ref() { + if (isReadOnly()) + return; + check(refcnt > 1, ERR_REF_DELETED); + // DMESG("INCR "); this->print(); + refcnt += 2; + } + + inline void unref() { + if (isReadOnly()) + return; + check(refcnt > 1, ERR_REF_DELETED); + check((refcnt & 1), ERR_REF_DELETED); + // DMESG("DECR "); this->print(); + refcnt -= 2; + if (refcnt == 1) { + untrack(); + destroyVT(); + } + } +}; + +class Segment { + private: + TValue *data; + ramint_t length; + ramint_t size; + + // this just gives max value of ramint_t + static constexpr ramint_t MaxSize = (((1U << (8 * sizeof(ramint_t) - 1)) - 1) << 1) + 1; + static constexpr TValue DefaultValue = TAG_UNDEFINED; + + static ramint_t growthFactor(ramint_t size); + void growByMin(ramint_t minSize); + void growBy(ramint_t newSize); + void ensure(ramint_t newSize); + + public: + Segment() : data(nullptr), length(0), size(0){}; + + TValue get(unsigned i); + void set(unsigned i, TValue value); + void setRef(unsigned i, TValue value); + + unsigned getLength() { return length; }; + void setLength(unsigned newLength); + void resize(unsigned newLength) { setLength(newLength); } + + void push(TValue value); + TValue pop(); + + TValue remove(unsigned i); + void insert(unsigned i, TValue value); + + bool isValidIndex(unsigned i); + + void destroy(); + + void print(); +}; + +// A ref-counted collection of either primitive or ref-counted objects (String, Image, +// user-defined record, another collection) +class RefCollection : public RefObject { + private: + Segment head; + + public: + RefCollection(); + + static void destroy(RefCollection *coll); + static void print(RefCollection *coll); + + unsigned length() { return head.getLength(); } + void setLength(unsigned newLength) { head.setLength(newLength); } + + void push(TValue x); + TValue pop(); + TValue getAt(int i); + void setAt(int i, TValue x); + // removes the element at index i and shifts the other elements left + TValue removeAt(int i); + // inserts the element at index i and moves the other elements right. + void insertAt(int i, TValue x); + + int indexOf(TValue x, int start); + bool removeElement(TValue x); +}; + +class RefMap : public RefObject { + public: + Segment keys; + Segment values; + + RefMap(); + static void destroy(RefMap *map); + static void print(RefMap *map); + int findIdx(unsigned key); +}; + +// A ref-counted, user-defined JS object. +class RefRecord : public RefObject { + public: + // The object is allocated, so that there is space at the end for the fields. + TValue fields[]; + + RefRecord(uint16_t v) : RefObject(v) {} + + TValue ld(int idx); + TValue ldref(int idx); + void st(int idx, TValue v); + void stref(int idx, TValue v); +}; + +//% +VTable *getVTable(RefObject *r); + +// these are needed when constructing vtables for user-defined classes +//% +void RefRecord_destroy(RefRecord *r); +//% +void RefRecord_print(RefRecord *r); + +class RefAction; +typedef TValue (*ActionCB)(TValue *captured, TValue arg0, TValue arg1, TValue arg2); + +// Ref-counted function pointer. +class RefAction : public RefObject { + public: + // This is the same as for RefRecord. + uint8_t len; + uint8_t reflen; + ActionCB func; // The function pointer + // fields[] contain captured locals + TValue fields[]; + + static void destroy(RefAction *act); + static void print(RefAction *act); + + RefAction(); + + inline void stCore(int idx, TValue v) { + // DMESG("ST [%d] = %d ", idx, v); this->print(); + intcheck(0 <= idx && idx < len, ERR_OUT_OF_BOUNDS, 10); + intcheck(fields[idx] == 0, ERR_OUT_OF_BOUNDS, 11); // only one assignment permitted + fields[idx] = v; + } + + inline TValue runCore(TValue arg0, TValue arg1, + TValue arg2) // internal; use runAction*() functions + { + this->ref(); + TValue r = this->func(&this->fields[0], arg0, arg1, arg2); + this->unref(); + return r; + } +}; + +// These two are used to represent locals written from inside inline functions +class RefLocal : public RefObject { + public: + TValue v; + static void destroy(RefLocal *l); + static void print(RefLocal *l); + RefLocal(); +}; + +class RefRefLocal : public RefObject { + public: + TValue v; + static void destroy(RefRefLocal *l); + static void print(RefRefLocal *l); + RefRefLocal(); +}; + +typedef int color; + +// note: this is hardcoded in PXT (hexfile.ts) + +#define PXT_REF_TAG_STRING 1 +#define PXT_REF_TAG_BUFFER 2 +#define PXT_REF_TAG_IMAGE 3 +#define PXT_REF_TAG_NUMBER 32 +#define PXT_REF_TAG_ACTION 33 + +class BoxedNumber : public RefObject { + public: + double num; + BoxedNumber() : RefObject(PXT_REF_TAG_NUMBER) {} +} __attribute__((packed)); + +class BoxedString : public RefObject { + public: + uint16_t length; + char data[0]; + BoxedString() : RefObject(PXT_REF_TAG_STRING) {} +}; + +class BoxedBuffer : public RefObject { + public: + // data needs to be word-aligned, so we use 32 bits for length + int length; + uint8_t data[0]; + BoxedBuffer() : RefObject(PXT_REF_TAG_BUFFER) {} +}; + + +// the first byte of data indicates the format - currently 0xE1 or 0xE4 to 1 or 4 bit bitmaps +// second byte indicates width in pixels +// third byte indicates the height (which should also match the size of the buffer) +// just like ordinary buffers, these can be layed out in flash +class RefImage : public RefObject { + uintptr_t _buffer; + uint8_t _data[0]; + + public: + RefImage(BoxedBuffer *buf); + RefImage(uint32_t sz); + + bool hasBuffer() { return !(_buffer & 1); } + BoxedBuffer *buffer() { return hasBuffer() ? (BoxedBuffer *)_buffer : NULL; } + void setBuffer(BoxedBuffer *b); + bool isDirty() { return (_buffer & 3) == 3; } + void clearDirty() { if (isDirty()) _buffer &= ~2; } + + uint8_t *data() { return hasBuffer() ? buffer()->data : _data; } + int length() { return hasBuffer() ? buffer()->length : (_buffer >> 2); } + int pixLength() { return length() - 4; } + + int height(); + int width(); + int byteHeight(); + int wordHeight(); + int bpp(); + + bool hasPadding() { return (height() & 0x1f) != 0; } + + uint8_t *pix() { return data() + 4; } + uint8_t *pix(int x, int y); + uint8_t fillMask(color c); + bool inRange(int x, int y); + void clamp(int *x, int *y); + void makeWritable(); + + static void destroy(RefImage *t); + static void print(RefImage *t); +}; + +RefImage *mkImage(int w, int h, int bpp); + + +typedef BoxedBuffer *Buffer; +typedef BoxedString *String; +typedef RefImage *Image_; + +// keep in sync with github/pxt/pxtsim/libgeneric.ts +enum class NumberFormat { + Int8LE = 1, + UInt8LE, + Int16LE, + UInt16LE, + Int32LE, + Int8BE, + UInt8BE, + Int16BE, + UInt16BE, + Int32BE, + + UInt32LE, + UInt32BE, + Float32LE, + Float64LE, + Float32BE, + Float64BE, +}; + +// data can be NULL in both cases +String mkString(const char *data, int len = -1); +Buffer mkBuffer(const uint8_t *data, int len); + +TNumber getNumberCore(uint8_t *buf, int size, NumberFormat format); +void setNumberCore(uint8_t *buf, int size, NumberFormat format, TNumber value); + +TNumber mkNaN(); + +void seedRandom(unsigned seed); +// max is inclusive +unsigned getRandom(unsigned max); + +extern const VTable string_vt; +extern const VTable image_vt; +extern const VTable buffer_vt; +extern const VTable number_vt; +extern const VTable RefAction_vtable; + +enum class ValType { + Undefined, + Boolean, + Number, + String, + Object, + Function, +}; + +ValType valType(TValue v); +} // namespace pxt + +#define PXT_DEF_STRING(name, val) \ + static const char name[] __attribute__((aligned(4))) = "\xff\xff\x01\x00" val; + +using namespace pxt; + +namespace pins { +Buffer createBuffer(int size); +} + +namespace String_ { +int compare(String s, String that); +} + +// The ARM Thumb generator in the JavaScript code is parsing +// the hex file and looks for the magic numbers as present here. +// +// Then it fetches function pointer addresses from there. +// +// The vtable pointers are there, so that the ::emptyData for various types +// can be patched with the right vtable. +// +#define PXT_SHIMS_BEGIN \ + namespace pxt { \ + const unsigned functionsAndBytecode[] \ + __attribute__((aligned(0x20))) = {0x08010801, 0x42424242, 0x08010801, 0x8de9d83e, + +#define PXT_SHIMS_END \ + } \ + ; \ + } + +#ifndef X86_64 +#pragma GCC diagnostic ignored "-Wpmf-conversions" +#endif + +#define PXT_VTABLE_TO_INT(vt) ((uintptr_t)(vt) >> vtableShift) +#define PXT_VTABLE_BEGIN(classname, flags, iface) \ + const VTable classname##_vtable __attribute__((aligned(1 << vtableShift))) = { \ + sizeof(classname), flags, iface, {(void *)&classname::destroy, (void *)&classname::print, + +#define PXT_VTABLE_END \ + } \ + } \ + ; + +#define PXT_VTABLE_INIT(classname) RefObject(PXT_VTABLE_TO_INT(&classname##_vtable)) + +#define PXT_VTABLE_CTOR(classname) \ + PXT_VTABLE_BEGIN(classname, 0, 0) \ + PXT_VTABLE_END classname::classname() : PXT_VTABLE_INIT(classname) + +#define PXT_MAIN \ + int main() { \ + pxt::start(); \ + return 0; \ + } + +#define PXT_FNPTR(x) (unsigned)(void *)(x) + +#define PXT_ABI(...) + +#define JOIN(a, b) a##b +/// Defines getClassName() function to fetch the singleton +#define SINGLETON(ClassName) \ + static ClassName *JOIN(inst, ClassName); \ + ClassName *JOIN(get, ClassName)() { \ + if (!JOIN(inst, ClassName)) \ + JOIN(inst, ClassName) = new ClassName(); \ + return JOIN(inst, ClassName); \ + } + +#endif diff --git a/libs/core/pxtcore.h b/libs/core/pxtcore.h new file mode 100644 index 00000000..e6d172fa --- /dev/null +++ b/libs/core/pxtcore.h @@ -0,0 +1,16 @@ +#ifndef __PXTCORE_H +#define __PXTCORE_H + +#include "MicroBit.h" +#include "MicroBitImage.h" +#include "ManagedString.h" +#include "ManagedType.h" + +namespace pxt { +void debuglog(const char *format, ...); +} + + +#define DMESG NOLOG + +#endif diff --git a/libs/core/serial.cpp b/libs/core/serial.cpp index ddb0ed6d..6018b73b 100644 --- a/libs/core/serial.cpp +++ b/libs/core/serial.cpp @@ -68,8 +68,8 @@ namespace serial { //% help=serial/read-until //% blockId=serial_read_until block="serial|read until %delimiter=serial_delimiter_conv" //% weight=19 - StringData* readUntil(StringData* delimiter) { - return uBit.serial.readUntil(ManagedString(delimiter)).leakData(); + String readUntil(String delimiter) { + return PSTR(uBit.serial.readUntil(MSTR(delimiter))); } /** @@ -78,10 +78,10 @@ namespace serial { //% help=serial/read-string //% blockId=serial_read_buffer block="serial|read string" //% weight=18 - StringData* readString() { + String readString() { int n = uBit.serial.getRxBufferSize(); - if (n == 0) return ManagedString("").leakData(); - return ManagedString(uBit.serial.read(n, MicroBitSerialMode::ASYNC)).leakData(); + if (n == 0) return mkString("", 0); + return PSTR(uBit.serial.read(n, MicroBitSerialMode::ASYNC)); } /** @@ -90,8 +90,8 @@ namespace serial { */ //% help=serial/on-data-received //% weight=18 blockId=serial_on_data_received block="serial|on data received %delimiters=serial_delimiter_conv" - void onDataReceived(StringData* delimiters, Action body) { - uBit.serial.eventOn(ManagedString(delimiters)); + void onDataReceived(String delimiters, Action body) { + uBit.serial.eventOn(MSTR(delimiters)); registerWithDal(MICROBIT_ID_SERIAL, MICROBIT_SERIAL_EVT_DELIM_MATCH, body); // lazy initialization of serial buffers uBit.serial.read(MicroBitSerialMode::ASYNC); @@ -103,10 +103,10 @@ namespace serial { //% help=serial/write-string //% weight=87 blockGap=8 //% blockId=serial_writestring block="serial|write string %text" - void writeString(StringData *text) { + void writeString(String text) { if (!text) return; - uBit.serial.send(ManagedString(text)); + uBit.serial.send(MSTR(text)); } /** @@ -117,8 +117,7 @@ namespace serial { void writeBuffer(Buffer buffer) { if (!buffer) return; - ManagedBuffer buf(buffer); - uBit.serial.send(buf.getBytes(), buf.length()); + uBit.serial.send(buffer->data, buffer->length); } /** @@ -131,12 +130,15 @@ namespace serial { if (length <= 0) length = MICROBIT_SERIAL_READ_BUFFER_LENGTH; - ManagedBuffer buf(length); - int read = uBit.serial.read(buf.getBytes(), buf.length()); - if (read != buf.length()) - buf = buf.slice(read); + auto buf = mkBuffer(NULL, length); + int read = uBit.serial.read(buf->data, buf->length); + if (read != length) { + auto prev = buf; + buf = mkBuffer(buf->data, read); + decrRC(prev); + } - return buf.leakData(); + return buf; } /** diff --git a/libs/core/shims.d.ts b/libs/core/shims.d.ts index 5df6892c..1d7c1ed6 100644 --- a/libs/core/shims.d.ts +++ b/libs/core/shims.d.ts @@ -57,8 +57,9 @@ declare interface Image { * @param interval time between each animation step in milli seconds, eg: 200 */ //% help=images/scroll-image weight=79 async blockNamespace=images - //% blockId=device_scroll_image block="scroll image %sprite|with offset %frameoffset|and interval (ms) %delay" blockGap=8 - //% parts="ledmatrix" shim=ImageMethods::scrollImage + //% blockId=device_scroll_image + //% block="scroll image %sprite|with offset %frameoffset|and interval (ms) %delay" + //% blockGap=8 parts="ledmatrix" shim=ImageMethods::scrollImage scrollImage(frameOffset: int32, interval: int32): void; /** @@ -852,13 +853,13 @@ declare interface Buffer { * Write a number in specified format in the buffer. */ //% shim=BufferMethods::setNumber - setNumber(format: NumberFormat, offset: int32, value: int32): void; + setNumber(format: NumberFormat, offset: int32, value: number): void; /** * Read a number in specified format from the buffer. */ //% shim=BufferMethods::getNumber - getNumber(format: NumberFormat, offset: int32): int32; + getNumber(format: NumberFormat, offset: int32): number; /** Returns the length of a Buffer object. */ //% property shim=BufferMethods::length @@ -880,16 +881,24 @@ declare interface Buffer { * Shift buffer left in place, with zero padding. * @param offset number of bytes to shift; use negative value to shift right * @param start start offset in buffer. Default is 0. - * @param length number of elements in buffer. If negative, length is set as the buffer length minus start. eg: -1 + * @param length number of elements in buffer. If negative, length is set as the buffer length minus + * start. eg: -1 */ //% start.defl=0 length.defl=-1 shim=BufferMethods::shift shift(offset: int32, start?: int32, length?: int32): void; + /** + * Convert a buffer to its hexadecimal representation. + */ + //% shim=BufferMethods::toHex + toHex(): string; + /** * Rotate buffer left in place. * @param offset number of bytes to shift; use negative value to shift right * @param start start offset in buffer. Default is 0. - * @param length number of elements in buffer. If negative, length is set as the buffer length minus start. eg: -1 + * @param length number of elements in buffer. If negative, length is set as the buffer length minus + * start. eg: -1 */ //% start.defl=0 length.defl=-1 shim=BufferMethods::rotate rotate(offset: int32, start?: int32, length?: int32): void; @@ -900,5 +909,14 @@ declare interface Buffer { //% shim=BufferMethods::write write(dstOffset: int32, src: Buffer): void; } +declare namespace control { + + /** + * Create a new zero-initialized buffer. + * @param size number of bytes in the buffer + */ + //% shim=control::createBuffer + function createBuffer(size: int32): Buffer; +} // Auto-generated. Do not edit. Really. diff --git a/libs/radio/radio.cpp b/libs/radio/radio.cpp index 66df5ba0..7c0ee985 100644 --- a/libs/radio/radio.cpp +++ b/libs/radio/radio.cpp @@ -42,8 +42,8 @@ namespace radio { uint32_t time; uint32_t serial; int value; - StringData* msg; // may be NULL before first packet - BufferData* bufMsg; // may be NULL before first packet + String msg; // may be NULL before first packet + Buffer bufMsg; // may be NULL before first packet int radioEnable() { int r = uBit.radio.enable(); @@ -78,55 +78,43 @@ namespace radio { memcpy(buf + 5, &sn, 4); } - uint8_t copyStringValue(uint8_t* buf, StringData* data, uint8_t maxLength) { - ManagedString s(data); - uint8_t len = min(maxLength, s.length()); + uint8_t copyStringValue(uint8_t* buf, String data, uint8_t maxLength) { + uint8_t len = min_(maxLength, data->length); // One byte for length of the string buf[0] = len; if (len > 0) { - memcpy(buf + 1, s.toCharArray(), len); + memcpy(buf + 1, data->data, len); } return len + 1; } - StringData* getStringValue(uint8_t* buf, uint8_t maxLength) { + String getStringValue(uint8_t* buf, uint8_t maxLength) { // First byte is the string length - uint8_t len = min(maxLength, buf[0]); - - if (len) { - char name[len + 1]; - memcpy(name, buf + 1, len); - name[len] = 0; - return ManagedString(name).leakData(); - } - return ManagedString().leakData(); + uint8_t len = min_(maxLength, buf[0]); + return mkString((char*)buf + 1, len); } - uint8_t copyBufferValue(uint8_t* buf, BufferData* data, uint8_t maxLength) { - ManagedBuffer s(data); - uint8_t len = min(maxLength, s.length()); + uint8_t copyBufferValue(uint8_t* buf, Buffer data, uint8_t maxLength) { + uint8_t len = min_(maxLength, data->length); // One byte for length of the buffer buf[0] = len; if (len > 0) { - memcpy(buf + 1, s.getBytes(), len); + memcpy(buf + 1, data->data, len); } return len + 1; } - BufferData* getBufferValue(uint8_t* buf, uint8_t maxLength) { + Buffer getBufferValue(uint8_t* buf, uint8_t maxLength) { // First byte is the buffer length - uint8_t len = min(maxLength, buf[0]); - if (len) { - // skip first byte - return ManagedBuffer(buf + 1, len).leakData(); - } - return ManagedBuffer().leakData(); + uint8_t len = min_(maxLength, buf[0]); + // skip first byte + return mkBuffer(buf + 1, len); } - void writePacketAsJSON(uint8_t tp, int v, int s, int t, StringData* m, BufferData* b) { + void writePacketAsJSON(uint8_t tp, int v, int s, int t, String m, Buffer b) { // Convert the packet to JSON and send over serial uBit.serial.send("{"); uBit.serial.send("\"t\":"); @@ -135,14 +123,13 @@ namespace radio { uBit.serial.send(s); if ((tp == PACKET_TYPE_STRING || tp == PACKET_TYPE_VALUE) && NULL != m) { uBit.serial.send(",\"n\":\""); - uBit.serial.send(ManagedString(m)); + uBit.serial.send((uint8_t*)m->data, m->length); uBit.serial.send("\""); } if (tp == PACKET_TYPE_BUFFER && NULL != b) { - ManagedBuffer mb(b); uBit.serial.send(",\"b\":\""); // TODO: proper base64 encoding - uBit.serial.send(mb.getBytes(), mb.length()); + uBit.serial.send(b->data, b->length); uBit.serial.send("\""); } if (tp == PACKET_TYPE_NUMBER || tp == PACKET_TYPE_VALUE) { @@ -165,8 +152,8 @@ namespace radio { int t; int s; int v = 0; - StringData* m = NULL; - BufferData* b = NULL; + String m = NULL; + Buffer b = NULL; memcpy(&tp, buf, 1); memcpy(&t, buf + 1, 4); @@ -186,9 +173,9 @@ namespace radio { } if (NULL == m) - m = ManagedString().leakData(); + m = mkString("", 0); if (NULL == b) - b = ManagedBuffer().leakData(); + b = mkBuffer(NULL, 0); if (!writeToSerial) { // Refresh global packet @@ -197,11 +184,15 @@ namespace radio { time = t; serial = s; value = v; + decrRC(msg); + decrRC(bufMsg); msg = m; bufMsg = b; } else { writePacketAsJSON(tp, v, s, t, m, b); + decrRC(m); + decrRC(b); } } @@ -232,10 +223,9 @@ namespace radio { //% help=radio/send-value //% weight=59 //% blockId=radio_datagram_send_value block="radio send|value %name|= %value" blockGap=8 - void sendValue(StringData* name, int value) { + void sendValue(String name, int value) { if (radioEnable() != MICROBIT_OK) return; - ManagedString n(name); uint8_t buf[32]; memset(buf, 0, 32); @@ -254,7 +244,7 @@ namespace radio { //% help=radio/send-string //% weight=58 //% blockId=radio_datagram_send_string block="radio send string %msg" - void sendString(StringData* msg) { + void sendString(String msg) { if (radioEnable() != MICROBIT_OK || NULL == msg) return; uint8_t buf[32]; @@ -350,8 +340,8 @@ namespace radio { //% weight=44 //% help=radio/receive-string //% deprecated=true - StringData* receiveString() { - if (radioEnable() != MICROBIT_OK) return ManagedString().leakData(); + String receiveString() { + if (radioEnable() != MICROBIT_OK) return mkString("", 0); receivePacket(false); return msg; } @@ -438,8 +428,9 @@ namespace radio { * packet did not contain a string. */ //% help=radio/received-string - StringData* receivedString() { - if (radioEnable() != MICROBIT_OK || NULL == msg) return ManagedString().leakData(); + String receivedString() { + if (radioEnable() != MICROBIT_OK || NULL == msg) return mkString("", 0); + incrRC(msg); return msg; } @@ -450,7 +441,8 @@ namespace radio { */ //% help=radio/received-buffer Buffer receivedBuffer() { - if (radioEnable() != MICROBIT_OK || NULL == bufMsg) return ManagedBuffer().leakData(); + if (radioEnable() != MICROBIT_OK || NULL == bufMsg) return mkBuffer(NULL, 0); + incrRC(bufMsg); return bufMsg; } diff --git a/pxtarget.json b/pxtarget.json index 7e3a27ff..4eaa87ea 100644 --- a/pxtarget.json +++ b/pxtarget.json @@ -28,10 +28,11 @@ "driveName": "MICROBIT", "hexMimeType": "application/x-microbit-hex", "openocdScript": "source [find interface/cmsis-dap.cfg]; source [find target/nrf51.cfg]", - "flashUsableEnd": 245760, - "flashEnd": 245760, + "flashUsableEnd": 242688, + "flashEnd": 242688, "flashCodeAlign": 1024, "floatingPoint": true, + "taggedInts": true, "patches": { "0.0.0 - 1.0.0": [ {