diff --git a/src/worker/assembler.ts b/src/worker/assembler.ts index 58686a48..d4d730ff 100644 --- a/src/worker/assembler.ts +++ b/src/worker/assembler.ts @@ -1,8 +1,14 @@ +type Endian = 'big' | 'little'; + +type Symbol = { + value: number +} + type AssemblerVar = { bits : number, toks : string[], - endian? : 'big' | 'little', + endian? : Endian, iprel? : boolean, ipofs? : number, ipmul? : number, @@ -38,6 +44,7 @@ type AssemblerFixup = { iprel:boolean, ipofs:number, ipmul:number, + endian:Endian }; type AssemblerSpec = { @@ -96,7 +103,7 @@ export class Assembler { ip = 0; origin = 0; linenum = 0; - symbols : {[name:string] : {value:number}} = {}; + symbols : {[name:string] : Symbol} = {}; errors : AssemblerError[] = []; outwords : number[] = []; asmlines : AssemblerLine[] = []; @@ -152,7 +159,7 @@ export class Assembler { preprocessRules() { if (this.spec.width) { - this.width = this.spec.width|0; + this.width = this.spec.width || 8; } for (var rule of this.spec.rules) { this.rule2regex(rule, this.spec.vars); @@ -220,23 +227,17 @@ export class Assembler { return parseInt(s); } - changeEndian(endian: 'big'|'little', x: number, nbits: number) { - if (endian == null || endian == 'big') { - return x; - } else if (endian == 'little') { - var y = 0; - while (nbits > 0) { - var n = Math.min(nbits, this.width); - var mask = (1 << n) - 1; - y <<= n; - y |= (x & mask); - x >>>= n; - nbits -= n; - } - return y; - } else { - this.fatal('Endian must be "big" or "little"'); + swapEndian(x: number, nbits: number) { + var y = 0; + while (nbits > 0) { + var n = Math.min(nbits, this.width); + var mask = (1 << n) - 1; + y <<= n; + y |= (x & mask); + x >>>= n; + nbits -= n; } + return y; } buildInstruction(rule:AssemblerRule, m:string[]) : AssemblerLineResult { @@ -273,12 +274,12 @@ export class Assembler { } else { // otherwise, parse it as a constant x = this.parseConst(id, n); - x = this.changeEndian(v.endian, x, v.bits); // is it a label? add fixup if (isNaN(x)) { this.fixups.push({ sym:id, ofs:this.ip, size:v.bits, line:this.linenum, dstlen:n, dstofs:oplen, srcofs:shift, + endian:v.endian, iprel:!!v.iprel, ipofs:(v.ipofs+0), ipmul:v.ipmul||1 }); x = 0; @@ -288,6 +289,8 @@ export class Assembler { return {error:"Value " + x + " does not fit in " + v.bits + " bits"}; } } + // if little endian, we need to swap ordering + if (v.endian == 'little') x = this.swapEndian(x, v.bits); // is it an array slice? slice the bits if (typeof b !== "number") { x = (x >>> shift) & ((1 << b.n)-1); @@ -381,35 +384,49 @@ export class Assembler { this.warning(lastError ? lastError : ("Could not decode instruction: " + line)); } + applyFixup(fix: AssemblerFixup, sym: Symbol) { + var ofs = fix.ofs + Math.floor(fix.dstofs/this.width); + var mask = ((1< mask || value < -mask)) + this.warning("Symbol " + fix.sym + " (" + value + ") does not fit in " + fix.dstlen + " bits", fix.line); + //console.log(hex(value,8), fix.srcofs, fix.dstofs, fix.dstlen); + if (fix.srcofs > 0) + value >>>= fix.srcofs; + value &= (1 << fix.dstlen) - 1; + // TODO: make it work for all widths + if (this.width == 32) { + var shift = 32 - fix.dstofs - fix.dstlen; + value <<= shift; + } + // TODO: check range + if (fix.size <= this.width) { + this.outwords[ofs - this.origin] ^= value; + } else { + // swap if we want big endian (we'll apply in LSB first order) + if (fix.endian == 'big') value = this.swapEndian(value, fix.size); + // apply multi-byte fixup + while (value) { + if (value & this.outwords[ofs - this.origin]) { + this.warning("Instruction bits overlapped: " + hex(this.outwords[ofs - this.origin],8), hex(value,8)); + } else { + this.outwords[ofs - this.origin] ^= value & ((1<>>= this.width; + ofs++; + } + } + } + finish() : AssemblerState { // apply fixups for (var i=0; i mask || value < -mask)) - this.warning("Symbol " + fix.sym + " (" + value + ") does not fit in " + fix.dstlen + " bits", fix.line); - //console.log(hex(value,8), fix.srcofs, fix.dstofs, fix.dstlen); - if (fix.srcofs > 0) - value >>>= fix.srcofs; - value &= (1 << fix.dstlen) - 1; - // TODO: make it work for all widths - if (this.width == 32) { - var shift = 32 - fix.dstofs - fix.dstlen; - value <<= shift; - //console.log(fix, shift, fix.dstlen, hex(value,8)); - } - // TODO: check range - // TODO: span multiple words? - if (value & this.outwords[ofs - this.origin]) { - //this.warning("Instruction bits overlapped: " + hex(this.outwords[ofs - this.origin],8), hex(value,8)); - } - this.outwords[ofs - this.origin] ^= value; // TODO: << shift? + this.applyFixup(fix, sym); } else { this.warning("Symbol '" + fix.sym + "' not found"); } diff --git a/test/cli/testasm.js b/test/cli/testasm.js index 49c00db9..b441fa35 100644 --- a/test/cli/testasm.js +++ b/test/cli/testasm.js @@ -177,7 +177,7 @@ WaitVsync: //assert.equal(result, {}); assert.equal(128, result.origin); assert.equal(152, result.ip); - console.log(result); + assert.deepEqual([], result.errors); assert.deepEqual({ insns: "0B", line: 13, @@ -215,7 +215,6 @@ WaitVsync: `; let asm = new assembler.Assembler(EXAMPLE_SPEC); let result = asm.assembleFile(source); - console.log(result); assert.deepEqual( [ { msg: "Can't use 'c' here, only one of: a, b, ip, none", line: 2 } ], result.errors); @@ -246,6 +245,7 @@ WaitVsync: `; let asm = new assembler.Assembler(femto16_spec); let result = asm.assembleFile(source); + assert.deepEqual([], result.errors); assert.deepEqual(result.lines, [ { line: 5, offset: 32768, nbits: 32, insns: '1E58 6FFF' }, { line: 6, offset: 32770, nbits: 32, insns: '1B58 8006' }, @@ -289,6 +289,7 @@ WaitVsync: `; let asm = new assembler.Assembler(riscv_spec); let result = asm.assembleFile(source); + assert.deepEqual([], result.errors); assert.deepEqual(result.lines, [ { line: 7, offset: 0, nbits: 32, insns: '002100B3' }, { line: 8, offset: 1, nbits: 32, insns: '402001B3' }, @@ -304,7 +305,7 @@ WaitVsync: ]); }); - it('Should assemble 16-bit constants', function() { + it('Should assemble multiword little endian constants', function() { let source = ` .arch Beaker8 .org 0 @@ -326,10 +327,10 @@ init: call setTextMode setTextMode: send vdpReg0, $14 const.w $2000 ;// length - const.w.0 ;// address + const.w.0 ;// address + const.w init ;// symbol call clrVram ret - clrVram: send vdpReg1 send vdpReg2 @@ -342,23 +343,24 @@ _loop: `; let asm = new assembler.Assembler(BEAKER8_SPEC); let result = asm.assembleFile(source); - console.log(result.lines); + assert.deepEqual([], result.errors); assert.deepEqual(result.lines, [ { line: 13, offset: 0, nbits: 8, insns: 'E4' }, - { line: 14, offset: 1, nbits: 24, insns: 'E9 00 00' }, - { line: 16, offset: 4, nbits: 24, insns: 'EB 00 00' }, + { line: 14, offset: 1, nbits: 24, insns: 'E9 04 00' }, + { line: 16, offset: 4, nbits: 24, insns: 'EB 08 00' }, { line: 17, offset: 7, nbits: 8, insns: 'E1' }, { line: 20, offset: 8, nbits: 32, insns: '06 40 29 40' }, { line: 21, offset: 12, nbits: 24, insns: '0E 00 20' }, { line: 22, offset: 15, nbits: 8, insns: '08' }, - { line: 23, offset: 16, nbits: 24, insns: 'EB 00 00' }, - { line: 24, offset: 19, nbits: 8, insns: 'F8' }, - { line: 27, offset: 20, nbits: 16, insns: '29 41' }, - { line: 28, offset: 22, nbits: 16, insns: '29 42' }, - { line: 31, offset: 24, nbits: 8, insns: '00' }, - { line: 32, offset: 25, nbits: 16, insns: '29 00' }, - { line: 33, offset: 27, nbits: 8, insns: 'EF' }, - { line: 34, offset: 28, nbits: 8, insns: 'F8' } + { line: 23, offset: 16, nbits: 24, insns: '0E 04 00' }, + { line: 24, offset: 19, nbits: 24, insns: 'EB 17 00' }, + { line: 25, offset: 22, nbits: 8, insns: 'F8' }, + { line: 27, offset: 23, nbits: 16, insns: '29 41' }, + { line: 28, offset: 25, nbits: 16, insns: '29 42' }, + { line: 31, offset: 27, nbits: 8, insns: '00' }, + { line: 32, offset: 28, nbits: 16, insns: '29 00' }, + { line: 33, offset: 30, nbits: 8, insns: 'EF' }, + { line: 34, offset: 31, nbits: 8, insns: 'F8' } ]); })