split workermain into tools/* modules; share parseXMLPoorly()

This commit is contained in:
Steven Hugg 2021-08-12 13:51:10 -05:00
parent a12d1a0638
commit bd00d98b77
13 changed files with 2484 additions and 2450 deletions

View File

@ -1,5 +1,5 @@
import { HDLError } from "./hdlruntime";
import { parseXMLPoorly, XMLNode } from "../util";
import { HDLAlwaysBlock, HDLArrayItem, HDLBinop, HDLBlock, HDLConstant, HDLDataType, HDLDataTypeObject, HDLExpr, HDLExtendop, HDLFile, HDLFuncCall, HDLHierarchyDef, HDLInstanceDef, HDLLogicType, HDLModuleDef, HDLNativeType, HDLPort, HDLSensItem, HDLSourceLocation, HDLSourceObject, HDLTriop, HDLUnit, HDLUnop, HDLUnpackArray, HDLValue, HDLVariableDef, HDLVarRef, HDLWhileOp, isArrayType, isBinop, isBlock, isConstExpr, isFuncCall, isLogicType, isTriop, isUnop, isVarDecl, isVarRef } from "./hdltypes";
/**
@ -26,80 +26,6 @@ import { HDLAlwaysBlock, HDLArrayItem, HDLBinop, HDLBlock, HDLConstant, HDLDataT
}
}
interface XMLNode {
type: string;
text: string | null;
children: XMLNode[];
attrs: { [id: string]: string };
obj: any;
}
type XMLVisitFunction = (node: XMLNode) => any;
function escapeXML(s: string): string {
if (s.indexOf('&') >= 0) {
return s.replace(/'/g, "'")
.replace(/"/g, '"')
.replace(/>/g, '>')
.replace(/&lt;/g, '<')
.replace(/&amp;/g, '&');
} else {
return s;
}
}
function parseXMLPoorly(s: string, openfn?: XMLVisitFunction, closefn?: XMLVisitFunction): XMLNode {
const tag_re = /[<]([/]?)([?a-z_-]+)([^>]*)[>]+|(\s*[^<]+)/gi;
const attr_re = /\s*(\w+)="(.*?)"\s*/gi;
var fm: RegExpMatchArray;
var stack: XMLNode[] = [];
var top: XMLNode;
function closetop() {
top = stack.pop();
if (top == null || top.type != ident) throw new CompileError(null, "mismatch close tag: " + ident);
if (closefn) {
top.obj = closefn(top);
}
if (stack.length == 0) throw new CompileError(null, "close tag without open: " + ident);
stack[stack.length - 1].children.push(top);
}
function parseattrs(as: string): { [id: string]: string } {
var am;
var attrs = {};
if (as != null) {
while (am = attr_re.exec(as)) {
attrs[am[1]] = escapeXML(am[2]);
}
}
return attrs;
}
while (fm = tag_re.exec(s)) {
var [_m0, close, ident, attrs, content] = fm;
//console.log(stack.length, close, ident, attrs, content);
if (close) {
closetop();
} else if (ident) {
var node = { type: ident, text: null, children: [], attrs: parseattrs(attrs), obj: null };
stack.push(node);
if (attrs) {
parseattrs(attrs);
}
if (openfn) {
node.obj = openfn(node);
}
if (attrs && attrs.endsWith('/')) closetop();
} else if (content != null) {
if (stack.length == 0) throw new CompileError(null, "content without element");
var txt = escapeXML(content as string).trim();
if (txt.length) stack[stack.length - 1].text = txt;
}
}
if (stack.length != 1) throw new CompileError(null, "tag not closed");
if (stack[0].type != '?xml') throw new CompileError(null, "?xml needs to be first element");
return top;
}
export class VerilogXMLParser implements HDLUnit {
files: { [id: string]: HDLFile } = {};

View File

@ -538,3 +538,83 @@ export function parseBool(s : string) : boolean {
if (s == 'true' || s == '1') return true;
return s ? true : false;
}
///
export class XMLParseError extends Error {
}
export interface XMLNode {
type: string;
text: string | null;
children: XMLNode[];
attrs: { [id: string]: string };
obj: any;
}
export type XMLVisitFunction = (node: XMLNode) => any;
function escapeXML(s: string): string {
if (s.indexOf('&') >= 0) {
return s.replace(/&apos;/g, "'")
.replace(/&quot;/g, '"')
.replace(/&gt;/g, '>')
.replace(/&lt;/g, '<')
.replace(/&amp;/g, '&');
} else {
return s;
}
}
export function parseXMLPoorly(s: string, openfn?: XMLVisitFunction, closefn?: XMLVisitFunction): XMLNode {
const tag_re = /[<]([/]?)([?a-z_-]+)([^>]*)[>]+|(\s*[^<]+)/gi;
const attr_re = /\s*(\w+)="(.*?)"\s*/gi;
var fm: RegExpMatchArray;
var stack: XMLNode[] = [];
var top: XMLNode;
function closetop() {
top = stack.pop();
if (top == null || top.type != ident) throw new XMLParseError("mismatch close tag: " + ident);
if (closefn) {
top.obj = closefn(top);
}
if (stack.length == 0) throw new XMLParseError("close tag without open: " + ident);
stack[stack.length - 1].children.push(top);
}
function parseattrs(as: string): { [id: string]: string } {
var am;
var attrs = {};
if (as != null) {
while (am = attr_re.exec(as)) {
attrs[am[1]] = escapeXML(am[2]);
}
}
return attrs;
}
while (fm = tag_re.exec(s)) {
var [_m0, close, ident, attrs, content] = fm;
//console.log(stack.length, close, ident, attrs, content);
if (close) {
closetop();
} else if (ident) {
var node = { type: ident, text: null, children: [], attrs: parseattrs(attrs), obj: null };
stack.push(node);
if (attrs) {
parseattrs(attrs);
}
if (openfn) {
node.obj = openfn(node);
}
if (attrs && attrs.endsWith('/')) closetop();
} else if (content != null) {
if (stack.length == 0) throw new XMLParseError("content without element");
var txt = escapeXML(content as string).trim();
if (txt.length) stack[stack.length - 1].text = txt;
}
}
if (stack.length != 1) throw new XMLParseError("tag not closed");
if (stack[0].type != '?xml') throw new XMLParseError("?xml needs to be first element");
return top;
}

239
src/worker/tools/arm.ts Normal file
View File

@ -0,0 +1,239 @@
import { hex } from "../../common/util";
import { WorkerResult, CodeListingMap, WorkerError, SourceLine } from "../../common/workertypes";
import { anyTargetChanged, BuildStep, BuildStepResult, emglobal, EmscriptenModule, execMain, gatherFiles, getPrefix, getWorkFileAsString, loadNative, makeErrorMatcher, moduleInstFn, populateFiles, putWorkFile, re_crlf, staleFiles } from "../workermain"
export function assembleARMIPS(step: BuildStep): WorkerResult {
loadNative("armips");
var errors = [];
gatherFiles(step, { mainFilePath: "main.asm" });
var objpath = "main.bin";
var lstpath = step.prefix + ".lst";
var sympath = step.prefix + ".sym";
//test.armips(3) error: Parse error '.arm'
var error_fn = makeErrorMatcher(errors, /^(.+?)\((\d+)\)\s+(fatal error|error|warning):\s+(.+)/, 2, 4, step.path, 1);
if (staleFiles(step, [objpath])) {
var args = [step.path, '-temp', lstpath, '-sym', sympath, '-erroronwarning'];
var armips: EmscriptenModule = emglobal.armips({
instantiateWasm: moduleInstFn('armips'),
noInitialRun: true,
print: error_fn,
printErr: error_fn,
});
var FS = armips.FS;
var code = getWorkFileAsString(step.path);
code = `.arm.little :: .create "${objpath}",0 :: ${code}
.close`;
putWorkFile(step.path, code);
populateFiles(step, FS);
execMain(step, armips, args);
if (errors.length)
return { errors: errors };
var objout = FS.readFile(objpath, { encoding: 'binary' }) as Uint8Array;
putWorkFile(objpath, objout);
if (!anyTargetChanged(step, [objpath]))
return;
var symbolmap = {};
var segments = [];
var listings: CodeListingMap = {};
var lstout = FS.readFile(lstpath, { encoding: 'utf8' }) as string;
var lines = lstout.split(re_crlf);
//00000034 .word 0x11223344 ; /vidfill.armips line 25
var re_asmline = /^([0-9A-F]+) (.+?); [/](.+?) line (\d+)/;
var lastofs = -1;
for (var line of lines) {
var m;
if (m = re_asmline.exec(line)) {
var path = m[3];
var path2 = getPrefix(path) + '.lst'; // TODO: don't rename listing
var lst = listings[path2];
if (lst == null) { lst = listings[path2] = { lines: [] }; }
var ofs = parseInt(m[1], 16);
if (lastofs == ofs) {
lst.lines.pop(); // get rid of duplicate offset
} else if (ofs > lastofs) {
var lastline = lst.lines[lst.lines.length - 1];
if (lastline && !lastline.insns) {
var insns = objout.slice(lastofs, ofs).reverse();
lastline.insns = Array.from(insns).map((b) => hex(b, 2)).join('');
}
}
lst.lines.push({
path: path,
line: parseInt(m[4]),
offset: ofs
});
lastofs = ofs;
}
}
//listings[lstpath] = {lines:lstlines, text:lstout};
var symout = FS.readFile(sympath, { encoding: 'utf8' }) as string;
//0000000C loop2
//00000034 .dbl:0004
var re_symline = /^([0-9A-F]+)\s+(.+)/;
for (var line of symout.split(re_crlf)) {
var m;
if (m = re_symline.exec(line)) {
symbolmap[m[2]] = parseInt(m[1], 16);
}
}
return {
output: objout, //.slice(0),
listings: listings,
errors: errors,
symbolmap: symbolmap,
segments: segments
};
}
}
export function assembleVASMARM(step: BuildStep): BuildStepResult {
loadNative("vasmarm_std");
/// error 2 in line 8 of "gfxtest.c": unknown mnemonic <ew>
/// error 3007: undefined symbol <XXLOOP>
/// TODO: match undefined symbols
var re_err1 = /^(fatal error|error|warning)? (\d+) in line (\d+) of "(.+)": (.+)/;
var re_err2 = /^(fatal error|error|warning)? (\d+): (.+)/;
var re_undefsym = /symbol <(.+?)>/;
var errors: WorkerError[] = [];
var undefsyms = [];
function findUndefinedSymbols(line: string) {
// find undefined symbols in line
undefsyms.forEach((sym) => {
if (line.indexOf(sym) >= 0) {
errors.push({
path: curpath,
line: curline,
msg: "Undefined symbol: " + sym,
})
}
});
}
function match_fn(s) {
let matches = re_err1.exec(s);
if (matches) {
errors.push({
line: parseInt(matches[3]),
path: matches[4],
msg: matches[5],
});
} else {
matches = re_err2.exec(s);
if (matches) {
let m = re_undefsym.exec(matches[3]);
if (m) {
undefsyms.push(m[1]);
} else {
errors.push({
line: 0,
msg: s,
});
}
} else {
console.log(s);
}
}
}
gatherFiles(step, { mainFilePath: "main.asm" });
var objpath = step.prefix + ".bin";
var lstpath = step.prefix + ".lst";
if (staleFiles(step, [objpath])) {
var args = ['-Fbin', '-m7tdmi', '-x', '-wfail', step.path, '-o', objpath, '-L', lstpath];
var vasm: EmscriptenModule = emglobal.vasm({
instantiateWasm: moduleInstFn('vasmarm_std'),
noInitialRun: true,
print: match_fn,
printErr: match_fn,
});
var FS = vasm.FS;
populateFiles(step, FS);
execMain(step, vasm, args);
if (errors.length) {
return { errors: errors };
}
if (undefsyms.length == 0) {
var objout = FS.readFile(objpath, { encoding: 'binary' });
putWorkFile(objpath, objout);
if (!anyTargetChanged(step, [objpath]))
return;
}
var lstout = FS.readFile(lstpath, { encoding: 'utf8' });
// 00:00000018 023020E0 14: eor r3, r0, r2
// Source: "vidfill.vasm"
// 00: ".text" (0-40)
// LOOP 00:00000018
// STACK S:20010000
var symbolmap = {};
var segments = []; // TODO
var listings: CodeListingMap = {};
// TODO: parse listings
var re_asmline = /^(\d+):([0-9A-F]+)\s+([0-9A-F ]+)\s+(\d+)([:M])/;
var re_secline = /^(\d+):\s+"(.+)"/;
var re_nameline = /^Source:\s+"(.+)"/;
var re_symline = /^(\w+)\s+(\d+):([0-9A-F]+)/;
var re_emptyline = /^\s+(\d+)([:M])/;
var curpath = step.path;
var curline = 0;
var sections = {};
// map file and section indices -> names
var lines: string[] = lstout.split(re_crlf);
// parse lines
var lstlines: SourceLine[] = [];
for (var line of lines) {
var m;
if (m = re_secline.exec(line)) {
sections[m[1]] = m[2];
} else if (m = re_nameline.exec(line)) {
curpath = m[1];
} else if (m = re_symline.exec(line)) {
symbolmap[m[1]] = parseInt(m[3], 16);
} else if (m = re_asmline.exec(line)) {
if (m[5] == ':') {
curline = parseInt(m[4]);
} else {
// TODO: macro line
}
lstlines.push({
path: curpath,
line: curline,
offset: parseInt(m[2], 16),
insns: m[3].replaceAll(' ', '')
});
findUndefinedSymbols(line);
} else if (m = re_emptyline.exec(line)) {
curline = parseInt(m[1]);
findUndefinedSymbols(line);
} else {
//console.log(line);
}
}
listings[lstpath] = { lines: lstlines, text: lstout };
// catch-all if no error generated
if (undefsyms.length && errors.length == 0) {
errors.push({
line: 0,
msg: 'Undefined symbols: ' + undefsyms.join(', ')
})
}
return {
output: objout, //.slice(0x34),
listings: listings,
errors: errors,
symbolmap: symbolmap,
segments: segments
};
}
}

303
src/worker/tools/cc65.ts Normal file
View File

@ -0,0 +1,303 @@
import { getRootBasePlatform } from "../../common/util";
import { CodeListingMap, WorkerError } from "../../common/workertypes";
import { re_crlf, BuildStepResult, anyTargetChanged, execMain, gatherFiles, msvcErrorMatcher, populateEntry, populateExtraFiles, populateFiles, print_fn, putWorkFile, setupFS, staleFiles, BuildStep, emglobal, loadNative, moduleInstFn, fixParamsWithDefines, store } from "../workermain";
import { EmscriptenModule } from "../workermain"
/*
000000r 1 .segment "CODE"
000000r 1 .proc _rasterWait: near
000000r 1 ; int main() { return mul2(2); }
000000r 1 .dbg line, "main.c", 3
000014r 1 .dbg func, "main", "00", extern, "_main"
000000r 1 A2 00 ldx #$00
00B700 1 BOOT2:
00B700 1 A2 01 ldx #1 ;track
00B725 1 00 IBLASTDRVN: .byte 0
00B726 1 xx xx IBSECSZ: .res 2
00BA2F 1 2A 2B E8 2C HEX "2A2BE82C2D2E2F303132F0F133343536"
*/
function parseCA65Listing(code, symbols, params, dbg) {
var segofs = 0;
var offset = 0;
var dbgLineMatch = /^([0-9A-F]+)([r]?)\s+(\d+)\s+[.]dbg\s+(\w+), "([^"]+)", (.+)/;
var funcLineMatch = /"(\w+)", (\w+), "(\w+)"/;
var insnLineMatch = /^([0-9A-F]+)([r]?)\s{1,2}(\d+)\s{1,2}([0-9A-Frx ]{11})\s+(.*)/;
var segMatch = /[.]segment\s+"(\w+)"/i;
var lines = [];
var linenum = 0;
// TODO: only does .c functions, not all .s files
for (var line of code.split(re_crlf)) {
var dbgm = dbgLineMatch.exec(line);
if (dbgm && dbgm[1]) {
var dbgtype = dbgm[4];
offset = parseInt(dbgm[1], 16);
if (dbgtype == 'func') {
var funcm = funcLineMatch.exec(dbgm[6]);
if (funcm) {
var funcofs = symbols[funcm[3]];
if (typeof funcofs === 'number') {
segofs = funcofs - offset;
//console.log(funcm[3], funcofs, '-', offset);
}
}
}
}
if (dbg) {
if (dbgm && dbgtype == 'line') {
lines.push({
// TODO: sourcefile
line: parseInt(dbgm[6]),
offset: offset + segofs,
insns: null
});
}
} else {
var linem = insnLineMatch.exec(line);
var topfile = linem && linem[3] == '1';
if (topfile) linenum++;
if (topfile && linem[1]) {
var offset = parseInt(linem[1], 16);
var insns = linem[4].trim();
if (insns.length) {
// take back one to honor the long .byte line
if (linem[5].length == 0) {
linenum--;
} else {
lines.push({
line: linenum,
offset: offset + segofs,
insns: insns,
iscode: true // TODO: can't really tell unless we parse it
});
}
} else {
var sym = linem[5];
var segm = sym && segMatch.exec(sym);
if (segm && segm[1]) {
var symofs = symbols['__' + segm[1] + '_RUN__'];
if (typeof symofs === 'number') {
segofs = symofs;
//console.log(sym, symofs, '-', offset);
}
} else if (sym.endsWith(':') && !sym.startsWith('@')) {
var symofs = symbols[sym.substring(0, sym.length - 1)];
if (typeof symofs === 'number') {
segofs = symofs - offset;
//console.log(sym, segofs, symofs, offset);
}
}
}
}
}
}
return lines;
}
export function assembleCA65(step: BuildStep): BuildStepResult {
loadNative("ca65");
var errors = [];
gatherFiles(step, { mainFilePath: "main.s" });
var objpath = step.prefix + ".o";
var lstpath = step.prefix + ".lst";
if (staleFiles(step, [objpath, lstpath])) {
var objout, lstout;
var CA65: EmscriptenModule = emglobal.ca65({
instantiateWasm: moduleInstFn('ca65'),
noInitialRun: true,
//logReadFiles:true,
print: print_fn,
printErr: msvcErrorMatcher(errors),
});
var FS = CA65.FS;
setupFS(FS, '65-' + getRootBasePlatform(step.platform));
populateFiles(step, FS);
fixParamsWithDefines(step.path, step.params);
var args = ['-v', '-g', '-I', '/share/asminc', '-o', objpath, '-l', lstpath, step.path];
args.unshift.apply(args, ["-D", "__8BITWORKSHOP__=1"]);
if (step.mainfile) {
args.unshift.apply(args, ["-D", "__MAIN__=1"]);
}
execMain(step, CA65, args);
if (errors.length)
return { errors: errors };
objout = FS.readFile(objpath, { encoding: 'binary' });
lstout = FS.readFile(lstpath, { encoding: 'utf8' });
putWorkFile(objpath, objout);
putWorkFile(lstpath, lstout);
}
return {
linktool: "ld65",
files: [objpath, lstpath],
args: [objpath]
};
}
export function linkLD65(step: BuildStep): BuildStepResult {
loadNative("ld65");
var params = step.params;
gatherFiles(step);
var binpath = "main";
if (staleFiles(step, [binpath])) {
var errors = [];
var LD65: EmscriptenModule = emglobal.ld65({
instantiateWasm: moduleInstFn('ld65'),
noInitialRun: true,
//logReadFiles:true,
print: print_fn,
printErr: function (s) { errors.push({ msg: s, line: 0 }); }
});
var FS = LD65.FS;
setupFS(FS, '65-' + getRootBasePlatform(step.platform));
populateFiles(step, FS);
populateExtraFiles(step, FS, params.extra_link_files);
// populate .cfg file, if it is a custom one
if (store.hasFile(params.cfgfile)) {
populateEntry(FS, params.cfgfile, store.getFileEntry(params.cfgfile), null);
}
var libargs = params.libargs || [];
var cfgfile = params.cfgfile;
var args = ['--cfg-path', '/share/cfg',
'--lib-path', '/share/lib',
'-C', cfgfile,
'-Ln', 'main.vice',
//'--dbgfile', 'main.dbg', // TODO: get proper line numbers
'-o', 'main', '-m', 'main.map'].concat(step.args, libargs);
//console.log(args);
execMain(step, LD65, args);
if (errors.length)
return { errors: errors };
var aout = FS.readFile("main", { encoding: 'binary' });
var mapout = FS.readFile("main.map", { encoding: 'utf8' });
var viceout = FS.readFile("main.vice", { encoding: 'utf8' });
//var dbgout = FS.readFile("main.dbg", {encoding:'utf8'});
putWorkFile("main", aout);
putWorkFile("main.map", mapout);
putWorkFile("main.vice", viceout);
// return unchanged if no files changed
if (!anyTargetChanged(step, ["main", "main.map", "main.vice"]))
return;
// parse symbol map (TODO: omit segments, constants)
var symbolmap = {};
for (var s of viceout.split("\n")) {
var toks = s.split(" ");
if (toks[0] == 'al') {
let ident = toks[2].substr(1);
if (ident.length != 5 || !ident.startsWith('L')) { // no line numbers
let ofs = parseInt(toks[1], 16);
symbolmap[ident] = ofs;
}
}
}
// build segment map
var seg_re = /^__(\w+)_SIZE__$/;
// TODO: move to Platform class
var segments = [];
segments.push({ name: 'CPU Stack', start: 0x100, size: 0x100, type: 'ram' });
segments.push({ name: 'CPU Vectors', start: 0xfffa, size: 0x6, type: 'rom' });
// TODO: CHR, banks, etc
for (let ident in symbolmap) {
let m = seg_re.exec(ident);
if (m) {
let seg = m[1];
let segstart = symbolmap['__' + seg + '_RUN__'] || symbolmap['__' + seg + '_START__'];
let segsize = symbolmap['__' + seg + '_SIZE__'];
let seglast = symbolmap['__' + seg + '_LAST__'];
if (segstart >= 0 && segsize > 0 && !seg.startsWith('PRG') && seg != 'RAM') { // TODO
var type = null;
if (seg.startsWith('CODE') || seg == 'STARTUP' || seg == 'RODATA' || seg.endsWith('ROM')) type = 'rom';
else if (seg == 'ZP' || seg == 'DATA' || seg == 'BSS' || seg.endsWith('RAM')) type = 'ram';
segments.push({ name: seg, start: segstart, size: segsize, last: seglast, type: type });
}
}
}
// build listings
var listings: CodeListingMap = {};
for (var fn of step.files) {
if (fn.endsWith('.lst')) {
var lstout = FS.readFile(fn, { encoding: 'utf8' });
lstout = lstout.split('\n\n')[1] || lstout; // remove header
var asmlines = parseCA65Listing(lstout, symbolmap, params, false);
var srclines = parseCA65Listing(lstout, symbolmap, params, true);
putWorkFile(fn, lstout);
// TODO: you have to get rid of all source lines to get asm listing
listings[fn] = {
asmlines: srclines.length ? asmlines : null,
lines: srclines.length ? srclines : asmlines,
text: lstout
};
}
}
return {
output: aout, //.slice(0),
listings: listings,
errors: errors,
symbolmap: symbolmap,
segments: segments
};
}
}
export function compileCC65(step: BuildStep): BuildStepResult {
loadNative("cc65");
var params = step.params;
// stderr
var re_err1 = /(.*?)[(](\d+)[)].*?: (.+)/;
var errors: WorkerError[] = [];
var errline = 0;
function match_fn(s) {
console.log(s);
var matches = re_err1.exec(s);
if (matches) {
errline = parseInt(matches[2]);
errors.push({
line: errline,
msg: matches[3],
path: matches[1]
});
}
}
gatherFiles(step, { mainFilePath: "main.c" });
var destpath = step.prefix + '.s';
if (staleFiles(step, [destpath])) {
var CC65: EmscriptenModule = emglobal.cc65({
instantiateWasm: moduleInstFn('cc65'),
noInitialRun: true,
//logReadFiles:true,
print: print_fn,
printErr: match_fn,
});
var FS = CC65.FS;
setupFS(FS, '65-' + getRootBasePlatform(step.platform));
populateFiles(step, FS);
fixParamsWithDefines(step.path, params);
var args = [
'-I', '/share/include',
'-I', '.',
"-D", "__8BITWORKSHOP__",
];
if (params.define) {
params.define.forEach((x) => args.push('-D' + x));
}
if (step.mainfile) {
args.unshift.apply(args, ["-D", "__MAIN__"]);
}
var customArgs = params.extra_compiler_args || ['-T', '-g', '-Oirs', '-Cl'];
args = args.concat(customArgs, args);
args.push(step.path);
//console.log(args);
execMain(step, CC65, 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],
};
}

