import type { WorkerResult, WorkerBuildStep, WorkerMessage, WorkerError, SourceLine, WorkerErrorResult, WorkingStore } from "../common/workertypes"; import { getBasePlatform, getRootBasePlatform } from "../common/util"; /// export interface EmscriptenModule { callMain: (args: string[]) => void; FS : any; // TODO } 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; } 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 loadWASM(modulename:string, debug?:boolean) { if (!loaded[modulename]) { importScripts(PWORKER+"wasm/" + modulename+(debug?"."+debug+".js":".js")); 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"); } } } 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) { this.window = {}; } }; 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' 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 } 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 { // preload file system if (data.preload) { var fs = TOOL_PRELOADFS[data.preload]; if (!fs && data.platform) fs = TOOL_PRELOADFS[data.preload+'-'+getBasePlatform(data.platform)]; if (!fs && data.platform) fs = TOOL_PRELOADFS[data.preload+'-'+getRootBasePlatform(data.platform)]; if (fs && !fsMeta[fs]) loadFilesystem(fs); return; } // clear filesystem? (TODO: buildkey) if (data.reset) { store.reset(); return; } return builder.handleMessage(data); } if (ENVIRONMENT_IS_WORKER) { var lastpromise = null; onmessage = async function(e) { await lastpromise; // wait for previous message to complete lastpromise = handleMessage(e.data); var result = await lastpromise; lastpromise = null; if (result) { try { postMessage(result); } catch (e) { console.log(e); postMessage(errorResult(`${e}`)); } } } }