Implement tagged integers in C++ (#813)
* Start on the C++ conversion for tagged ints
This commit is contained in:
parent
01c7c0b588
commit
cad13785e2
6
.clang-format
Normal file
6
.clang-format
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
BasedOnStyle: LLVM
|
||||||
|
IndentWidth: 4
|
||||||
|
UseTab: Never
|
||||||
|
ColumnLimit: 100
|
||||||
|
AllowShortFunctionsOnASingleLine: Inline
|
||||||
|
SortIncludes: false
|
@ -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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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;
|
|
||||||
}
|
|
@ -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
|
|
||||||
|
|
@ -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",
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,160 +1,356 @@
|
|||||||
#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 {
|
||||||
//%
|
//%
|
||||||
int getByte(Buffer buf, int off) {
|
uint8_t *getBytes(Buffer buf) {
|
||||||
return max(ManagedBuffer(buf).getByte(off), 0);
|
return buf->data;
|
||||||
}
|
}
|
||||||
|
|
||||||
//%
|
//%
|
||||||
void setByte(Buffer buf, int off, int v) {
|
int getByte(Buffer buf, int off) {
|
||||||
ManagedBuffer(buf).setByte(off, v);
|
if (buf && 0 <= off && off < buf->length)
|
||||||
}
|
return buf->data[off];
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
//%
|
//%
|
||||||
uint8_t *getBytes(Buffer buf) {
|
void setByte(Buffer buf, int off, int v) {
|
||||||
return buf->payload;
|
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) {
|
||||||
* Write a number in specified format in the buffer.
|
if (length < 0)
|
||||||
*/
|
length = src->length;
|
||||||
//%
|
|
||||||
void setNumber(Buffer buf, NumberFormat format, int offset, int value)
|
|
||||||
{
|
|
||||||
int8_t i8;
|
|
||||||
uint8_t u8;
|
|
||||||
int16_t i16;
|
|
||||||
uint16_t u16;
|
|
||||||
int32_t i32;
|
|
||||||
|
|
||||||
ManagedBuffer b(buf);
|
if (srcOffset < 0 || dstOffset < 0 || dstOffset > buf->length)
|
||||||
|
return -1;
|
||||||
|
|
||||||
// Assume little endian
|
length = min(src->length - srcOffset, buf->length - dstOffset);
|
||||||
#define WRITEBYTES(isz, swap) isz = value; b.writeBytes(offset, (uint8_t*)&isz, sizeof(isz), swap); break
|
|
||||||
|
|
||||||
switch (format) {
|
if (length < 0)
|
||||||
case NumberFormat::Int8LE: WRITEBYTES(i8, false);
|
return -1;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
if (buf == src) {
|
||||||
* Read a number in specified format from the buffer.
|
memmove(buf->data + dstOffset, src->data + srcOffset, length);
|
||||||
*/
|
} else {
|
||||||
//%
|
memcpy(buf->data + dstOffset, src->data + srcOffset, length);
|
||||||
int getNumber(Buffer buf, NumberFormat format, int offset)
|
|
||||||
{
|
|
||||||
int8_t i8;
|
|
||||||
uint8_t u8;
|
|
||||||
int16_t i16;
|
|
||||||
uint16_t u16;
|
|
||||||
int32_t i32;
|
|
||||||
|
|
||||||
ManagedBuffer b(buf);
|
|
||||||
|
|
||||||
// Assume little endian
|
|
||||||
#define READBYTES(isz, swap) b.readBytes((uint8_t*)&isz, offset, sizeof(isz), swap); return isz
|
|
||||||
|
|
||||||
switch (format) {
|
|
||||||
case NumberFormat::Int8LE: READBYTES(i8, false);
|
|
||||||
case NumberFormat::UInt8LE: READBYTES(u8, false);
|
|
||||||
case NumberFormat::Int16LE: READBYTES(i16, false);
|
|
||||||
case NumberFormat::UInt16LE: READBYTES(u16, false);
|
|
||||||
case NumberFormat::Int32LE: READBYTES(i32, false);
|
|
||||||
case NumberFormat::Int8BE: READBYTES(i8, true);
|
|
||||||
case NumberFormat::UInt8BE: READBYTES(u8, true);
|
|
||||||
case NumberFormat::Int16BE: READBYTES(i16, true);
|
|
||||||
case NumberFormat::UInt16BE: READBYTES(u16, true);
|
|
||||||
case NumberFormat::Int32BE: READBYTES(i32, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the length of a Buffer object. */
|
/**
|
||||||
//% property
|
* Write a number in specified format in the buffer.
|
||||||
int length(Buffer s) {
|
*/
|
||||||
|
//%
|
||||||
|
void setNumber(Buffer buf, NumberFormat format, int offset, TNumber value) {
|
||||||
|
if (offset < 0)
|
||||||
|
return;
|
||||||
|
setNumberCore(buf->data + offset, buf->length - offset, format, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a number in specified format from the buffer.
|
||||||
|
*/
|
||||||
|
//%
|
||||||
|
TNumber getNumber(Buffer buf, NumberFormat format, int offset) {
|
||||||
|
if (offset < 0)
|
||||||
|
return fromInt(0);
|
||||||
|
return getNumberCore(buf->data + offset, buf->length - offset, format);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the length of a Buffer object. */
|
||||||
|
//% property
|
||||||
|
int length(Buffer s) {
|
||||||
return s->length;
|
return s->length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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;
|
||||||
|
|
||||||
|
if (offset < 0)
|
||||||
|
offset += length << 8; // try to make it positive
|
||||||
|
offset %= length;
|
||||||
|
if (offset < 0)
|
||||||
|
offset += length;
|
||||||
|
|
||||||
|
uint8_t *data = buf->data + start;
|
||||||
|
|
||||||
|
uint8_t *n_first = data + offset;
|
||||||
|
uint8_t *first = data;
|
||||||
|
uint8_t *next = n_first;
|
||||||
|
uint8_t *last = data + length;
|
||||||
|
|
||||||
|
while (first != next) {
|
||||||
|
uint8_t tmp = *first;
|
||||||
|
*first++ = *next;
|
||||||
|
*next++ = tmp;
|
||||||
|
if (next == last) {
|
||||||
|
next = n_first;
|
||||||
|
} else if (first == n_first) {
|
||||||
|
n_first = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
// int readBytes(uint8_t *dst, int offset, int length, bool swapBytes = false) const;
|
|
||||||
// int writeBytes(int dstOffset, uint8_t *src, int length, bool swapBytes = false);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write contents of `src` at `dstOffset` in current buffer.
|
|
||||||
*/
|
|
||||||
//%
|
|
||||||
void write(Buffer buf, int dstOffset, Buffer src)
|
|
||||||
{
|
|
||||||
//Not supported, we only do up to 4 args :/
|
|
||||||
//void write(Buffer buf, int dstOffset, Buffer src, int srcOffset = 0, int length = -1)
|
|
||||||
ManagedBuffer(buf).writeBuffer(dstOffset, ManagedBuffer(src), 0, -1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write contents of `src` at `dstOffset` in current buffer.
|
||||||
|
*/
|
||||||
|
//%
|
||||||
|
void write(Buffer buf, int dstOffset, Buffer src) {
|
||||||
|
// srcOff and length not supported, we only do up to 4 args :/
|
||||||
|
writeBuffer(buf, dstOffset, src, 0, -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace control {
|
||||||
|
/**
|
||||||
|
* Create a new zero-initialized buffer.
|
||||||
|
* @param size number of bytes in the buffer
|
||||||
|
*/
|
||||||
|
//%
|
||||||
|
Buffer createBuffer(int size) {
|
||||||
|
return mkBuffer(NULL, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace pxt {
|
||||||
|
static int writeBytes(uint8_t *dst, uint8_t *src, int length, bool swapBytes, int szLeft) {
|
||||||
|
if (szLeft < length) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (swapBytes) {
|
||||||
|
uint8_t *p = dst + length;
|
||||||
|
for (int i = 0; i < length; ++i)
|
||||||
|
*--p = src[i];
|
||||||
|
} else {
|
||||||
|
if (length == 4 && ((uint32_t)dst & 3) == 0)
|
||||||
|
*(uint32_t *)dst = *(uint32_t *)src;
|
||||||
|
else if (length == 2 && ((uint32_t)dst & 1) == 0)
|
||||||
|
*(uint16_t *)dst = *(uint16_t *)src;
|
||||||
|
else
|
||||||
|
memcpy(dst, src, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int readBytes(uint8_t *src, uint8_t *dst, int length, bool swapBytes, int szLeft) {
|
||||||
|
if (szLeft < length) {
|
||||||
|
memset(dst, 0, length);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (swapBytes) {
|
||||||
|
uint8_t *p = src + length;
|
||||||
|
for (int i = 0; i < length; ++i)
|
||||||
|
dst[i] = *--p;
|
||||||
|
} else {
|
||||||
|
if (length == 4 && ((uint32_t)src & 3) == 0)
|
||||||
|
*(uint32_t *)dst = *(uint32_t *)src;
|
||||||
|
else if (length == 2 && ((uint32_t)src & 1) == 0)
|
||||||
|
*(uint16_t *)dst = *(uint16_t *)src;
|
||||||
|
else
|
||||||
|
memcpy(dst, src, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setNumberCore(uint8_t *buf, int szLeft, NumberFormat format, TNumber value) {
|
||||||
|
int8_t i8;
|
||||||
|
uint8_t u8;
|
||||||
|
int16_t i16;
|
||||||
|
uint16_t u16;
|
||||||
|
int32_t i32;
|
||||||
|
uint32_t u32;
|
||||||
|
float f32;
|
||||||
|
double f64;
|
||||||
|
|
||||||
|
// Assume little endian
|
||||||
|
#define WRITEBYTES(isz, swap, toInt) \
|
||||||
|
isz = toInt(value); \
|
||||||
|
writeBytes(buf, (uint8_t *)&isz, sizeof(isz), swap, szLeft); \
|
||||||
|
break
|
||||||
|
|
||||||
|
switch (format) {
|
||||||
|
case NumberFormat::Int8LE:
|
||||||
|
WRITEBYTES(i8, false, toInt);
|
||||||
|
case NumberFormat::UInt8LE:
|
||||||
|
WRITEBYTES(u8, false, toInt);
|
||||||
|
case NumberFormat::Int16LE:
|
||||||
|
WRITEBYTES(i16, false, toInt);
|
||||||
|
case NumberFormat::UInt16LE:
|
||||||
|
WRITEBYTES(u16, false, toInt);
|
||||||
|
case NumberFormat::Int32LE:
|
||||||
|
WRITEBYTES(i32, false, toInt);
|
||||||
|
case NumberFormat::UInt32LE:
|
||||||
|
WRITEBYTES(u32, false, toUInt);
|
||||||
|
|
||||||
|
case NumberFormat::Int8BE:
|
||||||
|
WRITEBYTES(i8, true, toInt);
|
||||||
|
case NumberFormat::UInt8BE:
|
||||||
|
WRITEBYTES(u8, true, toInt);
|
||||||
|
case NumberFormat::Int16BE:
|
||||||
|
WRITEBYTES(i16, true, toInt);
|
||||||
|
case NumberFormat::UInt16BE:
|
||||||
|
WRITEBYTES(u16, true, toInt);
|
||||||
|
case NumberFormat::Int32BE:
|
||||||
|
WRITEBYTES(i32, true, toInt);
|
||||||
|
case NumberFormat::UInt32BE:
|
||||||
|
WRITEBYTES(u32, true, toUInt);
|
||||||
|
|
||||||
|
case NumberFormat::Float32LE:
|
||||||
|
WRITEBYTES(f32, false, toFloat);
|
||||||
|
case NumberFormat::Float32BE:
|
||||||
|
WRITEBYTES(f32, true, toFloat);
|
||||||
|
case NumberFormat::Float64LE:
|
||||||
|
WRITEBYTES(f64, false, toDouble);
|
||||||
|
case NumberFormat::Float64BE:
|
||||||
|
WRITEBYTES(f64, true, toDouble);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TNumber getNumberCore(uint8_t *buf, int szLeft, NumberFormat format) {
|
||||||
|
int8_t i8;
|
||||||
|
uint8_t u8;
|
||||||
|
int16_t i16;
|
||||||
|
uint16_t u16;
|
||||||
|
int32_t i32;
|
||||||
|
uint32_t u32;
|
||||||
|
float f32;
|
||||||
|
double f64;
|
||||||
|
|
||||||
|
// Assume little endian
|
||||||
|
#define READBYTES(isz, swap, conv) \
|
||||||
|
readBytes(buf, (uint8_t *)&isz, sizeof(isz), swap, szLeft); \
|
||||||
|
return conv(isz)
|
||||||
|
|
||||||
|
switch (format) {
|
||||||
|
case NumberFormat::Int8LE:
|
||||||
|
READBYTES(i8, false, fromInt);
|
||||||
|
case NumberFormat::UInt8LE:
|
||||||
|
READBYTES(u8, false, fromInt);
|
||||||
|
case NumberFormat::Int16LE:
|
||||||
|
READBYTES(i16, false, fromInt);
|
||||||
|
case NumberFormat::UInt16LE:
|
||||||
|
READBYTES(u16, false, fromInt);
|
||||||
|
case NumberFormat::Int32LE:
|
||||||
|
READBYTES(i32, false, fromInt);
|
||||||
|
case NumberFormat::UInt32LE:
|
||||||
|
READBYTES(u32, false, fromUInt);
|
||||||
|
|
||||||
|
case NumberFormat::Int8BE:
|
||||||
|
READBYTES(i8, true, fromInt);
|
||||||
|
case NumberFormat::UInt8BE:
|
||||||
|
READBYTES(u8, true, fromInt);
|
||||||
|
case NumberFormat::Int16BE:
|
||||||
|
READBYTES(i16, true, fromInt);
|
||||||
|
case NumberFormat::UInt16BE:
|
||||||
|
READBYTES(u16, true, fromInt);
|
||||||
|
case NumberFormat::Int32BE:
|
||||||
|
READBYTES(i32, true, fromInt);
|
||||||
|
case NumberFormat::UInt32BE:
|
||||||
|
READBYTES(u32, true, fromUInt);
|
||||||
|
|
||||||
|
case NumberFormat::Float32LE:
|
||||||
|
READBYTES(f32, false, fromFloat);
|
||||||
|
case NumberFormat::Float32BE:
|
||||||
|
READBYTES(f32, true, fromFloat);
|
||||||
|
case NumberFormat::Float64LE:
|
||||||
|
READBYTES(f64, false, fromDouble);
|
||||||
|
case NumberFormat::Float64BE:
|
||||||
|
READBYTES(f64, true, fromDouble);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
234
libs/core/codal.cpp
Normal file
234
libs/core/codal.cpp
Normal 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
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
1424
libs/core/core.cpp
1424
libs/core/core.cpp
File diff suppressed because it is too large
Load Diff
46
libs/core/enums.d.ts
vendored
46
libs/core/enums.d.ts
vendored
@ -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.
|
||||||
|
@ -1,156 +1,178 @@
|
|||||||
#include "pxt.h"
|
#include "pxt.h"
|
||||||
|
|
||||||
/**
|
PXT_VTABLE_BEGIN(RefMImage, 0, 0)
|
||||||
* Creation, manipulation and display of LED images.
|
PXT_VTABLE_END
|
||||||
*/
|
|
||||||
//% color=#5C2D91 weight=31 icon="\uf03e"
|
|
||||||
//% advanced=true
|
|
||||||
namespace images {
|
|
||||||
/**
|
|
||||||
* Creates an image that fits on the LED screen.
|
|
||||||
*/
|
|
||||||
//% weight=75 help=images/create-image
|
|
||||||
//% blockId=device_build_image block="create image"
|
|
||||||
//% parts="ledmatrix"
|
|
||||||
Image createImage(ImageLiteral leds) {
|
|
||||||
return MicroBitImage(imageBytes(leds)).clone().leakData();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
RefMImage::RefMImage(ImageData *d) : PXT_VTABLE_INIT(RefMImage), img(d) {
|
||||||
* Creates an image with 2 frames.
|
img->incr();
|
||||||
*/
|
}
|
||||||
//% weight=74 help=images/create-big-image
|
|
||||||
//% blockId=device_build_big_image block="create big image" imageLiteral=2
|
void RefMImage::destroy(RefMImage *t) {
|
||||||
//% parts="ledmatrix"
|
t->img->decr();
|
||||||
Image createBigImage(ImageLiteral leds) {
|
}
|
||||||
return createImage(leds);
|
|
||||||
|
void RefMImage::print(RefMImage *t) {
|
||||||
|
DMESG("RefMImage %p r=%d size=%d x %d", t, t->refcnt, img->width, img->height);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RefMImage::makeWritable() {
|
||||||
|
if (img->isReadOnly()) {
|
||||||
|
MicroBitImage i(img);
|
||||||
|
img = i.clone().leakData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creation, manipulation and display of LED images.
|
||||||
|
*/
|
||||||
|
//% color=#5C2D91 weight=31 icon="\uf03e"
|
||||||
|
//% advanced=true
|
||||||
|
namespace images {
|
||||||
|
/**
|
||||||
|
* Creates an image that fits on the LED screen.
|
||||||
|
*/
|
||||||
|
//% weight=75 help=images/create-image
|
||||||
|
//% blockId=device_build_image block="create image"
|
||||||
|
//% parts="ledmatrix"
|
||||||
|
Image createImage(ImageLiteral_ leds) {
|
||||||
|
return new RefMImage(imageBytes(leds));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an image with 2 frames.
|
||||||
|
*/
|
||||||
|
//% weight=74 help=images/create-big-image
|
||||||
|
//% blockId=device_build_big_image block="create big image" imageLiteral=2
|
||||||
|
//% parts="ledmatrix"
|
||||||
|
Image createBigImage(ImageLiteral_ leds) {
|
||||||
|
return createImage(leds);
|
||||||
|
}
|
||||||
|
} // namespace images
|
||||||
|
|
||||||
namespace ImageMethods {
|
namespace ImageMethods {
|
||||||
/**
|
/**
|
||||||
* Plots the image at a given column to the screen
|
* Plots the image at a given column to the screen
|
||||||
*/
|
*/
|
||||||
//% 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows an frame from the image at offset ``x offset``.
|
* Shows an frame from the image at offset ``x offset``.
|
||||||
* @param xOffset column index to start displaying the image
|
* @param xOffset column index to start displaying the image
|
||||||
*/
|
*/
|
||||||
//% help=images/show-image weight=80 blockNamespace=images
|
//% help=images/show-image weight=80 blockNamespace=images
|
||||||
//% 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws the ``index``-th frame of the image on the screen.
|
* Draws the ``index``-th frame of the image on the screen.
|
||||||
* @param xOffset column index to start displaying the image
|
* @param xOffset column index to start displaying the image
|
||||||
*/
|
*/
|
||||||
//% help=images/plot-frame weight=80
|
//% help=images/plot-frame weight=80
|
||||||
//% parts="ledmatrix"
|
//% parts="ledmatrix"
|
||||||
void plotFrame(Image i, int xOffset) {
|
void plotFrame(Image i, int xOffset) {
|
||||||
// TODO showImage() used in original implementation
|
// TODO showImage() used in original implementation
|
||||||
plotImage(i, xOffset * 5);
|
plotImage(i, xOffset * 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scrolls an image .
|
* Scrolls an image .
|
||||||
* @param frameOffset x offset moved on each animation step, eg: 1, 2, 5
|
* @param frameOffset x offset moved on each animation step, eg: 1, 2, 5
|
||||||
* @param interval time between each animation step in milli seconds, eg: 200
|
* @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"
|
||||||
void scrollImage(Image id, int frameOffset, int interval) {
|
//% blockGap=8 parts="ledmatrix"
|
||||||
MicroBitImage i(id);
|
void scrollImage(Image id, int frameOffset, int interval) {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a specific pixel brightness at a given position
|
* Sets a specific pixel brightness at a given position
|
||||||
*/
|
*/
|
||||||
//%
|
//%
|
||||||
//% 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the height in rows (always 5)
|
* Gets the height in rows (always 5)
|
||||||
*/
|
*/
|
||||||
//%
|
//%
|
||||||
int height(Image i) {
|
int height(Image i) {
|
||||||
return i->height;
|
return i->img->height;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a pixel state at position ``(x,y)``
|
* Set a pixel state at position ``(x,y)``
|
||||||
* @param x TODO
|
* @param x TODO
|
||||||
* @param y TODO
|
* @param y TODO
|
||||||
* @param value TODO
|
* @param value TODO
|
||||||
*/
|
*/
|
||||||
//% help=images/set-pixel
|
//% help=images/set-pixel
|
||||||
//% parts="ledmatrix"
|
//% parts="ledmatrix"
|
||||||
void setPixel(Image i, int x, int y, bool value) {
|
void setPixel(Image i, int x, int y, bool value) {
|
||||||
setPixelBrightness(i, x, y, value ? 255 : 0);
|
setPixelBrightness(i, x, y, value ? 255 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the pixel state at position ``(x,y)``
|
* Get the pixel state at position ``(x,y)``
|
||||||
* @param x TODO
|
* @param x TODO
|
||||||
* @param y TODO
|
* @param y TODO
|
||||||
*/
|
*/
|
||||||
//% help=images/pixel
|
//% help=images/pixel
|
||||||
//% parts="ledmatrix"
|
//% parts="ledmatrix"
|
||||||
bool pixel(Image i, int x, int y) {
|
bool pixel(Image i, int x, int y) {
|
||||||
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
|
||||||
*/
|
*/
|
||||||
//% weight=70 help=images/show-frame
|
//% weight=70 help=images/show-frame
|
||||||
//% parts="ledmatrix"
|
//% parts="ledmatrix"
|
||||||
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
|
@ -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("");
|
||||||
|
@ -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;
|
||||||
|
File diff suppressed because it is too large
Load Diff
379
libs/core/pxt.h
379
libs/core/pxt.h
@ -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 {
|
||||||
typedef enum {
|
|
||||||
ERR_INVALID_BINARY_HEADER = 5,
|
|
||||||
ERR_OUT_OF_BOUNDS = 8,
|
|
||||||
ERR_REF_DELETED = 7,
|
|
||||||
ERR_SIZE = 9,
|
|
||||||
} ERROR;
|
|
||||||
|
|
||||||
extern const uint32_t functionsAndBytecode[];
|
|
||||||
extern uint32_t *globals;
|
|
||||||
extern uint16_t *bytecode;
|
|
||||||
class RefRecord;
|
|
||||||
|
|
||||||
// Utility functions
|
|
||||||
extern MicroBitEvent lastEvent;
|
|
||||||
void registerWithDal(int id, int event, Action a);
|
|
||||||
void runInBackground(Action a);
|
|
||||||
uint32_t runAction3(Action a, int arg0, int arg1, int arg2);
|
|
||||||
uint32_t runAction2(Action a, int arg0, int arg1);
|
|
||||||
uint32_t runAction1(Action a, int arg0);
|
|
||||||
uint32_t runAction0(Action a);
|
|
||||||
Action mkAction(int reflen, int totallen, int startptr);
|
|
||||||
void error(ERROR code, int subcode = 0);
|
|
||||||
void exec_binary(uint16_t *pc);
|
|
||||||
void start();
|
|
||||||
void debugMemLeaks();
|
|
||||||
// allocate [sz] words and clear them
|
|
||||||
uint32_t *allocate(uint16_t sz);
|
|
||||||
int templateHash();
|
|
||||||
int programHash();
|
|
||||||
uint32_t programSize();
|
|
||||||
uint32_t afterProgramPage();
|
|
||||||
int getNumGlobals();
|
|
||||||
RefRecord* mkClassInstance(int vtableOffset);
|
|
||||||
|
|
||||||
// The standard calling convention is:
|
|
||||||
// - when a pointer is loaded from a local/global/field etc, and incr()ed
|
|
||||||
// (in other words, its presence on stack counts as a reference)
|
|
||||||
// - after a function call, all pointers are popped off the stack and decr()ed
|
|
||||||
// This does not apply to the RefRecord and st/ld(ref) methods - they unref()
|
|
||||||
// the RefRecord* this.
|
|
||||||
int incr(uint32_t e);
|
|
||||||
void decr(uint32_t e);
|
|
||||||
|
|
||||||
inline void *ptrOfLiteral(int offset)
|
|
||||||
{
|
|
||||||
return &bytecode[offset];
|
|
||||||
}
|
|
||||||
|
|
||||||
inline ImageData* imageBytes(int offset)
|
|
||||||
{
|
|
||||||
return (ImageData*)(void*)&bytecode[offset];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checks if object has a VTable, or if its RefCounted* from the runtime.
|
|
||||||
inline bool hasVTable(uint32_t e)
|
|
||||||
{
|
|
||||||
return (*((uint32_t*)e) & 1) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void check(int cond, ERROR code, int subcode = 0)
|
|
||||||
{
|
|
||||||
if (!cond) error(code, subcode);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class RefObject;
|
|
||||||
#ifdef DEBUG_MEMLEAKS
|
|
||||||
extern std::set<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:
|
public:
|
||||||
uint16_t refcnt;
|
ImageData *img;
|
||||||
uint16_t vtable;
|
|
||||||
|
|
||||||
RefObject(uint16_t vt)
|
RefMImage(ImageData *d);
|
||||||
{
|
void makeWritable();
|
||||||
refcnt = 2;
|
static void destroy(RefMImage *map);
|
||||||
vtable = vt;
|
static void print(RefMImage *map);
|
||||||
#ifdef DEBUG_MEMLEAKS
|
};
|
||||||
allptrs.insert(this);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
inline VTable *getVTable() {
|
#define MSTR(s) ManagedString((s)->data, (s)->length)
|
||||||
return (VTable*)(vtable << vtableShift);
|
|
||||||
}
|
|
||||||
|
|
||||||
void destroy();
|
static inline String PSTR(ManagedString s) {
|
||||||
void print();
|
return mkString(s.toCharArray(), s.length());
|
||||||
|
}
|
||||||
|
|
||||||
// Call to disable pointer tracking on the current instance (in destructor or some other hack)
|
typedef uint32_t ImageLiteral_;
|
||||||
inline void untrack() {
|
|
||||||
#ifdef DEBUG_MEMLEAKS
|
|
||||||
allptrs.erase(this);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Increment/decrement the ref-count. Decrementing to zero deletes the current object.
|
static inline ImageData *imageBytes(ImageLiteral_ lit) {
|
||||||
inline void ref()
|
return (ImageData*)ptrOfLiteral(lit);
|
||||||
{
|
}
|
||||||
check(refcnt > 0, ERR_REF_DELETED);
|
|
||||||
//printf("INCR "); this->print();
|
|
||||||
refcnt += 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void unref()
|
typedef RefMImage *Image;
|
||||||
{
|
|
||||||
//printf("DECR "); this->print();
|
|
||||||
check(refcnt > 0, ERR_REF_DELETED);
|
|
||||||
refcnt -= 2;
|
|
||||||
if (refcnt == 0) {
|
|
||||||
destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class Segment {
|
extern MicroBit uBit;
|
||||||
private:
|
extern MicroBitEvent lastEvent;
|
||||||
uint32_t* data;
|
|
||||||
uint16_t length;
|
|
||||||
uint16_t size;
|
|
||||||
|
|
||||||
static const uint16_t MaxSize = 0xFFFF;
|
MicroBitPin *getPin(int id);
|
||||||
static const uint32_t DefaultValue = 0x0;
|
|
||||||
|
|
||||||
static uint16_t growthFactor(uint16_t size);
|
static inline int min_(int a, int b) {
|
||||||
void growByMin(uint16_t minSize);
|
if (a < b) return a;
|
||||||
void growBy(uint16_t newSize);
|
else return b;
|
||||||
void ensure(uint16_t newSize);
|
}
|
||||||
|
|
||||||
public:
|
static inline int max_(int a, int b) {
|
||||||
Segment() : data (nullptr), length(0), size(0) {};
|
if (a > b) return a;
|
||||||
|
else return b;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t get(uint32_t i);
|
|
||||||
void set(uint32_t i, uint32_t value);
|
|
||||||
|
|
||||||
uint32_t getLength() { return length;};
|
|
||||||
void setLength(uint32_t newLength);
|
|
||||||
|
|
||||||
void push(uint32_t value);
|
|
||||||
uint32_t pop();
|
|
||||||
|
|
||||||
uint32_t remove(uint32_t i);
|
|
||||||
void insert(uint32_t i, uint32_t value);
|
|
||||||
|
|
||||||
bool isValidIndex(uint32_t i);
|
|
||||||
|
|
||||||
void destroy();
|
|
||||||
|
|
||||||
void print();
|
|
||||||
};
|
|
||||||
|
|
||||||
// A ref-counted collection of either primitive or ref-counted objects (String, Image,
|
|
||||||
// user-defined record, another collection)
|
|
||||||
class RefCollection
|
|
||||||
: public RefObject
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
Segment head;
|
|
||||||
public:
|
|
||||||
// 1 - collection of refs (need decr)
|
|
||||||
// 2 - collection of strings (in fact we always have 3, never 2 alone)
|
|
||||||
inline uint32_t getFlags() { return getVTable()->userdata; }
|
|
||||||
inline bool isRef() { return getFlags() & 1; }
|
|
||||||
inline bool isString() { return getFlags() & 2; }
|
|
||||||
|
|
||||||
RefCollection(uint16_t f);
|
|
||||||
|
|
||||||
void destroy();
|
|
||||||
void print();
|
|
||||||
|
|
||||||
uint32_t length() { return head.getLength();}
|
|
||||||
void setLength(uint32_t newLength) { head.setLength(newLength); }
|
|
||||||
|
|
||||||
void push(uint32_t x);
|
|
||||||
uint32_t pop();
|
|
||||||
uint32_t getAt(int i);
|
|
||||||
void setAt(int i, uint32_t x);
|
|
||||||
//removes the element at index i and shifts the other elements left
|
|
||||||
uint32_t removeAt(int i);
|
|
||||||
//inserts the element at index i and moves the other elements right.
|
|
||||||
void insertAt(int i, uint32_t x);
|
|
||||||
|
|
||||||
int indexOf(uint32_t x, int start);
|
|
||||||
int removeElement(uint32_t x);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct MapEntry {
|
|
||||||
uint32_t key;
|
|
||||||
uint32_t val;
|
|
||||||
};
|
|
||||||
|
|
||||||
class RefMap
|
|
||||||
: public RefObject
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
std::vector<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
|
||||||
|
|
||||||
|
@ -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
701
libs/core/pxtbase.h
Normal 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
16
libs/core/pxtcore.h
Normal 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
|
@ -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
30
libs/core/shims.d.ts
vendored
@ -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.
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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": [
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user