split workermain into tools/* modules; share parseXMLPoorly()
This commit is contained in:
parent
a12d1a0638
commit
bd00d98b77
|
@ -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(/</g, '<')
|
||||
.replace(/&/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 } = {};
|
||||
|
|
|
@ -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(/'/g, "'")
|
||||
.replace(/"/g, '"')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/</g, '<')
|
||||
.replace(/&/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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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],
|
||||
};
|
||||
}
|
||||
|
|
@ -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,
|
||||
};
|
||||
}
|
|
@ -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],
|
||||
};
|
||||
}
|
||||
|
|
@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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],
|
||||
};
|
||||
}
|
|
@ -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],
|
||||
};
|
||||
}
|
||||
|
|
@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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
Loading…
Reference in New Issue