311
src/worker/tools/dasm.ts Normal file
View File

@ -0,0 +1,311 @@
import { CodeListingMap, WorkerError } from "../../common/workertypes";
import { re_crlf, BuildStep, BuildStepResult, load, msvcErrorMatcher, emglobal, populateFiles, execMain, putWorkFile, anyTargetChanged, re_msvc, gatherFiles, getWorkFileAsString, print_fn, setupFS, setupStdin, staleFiles } from "../workermain";
import { EmscriptenModule } from "../workermain"
function parseDASMListing(lstpath: string, lsttext: string, listings: CodeListingMap, errors: WorkerError[], 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(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 = re_msvc.exec(line);
if (errm) {
errors.push({
path: errm[1],
line: parseInt(errm[2]),
msg: errm[4]
})
}
}
}
export function assembleDASM(step: BuildStep): BuildStepResult {
load("dasm");
var re_usl = /(\w+)\s+0000\s+[?][?][?][?]/;
var unresolved = {};
var errors = [];
var errorMatcher = msvcErrorMatcher(errors);
function match_fn(s: string) {
// 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: EmscriptenModule = emglobal.DASM({
noInitialRun: true,
print: match_fn
});
var FS = Module.FS;
populateFiles(step, FS, {
mainFilePath: 'main.a'
});
var binpath = step.prefix + '.bin';
var lstpath = step.prefix + '.lst';
var sympath = step.prefix + '.sym';
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: CodeListingMap = {};
//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 }
}
putWorkFile(binpath, aout);
putWorkFile(lstpath, alst);
putWorkFile(sympath, asym);
// return unchanged if no files changed
// TODO: what if listing or symbols change?
if (!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,
};
}
function preprocessBatariBasic(code: string): string {
load("bbpreprocess");
var bbout = "";
function addbbout_fn(s) {
bbout += s;
bbout += "\n";
}
var BBPRE: EmscriptenModule = emglobal.preprocess({
noInitialRun: true,
//logReadFiles:true,
print: addbbout_fn,
printErr: print_fn,
noFSInit: true,
});
var FS = BBPRE.FS;
setupStdin(FS, code);
BBPRE.callMain([]);
console.log("preprocess " + code.length + " -> " + bbout.length + " bytes");
return bbout;
}
export function compileBatariBasic(step: BuildStep): BuildStepResult {
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]
});
}
}
gatherFiles(step, { mainFilePath: "main.bas" });
var destpath = step.prefix + '.asm';
if (staleFiles(step, [destpath])) {
var BB: EmscriptenModule = emglobal.bb2600basic({
noInitialRun: true,
//logReadFiles:true,
print: addasmout_fn,
printErr: match_fn,
noFSInit: true,
TOTAL_MEMORY: 64 * 1024 * 1024,
});
var FS = BB.FS;
populateFiles(step, FS);
// preprocess, pipe file to stdin
var code = getWorkFileAsString(step.path);
code = preprocessBatariBasic(code);
setupStdin(FS, code);
setupFS(FS, '2600basic');
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
putWorkFile(destpath, combinedasm);
putWorkFile("2600basic.h", FS.readFile("/share/includes/2600basic.h"));
putWorkFile("2600basic_variable_redefs.h", redefsout);
}
return {
nexttool: "dasm",
path: destpath,
args: [destpath],
files: [destpath, "2600basic.h", "2600basic_variable_redefs.h"],
bblines: true,
};
}

