Merge branch 'master' into secure-ble

This commit is contained in:
Joe Finney 2016-01-05 01:25:52 +00:00
commit 55601f3e0e
15 changed files with 492 additions and 316 deletions

2
.gitignore vendored
View File

@ -2,3 +2,5 @@ build
.yotta.json
yotta_modules
yotta_targets
*.swp
Makefile

View File

@ -1,7 +1,14 @@
#ifndef MANAGED_STRING_H
#define MANAGED_STRING_H
#include "mbed.h"
#include "RefCounted.h"
struct StringData : RefCounted
{
uint16_t len;
char data[0];
};
/**
* Class definition for a ManagedString.
@ -14,21 +21,43 @@
* 1) std::shared_ptr is not yet availiable on the ARMCC compiler
* 2) to reduce memory footprint - we don't need many of the other features in the std library
* 3) it makes an interestin case study for anyone interested in seeing how it works!
* 4) we need explicit reference counting to inter-op with low-level application langauge runtimes
* 5) the reference counting needs to also work for read-only, flash-resident strings
*/
class ManagedString
{
// Internally we record the string as a char *, but control access to this to proide immutability
// and reference counting.
char *data;
int16_t *ref;
int16_t len;
// StringData contains the reference count, the length, follwed by char[] data, all in one block.
// When referece count is 0xffff, then it's read only and should not be counted.
// Otherwise the block was malloc()ed.
// We control access to this to proide immutability and reference counting.
StringData *ptr;
public:
/**
* Constructor.
* Create a managed string from a specially prepared string literal. It will ptr->incr().
*
* @param ptr The literal - first two bytes should be 0xff, then the length in little endian, then the literal. The literal has to be 4-byte aligned.
*
* Example:
* @code
* static const char hello[] __attribute__ ((aligned (4))) = "\xff\xff\x05\x00" "Hello";
* ManagedString s((StringData*)(void*)hello);
* @endcode
*/
ManagedString(StringData *ptr);
/**
* Get current ptr, do not decr() it, and set the current instance to empty string.
* This is to be used by specialized runtimes which pass StringData around.
*/
StringData *leakData();
/**
* Constructor.
* Create a managed string from a pointer to an 8-bit character buffer.
* The buffer is copied to ensure sage memory management (the supplied
* The buffer is copied to ensure safe memory management (the supplied
* character buffer may be decalred on the stack for instance).
*
* @param str The character array on which to base the new ManagedString.
@ -115,7 +144,7 @@ class ManagedString
*
* Free this ManagedString, and decrement the reference count to the
* internal character buffer. If we're holding the last reference,
* also free the character buffer and reference counter.
* also free the character buffer.
*/
~ManagedString();
@ -251,11 +280,14 @@ class ManagedString
/**
* Provides an immutable 8 bit wide haracter buffer representing this string.
* Provides an immutable 8 bit wide character buffer representing this string.
*
* @return a pointer to the character buffer.
*/
const char *toCharArray();
const char *toCharArray() const
{
return ptr->data;
}
/**
* Determines the length of this ManagedString in characters.
@ -269,7 +301,10 @@ class ManagedString
* print(s.length()) // prints "4"
* @endcode
*/
int16_t length();
int16_t length() const
{
return ptr->len;
}
/**
* Empty String constant

View File

@ -253,7 +253,7 @@
// Selects the default brightness for the display
// in the region of zero (off) to 255 (full brightness)
#ifndef MICROBIT_DISPLAY_DEFAULT_BRIGHTNESS
#define MICROBIT_DISPLAY_DEFAULT_BRIGHTNESS ((MICROBIT_DISPLAY_MAXIMUM_BRIGHTNESS - MICROBIT_DISPLAY_MINIMUM_BRIGHTNESS) / 2)
#define MICROBIT_DISPLAY_DEFAULT_BRIGHTNESS MICROBIT_DISPLAY_MAXIMUM_BRIGHTNESS
#endif
// Selects the default scroll speed for the display.

View File

@ -2,6 +2,14 @@
#define MICROBIT_IMAGE_H
#include "mbed.h"
#include "RefCounted.h"
struct ImageData : RefCounted
{
uint16_t width; // Width in pixels
uint16_t height; // Height in pixels
uint8_t data[0]; // 2D array representing the bitmap image
};
/**
* Class definition for a MicroBitImage.
@ -11,9 +19,7 @@
*/
class MicroBitImage
{
int16_t width; // Width of the bitmap, in pixels.
int16_t height; // Height of the bitmap, in pixels.
int16_t *ref; // Reference count.
ImageData *ptr; // Pointer to payload data
/**
@ -32,7 +38,34 @@ class MicroBitImage
public:
static MicroBitImage EmptyImage; // Shared representation of a null image.
uint8_t *bitmap; // 2D array representing the bitmap image.
/**
* Get current ptr, do not decr() it, and set the current instance to empty image.
* This is to be used by specialized runtimes which pass ImageData around.
*/
ImageData *leakData();
/**
* Return a 2D array representing the bitmap image.
*/
uint8_t *getBitmap()
{
return ptr->data;
}
/**
* Constructor.
* Create an image from a specially prepared constant array, with no copying. Will call ptr->incr().
*
* @param ptr The literal - first two bytes should be 0xff, then width, 0, height, 0, and the bitmap. Width and height are 16 bit. The literal has to be 4-byte aligned.
*
* Example:
* @code
* static const uint8_t heart[] __attribute__ ((aligned (4))) = { 0xff, 0xff, 10, 0, 5, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart
* MicroBitImage i((ImageData*)(void*)heart);
* @endcode
*/
MicroBitImage(ImageData *ptr);
/**
* Default Constructor.
@ -325,7 +358,10 @@ class MicroBitImage
* i.getWidth(); //equals 10...
* @endcode
*/
int getWidth();
int getWidth() const
{
return ptr->width;
}
/**
* Gets the height of this image.
@ -339,7 +375,27 @@ class MicroBitImage
* i.getHeight(); //equals 5...
* @endcode
*/
int getHeight();
int getHeight() const
{
return ptr->height;
}
/**
* Gets number of bytes in the bitmap, ie., width * height.
*
* @return The size of the bitmap.
*
* Example:
* @code
* const uint8_t heart[] = { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart
* MicroBitImage i(10,5,heart);
* i.getSize(); //equals 50...
* @endcode
*/
int getSize() const
{
return ptr->width * ptr->height;
}
/**
* Converts the bitmap to a csv string.
@ -372,6 +428,17 @@ class MicroBitImage
*/
MicroBitImage crop(int startx, int starty, int finx, int finy);
/**
* Check if image is read-only (i.e., residing in flash).
*/
bool isReadOnly();
/**
* Create a copy of the image bitmap. Used particularly, when isReadOnly() is true.
*
* @return an instance of MicroBitImage which can be modified independently of the current instance
*/
MicroBitImage clone();
};
#endif

34
inc/RefCounted.h Normal file
View File

@ -0,0 +1,34 @@
#ifndef REF_COUNTED_H
#define REF_COUNTED_H
#include "mbed.h"
/**
* Base class for payload for ref-counted objects. Used by ManagedString and MicroBitImage.
* There is no constructor, as this struct is typically malloc()ed.
*/
struct RefCounted
{
public:
/**
* The high 15 bits hold the number of outstanding references. The lowest bit is always 1
* to make sure it doesn't look like vtable.
* Should never be even or one (object should be deleted then).
* When it's set to 0xffff, it means the object sits in flash and should not be counted.
*/
uint16_t refCount;
/** Increment reference count. */
void incr();
/** Decrement reference count. */
void decr();
/** Initializes for one outstanding reference. */
void init();
/** Checks if the object sits in flash memory. */
bool isReadOnly();
};
#endif

View File

@ -1,6 +1,6 @@
{
"name": "microbit-dal",
"version": "1.3.6",
"version": "1.3.10",
"license": "Apache2",
"description": "The runtime library for the BBC micro:bit, developed by Lancaster University",
"keywords": [

View File

@ -28,6 +28,7 @@ set(YOTTA_AUTO_MICROBIT-DAL_CPP_FILES
"MicroBitSerial.cpp"
"MicroBitHeapAllocator.cpp"
"MicroBitListener.cpp"
"RefCounted.cpp"
"MemberFunctionCallback.cpp"
"ble-services/MicroBitBLEManager.cpp"
"ble-services/MicroBitDFUService.cpp"

View File

@ -3,6 +3,7 @@
#include "mbed.h"
#include "MicroBit.h"
static const char empty[] __attribute__ ((aligned (4))) = "\xff\xff\0\0\0";
/**
* Internal constructor helper.
@ -10,11 +11,7 @@
*/
void ManagedString::initEmpty()
{
data = ManagedString::EmptyString.data;
ref = ManagedString::EmptyString.ref;
len = ManagedString::EmptyString.len;
(*ref)++;
ptr = (StringData*)(void*)empty;
}
/**
@ -25,13 +22,41 @@ void ManagedString::initString(const char *str)
{
// Initialise this ManagedString as a new string, using the data provided.
// We assume the string is sane, and null terminated.
len = strlen(str);
data = (char *) malloc(len+1);
memcpy(data, str, len+1);
ref = (int16_t *) malloc(sizeof(int16_t));
*ref = 1;
int len = strlen(str);
ptr = (StringData *) malloc(4+len+1);
ptr->init();
ptr->len = len;
memcpy(ptr->data, str, len+1);
}
/**
* Constructor.
* Create a managed string from a specially prepared string literal. It will ptr->incr().
*
* @param ptr The literal - first two bytes should be 0xff, then the length in little endian, then the literal. The literal has to be 4-byte aligned.
*
* Example:
* @code
* static const char hello[] __attribute__ ((aligned (4))) = "\xff\xff\x05\x00" "Hello";
* ManagedString s((StringData*)(void*)hello);
* @endcode
*/
ManagedString::ManagedString(StringData *p)
{
ptr = p;
ptr->incr();
}
/**
* Get current ptr, do not decr() it, and set the current instance to empty string.
* This is to be used by specialized runtimes which pass StringData around.
*/
StringData* ManagedString::leakData()
{
StringData *res = ptr;
initEmpty();
return res;
}
/**
* Constructor.
@ -65,7 +90,6 @@ ManagedString::ManagedString(const int value)
*/
ManagedString::ManagedString(const char value)
{
char str[2] = {value, 0};
initString(str);
}
@ -82,7 +106,7 @@ ManagedString::ManagedString(const char value)
ManagedString::ManagedString(const char *str)
{
// Sanity check. Return EmptyString for anything distasteful
if ((str == NULL || *str == 0) && this != &ManagedString::EmptyString)
if (str == NULL || *str == 0)
{
initEmpty();
return;
@ -94,19 +118,17 @@ ManagedString::ManagedString(const char *str)
ManagedString::ManagedString(const ManagedString &s1, const ManagedString &s2)
{
// Calculate length of new string.
len = s1.len + s2.len;
int len = s1.length() + s2.length();
// Create a new buffer for holding the new string data.
data = (char *) malloc(len+1);
ptr = (StringData*) malloc(4+len+1);
ptr->init();
ptr->len = len;
// Enter the data, and terminate the string.
memcpy(data, s1.data, s1.len);
memcpy(data + s1.len, s2.data, s2.len);
data[len] = 0;
// Initialise the ref count and we're done.
ref = (int16_t *) malloc(sizeof(int16_t));
*ref = 1;
memcpy(ptr->data, s1.toCharArray(), s1.length());
memcpy(ptr->data + s1.length(), s2.toCharArray(), s2.length());
ptr->data[len] = 0;
}
@ -133,17 +155,14 @@ ManagedString::ManagedString(const char *str, const int16_t length)
return;
}
// Store the length of the new string
len = length;
// Allocate a new buffer, and create a NULL terminated string.
data = (char *) malloc(len+1);
memcpy(data, str, len);
data[len] = 0;
// Initialize a refcount and we're done.
ref = (int16_t *) malloc(sizeof(int16_t));
*ref = 1;
ptr = (StringData*) malloc(4+length+1);
ptr->init();
// Store the length of the new string
ptr->len = length;
memcpy(ptr->data, str, length);
ptr->data[length] = 0;
}
/**
@ -161,11 +180,8 @@ ManagedString::ManagedString(const char *str, const int16_t length)
*/
ManagedString::ManagedString(const ManagedString &s)
{
data = s.data;
ref = s.ref;
len = s.len;
(*ref)++;
ptr = s.ptr;
ptr->incr();
}
@ -193,11 +209,7 @@ ManagedString::ManagedString()
*/
ManagedString::~ManagedString()
{
if(--(*ref) == 0)
{
free(data);
free(ref);
}
ptr->decr();
}
/**
@ -220,19 +232,12 @@ ManagedString::~ManagedString()
*/
ManagedString& ManagedString::operator = (const ManagedString& s)
{
if(this == &s)
if (this->ptr == s.ptr)
return *this;
if(--(*ref) == 0)
{
free(data);
free(ref);
}
data = s.data;
ref = s.ref;
len = s.len;
(*ref)++;
ptr->decr();
ptr = s.ptr;
ptr->incr();
return *this;
}
@ -258,7 +263,7 @@ ManagedString& ManagedString::operator = (const ManagedString& s)
*/
bool ManagedString::operator== (const ManagedString& s)
{
return ((len == s.len) && (memcmp(data,s.data,len)==0));
return ((length() == s.length()) && (strcmp(toCharArray(),s.toCharArray())==0));
}
/**
@ -282,7 +287,7 @@ bool ManagedString::operator== (const ManagedString& s)
*/
bool ManagedString::operator< (const ManagedString& s)
{
return (memcmp(data, s.data,min(len,s.len))<0);
return (strcmp(toCharArray(), s.toCharArray())<0);
}
/**
@ -306,7 +311,7 @@ bool ManagedString::operator< (const ManagedString& s)
*/
bool ManagedString::operator> (const ManagedString& s)
{
return (memcmp(data, s.data,min(len,s.len))>0);
return (strcmp(toCharArray(), s.toCharArray())>0);
}
/**
@ -326,14 +331,14 @@ bool ManagedString::operator> (const ManagedString& s)
ManagedString ManagedString::substring(int16_t start, int16_t length)
{
// If the parameters are illegal, just return a reference to the empty string.
if (start >= len)
if (start >= this->length())
return ManagedString(ManagedString::EmptyString);
// Compute a safe copy length;
length = min(len-start, length);
length = min(this->length()-start, length);
// Build a ManagedString from this.
return ManagedString(data+start, length);
return ManagedString(toCharArray()+start, length);
}
/**
@ -353,19 +358,13 @@ ManagedString ManagedString::substring(int16_t start, int16_t length)
ManagedString ManagedString::operator+ (ManagedString& s)
{
// If the other string is empty, nothing to do!
if(s.len == 0)
if(s.length() == 0)
return *this;
if (len == 0)
if (length() == 0)
return s;
if(s == ManagedString::EmptyString)
return *this;
if(*this == ManagedString::EmptyString)
return s;
return ManagedString(data, s.data);
return ManagedString(*this, s);
}
@ -384,39 +383,10 @@ ManagedString ManagedString::operator+ (ManagedString& s)
*/
char ManagedString::charAt(int16_t index)
{
return (index >=0 && index < len) ? data[index] : 0;
}
/**
* Provides an immutable 8 bit wide haracter buffer representing this string.
*
* @return a pointer to the charcter buffer.
*/
const char *ManagedString::toCharArray()
{
return data;
}
/**
* Determines the length of this ManagedString in characters.
*
* @return the length of the string in characters.
*
* Example:
* @code
* ManagedString s("abcd");
*
* print(s.length()) // prints "4"
* @endcode
*/
int16_t ManagedString::length()
{
return len;
return (index >=0 && index < length()) ? ptr->data[index] : 0;
}
/**
* Empty string constant literal
*/
ManagedString ManagedString::EmptyString("\0");
ManagedString ManagedString::EmptyString((StringData*)(void*)empty);

View File

@ -39,6 +39,13 @@ microbit_reset()
NVIC_SystemReset();
}
void bleDisconnectionCallback(const Gap::DisconnectionCallbackParams_t *reason)
{
(void) reason; /* -Wunused-param */
uBit.ble->startAdvertising();
}
/**
* Constructor.
@ -174,7 +181,7 @@ ManagedString MicroBit::getSerial()
ManagedString s1 = ManagedString(n1);
ManagedString s2 = ManagedString(n2);
return s1+s2;
return s1 + s2;
}
/**
@ -242,17 +249,44 @@ int MicroBit::sleep(int milliseconds)
*/
int MicroBit::random(int max)
{
uint32_t m, result;
//return MICROBIT_INVALID_VALUE if max is <= 0...
if(max <= 0)
return MICROBIT_INVALID_PARAMETER;
// Cycle the LFSR (Linear Feedback Shift Register).
// We use an optimal sequence with a period of 2^32-1, as defined by Bruce Schneider here (a true legend in the field!),
// For those interested, it's documented in his paper:
// "Pseudo-Random Sequence Generator for 32-Bit CPUs: A fast, machine-independent generator for 32-bit Microprocessors"
// Our maximum return value is actually one less than passed
max--;
randomValue = ((((randomValue >> 31) ^ (randomValue >> 6) ^ (randomValue >> 4) ^ (randomValue >> 2) ^ (randomValue >> 1) ^ randomValue) & 0x0000001) << 31 ) | (randomValue >> 1);
return randomValue % max;
do {
m = (uint32_t)max;
result = 0;
do {
// Cycle the LFSR (Linear Feedback Shift Register).
// We use an optimal sequence with a period of 2^32-1, as defined by Bruce Schneier here (a true legend in the field!),
// For those interested, it's documented in his paper:
// "Pseudo-Random Sequence Generator for 32-Bit CPUs: A fast, machine-independent generator for 32-bit Microprocessors"
// https://www.schneier.com/paper-pseudorandom-sequence.html
uint32_t rnd = randomValue;
rnd = ((((rnd >> 31)
^ (rnd >> 6)
^ (rnd >> 4)
^ (rnd >> 2)
^ (rnd >> 1)
^ rnd)
& 0x0000001)
<< 31 )
| (rnd >> 1);
randomValue = rnd;
result = ((result << 1) | (rnd & 0x00000001));
} while(m >>= 1);
} while (result > (uint32_t)max);
return result;
}

View File

@ -126,7 +126,7 @@ void MicroBitDisplay::render()
y = height - 1 - t;
}
if(image.bitmap[y*(width*2)+x])
if(image.getBitmap()[y*(width*2)+x])
coldata |= (1 << i);
}
@ -175,7 +175,7 @@ void MicroBitDisplay::renderGreyscale()
y = height - 1 - t;
}
if(min(image.bitmap[y * (width * 2) + x],brightness) & greyscaleBitMsk)
if(min(image.getBitmap()[y * (width * 2) + x],brightness) & greyscaleBitMsk)
coldata |= (1 << i);
}
//write the new bit pattern

