From 2e0382b0a6ada8b88a66125268f5632a55e04f58 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Wed, 29 Nov 2023 14:36:58 -0600 Subject: [PATCH] worker: refactor --- src/worker/builder.ts | 416 +++++++++++ src/worker/listingutils.ts | 129 ++++ src/worker/platforms.ts | 334 +++++++++ src/worker/server/buildenv.ts | 7 +- src/worker/tools/acme.ts | 5 +- src/worker/tools/arm.ts | 6 +- src/worker/tools/bataribasic.ts | 100 +++ src/worker/tools/cc65.ts | 5 +- src/worker/tools/cc7800.ts | 38 +- src/worker/tools/dasm.ts | 102 +-- src/worker/tools/ecs.ts | 2 +- src/worker/tools/m6502.ts | 6 +- src/worker/tools/m6809.ts | 6 +- src/worker/tools/mcpp.ts | 64 ++ src/worker/tools/misc.ts | 6 +- src/worker/tools/remote.ts | 2 +- src/worker/tools/sdcc.ts | 6 +- src/worker/tools/verilog.ts | 5 +- src/worker/tools/x86.ts | 6 +- src/worker/tools/z80.ts | 4 +- src/worker/wasiutils.ts | 33 + src/worker/wasmutils.ts | 188 +++++ src/worker/workermain.ts | 1224 +------------------------------ src/worker/workertools.ts | 90 +++ 24 files changed, 1416 insertions(+), 1368 deletions(-) create mode 100644 src/worker/builder.ts create mode 100644 src/worker/listingutils.ts create mode 100644 src/worker/platforms.ts create mode 100644 src/worker/tools/bataribasic.ts create mode 100644 src/worker/tools/mcpp.ts create mode 100644 src/worker/wasiutils.ts create mode 100644 src/worker/wasmutils.ts create mode 100644 src/worker/workertools.ts diff --git a/src/worker/builder.ts b/src/worker/builder.ts new file mode 100644 index 00000000..4b427541 --- /dev/null +++ b/src/worker/builder.ts @@ -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 { + 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 { + 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); + } + } + } + } +} diff --git a/src/worker/listingutils.ts b/src/worker/listingutils.ts new file mode 100644 index 00000000..4a25194f --- /dev/null +++ b/src/worker/listingutils.ts @@ -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; +} diff --git a/src/worker/platforms.ts b/src/worker/platforms.ts new file mode 100644 index 00000000..0c53adc4 --- /dev/null +++ b/src/worker/platforms.ts @@ -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']; + \ No newline at end of file diff --git a/src/worker/server/buildenv.ts b/src/worker/server/buildenv.ts index e9c5e079..7ad8f0e4 100644 --- a/src/worker/server/buildenv.ts +++ b/src/worker/server/buildenv.ts @@ -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 = { diff --git a/src/worker/tools/acme.ts b/src/worker/tools/acme.ts index 90d63b12..f9e3c654 100644 --- a/src/worker/tools/acme.ts +++ b/src/worker/tools/acme.ts @@ -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 = {}; diff --git a/src/worker/tools/arm.ts b/src/worker/tools/arm.ts index b51bab66..c59fcc9d 100644 --- a/src/worker/tools/arm.ts +++ b/src/worker/tools/arm.ts @@ -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"); diff --git a/src/worker/tools/bataribasic.ts b/src/worker/tools/bataribasic.ts new file mode 100644 index 00000000..b24ab30a --- /dev/null +++ b/src/worker/tools/bataribasic.ts @@ -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, + }; +} \ No newline at end of file diff --git a/src/worker/tools/cc65.ts b/src/worker/tools/cc65.ts index 24773f6c..656512a5 100644 --- a/src/worker/tools/cc65.ts +++ b/src/worker/tools/cc65.ts @@ -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"; /* diff --git a/src/worker/tools/cc7800.ts b/src/worker/tools/cc7800.ts index 363b2be1..14b6bebd 100644 --- a/src/worker/tools/cc7800.ts +++ b/src/worker/tools/cc7800.ts @@ -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 { const errors = []; gatherFiles(step, { mainFilePath: "main.c" }); diff --git a/src/worker/tools/dasm.ts b/src/worker/tools/dasm.ts index d6a9c928..02af5da7 100644 --- a/src/worker/tools/dasm.ts +++ b/src/worker/tools/dasm.ts @@ -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, - }; -} \ No newline at end of file diff --git a/src/worker/tools/ecs.ts b/src/worker/tools/ecs.ts index 6798ca5a..b00fbf88 100644 --- a/src/worker/tools/ecs.ts +++ b/src/worker/tools/ecs.ts @@ -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 diff --git a/src/worker/tools/m6502.ts b/src/worker/tools/m6502.ts index 3b4dceaa..e35b2047 100644 --- a/src/worker/tools/m6502.ts +++ b/src/worker/tools/m6502.ts @@ -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 { diff --git a/src/worker/tools/m6809.ts b/src/worker/tools/m6809.ts index 760872eb..bc4ca4e4 100644 --- a/src/worker/tools/m6809.ts +++ b/src/worker/tools/m6809.ts @@ -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 { diff --git a/src/worker/tools/mcpp.ts b/src/worker/tools/mcpp.ts new file mode 100644 index 00000000..ccc57614 --- /dev/null +++ b/src/worker/tools/mcpp.ts @@ -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); + // :2: error: Can't open include file "foo.h" + var errors = []; + var match_fn = makeErrorMatcher(errors, /:(\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 }; +} \ No newline at end of file diff --git a/src/worker/tools/misc.ts b/src/worker/tools/misc.ts index e55937f2..55fe3bba 100644 --- a/src/worker/tools/misc.ts +++ b/src/worker/tools/misc.ts @@ -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(); diff --git a/src/worker/tools/remote.ts b/src/worker/tools/remote.ts index bf0cf5a9..be6647b0 100644 --- a/src/worker/tools/remote.ts +++ b/src/worker/tools/remote.ts @@ -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); diff --git a/src/worker/tools/sdcc.ts b/src/worker/tools/sdcc.ts index 2f53be94..e665edfb 100644 --- a/src/worker/tools/sdcc.ts +++ b/src/worker/tools/sdcc.ts @@ -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); diff --git a/src/worker/tools/verilog.ts b/src/worker/tools/verilog.ts index a3e29c59..303a2546 100644 --- a/src/worker/tools/verilog.ts +++ b/src/worker/tools/verilog.ts @@ -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) diff --git a/src/worker/tools/x86.ts b/src/worker/tools/x86.ts index 96842e78..1127b33c 100644 --- a/src/worker/tools/x86.ts +++ b/src/worker/tools/x86.ts @@ -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 { diff --git a/src/worker/tools/z80.ts b/src/worker/tools/z80.ts index 0c5292d7..f638ab04 100644 --- a/src/worker/tools/z80.ts +++ b/src/worker/tools/z80.ts @@ -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 { diff --git a/src/worker/wasiutils.ts b/src/worker/wasiutils.ts new file mode 100644 index 00000000..6ddc8be7 --- /dev/null +++ b/src/worker/wasiutils.ts @@ -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; +} diff --git a/src/worker/wasmutils.ts b/src/worker/wasmutils.ts new file mode 100644 index 00000000..1d76598b --- /dev/null +++ b/src/worker/wasmutils.ts @@ -0,0 +1,188 @@ + +// WebAssembly module cache +// for Emscripten-compiled functions + +import { BuildStep, PWORKER, endtime, starttime } from "./builder"; + +/// +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; } + ); +} diff --git a/src/worker/workermain.ts b/src/worker/workermain.ts index 371807b2..156eb07d 100644 --- a/src/worker/workermain.ts +++ b/src/worker/workermain.ts @@ -1,1237 +1,38 @@ -import type { WorkerResult, WorkerBuildStep, WorkerMessage, WorkerError, SourceLine, WorkerErrorResult, WorkingStore } from "../common/workertypes"; +import type { WorkerResult, WorkerMessage, WorkerError, SourceLine } from "../common/workertypes"; import { getBasePlatform, getRootBasePlatform } from "../common/util"; +import { TOOL_PRELOADFS } from "./workertools"; +import { store, builder, errorResult, getWorkFileAsString } from "./builder"; +import { emglobal, fsMeta, loadFilesystem } from "./wasmutils"; -/// -export interface EmscriptenModule { - callMain: (args: string[]) => void; - FS : any; // TODO -} - -declare function importScripts(path:string); +declare function importScripts(path: string); declare function postMessage(msg); -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 - } -} - -// WebAssembly module cache -// 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; - } -} - -// - -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']; - -var _t1; -export function starttime() { _t1 = new Date(); } -export function endtime(msg) { var _t2 = new Date(); console.log(msg, _t2.getTime() - _t1.getTime(), "ms"); } - -/// working file store and build steps - -type FileData = string | Uint8Array; - -type FileEntry = { - path: string - encoding: string - data: FileData - ts: number -}; - -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(); - -/// - -function errorResult(msg: string) : WorkerErrorResult { - return { errors:[{ line:0, msg:msg }]}; -} - -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 { - 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 { - 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); - } -} - -var builder = new Builder(); - -/// - -function applyDefaultErrorPath(errors:WorkerError[], path:string) { - if (!path) return; - for (var i=0; i 1) { - for (var i=0; i 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 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 step.maxts) - return true; - } - console.log("unchanged", step.maxts, targets); - return false; -} - -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 - -var fsMeta = {}; -var fsBlob = {}; -var wasmBlob = {}; - -const PSRC = "../../src/"; -const PWORKER = PSRC+"worker/"; - -// load filesystems for CC65 and others asynchronously -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 { - 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; -} - -export function setupStdin(fs, code:string) { - var i = 0; - fs.init( - function() { return i= 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); - } - } - } - } -} - - -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); - // :2: error: Can't open include file "foo.h" - var errors = []; - var match_fn = makeErrorMatcher(errors, /:(\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}; -} - export function setupRequireFunction() { var exports = {}; exports['jsdom'] = { - JSDOM: function(a,b) { + JSDOM: function (a, b) { this.window = {}; } }; - emglobal['require'] = (modname:string) => { - console.log('require',modname,exports[modname]!=null); + emglobal['require'] = (modname: string) => { + console.log('require', modname, exports[modname] != null); return exports[modname]; } } //////////////////////////// -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' - -var 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': dasm.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, -} - -var 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 -} - //const waitFor = delay => new Promise(resolve => setTimeout(resolve, delay)); // for testing -async function handleMessage(data : WorkerMessage) : Promise { +async function handleMessage(data: WorkerMessage): Promise { // preload file system if (data.preload) { var fs = TOOL_PRELOADFS[data.preload]; if (!fs && data.platform) - fs = TOOL_PRELOADFS[data.preload+'-'+getBasePlatform(data.platform)]; + fs = TOOL_PRELOADFS[data.preload + '-' + getBasePlatform(data.platform)]; if (!fs && data.platform) - fs = TOOL_PRELOADFS[data.preload+'-'+getRootBasePlatform(data.platform)]; + fs = TOOL_PRELOADFS[data.preload + '-' + getRootBasePlatform(data.platform)]; if (fs && !fsMeta[fs]) loadFilesystem(fs); return; @@ -1244,9 +45,10 @@ async function handleMessage(data : WorkerMessage) : Promise { return builder.handleMessage(data); } +const ENVIRONMENT_IS_WORKER = typeof importScripts === 'function'; if (ENVIRONMENT_IS_WORKER) { var lastpromise = null; - onmessage = async function(e) { + onmessage = async function (e) { await lastpromise; // wait for previous message to complete lastpromise = handleMessage(e.data); var result = await lastpromise; diff --git a/src/worker/workertools.ts b/src/worker/workertools.ts new file mode 100644 index 00000000..4040e470 --- /dev/null +++ b/src/worker/workertools.ts @@ -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 +} +