239
src/worker/tools/m6502.ts Normal file
View File

@ -0,0 +1,239 @@
import { WorkerError, CodeListingMap } from "../../common/workertypes";
import { anyTargetChanged, BuildStep, BuildStepResult, emglobal, EmscriptenModule, execMain, gatherFiles, loadNative, makeErrorMatcher, moduleInstFn, parseListing, populateFiles, print_fn, putWorkFile, staleFiles } from "../workermain"
// 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],
};
}

253
src/worker/tools/m6809.ts Normal file
View File

@ -0,0 +1,253 @@
import { CodeListingMap, WorkerError } from "../../common/workertypes";
import { BuildStep, BuildStepResult, load, emglobal, print_fn, populateFiles, execMain, putWorkFile, parseListing, loadNative, gatherFiles, staleFiles, moduleInstFn, getWorkFileAsString, preprocessMCPP, fixParamsWithDefines, msvcErrorMatcher, populateExtraFiles, anyTargetChanged, parseSourceLines } from "../workermain";
import { EmscriptenModule } from "../workermain";
// http://datapipe-blackbeltsystems.com/windows/flex/asm09.html
export function assembleXASM6809(step: BuildStep): BuildStepResult {
load("xasm6809");
var alst = "";
var lasterror = null;
var errors = [];
function match_fn(s) {
alst += s;
alst += "\n";
if (lasterror) {
var line = parseInt(s.slice(0, 5)) || 0;
errors.push({
line: line,
msg: lasterror
});
lasterror = null;
}
else if (s.startsWith("***** ")) {
lasterror = s.slice(6);
}
}
var Module: EmscriptenModule = emglobal.xasm6809({
noInitialRun: true,
//logReadFiles:true,
print: match_fn,
printErr: print_fn
});
var FS = Module.FS;
//setupFS(FS);
populateFiles(step, FS, {
mainFilePath: 'main.asm'
});
var binpath = step.prefix + '.bin';
var lstpath = step.prefix + '.lst'; // in stdout
execMain(step, Module, ["-c", "-l", "-s", "-y", "-o=" + binpath, step.path]);
if (errors.length)
return { errors: errors };
var aout = FS.readFile(binpath, { encoding: 'binary' });
if (aout.length == 0) {
console.log(alst);
errors.push({ line: 0, msg: "Empty output file" });
return { errors: errors };
}
putWorkFile(binpath, aout);
putWorkFile(lstpath, alst);
// TODO: symbol map
//mond09 0000
var symbolmap = {};
//00005 W 0003 [ 8] A6890011 lda >PALETTE,x
//00012 0011 0C0203 fcb 12,2,3
var asmlines = parseListing(alst, /^\s*([0-9]+) .+ ([0-9A-F]+)\s+\[([0-9 ]+)\]\s+([0-9A-F]+) (.*)/i, 1, 2, 4, 3);
var listings: CodeListingMap = {};
listings[step.prefix + '.lst'] = { lines: asmlines, text: alst };
return {
output: aout,
listings: listings,
errors: errors,
symbolmap: symbolmap,
};
}
export function compileCMOC(step: BuildStep): BuildStepResult {
loadNative("cmoc");
var params = step.params;
// stderr
var re_err1 = /^[/]*([^:]*):(\d+): (.+)$/;
var errors: WorkerError[] = [];
var errline = 0;
function match_fn(s) {
var matches = re_err1.exec(s);
if (matches) {
errors.push({
line: parseInt(matches[2]),
msg: matches[3],
path: matches[1] || step.path
});
} else {
console.log(s);
}
}
gatherFiles(step, { mainFilePath: "main.c" });
var destpath = step.prefix + '.s';
if (staleFiles(step, [destpath])) {
var args = ['-S', '-Werror', '-V',
'-I/share/include',
'-I.',
step.path];
var CMOC: EmscriptenModule = emglobal.cmoc({
instantiateWasm: moduleInstFn('cmoc'),
noInitialRun: true,
//logReadFiles:true,
print: match_fn,
printErr: match_fn,
});
// load source file and preprocess
var code = getWorkFileAsString(step.path);
var preproc = preprocessMCPP(step, null);
if (preproc.errors) {
return { errors: preproc.errors }
}
else code = preproc.code;
// set up filesystem
var FS = CMOC.FS;
//setupFS(FS, '65-'+getRootBasePlatform(step.platform));
populateFiles(step, FS);
FS.writeFile(step.path, code);
fixParamsWithDefines(step.path, params);
if (params.extra_compile_args) {
args.unshift.apply(args, params.extra_compile_args);
}
execMain(step, CMOC, args);
if (errors.length)
return { errors: errors };
var asmout = FS.readFile(destpath, { encoding: 'utf8' });
putWorkFile(destpath, asmout);
}
return {
nexttool: "lwasm",
path: destpath,
args: [destpath],
files: [destpath],
};
}
export function assembleLWASM(step: BuildStep): BuildStepResult {
loadNative("lwasm");
var errors = [];
gatherFiles(step, { mainFilePath: "main.s" });
var objpath = step.prefix + ".o";
var lstpath = step.prefix + ".lst";
if (staleFiles(step, [objpath, lstpath])) {
var objout, lstout;
var args = ['-9', '--obj', '-I/share/asminc', '-o' + objpath, '-l' + lstpath, step.path];
var LWASM: EmscriptenModule = emglobal.lwasm({
instantiateWasm: moduleInstFn('lwasm'),
noInitialRun: true,
//logReadFiles:true,
print: print_fn,
printErr: msvcErrorMatcher(errors),
});
var FS = LWASM.FS;
//setupFS(FS, '65-'+getRootBasePlatform(step.platform));
populateFiles(step, FS);
fixParamsWithDefines(step.path, step.params);
execMain(step, LWASM, args);
if (errors.length)
return { errors: errors };
objout = FS.readFile(objpath, { encoding: 'binary' });
lstout = FS.readFile(lstpath, { encoding: 'utf8' });
putWorkFile(objpath, objout);
putWorkFile(lstpath, lstout);
}
return {
linktool: "lwlink",
files: [objpath, lstpath],
args: [objpath]
};
}
export function linkLWLINK(step: BuildStep): BuildStepResult {
loadNative("lwlink");
var params = step.params;
gatherFiles(step);
var binpath = "main";
if (staleFiles(step, [binpath])) {
var errors = [];
var LWLINK: EmscriptenModule = emglobal.lwlink({
instantiateWasm: moduleInstFn('lwlink'),
noInitialRun: true,
//logReadFiles:true,
print: print_fn,
printErr: function (s) {
if (s.startsWith("Warning:"))
console.log(s);
else
errors.push({ msg: s, line: 0 });
}
});
var FS = LWLINK.FS;
//setupFS(FS, '65-'+getRootBasePlatform(step.platform));
populateFiles(step, FS);
populateExtraFiles(step, FS, params.extra_link_files);
var libargs = params.extra_link_args || [];
var args = [
'-L.',
'--entry=program_start',
'--raw',
'--output=main',
'--map=main.map'].concat(libargs, step.args);
console.log(args);
execMain(step, LWLINK, args);
if (errors.length)
return { errors: errors };
var aout = FS.readFile("main", { encoding: 'binary' });
var mapout = FS.readFile("main.map", { encoding: 'utf8' });
putWorkFile("main", aout);
putWorkFile("main.map", mapout);
// return unchanged if no files changed
if (!anyTargetChanged(step, ["main", "main.map"]))
return;
// parse symbol map
//console.log(mapout);
var symbolmap = {};
var segments = [];
for (var s of mapout.split("\n")) {
var toks = s.split(" ");
// TODO: use regex
if (toks[0] == 'Symbol:') {
let ident = toks[1];
let ofs = parseInt(toks[4], 16);
if (ident && ofs >= 0 && !ident.startsWith("l_") && !/^L\d+$/.test(ident)) {
symbolmap[ident] = ofs;
}
}
else if (toks[0] == 'Section:') {
let seg = toks[1];
let segstart = parseInt(toks[5], 16);
let segsize = parseInt(toks[7], 16);
segments.push({ name: seg, start: segstart, size: segsize });
}
}
// build listings
var listings: CodeListingMap = {};
for (var fn of step.files) {
if (fn.endsWith('.lst')) {
// TODO
var lstout = FS.readFile(fn, { encoding: 'utf8' });
var asmlines = parseListing(lstout, /^([0-9A-F]+)\s+([0-9A-F]+)\s+[(]\s*(.+?)[)]:(\d+) (.*)/i, 4, 1, 2, 3);
// * Line //threed.c:117: init of variable e
var srclines = parseSourceLines(lstout, /Line .+?:(\d+)/i, /^([0-9A-F]{4})/i);
putWorkFile(fn, lstout);
// TODO: you have to get rid of all source lines to get asm listing
listings[fn] = {
asmlines: srclines.length ? asmlines : null,
lines: srclines.length ? srclines : asmlines,
text: lstout
};
}
}
return {
output: aout, //.slice(0),
listings: listings,
errors: errors,
symbolmap: symbolmap,
segments: segments
};
}
}