View File

@ -77,7 +77,7 @@ void microbit_heap_print(HeapDefinition &heap)
while (block < heap.heap_end)
{
blockSize = *block & ~MICROBIT_HEAP_BLOCK_FREE;
uBit.serialpc.printf("[%C:%d] ", *block & MICROBIT_HEAP_BLOCK_FREE ? 'F' : 'U', blockSize*4);
uBit.serial.printf("[%C:%d] ", *block & MICROBIT_HEAP_BLOCK_FREE ? 'F' : 'U', blockSize*4);
if (cols++ == 20)
{
uBit.serial.printf("\n");
@ -332,7 +332,7 @@ void *microbit_malloc(size_t size)
if (p != NULL)
{
#if CONFIG_ENABLED(MICROBIT_DBG) && CONFIG_ENABLED(MICROBIT_HEAP_DBG)
pc.uBit.serial("microbit_malloc: ALLOCATED: %d [%p]\n", size, p);
uBit.serial.printf("microbit_malloc: ALLOCATED: %d [%p]\n", size, p);
#endif
return p;
}
@ -348,7 +348,7 @@ void *microbit_malloc(size_t size)
#if CONFIG_ENABLED(MICROBIT_DBG) && CONFIG_ENABLED(MICROBIT_HEAP_DBG)
// Keep everything trasparent if we've not been initialised yet
if (microbit_active_heaps())
pc.uBit.serial("microbit_malloc: NATIVE ALLOCATED: %d [%p]\n", size, p);
uBit.serial.printf("microbit_malloc: NATIVE ALLOCATED: %d [%p]\n", size, p);
#endif
return p;
}
@ -357,7 +357,7 @@ void *microbit_malloc(size_t size)
#if CONFIG_ENABLED(MICROBIT_DBG) && CONFIG_ENABLED(MICROBIT_HEAP_DBG)
// Keep everything trasparent if we've not been initialised yet
if (microbit_active_heaps())
pc.uBit.serial("microbit_malloc: OUT OF MEMORY\n");
uBit.serial.printf("microbit_malloc: OUT OF MEMORY\n");
#endif
#if CONFIG_ENABLED(MICROBIT_PANIC_HEAP_FULL)
@ -378,7 +378,7 @@ void microbit_free(void *mem)
#if CONFIG_ENABLED(MICROBIT_DBG) && CONFIG_ENABLED(MICROBIT_HEAP_DBG)
if (microbit_active_heaps())
pc.uBit.serial("microbit_free: %p\n", mem);
uBit.serial.printf("microbit_free: %p\n", mem);
#endif
// Sanity check.
if (memory == NULL)

View File

@ -7,10 +7,12 @@
#include "MicroBit.h"
/*
* The null image. We actally create a small one byte buffer here, just to keep NULL pointers out of the equation.
*/
MicroBitImage MicroBitImage::EmptyImage(1,1);
static const uint16_t empty[] __attribute__ ((aligned (4))) = { 0xffff, 1, 1, 0, };
MicroBitImage MicroBitImage::EmptyImage((ImageData*)(void*)empty);
/**
* Default Constructor.
@ -65,12 +67,8 @@ MicroBitImage::MicroBitImage(const int16_t x, const int16_t y)
*/
MicroBitImage::MicroBitImage(const MicroBitImage &image)
{
bitmap = image.bitmap;
width = image.width;
height = image.height;
ref = image.ref;
(*ref)++;
ptr = image.ptr;
ptr->incr();
}
/**
@ -138,17 +136,12 @@ MicroBitImage::MicroBitImage(const char *s)
parseReadPtr++;
}
// Store the geomtery.
this->width = width;
this->height = height;
this->bitmap = (uint8_t *) malloc(width * height);
this->ref = (int16_t *) malloc(sizeof(int16_t));
*ref = 1;
this->init(width, height, NULL);
// Second pass: collect the data.
parseReadPtr = s;
parseWritePtr = parseBuf;
bitmapPtr = this->bitmap;
bitmapPtr = this->getBitmap();
while (*parseReadPtr)
{
@ -172,6 +165,36 @@ MicroBitImage::MicroBitImage(const char *s)
}
}
/**
* Constructor.
* Create an image from a specially prepared constant array, with no copying. Will call ptr->incr().
*
* @param ptr The literal - first two bytes should be 0xff, then width, 0, height, 0, and the bitmap. Width and height are 16 bit. The literal has to be 4-byte aligned.
*
* Example:
* @code
* static const uint8_t heart[] __attribute__ ((aligned (4))) = { 0xff, 0xff, 10, 0, 5, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart
* MicroBitImage i((ImageData*)(void*)heart);
* @endcode
*/
MicroBitImage::MicroBitImage(ImageData *p)
{
ptr = p;
ptr->incr();
}
/**
* Get current ptr, do not decr() it, and set the current instance to empty image.
* This is to be used by specialized runtimes which pass ImageData around.
*/
ImageData *MicroBitImage::leakData()
{
ImageData* res = ptr;
init_empty();
return res;
}
/**
* Constructor.
* Create a bitmap representation of a given size, based on a given buffer.
@ -197,11 +220,7 @@ MicroBitImage::MicroBitImage(const int16_t x, const int16_t y, const uint8_t *bi
*/
MicroBitImage::~MicroBitImage()
{
if(--(*ref) == 0)
{
free(bitmap);
free(ref);
}
ptr->decr();
}
/**
@ -209,12 +228,7 @@ MicroBitImage::~MicroBitImage()
*/
void MicroBitImage::init_empty()
{
bitmap = MicroBitImage::EmptyImage.bitmap;
width = MicroBitImage::EmptyImage.width;
height = MicroBitImage::EmptyImage.height;
ref = MicroBitImage::EmptyImage.ref;
(*ref)++;
ptr = (ImageData*)(void*)empty;
}
/**
@ -233,22 +247,20 @@ void MicroBitImage::init(const int16_t x, const int16_t y, const uint8_t *bitmap
return;
}
// Create a copy of the array
this->width = x;
this->height = y;
ptr = (ImageData*)malloc(sizeof(ImageData) + x * y);
ptr->init();
ptr->width = x;
ptr->height = y;
// create a linear buffer to represent the image. We could use a jagged/2D array here, but experimentation
// showed this had a negative effect on memory management (heap fragmentation etc).
this->bitmap = (uint8_t *) malloc(width*height);
if (bitmap)
this->printImage(x,y,bitmap);
else
this->clear();
ref = (int16_t *) malloc(sizeof(int16_t));
*ref = 1;
}
/**
@ -271,21 +283,12 @@ void MicroBitImage::init(const int16_t x, const int16_t y, const uint8_t *bitmap
*/
MicroBitImage& MicroBitImage::operator = (const MicroBitImage& i)
{
if(this == &i)
if(ptr == i.ptr)
return *this;
if(--(*ref) == 0)
{
free(bitmap);
free(ref);
}
bitmap = i.bitmap;
width = i.width;
height = i.height;
ref = i.ref;
(*ref)++;
ptr->decr();
ptr = i.ptr;
ptr->incr();
return *this;
}
@ -309,10 +312,10 @@ MicroBitImage& MicroBitImage::operator = (const MicroBitImage& i)
*/
bool MicroBitImage::operator== (const MicroBitImage& i)
{
if (bitmap == i.bitmap)
if (ptr == i.ptr)
return true;
else
return ((width == i.width) && (height == i.height) && (memcmp(bitmap, i.bitmap,width*height)==0));
return (ptr->width == i.ptr->width && ptr->height == i.ptr->height && (memcmp(getBitmap(), i.ptr->data, getSize())==0));
}
@ -327,7 +330,7 @@ bool MicroBitImage::operator== (const MicroBitImage& i)
*/
void MicroBitImage::clear()
{
memclr(this->bitmap, width*height);
memclr(getBitmap(), getSize());
}
/**
@ -346,10 +349,10 @@ void MicroBitImage::clear()
int MicroBitImage::setPixelValue(int16_t x , int16_t y, uint8_t value)
{
//sanity check
if(x >= width || y >= height || x < 0 || y < 0)
if(x >= getWidth() || y >= getHeight() || x < 0 || y < 0)
return MICROBIT_INVALID_PARAMETER;
this->bitmap[y*width+x] = value;
this->getBitmap()[y*getWidth()+x] = value;
return MICROBIT_OK;
}
@ -369,10 +372,10 @@ int MicroBitImage::setPixelValue(int16_t x , int16_t y, uint8_t value)
int MicroBitImage::getPixelValue(int16_t x , int16_t y)
{
//sanity check
if(x >= width || y >= height || x < 0 || y < 0)
if(x >= getWidth() || y >= getHeight() || x < 0 || y < 0)
return MICROBIT_INVALID_PARAMETER;
return this->bitmap[y*width+x];
return this->getBitmap()[y*getWidth()+x];
}
/**
@ -403,18 +406,18 @@ int MicroBitImage::printImage(int16_t width, int16_t height, const uint8_t *bitm
return MICROBIT_INVALID_PARAMETER;
// Calcualte sane start pointer.
pixelsToCopyX = min(width,this->width);
pixelsToCopyY = min(height,this->height);
pixelsToCopyX = min(width,this->getWidth());
pixelsToCopyY = min(height,this->getHeight());
pIn = bitmap;
pOut = this->bitmap;
pOut = this->getBitmap();
// Copy the image, stride by stride.
for (int i=0; i<pixelsToCopyY; i++)
{
memcpy(pOut, pIn, pixelsToCopyX);
pIn += width;
pOut += this->width;
pOut += this->getWidth();
}
return MICROBIT_OK;
@ -445,21 +448,21 @@ int MicroBitImage::paste(const MicroBitImage &image, int16_t x, int16_t y, uint8
// Sanity check.
// We permit writes that overlap us, but ones that are clearly out of scope we can filter early.
if (x >= width || y >= height || x+image.width <= 0 || y+image.height <= 0)
if (x >= getWidth() || y >= getHeight() || x+image.getWidth() <= 0 || y+image.getHeight() <= 0)
return 0;
//Calculate the number of byte we need to copy in each dimension.
cx = x < 0 ? min(image.width + x, width) : min(image.width, width - x);
cy = y < 0 ? min(image.height + y, height) : min(image.height, height - y);
cx = x < 0 ? min(image.getWidth() + x, getWidth()) : min(image.getWidth(), getWidth() - x);
cy = y < 0 ? min(image.getHeight() + y, getHeight()) : min(image.getHeight(), getHeight() - y);
// Calculate sane start pointer.
pIn = image.bitmap;
pIn = image.ptr->data;
pIn += (x < 0) ? -x : 0;
pIn += (y < 0) ? -image.width*y : 0;
pIn += (y < 0) ? -image.getWidth()*y : 0;
pOut = bitmap;
pOut = getBitmap();
pOut += (x > 0) ? x : 0;
pOut += (y > 0) ? width*y : 0;
pOut += (y > 0) ? getWidth()*y : 0;
// Copy the image, stride by stride
// If we want primitive transparecy, we do this byte by byte.
@ -478,8 +481,8 @@ int MicroBitImage::paste(const MicroBitImage &image, int16_t x, int16_t y, uint8
}
}
pIn += image.width;
pOut += width;
pIn += image.getWidth();
pOut += getWidth();
}
}
else
@ -489,8 +492,8 @@ int MicroBitImage::paste(const MicroBitImage &image, int16_t x, int16_t y, uint8
memcpy(pOut, pIn, cx);
pxWritten += cx;
pIn += image.width;
pOut += width;
pIn += image.getWidth();
pOut += getWidth();
}
}
@ -519,7 +522,7 @@ int MicroBitImage::print(char c, int16_t x, int16_t y)
MicroBitFont font = uBit.display.getFont();
// Sanity check. Silently ignore anything out of bounds.
if (x >= width || y >= height || c < MICROBIT_FONT_ASCII_START || c > font.asciiEnd)
if (x >= getWidth() || y >= getHeight() || c < MICROBIT_FONT_ASCII_START || c > font.asciiEnd)
return MICROBIT_INVALID_PARAMETER;
// Paste.
@ -539,8 +542,8 @@ int MicroBitImage::print(char c, int16_t x, int16_t y)
// Update our X co-ord write position
x1 = x+col;
if (x1 < width && y1 < height)
this->bitmap[y1*width+x1] = (v & (0x10 >> col)) ? 255 : 0;
if (x1 < getWidth() && y1 < getHeight())
this->getBitmap()[y1*getWidth()+x1] = (v & (0x10 >> col)) ? 255 : 0;
}
}
@ -563,24 +566,24 @@ int MicroBitImage::print(char c, int16_t x, int16_t y)
*/
int MicroBitImage::shiftLeft(int16_t n)
{
uint8_t *p = bitmap;
int pixels = width-n;
uint8_t *p = getBitmap();
int pixels = getWidth()-n;
if (n <= 0 )
return MICROBIT_INVALID_PARAMETER;
if(n >= width)
if(n >= getWidth())
{
clear();
return MICROBIT_OK;
}
for (int y = 0; y < height; y++)
for (int y = 0; y < getHeight(); y++)
{
// Copy, and blank fill the rightmost column.
memcpy(p, p+n, pixels);
memclr(p+pixels, n);
p += width;
p += getWidth();
}
return MICROBIT_OK;
@ -602,24 +605,24 @@ int MicroBitImage::shiftLeft(int16_t n)
*/
int MicroBitImage::shiftRight(int16_t n)
{
uint8_t *p = bitmap;
int pixels = width-n;
uint8_t *p = getBitmap();
int pixels = getWidth()-n;
if (n <= 0)
return MICROBIT_INVALID_PARAMETER;
if(n >= width)
if(n >= getWidth())
{
clear();
return MICROBIT_OK;
}
for (int y = 0; y < height; y++)
for (int y = 0; y < getHeight(); y++)
{
// Copy, and blank fill the leftmost column.
memmove(p+n, p, pixels);
memclr(p, n);
p += width;
p += getWidth();
}
return MICROBIT_OK;
@ -646,25 +649,25 @@ int MicroBitImage::shiftUp(int16_t n)
if (n <= 0 )
return MICROBIT_INVALID_PARAMETER;
if(n >= height)
if(n >= getHeight())
{
clear();
return MICROBIT_OK;
}
pOut = bitmap;
pIn = bitmap+width*n;
pOut = getBitmap();
pIn = getBitmap()+getWidth()*n;
for (int y = 0; y < height; y++)
for (int y = 0; y < getHeight(); y++)
{
// Copy, and blank fill the leftmost column.
if (y < height-n)
memcpy(pOut, pIn, width);
if (y < getHeight()-n)
memcpy(pOut, pIn, getWidth());
else
memclr(pOut, width);
memclr(pOut, getWidth());
pIn += width;
pOut += width;
pIn += getWidth();
pOut += getWidth();
}
return MICROBIT_OK;
@ -691,63 +694,30 @@ int MicroBitImage::shiftDown(int16_t n)
if (n <= 0 )
return MICROBIT_INVALID_PARAMETER;
if(n >= height)
if(n >= getHeight())
{
clear();
return MICROBIT_OK;
}
pOut = bitmap + width*(height-1);
pIn = pOut - width*n;
pOut = getBitmap() + getWidth()*(getHeight()-1);
pIn = pOut - getWidth()*n;
for (int y = 0; y < height; y++)
for (int y = 0; y < getHeight(); y++)
{
// Copy, and blank fill the leftmost column.
if (y < height-n)
memcpy(pOut, pIn, width);
if (y < getHeight()-n)
memcpy(pOut, pIn, getWidth());
else
memclr(pOut, width);
memclr(pOut, getWidth());
pIn -= width;
pOut -= width;
pIn -= getWidth();
pOut -= getWidth();
}
return MICROBIT_OK;
}
/**
* Gets the width of this image.
*
* @return The width of this image.
*
* Example:
* @code
* const uint8_t heart[] = { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart
* MicroBitImage i(10,5,heart);
* i.getWidth(); //equals 10...
* @endcode
*/
int MicroBitImage::getWidth()
{
return width;
}
/**
* Gets the height of this image.
*
* @return The height of this image.
*
* Example:
* @code
* const uint8_t heart[] = { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart
* MicroBitImage i(10,5,heart);
* i.getHeight(); //equals 5...
* @endcode
*/
int MicroBitImage::getHeight()
{
return height;
}
/**
* Converts the bitmap to a csv string.
@ -762,14 +732,14 @@ int MicroBitImage::getHeight()
ManagedString MicroBitImage::toString()
{
//width including commans and \n * height
int stringSize = ((width * 2) * height);
int stringSize = getSize() * 2;
//plus one for string terminator
char parseBuffer[stringSize + 1];
parseBuffer[stringSize] = '\0';
uint8_t *bitmapPtr = bitmap;
uint8_t *bitmapPtr = getBitmap();
int parseIndex = 0;
int widthCount = 0;
@ -783,7 +753,7 @@ ManagedString MicroBitImage::toString()
parseIndex++;
if(widthCount == width-1)
if(widthCount == getWidth()-1)
{
parseBuffer[parseIndex] = '\n';
widthCount = 0;
@ -821,17 +791,17 @@ MicroBitImage MicroBitImage::crop(int startx, int starty, int cropWidth, int cro
int newWidth = startx + cropWidth;
int newHeight = starty + cropHeight;
if (newWidth >= width || newWidth <=0)
newWidth = width;
if (newWidth >= getWidth() || newWidth <=0)
newWidth = getWidth();
if (newHeight >= height || newHeight <= 0)
newHeight = height;
if (newHeight >= getHeight() || newHeight <= 0)
newHeight = getHeight();
//allocate our storage.
uint8_t cropped[newWidth * newHeight];
//calculate the pointer to where we want to begin cropping
uint8_t *copyPointer = bitmap + (width * starty) + startx;
uint8_t *copyPointer = getBitmap() + (getWidth() * starty) + startx;
//get a reference to our storage
uint8_t *pastePointer = cropped;
@ -841,9 +811,27 @@ MicroBitImage MicroBitImage::crop(int startx, int starty, int cropWidth, int cro
{
memcpy(pastePointer, copyPointer, newWidth);
copyPointer += width;
copyPointer += getWidth();
pastePointer += newHeight;
}
return MicroBitImage(newWidth, newHeight, cropped);
}
/**
* Check if image is read-only (i.e., residing in flash).
*/
bool MicroBitImage::isReadOnly()
{
return ptr->isReadOnly();
}
/**
* Create a copy of the image bitmap. Used particularly, when isReadOnly() is true.
*
* @return an instance of MicroBitImage which can be modified independently of the current instance
*/
MicroBitImage MicroBitImage::clone()
{
return MicroBitImage(getWidth(), getHeight(), getBitmap());
}

View File

@ -47,13 +47,12 @@ int main()
if (i == 10)
{
// Bring up the BLE stack if it isn't alredy done.
if (!uBit.ble)
uBit.bleManager.init(uBit.getName(), uBit.getSerial());
// Bring up the BLE stack if it isn't alredy done.
if (!uBit.ble)
uBit.bleManager.init(uBit.getName(), uBit.getSerial());
// Enter pairing mode, using the LED matrix for any necessary pairing operations
uBit.bleManager.pairingMode(uBit.display);
uBit.bleManager.pairingMode(uBit.display);
}
}
#endif

46
source/RefCounted.cpp Normal file
View File

@ -0,0 +1,46 @@
#include "mbed.h"
#include "MicroBit.h"
void RefCounted::init()
{
// Initialize to one reference (lowest bit set to 1)
refCount = 3;
}
static inline bool isReadOnlyInline(RefCounted *t)
{
uint32_t refCount = t->refCount;
if (refCount == 0xffff)
return true; // object in flash
// Do some sanity checking while we're here
if (refCount == 1 || // object should have been deleted
(refCount & 1) == 0) // refCount doesn't look right
uBit.panic(MICROBIT_HEAP_ERROR);
// Not read only
return false;
}
bool RefCounted::isReadOnly()
{
return isReadOnlyInline(this);
}
void RefCounted::incr()
{
if (!isReadOnlyInline(this))
refCount += 2;
}
void RefCounted::decr()
{
if (isReadOnlyInline(this))
return;
refCount -= 2;
if (refCount == 1) {
free(this);
}
}

View File

@ -1,12 +1,12 @@
/**
* Class definition for a MicroBit Device Firmware Update loader.
*
* This is actually just a frontend to a memory resident nordic DFU loader.
*
* We rely on the BLE standard pairing processes to provide encryption and authentication.
* We assume any device that is paied with the micro:bit is authorized to reprogram the device.
*
*/
* Class definition for a MicroBit Device Firmware Update loader.
*
* This is actually just a frontend to a memory resident nordic DFU loader.
*
* We rely on the BLE standard pairing processes to provide encryption and authentication.
* We assume any device that is paied with the micro:bit is authorized to reprogram the device.
*
*/
#include "MicroBit.h"
#include "ble/UUID.h"
@ -36,16 +36,16 @@ extern "C" {
/**
* Constructor.
* Create a representation of a MicroBit device.
* @param messageBus callback function to receive MicroBitMessageBus events.
*/
* Constructor.
* Create a representation of a MicroBit device.
* @param messageBus callback function to receive MicroBitMessageBus events.
*/
MicroBitDFUService::MicroBitDFUService(BLEDevice &_ble) :
ble(_ble)
ble(_ble)
{
// Opcodes can be issued here to control the MicroBitDFU Service, as defined above.
GattCharacteristic microBitDFUServiceControlCharacteristic(MicroBitDFUServiceControlCharacteristicUUID, &controlByte, 0, sizeof(uint8_t),
GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE);
GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE);
controlByte = 0x00;
@ -66,41 +66,41 @@ MicroBitDFUService::MicroBitDFUService(BLEDevice &_ble) :
/**
* Callback. Invoked when any of our attributes are written via BLE.
*/
* Callback. Invoked when any of our attributes are written via BLE.
*/
void MicroBitDFUService::onDataWritten(const GattWriteCallbackParams *params)
{
if (params->handle == microBitDFUServiceControlCharacteristicHandle)
{
if(params->len > 0 && params->data[0] == MICROBIT_DFU_OPCODE_START_DFU)
{
uBit.display.stopAnimation();
{
uBit.display.stopAnimation();
uBit.display.clear();
#if CONFIG_ENABLED(MICROBIT_DBG)
uBit.serial.printf(" ACTIVATING BOOTLOADER.\n");
#endif
// Perform an explicit disconnection to assist our peer to reconnect to the DFU service
ble.disconnect(Gap::LOCAL_HOST_TERMINATED_CONNECTION);
// Perform an explicit disconnection to assist our peer to reconnect to the DFU service
ble.disconnect(Gap::LOCAL_HOST_TERMINATED_CONNECTION);
// Call bootloader_start implicitly trough a event handler call
// it is a work around for bootloader_start not being public in sdk 8.1
ble_dfu_t p_dfu;
ble_dfu_evt_t p_evt;
// Call bootloader_start implicitly trough a event handler call
// it is a work around for bootloader_start not being public in sdk 8.1
ble_dfu_t p_dfu;
ble_dfu_evt_t p_evt;
p_dfu.conn_handle = params->connHandle;
p_evt.ble_dfu_evt_type = BLE_DFU_START;
p_dfu.conn_handle = params->connHandle;
p_evt.ble_dfu_evt_type = BLE_DFU_START;
dfu_app_on_dfu_evt(&p_dfu, &p_evt);
}
}
dfu_app_on_dfu_evt(&p_dfu, &p_evt);
}
}
}
/**
* UUID definitions for BLE Services and Characteristics.
*/
* UUID definitions for BLE Services and Characteristics.
*/
const uint8_t MicroBitDFUServiceUUID[] = {
0xe9,0x5d,0x93,0xb0,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8