worker: refactor

This commit is contained in:
Steven Hugg 2023-11-29 14:36:58 -06:00
parent 611c174aed
commit 2e0382b0a6
24 changed files with 1416 additions and 1368 deletions

416
src/worker/builder.ts Normal file
View File

@ -0,0 +1,416 @@
import { getBasePlatform } from "../common/util";
import { WorkerBuildStep, WorkerError, WorkerErrorResult, WorkerMessage, WorkerResult, WorkingStore } from "../common/workertypes";
import { PLATFORM_PARAMS } from "./platforms";
import { TOOLS } from "./workertools";
/// working file store and build steps
const PSRC = "../../src/";
export const PWORKER = PSRC + "worker/";
export type FileData = string | Uint8Array;
export type FileEntry = {
path: string
encoding: string
data: FileData
ts: number
};
export type BuildOptions = {
mainFilePath: string,
processFn?: (s: string, d: FileData) => FileData
};
// TODO
export type BuildStepResult = WorkerResult | WorkerNextToolResult;
export interface WorkerNextToolResult {
nexttool?: string
linktool?: string
path?: string
args: string[]
files: string[]
bblines?: boolean
}
export interface BuildStep extends WorkerBuildStep {
files?: string[]
args?: string[]
nextstep?: BuildStep
linkstep?: BuildStep
params?
result?: BuildStepResult
code?
prefix?
maxts?
debuginfo?
};
///
export class FileWorkingStore implements WorkingStore {
workfs: { [path: string]: FileEntry } = {};
workerseq: number = 0;
items: {};
constructor() {
this.reset();
}
reset() {
this.workfs = {};
this.newVersion();
}
currentVersion() {
return this.workerseq;
}
newVersion() {
let ts = new Date().getTime();
if (ts <= this.workerseq)
ts = ++this.workerseq;
return ts;
}
putFile(path: string, data: FileData): FileEntry {
var encoding = (typeof data === 'string') ? 'utf8' : 'binary';
var entry = this.workfs[path];
if (!entry || !compareData(entry.data, data) || entry.encoding != encoding) {
this.workfs[path] = entry = { path: path, data: data, encoding: encoding, ts: this.newVersion() };
console.log('+++', entry.path, entry.encoding, entry.data.length, entry.ts);
}
return entry;
}
hasFile(path: string) {
return this.workfs[path] != null;
}
getFileData(path: string): FileData {
return this.workfs[path] && this.workfs[path].data;
}
getFileAsString(path: string): string {
let data = this.getFileData(path);
if (data != null && typeof data !== 'string')
throw new Error(`${path}: expected string`)
return data as string; // TODO
}
getFileEntry(path: string): FileEntry {
return this.workfs[path];
}
setItem(key: string, value: object) {
this.items[key] = value;
}
}
export var store = new FileWorkingStore();
///
export function errorResult(msg: string): WorkerErrorResult {
return { errors: [{ line: 0, msg: msg }] };
}
export class Builder {
steps: BuildStep[] = [];
startseq: number = 0;
// returns true if file changed during this build step
wasChanged(entry: FileEntry): boolean {
return entry.ts > this.startseq;
}
async executeBuildSteps(): Promise<WorkerResult> {
this.startseq = store.currentVersion();
var linkstep: BuildStep = null;
while (this.steps.length) {
var step = this.steps.shift(); // get top of array
var platform = step.platform;
var [tool, remoteTool] = step.tool.split(':', 2);
var toolfn = TOOLS[tool];
if (!toolfn) {
throw Error(`no tool named "${tool}"`);
}
if (remoteTool) {
step.tool = remoteTool;
}
step.params = PLATFORM_PARAMS[getBasePlatform(platform)];
try {
step.result = await toolfn(step);
} catch (e) {
console.log("EXCEPTION", e, e.stack);
return errorResult(e + ""); // TODO: catch errors already generated?
}
if (step.result) {
(step.result as any).params = step.params; // TODO: type check
if (step.debuginfo) {
let r = step.result as any; // TODO
if (!r.debuginfo) r.debuginfo = {};
Object.assign(r.debuginfo, step.debuginfo);
}
// errors? return them
if ('errors' in step.result && step.result.errors.length) {
applyDefaultErrorPath(step.result.errors, step.path);
return step.result;
}
// if we got some output, return it immediately
if ('output' in step.result && step.result.output) {
return step.result;
}
// combine files with a link tool?
if ('linktool' in step.result) {
// add to existing link step
if (linkstep) {
linkstep.files = linkstep.files.concat(step.result.files);
linkstep.args = linkstep.args.concat(step.result.args);
} else {
linkstep = {
tool: step.result.linktool,
platform: platform,
files: step.result.files,
args: step.result.args
};
}
linkstep.debuginfo = step.debuginfo; // TODO: multiple debuginfos
}
// process with another tool?
if ('nexttool' in step.result) {
var asmstep: BuildStep = {
tool: step.result.nexttool,
platform: platform,
...step.result
}
this.steps.push(asmstep);
}
// process final step?
if (this.steps.length == 0 && linkstep) {
this.steps.push(linkstep);
linkstep = null;
}
}
}
}
async handleMessage(data: WorkerMessage): Promise<WorkerResult> {
this.steps = [];
// file updates
if (data.updates) {
data.updates.forEach((u) => store.putFile(u.path, u.data));
}
// object update
if (data.setitems) {
data.setitems.forEach((i) => store.setItem(i.key, i.value));
}
// build steps
if (data.buildsteps) {
this.steps.push.apply(this.steps, data.buildsteps);
}
// single-file
if (data.code) {
this.steps.push(data as BuildStep); // TODO: remove cast
}
// execute build steps
if (this.steps.length) {
var result = await this.executeBuildSteps();
return result ? result : { unchanged: true };
}
// TODO: cache results
// message not recognized
console.log("Unknown message", data);
}
}
function applyDefaultErrorPath(errors: WorkerError[], path: string) {
if (!path) return;
for (var i = 0; i < errors.length; i++) {
var err = errors[i];
if (!err.path && err.line) err.path = path;
}
}
function compareData(a: FileData, b: FileData): boolean {
if (a.length != b.length) return false;
if (typeof a === 'string' && typeof b === 'string') {
return a == b;
} else {
for (var i = 0; i < a.length; i++) {
//if (a[i] != b[i]) console.log('differ at byte',i,a[i],b[i]);
if (a[i] != b[i]) return false;
}
return true;
}
}
export const builder = new Builder();
var _t1;
export function starttime() { _t1 = new Date(); }
export function endtime(msg) { var _t2 = new Date(); console.log(msg, _t2.getTime() - _t1.getTime(), "ms"); }
///
export function putWorkFile(path: string, data: FileData) {
return store.putFile(path, data);
}
export function getWorkFileAsString(path: string): string {
return store.getFileAsString(path);
}
export function populateEntry(fs, path: string, entry: FileEntry, options: BuildOptions) {
var data = entry.data;
if (options && options.processFn) {
data = options.processFn(path, data);
}
// create subfolders
var toks = path.split('/');
if (toks.length > 1) {
for (var i = 0; i < toks.length - 1; i++)
try {
fs.mkdir(toks[i]);
} catch (e) { }
}
// write file
fs.writeFile(path, data, { encoding: entry.encoding });
var time = new Date(entry.ts);
fs.utime(path, time, time);
console.log("<<<", path, entry.data.length);
}
// can call multiple times (from populateFiles)
export function gatherFiles(step: BuildStep, options?: BuildOptions): number {
var maxts = 0;
if (step.files) {
for (var i = 0; i < step.files.length; i++) {
var path = step.files[i];
var entry = store.workfs[path];
if (!entry) {
throw new Error("No entry for path '" + path + "'");
} else {
maxts = Math.max(maxts, entry.ts);
}
}
}
else if (step.code) {
var path = step.path ? step.path : options.mainFilePath; // TODO: what if options null
if (!path) throw Error("need path or mainFilePath");
var code = step.code;
var entry = putWorkFile(path, code);
step.path = path;
step.files = [path];
maxts = entry.ts;
}
else if (step.path) {
var path = step.path;
var entry = store.workfs[path];
maxts = entry.ts;
step.files = [path];
}
if (step.path && !step.prefix) {
step.prefix = getPrefix(step.path);
}
step.maxts = maxts;
return maxts;
}
export function getPrefix(s: string): string {
var pos = s.lastIndexOf('.');
return (pos > 0) ? s.substring(0, pos) : s;
}
export function populateFiles(step: BuildStep, fs, options?: BuildOptions) {
gatherFiles(step, options);
if (!step.files) throw Error("call gatherFiles() first");
for (var i = 0; i < step.files.length; i++) {
var path = step.files[i];
populateEntry(fs, path, store.workfs[path], options);
}
}
export function populateExtraFiles(step: BuildStep, fs, extrafiles) {
if (extrafiles) {
for (var i = 0; i < extrafiles.length; i++) {
var xfn = extrafiles[i];
// is this file cached?
if (store.workfs[xfn]) {
fs.writeFile(xfn, store.workfs[xfn].data, { encoding: 'binary' });
continue;
}
// fetch from network
var xpath = "lib/" + getBasePlatform(step.platform) + "/" + xfn;
var xhr = new XMLHttpRequest();
xhr.responseType = 'arraybuffer';
xhr.open("GET", PWORKER + xpath, false); // synchronous request
xhr.send(null);
if (xhr.response && xhr.status == 200) {
var data = new Uint8Array(xhr.response);
fs.writeFile(xfn, data, { encoding: 'binary' });
putWorkFile(xfn, data);
console.log(":::", xfn, data.length);
} else {
throw Error("Could not load extra file " + xpath);
}
}
}
}
export function staleFiles(step: BuildStep, targets: string[]) {
if (!step.maxts) throw Error("call populateFiles() first");
// see if any target files are more recent than inputs
for (var i = 0; i < targets.length; i++) {
var entry = store.workfs[targets[i]];
if (!entry || step.maxts > entry.ts)
return true;
}
console.log("unchanged", step.maxts, targets);
return false;
}
export function anyTargetChanged(step: BuildStep, targets: string[]) {
if (!step.maxts) throw Error("call populateFiles() first");
// see if any target files are more recent than inputs
for (var i = 0; i < targets.length; i++) {
var entry = store.workfs[targets[i]];
if (!entry || entry.ts > step.maxts)
return true;
}
console.log("unchanged", step.maxts, targets);
return false;
}
export function fixParamsWithDefines(path: string, params) {
var libargs = params.libargs;
if (path && libargs) {
var code = getWorkFileAsString(path);
if (code) {
var oldcfgfile = params.cfgfile;
var ident2index = {};
// find all lib args "IDENT=VALUE"
for (var i = 0; i < libargs.length; i++) {
var toks = libargs[i].split('=');
if (toks.length == 2) {
ident2index[toks[0]] = i;
}
}
// find #defines and replace them
var re = /^[;]?#define\s+(\w+)\s+(\S+)/gmi; // TODO: empty string?
var m;
while (m = re.exec(code)) {
var ident = m[1];
var value = m[2];
var index = ident2index[ident];
if (index >= 0) {
libargs[index] = ident + "=" + value;
console.log('Using libargs', index, libargs[index]);
// TODO: MMC3 mapper switch
if (ident == 'NES_MAPPER' && value == '4') {
params.cfgfile = 'nesbanked.cfg';
console.log("using config file", params.cfgfile);
}
} else if (ident == 'CFGFILE' && value) {
params.cfgfile = value;
} else if (ident == 'LIBARGS' && value) {
params.libargs = value.split(',').filter((s) => { return s != ''; });
console.log('Using libargs', params.libargs);
} else if (ident == 'CC65_FLAGS' && value) {
params.extra_compiler_args = value.split(',').filter((s) => { return s != ''; });
console.log('Using compiler flags', params.extra_compiler_args);
}
}
}
}
}