191
src/worker/tools/misc.ts Normal file
View File

@ -0,0 +1,191 @@
import { Segment, CodeListingMap, WorkerResult, WorkerError } from "../../common/workertypes";
import { BuildStep, BuildStepResult, setupRequireFunction, load, emglobal, getWorkFileAsString, loadNative, gatherFiles, staleFiles, msvcErrorMatcher, moduleInstFn, setupFS, populateFiles, execMain, putWorkFile, anyTargetChanged, parseListing, print_fn, makeErrorMatcher, populateExtraFiles } from "../workermain";
import { EmscriptenModule } from "../workermain"
import * as basic_compiler from '../../common/basic/compiler';
import { parseXMLPoorly } from "../../common/util";
export function translateShowdown(step: BuildStep): BuildStepResult {
setupRequireFunction();
load("showdown.min");
var showdown = emglobal['showdown'];
var converter = new showdown.Converter({
tables: 'true',
smoothLivePreview: 'true',
requireSpaceBeforeHeadingText: 'true',
emoji: 'true',
});
var code = getWorkFileAsString(step.path);
var html = converter.makeHtml(code);
delete emglobal['require'];
return {
output: html
};
}
export function compileInform6(step: BuildStep): BuildStepResult {
loadNative("inform");
var errors = [];
gatherFiles(step, { mainFilePath: "main.inf" });
var objpath = step.prefix + ".z5";
if (staleFiles(step, [objpath])) {
var errorMatcher = msvcErrorMatcher(errors);
var lstout = "";
var match_fn = (s: string) => {
if (s.indexOf("Error:") >= 0) {
errorMatcher(s);
} else {
lstout += s;
lstout += "\n";
}
}
// TODO: step.path must end in '.inf' or error
var args = ['-afjnops', '-v5', '-Cu', '-E1', '-k', '+/share/lib', step.path];
var inform: EmscriptenModule = emglobal.inform({
instantiateWasm: moduleInstFn('inform'),
noInitialRun: true,
//logReadFiles:true,
print: match_fn,
printErr: match_fn,
});
var FS = inform.FS;
setupFS(FS, 'inform');
populateFiles(step, FS);
//fixParamsWithDefines(step.path, step.params);
execMain(step, inform, args);
if (errors.length)
return { errors: errors };
var objout = FS.readFile(objpath, { encoding: 'binary' });
putWorkFile(objpath, objout);
if (!anyTargetChanged(step, [objpath]))
return;
// parse debug XML
var symbolmap = {};
var segments: Segment[] = [];
var entitymap = {
// number -> string
'object': {}, 'property': {}, 'attribute': {}, 'constant': {}, 'global-variable': {}, 'routine': {},
};
var dbgout = FS.readFile("gameinfo.dbg", { encoding: 'utf8' });
var xmlroot = parseXMLPoorly(dbgout);
//console.log(xmlroot);
var segtype = "ram";
xmlroot.children.forEach((node) => {
switch (node.type) {
case 'global-variable':
case 'routine':
var ident = node.children.find((c, v) => c.type == 'identifier').text;
var address = parseInt(node.children.find((c, v) => c.type == 'address').text);
symbolmap[ident] = address;
entitymap[node.type][address] = ident;
break;
case 'object':
case 'property':
case 'attribute':
var ident = node.children.find((c, v) => c.type == 'identifier').text;
var value = parseInt(node.children.find((c, v) => c.type == 'value').text);
//entitymap[node.type][ident] = value;
entitymap[node.type][value] = ident;
//symbolmap[ident] = address | 0x1000000;
break;
case 'story-file-section':
var name = node.children.find((c, v) => c.type == 'type').text;
var address = parseInt(node.children.find((c, v) => c.type == 'address').text);
var endAddress = parseInt(node.children.find((c, v) => c.type == 'end-address').text);
if (name == "grammar table") segtype = "rom";
segments.push({ name: name, start: address, size: endAddress - address, type: segtype });
}
});
// parse listing
var listings: CodeListingMap = {};
// 35 +00015 <*> call_vs long_19 location long_424 -> sp
var lines = parseListing(lstout, /\s*(\d+)\s+[+]([0-9a-f]+)\s+([<*>]*)\s*(\w+)\s+(.+)/i, -1, 2, 4);
var lstpath = step.prefix + '.lst';
listings[lstpath] = { lines: [], asmlines: lines, text: lstout };
return {
output: objout, //.slice(0),
listings: listings,
errors: errors,
symbolmap: symbolmap,
segments: segments,
debuginfo: entitymap,
};
}
}
export function compileBASIC(step: BuildStep): WorkerResult {
var jsonpath = step.path + ".json";
gatherFiles(step);
if (staleFiles(step, [jsonpath])) {
var parser = new basic_compiler.BASICParser();
var code = getWorkFileAsString(step.path);
try {
var ast = parser.parseFile(code, step.path);
} catch (e) {
console.log(e);
if (parser.errors.length == 0) throw e;
}
if (parser.errors.length) {
return { errors: parser.errors };
}
// put AST into JSON (sans source locations) to see if it has changed
var json = JSON.stringify(ast, (key, value) => { return (key == '$loc' ? undefined : value) });
putWorkFile(jsonpath, json);
if (anyTargetChanged(step, [jsonpath])) return {
output: ast,
listings: parser.getListings(),
};
}
}
export function compileWiz(step: BuildStep): WorkerResult {
loadNative("wiz");
var params = step.params;
gatherFiles(step, { mainFilePath: "main.wiz" });
var destpath = step.prefix + (params.wiz_rom_ext || ".bin");
var errors: WorkerError[] = [];
if (staleFiles(step, [destpath])) {
var wiz: EmscriptenModule = emglobal.wiz({
instantiateWasm: moduleInstFn('wiz'),
noInitialRun: true,
print: print_fn,
//test.wiz:2: error: expected statement, but got identifier `test`
printErr: makeErrorMatcher(errors, /(.+?):(\d+):\s*(.+)/, 2, 3, step.path, 1),
});
var FS = wiz.FS;
setupFS(FS, 'wiz');
populateFiles(step, FS);
populateExtraFiles(step, FS, params.extra_compile_files);
const FWDIR = '/share/common';
var args = [
'-o', destpath,
'-I', FWDIR + '/' + (params.wiz_inc_dir || step.platform),
'-s', 'wla',
'--color=none',
step.path];
args.push('--system', params.wiz_sys_type || params.arch);
execMain(step, wiz, args);
if (errors.length)
return { errors: errors };
var binout = FS.readFile(destpath, { encoding: 'binary' });
putWorkFile(destpath, binout);
var dbgout = FS.readFile(step.prefix + '.sym', { encoding: 'utf8' });
var symbolmap = {};
for (var s of dbgout.split("\n")) {
var toks = s.split(/ /);
// 00:4008 header.basic_start
if (toks && toks.length >= 2) {
var tokrange = toks[0].split(':');
var start = parseInt(tokrange[1], 16);
var sym = toks[1];
symbolmap[sym] = start;
}
}
return {
output: binout, //.slice(0),
errors: errors,
symbolmap: symbolmap,
};
}
}

