Pull common files from pxt-common-packages (#1165)
This commit is contained in:
		@@ -1,356 +0,0 @@
 | 
			
		||||
#include "pxtbase.h"
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
 | 
			
		||||
using namespace std;
 | 
			
		||||
 | 
			
		||||
//% indexerGet=BufferMethods::getByte indexerSet=BufferMethods::setByte
 | 
			
		||||
namespace BufferMethods {
 | 
			
		||||
//%
 | 
			
		||||
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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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 *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;
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1137
									
								
								libs/core/core.cpp
									
									
									
									
									
								
							
							
						
						
									
										1137
									
								
								libs/core/core.cpp
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										13
									
								
								libs/core/platform.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								libs/core/platform.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
// helpful define to handle C++ differences in package
 | 
			
		||||
#define PXT_MICROBIT_TAGGED_INT 1
 | 
			
		||||
 | 
			
		||||
// cross version compatible way of access data field
 | 
			
		||||
#ifndef PXT_BUFFER_DATA
 | 
			
		||||
#define PXT_BUFFER_DATA(buffer) buffer->data
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef PXT_CREATE_BUFFER
 | 
			
		||||
#define PXT_CREATE_BUFFER(data, len) pxt::mkBuffer(data, len)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define PXT_POWI 1
 | 
			
		||||
@@ -1,567 +0,0 @@
 | 
			
		||||
#include "pxtbase.h"
 | 
			
		||||
 | 
			
		||||
using namespace std;
 | 
			
		||||
 | 
			
		||||
namespace pxt {
 | 
			
		||||
 | 
			
		||||
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 *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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TValue runAction2(Action a, TValue arg0, TValue arg1) {
 | 
			
		||||
    return runAction3(a, arg0, arg1, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TValue runAction1(Action a, TValue arg0) {
 | 
			
		||||
    return runAction3(a, arg0, 0, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TValue 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));
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RefRecord_print(RefRecord *r) {
 | 
			
		||||
    DMESG("RefRecord %p r=%d size=%d bytes", r, r->refcnt, getVTable(r)->numbytes);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TValue Segment::get(unsigned i) {
 | 
			
		||||
#ifdef DEBUG_BUILD
 | 
			
		||||
    DMESG("In Segment::get index:%d", i);
 | 
			
		||||
    this->print();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#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
 | 
			
		||||
        DMESG("growBy - after reallocation");
 | 
			
		||||
        this->print();
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
    // else { no shrinking yet; }
 | 
			
		||||
    return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Segment::ensure(ramint_t newSize) {
 | 
			
		||||
    if (newSize < size) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    growByMin(newSize);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Segment::setLength(unsigned newLength) {
 | 
			
		||||
    if (newLength > size) {
 | 
			
		||||
        ensure(length);
 | 
			
		||||
    }
 | 
			
		||||
    length = newLength;
 | 
			
		||||
    return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Segment::push(TValue value) {
 | 
			
		||||
    this->set(length, value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TValue Segment::pop() {
 | 
			
		||||
#ifdef DEBUG_BUILD
 | 
			
		||||
    DMESG("In Segment::pop");
 | 
			
		||||
    this->print();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    if (length > 0) {
 | 
			
		||||
        --length;
 | 
			
		||||
        TValue value = data[length];
 | 
			
		||||
        data[length] = Segment::DefaultValue;
 | 
			
		||||
        return value;
 | 
			
		||||
    }
 | 
			
		||||
    return Segment::DefaultValue;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
    DMESG("In Segment::remove index:%d", i);
 | 
			
		||||
    this->print();
 | 
			
		||||
#endif
 | 
			
		||||
    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
 | 
			
		||||
        DMESG("After Segment::remove index:%d", i);
 | 
			
		||||
        this->print();
 | 
			
		||||
#endif
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
    return Segment::DefaultValue;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
    DMESG("In Segment::insert index:%d value:%d", i, value);
 | 
			
		||||
    this->print();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    if (i < length) {
 | 
			
		||||
        ensure(length + 1);
 | 
			
		||||
        
 | 
			
		||||
        // 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
 | 
			
		||||
        set(i, value);
 | 
			
		||||
    }
 | 
			
		||||
#ifdef DEBUG_BUILD
 | 
			
		||||
    DMESG("After Segment::insert index:%d", i);
 | 
			
		||||
    this->print();
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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(unsigned i) {
 | 
			
		||||
    if (i > length) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Segment::destroy() {
 | 
			
		||||
#ifdef DEBUG_BUILD
 | 
			
		||||
    DMESG("In Segment::destroy");
 | 
			
		||||
    this->print();
 | 
			
		||||
#endif
 | 
			
		||||
    length = size = 0;
 | 
			
		||||
    ::operator delete(data);
 | 
			
		||||
    data = nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RefCollection::push(TValue x) {
 | 
			
		||||
    incr(x);
 | 
			
		||||
    head.push(x);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TValue RefCollection::pop() {
 | 
			
		||||
    TValue ret = head.pop();
 | 
			
		||||
    incr(ret);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TValue RefCollection::getAt(int i) {
 | 
			
		||||
    TValue tmp = head.get(i);
 | 
			
		||||
    incr(tmp);
 | 
			
		||||
    return tmp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TValue RefCollection::removeAt(int i) {
 | 
			
		||||
    return head.remove(i);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RefCollection::insertAt(int i, TValue value) {
 | 
			
		||||
    head.insert(i, value);
 | 
			
		||||
    incr(value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RefCollection::setAt(int i, TValue value) {
 | 
			
		||||
    incr(value);
 | 
			
		||||
    head.setRef(i, value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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++;
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool RefCollection::removeElement(TValue x) {
 | 
			
		||||
    int idx = indexOf(x, 0);
 | 
			
		||||
    if (idx >= 0) {
 | 
			
		||||
        decr(removeAt(idx));
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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));
 | 
			
		||||
    }
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 RefMap::print(RefMap *t) {
 | 
			
		||||
    DMESG("RefMap %p r=%d size=%d", t, t->refcnt, t->keys.getLength());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef PXT_MEMLEAK_DEBUG
 | 
			
		||||
std::set<TValue> allptrs;
 | 
			
		||||
void debugMemLeaks() {
 | 
			
		||||
    DMESG("LIVE POINTERS:");
 | 
			
		||||
    for (std::set<TValue>::iterator itr = allptrs.begin(); itr != allptrs.end(); itr++) {
 | 
			
		||||
        anyPrint(*itr);
 | 
			
		||||
    }
 | 
			
		||||
    DMESG("LIVE POINTERS END.");
 | 
			
		||||
    dumpDmesg();
 | 
			
		||||
}
 | 
			
		||||
#else
 | 
			
		||||
void debugMemLeaks() {}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void error(PXT_ERROR code, int subcode) {
 | 
			
		||||
    DMESG("Error: %d [%d]", code, subcode);
 | 
			
		||||
    target_panic(42);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint16_t *bytecode;
 | 
			
		||||
TValue *globals;
 | 
			
		||||
 | 
			
		||||
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) {
 | 
			
		||||
    if (!cond) {
 | 
			
		||||
        while (true) {
 | 
			
		||||
            // uBit.display.scroll(msg, 100);
 | 
			
		||||
            // uBit.sleep(100);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int templateHash() {
 | 
			
		||||
    return ((int *)bytecode)[4];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int programHash() {
 | 
			
		||||
    return ((int *)bytecode)[6];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int getNumGlobals() {
 | 
			
		||||
    return bytecode[16];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#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();
 | 
			
		||||
 | 
			
		||||
    // unique group for radio based on source hash
 | 
			
		||||
    // ::touch_develop::micro_bit::radioDefaultGroup = programHash();
 | 
			
		||||
 | 
			
		||||
    unsigned ver = *pc++;
 | 
			
		||||
    checkStr(ver == 0x4209, ":( Bad runtime version");
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
    // TODO
 | 
			
		||||
    checkStr(((uint32_t *)bytecode)[0] == 0x923B8E70 && (unsigned)templateHash() == *pc,
 | 
			
		||||
             ":( Failed partial flash");
 | 
			
		||||
 | 
			
		||||
    uintptr_t startptr = (uintptr_t)bytecode;
 | 
			
		||||
 | 
			
		||||
    startptr += 48; // header
 | 
			
		||||
    startptr |= 1;  // Thumb state
 | 
			
		||||
 | 
			
		||||
    initRuntime();
 | 
			
		||||
 | 
			
		||||
    ((unsigned (*)())startptr)();
 | 
			
		||||
 | 
			
		||||
#ifdef PXT_MEMLEAK_DEBUG
 | 
			
		||||
    pxt::debugMemLeaks();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    pxt::releaseFiber();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void start() {
 | 
			
		||||
    exec_binary((unsigned *)functionsAndBytecode);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
} // namespace pxt
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
{
 | 
			
		||||
    "name": "core",
 | 
			
		||||
    "description": "The microbit core library",
 | 
			
		||||
    "installedVersion": "tsmdvf",
 | 
			
		||||
    "additionalFilePath": "../../node_modules/pxt-common-packages/libs/base",
 | 
			
		||||
    "files": [
 | 
			
		||||
        "README.md",
 | 
			
		||||
        "pxt.cpp",
 | 
			
		||||
@@ -105,4 +105,4 @@
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,713 +0,0 @@
 | 
			
		||||
#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"
 | 
			
		||||
 | 
			
		||||
// helpful define to handle C++ differences in package
 | 
			
		||||
#define PXT_MICROBIT_TAGGED_INT 1
 | 
			
		||||
 | 
			
		||||
// cross version compatible way of access data field
 | 
			
		||||
#ifndef PXT_BUFFER_DATA
 | 
			
		||||
#define PXT_BUFFER_DATA(buffer) buffer->data
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef PXT_CREATE_BUFFER
 | 
			
		||||
#define PXT_CREATE_BUFFER(data, len) pxt::mkBuffer(data, len)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// 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 <string.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <math.h>
 | 
			
		||||
 | 
			
		||||
#include <new>
 | 
			
		||||
 | 
			
		||||
#ifdef PXT_MEMLEAK_DEBUG
 | 
			
		||||
#include <set>
 | 
			
		||||
#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 <typename T> inline const T &max(const T &a, const T &b) {
 | 
			
		||||
    if (a < b)
 | 
			
		||||
        return b;
 | 
			
		||||
    return a;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T> inline const T &min(const T &a, const T &b) {
 | 
			
		||||
    if (a < b)
 | 
			
		||||
        return a;
 | 
			
		||||
    return b;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T> 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<TValue> 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
 | 
			
		||||
		Reference in New Issue
	
	Block a user