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,