in pxtlib/emitter/assembler.ts [580:785]
private handleDirective(l: Line) {
let words = l.words;
let expectOne = () => {
if (words.length != 2)
this.directiveError(lf("expecting one argument"));
}
let num0: number;
switch (words[0]) {
case ".ascii":
case ".asciz":
case ".string":
this.emitString(l.text);
break;
case ".utf16":
this.emitString(l.text, true);
break;
case ".align":
expectOne();
num0 = this.parseOneInt(words[1]);
if (num0 != null) {
if (num0 == 0) return;
if (num0 <= 4) {
this.align(1 << num0);
} else {
this.directiveError(lf("expecting 1, 2, 3 or 4 (for 2, 4, 8, or 16 byte alignment)"))
}
} else this.directiveError(lf("expecting number"));
break;
case ".balign":
expectOne();
num0 = this.parseOneInt(words[1]);
if (num0 != null) {
if (num0 == 1) return;
if (num0 == 2 || num0 == 4 || num0 == 8 || num0 == 16) {
this.align(num0);
} else {
this.directiveError(lf("expecting 2, 4, 8, or 16"))
}
} else this.directiveError(lf("expecting number"));
break;
case ".p2align":
expectOne();
num0 = this.parseOneInt(words[1]);
if (num0 != null) {
this.align(1 << num0);
} else this.directiveError(lf("expecting number"));
break;
case ".byte":
this.emitBytes(words);
break;
case ".hex":
this.emitHex(words);
break;
case ".hword":
case ".short":
case ".2bytes":
this.parseNumbers(words).forEach(n => {
// we allow negative numbers
if (-0x8000 <= n && n <= 0xffff)
this.emitShort(n & 0xffff)
else
this.directiveError(lf("expecting int16"))
})
break;
case ".word":
case ".4bytes":
case ".long":
// TODO: a word is machine-dependent (16-bit for AVR, 32-bit for ARM)
this.parseNumbers(words).forEach(n => {
// we allow negative numbers
if (-0x80000000 <= n && n <= 0xffffffff) {
this.emitShort(n & 0xffff)
this.emitShort((n >> 16) & 0xffff)
} else {
this.directiveError(lf("expecting int32"))
}
})
break;
case ".skip":
case ".space":
this.emitSpace(words);
break;
case ".set":
case ".equ":
if (!/^\w+$/.test(words[1]))
this.directiveError(lf("expecting name"))
const nums = this.parseNumbers(words.slice(words[2] == "," || words[2] == "="
? 2 : 1))
if (nums.length != 1)
this.directiveError(lf("expecting one value"))
if (this.equs[words[1]] !== undefined &&
this.equs[words[1]] != nums[0])
this.directiveError(lf("redefinition of {0}", words[1]))
this.equs[words[1]] = nums[0]
break;
case ".startaddr":
if (this.location())
this.directiveError(lf(".startaddr can be only be specified at the beginning of the file"))
expectOne()
this.baseOffset = this.parseOneInt(words[1]);
break;
// The usage for this is as follows:
// push {...}
// @stackmark locals ; locals := sp
// ... some push/pops ...
// ldr r0, [sp, locals@3] ; load local number 3
// ... some push/pops ...
// @stackempty locals ; expect an empty stack here
case "@stackmark":
expectOne();
this.stackpointers[words[1]] = this.stack;
break;
case "@stackempty":
if (this.checkStack) {
if (this.stackpointers[words[1]] == null)
this.directiveError(lf("no such saved stack"))
else if (this.stackpointers[words[1]] != this.stack)
this.directiveError(lf("stack mismatch"))
}
break;
case "@scope":
this.scope = words[1] || "";
this.currLineNo = this.scope ? 0 : this.realCurrLineNo;
break;
case ".syntax":
case "@nostackcheck":
this.checkStack = false
break
case "@dummystack":
expectOne()
this.stack += this.parseOneInt(words[1]);
break
case ".section":
case ".global":
this.stackpointers = {};
this.stack = 0;
this.scope = "$S" + this.scopeId++
break;
case ".comm": {
words = words.filter(x => x != ",")
words.shift()
let sz = this.parseOneInt(words[1])
let align = 0
if (words[2])
align = this.parseOneInt(words[2])
else
align = 4 // not quite what AS does...
let val = this.lookupLabel(words[0])
if (val == null) {
if (!this.commPtr) {
this.commPtr = this.lookupExternalLabel("_pxt_comm_base") || 0
if (!this.commPtr)
this.directiveError(lf("PXT_COMM_BASE not defined"))
}
while (this.commPtr & (align - 1))
this.commPtr++
this.labels[this.scopedName(words[0])] = this.commPtr - this.baseOffset
this.commPtr += sz
}
break
}
case ".file":
case ".text":
case ".cpu":
case ".fpu":
case ".eabi_attribute":
case ".code":
case ".thumb_func":
case ".type":
case ".fnstart":
case ".save":
case ".size":
case ".fnend":
case ".pad":
case ".globl": // TODO might need this one
case ".local":
break;
case "@":
// @ sp needed
break;
default:
if (/^\.cfi_/.test(words[0])) {
// ignore
} else {
this.directiveError(lf("unknown directive"))
}
break;
}
}