Implement tagged integers in C++ (#813)

* Start on the C++ conversion for tagged ints
This commit is contained in:
Michał Moskal 2018-05-29 23:55:58 +01:00 committed by Sam El-Husseini
parent 01c7c0b588
commit cad13785e2
23 changed files with 3136 additions and 2420 deletions

6
.clang-format Normal file
View File

@ -0,0 +1,6 @@
BasedOnStyle: LLVM
IndentWidth: 4
UseTab: Never
ColumnLimit: 100
AllowShortFunctionsOnASingleLine: Inline
SortIncludes: false

View File

@ -87,15 +87,15 @@ namespace bluetooth {
} }
//% //%
void uartWriteString(StringData *data) { void uartWriteString(String data) {
startUartService(); startUartService();
uart->send(ManagedString(data)); uart->send(MSTR(data));
} }
//% //%
StringData* uartReadUntil(StringData *del) { String uartReadUntil(String del) {
startUartService(); 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 //% help=bluetooth/on-uart-data-received
//% weight=18 blockId=bluetooth_on_data_received block="bluetooth|on data received %delimiters=serial_delimiter_conv" //% 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(); startUartService();
uart->eventOn(ManagedString(delimiters)); uart->eventOn(MSTR(delimiters));
registerWithDal(MICROBIT_ID_BLE_UART, MICROBIT_UART_S_EVT_DELIM_MATCH, body); 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" //% blockId=eddystone_advertise_url block="bluetooth advertise url %url|with power %power|connectable %connectable"
//% parts=bluetooth weight=11 blockGap=8 //% parts=bluetooth weight=11 blockGap=8
//% help=bluetooth/advertise-url blockExternalInputs=1 //% 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)); power = min(MICROBIT_BLE_POWER_LEVELS-1, max(0, power));
int8_t level = CALIBRATED_POWERS[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); uBit.bleManager.setTransmitPower(power);
} }
@ -157,14 +157,12 @@ namespace bluetooth {
*/ */
//% parts=bluetooth weight=12 advanced=true //% parts=bluetooth weight=12 advanced=true
void advertiseUidBuffer(Buffer nsAndInstance, int power, bool connectable) { void advertiseUidBuffer(Buffer nsAndInstance, int power, bool connectable) {
ManagedBuffer buf(nsAndInstance); auto buf = nsAndInstance;
if (buf.length() != 16) return; if (buf->length != 16) return;
power = min(MICROBIT_BLE_POWER_LEVELS-1, max(0, power)); power = min(MICROBIT_BLE_POWER_LEVELS-1, max(0, power));
int8_t level = CALIBRATED_POWERS[power]; int8_t level = CALIBRATED_POWERS[power];
uint8_t uidNs[10]; buf.readBytes(uidNs, 0, 10); uBit.bleManager.advertiseEddystoneUid((const char*)buf->data, (const char*)buf->data + 10, level, connectable);
uint8_t uidInst[6]; buf.readBytes(uidInst, 10, 6);
uBit.bleManager.advertiseEddystoneUid((const char*)uidNs, (const char*)uidInst, level, connectable);
} }
/** /**

View File

@ -1,373 +0,0 @@
#include "MicroBit.h"
#include "ManagedBuffer.h"
#include <limits.h>
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;
}

View File

@ -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

View File

@ -47,16 +47,17 @@
"Buffer.fill": "Fill (a fragment) of the buffer with given value.", "Buffer.fill": "Fill (a fragment) of the buffer with given value.",
"Buffer.getNumber": "Read a number in specified format from the buffer.", "Buffer.getNumber": "Read a number in specified format from the buffer.",
"Buffer.length": "Returns the length of a Buffer object.", "Buffer.length": "Returns the length of a Buffer object.",
"Buffer.rotate": "Rotate buffer left in place.", "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 start. 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|offset": "number of bytes to shift; use negative value to shift right",
"Buffer.rotate|param|start": "start offset in buffer. Default is 0.", "Buffer.rotate|param|start": "start offset in buffer. Default is 0.",
"Buffer.setNumber": "Write a number in specified format in the buffer.", "Buffer.setNumber": "Write a number in specified format in the buffer.",
"Buffer.shift": "Shift buffer left in place, with zero padding.", "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 start. 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|offset": "number of bytes to shift; use negative value to shift right",
"Buffer.shift|param|start": "start offset in buffer. Default is 0.", "Buffer.shift|param|start": "start offset in buffer. Default is 0.",
"Buffer.slice": "Return a copy of a fragment of a buffer.", "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.", "Buffer.write": "Write contents of `src` at `dstOffset` in current buffer.",
"EventCreationMode": "How to create the event.", "EventCreationMode": "How to create the event.",
"EventCreationMode.CreateAndFire": "MicroBitEvent is initialised, and its event handlers are immediately fired (not suitable for use in interrupts!).", "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!\"", "basic.showString|param|text": "the text to scroll on the screen, eg: \"Hello!\"",
"control": "Runtime and event utilities.", "control": "Runtime and event utilities.",
"control.assert": "If the condition is false, display msg on serial console, and panic with code 098.", "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.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.deviceSerialNumber": "Derive a unique, consistent serial number of this device from internal data.",
"control.eventSourceId": "Returns the value of a C++ runtime constant", "control.eventSourceId": "Returns the value of a C++ runtime constant",

View File

@ -38,7 +38,7 @@ namespace basic {
//% blockId=device_show_leds //% blockId=device_show_leds
//% block="show leds" icon="\uf00a" //% block="show leds" icon="\uf00a"
//% parts="ledmatrix" //% 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); uBit.display.print(MicroBitImage(imageBytes(leds)), 0, 0, 0, interval);
} }
@ -53,18 +53,17 @@ namespace basic {
//% async //% async
//% blockId=device_print_message //% blockId=device_print_message
//% parts="ledmatrix" //% parts="ledmatrix"
void showString(StringData *text, int interval = 150) { void showString(String text, int interval = 150) {
if (interval <= 0) if (interval <= 0)
interval = 1; interval = 1;
ManagedString s(text); int l = text ? text->length : 0;
int l = s.length();
if (l == 0) { if (l == 0) {
uBit.display.clear(); uBit.display.clear();
fiber_sleep(interval * 5); fiber_sleep(interval * 5);
} else if (l > 1) { } else if (l > 1) {
uBit.display.scroll(s, interval); uBit.display.scroll(MSTR(text), interval);
} else { } 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 //% help=basic/show-animation imageLiteral=1 async
//% parts="ledmatrix" //% 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); uBit.display.animate(MicroBitImage(imageBytes(leds)), interval, 5, 0, 0);
} }
@ -96,18 +95,11 @@ namespace basic {
*/ */
//% help=basic/plot-leds weight=80 //% help=basic/plot-leds weight=80
//% parts="ledmatrix" //% parts="ledmatrix"
void plotLeds(ImageLiteral leds) { void plotLeds(ImageLiteral_ leds) {
MicroBitImage i(imageBytes(leds)); MicroBitImage i(imageBytes(leds));
uBit.display.print(i, 0, 0, 0, 0); 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. * Repeats the code forever in the background. On each iteration, allows other codes to run.
* @param body code to execute * @param body code to execute
@ -115,10 +107,7 @@ namespace basic {
//% help=basic/forever weight=55 blockGap=8 blockAllowMultiple=1 afterOnStart=true //% help=basic/forever weight=55 blockGap=8 blockAllowMultiple=1 afterOnStart=true
//% blockId=device_forever block="forever" icon="\uf01e" //% blockId=device_forever block="forever" icon="\uf01e"
void forever(Action a) { void forever(Action a) {
if (a != 0) { runForever(a);
incr(a);
create_fiber(forever_stub, (void*)a);
}
} }
/** /**

View File

@ -1,99 +1,67 @@
#include "pxt.h" #include "pxtbase.h"
#include <limits.h>
// keep in sync with github/pxt/pxtsim/libgeneric.ts using namespace std;
enum class NumberFormat {
Int8LE = 1,
UInt8LE,
Int16LE,
UInt16LE,
Int32LE,
Int8BE,
UInt8BE,
Int16BE,
UInt16BE,
Int32BE,
// UInt32,
};
//% indexerGet=BufferMethods::getByte indexerSet=BufferMethods::setByte //% indexerGet=BufferMethods::getByte indexerSet=BufferMethods::setByte
namespace BufferMethods { namespace BufferMethods {
//%
uint8_t *getBytes(Buffer buf) {
return buf->data;
}
//% //%
int getByte(Buffer buf, int off) { int getByte(Buffer buf, int off) {
return max(ManagedBuffer(buf).getByte(off), 0); if (buf && 0 <= off && off < buf->length)
return buf->data[off];
return 0;
} }
//% //%
void setByte(Buffer buf, int off, int v) { void setByte(Buffer buf, int off, int v) {
ManagedBuffer(buf).setByte(off, 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) {
uint8_t *getBytes(Buffer buf) { if (length < 0)
return buf->payload; 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. * Write a number in specified format in the buffer.
*/ */
//% //%
void setNumber(Buffer buf, NumberFormat format, int offset, int value) void setNumber(Buffer buf, NumberFormat format, int offset, TNumber value) {
{ if (offset < 0)
int8_t i8; return;
uint8_t u8; setNumberCore(buf->data + offset, buf->length - offset, format, value);
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. * Read a number in specified format from the buffer.
*/ */
//% //%
int getNumber(Buffer buf, NumberFormat format, int offset) TNumber getNumber(Buffer buf, NumberFormat format, int offset) {
{ if (offset < 0)
int8_t i8; return fromInt(0);
uint8_t u8; return getNumberCore(buf->data + offset, buf->length - offset, format);
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. */ /** Returns the length of a Buffer object. */
@ -106,55 +74,283 @@ namespace BufferMethods {
* Fill (a fragment) of the buffer with given value. * Fill (a fragment) of the buffer with given value.
*/ */
//% //%
void fill(Buffer buf, int value, int offset = 0, int length = -1) void fill(Buffer buf, int value, int offset = 0, int length = -1) {
{ if (offset < 0 || offset > buf->length)
ManagedBuffer(buf).fill(value, offset, 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. * Return a copy of a fragment of a buffer.
*/ */
//% //%
Buffer slice(Buffer buf, int offset = 0, int length = -1) Buffer slice(Buffer buf, int offset = 0, int length = -1) {
{ offset = min((int)buf->length, offset);
return ManagedBuffer(buf).slice(offset, length).leakData(); 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. * Shift buffer left in place, with zero padding.
* @param offset number of bytes to shift; use negative value to shift right * @param offset number of bytes to shift; use negative value to shift right
* @param start start offset in buffer. Default is 0. * @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
*/ */
//% //%
void shift(Buffer buf, int offset, int start = 0, int length = -1) void shift(Buffer buf, int offset, int start = 0, int length = -1) {
{ if (length < 0)
ManagedBuffer(buf).shift(offset, start, length); 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. * Rotate buffer left in place.
* @param offset number of bytes to shift; use negative value to shift right * @param offset number of bytes to shift; use negative value to shift right
* @param start start offset in buffer. Default is 0. * @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
*/ */
//% //%
void rotate(Buffer buf, int offset, int start = 0, int length = -1) void rotate(Buffer buf, int offset, int start = 0, int length = -1) {
{ if (length < 0)
ManagedBuffer(buf).rotate(offset, start, length); length = buf->length - start;
} if (start < 0 || start + length > buf->length || start + length < start || length == 0 ||
offset == 0 || offset == INT_MIN)
return;
// int readBytes(uint8_t *dst, int offset, int length, bool swapBytes = false) const; if (offset < 0)
// int writeBytes(int dstOffset, uint8_t *src, int length, bool swapBytes = false); 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. * Write contents of `src` at `dstOffset` in current buffer.
*/ */
//% //%
void write(Buffer buf, int dstOffset, Buffer src) void write(Buffer buf, int dstOffset, Buffer src) {
{ // srcOff and length not supported, we only do up to 4 args :/
//Not supported, we only do up to 4 args :/ writeBuffer(buf, dstOffset, src, 0, -1);
//void write(Buffer buf, int dstOffset, Buffer src, int srcOffset = 0, int length = -1) }
ManagedBuffer(buf).writeBuffer(dstOffset, ManagedBuffer(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;
} }
} }

234
libs/core/codal.cpp Normal file
View File

@ -0,0 +1,234 @@
#include "pxt.h"
#include <stdarg.h>
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

View File

@ -220,7 +220,7 @@ namespace control {
//% help=control/in-background blockAllowMultiple=1 afterOnStart=true //% help=control/in-background blockAllowMultiple=1 afterOnStart=true
//% blockId="control_in_background" block="run in background" blockGap=8 //% blockId="control_in_background" block="run in background" blockGap=8
void inBackground(Action a) { 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 //% blockId="control_device_name" block="device name" weight=10 blockGap=8
//% advanced=true //% advanced=true
StringData* deviceName() { String deviceName() {
return ManagedString(microbit_friendly_name()).leakData(); return mkString(microbit_friendly_name(), -1);
} }
/** /**

File diff suppressed because it is too large Load Diff

46
libs/core/enums.d.ts vendored
View File

@ -1,4 +1,35 @@
// Auto-generated. Do not edit. // 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 images {
} }
declare namespace basic { declare namespace basic {
@ -536,19 +567,4 @@ declare namespace led {
declare namespace serial { 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. // Auto-generated. Do not edit. Really.

View File

@ -1,5 +1,27 @@
#include "pxt.h" #include "pxt.h"
PXT_VTABLE_BEGIN(RefMImage, 0, 0)
PXT_VTABLE_END
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. * Creation, manipulation and display of LED images.
*/ */
@ -12,8 +34,8 @@ namespace images {
//% weight=75 help=images/create-image //% weight=75 help=images/create-image
//% blockId=device_build_image block="create image" //% blockId=device_build_image block="create image"
//% parts="ledmatrix" //% parts="ledmatrix"
Image createImage(ImageLiteral leds) { Image createImage(ImageLiteral_ leds) {
return MicroBitImage(imageBytes(leds)).clone().leakData(); return new RefMImage(imageBytes(leds));
} }
/** /**
@ -22,10 +44,10 @@ namespace images {
//% weight=74 help=images/create-big-image //% weight=74 help=images/create-big-image
//% blockId=device_build_big_image block="create big image" imageLiteral=2 //% blockId=device_build_big_image block="create big image" imageLiteral=2
//% parts="ledmatrix" //% parts="ledmatrix"
Image createBigImage(ImageLiteral leds) { Image createBigImage(ImageLiteral_ leds) {
return createImage(leds); return createImage(leds);
} }
} } // namespace images
namespace ImageMethods { namespace ImageMethods {
/** /**
@ -34,7 +56,7 @@ namespace ImageMethods {
//% help=images/plot-image //% help=images/plot-image
//% parts="ledmatrix" //% parts="ledmatrix"
void plotImage(Image i, int xOffset = 0) { void plotImage(Image i, int xOffset = 0) {
uBit.display.print(MicroBitImage(i), -xOffset, 0, 0, 0); uBit.display.print(MicroBitImage(i->img), -xOffset, 0, 0, 0);
} }
/** /**
@ -45,7 +67,7 @@ namespace ImageMethods {
//% blockId=device_show_image_offset block="show image %sprite|at offset %offset" blockGap=8 //% blockId=device_show_image_offset block="show image %sprite|at offset %offset" blockGap=8
//% parts="ledmatrix" async //% parts="ledmatrix" async
void showImage(Image sprite, int xOffset, int interval = 400) { void showImage(Image sprite, int xOffset, int interval = 400) {
uBit.display.print(MicroBitImage(sprite), -xOffset, 0, 0, interval); uBit.display.print(MicroBitImage(sprite->img), -xOffset, 0, 0, interval);
} }
/** /**
@ -65,21 +87,22 @@ namespace ImageMethods {
* @param interval time between each animation step in milli seconds, eg: 200 * @param interval time between each animation step in milli seconds, eg: 200
*/ */
//% help=images/scroll-image weight=79 async blockNamespace=images //% 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 //% blockId=device_scroll_image
//% parts="ledmatrix" //% block="scroll image %sprite|with offset %frameoffset|and interval (ms) %delay"
//% blockGap=8 parts="ledmatrix"
void scrollImage(Image id, int frameOffset, int interval) { void scrollImage(Image id, int frameOffset, int interval) {
MicroBitImage i(id); MicroBitImage i(id->img);
uBit.display.animate(i, interval, frameOffset, MICROBIT_DISPLAY_ANIMATE_DEFAULT_POS, 0); uBit.display.animate(i, interval, frameOffset, MICROBIT_DISPLAY_ANIMATE_DEFAULT_POS, 0);
} }
/** /**
* Sets all pixels off. * Sets all pixels off.
*/ */
//% help=images/clear //% help=images/clear
//% parts="ledmatrix" //% parts="ledmatrix"
void clear(Image i) { void clear(Image i) {
MicroBitImage(i).clear(); i->makeWritable();
MicroBitImage(i->img).clear();
} }
/** /**
@ -88,28 +111,28 @@ namespace ImageMethods {
//% //%
//% parts="ledmatrix" //% parts="ledmatrix"
void setPixelBrightness(Image i, int x, int y, int value) { void setPixelBrightness(Image i, int x, int y, int value) {
MicroBitImage(i).setPixelValue(x, y, value); i->makeWritable();
MicroBitImage(i->img).setPixelValue(x, y, value);
} }
/** /**
* Gets the pixel brightness ([0..255]) at a given position * Gets the pixel brightness ([0..255]) at a given position
*/ */
//% //%
//% parts="ledmatrix" //% parts="ledmatrix"
int pixelBrightness(Image i, int x, int y) { int pixelBrightness(Image i, int x, int y) {
int pix = MicroBitImage(i).getPixelValue(x, y); int pix = MicroBitImage(i->img).getPixelValue(x, y);
if (pix < 0) return 0; if (pix < 0)
return 0;
return pix; return pix;
} }
/** /**
* Gets the width in columns * Gets the width in columns
*/ */
//% help=functions/width //% help=functions/width
int width(Image i) { int width(Image i) {
return i->width; return i->img->width;
} }
/** /**
@ -117,7 +140,7 @@ namespace ImageMethods {
*/ */
//% //%
int height(Image i) { int height(Image i) {
return i->height; return i->img->height;
} }
/** /**
@ -143,7 +166,6 @@ namespace ImageMethods {
return pixelBrightness(i, x, y) > 0; return pixelBrightness(i, x, y) > 0;
} }
/** /**
* Shows a particular frame of the image strip. * Shows a particular frame of the image strip.
* @param frame TODO * @param frame TODO
@ -153,4 +175,4 @@ namespace ImageMethods {
void showFrame(Image i, int frame, int interval = 400) { void showFrame(Image i, int frame, int interval = 400) {
showImage(i, frame * 5, interval); showImage(i, frame * 5, interval);
} }
} } // namespace ImageMethods

View File

@ -139,7 +139,10 @@ namespace led {
//% help=led/screenshot //% help=led/screenshot
//% parts="ledmatrix" //% parts="ledmatrix"
Image screenshot() { 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; let Image img;
img = image.createImage(""); img = image.createImage("");

View File

@ -84,6 +84,9 @@ enum class PinEventType {
None = MICROBIT_PIN_EVENT_NONE None = MICROBIT_PIN_EVENT_NONE
}; };
namespace pxt
{
MicroBitPin *getPin(int id) { MicroBitPin *getPin(int id) {
switch (id) { switch (id) {
case MICROBIT_ID_IO_P0: return &uBit.io.P0; case MICROBIT_ID_IO_P0: return &uBit.io.P0;
@ -109,6 +112,7 @@ MicroBitPin *getPin(int id) {
} }
} }
} // pxt
namespace pins { namespace pins {
#define PINOP(op) \ #define PINOP(op) \
@ -359,7 +363,7 @@ namespace pins {
//% //%
Buffer createBuffer(int size) 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 i2cReadBuffer(int address, int size, bool repeat = false)
{ {
Buffer buf = createBuffer(size); 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; return buf;
} }
@ -379,7 +383,7 @@ namespace pins {
//% //%
void i2cWriteBuffer(int address, Buffer buf, bool repeat = false) 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; SPI* spi = NULL;

View File

@ -1,80 +1,81 @@
#include "pxt.h" #include "pxtbase.h"
#include <map>
MicroBit uBit; using namespace std;
namespace pxt { namespace pxt {
int incr(uint32_t e)
{ TValue incr(TValue e) {
if (e) { if (isRefCounted(e)) {
if (hasVTable(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(); ((RefObject *)e)->ref();
else
((RefCounted*)e)->incr();
} }
return e; return e;
} }
void decr(uint32_t e) void decr(TValue e) {
{ if (isRefCounted(e)) {
if (e) { #if MEMDBG_ENABLED
if (hasVTable(e)) if (((RefObject *)e)->refcnt != 0xffff)
MEMDBG("DECR: %p refs=%d", e, ((RefObject *)e)->refcnt);
#endif
((RefObject *)e)->unref(); ((RefObject *)e)->unref();
else
((RefCounted*)e)->decr();
} }
} }
Action mkAction(int reflen, int totallen, int startptr) // TODO
{ Action mkAction(int reflen, int totallen, int startptr) {
check(0 <= reflen && reflen <= totallen, ERR_SIZE, 1); check(0 <= reflen && reflen <= totallen, ERR_SIZE, 1);
check(reflen <= totallen && totallen <= 255, ERR_SIZE, 2); check(reflen <= totallen && totallen <= 255, ERR_SIZE, 2);
check(bytecode[startptr] == 0xffff, ERR_INVALID_BINARY_HEADER, 3); check(bytecode[startptr] == 0xffff, ERR_INVALID_BINARY_HEADER, 3);
check(bytecode[startptr + 1] == 0, ERR_INVALID_BINARY_HEADER, 4); check(bytecode[startptr + 1] == PXT_REF_TAG_ACTION, ERR_INVALID_BINARY_HEADER, 4);
uint32_t tmp = (uint32_t)&bytecode[startptr]; uintptr_t tmp = (uintptr_t)&bytecode[startptr];
if (totallen == 0) { if (totallen == 0) {
return tmp; // no closure needed return (TValue)tmp; // no closure needed
} }
void *ptr = ::operator new(sizeof(RefAction) + totallen * sizeof(uint32_t)); void *ptr = ::operator new(sizeof(RefAction) + totallen * sizeof(unsigned));
RefAction *r = new (ptr) RefAction(); RefAction *r = new (ptr) RefAction();
r->len = totallen; r->len = totallen;
r->reflen = reflen; r->reflen = reflen;
r->func = (ActionCB)((tmp + 4) | 1); r->func = (ActionCB)((tmp + 4) | 1);
memset(r->fields, 0, r->len * sizeof(uint32_t)); memset(r->fields, 0, r->len * sizeof(unsigned));
MEMDBG("mkAction: start=%p => %p", startptr, r);
return (Action)r; return (Action)r;
} }
uint32_t runAction3(Action a, int arg0, int arg1, int arg2) // TODO
{ TValue runAction3(Action a, TValue arg0, TValue arg1, TValue arg2) {
if (hasVTable(a)) auto aa = (RefAction *)a;
return ((RefAction*)a)->runCore(arg0, arg1, arg2); if (aa->vtable == PXT_REF_TAG_ACTION) {
else { check(aa->refcnt == 0xffff, ERR_INVALID_BINARY_HEADER, 4);
check(*(uint16_t*)a == 0xffff, ERR_INVALID_BINARY_HEADER, 4); return ((ActionCB)(((uintptr_t)a + 4) | 1))(NULL, arg0, arg1, arg2);
return ((ActionCB)((a + 4) | 1))(NULL, arg0, arg1, arg2); } else {
check(aa->refcnt != 0xffff, ERR_INVALID_BINARY_HEADER, 4);
return aa->runCore(arg0, arg1, arg2);
} }
} }
uint32_t runAction2(Action a, int arg0, int arg1) TValue runAction2(Action a, TValue arg0, TValue arg1) {
{
return runAction3(a, arg0, arg1, 0); return runAction3(a, arg0, arg1, 0);
} }
uint32_t runAction1(Action a, int arg0) TValue runAction1(Action a, TValue arg0) {
{
return runAction3(a, arg0, 0, 0); return runAction3(a, arg0, 0, 0);
} }
uint32_t runAction0(Action a) TValue runAction0(Action a) {
{
return runAction3(a, 0, 0, 0); return runAction3(a, 0, 0, 0);
} }
RefRecord* mkClassInstance(int vtableOffset) RefRecord *mkClassInstance(int vtableOffset) {
{
VTable *vtable = (VTable *)&bytecode[vtableOffset]; VTable *vtable = (VTable *)&bytecode[vtableOffset];
intcheck(vtable->methods[0] == &RefRecord_destroy, ERR_SIZE, 3); intcheck(vtable->methods[0] == &RefRecord_destroy, ERR_SIZE, 3);
@ -83,142 +84,133 @@ namespace pxt {
void *ptr = ::operator new(vtable->numbytes); void *ptr = ::operator new(vtable->numbytes);
RefRecord *r = new (ptr) RefRecord(PXT_VTABLE_TO_INT(vtable)); RefRecord *r = new (ptr) RefRecord(PXT_VTABLE_TO_INT(vtable));
memset(r->fields, 0, vtable->numbytes - sizeof(RefRecord)); memset(r->fields, 0, vtable->numbytes - sizeof(RefRecord));
MEMDBG("mkClass: vt=%p => %p", vtable, r);
return r; return r;
} }
uint32_t RefRecord::ld(int idx) TValue RefRecord::ld(int idx) {
{
// intcheck((reflen == 255 ? 0 : reflen) <= idx && idx < len, ERR_OUT_OF_BOUNDS, 1); // intcheck((reflen == 255 ? 0 : reflen) <= idx && idx < len, ERR_OUT_OF_BOUNDS, 1);
return fields[idx]; return fields[idx];
} }
uint32_t RefRecord::ldref(int idx) TValue RefRecord::ldref(int idx) {
{ // DMESG("LD %p len=%d reflen=%d idx=%d", this, len, reflen, 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); // intcheck(0 <= idx && idx < reflen, ERR_OUT_OF_BOUNDS, 2);
uint32_t tmp = fields[idx]; TValue tmp = fields[idx];
incr(tmp); incr(tmp);
return tmp; return tmp;
} }
void RefRecord::st(int idx, uint32_t v) void RefRecord::st(int idx, TValue v) {
{
// intcheck((reflen == 255 ? 0 : reflen) <= idx && idx < len, ERR_OUT_OF_BOUNDS, 3); // intcheck((reflen == 255 ? 0 : reflen) <= idx && idx < len, ERR_OUT_OF_BOUNDS, 3);
fields[idx] = v; fields[idx] = v;
} }
void RefRecord::stref(int idx, uint32_t v) void RefRecord::stref(int idx, TValue v) {
{ // DMESG("ST %p len=%d reflen=%d idx=%d", this, len, reflen, idx);
//printf("ST %p len=%d reflen=%d idx=%d\n", this, len, reflen, idx);
// intcheck(0 <= idx && idx < reflen, ERR_OUT_OF_BOUNDS, 4); // intcheck(0 <= idx && idx < reflen, ERR_OUT_OF_BOUNDS, 4);
decr(fields[idx]); decr(fields[idx]);
fields[idx] = v; fields[idx] = v;
} }
void RefObject::destroy() { void RefObject::destroyVT() {
((RefObjectMethod)getVTable()->methods[0])(this); ((RefObjectMethod)getVTable(this)->methods[0])(this);
::operator delete(this);
} }
void RefObject::print() { void RefObject::printVT() {
((RefObjectMethod)getVTable()->methods[1])(this); ((RefObjectMethod)getVTable(this)->methods[1])(this);
} }
void RefRecord_destroy(RefRecord *r) { void RefRecord_destroy(RefRecord *r) {
auto tbl = r->getVTable(); VTable *tbl = getVTable(r);
uint8_t *refmask = (uint8_t *)&tbl->methods[tbl->userdata & 0xff]; uint8_t *refmask = (uint8_t *)&tbl->methods[tbl->userdata & 0xff];
int len = (tbl->numbytes >> 2) - 1; int len = (tbl->numbytes >> 2) - 1;
for (int i = 0; i < len; ++i) { for (int i = 0; i < len; ++i) {
if (refmask[i]) decr(r->fields[i]); if (refmask[i])
decr(r->fields[i]);
r->fields[i] = 0; r->fields[i] = 0;
} }
//RefRecord is allocated using placement new
r->~RefRecord();
::operator delete(r);
} }
void RefRecord_print(RefRecord *r) void RefRecord_print(RefRecord *r) {
{ DMESG("RefRecord %p r=%d size=%d bytes", r, r->refcnt, getVTable(r)->numbytes);
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 #ifdef DEBUG_BUILD
printf("In Segment::get index:%u\n", i); DMESG("In Segment::get index:%d", i);
this->print(); this->print();
#endif #endif
if (i < length) if (i < length) {
{
return data[i]; return data[i];
} }
return Segment::DefaultValue; return Segment::DefaultValue;
} }
void Segment::set(uint32_t i, uint32_t value) void Segment::setRef(unsigned i, TValue value) {
{ decr(get(i));
if (i < size) set(i, value);
{
data[i] = value;
} }
else if (i < Segment::MaxSize)
{ void Segment::set(unsigned i, TValue value) {
if (i < size) {
data[i] = value;
} else if (i < Segment::MaxSize) {
growByMin(i + 1); growByMin(i + 1);
data[i] = value; data[i] = value;
} else {
return;
} }
if (length <= i) if (length <= i) {
{
length = i + 1; length = i + 1;
} }
#ifdef DEBUG_BUILD #ifdef DEBUG_BUILD
printf("In Segment::set\n"); DMESG("In Segment::set");
this->print(); this->print();
#endif #endif
return; return;
} }
uint16_t Segment::growthFactor(uint16_t size) ramint_t Segment::growthFactor(ramint_t size) {
{ if (size == 0) {
if (size == 0)
{
return 4; return 4;
} }
if (size < 64) if (size < 64) {
{
return size * 2; // Double return size * 2; // Double
} }
if (size < 512) if (size < 512) {
{
return size * 5 / 3; // Grow by 1.66 rate return size * 5 / 3; // Grow by 1.66 rate
} }
return size + 256; //Grow by constant rate // Grow by constant rate
if ((unsigned)size + 256 < MaxSize)
return size + 256;
else
return MaxSize;
} }
void Segment::growByMin(uint16_t minSize) void Segment::growByMin(ramint_t minSize) {
{
growBy(max(minSize, growthFactor(size))); growBy(max(minSize, growthFactor(size)));
} }
void Segment::growBy(uint16_t newSize) void Segment::growBy(ramint_t newSize) {
{
#ifdef DEBUG_BUILD #ifdef DEBUG_BUILD
printf("growBy: %d\n", newSize); DMESG("growBy: %d", newSize);
this->print(); this->print();
#endif #endif
if (size < newSize) if (size < newSize) {
{
// this will throw if unable to allocate // this will throw if unable to allocate
uint32_t *tmp = (uint32_t*)(::operator new(newSize * sizeof(uint32_t))); TValue *tmp = (TValue *)(::operator new(newSize * sizeof(TValue)));
// Copy existing data // Copy existing data
if (size) if (size) {
{ memcpy(tmp, data, size * sizeof(TValue));
memcpy(tmp, data, size * sizeof(uint32_t));
} }
// fill the rest with default value // fill the rest with default value
memset(tmp + size, Segment::DefaultValue, (newSize - size) * sizeof(uint32_t)); memset(tmp + size, 0, (newSize - size) * sizeof(TValue));
// free older segment; // free older segment;
::operator delete(data); ::operator delete(data);
@ -227,50 +219,42 @@ namespace pxt {
size = newSize; size = newSize;
#ifdef DEBUG_BUILD #ifdef DEBUG_BUILD
printf("growBy - after reallocation\n"); DMESG("growBy - after reallocation");
this->print(); this->print();
#endif #endif
} }
// else { no shrinking yet; } // else { no shrinking yet; }
return; return;
} }
void Segment::ensure(uint16_t newSize) void Segment::ensure(ramint_t newSize) {
{ if (newSize < size) {
if (newSize < size)
{
return; return;
} }
growByMin(newSize); growByMin(newSize);
} }
void Segment::setLength(uint32_t newLength) void Segment::setLength(unsigned newLength) {
{ if (newLength > size) {
if (newLength > size)
{
ensure(length); ensure(length);
} }
length = newLength; length = newLength;
return; return;
} }
void Segment::push(uint32_t value) void Segment::push(TValue value) {
{
this->set(length, value); this->set(length, value);
} }
uint32_t Segment::pop() TValue Segment::pop() {
{
#ifdef DEBUG_BUILD #ifdef DEBUG_BUILD
printf("In Segment::pop\n"); DMESG("In Segment::pop");
this->print(); this->print();
#endif #endif
if (length > 0) if (length > 0) {
{
--length; --length;
uint32_t value = data[length]; TValue value = data[length];
data[length] = Segment::DefaultValue; data[length] = Segment::DefaultValue;
return value; return value;
} }
@ -279,25 +263,22 @@ namespace pxt {
// this function removes an element at index i and shifts the rest of the elements to // this function removes an element at index i and shifts the rest of the elements to
// left to fill the gap // left to fill the gap
uint32_t Segment::remove(uint32_t i) TValue Segment::remove(unsigned i) {
{
#ifdef DEBUG_BUILD #ifdef DEBUG_BUILD
printf("In Segment::remove index:%u\n", i); DMESG("In Segment::remove index:%d", i);
this->print(); this->print();
#endif #endif
if (i < length) if (i < length) {
{
// value to return // value to return
uint32_t ret = data[i]; TValue ret = data[i];
if (i + 1 < length) if (i + 1 < length) {
{
// Move the rest of the elements to fill in the gap. // Move the rest of the elements to fill in the gap.
memmove(data + i, data + i + 1, (length - i - 1) * sizeof(uint32_t)); memmove(data + i, data + i + 1, (length - i - 1) * sizeof(unsigned));
} }
length--; length--;
data[length] = Segment::DefaultValue; data[length] = Segment::DefaultValue;
#ifdef DEBUG_BUILD #ifdef DEBUG_BUILD
printf("After Segment::remove index:%u\n", i); DMESG("After Segment::remove index:%d", i);
this->print(); this->print();
#endif #endif
return ret; return ret;
@ -306,59 +287,47 @@ namespace pxt {
} }
// this function inserts element value at index i by shifting the rest of the elements right. // 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) void Segment::insert(unsigned i, TValue value) {
{
#ifdef DEBUG_BUILD #ifdef DEBUG_BUILD
printf("In Segment::insert index:%u value:%u\n", i, value); DMESG("In Segment::insert index:%d value:%d", i, value);
this->print(); this->print();
#endif #endif
if (i < length) if (i < length) {
{
ensure(length + 1); ensure(length + 1);
if (i + 1 < length)
{
// Move the rest of the elements to fill in the gap. // Move the rest of the elements to fill in the gap.
memmove(data + i + 1, data + i, (length - i) * sizeof(uint32_t)); memmove(data + i + 1, data + i, (length - i) * sizeof(unsigned));
}
data[i] = value; data[i] = value;
length++; length++;
} } else {
else
{
// This is insert beyond the length, just call set which will adjust the length // This is insert beyond the length, just call set which will adjust the length
set(i, value); set(i, value);
} }
#ifdef DEBUG_BUILD #ifdef DEBUG_BUILD
printf("After Segment::insert index:%u\n", i); DMESG("After Segment::insert index:%d", i);
this->print(); this->print();
#endif #endif
} }
void Segment::print() void Segment::print() {
{ DMESG("Segment: %p, length: %d, size: %d", data, (unsigned)length, (unsigned)size);
printf("Segment: %x, length: %u, size: %u\n", data, (uint32_t)length, (uint32_t)size); for (unsigned i = 0; i < size; i++) {
for(uint32_t i = 0; i < size; i++) DMESG("-> %d", (unsigned)(uintptr_t)data[i]);
{
printf("%d ",(uint32_t)data[i]);
} }
printf("\n");
} }
bool Segment::isValidIndex(uint32_t i) bool Segment::isValidIndex(unsigned i) {
{ if (i > length) {
if (i > length)
{
return false; return false;
} }
return true; return true;
} }
void Segment::destroy() void Segment::destroy() {
{
#ifdef DEBUG_BUILD #ifdef DEBUG_BUILD
printf("In Segment::destroy\n"); DMESG("In Segment::destroy");
this->print(); this->print();
#endif #endif
length = size = 0; length = size = 0;
@ -366,103 +335,54 @@ namespace pxt {
data = nullptr; data = nullptr;
} }
void RefCollection::push(uint32_t x) void RefCollection::push(TValue x) {
{ incr(x);
if (isRef()) incr(x);
head.push(x); head.push(x);
} }
uint32_t RefCollection::pop() TValue RefCollection::pop() {
{ TValue ret = head.pop();
uint32_t ret = head.pop();
if (isRef())
{
incr(ret); incr(ret);
}
return ret; return ret;
} }
uint32_t RefCollection::getAt(int i) TValue RefCollection::getAt(int i) {
{ TValue tmp = head.get(i);
uint32_t tmp = head.get(i);
if (isRef())
{
incr(tmp); incr(tmp);
}
return tmp; return tmp;
} }
uint32_t RefCollection::removeAt(int i) TValue RefCollection::removeAt(int i) {
{
// no decr() - we return the result
return head.remove(i); return head.remove(i);
} }
void RefCollection::insertAt(int i, uint32_t value) void RefCollection::insertAt(int i, TValue value) {
{
head.insert(i, value); head.insert(i, value);
if (isRef())
{
incr(value); incr(value);
} }
}
void RefCollection::setAt(int i, uint32_t value) void RefCollection::setAt(int i, TValue value) {
{
if (isRef())
{
if (head.isValidIndex((uint32_t)i))
{
decr(head.get(i));
}
incr(value); incr(value);
} head.setRef(i, value);
head.set(i, value);
} }
int RefCollection::indexOf(uint32_t x, int start) int RefCollection::indexOf(TValue x, int start) {
{ #ifndef X86_64
if (isString()) unsigned i = start;
{ while (head.isValidIndex(i)) {
StringData *xx = (StringData*)x; if (pxt::eq_bool(head.get(i), 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)
{
return (int)i; return (int)i;
} }
i++; i++;
} }
} #endif
else
{
uint32_t i = start;
while(head.isValidIndex(i))
{
if (head.get(i) == x)
{
return (int)i;
}
i++;
}
}
return -1; return -1;
} }
int RefCollection::removeElement(uint32_t x) bool RefCollection::removeElement(TValue x) {
{
int idx = indexOf(x, 0); int idx = indexOf(x, 0);
if (idx >= 0) { if (idx >= 0) {
uint32_t elt = removeAt(idx); decr(removeAt(idx));
if (isRef()) decr(elt);
return 1; return 1;
} }
return 0; return 0;
@ -471,80 +391,44 @@ namespace pxt {
namespace Coll0 { namespace Coll0 {
PXT_VTABLE_BEGIN(RefCollection, 0, 0) PXT_VTABLE_BEGIN(RefCollection, 0, 0)
PXT_VTABLE_END PXT_VTABLE_END
} } // namespace Coll0
namespace Coll1 {
PXT_VTABLE_BEGIN(RefCollection, 1, 0)
PXT_VTABLE_END
}
namespace Coll3 {
PXT_VTABLE_BEGIN(RefCollection, 3, 0)
PXT_VTABLE_END
}
RefCollection::RefCollection(uint16_t flags) : RefObject(0) { RefCollection::RefCollection() : RefObject(0) {
switch (flags) {
case 0:
vtable = PXT_VTABLE_TO_INT(&Coll0::RefCollection_vtable); 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;
}
} }
void RefCollection::destroy() void RefCollection::destroy(RefCollection *t) {
{ for (unsigned i = 0; i < t->head.getLength(); i++) {
if (this->isRef()) decr(t->head.get(i));
{
for(uint32_t i = 0; i < this->head.getLength(); i++)
{
decr(this->head.get(i));
} }
} t->head.destroy();
this->head.destroy();
delete this;
} }
void RefCollection::print() void RefCollection::print(RefCollection *t) {
{ DMESG("RefCollection %p r=%d size=%d", t, t->refcnt, t->head.getLength());
printf("RefCollection %p r=%d flags=%d size=%d\n", this, refcnt, getFlags(), head.getLength()); t->head.print();
head.print();
} }
PXT_VTABLE_CTOR(RefAction) {} PXT_VTABLE_CTOR(RefAction) {}
// fields[] contain captured locals // fields[] contain captured locals
void RefAction::destroy() void RefAction::destroy(RefAction *t) {
{ for (int i = 0; i < t->reflen; ++i) {
for (int i = 0; i < this->reflen; ++i) { decr(t->fields[i]);
decr(fields[i]); t->fields[i] = 0;
fields[i] = 0;
} }
//RefAction is allocated using placement new
this->~RefAction();
::operator delete(this);
} }
void RefAction::print() void RefAction::print(RefAction *t) {
{ DMESG("RefAction %p r=%d pc=%X size=%d (%d refs)", t, t->refcnt,
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); (const uint8_t *)t->func - (const uint8_t *)bytecode, t->len, t->reflen);
} }
void RefLocal::print() void RefLocal::print(RefLocal *t) {
{ DMESG("RefLocal %p r=%d v=%d", t, t->refcnt, t->v);
printf("RefLocal %p r=%d v=%d\n", this, refcnt, v);
} }
void RefLocal::destroy() void RefLocal::destroy(RefLocal *) {}
{
delete this;
}
PXT_VTABLE_CTOR(RefLocal) { PXT_VTABLE_CTOR(RefLocal) {
v = 0; v = 0;
@ -554,155 +438,90 @@ namespace pxt {
v = 0; v = 0;
} }
void RefRefLocal::print() void RefRefLocal::print(RefRefLocal *t) {
{ DMESG("RefRefLocal %p r=%d v=%p", t, t->refcnt, (void *)t->v);
printf("RefRefLocal %p r=%d v=%p\n", this, refcnt, (void*)v);
} }
void RefRefLocal::destroy() void RefRefLocal::destroy(RefRefLocal *t) {
{ decr(t->v);
decr(v);
delete this;
} }
PXT_VTABLE_BEGIN(RefMap, 0, RefMapMarker) PXT_VTABLE_BEGIN(RefMap, 0, RefMapMarker)
PXT_VTABLE_END PXT_VTABLE_END
RefMap::RefMap() : PXT_VTABLE_INIT(RefMap) {} RefMap::RefMap() : PXT_VTABLE_INIT(RefMap) {}
void RefMap::destroy() { void RefMap::destroy(RefMap *t) {
for (unsigned i = 0; i < data.size(); ++i) { for (unsigned i = 0; i < t->values.getLength(); ++i) {
if (data[i].key & 1) { decr(t->values.get(i));
decr(data[i].val); t->values.set(i, 0);
} }
data[i].val = 0; t->keys.destroy();
} t->values.destroy();
data.resize(0);
delete this;
} }
int RefMap::findIdx(uint32_t key) { int RefMap::findIdx(unsigned key) {
for (unsigned i = 0; i < data.size(); ++i) { for (unsigned i = 0; i < keys.getLength(); ++i) {
if (data[i].key >> 1 == key) if ((uintptr_t)keys.get(i) == key)
return i; return i;
} }
return -1; return -1;
} }
void RefMap::print() void RefMap::print(RefMap *t) {
{ DMESG("RefMap %p r=%d size=%d", t, t->refcnt, t->keys.getLength());
printf("RefMap %p r=%d size=%d\n", this, refcnt, data.size());
} }
#ifdef PXT_MEMLEAK_DEBUG
#ifdef DEBUG_MEMLEAKS std::set<TValue> allptrs;
std::set<RefObject*> allptrs; void debugMemLeaks() {
void debugMemLeaks() DMESG("LIVE POINTERS:");
{ for (std::set<TValue>::iterator itr = allptrs.begin(); itr != allptrs.end(); itr++) {
printf("LIVE POINTERS:\n"); anyPrint(*itr);
for(std::set<RefObject*>::iterator itr = allptrs.begin();itr!=allptrs.end();itr++)
{
(*itr)->print();
} }
printf("\n"); DMESG("LIVE POINTERS END.");
dumpDmesg();
} }
#else #else
void debugMemLeaks() {} void debugMemLeaks() {}
#endif #endif
void error(PXT_ERROR code, int subcode) {
// --------------------------------------------------------------------------- DMESG("Error: %d [%d]", code, subcode);
// An adapter for the API expected by the run-time. target_panic(42);
// ---------------------------------------------------------------------------
map<pair<int, int>, 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; uint16_t *bytecode;
uint32_t *globals; TValue *globals;
int numGlobals;
uint32_t *allocate(uint16_t sz) unsigned *allocate(ramint_t sz) {
{ unsigned *arr = new unsigned[sz];
uint32_t *arr = new uint32_t[sz]; memset(arr, 0, sz * sizeof(unsigned));
memset(arr, 0, sz * 4);
return arr; return arr;
} }
void checkStr(bool cond, const char *msg) void checkStr(bool cond, const char *msg) {
{
if (!cond) { if (!cond) {
while (true) { while (true) {
uBit.display.scroll(msg, 100); // uBit.display.scroll(msg, 100);
uBit.sleep(100); // uBit.sleep(100);
} }
} }
} }
int templateHash() int templateHash() {
{
return ((int *)bytecode)[4]; return ((int *)bytecode)[4];
} }
int programHash() int programHash() {
{
return ((int *)bytecode)[6]; return ((int *)bytecode)[6];
} }
int getNumGlobals() int getNumGlobals() {
{
return bytecode[16]; 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] // XXX re-enable once the calibration code is fixed and [editor/embedded.ts]
// properly prepends a call to [internal_main]. // properly prepends a call to [internal_main].
// ::touch_develop::internal_main(); // ::touch_develop::internal_main();
@ -710,37 +529,39 @@ namespace pxt {
// unique group for radio based on source hash // unique group for radio based on source hash
// ::touch_develop::micro_bit::radioDefaultGroup = programHash(); // ::touch_develop::micro_bit::radioDefaultGroup = programHash();
// repeat error 4 times and restart as needed unsigned ver = *pc++;
microbit_panic_timeout(4);
int32_t ver = *pc++;
checkStr(ver == 0x4209, ":( Bad runtime version"); checkStr(ver == 0x4209, ":( Bad runtime version");
bytecode = *((uint16_t **)pc++); // the actual bytecode is here bytecode = *((uint16_t **)pc++); // the actual bytecode is here
globals = allocate(getNumGlobals()); globals = (TValue *)allocate(getNumGlobals());
// can be any valid address, best in RAM for speed
globals[0] = (TValue)&globals;
// just compare the first word // just compare the first word
checkStr(((uint32_t*)bytecode)[0] == 0x923B8E70 && // TODO
templateHash() == *pc, checkStr(((uint32_t *)bytecode)[0] == 0x923B8E70 && (unsigned)templateHash() == *pc,
":( Failed partial flash"); ":( Failed partial flash");
uint32_t startptr = (uint32_t)bytecode; uintptr_t startptr = (uintptr_t)bytecode;
startptr += 48; // header 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(); pxt::debugMemLeaks();
#endif #endif
return; pxt::releaseFiber();
} }
void start() void start() {
{ exec_binary((unsigned *)functionsAndBytecode);
exec_binary((int32_t*)functionsAndBytecode);
}
} }
#endif
// vim: ts=2 sw=2 expandtab } // namespace pxt

View File

@ -5,375 +5,54 @@
#pragma GCC diagnostic ignored "-Wunused-parameter" #pragma GCC diagnostic ignored "-Wunused-parameter"
#include "MicroBit.h" #include "pxtbase.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 <stdio.h>
#include <string.h>
#include <vector>
#include <stdint.h>
#ifdef DEBUG_MEMLEAKS
#include <set>
#endif
extern MicroBit uBit;
namespace pxt { namespace pxt {
typedef uint32_t Action;
typedef uint32_t ImageLiteral;
class RefMImage : public RefObject {
public:
ImageData *img;
typedef enum { RefMImage(ImageData *d);
ERR_INVALID_BINARY_HEADER = 5, void makeWritable();
ERR_OUT_OF_BOUNDS = 8, static void destroy(RefMImage *map);
ERR_REF_DELETED = 7, static void print(RefMImage *map);
ERR_SIZE = 9, };
} ERROR;
extern const uint32_t functionsAndBytecode[]; #define MSTR(s) ManagedString((s)->data, (s)->length)
extern uint32_t *globals;
extern uint16_t *bytecode;
class RefRecord;
// Utility functions static inline String PSTR(ManagedString s) {
return mkString(s.toCharArray(), s.length());
}
typedef uint32_t ImageLiteral_;
static inline ImageData *imageBytes(ImageLiteral_ lit) {
return (ImageData*)ptrOfLiteral(lit);
}
typedef RefMImage *Image;
extern MicroBit uBit;
extern MicroBitEvent lastEvent; 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: MicroBitPin *getPin(int id);
// - 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) static inline int min_(int a, int b) {
{ if (a < b) return a;
return &bytecode[offset]; else return b;
} }
inline ImageData* imageBytes(int offset) static inline int max_(int a, int b) {
{ if (a > b) return a;
return (ImageData*)(void*)&bytecode[offset]; else return b;
} }
// 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<RefObject*> 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
{
public:
uint16_t refcnt;
uint16_t vtable;
RefObject(uint16_t vt)
{
refcnt = 2;
vtable = vt;
#ifdef DEBUG_MEMLEAKS
allptrs.insert(this);
#endif
}
inline VTable *getVTable() {
return (VTable*)(vtable << vtableShift);
}
void destroy();
void print();
// 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
}
// 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;
}
inline void unref()
{
//printf("DECR "); this->print();
check(refcnt > 0, ERR_REF_DELETED);
refcnt -= 2;
if (refcnt == 0) {
destroy();
}
}
};
class Segment {
private:
uint32_t* data;
uint16_t length;
uint16_t size;
static const uint16_t MaxSize = 0xFFFF;
static const uint32_t DefaultValue = 0x0;
static uint16_t growthFactor(uint16_t size);
void growByMin(uint16_t minSize);
void growBy(uint16_t newSize);
void ensure(uint16_t newSize);
public:
Segment() : data (nullptr), length(0), size(0) {};
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<MapEntry> 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; using namespace pxt;
MicroBitPin *getPin(int id);
typedef ImageData* Image;
typedef BufferData* Buffer;
// The ARM Thumb generator in the JavaScript code is parsing #define DEVICE_EVT_ANY 0
// 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)
#endif #endif

View File

@ -4,10 +4,10 @@
"installedVersion": "tsmdvf", "installedVersion": "tsmdvf",
"files": [ "files": [
"README.md", "README.md",
"ManagedBuffer.cpp",
"ManagedBuffer.h",
"pxt.cpp", "pxt.cpp",
"pxt.h", "pxt.h",
"pxtbase.h",
"pxtcore.h",
"dal.d.ts", "dal.d.ts",
"enums.d.ts", "enums.d.ts",
"shims.d.ts", "shims.d.ts",
@ -15,6 +15,7 @@
"core.cpp", "core.cpp",
"pxt-helpers.ts", "pxt-helpers.ts",
"helpers.ts", "helpers.ts",
"codal.cpp",
"images.cpp", "images.cpp",
"basic.cpp", "basic.cpp",
"icons.ts", "icons.ts",

701
libs/core/pxtbase.h Normal file
View File

@ -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 <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

16
libs/core/pxtcore.h Normal file
View File

@ -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

View File

@ -68,8 +68,8 @@ namespace serial {
//% help=serial/read-until //% help=serial/read-until
//% blockId=serial_read_until block="serial|read until %delimiter=serial_delimiter_conv" //% blockId=serial_read_until block="serial|read until %delimiter=serial_delimiter_conv"
//% weight=19 //% weight=19
StringData* readUntil(StringData* delimiter) { String readUntil(String delimiter) {
return uBit.serial.readUntil(ManagedString(delimiter)).leakData(); return PSTR(uBit.serial.readUntil(MSTR(delimiter)));
} }
/** /**
@ -78,10 +78,10 @@ namespace serial {
//% help=serial/read-string //% help=serial/read-string
//% blockId=serial_read_buffer block="serial|read string" //% blockId=serial_read_buffer block="serial|read string"
//% weight=18 //% weight=18
StringData* readString() { String readString() {
int n = uBit.serial.getRxBufferSize(); int n = uBit.serial.getRxBufferSize();
if (n == 0) return ManagedString("").leakData(); if (n == 0) return mkString("", 0);
return ManagedString(uBit.serial.read(n, MicroBitSerialMode::ASYNC)).leakData(); return PSTR(uBit.serial.read(n, MicroBitSerialMode::ASYNC));
} }
/** /**
@ -90,8 +90,8 @@ namespace serial {
*/ */
//% help=serial/on-data-received //% help=serial/on-data-received
//% weight=18 blockId=serial_on_data_received block="serial|on data received %delimiters=serial_delimiter_conv" //% weight=18 blockId=serial_on_data_received block="serial|on data received %delimiters=serial_delimiter_conv"
void onDataReceived(StringData* delimiters, Action body) { void onDataReceived(String delimiters, Action body) {
uBit.serial.eventOn(ManagedString(delimiters)); uBit.serial.eventOn(MSTR(delimiters));
registerWithDal(MICROBIT_ID_SERIAL, MICROBIT_SERIAL_EVT_DELIM_MATCH, body); registerWithDal(MICROBIT_ID_SERIAL, MICROBIT_SERIAL_EVT_DELIM_MATCH, body);
// lazy initialization of serial buffers // lazy initialization of serial buffers
uBit.serial.read(MicroBitSerialMode::ASYNC); uBit.serial.read(MicroBitSerialMode::ASYNC);
@ -103,10 +103,10 @@ namespace serial {
//% help=serial/write-string //% help=serial/write-string
//% weight=87 blockGap=8 //% weight=87 blockGap=8
//% blockId=serial_writestring block="serial|write string %text" //% blockId=serial_writestring block="serial|write string %text"
void writeString(StringData *text) { void writeString(String text) {
if (!text) return; if (!text) return;
uBit.serial.send(ManagedString(text)); uBit.serial.send(MSTR(text));
} }
/** /**
@ -117,8 +117,7 @@ namespace serial {
void writeBuffer(Buffer buffer) { void writeBuffer(Buffer buffer) {
if (!buffer) return; if (!buffer) return;
ManagedBuffer buf(buffer); uBit.serial.send(buffer->data, buffer->length);
uBit.serial.send(buf.getBytes(), buf.length());
} }
/** /**
@ -131,12 +130,15 @@ namespace serial {
if (length <= 0) if (length <= 0)
length = MICROBIT_SERIAL_READ_BUFFER_LENGTH; length = MICROBIT_SERIAL_READ_BUFFER_LENGTH;
ManagedBuffer buf(length); auto buf = mkBuffer(NULL, length);
int read = uBit.serial.read(buf.getBytes(), buf.length()); int read = uBit.serial.read(buf->data, buf->length);
if (read != buf.length()) if (read != length) {
buf = buf.slice(read); auto prev = buf;
buf = mkBuffer(buf->data, read);
decrRC(prev);
}
return buf.leakData(); return buf;
} }
/** /**

30
libs/core/shims.d.ts vendored
View File

@ -57,8 +57,9 @@ declare interface Image {
* @param interval time between each animation step in milli seconds, eg: 200 * @param interval time between each animation step in milli seconds, eg: 200
*/ */
//% help=images/scroll-image weight=79 async blockNamespace=images //% 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 //% blockId=device_scroll_image
//% parts="ledmatrix" shim=ImageMethods::scrollImage //% block="scroll image %sprite|with offset %frameoffset|and interval (ms) %delay"
//% blockGap=8 parts="ledmatrix" shim=ImageMethods::scrollImage
scrollImage(frameOffset: int32, interval: int32): void; scrollImage(frameOffset: int32, interval: int32): void;
/** /**
@ -852,13 +853,13 @@ declare interface Buffer {
* Write a number in specified format in the buffer. * Write a number in specified format in the buffer.
*/ */
//% shim=BufferMethods::setNumber //% 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. * Read a number in specified format from the buffer.
*/ */
//% shim=BufferMethods::getNumber //% shim=BufferMethods::getNumber
getNumber(format: NumberFormat, offset: int32): int32; getNumber(format: NumberFormat, offset: int32): number;
/** Returns the length of a Buffer object. */ /** Returns the length of a Buffer object. */
//% property shim=BufferMethods::length //% property shim=BufferMethods::length
@ -880,16 +881,24 @@ declare interface Buffer {
* Shift buffer left in place, with zero padding. * Shift buffer left in place, with zero padding.
* @param offset number of bytes to shift; use negative value to shift right * @param offset number of bytes to shift; use negative value to shift right
* @param start start offset in buffer. Default is 0. * @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 //% start.defl=0 length.defl=-1 shim=BufferMethods::shift
shift(offset: int32, start?: int32, length?: int32): void; 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. * Rotate buffer left in place.
* @param offset number of bytes to shift; use negative value to shift right * @param offset number of bytes to shift; use negative value to shift right
* @param start start offset in buffer. Default is 0. * @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 //% start.defl=0 length.defl=-1 shim=BufferMethods::rotate
rotate(offset: int32, start?: int32, length?: int32): void; rotate(offset: int32, start?: int32, length?: int32): void;
@ -900,5 +909,14 @@ declare interface Buffer {
//% shim=BufferMethods::write //% shim=BufferMethods::write
write(dstOffset: int32, src: Buffer): void; 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. // Auto-generated. Do not edit. Really.

View File

@ -42,8 +42,8 @@ namespace radio {
uint32_t time; uint32_t time;
uint32_t serial; uint32_t serial;
int value; int value;
StringData* msg; // may be NULL before first packet String msg; // may be NULL before first packet
BufferData* bufMsg; // may be NULL before first packet Buffer bufMsg; // may be NULL before first packet
int radioEnable() { int radioEnable() {
int r = uBit.radio.enable(); int r = uBit.radio.enable();
@ -78,55 +78,43 @@ namespace radio {
memcpy(buf + 5, &sn, 4); memcpy(buf + 5, &sn, 4);
} }
uint8_t copyStringValue(uint8_t* buf, StringData* data, uint8_t maxLength) { uint8_t copyStringValue(uint8_t* buf, String data, uint8_t maxLength) {
ManagedString s(data); uint8_t len = min_(maxLength, data->length);
uint8_t len = min(maxLength, s.length());
// One byte for length of the string // One byte for length of the string
buf[0] = len; buf[0] = len;
if (len > 0) { if (len > 0) {
memcpy(buf + 1, s.toCharArray(), len); memcpy(buf + 1, data->data, len);
} }
return len + 1; 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 // First byte is the string length
uint8_t len = min(maxLength, buf[0]); uint8_t len = min_(maxLength, buf[0]);
return mkString((char*)buf + 1, len);
if (len) {
char name[len + 1];
memcpy(name, buf + 1, len);
name[len] = 0;
return ManagedString(name).leakData();
}
return ManagedString().leakData();
} }
uint8_t copyBufferValue(uint8_t* buf, BufferData* data, uint8_t maxLength) { uint8_t copyBufferValue(uint8_t* buf, Buffer data, uint8_t maxLength) {
ManagedBuffer s(data); uint8_t len = min_(maxLength, data->length);
uint8_t len = min(maxLength, s.length());
// One byte for length of the buffer // One byte for length of the buffer
buf[0] = len; buf[0] = len;
if (len > 0) { if (len > 0) {
memcpy(buf + 1, s.getBytes(), len); memcpy(buf + 1, data->data, len);
} }
return len + 1; 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 // First byte is the buffer length
uint8_t len = min(maxLength, buf[0]); uint8_t len = min_(maxLength, buf[0]);
if (len) {
// skip first byte // skip first byte
return ManagedBuffer(buf + 1, len).leakData(); return mkBuffer(buf + 1, len);
}
return ManagedBuffer().leakData();
} }
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 // Convert the packet to JSON and send over serial
uBit.serial.send("{"); uBit.serial.send("{");
uBit.serial.send("\"t\":"); uBit.serial.send("\"t\":");
@ -135,14 +123,13 @@ namespace radio {
uBit.serial.send(s); uBit.serial.send(s);
if ((tp == PACKET_TYPE_STRING || tp == PACKET_TYPE_VALUE) && NULL != m) { if ((tp == PACKET_TYPE_STRING || tp == PACKET_TYPE_VALUE) && NULL != m) {
uBit.serial.send(",\"n\":\""); uBit.serial.send(",\"n\":\"");
uBit.serial.send(ManagedString(m)); uBit.serial.send((uint8_t*)m->data, m->length);
uBit.serial.send("\""); uBit.serial.send("\"");
} }
if (tp == PACKET_TYPE_BUFFER && NULL != b) { if (tp == PACKET_TYPE_BUFFER && NULL != b) {
ManagedBuffer mb(b);
uBit.serial.send(",\"b\":\""); uBit.serial.send(",\"b\":\"");
// TODO: proper base64 encoding // TODO: proper base64 encoding
uBit.serial.send(mb.getBytes(), mb.length()); uBit.serial.send(b->data, b->length);
uBit.serial.send("\""); uBit.serial.send("\"");
} }
if (tp == PACKET_TYPE_NUMBER || tp == PACKET_TYPE_VALUE) { if (tp == PACKET_TYPE_NUMBER || tp == PACKET_TYPE_VALUE) {
@ -165,8 +152,8 @@ namespace radio {
int t; int t;
int s; int s;
int v = 0; int v = 0;
StringData* m = NULL; String m = NULL;
BufferData* b = NULL; Buffer b = NULL;
memcpy(&tp, buf, 1); memcpy(&tp, buf, 1);
memcpy(&t, buf + 1, 4); memcpy(&t, buf + 1, 4);
@ -186,9 +173,9 @@ namespace radio {
} }
if (NULL == m) if (NULL == m)
m = ManagedString().leakData(); m = mkString("", 0);
if (NULL == b) if (NULL == b)
b = ManagedBuffer().leakData(); b = mkBuffer(NULL, 0);
if (!writeToSerial) { if (!writeToSerial) {
// Refresh global packet // Refresh global packet
@ -197,11 +184,15 @@ namespace radio {
time = t; time = t;
serial = s; serial = s;
value = v; value = v;
decrRC(msg);
decrRC(bufMsg);
msg = m; msg = m;
bufMsg = b; bufMsg = b;
} }
else { else {
writePacketAsJSON(tp, v, s, t, m, b); writePacketAsJSON(tp, v, s, t, m, b);
decrRC(m);
decrRC(b);
} }
} }
@ -232,10 +223,9 @@ namespace radio {
//% help=radio/send-value //% help=radio/send-value
//% weight=59 //% weight=59
//% blockId=radio_datagram_send_value block="radio send|value %name|= %value" blockGap=8 //% 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; if (radioEnable() != MICROBIT_OK) return;
ManagedString n(name);
uint8_t buf[32]; uint8_t buf[32];
memset(buf, 0, 32); memset(buf, 0, 32);
@ -254,7 +244,7 @@ namespace radio {
//% help=radio/send-string //% help=radio/send-string
//% weight=58 //% weight=58
//% blockId=radio_datagram_send_string block="radio send string %msg" //% 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; if (radioEnable() != MICROBIT_OK || NULL == msg) return;
uint8_t buf[32]; uint8_t buf[32];
@ -350,8 +340,8 @@ namespace radio {
//% weight=44 //% weight=44
//% help=radio/receive-string //% help=radio/receive-string
//% deprecated=true //% deprecated=true
StringData* receiveString() { String receiveString() {
if (radioEnable() != MICROBIT_OK) return ManagedString().leakData(); if (radioEnable() != MICROBIT_OK) return mkString("", 0);
receivePacket(false); receivePacket(false);
return msg; return msg;
} }
@ -438,8 +428,9 @@ namespace radio {
* packet did not contain a string. * packet did not contain a string.
*/ */
//% help=radio/received-string //% help=radio/received-string
StringData* receivedString() { String receivedString() {
if (radioEnable() != MICROBIT_OK || NULL == msg) return ManagedString().leakData(); if (radioEnable() != MICROBIT_OK || NULL == msg) return mkString("", 0);
incrRC(msg);
return msg; return msg;
} }
@ -450,7 +441,8 @@ namespace radio {
*/ */
//% help=radio/received-buffer //% help=radio/received-buffer
Buffer receivedBuffer() { 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; return bufMsg;
} }

View File

@ -28,10 +28,11 @@
"driveName": "MICROBIT", "driveName": "MICROBIT",
"hexMimeType": "application/x-microbit-hex", "hexMimeType": "application/x-microbit-hex",
"openocdScript": "source [find interface/cmsis-dap.cfg]; source [find target/nrf51.cfg]", "openocdScript": "source [find interface/cmsis-dap.cfg]; source [find target/nrf51.cfg]",
"flashUsableEnd": 245760, "flashUsableEnd": 242688,
"flashEnd": 245760, "flashEnd": 242688,
"flashCodeAlign": 1024, "flashCodeAlign": 1024,
"floatingPoint": true, "floatingPoint": true,
"taggedInts": true,
"patches": { "patches": {
"0.0.0 - 1.0.0": [ "0.0.0 - 1.0.0": [
{ {