mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2025-02-22 12:29:06 +00:00
319 lines
11 KiB
JavaScript
319 lines
11 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.compileBatariBasic = exports.assembleDASM = void 0;
|
|
const workermain_1 = require("../workermain");
|
|
function parseDASMListing(lstpath, lsttext, listings, errors, unresolved) {
|
|
// TODO: this gets very slow
|
|
// TODO: macros that are on adjacent lines don't get offset addresses
|
|
// 4 08ee a9 00 start lda #01workermain.js:23:5
|
|
let lineMatch = /\s*(\d+)\s+(\S+)\s+([0-9a-f]+)\s+([?0-9a-f][?0-9a-f ]+)?\s+(.+)?/i;
|
|
let equMatch = /\bequ\b/i;
|
|
let macroMatch = /\bMAC\s+(\S+)?/i;
|
|
let lastline = 0;
|
|
let macros = {};
|
|
let lstline = 0;
|
|
let lstlist = listings[lstpath];
|
|
for (let line of lsttext.split(workermain_1.re_crlf)) {
|
|
lstline++;
|
|
let linem = lineMatch.exec(line + " ");
|
|
if (linem && linem[1] != null) {
|
|
let linenum = parseInt(linem[1]);
|
|
let filename = linem[2];
|
|
let offset = parseInt(linem[3], 16);
|
|
let insns = linem[4];
|
|
let restline = linem[5];
|
|
if (insns && insns.startsWith('?'))
|
|
insns = null;
|
|
// don't use listing yet
|
|
if (lstlist && lstlist.lines) {
|
|
lstlist.lines.push({
|
|
line: lstline,
|
|
offset: offset,
|
|
insns: insns,
|
|
iscode: true,
|
|
});
|
|
}
|
|
// inside of a file?
|
|
let lst = listings[filename];
|
|
if (lst) {
|
|
var lines = lst.lines;
|
|
// look for MAC statement
|
|
let macmatch = macroMatch.exec(restline);
|
|
if (macmatch) {
|
|
macros[macmatch[1]] = { line: parseInt(linem[1]), file: linem[2].toLowerCase() };
|
|
}
|
|
else if (insns && restline && !restline.match(equMatch)) {
|
|
lines.push({
|
|
line: linenum,
|
|
offset: offset,
|
|
insns: insns,
|
|
iscode: restline[0] != '.'
|
|
});
|
|
}
|
|
lastline = linenum;
|
|
}
|
|
else {
|
|
// inside of macro?
|
|
let mac = macros[filename.toLowerCase()];
|
|
// macro invocation in main file
|
|
if (mac && linenum == 0) {
|
|
lines.push({
|
|
line: lastline + 1,
|
|
offset: offset,
|
|
insns: insns,
|
|
iscode: true
|
|
});
|
|
}
|
|
if (insns && mac) {
|
|
let maclst = listings[mac.file];
|
|
if (maclst && maclst.lines) {
|
|
maclst.lines.push({
|
|
path: mac.file,
|
|
line: mac.line + linenum,
|
|
offset: offset,
|
|
insns: insns,
|
|
iscode: true
|
|
});
|
|
}
|
|
// TODO: a listing file can't include other files
|
|
}
|
|
else {
|
|
// inside of macro or include file
|
|
if (insns && linem[3] && lastline > 0) {
|
|
lines.push({
|
|
line: lastline + 1,
|
|
offset: offset,
|
|
insns: null
|
|
});
|
|
}
|
|
}
|
|
}
|
|
// TODO: better symbol test (word boundaries)
|
|
// TODO: ignore IFCONST and IFNCONST usage
|
|
for (let key in unresolved) {
|
|
let l = restline || line;
|
|
// find the identifier substring
|
|
let pos = l.indexOf(key);
|
|
if (pos >= 0) {
|
|
// strip the comment, if any
|
|
let cmt = l.indexOf(';');
|
|
if (cmt < 0 || cmt > pos) {
|
|
// make sure identifier is flanked by non-word chars
|
|
if (new RegExp("\\b" + key + "\\b").exec(l)) {
|
|
errors.push({
|
|
path: filename,
|
|
line: linenum,
|
|
msg: "Unresolved symbol '" + key + "'"
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
let errm = workermain_1.re_msvc.exec(line);
|
|
if (errm) {
|
|
errors.push({
|
|
path: errm[1],
|
|
line: parseInt(errm[2]),
|
|
msg: errm[4]
|
|
});
|
|
}
|
|
}
|
|
}
|
|
function assembleDASM(step) {
|
|
(0, workermain_1.load)("dasm");
|
|
var re_usl = /(\w+)\s+0000\s+[?][?][?][?]/;
|
|
var unresolved = {};
|
|
var errors = [];
|
|
var errorMatcher = (0, workermain_1.msvcErrorMatcher)(errors);
|
|
function match_fn(s) {
|
|
// TODO: what if s is not string? (startsWith is not a function)
|
|
var matches = re_usl.exec(s);
|
|
if (matches) {
|
|
var key = matches[1];
|
|
if (key != 'NO_ILLEGAL_OPCODES') { // TODO
|
|
unresolved[matches[1]] = 0;
|
|
}
|
|
}
|
|
else if (s.startsWith("Warning:")) {
|
|
errors.push({ line: 0, msg: s.substr(9) });
|
|
}
|
|
else if (s.startsWith("unable ")) {
|
|
errors.push({ line: 0, msg: s });
|
|
}
|
|
else if (s.startsWith("segment: ")) {
|
|
errors.push({ line: 0, msg: "Segment overflow: " + s.substring(9) });
|
|
}
|
|
else if (s.toLowerCase().indexOf('error:') >= 0) {
|
|
errors.push({ line: 0, msg: s.trim() });
|
|
}
|
|
else {
|
|
errorMatcher(s);
|
|
}
|
|
}
|
|
var Module = workermain_1.emglobal.DASM({
|
|
noInitialRun: true,
|
|
print: match_fn
|
|
});
|
|
var FS = Module.FS;
|
|
(0, workermain_1.populateFiles)(step, FS, {
|
|
mainFilePath: 'main.a'
|
|
});
|
|
var binpath = step.prefix + '.bin';
|
|
var lstpath = step.prefix + '.lst';
|
|
var sympath = step.prefix + '.sym';
|
|
(0, workermain_1.execMain)(step, Module, [step.path, '-f3',
|
|
"-l" + lstpath,
|
|
"-o" + binpath,
|
|
"-s" + sympath]);
|
|
var alst = FS.readFile(lstpath, { 'encoding': 'utf8' });
|
|
// parse main listing, get errors and listings for each file
|
|
var listings = {};
|
|
//listings[lstpath] = {lines:[], text:alst};
|
|
for (let path of step.files) {
|
|
listings[path] = { lines: [] };
|
|
}
|
|
parseDASMListing(lstpath, alst, listings, errors, unresolved);
|
|
if (errors.length) {
|
|
return { errors: errors };
|
|
}
|
|
// read binary rom output and symbols
|
|
var aout, asym;
|
|
aout = FS.readFile(binpath);
|
|
try {
|
|
asym = FS.readFile(sympath, { 'encoding': 'utf8' });
|
|
}
|
|
catch (e) {
|
|
console.log(e);
|
|
errors.push({ line: 0, msg: "No symbol table generated, maybe segment overflow?" });
|
|
return { errors: errors };
|
|
}
|
|
(0, workermain_1.putWorkFile)(binpath, aout);
|
|
(0, workermain_1.putWorkFile)(lstpath, alst);
|
|
(0, workermain_1.putWorkFile)(sympath, asym);
|
|
// return unchanged if no files changed
|
|
// TODO: what if listing or symbols change?
|
|
if (!(0, workermain_1.anyTargetChanged)(step, [binpath /*, lstpath, sympath*/]))
|
|
return;
|
|
var symbolmap = {};
|
|
for (var s of asym.split("\n")) {
|
|
var toks = s.split(/\s+/);
|
|
if (toks && toks.length >= 2 && !toks[0].startsWith('-')) {
|
|
symbolmap[toks[0]] = parseInt(toks[1], 16);
|
|
}
|
|
}
|
|
// for bataribasic (TODO)
|
|
if (step['bblines']) {
|
|
let lst = listings[step.path];
|
|
if (lst) {
|
|
lst.asmlines = lst.lines;
|
|
lst.text = alst;
|
|
lst.lines = [];
|
|
}
|
|
}
|
|
return {
|
|
output: aout,
|
|
listings: listings,
|
|
errors: errors,
|
|
symbolmap: symbolmap,
|
|
};
|
|
}
|
|
exports.assembleDASM = assembleDASM;
|
|
function preprocessBatariBasic(code) {
|
|
(0, workermain_1.load)("bbpreprocess");
|
|
var bbout = "";
|
|
function addbbout_fn(s) {
|
|
bbout += s;
|
|
bbout += "\n";
|
|
}
|
|
var BBPRE = workermain_1.emglobal.preprocess({
|
|
noInitialRun: true,
|
|
//logReadFiles:true,
|
|
print: addbbout_fn,
|
|
printErr: workermain_1.print_fn,
|
|
noFSInit: true,
|
|
});
|
|
var FS = BBPRE.FS;
|
|
(0, workermain_1.setupStdin)(FS, code);
|
|
BBPRE.callMain([]);
|
|
console.log("preprocess " + code.length + " -> " + bbout.length + " bytes");
|
|
return bbout;
|
|
}
|
|
function compileBatariBasic(step) {
|
|
(0, workermain_1.load)("bb2600basic");
|
|
var params = step.params;
|
|
// stdout
|
|
var asmout = "";
|
|
function addasmout_fn(s) {
|
|
asmout += s;
|
|
asmout += "\n";
|
|
}
|
|
// stderr
|
|
var re_err1 = /[(](\d+)[)]:?\s*(.+)/;
|
|
var errors = [];
|
|
var errline = 0;
|
|
function match_fn(s) {
|
|
console.log(s);
|
|
var matches = re_err1.exec(s);
|
|
if (matches) {
|
|
errline = parseInt(matches[1]);
|
|
errors.push({
|
|
line: errline,
|
|
msg: matches[2]
|
|
});
|
|
}
|
|
}
|
|
(0, workermain_1.gatherFiles)(step, { mainFilePath: "main.bas" });
|
|
var destpath = step.prefix + '.asm';
|
|
if ((0, workermain_1.staleFiles)(step, [destpath])) {
|
|
var BB = workermain_1.emglobal.bb2600basic({
|
|
noInitialRun: true,
|
|
//logReadFiles:true,
|
|
print: addasmout_fn,
|
|
printErr: match_fn,
|
|
noFSInit: true,
|
|
TOTAL_MEMORY: 64 * 1024 * 1024,
|
|
});
|
|
var FS = BB.FS;
|
|
(0, workermain_1.populateFiles)(step, FS);
|
|
// preprocess, pipe file to stdin
|
|
var code = (0, workermain_1.getWorkFileAsString)(step.path);
|
|
code = preprocessBatariBasic(code);
|
|
(0, workermain_1.setupStdin)(FS, code);
|
|
(0, workermain_1.setupFS)(FS, '2600basic');
|
|
(0, workermain_1.execMain)(step, BB, ["-i", "/share", step.path]);
|
|
if (errors.length)
|
|
return { errors: errors };
|
|
// build final assembly output from include file list
|
|
var includesout = FS.readFile("includes.bB", { encoding: 'utf8' });
|
|
var redefsout = FS.readFile("2600basic_variable_redefs.h", { encoding: 'utf8' });
|
|
var includes = includesout.trim().split("\n");
|
|
var combinedasm = "";
|
|
var splitasm = asmout.split("bB.asm file is split here");
|
|
for (var incfile of includes) {
|
|
var inctext;
|
|
if (incfile == "bB.asm")
|
|
inctext = splitasm[0];
|
|
else if (incfile == "bB2.asm")
|
|
inctext = splitasm[1];
|
|
else
|
|
inctext = FS.readFile("/share/includes/" + incfile, { encoding: 'utf8' });
|
|
console.log(incfile, inctext.length);
|
|
combinedasm += "\n\n;;;" + incfile + "\n\n";
|
|
combinedasm += inctext;
|
|
}
|
|
// TODO: ; bB.asm file is split here
|
|
(0, workermain_1.putWorkFile)(destpath, combinedasm);
|
|
(0, workermain_1.putWorkFile)("2600basic.h", FS.readFile("/share/includes/2600basic.h"));
|
|
(0, workermain_1.putWorkFile)("2600basic_variable_redefs.h", redefsout);
|
|
}
|
|
return {
|
|
nexttool: "dasm",
|
|
path: destpath,
|
|
args: [destpath],
|
|
files: [destpath, "2600basic.h", "2600basic_variable_redefs.h"],
|
|
bblines: true,
|
|
};
|
|
}
|
|
exports.compileBatariBasic = compileBatariBasic;
|
|
//# sourceMappingURL=dasm.js.map
|