├── Makefile ├── generated └── metainfo.json ├── bump.sh ├── run.sh ├── .gitignore ├── .vscode ├── settings.json └── tasks.json ├── .gitattributes ├── README.md ├── module.json ├── LICENSE ├── inc ├── ManagedBuffer.h └── pxt.h └── source ├── ManagedBuffer.cpp └── pxt.cpp /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | yotta build 3 | -------------------------------------------------------------------------------- /generated/metainfo.json: -------------------------------------------------------------------------------- 1 | { 2 | "functions": [], 3 | "enums": {} 4 | } 5 | -------------------------------------------------------------------------------- /bump.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -x 4 | set -e 5 | yotta version patch 6 | git push --tags 7 | git push 8 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -x 4 | set -e 5 | mkdir -p build 6 | yotta target bbc-microbit-classic-gcc 7 | yotta update 8 | yotta build 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .yotta.json 2 | build 3 | *.swp 4 | *.log 5 | test.cpp 6 | yotta_modules 7 | yotta_targets 8 | Makefile.local 9 | ext 10 | upload.tar.gz -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.autoSave": "afterDelay", 4 | "editor.formatOnType": true, 5 | "search.exclude": { 6 | "yotta_modules/**":true, 7 | "yotta_targets/**":true, 8 | "build":true, 9 | "generated":true 10 | } 11 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.1.0", 3 | "command": "yotta", 4 | "isShellCommand": true, 5 | "args": ["--target","bbc-microbit-classic-gcc"], 6 | "tasks": [ 7 | { 8 | "taskName": "build", 9 | "args": [], 10 | "isBuildCommand": true, 11 | "problemMatcher": "$msCompile" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # enforce unix style line endings 2 | *.ts text eol=lf 3 | *.md text eol=lf 4 | *.txt text eol=lf 5 | *.js text eol=lf 6 | *.json text eol=lf 7 | *.xml text eol=lf 8 | *.svg text eol=lf 9 | *.yaml text eol=lf 10 | *.css text eol=lf 11 | *.html text eol=lf 12 | *.py text eol=lf 13 | *.inc text eol=lf 14 | *.cpp text eol=lf 15 | *.h text eol=lf 16 | 17 | # do not enforce text for everything - it causes issues with random binary files 18 | 19 | *.sln text eol=crlf 20 | 21 | *.png binary 22 | *.jpg binary 23 | *.jpeg binary 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Building 2 | 3 | You most likely do not need to build this package - it's built automatically 4 | by [pxt](https://pxt.io) when needed. 5 | 6 | Otherwise, instructions follow: 7 | 8 | - Install Yotta http://docs.yottabuild.org/#installing 9 | - Install [srecord](http://srecord.sourceforge.net/); add it to your path 10 | 11 | Run `./run.sh`. 12 | 13 | You cannot use local version of this package in PXT. After making changes, 14 | and making sure everything builds, you need to bump version in git 15 | using `./bump.sh` and then update version number in `pxt-microbit/pxtarget.json`. 16 | -------------------------------------------------------------------------------- /module.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pxt-microbit-core", 3 | "version": "0.5.1", 4 | "description": "The glue layer that binds together PXT programs compiled to C++ and a BBC micro:bit runtime system.", 5 | "keywords": [], 6 | "author": "Microsoft Corporation", 7 | "repository": { 8 | "url": "ssh://git@github.com:Microsoft/pxt-microbit-core.git", 9 | "type": "git" 10 | }, 11 | "homepage": "https://github.com/microsoft/pxt-microbit-core", 12 | "license": "MIT", 13 | "dependencies": { 14 | "microbit": "lancaster-university/microbit#v2.0.0-rc5" 15 | }, 16 | "targetDependencies": {}, 17 | "extraIncludes": [ 18 | "inc" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Microsoft 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | Third Party Programs: The software may include third party programs that 24 | Microsoft, not the third party, licenses to you under this agreement. 25 | Notices, if any, for the third party programs are included for your 26 | information only. 27 | -------------------------------------------------------------------------------- /inc/ManagedBuffer.h: -------------------------------------------------------------------------------- 1 | #ifndef MICROBIT_MANAGED_BUFFER_H 2 | #define MICROBIT_MANAGED_BUFFER_H 3 | 4 | #include "mbed.h" 5 | #include "RefCounted.h" 6 | 7 | struct BufferData : RefCounted 8 | { 9 | uint16_t length; // The length of the payload in bytes 10 | uint8_t payload[0]; // ManagedBuffer data 11 | }; 12 | 13 | /** 14 | * Class definition for a ManagedBuffer. 15 | * A ManagedBuffer holds a series of bytes, used with MicroBitRadio channels and in other places. 16 | * n.b. This is a mutable, managed type. 17 | */ 18 | class ManagedBuffer 19 | { 20 | BufferData *ptr; // Pointer to payload data 21 | 22 | public: 23 | 24 | /** 25 | * Default Constructor. 26 | * Creates an empty ManagedBuffer. The 'ptr' field in all empty buffers is shared. 27 | * 28 | * Example: 29 | * @code 30 | * ManagedBuffer p(); 31 | * @endcode 32 | */ 33 | ManagedBuffer(); 34 | 35 | /** 36 | * Constructor. 37 | * Creates a new ManagedBuffer of the given size. 38 | * 39 | * @param length The length of the buffer to create. 40 | * 41 | * Example: 42 | * @code 43 | * ManagedBuffer p(16); // Creates a ManagedBuffer 16 bytes long. 44 | * @endcode 45 | */ 46 | ManagedBuffer(int length); 47 | 48 | /** 49 | * Constructor. 50 | * Creates an empty ManagedBuffer of the given size, 51 | * and fills it with the data provided. 52 | * 53 | * @param data The data with which to fill the buffer. 54 | * @param length The length of the buffer to create. 55 | * 56 | * Example: 57 | * @code 58 | * uint8_t buf[] = {13,5,2}; 59 | * ManagedBuffer p(buf, 3); // Creates a ManagedBuffer 3 bytes long. 60 | * @endcode 61 | */ 62 | ManagedBuffer(uint8_t *data, int length); 63 | 64 | /** 65 | * Copy Constructor. 66 | * Add ourselves as a reference to an existing ManagedBuffer. 67 | * 68 | * @param buffer The ManagedBuffer to reference. 69 | * 70 | * Example: 71 | * @code 72 | * ManagedBuffer p(); 73 | * ManagedBuffer p2(i); // Refers to the same buffer as p. 74 | * @endcode 75 | */ 76 | ManagedBuffer(const ManagedBuffer &buffer); 77 | 78 | /** 79 | * Constructor. 80 | * Create a buffer from a raw BufferData pointer. It will ptr->incr(). This is to be used by specialized runtimes. 81 | * 82 | * @param p The pointer to use. 83 | */ 84 | ManagedBuffer(BufferData *p); 85 | 86 | /** 87 | * Internal constructor helper. 88 | * Configures this ManagedBuffer to refer to the static empty buffer. 89 | */ 90 | void initEmpty(); 91 | 92 | /** 93 | * Internal constructor-initialiser. 94 | * 95 | * @param data The data with which to fill the buffer. 96 | * @param length The length of the buffer to create. 97 | * 98 | */ 99 | void init(uint8_t *data, int length); 100 | 101 | /** 102 | * Destructor. 103 | * Removes buffer resources held by the instance. 104 | */ 105 | ~ManagedBuffer(); 106 | 107 | /** 108 | * Provide an array containing the buffer data. 109 | * @return The contents of this buffer, as an array of bytes. 110 | */ 111 | uint8_t *getBytes() 112 | { 113 | return ptr->payload; 114 | } 115 | 116 | /** 117 | * Get current ptr, do not decr() it, and set the current instance to an empty buffer. 118 | * This is to be used by specialized runtimes which pass BufferData around. 119 | */ 120 | BufferData *leakData(); 121 | 122 | /** 123 | * Copy assign operation. 124 | * 125 | * Called when one ManagedBuffer is assigned the value of another using the '=' operator. 126 | * Decrements our reference count and free up the buffer as necessary. 127 | * Then, update our buffer to refer to that of the supplied ManagedBuffer, 128 | * and increase its reference count. 129 | * 130 | * @param p The ManagedBuffer to reference. 131 | * 132 | * Example: 133 | * @code 134 | * uint8_t buf = {13,5,2}; 135 | * ManagedBuffer p1(16); 136 | * ManagedBuffer p2(buf, 3); 137 | * 138 | * p1 = p2; 139 | * @endcode 140 | */ 141 | ManagedBuffer& operator = (const ManagedBuffer& p); 142 | 143 | /** 144 | * Array access operation (read). 145 | * 146 | * Called when a ManagedBuffer is dereferenced with a [] operation. 147 | * Transparently map this through to the underlying payload for elegance of programming. 148 | * 149 | * Example: 150 | * @code 151 | * ManagedBuffer p1(16); 152 | * uint8_t data = p1[0]; 153 | * @endcode 154 | */ 155 | uint8_t operator [] (int i) const 156 | { 157 | return ptr->payload[i]; 158 | } 159 | 160 | /** 161 | * Array access operation (modify). 162 | * 163 | * Called when a ManagedBuffer is dereferenced with a [] operation. 164 | * Transparently map this through to the underlying payload for elegance of programming. 165 | * 166 | * Example: 167 | * @code 168 | * ManagedBuffer p1(16); 169 | * p1[0] = 42; 170 | * @endcode 171 | */ 172 | uint8_t& operator [] (int i) 173 | { 174 | return ptr->payload[i]; 175 | } 176 | 177 | /** 178 | * Equality operation. 179 | * 180 | * Called when one ManagedBuffer is tested to be equal to another using the '==' operator. 181 | * 182 | * @param p The ManagedBuffer to test ourselves against. 183 | * @return true if this ManagedBuffer is identical to the one supplied, false otherwise. 184 | * 185 | * Example: 186 | * @code 187 | * 188 | * uint8_t buf = {13,5,2}; 189 | * ManagedBuffer p1(16); 190 | * ManagedBuffer p2(buf, 3); 191 | * 192 | * if(p1 == p2) // will be true 193 | * uBit.display.scroll("same!"); 194 | * @endcode 195 | */ 196 | bool operator== (const ManagedBuffer& p); 197 | 198 | /** 199 | * Sets the byte at the given index to value provided. 200 | * @param position The index of the byte to change. 201 | * @param value The new value of the byte (0-255). 202 | * @return MICROBIT_OK, or MICROBIT_INVALID_PARAMETER. 203 | * 204 | * Example: 205 | * @code 206 | * ManagedBuffer p1(16); 207 | * p1.setByte(0,255); // Sets the first byte in the buffer to the value 255. 208 | * @endcode 209 | */ 210 | int setByte(int position, uint8_t value); 211 | 212 | /** 213 | * Determines the value of the given byte in the buffer. 214 | * 215 | * @param position The index of the byte to read. 216 | * @return The value of the byte at the given position, or MICROBIT_INVALID_PARAMETER. 217 | * 218 | * Example: 219 | * @code 220 | * ManagedBuffer p1(16); 221 | * p1.setByte(0,255); // Sets the first byte in the buffer to the value 255. 222 | * p1.getByte(0); // Returns 255. 223 | * @endcode 224 | */ 225 | int getByte(int position); 226 | 227 | /** 228 | * Gets number of bytes in this buffer 229 | * @return The size of the buffer in bytes. 230 | * 231 | * Example: 232 | * @code 233 | * ManagedBuffer p1(16); 234 | * p1.length(); // Returns 16. 235 | * @endcode 236 | */ 237 | int length() const { return ptr->length; } 238 | 239 | int fill(uint8_t value, int offset = 0, int length = -1); 240 | 241 | ManagedBuffer slice(int offset = 0, int length = -1) const; 242 | 243 | void shift(int offset, int start = 0, int length = -1); 244 | 245 | void rotate(int offset, int start = 0, int length = -1); 246 | 247 | int readBytes(uint8_t *dst, int offset, int length, bool swapBytes = false) const; 248 | 249 | int writeBytes(int dstOffset, uint8_t *src, int length, bool swapBytes = false); 250 | 251 | int writeBuffer(int dstOffset, const ManagedBuffer &src, int srcOffset = 0, int length = -1); 252 | 253 | bool isReadOnly() const { return ptr->isReadOnly(); } 254 | }; 255 | 256 | #endif 257 | 258 | -------------------------------------------------------------------------------- /inc/pxt.h: -------------------------------------------------------------------------------- 1 | #ifndef __PXT_H 2 | #define __PXT_H 3 | 4 | // #define DEBUG_MEMLEAKS 1 5 | 6 | #pragma GCC diagnostic ignored "-Wunused-parameter" 7 | 8 | #include "MicroBit.h" 9 | #include "MicroBitImage.h" 10 | #include "ManagedString.h" 11 | #include "ManagedType.h" 12 | 13 | #define printf(...) uBit.serial.printf(__VA_ARGS__) 14 | // #define printf(...) 15 | 16 | #define intcheck(...) check(__VA_ARGS__) 17 | //#define intcheck(...) do {} while (0) 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #ifdef DEBUG_MEMLEAKS 25 | #include 26 | #endif 27 | 28 | extern MicroBit uBit; 29 | 30 | namespace pxt { 31 | typedef uint32_t Action; 32 | typedef uint32_t ImageLiteral; 33 | 34 | 35 | typedef enum { 36 | ERR_INVALID_BINARY_HEADER = 5, 37 | ERR_OUT_OF_BOUNDS = 8, 38 | ERR_REF_DELETED = 7, 39 | ERR_SIZE = 9, 40 | } ERROR; 41 | 42 | extern const uint32_t functionsAndBytecode[]; 43 | extern uint32_t *globals; 44 | extern uint16_t *bytecode; 45 | class RefRecord; 46 | 47 | // Utility functions 48 | extern MicroBitEvent lastEvent; 49 | void registerWithDal(int id, int event, Action a); 50 | void runInBackground(Action a); 51 | uint32_t runAction3(Action a, int arg0, int arg1, int arg2); 52 | uint32_t runAction2(Action a, int arg0, int arg1); 53 | uint32_t runAction1(Action a, int arg0); 54 | uint32_t runAction0(Action a); 55 | Action mkAction(int reflen, int totallen, int startptr); 56 | void error(ERROR code, int subcode = 0); 57 | void exec_binary(uint16_t *pc); 58 | void start(); 59 | void debugMemLeaks(); 60 | // allocate [sz] words and clear them 61 | uint32_t *allocate(uint16_t sz); 62 | int templateHash(); 63 | int programHash(); 64 | int getNumGlobals(); 65 | RefRecord* mkClassInstance(int vtableOffset); 66 | 67 | // The standard calling convention is: 68 | // - when a pointer is loaded from a local/global/field etc, and incr()ed 69 | // (in other words, its presence on stack counts as a reference) 70 | // - after a function call, all pointers are popped off the stack and decr()ed 71 | // This does not apply to the RefRecord and st/ld(ref) methods - they unref() 72 | // the RefRecord* this. 73 | int incr(uint32_t e); 74 | void decr(uint32_t e); 75 | 76 | inline void *ptrOfLiteral(int offset) 77 | { 78 | return &bytecode[offset]; 79 | } 80 | 81 | inline ImageData* imageBytes(int offset) 82 | { 83 | return (ImageData*)(void*)&bytecode[offset]; 84 | } 85 | 86 | // Checks if object has a VTable, or if its RefCounted* from the runtime. 87 | inline bool hasVTable(uint32_t e) 88 | { 89 | return (*((uint32_t*)e) & 1) == 0; 90 | } 91 | 92 | inline void check(int cond, ERROR code, int subcode = 0) 93 | { 94 | if (!cond) error(code, subcode); 95 | } 96 | 97 | 98 | class RefObject; 99 | #ifdef DEBUG_MEMLEAKS 100 | extern std::set allptrs; 101 | #endif 102 | 103 | typedef void (*RefObjectMethod)(RefObject *self); 104 | typedef void *PVoid; 105 | typedef void **PPVoid; 106 | 107 | const PPVoid RefMapMarker = (PPVoid)(void*)43; 108 | 109 | struct VTable { 110 | uint16_t numbytes; // in the entire object, including the vtable pointer 111 | uint16_t userdata; 112 | PVoid *ifaceTable; 113 | PVoid methods[2]; // we only use up to two methods here; pxt will generate more 114 | // refmask sits at &methods[nummethods] 115 | }; 116 | 117 | const int vtableShift = 2; 118 | 119 | // A base abstract class for ref-counted objects. 120 | class RefObject 121 | { 122 | public: 123 | uint16_t refcnt; 124 | uint16_t vtable; 125 | 126 | RefObject(uint16_t vt) 127 | { 128 | refcnt = 2; 129 | vtable = vt; 130 | #ifdef DEBUG_MEMLEAKS 131 | allptrs.insert(this); 132 | #endif 133 | } 134 | 135 | inline VTable *getVTable() { 136 | return (VTable*)(vtable << vtableShift); 137 | } 138 | 139 | void destroy(); 140 | void print(); 141 | 142 | // Call to disable pointer tracking on the current instance (in destructor or some other hack) 143 | inline void untrack() { 144 | #ifdef DEBUG_MEMLEAKS 145 | allptrs.erase(this); 146 | #endif 147 | } 148 | 149 | // Increment/decrement the ref-count. Decrementing to zero deletes the current object. 150 | inline void ref() 151 | { 152 | check(refcnt > 0, ERR_REF_DELETED); 153 | //printf("INCR "); this->print(); 154 | refcnt += 2; 155 | } 156 | 157 | inline void unref() 158 | { 159 | //printf("DECR "); this->print(); 160 | refcnt -= 2; 161 | if (refcnt == 0) { 162 | destroy(); 163 | } 164 | } 165 | }; 166 | 167 | // A ref-counted collection of either primitive or ref-counted objects (String, Image, 168 | // user-defined record, another collection) 169 | class RefCollection 170 | : public RefObject 171 | { 172 | public: 173 | // 1 - collection of refs (need decr) 174 | // 2 - collection of strings (in fact we always have 3, never 2 alone) 175 | inline uint32_t getFlags() { return getVTable()->userdata; } 176 | inline bool isRef() { return getFlags() & 1; } 177 | inline bool isString() { return getFlags() & 2; } 178 | 179 | std::vector data; 180 | 181 | RefCollection(uint16_t f); 182 | 183 | inline bool in_range(int x) { 184 | return (0 <= x && x < (int)data.size()); 185 | } 186 | 187 | inline int length() { return data.size(); } 188 | 189 | void destroy(); 190 | void print(); 191 | 192 | void push(uint32_t x); 193 | uint32_t getAt(int x); 194 | void removeAt(int x); 195 | void setAt(int x, uint32_t y); 196 | int indexOf(uint32_t x, int start); 197 | int removeElement(uint32_t x); 198 | }; 199 | 200 | struct MapEntry { 201 | uint32_t key; 202 | uint32_t val; 203 | }; 204 | 205 | class RefMap 206 | : public RefObject 207 | { 208 | public: 209 | std::vector data; 210 | 211 | RefMap(); 212 | void destroy(); 213 | void print(); 214 | int findIdx(uint32_t key); 215 | }; 216 | 217 | // A ref-counted, user-defined JS object. 218 | class RefRecord 219 | : public RefObject 220 | { 221 | public: 222 | // The object is allocated, so that there is space at the end for the fields. 223 | uint32_t fields[]; 224 | 225 | RefRecord(uint16_t v) : RefObject(v) {} 226 | 227 | uint32_t ld(int idx); 228 | uint32_t ldref(int idx); 229 | void st(int idx, uint32_t v); 230 | void stref(int idx, uint32_t v); 231 | }; 232 | 233 | // these are needed when constructing vtables for user-defined classes 234 | void RefRecord_destroy(RefRecord *r); 235 | void RefRecord_print(RefRecord *r); 236 | 237 | class RefAction; 238 | typedef uint32_t (*ActionCB)(uint32_t *captured, uint32_t arg0, uint32_t arg1, uint32_t arg2); 239 | 240 | // Ref-counted function pointer. It's currently always a ()=>void procedure pointer. 241 | class RefAction 242 | : public RefObject 243 | { 244 | public: 245 | // This is the same as for RefRecord. 246 | uint8_t len; 247 | uint8_t reflen; 248 | ActionCB func; // The function pointer 249 | // fields[] contain captured locals 250 | uint32_t fields[]; 251 | 252 | void destroy(); 253 | void print(); 254 | 255 | RefAction(); 256 | 257 | inline void stCore(int idx, uint32_t v) 258 | { 259 | //printf("ST [%d] = %d ", idx, v); this->print(); 260 | intcheck(0 <= idx && idx < len, ERR_OUT_OF_BOUNDS, 10); 261 | intcheck(fields[idx] == 0, ERR_OUT_OF_BOUNDS, 11); // only one assignment permitted 262 | fields[idx] = v; 263 | } 264 | 265 | inline uint32_t runCore(int arg0, int arg1, int arg2) // internal; use runAction*() functions 266 | { 267 | this->ref(); 268 | uint32_t r = this->func(&this->fields[0], arg0, arg1, arg2); 269 | this->unref(); 270 | return r; 271 | } 272 | }; 273 | 274 | // These two are used to represent locals written from inside inline functions 275 | class RefLocal 276 | : public RefObject 277 | { 278 | public: 279 | uint32_t v; 280 | void destroy(); 281 | void print(); 282 | RefLocal(); 283 | }; 284 | 285 | class RefRefLocal 286 | : public RefObject 287 | { 288 | public: 289 | uint32_t v; 290 | void destroy(); 291 | void print(); 292 | RefRefLocal(); 293 | }; 294 | } 295 | 296 | // The ARM Thumb generator in the JavaScript code is parsing 297 | // the hex file and looks for the magic numbers as present here. 298 | // 299 | // Then it fetches function pointer addresses from there. 300 | 301 | #define PXT_SHIMS_BEGIN \ 302 | namespace pxt { \ 303 | const uint32_t functionsAndBytecode[] __attribute__((aligned(0x20))) = { \ 304 | 0x08010801, 0x42424242, 0x08010801, 0x8de9d83e, 305 | 306 | #define PXT_SHIMS_END }; } 307 | 308 | #pragma GCC diagnostic ignored "-Wpmf-conversions" 309 | 310 | #define PXT_VTABLE_TO_INT(vt) ((uint32_t)(vt) >> vtableShift) 311 | #define PXT_VTABLE_BEGIN(classname, flags, iface) \ 312 | const VTable classname ## _vtable \ 313 | __attribute__((aligned(1 << vtableShift))) \ 314 | = { \ 315 | sizeof(classname), \ 316 | flags, \ 317 | iface, \ 318 | { \ 319 | (void*)&classname::destroy, \ 320 | (void*)&classname::print, 321 | 322 | #define PXT_VTABLE_END } }; 323 | 324 | #define PXT_VTABLE_INIT(classname) \ 325 | RefObject(PXT_VTABLE_TO_INT(&classname ## _vtable)) 326 | 327 | #define PXT_VTABLE_CTOR(classname) \ 328 | PXT_VTABLE_BEGIN(classname, 0, 0) PXT_VTABLE_END \ 329 | classname::classname() : PXT_VTABLE_INIT(classname) 330 | 331 | #endif 332 | 333 | // vim: ts=2 sw=2 expandtab 334 | -------------------------------------------------------------------------------- /source/ManagedBuffer.cpp: -------------------------------------------------------------------------------- 1 | #include "MicroBit.h" 2 | #include "ManagedBuffer.h" 3 | #include 4 | 5 | static const char empty[] __attribute__ ((aligned (4))) = "\xff\xff\0\0\0"; 6 | 7 | /** 8 | * Internal constructor helper. 9 | * Configures this ManagedBuffer to refer to the static empty buffer. 10 | */ 11 | void ManagedBuffer::initEmpty() 12 | { 13 | ptr = (BufferData*)(void*)empty; 14 | } 15 | 16 | /** 17 | * Default Constructor. 18 | * Creates an empty ManagedBuffer. 19 | * 20 | * Example: 21 | * @code 22 | * ManagedBuffer p(); 23 | * @endcode 24 | */ 25 | ManagedBuffer::ManagedBuffer() 26 | { 27 | initEmpty(); 28 | } 29 | 30 | /** 31 | * Constructor. 32 | * Creates an empty ManagedBuffer of the given size. 33 | * 34 | * @param length The length of the buffer to create. 35 | * 36 | * Example: 37 | * @code 38 | * ManagedBuffer p(16); // Creates a ManagedBuffer 16 bytes long. 39 | * @endcode 40 | */ 41 | ManagedBuffer::ManagedBuffer(int length) 42 | { 43 | this->init(NULL, length); 44 | } 45 | 46 | /** 47 | * Constructor. 48 | * Creates a new ManagedBuffer of the given size, 49 | * and fills it with the data provided. 50 | * 51 | * @param data The data with which to fill the buffer. 52 | * @param length The length of the buffer to create. 53 | * 54 | * Example: 55 | * @code 56 | * uint8_t buf = {13,5,2}; 57 | * ManagedBuffer p(buf, 3); // Creates a ManagedBuffer 3 bytes long. 58 | * @endcode 59 | */ 60 | ManagedBuffer::ManagedBuffer(uint8_t *data, int length) 61 | { 62 | this->init(data, length); 63 | } 64 | 65 | /** 66 | * Copy Constructor. 67 | * Add ourselves as a reference to an existing ManagedBuffer. 68 | * 69 | * @param buffer The ManagedBuffer to reference. 70 | * 71 | * Example: 72 | * @code 73 | * ManagedBuffer p(); 74 | * ManagedBuffer p2(i); // Refers to the same buffer as p. 75 | * @endcode 76 | */ 77 | ManagedBuffer::ManagedBuffer(const ManagedBuffer &buffer) 78 | { 79 | ptr = buffer.ptr; 80 | ptr->incr(); 81 | } 82 | 83 | /** 84 | * Constructor. 85 | * Create a buffer from a raw BufferData pointer. It will ptr->incr(). This is to be used by specialized runtimes. 86 | * 87 | * @param p The pointer to use. 88 | */ 89 | ManagedBuffer::ManagedBuffer(BufferData *p) 90 | { 91 | ptr = p; 92 | ptr->incr(); 93 | } 94 | 95 | /** 96 | * Internal constructor-initialiser. 97 | * 98 | * @param data The data with which to fill the buffer. 99 | * @param length The length of the buffer to create. 100 | * 101 | */ 102 | void ManagedBuffer::init(uint8_t *data, int length) 103 | { 104 | if (length <= 0) { 105 | initEmpty(); 106 | return; 107 | } 108 | 109 | ptr = (BufferData *) malloc(sizeof(BufferData) + length); 110 | ptr->init(); 111 | 112 | ptr->length = length; 113 | 114 | // Copy in the data buffer, if provided. 115 | if (data) 116 | memcpy(ptr->payload, data, length); 117 | else 118 | memset(ptr->payload, 0, length); 119 | } 120 | 121 | /** 122 | * Destructor. 123 | * Removes buffer resources held by the instance. 124 | */ 125 | ManagedBuffer::~ManagedBuffer() 126 | { 127 | ptr->decr(); 128 | } 129 | 130 | /** 131 | * Copy assign operation. 132 | * 133 | * Called when one ManagedBuffer is assigned the value of another using the '=' operator. 134 | * Decrements our reference count and free up the buffer as necessary. 135 | * Then, update our buffer to refer to that of the supplied ManagedBuffer, 136 | * and increase its reference count. 137 | * 138 | * @param p The ManagedBuffer to reference. 139 | * 140 | * Example: 141 | * @code 142 | * uint8_t buf = {13,5,2}; 143 | * ManagedBuffer p1(16); 144 | * ManagedBuffer p2(buf, 3); 145 | * 146 | * p1 = p2; 147 | * @endcode 148 | */ 149 | ManagedBuffer& ManagedBuffer::operator = (const ManagedBuffer &p) 150 | { 151 | if(ptr == p.ptr) 152 | return *this; 153 | 154 | ptr->decr(); 155 | ptr = p.ptr; 156 | ptr->incr(); 157 | 158 | return *this; 159 | } 160 | 161 | /** 162 | * Equality operation. 163 | * 164 | * Called when one ManagedBuffer is tested to be equal to another using the '==' operator. 165 | * 166 | * @param p The ManagedBuffer to test ourselves against. 167 | * @return true if this ManagedBuffer is identical to the one supplied, false otherwise. 168 | * 169 | * Example: 170 | * @code 171 | * 172 | * uint8_t buf = {13,5,2}; 173 | * ManagedBuffer p1(16); 174 | * ManagedBuffer p2(buf, 3); 175 | * 176 | * if(p1 == p2) // will be true 177 | * uBit.display.scroll("same!"); 178 | * @endcode 179 | */ 180 | bool ManagedBuffer::operator== (const ManagedBuffer& p) 181 | { 182 | if (ptr == p.ptr) 183 | return true; 184 | else 185 | return (ptr->length == p.ptr->length && (memcmp(ptr->payload, p.ptr->payload, ptr->length)==0)); 186 | } 187 | 188 | /** 189 | * Sets the byte at the given index to value provided. 190 | * @param position The index of the byte to change. 191 | * @param value The new value of the byte (0-255). 192 | * @return MICROBIT_OK, or MICROBIT_INVALID_PARAMETER. 193 | * 194 | * Example: 195 | * @code 196 | * ManagedBuffer p1(16); 197 | * p1.setByte(0,255); // Sets the firts byte in the buffer to the value 255. 198 | * @endcode 199 | */ 200 | int ManagedBuffer::setByte(int position, uint8_t value) 201 | { 202 | if (0 <= position && position < ptr->length) 203 | { 204 | ptr->payload[position] = value; 205 | return MICROBIT_OK; 206 | } 207 | else 208 | { 209 | return MICROBIT_INVALID_PARAMETER; 210 | } 211 | } 212 | 213 | /** 214 | * Determines the value of the given byte in the buffer. 215 | * 216 | * @param position The index of the byte to read. 217 | * @return The value of the byte at the given position, or MICROBIT_INVALID_PARAMETER. 218 | * 219 | * Example: 220 | * @code 221 | * ManagedBuffer p1(16); 222 | * p1.setByte(0,255); // Sets the firts byte in the buffer to the value 255. 223 | * p1.getByte(0); // Returns 255. 224 | * @endcode 225 | */ 226 | int ManagedBuffer::getByte(int position) 227 | { 228 | if (0 <= position && position < ptr->length) 229 | return ptr->payload[position]; 230 | else 231 | return MICROBIT_INVALID_PARAMETER; 232 | } 233 | 234 | /** 235 | * Get current ptr, do not decr() it, and set the current instance to an empty buffer. 236 | * This is to be used by specialized runtimes which pass BufferData around. 237 | */ 238 | BufferData *ManagedBuffer::leakData() 239 | { 240 | BufferData* res = ptr; 241 | initEmpty(); 242 | return res; 243 | } 244 | 245 | 246 | int ManagedBuffer::fill(uint8_t value, int offset, int length) 247 | { 248 | if (offset < 0 || offset > ptr->length) 249 | return MICROBIT_INVALID_PARAMETER; 250 | if (length < 0) 251 | length = ptr->length; 252 | length = min(length, ptr->length - offset); 253 | 254 | memset(ptr->payload + offset, value, length); 255 | 256 | return MICROBIT_OK; 257 | } 258 | 259 | ManagedBuffer ManagedBuffer::slice(int offset, int length) const 260 | { 261 | offset = min(ptr->length, offset); 262 | if (length < 0) 263 | length = ptr->length; 264 | length = min(length, ptr->length - offset); 265 | return ManagedBuffer(ptr->payload + offset, length); 266 | } 267 | 268 | void ManagedBuffer::shift(int offset, int start, int len) 269 | { 270 | if (len < 0) len = ptr->length - start; 271 | if (start < 0 || start + len > ptr->length || start + len < start 272 | || len == 0 || offset == 0 || offset == INT_MIN) return; 273 | if (offset <= -len || offset >= len) { 274 | fill(0); 275 | return; 276 | } 277 | 278 | uint8_t *data = ptr->payload + start; 279 | if (offset < 0) { 280 | offset = -offset; 281 | memmove(data + offset, data, len - offset); 282 | memset(data, 0, offset); 283 | } else { 284 | len = len - offset; 285 | memmove(data, data + offset, len); 286 | memset(data + len, 0, offset); 287 | } 288 | } 289 | 290 | void ManagedBuffer::rotate(int offset, int start, int len) 291 | { 292 | if (len < 0) len = ptr->length - start; 293 | if (start < 0 || start + len > ptr-> length || start + len < start 294 | || len == 0 || offset == 0 || offset == INT_MIN) return; 295 | 296 | if (offset < 0) 297 | offset += len << 8; // try to make it positive 298 | offset %= len; 299 | if (offset < 0) 300 | offset += len; 301 | 302 | uint8_t *data = ptr->payload + start; 303 | 304 | uint8_t *n_first = data + offset; 305 | uint8_t *first = data; 306 | uint8_t *next = n_first; 307 | uint8_t *last = data + len; 308 | 309 | while (first != next) { 310 | uint8_t tmp = *first; 311 | *first++ = *next; 312 | *next++ = tmp; 313 | if (next == last) { 314 | next = n_first; 315 | } else if (first == n_first) { 316 | n_first = next; 317 | } 318 | } 319 | } 320 | 321 | int ManagedBuffer::writeBuffer(int dstOffset, const ManagedBuffer &src, int srcOffset, int length) 322 | { 323 | if (length < 0) 324 | length = src.length(); 325 | 326 | if (srcOffset < 0 || dstOffset < 0 || dstOffset > ptr->length) 327 | return MICROBIT_INVALID_PARAMETER; 328 | 329 | length = min(src.length() - srcOffset, ptr->length - dstOffset); 330 | 331 | if (length < 0) 332 | return MICROBIT_INVALID_PARAMETER; 333 | 334 | if (ptr == src.ptr) { 335 | memmove(getBytes() + dstOffset, src.ptr->payload + srcOffset, length); 336 | } else { 337 | memcpy(getBytes() + dstOffset, src.ptr->payload + srcOffset, length); 338 | } 339 | 340 | return MICROBIT_OK; 341 | } 342 | 343 | int ManagedBuffer::writeBytes(int offset, uint8_t *src, int length, bool swapBytes) 344 | { 345 | if (offset < 0 || length < 0 || offset + length > ptr->length) 346 | return MICROBIT_INVALID_PARAMETER; 347 | 348 | if (swapBytes) { 349 | uint8_t *p = ptr->payload + offset + length; 350 | for (int i = 0; i < length; ++i) 351 | *--p = src[i]; 352 | } else { 353 | memcpy(ptr->payload + offset, src, length); 354 | } 355 | 356 | return MICROBIT_OK; 357 | } 358 | 359 | int ManagedBuffer::readBytes(uint8_t *dst, int offset, int length, bool swapBytes) const 360 | { 361 | if (offset < 0 || length < 0 || offset + length > ptr->length) 362 | return MICROBIT_INVALID_PARAMETER; 363 | 364 | if (swapBytes) { 365 | uint8_t *p = ptr->payload + offset + length; 366 | for (int i = 0; i < length; ++i) 367 | dst[i] = *--p; 368 | } else { 369 | memcpy(dst, ptr->payload + offset, length); 370 | } 371 | 372 | return MICROBIT_OK; 373 | } 374 | -------------------------------------------------------------------------------- /source/pxt.cpp: -------------------------------------------------------------------------------- 1 | #include "pxt.h" 2 | #include 3 | 4 | MicroBit uBit; 5 | 6 | namespace pxt { 7 | int incr(uint32_t e) 8 | { 9 | if (e) { 10 | if (hasVTable(e)) 11 | ((RefObject*)e)->ref(); 12 | else 13 | ((RefCounted*)e)->incr(); 14 | } 15 | return e; 16 | } 17 | 18 | void decr(uint32_t e) 19 | { 20 | if (e) { 21 | if (hasVTable(e)) 22 | ((RefObject*)e)->unref(); 23 | else 24 | ((RefCounted*)e)->decr(); 25 | } 26 | } 27 | 28 | Action mkAction(int reflen, int totallen, int startptr) 29 | { 30 | check(0 <= reflen && reflen <= totallen, ERR_SIZE, 1); 31 | check(reflen <= totallen && totallen <= 255, ERR_SIZE, 2); 32 | check(bytecode[startptr] == 0xffff, ERR_INVALID_BINARY_HEADER, 3); 33 | check(bytecode[startptr + 1] == 0, ERR_INVALID_BINARY_HEADER, 4); 34 | 35 | uint32_t tmp = (uint32_t)&bytecode[startptr]; 36 | 37 | if (totallen == 0) { 38 | return tmp; // no closure needed 39 | } 40 | 41 | void *ptr = ::operator new(sizeof(RefAction) + totallen * sizeof(uint32_t)); 42 | RefAction *r = new (ptr) RefAction(); 43 | r->len = totallen; 44 | r->reflen = reflen; 45 | r->func = (ActionCB)((tmp + 4) | 1); 46 | memset(r->fields, 0, r->len * sizeof(uint32_t)); 47 | 48 | return (Action)r; 49 | } 50 | 51 | uint32_t runAction3(Action a, int arg0, int arg1, int arg2) 52 | { 53 | if (hasVTable(a)) 54 | return ((RefAction*)a)->runCore(arg0, arg1, arg2); 55 | else { 56 | check(*(uint16_t*)a == 0xffff, ERR_INVALID_BINARY_HEADER, 4); 57 | return ((ActionCB)((a + 4) | 1))(NULL, arg0, arg1, arg2); 58 | } 59 | } 60 | 61 | uint32_t runAction2(Action a, int arg0, int arg1) 62 | { 63 | return runAction3(a, arg0, arg1, 0); 64 | } 65 | 66 | uint32_t runAction1(Action a, int arg0) 67 | { 68 | return runAction3(a, arg0, 0, 0); 69 | } 70 | 71 | uint32_t runAction0(Action a) 72 | { 73 | return runAction3(a, 0, 0, 0); 74 | } 75 | 76 | RefRecord* mkClassInstance(int vtableOffset) 77 | { 78 | VTable *vtable = (VTable*)&bytecode[vtableOffset]; 79 | 80 | intcheck(vtable->methods[0] == &RefRecord_destroy, ERR_SIZE, 3); 81 | intcheck(vtable->methods[1] == &RefRecord_print, ERR_SIZE, 4); 82 | 83 | void *ptr = ::operator new(vtable->numbytes); 84 | RefRecord *r = new (ptr) RefRecord(PXT_VTABLE_TO_INT(vtable)); 85 | memset(r->fields, 0, vtable->numbytes - sizeof(RefRecord)); 86 | return r; 87 | } 88 | 89 | uint32_t RefRecord::ld(int idx) 90 | { 91 | //intcheck((reflen == 255 ? 0 : reflen) <= idx && idx < len, ERR_OUT_OF_BOUNDS, 1); 92 | return fields[idx]; 93 | } 94 | 95 | uint32_t RefRecord::ldref(int idx) 96 | { 97 | //printf("LD %p len=%d reflen=%d idx=%d\n", this, len, reflen, idx); 98 | //intcheck(0 <= idx && idx < reflen, ERR_OUT_OF_BOUNDS, 2); 99 | uint32_t tmp = fields[idx]; 100 | incr(tmp); 101 | return tmp; 102 | } 103 | 104 | void RefRecord::st(int idx, uint32_t v) 105 | { 106 | //intcheck((reflen == 255 ? 0 : reflen) <= idx && idx < len, ERR_OUT_OF_BOUNDS, 3); 107 | fields[idx] = v; 108 | } 109 | 110 | void RefRecord::stref(int idx, uint32_t v) 111 | { 112 | //printf("ST %p len=%d reflen=%d idx=%d\n", this, len, reflen, idx); 113 | //intcheck(0 <= idx && idx < reflen, ERR_OUT_OF_BOUNDS, 4); 114 | decr(fields[idx]); 115 | fields[idx] = v; 116 | } 117 | 118 | void RefObject::destroy() { 119 | ((RefObjectMethod)getVTable()->methods[0])(this); 120 | delete this; 121 | } 122 | 123 | void RefObject::print() { 124 | ((RefObjectMethod)getVTable()->methods[1])(this); 125 | } 126 | 127 | void RefRecord_destroy(RefRecord *r) { 128 | auto tbl = r->getVTable(); 129 | uint8_t *refmask = (uint8_t*)&tbl->methods[tbl->userdata & 0xff]; 130 | int len = (tbl->numbytes >> 2) - 1; 131 | for (int i = 0; i < len; ++i) { 132 | if (refmask[i]) decr(r->fields[i]); 133 | r->fields[i] = 0; 134 | } 135 | } 136 | 137 | void RefRecord_print(RefRecord *r) 138 | { 139 | printf("RefRecord %p r=%d size=%d bytes\n", r, r->refcnt, r->getVTable()->numbytes); 140 | } 141 | 142 | void RefCollection::push(uint32_t x) { 143 | if (isRef()) incr(x); 144 | data.push_back(x); 145 | } 146 | 147 | uint32_t RefCollection::getAt(int x) { 148 | if (in_range(x)) { 149 | uint32_t tmp = data.at(x); 150 | if (isRef()) incr(tmp); 151 | return tmp; 152 | } 153 | else { 154 | error(ERR_OUT_OF_BOUNDS); 155 | return 0; 156 | } 157 | } 158 | 159 | void RefCollection::removeAt(int x) { 160 | if (!in_range(x)) 161 | return; 162 | 163 | if (isRef()) decr(data.at(x)); 164 | data.erase(data.begin()+x); 165 | } 166 | 167 | void RefCollection::setAt(int x, uint32_t y) { 168 | if (!in_range(x)) 169 | return; 170 | 171 | if (isRef()) { 172 | decr(data.at(x)); 173 | incr(y); 174 | } 175 | data.at(x) = y; 176 | } 177 | 178 | int RefCollection::indexOf(uint32_t x, int start) { 179 | if (!in_range(start)) 180 | return -1; 181 | 182 | if (isString()) { 183 | StringData *xx = (StringData*)x; 184 | for (uint32_t i = start; i < data.size(); ++i) { 185 | StringData *ee = (StringData*)data.at(i); 186 | if (xx->len == ee->len && memcmp(xx->data, ee->data, xx->len) == 0) 187 | return (int)i; 188 | } 189 | } else { 190 | for (uint32_t i = start; i < data.size(); ++i) 191 | if (data.at(i) == x) 192 | return (int)i; 193 | } 194 | 195 | return -1; 196 | } 197 | 198 | int RefCollection::removeElement(uint32_t x) { 199 | int idx = indexOf(x, 0); 200 | if (idx >= 0) { 201 | removeAt(idx); 202 | return 1; 203 | } 204 | return 0; 205 | } 206 | 207 | namespace Coll0 { 208 | PXT_VTABLE_BEGIN(RefCollection, 0, 0) 209 | PXT_VTABLE_END 210 | } 211 | namespace Coll1 { 212 | PXT_VTABLE_BEGIN(RefCollection, 1, 0) 213 | PXT_VTABLE_END 214 | } 215 | namespace Coll3 { 216 | PXT_VTABLE_BEGIN(RefCollection, 3, 0) 217 | PXT_VTABLE_END 218 | } 219 | 220 | RefCollection::RefCollection(uint16_t flags) : RefObject(0) { 221 | switch (flags) { 222 | case 0: 223 | vtable = PXT_VTABLE_TO_INT(&Coll0::RefCollection_vtable); 224 | break; 225 | case 1: 226 | vtable = PXT_VTABLE_TO_INT(&Coll1::RefCollection_vtable); 227 | break; 228 | case 3: 229 | vtable = PXT_VTABLE_TO_INT(&Coll3::RefCollection_vtable); 230 | break; 231 | default: 232 | error(ERR_SIZE); 233 | break; 234 | } 235 | } 236 | 237 | void RefCollection::destroy() 238 | { 239 | if (this->isRef()) 240 | for (uint32_t i = 0; i < this->data.size(); ++i) { 241 | decr(this->data[i]); 242 | this->data[i] = 0; 243 | } 244 | this->data.resize(0); 245 | } 246 | 247 | void RefCollection::print() 248 | { 249 | printf("RefCollection %p r=%d flags=%d size=%d [%p, ...]\n", this, refcnt, getFlags(), data.size(), data.size() > 0 ? data[0] : 0); 250 | } 251 | 252 | PXT_VTABLE_CTOR(RefAction) {} 253 | 254 | // fields[] contain captured locals 255 | void RefAction::destroy() 256 | { 257 | for (int i = 0; i < this->reflen; ++i) { 258 | decr(fields[i]); 259 | fields[i] = 0; 260 | } 261 | } 262 | 263 | void RefAction::print() 264 | { 265 | 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); 266 | } 267 | 268 | void RefLocal::print() 269 | { 270 | printf("RefLocal %p r=%d v=%d\n", this, refcnt, v); 271 | } 272 | 273 | void RefLocal::destroy() 274 | { 275 | } 276 | 277 | PXT_VTABLE_CTOR(RefLocal) { 278 | v = 0; 279 | } 280 | 281 | PXT_VTABLE_CTOR(RefRefLocal) { 282 | v = 0; 283 | } 284 | 285 | void RefRefLocal::print() 286 | { 287 | printf("RefRefLocal %p r=%d v=%p\n", this, refcnt, (void*)v); 288 | } 289 | 290 | void RefRefLocal::destroy() 291 | { 292 | decr(v); 293 | } 294 | 295 | PXT_VTABLE_BEGIN(RefMap, 0, RefMapMarker) 296 | PXT_VTABLE_END 297 | RefMap::RefMap() : PXT_VTABLE_INIT(RefMap) {} 298 | 299 | void RefMap::destroy() { 300 | for (unsigned i = 0; i < data.size(); ++i) { 301 | if (data[i].key & 1) { 302 | decr(data[i].val); 303 | } 304 | data[i].val = 0; 305 | } 306 | data.resize(0); 307 | } 308 | 309 | int RefMap::findIdx(uint32_t key) { 310 | for (unsigned i = 0; i < data.size(); ++i) { 311 | if (data[i].key >> 1 == key) 312 | return i; 313 | } 314 | return -1; 315 | } 316 | 317 | void RefMap::print() 318 | { 319 | printf("RefMap %p r=%d size=%d\n", this, refcnt, data.size()); 320 | } 321 | 322 | 323 | #ifdef DEBUG_MEMLEAKS 324 | std::set allptrs; 325 | void debugMemLeaks() 326 | { 327 | printf("LIVE POINTERS:\n"); 328 | for(std::set::iterator itr = allptrs.begin();itr!=allptrs.end();itr++) 329 | { 330 | (*itr)->print(); 331 | } 332 | printf("\n"); 333 | } 334 | #else 335 | void debugMemLeaks() {} 336 | #endif 337 | 338 | 339 | // --------------------------------------------------------------------------- 340 | // An adapter for the API expected by the run-time. 341 | // --------------------------------------------------------------------------- 342 | 343 | map, Action> handlersMap; 344 | 345 | MicroBitEvent lastEvent; 346 | 347 | // We have the invariant that if [dispatchEvent] is registered against the DAL 348 | // for a given event, then [handlersMap] contains a valid entry for that 349 | // event. 350 | void dispatchEvent(MicroBitEvent e) { 351 | 352 | lastEvent = e; 353 | 354 | Action curr = handlersMap[{ e.source, e.value }]; 355 | if (curr) 356 | runAction1(curr, e.value); 357 | 358 | curr = handlersMap[{ e.source, MICROBIT_EVT_ANY }]; 359 | if (curr) 360 | runAction1(curr, e.value); 361 | } 362 | 363 | void registerWithDal(int id, int event, Action a) { 364 | Action prev = handlersMap[{ id, event }]; 365 | if (prev) 366 | decr(prev); 367 | else 368 | uBit.messageBus.listen(id, event, dispatchEvent); 369 | incr(a); 370 | handlersMap[{ id, event }] = a; 371 | } 372 | 373 | void fiberDone(void *a) 374 | { 375 | decr((Action)a); 376 | release_fiber(); 377 | } 378 | 379 | 380 | void runInBackground(Action a) { 381 | if (a != 0) { 382 | incr(a); 383 | create_fiber((void(*)(void*))runAction0, (void*)a, fiberDone); 384 | } 385 | } 386 | 387 | 388 | void error(ERROR code, int subcode) 389 | { 390 | printf("Error: %d [%d]\n", code, subcode); 391 | uBit.panic(42); 392 | } 393 | 394 | uint16_t *bytecode; 395 | uint32_t *globals; 396 | int numGlobals; 397 | 398 | uint32_t *allocate(uint16_t sz) 399 | { 400 | uint32_t *arr = new uint32_t[sz]; 401 | memset(arr, 0, sz * 4); 402 | return arr; 403 | } 404 | 405 | void checkStr(bool cond, const char *msg) 406 | { 407 | if (!cond) { 408 | while (true) { 409 | uBit.display.scroll(msg, 100); 410 | uBit.sleep(100); 411 | } 412 | } 413 | } 414 | 415 | int templateHash() 416 | { 417 | return ((int*)bytecode)[4]; 418 | } 419 | 420 | int programHash() 421 | { 422 | return ((int*)bytecode)[6]; 423 | } 424 | 425 | int getNumGlobals() 426 | { 427 | return bytecode[16]; 428 | } 429 | 430 | void exec_binary(int32_t *pc) 431 | { 432 | // XXX re-enable once the calibration code is fixed and [editor/embedded.ts] 433 | // properly prepends a call to [internal_main]. 434 | // ::touch_develop::internal_main(); 435 | 436 | // unique group for radio based on source hash 437 | // ::touch_develop::micro_bit::radioDefaultGroup = programHash(); 438 | 439 | // repeat error 4 times and restart as needed 440 | microbit_panic_timeout(4); 441 | 442 | int32_t ver = *pc++; 443 | checkStr(ver == 0x4209, ":( Bad runtime version"); 444 | 445 | bytecode = *((uint16_t**)pc++); // the actual bytecode is here 446 | globals = allocate(getNumGlobals()); 447 | 448 | // just compare the first word 449 | checkStr(((uint32_t*)bytecode)[0] == 0x923B8E70 && 450 | templateHash() == *pc, 451 | ":( Failed partial flash"); 452 | 453 | uint32_t startptr = (uint32_t)bytecode; 454 | startptr += 48; // header 455 | startptr |= 1; // Thumb state 456 | 457 | ((uint32_t (*)())startptr)(); 458 | 459 | #ifdef DEBUG_MEMLEAKS 460 | pxt::debugMemLeaks(); 461 | #endif 462 | 463 | return; 464 | } 465 | 466 | void start() 467 | { 468 | exec_binary((int32_t*)functionsAndBytecode); 469 | } 470 | } 471 | 472 | // vim: ts=2 sw=2 expandtab 473 | --------------------------------------------------------------------------------