nanoasm: fixed multi-byte fixups

This commit is contained in:
Steven Hugg 2021-07-12 18:05:37 -05:00
parent bf12984f26
commit 5109986557
2 changed files with 78 additions and 59 deletions

View File

@ -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<<fix.size)-1);
var value = this.parseConst(sym.value+"", fix.dstlen);
if (fix.iprel)
value = (value - fix.ofs) * fix.ipmul - fix.ipofs;
if (fix.srcofs == 0 && (value > 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)-1);
}
value >>>= this.width;
ofs++;
}
}
}
finish() : AssemblerState {
// apply fixups
for (var i=0; i<this.fixups.length; i++) {
var fix = this.fixups[i];
var sym = this.symbols[fix.sym];
if (sym) {
var ofs = fix.ofs + Math.floor(fix.dstofs/this.width);
var mask = ((1<<fix.size)-1);
var value = this.parseConst(sym.value+"", fix.dstlen);
if (fix.iprel)
value = (value - fix.ofs) * fix.ipmul - fix.ipofs;
if (fix.srcofs == 0 && (value > 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");
}

View File

@ -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' }
]);
})