129
src/worker/listingutils.ts Normal file
View File

@ -0,0 +1,129 @@
// test.c(6) : warning 85: in function main unreferenced local variable : 'x'
// main.a (4): error: Unknown Mnemonic 'xxx'.
import { SourceLine, WorkerError } from "../common/workertypes";
// at 2: warning 190: ISO C forbids an empty source file
export const re_msvc = /[/]*([^( ]+)\s*[(](\d+)[)]\s*:\s*(.+?):\s*(.*)/;
export const re_msvc2 = /\s*(at)\s+(\d+)\s*(:)\s*(.*)/;
export function msvcErrorMatcher(errors: WorkerError[]) {
return function (s: string) {
var matches = re_msvc.exec(s) || re_msvc2.exec(s);
if (matches) {
var errline = parseInt(matches[2]);
errors.push({
line: errline,
path: matches[1],
//type:matches[3],
msg: matches[4]
});
} else {
console.log(s);
}
}
}
export function makeErrorMatcher(errors: WorkerError[], regex, iline: number, imsg: number, mainpath: string, ifilename?: number) {
return function (s) {
var matches = regex.exec(s);
if (matches) {
errors.push({
line: parseInt(matches[iline]) || 1,
msg: matches[imsg],
path: ifilename ? matches[ifilename] : mainpath
});
} else {
console.log("??? " + s);
}
}
}
export function extractErrors(regex, strings: string[], path: string, iline, imsg, ifilename) {
var errors = [];
var matcher = makeErrorMatcher(errors, regex, iline, imsg, path, ifilename);
for (var i = 0; i < strings.length; i++) {
matcher(strings[i]);
}
return errors;
}
export const re_crlf = /\r?\n/;
// 1 %line 16+1 hello.asm
export const re_lineoffset = /\s*(\d+)\s+[%]line\s+(\d+)\+(\d+)\s+(.+)/;
export function parseListing(code: string,
lineMatch, iline: number, ioffset: number, iinsns: number, icycles?: number,
funcMatch?, segMatch?): SourceLine[] {
var lines: SourceLine[] = [];
var lineofs = 0;
var segment = '';
var func = '';
var funcbase = 0;
code.split(re_crlf).forEach((line, lineindex) => {
let segm = segMatch && segMatch.exec(line);
if (segm) { segment = segm[1]; }
let funcm = funcMatch && funcMatch.exec(line);
if (funcm) { funcbase = parseInt(funcm[1], 16); func = funcm[2]; }
var linem = lineMatch.exec(line);
if (linem && linem[1]) {
var linenum = iline < 0 ? lineindex : parseInt(linem[iline]);
var offset = parseInt(linem[ioffset], 16);
var insns = linem[iinsns];
var cycles: number = icycles ? parseInt(linem[icycles]) : null;
var iscode = cycles > 0;
if (insns) {
lines.push({
line: linenum + lineofs,
offset: offset - funcbase,
insns,
cycles,
iscode,
segment,
func
});
}
} else {
let m = re_lineoffset.exec(line);
// TODO: check filename too
if (m) {
lineofs = parseInt(m[2]) - parseInt(m[1]) - parseInt(m[3]);
}
}
});
return lines;
}
export function parseSourceLines(code: string, lineMatch, offsetMatch, funcMatch?, segMatch?) {
var lines = [];
var lastlinenum = 0;
var segment = '';
var func = '';
var funcbase = 0;
for (var line of code.split(re_crlf)) {
let segm = segMatch && segMatch.exec(line);
if (segm) { segment = segm[1]; }
let funcm = funcMatch && funcMatch.exec(line);
if (funcm) { funcbase = parseInt(funcm[1], 16); func = funcm[2]; }
var linem = lineMatch.exec(line);
if (linem && linem[1]) {
lastlinenum = parseInt(linem[1]);
} else if (lastlinenum) {
var linem = offsetMatch.exec(line);
if (linem && linem[1]) {
var offset = parseInt(linem[1], 16);
lines.push({
line: lastlinenum,
offset: offset - funcbase,
segment,
func
});
lastlinenum = 0;
}
}
}
return lines;
}

334
src/worker/platforms.ts Normal file
View File

@ -0,0 +1,334 @@
export var PLATFORM_PARAMS = {
'vcs': {
arch: '6502',
code_start: 0x1000,
code_size: 0xf000,
data_start: 0x80,
data_size: 0x80,
wiz_rom_ext: '.a26',
wiz_inc_dir: '2600',
cfgfile: 'atari2600.cfg',
libargs: ['crt0.o', 'atari2600.lib'],
extra_link_files: ['crt0.o', 'atari2600.cfg'],
define: ['__ATARI2600__'],
},
'mw8080bw': {
arch: 'z80',
code_start: 0x0,
rom_size: 0x2000,
data_start: 0x2000,
data_size: 0x400,
stack_end: 0x2400,
},
'vicdual': {
arch: 'z80',
code_start: 0x0,
rom_size: 0x4020,
data_start: 0xe400,
data_size: 0x400,
stack_end: 0xe800,
},
'galaxian': {
arch: 'z80',
code_start: 0x0,
rom_size: 0x4000,
data_start: 0x4000,
data_size: 0x400,
stack_end: 0x4800,
},
'galaxian-scramble': {
arch: 'z80',
code_start: 0x0,
rom_size: 0x5020,
data_start: 0x4000,
data_size: 0x400,
stack_end: 0x4800,
},
'williams': {
arch: '6809',
code_start: 0x0,
rom_size: 0xc000,
data_start: 0x9800,
data_size: 0x2800,
stack_end: 0xc000,
set_stack_end: 0xc000,
extra_link_files: ['williams.scr', 'libcmoc-crt-vec.a', 'libcmoc-std-vec.a'],
extra_link_args: ['-swilliams.scr', '-lcmoc-crt-vec', '-lcmoc-std-vec'],
extra_compile_files: ['assert.h','cmoc.h','stdarg.h','stdlib.h'],
//extra_compile_args: ['--vectrex'],
},
'williams-defender': {
arch: '6809',
code_start: 0x0,
rom_size: 0xc000,
data_start: 0x9800,
data_size: 0x2800,
stack_end: 0xc000,
},
'williams-z80': {
arch: 'z80',
code_start: 0x0,
rom_size: 0x9800,
data_start: 0x9800,
data_size: 0x2800,
stack_end: 0xc000,
},
'vector-z80color': {
arch: 'z80',
code_start: 0x0,
rom_size: 0x8000,
data_start: 0xe000,
data_size: 0x2000,
stack_end: 0x0,
},
'vector-ataricolor': { //TODO
arch: '6502',
define: ['__VECTOR__'],
cfgfile: 'vector-color.cfg',
libargs: ['crt0.o', 'none.lib'],
extra_link_files: ['crt0.o', 'vector-color.cfg'],
},
'sound_williams-z80': {
arch: 'z80',
code_start: 0x0,
rom_size: 0x4000,
data_start: 0x4000,
data_size: 0x400,
stack_end: 0x8000,
},
'base_z80': {
arch: 'z80',
code_start: 0x0,
rom_size: 0x8000,
data_start: 0x8000,
data_size: 0x8000,
stack_end: 0x0,
},
'coleco': {
arch: 'z80',
rom_start: 0x8000,
code_start: 0x8100,
rom_size: 0x8000,
data_start: 0x7000,
data_size: 0x400,
stack_end: 0x8000,
extra_preproc_args: ['-I', '/share/include/coleco', '-D', 'CV_CV'],
extra_link_args: ['-k', '/share/lib/coleco', '-l', 'libcv', '-l', 'libcvu', 'crt0.rel'],
},
'msx': {
arch: 'z80',
rom_start: 0x4000,
code_start: 0x4000,
rom_size: 0x8000,
data_start: 0xc000,
data_size: 0x3000,
stack_end: 0xffff,
extra_link_args: ['crt0-msx.rel'],
extra_link_files: ['crt0-msx.rel', 'crt0-msx.lst'],
wiz_sys_type: 'z80',
wiz_inc_dir: 'msx',
},
'msx-libcv': {
arch: 'z80',
rom_start: 0x4000,
code_start: 0x4000,
rom_size: 0x8000,
data_start: 0xc000,
data_size: 0x3000,
stack_end: 0xffff,
extra_preproc_args: ['-I', '.', '-D', 'CV_MSX'],
extra_link_args: ['-k', '.', '-l', 'libcv-msx', '-l', 'libcvu-msx', 'crt0-msx.rel'],
extra_link_files: ['libcv-msx.lib', 'libcvu-msx.lib', 'crt0-msx.rel', 'crt0-msx.lst'],
extra_compile_files: ['cv.h','cv_graphics.h','cv_input.h','cv_sound.h','cv_support.h','cvu.h','cvu_c.h','cvu_compression.h','cvu_f.h','cvu_graphics.h','cvu_input.h','cvu_sound.h'],
},
'sms-sg1000-libcv': {
arch: 'z80',
rom_start: 0x0000,
code_start: 0x0100,
rom_size: 0xc000,
data_start: 0xc000,
data_size: 0x400,
stack_end: 0xe000,
extra_preproc_args: ['-I', '.', '-D', 'CV_SMS'],
extra_link_args: ['-k', '.', '-l', 'libcv-sms', '-l', 'libcvu-sms', 'crt0-sms.rel'],
extra_link_files: ['libcv-sms.lib', 'libcvu-sms.lib', 'crt0-sms.rel', 'crt0-sms.lst'],
extra_compile_files: ['cv.h','cv_graphics.h','cv_input.h','cv_sound.h','cv_support.h','cvu.h','cvu_c.h','cvu_compression.h','cvu_f.h','cvu_graphics.h','cvu_input.h','cvu_sound.h'],
},
'nes': { //TODO
arch: '6502',
define: ['__NES__'],
cfgfile: 'neslib2.cfg',
libargs: ['crt0.o', 'nes.lib', 'neslib2.lib',
'-D', 'NES_MAPPER=0', // NROM
'-D', 'NES_PRG_BANKS=2', // 2 16K PRG banks
'-D', 'NES_CHR_BANKS=1', // 1 CHR bank
'-D', 'NES_MIRRORING=0', // horizontal mirroring
],
extra_link_files: ['crt0.o', 'neslib2.lib', 'neslib2.cfg', 'nesbanked.cfg'],
wiz_rom_ext: '.nes',
},
'apple2': {
arch: '6502',
define: ['__APPLE2__'],
cfgfile: 'apple2.cfg',
libargs: [ '--lib-path', '/share/target/apple2/drv', 'apple2.lib'],
__CODE_RUN__: 16384,
code_start: 0x803,
acmeargs: ['-f', 'apple'],
},
'apple2-e': {
arch: '6502',
define: ['__APPLE2__'],
cfgfile: 'apple2.cfg',
libargs: ['apple2.lib'],
acmeargs: ['-f', 'apple'],
},
'atari8-800xl.disk': {
arch: '6502',
define: ['__ATARI__'],
cfgfile: 'atari.cfg',
libargs: ['atari.lib'],
fastbasic_cfgfile: 'fastbasic-cart.cfg',
},
'atari8-800xl': {
arch: '6502',
define: ['__ATARI__'],
cfgfile: 'atari-cart.cfg',
libargs: ['atari.lib', '-D', '__CARTFLAGS__=4'],
fastbasic_cfgfile: 'fastbasic-cart.cfg',
},
'atari8-800': {
arch: '6502',
define: ['__ATARI__'],
cfgfile: 'atari-cart.cfg',
libargs: ['atari.lib', '-D', '__CARTFLAGS__=4'],
fastbasic_cfgfile: 'fastbasic-cart.cfg',
},
'atari8-5200': {
arch: '6502',
define: ['__ATARI5200__'],
cfgfile: 'atari5200.cfg',
libargs: ['atari5200.lib', '-D', '__CARTFLAGS__=255'],
fastbasic_cfgfile: 'fastbasic-cart.cfg',
},
'verilog': {
arch: 'verilog',
extra_compile_files: ['8bitworkshop.v'],
},
'astrocade': {
arch: 'z80',
code_start: 0x2000,
rom_size: 0x2000,
data_start: 0x4e10,
data_size: 0x1f0,
stack_end: 0x5000,
},
'astrocade-arcade': {
arch: 'z80',
code_start: 0x0000,
rom_size: 0x4000,
data_start: 0x7de0,
data_size: 0x220,
stack_end: 0x8000,
},
'astrocade-bios': {
arch: 'z80',
code_start: 0x0000,
rom_size: 0x2000,
data_start: 0x4fce,
data_size: 50,
stack_end: 0x4fce,
},
'atari7800': {
arch: '6502',
define: ['__ATARI7800__'],
cfgfile: 'atari7800.cfg',
libargs: ['crt0.o', 'none.lib'],
extra_link_files: ['crt0.o', 'atari7800.cfg'],
},
'c64': {
arch: '6502',
define: ['__CBM__', '__C64__'],
cfgfile: 'c64.cfg', // SYS 2061
libargs: ['c64.lib'],
acmeargs: ['-f', 'cbm'],
//extra_link_files: ['c64-cart.cfg'],
},
'vic20': {
arch: '6502',
define: ['__CBM__', '__VIC20__'],
cfgfile: 'vic20.cfg',
libargs: ['vic20.lib'],
acmeargs: ['-f', 'cbm'],
//extra_link_files: ['c64-cart.cfg'],
},
'kim1': {
arch: '6502',
},
'vectrex': {
arch: '6809',
code_start: 0x0,
rom_size: 0x8000,
data_start: 0xc880,
data_size: 0x380,
stack_end: 0xcc00,
extra_compile_files: ['assert.h','cmoc.h','stdarg.h','vectrex.h','stdlib.h','bios.h'],
extra_link_files: ['vectrex.scr', 'libcmoc-crt-vec.a', 'libcmoc-std-vec.a'],
extra_compile_args: ['--vectrex'],
extra_link_args: ['-svectrex.scr', '-lcmoc-crt-vec', '-lcmoc-std-vec'],
},
'x86': {
arch: 'x86',
},
'zx': {
arch: 'z80',
code_start: 0x5ccb,
rom_size: 0xff58-0x5ccb,
data_start: 0xf000,
data_size: 0xfe00-0xf000,
stack_end: 0xff58,
extra_link_args: ['crt0-zx.rel'],
extra_link_files: ['crt0-zx.rel', 'crt0-zx.lst'],
},
'devel-6502': {
arch: '6502',
cfgfile: 'devel-6502.cfg',
libargs: ['crt0.o', 'none.lib'],
extra_link_files: ['crt0.o', 'devel-6502.cfg'],
},
// https://github.com/cpcitor/cpc-dev-tool-chain
'cpc.rslib': {
arch: 'z80',
code_start: 0x4000,
rom_size: 0xb100-0x4000,
data_start: 0xb100,
data_size: 0xb100-0xc000,
stack_end: 0xc000,
extra_compile_files: ['cpcrslib.h'],
extra_link_args: ['crt0-cpc.rel', 'cpcrslib.lib'],
extra_link_files: ['crt0-cpc.rel', 'crt0-cpc.lst', 'cpcrslib.lib', 'cpcrslib.lst'],
},
// https://lronaldo.github.io/cpctelera/ (TODO)
'cpc': {
arch: 'z80',
code_start: 0x4000,
rom_size: 0xb100-0x4000,
data_start: 0xb100,
data_size: 0xb100-0xc000,
stack_end: 0xc000,
extra_compile_files: ['cpctelera.h'],
extra_link_args: ['crt0-cpc.rel', 'cpctelera.lib'],
extra_link_files: ['crt0-cpc.rel', 'crt0-cpc.lst', 'cpctelera.lib', 'cpctelera.lst'],
},
'pce': {
arch: 'huc6280',
define: ['__PCE__'],
cfgfile: 'pce.cfg',
libargs: ['pce.lib', '-D', '__CARTSIZE__=0x8000'],
},
};
PLATFORM_PARAMS['sms-sms-libcv'] = PLATFORM_PARAMS['sms-sg1000-libcv'];
PLATFORM_PARAMS['sms-gg-libcv'] = PLATFORM_PARAMS['sms-sms-libcv'];

View File

@ -2,10 +2,11 @@
import fs from 'fs';
import path from 'path';
import { spawn } from 'child_process';
import { CodeListingMap, WorkerBuildStep, WorkerError, WorkerErrorResult, WorkerFileUpdate, WorkerResult, isOutputResult } from '../../common/workertypes';
import { getBasePlatform, getRootBasePlatform, replaceAll } from '../../common/util';
import { BuildStep, makeErrorMatcher } from '../workermain';
import { WorkerBuildStep, WorkerErrorResult, WorkerFileUpdate, WorkerResult, isOutputResult } from '../../common/workertypes';
import { getRootBasePlatform, replaceAll } from '../../common/util';
import { parseObjDump } from './clang';
import { BuildStep } from '../builder';
import { makeErrorMatcher } from '../listingutils';
const LLVM_MOS_TOOL: ServerBuildTool = {

View File

@ -1,6 +1,7 @@
import { CodeListing, CodeListingMap } from "../../common/workertypes";
import { BuildStep, BuildStepResult, emglobal, execMain, fixParamsWithDefines, gatherFiles, loadNative, makeErrorMatcher, moduleInstFn, msvcErrorMatcher, populateFiles, print_fn, putWorkFile, setupFS, staleFiles } from "../workermain";
import { EmscriptenModule } from "../workermain"
import { BuildStep, BuildStepResult, gatherFiles, staleFiles, populateFiles, fixParamsWithDefines, putWorkFile } from "../builder";
import { msvcErrorMatcher } from "../listingutils";
import { loadNative, moduleInstFn, print_fn, setupFS, execMain, emglobal, EmscriptenModule } from "../wasmutils";
function parseACMESymbolTable(text: string) {
var symbolmap = {};

View File

@ -1,7 +1,9 @@
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"
import { CodeListingMap, SourceLine, WorkerError, WorkerResult } from "../../common/workertypes";
import { BuildStep, BuildStepResult, gatherFiles, staleFiles, populateFiles, putWorkFile, anyTargetChanged, getPrefix, getWorkFileAsString } from "../builder";
import { makeErrorMatcher, re_crlf } from "../listingutils";
import { loadNative, moduleInstFn, execMain, emglobal, EmscriptenModule } from "../wasmutils";
export function assembleARMIPS(step: BuildStep): WorkerResult {
loadNative("armips");

View File

@ -0,0 +1,100 @@
import { BuildStep, BuildStepResult, gatherFiles, getWorkFileAsString, populateFiles, putWorkFile, staleFiles } from "../builder";
import { EmscriptenModule, emglobal, execMain, load, print_fn, setupFS, setupStdin } from "../wasmutils";
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,
};
}

View File

@ -1,8 +1,9 @@
import { convertDataToUint8Array, getFilenamePrefix, getRootBasePlatform, safeident } 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, makeErrorMatcher, getWorkFileAsString } from "../workermain";
import { EmscriptenModule } from "../workermain"
import { BuildStep, BuildStepResult, gatherFiles, staleFiles, populateFiles, fixParamsWithDefines, putWorkFile, populateExtraFiles, store, populateEntry, anyTargetChanged } from "../builder";
import { re_crlf, makeErrorMatcher } from "../listingutils";
import { loadNative, moduleInstFn, print_fn, setupFS, execMain, emglobal, EmscriptenModule } from "../wasmutils";
/*

View File

@ -1,42 +1,12 @@
import { WASIFilesystem, WASIMemoryFilesystem, WASIRunner } from "../../common/wasi/wasishim";
import { BuildStep, BuildStepResult, gatherFiles, getWASMBinary, loadNative, loadWASMBinary, makeErrorMatcher, putWorkFile, staleFiles, store } from "../workermain";
import JSZip from 'jszip';
import { BuildStep, BuildStepResult, gatherFiles, staleFiles, store, putWorkFile } from "../builder";
import { makeErrorMatcher } from "../listingutils";
import { loadWASIFilesystemZip } from "../wasiutils";
import { loadWASMBinary } from "../wasmutils";
let cc7800_fs: WASIFilesystem | null = null;
let wasiModule: WebAssembly.Module | null = null;
function loadBlobSync(path: string) {
var xhr = new XMLHttpRequest();
xhr.responseType = 'blob';
xhr.open("GET", path, false); // synchronous request
xhr.send(null);
return xhr.response;
}
async function loadWASIFilesystemZip(zippath: string) {
const jszip = new JSZip();
const path = '../../src/worker/fs/' + zippath;
const zipdata = loadBlobSync(path);
console.log(zippath, zipdata);
await jszip.loadAsync(zipdata);
let fs = new WASIMemoryFilesystem();
let promises = [];
jszip.forEach(async (relativePath, zipEntry) => {
if (zipEntry.dir) {
fs.putDirectory(relativePath);
} else {
let path = './' + relativePath;
let prom = zipEntry.async("uint8array").then((data) => {
fs.putFile(path, data);
});
promises.push(prom);
}
});
await Promise.all(promises);
return fs;
}
export async function compileCC7800(step: BuildStep): Promise<BuildStepResult> {
const errors = [];
gatherFiles(step, { mainFilePath: "main.c" });

View File

@ -1,6 +1,7 @@
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"
import { BuildStep, BuildStepResult, populateFiles, putWorkFile, anyTargetChanged } from "../builder";
import { msvcErrorMatcher, re_crlf, re_msvc } from "../listingutils";
import { execMain, emglobal, EmscriptenModule, load } from "../wasmutils";
function parseDASMListing(lstpath: string, lsttext: string, listings: CodeListingMap, errors: WorkerError[], unresolved: {}) {
// TODO: this gets very slow
@ -212,100 +213,3 @@ export function assembleDASM(step: BuildStep): BuildStepResult {
}
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,
};
}

View File

@ -2,7 +2,7 @@ import { ECSCompiler } from "../../common/ecs/compiler";
import { Dialect_CA65, ECSError, EntityManager } from "../../common/ecs/ecs";
import { CompileError } from "../../common/tokenizer";
import { CodeListingMap } from "../../common/workertypes";
import { BuildStep, BuildStepResult, fixParamsWithDefines, gatherFiles, getWorkFileAsString, putWorkFile, staleFiles } from "../workermain";
import { BuildStep, BuildStepResult, getWorkFileAsString, gatherFiles, staleFiles, fixParamsWithDefines, putWorkFile } from "../builder";
export function assembleECS(step: BuildStep): BuildStepResult {
let em = new EntityManager(new Dialect_CA65()); // TODO

View File

@ -1,7 +1,9 @@
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"
import { BuildStep, BuildStepResult, populateFiles, putWorkFile, anyTargetChanged, gatherFiles, staleFiles } from "../builder";
import { parseListing, makeErrorMatcher } from "../listingutils";
import { loadNative, emglobal, moduleInstFn, execMain, print_fn } from "../wasmutils";
import { EmscriptenModule } from "../wasmutils";
// http://www.nespowerpak.com/nesasm/
export function assembleNESASM(step: BuildStep): BuildStepResult {

View File

@ -1,6 +1,8 @@
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";
import { BuildStep, BuildStepResult, populateFiles, putWorkFile, gatherFiles, staleFiles, getWorkFileAsString, fixParamsWithDefines, populateExtraFiles, anyTargetChanged } from "../builder";
import { parseListing, msvcErrorMatcher, parseSourceLines } from "../listingutils";
import { EmscriptenModule, emglobal, execMain, load, loadNative, moduleInstFn, print_fn } from "../wasmutils";
import { preprocessMCPP } from "./mcpp";
// http://datapipe-blackbeltsystems.com/windows/flex/asm09.html
export function assembleXASM6809(step: BuildStep): BuildStepResult {

64
src/worker/tools/mcpp.ts Normal file
View File

@ -0,0 +1,64 @@
import { getBasePlatform } from "../../common/util";
import { BuildStep, populateFiles, populateExtraFiles, errorResult } from "../builder";
import { makeErrorMatcher, extractErrors } from "../listingutils";
import { PLATFORM_PARAMS } from "../platforms";
import { load, print_fn, setupFS, execMain, emglobal, EmscriptenModule } from "../wasmutils";
function makeCPPSafe(s: string): string {
return s.replace(/[^A-Za-z0-9_]/g, '_');
}
export function preprocessMCPP(step: BuildStep, filesys: string) {
load("mcpp");
var platform = step.platform;
var params = PLATFORM_PARAMS[getBasePlatform(platform)];
if (!params) throw Error("Platform not supported: " + platform);
// <stdin>:2: error: Can't open include file "foo.h"
var errors = [];
var match_fn = makeErrorMatcher(errors, /<stdin>:(\d+): (.+)/, 1, 2, step.path);
var MCPP: EmscriptenModule = emglobal.mcpp({
noInitialRun: true,
noFSInit: true,
print: print_fn,
printErr: match_fn,
});
var FS = MCPP.FS;
if (filesys) setupFS(FS, filesys);
populateFiles(step, FS);
populateExtraFiles(step, FS, params.extra_compile_files);
// TODO: make configurable by other compilers
var args = [
"-D", "__8BITWORKSHOP__",
"-D", "__SDCC_z80",
"-D", makeCPPSafe(platform.toUpperCase()),
"-I", "/share/include",
"-Q",
step.path, "main.i"];
if (step.mainfile) {
args.unshift.apply(args, ["-D", "__MAIN__"]);
}
let platform_def = (platform.toUpperCase() as any).replaceAll(/[^a-zA-Z0-9]/g, '_');
args.unshift.apply(args, ["-D", `__PLATFORM_${platform_def}__`]);
if (params.extra_preproc_args) {
args.push.apply(args, params.extra_preproc_args);
}
execMain(step, MCPP, args);
if (errors.length)
return { errors: errors };
var iout = FS.readFile("main.i", { encoding: 'utf8' });
iout = iout.replace(/^#line /gm, '\n# ');
try {
var errout = FS.readFile("mcpp.err", { encoding: 'utf8' });
if (errout.length) {
// //main.c:2: error: Can't open include file "stdiosd.h"
var errors = extractErrors(/([^:]+):(\d+): (.+)/, errout.split("\n"), step.path, 2, 3, 1);
if (errors.length == 0) {
errors = errorResult(errout).errors;
}
return { errors: errors };
}
} catch (e) {
//
}
return { code: iout };
}

View File

@ -1,8 +1,10 @@
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 { getRootBasePlatform, parseXMLPoorly } from "../../common/util";
import { EmscriptenModule, emglobal, execMain, load, loadNative, moduleInstFn, print_fn, setupFS } from "../wasmutils";
import { BuildStep, BuildStepResult, getWorkFileAsString, gatherFiles, staleFiles, populateFiles, putWorkFile, anyTargetChanged, populateExtraFiles } from "../builder";
import { msvcErrorMatcher, parseListing, makeErrorMatcher } from "../listingutils";
import { setupRequireFunction } from "../workermain";
export function translateShowdown(step: BuildStep): BuildStepResult {
setupRequireFunction();

View File

@ -1,6 +1,6 @@
import { byteArrayToString, stringToByteArray } from "../../common/util";
import { WorkerFileUpdate, isErrorResult, isOutputResult, isUnchanged } from "../../common/workertypes";
import { BuildStep, BuildStepResult, gatherFiles, staleFiles, store } from "../workermain";
import { BuildStep, BuildStepResult, gatherFiles, staleFiles, store } from "../builder";
// create random UID
const sessionID = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);

View File

@ -1,6 +1,8 @@
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"
import { BuildStep, BuildStepResult, gatherFiles, staleFiles, populateFiles, putWorkFile, populateExtraFiles, anyTargetChanged, getWorkFileAsString } from "../builder";
import { parseListing, parseSourceLines, msvcErrorMatcher } from "../listingutils";
import { EmscriptenModule, emglobal, execMain, loadNative, moduleInstFn, print_fn, setupFS, setupStdin } from "../wasmutils";
import { preprocessMCPP } from "./mcpp";
function hexToArray(s, ofs) {
var buf = new ArrayBuffer(s.length / 2);

View File

@ -4,8 +4,9 @@
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"
import { EmscriptenModule, emglobal, execMain, getWASMMemory, loadNative, moduleInstFn, print_fn, setupFS } from "../wasmutils";
import { getWorkFileAsString, BuildStep, BuildStepResult, gatherFiles, staleFiles, populateFiles, starttime, endtime, putWorkFile, anyTargetChanged, populateExtraFiles } from "../builder";
import { makeErrorMatcher } from "../listingutils";
function detectModuleName(code: string) {
var m = /^\s*module\s+(\w+_top)\b/m.exec(code)

View File

@ -1,6 +1,8 @@
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"
import { BuildStep, BuildStepResult, gatherFiles, staleFiles, getWorkFileAsString, populateFiles, fixParamsWithDefines, putWorkFile, anyTargetChanged } from "../builder";
import { msvcErrorMatcher, parseListing } from "../listingutils";
import { EmscriptenModule, emglobal, execMain, loadNative, moduleInstFn, print_fn } from "../wasmutils";
import { preprocessMCPP } from "./mcpp";
// http://www.techhelpmanual.com/829-program_startup___exit.html
export function compileSmallerC(step: BuildStep): BuildStepResult {

View File

@ -1,6 +1,8 @@
import { CodeListingMap } from "../../common/workertypes";
import { anyTargetChanged, BuildStep, BuildStepResult, emglobal, EmscriptenModule, execMain, gatherFiles, loadNative, makeErrorMatcher, moduleInstFn, parseListing, populateFiles, print_fn, putWorkFile, staleFiles } from "../workermain"
import { BuildStep, BuildStepResult, gatherFiles, staleFiles, populateFiles, putWorkFile, anyTargetChanged } from "../builder";
import { makeErrorMatcher, parseListing } from "../listingutils";
import { EmscriptenModule, emglobal, execMain, loadNative, moduleInstFn, print_fn } from "../wasmutils";
export function assembleZMAC(step: BuildStep): BuildStepResult {

33
src/worker/wasiutils.ts Normal file
View File

@ -0,0 +1,33 @@
import JSZip from 'jszip';
import { WASIMemoryFilesystem } from "../common/wasi/wasishim";
export function loadBlobSync(path: string) {
var xhr = new XMLHttpRequest();
xhr.responseType = 'blob';
xhr.open("GET", path, false); // synchronous request
xhr.send(null);
return xhr.response;
}
export async function loadWASIFilesystemZip(zippath: string) {
const jszip = new JSZip();
const path = '../../src/worker/fs/' + zippath;
const zipdata = loadBlobSync(path);
console.log(zippath, zipdata);
await jszip.loadAsync(zipdata);
let fs = new WASIMemoryFilesystem();
let promises = [];
jszip.forEach(async (relativePath, zipEntry) => {
if (zipEntry.dir) {
fs.putDirectory(relativePath);
} else {
let path = './' + relativePath;
let prom = zipEntry.async("uint8array").then((data) => {
fs.putFile(path, data);
});
promises.push(prom);
}
});
await Promise.all(promises);
return fs;
}

188
src/worker/wasmutils.ts Normal file
View File

@ -0,0 +1,188 @@
// WebAssembly module cache
// for Emscripten-compiled functions
import { BuildStep, PWORKER, endtime, starttime } from "./builder";
/// <reference types="emscripten" />
export interface EmscriptenModule {
callMain: (args: string[]) => void;
FS: any; // TODO
}
declare function importScripts(path: string);
const ENVIRONMENT_IS_WEB = typeof window === 'object';
const ENVIRONMENT_IS_WORKER = typeof importScripts === 'function';
export const emglobal: any = ENVIRONMENT_IS_WORKER ? self : ENVIRONMENT_IS_WEB ? window : global;
// simple CommonJS module loader
// TODO: relative paths for dependencies
if (!emglobal['require']) {
emglobal['require'] = (modpath: string) => {
if (modpath.endsWith('.js')) modpath = modpath.slice(-3);
var modname = modpath.split('/').slice(-1)[0];
var hasNamespace = emglobal[modname] != null;
console.log('@@@ require', modname, modpath, hasNamespace);
if (!hasNamespace) {
exports = {};
importScripts(`${modpath}.js`);
}
if (emglobal[modname] == null) {
emglobal[modname] = exports; // TODO: always put in global scope?
}
return emglobal[modname]; // TODO
}
}
// TODO: leaks memory even when disabled...
var _WASM_module_cache = {};
var CACHE_WASM_MODULES = true; // if false, use asm.js only
// TODO: which modules need this?
var wasmMemory;
export function getWASMMemory() {
if (wasmMemory == null) {
wasmMemory = new WebAssembly.Memory({
'initial': 1024, // 64MB
'maximum': 16384, // 1024MB
});
}
return wasmMemory;
}
export function getWASMBinary(module_id: string) {
return wasmBlob[module_id];
}
function getWASMModule(module_id: string) {
var module = _WASM_module_cache[module_id];
if (!module) {
starttime();
module = new WebAssembly.Module(wasmBlob[module_id]);
if (CACHE_WASM_MODULES) {
_WASM_module_cache[module_id] = module;
delete wasmBlob[module_id];
}
endtime("module creation " + module_id);
}
return module;
}
// function for use with instantiateWasm
export function moduleInstFn(module_id: string) {
return function (imports, ri) {
var mod = getWASMModule(module_id);
var inst = new WebAssembly.Instance(mod, imports);
ri(inst);
return inst.exports;
}
}
export function execMain(step: BuildStep, mod, args: string[]) {
starttime();
var run = mod.callMain || mod.run; // TODO: run?
run(args);
endtime(step.tool);
}
/// asm.js / WASM / filesystem loading
export var fsMeta = {};
var fsBlob = {};
var wasmBlob = {};
// load filesystems for CC65 and others asynchronously
export function loadFilesystem(name: string) {
var xhr = new XMLHttpRequest();
xhr.responseType = 'blob';
xhr.open("GET", PWORKER + "fs/fs" + name + ".data", false); // synchronous request
xhr.send(null);
fsBlob[name] = xhr.response;
xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.open("GET", PWORKER + "fs/fs" + name + ".js.metadata", false); // synchronous request
xhr.send(null);
fsMeta[name] = xhr.response;
console.log("Loaded " + name + " filesystem", fsMeta[name].files.length, 'files', fsBlob[name].size, 'bytes');
}
var loaded = {};
export function load(modulename: string, debug?: boolean) {
if (!loaded[modulename]) {
importScripts(PWORKER + 'asmjs/' + modulename + (debug ? "." + debug + ".js" : ".js"));
loaded[modulename] = 1;
}
}
export function loadWASMBinary(modulename: string) {
if (!loaded[modulename]) {
var xhr = new XMLHttpRequest();
xhr.responseType = 'arraybuffer';
xhr.open("GET", PWORKER + "wasm/" + modulename + ".wasm", false); // synchronous request
xhr.send(null);
if (xhr.response) {
wasmBlob[modulename] = new Uint8Array(xhr.response);
console.log("Loaded " + modulename + ".wasm (" + wasmBlob[modulename].length + " bytes)");
loaded[modulename] = 1;
} else {
throw Error("Could not load WASM file " + modulename + ".wasm");
}
}
return wasmBlob[modulename];
}
export function loadWASM(modulename: string, debug?: boolean) {
if (!loaded[modulename]) {
importScripts(PWORKER + "wasm/" + modulename + (debug ? "." + debug + ".js" : ".js"));
loadWASMBinary(modulename);
}
}
export function loadNative(modulename: string) {
// detect WASM
if (CACHE_WASM_MODULES && typeof WebAssembly === 'object') {
loadWASM(modulename);
} else {
load(modulename);
}
}
// mount the filesystem at /share
export function setupFS(FS, name: string) {
var WORKERFS = FS.filesystems['WORKERFS'];
if (name === '65-vector') name = '65-none'; // TODO
if (name === '65-atari7800') name = '65-none'; // TODO
if (name === '65-devel') name = '65-none'; // TODO
if (name === '65-vcs') name = '65-atari2600'; // TODO
if (!fsMeta[name]) throw Error("No filesystem for '" + name + "'");
FS.mkdir('/share');
FS.mount(WORKERFS, {
packages: [{ metadata: fsMeta[name], blob: fsBlob[name] }]
}, '/share');
// fix for slow Blob operations by caching typed arrays
// https://github.com/kripken/emscripten/blob/incoming/src/library_workerfs.js
// https://bugs.chromium.org/p/chromium/issues/detail?id=349304#c30
var reader = WORKERFS.reader;
var blobcache = {};
WORKERFS.stream_ops.read = function (stream, buffer, offset, length, position) {
if (position >= stream.node.size) return 0;
var contents = blobcache[stream.path];
if (!contents) {
var ab = reader.readAsArrayBuffer(stream.node.contents);
contents = blobcache[stream.path] = new Uint8Array(ab);
}
if (position + length > contents.length)
length = contents.length - position;
for (var i = 0; i < length; i++) {
buffer[offset + i] = contents[position + i];
}
return length;
};
}
export var print_fn = function (s: string) {
console.log(s);
//console.log(new Error().stack);
}
export function setupStdin(fs, code: string) {
var i = 0;
fs.init(
function () { return i < code.length ? code.charCodeAt(i++) : null; }
);
}

File diff suppressed because it is too large Load Diff

90
src/worker/workertools.ts Normal file
View File

@ -0,0 +1,90 @@
import * as misc from './tools/misc'
import * as cc65 from './tools/cc65'
import * as dasm from './tools/dasm'
import * as sdcc from './tools/sdcc'
import * as verilog from './tools/verilog'
import * as m6809 from './tools/m6809'
import * as m6502 from './tools/m6502'
import * as z80 from './tools/z80'
import * as x86 from './tools/x86'
import * as arm from './tools/arm'
import * as ecs from './tools/ecs'
import * as remote from './tools/remote'
import * as acme from './tools/acme'
import * as cc7800 from './tools/cc7800'
import * as bataribasic from './tools/bataribasic'
import { PLATFORM_PARAMS } from "./platforms";
export const TOOLS = {
'dasm': dasm.assembleDASM,
'acme': acme.assembleACME,
'cc65': cc65.compileCC65,
'ca65': cc65.assembleCA65,
'ld65': cc65.linkLD65,
//'z80asm': assembleZ80ASM,
//'sccz80': compileSCCZ80,
'sdasz80': sdcc.assembleSDASZ80,
'sdldz80': sdcc.linkSDLDZ80,
'sdcc': sdcc.compileSDCC,
'xasm6809': m6809.assembleXASM6809,
'cmoc': m6809.compileCMOC,
'lwasm': m6809.assembleLWASM,
'lwlink': m6809.linkLWLINK,
//'naken': assembleNAKEN,
'verilator': verilog.compileVerilator,
'yosys': verilog.compileYosys,
'jsasm': verilog.compileJSASMStep,
'zmac': z80.assembleZMAC,
'nesasm': m6502.assembleNESASM,
'smlrc': x86.compileSmallerC,
'yasm': x86.assembleYASM,
'bataribasic': bataribasic.compileBatariBasic,
'markdown': misc.translateShowdown,
'inform6': misc.compileInform6,
'merlin32': m6502.assembleMerlin32,
'fastbasic': m6502.compileFastBasic,
'basic': misc.compileBASIC,
'silice': verilog.compileSilice,
'wiz': misc.compileWiz,
'armips': arm.assembleARMIPS,
'vasmarm': arm.assembleVASMARM,
'ecs': ecs.assembleECS,
'remote': remote.buildRemote,
'cc7800': cc7800.compileCC7800,
}
export const TOOL_PRELOADFS = {
'cc65-apple2': '65-apple2',
'ca65-apple2': '65-apple2',
'cc65-c64': '65-c64',
'ca65-c64': '65-c64',
'cc65-vic20': '65-vic20',
'ca65-vic20': '65-vic20',
'cc65-nes': '65-nes',
'ca65-nes': '65-nes',
'cc65-atari8': '65-atari8',
'ca65-atari8': '65-atari8',
'cc65-vector': '65-none',
'ca65-vector': '65-none',
'cc65-atari7800': '65-none',
'ca65-atari7800': '65-none',
'cc65-devel': '65-none',
'ca65-devel': '65-none',
'cc65-vcs': '65-atari2600',
'ca65-vcs': '65-atari2600',
'cc65-pce': '65-pce',
'ca65-pce': '65-pce',
'sdasz80': 'sdcc',
'sdcc': 'sdcc',
'sccz80': 'sccz80',
'bataribasic': '2600basic',
'inform6': 'inform',
'fastbasic': '65-atari8',
'silice': 'Silice',
'wiz': 'wiz',
'ecs-vcs': '65-atari2600', // TODO: support multiple platforms
'ecs-nes': '65-nes', // TODO: support multiple platforms
'ecs-c64': '65-c64', // TODO: support multiple platforms
}