8bitworkshop/src/worker/tools/m6502.ts

242 lines
8.9 KiB
TypeScript

import { WorkerError, CodeListingMap } from "../../common/workertypes";
import { BuildStep, BuildStepResult, populateFiles, putWorkFile, anyTargetChanged, gatherFiles, staleFiles } from "../builder";
import { parseListing, makeErrorMatcher } from "../listingutils";
import { loadNative, emglobal, moduleInstFn, execMain, print_fn } from "../wasmutils";
import { EmscriptenModule } from "../wasmutils";
// http://www.nespowerpak.com/nesasm/
export function assembleNESASM(step: BuildStep): BuildStepResult {
loadNative("nesasm");
var re_filename = /\#\[(\d+)\]\s+(\S+)/;
var re_insn = /\s+(\d+)\s+([0-9A-F]+):([0-9A-F]+)/;
var re_error = /\s+(.+)/;
var errors: WorkerError[] = [];
var state = 0;
var lineno = 0;
var filename;
function match_fn(s) {
var m;
switch (state) {
case 0:
m = re_filename.exec(s);
if (m) {
filename = m[2];
}
m = re_insn.exec(s);
if (m) {
lineno = parseInt(m[1]);
state = 1;
}
break;
case 1:
m = re_error.exec(s);
if (m) {
errors.push({ path: filename, line: lineno, msg: m[1] });
state = 0;
}
break;
}
}
var Module: EmscriptenModule = emglobal.nesasm({
instantiateWasm: moduleInstFn('nesasm'),
noInitialRun: true,
print: match_fn
});
var FS = Module.FS;
populateFiles(step, FS, {
mainFilePath: 'main.a'
});
var binpath = step.prefix + '.nes';
var lstpath = step.prefix + '.lst';
var sympath = step.prefix + '.fns';
execMain(step, Module, [step.path, '-s', "-l", "2"]);
// parse main listing, get errors and listings for each file
var listings: CodeListingMap = {};
try {
var alst = FS.readFile(lstpath, { 'encoding': 'utf8' });
// 16 00:C004 8E 17 40 STX $4017 ; disable APU frame IRQ
var asmlines = parseListing(alst, /^\s*(\d+)\s+([0-9A-F]+):([0-9A-F]+)\s+([0-9A-F ]+?) (.*)/i, 1, 3, 4);
putWorkFile(lstpath, alst);
listings[lstpath] = {
lines: asmlines,
text: alst
};
} catch (e) {
//
}
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 missing ENDM or segment overflow?" });
return { errors: errors }
}
putWorkFile(binpath, aout);
putWorkFile(sympath, asym);
if (alst) putWorkFile(lstpath, alst); // listing optional (use LIST)
// return unchanged if no files changed
if (!anyTargetChanged(step, [binpath, sympath]))
return;
// parse symbols
var symbolmap = {};
for (var s of asym.split("\n")) {
if (!s.startsWith(';')) {
var m = /(\w+)\s+=\s+[$]([0-9A-F]+)/.exec(s);
if (m) {
symbolmap[m[1]] = parseInt(m[2], 16);
}
}
}
return {
output: aout,
listings: listings,
errors: errors,
symbolmap: symbolmap,
};
}
/*
------+-------------------+-------------+----+---------+------+-----------------------+-------------------------------------------------------------------
Line | # File Line | Line Type | MX | Reloc | Size | Address Object Code | Source Code
------+-------------------+-------------+----+---------+------+-----------------------+-------------------------------------------------------------------
1 | 1 zap.asm 1 | Unknown | ?? | | -1 | 00/FFFF | broak
2 | 1 zap.asm 2 | Comment | ?? | | -1 | 00/FFFF | * SPACEGAME
=> [Error] Impossible to decode address mode for instruction 'BNE KABOOM!' (line 315, file 'zap.asm') : The number of element in 'KABOOM!' is even (should be value [operator value [operator value]...]).
=> [Error] Unknown line 'foo' in source file 'zap.asm' (line 315)
=> Creating Object file 'pcs.bin'
=> Creating Output file 'pcs.bin_S01__Output.txt'
*/
export function assembleMerlin32(step: BuildStep): BuildStepResult {
loadNative("merlin32");
var errors = [];
var lstfiles = [];
gatherFiles(step, { mainFilePath: "main.lnk" });
var objpath = step.prefix + ".bin";
if (staleFiles(step, [objpath])) {
var args = ['-v', step.path];
var merlin32: EmscriptenModule = emglobal.merlin32({
instantiateWasm: moduleInstFn('merlin32'),
noInitialRun: true,
print: (s: string) => {
var m = /\s*=>\s*Creating Output file '(.+?)'/.exec(s);
if (m) {
lstfiles.push(m[1]);
}
var errpos = s.indexOf('Error');
if (errpos >= 0) {
s = s.slice(errpos + 6).trim();
var mline = /\bline (\d+)\b/.exec(s);
var mpath = /\bfile '(.+?)'/.exec(s);
errors.push({
line: parseInt(mline[1]) || 0,
msg: s,
path: mpath[1] || step.path,
});
}
},
printErr: print_fn,
});
var FS = merlin32.FS;
populateFiles(step, FS);
execMain(step, merlin32, args);
if (errors.length)
return { errors: errors };
var errout = null;
try {
errout = FS.readFile("error_output.txt", { encoding: 'utf8' });
} catch (e) {
//
}
var objout = FS.readFile(objpath, { encoding: 'binary' });
putWorkFile(objpath, objout);
if (!anyTargetChanged(step, [objpath]))
return;
var symbolmap = {};
var segments = [];
var listings: CodeListingMap = {};
lstfiles.forEach((lstfn) => {
var lst = FS.readFile(lstfn, { encoding: 'utf8' }) as string;
lst.split('\n').forEach((line) => {
var toks = line.split(/\s*\|\s*/);
if (toks && toks[6]) {
var toks2 = toks[1].split(/\s+/);
var toks3 = toks[6].split(/[:/]/, 4);
var path = toks2[1];
if (path && toks2[2] && toks3[1]) {
var lstline = {
line: parseInt(toks2[2]),
offset: parseInt(toks3[1].trim(), 16),
insns: toks3[2],
cycles: null,
iscode: false // TODO
};
var lst = listings[path];
if (!lst) listings[path] = lst = { lines: [] };
lst.lines.push(lstline);
//console.log(path,toks2,toks3);
}
}
});
});
return {
output: objout, //.slice(0),
listings: listings,
errors: errors,
symbolmap: symbolmap,
segments: segments
};
}
}
// README.md:2:5: parse error, expected: statement or variable assignment, integer variable, variable assignment
export function compileFastBasic(step: BuildStep): BuildStepResult {
// TODO: fastbasic-fp?
loadNative("fastbasic-int");
var params = step.params;
gatherFiles(step, { mainFilePath: "main.fb" });
var destpath = step.prefix + '.s';
var errors = [];
if (staleFiles(step, [destpath])) {
var fastbasic: EmscriptenModule = emglobal.fastbasic({
instantiateWasm: moduleInstFn('fastbasic-int'),
noInitialRun: true,
print: print_fn,
printErr: makeErrorMatcher(errors, /(.+?):(\d+):(\d+):\s*(.+)/, 2, 4, step.path, 1),
});
var FS = fastbasic.FS;
populateFiles(step, FS);
var libfile = 'fastbasic-int.lib'
params.libargs = [libfile];
params.cfgfile = params.fastbasic_cfgfile;
//params.extra_compile_args = ["--asm-define", "NO_SMCODE"];
params.extra_link_files = [libfile, params.cfgfile];
//fixParamsWithDefines(step.path, params);
var args = [step.path, destpath];
execMain(step, fastbasic, args);
if (errors.length)
return { errors: errors };
var asmout = FS.readFile(destpath, { encoding: 'utf8' });
putWorkFile(destpath, asmout);
}
return {
nexttool: "ca65",
path: destpath,
args: [destpath],
files: [destpath],
};
}