removing dependency on pxt-calliope-core
This commit is contained in:
parent
8eb1b98cd4
commit
8cf0984a3f
373
libs/core/ManagedBuffer.cpp
Normal file
373
libs/core/ManagedBuffer.cpp
Normal file
@ -0,0 +1,373 @@
|
|||||||
|
#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;
|
||||||
|
}
|
257
libs/core/ManagedBuffer.h
Normal file
257
libs/core/ManagedBuffer.h
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
#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
|
||||||
|
|
472
libs/core/pxt.cpp
Normal file
472
libs/core/pxt.cpp
Normal file
@ -0,0 +1,472 @@
|
|||||||
|
#include "pxt.h"
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
MicroBit uBit;
|
||||||
|
|
||||||
|
namespace pxt {
|
||||||
|
int incr(uint32_t e)
|
||||||
|
{
|
||||||
|
if (e) {
|
||||||
|
if (hasVTable(e))
|
||||||
|
((RefObject*)e)->ref();
|
||||||
|
else
|
||||||
|
((RefCounted*)e)->incr();
|
||||||
|
}
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
void decr(uint32_t e)
|
||||||
|
{
|
||||||
|
if (e) {
|
||||||
|
if (hasVTable(e))
|
||||||
|
((RefObject*)e)->unref();
|
||||||
|
else
|
||||||
|
((RefCounted*)e)->decr();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Action mkAction(int reflen, int totallen, int startptr)
|
||||||
|
{
|
||||||
|
check(0 <= reflen && reflen <= totallen, ERR_SIZE, 1);
|
||||||
|
check(reflen <= totallen && totallen <= 255, ERR_SIZE, 2);
|
||||||
|
check(bytecode[startptr] == 0xffff, ERR_INVALID_BINARY_HEADER, 3);
|
||||||
|
check(bytecode[startptr + 1] == 0, ERR_INVALID_BINARY_HEADER, 4);
|
||||||
|
|
||||||
|
uint32_t tmp = (uint32_t)&bytecode[startptr];
|
||||||
|
|
||||||
|
if (totallen == 0) {
|
||||||
|
return tmp; // no closure needed
|
||||||
|
}
|
||||||
|
|
||||||
|
void *ptr = ::operator new(sizeof(RefAction) + totallen * sizeof(uint32_t));
|
||||||
|
RefAction *r = new (ptr) RefAction();
|
||||||
|
r->len = totallen;
|
||||||
|
r->reflen = reflen;
|
||||||
|
r->func = (ActionCB)((tmp + 4) | 1);
|
||||||
|
memset(r->fields, 0, r->len * sizeof(uint32_t));
|
||||||
|
|
||||||
|
return (Action)r;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t runAction3(Action a, int arg0, int arg1, int arg2)
|
||||||
|
{
|
||||||
|
if (hasVTable(a))
|
||||||
|
return ((RefAction*)a)->runCore(arg0, arg1, arg2);
|
||||||
|
else {
|
||||||
|
check(*(uint16_t*)a == 0xffff, ERR_INVALID_BINARY_HEADER, 4);
|
||||||
|
return ((ActionCB)((a + 4) | 1))(NULL, arg0, arg1, arg2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t runAction2(Action a, int arg0, int arg1)
|
||||||
|
{
|
||||||
|
return runAction3(a, arg0, arg1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t runAction1(Action a, int arg0)
|
||||||
|
{
|
||||||
|
return runAction3(a, arg0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t runAction0(Action a)
|
||||||
|
{
|
||||||
|
return runAction3(a, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
RefRecord* mkClassInstance(int vtableOffset)
|
||||||
|
{
|
||||||
|
VTable *vtable = (VTable*)&bytecode[vtableOffset];
|
||||||
|
|
||||||
|
intcheck(vtable->methods[0] == &RefRecord_destroy, ERR_SIZE, 3);
|
||||||
|
intcheck(vtable->methods[1] == &RefRecord_print, ERR_SIZE, 4);
|
||||||
|
|
||||||
|
void *ptr = ::operator new(vtable->numbytes);
|
||||||
|
RefRecord *r = new (ptr) RefRecord(PXT_VTABLE_TO_INT(vtable));
|
||||||
|
memset(r->fields, 0, vtable->numbytes - sizeof(RefRecord));
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t RefRecord::ld(int idx)
|
||||||
|
{
|
||||||
|
//intcheck((reflen == 255 ? 0 : reflen) <= idx && idx < len, ERR_OUT_OF_BOUNDS, 1);
|
||||||
|
return fields[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t RefRecord::ldref(int idx)
|
||||||
|
{
|
||||||
|
//printf("LD %p len=%d reflen=%d idx=%d\n", this, len, reflen, idx);
|
||||||
|
//intcheck(0 <= idx && idx < reflen, ERR_OUT_OF_BOUNDS, 2);
|
||||||
|
uint32_t tmp = fields[idx];
|
||||||
|
incr(tmp);
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RefRecord::st(int idx, uint32_t v)
|
||||||
|
{
|
||||||
|
//intcheck((reflen == 255 ? 0 : reflen) <= idx && idx < len, ERR_OUT_OF_BOUNDS, 3);
|
||||||
|
fields[idx] = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RefRecord::stref(int idx, uint32_t v)
|
||||||
|
{
|
||||||
|
//printf("ST %p len=%d reflen=%d idx=%d\n", this, len, reflen, idx);
|
||||||
|
//intcheck(0 <= idx && idx < reflen, ERR_OUT_OF_BOUNDS, 4);
|
||||||
|
decr(fields[idx]);
|
||||||
|
fields[idx] = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RefObject::destroy() {
|
||||||
|
((RefObjectMethod)getVTable()->methods[0])(this);
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RefObject::print() {
|
||||||
|
((RefObjectMethod)getVTable()->methods[1])(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RefRecord_destroy(RefRecord *r) {
|
||||||
|
auto tbl = r->getVTable();
|
||||||
|
uint8_t *refmask = (uint8_t*)&tbl->methods[tbl->userdata & 0xff];
|
||||||
|
int len = (tbl->numbytes >> 2) - 1;
|
||||||
|
for (int i = 0; i < len; ++i) {
|
||||||
|
if (refmask[i]) decr(r->fields[i]);
|
||||||
|
r->fields[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RefRecord_print(RefRecord *r)
|
||||||
|
{
|
||||||
|
printf("RefRecord %p r=%d size=%d bytes\n", r, r->refcnt, r->getVTable()->numbytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RefCollection::push(uint32_t x) {
|
||||||
|
if (isRef()) incr(x);
|
||||||
|
data.push_back(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t RefCollection::getAt(int x) {
|
||||||
|
if (in_range(x)) {
|
||||||
|
uint32_t tmp = data.at(x);
|
||||||
|
if (isRef()) incr(tmp);
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
error(ERR_OUT_OF_BOUNDS);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RefCollection::removeAt(int x) {
|
||||||
|
if (!in_range(x))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (isRef()) decr(data.at(x));
|
||||||
|
data.erase(data.begin()+x);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RefCollection::setAt(int x, uint32_t y) {
|
||||||
|
if (!in_range(x))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (isRef()) {
|
||||||
|
decr(data.at(x));
|
||||||
|
incr(y);
|
||||||
|
}
|
||||||
|
data.at(x) = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RefCollection::indexOf(uint32_t x, int start) {
|
||||||
|
if (!in_range(start))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (isString()) {
|
||||||
|
StringData *xx = (StringData*)x;
|
||||||
|
for (uint32_t i = start; i < data.size(); ++i) {
|
||||||
|
StringData *ee = (StringData*)data.at(i);
|
||||||
|
if (xx->len == ee->len && memcmp(xx->data, ee->data, xx->len) == 0)
|
||||||
|
return (int)i;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (uint32_t i = start; i < data.size(); ++i)
|
||||||
|
if (data.at(i) == x)
|
||||||
|
return (int)i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RefCollection::removeElement(uint32_t x) {
|
||||||
|
int idx = indexOf(x, 0);
|
||||||
|
if (idx >= 0) {
|
||||||
|
removeAt(idx);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Coll0 {
|
||||||
|
PXT_VTABLE_BEGIN(RefCollection, 0, 0)
|
||||||
|
PXT_VTABLE_END
|
||||||
|
}
|
||||||
|
namespace Coll1 {
|
||||||
|
PXT_VTABLE_BEGIN(RefCollection, 1, 0)
|
||||||
|
PXT_VTABLE_END
|
||||||
|
}
|
||||||
|
namespace Coll3 {
|
||||||
|
PXT_VTABLE_BEGIN(RefCollection, 3, 0)
|
||||||
|
PXT_VTABLE_END
|
||||||
|
}
|
||||||
|
|
||||||
|
RefCollection::RefCollection(uint16_t flags) : RefObject(0) {
|
||||||
|
switch (flags) {
|
||||||
|
case 0:
|
||||||
|
vtable = PXT_VTABLE_TO_INT(&Coll0::RefCollection_vtable);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
vtable = PXT_VTABLE_TO_INT(&Coll1::RefCollection_vtable);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
vtable = PXT_VTABLE_TO_INT(&Coll3::RefCollection_vtable);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
error(ERR_SIZE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RefCollection::destroy()
|
||||||
|
{
|
||||||
|
if (this->isRef())
|
||||||
|
for (uint32_t i = 0; i < this->data.size(); ++i) {
|
||||||
|
decr(this->data[i]);
|
||||||
|
this->data[i] = 0;
|
||||||
|
}
|
||||||
|
this->data.resize(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RefCollection::print()
|
||||||
|
{
|
||||||
|
printf("RefCollection %p r=%d flags=%d size=%d [%p, ...]\n", this, refcnt, getFlags(), data.size(), data.size() > 0 ? data[0] : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
PXT_VTABLE_CTOR(RefAction) {}
|
||||||
|
|
||||||
|
// fields[] contain captured locals
|
||||||
|
void RefAction::destroy()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < this->reflen; ++i) {
|
||||||
|
decr(fields[i]);
|
||||||
|
fields[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RefAction::print()
|
||||||
|
{
|
||||||
|
printf("RefAction %p r=%d pc=0x%lx size=%d (%d refs)\n", this, refcnt, (const uint8_t*)func - (const uint8_t*)bytecode, len, reflen);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RefLocal::print()
|
||||||
|
{
|
||||||
|
printf("RefLocal %p r=%d v=%d\n", this, refcnt, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RefLocal::destroy()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
PXT_VTABLE_CTOR(RefLocal) {
|
||||||
|
v = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
PXT_VTABLE_CTOR(RefRefLocal) {
|
||||||
|
v = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RefRefLocal::print()
|
||||||
|
{
|
||||||
|
printf("RefRefLocal %p r=%d v=%p\n", this, refcnt, (void*)v);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RefRefLocal::destroy()
|
||||||
|
{
|
||||||
|
decr(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
PXT_VTABLE_BEGIN(RefMap, 0, RefMapMarker)
|
||||||
|
PXT_VTABLE_END
|
||||||
|
RefMap::RefMap() : PXT_VTABLE_INIT(RefMap) {}
|
||||||
|
|
||||||
|
void RefMap::destroy() {
|
||||||
|
for (unsigned i = 0; i < data.size(); ++i) {
|
||||||
|
if (data[i].key & 1) {
|
||||||
|
decr(data[i].val);
|
||||||
|
}
|
||||||
|
data[i].val = 0;
|
||||||
|
}
|
||||||
|
data.resize(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int RefMap::findIdx(uint32_t key) {
|
||||||
|
for (unsigned i = 0; i < data.size(); ++i) {
|
||||||
|
if (data[i].key >> 1 == key)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RefMap::print()
|
||||||
|
{
|
||||||
|
printf("RefMap %p r=%d size=%d\n", this, refcnt, data.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef DEBUG_MEMLEAKS
|
||||||
|
std::set<RefObject*> allptrs;
|
||||||
|
void debugMemLeaks()
|
||||||
|
{
|
||||||
|
printf("LIVE POINTERS:\n");
|
||||||
|
for(std::set<RefObject*>::iterator itr = allptrs.begin();itr!=allptrs.end();itr++)
|
||||||
|
{
|
||||||
|
(*itr)->print();
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
void debugMemLeaks() {}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// An adapter for the API expected by the run-time.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
map<pair<int, int>, Action> handlersMap;
|
||||||
|
|
||||||
|
MicroBitEvent lastEvent;
|
||||||
|
|
||||||
|
// We have the invariant that if [dispatchEvent] is registered against the DAL
|
||||||
|
// for a given event, then [handlersMap] contains a valid entry for that
|
||||||
|
// event.
|
||||||
|
void dispatchEvent(MicroBitEvent e) {
|
||||||
|
|
||||||
|
lastEvent = e;
|
||||||
|
|
||||||
|
Action curr = handlersMap[{ e.source, e.value }];
|
||||||
|
if (curr)
|
||||||
|
runAction1(curr, e.value);
|
||||||
|
|
||||||
|
curr = handlersMap[{ e.source, MICROBIT_EVT_ANY }];
|
||||||
|
if (curr)
|
||||||
|
runAction1(curr, e.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void registerWithDal(int id, int event, Action a) {
|
||||||
|
Action prev = handlersMap[{ id, event }];
|
||||||
|
if (prev)
|
||||||
|
decr(prev);
|
||||||
|
else
|
||||||
|
uBit.messageBus.listen(id, event, dispatchEvent);
|
||||||
|
incr(a);
|
||||||
|
handlersMap[{ id, event }] = a;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fiberDone(void *a)
|
||||||
|
{
|
||||||
|
decr((Action)a);
|
||||||
|
release_fiber();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void runInBackground(Action a) {
|
||||||
|
if (a != 0) {
|
||||||
|
incr(a);
|
||||||
|
create_fiber((void(*)(void*))runAction0, (void*)a, fiberDone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void error(ERROR code, int subcode)
|
||||||
|
{
|
||||||
|
printf("Error: %d [%d]\n", code, subcode);
|
||||||
|
uBit.panic(42);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t *bytecode;
|
||||||
|
uint32_t *globals;
|
||||||
|
int numGlobals;
|
||||||
|
|
||||||
|
uint32_t *allocate(uint16_t sz)
|
||||||
|
{
|
||||||
|
uint32_t *arr = new uint32_t[sz];
|
||||||
|
memset(arr, 0, sz * 4);
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkStr(bool cond, const char *msg)
|
||||||
|
{
|
||||||
|
if (!cond) {
|
||||||
|
while (true) {
|
||||||
|
uBit.display.scroll(msg, 100);
|
||||||
|
uBit.sleep(100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int templateHash()
|
||||||
|
{
|
||||||
|
return ((int*)bytecode)[4];
|
||||||
|
}
|
||||||
|
|
||||||
|
int programHash()
|
||||||
|
{
|
||||||
|
return ((int*)bytecode)[6];
|
||||||
|
}
|
||||||
|
|
||||||
|
int getNumGlobals()
|
||||||
|
{
|
||||||
|
return bytecode[16];
|
||||||
|
}
|
||||||
|
|
||||||
|
void exec_binary(int32_t *pc)
|
||||||
|
{
|
||||||
|
// XXX re-enable once the calibration code is fixed and [editor/embedded.ts]
|
||||||
|
// properly prepends a call to [internal_main].
|
||||||
|
// ::touch_develop::internal_main();
|
||||||
|
|
||||||
|
// unique group for radio based on source hash
|
||||||
|
// ::touch_develop::micro_bit::radioDefaultGroup = programHash();
|
||||||
|
|
||||||
|
// repeat error 4 times and restart as needed
|
||||||
|
microbit_panic_timeout(4);
|
||||||
|
|
||||||
|
int32_t ver = *pc++;
|
||||||
|
checkStr(ver == 0x4209, ":( Bad runtime version");
|
||||||
|
|
||||||
|
bytecode = *((uint16_t**)pc++); // the actual bytecode is here
|
||||||
|
globals = allocate(getNumGlobals());
|
||||||
|
|
||||||
|
// just compare the first word
|
||||||
|
checkStr(((uint32_t*)bytecode)[0] == 0x923B8E70 &&
|
||||||
|
templateHash() == *pc,
|
||||||
|
":( Failed partial flash");
|
||||||
|
|
||||||
|
uint32_t startptr = (uint32_t)bytecode;
|
||||||
|
startptr += 48; // header
|
||||||
|
startptr |= 1; // Thumb state
|
||||||
|
|
||||||
|
((uint32_t (*)())startptr)();
|
||||||
|
|
||||||
|
#ifdef DEBUG_MEMLEAKS
|
||||||
|
pxt::debugMemLeaks();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void start()
|
||||||
|
{
|
||||||
|
exec_binary((int32_t*)functionsAndBytecode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// vim: ts=2 sw=2 expandtab
|
334
libs/core/pxt.h
Normal file
334
libs/core/pxt.h
Normal file
@ -0,0 +1,334 @@
|
|||||||
|
#ifndef __PXT_H
|
||||||
|
#define __PXT_H
|
||||||
|
|
||||||
|
// #define DEBUG_MEMLEAKS 1
|
||||||
|
|
||||||
|
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||||
|
|
||||||
|
#include "MicroBit.h"
|
||||||
|
#include "MicroBitImage.h"
|
||||||
|
#include "ManagedString.h"
|
||||||
|
#include "ManagedType.h"
|
||||||
|
#include "ManagedBuffer.h"
|
||||||
|
|
||||||
|
#define printf(...) uBit.serial.printf(__VA_ARGS__)
|
||||||
|
// #define printf(...)
|
||||||
|
|
||||||
|
#define intcheck(...) check(__VA_ARGS__)
|
||||||
|
//#define intcheck(...) do {} while (0)
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef DEBUG_MEMLEAKS
|
||||||
|
#include <set>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern MicroBit uBit;
|
||||||
|
|
||||||
|
namespace pxt {
|
||||||
|
typedef uint32_t Action;
|
||||||
|
typedef uint32_t ImageLiteral;
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
ERR_INVALID_BINARY_HEADER = 5,
|
||||||
|
ERR_OUT_OF_BOUNDS = 8,
|
||||||
|
ERR_REF_DELETED = 7,
|
||||||
|
ERR_SIZE = 9,
|
||||||
|
} ERROR;
|
||||||
|
|
||||||
|
extern const uint32_t functionsAndBytecode[];
|
||||||
|
extern uint32_t *globals;
|
||||||
|
extern uint16_t *bytecode;
|
||||||
|
class RefRecord;
|
||||||
|
|
||||||
|
// Utility functions
|
||||||
|
extern MicroBitEvent lastEvent;
|
||||||
|
void registerWithDal(int id, int event, Action a);
|
||||||
|
void runInBackground(Action a);
|
||||||
|
uint32_t runAction3(Action a, int arg0, int arg1, int arg2);
|
||||||
|
uint32_t runAction2(Action a, int arg0, int arg1);
|
||||||
|
uint32_t runAction1(Action a, int arg0);
|
||||||
|
uint32_t runAction0(Action a);
|
||||||
|
Action mkAction(int reflen, int totallen, int startptr);
|
||||||
|
void error(ERROR code, int subcode = 0);
|
||||||
|
void exec_binary(uint16_t *pc);
|
||||||
|
void start();
|
||||||
|
void debugMemLeaks();
|
||||||
|
// allocate [sz] words and clear them
|
||||||
|
uint32_t *allocate(uint16_t sz);
|
||||||
|
int templateHash();
|
||||||
|
int programHash();
|
||||||
|
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:
|
||||||
|
uint16_t refcnt;
|
||||||
|
uint16_t vtable;
|
||||||
|
|
||||||
|
RefObject(uint16_t vt)
|
||||||
|
{
|
||||||
|
refcnt = 2;
|
||||||
|
vtable = vt;
|
||||||
|
#ifdef DEBUG_MEMLEAKS
|
||||||
|
allptrs.insert(this);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
inline VTable *getVTable() {
|
||||||
|
return (VTable*)(vtable << vtableShift);
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy();
|
||||||
|
void print();
|
||||||
|
|
||||||
|
// Call to disable pointer tracking on the current instance (in destructor or some other hack)
|
||||||
|
inline void untrack() {
|
||||||
|
#ifdef DEBUG_MEMLEAKS
|
||||||
|
allptrs.erase(this);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment/decrement the ref-count. Decrementing to zero deletes the current object.
|
||||||
|
inline void ref()
|
||||||
|
{
|
||||||
|
check(refcnt > 0, ERR_REF_DELETED);
|
||||||
|
//printf("INCR "); this->print();
|
||||||
|
refcnt += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void unref()
|
||||||
|
{
|
||||||
|
//printf("DECR "); this->print();
|
||||||
|
refcnt -= 2;
|
||||||
|
if (refcnt == 0) {
|
||||||
|
destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// A ref-counted collection of either primitive or ref-counted objects (String, Image,
|
||||||
|
// user-defined record, another collection)
|
||||||
|
class RefCollection
|
||||||
|
: public RefObject
|
||||||
|
{
|
||||||
|
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; }
|
||||||
|
|
||||||
|
std::vector<uint32_t> data;
|
||||||
|
|
||||||
|
RefCollection(uint16_t f);
|
||||||
|
|
||||||
|
inline bool in_range(int x) {
|
||||||
|
return (0 <= x && x < (int)data.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int length() { return data.size(); }
|
||||||
|
|
||||||
|
void destroy();
|
||||||
|
void print();
|
||||||
|
|
||||||
|
void push(uint32_t x);
|
||||||
|
uint32_t getAt(int x);
|
||||||
|
void removeAt(int x);
|
||||||
|
void setAt(int x, uint32_t y);
|
||||||
|
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();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// The ARM Thumb generator in the JavaScript code is parsing
|
||||||
|
// the hex file and looks for the magic numbers as present here.
|
||||||
|
//
|
||||||
|
// Then it fetches function pointer addresses from there.
|
||||||
|
|
||||||
|
#define PXT_SHIMS_BEGIN \
|
||||||
|
namespace pxt { \
|
||||||
|
const uint32_t functionsAndBytecode[] __attribute__((aligned(0x20))) = { \
|
||||||
|
0x08010801, 0x42424242, 0x08010801, 0x8de9d83e,
|
||||||
|
|
||||||
|
#define PXT_SHIMS_END }; }
|
||||||
|
|
||||||
|
#pragma GCC diagnostic ignored "-Wpmf-conversions"
|
||||||
|
|
||||||
|
#define PXT_VTABLE_TO_INT(vt) ((uint32_t)(vt) >> vtableShift)
|
||||||
|
#define PXT_VTABLE_BEGIN(classname, flags, iface) \
|
||||||
|
const VTable classname ## _vtable \
|
||||||
|
__attribute__((aligned(1 << vtableShift))) \
|
||||||
|
= { \
|
||||||
|
sizeof(classname), \
|
||||||
|
flags, \
|
||||||
|
iface, \
|
||||||
|
{ \
|
||||||
|
(void*)&classname::destroy, \
|
||||||
|
(void*)&classname::print,
|
||||||
|
|
||||||
|
#define PXT_VTABLE_END } };
|
||||||
|
|
||||||
|
#define PXT_VTABLE_INIT(classname) \
|
||||||
|
RefObject(PXT_VTABLE_TO_INT(&classname ## _vtable))
|
||||||
|
|
||||||
|
#define PXT_VTABLE_CTOR(classname) \
|
||||||
|
PXT_VTABLE_BEGIN(classname, 0, 0) PXT_VTABLE_END \
|
||||||
|
classname::classname() : PXT_VTABLE_INIT(classname)
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// vim: ts=2 sw=2 expandtab
|
@ -4,6 +4,10 @@
|
|||||||
"installedVersion": "tsmdvf",
|
"installedVersion": "tsmdvf",
|
||||||
"files": [
|
"files": [
|
||||||
"README.md",
|
"README.md",
|
||||||
|
"ManagedBuffer.cpp",
|
||||||
|
"ManagedBuffer.h",
|
||||||
|
"pxt.cpp",
|
||||||
|
"pxt.h",
|
||||||
"dal.d.ts",
|
"dal.d.ts",
|
||||||
"enums.d.ts",
|
"enums.d.ts",
|
||||||
"shims.d.ts",
|
"shims.d.ts",
|
||||||
|
@ -166,10 +166,10 @@
|
|||||||
},
|
},
|
||||||
"compileService": {
|
"compileService": {
|
||||||
"yottaTarget": "bbc-microbit-classic-gcc",
|
"yottaTarget": "bbc-microbit-classic-gcc",
|
||||||
"yottaCorePackage": "pxt-calliope-core",
|
"yottaCorePackage": "microbit",
|
||||||
"githubCorePackage": "microsoft/pxt-calliope-core",
|
"githubCorePackage": "calliope-mini/microbit",
|
||||||
"gittag": "v0.5.16",
|
"gittag": "v1.0.2-calliope",
|
||||||
"serviceId": "calliope"
|
"serviceId": "calliopemini"
|
||||||
},
|
},
|
||||||
"serial": {
|
"serial": {
|
||||||
"manufacturerFilter": "^mbed$",
|
"manufacturerFilter": "^mbed$",
|
||||||
|
Loading…
Reference in New Issue
Block a user