2017-04-19 01:26:13 +02:00
/// <reference path="../node_modules/pxt-core/built/pxteditor.d.ts" />
2017-09-18 18:45:27 +02:00
interface Math {
imul ( x : number , y : number ) : number ;
}
2017-04-19 01:26:13 +02:00
namespace pxt . editor {
2017-09-18 18:45:27 +02:00
import UF2 = pxtc . UF2 ;
const pageSize = 1024 ;
const numPages = 256 ;
function murmur3_core ( data : Uint8Array ) {
let h0 = 0x2F9BE6CC ;
let h1 = 0x1EC3A6C8 ;
for ( let i = 0 ; i < data . length ; i += 4 ) {
let k = HF2 . read32 ( data , i ) >>> 0
k = Math . imul ( k , 0xcc9e2d51 ) ;
k = ( k << 15 ) | ( k >>> 17 ) ;
k = Math . imul ( k , 0x1b873593 ) ;
h0 ^= k ;
h1 ^= k ;
h0 = ( h0 << 13 ) | ( h0 >>> 19 ) ;
h1 = ( h1 << 13 ) | ( h1 >>> 19 ) ;
h0 = ( Math . imul ( h0 , 5 ) + 0xe6546b64 ) >>> 0 ;
h1 = ( Math . imul ( h1 , 5 ) + 0xe6546b64 ) >>> 0 ;
}
return [ h0 , h1 ]
}
class DAPWrapper {
cortexM : DapJS.CortexM
2017-10-05 07:37:13 +02:00
packetIo : HF2.PacketIO ;
2018-07-27 22:28:58 +02:00
cmsisdap : any ;
flashing = true ;
private useSerial = true ;
2017-09-18 18:45:27 +02:00
constructor ( h : HF2.PacketIO ) {
2017-10-05 07:37:13 +02:00
this . packetIo = h ;
let pbuf = new U . PromiseBuffer < Uint8Array > ( ) ;
2017-09-18 18:45:27 +02:00
2018-07-27 22:28:58 +02:00
/ *
2017-09-18 18:45:27 +02:00
let sendMany = ( cmds : Uint8Array [ ] ) = > {
2017-10-05 07:37:13 +02:00
return h . talksAsync ( cmds . map ( c = > ( { cmd : 0 , data : c } ) ) ) ;
2017-09-18 18:45:27 +02:00
}
if ( ! h . talksAsync )
2017-10-05 07:37:13 +02:00
sendMany = null ;
2018-07-27 22:28:58 +02:00
* /
2017-09-18 18:45:27 +02:00
let dev = new DapJS . DAP ( {
write : writeAsync ,
2017-10-05 07:37:13 +02:00
close : this.disconnectAsync ,
2017-09-18 18:45:27 +02:00
read : readAsync ,
2018-07-27 22:28:58 +02:00
//sendMany: sendMany
2017-10-05 07:37:13 +02:00
} ) ;
2018-07-27 22:28:58 +02:00
this . cmsisdap = ( dev as any ) . dap ;
2017-10-05 07:37:13 +02:00
this . cortexM = new DapJS . CortexM ( dev ) ;
2017-09-18 18:45:27 +02:00
h . onData = buf = > {
2017-10-05 07:37:13 +02:00
pbuf . push ( buf ) ;
2017-09-18 18:45:27 +02:00
}
function writeAsync ( data : ArrayBuffer ) {
2018-03-21 18:11:56 +01:00
return h . sendPacketAsync ( new Uint8Array ( data ) ) ;
2017-09-18 18:45:27 +02:00
}
function readAsync() {
2017-10-05 07:37:13 +02:00
return pbuf . shiftAsync ( ) ;
2017-09-18 18:45:27 +02:00
}
2018-07-27 22:28:58 +02:00
const readSerial = ( ) = > {
if ( ! this . useSerial ) {
return
}
if ( this . flashing ) {
setTimeout ( readSerial , 300 )
return
}
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 ) {
U . nextTick ( readSerial )
window . postMessage ( {
type : 'serial' ,
id : 'n/a' , // TODO
data : str
} , "*" )
// console.log("SERIAL: " + str)
} else
setTimeout ( readSerial , 50 )
} , ( err : any ) = > {
setTimeout ( readSerial , 1000 )
} )
}
readSerial ( )
2017-09-18 18:45:27 +02:00
}
reconnectAsync ( first : boolean ) {
2018-03-21 18:11:56 +01:00
if ( ! first )
return this . packetIo . reconnectAsync ( )
2018-07-27 22:28:58 +02:00
// configure serial at 115200
. then ( ( ) = > this . cmsisdap . cmdNums ( 0x82 , [ 0x00 , 0xC2 , 0x01 , 0x00 ] ) )
. then ( ( ) = > { } , err = > { this . useSerial = false } )
2018-03-21 18:11:56 +01:00
. then ( ( ) = > this . cortexM . init ( ) )
else
return this . cortexM . init ( ) ;
2017-10-05 07:37:13 +02:00
}
disconnectAsync() {
return this . packetIo . disconnectAsync ( ) ;
2017-09-18 18:45:27 +02:00
}
}
2018-02-07 18:42:55 +01:00
let packetIoPromise : Promise < pxt.HF2.PacketIO > ;
function initPacketIOAsync ( ) : Promise < pxt.HF2.PacketIO > {
if ( ! packetIoPromise ) {
packetIoPromise = pxt . HF2 . mkPacketIOAsync ( )
. catch ( err = > {
packetIoPromise = null ;
return Promise . reject ( err ) ;
} ) ;
2018-02-07 21:55:50 +01:00
return packetIoPromise ;
} else {
let packetIo : pxt.HF2.PacketIO ;
return packetIoPromise
. then ( ( io ) = > {
packetIo = io ;
return io . reconnectAsync ( ) ;
} )
. then ( ( ) = > packetIo ) ;
2018-02-07 18:42:55 +01:00
}
}
2017-10-05 07:37:13 +02:00
let previousDapWrapper : DAPWrapper ;
2017-09-18 18:45:27 +02:00
function dapAsync() {
2018-03-21 18:11:56 +01:00
if ( previousDapWrapper )
return Promise . resolve ( previousDapWrapper )
2017-10-05 07:37:13 +02:00
return Promise . resolve ( )
. then ( ( ) = > {
if ( previousDapWrapper ) {
return previousDapWrapper . disconnectAsync ( )
. finally ( ( ) = > {
previousDapWrapper = null ;
} ) ;
}
return Promise . resolve ( ) ;
} )
2018-02-07 18:42:55 +01:00
. then ( ( ) = > initPacketIOAsync ( ) )
2017-09-18 18:45:27 +02:00
. then ( h = > {
let w = new DAPWrapper ( h )
2017-10-05 07:37:13 +02:00
previousDapWrapper = w ;
2017-09-18 18:45:27 +02:00
return w . reconnectAsync ( true )
2018-07-27 22:28:58 +02:00
. then ( ( ) = > {
return w
} )
2017-09-18 18:45:27 +02:00
} )
}
2018-02-26 16:43:02 +01:00
function canHID ( ) : boolean {
let r = false
2018-03-21 18:11:56 +01:00
if ( pxt . usb . isEnabled ) {
r = true
} else if ( U . isNodeJS ) {
2018-02-26 16:43:02 +01:00
r = true
2017-09-18 18:45:27 +02:00
} else {
const forceHexDownload = /forceHexDownload/i . test ( window . location . href ) ;
2017-10-05 07:37:13 +02:00
const isUwp = ! ! ( window as any ) . Windows ;
if ( Cloud . isLocalHost ( ) && Cloud . localToken && ! forceHexDownload || isUwp )
2018-02-26 16:43:02 +01:00
r = true
2017-09-18 18:45:27 +02:00
}
2018-02-26 16:43:02 +01:00
return r ;
}
2017-09-18 18:45:27 +02:00
2018-02-26 16:43:02 +01:00
function initAsync() {
if ( canHID ( ) ) {
2017-10-05 07:37:13 +02:00
return dapAsync ( ) ;
2017-09-18 18:45:27 +02:00
} else {
2017-10-05 07:37:13 +02:00
return Promise . reject ( new Error ( "no HID" ) )
2017-09-18 18:45:27 +02:00
}
}
function pageAlignBlocks ( blocks : UF2.Block [ ] , pageSize : number ) {
U . assert ( pageSize % 256 == 0 )
let res : UF2.Block [ ] = [ ]
for ( let i = 0 ; i < blocks . length ; ) {
let b0 = blocks [ i ]
let newbuf = new Uint8Array ( pageSize )
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
U . memcpy ( newbuf , b . targetAddr - newAddr , b . data , 0 , b . payloadSize )
}
let bb = U . flatClone ( b0 )
bb . data = newbuf
bb . targetAddr = newAddr
bb . payloadSize = pageSize
res . push ( bb )
}
return res
}
const flashPageBINquick = new Uint32Array ( [
0xbe00be00 , // bkpt - LR is set to this
0x2480b5f0 , 0x00e42300 , 0x58cd58c2 , 0xd10342aa , 0x42a33304 , 0xbdf0d1f8 ,
0x4b162502 , 0x509d4a16 , 0x2d00591d , 0x24a1d0fc , 0x511800e4 , 0x3cff3c09 ,
0x591e0025 , 0xd0fc2e00 , 0x509c2400 , 0x2c00595c , 0x2401d0fc , 0x509c2580 ,
0x595c00ed , 0xd0fc2c00 , 0x00ed2580 , 0x002e2400 , 0x5107590f , 0x2f00595f ,
0x3404d0fc , 0xd1f742ac , 0x50992100 , 0x2a00599a , 0xe7d0d0fc , 0x4001e000 ,
0x00000504 ,
] )
// doesn't check if data is already there - for timing
const flashPageBIN = new Uint32Array ( [
0xbe00be00 , // bkpt - LR is set to this
0x2402b5f0 , 0x4a174b16 , 0x2480509c , 0x002500e4 , 0x2e00591e , 0x24a1d0fc ,
0x511800e4 , 0x2c00595c , 0x2400d0fc , 0x2480509c , 0x002500e4 , 0x2e00591e ,
0x2401d0fc , 0x595c509c , 0xd0fc2c00 , 0x00ed2580 , 0x002e2400 , 0x5107590f ,
0x2f00595f , 0x3404d0fc , 0xd1f742ac , 0x50992100 , 0x2a00599a , 0xbdf0d0fc ,
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 . log ( ` HID ${ ts } : ${ msg } ` )
}
const membase = 0x20000000
const loadAddr = membase
const dataAddr = 0x20002000
const stackAddr = 0x20001000
export const bufferConcat = ( bufs : Uint8Array [ ] ) = > {
let len = 0 ;
for ( const b of bufs ) {
len += b . length ;
}
const r = new Uint8Array ( len ) ;
len = 0 ;
for ( const b of bufs ) {
r . set ( b , len ) ;
len += b . length ;
}
return r ;
} ;
function getFlashChecksumsAsync ( wrap : DAPWrapper ) {
log ( "getting existing flash checksums" )
let pages = numPages
return wrap . cortexM . runCode ( computeChecksums2 , loadAddr , loadAddr + 1 , 0xffffffff , stackAddr , true ,
dataAddr , 0 , pageSize , pages )
. then ( ( ) = > wrap . cortexM . memory . readBlock ( dataAddr , pages * 2 , pageSize ) )
}
function onlyChanged ( blocks : UF2.Block [ ] , checksums : Uint8Array ) {
return blocks . filter ( b = > {
let idx = b . targetAddr / pageSize
U . assert ( ( idx | 0 ) == idx )
U . assert ( b . data . length == pageSize )
if ( idx * 8 + 8 > checksums . length )
return true // out of range?
let c0 = HF2 . read32 ( checksums , idx * 8 )
let c1 = HF2 . read32 ( checksums , idx * 8 + 4 )
let ch = murmur3_core ( b . data )
if ( c0 == ch [ 0 ] && c1 == ch [ 1 ] )
return false
return true
} )
}
2018-04-21 19:25:43 +02:00
export function deployCoreAsync ( resp : pxtc.CompileResult ) : Promise < void > {
2017-09-18 18:45:27 +02:00
let saveHexAsync = ( ) = > {
2017-10-17 03:35:45 +02:00
return pxt . commands . saveOnlyAsync ( resp )
2017-09-18 18:45:27 +02:00
}
startTime = 0
let wrap : DAPWrapper
log ( "init" )
let logV = ( msg : string ) = > { }
//let logV = log
const runFlash = ( b : UF2.Block , dataAddr : number ) = > {
const cmd = wrap . 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 ) ;
return Promise . resolve ( )
. then ( ( ) = > {
logV ( "setregs" )
return cmd . go ( )
} )
. then ( ( ) = > {
logV ( "dbg en" )
// starts the program
return wrap . cortexM . debug . enable ( )
} )
}
let checksums : Uint8Array
2018-02-20 17:49:28 +01:00
pxt . tickEvent ( "hid.flash.start" ) ;
2018-07-27 22:28:58 +02:00
return Promise . resolve ( )
. then ( ( ) = > {
if ( previousDapWrapper ) {
previousDapWrapper . flashing = true
return Promise . delay ( 100 )
}
return Promise . resolve ( )
} )
. then ( initAsync )
2017-09-18 18:45:27 +02:00
. then ( w = > {
wrap = w
log ( "reset" )
return wrap . cortexM . reset ( true )
2018-03-21 18:11:56 +01:00
. catch ( e = > {
log ( "trying re-connect" )
return wrap . reconnectAsync ( false )
. then ( ( ) = > wrap . cortexM . reset ( true ) )
} )
2017-09-18 18:45:27 +02:00
} )
2018-02-16 20:39:12 +01:00
. then ( ( ) = > wrap . cortexM . memory . readBlock ( 0x10001014 , 1 , pageSize ) )
. then ( v = > {
2018-02-20 17:49:28 +01:00
if ( HF2 . read32 ( v , 0 ) != 0x3C000 ) {
pxt . tickEvent ( "hid.flash.uicrfail" ) ;
2018-02-16 20:39:12 +01:00
U . userError ( U . lf ( "Please flash any MakeCode hex file using drag and drop. Flashing from app will work afterwards." ) )
2018-02-20 17:49:28 +01:00
}
2018-02-16 20:39:12 +01:00
} )
2017-09-18 18:45:27 +02:00
. then ( ( ) = > getFlashChecksumsAsync ( wrap ) )
. then ( buf = > {
checksums = buf
log ( "write code" )
return wrap . cortexM . memory . writeBlock ( loadAddr , flashPageBIN )
} )
. then ( ( ) = > {
log ( "convert" )
// TODO this is seriously inefficient (130ms on a fast machine)
let uf2 = UF2 . newBlockFile ( )
UF2 . writeHex ( uf2 , resp . outfiles [ pxtc . BINARY_HEX ] . split ( /\r?\n/ ) )
let bytes = U . stringToUint8Array ( UF2 . serializeFile ( uf2 ) )
let parsed = UF2 . parseFile ( bytes )
let aligned = pageAlignBlocks ( parsed , pageSize )
log ( ` initial: ${ aligned . length } pages ` )
aligned = onlyChanged ( aligned , checksums )
log ( ` incremental: ${ aligned . length } pages ` )
return Promise . mapSeries ( U . range ( aligned . length ) ,
i = > {
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 + pageSize
let nextAddr = ( i & 1 ) ? dataAddr + 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 ] = HF2 . read32 ( b . data , i )
writeBl = wrap . 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 wrap . cortexM . memory . writeBlock ( nextAddr , buf )
} )
. then ( ( ) = > {
logV ( "wait" )
return wrap . cortexM . waitForHalt ( 500 )
} )
. then ( ( ) = > {
logV ( "done block" )
} )
} )
. then ( ( ) = > {
log ( "flash done" )
2018-02-20 17:49:28 +01:00
pxt . tickEvent ( "hid.flash.done" ) ;
2017-09-18 18:45:27 +02:00
return wrap . cortexM . reset ( false )
} )
2018-07-27 22:28:58 +02:00
. then ( ( ) = > {
wrap . flashing = false ;
} )
2017-09-18 18:45:27 +02:00
} )
2018-02-07 18:42:55 +01:00
. catch ( e = > {
2018-04-21 19:25:43 +02:00
// TODO: (microbit master)
if ( e . type === "devicenotfound" ) { //&& d.reportDeviceNotFoundAsync) {
2018-02-20 17:49:28 +01:00
pxt . tickEvent ( "hid.flash.devicenotfound" ) ;
2018-04-21 19:25:43 +02:00
//return d.reportDeviceNotFoundAsync("/device/windows-app/troubleshoot", resp);
return undefined ;
2017-10-17 03:35:45 +02:00
} else {
2017-10-20 19:27:43 +02:00
return saveHexAsync ( )
2018-02-07 18:42:55 +01:00
}
2017-09-18 18:45:27 +02:00
} )
}
2018-05-16 00:10:30 +02:00
/ * *
* < 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" > `
2018-07-27 22:12:36 +02:00
# # # # #
. . . . #
. . . . .
. . . . #
2018-05-16 00:10:30 +02:00
. . . . #
`
< / field >
< / block >
* /
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 = U . toArray ( dom . querySelectorAll ( "block[type=device_show_leds]" ) )
. concat ( U . toArray ( dom . querySelectorAll ( "block[type=device_build_image]" ) ) )
. concat ( U . toArray ( dom . querySelectorAll ( "block[type=device_build_big_image]" ) ) )
2018-07-27 22:12:36 +02:00
nodes . forEach ( node = > {
2018-06-18 23:28:17 +02:00
// don't rewrite if already upgraded, eg. field LEDS already present
if ( U . toArray ( node . children ) . filter ( child = > child . tagName == "field" && "LEDS" == child . getAttribute ( "name" ) ) [ 0 ] )
return ;
// read LEDxx value and assmebly into a new field
2018-05-16 00:10:30 +02:00
const leds : string [ ] [ ] = [ [ ] , [ ] , [ ] , [ ] , [ ] ] ;
2018-06-12 15:05:18 +02:00
U . toArray ( node . children )
. filter ( child = > child . tagName == "field" && /^LED\d+$/ . test ( child . getAttribute ( "name" ) ) )
2018-05-16 00:10:30 +02:00
. forEach ( lednode = > {
let n = lednode . getAttribute ( "name" ) ;
let col = parseInt ( n [ 3 ] ) ;
let row = parseInt ( n [ 4 ] ) ;
leds [ row ] [ col ] = lednode . innerHTML == "TRUE" ? "#" : "." ;
2018-06-12 15:05:18 +02:00
// remove node
node . removeChild ( lednode ) ;
2018-05-16 00:10:30 +02:00
} ) ;
2018-06-12 15:05:18 +02:00
// add new field
2018-05-16 00:10:30 +02:00
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 ) ) ;
2018-06-12 15:05:18 +02:00
node . insertBefore ( f , null ) ;
2018-05-16 00:10:30 +02:00
} ) ;
// 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 > = { } ;
2018-05-16 00:20:01 +02:00
function addField ( node : Element , renameMap : pxt.Map < string > , name : string ) {
2018-05-16 00:10:30 +02:00
const f = node . ownerDocument . createElement ( "field" ) ;
2018-05-16 00:20:01 +02:00
f . setAttribute ( "name" , "HANDLER_" + name )
f . setAttribute ( "id" , varids [ renameMap [ name ] || name ] ) ;
2018-05-16 00:10:30 +02:00
f . appendChild ( node . ownerDocument . createTextNode ( name ) ) ;
node . appendChild ( f ) ;
}
U . toArray ( dom . querySelectorAll ( "variable" ) ) . forEach ( node = > varids [ node . innerHTML ] = node . getAttribute ( "id" ) ) ;
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" ) || "{}" ) ;
2018-07-27 22:12:36 +02:00
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 ) {
2018-05-16 00:10:30 +02:00
node . setAttribute ( "type" , "radio_on_value" ) ;
node . removeChild ( node . querySelector ( "field[name=receivedNumber]" ) ) ;
node . removeChild ( node . querySelector ( "field[name=receivedString]" ) ) ;
2018-05-16 00:20:01 +02:00
addField ( node , renameMap , "name" ) ;
addField ( node , renameMap , "value" ) ;
2018-07-27 22:12:36 +02:00
node . removeChild ( mutation ) ;
}
2018-05-16 00:10:30 +02:00
}
} )
2018-07-30 23:13:28 +02:00
// device_random now refers to randomRange() so we need to add the missing lower bound argument
U . toArray ( dom . querySelectorAll ( "block[type=device_random]" ) )
. forEach ( node = > {
2018-08-13 21:51:26 +02:00
if ( getValue ( node , "min" ) ) return ;
2018-07-30 23:13:28 +02:00
const v = node . ownerDocument . createElement ( "value" ) ;
v . setAttribute ( "name" , "min" ) ;
2018-08-13 21:51:26 +02:00
addNumberShadow ( v ) ;
2018-07-30 23:13:28 +02:00
node . appendChild ( v ) ;
} ) ;
2018-08-13 21:51:26 +02:00
/ *
< 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 >
* /
U . toArray ( dom . querySelectorAll ( "block[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" ) ;
} )
2018-05-16 00:10:30 +02:00
}
2017-09-18 18:45:27 +02:00
initExtensionsAsync = function ( opts : pxt.editor.ExtensionOptions ) : Promise < pxt.editor.ExtensionResult > {
2017-04-19 01:26:13 +02:00
pxt . debug ( 'loading microbit target extensions...' )
2017-09-18 18:45:27 +02:00
if ( ! Math . imul )
2018-07-27 22:28:58 +02:00
Math . imul = function ( a , b ) {
2018-06-12 15:05:18 +02:00
const ah = ( a >>> 16 ) & 0xffff ;
const al = a & 0xffff ;
const bh = ( b >>> 16 ) & 0xffff ;
const bl = b & 0xffff ;
2017-09-18 18:45:27 +02:00
// the shift by 0 fixes the sign on the high part
// the final |0 converts the unsigned value into a signed value
return ( ( al * bl ) + ( ( ( ah * bl + al * bh ) << 16 ) >>> 0 ) | 0 ) ;
} ;
2017-04-19 01:26:13 +02:00
const res : pxt.editor.ExtensionResult = {
hexFileImporters : [ {
id : "blockly" ,
canImport : data = > data . meta . cloudId == "microbit.co.uk" && data . meta . editor == "blockly" ,
importAsync : ( project , data ) = > project . createProjectAsync ( {
filesOverride : {
"main.blocks" : data . source
} , name : data.meta.name
} )
2017-10-05 07:37:13 +02:00
} ]
2017-04-19 01:26:13 +02:00
} ;
2018-02-26 16:43:02 +01:00
2018-03-21 18:11:56 +01:00
pxt . usb . setFilters ( [ {
vendorId : 0x0D28 ,
productId : 0x0204 ,
classCode : 0xff ,
subclassCode : 0x03
} ] )
2018-02-26 16:43:02 +01:00
if ( canHID ( ) )
2018-08-10 00:30:37 +02:00
pxt . commands . deployCoreAsync = ( r : pxtc.CompileResult , d : pxt.commands.DeployOptions ) : Promise < void > = > {
return deployCoreAsync ( r )
. timeout ( 18000 )
. catch ( ( e ) = > {
return previousDapWrapper . reconnectAsync ( true )
. catch ( ( e ) = > {
// Best effort disconnect; at this point we don't even know the state of the device
pxt . reportException ( e ) ;
} )
. then ( ( ) = > {
return r . confirmAsync ( {
header : lf ( "Something went wrong..." ) ,
body : lf ( "Flashing your {0} took too long. Please disconnect your {0} from your computer and try reconnecting it." , pxt . appTarget . appTheme . boardName || lf ( "device" ) ) ,
disagreeLbl : lf ( "Ok" ) ,
hideAgree : true
} ) ;
} )
. then ( ( ) = > {
return pxt . commands . saveOnlyAsync ( r ) ;
} ) ;
} ) ;
}
2018-05-16 00:10:30 +02:00
res . blocklyPatch = patchBlocks ;
2017-04-19 01:26:13 +02:00
return Promise . resolve < pxt.editor.ExtensionResult > ( res ) ;
}
2017-09-18 18:45:27 +02:00
2018-08-13 21:51:26 +02:00
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 ) ;
}
2017-09-18 18:45:27 +02:00
}