Bump V3.0.22 (#110)
* change simulator svg * change radio image * Remove google fonts cdn * change color of 'advanced' button * font fix * font fix 2 * display fix * change fullsceen simulator bg * Continuous servo * handle continuous state * adding shims * update rendering for continuous servos * fixing sim * fix sig * typo * fix sim * bump pxt * bump pxt * rerun travis * Input blocks revision - add Button and Pin event types - merge onPinPressed & onPinReleased in new onPinEvent function - create new onButtonEvent function * update input blocks in docs and tests * remove device_pin_release block * Hide DAL.x behind Enum * bring back deprecated blocks, but hide them * shims and locales files * fix input.input. typing * remove buildpr * bump V3 * update simulator aspect ratio * add Loudness Block * revoke loudness block * Adds soundLevel To be replaced by pxt-common-packages when DAL is updated. * Remove P0 & P3 from AnalogPin Co-authored-by: Juri <gitkraken@juriwolf.de>
This commit is contained in:
123
editor/dialogs.tsx
Normal file
123
editor/dialogs.tsx
Normal file
@ -0,0 +1,123 @@
|
||||
import * as React from "react";
|
||||
|
||||
export function renderUsbPairDialog(firmwareUrl?: string, failedOnce?: boolean): JSX.Element {
|
||||
const boardName = pxt.appTarget.appTheme.boardName || "???";
|
||||
const helpUrl = pxt.appTarget.appTheme.usbDocs;
|
||||
firmwareUrl = failedOnce && `${helpUrl}/webusb/troubleshoot`; // todo mo
|
||||
|
||||
const instructions = <div className="ui grid">
|
||||
<div className="row">
|
||||
<div className="column">
|
||||
<div className="ui two column grid padded">
|
||||
<div className="column">
|
||||
<div className="ui">
|
||||
<div className="image">
|
||||
<img alt={lf("Comic connecting micro:bit to computer")} className="ui medium rounded image" src="./static/download/connect.png" />
|
||||
</div>
|
||||
<div className="content">
|
||||
<div className="description">
|
||||
<span className="ui purple circular label">1</span>
|
||||
<strong>{lf("Connect the {0} to your computer with a USB cable", boardName)}</strong>
|
||||
<br />
|
||||
<span className="ui small">{lf("Use the microUSB port on the top of the {0}", boardName)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="column">
|
||||
<div className="ui">
|
||||
<div className="image">
|
||||
<img alt={lf("Comic of successful micro:bit connection")} className="ui medium rounded image" src="./static/download/pair.png" />
|
||||
</div>
|
||||
<div className="content">
|
||||
<div className="description">
|
||||
<span className="ui purple circular label">2</span>
|
||||
<strong>{lf("Pair your {0}", boardName)}</strong>
|
||||
<br />
|
||||
<span className="ui small">{lf("Click 'Pair device' below and select 'Calliope mini' or 'DAPLink CMSIS-DAP' from the list")}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
|
||||
if (!firmwareUrl) return instructions;
|
||||
|
||||
return <div className="ui grid stackable">
|
||||
<div className="column five wide firmware orange">
|
||||
<div className="ui header inverted">{lf("Update Firmware")}</div>
|
||||
<strong className="ui small">{lf("You must have version 0249 or above of the firmware")}</strong>
|
||||
<div className="image">
|
||||
<img alt={lf("Comic rainbow updating micro:bit firmware")} className="ui image" src="./static/download/firmware.png" />
|
||||
</div>
|
||||
<a className="ui button" role="button" href={firmwareUrl} target="_blank">{lf("Check Firmware")}</a>
|
||||
</div>
|
||||
<div className="column eleven wide instructions">
|
||||
{instructions}
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
export function renderBrowserDownloadInstructions(): JSX.Element {
|
||||
const boardName = pxt.appTarget.appTheme.boardName || lf("device");
|
||||
const boardDriveName = pxt.appTarget.appTheme.driveDisplayName || pxt.appTarget.compile.driveName || "???";
|
||||
return <div className="ui grid stackable upload">
|
||||
<div className="column sixteen wide instructions">
|
||||
<div className="ui grid">
|
||||
<div className="row">
|
||||
<div className="column">
|
||||
<div className="ui two column grid padded">
|
||||
<div className="column">
|
||||
<div className="ui">
|
||||
<div className="image">
|
||||
<img alt={lf("Comic connecting micro:bit to computer")} className="ui medium rounded image" src="./static/download/connect.png" />
|
||||
</div>
|
||||
<div className="content">
|
||||
<div className="description">
|
||||
<span className="ui purple circular label">1</span>
|
||||
<strong>{lf("Connect the {0} to your computer with a USB cable", boardName)}</strong>
|
||||
<br />
|
||||
<span className="ui small">{lf("Use the microUSB port on the top of the {0}", boardName)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="column">
|
||||
<div className="ui">
|
||||
<div className="image">
|
||||
<img alt={lf("Comic moving hex file to micro:bit")} className="ui medium rounded image" src="./static/download/transfer.png" />
|
||||
</div>
|
||||
<div className="content">
|
||||
<div className="description">
|
||||
<span className="ui purple circular label">2</span>
|
||||
<strong>{lf("Move the .hex file to the {0}", boardName)}</strong>
|
||||
<br />
|
||||
<span className="ui small">{lf("Locate the downloaded .hex file and drag it to the {0} drive", boardDriveName)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
export function cantImportAsync(project: pxt.editor.IProjectView) {
|
||||
// this feature is support in v0 only
|
||||
return project.showModalDialogAsync({
|
||||
header: lf("Can't import microbit.co.uk scripts..."),
|
||||
body: lf("Importing microbit.co.uk programs is not supported in this editor anymore. Please open this script in the https://makecode.microbit.org/v0 editor."),
|
||||
buttons: [
|
||||
{
|
||||
label: lf("Go to the old editor"),
|
||||
url: `https://makecode.microbit.org/v0`
|
||||
}
|
||||
]
|
||||
}).then(() => project.openHome())
|
||||
}
|
1029
editor/extension.tsx
1029
editor/extension.tsx
File diff suppressed because it is too large
Load Diff
400
editor/flash.ts
Normal file
400
editor/flash.ts
Normal file
@ -0,0 +1,400 @@
|
||||
const imul = (Math as any).imul;
|
||||
const timeoutMessage = "timeout"
|
||||
const membase = 0x20000000
|
||||
const loadAddr = membase
|
||||
const dataAddr = 0x20002000
|
||||
const stackAddr = 0x20001000
|
||||
|
||||
const flashPageBIN = new Uint32Array([
|
||||
0xbe00be00, // bkpt - LR is set to this
|
||||
0x2502b5f0, 0x4c204b1f, 0xf3bf511d, 0xf3bf8f6f, 0x25808f4f, 0x002e00ed,
|
||||
0x2f00595f, 0x25a1d0fc, 0x515800ed, 0x2d00599d, 0x2500d0fc, 0xf3bf511d,
|
||||
0xf3bf8f6f, 0x25808f4f, 0x002e00ed, 0x2f00595f, 0x2501d0fc, 0xf3bf511d,
|
||||
0xf3bf8f6f, 0x599d8f4f, 0xd0fc2d00, 0x25002680, 0x00f60092, 0xd1094295,
|
||||
0x511a2200, 0x8f6ff3bf, 0x8f4ff3bf, 0x2a00599a, 0xbdf0d0fc, 0x5147594f,
|
||||
0x2f00599f, 0x3504d0fc, 0x46c0e7ec, 0x4001e000, 0x00000504,
|
||||
])
|
||||
|
||||
// void computeHashes(uint32_t *dst, uint8_t *ptr, uint32_t pageSize, uint32_t numPages)
|
||||
const computeChecksums2 = new Uint32Array([
|
||||
0x4c27b5f0, 0x44a52680, 0x22009201, 0x91004f25, 0x00769303, 0x24080013,
|
||||
0x25010019, 0x40eb4029, 0xd0002900, 0x3c01407b, 0xd1f52c00, 0x468c0091,
|
||||
0xa9044665, 0x506b3201, 0xd1eb42b2, 0x089b9b01, 0x23139302, 0x9b03469c,
|
||||
0xd104429c, 0x2000be2a, 0x449d4b15, 0x9f00bdf0, 0x4d149e02, 0x49154a14,
|
||||
0x3e01cf08, 0x2111434b, 0x491341cb, 0x405a434b, 0x4663405d, 0x230541da,
|
||||
0x4b10435a, 0x466318d2, 0x230541dd, 0x4b0d435d, 0x2e0018ed, 0x6002d1e7,
|
||||
0x9a009b01, 0x18d36045, 0x93003008, 0xe7d23401, 0xfffffbec, 0xedb88320,
|
||||
0x00000414, 0x1ec3a6c8, 0x2f9be6cc, 0xcc9e2d51, 0x1b873593, 0xe6546b64,
|
||||
])
|
||||
|
||||
let startTime = 0
|
||||
function log(msg: string) {
|
||||
let now = Date.now()
|
||||
if (!startTime) startTime = now
|
||||
now -= startTime
|
||||
let ts = ("00000" + now).slice(-5)
|
||||
pxt.debug(`dap ${ts}: ${msg}`)
|
||||
}
|
||||
|
||||
|
||||
function murmur3_core(data: Uint8Array) {
|
||||
let h0 = 0x2F9BE6CC;
|
||||
let h1 = 0x1EC3A6C8;
|
||||
|
||||
for (let i = 0; i < data.length; i += 4) {
|
||||
let k = pxt.HF2.read32(data, i) >>> 0
|
||||
k = imul(k, 0xcc9e2d51);
|
||||
k = (k << 15) | (k >>> 17);
|
||||
k = imul(k, 0x1b873593);
|
||||
|
||||
h0 ^= k;
|
||||
h1 ^= k;
|
||||
h0 = (h0 << 13) | (h0 >>> 19);
|
||||
h1 = (h1 << 13) | (h1 >>> 19);
|
||||
h0 = (imul(h0, 5) + 0xe6546b64) >>> 0;
|
||||
h1 = (imul(h1, 5) + 0xe6546b64) >>> 0;
|
||||
}
|
||||
return [h0, h1]
|
||||
}
|
||||
|
||||
class DAPWrapper implements pxt.packetio.PacketIOWrapper {
|
||||
familyID: number;
|
||||
private dap: DapJS.DAP;
|
||||
private cortexM: DapJS.CortexM
|
||||
private cmsisdap: any;
|
||||
private flashing = false;
|
||||
private readSerialId = 0;
|
||||
private pbuf = new pxt.U.PromiseBuffer<Uint8Array>();
|
||||
private pageSize = 1024;
|
||||
private numPages = 256;
|
||||
private binName = pxtc.BINARY_HEX;
|
||||
|
||||
constructor(public readonly io: pxt.packetio.PacketIO) {
|
||||
this.familyID = 0x1366; // this is the microbit vendor id, not quite UF2 family id
|
||||
this.io.onDeviceConnectionChanged = (connect) =>
|
||||
this.disconnectAsync()
|
||||
.then(() => connect && this.reconnectAsync());
|
||||
this.io.onData = buf => {
|
||||
// console.log("RD: " + pxt.Util.toHex(buf))
|
||||
this.pbuf.push(buf);
|
||||
}
|
||||
|
||||
this.allocDAP();
|
||||
}
|
||||
|
||||
icon = "usb";
|
||||
|
||||
private startReadSerial() {
|
||||
log(`start read serial`)
|
||||
const rid = this.readSerialId;
|
||||
const readSerial = () => {
|
||||
if (rid != this.readSerialId) {
|
||||
log(`stopped read serial ${rid}`)
|
||||
return;
|
||||
}
|
||||
if (this.flashing) {
|
||||
setTimeout(readSerial, 500);
|
||||
return;
|
||||
}
|
||||
// done
|
||||
this.cmsisdap.cmdNums(0x83, [])
|
||||
.then((r: number[]) => {
|
||||
const len = r[1]
|
||||
let str = ""
|
||||
for (let i = 2; i < len + 2; ++i) {
|
||||
str += String.fromCharCode(r[i])
|
||||
}
|
||||
if (str.length > 0) {
|
||||
pxt.U.nextTick(readSerial)
|
||||
if (this.onSerial) {
|
||||
const utf8Str = pxt.U.toUTF8(str);
|
||||
this.onSerial(pxt.U.stringToUint8Array(utf8Str), false)
|
||||
}
|
||||
} else
|
||||
setTimeout(readSerial, 50)
|
||||
}, (err: any) => {
|
||||
log(`read error: ` + err.message);
|
||||
this.disconnectAsync(); // force disconnect
|
||||
});
|
||||
}
|
||||
readSerial();
|
||||
}
|
||||
|
||||
private stopSerialAsync() {
|
||||
log(`stopping serial reader`)
|
||||
this.readSerialId++;
|
||||
return Promise.delay(200);
|
||||
}
|
||||
|
||||
onSerial: (buf: Uint8Array, isStderr: boolean) => void;
|
||||
|
||||
private allocDAP() {
|
||||
log(`alloc dap`);
|
||||
this.dap = new DapJS.DAP({
|
||||
write: writeAsync,
|
||||
close: this.disconnectAsync,
|
||||
read: readAsync,
|
||||
//sendMany: sendMany
|
||||
});
|
||||
this.cmsisdap = (this.dap as any).dap;
|
||||
this.cortexM = new DapJS.CortexM(this.dap);
|
||||
|
||||
const h = this.io;
|
||||
const pbuf = this.pbuf;
|
||||
function writeAsync(data: ArrayBuffer) {
|
||||
//console.log("WR: " + pxt.Util.toHex(new Uint8Array(data)));
|
||||
return h.sendPacketAsync(new Uint8Array(data));
|
||||
}
|
||||
|
||||
function readAsync() {
|
||||
return pbuf.shiftAsync();
|
||||
}
|
||||
}
|
||||
|
||||
reconnectAsync(): Promise<void> {
|
||||
log(`reconnect`)
|
||||
// configure serial at 115200
|
||||
return this.stopSerialAsync()
|
||||
.then(() => this.io.reconnectAsync())
|
||||
.then(() => this.cortexM.init())
|
||||
.then(() => this.cmsisdap.cmdNums(0x80, []))
|
||||
.then(r => {
|
||||
this.binName = (r[2] == 57 && r[3] == 57 && r[5] >= 51 ? "mbcodal-" : "") + pxtc.BINARY_HEX
|
||||
})
|
||||
.then(() => this.cortexM.memory.readBlock(0x10000010, 2, this.pageSize))
|
||||
.then(res => {
|
||||
this.pageSize = pxt.HF2.read32(res, 0)
|
||||
this.numPages = pxt.HF2.read32(res, 4)
|
||||
})
|
||||
.then(() => this.cmsisdap.cmdNums(0x82, [0x00, 0xC2, 0x01, 0x00]))
|
||||
.then(() => this.startReadSerial());
|
||||
}
|
||||
|
||||
disconnectAsync() {
|
||||
log(`disconnect`)
|
||||
return this.stopSerialAsync()
|
||||
.then(() => this.io.disconnectAsync());
|
||||
}
|
||||
|
||||
reflashAsync(resp: pxtc.CompileResult): Promise<void> {
|
||||
log("reflash")
|
||||
startTime = 0
|
||||
pxt.tickEvent("hid.flash.start");
|
||||
this.flashing = true;
|
||||
return (this.io.isConnected() ? Promise.resolve() : this.io.reconnectAsync())
|
||||
.then(() => this.cortexM.init())
|
||||
.then(() => this.cortexM.reset(true))
|
||||
.then(() => this.cortexM.memory.readBlock(0x10001014, 1, this.pageSize))
|
||||
.then(v => {
|
||||
if ((pxt.HF2.read32(v, 0) & 0xff) != 0) {
|
||||
pxt.tickEvent("hid.flash.uicrfail");
|
||||
return this.fullVendorCommandFlashAsync(resp);
|
||||
}
|
||||
return this.quickHidFlashAsync(resp);
|
||||
})
|
||||
.finally(() => { this.flashing = false })
|
||||
.then(() => Promise.delay(100))
|
||||
.then(() => this.disconnectAsync())
|
||||
}
|
||||
|
||||
private fullVendorCommandFlashAsync(resp: pxtc.CompileResult): Promise<void> {
|
||||
log("full flash")
|
||||
|
||||
const chunkSize = 62;
|
||||
let aborted = false;
|
||||
return Promise.resolve()
|
||||
.then(() => {
|
||||
return this.cmsisdap.cmdNums(0x8A /* DAPLinkFlash.OPEN */, [1]);
|
||||
})
|
||||
.then((res) => {
|
||||
const hexUint8 = pxt.U.stringToUint8Array(resp.outfiles[this.binName]);
|
||||
const hexArray: number[] = Array.prototype.slice.call(hexUint8);
|
||||
|
||||
const sendPages = (offset: number = 0): Promise<void> => {
|
||||
const end = Math.min(hexArray.length, offset + chunkSize);
|
||||
const nextPage = hexArray.slice(offset, end);
|
||||
nextPage.unshift(nextPage.length);
|
||||
return this.cmsisdap.cmdNums(0x8C /* DAPLinkFlash.WRITE */, nextPage)
|
||||
.then(() => {
|
||||
if (!aborted && end < hexArray.length) {
|
||||
return sendPages(end);
|
||||
}
|
||||
return Promise.resolve();
|
||||
});
|
||||
}
|
||||
|
||||
return sendPages();
|
||||
})
|
||||
.then((res) => {
|
||||
return this.cmsisdap.cmdNums(0x8B /* DAPLinkFlash.CLOSE */, []);
|
||||
})
|
||||
.timeout(60000, timeoutMessage)
|
||||
.catch((e) => {
|
||||
aborted = true;
|
||||
return this.cmsisdap.cmdNums(0x89 /* DAPLinkFlash.RESET */, [])
|
||||
.catch((e2: any) => {
|
||||
// Best effort reset, no-op if there's an error
|
||||
})
|
||||
.then(() => {
|
||||
return Promise.reject(e);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private quickHidFlashAsync(resp: pxtc.CompileResult): Promise<void> {
|
||||
log("quick flash")
|
||||
let logV = (msg: string) => { }
|
||||
//let logV = log
|
||||
let aborted = false;
|
||||
|
||||
const runFlash = (b: ts.pxtc.UF2.Block, dataAddr: number) => {
|
||||
const cmd = this.cortexM.prepareCommand();
|
||||
|
||||
cmd.halt();
|
||||
|
||||
cmd.writeCoreRegister(DapJS.CortexReg.PC, loadAddr + 4 + 1);
|
||||
cmd.writeCoreRegister(DapJS.CortexReg.LR, loadAddr + 1);
|
||||
cmd.writeCoreRegister(DapJS.CortexReg.SP, stackAddr);
|
||||
|
||||
cmd.writeCoreRegister(0, b.targetAddr);
|
||||
cmd.writeCoreRegister(1, dataAddr);
|
||||
cmd.writeCoreRegister(2, this.pageSize >> 2);
|
||||
|
||||
return Promise.resolve()
|
||||
.then(() => {
|
||||
logV("setregs")
|
||||
return cmd.go()
|
||||
})
|
||||
.then(() => {
|
||||
logV("dbg en")
|
||||
// starts the program
|
||||
return this.cortexM.debug.enable()
|
||||
})
|
||||
}
|
||||
|
||||
let checksums: Uint8Array
|
||||
return this.getFlashChecksumsAsync()
|
||||
.then(buf => {
|
||||
checksums = buf;
|
||||
log("write code");
|
||||
return this.cortexM.memory.writeBlock(loadAddr, flashPageBIN);
|
||||
})
|
||||
.then(() => {
|
||||
log("convert");
|
||||
// TODO this is seriously inefficient (130ms on a fast machine)
|
||||
let uf2 = ts.pxtc.UF2.newBlockFile();
|
||||
ts.pxtc.UF2.writeHex(uf2, resp.outfiles[this.binName].split(/\r?\n/));
|
||||
let bytes = pxt.U.stringToUint8Array(ts.pxtc.UF2.serializeFile(uf2));
|
||||
let parsed = ts.pxtc.UF2.parseFile(bytes);
|
||||
|
||||
let aligned = DAPWrapper.pageAlignBlocks(parsed, this.pageSize);
|
||||
log(`initial: ${aligned.length} pages`);
|
||||
aligned = DAPWrapper.onlyChanged(aligned, checksums, this.pageSize);
|
||||
log(`incremental: ${aligned.length} pages`);
|
||||
|
||||
return Promise.mapSeries(pxt.U.range(aligned.length),
|
||||
i => {
|
||||
if (aborted) return Promise.resolve();
|
||||
let b = aligned[i];
|
||||
if (b.targetAddr >= 0x10000000)
|
||||
return Promise.resolve();
|
||||
|
||||
logV("about to write at 0x" + b.targetAddr.toString(16));
|
||||
|
||||
let writeBl = Promise.resolve();
|
||||
|
||||
let thisAddr = (i & 1) ? dataAddr : dataAddr + this.pageSize;
|
||||
let nextAddr = (i & 1) ? dataAddr + this.pageSize : dataAddr;
|
||||
|
||||
if (i == 0) {
|
||||
let u32data = new Uint32Array(b.data.length / 4);
|
||||
for (let i = 0; i < b.data.length; i += 4)
|
||||
u32data[i >> 2] = pxt.HF2.read32(b.data, i);
|
||||
writeBl = this.cortexM.memory.writeBlock(thisAddr, u32data);
|
||||
}
|
||||
|
||||
return writeBl
|
||||
.then(() => runFlash(b, thisAddr))
|
||||
.then(() => {
|
||||
let next = aligned[i + 1];
|
||||
if (!next)
|
||||
return Promise.resolve();
|
||||
logV("write next");
|
||||
let buf = new Uint32Array(next.data.buffer);
|
||||
return this.cortexM.memory.writeBlock(nextAddr, buf);
|
||||
})
|
||||
.then(() => {
|
||||
logV("wait");
|
||||
return this.cortexM.waitForHalt(500);
|
||||
})
|
||||
.then(() => {
|
||||
logV("done block");
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
log("flash done");
|
||||
pxt.tickEvent("hid.flash.done");
|
||||
return this.cortexM.reset(false);
|
||||
});
|
||||
})
|
||||
.timeout(25000, timeoutMessage)
|
||||
.catch((e) => {
|
||||
aborted = true;
|
||||
return Promise.reject(e);
|
||||
});
|
||||
}
|
||||
|
||||
private getFlashChecksumsAsync() {
|
||||
log("flash checksums")
|
||||
let pages = this.numPages
|
||||
return this.cortexM.runCode(computeChecksums2, loadAddr, loadAddr + 1, 0xffffffff, stackAddr, true,
|
||||
dataAddr, 0, this.pageSize, pages)
|
||||
.then(() => this.cortexM.memory.readBlock(dataAddr, pages * 2, this.pageSize))
|
||||
}
|
||||
|
||||
static onlyChanged(blocks: ts.pxtc.UF2.Block[], checksums: Uint8Array, pageSize: number) {
|
||||
return blocks.filter(b => {
|
||||
let idx = b.targetAddr / pageSize
|
||||
pxt.U.assert((idx | 0) == idx)
|
||||
pxt.U.assert(b.data.length == pageSize)
|
||||
if (idx * 8 + 8 > checksums.length)
|
||||
return true // out of range?
|
||||
let c0 = pxt.HF2.read32(checksums, idx * 8)
|
||||
let c1 = pxt.HF2.read32(checksums, idx * 8 + 4)
|
||||
let ch = murmur3_core(b.data)
|
||||
if (c0 == ch[0] && c1 == ch[1])
|
||||
return false
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
static pageAlignBlocks(blocks: ts.pxtc.UF2.Block[], pageSize: number) {
|
||||
pxt.U.assert(pageSize % 256 == 0)
|
||||
let res: ts.pxtc.UF2.Block[] = []
|
||||
for (let i = 0; i < blocks.length;) {
|
||||
let b0 = blocks[i]
|
||||
let newbuf = new Uint8Array(pageSize)
|
||||
for (let i = 0; i < newbuf.length; ++i)
|
||||
newbuf[i] = 0xff
|
||||
let startPad = b0.targetAddr & (pageSize - 1)
|
||||
let newAddr = b0.targetAddr - startPad
|
||||
for (; i < blocks.length; ++i) {
|
||||
let b = blocks[i]
|
||||
if (b.targetAddr + b.payloadSize > newAddr + pageSize)
|
||||
break
|
||||
pxt.U.memcpy(newbuf, b.targetAddr - newAddr, b.data, 0, b.payloadSize)
|
||||
}
|
||||
let bb = pxt.U.flatClone(b0)
|
||||
bb.data = newbuf
|
||||
bb.targetAddr = newAddr
|
||||
bb.payloadSize = pageSize
|
||||
res.push(bb)
|
||||
}
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
export function mkDAPLinkPacketIOWrapper(io: pxt.packetio.PacketIO): pxt.packetio.PacketIOWrapper {
|
||||
pxt.log(`packetio: mk wrapper dap wrapper`)
|
||||
return new DAPWrapper(io);
|
||||
}
|
263
editor/patch.ts
Normal file
263
editor/patch.ts
Normal file
@ -0,0 +1,263 @@
|
||||
/**
|
||||
* <block type="device_show_leds">
|
||||
<field name="LED00">FALSE</field>
|
||||
<field name="LED10">FALSE</field>
|
||||
<field name="LED20">FALSE</field>
|
||||
<field name="LED30">FALSE</field>
|
||||
<field name="LED40">FALSE</field>
|
||||
<field name="LED01">FALSE</field>
|
||||
<field name="LED11">FALSE</field>
|
||||
<field name="LED21">FALSE</field>
|
||||
<field name="LED31">TRUE</field>
|
||||
<field name="LED41">FALSE</field>
|
||||
<field name="LED02">FALSE</field>
|
||||
<field name="LED12">FALSE</field>
|
||||
<field name="LED22">FALSE</field>
|
||||
<field name="LED32">FALSE</field>
|
||||
<field name="LED42">FALSE</field>
|
||||
<field name="LED03">FALSE</field>
|
||||
<field name="LED13">TRUE</field>
|
||||
<field name="LED23">FALSE</field>
|
||||
<field name="LED33">FALSE</field>
|
||||
<field name="LED43">FALSE</field>
|
||||
<field name="LED04">FALSE</field>
|
||||
<field name="LED14">FALSE</field>
|
||||
<field name="LED24">FALSE</field>
|
||||
<field name="LED34">FALSE</field>
|
||||
<field name="LED44">FALSE</field>
|
||||
</block>
|
||||
|
||||
to
|
||||
<block type="device_show_leds">
|
||||
<field name="LEDS">`
|
||||
# # # # #
|
||||
. . . . #
|
||||
. . . . .
|
||||
. . . . #
|
||||
. . . . #
|
||||
`
|
||||
</field>
|
||||
</block>
|
||||
*/
|
||||
|
||||
export function patchBlocks(pkgTargetVersion: string, dom: Element) {
|
||||
// is this a old script?
|
||||
if (pxt.semver.majorCmp(pkgTargetVersion || "0.0.0", "1.0.0") >= 0) return;
|
||||
|
||||
// showleds
|
||||
const nodes = pxt.U.toArray(dom.querySelectorAll("block[type=device_show_leds]"))
|
||||
.concat(pxt.U.toArray(dom.querySelectorAll("block[type=device_build_image]")))
|
||||
.concat(pxt.U.toArray(dom.querySelectorAll("shadow[type=device_build_image]")))
|
||||
.concat(pxt.U.toArray(dom.querySelectorAll("block[type=device_build_big_image]")))
|
||||
.concat(pxt.U.toArray(dom.querySelectorAll("shadow[type=device_build_big_image]")));
|
||||
nodes.forEach(node => {
|
||||
// don't rewrite if already upgraded, eg. field LEDS already present
|
||||
if (pxt.U.toArray(node.children).filter(child => child.tagName == "field" && "LEDS" == child.getAttribute("name"))[0])
|
||||
return;
|
||||
// read LEDxx value and assmebly into a new field
|
||||
const leds: string[][] = [[], [], [], [], []];
|
||||
pxt.U.toArray(node.children)
|
||||
.filter(child => child.tagName == "field" && /^LED\d+$/.test(child.getAttribute("name")))
|
||||
.forEach(lednode => {
|
||||
let n = lednode.getAttribute("name");
|
||||
let col = parseInt(n[3]);
|
||||
let row = parseInt(n[4]);
|
||||
leds[row][col] = lednode.innerHTML == "TRUE" ? "#" : ".";
|
||||
// remove node
|
||||
node.removeChild(lednode);
|
||||
});
|
||||
// add new field
|
||||
const f = node.ownerDocument.createElement("field");
|
||||
f.setAttribute("name", "LEDS");
|
||||
const s = '`\n' + leds.map(row => row.join('')).join('\n') + '\n`';
|
||||
f.appendChild(node.ownerDocument.createTextNode(s));
|
||||
node.insertBefore(f, null);
|
||||
});
|
||||
|
||||
// radio
|
||||
/*
|
||||
<block type="radio_on_packet" x="174" y="120">
|
||||
<mutation callbackproperties="receivedNumber" renamemap="{}"></mutation>
|
||||
<field name="receivedNumber">receivedNumber</field>
|
||||
</block>
|
||||
<block type="radio_on_packet" disabled="true" x="127" y="263">
|
||||
<mutation callbackproperties="receivedString,receivedNumber" renamemap="{"receivedString":"name","receivedNumber":"value"}"></mutation>
|
||||
<field name="receivedString">name</field>
|
||||
<field name="receivedNumber">value</field>
|
||||
</block>
|
||||
<block type="radio_on_packet" disabled="true" x="162" y="420">
|
||||
<mutation callbackproperties="receivedString" renamemap="{}"></mutation>
|
||||
<field name="receivedString">receivedString</field>
|
||||
</block>
|
||||
|
||||
converts to
|
||||
|
||||
<block type="radio_on_number" x="196" y="208">
|
||||
<field name="HANDLER_receivedNumber" id="DCy(W;1)*jLWQUpoy4Mm" variabletype="">receivedNumber</field>
|
||||
</block>
|
||||
<block type="radio_on_value" x="134" y="408">
|
||||
<field name="HANDLER_name" id="*d-Jm^MJXO]Djs(dTR*?" variabletype="">name</field>
|
||||
<field name="HANDLER_value" id="A6HQjH[k^X43o3h775+G" variabletype="">value</field>
|
||||
</block>
|
||||
<block type="radio_on_string" x="165" y="583">
|
||||
<field name="HANDLER_receivedString" id="V9KsE!h$(iO?%W:[32CV" variabletype="">receivedString</field>
|
||||
</block>
|
||||
*/
|
||||
const varids: pxt.Map<string> = {};
|
||||
|
||||
function addField(node: Element, renameMap: pxt.Map<string>, name: string) {
|
||||
const f = node.ownerDocument.createElement("field");
|
||||
f.setAttribute("name", "HANDLER_" + name)
|
||||
f.setAttribute("id", varids[renameMap[name] || name]);
|
||||
f.appendChild(node.ownerDocument.createTextNode(name));
|
||||
node.appendChild(f);
|
||||
}
|
||||
|
||||
pxt.U.toArray(dom.querySelectorAll("variable")).forEach(node => varids[node.innerHTML] = node.getAttribute("id"));
|
||||
pxt.U.toArray(dom.querySelectorAll("block[type=radio_on_packet]"))
|
||||
.forEach(node => {
|
||||
const mutation = node.querySelector("mutation");
|
||||
if (!mutation) return;
|
||||
const renameMap = JSON.parse(node.getAttribute("renamemap") || "{}");
|
||||
const props = mutation.getAttribute("callbackproperties");
|
||||
|
||||
if (props) {
|
||||
const parts = props.split(",");
|
||||
|
||||
// It's tempting to generate radio_on_number if parts.length === 0 but
|
||||
// that would create a variable named "receivedNumber" and possibly shadow
|
||||
// an existing variable in the user's program. It's safer to stick to the
|
||||
// old block.
|
||||
if (parts.length === 1) {
|
||||
if (parts[0] === "receivedNumber") {
|
||||
node.setAttribute("type", "radio_on_number");
|
||||
node.removeChild(node.querySelector("field[name=receivedNumber]"));
|
||||
addField(node, renameMap, "receivedNumber");
|
||||
}
|
||||
else if (parts[0] === "receivedString") {
|
||||
node.setAttribute("type", "radio_on_string");
|
||||
node.removeChild(node.querySelector("field[name=receivedString]"));
|
||||
addField(node, renameMap, "receivedString");
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
node.removeChild(mutation);
|
||||
}
|
||||
else if (parts.length === 2 && parts.indexOf("receivedNumber") !== -1 && parts.indexOf("receivedString") !== -1) {
|
||||
node.setAttribute("type", "radio_on_value");
|
||||
node.removeChild(node.querySelector("field[name=receivedNumber]"));
|
||||
node.removeChild(node.querySelector("field[name=receivedString]"));
|
||||
addField(node, renameMap, "name");
|
||||
addField(node, renameMap, "value");
|
||||
node.removeChild(mutation);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
// device_random now refers to randomRange() so we need to add the missing lower bound argument
|
||||
pxt.U.toArray(dom.querySelectorAll("block[type=device_random]"))
|
||||
.concat(pxt.U.toArray(dom.querySelectorAll("shadow[type=device_random]")))
|
||||
.forEach(node => {
|
||||
if (getValue(node, "min")) return;
|
||||
const v = node.ownerDocument.createElement("value");
|
||||
v.setAttribute("name", "min");
|
||||
addNumberShadow(v);
|
||||
node.appendChild(v);
|
||||
});
|
||||
|
||||
/*
|
||||
<block type="math_arithmetic">
|
||||
<field name="OP">DIVIDE</field>
|
||||
<value name="A">
|
||||
<shadow type="math_number"><field name="NUM">0</field></shadow>
|
||||
<block type="math_number"><field name="NUM">2</field></block>
|
||||
</value>
|
||||
<value name="B">
|
||||
<shadow type="math_number"><field name="NUM">1</field></shadow>
|
||||
<block type="math_number"><field name="NUM">3</field></block>
|
||||
</value>
|
||||
</block>
|
||||
*/
|
||||
pxt.U.toArray(dom.querySelectorAll("block[type=math_arithmetic]"))
|
||||
.concat(pxt.U.toArray(dom.querySelectorAll("shadow[type=math_arithmetic]")))
|
||||
.forEach(node => {
|
||||
const op = getField(node, "OP");
|
||||
if (!op || op.textContent.trim() !== "DIVIDE") return;
|
||||
|
||||
// Convert to integer division
|
||||
/*
|
||||
<block type="math_js_op">
|
||||
<mutation op-type="infix"></mutation>
|
||||
<field name="OP">idiv</field>
|
||||
<value name="ARG0">
|
||||
<shadow type="math_number"><field name="NUM">0</field></shadow>
|
||||
</value>
|
||||
<value name="ARG1">
|
||||
<shadow type="math_number"><field name="NUM">0</field></shadow>
|
||||
</value>
|
||||
</block>
|
||||
*/
|
||||
|
||||
node.setAttribute("type", "math_js_op");
|
||||
op.textContent = "idiv";
|
||||
|
||||
const mutation = node.ownerDocument.createElement("mutation");
|
||||
mutation.setAttribute("op-type", "infix");
|
||||
// mutation has to be first or Blockly will drop the second argument
|
||||
node.insertBefore(mutation, node.firstChild);
|
||||
|
||||
const a = getValue(node, "A");
|
||||
if (a) a.setAttribute("name", "ARG0");
|
||||
|
||||
const b = getValue(node, "B");
|
||||
if (b) b.setAttribute("name", "ARG1");
|
||||
});
|
||||
|
||||
renameField(dom, "math_number_minmax", "NUM", "SLIDER");
|
||||
renameField(dom, "device_note", "note", "name");
|
||||
}
|
||||
|
||||
function renameField(dom: Element, blockType: string, oldName: string, newName: string) {
|
||||
pxt.U.toArray(dom.querySelectorAll(`block[type=${blockType}]`))
|
||||
.concat(pxt.U.toArray(dom.querySelectorAll(`shadow[type=${blockType}]`)))
|
||||
.forEach(node => {
|
||||
const thefield = getField(node, oldName);
|
||||
if (thefield) {
|
||||
thefield.setAttribute("name", newName);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function getField(parent: Element, name: string) {
|
||||
return getFieldOrValue(parent, name, true);
|
||||
}
|
||||
|
||||
function getValue(parent: Element, name: string) {
|
||||
return getFieldOrValue(parent, name, false);
|
||||
}
|
||||
|
||||
function getFieldOrValue(parent: Element, name: string, isField: boolean) {
|
||||
const nodeType = isField ? "field" : "value";
|
||||
for (let i = 0; i < parent.children.length; i++) {
|
||||
const child = parent.children.item(i);
|
||||
if (child.tagName === nodeType && child.getAttribute("name") === name) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function addNumberShadow(valueNode: Element) {
|
||||
const s = valueNode.ownerDocument.createElement("shadow");
|
||||
s.setAttribute("type", "math_number");
|
||||
|
||||
const f = valueNode.ownerDocument.createElement("field");
|
||||
f.setAttribute("name", "NUM");
|
||||
f.textContent = "0";
|
||||
|
||||
s.appendChild(f);
|
||||
valueNode.appendChild(s);
|
||||
}
|
Reference in New Issue
Block a user