private handleDirective()

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;
            }
        }