diff --git a/aux/build.sh b/aux/build.sh
index 6eb02b96..b8af9d59 100755
--- a/aux/build.sh
+++ b/aux/build.sh
@@ -1,7 +1,10 @@
#!/bin/sh
LEGO=$HOME/src/lego/lms2012
-F=`pwd`/pxt
+P="`pwd`"
+F="$P"/pxt
set -xe
cd $LEGO/lmssrc/adk/lmsasm
java -jar assembler.jar $F
+cd "$P"
+node -p 'require("fs").readFileSync("pxt.rbf").toString("hex")'
diff --git a/aux/pxt.lms b/aux/pxt.lms
index a4334a2f..94456c98 100644
--- a/aux/pxt.lms
+++ b/aux/pxt.lms
@@ -8,7 +8,8 @@ vmthread MAIN
UI_WRITE(LED,LED_RED)
UI_DRAW(TEXT,FG_COLOR,48,62,'Starting...')
UI_DRAW(UPDATE)
- SYSTEM('../prjs/BrkProg_SAVE/binary.elf', Status)
+ SYSTEM('XXXXXXXXX', Status)
+ //SYSTEM('../prjs/BrkProg_SAVE/binary.elf', Status)
Loop:
UI_BUTTON(WAIT_FOR_PRESS)
UI_BUTTON(SHORTPRESS,BACK_BUTTON,State)
diff --git a/aux/pxt.rbf b/aux/pxt.rbf
index 1e4c6c00..a9452871 100644
Binary files a/aux/pxt.rbf and b/aux/pxt.rbf differ
diff --git a/cmds/cmds.ts b/cmds/cmds.ts
new file mode 100644
index 00000000..66c86b30
--- /dev/null
+++ b/cmds/cmds.ts
@@ -0,0 +1,12 @@
+///
+///
+
+require("./editor")
+
+declare namespace pxt.editor {
+ function deployCoreAsync(resp: pxtc.CompileResult, isCli?: boolean): Promise;
+}
+
+export function deployCoreAsync(resp: pxtc.CompileResult) {
+ return pxt.editor.deployCoreAsync(resp, true)
+}
diff --git a/editor/extension.ts b/editor/extension.ts
new file mode 100644
index 00000000..f9a7a141
--- /dev/null
+++ b/editor/extension.ts
@@ -0,0 +1,64 @@
+///
+
+// When require()d from node, bind the global pxt namespace
+namespace pxt {
+ export const dummyExport = 1;
+}
+eval("if (typeof process === 'object' && process + '' === '[object process]') pxt = global.pxt")
+
+namespace pxt.editor {
+ // this comes from aux/pxt.lms
+ const rbfTemplate = "4c45474f5d0000006d000100000000001c0000000000000008000000821b028405018130813e805374617274696e672e2e2e00840060805858585858585858580044830383010640414082f5ff8405018130813e80427965210084000a";
+
+
+ function hf2Async() {
+ return pxt.HF2.mkPacketIOAsync()
+ .then(h => {
+ let w = new Ev3Wrapper(h)
+ return w.reconnectAsync(true)
+ .then(() => w)
+ })
+ }
+
+ let initPromise: Promise
+ function initAsync() {
+ if (!initPromise)
+ initPromise = hf2Async()
+ .catch(err => {
+ initPromise = null
+ return Promise.reject(err)
+ })
+ return initPromise
+ }
+
+ export function deployCoreAsync(resp: pxtc.CompileResult, isCli = false) {
+ let w: Ev3Wrapper
+ let elfPath = "../prjs/BrkProg_SAVE/binary.elf"
+ let rbfPath = "../prjs/BrkProg_SAVE/pxt0.rbf"
+ return initAsync()
+ .then(w_ => {
+ w = w_
+ let f = U.stringToUint8Array(atob(resp.outfiles[pxt.outputName()]))
+ return w.flashAsync(elfPath, f)
+ })
+ .then(() => {
+ let rbfHex = rbfTemplate.replace(/58585858(58)+/, U.toHex(U.stringToUint8Array(elfPath)))
+ let rbf = U.fromHex(rbfHex)
+ HF2.write16(rbf, 4, rbf.length)
+ return w.flashAsync(rbfPath, rbf)
+ }).then(() => {
+ if (isCli)
+ return w.disconnectAsync()
+ else
+ return Promise.resolve()
+ })
+ }
+
+ initExtensionsAsync = function (opts: pxt.editor.ExtensionOptions): Promise {
+ pxt.debug('loading pxt-adafruit target extensions...')
+ const res: pxt.editor.ExtensionResult = {
+ deployCoreAsync,
+ };
+ return Promise.resolve(res);
+ }
+}
\ No newline at end of file
diff --git a/editor/tsconfig.json b/editor/tsconfig.json
new file mode 100644
index 00000000..de974d4b
--- /dev/null
+++ b/editor/tsconfig.json
@@ -0,0 +1,12 @@
+{
+ "compilerOptions": {
+ "target": "es5",
+ "noImplicitAny": true,
+ "noImplicitReturns": true,
+ "declaration": true,
+ "out": "../built/editor.js",
+ "rootDir": ".",
+ "newLine": "LF",
+ "sourceMap": false
+ }
+}
\ No newline at end of file
diff --git a/editor/wrap.ts b/editor/wrap.ts
new file mode 100644
index 00000000..2e4ac5d9
--- /dev/null
+++ b/editor/wrap.ts
@@ -0,0 +1,122 @@
+namespace pxt.editor {
+ import HF2 = pxt.HF2
+ import U = pxt.U
+
+ function log(msg: string) {
+ pxt.log("EWRAP: " + msg)
+ }
+
+ export interface DirEntry {
+ name: string;
+ md5?: string;
+ size?: number;
+ }
+
+ export class Ev3Wrapper {
+ msgs = new U.PromiseBuffer()
+ private cmdSeq = U.randomUint32() & 0xffff;
+
+ constructor(public io: pxt.HF2.PacketIO) {
+ io.onData = buf => {
+ buf = buf.slice(0, HF2.read16(buf, 0) + 2)
+ // log("DATA: " + U.toHex(buf))
+ this.msgs.push(buf)
+ }
+ }
+
+ private alloc(addSize: number, cmd: number, replyType = 1) {
+ let len = 6 + addSize
+ let buf = new Uint8Array(len)
+ HF2.write16(buf, 0, len - 2) // pktLen
+ HF2.write16(buf, 2, this.cmdSeq++) // msgCount
+ buf[4] = replyType
+ buf[5] = cmd
+ return buf
+ }
+
+ talkAsync(buf: Uint8Array) {
+ return this.io.sendPacketAsync(buf)
+ .then(() => this.msgs.shiftAsync(1000))
+ .then(resp => {
+ if (resp[2] != buf[2] || resp[3] != buf[3])
+ U.userError("msg count de-sync")
+ if (resp[5] != buf[5])
+ U.userError("cmd de-sync")
+ if (resp[6] != 0 && resp[6] != 8 /* EOF */)
+ U.userError("cmd error: " + resp[6])
+ return resp
+ })
+ }
+
+ flashAsync(path: string, file: Uint8Array) {
+ log(`write ${file.length} to ${path}`)
+ let begin = this.alloc(4 + path.length + 1, 0x92)
+ HF2.write32(begin, 6, file.length) // fileSize
+ U.memcpy(begin, 10, U.stringToUint8Array(path))
+
+ let loopAsync = (pos: number): Promise => {
+ if (pos >= file.length) return Promise.resolve()
+ let size = file.length - pos
+ if (size > 1000) size = 1000
+ let upl = this.alloc(1 + size, 0x93, 0x1)
+ upl[6] = handle
+ U.memcpy(upl, 6 + 1, file, pos, size)
+ return this.talkAsync(upl)
+ .then(() => loopAsync(pos + size))
+ }
+
+ let handle = -1
+ return this.talkAsync(begin)
+ .then(resp => {
+ handle = resp[7]
+ return loopAsync(0)
+ })
+ }
+
+ lsAsync(path: string): Promise {
+ let lsReq = this.alloc(2 + path.length + 1, 0x99)
+ HF2.write16(lsReq, 6, 1024) // maxRead
+ U.memcpy(lsReq, 8, U.stringToUint8Array(path))
+
+ return this.talkAsync(lsReq)
+ .then(resp =>
+ U.uint8ArrayToString(resp.slice(12)).split(/\n/).map(s => {
+ if (!s) return null as DirEntry
+ let m = /^([A-F0-9]+) ([A-F0-9]+) ([^\/]*)$/.exec(s)
+ if (m)
+ return {
+ md5: m[1],
+ size: parseInt(m[2], 16),
+ name: m[3]
+ }
+ else
+ return {
+ name: s.replace(/\/$/, "")
+ }
+ }).filter(v => !!v))
+ }
+
+ private initAsync() {
+ return Promise.resolve()
+ }
+
+ private resetState() {
+
+ }
+
+ reconnectAsync(first = false): Promise {
+ this.resetState()
+ if (first) return this.initAsync()
+ log(`reconnect`);
+ return this.io.reconnectAsync()
+ .then(() => this.initAsync())
+ }
+
+ disconnectAsync() {
+ log(`disconnect`);
+ return this.io.disconnectAsync()
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/pxtarget.json b/pxtarget.json
index 0b72ed4f..d4c44209 100644
--- a/pxtarget.json
+++ b/pxtarget.json
@@ -39,6 +39,11 @@
"taggedInts": true,
"stackAlign": 2
},
+ "serial": {
+ "vendorId": "0x0694",
+ "productId": "0x0005",
+ "rawHID": true
+ },
"runtime": {
"mathBlocks": true,
"loopsBlocks": true,