290
src/worker/tools/sdcc.ts Normal file
View File

@ -0,0 +1,290 @@
import { CodeListingMap } from "../../common/workertypes";
import { BuildStep, BuildStepResult, loadNative, gatherFiles, staleFiles, emglobal, moduleInstFn, populateFiles, execMain, putWorkFile, setupFS, populateExtraFiles, anyTargetChanged, parseListing, print_fn, msvcErrorMatcher, getWorkFileAsString, setupStdin, preprocessMCPP, parseSourceLines } from "../workermain";
import { EmscriptenModule } from "../workermain"
function hexToArray(s, ofs) {
var buf = new ArrayBuffer(s.length / 2);
var arr = new Uint8Array(buf);
for (var i = 0; i < arr.length; i++) {
arr[i] = parseInt(s.slice(i * 2 + ofs, i * 2 + ofs + 2), 16);
}
return arr;
}
function parseIHX(ihx, rom_start, rom_size, errors) {
var output = new Uint8Array(new ArrayBuffer(rom_size));
var high_size = 0;
for (var s of ihx.split("\n")) {
if (s[0] == ':') {
var arr = hexToArray(s, 1);
var count = arr[0];
var address = (arr[1] << 8) + arr[2] - rom_start;
var rectype = arr[3];
//console.log(rectype,address.toString(16),count,arr);
if (rectype == 0) {
for (var i = 0; i < count; i++) {
var b = arr[4 + i];
output[i + address] = b;
}
if (i + address > high_size) high_size = i + address;
} else if (rectype == 1) {
break;
} else {
console.log(s); // unknown record type
}
}
}
// TODO: return ROM anyway?
if (high_size > rom_size) {
//errors.push({line:0, msg:"ROM size too large: 0x" + high_size.toString(16) + " > 0x" + rom_size.toString(16)});
}
return output;
}
export function assembleSDASZ80(step: BuildStep): BuildStepResult {
loadNative("sdasz80");
var objout, lstout, symout;
var errors = [];
gatherFiles(step, { mainFilePath: "main.asm" });
var objpath = step.prefix + ".rel";
var lstpath = step.prefix + ".lst";
if (staleFiles(step, [objpath, lstpath])) {
//?ASxxxx-Error-<o> in line 1 of main.asm null
// <o> .org in REL area or directive / mnemonic error
// ?ASxxxx-Error-<q> in line 1627 of cosmic.asm
// <q> missing or improper operators, terminators, or delimiters
var match_asm_re1 = / in line (\d+) of (\S+)/; // TODO
var match_asm_re2 = / <\w> (.+)/; // TODO
var errline = 0;
var errpath = step.path;
var match_asm_fn = (s: string) => {
var m = match_asm_re1.exec(s);
if (m) {
errline = parseInt(m[1]);
errpath = m[2];
} else {
m = match_asm_re2.exec(s);
if (m) {
errors.push({
line: errline,
path: errpath,
msg: m[1]
});
}
}
}
var ASZ80: EmscriptenModule = emglobal.sdasz80({
instantiateWasm: moduleInstFn('sdasz80'),
noInitialRun: true,
//logReadFiles:true,
print: match_asm_fn,
printErr: match_asm_fn,
});
var FS = ASZ80.FS;
populateFiles(step, FS);
execMain(step, ASZ80, ['-plosgffwy', step.path]);
if (errors.length) {
return { errors: errors };
}
objout = FS.readFile(objpath, { encoding: 'utf8' });
lstout = FS.readFile(lstpath, { encoding: 'utf8' });
putWorkFile(objpath, objout);
putWorkFile(lstpath, lstout);
}
return {
linktool: "sdldz80",
files: [objpath, lstpath],
args: [objpath]
};
//symout = FS.readFile("main.sym", {encoding:'utf8'});
}
export function linkSDLDZ80(step: BuildStep) {
loadNative("sdldz80");
var errors = [];
gatherFiles(step);
var binpath = "main.ihx";
if (staleFiles(step, [binpath])) {
//?ASlink-Warning-Undefined Global '__divsint' referenced by module 'main'
var match_aslink_re = /\?ASlink-(\w+)-(.+)/;
var match_aslink_fn = (s: string) => {
var matches = match_aslink_re.exec(s);
if (matches) {
errors.push({
line: 0,
msg: matches[2]
});
}
}
var params = step.params;
var LDZ80: EmscriptenModule = emglobal.sdldz80({
instantiateWasm: moduleInstFn('sdldz80'),
noInitialRun: true,
//logReadFiles:true,
print: match_aslink_fn,
printErr: match_aslink_fn,
});
var FS = LDZ80.FS;
setupFS(FS, 'sdcc');
populateFiles(step, FS);
populateExtraFiles(step, FS, params.extra_link_files);
// TODO: coleco hack so that -u flag works
if (step.platform.startsWith("coleco")) {
FS.writeFile('crt0.rel', FS.readFile('/share/lib/coleco/crt0.rel', { encoding: 'utf8' }));
FS.writeFile('crt0.lst', '\n'); // TODO: needed so -u flag works
}
var args = ['-mjwxyu',
'-i', 'main.ihx', // TODO: main?
'-b', '_CODE=0x' + params.code_start.toString(16),
'-b', '_DATA=0x' + params.data_start.toString(16),
'-k', '/share/lib/z80',
'-l', 'z80'];
if (params.extra_link_args)
args.push.apply(args, params.extra_link_args);
args.push.apply(args, step.args);
//console.log(args);
execMain(step, LDZ80, args);
var hexout = FS.readFile("main.ihx", { encoding: 'utf8' });
var noiout = FS.readFile("main.noi", { encoding: 'utf8' });
putWorkFile("main.ihx", hexout);
putWorkFile("main.noi", noiout);
// return unchanged if no files changed
if (!anyTargetChanged(step, ["main.ihx", "main.noi"]))
return;
// parse binary file
var binout = parseIHX(hexout, params.rom_start !== undefined ? params.rom_start : params.code_start, params.rom_size, errors);
if (errors.length) {
return { errors: errors };
}
// parse listings
var listings: CodeListingMap = {};
for (var fn of step.files) {
if (fn.endsWith('.lst')) {
var rstout = FS.readFile(fn.replace('.lst', '.rst'), { encoding: 'utf8' });
// 0000 21 02 00 [10] 52 ld hl, #2
var asmlines = parseListing(rstout, /^\s*([0-9A-F]{4})\s+([0-9A-F][0-9A-F r]*[0-9A-F])\s+\[([0-9 ]+)\]?\s+(\d+) (.*)/i, 4, 1, 2, 3);
var srclines = parseSourceLines(rstout, /^\s+\d+ ;<stdin>:(\d+):/i, /^\s*([0-9A-F]{4})/i);
putWorkFile(fn, rstout);
// TODO: you have to get rid of all source lines to get asm listing
listings[fn] = {
asmlines: srclines.length ? asmlines : null,
lines: srclines.length ? srclines : asmlines,
text: rstout
};
}
}
// parse symbol map
var symbolmap = {};
for (var s of noiout.split("\n")) {
var toks = s.split(" ");
if (toks[0] == 'DEF' && !toks[1].startsWith("A$")) {
symbolmap[toks[1]] = parseInt(toks[2], 16);
}
}
// build segment map
var seg_re = /^s__(\w+)$/;
var segments = [];
// TODO: use stack params for stack segment
for (let ident in symbolmap) {
let m = seg_re.exec(ident);
if (m) {
let seg = m[1];
let segstart = symbolmap[ident]; // s__SEG
let segsize = symbolmap['l__' + seg]; // l__SEG
if (segstart >= 0 && segsize > 0) {
var type = null;
if (['INITIALIZER', 'GSINIT', 'GSFINAL'].includes(seg)) type = 'rom';
else if (seg.startsWith('CODE')) type = 'rom';
else if (['DATA', 'INITIALIZED'].includes(seg)) type = 'ram';
if (type == 'rom' || segstart > 0) // ignore HEADER0, CABS0, etc (TODO?)
segments.push({ name: seg, start: segstart, size: segsize, type: type });
}
}
}
return {
output: binout,
listings: listings,
errors: errors,
symbolmap: symbolmap,
segments: segments
};
}
}
export function compileSDCC(step: BuildStep): BuildStepResult {
gatherFiles(step, {
mainFilePath: "main.c" // not used
});
var outpath = step.prefix + ".asm";
if (staleFiles(step, [outpath])) {
var errors = [];
var params = step.params;
loadNative('sdcc');
var SDCC: EmscriptenModule = emglobal.sdcc({
instantiateWasm: moduleInstFn('sdcc'),
noInitialRun: true,
noFSInit: true,
print: print_fn,
printErr: msvcErrorMatcher(errors),
//TOTAL_MEMORY:256*1024*1024,
});
var FS = SDCC.FS;
populateFiles(step, FS);
// load source file and preprocess
var code = getWorkFileAsString(step.path);
var preproc = preprocessMCPP(step, 'sdcc');
if (preproc.errors) {
return { errors: preproc.errors };
}
else code = preproc.code;
// pipe file to stdin
setupStdin(FS, code);
setupFS(FS, 'sdcc');
var args = ['--vc', '--std-sdcc99', '-mz80', //'-Wall',
'--c1mode',
//'--debug',
//'-S', 'main.c',
//'--asm=sdasz80',
//'--reserve-regs-iy',
'--less-pedantic',
///'--fomit-frame-pointer',
//'--opt-code-speed',
//'--max-allocs-per-node', '1000',
//'--cyclomatic',
//'--nooverlay',
//'--nogcse',
//'--nolabelopt',
//'--noinvariant',
//'--noinduction',
//'--nojtbound',
//'--noloopreverse',
'-o', outpath];
// if "#pragma opt_code" found do not disable optimziations
if (!/^\s*#pragma\s+opt_code/m.exec(code)) {
args.push.apply(args, [
'--oldralloc',
'--no-peep',
'--nolospre'
]);
}
if (params.extra_compile_args) {
args.push.apply(args, params.extra_compile_args);
}
execMain(step, SDCC, args);
// TODO: preprocessor errors w/ correct file
if (errors.length /* && nwarnings < msvc_errors.length*/) {
return { errors: errors };
}
// massage the asm output
var asmout = FS.readFile(outpath, { encoding: 'utf8' });
asmout = " .area _HOME\n .area _CODE\n .area _INITIALIZER\n .area _DATA\n .area _INITIALIZED\n .area _BSEG\n .area _BSS\n .area _HEAP\n" + asmout;
putWorkFile(outpath, asmout);
}
return {
nexttool: "sdasz80",
path: outpath,
args: [outpath],
files: [outpath],
};
}

