@asm80/core
Version:
Core ASM80 compiler / assembler
411 lines (380 loc) • 10.8 kB
JavaScript
import { btoax, atobx } from "./utils/base64escaped.js";
import { Parser } from "./expression-parser.js";
/**
* Vrátí číslo řádku včetně informace o includovaném souboru
* @param {Object} s - Řádek s případnými atributy includedFile a numline
* @returns {string|number} Číslo řádku nebo kombinace soubor/řádek
*/
const includedLineNumber = (s) => {
if (!s.includedFile) return s.numline;
return s.includedFileAtLine + "__" + s.numline;
}
/**
* Parsuje jeden řádek assemblerového zdrojového kódu do tokenizované struktury
* @param {Object} s - Objekt s atributem line (řetězec řádku) a dalšími metadaty
* @param {Object} macros - Objekt s definovanými makry
* @param {Object} opts - Možnosti assembleru (stopFlag, olds, assembler, ...)
* @returns {Object} Tokenizovaný řádek s rozpoznanými poli (label, opcode, params, ...)
* @throws {Object} Pokud dojde k chybě v syntaxi nebo neznámé instrukci
*/
export const parseLine = (s, macros, opts = {stopFlag:null, olds:null, assembler:null}) => {
let t = s.line;
let ll;
//anonymous labels
//format: : label
ll = t.match(/^\s*:\s*(.*)/);
if (ll) {
s.anonymousLabel = "anon__" + includedLineNumber(s);
t = ll[1];
}
//console.log(s, ll)
//labels
//format: label:
ll = t.match(/^\s*(\@{0,1}[a-zA-Z0-9-_]+):\s*(.*)/);
//console.log(t, ll)
if (ll) {
s.label = ll[1].toUpperCase();
t = ll[2];
}
//anonymous labels
//format: : label
ll = t.match(/^\s*:\s*(.*)/);
//console.log(s, ll)
if (ll) {
s.label = "__@anon" + s.numline;
t = ll[2];
}
s._dp = 0;
s.params = [];
//special EQU format as "label = value"
let oo = t.match(/^\s*(\=)\s*(.*)/);
if (oo) {
s.opcode = oo[1].toUpperCase();
t = oo[2];
} else {
oo = t.match(/^\s*([\.a-zA-Z0-9-_]+)\s*(.*)/);
//console.log("2",oo,t)
if (oo) {
s.opcode = oo[1].toUpperCase();
t = oo[2];
}
}
/*
oo = t.match(/^\s*(:\=)\s*(.*)/);
if (oo) {
s.opcode = "=";
t = oo[2];
}
*/
if (t) {
//param grouping by {}
//try {
//console.log(t)
while (t.match(/\"(.*?)\"/g)) {
t = t.replace(/\"(.*?)\"/g, (n) => "00ss" + btoax(n) + "!");
}
while (t.match(/\'(.*?)\'/g)) {
//console.log(t)
t = t.replace(/\'(.*?)\'/g, (n) => "00ss" + btoax('"' + n.substr(1, n.length - 2) + '"') + "!");
}
while (t.match(/\{(.*?)\}/g)) {
t = t.replace(/\{(.*?)\}/g, (n) => "00bb" + btoax(n.substr(1, n.length - 2)));
}
//} catch(e) {
// console.log(e,t)
//}
//semicolon fix
while (t.match(/"(.*?);(.*?)"/g)) {
t = t.replace(/"(.*?);(.*?)"/g, '"$1§$2"');
}
while (t.match(/'(.*?);(.*?)'/g)) {
t = t.replace(/'(.*?);(.*?)'/g, '"$1§$2"');
}
let pp = t.match(/^\s*([^;]*)(.*)/);
if (pp && pp[1].length) {
s.paramstring = pp[1];
//sane strings
let ppc = pp[1];
while (ppc.match(/"(.*?),(.*?)"/g)) {
ppc = ppc.replace(/"(.*?),(.*?)"/g, '"$1€$2"');
}
while (ppc.match(/'(.*?),(.*?)'/g)) {
ppc = ppc.replace(/'(.*?),(.*?)'/g, '"$1€$2"');
}
let n = ppc.match(/([0-9]+)\s*DUP\s*\((.*)\)/i);
if (n) {
let dup = parseInt(n[1]);
let nln = "";
for (let i = 0; i < dup; i++) {
nln += n[2] + ",";
}
ppc = nln.substring(0, nln.length - 1);
//console.log(ppc);
}
let px = ppc.split(/\s*,\s*/);
s.params = px.map((ppc) => {
let p = (ppc.replace(/€/g, ",").replace(/§/g, ";")).trim();
p = p.replace(/00ss(.*?)\!/g, (n) => atobx(n.substr(4, n.length - 5)));
return p;
});
//console.log(s)
t = pp[2].replace(/§/g, ";");
}
}
//console.log("SSS",s)
if (t) {
let rr = t.match(/^\s*;*(.*)/);
if (rr) {
s.remark = rr[1].replace(/00ss(.*?)\!/g, (n) => atobx(n.substr(4, n.length - 5)));
if (!s.remark) {
s.remark = " ";
}
t = "";
}
}
s.notparsed = t;
//pokus s opts
//console.log("ZDECH", s)
if (s.opcode === "ORG") {
s.opcode = ".ORG";
}
if (s.opcode === ".ERROR") {
s.paramstring = s.paramstring.replace(/00ss(.*?)\!/g, (n) => atobx(n.substr(4, n.length - 5)));
return s;
//console.log(stopFlag,olds,vars)
//throw { "msg": s.paramstring.replace(/00ss(.*?)\!/g, function (n) { return atobx(n.substr(4, n.length - 5)) }), "s":s};
}
if (s.opcode === ".EQU") {
s.opcode = "EQU";
}
if (s.opcode === ".FILL") {
s.opcode = "FILL";
}
if (s.opcode === ".ORG") {
return s;
// obsolete - evaluate origin has been suppressed
/*
try {
// s.addr = Parser.evaluate(s.paramstring);
return s;
} catch (e) {
throw {
msg: e.msg,
s: s
};
}
*/
}
if (s.opcode === "DEFB") {
s.opcode = "DB";
return s;
}
if (s.opcode === ".BYTE") {
s.opcode = "DB";
return s;
}
if (s.opcode === ".DB") {
s.opcode = "DB";
return s;
}
if (s.opcode === ".WORD") {
s.opcode = "DW";
return s;
}
if (s.opcode === ".DW") {
s.opcode = "DW";
return s;
}
if (s.opcode === "DEFW") {
s.opcode = "DW";
return s;
}
if (s.opcode === ".DD") {
s.opcode = "DD";
return s;
}
if (s.opcode === ".DF") {
s.opcode = "DF";
return s;
}
if (s.opcode === ".DFZXS") {
s.opcode = "DFZXS";
return s;
}
if (s.opcode === ".DFF") {
s.opcode = "DFF";
return s;
}
if (s.opcode === "DEFS") {
s.opcode = "DS";
return s;
}
if (s.opcode === ".RES") {
s.opcode = "DS";
return s;
}
if (s.opcode === "DEFM") {
s.opcode = "DS";
return s;
}
if (s.opcode === ".ALIGN") {
s.opcode = "ALIGN";
return s;
}
if (s.opcode === ".IFN") {
s.opcode = "IFN";
return s;
}
if (s.opcode === ".IF") {
s.opcode = "IF";
return s;
}
if (s.opcode === ".ELSE") {
s.opcode = "ELSE";
return s;
}
if (s.opcode === ".ENDIF") {
s.opcode = "ENDIF";
return s;
}
if (s.opcode === ".PRAGMA") {
opts.PRAGMAS = opts.PRAGMAS || [];
opts.PRAGMAS.push(s.params[0].toUpperCase());
return s;
}
if (s.opcode === "EQU" ||
s.opcode === "=" ||
s.opcode === ".SET" ||
s.opcode === "IF" ||
s.opcode === "IFN" ||
s.opcode === "ELSE" ||
s.opcode === "ENDIF" ||
s.opcode === ".ERROR" ||
s.opcode === ".INCLUDE" ||
s.opcode === ".INCBIN" ||
s.opcode === ".MACRO" ||
s.opcode === ".ENDM" ||
s.opcode === ".BLOCK" ||
s.opcode === ".ENDBLOCK" ||
s.opcode === ".REPT" ||
s.opcode === ".CPU" ||
s.opcode === ".ENT" ||
s.opcode === ".BINFROM" ||
s.opcode === ".BINTO" ||
s.opcode === ".ENGINE" ||
s.opcode === ".PRAGMA" ||
s.opcode === "END" ||
s.opcode === ".END" ||
//6809 assembler ops
s.opcode === "BSZ" ||
s.opcode === "FCB" ||
s.opcode === "FCC" ||
s.opcode === "FDB" ||
s.opcode === "FILL" ||
s.opcode === "RMB" ||
s.opcode === "ZMB" ||
s.opcode === "SETDP" ||
//65816
s.opcode === ".M8" ||
s.opcode === ".X8" ||
s.opcode === ".M16" ||
s.opcode === ".X16" ||
//phase, dephase
s.opcode === ".PHASE" ||
s.opcode === ".DEPHASE" ||
s.opcode === ".SETPHASE" ||
s.opcode === "ALIGN" ||
s.opcode === ".CSTR" ||
s.opcode === ".ISTR" ||
s.opcode === ".PSTR" ||
//segments
s.opcode === ".CSEG" ||
s.opcode === ".DSEG" ||
s.opcode === ".ESEG" ||
s.opcode === ".BSSEG" ||
//modules
s.opcode === ".EXPORT" ||
s.opcode === ".EXTERN" ||
s.opcode === "DB" ||
s.opcode === "DW" ||
s.opcode === "DD" ||
s.opcode === "DF" ||
s.opcode === "DFF" ||
s.opcode === "DFZXS" ||
s.opcode === "DS") {
return s;
}
if (s.opcode === ".DEBUGINFO" ||
s.opcode === ".MACPACK" ||
s.opcode === ".FEATURE" ||
s.opcode === ".ZEROPAGE" ||
s.opcode === ".SEGMENT" ||
s.opcode === ".SETCPU") {
s.opcode = "";
return s;
}
if (!s.opcode && s.label) {
return s;
}
let ax = null
try {
ax = opts.assembler.parseOpcode(s, {}, Parser);
} catch (e) {
throw {
msg: e,
s: s
};
}
//console.log("SS",JSON.stringify(s),ax)
if (ax !== null) return ax;
if (macros[s.opcode]) {
s.macro = s.opcode;
return s;
}
//label bez dvojtecky
//console.log(s,s2)
if (!s.label && !opts.stopFlag) {
//console.log(s)
//let s2 = {line:s.line,numline:s.numline, addr:null,bytes:0};
let s2 = JSON.parse(JSON.stringify(s));
s2.addr = null;
s2.bytes = 0;
s2.oldline = s.line;
if (s.remark && !s.opcode) {
return s;
}
if (!s.params || s.params.length === 0)
throw {
msg: "Unrecognized instruction " + s.opcode,
s: s
};
if (!s.opcode)
throw {
msg: "Unrecognized instruction " + s.opcode,
s: s
};
//hotfix
//console.log(s)
if (s.params[0].indexOf(":=") === 0)
s.params[0] = ".SET" + s.params[0].substr(2);
s2.line = s.opcode + ": " + s.params.join();
if (s.remark) s2.line += " ;" + s.remark;
//console.log("ATTEMPT2",s2.line)
let sx = parseLine(s2, macros, {stopFlag:true, olds:s, ...opts});
if (!sx.opcode)
throw {
msg: "Unrecognized instruction " + s.opcode,
s: s
};
return sx;
}
if (opts.stopFlag)
throw {
msg: "Unrecognized instruction " + opts.olds.opcode,
s: s
};
throw {
msg: "Unrecognized instruction " + s.opcode,
s: s
};
};