From 0ccddf9fe8fa5ca5ed1a15a474f2768f9f23631a Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Tue, 29 Mar 2016 17:11:17 -0700 Subject: [PATCH 1/4] Start on direct CPP migration --- libs/cpp-test/hello.ts | 6 +-- libs/cpp-test/support.cpp | 8 ++- libs/microbit/{basic.ts => basic.cpp} | 77 +++++++++++++++++++++------ libs/microbit/control.cpp | 30 +++++++++++ libs/microbit/control.ts | 47 ++++++---------- libs/microbit/kind.json | 3 +- 6 files changed, 118 insertions(+), 53 deletions(-) rename libs/microbit/{basic.ts => basic.cpp} (54%) create mode 100644 libs/microbit/control.cpp diff --git a/libs/cpp-test/hello.ts b/libs/cpp-test/hello.ts index 6f4776d1..4b8b9240 100644 --- a/libs/cpp-test/hello.ts +++ b/libs/cpp-test/hello.ts @@ -1,7 +1,3 @@ -//% shim=foo::bar -function test() { -} - basic.plotLeds(` # # . # # . . # . . @@ -10,4 +6,4 @@ basic.plotLeds(` . # # # . `); basic.pause(300); -test(); +foo.bar(); diff --git a/libs/cpp-test/support.cpp b/libs/cpp-test/support.cpp index 1474159a..89d523f9 100644 --- a/libs/cpp-test/support.cpp +++ b/libs/cpp-test/support.cpp @@ -1,6 +1,10 @@ +#include "BitVM.h" +#include "MicroBitTouchDevelop.h" + namespace foo { - GLUE void bar() + //% + void bar() { - micro_bit::scrollNumber(108108, 50); + touch_develop::micro_bit::scrollNumber(108108, 50); } } diff --git a/libs/microbit/basic.ts b/libs/microbit/basic.cpp similarity index 54% rename from libs/microbit/basic.ts rename to libs/microbit/basic.cpp index 5c94e097..f2b999a4 100644 --- a/libs/microbit/basic.ts +++ b/libs/microbit/basic.cpp @@ -1,3 +1,8 @@ +#include "BitVM.h" + + +typedef uint32_t ImageLiteral; + /** * Provides access to basic micro:bit functionality. */ @@ -10,10 +15,19 @@ namespace basic { */ //% help=basic/show-number //% weight=96 - //% shim=micro_bit::scrollNumber //% blockId=device_show_number block="show|number %number" blockGap=8 icon="\uf1ec" //% async - export function showNumber(value: number, interval: number = 150): void { } + void showNumber(int value, int interval = 150) { + if (interval < 0) + return; + + ManagedString t(value); + if (value < 0 || value >= 10) { + uBit.display.scroll(t, interval); + } else { + uBit.display.print(t.charAt(0), interval * 5); + } + } /** * Draws an image on the LED screen. @@ -22,11 +36,12 @@ namespace basic { */ //% help=basic/show-leds //% weight=95 blockGap=8 - //% shim=micro_bit::showLeds //% imageLiteral=1 async //% blockId=device_show_leds //% block="show leds" icon="\uf00a" - export function showLeds(leds: string, interval: number = 400): void { } + void showLeds(ImageLiteral leds, int interval = 400) { + uBit.display.print(MicroBitImage(getbytes(leds)), 0, 0, 0, delay); + } /** * Display text on the display, one character at a time. If the string fits on the screen (i.e. is one letter), does not scroll. @@ -35,19 +50,32 @@ namespace basic { */ //% help=basic/show-string //% weight=87 blockGap=8 - //% shim=micro_bit::scrollString async //% block="show|string %text" icon="\uf031" //% async //% blockId=device_print_message - export function showString(text: string, interval: number = 150): void { } + void showString(StringData *text, int interval = 150) { + if (interval < 0) + return; + ManagedString s(text); + int l = s.length(); + if (l == 0) { + uBit.display.clear(); + uBit.sleep(interval * 5); + } else if (l > 1) { + uBit.display.scroll(s, interval); + } else { + uBit.display.print(s.charAt(0), interval * 5); + } + } /** * Turn off all LEDs */ //% help=basic/clear-screen weight=79 - //% shim=micro_bit::clearScreen //% blockId=device_clear_display block="clear screen" icon="\uf12d" - export function clearScreen(): void { } + void clearScreen() { + uBit.display.image.clear(); + } /** * Shows a sequence of LED screens as an animation. @@ -55,29 +83,48 @@ namespace basic { * @param interval TODO */ //% help=basic/show-animation shim=micro_bit::showAnimation imageLiteral=1 async - export function showAnimation(leds: string, interval: number = 400): void { } + void showAnimation(ImageLiteral leds, int interval = 400) { + uBit.display.animate(MicroBitImage(getbytes(leds)), interval, 5, 0); + } /** * Draws an image on the LED screen. * @param leds TODO */ - //% help=basic/plot-leds weight=80 shim=micro_bit::plotLeds imageLiteral=1 - export function plotLeds(leds: string): void { } + //% help=basic/plot-leds weight=80 shim=micro_bit::plotLeds + void plotLeds(ImageLiteral leds) { + MicroBitImage i(getbytes(leds)); + uBit.display.print(i, 0, 0, 0, 0); + } + + void forever_stub(void *a) { + while (true) { + action::run((Action)a); + micro_bit::pause(20); + } + } /** * Repeats the code forever in the background. On each iteration, allows other codes to run. * @param body TODO */ //% help=basic/forever weight=55 blockGap=8 - //% blockId=device_forever block="forever" icon="\uf01e" shim=micro_bit::forever - export function forever(body: () => void): void { } + //% blockId=device_forever block="forever" icon="\uf01e" + void forever(Action a) { + if (a != 0) { + incr(a); + create_fiber(forever_stub, (void*)a); + } + } /** * Pause for the specified time in milliseconds * @param ms how long to pause for, eg: 100, 200, 500, 1000, 2000 */ //% help=basic/pause weight=54 - //% shim=micro_bit::pause async block="pause (ms) %pause" + //% async block="pause (ms) %pause" //% blockId=device_pause icon="\uf110" - export function pause(ms: number): void { } + void pause(int ms) { + uBit.sleep(ms); + } } diff --git a/libs/microbit/control.cpp b/libs/microbit/control.cpp new file mode 100644 index 00000000..167f3820 --- /dev/null +++ b/libs/microbit/control.cpp @@ -0,0 +1,30 @@ +#include "BitVM.h" + +namespace control { + void fiberDone(void *a) + { + decr((Action)a); + release_fiber(); + } + + /** + * Schedules code that run in the background. + */ + //% help=control/in-background + //% blockId="control_in_background" block="run in background" blockGap=8 + void inBackground(Action a) { + if (a != 0) { + incr(a); + create_fiber((void(*)(void*))action::run, (void*)a, fiberDone); + } + } + + /** + * Resets the BBC micro:bit. + */ + //% weight=30 async help=control/reset + //% blockId="control_reset" block="reset" + void reset() { + uBit.reset(); + } +} diff --git a/libs/microbit/control.ts b/libs/microbit/control.ts index a1d358d1..f8112492 100644 --- a/libs/microbit/control.ts +++ b/libs/microbit/control.ts @@ -192,27 +192,29 @@ enum EventBusValue { //% weight=1 color="#333333" namespace control { - /** - * Schedules code that run in the background. - */ - //% help=control/in-background shim=micro_bit::runInBackground - //% blockId="control_in_background" block="run in background" blockGap=8 - export function inBackground(body: Action): void { } /** - * Resets the BBC micro:bit. + * Returns the value of a C++ runtime constant */ - //% weight=30 shim=uBit.reset async help=control/reset - //% blockId="control_reset" block="reset" - export function reset(): void { } + //% weight=19 weight=19 blockId="control_event_source" block="%id" + export function eventSource(id: EventBusSource) : number { + return id; + } + /** + * Returns the value of a C++ runtime constant + */ + //% weight=19 weight=19 blockId="control_event_value" block="%id" + export function eventValue(id: EventBusValue) : number { + return id; + } /** * Raises an event in the event bus. - @param src ID of the MicroBit Component that generated the event e.g. MICROBIT_ID_BUTTON_A. - @param value Component specific code indicating the cause of the event. - @param mode optional definition of how the event should be processed after construction (default is CREATE_AND_QUEUE). - */ - // shim=micro_bit::busRaiseEvent + * @param src ID of the MicroBit Component that generated the event e.g. MICROBIT_ID_BUTTON_A. + * @param value Component specific code indicating the cause of the event. + * @param mode optional definition of how the event should be processed after construction (default is CREATE_AND_QUEUE). + */ + //% shim=micro_bit::busRaiseEvent //% weight=21 blockGap=12 blockId="control_raise_event" block="raise event|from source %src=control_event_source|with value %value=control_event_value" blockExternalInputs=1 export function raiseEvent(src: number, value: number, mode: EventCreationMode = EventCreationMode.CreateAndQueue): void { } @@ -223,19 +225,4 @@ namespace control { //% weight=20 blockGap=8 blockId="control_on_event" block="on event|from %src=control_event_source|with value %value=control_event_value" //% blockExternalInputs=1 blockStatement=1 export function onEvent(src: number, value: number, handler: Action): void { } - - /** - * Returns the value of a C++ runtime constant - */ - //% weight=19 shimw=TD_ID weight=19 blockId="control_event_source" block="%id" - export function eventSource(id: EventBusSource) : number { - return 0; - } - /** - * Returns the value of a C++ runtime constant - */ - //% weight=19 shimw=TD_ID weight=19 blockId="control_event_value" block="%id" - export function eventValue(id: EventBusValue) : number { - return 0; - } } diff --git a/libs/microbit/kind.json b/libs/microbit/kind.json index 6cd6bc30..6f827ac1 100644 --- a/libs/microbit/kind.json +++ b/libs/microbit/kind.json @@ -7,9 +7,10 @@ "core.d.ts", "mbit.ts", "images.ts", - "basic.ts", + "basic.cpp", "input.ts", "control.ts", + "control.cpp", "game.ts", "led.ts", "music.ts", From 45aa7809344427e2902327da1d5bc8c37316a629 Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Tue, 29 Mar 2016 17:56:01 -0700 Subject: [PATCH 2/4] C++ fixes --- libs/cpp-test/hello.ts | 3 ++- libs/microbit/basic.cpp | 6 ++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/libs/cpp-test/hello.ts b/libs/cpp-test/hello.ts index 4b8b9240..2153f128 100644 --- a/libs/cpp-test/hello.ts +++ b/libs/cpp-test/hello.ts @@ -6,4 +6,5 @@ basic.plotLeds(` . # # # . `); basic.pause(300); -foo.bar(); +basic.showString("Hello"); +// foo.bar(); diff --git a/libs/microbit/basic.cpp b/libs/microbit/basic.cpp index f2b999a4..719465a8 100644 --- a/libs/microbit/basic.cpp +++ b/libs/microbit/basic.cpp @@ -1,8 +1,6 @@ #include "BitVM.h" -typedef uint32_t ImageLiteral; - /** * Provides access to basic micro:bit functionality. */ @@ -40,7 +38,7 @@ namespace basic { //% blockId=device_show_leds //% block="show leds" icon="\uf00a" void showLeds(ImageLiteral leds, int interval = 400) { - uBit.display.print(MicroBitImage(getbytes(leds)), 0, 0, 0, delay); + uBit.display.print(MicroBitImage(getbytes(leds)), 0, 0, 0, interval); } /** @@ -100,7 +98,7 @@ namespace basic { void forever_stub(void *a) { while (true) { action::run((Action)a); - micro_bit::pause(20); + uBit.sleep(20); } } From 61dd0075b2a3c9817debab95c219c2a088027e45 Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Tue, 29 Mar 2016 19:13:46 -0700 Subject: [PATCH 3/4] Fix build --- libs/microbit/control.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/microbit/control.ts b/libs/microbit/control.ts index f8112492..6017ad6f 100644 --- a/libs/microbit/control.ts +++ b/libs/microbit/control.ts @@ -214,7 +214,7 @@ namespace control { * @param value Component specific code indicating the cause of the event. * @param mode optional definition of how the event should be processed after construction (default is CREATE_AND_QUEUE). */ - //% shim=micro_bit::busRaiseEvent + // shim=micro_bit::busRaiseEvent //% weight=21 blockGap=12 blockId="control_raise_event" block="raise event|from source %src=control_event_source|with value %value=control_event_value" blockExternalInputs=1 export function raiseEvent(src: number, value: number, mode: EventCreationMode = EventCreationMode.CreateAndQueue): void { } From b76b32a825416ccaf12c3d2dc4a669f4d4cc0c5d Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Wed, 30 Mar 2016 15:53:00 -0700 Subject: [PATCH 4/4] Move C++ sim stuff to proper namespace --- libs/hello/kind.json | 2 +- sim/libmbit.ts | 291 ++++++++++++++++++++++--------------------- 2 files changed, 147 insertions(+), 146 deletions(-) diff --git a/libs/hello/kind.json b/libs/hello/kind.json index d7f7e7c1..468413e1 100644 --- a/libs/hello/kind.json +++ b/libs/hello/kind.json @@ -6,6 +6,6 @@ ], "public": true, "dependencies": { - "core": "file:../microbit" + "microbit": "file:../microbit" } } diff --git a/sim/libmbit.ts b/sim/libmbit.ts index 17cf2181..ab2cb9b5 100644 --- a/sim/libmbit.ts +++ b/sim/libmbit.ts @@ -88,78 +88,6 @@ namespace ks.rt.micro_bit { throw new Error("PANIC " + code) } - /* basic */ - export function showDigit(v: number) { - if (!quiet) - console.log("DIGIT:", v) - plotLeds(createImageFromString(v.toString()[0])); - } - - export function clearScreen() { - board().image.clear(); - runtime.queueDisplayUpdate() - } - - export function showLeds(leds: micro_bit.Image, delay: number): void { - showAnimation(leds, delay); - } - - function scrollImage(leds: micro_bit.Image, interval: number, stride: number): void { - let cb = getResume() - let off = stride > 0 ? 0 : leds.width - 1; - let display = board().image; - - board().animationQ.enqueue({ - interval: interval, - frame: () => { - if (off >= leds.width || off < 0) return false; - stride > 0 ? display.shiftLeft(stride) : display.shiftRight(-stride); - let c = Math.min(stride, leds.width - off); - leds.copyTo(off, c, display, 5 - stride) - off += stride; - return true; - }, - whenDone: cb - }) - } - - export function showAnimation(leds: micro_bit.Image, interval: number = 400): void { - scrollImage(leds, interval, 5); - } - - export function scrollNumber(x: number, interval: number) { - if (interval < 0) return; - - let leds = createImageFromString(x.toString()); - if (x < 0 || x >= 10) scrollImage(leds, interval, 1); - else showLeds(leds, interval * 5); - } - - export function scrollString(s: string, interval: number) { - if (interval < 0) return; - if (s.length == 0) { - clearScreen(); - pause(interval * 5); - } else { - let leds = createImageFromString(s); - if (s.length == 1) showLeds(leds, interval * 5) - else scrollImage(leds, interval, 1); - } - } - - export function forever(a: RefAction) { - function loop() { - runtime.runFiberAsync(a) - .then(() => Promise.delay(20)) - .then(loop) - .done() - } - incr(a) - loop() - } - - export var pause = thread.pause; - /* leds */ export function plot(x: number, y: number) { board().image.set(x, y, 255); @@ -198,20 +126,17 @@ namespace ks.rt.micro_bit { runtime.queueDisplayUpdate() } - /* control */ - export var runInBackground = thread.runInBackground; - /* serial */ export function serialSendString(s: string) { board().writeSerial(s); } - - export function serialReadString() : string { + + export function serialReadString(): string { return board().readSerial(); } /* input */ - export function onButtonPressed(button : number, handler: RefAction) : void { + export function onButtonPressed(button: number, handler: RefAction): void { let ens = enums(); let b = board(); if (button == ens.MICROBIT_ID_BUTTON_AB && !board().usesButtonAB) { @@ -220,7 +145,7 @@ namespace ks.rt.micro_bit { } b.bus.listen(button, ens.MICROBIT_BUTTON_EVT_CLICK, handler); } - + export function isButtonPressed(button: number): boolean { let ens = enums(); let b = board(); @@ -233,19 +158,19 @@ namespace ks.rt.micro_bit { if (button == ens.MICROBIT_ID_BUTTON_B) return bts[1].pressed; return bts[2].pressed || (bts[0].pressed && bts[1].pressed); } - + export function onGesture(gesture: number, handler: RefAction) { let ens = enums(); let b = board(); b.accelerometer.activate(); - + if (gesture == 11 && !b.useShake) { // SAKE b.useShake = true; runtime.queueDisplayUpdate(); } b.bus.listen(ens.MICROBIT_ID_GESTURE, gesture, handler); } - + export function onPinPressed(pin: Pin, handler: RefAction) { pin.isTouched(); onButtonPressed(pin.id, handler); @@ -283,7 +208,7 @@ namespace ks.rt.micro_bit { } return b.heading; } - + export function temperature(): number { var b = board(); if (!b.usesTemperature) { @@ -292,7 +217,7 @@ namespace ks.rt.micro_bit { } return b.temperature; } - + export function getAcceleration(dimension: number): number { let b = board(); let acc = b.accelerometer; @@ -304,8 +229,8 @@ namespace ks.rt.micro_bit { default: return Math.floor(Math.sqrt(acc.instantaneousAccelerationSquared())); } } - - export function setAccelerometerRange(range : number) { + + export function setAccelerometerRange(range: number) { let b = board(); b.accelerometer.setSampleRange(range); } @@ -327,76 +252,76 @@ namespace ks.rt.micro_bit { export function getCurrentTime(): number { return runtime.runningTime(); } - + /* pins */ - export function digitalReadPin(pin : Pin) : number { + export function digitalReadPin(pin: Pin): number { pin.mode = PinMode.Digital | PinMode.Input; return pin.value > 100 ? 1 : 0; } - - export function digitalWritePin(pin : Pin, value: number) { + + export function digitalWritePin(pin: Pin, value: number) { pin.mode = PinMode.Digital | PinMode.Output; pin.value = value > 0 ? 1023 : 0; runtime.queueDisplayUpdate(); } - export function analogReadPin(pin : Pin) : number { + export function analogReadPin(pin: Pin): number { pin.mode = PinMode.Analog | PinMode.Input; return pin.value || 0; } - - export function analogWritePin(pin : Pin, value: number) { + + export function analogWritePin(pin: Pin, value: number) { pin.mode = PinMode.Analog | PinMode.Output; pin.value = value ? 1 : 0; runtime.queueDisplayUpdate(); } - - export function setAnalogPeriodUs(pin: Pin, micros:number) { + + export function setAnalogPeriodUs(pin: Pin, micros: number) { pin.mode = PinMode.Analog | PinMode.Output; pin.period = micros; runtime.queueDisplayUpdate(); } - + export function servoWritePin(pin: Pin, value: number) { setAnalogPeriodUs(pin, 20000); // TODO } - - export function servoSetPulse(pin: Pin, micros:number) { + + export function servoSetPulse(pin: Pin, micros: number) { } - + module AudioContextManager { - var _context : any; // AudioContext - var _vco : any; //OscillatorNode; + var _context: any; // AudioContext + var _vco: any; //OscillatorNode; var _vca: any; // GainNode; - - function context() : any { + + function context(): any { if (!_context) _context = freshContext(); return _context; } - - function freshContext() : any { + + function freshContext(): any { (window).AudioContext = (window).AudioContext || (window).webkitAudioContext; if ((window).AudioContext) { try { // this call my crash. // SyntaxError: audio resources unavailable for AudioContext construction - return new (window).AudioContext(); - } catch(e) {} - } + return new (window).AudioContext(); + } catch (e) { } + } return undefined; } - + export function stop() { if (_vca) _vca.gain.value = 0; } - - export function tone(frequency: number, gain: number) { - if (frequency <= 0) return; + + export function tone(frequency: number, gain: number) { + if (frequency <= 0) return; var ctx = context(); if (!ctx) return; - - gain = Math.max(0, Math.min(1, gain)); + + gain = Math.max(0, Math.min(1, gain)); if (!_vco) { try { _vco = ctx.createOscillator(); @@ -405,89 +330,165 @@ namespace ks.rt.micro_bit { _vca.connect(ctx.destination); _vca.gain.value = gain; _vco.start(0); - } catch(e) { + } catch (e) { _vco = undefined; _vca = undefined; return; } } - + _vco.frequency.value = frequency; _vca.gain.value = gain; } } - + export function enablePitch(pin: Pin) { board().pins.filter(p => !!p).forEach(p => p.pitch = false); pin.pitch = true; } - + export function pitch(frequency: number, ms: number) { // update analog output let pin = board().pins.filter(pin => !!pin && pin.pitch)[0] || board().pins[0]; pin.mode = PinMode.Analog | PinMode.Output; if (frequency <= 0) { - pin.value = 0; - pin.period = 0; + pin.value = 0; + pin.period = 0; } else { pin.value = 512; - pin.period = 1000000/frequency; + pin.period = 1000000 / frequency; } runtime.queueDisplayUpdate(); - + let cb = getResume(); AudioContextManager.tone(frequency, 1); if (ms <= 0) cb(); else { setTimeout(() => { - AudioContextManager.stop(); - pin.value = 0; - pin.period = 0; + AudioContextManager.stop(); + pin.value = 0; + pin.period = 0; pin.mode = PinMode.Unused; runtime.queueDisplayUpdate(); - cb() + cb() }, ms); } } - - + + /* radio */ - export function broadcastMessage(msg: number) : void { + export function broadcastMessage(msg: number): void { board().radio.broadcast(msg); } - - export function onBroadcastMessageReceived(msg: number, handler: RefAction) : void { + + export function onBroadcastMessageReceived(msg: number, handler: RefAction): void { let ens = enums() board().bus.listen(ens.MES_BROADCAST_GENERAL_ID, msg, handler); } - - export function setGroup(id : number) : void { + + export function setGroup(id: number): void { board().radio.setGroup(id); } - - export function setTransmitPower(power: number) : void { - board().radio.setTransmitPower(power); + + export function setTransmitPower(power: number): void { + board().radio.setTransmitPower(power); } - export function datagramSendNumbers(value0 : number, value1: number, value2: number, value3: number) : void { + export function datagramSendNumbers(value0: number, value1: number, value2: number, value3: number): void { board().radio.datagram.send([value0, value1, value2, value3]); } - - export function datagramReceiveNumber() : number { + + export function datagramReceiveNumber(): number { return board().radio.datagram.recv().data[0]; } - - export function datagramGetNumber(index : number) : number { + + export function datagramGetNumber(index: number): number { return board().radio.datagram.lastReceived.data[index] || 0; } - - export function datagramGetRSSI() : number { + + export function datagramGetRSSI(): number { return board().radio.datagram.lastReceived.rssi; } - - export function onDatagramReceived(handler: RefAction) : void { + + export function onDatagramReceived(handler: RefAction): void { let ens = enums(); board().bus.listen(ens.MICROBIT_ID_RADIO, ens.MICROBIT_RADIO_EVT_DATAGRAM, handler); } } +namespace ks.rt.basic { + var board = micro_bit.board; + + export var pause = thread.pause; + + export function showNumber(x: number, interval: number) { + if (interval < 0) return; + + let leds = micro_bit.createImageFromString(x.toString()); + if (x < 0 || x >= 10) scrollImage(leds, interval, 1); + else showLeds(leds, interval * 5); + } + + export function showString(s: string, interval: number) { + if (interval < 0) return; + if (s.length == 0) { + clearScreen(); + pause(interval * 5); + } else { + let leds = micro_bit.createImageFromString(s); + if (s.length == 1) showLeds(leds, interval * 5) + else scrollImage(leds, interval, 1); + } + } + + export function showLeds(leds: micro_bit.Image, delay: number): void { + showAnimation(leds, delay); + } + + export function clearScreen() { + board().image.clear(); + runtime.queueDisplayUpdate() + } + + function scrollImage(leds: micro_bit.Image, interval: number, stride: number): void { + let cb = getResume() + let off = stride > 0 ? 0 : leds.width - 1; + let display = board().image; + + board().animationQ.enqueue({ + interval: interval, + frame: () => { + if (off >= leds.width || off < 0) return false; + stride > 0 ? display.shiftLeft(stride) : display.shiftRight(-stride); + let c = Math.min(stride, leds.width - off); + leds.copyTo(off, c, display, 5 - stride) + off += stride; + return true; + }, + whenDone: cb + }) + } + + export function showAnimation(leds: micro_bit.Image, interval: number = 400): void { + scrollImage(leds, interval, 5); + } + + export function forever(a: RefAction) { + function loop() { + runtime.runFiberAsync(a) + .then(() => Promise.delay(20)) + .then(loop) + .done() + } + incr(a) + loop() + } +} + +namespace ks.rt.control { + export var inBackground = thread.runInBackground; + + export function reset() { + U.userError("reset not implemented in simulator yet") + } +}