310
src/worker/tools/verilog.ts Normal file
View File

@ -0,0 +1,310 @@
// TODO: must be a better way to do all this
import { WorkerError, CodeListingMap, SourceLocation } from "../../common/workertypes";
import { Assembler } from "../assembler";
import * as vxmlparser from '../../common/hdl/vxmlparser';
import { getWorkFileAsString, BuildStep, BuildStepResult, gatherFiles, loadNative, staleFiles, makeErrorMatcher, emglobal, moduleInstFn, print_fn, populateFiles, execMain, putWorkFile, anyTargetChanged, endtime, getWASMMemory, starttime, populateExtraFiles, setupFS } from "../workermain";
import { EmscriptenModule } from "../workermain"
function detectModuleName(code: string) {
var m = /^\s*module\s+(\w+_top)\b/m.exec(code)
|| /^\s*module\s+(top|t)\b/m.exec(code)
|| /^\s*module\s+(\w+)\b/m.exec(code);
return m ? m[1] : null;
}
function detectTopModuleName(code: string) {
var topmod = detectModuleName(code) || "top";
var m = /^\s*module\s+(\w+?_top)/m.exec(code);
if (m && m[1]) topmod = m[1];
return topmod;
}
// cached stuff (TODO)
var jsasm_module_top;
var jsasm_module_output;
var jsasm_module_key;
function compileJSASM(asmcode: string, platform, options, is_inline) {
var asm = new Assembler(null);
var includes = [];
asm.loadJSON = (filename: string) => {
var jsontext = getWorkFileAsString(filename);
if (!jsontext) throw Error("could not load " + filename);
return JSON.parse(jsontext);
};
asm.loadInclude = (filename) => {
if (!filename.startsWith('"') || !filename.endsWith('"'))
return 'Expected filename in "double quotes"';
filename = filename.substr(1, filename.length - 2);
includes.push(filename);
};
var loaded_module = false;
asm.loadModule = (top_module: string) => {
// compile last file in list
loaded_module = true;
var key = top_module + '/' + includes;
if (jsasm_module_key != key) {
jsasm_module_key = key;
jsasm_module_output = null;
}
jsasm_module_top = top_module;
var main_filename = includes[includes.length - 1];
// TODO: take out .asm dependency
var voutput = compileVerilator({ platform: platform, files: includes, path: main_filename, tool: 'verilator' });
if (voutput)
jsasm_module_output = voutput;
return null; // no error
}
var result = asm.assembleFile(asmcode);
if (loaded_module && jsasm_module_output) {
// errors? return them
if (jsasm_module_output.errors && jsasm_module_output.errors.length)
return jsasm_module_output;
// return program ROM array
var asmout = result.output;
// TODO: unify
result.output = jsasm_module_output.output;
// TODO: typecheck this garbage
(result as any).output.program_rom = asmout;
// TODO: not cpu_platform__DOT__program_rom anymore, make const
(result as any).output.program_rom_variable = jsasm_module_top + "$program_rom";
(result as any).listings = {};
(result as any).listings[options.path] = { lines: result.lines };
return result;
} else {
return result;
}
}
export function compileJSASMStep(step: BuildStep): BuildStepResult {
gatherFiles(step);
var code = getWorkFileAsString(step.path);
var platform = step.platform || 'verilog';
return compileJSASM(code, platform, step, false);
}
function compileInlineASM(code: string, platform, options, errors, asmlines) {
code = code.replace(/__asm\b([\s\S]+?)\b__endasm\b/g, function (s, asmcode, index) {
var firstline = code.substr(0, index).match(/\n/g).length;
var asmout = compileJSASM(asmcode, platform, options, true);
if (asmout.errors && asmout.errors.length) {
for (var i = 0; i < asmout.errors.length; i++) {
asmout.errors[i].line += firstline;
errors.push(asmout.errors[i]);
}
return "";
} else if (asmout.output) {
let s = "";
var out = asmout.output;
for (var i = 0; i < out.length; i++) {
if (i > 0) {
s += ",";
if ((i & 0xff) == 0) s += "\n";
}
s += 0 | out[i];
}
if (asmlines) {
var al = asmout.lines;
for (var i = 0; i < al.length; i++) {
al[i].line += firstline;
asmlines.push(al[i]);
}
}
return s;
}
});
return code;
}
export function compileVerilator(step: BuildStep): BuildStepResult {
loadNative("verilator_bin");
var platform = step.platform || 'verilog';
var errors: WorkerError[] = [];
gatherFiles(step);
// compile verilog if files are stale
if (staleFiles(step, [xmlPath])) {
// TODO: %Error: Specified --top-module 'ALU' isn't at the top level, it's under another cell 'cpu'
// TODO: ... Use "/* verilator lint_off BLKSEQ */" and lint_on around source to disable this message.
var match_fn = makeErrorMatcher(errors, /%(.+?): (.+?):(\d+)?[:]?\s*(.+)/i, 3, 4, step.path, 2);
var verilator_mod: EmscriptenModule = emglobal.verilator_bin({
instantiateWasm: moduleInstFn('verilator_bin'),
noInitialRun: true,
noExitRuntime: true,
print: print_fn,
printErr: match_fn,
wasmMemory: getWASMMemory(), // reuse memory
//INITIAL_MEMORY:256*1024*1024,
});
var code = getWorkFileAsString(step.path);
var topmod = detectTopModuleName(code);
var FS = verilator_mod.FS;
var listings: CodeListingMap = {};
// process inline assembly, add listings where found
populateFiles(step, FS, {
mainFilePath: step.path,
processFn: (path, code) => {
if (typeof code === 'string') {
let asmlines = [];
code = compileInlineASM(code, platform, step, errors, asmlines);
if (asmlines.length) {
listings[path] = { lines: asmlines };
}
}
return code;
}
});
starttime();
var xmlPath = `obj_dir/V${topmod}.xml`;
try {
var args = ["--cc", "-O3",
"-DEXT_INLINE_ASM", "-DTOPMOD__" + topmod, "-D__8BITWORKSHOP__",
"-Wall",
"-Wno-DECLFILENAME", "-Wno-UNUSED", "-Wno-EOFNEWLINE", "-Wno-PROCASSWIRE",
"--x-assign", "fast", "--noassert", "--pins-sc-biguint",
"--debug-check", // for XML output
"--top-module", topmod, step.path]
execMain(step, verilator_mod, args);
} catch (e) {
console.log(e);
errors.push({ line: 0, msg: "Compiler internal error: " + e });
}
endtime("compile");
// remove boring errors
errors = errors.filter(function (e) { return !/Exiting due to \d+/.exec(e.msg); }, errors);
errors = errors.filter(function (e) { return !/Use ["][/][*]/.exec(e.msg); }, errors);
if (errors.length) {
return { errors: errors };
}
starttime();
var xmlParser = new vxmlparser.VerilogXMLParser();
try {
var xmlContent = FS.readFile(xmlPath, { encoding: 'utf8' });
var xmlScrubbed = xmlContent.replace(/ fl=".+?" loc=".+?"/g, '');
// TODO: this squelches the .asm listing
//listings[step.prefix + '.xml'] = {lines:[],text:xmlContent};
putWorkFile(xmlPath, xmlScrubbed); // don't detect changes in source position
if (!anyTargetChanged(step, [xmlPath]))
return;
xmlParser.parse(xmlContent);
} catch (e) {
console.log(e, e.stack);
if (e.$loc != null) {
let $loc = e.$loc as SourceLocation;
errors.push({ msg: "" + e, path: $loc.path, line: $loc.line });
} else {
errors.push({ line: 0, msg: "" + e });
}
return { errors: errors, listings: listings };
} finally {
endtime("parse");
}
return {
output: xmlParser,
errors: errors,
listings: listings,
};
}
}
// TODO: test
export function compileYosys(step: BuildStep): BuildStepResult {
loadNative("yosys");
var code = step.code;
var errors = [];
var match_fn = makeErrorMatcher(errors, /ERROR: (.+?) in line (.+?[.]v):(\d+)[: ]+(.+)/i, 3, 4, step.path);
starttime();
var yosys_mod: EmscriptenModule = emglobal.yosys({
instantiateWasm: moduleInstFn('yosys'),
noInitialRun: true,
print: print_fn,
printErr: match_fn,
});
endtime("create module");
var topmod = detectTopModuleName(code);
var FS = yosys_mod.FS;
FS.writeFile(topmod + ".v", code);
starttime();
try {
execMain(step, yosys_mod, ["-q", "-o", topmod + ".json", "-S", topmod + ".v"]);
} catch (e) {
console.log(e);
endtime("compile");
return { errors: errors };
}
endtime("compile");
//TODO: filename in errors
if (errors.length) return { errors: errors };
try {
var json_file = FS.readFile(topmod + ".json", { encoding: 'utf8' });
var json = JSON.parse(json_file);
console.log(json);
return { output: json, errors: errors }; // TODO
} catch (e) {
console.log(e);
return { errors: errors };
}
}
export function compileSilice(step: BuildStep): BuildStepResult {
loadNative("silice");
var params = step.params;
gatherFiles(step, { mainFilePath: "main.ice" });
var destpath = step.prefix + '.v';
var errors: WorkerError[] = [];
var errfile: string;
var errline: number;
if (staleFiles(step, [destpath])) {
//[preprocessor] 97] attempt to concatenate a nil value (global 'addrW')
var match_fn = (s: string) => {
s = (s as any).replaceAll(/\033\[\d+\w/g, '');
var mf = /file:\s*(\w+)/.exec(s);
var ml = /line:\s+(\d+)/.exec(s);
var preproc = /\[preprocessor\] (\d+)\] (.+)/.exec(s);
if (mf) errfile = mf[1];
else if (ml) errline = parseInt(ml[1]);
else if (preproc) {
errors.push({ path: step.path, line: parseInt(preproc[1]), msg: preproc[2] });
}
else if (errfile && errline && s.length > 1) {
if (s.length > 2) {
errors.push({ path: errfile + ".ice", line: errline, msg: s });
} else {
errfile = null;
errline = null;
}
}
else console.log(s);
}
var silice: EmscriptenModule = emglobal.silice({
instantiateWasm: moduleInstFn('silice'),
noInitialRun: true,
print: match_fn,
printErr: match_fn,
});
var FS = silice.FS;
setupFS(FS, 'Silice');
populateFiles(step, FS);
populateExtraFiles(step, FS, params.extra_compile_files);
const FWDIR = '/share/frameworks';
var args = [
'-D', 'NTSC=1',
'--frameworks_dir', FWDIR,
'-f', `/8bitworkshop.v`,
'-o', destpath,
step.path];
execMain(step, silice, args);
if (errors.length)
return { errors: errors };
var vout = FS.readFile(destpath, { encoding: 'utf8' });
putWorkFile(destpath, vout);
}
return {
nexttool: "verilator",
path: destpath,
args: [destpath],
files: [destpath],
};
}

124
src/worker/tools/x86.ts Normal file
View File

@ -0,0 +1,124 @@
import { WorkerError, CodeListingMap } from "../../common/workertypes";
import { BuildStep, BuildStepResult, loadNative, gatherFiles, staleFiles, emglobal, moduleInstFn, getWorkFileAsString, preprocessMCPP, populateFiles, fixParamsWithDefines, execMain, putWorkFile, print_fn, msvcErrorMatcher, anyTargetChanged, parseListing } from "../workermain";
import { EmscriptenModule } from "../workermain"
// http://www.techhelpmanual.com/829-program_startup___exit.html
export function compileSmallerC(step: BuildStep): BuildStepResult {
loadNative("smlrc");
var params = step.params;
// stderr
var re_err1 = /^Error in "[/]*(.+)" [(](\d+):(\d+)[)]/;
var errors: WorkerError[] = [];
var errline = 0;
var errpath = step.path;
function match_fn(s) {
var matches = re_err1.exec(s);
if (matches) {
errline = parseInt(matches[2]);
errpath = matches[1];
} else {
errors.push({
line: errline,
msg: s,
path: errpath,
});
}
}
gatherFiles(step, { mainFilePath: "main.c" });
var destpath = step.prefix + '.asm';
if (staleFiles(step, [destpath])) {
var args = ['-seg16',
//'-nobss',
'-no-externs',
step.path, destpath];
var smlrc: EmscriptenModule = emglobal.smlrc({
instantiateWasm: moduleInstFn('smlrc'),
noInitialRun: true,
//logReadFiles:true,
print: match_fn,
printErr: match_fn,
});
// load source file and preprocess
var code = getWorkFileAsString(step.path);
var preproc = preprocessMCPP(step, null);
if (preproc.errors) {
return { errors: preproc.errors };
}
else code = preproc.code;
// set up filesystem
var FS = smlrc.FS;
//setupFS(FS, '65-'+getRootBasePlatform(step.platform));
populateFiles(step, FS);
FS.writeFile(step.path, code);
fixParamsWithDefines(step.path, params);
if (params.extra_compile_args) {
args.unshift.apply(args, params.extra_compile_args);
}
execMain(step, smlrc, args);
if (errors.length)
return { errors: errors };
var asmout = FS.readFile(destpath, { encoding: 'utf8' });
putWorkFile(destpath, asmout);
}
return {
nexttool: "yasm",
path: destpath,
args: [destpath],
files: [destpath],
};
}
export function assembleYASM(step: BuildStep): BuildStepResult {
loadNative("yasm");
var errors = [];
gatherFiles(step, { mainFilePath: "main.asm" });
var objpath = step.prefix + ".exe";
var lstpath = step.prefix + ".lst";
var mappath = step.prefix + ".map";
if (staleFiles(step, [objpath])) {
var args = ['-X', 'vc',
'-a', 'x86', '-f', 'dosexe', '-p', 'nasm',
'-D', 'freedos',
//'-g', 'dwarf2',
//'-I/share/asminc',
'-o', objpath, '-l', lstpath, '--mapfile=' + mappath,
step.path];
// return yasm/*.ready*/
var YASM: EmscriptenModule = emglobal.yasm({
instantiateWasm: moduleInstFn('yasm'),
noInitialRun: true,
//logReadFiles:true,
print: print_fn,
printErr: msvcErrorMatcher(errors),
});
var FS = YASM.FS;
//setupFS(FS, '65-'+getRootBasePlatform(step.platform));
populateFiles(step, FS);
//fixParamsWithDefines(step.path, step.params);
execMain(step, YASM, args);
if (errors.length)
return { errors: errors };
var objout, lstout, mapout;
objout = FS.readFile(objpath, { encoding: 'binary' });
lstout = FS.readFile(lstpath, { encoding: 'utf8' });
mapout = FS.readFile(mappath, { encoding: 'utf8' });
putWorkFile(objpath, objout);
putWorkFile(lstpath, lstout);
//putWorkFile(mappath, mapout);
if (!anyTargetChanged(step, [objpath]))
return;
var symbolmap = {};
var segments = [];
var lines = parseListing(lstout, /\s*(\d+)\s+([0-9a-f]+)\s+([0-9a-f]+)\s+(.+)/i, 1, 2, 3);
var listings: CodeListingMap = {};
listings[lstpath] = { lines: lines, text: lstout };
return {
output: objout, //.slice(0),
listings: listings,
errors: errors,
symbolmap: symbolmap,
segments: segments
};
}
}

67
src/worker/tools/z80.ts Normal file
View File

@ -0,0 +1,67 @@
import { CodeListingMap } from "../../common/workertypes";
import { anyTargetChanged, BuildStep, BuildStepResult, emglobal, EmscriptenModule, execMain, gatherFiles, loadNative, makeErrorMatcher, moduleInstFn, parseListing, populateFiles, print_fn, putWorkFile, staleFiles } from "../workermain"
export function assembleZMAC(step: BuildStep): BuildStepResult {
loadNative("zmac");
var hexout, lstout, binout;
var errors = [];
var params = step.params;
gatherFiles(step, { mainFilePath: "main.asm" });
var lstpath = step.prefix + ".lst";
var binpath = step.prefix + ".cim";
if (staleFiles(step, [binpath, lstpath])) {
/*
error1.asm(4) : 'l18d4' Undeclared
JP L18D4
error1.asm(11): warning: 'foobar' treated as label (instruction typo?)
Add a colon or move to first column to stop this warning.
1 errors (see listing if no diagnostics appeared here)
*/
var ZMAC: EmscriptenModule = emglobal.zmac({
instantiateWasm: moduleInstFn('zmac'),
noInitialRun: true,
//logReadFiles:true,
print: print_fn,
printErr: makeErrorMatcher(errors, /([^( ]+)\s*[(](\d+)[)]\s*:\s*(.+)/, 2, 3, step.path),
});
var FS = ZMAC.FS;
populateFiles(step, FS);
// TODO: don't know why CIM (hexary) doesn't work
execMain(step, ZMAC, ['-z', '-c', '--oo', 'lst,cim', step.path]);
if (errors.length) {
return { errors: errors };
}
lstout = FS.readFile("zout/" + lstpath, { encoding: 'utf8' });
binout = FS.readFile("zout/" + binpath, { encoding: 'binary' });
putWorkFile(binpath, binout);
putWorkFile(lstpath, lstout);
if (!anyTargetChanged(step, [binpath, lstpath]))
return;
// 230: 1739+7+x 017A 1600 L017A: LD D,00h
var lines = parseListing(lstout, /\s*(\d+):\s*([0-9a-f]+)\s+([0-9a-f]+)\s+(.+)/i, 1, 2, 3);
var listings: CodeListingMap = {};
listings[lstpath] = { lines: lines };
// parse symbol table
var symbolmap = {};
var sympos = lstout.indexOf('Symbol Table:');
if (sympos > 0) {
var symout = lstout.slice(sympos + 14);
symout.split('\n').forEach(function (l) {
var m = l.match(/(\S+)\s+([= ]*)([0-9a-f]+)/i);
if (m) {
symbolmap[m[1]] = parseInt(m[3], 16);
}
});
}
return {
output: binout,
listings: listings,
errors: errors,
symbolmap: symbolmap
};
}
}

File diff suppressed because it is too large Load Diff