espruino-web-ide
Version:
A Terminal and Graphical code Editor for Espruino JavaScript Microcontrollers
486 lines (439 loc) • 23.6 kB
JavaScript
/**
Copyright 2014 Gordon Williams (gw@pur3.co.uk)
This Source Code is subject to the terms of the Mozilla Public
License, v2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
------------------------------------------------------------------
Automatically run an assembler on inline assembler statements
------------------------------------------------------------------
**/
;
(function(){
var base64_encode;
if (typeof btoa == "undefined")
base64_encode = function(s) { return Buffer.from(s).toString('base64'); };
else
base64_encode = btoa;
/* Thumb reference :
http://ece.uwaterloo.ca/~ece222/ARM/ARM7-TDMI-manual-pt3.pdf
ARM reference
https://web.eecs.umich.edu/~prabal/teaching/eecs373-f11/readings/ARMv7-M_ARM.pdf
*/
// list of registers (for push/pop type commands)
function rlist_lr(value) {
var regs = value.split(",");
var vals = { r0:1,r1:2,r2:4,r3:8,r4:16,r5:32,r6:64,r7:128,lr:256 };
var bits = 0;
for (var i in regs) {
var reg = regs[i].trim();
if (!(reg in vals)) throw "Unknown register name "+reg;
bits |= vals[reg];
}
return bits;
}
function reg(reg_offset) {
return function(reg) {
var vals = { r0:0,r1:1,r2:2,r3:3,r4:4,r5:5,r6:6,r7:7 };
if (!(reg in vals)) throw "Unknown register name "+reg;
return vals[reg]<<reg_offset;
};
}
function reg4(reg_offset) { // 4 bit register
return function(reg) {
var vals = { r0:0,r1:1,r2:2,r3:3,r4:4,r5:5,r6:6,r7:7,
r8:8,r9:9,r10:10,r11:11,r12:12,r13:13,r14:14,r15:15,
lr:14, pc:15 };
if (!(reg in vals)) throw "Unknown register name "+reg;
return vals[reg]<<reg_offset;
};
}
function reg_or_immediate(reg_offset, immediate_bit) {
return function(reg) {
var regVal = parseInt(reg);
if (regVal>=0 && regVal<8)
return ((regVal&7)<<reg_offset) | (1<<immediate_bit);
var vals = { r0:0,r1:1,r2:2,r3:3,r4:4,r5:5,r6:6,r7:7 };
if (!(reg in vals)) throw "Unknown register name, or immediate out of range 0..7 "+reg;
return vals[reg]<<reg_offset;
};
}
function reg_base_offset(base_offset, offset_offset) {
return function(value) {
var parms = value.split(",");
return reg(base_offset)(parms[0]) | reg(offset_offset)(parms[0]);
};
}
function thumb2_immediate_t3(value) {
if (value[0]!="#")
throw new "Expecting '#' before number";
var v = parseInt(value.substr(1));
if (v>=0 && v<65536) {
// https://web.eecs.umich.edu/~prabal/teaching/eecs373-f11/readings/ARMv7-M_ARM.pdf page 347
var imm4,i,imm3,imm8; // what the...?
imm4 = (v>>12)&15;
i = (v>>11)&1;
imm3 = (v>>8)&7;
imm8 = v&255;
return (i<<26) | (imm4<<16) | (imm3<<12) | imm8;
}
throw "Invalid number '"+value+"' - must be between 0 and 65535";
}
function _int(offset, bits, shift, signed) {
return function(value, labels) {
var maxValue = ((1<<bits)-1) << shift;
var minValue = 0;
if (signed) {
minValue = -(1<<(bits-1));
maxValue += minValue;
}
var binValue = undefined;
if (value[0]=="#") {
binValue = parseInt(value.substr(1));
} else {
var addValue = 0;
var maths = value.indexOf("+");
if (maths >= 0) {
addValue = parseInt(value.substr(maths));
value = value.substr(0,maths);
}
if (value in labels)
binValue = labels[value] + addValue - labels["PC"];
else
throw "Unknown label '"+value+"'";
}
//console.log("VALUE----------- "+binValue+" PC "+labels["PC"]+" L "+labels[value]);
if (binValue>=minValue && binValue<=maxValue && (binValue&((1<<shift)-1))==0)
return ((binValue >> shift) & ((1<<bits)-1)) << offset;
var msg = "Invalid number '"+value+"' ("+binValue+") - must be between 0 and "+maxValue;
if (shift!=0) msg += " and a multiple of "+(1<<shift);
throw msg;
};
}
function uint(offset, bits, shift) {
return _int(offset, bits, shift, false);
}
function sint(offset, bits, shift) {
return _int(offset, bits, shift, true);
}
// special 23-bit address (bottom bit ignored) split into two halves
function bl_addr() {
var normal = sint(0, 22, 1); // un-split address
return function(value, labels) {
var v = normal(value, labels);
return ((v>>11)&0x7FF)<<16 | (v&0x7FF);
};
}
var ops = {
// Format 1: move shifted register
"lsl" :[{ base:"00000-----___---", regex : /(r[0-7]),(r[0-7]),(#[0-9]+)$/, args:[reg(0),reg(3),uint(6,5,0)] },
{ base:"0100000010___---", regex : /(r[0-7]),(r[0-7])$/, args:[reg(0),reg(3)] }], // 5.4 d = d << s
"lsr" :[{ base:"00001-----___---", regex : /(r[0-7]),(r[0-7]),(#[0-9]+)$/, args:[reg(0),reg(3),uint(6,5,0)] },
{ base:"0100000011___---", regex : /(r[0-7]),(r[0-7])$/, args:[reg(0),reg(3)] }], // 5.4 d = d >> s
"asr" :[{ base:"00010-----___---", regex : /(r[0-7]),(r[0-7]),(#[0-9]+)$/, args:[reg(0),reg(3),uint(6,5,0)] },
{ base:"0100000100___---", regex : /(r[0-7]),(r[0-7])$/, args:[reg(0),reg(3)] }], // 5.4 d = d >>> s
// 5.2 Format 2: add/subtract
// 00011
// 5.3 Format 3: move/compare/add/subtract immediate
"cmp" :[{ base:"00101---________", regex : /(r[0-7]),(#[0-9]+)$/, args:[reg(8),uint(0,8,0)] }, // move/compare/subtract immediate
{ base:"0100001010___---", regex : /(r[0-7]),(r[0-7])$/, args:[reg(0),reg(3)] }], // 5.4 test d-s
// 5.4 Format 4: ALU operations
"and" :[{ base:"0100000000___---", regex : /(r[0-7]),(r[0-7])$/, args:[reg(0),reg(3)] }],
"eor" :[{ base:"0100000001___---", regex : /(r[0-7]),(r[0-7])$/, args:[reg(0),reg(3)] }],
// lsl is above
// lsr is above
// asr is above
"adc" :[{ base:"0100000101___---", regex : /(r[0-7]),(r[0-7])$/, args:[reg(0),reg(3)] }], // d + s + carry
"sbc" :[{ base:"0100000110___---", regex : /(r[0-7]),(r[0-7])$/, args:[reg(0),reg(3)] }], // d - s - !carry
"ror" :[{ base:"0100000111___---", regex : /(r[0-7]),(r[0-7])$/, args:[reg(0),reg(3)] }], // rotate right
"tst" :[{ base:"0100001000___---", regex : /(r[0-7]),(r[0-7])$/, args:[reg(0),reg(3)] }], // test
"neg" :[{ base:"0100001001___---", regex : /(r[0-7]),(r[0-7])$/, args:[reg(0),reg(3)] }], // - s
// cmp is above
"cmn" :[{ base:"0100001011___---", regex : /(r[0-7]),(r[0-7])$/, args:[reg(0),reg(3)] }], // test d+s
"orr" :[{ base:"0100001100___---", regex : /(r[0-7]),(r[0-7])$/, args:[reg(0),reg(3)] }], // |
"mul" :[{ base:"0100001101___---", regex : /(r[0-7]),(r[0-7])$/, args:[reg(0),reg(3)] }], // s*d
"bic" :[{ base:"0100001110___---", regex : /(r[0-7]),(r[0-7])$/, args:[reg(0),reg(3)] }], // d & ~s
"mvn" :[{ base:"0100001111___---", regex : /(r[0-7]),(r[0-7])$/, args:[reg(0),reg(3)] }], // ~s
// 5.5 Format 5: Hi register operations/branch exchange
// 5.6 Format 6: PC-relative load
// done (below)
// 5.7 Format 7: load/store with register offset
// done (below)
// 5.8 Format 8: load/store sign-extended byte/halfword
// 5.9 Format 9: load/store with immediate offset
// done (below)
// 5.10 Format 10: load/store halfword
// 5.11 Format 11: SP-relative load/store
// 5.12 Format 12: load address
// done (below)
// 5.13 Format 13: add offset to Stack Pointer
// 5.14 Format 14: push/pop registers
// done (below)
// 5.16 Format 16: conditional branch
"beq" :[{ base:"11010000________", regex : /^(.*)$/, args:[sint(0,8,1)] }], // 5.16 Format 16: conditional branch
"bne" :[{ base:"11010001________", regex : /^(.*)$/, args:[sint(0,8,1)] }], // 5.16 Format 16: conditional branch
"bcs" :[{ base:"11010010________", regex : /^(.*)$/, args:[sint(0,8,1)] }], // 5.16 Format 16: conditional branch
"bcc" :[{ base:"11010011________", regex : /^(.*)$/, args:[sint(0,8,1)] }], // 5.16 Format 16: conditional branch
"bmi" :[{ base:"11010100________", regex : /^(.*)$/, args:[sint(0,8,1)] }], // 5.16 Format 16: conditional branch
"bpl" :[{ base:"11010101________", regex : /^(.*)$/, args:[sint(0,8,1)] }], // 5.16 Format 16: conditional branch
"bvs" :[{ base:"11010110________", regex : /^(.*)$/, args:[sint(0,8,1)] }], // 5.16 Format 16: conditional branch
"bvc" :[{ base:"11010111________", regex : /^(.*)$/, args:[sint(0,8,1)] }], // 5.16 Format 16: conditional branch
"bhi" :[{ base:"11011000________", regex : /^(.*)$/, args:[sint(0,8,1)] }], // 5.16 Format 16: conditional branch
"bls" :[{ base:"11011001________", regex : /^(.*)$/, args:[sint(0,8,1)] }], // 5.16 Format 16: conditional branch
"bge" :[{ base:"11011010________", regex : /^(.*)$/, args:[sint(0,8,1)] }], // 5.16 Format 16: conditional branch
"blt" :[{ base:"11011011________", regex : /^(.*)$/, args:[sint(0,8,1)] }], // 5.16 Format 16: conditional branch
"bgt" :[{ base:"11011100________", regex : /^(.*)$/, args:[sint(0,8,1)] }], // 5.16 Format 16: conditional branch
"ble" :[{ base:"11011101________", regex : /^(.*)$/, args:[sint(0,8,1)] }], // 5.16 Format 16: conditional branch
// 5.17 Format 17: software interrupt
// 5.18 Format 18: unconditional branch
"b" :[{ base:"11100___________", regex : /^(.*)$/, args:[sint(0,11,1)] }],
// 5.19 Format 19: long branch with link
"bl" :[{ base:"11110___________11111___________", regex : /^(.*)$/, args:[bl_addr()] }],
"bx" :[{ base:"010001110----000", regex : /(lr|r[0-9]+)$/, args:[reg4(3)] }],
// ....
"adr" :[{ base:"10100---________", regex : /^(r[0-7]),([a-zA-Z_][0-9a-zA-Z_]*)$/,args:[reg(8),uint(0,8,2)] }, // ADR pseudo-instruction to save address (actually ADD PC)
{ base:"10100---________", regex : /^(r[0-7]),([a-zA-Z_][0-9a-zA-Z_]*\+[0-9]+)$/,args:[reg(8),uint(0,8,2)] }],
"push" :[{ base:"1011010-________", regex : /^{(.*)}$/, args:[rlist_lr] }], // 5.14 Format 14: push/pop registers
"pop" :[{ base:"1011110-________", regex : /^{(.*)}$/, args:[rlist_lr] }], // 5.14 Format 14: push/pop registers
"add" :[{ base:"00110---________", regex : /(r[0-7]),(#[0-9]+)$/, args:[reg(8),uint(0,8,0)] }, // move/compare/subtract immediate
{ base:"10100---________", regex : /^(r[0-7]),pc,(#[0-9]+)$/,args:[reg(8),uint(0,8,2)] },
{ base:"10101---________", regex : /^(r[0-7]),sp,(#[0-9]+)$/, args:[reg(8),uint(0,8,2)] },
{ base:"101100000_______", regex : /^sp,(#[0-9]+)$/, args:[uint(0,7,2)] },
{ base:"00011-0___---___", regex : /^(r[0-7]),(r[0-7]),([^,]+)$/, args:[reg(0),reg(3),reg_or_immediate(6,10)] } ], // Format 2: add/subtract
"adds" :[{ base:"00011-0___---___", regex : /^(r[0-7]),(r[0-7]),([^,]+)$/, args:[reg(0),reg(3),reg_or_immediate(6,10)] } ], //?
"adc.w":[{ base:"111010110100----________--------", regex : /^(r[0-7]),(r[0-7]),(r[0-7])$/,args:[reg(16),reg(8),reg(0)] }], // made this up. probably wrong
"add.w":[{ base:"11110001--------________--------", regex : /^(r[0-7]),(r[0-7]),(#[0-9]+)$/,args:[reg(16),reg(8),uint(0,8,0)] }], // made this up. probably wrong
"sub" :[{ base:"00111---________", regex : /(r[0-7]),(#[0-9]+)$/, args:[reg(8),uint(0,8,0)] }, // move/compare/subtract immediate
/*{ base:"10100---________", regex : /^([^,]+),pc,(#[0-9]+)$/,args:[reg(8),uint(0,8,2)] },*/
{ base:"101100001_______", regex : /^sp,(#[0-9]+)$/, args:[uint(0,7,2)] },
{ base:"00011-1___---___", regex : /^([^,]+),([^,]+),([^,]+)$/, args:[reg(0),reg(3),reg_or_immediate(6,10)] } ],
"str" :[{ base:"0101000---___---", regex : /(r[0-7]),\[(r[0-7]),(r[0-7])\]$/, args:[reg(0),reg(3),reg(6)] }, // 5.7 Format 7: load/store with register offset
{ base:"10010---________", regex : /(r[0-7]),\[sp,(#[0-9]+)\]$/, args:[reg(8),uint(0,8,2)] }, // 5.11 SP-relative store
{ base:"0110000000___---", regex : /(r[0-7]),\[(r[0-7])\]$/, args:[reg(0),reg(3)] }, // 5.9 Format 9: load/store with no offset
{ base:"0110000---___---", regex : /(r[0-7]),\[(r[0-7]),(#[0-9]+)\]$/, args:[reg(0),reg(3), uint(6,5,2)] }], // 5.9 Format 9: load/store with immediate offset
"strb" :[{ base:"0101010---___---", regex : /(r[0-7]),\[(r[0-7]),(r[0-7])\]$/, args:[reg(0),reg(3),reg(6)] }, // 5.7 Format 7: load/store with register offset
{ base:"01110-----___---", regex : /(r[0-7]),\[(r[0-7]),(#[0-9]+)\]$/, args:[reg(0),reg(3), uint(6,5,0)] }], // 5.9 Format 9: load/store with immediate offset
"strh" :[{ base:"0101001---___---", regex : /(r[0-7]),\[(r[0-7]),(r[0-7])\]$/, args:[reg(0),reg(3),reg(6)] }, // 5.7 Format 7: load/store with register offset
{ base:"10000-----___---", regex : /(r[0-7]),\[(r[0-7]),(#[0-9]+)\]$/, args:[reg(0),reg(3), uint(6,5,1)] }], // 5.9 Format 9: load/store with immediate offset
"ldr" :[{ base:"01001---________", regex : /(r[0-7]),\[pc,(#[0-9]+)\]$/, args:[reg(8),uint(0,8,2)] }, // 5.6 Format 6: PC-relative load
{ base:"10011---________", regex : /(r[0-7]),\[sp,(#[0-9]+)\]$/, args:[reg(8),uint(0,8,2)] }, // 5.11 SP-relative load
{ base:"01001---________", regex : /(r[0-7]),([a-zA-Z_][0-9a-zA-Z_]*)$/, args:[reg(8),uint(0,8,2)] }, // 5.6 Format 6: PC-relative load (using label)
{ base:"01001---________", regex : /(r[0-7]),([a-zA-Z_][0-9a-zA-Z_]*\+[0-9]+)$/, args:[reg(8),uint(0,8,2)] }, // 5.6 Format 6: PC-relative load (using label and maths - huge hack)
{ base:"0101100---___---", regex : /(r[0-7]),\[(r[0-7]),(r[0-7])\]$/, args:[reg(0),reg(3),reg(6)] }, // 5.7 Format 7: load/store with register offset
{ base:"0110100000___---", regex : /(r[0-7]),\[(r[0-7])\]$/, args:[reg(0),reg(3)] }, // 5.9 Format 9: load/store with no offset
{ base:"0110100---___---", regex : /(r[0-7]),\[(r[0-7]),(#[0-9]+)\]$/, args:[reg(0),reg(3), uint(6,5,2)] }], // 5.9 Format 9: load/store with immediate offset
"ldrb" :[{ base:"0101110---___---", regex : /(r[0-7]),\[(r[0-7]),(r[0-7])\]$/, args:[reg(0),reg(3),reg(6)] }, // 5.7 Format 7: load/store with register offset
{ base:"01111-----___---", regex : /(r[0-7]),\[(r[0-7]),(#[0-9]+)\]$/, args:[reg(0),reg(3), uint(6,5,0)] }], // 5.9 Format 9: load/store with immediate offset
"ldrsb":[{ base:"0101011---___---", regex : /(r[0-7]),\[(r[0-7]),(r[0-7])\]$/, args:[reg(0),reg(3),reg(6)] }], // 5.7 Format 7: load/store with register offset
"ldrh" :[{ base:"0101101---___---", regex : /(r[0-7]),\[(r[0-7]),(r[0-7])\]$/, args:[reg(0),reg(3),reg(6)] }, // 5.7 Format 7: load/store with register offset
{ base:"10001-----___---", regex : /(r[0-7]),\[(r[0-7]),(#[0-9]+)\]$/, args:[reg(0),reg(3), uint(6,5,1)] }], // 5.9 Format 9: load/store with immediate offset
"ldrsh":[{ base:"0101111---___---", regex : /(r[0-7]),\[(r[0-7]),(r[0-7])\]$/, args:[reg(0),reg(3),reg(6)] }], // 5.7 Format 7: load/store with register offset
"mov" :[{ base:"00100---________", regex : /(r[0-7]),(#[0-9]+)$/, args:[reg(8),uint(0,8,0)] }, // move/compare/subtract immediate
{ base:"0001110000---___", regex : /(r[0-7]),(r[0-7])$/, args:[reg(0),reg(3)] }, // actually 'add Rd,Rs,#0'
{ base:"0100011010---101", regex : /sp,(r[0-7])$/, args:[reg(3)] }], // made up again
"movs" :[{ base:"00100---________", regex : /(r[0-7]),(#[0-9]+)$/, args:[reg(8),uint(0,8,0)] }], // is this even in thumb?
"movw" :[{ base:"11110-100100----0___----________", regex : /(r[0-7]),(#[0-9]+)$/, args:[reg4(8),thumb2_immediate_t3] }],
".word":[{ base:"--------------------------------", regex : /0x([0-9A-Fa-f]+)$/, args:[function(v){v=parseInt(v,16);return (v>>16)|(v<<16);}] },
{ base:"--------------------------------", regex : /([0-9]+)$/, args:[function(v){v=parseInt(v);return (v>>16)|(v<<16);}] }],
"nop" :[{ base:"0100011011000000", regex : "", args:[] }], // MOV R8,R8 (Format 5)
"cpsie" :[{ base:"1011011001100010", regex : /i/, args:[] }], // made up again
"cpsid" :[{ base:"1011011001110010", regex : /i/, args:[] }], // made up again
"wfe" :[{ base:"1011111100100000", regex : /i/, args:[] }],
"wfi" :[{ base:"1011111100110000", regex : /i/, args:[] }],
// for this, uint needs to work without a hash
// "swi" :[{ base:"11011111--------", regex : /([0-9]+)$/, args:[uint(0,8,0)] }], // Format 17: software interrupt
};
function getOpCode(binary) {
var base = "";
for (var b in binary)
if ("-_".indexOf(binary[b])>=0)
base += "0";
else
base += binary[b];
var opCode = parseInt(base,2);
if (opCode<0) opCode = opCode + 2147483648.0;
return opCode;
}
function assemble_internal(asmLines, wordCallback, labels) {
var addr = 0;
var newLabels = {};
asmLines.forEach(function (line) {
// setup labels
if (labels!==undefined)
labels["PC"] = addr+4;
// handle line
line = line.trim();
if (line=="") return;
if (line.substr(-1)==":") {
// it's a label
var labelName = line.substr(0,line.length-1);
if (newLabels[labelName] !== undefined)
throw "Label '"+labelName+"' was already defined";
newLabels[labelName] = addr;
return;
}
// parse instruction
var firstArgEnd = line.indexOf("\t");
if (firstArgEnd<0) firstArgEnd = line.indexOf(" ");
if (firstArgEnd<0) firstArgEnd=line.length;
var opName = line.substr(0,firstArgEnd);
var args = line.substr(firstArgEnd).replace(/[ \t]/g,"").trim();
if (!(opName in ops)) throw "Unknown Op '"+opName+"' in '"+line+"'";
// search ops
var found = false;
for (var n in ops[opName]) {
var op = ops[opName][n];
var m;
if (m=args.match(op.regex)) {
found = true;
// work out the base opcode
var opCode = getOpCode(op.base);
if (labels!==undefined) {
/* If we're properly generating code, parse each argument.
Otherwise we're just working out the size in bytes of each line
and we can skip this */
for (var i in op.args) {
//console.log(i,m[(i|0)+1]);
var argFunction = op.args[i];
var bits = argFunction(m[(i|0)+1], labels);
//console.log(" ",bits)
opCode |= bits;
}
}
if (op.base.length > 16) {
wordCallback((opCode>>>16));
wordCallback(opCode&0xFFFF);
addr += 4;
} else {
wordCallback(opCode);
addr += 2;
}
break;
}
}
// now parse args
if (!found)
throw "Unknown arg style '"+args+"' in '"+line+"'";
});
return newLabels;
}
function assemble(asmLines, wordCallback) {
// remove line comments
asmLines = asmLines.map(function(l) {
var i;
i = l.indexOf(";");
if (i>=0) l = l.substr(0,i);
i = l.indexOf("//");
if (i>=0) l = l.substr(0,i);
return l;
});
// process assembly to grab labels
var labels = assemble_internal(asmLines, function() {}, undefined);
console.log("Assembler Labels:",labels);
// process again to actually get an output
assemble_internal(asmLines, wordCallback, labels);
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
function init() {
// When code is sent to Espruino, search it for bits of assembler and then assemble them
Espruino.addProcessor("transformForEspruino", function(code, callback) {
findASMBlocks(code, "", callback);
});
// When a module is sent to Espruino...
Espruino.addProcessor("transformModuleForEspruino", function(module, callback) {
findASMBlocks(module.code, " in "+module.name, function(code) {
module.code = code;
callback(module);
});
});
}
function assembleBlock(asmLines, description) {
var machineCode = [];
try {
assemble(asmLines, function(word) { machineCode.push("0x"+word.toString(16)); });
} catch (err) {
console.log("Assembler failed: "+err+description);
Espruino.Core.Notifications.error("Assembler failed: "+err+description);
return undefined;
}
return machineCode;
}
/* Finds instances of 'E.asm' and replaces them */
function findASMBlocks(code, description, callback){
function match(str, type) {
if (str!==undefined && tok.str!=str) {
Espruino.Core.Notifications.error("Expecting '"+str+"' but got '"+tok.str+description+"'. Should have E.asm('arg spec', 'asmline1', ..., 'asmline2'");
return false;
}
if (type!==undefined && tok.type!=type) {
Espruino.Core.Notifications.error("Expecting a "+type+" but got "+tok.type+description+". Should have E.asm('arg spec', 'asmline1', ..., 'asmline2'");
return false;
}
tok = lex.next();
return true;
}
var foundAsm = true;
var assembledCode = "";
var asmBlockCount = 1;
while (foundAsm) {
foundAsm = false;
var lex = Espruino.Core.Utils.getLexer(code);
var tok = lex.next();
var state = 0;
var startIndex = -1;
while (tok!==undefined) {
if (state==0 && tok.str=="E") { state=1; startIndex = tok.startIdx; tok = lex.next();
} else if (state==1 && tok.str==".") { state=2; tok = lex.next();
} else if (state==2 && (tok.str=="asm")) { state=3; tok = lex.next();
} else if (state==3 && (tok.str=="(")) {
foundAsm = true;
state=0;
tok = lex.next(); // skip (
var argSpec = tok.value;
var asmLines = [];
if (!match(undefined,"STRING")) return;
if (!match(",",undefined)) return;
while (tok && tok.str!=")") {
var lines = tok.value.split("\n");
lines.forEach(function(l) {
asmLines.push(l);
});
if (!match(undefined,"STRING")) return;
if (tok.str!=")")
if (!match(",",undefined)) return;
}
if (!match(")",undefined)) return;
var endIndex = tok.endIdx;
var machineCode = assembleBlock(asmLines, description);
//console.log(machineCode);
if (machineCode===undefined) return; // There was an error - just leave and don't try to flash
var raw = "";
machineCode.forEach(function(short) {
var v = parseInt(short,16);
raw += String.fromCharCode(v&255,v>>8);
});
var base64 = base64_encode(raw);
code = code.substr(0,startIndex) +
'E.nativeCall(1, '+JSON.stringify(argSpec)+', atob('+JSON.stringify(base64)+'))'+
code.substr(endIndex);
asmBlockCount++;
// Break out
tok = undefined;
} else {
state = 0;
tok = lex.next();
}
}
}
if (assembledCode!="") {
code = "var ASM_BASE=process.memory().stackEndAddress;\n"+
assembledCode+
code;
}
callback(code);
};
Espruino.Plugins.Assembler = {
init : init,
};
}());