2018-02-18 05:12:09 +00:00
|
|
|
|
2018-07-11 04:04:28 +00:00
|
|
|
type AssemblerVar = {
|
|
|
|
bits : number,
|
|
|
|
toks : string[],
|
|
|
|
iprel? : boolean,
|
|
|
|
ipofs? : number
|
|
|
|
}
|
|
|
|
|
|
|
|
type AssemblerRule = {
|
|
|
|
fmt : string,
|
|
|
|
bits : (string | number)[],
|
|
|
|
// added at runtime
|
|
|
|
re? : RegExp,
|
|
|
|
prefix? : string,
|
|
|
|
varlist? : string[]
|
|
|
|
}
|
|
|
|
|
|
|
|
type AssemblerVarList = {[name:string] : AssemblerVar};
|
|
|
|
|
|
|
|
type AssemblerLine = {line:number, offset:number, nbits:number, insns?:string};
|
|
|
|
|
|
|
|
type AssemblerFixup = {
|
|
|
|
sym:string,
|
|
|
|
ofs:number,
|
|
|
|
bitlen:number,
|
|
|
|
bitofs:number,
|
|
|
|
line:number,
|
|
|
|
iprel:boolean,
|
|
|
|
ipofs:number
|
|
|
|
};
|
|
|
|
|
|
|
|
type AssemblerSpec = {
|
|
|
|
name : string,
|
|
|
|
width : number,
|
|
|
|
vars : AssemblerVarList,
|
|
|
|
rules : AssemblerRule[]
|
|
|
|
}
|
|
|
|
|
|
|
|
type AssemblerInstruction = {opcode:number, nbits : number};
|
2019-05-25 21:46:48 +00:00
|
|
|
type AssemblerErrorResult = {error:string};
|
|
|
|
type AssemblerLineResult = AssemblerErrorResult | AssemblerInstruction;
|
2018-07-11 04:04:28 +00:00
|
|
|
|
|
|
|
type AssemblerError = {msg:string, line:number};
|
|
|
|
|
|
|
|
type AssemblerState = {
|
|
|
|
ip: number,
|
|
|
|
line: number,
|
|
|
|
origin: number,
|
|
|
|
codelen: number,
|
|
|
|
intermediate: any,
|
|
|
|
output: number[],
|
|
|
|
lines: AssemblerLine[],
|
|
|
|
errors: AssemblerError[],
|
|
|
|
fixups: AssemblerFixup[]
|
|
|
|
}
|
|
|
|
|
2019-05-25 21:46:48 +00:00
|
|
|
const isError = (o: AssemblerLineResult): o is AssemblerErrorResult => (<AssemblerErrorResult>o).error !== undefined
|
|
|
|
|
|
|
|
function hex(v:number, nd:number) {
|
|
|
|
try {
|
|
|
|
if (!nd) nd = 2;
|
|
|
|
var s = v.toString(16).toUpperCase();
|
|
|
|
while (s.length < nd)
|
|
|
|
s = "0" + s;
|
|
|
|
return s;
|
|
|
|
} catch (e) {
|
|
|
|
return v+"";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function stringToData(s:string) : number[] {
|
|
|
|
var data = [];
|
|
|
|
for (var i=0; i<s.length; i++) {
|
|
|
|
data[i] = s.charCodeAt(i);
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export class Assembler {
|
|
|
|
spec : AssemblerSpec;
|
|
|
|
ip = 0;
|
|
|
|
origin = 0;
|
|
|
|
linenum = 0;
|
|
|
|
symbols : {[name:string] : {value:number}} = {};
|
|
|
|
errors : AssemblerError[] = [];
|
|
|
|
outwords : number[] = [];
|
|
|
|
asmlines : AssemblerLine[] = [];
|
|
|
|
fixups : AssemblerFixup[] = [];
|
|
|
|
width = 8;
|
|
|
|
codelen = 0;
|
|
|
|
aborted = false;
|
|
|
|
|
|
|
|
constructor(spec : AssemblerSpec) {
|
|
|
|
this.spec = spec;
|
|
|
|
if (spec) {
|
|
|
|
this.preprocessRules();
|
|
|
|
}
|
|
|
|
}
|
2018-02-18 05:12:09 +00:00
|
|
|
|
2019-05-25 21:46:48 +00:00
|
|
|
rule2regex(rule : AssemblerRule, vars : AssemblerVarList) {
|
2018-02-18 05:12:09 +00:00
|
|
|
var s = rule.fmt;
|
2018-12-05 13:33:40 +00:00
|
|
|
if (!s || !(typeof s === 'string'))
|
|
|
|
throw Error('Each rule must have a "fmt" string field');
|
|
|
|
if (!rule.bits || !(rule.bits instanceof Array))
|
|
|
|
throw Error('Each rule must have a "bits" array field');
|
2018-02-18 05:12:09 +00:00
|
|
|
var varlist = [];
|
|
|
|
rule.prefix = s.split(/\s+/)[0];
|
2018-05-27 18:13:06 +00:00
|
|
|
s = s.replace(/\+/g, '\\+');
|
|
|
|
s = s.replace(/\*/g, '\\*');
|
2018-02-18 05:12:09 +00:00
|
|
|
s = s.replace(/\s+/g, '\\s+');
|
|
|
|
s = s.replace(/\[/g, '\\[');
|
|
|
|
s = s.replace(/\]/g, '\\]');
|
|
|
|
s = s.replace(/\./g, '\\.');
|
2018-05-27 18:13:06 +00:00
|
|
|
// TODO: more escapes?
|
2019-05-25 21:46:48 +00:00
|
|
|
s = s.replace(/~\w+/g, (varname:string) => {
|
2018-02-18 05:12:09 +00:00
|
|
|
varname = varname.substr(1);
|
|
|
|
var v = vars[varname];
|
|
|
|
varlist.push(varname);
|
|
|
|
if (!v)
|
2018-12-05 13:33:40 +00:00
|
|
|
throw Error('Could not find variable definition for "~' + varname + '"');
|
2018-02-18 05:12:09 +00:00
|
|
|
else if (v.toks)
|
|
|
|
return '(\\w+)';
|
|
|
|
else
|
|
|
|
return '([0-9]+|[$][0-9a-f]+|\\w+)';
|
|
|
|
});
|
2018-12-05 13:33:40 +00:00
|
|
|
try {
|
|
|
|
rule.re = new RegExp('^'+s+'$', 'i');
|
|
|
|
} catch (e) {
|
|
|
|
throw Error("Bad regex for rule \"" + rule.fmt + "\": /" + s + "/ -- " + e);
|
|
|
|
}
|
2018-02-18 05:12:09 +00:00
|
|
|
rule.varlist = varlist;
|
|
|
|
// TODO: check rule constraints
|
|
|
|
return rule;
|
|
|
|
}
|
|
|
|
|
2019-05-25 21:46:48 +00:00
|
|
|
preprocessRules() {
|
|
|
|
if (this.spec.width) {
|
|
|
|
this.width = this.spec.width|0;
|
|
|
|
}
|
|
|
|
for (var rule of this.spec.rules) {
|
|
|
|
this.rule2regex(rule, this.spec.vars);
|
|
|
|
}
|
2018-02-18 05:12:09 +00:00
|
|
|
}
|
2019-05-25 21:46:48 +00:00
|
|
|
warning(msg:string, line?:number) {
|
|
|
|
this.errors.push({msg:msg, line:line?line:this.linenum});
|
2018-02-18 05:12:09 +00:00
|
|
|
}
|
2019-05-25 21:46:48 +00:00
|
|
|
fatal(msg:string, line?:number) {
|
|
|
|
this.warning(msg, line);
|
|
|
|
this.aborted = true;
|
2018-03-02 05:15:33 +00:00
|
|
|
}
|
2019-05-25 21:46:48 +00:00
|
|
|
fatalIf(msg?:string, line?:number) {
|
|
|
|
if (msg) this.fatal(msg, line);
|
2018-02-18 05:12:09 +00:00
|
|
|
}
|
2019-05-25 21:46:48 +00:00
|
|
|
addBytes(result:AssemblerInstruction) {
|
|
|
|
this.asmlines.push({
|
|
|
|
line:this.linenum,
|
|
|
|
offset:this.ip,
|
2018-02-18 05:12:09 +00:00
|
|
|
nbits:result.nbits
|
|
|
|
});
|
|
|
|
var op = result.opcode;
|
2019-05-25 21:46:48 +00:00
|
|
|
var nb = result.nbits/this.width;
|
2018-02-18 05:12:09 +00:00
|
|
|
for (var i=0; i<nb; i++) {
|
2019-05-25 21:46:48 +00:00
|
|
|
this.outwords[this.ip++ - this.origin] = (op >> (nb-1-i)*this.width) & ((1<<this.width)-1);
|
2018-02-18 05:12:09 +00:00
|
|
|
}
|
|
|
|
}
|
2019-05-25 21:46:48 +00:00
|
|
|
addWords(data:number[]) {
|
|
|
|
this.asmlines.push({
|
|
|
|
line:this.linenum,
|
|
|
|
offset:this.ip,
|
|
|
|
nbits:this.width*data.length
|
2018-04-16 22:12:12 +00:00
|
|
|
});
|
|
|
|
for (var i=0; i<data.length; i++) {
|
2019-05-25 21:46:48 +00:00
|
|
|
this.outwords[this.ip++ - this.origin] = data[i] & ((1<<this.width)-1);
|
2018-04-16 22:12:12 +00:00
|
|
|
}
|
|
|
|
}
|
2018-06-11 17:01:09 +00:00
|
|
|
|
2019-05-25 21:46:48 +00:00
|
|
|
parseData(toks:string[]) : number[] {
|
2018-04-16 22:12:12 +00:00
|
|
|
var data = [];
|
|
|
|
for (var i=0; i<toks.length; i++) {
|
2019-05-25 21:46:48 +00:00
|
|
|
data[i] = this.parseConst(toks[i]);
|
2018-04-16 22:12:12 +00:00
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
2018-06-11 17:01:09 +00:00
|
|
|
|
2019-05-25 21:46:48 +00:00
|
|
|
alignIP(align) {
|
|
|
|
if (align < 1 || align > this.codelen)
|
|
|
|
this.fatal("Invalid alignment value");
|
2018-04-16 22:12:12 +00:00
|
|
|
else
|
2019-05-25 21:46:48 +00:00
|
|
|
this.ip = Math.floor((this.ip+align-1)/align)*align;
|
2018-04-16 22:12:12 +00:00
|
|
|
}
|
2018-02-18 05:12:09 +00:00
|
|
|
|
2019-05-25 21:46:48 +00:00
|
|
|
parseConst(s:string, nbits?:number) : number {
|
2018-02-19 00:19:20 +00:00
|
|
|
// TODO: check bit length
|
2019-05-25 21:46:48 +00:00
|
|
|
if (s && s[0] == '$')
|
2018-02-18 05:12:09 +00:00
|
|
|
return parseInt(s.substr(1), 16);
|
|
|
|
else
|
|
|
|
return parseInt(s);
|
|
|
|
}
|
|
|
|
|
2019-05-25 21:46:48 +00:00
|
|
|
buildInstruction(rule:AssemblerRule, m:string[]) : AssemblerLineResult {
|
2018-02-18 05:12:09 +00:00
|
|
|
var opcode = 0;
|
|
|
|
var oplen = 0;
|
2018-03-23 21:05:08 +00:00
|
|
|
// iterate over each component of the rule output ("bits")
|
2019-05-25 21:46:48 +00:00
|
|
|
for (var b of rule.bits) {
|
2018-02-18 05:12:09 +00:00
|
|
|
var n,x;
|
2018-03-23 21:05:08 +00:00
|
|
|
// is a string? then it's a bit constant
|
|
|
|
// TODO
|
2018-07-11 04:04:28 +00:00
|
|
|
if (typeof b == "string") {
|
2018-02-18 05:12:09 +00:00
|
|
|
n = b.length;
|
|
|
|
x = parseInt(b,2);
|
|
|
|
} else {
|
2018-03-23 21:05:08 +00:00
|
|
|
// it's an indexed variable, look up its variable
|
2018-02-18 05:12:09 +00:00
|
|
|
var id = m[b+1];
|
2019-05-25 21:46:48 +00:00
|
|
|
var v = this.spec.vars[rule.varlist[b]];
|
2018-02-18 05:12:09 +00:00
|
|
|
if (!v) {
|
2018-02-19 00:19:20 +00:00
|
|
|
return {error:"Could not find matching identifier for '" + m[0] + "'"};
|
2018-02-18 05:12:09 +00:00
|
|
|
}
|
|
|
|
n = v.bits;
|
2018-03-23 21:05:08 +00:00
|
|
|
// is it an enumerated type? look up the index of its keyword
|
2018-02-18 05:12:09 +00:00
|
|
|
if (v.toks) {
|
|
|
|
x = v.toks.indexOf(id);
|
|
|
|
if (x < 0)
|
2019-05-25 21:46:48 +00:00
|
|
|
return {error:"Can't use '" + id + "' here, only one of: " + v.toks.join(', ')};
|
2018-02-18 05:12:09 +00:00
|
|
|
} else {
|
2018-03-23 21:05:08 +00:00
|
|
|
// otherwise, parse it as a constant
|
2019-05-25 21:46:48 +00:00
|
|
|
x = this.parseConst(id, n);
|
2018-02-18 05:12:09 +00:00
|
|
|
// is it a label? add fixup
|
|
|
|
if (isNaN(x)) {
|
2019-05-25 21:46:48 +00:00
|
|
|
this.fixups.push({sym:id, ofs:this.ip, bitlen:n, bitofs:oplen, line:this.linenum, iprel:!!v.iprel, ipofs:(v.ipofs+0)});
|
2018-02-18 05:12:09 +00:00
|
|
|
x = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var mask = (1<<n)-1;
|
|
|
|
if ((x&mask) != x)
|
2018-02-19 00:19:20 +00:00
|
|
|
return {error:"Value " + x + " does not fit in " + n + " bits"};
|
2018-02-18 05:12:09 +00:00
|
|
|
opcode = (opcode << n) | x;
|
|
|
|
oplen += n;
|
|
|
|
}
|
|
|
|
if (oplen == 0)
|
2019-05-25 21:46:48 +00:00
|
|
|
this.warning("Opcode had zero length");
|
2018-03-23 21:05:08 +00:00
|
|
|
else if (oplen > 32)
|
2019-05-25 21:46:48 +00:00
|
|
|
this.warning("Opcodes > 32 bits not supported");
|
|
|
|
else if ((oplen % this.width) != 0)
|
|
|
|
this.warning("Opcode was not word-aligned (" + oplen + " bits)");
|
2018-02-18 05:12:09 +00:00
|
|
|
return {opcode:opcode, nbits:oplen};
|
|
|
|
}
|
|
|
|
|
2019-05-25 21:46:48 +00:00
|
|
|
loadArch(arch:string) : string {
|
|
|
|
if (this.loadJSON) {
|
|
|
|
var json = this.loadJSON(arch + ".json");
|
2018-02-18 05:12:09 +00:00
|
|
|
if (json && json.vars && json.rules) {
|
2019-05-25 21:46:48 +00:00
|
|
|
this.spec = json;
|
|
|
|
this.preprocessRules();
|
2018-02-18 05:12:09 +00:00
|
|
|
} else {
|
2019-05-25 21:46:48 +00:00
|
|
|
return ("Could not load arch file '" + arch + ".json'");
|
2018-02-18 05:12:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-25 21:46:48 +00:00
|
|
|
parseDirective(tokens) {
|
2018-04-16 22:12:12 +00:00
|
|
|
var cmd = tokens[0].toLowerCase();
|
|
|
|
if (cmd == '.define')
|
2019-05-25 21:46:48 +00:00
|
|
|
this.symbols[tokens[1].toLowerCase()] = {value:tokens[2]};
|
2018-04-16 22:12:12 +00:00
|
|
|
else if (cmd == '.org')
|
2019-05-25 21:46:48 +00:00
|
|
|
this.ip = this.origin = parseInt(tokens[1]);
|
2018-04-16 22:12:12 +00:00
|
|
|
else if (cmd == '.len')
|
2019-05-25 21:46:48 +00:00
|
|
|
this.codelen = parseInt(tokens[1]);
|
2018-04-16 22:12:12 +00:00
|
|
|
else if (cmd == '.width')
|
2019-05-25 21:46:48 +00:00
|
|
|
this.width = parseInt(tokens[1]);
|
2018-04-16 22:12:12 +00:00
|
|
|
else if (cmd == '.arch')
|
2019-05-25 21:46:48 +00:00
|
|
|
this.fatalIf(this.loadArch(tokens[1]));
|
2018-04-16 22:12:12 +00:00
|
|
|
else if (cmd == '.include')
|
2019-05-25 21:46:48 +00:00
|
|
|
this.fatalIf(this.loadInclude(tokens[1]));
|
2018-04-16 22:12:12 +00:00
|
|
|
else if (cmd == '.module')
|
2019-05-25 21:46:48 +00:00
|
|
|
this.fatalIf(this.loadModule(tokens[1]));
|
2018-04-16 22:12:12 +00:00
|
|
|
else if (cmd == '.data')
|
2019-05-25 21:46:48 +00:00
|
|
|
this.addWords(this.parseData(tokens.slice(1)));
|
2018-04-16 22:12:12 +00:00
|
|
|
else if (cmd == '.string')
|
2019-05-25 21:46:48 +00:00
|
|
|
this.addWords(stringToData(tokens.slice(1).join(' ')));
|
2018-04-16 22:12:12 +00:00
|
|
|
else if (cmd == '.align')
|
2019-05-25 21:46:48 +00:00
|
|
|
this.alignIP(this.parseConst(tokens[1]));
|
2018-02-18 05:12:09 +00:00
|
|
|
else
|
2019-05-25 21:46:48 +00:00
|
|
|
this.warning("Unrecognized directive: " + tokens);
|
2018-02-18 05:12:09 +00:00
|
|
|
}
|
|
|
|
|
2019-05-25 21:46:48 +00:00
|
|
|
assemble(line:string) : AssemblerInstruction {
|
|
|
|
this.linenum++;
|
2018-02-18 05:12:09 +00:00
|
|
|
// remove comments
|
2018-04-16 22:12:12 +00:00
|
|
|
line = line.replace(/[;].*/g, '').trim();
|
2018-02-18 05:12:09 +00:00
|
|
|
// is it a directive?
|
|
|
|
if (line[0] == '.') {
|
|
|
|
var tokens = line.split(/\s+/);
|
2019-05-25 21:46:48 +00:00
|
|
|
this.parseDirective(tokens);
|
2018-02-18 05:12:09 +00:00
|
|
|
return;
|
|
|
|
}
|
2018-04-16 22:12:12 +00:00
|
|
|
// make it lowercase
|
|
|
|
line = line.toLowerCase();
|
2018-02-18 05:12:09 +00:00
|
|
|
// find labels
|
2019-05-25 21:46:48 +00:00
|
|
|
line = line.replace(/(\w+):/, (_label, label) => {
|
|
|
|
this.symbols[label] = {value:this.ip};
|
2018-02-18 05:12:09 +00:00
|
|
|
return ''; // replace label with blank
|
|
|
|
});
|
|
|
|
line = line.trim();
|
|
|
|
if (line == '')
|
|
|
|
return; // empty line
|
|
|
|
// look at each rule in order
|
2019-05-25 21:46:48 +00:00
|
|
|
if (!this.spec) { this.fatal("Need to load .arch first"); return; }
|
2018-02-19 00:19:20 +00:00
|
|
|
var lastError;
|
2019-05-25 21:46:48 +00:00
|
|
|
for (var rule of this.spec.rules) {
|
2018-02-18 05:12:09 +00:00
|
|
|
var m = rule.re.exec(line);
|
|
|
|
if (m) {
|
2019-05-25 21:46:48 +00:00
|
|
|
var result = this.buildInstruction(rule, m);
|
|
|
|
if (!isError(result)) {
|
|
|
|
this.addBytes(result);
|
2018-02-18 05:12:09 +00:00
|
|
|
return result;
|
2019-05-25 21:46:48 +00:00
|
|
|
} else {
|
2018-02-19 00:19:20 +00:00
|
|
|
lastError = result.error;
|
2018-02-18 05:12:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-05-25 21:46:48 +00:00
|
|
|
this.warning(lastError ? lastError : ("Could not decode instruction: " + line));
|
2018-02-18 05:12:09 +00:00
|
|
|
}
|
|
|
|
|
2019-05-25 21:46:48 +00:00
|
|
|
finish() : AssemblerState {
|
2018-02-18 05:12:09 +00:00
|
|
|
// apply fixups
|
2019-05-25 21:46:48 +00:00
|
|
|
for (var i=0; i<this.fixups.length; i++) {
|
|
|
|
var fix = this.fixups[i];
|
|
|
|
var sym = this.symbols[fix.sym];
|
2018-02-18 05:12:09 +00:00
|
|
|
if (sym) {
|
2019-05-25 21:46:48 +00:00
|
|
|
var ofs = fix.ofs + Math.floor(fix.bitofs/this.width);
|
|
|
|
var shift = fix.bitofs & (this.width-1);
|
2018-02-18 05:12:09 +00:00
|
|
|
var mask = ((1<<fix.bitlen)-1);
|
2019-05-25 21:46:48 +00:00
|
|
|
var value = this.parseConst(sym.value+"", fix.bitlen);
|
2018-02-19 00:19:20 +00:00
|
|
|
if (fix.iprel)
|
|
|
|
value -= fix.ofs + fix.ipofs;
|
|
|
|
if (value > mask || value < -mask)
|
2019-05-25 21:46:48 +00:00
|
|
|
this.warning("Symbol " + fix.sym + " (" + value + ") does not fit in " + fix.bitlen + " bits", fix.line);
|
2018-02-18 05:12:09 +00:00
|
|
|
value &= mask;
|
|
|
|
// TODO: check range
|
|
|
|
// TODO: span multiple words?
|
2019-05-25 21:46:48 +00:00
|
|
|
this.outwords[ofs - this.origin] ^= value; // TODO: << shift?
|
2018-02-18 05:12:09 +00:00
|
|
|
} else {
|
2019-05-25 21:46:48 +00:00
|
|
|
this.warning("Symbol '" + fix.sym + "' not found");
|
2018-02-18 05:12:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// update asmlines
|
2019-05-25 21:46:48 +00:00
|
|
|
for (var i=0; i<this.asmlines.length; i++) {
|
|
|
|
var al = this.asmlines[i];
|
2018-02-18 05:12:09 +00:00
|
|
|
al.insns = '';
|
2019-05-25 21:46:48 +00:00
|
|
|
for (var j=0; j<al.nbits/this.width; j++) {
|
|
|
|
var word = this.outwords[al.offset + j - this.origin];
|
2018-02-18 05:12:09 +00:00
|
|
|
if (j>0) al.insns += ' ';
|
2019-05-25 21:46:48 +00:00
|
|
|
al.insns += hex(word,this.width/4);
|
2018-02-18 05:12:09 +00:00
|
|
|
}
|
|
|
|
}
|
2019-05-25 21:46:48 +00:00
|
|
|
while (this.outwords.length < this.codelen) {
|
|
|
|
this.outwords.push(0);
|
2018-02-18 05:12:09 +00:00
|
|
|
}
|
2019-05-25 21:46:48 +00:00
|
|
|
this.fixups = [];
|
|
|
|
return this.state();
|
2018-02-18 05:12:09 +00:00
|
|
|
}
|
|
|
|
|
2019-05-25 21:46:48 +00:00
|
|
|
assembleFile(text) : AssemblerState {
|
2018-02-18 05:12:09 +00:00
|
|
|
var lines = text.split(/\n/g);
|
2019-05-25 21:46:48 +00:00
|
|
|
for (var i=0; i<lines.length && !this.aborted; i++) {
|
2018-04-16 22:12:12 +00:00
|
|
|
try {
|
2019-05-25 21:46:48 +00:00
|
|
|
this.assemble(lines[i]);
|
2018-04-16 22:12:12 +00:00
|
|
|
} catch (e) {
|
|
|
|
console.log(e);
|
2019-05-25 21:46:48 +00:00
|
|
|
this.fatal("Exception during assembly: " + e);
|
2018-04-16 22:12:12 +00:00
|
|
|
}
|
2018-02-18 05:12:09 +00:00
|
|
|
}
|
2019-05-25 21:46:48 +00:00
|
|
|
return this.finish();
|
2018-02-18 05:12:09 +00:00
|
|
|
}
|
|
|
|
|
2019-05-25 21:46:48 +00:00
|
|
|
state() : AssemblerState {
|
|
|
|
return {ip:this.ip, line:this.linenum, origin:this.origin, codelen:this.codelen,
|
2018-03-02 05:15:33 +00:00
|
|
|
intermediate:{}, // TODO: listing, symbols?
|
2019-05-25 21:46:48 +00:00
|
|
|
output:this.outwords,
|
|
|
|
lines:this.asmlines,
|
|
|
|
errors:this.errors,
|
|
|
|
fixups:this.fixups};
|
2018-02-18 05:12:09 +00:00
|
|
|
}
|
2019-05-25 21:46:48 +00:00
|
|
|
|
|
|
|
// methods to implement in subclass
|
|
|
|
|
|
|
|
loadJSON : (path : string) => any;
|
|
|
|
loadInclude : (path : string) => string;
|
|
|
|
loadModule : (path : string) => string;
|
2018-02-18 05:12:09 +00:00
|
|
|
}
|
2018-02-21 19:32:11 +00:00
|
|
|
|