removing dependency on pxt-calliope-core

This commit is contained in:
Peli de Halleux 2016-11-29 21:51:14 -08:00
parent 8eb1b98cd4
commit 8cf0984a3f
6 changed files with 1444 additions and 4 deletions

373
libs/core/ManagedBuffer.cpp Normal file
View 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
View 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
View 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
View 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

View File

@ -4,6 +4,10 @@
"installedVersion": "tsmdvf",
"files": [
"README.md",
"ManagedBuffer.cpp",
"ManagedBuffer.h",
"pxt.cpp",
"pxt.h",
"dal.d.ts",
"enums.d.ts",
"shims.d.ts",

View File

@ -166,10 +166,10 @@
},
"compileService": {
"yottaTarget": "bbc-microbit-classic-gcc",
"yottaCorePackage": "pxt-calliope-core",
"githubCorePackage": "microsoft/pxt-calliope-core",
"gittag": "v0.5.16",
"serviceId": "calliope"
"yottaCorePackage": "microbit",
"githubCorePackage": "calliope-mini/microbit",
"gittag": "v1.0.2-calliope",
"serviceId": "calliopemini"
},
"serial": {
"manufacturerFilter": "^mbed$",